Mercurial > hg > webaudioevaluationtool
changeset 2713:2dfc19a33bbc
Merge branch 'master' into vnext
# Conflicts:
# js/core.js
# js/specification.js
author | Nicholas Jillings <n.g.r.jillings@se14.qmul.ac.uk> |
---|---|
date | Fri, 14 Apr 2017 16:02:52 +0100 |
parents | 549e2f97a401 (current diff) b9efbbe0d829 (diff) |
children | 52224b9d6e8b |
files | js/core.js js/specification.js |
diffstat | 3 files changed, 838 insertions(+), 918 deletions(-) [+] |
line wrap: on
line diff
--- a/js/core.js Wed Mar 29 15:57:09 2017 +0100 +++ b/js/core.js Fri Apr 14 16:02:52 2017 +0100 @@ -5,10 +5,6 @@ * Also contains all global variables. */ -/*globals window, document, XMLDocument, Element, XMLHttpRequest, DOMParser, console, Blob, $, Promise, navigator */ -/*globals AudioBuffer, AudioBufferSourceNode */ -/*globals Specification, calculateLoudness, WAVE, validateXML, showdown, pageXMLSave, loadTest, resizeWindow */ - /* create the web audio API context and store in audioContext*/ var audioContext; // Hold the browser web audio API var projectXML; // Hold the parsed setup XML @@ -52,13 +48,13 @@ name = String(name); var selected = this.documentElement.getAllElementsByName(name); return selected; -}; +} Element.prototype.getAllElementsByName = function (name) { name = String(name); var selected = []; var node = this.firstElementChild; - while (node !== null) { + while (node != null) { if (node.getAttribute('name') == name) { selected.push(node); } @@ -68,19 +64,19 @@ node = node.nextElementSibling; } return selected; -}; +} XMLDocument.prototype.getAllElementsByTagName = function (name) { name = String(name); var selected = this.documentElement.getAllElementsByTagName(name); return selected; -}; +} Element.prototype.getAllElementsByTagName = function (name) { name = String(name); var selected = []; var node = this.firstElementChild; - while (node !== null) { + while (node != null) { if (node.nodeName == name) { selected.push(node); } @@ -90,7 +86,7 @@ node = node.nextElementSibling; } return selected; -}; +} // Firefox does not have an XMLDocument.prototype.getElementsByName if (typeof XMLDocument.prototype.getElementsByName != "function") { @@ -98,14 +94,14 @@ name = String(name); var node = this.documentElement.firstElementChild; var selected = []; - while (node !== null) { + while (node != null) { if (node.getAttribute('name') == name) { selected.push(node); } node = node.nextElementSibling; } return selected; - }; + } } var check_dependancies = function () { @@ -126,7 +122,7 @@ return false; } return true; -}; +} var onload = function () { // Function called once the browser has loaded all files. @@ -135,7 +131,7 @@ // Create a web audio API context // Fixed for cross-browser support var AudioContext = window.AudioContext || window.webkitAudioContext; - audioContext = new AudioContext(); + audioContext = new AudioContext; // Create test state testState = new stateMachine(); @@ -156,11 +152,11 @@ interfaceContext.resizeWindow(event); }; - if (window.location.search.length !== 0) { + if (window.location.search.length != 0) { var search = window.location.search.split('?')[1]; // Now split the requests into pairs var searchQueries = search.split('&'); - var url; + for (var i in searchQueries) { // Split each key-value pair searchQueries[i] = searchQueries[i].split('='); @@ -169,7 +165,6 @@ switch (key) { case "url": url = value; - specification.url = url; break; case "returnURL": gReturnURL = value; @@ -193,7 +188,9 @@ var xmlhttp = new XMLHttpRequest(); xmlhttp.open("GET", 'xml/test-schema.xsd', true); xmlhttp.onload = function () { - specification.processSchema(xmlhttp.response); + schemaXSD = xmlhttp.response; + var parse = new DOMParser(); + specification.schema = parse.parseFromString(xmlhttp.response, 'text/xml'); var r = new XMLHttpRequest(); r.open('GET', url, true); r.onload = function () { @@ -207,11 +204,11 @@ span.textContent = "There was an error when loading your XML file. Please check your path in the URL. After the path to this page, there should be '?url=path/to/your/file.xml'. Check the spelling of your filename as well. If you are still having issues, check the log of the python server or your webserver distribution for 404 codes for your file."; document.getElementsByTagName('body')[0].appendChild(msg); document.getElementsByTagName('body')[0].appendChild(span); - }; + } r.send(); }; xmlhttp.send(); -} +}; function loadProjectSpecCallback(response) { // Function called after asynchronous download of XML project specification @@ -222,11 +219,10 @@ var parse = new DOMParser(); var responseDocument = parse.parseFromString(response, 'text/xml'); var errorNode = responseDocument.getElementsByTagName('parsererror'); - var msg, span; if (errorNode.length >= 1) { - msg = document.createElement("h3"); + var msg = document.createElement("h3"); msg.textContent = "FATAL ERROR"; - span = document.createElement("span"); + var span = document.createElement("span"); span.textContent = "The XML parser returned the following errors when decoding your XML file"; document.getElementsByTagName('body')[0].innerHTML = null; document.getElementsByTagName('body')[0].appendChild(msg); @@ -234,10 +230,10 @@ document.getElementsByTagName('body')[0].appendChild(errorNode[0]); return; } - if (responseDocument === undefined || responseDocument.firstChild === undefined) { - msg = document.createElement("h3"); + if (responseDocument == undefined || responseDocument.firstChild == undefined) { + var msg = document.createElement("h3"); msg.textContent = "FATAL ERROR"; - span = document.createElement("span"); + var span = document.createElement("span"); span.textContent = "The project XML was not decoded properly, try refreshing your browser and clearing caches. If the problem persists, contact the test creator."; document.getElementsByTagName('body')[0].innerHTML = null; document.getElementsByTagName('body')[0].appendChild(msg); @@ -250,7 +246,7 @@ // Perform XML schema validation var Module = { xml: response, - schema: specification.getSchemaString(), + schema: schemaXSD, arguments: ["--noout", "--schema", 'test-schema.xsd', 'document.xml'] }; projectXML = responseDocument; @@ -258,16 +254,16 @@ console.log(xmllint); if (xmllint != 'document.xml validates\n') { document.getElementsByTagName('body')[0].innerHTML = null; - msg = document.createElement("h3"); + var msg = document.createElement("h3"); msg.textContent = "FATAL ERROR"; - span = document.createElement("h4"); + var span = document.createElement("h4"); span.textContent = "The XML validator returned the following errors when decoding your XML file"; document.getElementsByTagName('body')[0].appendChild(msg); document.getElementsByTagName('body')[0].appendChild(span); xmllint = xmllint.split('\n'); for (var i in xmllint) { document.getElementsByTagName('body')[0].appendChild(document.createElement('br')); - span = document.createElement("span"); + var span = document.createElement("span"); span.textContent = xmllint[i]; document.getElementsByTagName('body')[0].appendChild(span); } @@ -282,16 +278,15 @@ // document is a result projectXML = document.implementation.createDocument(null, "waet"); projectXML.firstChild.appendChild(responseDocument.getElementsByTagName('waet')[0].getElementsByTagName("setup")[0].cloneNode(true)); - var child = responseDocument.firstChild.firstChild, - copy; - while (child !== null) { + var child = responseDocument.firstChild.firstChild; + while (child != null) { if (child.nodeName == "survey") { // One of the global survey elements if (child.getAttribute("state") == "complete") { // We need to remove this survey from <setup> var location = child.getAttribute("location"); var globalSurveys = projectXML.getElementsByTagName("setup")[0].getElementsByTagName("survey")[0]; - while (globalSurveys !== null) { + while (globalSurveys != null) { if (location == "pre" || location == "before") { if (globalSurveys.getAttribute("location") == "pre" || globalSurveys.getAttribute("location") == "before") { projectXML.getElementsByTagName("setup")[0].removeChild(globalSurveys); @@ -307,7 +302,7 @@ } } else { // We need to complete this, so it must be regenerated by store - copy = child; + var copy = child; child = child.previousElementSibling; responseDocument.firstChild.removeChild(copy); } @@ -315,7 +310,7 @@ if (child.getAttribute("state") == "empty") { // We need to complete this page projectXML.firstChild.appendChild(responseDocument.getElementById(child.getAttribute("ref")).cloneNode(true)); - copy = child; + var copy = child; child = child.previousElementSibling; responseDocument.firstChild.removeChild(copy); } @@ -328,7 +323,7 @@ storage.initialise(responseDocument); } /// CHECK FOR SAMPLE RATE COMPATIBILITY - if (isFinite(specification.sampleRate)) { + if (specification.sampleRate != undefined) { if (Number(specification.sampleRate) != audioContext.sampleRate) { var errStr = 'Sample rates do not match! Requested ' + Number(specification.sampleRate) + ', got ' + audioContext.sampleRate + '. Please set the sample rate to match before completing this test.'; interfaceContext.lightbox.post("Error", errStr); @@ -340,7 +335,7 @@ getInterfaces.open("GET", "interfaces/interfaces.json"); getInterfaces.onerror = function (e) { throw (e); - }; + } getInterfaces.onload = function () { if (getInterfaces.status !== 200) { throw (new Error(getInterfaces.status)); @@ -368,14 +363,14 @@ css.setAttribute("href", v); head.appendChild(css); }); - }; + } getInterfaces.send(); - if (gReturnURL !== undefined) { + if (gReturnURL != undefined) { console.log("returnURL Overide from " + specification.returnURL + " to " + gReturnURL); specification.returnURL = gReturnURL; } - if (gSaveFilenamePrefix !== undefined) { + if (gSaveFilenamePrefix != undefined) { specification.saveFilenamePrefix = gSaveFilenamePrefix; } @@ -420,7 +415,7 @@ } } var saveURL = projectReturn + "php/save.php?key=" + storage.SessionKey.key + saveUrlSuffix; - var xmlhttp = new XMLHttpRequest(); + var xmlhttp = new XMLHttpRequest; xmlhttp.open("POST", saveURL, true); xmlhttp.setRequestHeader('Content-Type', 'text/xml'); xmlhttp.onerror = function () { @@ -522,7 +517,7 @@ } function randomString(length) { - var str = ""; + var str = "" for (var i = 0; i < length; i += 2) { var num = Math.floor(Math.random() * 1295); str += num.toString(36); @@ -537,7 +532,7 @@ var inputSequence = []; // For safety purposes: keep track of randomisation for (var counter = 0; counter < N; ++counter) - inputSequence.push(counter); // Fill array + inputSequence.push(counter) // Fill array var inputSequenceClone = inputSequence.slice(0); var holdArr = []; @@ -589,441 +584,6 @@ e.preventDefault(); } }); - // Generators & Processors // - - function processConditional(node, value) { - function jumpToId(jumpID) { - var index = this.popupOptions.findIndex(function (item, index, element) { - if (item.specification.id == jumpID) { - return true; - } else { - return false; - } - }, this); - this.currentIndex = index - 1; - } - var conditionFunction; - if (node.specification.type === "question") { - conditionFunction = processQuestionConditional; - } else if (node.specification.type === "checkbox") { - conditionFunction = processCheckboxConditional; - } else if (node.specification.type === "radio") { - conditionFunction = processRadioConditional; - } else if (node.specification.type === "number") { - conditionFunction = processNumberConditional; - } else if (node.specification.type === "slider") { - conditionFunction = processSliderConditional; - } else { - return; - } - for (var i = 0; i < node.specification.conditions.length; i++) { - var condition = node.specification.conditions[i]; - var pass = conditionFunction(condition, value); - var jumpID; - if (pass) { - jumpID = condition.jumpToOnPass; - } else { - jumpID = condition.jumpToOnFail; - } - if (jumpID !== undefined) { - jumpToId.call(this, jumpID); - break; - } - } - } - - function postQuestion(node) { - var textArea = document.createElement('textarea'); - switch (node.specification.boxsize) { - case 'small': - textArea.cols = "20"; - textArea.rows = "1"; - break; - case 'normal': - textArea.cols = "30"; - textArea.rows = "2"; - break; - case 'large': - textArea.cols = "40"; - textArea.rows = "5"; - break; - case 'huge': - textArea.cols = "50"; - textArea.rows = "10"; - break; - } - if (node.response === undefined) { - node.response = ""; - } else { - textArea.value = node.response; - } - this.popupResponse.appendChild(textArea); - textArea.focus(); - this.popupResponse.style.textAlign = "center"; - this.popupResponse.style.left = "0%"; - } - - function processQuestionConditional(condition, value) { - switch (condition.check) { - case "equals": - // Deliberately loose check - if (value == condition.value) { - return true; - } - break; - case "greaterThan": - case "lessThan": - console.log("Survey Element of type 'question' cannot interpret greaterThan/lessThan conditions. IGNORING"); - break; - case "contains": - if (value.includes(condition.value)) { - return true; - } - break; - } - return false; - } - - function processQuestion(node) { - var textArea = this.popupResponse.getElementsByTagName("textarea")[0]; - if (node.specification.mandatory === true && textArea.value.length === 0) { - interfaceContext.lightbox.post("Error", "This question is mandatory"); - return false; - } - // Save the text content - console.log("Question: " + node.specification.statement); - console.log("Question Response: " + textArea.value); - node.response = textArea.value; - processConditional.call(this, node, textArea.value); - return true; - } - - function postCheckbox(node) { - if (node.response === undefined) { - node.response = Array(node.specification.options.length); - } - var table = document.createElement("table"); - table.className = "popup-option-list"; - table.border = "0"; - node.response = []; - node.specification.options.forEach(function (option, index) { - var tr = document.createElement("tr"); - table.appendChild(tr); - var td = document.createElement("td"); - tr.appendChild(td); - var input = document.createElement('input'); - input.id = option.name; - input.type = 'checkbox'; - td.appendChild(input); - - td = document.createElement("td"); - tr.appendChild(td); - var span = document.createElement('span'); - span.textContent = option.text; - td.appendChild(span); - tr = document.createElement('div'); - tr.setAttribute('name', 'option'); - tr.className = "popup-option-checbox"; - if (node.response[index] !== undefined) { - if (node.response[index].checked === true) { - input.checked = "true"; - } - } - index++; - }); - this.popupResponse.appendChild(table); - } - - function processCheckbox(node) { - console.log("Checkbox: " + node.specification.statement); - var inputs = this.popupResponse.getElementsByTagName('input'); - node.response = []; - var numChecked = 0, - i; - for (i = 0; i < node.specification.options.length; i++) { - if (inputs[i].checked) { - numChecked++; - } - } - if (node.specification.min !== undefined) { - if (node.specification.max === undefined) { - if (numChecked < node.specification.min) { - var msg = "You must select at least " + node.specification.min + " option"; - if (node.specification.min > 1) { - msg += "s"; - } - interfaceContext.lightbox.post("Error", msg); - return; - } - } else { - if (numChecked < node.specification.min || numChecked > node.specification.max) { - if (node.specification.min == node.specification.max) { - interfaceContext.lightbox.post("Error", "You must only select " + node.specification.min); - } else { - interfaceContext.lightbox.post("Error", "You must select between " + node.specification.min + " and " + node.specification.max); - } - return false; - } - } - } - for (i = 0; i < node.specification.options.length; i++) { - node.response.push({ - name: node.specification.options[i].name, - text: node.specification.options[i].text, - checked: inputs[i].checked - }); - console.log(node.specification.options[i].name + ": " + inputs[i].checked); - } - processConditional.call(this, node, node.response); - return true; - } - - function processCheckboxConditional(condition, response) { - switch (condition.check) { - case "contains": - for (var i = 0; i < response.length; i++) { - var value = response[i]; - if (value.name === condition.value && value.checked) { - return true; - } - } - break; - case "equals": - case "greaterThan": - case "lessThan": - console.log("Survey Element of type 'checkbox' cannot interpret equals/greaterThan/lessThan conditions. IGNORING"); - break; - default: - console.log("Unknown condition. IGNORING"); - break; - } - return false; - } - - function postRadio(node) { - if (node.response === null) { - node.response = { - name: "", - text: "" - }; - } - var table = document.createElement("table"); - table.className = "popup-option-list"; - table.border = "0"; - if (node.response === null || node.response.length === 0) { - node.response = []; - } - node.specification.options.forEach(function (option, index) { - var tr = document.createElement("tr"); - table.appendChild(tr); - var td = document.createElement("td"); - tr.appendChild(td); - var input = document.createElement('input'); - input.id = option.name; - input.type = 'radio'; - input.name = node.specification.id; - td.appendChild(input); - - td = document.createElement("td"); - tr.appendChild(td); - var span = document.createElement('span'); - span.textContent = option.text; - td.appendChild(span); - tr = document.createElement('div'); - tr.setAttribute('name', 'option'); - tr.className = "popup-option-checbox"; - table.appendChild(tr); - }); - this.popupResponse.appendChild(table); - } - - function processRadio(node) { - var optHold = this.popupResponse; - console.log("Radio: " + node.specification.statement); - node.response = null; - var i = 0; - var inputs = optHold.getElementsByTagName('input'); - while (node.response === null) { - if (i == inputs.length) { - if (node.specification.mandatory === true) { - interfaceContext.lightbox.post("Error", "Please select one option"); - return false; - } - break; - } - if (inputs[i].checked === true) { - node.response = node.specification.options[i]; - console.log("Selected: " + node.specification.options[i].name); - } - i++; - } - processConditional.call(this, node, node.response); - return true; - } - - function processRadioConditional(condition, response) { - switch (condition.check) { - case "equals": - if (response === condition.value) { - return true; - } - break; - case "contains": - case "greaterThan": - case "lessThan": - console.log("Survey Element of type 'radio' cannot interpret contains/greaterThan/lessThan conditions. IGNORING"); - break; - default: - console.log("Unknown condition. IGNORING"); - break; - } - return false; - } - - function postNumber(node) { - var input = document.createElement('input'); - input.type = 'textarea'; - if (node.specification.min !== null) { - input.min = node.specification.min; - } - if (node.specification.max !== null) { - input.max = node.specification.max; - } - if (node.specification.step !== null) { - input.step = node.specification.step; - } - if (node.response !== undefined) { - input.value = node.response; - } - this.popupResponse.appendChild(input); - this.popupResponse.style.textAlign = "center"; - this.popupResponse.style.left = "0%"; - } - - function processNumber(node) { - var input = this.popupContent.getElementsByTagName('input')[0]; - if (node.mandatory === true && input.value.length === 0) { - interfaceContext.lightbox.post("Error", 'This question is mandatory. Please enter a number'); - return false; - } - var enteredNumber = Number(input.value); - if (isNaN(enteredNumber)) { - interfaceContext.lightbox.post("Error", 'Please enter a valid number'); - return false; - } - if (enteredNumber < node.min && node.min !== null) { - interfaceContext.lightbox.post("Error", 'Number is below the minimum value of ' + node.min); - return false; - } - if (enteredNumber > node.max && node.max !== null) { - interfaceContext.lightbox.post("Error", 'Number is above the maximum value of ' + node.max); - return false; - } - node.response = input.value; - processConditional.call(this, node, node.response); - return true; - } - - function processNumberConditional(condtion, value) { - var condition = condition; - switch (condition.check) { - case "greaterThan": - if (value > Number(condition.value)) { - return true; - } - break; - case "lessThan": - if (value < Number(condition.value)) { - return true; - } - break; - case "equals": - if (value == condition.value) { - return true; - } - break; - case "contains": - console.log("Survey Element of type 'number' cannot interpret \"contains\" conditions. IGNORING"); - break; - default: - console.log("Unknown condition. IGNORING"); - break; - } - return false; - } - - function postVideo(node) { - var video = document.createElement("video"); - video.src = node.specification.url; - this.popupResponse.appendChild(video); - } - - function postYoutube(node) { - var iframe = document.createElement("iframe"); - iframe.className = "youtube"; - iframe.src = node.specification.url; - this.popupResponse.appendChild(iframe); - } - - function postSlider(node) { - var hold = document.createElement('div'); - var input = document.createElement('input'); - input.type = 'range'; - input.style.width = "90%"; - if (node.specification.min !== null) { - input.min = node.specification.min; - } - if (node.specification.max !== null) { - input.max = node.specification.max; - } - if (node.response !== undefined) { - input.value = node.response; - } - hold.className = "survey-slider-text-holder"; - var minText = document.createElement('span'); - var maxText = document.createElement('span'); - minText.textContent = node.specification.leftText; - maxText.textContent = node.specification.rightText; - hold.appendChild(minText); - hold.appendChild(maxText); - this.popupResponse.appendChild(input); - this.popupResponse.appendChild(hold); - this.popupResponse.style.textAlign = "center"; - } - - function processSlider(node) { - var input = this.popupContent.getElementsByTagName('input')[0]; - node.response = input.value; - processConditional.call(this, node, node.response); - return true; - } - - function processSliderConditional(condition, value) { - switch (condition.check) { - case "contains": - console.log("Survey Element of type 'number' cannot interpret contains conditions. IGNORING"); - break; - case "greaterThan": - if (value > Number(condition.value)) { - return true; - } - break; - case "lessThan": - if (value < Number(condition.value)) { - return true; - } - break; - case "equals": - if (value == condition.value) { - return true; - } - break; - default: - console.log("Unknown condition. IGNORING"); - break; - } - return false; - } this.createPopup = function () { // Create popup window interface @@ -1054,7 +614,7 @@ }; this.showPopup = function () { - if (this.popup === null) { + if (this.popup == null) { this.createPopup(); } this.popup.style.visibility = 'visible'; @@ -1081,19 +641,157 @@ this.popupTitle.innerHTML = ""; this.popupTitle.appendChild(p.parseFromString(converter.makeHtml(node.specification.statement), "text/html").getElementsByTagName("body")[0].firstElementChild); if (node.specification.type == 'question') { - postQuestion.call(this, node); + var textArea = document.createElement('textarea'); + switch (node.specification.boxsize) { + case 'small': + textArea.cols = "20"; + textArea.rows = "1"; + break; + case 'normal': + textArea.cols = "30"; + textArea.rows = "2"; + break; + case 'large': + textArea.cols = "40"; + textArea.rows = "5"; + break; + case 'huge': + textArea.cols = "50"; + textArea.rows = "10"; + break; + } + if (node.response == undefined) { + node.response = ""; + } else { + textArea.value = node.response; + } + this.popupResponse.appendChild(textArea); + textArea.focus(); + this.popupResponse.style.textAlign = "center"; + this.popupResponse.style.left = "0%"; } else if (node.specification.type == 'checkbox') { - postCheckbox.call(this, node); + if (node.response == undefined) { + node.response = Array(node.specification.options.length); + } + var index = 0; + var table = document.createElement("table"); + table.className = "popup-option-list"; + table.border = "0"; + for (var option of node.specification.options) { + var tr = document.createElement("tr"); + table.appendChild(tr); + var td = document.createElement("td"); + tr.appendChild(td); + var input = document.createElement('input'); + input.id = option.name; + input.type = 'checkbox'; + td.appendChild(input); + + td = document.createElement("td"); + tr.appendChild(td); + var span = document.createElement('span'); + span.textContent = option.text; + td.appendChild(span); + var tr = document.createElement('div'); + tr.setAttribute('name', 'option'); + tr.className = "popup-option-checbox"; + if (node.response[index] != undefined) { + if (node.response[index].checked == true) { + input.checked = "true"; + } + } + index++; + } + this.popupResponse.appendChild(table); } else if (node.specification.type == 'radio') { - postRadio.call(this, node); + if (node.response == undefined) { + node.response = { + name: "", + text: "" + }; + } + var index = 0; + var table = document.createElement("table"); + table.className = "popup-option-list"; + table.border = "0"; + for (var option of node.specification.options) { + var tr = document.createElement("tr"); + table.appendChild(tr); + var td = document.createElement("td"); + tr.appendChild(td); + var input = document.createElement('input'); + input.id = option.name; + input.type = 'radio'; + input.name = node.specification.id; + td.appendChild(input); + + td = document.createElement("td"); + tr.appendChild(td); + var span = document.createElement('span'); + span.textContent = option.text; + td.appendChild(span); + var tr = document.createElement('div'); + tr.setAttribute('name', 'option'); + tr.className = "popup-option-checbox"; + if (node.response[index] != undefined) { + if (node.response[index].checked == true) { + input.checked = "true"; + } + } + index++; + } + this.popupResponse.appendChild(table); } else if (node.specification.type == 'number') { - postNumber.call(this, node); + var input = document.createElement('input'); + input.type = 'textarea'; + if (node.specification.min != null) { + input.min = node.specification.min; + } + if (node.specification.max != null) { + input.max = node.specification.max; + } + if (node.specification.step != null) { + input.step = node.specification.step; + } + if (node.response != undefined) { + input.value = node.response; + } + this.popupResponse.appendChild(input); + this.popupResponse.style.textAlign = "center"; + this.popupResponse.style.left = "0%"; } else if (node.specification.type == "video") { - postVideo.call(this, node); + var video = document.createElement("video"); + video.src = node.specification.url; + this.popupResponse.appendChild(video); } else if (node.specification.type == "youtube") { - postYoutube.call(this, node); + var iframe = document.createElement("iframe"); + iframe.className = "youtube"; + iframe.src = node.specification.url; + this.popupResponse.appendChild(iframe); } else if (node.specification.type == "slider") { - postSlider.call(this, node); + var hold = document.createElement('div'); + var input = document.createElement('input'); + input.type = 'range'; + input.style.width = "90%"; + if (node.specification.min != null) { + input.min = node.specification.min; + } + if (node.specification.max != null) { + input.max = node.specification.max; + } + if (node.response != undefined) { + input.value = node.response; + } + hold.className = "survey-slider-text-holder"; + var minText = document.createElement('span'); + var maxText = document.createElement('span'); + minText.textContent = node.specification.leftText; + maxText.textContent = node.specification.rightText; + hold.appendChild(minText); + hold.appendChild(maxText); + this.popupResponse.appendChild(input); + this.popupResponse.appendChild(hold); + this.popupResponse.style.textAlign = "center"; } if (this.currentIndex + 1 == this.popupOptions.length) { if (this.node.location == "pre") { @@ -1117,12 +815,12 @@ this.popupOptions = []; this.node = node; this.store = store; - node.options.forEach(function (opt) { + for (var opt of node.options) { this.popupOptions.push({ specification: opt, response: null }); - }, this); + } this.currentIndex = 0; this.showPopup(); this.postNode(); @@ -1133,30 +831,294 @@ this.proceedClicked = function () { // Each time the popup button is clicked! - if (testState.stateIndex === 0 && specification.calibration) { + if (testState.stateIndex == 0 && specification.calibration) { interfaceContext.calibrationModuleObject.collect(); advanceState(); return; } - var node = this.popupOptions[this.currentIndex], - pass = true; + var node = this.popupOptions[this.currentIndex]; if (node.specification.type == 'question') { // Must extract the question data - pass = processQuestion.call(this, node); + var textArea = $(popup.popupContent).find('textarea')[0]; + if (node.specification.mandatory == true && textArea.value.length == 0) { + interfaceContext.lightbox.post("Error", "This question is mandatory"); + return; + } else { + // Save the text content + console.log("Question: " + node.specification.statement); + console.log("Question Response: " + textArea.value); + node.response = textArea.value; + } + // Perform the conditional + for (var condition of node.specification.conditions) { + var pass = false; + switch (condition.check) { + case "equals": + if (textArea.value == condition.value) { + pass = true; + } + break; + case "greaterThan": + case "lessThan": + console.log("Survey Element of type 'question' cannot interpret greaterThan/lessThan conditions. IGNORING"); + break; + case "contains": + if (textArea.value.includes(condition.value)) { + pass = true; + } + break; + } + var jumpID; + if (pass) { + jumpID = condition.jumpToOnPass; + } else { + jumpID = condition.jumpToOnFail; + } + if (jumpID != undefined) { + var index = this.popupOptions.findIndex(function (item, index, element) { + if (item.specification.id == jumpID) { + return true; + } else { + return false; + } + }, this); + this.currentIndex = index - 1; + break; + } + } } else if (node.specification.type == 'checkbox') { // Must extract checkbox data - pass = processCheckbox.call(this, node); + console.log("Checkbox: " + node.specification.statement); + var inputs = this.popupResponse.getElementsByTagName('input'); + node.response = []; + var numChecked = 0; + for (var i = 0; i < node.specification.options.length; i++) { + if (inputs[i].checked) { + numChecked++; + } + } + if (node.specification.min != undefined) { + if (node.specification.max == undefined) { + if (numChecked < node.specification.min) { + var msg = "You must select at least " + node.specification.min + " option"; + if (node.specification.min > 1) { + msg += "s"; + } + interfaceContext.lightbox.post("Error", msg); + return; + } + } else { + if (numChecked < node.specification.min || numChecked > node.specification.max) { + if (node.specification.min == node.specification.max) { + interfaceContext.lightbox.post("Error", "You must only select " + node.specification.min); + } else { + interfaceContext.lightbox.post("Error", "You must select between " + node.specification.min + " and " + node.specification.max); + } + return; + } + } + } + for (var i = 0; i < node.specification.options.length; i++) { + node.response.push({ + name: node.specification.options[i].name, + text: node.specification.options[i].text, + checked: inputs[i].checked + }); + console.log(node.specification.options[i].name + ": " + inputs[i].checked); + } + // Perform the conditional + for (var condition of node.specification.conditions) { + var pass = false; + switch (condition.check) { + case "equals": + case "greaterThan": + case "lessThan": + console.log("Survey Element of type 'checkbox' cannot interpret equals/greaterThan/lessThan conditions. IGNORING"); + break; + case "contains": + for (var response of node.response) { + if (response.name == condition.value && response.checked) { + pass = true; + break; + } + } + break; + } + var jumpID; + if (pass) { + jumpID = condition.jumpToOnPass; + } else { + jumpID = condition.jumpToOnFail; + } + if (jumpID != undefined) { + var index = this.popupOptions.findIndex(function (item, index, element) { + if (item.specification.id == jumpID) { + return true; + } else { + return false; + } + }, this); + this.currentIndex = index - 1; + break; + } + } } else if (node.specification.type == "radio") { + var optHold = this.popupResponse; + console.log("Radio: " + node.specification.statement); + node.response = null; + var i = 0; + var inputs = optHold.getElementsByTagName('input'); + while (node.response == null) { + if (i == inputs.length) { + if (node.specification.mandatory == true) { + interfaceContext.lightbox.post("Error", "Please select one option"); + return; + } + break; + } + if (inputs[i].checked == true) { + node.response = node.specification.options[i]; + console.log("Selected: " + node.specification.options[i].name); + } + i++; + } // Perform the conditional - pass = processRadio.call(this, node); + for (var condition of node.specification.conditions) { + var pass = false; + switch (condition.check) { + case "contains": + case "greaterThan": + case "lessThan": + console.log("Survey Element of type 'radio' cannot interpret contains/greaterThan/lessThan conditions. IGNORING"); + break; + case "equals": + if (node.response.name == condition.value) { + pass = true; + } + break; + } + var jumpID; + if (pass) { + jumpID = condition.jumpToOnPass; + } else { + jumpID = condition.jumpToOnFail; + } + if (jumpID != undefined) { + var index = this.popupOptions.findIndex(function (item, index, element) { + if (item.specification.id == jumpID) { + return true; + } else { + return false; + } + }, this); + this.currentIndex = index - 1; + break; + } + } } else if (node.specification.type == "number") { + var input = this.popupContent.getElementsByTagName('input')[0]; + if (node.mandatory == true && input.value.length == 0) { + interfaceContext.lightbox.post("Error", 'This question is mandatory. Please enter a number'); + return; + } + var enteredNumber = Number(input.value); + if (isNaN(enteredNumber)) { + interfaceContext.lightbox.post("Error", 'Please enter a valid number'); + return; + } + if (enteredNumber < node.min && node.min != null) { + interfaceContext.lightbox.post("Error", 'Number is below the minimum value of ' + node.min); + return; + } + if (enteredNumber > node.max && node.max != null) { + interfaceContext.lightbox.post("Error", 'Number is above the maximum value of ' + node.max); + return; + } + node.response = input.value; // Perform the conditional - pass = processNumber.call(this, node); + for (var condition of node.specification.conditions) { + var pass = false; + switch (condition.check) { + case "contains": + console.log("Survey Element of type 'number' cannot interpret contains conditions. IGNORING"); + break; + case "greaterThan": + if (node.response > Number(condition.value)) { + pass = true; + } + break; + case "lessThan": + if (node.response < Number(condition.value)) { + pass = true; + } + break; + case "equals": + if (node.response == condition.value) { + pass = true; + } + break; + } + var jumpID; + if (pass) { + jumpID = condition.jumpToOnPass; + } else { + jumpID = condition.jumpToOnFail; + } + if (jumpID != undefined) { + var index = this.popupOptions.findIndex(function (item, index, element) { + if (item.specification.id == jumpID) { + return true; + } else { + return false; + } + }, this); + this.currentIndex = index - 1; + break; + } + } } else if (node.specification.type == 'slider') { - pass = processSlider.call(this, node); - } - if (pass === false) { - return; + var input = this.popupContent.getElementsByTagName('input')[0]; + node.response = input.value; + for (var condition of node.specification.conditions) { + var pass = false; + switch (condition.check) { + case "contains": + console.log("Survey Element of type 'number' cannot interpret contains conditions. IGNORING"); + break; + case "greaterThan": + if (node.response > Number(condition.value)) { + pass = true; + } + break; + case "lessThan": + if (node.response < Number(condition.value)) { + pass = true; + } + break; + case "equals": + if (node.response == condition.value) { + pass = true; + } + break; + } + var jumpID; + if (pass) { + jumpID = condition.jumpToOnPass; + } else { + jumpID = condition.jumpToOnFail; + } + if (jumpID != undefined) { + var index = this.popupOptions.findIndex(function (item, index, element) { + if (item.specification.id == jumpID) { + return true; + } else { + return false; + } + }, this); + this.currentIndex = index - 1; + break; + } + } } this.currentIndex++; if (this.currentIndex < this.popupOptions.length) { @@ -1166,9 +1128,9 @@ this.popupTitle.innerHTML = ""; this.popupResponse.innerHTML = ""; this.hidePopup(); - this.popupOptions.forEach(function (node) { + for (var node of this.popupOptions) { this.store.postResult(node); - }, this); + } this.store.complete(); advanceState(); } @@ -1184,7 +1146,7 @@ this.resize = function (event) { // Called on window resize; - if (this.popup !== null) { + if (this.popup != null) { this.popup.style.left = (window.innerWidth / 2) - 250 + 'px'; this.popup.style.top = (window.innerHeight / 2) - 125 + 'px'; var blank = document.getElementsByClassName('testHalt')[0]; @@ -1194,16 +1156,16 @@ }; this.hideNextButton = function () { this.buttonProceed.style.visibility = "hidden"; - }; + } this.hidePreviousButton = function () { this.buttonPrevious.style.visibility = "hidden"; - }; + } this.showNextButton = function () { this.buttonProceed.style.visibility = "visible"; - }; + } this.showPreviousButton = function () { this.buttonPrevious.style.visibility = "visible"; - }; + } } function advanceState() { @@ -1213,6 +1175,21 @@ function stateMachine() { // Object prototype for tracking and managing the test state + + function pickSubPool(pool, numElements) { + // Assumes each element of pool has function "alwaysInclude" + + // First extract those excluded from picking process + var picked = []; + pool.forEach(function (e) { + if (e.alwaysInclude) { + picked.push(e); + } + }); + + return picked.concat(randomSubArray(pool, numElements - excluded.length)); + } + this.stateMap = []; this.preTestSurvey = null; this.postTestSurvey = null; @@ -1224,76 +1201,65 @@ // Get the data from Specification var pagePool = []; - var pageInclude = []; - var i; - for (i = 0; i < specification.pages.length; i++) { - var page = specification.pages[i]; - if (page.alwaysInclude) { - pageInclude.push(page); - } else { - pagePool.push(page); + for (var page of specification.pages) { + if (page.position !== null || page.alwaysInclude) { + page.alwaysInclude = true; + } + pagePool.push(page) + } + if (specification.numPages > 0) { + specification.randomiseOrder = true; + pagePool = pickSubPool(pagePool, specification.numPages); + } + + // Now get the order of pages + var fixed = [] + for (var page of pagePool) { + if (page.position !== null) { + fixed.push(page); + var i = pagePool.indexOf(page); + pagePool.splice(i, 1); } } - // Find how many are left to get - var numPages = specification.poolSize; - if (numPages > pagePool.length) { - console.log("WARNING - You have specified more pages in <setup poolSize> than you have created!!"); - numPages = specification.pages.length; - } - if (specification.poolSize === 0) { - numPages = specification.pages.length; - } - numPages -= pageInclude.length; - - if (numPages > 0) { - // Go find the rest of the pages from the pool - var subarr = null; - if (specification.randomiseOrder) { - // Append a random sub-array - subarr = randomSubArray(pagePool, numPages); - } else { - // Append the matching number - subarr = pagePool.slice(0, numPages); - } - pageInclude = pageInclude.concat(subarr); + if (specification.randomiseOrder) { + pagePool = randomiseOrder(pagePool); } - // We now have our selected pages in pageInclude array - if (specification.randomiseOrder) { - pageInclude = randomiseOrder(pageInclude); - } - for (i = 0; i < pageInclude.length; i++) { - pageInclude[i].presentedId = i; - this.stateMap.push(pageInclude[i]); - // For each selected page, we must get the sub pool - if (pageInclude[i].poolSize !== 0 && pageInclude[i].poolSize !== pageInclude[i].audioElements.length) { - var elemInclude = []; - var elemPool = []; - for (var j = 0; j < pageInclude[i].audioElements.length; j++) { - var elem = pageInclude[i].audioElements[j]; - if (elem.alwaysInclude || elem.type != "normal") { - elemInclude.push(elem); - } else { - elemPool.push(elem); - } - } - var numElems = pageInclude[i].poolSize - elemInclude.length; - pageInclude[i].audioElements = elemInclude.concat(randomSubArray(elemPool, numElems)); - } - storage.createTestPageStore(pageInclude[i]); - audioEngineContext.loadPageData(pageInclude[i]); + // Place in the correct order + for (var page of fixed) { + pagePool.splice(page.position, 0, page) } - if (specification.preTest !== null) { + // Now process the pages + pagePool.forEach(function (page, i) { + page.presentedId = i; + this.stateMap.push(page); + var elements = page.audioElements + if (page.poolSize > 0 || page.randomiseOrder) { + page.randomiseOrder = true; + if (page.poolSize == 0) { + page.poolSize = page.randomiseOrder; + } + elements = pickSubPool(elements, page.poolSize); + } + if (page.randomiseOrder) { + elements = randomiseOrder(elements); + } + page.audioElements = elements; + storage.createTestPageStore(page); + audioEngineContext.loadPageData(page); + }, this) + + if (specification.preTest != null) { this.preTestSurvey = specification.preTest; } - if (specification.postTest !== null) { + if (specification.postTest != null) { this.postTestSurvey = specification.postTest; } if (this.stateMap.length > 0) { - if (this.stateIndex !== null) { + if (this.stateIndex != null) { console.log('NOTE - State already initialise'); } this.stateIndex = -2; @@ -1303,7 +1269,7 @@ } }; this.advanceState = function () { - if (this.stateIndex === null) { + if (this.stateIndex == null) { this.initialise(); } if (this.stateIndex > -2) { @@ -1311,7 +1277,7 @@ } if (this.stateIndex == -2) { this.stateIndex++; - if (this.preTestSurvey !== undefined) { + if (this.preTestSurvey != null) { popup.initState(this.preTestSurvey, storage.globalPreTest); } else { this.advanceState(); @@ -1331,7 +1297,7 @@ // All test pages complete, post test console.log('Ending test ...'); this.stateIndex++; - if (this.postTestSurvey === undefined) { + if (this.postTestSurvey == null) { this.advanceState(); } else { popup.initState(this.postTestSurvey, storage.globalPostTest); @@ -1340,19 +1306,18 @@ createProjectSave(specification.projectReturn); } else { popup.hidePopup(); - if (this.currentStateMap === null) { + if (this.currentStateMap == null) { this.currentStateMap = this.stateMap[this.stateIndex]; // Find and extract the outside reference var elements = [], ref = []; - var elem = this.currentStateMap.audioElements.pop(); - while (elem) { + var elem; + while (elem = this.currentStateMap.audioElements.pop()) { if (elem.type == "outside-reference") { ref.push(elem); } else { elements.push(elem); } - elem = this.currentStateMap.audioElements.pop(); } elements = elements.reverse(); if (this.currentStateMap.randomiseOrder) { @@ -1361,7 +1326,7 @@ this.currentStateMap.audioElements = elements.concat(ref); this.currentStore = storage.testPages[this.stateIndex]; - if (this.currentStateMap.preTest !== undefined) { + if (this.currentStateMap.preTest != null) { this.currentStatePosition = 'pre'; popup.initState(this.currentStateMap.preTest, storage.testPages[this.stateIndex].preTest); } else { @@ -1378,7 +1343,7 @@ this.currentStatePosition = 'post'; // Save the data this.testPageCompleted(); - if (this.currentStateMap.postTest === undefined) { + if (this.currentStateMap.postTest == null) { this.advanceState(); return; } else { @@ -1390,7 +1355,7 @@ this.currentStateMap = null; this.advanceState(); break; - } + }; } }; @@ -1408,12 +1373,12 @@ } var audioObjects = audioEngineContext.audioObjects; - audioEngineContext.audioObjects.forEach(function (ao) { + for (var ao of audioEngineContext.audioObjects) { ao.exportXMLDOM(); - }); - interfaceContext.commentQuestions.forEach(function (element) { + } + for (var element of interfaceContext.commentQuestions) { element.exportXMLDOM(storePoint); - }); + } pageXMLSave(storePoint.XMLDOM, this.currentStateMap); storePoint.complete(); }; @@ -1424,14 +1389,14 @@ } else { return null; } - }; + } this.getCurrentTestPageStore = function () { if (this.stateIndex >= 0 && this.stateIndex < this.stateMap.length) { return this.currentStore; } else { return null; } - }; + } } function AudioEngine(specification) { @@ -1486,7 +1451,7 @@ } for (var i = 0; i < this.users.length; i++) { this.users[i].state = 1; - if (this.users[i].interfaceDOM !== null) { + if (this.users[i].interfaceDOM != null) { this.users[i].bufferLoaded(this); } } @@ -1514,7 +1479,7 @@ } } return false; - }; + } this.getMedia = function () { var self = this; var currentUrlIndex = 0; @@ -1558,7 +1523,7 @@ return true; }, function (e) { var waveObj = new WAVE(); - if (waveObj.open(response) === 0) { + if (waveObj.open(response) == 0) { self.buffer = audioContext.createBuffer(waveObj.num_channels, waveObj.num_samples, waveObj.sample_rate); for (var c = 0; c < waveObj.num_channels; c++) { var buffer_ptr = self.buffer.getChannelData(c); @@ -1566,13 +1531,14 @@ buffer_ptr[n] = waveObj.decoded_data[c][n]; } } + + delete waveObj; } - if (self.buffer !== undefined) { + if (self.buffer != undefined) { self.status = 2; calculateLoudness(self, "I"); return true; } - waveObj = undefined; return false; }); } @@ -1582,7 +1548,7 @@ this.status = -1; for (var i = 0; i < this.users.length; i++) { this.users[i].state = -1; - if (this.users[i].interfaceDOM !== null) { + if (this.users[i].interfaceDOM != null) { this.users[i].bufferLoaded(this); } } @@ -1593,14 +1559,14 @@ if (event.lengthComputable) { this.progress = event.loaded / event.total; for (var i = 0; i < this.users.length; i++) { - if (this.users[i].interfaceDOM !== null) { + if (this.users[i].interfaceDOM != null) { if (typeof this.users[i].interfaceDOM.updateLoading === "function") { this.users[i].interfaceDOM.updateLoading(this.progress * 100); } } } } - } + }; this.progress = 0; this.status = 1; @@ -1611,11 +1577,11 @@ this.registerAudioObject = function (audioObject) { // Called by an audioObject to register to the buffer for use // First check if already in the register pool - this.users.forEach(function (object) { - if (audioObject.id == object.id) { + for (var objects of this.users) { + if (audioObject.id == objects.id) { return 0; } - }); + } this.users.push(audioObject); if (this.status == 3 || this.status == -1) { // The buffer is already ready, trigger bufferLoaded @@ -1625,24 +1591,23 @@ this.copyBuffer = function (preSilenceTime, postSilenceTime) { // Copies the entire bufferObj. - if (preSilenceTime === undefined) { + if (preSilenceTime == undefined) { preSilenceTime = 0; } - if (postSilenceTime === undefined) { + if (postSilenceTime == undefined) { postSilenceTime = 0; } var preSilenceSamples = secondsToSamples(preSilenceTime, this.buffer.sampleRate); var postSilenceSamples = secondsToSamples(postSilenceTime, this.buffer.sampleRate); var newLength = this.buffer.length + preSilenceSamples + postSilenceSamples; var copybuffer = audioContext.createBuffer(this.buffer.numberOfChannels, newLength, this.buffer.sampleRate); - var c; // Now we can use some efficient background copy schemes if we are just padding the end - if (preSilenceSamples === 0 && typeof copybuffer.copyToChannel === "function") { - for (c = 0; c < this.buffer.numberOfChannels; c++) { + if (preSilenceSamples == 0 && typeof copybuffer.copyToChannel == "function") { + for (var c = 0; c < this.buffer.numberOfChannels; c++) { copybuffer.copyToChannel(this.buffer.getChannelData(c), c); } } else { - for (c = 0; c < this.buffer.numberOfChannels; c++) { + for (var c = 0; c < this.buffer.numberOfChannels; c++) { var src = this.buffer.getChannelData(c); var dst = copybuffer.getChannelData(c); for (var n = 0; n < src.length; n++) @@ -1653,7 +1618,7 @@ copybuffer.lufs = this.buffer.lufs; copybuffer.playbackGain = this.buffer.playbackGain; return copybuffer; - }; + } this.cropBuffer = function (startTime, stopTime) { // Copy and return the cropped buffer @@ -1674,17 +1639,21 @@ } } return copybuffer; - }; + } }; this.loadPageData = function (page) { // Load the URL from pages - function loadAudioElementData(element) { + for (var element of page.audioElements) { var URL = page.hostURL + element.url; - var buffer = this.buffers.find(function (buffObj) { - return buffObj.hasUrl(URL); - }); - if (buffer === undefined) { + var buffer = null; + for (var buffObj of this.buffers) { + if (buffObj.hasUrl(URL)) { + buffer = buffObj; + break; + } + } + if (buffer == null) { buffer = new this.bufferObj(); var urls = [{ url: URL, @@ -1701,51 +1670,44 @@ this.buffers.push(buffer); } } - page.audioElements.forEach(loadAudioElementData, this); }; - function playNormal(id) { - var playTime = audioContext.currentTime + 0.1; - var stopTime = playTime + specification.crossFade; - this.audioObjects.forEach(function (ao) { - if (ao.id === id) { - ao.play(playTime); - } else { - ao.stop(stopTime); - } - }); - } - - function playLoopSync(id) { - var playTime = audioContext.currentTime + 0.1; - var stopTime = playTime + specification.crossFade; - this.audioObjects.forEach(function (ao) { - ao.play(playTime); - if (ao.id === id) { - ao.loopStart(playTime); - } else { - ao.loopStop(stopTime); - } - }); - } - this.play = function (id) { // Start the timer and set the audioEngine state to playing (1) - if (typeof id !== "number" || id < 0 || id > this.audioObjects.length) { - throw ('FATAL - Passed id was undefined - AudioEngineContext.play(id)'); + if (this.status == 0) { + // Check if all audioObjects are ready + this.bufferReady(id); + } else { + this.status = 1; } - if (this.status === 1) { + if (this.status == 1) { this.timer.startTest(); - interfaceContext.playhead.setTimePerPixel(this.audioObjects[id]); + if (id == undefined) { + id = -1; + console.error('FATAL - Passed id was undefined - AudioEngineContext.play(id)'); + return; + } else { + interfaceContext.playhead.setTimePerPixel(this.audioObjects[id]); + } + var setTime = audioContext.currentTime; if (this.synchPlayback && this.loopPlayback) { // Traditional looped playback - playLoopSync.call(this, id); + for (var i = 0; i < this.audioObjects.length; i++) { + this.audioObjects[i].play(audioContext.currentTime); + if (id == i) { + this.audioObjects[i].loopStart(setTime); + } else { + this.audioObjects[i].loopStop(setTime + specification.crossFade); + } + } } else { - if (this.bufferReady(id) === false) { - console.log("Cannot play. Buffer not ready"); - return; + for (var i = 0; i < this.audioObjects.length; i++) { + if (i != id) { + this.audioObjects[i].stop(setTime + specification.crossFade); + } else if (i == id) { + this.audioObjects[id].play(setTime); + } } - playNormal.call(this, id); } interfaceContext.playhead.start(); } @@ -1755,9 +1717,9 @@ // Send stop and reset command to all playback buffers if (this.status == 1) { var setTime = audioContext.currentTime + 0.1; - this.audioObjects.forEach(function (a) { - a.stop(setTime); - }); + for (var i = 0; i < this.audioObjects.length; i++) { + this.audioObjects[i].stop(setTime); + } interfaceContext.playhead.stop(); } }; @@ -1767,15 +1729,19 @@ // URLs must either be from the same source OR be setup to 'Access-Control-Allow-Origin' // Create the audioObject with ID of the new track length; - var audioObjectId = this.audioObjects.length; + audioObjectId = this.audioObjects.length; this.audioObjects[audioObjectId] = new audioObject(audioObjectId); // Check if audioObject buffer is currently stored by full URL var URL = testState.currentStateMap.hostURL + element.url; - var buffer = this.buffers.find(function (buffObj) { - return buffObj.hasUrl(URL); - }); - if (buffer === undefined) { + var buffer = null; + for (var i = 0; i < this.buffers.length; i++) { + if (this.buffers[i].hasUrl(URL)) { + buffer = this.buffers[i]; + break; + } + } + if (buffer == null) { console.log("[WARN]: Buffer was not loaded in pre-test! " + URL); buffer = new this.bufferObj(); this.buffers.push(buffer); @@ -1801,9 +1767,9 @@ this.status = 0; this.audioObjectsReady = false; this.metric.reset(); - this.buffers.forEach(function (buffer) { - buffer.users = []; - }); + for (var i = 0; i < this.buffers.length; i++) { + this.buffers[i].users = []; + } this.audioObjects = []; this.timer = new timer(); this.loopPlayback = audioHolderObject.loop; @@ -1811,9 +1777,9 @@ }; this.checkAllPlayed = function () { - var arr = []; + arr = []; for (var id = 0; id < this.audioObjects.length; id++) { - if (this.audioObjects[id].metric.wasListenedTo === false) { + if (this.audioObjects[id].metric.wasListenedTo == false) { arr.push(this.audioObjects[id].id); } } @@ -1823,11 +1789,11 @@ this.checkAllReady = function () { var ready = true; for (var i = 0; i < this.audioObjects.length; i++) { - if (this.audioObjects[i].state === 0) { + if (this.audioObjects[i].state == 0) { // Track not ready console.log('WAIT -- audioObject ' + i + ' not ready yet!'); ready = false; - } + }; } return ready; }; @@ -1844,11 +1810,11 @@ } } // Extract the audio and zero-pad - this.audioObjects.forEach(function (ao) { + for (var ao of this.audioObjects) { if (ao.buffer.buffer.duration !== duration) { ao.buffer.buffer = ao.buffer.copyBuffer(0, duration - ao.buffer.buffer.duration); } - }); + } }; this.bufferReady = function (id) { @@ -1860,6 +1826,10 @@ return true; } return false; + } + + this.exportXML = function () { + }; } @@ -1867,7 +1837,7 @@ function audioObject(id) { // The main buffer object with common control nodes to the AudioEngine - this.specification = undefined; + this.specification; this.id = id; this.state = 0; // 0 - no data, 1 - ready this.url = null; // Hold the URL given for the output back to the results. @@ -1890,7 +1860,7 @@ // the audiobuffer is not designed for multi-start playback // When stopeed, the buffer node is deleted and recreated with the stored buffer. - this.buffer = undefined; + this.buffer; this.bufferLoaded = function (callee) { // Called by the associated buffer when it has finished loading, will then 'bind' the buffer to the @@ -1898,7 +1868,7 @@ if (callee.status == -1) { // ERROR this.state = -1; - if (this.interfaceDOM !== null) { + if (this.interfaceDOM != null) { this.interfaceDOM.error(); } this.buffer = callee; @@ -1912,7 +1882,7 @@ var copybuffer = new callee.constructor(); copybuffer.buffer = callee.cropBuffer(startTime || 0, stopTime || callee.buffer.duration); - if (preSilenceTime !== 0 || postSilenceTime !== 0) { + if (preSilenceTime != 0 || postSilenceTime != 0) { copybuffer.buffer = copybuffer.copyBuffer(preSilenceTime, postSilenceTime); } @@ -1925,7 +1895,7 @@ } else { this.buffer.buffer.playbackGain = 1.0; } - if (this.interfaceDOM !== null) { + if (this.interfaceDOM != null) { this.interfaceDOM.enable(); } this.onplayGain = decibelToLinear(this.specification.gain) * (this.buffer.buffer.playbackGain || 1.0); @@ -1954,7 +1924,7 @@ }; this.loopStop = function (setTime) { - if (this.outputGain.gain.value !== 0.0) { + if (this.outputGain.gain.value != 0.0) { this.outputGain.gain.linearRampToValueAtTime(0.0, setTime); this.metric.stopListening(audioEngineContext.timer.getTestTime()); } @@ -1962,7 +1932,7 @@ }; this.play = function (startTime) { - if (this.bufferNode === undefined && this.buffer.buffer !== undefined) { + if (this.bufferNode == undefined && this.buffer.buffer != undefined) { this.bufferNode = audioContext.createBufferSource(); this.bufferNode.owner = this; this.bufferNode.connect(this.outputGain); @@ -1971,7 +1941,7 @@ this.bufferNode.onended = function (event) { // Safari does not like using 'this' to reference the calling object! //event.currentTarget.owner.metric.stopListening(audioEngineContext.timer.getTestTime(),event.currentTarget.owner.getCurrentPosition()); - if (event.currentTarget !== null) { + if (event.currentTarget != null) { event.currentTarget.owner.stop(audioContext.currentTime + 1); } }; @@ -1996,7 +1966,7 @@ this.stop = function (stopTime) { this.outputGain.gain.cancelScheduledValues(audioContext.currentTime); - if (this.bufferNode !== undefined) { + if (this.bufferNode != undefined) { this.metric.stopListening(audioEngineContext.timer.getTestTime(), this.getCurrentPosition()); this.bufferNode.stop(stopTime); this.bufferNode = undefined; @@ -2007,7 +1977,7 @@ this.getCurrentPosition = function () { var time = audioEngineContext.timer.getTestTime(); - if (this.bufferNode !== undefined) { + if (this.bufferNode != undefined) { var position = (time - this.bufferNode.playbackStartTime) % this.buffer.buffer.duration; if (isNaN(position)) { return 0; @@ -2027,8 +1997,8 @@ this.storeDOM.appendChild(file); if (this.specification.type != 'outside-reference') { var interfaceXML = this.interfaceDOM.exportXMLDOM(this); - if (interfaceXML !== null) { - if (interfaceXML.length === undefined) { + if (interfaceXML != null) { + if (interfaceXML.length == undefined) { this.storeDOM.appendChild(interfaceXML); } else { for (var i = 0; i < interfaceXML.length; i++) { @@ -2036,11 +2006,15 @@ } } } - if (this.commentDOM !== null) { + if (this.commentDOM != null) { this.storeDOM.appendChild(this.commentDOM.exportXMLDOM(this)); } } - this.metric.exportXMLDOM(this.storeDOM.getElementsByTagName('metric')[0]); + var nodes = this.metric.exportXMLDOM(); + var mroot = this.storeDOM.getElementsByTagName('metric')[0]; + for (var i = 0; i < nodes.length; i++) { + mroot.appendChild(nodes[i]); + } }; } @@ -2053,7 +2027,7 @@ this.testDuration = 0; this.minimumTestTime = 0; // No minimum test time this.startTest = function () { - if (this.testStarted === false) { + if (this.testStarted == false) { this.testStartTime = audioContext.currentTime; this.testStarted = true; this.updateTestTime(); @@ -2161,7 +2135,7 @@ }; this.startListening = function (time) { - if (this.listenHold === false) { + if (this.listenHold == false) { this.wasListenedTo = true; this.listenStart = time; this.listenHold = true; @@ -2180,7 +2154,7 @@ }; this.stopListening = function (time, bufferStopTime) { - if (this.listenHold === true) { + if (this.listenHold == true) { var diff = time - this.listenStart; this.listenedTimer += (diff); this.listenStart = 0; @@ -2190,7 +2164,7 @@ var testTime = evnt.getElementsByTagName('testTime')[0]; var bufferTime = evnt.getElementsByTagName('bufferTime')[0]; testTime.setAttribute('stop', time); - if (bufferStopTime === undefined) { + if (bufferStopTime == undefined) { bufferTime.setAttribute('stop', this.parent.getCurrentPosition()); } else { bufferTime.setAttribute('stop', bufferStopTime); @@ -2199,99 +2173,64 @@ } }; - function exportElementTimer(parentElement) { - var mElementTimer = storage.document.createElement('metricresult'); - mElementTimer.setAttribute('name', 'enableElementTimer'); - mElementTimer.textContent = this.listenedTimer; - parentElement.appendChild(mElementTimer); - return mElementTimer; - } - - function exportElementTrack(parentElement) { - var elementTrackerFull = storage.document.createElement('metricresult'); - elementTrackerFull.setAttribute('name', 'elementTrackerFull'); - for (var k = 0; k < this.movementTracker.length; k++) { - var timePos = storage.document.createElement('movement'); - timePos.setAttribute("time", this.movementTracker[k][0]); - timePos.setAttribute("value", this.movementTracker[k][1]); - elementTrackerFull.appendChild(timePos); - } - parentElement.appendChild(elementTrackerFull); - return elementTrackerFull; - } - - function exportElementListenTracker(parentElement) { - var elementListenTracker = storage.document.createElement('metricresult'); - elementListenTracker.setAttribute('name', 'elementListenTracker'); - for (var k = 0; k < this.listenTracker.length; k++) { - elementListenTracker.appendChild(this.listenTracker[k]); - } - parentElement.appendChild(elementListenTracker); - return elementListenTracker; - } - - function exportElementInitialPosition(parentElement) { - var elementInitial = storage.document.createElement('metricresult'); - elementInitial.setAttribute('name', 'elementInitialPosition'); - elementInitial.textContent = this.initialPosition; - parentElement.appendChild(elementInitial); - return elementInitial; - } - - function exportFlagListenedTo(parentElement) { - var flagListenedTo = storage.document.createElement('metricresult'); - flagListenedTo.setAttribute('name', 'elementFlagListenedTo'); - flagListenedTo.textContent = this.wasListenedTo; - parentElement.appendChild(flagListenedTo); - return flagListenedTo; - } - - function exportFlagMoved(parentElement) { - var flagMoved = storage.document.createElement('metricresult'); - flagMoved.setAttribute('name', 'elementFlagMoved'); - flagMoved.textContent = this.wasMoved; - parentElement.appendChild(flagMoved); - return flagMoved; - } - - function exportFlagComments(parentElement) { - var flagComments = storage.document.createElement('metricresult'); - flagComments.setAttribute('name', 'elementFlagComments'); - if (this.parent.commentDOM === null) { - flagComments.textContent = 'false'; - } else if (this.parent.commentDOM.textContent.length === 0) { - flagComments.textContent = 'false'; - } else { - flagComments.textContet = 'true'; - } - parentElement.appendChild(flagComments); - return flagComments; - } - - this.exportXMLDOM = function (parentElement) { - var elems = []; + this.exportXMLDOM = function () { + var storeDOM = []; if (audioEngineContext.metric.enableElementTimer) { - elems.push(exportElementTimer.call(this, parentElement)); + var mElementTimer = storage.document.createElement('metricresult'); + mElementTimer.setAttribute('name', 'enableElementTimer'); + mElementTimer.textContent = this.listenedTimer; + storeDOM.push(mElementTimer); } if (audioEngineContext.metric.enableElementTracker) { - elems.push(exportElementTrack.call(this, parentElement)); + var elementTrackerFull = storage.document.createElement('metricresult'); + elementTrackerFull.setAttribute('name', 'elementTrackerFull'); + for (var k = 0; k < this.movementTracker.length; k++) { + var timePos = storage.document.createElement('movement'); + timePos.setAttribute("time", this.movementTracker[k][0]); + timePos.setAttribute("value", this.movementTracker[k][1]); + elementTrackerFull.appendChild(timePos); + } + storeDOM.push(elementTrackerFull); } if (audioEngineContext.metric.enableElementListenTracker) { - elems.push(exportElementListenTracker.call(this, parentElement)); + var elementListenTracker = storage.document.createElement('metricresult'); + elementListenTracker.setAttribute('name', 'elementListenTracker'); + for (var k = 0; k < this.listenTracker.length; k++) { + elementListenTracker.appendChild(this.listenTracker[k]); + } + storeDOM.push(elementListenTracker); } if (audioEngineContext.metric.enableElementInitialPosition) { - elems.push(exportElementInitialPosition.call(this, parentElement)); + var elementInitial = storage.document.createElement('metricresult'); + elementInitial.setAttribute('name', 'elementInitialPosition'); + elementInitial.textContent = this.initialPosition; + storeDOM.push(elementInitial); } if (audioEngineContext.metric.enableFlagListenedTo) { - elems.push(exportFlagListenedTo.call(this, parentElement)); + var flagListenedTo = storage.document.createElement('metricresult'); + flagListenedTo.setAttribute('name', 'elementFlagListenedTo'); + flagListenedTo.textContent = this.wasListenedTo; + storeDOM.push(flagListenedTo); } if (audioEngineContext.metric.enableFlagMoved) { - elems.push(exportFlagMoved.call(this, parentElement)); + var flagMoved = storage.document.createElement('metricresult'); + flagMoved.setAttribute('name', 'elementFlagMoved'); + flagMoved.textContent = this.wasMoved; + storeDOM.push(flagMoved); } if (audioEngineContext.metric.enableFlagComments) { - elems.push(exportFlagComments.call(this, parentElement)); + var flagComments = storage.document.createElement('metricresult'); + flagComments.setAttribute('name', 'elementFlagComments'); + if (this.parent.commentDOM == null) { + flag.textContent = 'false'; + } else if (this.parent.commentDOM.textContent.length == 0) { + flag.textContent = 'false'; + } else { + flag.textContet = 'true'; + } + storeDOM.push(flagComments); } - return elems; + return storeDOM; }; } @@ -2317,12 +2256,12 @@ popup.resize(event); this.volume.resize(); this.lightbox.resize(); - this.commentBoxes.forEach(function (elem) { - elem.resize(); - }); - this.commentQuestions.forEach(function (elem) { - elem.resize(); - }); + for (var i = 0; i < this.commentBoxes.length; i++) { + this.commentBoxes[i].resize(); + } + for (var i = 0; i < this.commentQuestions.length; i++) { + this.commentQuestions[i].resize(); + } try { resizeWindow(event); } catch (err) { @@ -2371,7 +2310,7 @@ hold.appendChild(time); return hold; - }; + } this.lightbox = { parent: this, @@ -2413,7 +2352,7 @@ resize: function (event) { this.root.style.left = (window.innerWidth / 2) - 250 + 'px'; } - }; + } this.lightbox.root.appendChild(this.lightbox.content); this.lightbox.root.appendChild(this.lightbox.accept); @@ -2496,21 +2435,21 @@ if (sort) { this.sortCommentBoxes(); } - this.boxes.forEach(function (box) { + for (var box of this.boxes) { inject.appendChild(box.trackComment); - }); + } }; this.deleteCommentBoxes = function () { - if (this.injectPoint !== null) { - this.boxes.forEach(function (box) { + if (this.injectPoint != null) { + for (var box of this.boxes) { this.injectPoint.removeChild(box.trackComment); - }, this); + } this.injectPoint = null; } this.boxes = []; }; - }; + } this.commentQuestions = []; @@ -2585,7 +2524,7 @@ this.span.className = "comment-radio-span-holder"; var optCount = commentQuestion.options.length; - commentQuestion.options.forEach(function (optNode) { + for (var optNode of commentQuestion.options) { var div = document.createElement('div'); div.style.width = '80px'; div.style.float = 'left'; @@ -2608,7 +2547,7 @@ div.appendChild(span); this.span.appendChild(div); this.options.push(input); - }, this); + } this.holder.appendChild(this.span); this.holder.appendChild(this.inputs); @@ -2620,7 +2559,7 @@ question.textContent = this.string.textContent; var response = document.createElement('response'); var i = 0; - while (this.options[i].checked === false) { + while (this.options[i].checked == false) { i++; if (i >= this.options.length) { break; @@ -2651,13 +2590,14 @@ var options = this.holder.getElementsByClassName("comment-radio-inputs-holder")[0]; var optCount = options.childElementCount; var spanMargin = Math.floor(((boxwidth - 20 - (optCount * 80)) / (optCount)) / 2) + 'px'; - options = options.firstChild; - text = text.firstChild; + var options = options.firstChild; + var text = text.firstChild; options.style.marginRight = spanMargin; options.style.marginLeft = spanMargin; text.style.marginRight = spanMargin; text.style.marginLeft = spanMargin; - while (options = options.nextSibling) { + while (options.nextSibling != undefined) { + options = options.nextSibling; text = text.nextSibling; options.style.marginRight = spanMargin; options.style.marginLeft = spanMargin; @@ -2749,13 +2689,14 @@ var options = this.holder.getElementsByClassName("comment-checkbox-inputs-holder")[0]; var optCount = options.childElementCount; var spanMargin = Math.floor(((boxwidth - 20 - (optCount * 80)) / (optCount)) / 2) + 'px'; - options = options.firstChild; - text = text.firstChild; + var options = options.firstChild; + var text = text.firstChild; options.style.marginRight = spanMargin; options.style.marginLeft = spanMargin; text.style.marginRight = spanMargin; text.style.marginLeft = spanMargin; - while (options = options.nextSibling) { + while (options.nextSibling != undefined) { + options = options.nextSibling; text = text.nextSibling; options.style.marginRight = spanMargin; options.style.marginLeft = spanMargin; @@ -2849,10 +2790,10 @@ this.outsideReferenceHolder.setAttribute('track-id', index); this.outsideReferenceHolder.textContent = this.parent.specification.label || "Reference"; this.outsideReferenceHolder.disabled = true; - this.handleEvent = function (event) { - audioEngineContext.play(this.parent.id); + + this.outsideReferenceHolder.onclick = function (event) { + audioEngineContext.play(event.currentTarget.getAttribute('track-id')); }; - this.outsideReferenceHolder.addEventListener("click", this); inject.appendChild(this.outsideReferenceHolder); this.enable = function () { if (this.parent.state == 1) { @@ -2894,8 +2835,8 @@ // audioObject has an error!! this.outsideReferenceHolder.textContent = "Error"; this.outsideReferenceHolder.style.backgroundColor = "#F00"; - }; - }; + } + } this.playhead = new function () { this.object = document.createElement('div'); @@ -2918,7 +2859,7 @@ this.timePerPixel = 0; this.maxTime = 0; - this.playbackObject = undefined; + this.playbackObject; this.setTimePerPixel = function (audioObject) { //maxTime must be in seconds @@ -2966,20 +2907,20 @@ this.interval = undefined; this.start = function () { - if (this.playbackObject !== undefined && this.interval === undefined) { + if (this.playbackObject != undefined && this.interval == undefined) { if (this.maxTime < 60) { - this.interval = window.setInterval(function () { + this.interval = setInterval(function () { interfaceContext.playhead.update(); }, 10); } else { - this.interval = window.setInterval(function () { + this.interval = setInterval(function () { interfaceContext.playhead.update(); }, 100); } } }; this.stop = function () { - window.clearInterval(this.interval); + clearInterval(this.interval); this.interval = undefined; this.scrubberHead.style.left = '0px'; if (this.maxTime < 60) { @@ -3029,7 +2970,7 @@ } this.onmouseup = function () { var storePoint = testState.currentStore.XMLDOM.getElementsByTagName('metric')[0].getAllElementsByName('volumeTracker'); - if (storePoint.length === 0) { + if (storePoint.length == 0) { storePoint = storage.document.createElement('metricresult'); storePoint.setAttribute('name', 'volumeTracker'); testState.currentStore.XMLDOM.getElementsByTagName('metric')[0].appendChild(storePoint); @@ -3057,12 +2998,12 @@ this.resize = function (event) { if (window.innerWidth < 1000) { - this.object.className = "master-volume-holder-inline"; + this.object.className = "master-volume-holder-inline" } else { this.object.className = 'master-volume-holder-float'; } - }; - }; + } + } this.calibrationModuleObject = null; this.calibrationModule = function () { @@ -3102,7 +3043,7 @@ audioEngineContext.outputGain.gain.value = value; interfaceContext.volume.slider.value = this.input.value; } else { - this.gain.gain.value = value; + this.gain.gain.value = value } break; } @@ -3110,7 +3051,7 @@ disconnect: function () { this.gain.disconnect(); } - }; + } obj.root.className = "calibration-slider"; obj.root.appendChild(obj.input); obj.oscillator.connect(obj.gain); @@ -3138,50 +3079,48 @@ f0 *= 2; } inject.appendChild(this.holder); - }; + } this.collect = function () { - this.calibrationNodes.forEach(function (obj) { + for (var obj of this.calibrationNodes) { var node = storage.document.createElement("calibrationresult"); node.setAttribute("frequency", obj.f); node.setAttribute("range-min", obj.input.min); node.setAttribute("range-max", obj.input.max); node.setAttribute("gain-lin", obj.gain.gain.value); this.storeDOM.appendChild(node); - }, this); - }; - }; + } + } + } // Global Checkers // These functions will help enforce the checkers this.checkHiddenAnchor = function () { - var anchors = audioEngineContext.audioObjects.filter(function (ao) { - return ao.specification.type === "anchor"; - }); - var state = anchors.some(function (ao) { - return (ao.interfaceDOM.getValue() > (ao.specification.marker / 100) && ao.specification.marker > 0); - }); - if (state) { - console.log('Anchor node not below marker value'); - interfaceContext.lightbox.post("Message", 'Please keep listening'); - this.storeErrorNode('Anchor node not below marker value'); - return false; + for (var ao of audioEngineContext.audioObjects) { + if (ao.specification.type == "anchor") { + if (ao.interfaceDOM.getValue() > (ao.specification.marker / 100) && ao.specification.marker > 0) { + // Anchor is not set below + console.log('Anchor node not below marker value'); + interfaceContext.lightbox.post("Message", 'Please keep listening'); + this.storeErrorNode('Anchor node not below marker value'); + return false; + } + } } return true; }; this.checkHiddenReference = function () { - var references = audioEngineContext.audioObjects.filter(function (ao) { - return ao.specification.type === "reference"; - }); - var state = references.some(function (ao) { - return (ao.interfaceDOM.getValue() < (ao.specification.marker / 100) && ao.specification.marker > 0); - }); - if (state) { - console.log('Reference node not below marker value'); - interfaceContext.lightbox.post("Message", 'Please keep listening'); - this.storeErrorNode('Reference node not below marker value'); - return false; + for (var ao of audioEngineContext.audioObjects) { + if (ao.specification.type == "reference") { + if (ao.interfaceDOM.getValue() < (ao.specification.marker / 100) && ao.specification.marker > 0) { + // Anchor is not set below + console.log('Reference node not above marker value'); + this.storeErrorNode('Reference node not above marker value'); + interfaceContext.lightbox.post("Message", 'Please keep listening'); + return false; + } + } } return true; }; @@ -3194,9 +3133,8 @@ return true; } var check_pass = true; - var error_obj = [], - i; - for (i = 0; i < audioEngineContext.audioObjects.length; i++) { + var error_obj = []; + for (var i = 0; i < audioEngineContext.audioObjects.length; i++) { var object = audioEngineContext.audioObjects[i]; var time = object.buffer.buffer.duration; var metric = object.metric; @@ -3211,15 +3149,15 @@ break; } } - if (passed === false) { + if (passed == false) { check_pass = false; console.log("Continue listening to track-" + object.interfaceDOM.getPresentedId()); error_obj.push(object.interfaceDOM.getPresentedId()); } } - if (check_pass === false) { + if (check_pass == false) { var str_start = "You have not completely listened to fragments "; - for (i = 0; i < error_obj.length; i++) { + for (var i = 0; i < error_obj.length; i++) { str_start += error_obj[i]; if (i != error_obj.length - 1) { str_start += ', '; @@ -3236,12 +3174,12 @@ this.checkAllMoved = function () { var str = "You have not moved "; var failed = []; - audioEngineContext.audioObjects.forEach(function (ao) { - if (ao.metric.wasMoved === false && ao.interfaceDOM.canMove() === true) { + for (var ao of audioEngineContext.audioObjects) { + if (ao.metric.wasMoved == false && ao.interfaceDOM.canMove() == true) { failed.push(ao.interfaceDOM.getPresentedId()); } - }, this); - if (failed.length === 0) { + } + if (failed.length == 0) { return true; } else if (failed.length == 1) { str += 'track ' + failed[0]; @@ -3261,12 +3199,12 @@ this.checkAllPlayed = function () { var str = "You have not played "; var failed = []; - audioEngineContext.audioObjects.forEach(function (ao) { - if (ao.metric.wasListenedTo === false) { + for (var ao of audioEngineContext.audioObjects) { + if (ao.metric.wasListenedTo == false) { failed.push(ao.interfaceDOM.getPresentedId()); } - }, this); - if (failed.length === 0) { + } + if (failed.length == 0) { return true; } else if (failed.length == 1) { str += 'track ' + failed[0]; @@ -3298,49 +3236,38 @@ } } return true; - }; - this.checkScaleRange = function () { + } + this.checkScaleRange = function (min, max) { var page = testState.getCurrentTestPage(); - var interfaceObject = page.interfaces; + var audioObjects = audioEngineContext.audioObjects; var state = true; var str = "Please keep listening. "; - if (interfaceObject === undefined) { - return true; + var minRanking = Infinity; + var maxRanking = -Infinity; + for (var ao of audioObjects) { + var rank = ao.interfaceDOM.getValue(); + if (rank < minRanking) { + minRanking = rank; + } + if (rank > maxRanking) { + maxRanking = rank; + } } - interfaceObject = interfaceObject[0]; - var scales = (function () { - var scaleRange = interfaceObject.options.find(function (a) { - return a.name == "scalerange"; - }); - return { - min: scaleRange.min, - max: scaleRange.max - }; - })(); - var range = audioEngineContext.audioObjects.reduce(function (a, b) { - var v = b.interfaceDOM.getValue(); - return { - min: Math.min(a.min, v), - max: Math.max(a.max, v) - } - }, { - min: 100, - max: 0 - }); - if (range.min > scales.min) { - str += "At least one fragment must be below the " + range.min + " mark."; - state = false; - } else if (range.max < sacles.max) { - str += "At least one fragment must be above the " + range.max + " mark."; + if (minRanking * 100 > min) { + str += "At least one fragment must be below the " + min + " mark."; state = false; } - if (state === false) { + if (maxRanking * 100 < max) { + str += "At least one fragment must be above the " + max + " mark." + state = false; + } + if (!state) { console.log(str); this.storeErrorNode(str); interfaceContext.lightbox.post("Error", str); } return state; - }; + } this.storeErrorNode = function (errorMessage) { var time = audioEngineContext.timer.getTestTime(); @@ -3365,12 +3292,13 @@ case "capital": return String.fromCharCode((index + offset) % 26 + 65); case "samediff": - if (index === 0) { + if (index == 0) { return "Same"; } else if (index == 1) { return "Difference"; + } else { + return ""; } - return ""; case "number": return String(index + offset); default: @@ -3378,7 +3306,7 @@ } } - if (typeof labelStart !== "string" || labelStart.length === 0) { + if (typeof labelStart !== "string" || labelStart.length == 0) { labelStart = String.fromCharCode(0); } @@ -3403,6 +3331,7 @@ labelStart = 1; } break; + case "none": default: labelStart = 0; } @@ -3419,7 +3348,7 @@ } else { throw ("Invalid arguments"); } - }; + } this.getCombinedInterfaces = function (page) { // Combine the interfaces with the global interface nodes @@ -3444,7 +3373,7 @@ } }); return local; - }; + } } function Storage() { @@ -3457,14 +3386,14 @@ this.state = 0; this.initialise = function (existingStore) { - if (existingStore === undefined) { + if (existingStore == undefined) { // We need to get the sessionKey this.SessionKey.requestKey(); this.document = document.implementation.createDocument(null, "waetresult", null); this.root = this.document.childNodes[0]; var projectDocument = specification.projectXML; - projectDocument.setAttribute('file-name', specification.url); - projectDocument.setAttribute('url', qualifyURL(specification.url)); + projectDocument.setAttribute('file-name', url); + projectDocument.setAttribute('url', qualifyURL(url)); this.root.appendChild(projectDocument); this.root.appendChild(interfaceContext.returnDateNode()); this.root.appendChild(interfaceContext.returnNavigator()); @@ -3473,10 +3402,10 @@ this.root = existingStore.firstChild; this.SessionKey.key = this.root.getAttribute("key"); } - if (specification.preTest !== undefined) { + if (specification.preTest != undefined) { this.globalPreTest = new this.surveyNode(this, this.root, specification.preTest); } - if (specification.postTest !== undefined) { + if (specification.postTest != undefined) { this.globalPostTest = new this.surveyNode(this, this.root, specification.postTest); } }; @@ -3488,7 +3417,7 @@ handleEvent: function () { var parse = new DOMParser(); var xml = parse.parseFromString(this.request.response, "text/xml"); - if (this.request.response.length === 0) { + if (this.request.response.length == 0) { console.error("An unspecified error occured, no server key could be generated"); return; } @@ -3521,7 +3450,7 @@ this.request.send(); }, update: function () { - if (this.key === null) { + if (this.key == null) { console.log("Cannot save as key == null"); return; } @@ -3556,10 +3485,10 @@ console.log("Intermediate save: Error! " + message.textContent); } } - }; + } xmlhttp.send([hold.innerHTML]); } - }; + } this.createTestPageStore = function (specification) { var store = new this.pageNode(this, specification); @@ -3574,45 +3503,24 @@ this.XMLDOM = this.parent.document.createElement('survey'); this.XMLDOM.setAttribute('location', this.specification.location); this.XMLDOM.setAttribute("state", this.state); - this.specification.options.forEach(function (optNode) { + for (var optNode of this.specification.options) { if (optNode.type != 'statement') { var node = this.parent.document.createElement('surveyresult'); node.setAttribute("ref", optNode.id); node.setAttribute('type', optNode.type); this.XMLDOM.appendChild(node); } - }, this); + } root.appendChild(this.XMLDOM); this.postResult = function (node) { - function postNumber(doc, value) { - var child = doc.createElement("response"); - child.textContent = value; - return child; - } - - function postRadio(doc, node) { - var child = doc.createElement('response'); - if (node.response !== null) { - child.setAttribute('name', node.response.name); - child.textContent = node.response.text; - } - return child; - } - - function postCheckbox(doc, node) { - var checkNode = doc.createElement('response'); - checkNode.setAttribute('name', node.name); - checkNode.setAttribute('checked', node.checked); - return checkNode; - } // From popup: node is the popupOption node containing both spec. and results // ID is the position if (node.specification.type == 'statement') { return; } var surveyresult = this.XMLDOM.firstChild; - while (surveyresult !== null) { + while (surveyresult != null) { if (surveyresult.getAttribute("ref") == node.specification.id) { break; } @@ -3622,18 +3530,28 @@ case "number": case "question": case "slider": - surveyresult.appendChild(postNumber(this.parent.document, node.response)); + var child = this.parent.document.createElement('response'); + child.textContent = node.response; + surveyresult.appendChild(child); break; case "radio": - surveyresult.appendChild(postRadio(this.parent.document, node)); + var child = this.parent.document.createElement('response'); + if (node.response !== null) { + child.setAttribute('name', node.response.name); + child.textContent = node.response.text; + } + surveyresult.appendChild(child); break; case "checkbox": - if (node.response === undefined) { + if (node.response == undefined) { surveyresult.appendChild(this.parent.document.createElement('response')); break; } for (var i = 0; i < node.response.length; i++) { - surveyresult.appendChild(postCheckbox(this.parent.document, node.response[i])); + var checkNode = this.parent.document.createElement('response'); + checkNode.setAttribute('name', node.response[i].name); + checkNode.setAttribute('checked', node.response[i].checked); + surveyresult.appendChild(checkNode); } break; } @@ -3641,7 +3559,7 @@ this.complete = function () { this.state = "complete"; this.XMLDOM.setAttribute("state", this.state); - }; + } }; this.pageNode = function (parent, specification) { @@ -3653,10 +3571,10 @@ this.XMLDOM.setAttribute('ref', specification.id); this.XMLDOM.setAttribute('presentedId', specification.presentedId); this.XMLDOM.setAttribute("state", this.state); - if (specification.preTest !== undefined) { + if (specification.preTest != undefined) { this.preTest = new this.parent.surveyNode(this.parent, this.XMLDOM, this.specification.preTest); } - if (specification.postTest !== undefined) { + if (specification.postTest != undefined) { this.postTest = new this.parent.surveyNode(this.parent, this.XMLDOM, this.specification.postTest); } @@ -3665,12 +3583,12 @@ this.XMLDOM.appendChild(page_metric); // Add the audioelement - this.specification.audioElements.forEach(function (element) { + for (var element of this.specification.audioElements) { var aeNode = this.parent.document.createElement('audioelement'); aeNode.setAttribute('ref', element.id); - if (element.name !== undefined) { - aeNode.setAttribute('name', element.name); - } + if (element.name != undefined) { + aeNode.setAttribute('name', element.name) + }; aeNode.setAttribute('type', element.type); aeNode.setAttribute('url', element.url); aeNode.setAttribute('fqurl', qualifyURL(element.url)); @@ -3683,20 +3601,20 @@ var ae_metric = this.parent.document.createElement('metric'); aeNode.appendChild(ae_metric); this.XMLDOM.appendChild(aeNode); - }, this); + } this.parent.root.appendChild(this.XMLDOM); this.complete = function () { this.state = "complete"; this.XMLDOM.setAttribute("state", "complete"); - }; + } }; this.update = function () { this.SessionKey.update(); - }; + } this.finish = function () { - if (this.state === 0) { + if (this.state == 0) { this.update(); } this.state = 1;
--- a/js/specification.js Wed Mar 29 15:57:09 2017 +0100 +++ b/js/specification.js Fri Apr 14 16:02:52 2017 +0100 @@ -527,6 +527,7 @@ this.interfaces = []; this.playOne = undefined; this.restrictMovement = undefined; + this.position = undefined; this.commentBoxPrefix = "Comment on track"; this.audioElements = []; this.commentQuestions = [];
--- a/xml/test-schema.xsd Wed Mar 29 15:57:09 2017 +0100 +++ b/xml/test-schema.xsd Fri Apr 14 16:02:52 2017 +0100 @@ -100,6 +100,7 @@ <xs:attribute name="labelStart" type="xs:string" use="optional" default="" /> <xs:attribute ref="poolSize" /> <xs:attribute ref="alwaysInclude" /> + <xs:attribute name="position" use="optional" type="xs:nonNegativeInteger" /> <xs:attribute ref="preSilence" /> <xs:attribute ref="postSilence" /> <xs:attribute ref="playOne" />