Mercurial > hg > webaudioevaluationtool
changeset 2504:642dc3224962
Merge branch 'master' of https://github.com/BrechtDeMan/WebAudioEvaluationTool
author | www-data <www-data@sucuk.dcs.qmul.ac.uk> |
---|---|
date | Wed, 21 Sep 2016 13:21:06 +0100 |
parents | 4d8b4f60f589 (current diff) 42abe6eddfb5 (diff) |
children | 52c819e710ca |
files | |
diffstat | 5 files changed, 2167 insertions(+), 2268 deletions(-) [+] |
line wrap: on
line diff
--- a/interfaces/AB.js Wed Sep 21 10:20:57 2016 +0100 +++ b/interfaces/AB.js Wed Sep 21 13:21:06 2016 +0100 @@ -329,7 +329,7 @@ if (this.parent.specification.parent.playOne || specification.playOne) { $('.comparator-button').text('Wait'); $('.comparator-button').attr("disabled","true"); - $(this.playback).css("disabled","false"); + $(this.playback).removeAttr("disabled"); } else { $('.comparator-button').text('Listen'); }
--- a/interfaces/ABX.js Wed Sep 21 10:20:57 2016 +0100 +++ b/interfaces/ABX.js Wed Sep 21 13:21:06 2016 +0100 @@ -323,7 +323,7 @@ if (this.parent.specification.parent.playOne || specification.playOne) { $('.comparator-button').text('Wait'); $('.comparator-button').attr("disabled","true"); - $(this.playback).css("disabled","false"); + $(this.playback).removeAttr("disabled"); } else { $('.comparator-button').text('Listen'); }
--- a/js/core.js Wed Sep 21 10:20:57 2016 +0100 +++ b/js/core.js Wed Sep 21 13:21:06 2016 +0100 @@ -33,9 +33,10 @@ function escapeHTML(s) { return s.split('&').join('&').split('<').join('<').split('"').join('"'); } + function qualifyURL(url) { - var el= document.createElement('div'); - el.innerHTML= '<a href="'+escapeHTML(url)+'">x</a>'; + var el = document.createElement('div'); + el.innerHTML = '<a href="' + escapeHTML(url) + '">x</a>'; return el.firstChild.href; } @@ -43,26 +44,21 @@ // and there is no searchAll style command, this custom function will // search all children recusrively for the name. Used for XSD where all // element nodes must have a name and therefore can pull the schema node -XMLDocument.prototype.getAllElementsByName = function(name) -{ +XMLDocument.prototype.getAllElementsByName = function (name) { name = String(name); var selected = this.documentElement.getAllElementsByName(name); return selected; } -Element.prototype.getAllElementsByName = function(name) -{ +Element.prototype.getAllElementsByName = function (name) { name = String(name); var selected = []; var node = this.firstElementChild; - while(node != null) - { - if (node.getAttribute('name') == name) - { + while (node != null) { + if (node.getAttribute('name') == name) { selected.push(node); } - if (node.childElementCount > 0) - { + if (node.childElementCount > 0) { selected = selected.concat(node.getAllElementsByName(name)); } node = node.nextElementSibling; @@ -70,26 +66,21 @@ return selected; } -XMLDocument.prototype.getAllElementsByTagName = function(name) -{ +XMLDocument.prototype.getAllElementsByTagName = function (name) { name = String(name); var selected = this.documentElement.getAllElementsByTagName(name); return selected; } -Element.prototype.getAllElementsByTagName = function(name) -{ +Element.prototype.getAllElementsByTagName = function (name) { name = String(name); var selected = []; var node = this.firstElementChild; - while(node != null) - { - if (node.nodeName == name) - { + while (node != null) { + if (node.nodeName == name) { selected.push(node); } - if (node.childElementCount > 0) - { + if (node.childElementCount > 0) { selected = selected.concat(node.getAllElementsByTagName(name)); } node = node.nextElementSibling; @@ -99,15 +90,12 @@ // Firefox does not have an XMLDocument.prototype.getElementsByName if (typeof XMLDocument.prototype.getElementsByName != "function") { - XMLDocument.prototype.getElementsByName = function(name) - { + XMLDocument.prototype.getElementsByName = function (name) { name = String(name); var node = this.documentElement.firstElementChild; var selected = []; - while(node != null) - { - if (node.getAttribute('name') == name) - { + while (node != null) { + if (node.getAttribute('name') == name) { selected.push(node); } node = node.nextElementSibling; @@ -116,68 +104,78 @@ } } -var check_dependancies = function() { +var check_dependancies = function () { // This will check for the data dependancies - if (typeof(jQuery) != "function") {return false;} - if (typeof(Specification) != "function") {return false;} - if (typeof(calculateLoudness) != "function") {return false;} - if (typeof(WAVE) != "function") {return false;} - if (typeof(validateXML) != "function") {return false;} + if (typeof (jQuery) != "function") { + return false; + } + if (typeof (Specification) != "function") { + return false; + } + if (typeof (calculateLoudness) != "function") { + return false; + } + if (typeof (WAVE) != "function") { + return false; + } + if (typeof (validateXML) != "function") { + return false; + } return true; } -var onload = function() { - // Function called once the browser has loaded all files. - // This should perform any initial commands such as structure / loading documents - - // Create a web audio API context - // Fixed for cross-browser support - var AudioContext = window.AudioContext || window.webkitAudioContext; - audioContext = new AudioContext; - - // Create test state - testState = new stateMachine(); - - // Create the popup interface object - popup = new interfacePopup(); - +var onload = function () { + // Function called once the browser has loaded all files. + // This should perform any initial commands such as structure / loading documents + + // Create a web audio API context + // Fixed for cross-browser support + var AudioContext = window.AudioContext || window.webkitAudioContext; + audioContext = new AudioContext; + + // Create test state + testState = new stateMachine(); + + // Create the popup interface object + popup = new interfacePopup(); + // Create the specification object - specification = new Specification(); - - // Create the interface object - interfaceContext = new Interface(specification); - - // Create the storage object - storage = new Storage(); - // Define window callbacks for interface - window.onresize = function(event){interfaceContext.resizeWindow(event);}; - - if (window.location.search.length != 0) - { + specification = new Specification(); + + // Create the interface object + interfaceContext = new Interface(specification); + + // Create the storage object + storage = new Storage(); + // Define window callbacks for interface + window.onresize = function (event) { + interfaceContext.resizeWindow(event); + }; + + if (window.location.search.length != 0) { var search = window.location.search.split('?')[1]; // Now split the requests into pairs var searchQueries = search.split('&'); - for (var i in searchQueries) - { + for (var i in searchQueries) { // Split each key-value pair searchQueries[i] = searchQueries[i].split('='); var key = searchQueries[i][0]; var value = decodeURIComponent(searchQueries[i][1]); - switch(key) { - case "url": - url = value; - break; - case "returnURL": - gReturnURL = value; - break; - case "saveFilenamePrefix": - gSaveFilenamePrefix = value; - break; + switch (key) { + case "url": + url = value; + break; + case "returnURL": + gReturnURL = value; + break; + case "saveFilenamePrefix": + gSaveFilenamePrefix = value; + break; } } loadProjectSpec(url); - window.onbeforeunload = function() { + window.onbeforeunload = function () { return "Please only leave this page once you have completed the tests. Are you sure you have completed all testing?"; }; } @@ -185,21 +183,20 @@ }; function loadProjectSpec(url) { - // Load the project document from the given URL, decode the XML and instruct audioEngine to get audio data - // If url is null, request client to upload project XML document - var xmlhttp = new XMLHttpRequest(); - xmlhttp.open("GET",'xml/test-schema.xsd',true); - xmlhttp.onload = function() - { - 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() { - loadProjectSpecCallback(r.response); - }; - r.onerror = function() { + // Load the project document from the given URL, decode the XML and instruct audioEngine to get audio data + // If url is null, request client to upload project XML document + var xmlhttp = new XMLHttpRequest(); + xmlhttp.open("GET", 'xml/test-schema.xsd', true); + xmlhttp.onload = function () { + 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 () { + loadProjectSpecCallback(r.response); + }; + r.onerror = function () { document.getElementsByTagName('body')[0].innerHTML = null; var msg = document.createElement("h3"); msg.textContent = "FATAL ERROR"; @@ -208,56 +205,54 @@ document.getElementsByTagName('body')[0].appendChild(msg); document.getElementsByTagName('body')[0].appendChild(span); } - r.send(); - }; - xmlhttp.send(); + r.send(); + }; + xmlhttp.send(); }; function loadProjectSpecCallback(response) { - // Function called after asynchronous download of XML project specification - //var decode = $.parseXML(response); - //projectXML = $(decode); - + // Function called after asynchronous download of XML project specification + //var decode = $.parseXML(response); + //projectXML = $(decode); + // Check if XML is new or a resumption var parse = new DOMParser(); - var responseDocument = parse.parseFromString(response,'text/xml'); + var responseDocument = parse.parseFromString(response, 'text/xml'); var errorNode = responseDocument.getElementsByTagName('parsererror'); - if (errorNode.length >= 1) - { - var msg = document.createElement("h3"); - msg.textContent = "FATAL ERROR"; - 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); - document.getElementsByTagName('body')[0].appendChild(span); - document.getElementsByTagName('body')[0].appendChild(errorNode[0]); - return; - } + if (errorNode.length >= 1) { + var msg = document.createElement("h3"); + msg.textContent = "FATAL ERROR"; + 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); + document.getElementsByTagName('body')[0].appendChild(span); + document.getElementsByTagName('body')[0].appendChild(errorNode[0]); + return; + } if (responseDocument == undefined || responseDocument.firstChild == undefined) { var msg = document.createElement("h3"); - msg.textContent = "FATAL ERROR"; - 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); - document.getElementsByTagName('body')[0].appendChild(span); - return; + msg.textContent = "FATAL ERROR"; + 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); + document.getElementsByTagName('body')[0].appendChild(span); + return; } if (responseDocument.firstChild.nodeName == "waet") { // document is a specification - + // Perform XML schema validation var Module = { xml: response, schema: schemaXSD, - arguments:["--noout", "--schema", 'test-schema.xsd','document.xml'] + arguments: ["--noout", "--schema", 'test-schema.xsd', 'document.xml'] }; - projectXML = responseDocument; + projectXML = responseDocument; var xmllint = validateXML(Module); console.log(xmllint); - if(xmllint != 'document.xml validates\n') - { + if (xmllint != 'document.xml validates\n') { document.getElementsByTagName('body')[0].innerHTML = null; var msg = document.createElement("h3"); msg.textContent = "FATAL ERROR"; @@ -266,8 +261,7 @@ document.getElementsByTagName('body')[0].appendChild(msg); document.getElementsByTagName('body')[0].appendChild(span); xmllint = xmllint.split('\n'); - for (var i in xmllint) - { + for (var i in xmllint) { document.getElementsByTagName('body')[0].appendChild(document.createElement('br')); var span = document.createElement("span"); span.textContent = xmllint[i]; @@ -276,13 +270,13 @@ return; } // Build the specification - specification.decode(projectXML); + specification.decode(projectXML); // Generate the session-key storage.initialise(); - + } else if (responseDocument.firstChild.nodeName == "waetresult") { // document is a result - projectXML = document.implementation.createDocument(null,"waet"); + projectXML = document.implementation.createDocument(null, "waet"); projectXML.firstChild.appendChild(responseDocument.getElementsByTagName('waet')[0].getElementsByTagName("setup")[0].cloneNode(true)); var child = responseDocument.firstChild.firstChild; while (child != null) { @@ -292,7 +286,7 @@ // 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); @@ -324,26 +318,25 @@ child = child.nextElementSibling; } // Build the specification - specification.decode(projectXML); + specification.decode(projectXML); // Use the original storage.initialise(responseDocument); } - /// CHECK FOR SAMPLE RATE COMPATIBILITY - 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); - return; - } - } - - // Detect the interface to use and load the relevant javascripts. - var interfaceJS = document.createElement('script'); - interfaceJS.setAttribute("type","text/javascript"); - switch(specification.interface) - { - case "APE": - interfaceJS.setAttribute("src","interfaces/ape.js"); + /// CHECK FOR SAMPLE RATE COMPATIBILITY + 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); + return; + } + } + + // Detect the interface to use and load the relevant javascripts. + var interfaceJS = document.createElement('script'); + interfaceJS.setAttribute("type", "text/javascript"); + switch (specification.interface) { + case "APE": + interfaceJS.setAttribute("src", "interfaces/ape.js"); // APE comes with a css file var css = document.createElement('link'); @@ -354,8 +347,8 @@ document.getElementsByTagName("head")[0].appendChild(css); break; - case "MUSHRA": - interfaceJS.setAttribute("src","interfaces/mushra.js"); + case "MUSHRA": + interfaceJS.setAttribute("src", "interfaces/mushra.js"); // MUSHRA comes with a css file var css = document.createElement('link'); @@ -365,9 +358,9 @@ document.getElementsByTagName("head")[0].appendChild(css); break; - - case "AB": - interfaceJS.setAttribute("src","interfaces/AB.js"); + + case "AB": + interfaceJS.setAttribute("src", "interfaces/AB.js"); // AB comes with a css file var css = document.createElement('link'); @@ -377,9 +370,9 @@ document.getElementsByTagName("head")[0].appendChild(css); break; - + case "ABX": - interfaceJS.setAttribute("src","interfaces/ABX.js"); + interfaceJS.setAttribute("src", "interfaces/ABX.js"); // AB comes with a css file var css = document.createElement('link'); @@ -389,14 +382,14 @@ document.getElementsByTagName("head")[0].appendChild(css); break; - - case "Bipolar": - case "ACR": - case "DCR": - case "CCR": - case "ABC": + + case "Bipolar": + case "ACR": + case "DCR": + case "CCR": + case "ABC": // Above enumerate to horizontal sliders - interfaceJS.setAttribute("src","interfaces/horizontal-sliders.js"); + interfaceJS.setAttribute("src", "interfaces/horizontal-sliders.js"); // horizontal-sliders comes with a css file var css = document.createElement('link'); @@ -406,10 +399,10 @@ document.getElementsByTagName("head")[0].appendChild(css); break; - case "discrete": - case "likert": + case "discrete": + case "likert": // Above enumerate to horizontal discrete radios - interfaceJS.setAttribute("src","interfaces/discrete.js"); + interfaceJS.setAttribute("src", "interfaces/discrete.js"); // horizontal-sliders comes with a css file var css = document.createElement('link'); @@ -420,7 +413,7 @@ document.getElementsByTagName("head")[0].appendChild(css); break; case "timeline": - interfaceJS.setAttribute("src","interfaces/timeline.js"); + interfaceJS.setAttribute("src", "interfaces/timeline.js"); var css = document.createElement('link'); css.rel = 'stylesheet'; css.type = 'text/css'; @@ -428,64 +421,66 @@ document.getElementsByTagName("head")[0].appendChild(css); break; - } - document.getElementsByTagName("head")[0].appendChild(interfaceJS); - + } + document.getElementsByTagName("head")[0].appendChild(interfaceJS); + if (gReturnURL != undefined) { - console.log("returnURL Overide from "+specification.returnURL+" to "+gReturnURL); + console.log("returnURL Overide from " + specification.returnURL + " to " + gReturnURL); specification.returnURL = gReturnURL; } - if (gSaveFilenamePrefix != undefined){ + if (gSaveFilenamePrefix != undefined) { specification.saveFilenamePrefix = gSaveFilenamePrefix; } - - // Create the audio engine object - audioEngineContext = new AudioEngine(specification); + + // Create the audio engine object + audioEngineContext = new AudioEngine(specification); } function createProjectSave(destURL) { // Clear the window.onbeforeunload window.onbeforeunload = null; - // Save the data from interface into XML and send to destURL - // If destURL is null then download XML in client - // Now time to render file locally - var xmlDoc = interfaceXMLSave(); - var parent = document.createElement("div"); - parent.appendChild(xmlDoc); - var file = [parent.innerHTML]; - if (destURL == "local") { - var bb = new Blob(file,{type : 'application/xml'}); - var dnlk = window.URL.createObjectURL(bb); - var a = document.createElement("a"); - a.hidden = ''; - a.href = dnlk; - a.download = "save.xml"; - a.textContent = "Save File"; - - popup.showPopup(); - popup.popupContent.innerHTML = "<span>Please save the file below to give to your test supervisor</span><br>"; - popup.popupContent.appendChild(a); - } else { - var saveUrlSuffix = ""; - var saveFilenamePrefix = specification.saveFilenamePrefix; - if(typeof(saveFilenamePrefix) === "string" && saveFilenamePrefix.length > 0){ - saveUrlSuffix = "&saveFilenamePrefix="+saveFilenamePrefix; - } - var projectReturn = ""; - if (typeof specification.projectReturn == "string") { - if (specification.projectReturn.substr(0,4) == "http") { - projectReturn = specification.projectReturn; - } - } - var saveURL = projectReturn+"php/save.php?key="+storage.SessionKey.key+saveUrlSuffix; - var xmlhttp = new XMLHttpRequest; - xmlhttp.open("POST", saveURL, true); - xmlhttp.setRequestHeader('Content-Type', 'text/xml'); - xmlhttp.onerror = function(){ - console.log('Error saving file to server! Presenting download locally'); - createProjectSave("local"); - }; - xmlhttp.onload = function() { + // Save the data from interface into XML and send to destURL + // If destURL is null then download XML in client + // Now time to render file locally + var xmlDoc = interfaceXMLSave(); + var parent = document.createElement("div"); + parent.appendChild(xmlDoc); + var file = [parent.innerHTML]; + if (destURL == "local") { + var bb = new Blob(file, { + type: 'application/xml' + }); + var dnlk = window.URL.createObjectURL(bb); + var a = document.createElement("a"); + a.hidden = ''; + a.href = dnlk; + a.download = "save.xml"; + a.textContent = "Save File"; + + popup.showPopup(); + popup.popupContent.innerHTML = "<span>Please save the file below to give to your test supervisor</span><br>"; + popup.popupContent.appendChild(a); + } else { + var saveUrlSuffix = ""; + var saveFilenamePrefix = specification.saveFilenamePrefix; + if (typeof (saveFilenamePrefix) === "string" && saveFilenamePrefix.length > 0) { + saveUrlSuffix = "&saveFilenamePrefix=" + saveFilenamePrefix; + } + var projectReturn = ""; + if (typeof specification.projectReturn == "string") { + if (specification.projectReturn.substr(0, 4) == "http") { + projectReturn = specification.projectReturn; + } + } + var saveURL = projectReturn + "php/save.php?key=" + storage.SessionKey.key + saveUrlSuffix; + var xmlhttp = new XMLHttpRequest; + xmlhttp.open("POST", saveURL, true); + xmlhttp.setRequestHeader('Content-Type', 'text/xml'); + xmlhttp.onerror = function () { + console.log('Error saving file to server! Presenting download locally'); + createProjectSave("local"); + }; + xmlhttp.onload = function () { console.log(xmlhttp); if (this.status >= 300) { console.log("WARNING - Could not update at this time"); @@ -497,239 +492,239 @@ if (response.getAttribute("state") == "OK") { window.onbeforeunload = undefined; var file = response.getElementsByTagName("file")[0]; - console.log("Save: OK, written "+file.getAttribute("bytes")+"B"); + console.log("Save: OK, written " + file.getAttribute("bytes") + "B"); if (typeof specification.returnURL == "string" && specification.returnURL.length > 0) { - window.location = specification.returnURL; + window.location = specification.returnURL; } else { popup.popupContent.textContent = specification.exitText; } } else { var message = response.getElementsByTagName("message"); - console.log("Save: Error! "+message.textContent); + console.log("Save: Error! " + message.textContent); createProjectSave("local"); } } }; - xmlhttp.send(file); - popup.showPopup(); - popup.popupContent.innerHTML = null; - popup.popupContent.textContent = "Submitting. Please Wait"; - if(typeof(popup.hideNextButton) === "function"){ - popup.hideNextButton(); - } - if(typeof(popup.hidePreviousButton) === "function"){ - popup.hidePreviousButton(); - } - } + xmlhttp.send(file); + popup.showPopup(); + popup.popupContent.innerHTML = null; + popup.popupContent.textContent = "Submitting. Please Wait"; + if (typeof (popup.hideNextButton) === "function") { + popup.hideNextButton(); + } + if (typeof (popup.hidePreviousButton) === "function") { + popup.hidePreviousButton(); + } + } } -function errorSessionDump(msg){ - // Create the partial interface XML save - // Include error node with message on why the dump occured - popup.showPopup(); - popup.popupContent.innerHTML = null; - var err = document.createElement('error'); - var parent = document.createElement("div"); - if (typeof msg === "object") - { - err.appendChild(msg); - popup.popupContent.appendChild(msg); - - } else { - err.textContent = msg; - popup.popupContent.innerHTML = "ERROR : "+msg; - } - var xmlDoc = interfaceXMLSave(); - xmlDoc.appendChild(err); - parent.appendChild(xmlDoc); - var file = [parent.innerHTML]; - var bb = new Blob(file,{type : 'application/xml'}); - var dnlk = window.URL.createObjectURL(bb); - var a = document.createElement("a"); - a.hidden = ''; - a.href = dnlk; - a.download = "save.xml"; - a.textContent = "Save File"; - - - - popup.popupContent.appendChild(a); +function errorSessionDump(msg) { + // Create the partial interface XML save + // Include error node with message on why the dump occured + popup.showPopup(); + popup.popupContent.innerHTML = null; + var err = document.createElement('error'); + var parent = document.createElement("div"); + if (typeof msg === "object") { + err.appendChild(msg); + popup.popupContent.appendChild(msg); + + } else { + err.textContent = msg; + popup.popupContent.innerHTML = "ERROR : " + msg; + } + var xmlDoc = interfaceXMLSave(); + xmlDoc.appendChild(err); + parent.appendChild(xmlDoc); + var file = [parent.innerHTML]; + var bb = new Blob(file, { + type: 'application/xml' + }); + var dnlk = window.URL.createObjectURL(bb); + var a = document.createElement("a"); + a.hidden = ''; + a.href = dnlk; + a.download = "save.xml"; + a.textContent = "Save File"; + + + + popup.popupContent.appendChild(a); } // Only other global function which must be defined in the interface class. Determines how to create the XML document. -function interfaceXMLSave(){ - // Create the XML string to be exported with results - return storage.finish(); +function interfaceXMLSave() { + // Create the XML string to be exported with results + return storage.finish(); } -function linearToDecibel(gain) -{ - return 20.0*Math.log10(gain); +function linearToDecibel(gain) { + return 20.0 * Math.log10(gain); } -function decibelToLinear(gain) -{ - return Math.pow(10,gain/20.0); +function decibelToLinear(gain) { + return Math.pow(10, gain / 20.0); } -function secondsToSamples(time,fs) { - return Math.round(time*fs); +function secondsToSamples(time, fs) { + return Math.round(time * fs); } -function samplesToSeconds(samples,fs) { +function samplesToSeconds(samples, fs) { return samples / fs; } function randomString(length) { var str = "" - for (var i=0; i<length; i+=2) { - var num = Math.floor(Math.random()*1295); + for (var i = 0; i < length; i += 2) { + var num = Math.floor(Math.random() * 1295); str += num.toString(36); } return str; //return Math.round((Math.pow(36, length + 1) - Math.random() * Math.pow(36, length))).toString(36).slice(1); } -function randomiseOrder(input) -{ - // This takes an array of information and randomises the order - var N = input.length; - - var inputSequence = []; // For safety purposes: keep track of randomisation - for (var counter = 0; counter < N; ++counter) - inputSequence.push(counter) // Fill array - var inputSequenceClone = inputSequence.slice(0); - - var holdArr = []; - var outputSequence = []; - for (var n=0; n<N; n++) - { - // First pick a random number - var r = Math.random(); - // Multiply and floor by the number of elements left - r = Math.floor(r*input.length); - // Pick out that element and delete from the array - holdArr.push(input.splice(r,1)[0]); - // Do the same with sequence - outputSequence.push(inputSequence.splice(r,1)[0]); - } - console.log(inputSequenceClone.toString()); // print original array to console - console.log(outputSequence.toString()); // print randomised array to console - return holdArr; +function randomiseOrder(input) { + // This takes an array of information and randomises the order + var N = input.length; + + var inputSequence = []; // For safety purposes: keep track of randomisation + for (var counter = 0; counter < N; ++counter) + inputSequence.push(counter) // Fill array + var inputSequenceClone = inputSequence.slice(0); + + var holdArr = []; + var outputSequence = []; + for (var n = 0; n < N; n++) { + // First pick a random number + var r = Math.random(); + // Multiply and floor by the number of elements left + r = Math.floor(r * input.length); + // Pick out that element and delete from the array + holdArr.push(input.splice(r, 1)[0]); + // Do the same with sequence + outputSequence.push(inputSequence.splice(r, 1)[0]); + } + console.log(inputSequenceClone.toString()); // print original array to console + console.log(outputSequence.toString()); // print randomised array to console + return holdArr; } -function randomSubArray(array,num) { +function randomSubArray(array, num) { if (num > array.length) { num = array.length; } var ret = []; while (num > 0) { var index = Math.floor(Math.random() * array.length); - ret.push( array.splice(index,1)[0] ); + ret.push(array.splice(index, 1)[0]); num--; } return ret; } function interfacePopup() { - // Creates an object to manage the popup - this.popup = null; - this.popupContent = null; - this.popupTitle = null; - this.popupResponse = null; - this.buttonProceed = null; - this.buttonPrevious = null; - this.popupOptions = null; - this.currentIndex = null; - this.node = null; - this.store = null; - $(window).keypress(function(e){ - if (e.keyCode == 13 && popup.popup.style.visibility == 'visible') - { - console.log(e); - popup.buttonProceed.onclick(); - e.preventDefault(); - } - }); - - this.createPopup = function(){ - // Create popup window interface - var insertPoint = document.getElementById("topLevelBody"); - - this.popup = document.getElementById('popupHolder'); - this.popup.style.left = (window.innerWidth/2)-250 + 'px'; - this.popup.style.top = (window.innerHeight/2)-125 + 'px'; - - this.popupContent = document.getElementById('popupContent'); - - this.popupTitle = document.getElementById('popupTitle'); - - this.popupResponse = document.getElementById('popupResponse'); - - this.buttonProceed = document.getElementById('popup-proceed'); - this.buttonProceed.onclick = function(){popup.proceedClicked();}; - - this.buttonPrevious = document.getElementById('popup-previous'); - this.buttonPrevious.onclick = function(){popup.previousClick();}; - + // Creates an object to manage the popup + this.popup = null; + this.popupContent = null; + this.popupTitle = null; + this.popupResponse = null; + this.buttonProceed = null; + this.buttonPrevious = null; + this.popupOptions = null; + this.currentIndex = null; + this.node = null; + this.store = null; + $(window).keypress(function (e) { + if (e.keyCode == 13 && popup.popup.style.visibility == 'visible') { + console.log(e); + popup.buttonProceed.onclick(); + e.preventDefault(); + } + }); + + this.createPopup = function () { + // Create popup window interface + var insertPoint = document.getElementById("topLevelBody"); + + this.popup = document.getElementById('popupHolder'); + this.popup.style.left = (window.innerWidth / 2) - 250 + 'px'; + this.popup.style.top = (window.innerHeight / 2) - 125 + 'px'; + + this.popupContent = document.getElementById('popupContent'); + + this.popupTitle = document.getElementById('popupTitle'); + + this.popupResponse = document.getElementById('popupResponse'); + + this.buttonProceed = document.getElementById('popup-proceed'); + this.buttonProceed.onclick = function () { + popup.proceedClicked(); + }; + + this.buttonPrevious = document.getElementById('popup-previous'); + this.buttonPrevious.onclick = function () { + popup.previousClick(); + }; + this.hidePopup(); - this.popup.style.visibility = 'hidden'; - }; - - this.showPopup = function(){ - if (this.popup == null) { - this.createPopup(); - } - this.popup.style.visibility = 'visible'; - var blank = document.getElementsByClassName('testHalt')[0]; - blank.style.visibility = 'visible'; - this.popupResponse.style.left="0%"; - }; - - this.hidePopup = function(){ + this.popup.style.visibility = 'hidden'; + }; + + this.showPopup = function () { + if (this.popup == null) { + this.createPopup(); + } + this.popup.style.visibility = 'visible'; + var blank = document.getElementsByClassName('testHalt')[0]; + blank.style.visibility = 'visible'; + this.popupResponse.style.left = "0%"; + }; + + this.hidePopup = function () { if (this.popup) { this.popup.style.visibility = 'hidden'; var blank = document.getElementsByClassName('testHalt')[0]; blank.style.visibility = 'hidden'; this.buttonPrevious.style.visibility = 'inherit'; } - }; - - this.postNode = function() { - // This will take the node from the popupOptions and display it - var node = this.popupOptions[this.currentIndex]; - this.popupResponse.innerHTML = ""; - this.popupTitle.textContent = node.specification.statement; - if (node.specification.type == 'question') { - 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; - } + }; + + this.postNode = function () { + // This will take the node from the popupOptions and display it + var node = this.popupOptions[this.currentIndex]; + this.popupResponse.innerHTML = ""; + this.popupTitle.textContent = node.specification.statement; + if (node.specification.type == 'question') { + 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') { + this.popupResponse.appendChild(textArea); + textArea.focus(); + this.popupResponse.style.textAlign = "center"; + this.popupResponse.style.left = "0%"; + } else if (node.specification.type == 'checkbox') { if (node.response == undefined) { node.response = Array(node.specification.options.length); } @@ -737,25 +732,25 @@ var table = document.createElement("table"); table.className = "popup-option-list"; table.border = "0"; - for (var option of node.specification.options) { + 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'; + input.type = 'checkbox'; td.appendChild(input); - + td = document.createElement("td"); tr.appendChild(td); - var span = document.createElement('span'); - span.textContent = option.text; + var span = document.createElement('span'); + span.textContent = option.text; td.appendChild(span); - var tr = document.createElement('div'); - tr.setAttribute('name','option'); + var tr = document.createElement('div'); + tr.setAttribute('name', 'option'); tr.className = "popup-option-checbox"; - if (node.response[index] != undefined){ + if (node.response[index] != undefined) { if (node.response[index].checked == true) { input.checked = "true"; } @@ -763,34 +758,37 @@ index++; } this.popupResponse.appendChild(table); - } else if (node.specification.type == 'radio') { + } else if (node.specification.type == 'radio') { if (node.response == undefined) { - node.response = {name: "", text: ""}; + node.response = { + name: "", + text: "" + }; } var index = 0; - var table = document.createElement("table"); + var table = document.createElement("table"); table.className = "popup-option-list"; table.border = "0"; - for (var option of node.specification.options) { + 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.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; + var span = document.createElement('span'); + span.textContent = option.text; td.appendChild(span); - var tr = document.createElement('div'); - tr.setAttribute('name','option'); + var tr = document.createElement('div'); + tr.setAttribute('name', 'option'); tr.className = "popup-option-checbox"; - if (node.response[index] != undefined){ + if (node.response[index] != undefined) { if (node.response[index].checked == true) { input.checked = "true"; } @@ -798,19 +796,25 @@ index++; } this.popupResponse.appendChild(table); - } else if (node.specification.type == 'number') { - var input = document.createElement('input'); - input.type = 'textarea'; - if (node.min != null) {input.min = node.specification.min;} - if (node.max != null) {input.max = node.specification.max;} - if (node.step != null) {input.step = node.specification.step;} + } else if (node.specification.type == 'number') { + var input = document.createElement('input'); + input.type = 'textarea'; + if (node.min != null) { + input.min = node.specification.min; + } + if (node.max != null) { + input.max = node.specification.max; + } + if (node.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") { + this.popupResponse.appendChild(input); + this.popupResponse.style.textAlign = "center"; + this.popupResponse.style.left = "0%"; + } else if (node.specification.type == "video") { var video = document.createElement("video"); video.src = node.specification.url; this.popupResponse.appendChild(video); @@ -820,67 +824,66 @@ iframe.src = node.specification.url; this.popupResponse.appendChild(iframe); } - if(this.currentIndex+1 == this.popupOptions.length) { - if (this.node.location == "pre") { - this.buttonProceed.textContent = 'Start'; - } else { - this.buttonProceed.textContent = 'Submit'; - } - } else { - this.buttonProceed.textContent = 'Next'; - } - if(this.currentIndex > 0) - this.buttonPrevious.style.visibility = 'visible'; - else - this.buttonPrevious.style.visibility = 'hidden'; - }; - - this.initState = function(node,store) { - //Call this with your preTest and postTest nodes when needed to - // initialise the popup procedure. - if (node.options.length > 0) { - this.popupOptions = []; - this.node = node; - this.store = store; - for (var opt of node.options) - { - this.popupOptions.push({ - specification: opt, - response: null - }); - } - this.currentIndex = 0; - this.showPopup(); - this.postNode(); - } else { - advanceState(); - } - }; - - this.proceedClicked = function() { - // Each time the popup button is clicked! + if (this.currentIndex + 1 == this.popupOptions.length) { + if (this.node.location == "pre") { + this.buttonProceed.textContent = 'Start'; + } else { + this.buttonProceed.textContent = 'Submit'; + } + } else { + this.buttonProceed.textContent = 'Next'; + } + if (this.currentIndex > 0) + this.buttonPrevious.style.visibility = 'visible'; + else + this.buttonPrevious.style.visibility = 'hidden'; + }; + + this.initState = function (node, store) { + //Call this with your preTest and postTest nodes when needed to + // initialise the popup procedure. + if (node.options.length > 0) { + this.popupOptions = []; + this.node = node; + this.store = store; + for (var opt of node.options) { + this.popupOptions.push({ + specification: opt, + response: null + }); + } + this.currentIndex = 0; + this.showPopup(); + this.postNode(); + } else { + advanceState(); + } + }; + + this.proceedClicked = function () { + // Each time the popup button is clicked! if (testState.stateIndex == 0 && specification.calibration) { interfaceContext.calibrationModuleObject.collect(); advanceState(); return; } - var node = this.popupOptions[this.currentIndex]; - if (node.specification.type == 'question') { - // Must extract the question data - 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; - } + var node = this.popupOptions[this.currentIndex]; + if (node.specification.type == 'question') { + // Must extract the question data + 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) { + switch (condition.check) { case "equals": if (textArea.value == condition.value) { pass = true; @@ -903,30 +906,34 @@ 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; + 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 - console.log("Checkbox: "+ node.specification.statement); - var inputs = this.popupResponse.getElementsByTagName('input'); - node.response = []; - 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); - } + } else if (node.specification.type == 'checkbox') { + // Must extract checkbox data + console.log("Checkbox: " + node.specification.statement); + var inputs = this.popupResponse.getElementsByTagName('input'); + node.response = []; + 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) { + switch (condition.check) { case "equals": case "greaterThan": case "lessThan": @@ -948,39 +955,41 @@ 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; + 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"); + } 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++; - } + } + if (inputs[i].checked == true) { + node.response = node.specification.options[i]; + console.log("Selected: " + node.specification.options[i].name); + } + i++; + } // Perform the conditional for (var condition of node.specification.conditions) { var pass = false; - switch(condition.check) { + switch (condition.check) { case "contains": case "greaterThan": case "lessThan": @@ -999,37 +1008,41 @@ 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; + 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; + } 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 for (var condition of node.specification.conditions) { var pass = false; - switch(condition.check) { + switch (condition.check) { case "contains": console.log("Survey Element of type 'number' cannot interpret contains conditions. IGNORING"); break; @@ -1056,93 +1069,93 @@ 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; + 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) { - this.postNode(); - } else { - // Reached the end of the popupOptions - this.popupContent.innerHTML = ""; - this.hidePopup(); - for (var node of this.popupOptions) - { - this.store.postResult(node); - } + } + this.currentIndex++; + if (this.currentIndex < this.popupOptions.length) { + this.postNode(); + } else { + // Reached the end of the popupOptions + this.popupTitle.textContent = ""; + this.popupResponse.innerHTML = ""; + this.hidePopup(); + for (var node of this.popupOptions) { + this.store.postResult(node); + } this.store.complete(); - advanceState(); - } - }; - - this.previousClick = function() { - // Triggered when the 'Back' button is clicked in the survey - if (this.currentIndex > 0) { - this.currentIndex--; - this.postNode(); - } - }; - - this.resize = function(event) - { - // Called on window resize; - 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]; - blank.style.width = window.innerWidth; - blank.style.height = window.innerHeight; - } - }; - this.hideNextButton = function() { + advanceState(); + } + }; + + this.previousClick = function () { + // Triggered when the 'Back' button is clicked in the survey + if (this.currentIndex > 0) { + this.currentIndex--; + this.postNode(); + } + }; + + this.resize = function (event) { + // Called on window resize; + 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]; + blank.style.width = window.innerWidth; + blank.style.height = window.innerHeight; + } + }; + this.hideNextButton = function () { this.buttonProceed.style.visibility = "hidden"; } - this.hidePreviousButton = function() { + this.hidePreviousButton = function () { this.buttonPrevious.style.visibility = "hidden"; } - this.showNextButton = function() { + this.showNextButton = function () { this.buttonProceed.style.visibility = "visible"; } - this.showPreviousButton = function() { + this.showPreviousButton = function () { this.buttonPrevious.style.visibility = "visible"; } } -function advanceState() -{ - // Just for complete clarity - testState.advanceState(); +function advanceState() { + // Just for complete clarity + testState.advanceState(); } -function stateMachine() -{ - // Object prototype for tracking and managing the test state - this.stateMap = []; - this.preTestSurvey = null; - this.postTestSurvey = null; - this.stateIndex = null; - this.currentStateMap = null; - this.currentStatePosition = null; +function stateMachine() { + // Object prototype for tracking and managing the test state + this.stateMap = []; + this.preTestSurvey = null; + this.postTestSurvey = null; + this.stateIndex = null; + this.currentStateMap = null; + this.currentStatePosition = null; this.currentStore = null; - this.initialise = function(){ - - // Get the data from Specification - var pagePool = []; + this.initialise = function () { + + // Get the data from Specification + var pagePool = []; var pageInclude = []; - for (var page of specification.pages) - { + for (var page of specification.pages) { if (page.alwaysInclude) { pageInclude.push(page); } else { pagePool.push(page); } - } - + } + // Find how many are left to get var numPages = specification.poolSize; if (numPages > pagePool.length) { @@ -1153,29 +1166,27 @@ 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); + subarr = randomSubArray(pagePool, numPages); } else { // Append the matching number - subarr = pagePool.slice(0,numPages); + subarr = pagePool.slice(0, numPages); } pageInclude = pageInclude.concat(subarr); } - + // We now have our selected pages in pageInclude array - if (specification.randomiseOrder) - { - pageInclude = randomiseOrder(pageInclude); - } - for (var i=0; i<pageInclude.length; i++) - { + if (specification.randomiseOrder) { + pageInclude = randomiseOrder(pageInclude); + } + for (var i = 0; i < pageInclude.length; i++) { pageInclude[i].presentedId = i; - this.stateMap.push(pageInclude[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 = []; @@ -1188,41 +1199,44 @@ } } var numElems = pageInclude[i].poolSize - elemInclude.length; - pageInclude[i].audioElements = elemInclude.concat(randomSubArray(elemPool,numElems)); + pageInclude[i].audioElements = elemInclude.concat(randomSubArray(elemPool, numElems)); } storage.createTestPageStore(pageInclude[i]); audioEngineContext.loadPageData(pageInclude[i]); - } - - if (specification.preTest != null) {this.preTestSurvey = specification.preTest;} - if (specification.postTest != null) {this.postTestSurvey = specification.postTest;} - - if (this.stateMap.length > 0) { - if(this.stateIndex != null) { - console.log('NOTE - State already initialise'); - } - this.stateIndex = -2; + } + + if (specification.preTest != null) { + this.preTestSurvey = specification.preTest; + } + if (specification.postTest != null) { + this.postTestSurvey = specification.postTest; + } + + if (this.stateMap.length > 0) { + if (this.stateIndex != null) { + console.log('NOTE - State already initialise'); + } + this.stateIndex = -2; console.log('Starting test...'); - } else { - console.log('FATAL - StateMap not correctly constructed. EMPTY_STATE_MAP'); - } - }; - this.advanceState = function(){ - if (this.stateIndex == null) { - this.initialise(); - } + } else { + console.log('FATAL - StateMap not correctly constructed. EMPTY_STATE_MAP'); + } + }; + this.advanceState = function () { + if (this.stateIndex == null) { + this.initialise(); + } if (this.stateIndex > -2) { storage.update(); } - if (this.stateIndex == -2) { + if (this.stateIndex == -2) { this.stateIndex++; - if (this.preTestSurvey != null) - { - popup.initState(this.preTestSurvey,storage.globalPreTest); - } else { - this.advanceState(); - } - } else if (this.stateIndex == -1) { + if (this.preTestSurvey != null) { + popup.initState(this.preTestSurvey, storage.globalPreTest); + } else { + this.advanceState(); + } + } else if (this.stateIndex == -1) { this.stateIndex++; if (specification.calibration) { popup.showPopup(); @@ -1233,119 +1247,105 @@ } else { this.advanceState(); } - } - else if (this.stateIndex == this.stateMap.length) - { - // All test pages complete, post test - console.log('Ending test ...'); - this.stateIndex++; - if (this.postTestSurvey == null) { - this.advanceState(); - } else { - popup.initState(this.postTestSurvey,storage.globalPostTest); - } - } else if (this.stateIndex > this.stateMap.length) - { - createProjectSave(specification.projectReturn); - } - else - { + } else if (this.stateIndex == this.stateMap.length) { + // All test pages complete, post test + console.log('Ending test ...'); + this.stateIndex++; + if (this.postTestSurvey == null) { + this.advanceState(); + } else { + popup.initState(this.postTestSurvey, storage.globalPostTest); + } + } else if (this.stateIndex > this.stateMap.length) { + createProjectSave(specification.projectReturn); + } else { popup.hidePopup(); - if (this.currentStateMap == null) - { - this.currentStateMap = this.stateMap[this.stateIndex]; + if (this.currentStateMap == null) { + this.currentStateMap = this.stateMap[this.stateIndex]; // Find and extract the outside reference - var elements = [], ref = []; + var elements = [], + ref = []; var elem; - while(elem = this.currentStateMap.audioElements.pop()) - { + while (elem = this.currentStateMap.audioElements.pop()) { if (elem.type == "outside-reference") { ref.push(elem); - } - else { + } else { elements.push(elem); } } elements = elements.reverse(); - if (this.currentStateMap.randomiseOrder) - { - elements = randomiseOrder(elements); - } + if (this.currentStateMap.randomiseOrder) { + elements = randomiseOrder(elements); + } this.currentStateMap.audioElements = elements.concat(ref); - + this.currentStore = storage.testPages[this.stateIndex]; - if (this.currentStateMap.preTest != null) - { - this.currentStatePosition = 'pre'; - popup.initState(this.currentStateMap.preTest,storage.testPages[this.stateIndex].preTest); - } else { - this.currentStatePosition = 'test'; - } - interfaceContext.newPage(this.currentStateMap,storage.testPages[this.stateIndex]); - return; - } - switch(this.currentStatePosition) - { - case 'pre': - this.currentStatePosition = 'test'; - break; - case 'test': - this.currentStatePosition = 'post'; - // Save the data - this.testPageCompleted(); - if (this.currentStateMap.postTest == null) - { - this.advanceState(); - return; - } else { - popup.initState(this.currentStateMap.postTest,storage.testPages[this.stateIndex].postTest); - } - break; - case 'post': - this.stateIndex++; - this.currentStateMap = null; - this.advanceState(); - break; - }; - } - }; - - this.testPageCompleted = function() { - // Function called each time a test page has been completed - var storePoint = storage.testPages[this.stateIndex]; - // First get the test metric - - var metric = storePoint.XMLDOM.getElementsByTagName('metric')[0]; - if (audioEngineContext.metric.enableTestTimer) - { - var testTime = storePoint.parent.document.createElement('metricresult'); - testTime.id = 'testTime'; - testTime.textContent = audioEngineContext.timer.testDuration; - metric.appendChild(testTime); - } - - var audioObjects = audioEngineContext.audioObjects; - for (var ao of audioEngineContext.audioObjects) - { - ao.exportXMLDOM(); - } - for (var element of interfaceContext.commentQuestions) - { - element.exportXMLDOM(storePoint); - } - pageXMLSave(storePoint.XMLDOM, this.currentStateMap); + if (this.currentStateMap.preTest != null) { + this.currentStatePosition = 'pre'; + popup.initState(this.currentStateMap.preTest, storage.testPages[this.stateIndex].preTest); + } else { + this.currentStatePosition = 'test'; + } + interfaceContext.newPage(this.currentStateMap, storage.testPages[this.stateIndex]); + return; + } + switch (this.currentStatePosition) { + case 'pre': + this.currentStatePosition = 'test'; + break; + case 'test': + this.currentStatePosition = 'post'; + // Save the data + this.testPageCompleted(); + if (this.currentStateMap.postTest == null) { + this.advanceState(); + return; + } else { + popup.initState(this.currentStateMap.postTest, storage.testPages[this.stateIndex].postTest); + } + break; + case 'post': + this.stateIndex++; + this.currentStateMap = null; + this.advanceState(); + break; + }; + } + }; + + this.testPageCompleted = function () { + // Function called each time a test page has been completed + var storePoint = storage.testPages[this.stateIndex]; + // First get the test metric + + var metric = storePoint.XMLDOM.getElementsByTagName('metric')[0]; + if (audioEngineContext.metric.enableTestTimer) { + var testTime = storePoint.parent.document.createElement('metricresult'); + testTime.id = 'testTime'; + testTime.textContent = audioEngineContext.timer.testDuration; + metric.appendChild(testTime); + } + + var audioObjects = audioEngineContext.audioObjects; + for (var ao of audioEngineContext.audioObjects) { + ao.exportXMLDOM(); + } + for (var element of interfaceContext.commentQuestions) { + element.exportXMLDOM(storePoint); + } + pageXMLSave(storePoint.XMLDOM, this.currentStateMap); storePoint.complete(); - }; - - this.getCurrentTestPage = function() { - if (this.stateIndex >= 0 && this.stateIndex< this.stateMap.length) { + }; + + this.getCurrentTestPage = function () { + if (this.stateIndex >= 0 && this.stateIndex < this.stateMap.length) { return this.currentStateMap; } else { return null; } } - this.getCurrentTestPageStore = function() { - if (this.stateIndex >= 0 && this.stateIndex< this.stateMap.length) { + this.getCurrentTestPageStore = function () { + if (this.stateIndex >= 0 && this.stateIndex < this.stateMap.length) { return this.currentStore; } else { return null; @@ -1354,172 +1354,162 @@ } function AudioEngine(specification) { - - // Create two output paths, the main outputGain and fooGain. - // Output gain is default to 1 and any items for playback route here - // Foo gain is used for analysis to ensure paths get processed, but are not heard - // because web audio will optimise and any route which does not go to the destination gets ignored. - this.outputGain = audioContext.createGain(); - this.fooGain = audioContext.createGain(); - this.fooGain.gain = 0; - - // Use this to detect playback state: 0 - stopped, 1 - playing - this.status = 0; - - // Connect both gains to output - this.outputGain.connect(audioContext.destination); - this.fooGain.connect(audioContext.destination); - - // Create the timer Object - this.timer = new timer(); - // Create session metrics - this.metric = new sessionMetrics(this,specification); - - this.loopPlayback = false; + + // Create two output paths, the main outputGain and fooGain. + // Output gain is default to 1 and any items for playback route here + // Foo gain is used for analysis to ensure paths get processed, but are not heard + // because web audio will optimise and any route which does not go to the destination gets ignored. + this.outputGain = audioContext.createGain(); + this.fooGain = audioContext.createGain(); + this.fooGain.gain = 0; + + // Use this to detect playback state: 0 - stopped, 1 - playing + this.status = 0; + + // Connect both gains to output + this.outputGain.connect(audioContext.destination); + this.fooGain.connect(audioContext.destination); + + // Create the timer Object + this.timer = new timer(); + // Create session metrics + this.metric = new sessionMetrics(this, specification); + + this.loopPlayback = false; this.synchPlayback = false; this.pageSpecification = null; - - this.pageStore = null; - - // Create store for new audioObjects - this.audioObjects = []; - - this.buffers = []; - this.bufferObj = function() - { - this.url = null; - this.buffer = null; - this.xmlRequest = new XMLHttpRequest(); - this.xmlRequest.parent = this; - this.users = []; + + this.pageStore = null; + + // Create store for new audioObjects + this.audioObjects = []; + + this.buffers = []; + this.bufferObj = function () { + this.url = null; + this.buffer = null; + this.xmlRequest = new XMLHttpRequest(); + this.xmlRequest.parent = this; + this.users = []; this.progress = 0; this.status = 0; - this.ready = function() - { - if (this.status >= 2) - { + this.ready = function () { + if (this.status >= 2) { this.status = 3; } - for (var i=0; i<this.users.length; i++) - { - this.users[i].state = 1; - if (this.users[i].interfaceDOM != null) - { - this.users[i].bufferLoaded(this); - } - } - }; - this.getMedia = function(url) { - this.url = url; - this.xmlRequest.open('GET',this.url,true); - this.xmlRequest.responseType = 'arraybuffer'; - - var bufferObj = this; - - // Create callback to decode the data asynchronously - this.xmlRequest.onloadend = function() { + for (var i = 0; i < this.users.length; i++) { + this.users[i].state = 1; + if (this.users[i].interfaceDOM != null) { + this.users[i].bufferLoaded(this); + } + } + }; + this.getMedia = function (url) { + this.url = url; + this.xmlRequest.open('GET', this.url, true); + this.xmlRequest.responseType = 'arraybuffer'; + + var bufferObj = this; + + // Create callback to decode the data asynchronously + this.xmlRequest.onloadend = function () { // Use inbuilt WAVE decoder first - if (this.status == -1) {return;} + if (this.status == -1) { + return; + } var waveObj = new WAVE(); - audioContext.decodeAudioData(bufferObj.xmlRequest.response, function(decodedData) { - bufferObj.buffer = decodedData; - bufferObj.status = 2; - calculateLoudness(bufferObj,"I"); - }, function(e) { + audioContext.decodeAudioData(bufferObj.xmlRequest.response, function (decodedData) { + bufferObj.buffer = decodedData; + bufferObj.status = 2; + calculateLoudness(bufferObj, "I"); + }, function (e) { var waveObj = new WAVE(); - if (waveObj.open(bufferObj.xmlRequest.response) == 0) - { - bufferObj.buffer = audioContext.createBuffer(waveObj.num_channels,waveObj.num_samples,waveObj.sample_rate); - for (var c=0; c<waveObj.num_channels; c++) - { + if (waveObj.open(bufferObj.xmlRequest.response) == 0) { + bufferObj.buffer = audioContext.createBuffer(waveObj.num_channels, waveObj.num_samples, waveObj.sample_rate); + for (var c = 0; c < waveObj.num_channels; c++) { var buffer_ptr = bufferObj.buffer.getChannelData(c); - for (var n=0; n<waveObj.num_samples; n++) - { + for (var n = 0; n < waveObj.num_samples; n++) { buffer_ptr[n] = waveObj.decoded_data[c][n]; } } delete waveObj; } - if (bufferObj.buffer != undefined) - { + if (bufferObj.buffer != undefined) { bufferObj.status = 2; - calculateLoudness(bufferObj,"I"); + calculateLoudness(bufferObj, "I"); } }); - }; - + }; + // Create callback for any error in loading - this.xmlRequest.onerror = function() { + this.xmlRequest.onerror = function () { this.parent.status = -1; - for (var i=0; i<this.parent.users.length; i++) - { + for (var i = 0; i < this.parent.users.length; i++) { this.parent.users[i].state = -1; - if (this.parent.users[i].interfaceDOM != null) - { + if (this.parent.users[i].interfaceDOM != null) { this.parent.users[i].bufferLoaded(this); } } - interfaceContext.lightbox.post("Error","Could not load resource "+this.parent.url); + interfaceContext.lightbox.post("Error", "Could not load resource " + this.parent.url); } - - this.progress = 0; - this.progressCallback = function(event){ - if (event.lengthComputable) - { - this.parent.progress = event.loaded / event.total; - for (var i=0; i<this.parent.users.length; i++) - { - if(this.parent.users[i].interfaceDOM != null) - { - if (typeof this.parent.users[i].interfaceDOM.updateLoading === "function") - { - this.parent.users[i].interfaceDOM.updateLoading(this.parent.progress*100); - } - } - } - } - }; - this.xmlRequest.addEventListener("progress", this.progressCallback); + + this.progress = 0; + this.progressCallback = function (event) { + if (event.lengthComputable) { + this.parent.progress = event.loaded / event.total; + for (var i = 0; i < this.parent.users.length; i++) { + if (this.parent.users[i].interfaceDOM != null) { + if (typeof this.parent.users[i].interfaceDOM.updateLoading === "function") { + this.parent.users[i].interfaceDOM.updateLoading(this.parent.progress * 100); + } + } + } + } + }; + this.xmlRequest.addEventListener("progress", this.progressCallback); this.status = 1; - this.xmlRequest.send(); - }; - - this.registerAudioObject = function(audioObject) - { + this.xmlRequest.send(); + }; + + this.registerAudioObject = function (audioObject) { // Called by an audioObject to register to the buffer for use // First check if already in the register pool - for (var objects of this.users) - { - if (audioObject.id == objects.id){return 0;} + for (var objects of this.users) { + if (audioObject.id == objects.id) { + return 0; + } } this.users.push(audioObject); - if (this.status == 3 || this.status == -1) - { + if (this.status == 3 || this.status == -1) { // The buffer is already ready, trigger bufferLoaded audioObject.bufferLoaded(this); } }; - - this.copyBuffer = function(preSilenceTime,postSilenceTime) { + + this.copyBuffer = function (preSilenceTime, postSilenceTime) { // Copies the entire bufferObj. - if (preSilenceTime == undefined) {preSilenceTime = 0;} - 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; + if (preSilenceTime == undefined) { + preSilenceTime = 0; + } + 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); // Now we can use some efficient background copy schemes if we are just padding the end if (preSilenceSamples == 0 && typeof copybuffer.copyToChannel == "function") { - for (var c=0; c<this.buffer.numberOfChannels; c++) { - copybuffer.copyToChannel(this.buffer.getChannelData(c),c); + for (var c = 0; c < this.buffer.numberOfChannels; c++) { + copybuffer.copyToChannel(this.buffer.getChannelData(c), c); } } else { - for (var 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++) - dst[n+preSilenceSamples] = src[n]; + for (var n = 0; n < src.length; n++) + dst[n + preSilenceSamples] = src[n]; } } // Copy in the rest of the buffer information @@ -1527,30 +1517,30 @@ copybuffer.playbackGain = this.buffer.playbackGain; return copybuffer; } - - this.cropBuffer = function(startTime, stopTime) { + + this.cropBuffer = function (startTime, stopTime) { // Copy and return the cropped buffer - var start_sample = Math.floor(startTime*this.buffer.sampleRate); - var stop_sample = Math.floor(stopTime*this.buffer.sampleRate); + var start_sample = Math.floor(startTime * this.buffer.sampleRate); + var stop_sample = Math.floor(stopTime * this.buffer.sampleRate); var newLength = stop_sample - start_sample; var copybuffer = audioContext.createBuffer(this.buffer.numberOfChannels, newLength, this.buffer.sampleRate); // Now we can use some efficient background copy schemes if we are just padding the end - for (var c=0; c<this.buffer.numberOfChannels; c++) { + for (var c = 0; c < this.buffer.numberOfChannels; c++) { var buffer = this.buffer.getChannelData(c); - var sub_frame = buffer.subarray(start_sample,stop_sample); + var sub_frame = buffer.subarray(start_sample, stop_sample); if (typeof copybuffer.copyToChannel == "function") { - copybuffer.copyToChannel(sub_frame,c); + copybuffer.copyToChannel(sub_frame, c); } else { var dst = copybuffer.getChannelData(c); - for (var n=0; n<newLength; n++) - dst[n] = src[n+start_sample]; + for (var n = 0; n < newLength; n++) + dst[n] = src[n + start_sample]; } } return copybuffer; } - }; - - this.loadPageData = function(page) { + }; + + this.loadPageData = function (page) { // Load the URL from pages for (var element of page.audioElements) { var URL = page.hostURL + element.url; @@ -1568,217 +1558,207 @@ } } }; - - this.play = function(id) { - // Start the timer and set the audioEngine state to playing (1) - if (this.status == 0) { - // Check if all audioObjects are ready - this.bufferReady(id); - } - else - { - this.status = 1; - } - if (this.status== 1) { - this.timer.startTest(); - if (id == undefined) { - id = -1; - console.error('FATAL - Passed id was undefined - AudioEngineContext.play(id)'); - return; - } else { - interfaceContext.playhead.setTimePerPixel(this.audioObjects[id]); - } - if (this.synchPlayback && this.loopPlayback) { + + this.play = function (id) { + // Start the timer and set the audioEngine state to playing (1) + if (this.status == 0) { + // Check if all audioObjects are ready + this.bufferReady(id); + } else { + this.status = 1; + } + if (this.status == 1) { + this.timer.startTest(); + if (id == undefined) { + id = -1; + console.error('FATAL - Passed id was undefined - AudioEngineContext.play(id)'); + return; + } else { + interfaceContext.playhead.setTimePerPixel(this.audioObjects[id]); + } + if (this.synchPlayback && this.loopPlayback) { // Traditional looped playback - var setTime = audioContext.currentTime+specification.crossFade; - 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); - } - } + var setTime = audioContext.currentTime + specification.crossFade; + 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); + } + } } else { - var setTime = audioContext.currentTime+specification.crossFade; - for (var i=0; i<this.audioObjects.length; i++) - { - if (i != id) { - this.audioObjects[i].stop(setTime); - } else if (i == id) { - this.audioObjects[id].play(setTime); - } - } - } - interfaceContext.playhead.start(); - } - }; - - this.stop = function() { - // Send stop and reset command to all playback buffers - if (this.status == 1) { - var setTime = audioContext.currentTime+0.1; - for (var i=0; i<this.audioObjects.length; i++) - { - this.audioObjects[i].stop(setTime); - } - interfaceContext.playhead.stop(); - } - }; - - this.newTrack = function(element) { - // Pull data from given URL into new audio buffer - // 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; - audioObjectId = this.audioObjects.length; - this.audioObjects[audioObjectId] = new audioObject(audioObjectId); + var setTime = audioContext.currentTime + specification.crossFade; + for (var i = 0; i < this.audioObjects.length; i++) { + if (i != id) { + this.audioObjects[i].stop(setTime); + } else if (i == id) { + this.audioObjects[id].play(setTime); + } + } + } + interfaceContext.playhead.start(); + } + }; - // Check if audioObject buffer is currently stored by full URL - var URL = testState.currentStateMap.hostURL + element.url; - var buffer = null; - for (var i=0; i<this.buffers.length; i++) - { - if (URL == this.buffers[i].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.stop = function () { + // Send stop and reset command to all playback buffers + if (this.status == 1) { + var setTime = audioContext.currentTime + 0.1; + for (var i = 0; i < this.audioObjects.length; i++) { + this.audioObjects[i].stop(setTime); + } + interfaceContext.playhead.stop(); + } + }; + + this.newTrack = function (element) { + // Pull data from given URL into new audio buffer + // 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; + 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 = null; + for (var i = 0; i < this.buffers.length; i++) { + if (URL == this.buffers[i].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); - buffer.getMedia(URL); - } - this.audioObjects[audioObjectId].specification = element; - this.audioObjects[audioObjectId].url = URL; - // Obtain store node - var aeNodes = this.pageStore.XMLDOM.getElementsByTagName('audioelement'); - for (var i=0; i<aeNodes.length; i++) - { - if(aeNodes[i].getAttribute("ref") == element.id) - { - this.audioObjects[audioObjectId].storeDOM = aeNodes[i]; - break; - } - } + buffer.getMedia(URL); + } + this.audioObjects[audioObjectId].specification = element; + this.audioObjects[audioObjectId].url = URL; + // Obtain store node + var aeNodes = this.pageStore.XMLDOM.getElementsByTagName('audioelement'); + for (var i = 0; i < aeNodes.length; i++) { + if (aeNodes[i].getAttribute("ref") == element.id) { + this.audioObjects[audioObjectId].storeDOM = aeNodes[i]; + break; + } + } buffer.registerAudioObject(this.audioObjects[audioObjectId]); - return this.audioObjects[audioObjectId]; - }; - - this.newTestPage = function(audioHolderObject,store) { - this.pageStore = store; + return this.audioObjects[audioObjectId]; + }; + + this.newTestPage = function (audioHolderObject, store) { + this.pageStore = store; this.pageSpecification = audioHolderObject; - this.status = 0; - this.audioObjectsReady = false; - this.metric.reset(); - for (var i=0; i < this.buffers.length; i++) - { - this.buffers[i].users = []; - } - this.audioObjects = []; + this.status = 0; + this.audioObjectsReady = false; + this.metric.reset(); + for (var i = 0; i < this.buffers.length; i++) { + this.buffers[i].users = []; + } + this.audioObjects = []; this.timer = new timer(); this.loopPlayback = audioHolderObject.loop; this.synchPlayback = audioHolderObject.synchronous; - }; - - this.checkAllPlayed = function() { - arr = []; - for (var id=0; id<this.audioObjects.length; id++) { - if (this.audioObjects[id].metric.wasListenedTo == false) { - arr.push(this.audioObjects[id].id); - } - } - return arr; - }; - - this.checkAllReady = function() { - var ready = true; - for (var i=0; i<this.audioObjects.length; i++) { - if (this.audioObjects[i].state == 0) { - // Track not ready - console.log('WAIT -- audioObject '+i+' not ready yet!'); - ready = false; - }; - } - return ready; - }; - - this.setSynchronousLoop = function() { - // Pads the signals so they are all exactly the same length + }; + + this.checkAllPlayed = function () { + arr = []; + for (var id = 0; id < this.audioObjects.length; id++) { + if (this.audioObjects[id].metric.wasListenedTo == false) { + arr.push(this.audioObjects[id].id); + } + } + return arr; + }; + + this.checkAllReady = function () { + var ready = true; + for (var i = 0; i < this.audioObjects.length; i++) { + if (this.audioObjects[i].state == 0) { + // Track not ready + console.log('WAIT -- audioObject ' + i + ' not ready yet!'); + ready = false; + }; + } + return ready; + }; + + this.setSynchronousLoop = function () { + // Pads the signals so they are all exactly the same length // Get the length of the longest signal. - var length = 0; - var maxId; - for (var i=0; i<this.audioObjects.length; i++) - { - if (length < this.audioObjects[i].buffer.buffer.length) - { - length = this.audioObjects[i].buffer.buffer.length; - maxId = i; - } - } - // Extract the audio and zero-pad - for (var ao of this.audioObjects) - { + var length = 0; + var maxId; + for (var i = 0; i < this.audioObjects.length; i++) { + if (length < this.audioObjects[i].buffer.buffer.length) { + length = this.audioObjects[i].buffer.buffer.length; + maxId = i; + } + } + // Extract the audio and zero-pad + for (var ao of this.audioObjects) { var lengthDiff = length - ao.buffer.buffer.length; - ao.buffer = ao.buffer.copyBuffer(0,samplesToSeconds(lengthDiff,ao.buffer.buffer.sampleRate)); - } - }; - - this.bufferReady = function(id) { - if(this.checkAllReady()) { - if (this.synchPlayback){this.setSynchronousLoop();} + if (lengthDiff > 0) { + ao.buffer.buffer = ao.buffer.copyBuffer(0, samplesToSeconds(lengthDiff, ao.buffer.buffer.sampleRate)); + } + } + }; + + this.bufferReady = function (id) { + if (this.checkAllReady()) { + if (this.synchPlayback) { + this.setSynchronousLoop(); + } this.status = 1; return true; } return false; } - - this.exportXML = function() - { - + + this.exportXML = function () { + }; - + } function audioObject(id) { - // The main buffer object with common control nodes to the AudioEngine - - 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. - this.metric = new metricTracker(this); - this.storeDOM = null; - - // Bindings for GUI - this.interfaceDOM = null; - this.commentDOM = null; - - // Create a buffer and external gain control to allow internal patching of effects and volume leveling. - this.bufferNode = undefined; - this.outputGain = audioContext.createGain(); - - this.onplayGain = 1.0; - - // Connect buffer to the audio graph - this.outputGain.connect(audioEngineContext.outputGain); - - // the audiobuffer is not designed for multi-start playback - // When stopeed, the buffer node is deleted and recreated with the stored buffer. - this.buffer; - - this.bufferLoaded = function(callee) - { - // Called by the associated buffer when it has finished loading, will then 'bind' the buffer to the - // audioObject and trigger the interfaceDOM.enable() function for user feedback + // The main buffer object with common control nodes to the AudioEngine + + 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. + this.metric = new metricTracker(this); + this.storeDOM = null; + + // Bindings for GUI + this.interfaceDOM = null; + this.commentDOM = null; + + // Create a buffer and external gain control to allow internal patching of effects and volume leveling. + this.bufferNode = undefined; + this.outputGain = audioContext.createGain(); + + this.onplayGain = 1.0; + + // Connect buffer to the audio graph + this.outputGain.connect(audioEngineContext.outputGain); + + // the audiobuffer is not designed for multi-start playback + // When stopeed, the buffer node is deleted and recreated with the stored buffer. + this.buffer; + + this.bufferLoaded = function (callee) { + // Called by the associated buffer when it has finished loading, will then 'bind' the buffer to the + // audioObject and trigger the interfaceDOM.enable() function for user feedback if (callee.status == -1) { // ERROR this.state = -1; - if (this.interfaceDOM != null) {this.interfaceDOM.error();} + if (this.interfaceDOM != null) { + this.interfaceDOM.error(); + } this.buffer = callee; return; } @@ -1788,436 +1768,414 @@ var startTime = this.specification.startTime; var stopTime = this.specification.stopTime; var copybuffer = new callee.constructor(); - if (isFinite(startTime) || isFinite(stopTime)) { - copybuffer.buffer = callee.cropBuffer(startTime,stopTime); + + copybuffer.buffer = callee.cropBuffer(startTime || 0, stopTime || callee.buffer.duration); + if (preSilenceTime != 0 || postSilenceTime != 0) { + copybuffer.buffer = copybuffer.copyBuffer(preSilenceTime, postSilenceTime); } - if (preSilenceTime != 0 || postSilenceTime != 0) { - if (copybuffer.buffer == undefined) { - copybuffer.buffer = callee.copyBuffer(preSilenceTime,postSilenceTime); - } else { - copybuffer.buffer = copybuffer.copyBuffer(preSilenceTime,postSilenceTime); - } + + copybuffer.lufs = callee.buffer.lufs; + this.buffer = copybuffer; + + var targetLUFS = this.specification.parent.loudness || specification.loudness; + if (typeof targetLUFS === "number" && isFinite(targetLUFS)) { + this.buffer.buffer.playbackGain = decibelToLinear(targetLUFS - this.buffer.buffer.lufs); + } else { + this.buffer.buffer.playbackGain = 1.0; } - - var targetLUFS = this.specification.parent.loudness || specification.loudness; - if (typeof targetLUFS === "number" && isFinite(targetLUFS)) - { - this.buffer.buffer.playbackGain = decibelToLinear(targetLUFS - this.buffer.buffer.lufs); - } else { - this.buffer.buffer.playbackGain = 1.0; - } - if (this.interfaceDOM != null) { - this.interfaceDOM.enable(); - } - this.onplayGain = decibelToLinear(this.specification.gain)*(this.buffer.buffer.playbackGain||1.0); - this.storeDOM.setAttribute('playGain',linearToDecibel(this.onplayGain)); + if (this.interfaceDOM != null) { + this.interfaceDOM.enable(); + } + this.onplayGain = decibelToLinear(this.specification.gain) * (this.buffer.buffer.playbackGain || 1.0); + this.storeDOM.setAttribute('playGain', linearToDecibel(this.onplayGain)); this.state = 1; audioEngineContext.bufferReady(id); - }; - - this.bindInterface = function(interfaceObject) - { - this.interfaceDOM = interfaceObject; - this.metric.initialise(interfaceObject.getValue()); - if (this.state == 1) - { - this.interfaceDOM.enable(); - } else if (this.state == -1) { + }; + + this.bindInterface = function (interfaceObject) { + this.interfaceDOM = interfaceObject; + this.metric.initialise(interfaceObject.getValue()); + if (this.state == 1) { + this.interfaceDOM.enable(); + } else if (this.state == -1) { // ERROR this.interfaceDOM.error(); return; } - this.storeDOM.setAttribute('presentedId',interfaceObject.getPresentedId()); - }; - - this.loopStart = function(setTime) { - this.outputGain.gain.linearRampToValueAtTime(this.onplayGain,setTime); - this.metric.startListening(audioEngineContext.timer.getTestTime()); + this.storeDOM.setAttribute('presentedId', interfaceObject.getPresentedId()); + }; + + this.loopStart = function (setTime) { + this.outputGain.gain.linearRampToValueAtTime(this.onplayGain, setTime); + this.metric.startListening(audioEngineContext.timer.getTestTime()); this.interfaceDOM.startPlayback(); - }; - - this.loopStop = function(setTime) { - if (this.outputGain.gain.value != 0.0) { - this.outputGain.gain.linearRampToValueAtTime(0.0,setTime); - this.metric.stopListening(audioEngineContext.timer.getTestTime()); - } + }; + + this.loopStop = function (setTime) { + if (this.outputGain.gain.value != 0.0) { + this.outputGain.gain.linearRampToValueAtTime(0.0, setTime); + this.metric.stopListening(audioEngineContext.timer.getTestTime()); + } this.interfaceDOM.stopPlayback(); - }; - - this.play = function(startTime) { - if (this.bufferNode == undefined && this.buffer.buffer != undefined) { - this.bufferNode = audioContext.createBufferSource(); - this.bufferNode.owner = this; - this.bufferNode.connect(this.outputGain); - this.bufferNode.buffer = this.buffer.buffer; - this.bufferNode.loop = audioEngineContext.loopPlayback; - 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()); + }; + + this.play = function (startTime) { + if (this.bufferNode == undefined && this.buffer.buffer != undefined) { + this.bufferNode = audioContext.createBufferSource(); + this.bufferNode.owner = this; + this.bufferNode.connect(this.outputGain); + this.bufferNode.buffer = this.buffer.buffer; + this.bufferNode.loop = audioEngineContext.loopPlayback; + 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) { - event.currentTarget.owner.stop(audioContext.currentTime+1); + event.currentTarget.owner.stop(audioContext.currentTime + 1); } - }; - if (!audioEngineContext.loopPlayback || !audioEngineContext.synchPlayback) { - this.metric.startListening(audioEngineContext.timer.getTestTime()); - this.outputGain.gain.setValueAtTime(this.onplayGain,0.0); + }; + if (!audioEngineContext.loopPlayback || !audioEngineContext.synchPlayback) { + this.metric.startListening(audioEngineContext.timer.getTestTime()); + this.outputGain.gain.setValueAtTime(this.onplayGain, 0.0); this.interfaceDOM.startPlayback(); - } else { - this.outputGain.gain.setValueAtTime(0.0,startTime); + } else { + this.outputGain.gain.setValueAtTime(0.0, startTime); } - this.bufferNode.start(startTime,this.specification.startTime || 0, this.specification.stopTime-this.specification.startTime || this.buffer.buffer.duration); + if (audioEngineContext.loopPlayback) { + this.bufferNode.loopStart = this.specification.startTime || 0; + this.bufferNode.loopEnd = this.specification.stopTime - this.specification.startTime || this.buffer.buffer.duration; + this.bufferNode.start(startTime); + } else { + this.bufferNode.start(startTime, this.specification.startTime || 0, this.specification.stopTime - this.specification.startTime || this.buffer.buffer.duration); + } this.bufferNode.playbackStartTime = audioEngineContext.timer.getTestTime(); - } - }; - - this.stop = function(stopTime) { + } + }; + + this.stop = function (stopTime) { this.outputGain.gain.cancelScheduledValues(audioContext.currentTime); - if (this.bufferNode != undefined) - { - this.metric.stopListening(audioEngineContext.timer.getTestTime(),this.getCurrentPosition()); - this.bufferNode.stop(stopTime); - this.bufferNode = undefined; - } + if (this.bufferNode != undefined) { + this.metric.stopListening(audioEngineContext.timer.getTestTime(), this.getCurrentPosition()); + this.bufferNode.stop(stopTime); + this.bufferNode = undefined; + } this.outputGain.gain.value = 0.0; this.interfaceDOM.stopPlayback(); - }; - - this.getCurrentPosition = function() { - var time = audioEngineContext.timer.getTestTime(); - if (this.bufferNode != undefined) { - var position = (time - this.bufferNode.playbackStartTime)%this.buffer.buffer.duration; - if (isNaN(position)){return 0;} + }; + + this.getCurrentPosition = function () { + var time = audioEngineContext.timer.getTestTime(); + if (this.bufferNode != undefined) { + var position = (time - this.bufferNode.playbackStartTime) % this.buffer.buffer.duration; + if (isNaN(position)) { + return 0; + } return position; - } else { - return 0; - } - }; - - this.exportXMLDOM = function() { - var file = storage.document.createElement('file'); - file.setAttribute('sampleRate',this.buffer.buffer.sampleRate); - file.setAttribute('channels',this.buffer.buffer.numberOfChannels); - file.setAttribute('sampleCount',this.buffer.buffer.length); - file.setAttribute('duration',this.buffer.buffer.duration); - this.storeDOM.appendChild(file); - if (this.specification.type != 'outside-reference') { - var interfaceXML = this.interfaceDOM.exportXMLDOM(this); - if (interfaceXML != null) - { - if (interfaceXML.length == undefined) { - this.storeDOM.appendChild(interfaceXML); - } else { - for (var i=0; i<interfaceXML.length; i++) - { - this.storeDOM.appendChild(interfaceXML[i]); - } - } - } - if (this.commentDOM != null) { - this.storeDOM.appendChild(this.commentDOM.exportXMLDOM(this)); - } - } - var nodes = this.metric.exportXMLDOM(); - var mroot = this.storeDOM.getElementsByTagName('metric')[0]; - for (var i=0; i<nodes.length; i++) - { - mroot.appendChild(nodes[i]); - } - }; + } else { + return 0; + } + }; + + this.exportXMLDOM = function () { + var file = storage.document.createElement('file'); + file.setAttribute('sampleRate', this.buffer.buffer.sampleRate); + file.setAttribute('channels', this.buffer.buffer.numberOfChannels); + file.setAttribute('sampleCount', this.buffer.buffer.length); + file.setAttribute('duration', this.buffer.buffer.duration); + this.storeDOM.appendChild(file); + if (this.specification.type != 'outside-reference') { + var interfaceXML = this.interfaceDOM.exportXMLDOM(this); + if (interfaceXML != null) { + if (interfaceXML.length == undefined) { + this.storeDOM.appendChild(interfaceXML); + } else { + for (var i = 0; i < interfaceXML.length; i++) { + this.storeDOM.appendChild(interfaceXML[i]); + } + } + } + if (this.commentDOM != null) { + this.storeDOM.appendChild(this.commentDOM.exportXMLDOM(this)); + } + } + var nodes = this.metric.exportXMLDOM(); + var mroot = this.storeDOM.getElementsByTagName('metric')[0]; + for (var i = 0; i < nodes.length; i++) { + mroot.appendChild(nodes[i]); + } + }; } -function timer() -{ - /* Timer object used in audioEngine to keep track of session timings - * Uses the timer of the web audio API, so sample resolution - */ - this.testStarted = false; - this.testStartTime = 0; - this.testDuration = 0; - this.minimumTestTime = 0; // No minimum test time - this.startTest = function() - { - if (this.testStarted == false) - { - this.testStartTime = audioContext.currentTime; - this.testStarted = true; - this.updateTestTime(); - audioEngineContext.metric.initialiseTest(); - } - }; - this.stopTest = function() - { - if (this.testStarted) - { - this.testDuration = this.getTestTime(); - this.testStarted = false; - } else { - console.log('ERR: Test tried to end before beginning'); - } - }; - this.updateTestTime = function() - { - if (this.testStarted) - { - this.testDuration = audioContext.currentTime - this.testStartTime; - } - }; - this.getTestTime = function() - { - this.updateTestTime(); - return this.testDuration; - }; +function timer() { + /* Timer object used in audioEngine to keep track of session timings + * Uses the timer of the web audio API, so sample resolution + */ + this.testStarted = false; + this.testStartTime = 0; + this.testDuration = 0; + this.minimumTestTime = 0; // No minimum test time + this.startTest = function () { + if (this.testStarted == false) { + this.testStartTime = audioContext.currentTime; + this.testStarted = true; + this.updateTestTime(); + audioEngineContext.metric.initialiseTest(); + } + }; + this.stopTest = function () { + if (this.testStarted) { + this.testDuration = this.getTestTime(); + this.testStarted = false; + } else { + console.log('ERR: Test tried to end before beginning'); + } + }; + this.updateTestTime = function () { + if (this.testStarted) { + this.testDuration = audioContext.currentTime - this.testStartTime; + } + }; + this.getTestTime = function () { + this.updateTestTime(); + return this.testDuration; + }; } -function sessionMetrics(engine,specification) -{ - /* Used by audioEngine to link to audioObjects to minimise the timer call timers; - */ - this.engine = engine; - this.lastClicked = -1; - this.data = -1; - this.reset = function() { - this.lastClicked = -1; - this.data = -1; - }; - - this.enableElementInitialPosition = false; - this.enableElementListenTracker = false; - this.enableElementTimer = false; - this.enableElementTracker = false; - this.enableFlagListenedTo = false; - this.enableFlagMoved = false; - this.enableTestTimer = false; - // Obtain the metrics enabled - for (var i=0; i<specification.metrics.enabled.length; i++) - { - var node = specification.metrics.enabled[i]; - switch(node) - { - case 'testTimer': - this.enableTestTimer = true; - break; - case 'elementTimer': - this.enableElementTimer = true; - break; - case 'elementTracker': - this.enableElementTracker = true; - break; - case 'elementListenTracker': - this.enableElementListenTracker = true; - break; - case 'elementInitialPosition': - this.enableElementInitialPosition = true; - break; - case 'elementFlagListenedTo': - this.enableFlagListenedTo = true; - break; - case 'elementFlagMoved': - this.enableFlagMoved = true; - break; - case 'elementFlagComments': - this.enableFlagComments = true; - break; - } - } - this.initialiseTest = function(){}; +function sessionMetrics(engine, specification) { + /* Used by audioEngine to link to audioObjects to minimise the timer call timers; + */ + this.engine = engine; + this.lastClicked = -1; + this.data = -1; + this.reset = function () { + this.lastClicked = -1; + this.data = -1; + }; + + this.enableElementInitialPosition = false; + this.enableElementListenTracker = false; + this.enableElementTimer = false; + this.enableElementTracker = false; + this.enableFlagListenedTo = false; + this.enableFlagMoved = false; + this.enableTestTimer = false; + // Obtain the metrics enabled + for (var i = 0; i < specification.metrics.enabled.length; i++) { + var node = specification.metrics.enabled[i]; + switch (node) { + case 'testTimer': + this.enableTestTimer = true; + break; + case 'elementTimer': + this.enableElementTimer = true; + break; + case 'elementTracker': + this.enableElementTracker = true; + break; + case 'elementListenTracker': + this.enableElementListenTracker = true; + break; + case 'elementInitialPosition': + this.enableElementInitialPosition = true; + break; + case 'elementFlagListenedTo': + this.enableFlagListenedTo = true; + break; + case 'elementFlagMoved': + this.enableFlagMoved = true; + break; + case 'elementFlagComments': + this.enableFlagComments = true; + break; + } + } + this.initialiseTest = function () {}; } -function metricTracker(caller) -{ - /* Custom object to track and collect metric data - * Used only inside the audioObjects object. - */ - - this.listenedTimer = 0; - this.listenStart = 0; - this.listenHold = false; - this.initialPosition = -1; - this.movementTracker = []; - this.listenTracker =[]; - this.wasListenedTo = false; - this.wasMoved = false; - this.hasComments = false; - this.parent = caller; - - this.initialise = function(position) - { - if (this.initialPosition == -1) { - this.initialPosition = position; - this.moved(0,position); - } - }; - - this.moved = function(time,position) - { - if (time > 0) {this.wasMoved = true;} - this.movementTracker[this.movementTracker.length] = [time, position]; - }; - - this.startListening = function(time) - { - if (this.listenHold == false) - { - this.wasListenedTo = true; - this.listenStart = time; - this.listenHold = true; - - var evnt = document.createElement('event'); - var testTime = document.createElement('testTime'); - testTime.setAttribute('start',time); - var bufferTime = document.createElement('bufferTime'); - bufferTime.setAttribute('start',this.parent.getCurrentPosition()); - evnt.appendChild(testTime); - evnt.appendChild(bufferTime); - this.listenTracker.push(evnt); - - console.log('slider ' + this.parent.id + ' played (' + time + ')'); // DEBUG/SAFETY: show played slider id - } - }; - - this.stopListening = function(time,bufferStopTime) - { - if (this.listenHold == true) - { - var diff = time - this.listenStart; - this.listenedTimer += (diff); - this.listenStart = 0; - this.listenHold = false; - - var evnt = this.listenTracker[this.listenTracker.length-1]; - var testTime = evnt.getElementsByTagName('testTime')[0]; - var bufferTime = evnt.getElementsByTagName('bufferTime')[0]; - testTime.setAttribute('stop',time); - if (bufferStopTime == undefined) { - bufferTime.setAttribute('stop',this.parent.getCurrentPosition()); - } else { - bufferTime.setAttribute('stop',bufferStopTime); - } - console.log('slider ' + this.parent.id + ' played for (' + diff + ')'); // DEBUG/SAFETY: show played slider id - } - }; - - this.exportXMLDOM = function() { - var storeDOM = []; - if (audioEngineContext.metric.enableElementTimer) { - var mElementTimer = storage.document.createElement('metricresult'); - mElementTimer.setAttribute('name','enableElementTimer'); - mElementTimer.textContent = this.listenedTimer; - storeDOM.push(mElementTimer); - } - if (audioEngineContext.metric.enableElementTracker) { - 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) { - 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) { - var elementInitial = storage.document.createElement('metricresult'); - elementInitial.setAttribute('name','elementInitialPosition'); - elementInitial.textContent = this.initialPosition; - storeDOM.push(elementInitial); - } - if (audioEngineContext.metric.enableFlagListenedTo) { - var flagListenedTo = storage.document.createElement('metricresult'); - flagListenedTo.setAttribute('name','elementFlagListenedTo'); - flagListenedTo.textContent = this.wasListenedTo; - storeDOM.push(flagListenedTo); - } - if (audioEngineContext.metric.enableFlagMoved) { - var flagMoved = storage.document.createElement('metricresult'); - flagMoved.setAttribute('name','elementFlagMoved'); - flagMoved.textContent = this.wasMoved; - storeDOM.push(flagMoved); - } - if (audioEngineContext.metric.enableFlagComments) { - 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 storeDOM; - }; +function metricTracker(caller) { + /* Custom object to track and collect metric data + * Used only inside the audioObjects object. + */ + + this.listenedTimer = 0; + this.listenStart = 0; + this.listenHold = false; + this.initialPosition = -1; + this.movementTracker = []; + this.listenTracker = []; + this.wasListenedTo = false; + this.wasMoved = false; + this.hasComments = false; + this.parent = caller; + + this.initialise = function (position) { + if (this.initialPosition == -1) { + this.initialPosition = position; + this.moved(0, position); + } + }; + + this.moved = function (time, position) { + if (time > 0) { + this.wasMoved = true; + } + this.movementTracker[this.movementTracker.length] = [time, position]; + }; + + this.startListening = function (time) { + if (this.listenHold == false) { + this.wasListenedTo = true; + this.listenStart = time; + this.listenHold = true; + + var evnt = document.createElement('event'); + var testTime = document.createElement('testTime'); + testTime.setAttribute('start', time); + var bufferTime = document.createElement('bufferTime'); + bufferTime.setAttribute('start', this.parent.getCurrentPosition()); + evnt.appendChild(testTime); + evnt.appendChild(bufferTime); + this.listenTracker.push(evnt); + + console.log('slider ' + this.parent.id + ' played (' + time + ')'); // DEBUG/SAFETY: show played slider id + } + }; + + this.stopListening = function (time, bufferStopTime) { + if (this.listenHold == true) { + var diff = time - this.listenStart; + this.listenedTimer += (diff); + this.listenStart = 0; + this.listenHold = false; + + var evnt = this.listenTracker[this.listenTracker.length - 1]; + var testTime = evnt.getElementsByTagName('testTime')[0]; + var bufferTime = evnt.getElementsByTagName('bufferTime')[0]; + testTime.setAttribute('stop', time); + if (bufferStopTime == undefined) { + bufferTime.setAttribute('stop', this.parent.getCurrentPosition()); + } else { + bufferTime.setAttribute('stop', bufferStopTime); + } + console.log('slider ' + this.parent.id + ' played for (' + diff + ')'); // DEBUG/SAFETY: show played slider id + } + }; + + this.exportXMLDOM = function () { + var storeDOM = []; + if (audioEngineContext.metric.enableElementTimer) { + var mElementTimer = storage.document.createElement('metricresult'); + mElementTimer.setAttribute('name', 'enableElementTimer'); + mElementTimer.textContent = this.listenedTimer; + storeDOM.push(mElementTimer); + } + if (audioEngineContext.metric.enableElementTracker) { + 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) { + 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) { + var elementInitial = storage.document.createElement('metricresult'); + elementInitial.setAttribute('name', 'elementInitialPosition'); + elementInitial.textContent = this.initialPosition; + storeDOM.push(elementInitial); + } + if (audioEngineContext.metric.enableFlagListenedTo) { + var flagListenedTo = storage.document.createElement('metricresult'); + flagListenedTo.setAttribute('name', 'elementFlagListenedTo'); + flagListenedTo.textContent = this.wasListenedTo; + storeDOM.push(flagListenedTo); + } + if (audioEngineContext.metric.enableFlagMoved) { + var flagMoved = storage.document.createElement('metricresult'); + flagMoved.setAttribute('name', 'elementFlagMoved'); + flagMoved.textContent = this.wasMoved; + storeDOM.push(flagMoved); + } + if (audioEngineContext.metric.enableFlagComments) { + 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 storeDOM; + }; } - + function Interface(specificationObject) { - // This handles the bindings between the interface and the audioEngineContext; - this.specification = specificationObject; - this.insertPoint = document.getElementById("topLevelBody"); - - this.newPage = function(audioHolderObject,store) - { - audioEngineContext.newTestPage(audioHolderObject,store); - interfaceContext.commentBoxes.deleteCommentBoxes(); - interfaceContext.deleteCommentQuestions(); - loadTest(audioHolderObject,store); - }; - - // Bounded by interface!! - // Interface object MUST have an exportXMLDOM method which returns the various DOM levels - // For example, APE returns the slider position normalised in a <value> tag. - this.interfaceObjects = []; - this.interfaceObject = function(){}; - - this.resizeWindow = function(event) - { - popup.resize(event); + // This handles the bindings between the interface and the audioEngineContext; + this.specification = specificationObject; + this.insertPoint = document.getElementById("topLevelBody"); + + this.newPage = function (audioHolderObject, store) { + audioEngineContext.newTestPage(audioHolderObject, store); + interfaceContext.commentBoxes.deleteCommentBoxes(); + interfaceContext.deleteCommentQuestions(); + loadTest(audioHolderObject, store); + }; + + // Bounded by interface!! + // Interface object MUST have an exportXMLDOM method which returns the various DOM levels + // For example, APE returns the slider position normalised in a <value> tag. + this.interfaceObjects = []; + this.interfaceObject = function () {}; + + this.resizeWindow = function (event) { + popup.resize(event); this.volume.resize(); this.lightbox.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) - { - console.log("Warning - Interface does not have Resize option"); - console.log(err); - } - }; - - this.returnNavigator = function() - { - var node = storage.document.createElement("navigator"); - var platform = storage.document.createElement("platform"); - platform.textContent = navigator.platform; - var vendor = storage.document.createElement("vendor"); - vendor.textContent = navigator.vendor; - var userAgent = storage.document.createElement("uagent"); - userAgent.textContent = navigator.userAgent; + 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) { + console.log("Warning - Interface does not have Resize option"); + console.log(err); + } + }; + + this.returnNavigator = function () { + var node = storage.document.createElement("navigator"); + var platform = storage.document.createElement("platform"); + platform.textContent = navigator.platform; + var vendor = storage.document.createElement("vendor"); + vendor.textContent = navigator.vendor; + var userAgent = storage.document.createElement("uagent"); + userAgent.textContent = navigator.userAgent; var screen = storage.document.createElement("window"); - screen.setAttribute('innerWidth',window.innerWidth); - screen.setAttribute('innerHeight',window.innerHeight); - node.appendChild(platform); - node.appendChild(vendor); - node.appendChild(userAgent); + screen.setAttribute('innerWidth', window.innerWidth); + screen.setAttribute('innerHeight', window.innerHeight); + node.appendChild(platform); + node.appendChild(vendor); + node.appendChild(userAgent); node.appendChild(screen); - return node; - }; - - this.returnDateNode = function() - { + return node; + }; + + this.returnDateNode = function () { // Create an XML Node for the Date and Time a test was conducted // Structure is // <datetime> @@ -2228,27 +2186,27 @@ var hold = storage.document.createElement("datetime"); var date = storage.document.createElement("date"); var time = storage.document.createElement("time"); - date.setAttribute('year',dateTime.getFullYear()); - date.setAttribute('month',dateTime.getMonth()+1); - date.setAttribute('day',dateTime.getDate()); - time.setAttribute('hour',dateTime.getHours()); - time.setAttribute('minute',dateTime.getMinutes()); - time.setAttribute('secs',dateTime.getSeconds()); - + date.setAttribute('year', dateTime.getFullYear()); + date.setAttribute('month', dateTime.getMonth() + 1); + date.setAttribute('day', dateTime.getDate()); + time.setAttribute('hour', dateTime.getHours()); + time.setAttribute('minute', dateTime.getMinutes()); + time.setAttribute('secs', dateTime.getSeconds()); + hold.appendChild(date); hold.appendChild(time); return hold; } - + this.lightbox = { parent: this, root: document.createElement("div"), content: document.createElement("div"), accept: document.createElement("button"), blanker: document.createElement("div"), - post: function(type,message) { - switch(type) { + post: function (type, message) { + switch (type) { case "Error": this.content.className = "lightbox-error"; break; @@ -2264,25 +2222,25 @@ this.content.appendChild(msg); this.show(); }, - show: function() { + show: function () { this.root.style.visibility = "visible"; this.blanker.style.visibility = "visible"; }, - clear: function() { + clear: function () { this.root.style.visibility = ""; this.blanker.style.visibility = ""; this.content.textContent = ""; }, - handleEvent: function(event) { + handleEvent: function (event) { if (event.currentTarget == this.accept) { this.clear(); } }, - resize: function(event) { - this.root.style.left = (window.innerWidth/2)-250 + 'px'; + 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); this.lightbox.root.className = "popupHolder"; @@ -2291,16 +2249,16 @@ this.lightbox.accept.style.bottom = "10px"; this.lightbox.accept.textContent = "OK"; this.lightbox.accept.style.left = "237.5px"; - this.lightbox.accept.addEventListener("click",this.lightbox); + this.lightbox.accept.addEventListener("click", this.lightbox); this.lightbox.blanker.className = "testHalt"; this.lightbox.blanker.id = "lightbox-blanker"; document.getElementsByTagName("body")[0].appendChild(this.lightbox.root); document.getElementsByTagName("body")[0].appendChild(this.lightbox.blanker); - - this.commentBoxes = new function() { + + this.commentBoxes = new function () { this.boxes = []; this.injectPoint = null; - this.elementCommentBox = function(audioObject) { + this.elementCommentBox = function (audioObject) { var element = audioObject.specification; this.audioObject = audioObject; this.id = audioObject.id; @@ -2308,15 +2266,15 @@ // Create document objects to hold the comment boxes this.trackComment = document.createElement('div'); this.trackComment.className = 'comment-div'; - this.trackComment.id = 'comment-div-'+audioObject.id; + this.trackComment.id = 'comment-div-' + audioObject.id; // Create a string next to each comment asking for a comment this.trackString = document.createElement('span'); - this.trackString.innerHTML = audioHolderObject.commentBoxPrefix+' '+audioObject.interfaceDOM.getPresentedId(); + this.trackString.innerHTML = audioHolderObject.commentBoxPrefix + ' ' + audioObject.interfaceDOM.getPresentedId(); // Create the HTML5 comment box 'textarea' this.trackCommentBox = document.createElement('textarea'); this.trackCommentBox.rows = '4'; this.trackCommentBox.cols = '100'; - this.trackCommentBox.name = 'trackComment'+audioObject.id; + this.trackCommentBox.name = 'trackComment' + audioObject.id; this.trackCommentBox.className = 'trackComment'; var br = document.createElement('br'); // Add to the holder. @@ -2324,52 +2282,52 @@ this.trackComment.appendChild(br); this.trackComment.appendChild(this.trackCommentBox); - this.exportXMLDOM = function() { + this.exportXMLDOM = function () { var root = document.createElement('comment'); var question = document.createElement('question'); question.textContent = this.trackString.textContent; var response = document.createElement('response'); response.textContent = this.trackCommentBox.value; - console.log("Comment frag-"+this.id+": "+response.textContent); + console.log("Comment frag-" + this.id + ": " + response.textContent); root.appendChild(question); root.appendChild(response); return root; }; - this.resize = function() - { - var boxwidth = (window.innerWidth-100)/2; - if (boxwidth >= 600) - { + this.resize = function () { + var boxwidth = (window.innerWidth - 100) / 2; + if (boxwidth >= 600) { boxwidth = 600; - } - else if (boxwidth < 400) - { + } else if (boxwidth < 400) { boxwidth = 400; } - this.trackComment.style.width = boxwidth+"px"; - this.trackCommentBox.style.width = boxwidth-6+"px"; + this.trackComment.style.width = boxwidth + "px"; + this.trackCommentBox.style.width = boxwidth - 6 + "px"; }; this.resize(); }; - this.createCommentBox = function(audioObject) { + this.createCommentBox = function (audioObject) { var node = new this.elementCommentBox(audioObject); this.boxes.push(node); audioObject.commentDOM = node; return node; }; - this.sortCommentBoxes = function() { - this.boxes.sort(function(a,b){return a.id - b.id;}); + this.sortCommentBoxes = function () { + this.boxes.sort(function (a, b) { + return a.id - b.id; + }); }; - this.showCommentBoxes = function(inject, sort) { + this.showCommentBoxes = function (inject, sort) { this.injectPoint = inject; - if (sort) {this.sortCommentBoxes();} + if (sort) { + this.sortCommentBoxes(); + } for (var box of this.boxes) { inject.appendChild(box.trackComment); } }; - this.deleteCommentBoxes = function() { + this.deleteCommentBoxes = function () { if (this.injectPoint != null) { for (var box of this.boxes) { this.injectPoint.removeChild(box.trackComment); @@ -2379,34 +2337,34 @@ this.boxes = []; }; } - - this.commentQuestions = []; - - this.commentBox = function(commentQuestion) { - this.specification = commentQuestion; - // Create document objects to hold the comment boxes - this.holder = document.createElement('div'); - this.holder.className = 'comment-div'; - // Create a string next to each comment asking for a comment - this.string = document.createElement('span'); - this.string.innerHTML = commentQuestion.statement; - // Create the HTML5 comment box 'textarea' - this.textArea = document.createElement('textarea'); - this.textArea.rows = '4'; - this.textArea.cols = '100'; - this.textArea.className = 'trackComment'; - var br = document.createElement('br'); - // Add to the holder. - this.holder.appendChild(this.string); - this.holder.appendChild(br); - this.holder.appendChild(this.textArea); - - this.exportXMLDOM = function(storePoint) { - var root = storePoint.parent.document.createElement('comment'); - root.id = this.specification.id; - root.setAttribute('type',this.specification.type); - console.log("Question: "+this.string.textContent); - console.log("Response: "+root.textContent); + + this.commentQuestions = []; + + this.commentBox = function (commentQuestion) { + this.specification = commentQuestion; + // Create document objects to hold the comment boxes + this.holder = document.createElement('div'); + this.holder.className = 'comment-div'; + // Create a string next to each comment asking for a comment + this.string = document.createElement('span'); + this.string.innerHTML = commentQuestion.statement; + // Create the HTML5 comment box 'textarea' + this.textArea = document.createElement('textarea'); + this.textArea.rows = '4'; + this.textArea.cols = '100'; + this.textArea.className = 'trackComment'; + var br = document.createElement('br'); + // Add to the holder. + this.holder.appendChild(this.string); + this.holder.appendChild(br); + this.holder.appendChild(this.textArea); + + this.exportXMLDOM = function (storePoint) { + var root = storePoint.parent.document.createElement('comment'); + root.id = this.specification.id; + root.setAttribute('type', this.specification.type); + console.log("Question: " + this.string.textContent); + console.log("Response: " + root.textContent); var question = storePoint.parent.document.createElement('question'); question.textContent = this.string.textContent; var response = storePoint.parent.document.createElement('response'); @@ -2414,421 +2372,394 @@ root.appendChild(question); root.appendChild(response); storePoint.XMLDOM.appendChild(root); - return root; - }; - this.resize = function() - { - var boxwidth = (window.innerWidth-100)/2; - if (boxwidth >= 600) - { - boxwidth = 600; - } - else if (boxwidth < 400) - { - boxwidth = 400; - } - this.holder.style.width = boxwidth+"px"; - this.textArea.style.width = boxwidth-6+"px"; - }; - this.resize(); - }; - - this.radioBox = function(commentQuestion) { - this.specification = commentQuestion; - // Create document objects to hold the comment boxes - this.holder = document.createElement('div'); - this.holder.className = 'comment-div'; - // Create a string next to each comment asking for a comment - this.string = document.createElement('span'); - this.string.innerHTML = commentQuestion.statement; - var br = document.createElement('br'); - // Add to the holder. - this.holder.appendChild(this.string); - this.holder.appendChild(br); - this.options = []; - this.inputs = document.createElement('div'); - this.span = document.createElement('div'); - this.inputs.align = 'center'; - this.inputs.style.marginLeft = '12px'; + return root; + }; + this.resize = function () { + var boxwidth = (window.innerWidth - 100) / 2; + if (boxwidth >= 600) { + boxwidth = 600; + } else if (boxwidth < 400) { + boxwidth = 400; + } + this.holder.style.width = boxwidth + "px"; + this.textArea.style.width = boxwidth - 6 + "px"; + }; + this.resize(); + }; + + this.radioBox = function (commentQuestion) { + this.specification = commentQuestion; + // Create document objects to hold the comment boxes + this.holder = document.createElement('div'); + this.holder.className = 'comment-div'; + // Create a string next to each comment asking for a comment + this.string = document.createElement('span'); + this.string.innerHTML = commentQuestion.statement; + var br = document.createElement('br'); + // Add to the holder. + this.holder.appendChild(this.string); + this.holder.appendChild(br); + this.options = []; + this.inputs = document.createElement('div'); + this.span = document.createElement('div'); + this.inputs.align = 'center'; + this.inputs.style.marginLeft = '12px'; this.inputs.className = "comment-radio-inputs-holder"; - this.span.style.marginLeft = '12px'; - this.span.align = 'center'; - this.span.style.marginTop = '15px'; + this.span.style.marginLeft = '12px'; + this.span.align = 'center'; + this.span.style.marginTop = '15px'; this.span.className = "comment-radio-span-holder"; - - var optCount = commentQuestion.options.length; - for (var optNode of commentQuestion.options) - { - var div = document.createElement('div'); - div.style.width = '80px'; - div.style.float = 'left'; - var input = document.createElement('input'); - input.type = 'radio'; - input.name = commentQuestion.id; - input.setAttribute('setvalue',optNode.name); - input.className = 'comment-radio'; - div.appendChild(input); - this.inputs.appendChild(div); - - - div = document.createElement('div'); - div.style.width = '80px'; - div.style.float = 'left'; - div.align = 'center'; - var span = document.createElement('span'); - span.textContent = optNode.text; - span.className = 'comment-radio-span'; - div.appendChild(span); - this.span.appendChild(div); - this.options.push(input); - } - this.holder.appendChild(this.span); - this.holder.appendChild(this.inputs); - - this.exportXMLDOM = function(storePoint) { - var root = storePoint.parent.document.createElement('comment'); - root.id = this.specification.id; - root.setAttribute('type',this.specification.type); - var question = document.createElement('question'); - question.textContent = this.string.textContent; - var response = document.createElement('response'); - var i=0; - while(this.options[i].checked == false) { - i++; - if (i >= this.options.length) { - break; - } - } - if (i >= this.options.length) { - response.textContent = 'null'; - } else { - response.textContent = this.options[i].getAttribute('setvalue'); - response.setAttribute('number',i); - } - console.log('Comment: '+question.textContent); - console.log('Response: '+response.textContent); - root.appendChild(question); - root.appendChild(response); + + var optCount = commentQuestion.options.length; + for (var optNode of commentQuestion.options) { + var div = document.createElement('div'); + div.style.width = '80px'; + div.style.float = 'left'; + var input = document.createElement('input'); + input.type = 'radio'; + input.name = commentQuestion.id; + input.setAttribute('setvalue', optNode.name); + input.className = 'comment-radio'; + div.appendChild(input); + this.inputs.appendChild(div); + + + div = document.createElement('div'); + div.style.width = '80px'; + div.style.float = 'left'; + div.align = 'center'; + var span = document.createElement('span'); + span.textContent = optNode.text; + span.className = 'comment-radio-span'; + div.appendChild(span); + this.span.appendChild(div); + this.options.push(input); + } + this.holder.appendChild(this.span); + this.holder.appendChild(this.inputs); + + this.exportXMLDOM = function (storePoint) { + var root = storePoint.parent.document.createElement('comment'); + root.id = this.specification.id; + root.setAttribute('type', this.specification.type); + var question = document.createElement('question'); + question.textContent = this.string.textContent; + var response = document.createElement('response'); + var i = 0; + while (this.options[i].checked == false) { + i++; + if (i >= this.options.length) { + break; + } + } + if (i >= this.options.length) { + response.textContent = 'null'; + } else { + response.textContent = this.options[i].getAttribute('setvalue'); + response.setAttribute('number', i); + } + console.log('Comment: ' + question.textContent); + console.log('Response: ' + response.textContent); + root.appendChild(question); + root.appendChild(response); storePoint.XMLDOM.appendChild(root); - return root; - }; - this.resize = function() - { - var boxwidth = (window.innerWidth-100)/2; - if (boxwidth >= 600) - { - boxwidth = 600; - } - else if (boxwidth < 400) - { - boxwidth = 400; - } - this.holder.style.width = boxwidth+"px"; - var text = this.holder.getElementsByClassName("comment-radio-span-holder")[0]; - 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'; - 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.nextSibling != undefined) - { - options = options.nextSibling; - text = text.nextSibling; - options.style.marginRight = spanMargin; - options.style.marginLeft = spanMargin; - text.style.marginRight = spanMargin; - text.style.marginLeft = spanMargin; - } - }; - this.resize(); - }; - - this.checkboxBox = function(commentQuestion) { - this.specification = commentQuestion; - // Create document objects to hold the comment boxes - this.holder = document.createElement('div'); - this.holder.className = 'comment-div'; - // Create a string next to each comment asking for a comment - this.string = document.createElement('span'); - this.string.innerHTML = commentQuestion.statement; - var br = document.createElement('br'); - // Add to the holder. - this.holder.appendChild(this.string); - this.holder.appendChild(br); - this.options = []; - this.inputs = document.createElement('div'); - this.span = document.createElement('div'); - this.inputs.align = 'center'; - this.inputs.style.marginLeft = '12px'; + return root; + }; + this.resize = function () { + var boxwidth = (window.innerWidth - 100) / 2; + if (boxwidth >= 600) { + boxwidth = 600; + } else if (boxwidth < 400) { + boxwidth = 400; + } + this.holder.style.width = boxwidth + "px"; + var text = this.holder.getElementsByClassName("comment-radio-span-holder")[0]; + 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'; + 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.nextSibling != undefined) { + options = options.nextSibling; + text = text.nextSibling; + options.style.marginRight = spanMargin; + options.style.marginLeft = spanMargin; + text.style.marginRight = spanMargin; + text.style.marginLeft = spanMargin; + } + }; + this.resize(); + }; + + this.checkboxBox = function (commentQuestion) { + this.specification = commentQuestion; + // Create document objects to hold the comment boxes + this.holder = document.createElement('div'); + this.holder.className = 'comment-div'; + // Create a string next to each comment asking for a comment + this.string = document.createElement('span'); + this.string.innerHTML = commentQuestion.statement; + var br = document.createElement('br'); + // Add to the holder. + this.holder.appendChild(this.string); + this.holder.appendChild(br); + this.options = []; + this.inputs = document.createElement('div'); + this.span = document.createElement('div'); + this.inputs.align = 'center'; + this.inputs.style.marginLeft = '12px'; this.inputs.className = "comment-checkbox-inputs-holder"; - this.span.style.marginLeft = '12px'; - this.span.align = 'center'; - this.span.style.marginTop = '15px'; + this.span.style.marginLeft = '12px'; + this.span.align = 'center'; + this.span.style.marginTop = '15px'; this.span.className = "comment-checkbox-span-holder"; - - var optCount = commentQuestion.options.length; - for (var i=0; i<optCount; i++) - { - var div = document.createElement('div'); - div.style.width = '80px'; - div.style.float = 'left'; - var input = document.createElement('input'); - input.type = 'checkbox'; - input.name = commentQuestion.id; - input.setAttribute('setvalue',commentQuestion.options[i].name); - input.className = 'comment-radio'; - div.appendChild(input); - this.inputs.appendChild(div); - - - div = document.createElement('div'); - div.style.width = '80px'; - div.style.float = 'left'; - div.align = 'center'; - var span = document.createElement('span'); - span.textContent = commentQuestion.options[i].text; - span.className = 'comment-radio-span'; - div.appendChild(span); - this.span.appendChild(div); - this.options.push(input); - } - this.holder.appendChild(this.span); - this.holder.appendChild(this.inputs); - - this.exportXMLDOM = function(storePoint) { - var root = storePoint.parent.document.createElement('comment'); - root.id = this.specification.id; - root.setAttribute('type',this.specification.type); - var question = document.createElement('question'); - question.textContent = this.string.textContent; - root.appendChild(question); - console.log('Comment: '+question.textContent); - for (var i=0; i<this.options.length; i++) { - var response = document.createElement('response'); - response.textContent = this.options[i].checked; - response.setAttribute('name',this.options[i].getAttribute('setvalue')); - root.appendChild(response); - console.log('Response '+response.getAttribute('name') +': '+response.textContent); - } + + var optCount = commentQuestion.options.length; + for (var i = 0; i < optCount; i++) { + var div = document.createElement('div'); + div.style.width = '80px'; + div.style.float = 'left'; + var input = document.createElement('input'); + input.type = 'checkbox'; + input.name = commentQuestion.id; + input.setAttribute('setvalue', commentQuestion.options[i].name); + input.className = 'comment-radio'; + div.appendChild(input); + this.inputs.appendChild(div); + + + div = document.createElement('div'); + div.style.width = '80px'; + div.style.float = 'left'; + div.align = 'center'; + var span = document.createElement('span'); + span.textContent = commentQuestion.options[i].text; + span.className = 'comment-radio-span'; + div.appendChild(span); + this.span.appendChild(div); + this.options.push(input); + } + this.holder.appendChild(this.span); + this.holder.appendChild(this.inputs); + + this.exportXMLDOM = function (storePoint) { + var root = storePoint.parent.document.createElement('comment'); + root.id = this.specification.id; + root.setAttribute('type', this.specification.type); + var question = document.createElement('question'); + question.textContent = this.string.textContent; + root.appendChild(question); + console.log('Comment: ' + question.textContent); + for (var i = 0; i < this.options.length; i++) { + var response = document.createElement('response'); + response.textContent = this.options[i].checked; + response.setAttribute('name', this.options[i].getAttribute('setvalue')); + root.appendChild(response); + console.log('Response ' + response.getAttribute('name') + ': ' + response.textContent); + } storePoint.XMLDOM.appendChild(root); - return root; - }; - this.resize = function() - { - var boxwidth = (window.innerWidth-100)/2; - if (boxwidth >= 600) - { - boxwidth = 600; - } - else if (boxwidth < 400) - { - boxwidth = 400; - } - this.holder.style.width = boxwidth+"px"; - var text = this.holder.getElementsByClassName("comment-checkbox-span-holder")[0]; - 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'; - 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.nextSibling != undefined) - { - options = options.nextSibling; - text = text.nextSibling; - options.style.marginRight = spanMargin; - options.style.marginLeft = spanMargin; - text.style.marginRight = spanMargin; - text.style.marginLeft = spanMargin; - } - }; - this.resize(); - }; - - this.createCommentQuestion = function(element) { - var node; - if (element.type == 'question') { - node = new this.commentBox(element); - } else if (element.type == 'radio') { - node = new this.radioBox(element); - } else if (element.type == 'checkbox') { - node = new this.checkboxBox(element); - } - this.commentQuestions.push(node); - return node; - }; - - this.deleteCommentQuestions = function() - { - this.commentQuestions = []; - }; - - this.outsideReferenceDOM = function(audioObject,index,inject) - { + return root; + }; + this.resize = function () { + var boxwidth = (window.innerWidth - 100) / 2; + if (boxwidth >= 600) { + boxwidth = 600; + } else if (boxwidth < 400) { + boxwidth = 400; + } + this.holder.style.width = boxwidth + "px"; + var text = this.holder.getElementsByClassName("comment-checkbox-span-holder")[0]; + 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'; + 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.nextSibling != undefined) { + options = options.nextSibling; + text = text.nextSibling; + options.style.marginRight = spanMargin; + options.style.marginLeft = spanMargin; + text.style.marginRight = spanMargin; + text.style.marginLeft = spanMargin; + } + }; + this.resize(); + }; + + this.createCommentQuestion = function (element) { + var node; + if (element.type == 'question') { + node = new this.commentBox(element); + } else if (element.type == 'radio') { + node = new this.radioBox(element); + } else if (element.type == 'checkbox') { + node = new this.checkboxBox(element); + } + this.commentQuestions.push(node); + return node; + }; + + this.deleteCommentQuestions = function () { + this.commentQuestions = []; + }; + + this.outsideReferenceDOM = function (audioObject, index, inject) { this.parent = audioObject; this.outsideReferenceHolder = document.createElement('button'); this.outsideReferenceHolder.className = 'outside-reference'; - this.outsideReferenceHolder.setAttribute('track-id',index); + this.outsideReferenceHolder.setAttribute('track-id', index); this.outsideReferenceHolder.textContent = this.parent.specification.label || "Reference"; this.outsideReferenceHolder.disabled = true; - this.outsideReferenceHolder.onclick = function(event) - { + this.outsideReferenceHolder.onclick = function (event) { audioEngineContext.play(event.currentTarget.getAttribute('track-id')); }; inject.appendChild(this.outsideReferenceHolder); - this.enable = function() - { - if (this.parent.state == 1) - { + this.enable = function () { + if (this.parent.state == 1) { this.outsideReferenceHolder.disabled = false; } }; - this.updateLoading = function(progress) - { - if (progress != 100) - { + this.updateLoading = function (progress) { + if (progress != 100) { progress = String(progress); progress = progress.split('.')[0]; - this.outsideReferenceHolder.textContent = progress+'%'; + this.outsideReferenceHolder.textContent = progress + '%'; } else { this.outsideReferenceHolder.textContent = this.parent.specification.label || "Reference"; } }; - this.startPlayback = function() - { + this.startPlayback = function () { // Called when playback has begun $('.track-slider').removeClass('track-slider-playing'); $('.comment-div').removeClass('comment-box-playing'); this.outsideReferenceHolder.style.backgroundColor = "#FDD"; }; - this.stopPlayback = function() - { + this.stopPlayback = function () { // Called when playback has stopped. This gets called even if playback never started! this.outsideReferenceHolder.style.backgroundColor = ""; }; - this.exportXMLDOM = function(audioObject) - { + this.exportXMLDOM = function (audioObject) { return null; }; - this.getValue = function() - { + this.getValue = function () { return 0; }; - this.getPresentedId = function() - { + this.getPresentedId = function () { return this.parent.specification.label || "Reference"; }; - this.canMove = function() - { + this.canMove = function () { return false; }; - this.error = function() { - // audioObject has an error!! + this.error = function () { + // audioObject has an error!! this.outsideReferenceHolder.textContent = "Error"; this.outsideReferenceHolder.style.backgroundColor = "#F00"; } } - - this.playhead = new function() - { - this.object = document.createElement('div'); - this.object.className = 'playhead'; - this.object.align = 'left'; - var curTime = document.createElement('div'); - curTime.style.width = '50px'; - this.curTimeSpan = document.createElement('span'); - this.curTimeSpan.textContent = '00:00'; - curTime.appendChild(this.curTimeSpan); - this.object.appendChild(curTime); - this.scrubberTrack = document.createElement('div'); - this.scrubberTrack.className = 'playhead-scrub-track'; - - this.scrubberHead = document.createElement('div'); - this.scrubberHead.id = 'playhead-scrubber'; - this.scrubberTrack.appendChild(this.scrubberHead); - this.object.appendChild(this.scrubberTrack); - - this.timePerPixel = 0; - this.maxTime = 0; - - this.playbackObject; - - this.setTimePerPixel = function(audioObject) { - //maxTime must be in seconds - this.playbackObject = audioObject; - this.maxTime = audioObject.buffer.buffer.duration; - var width = 490; //500 - 10, 5 each side of the tracker head - this.timePerPixel = this.maxTime/490; - if (this.maxTime < 60) { - this.curTimeSpan.textContent = '0.00'; - } else { - this.curTimeSpan.textContent = '00:00'; - } - }; - - this.update = function() { - // Update the playhead position, startPlay must be called - if (this.timePerPixel > 0) { - var time = this.playbackObject.getCurrentPosition(); - if (time > 0 && time < this.maxTime) { - var width = 490; - var pix = Math.floor(time/this.timePerPixel); - this.scrubberHead.style.left = pix+'px'; - if (this.maxTime > 60.0) { - var secs = time%60; - var mins = Math.floor((time-secs)/60); - secs = secs.toString(); - secs = secs.substr(0,2); - mins = mins.toString(); - this.curTimeSpan.textContent = mins+':'+secs; - } else { - time = time.toString(); - this.curTimeSpan.textContent = time.substr(0,4); - } - } else { - this.scrubberHead.style.left = '0px'; - if (this.maxTime < 60) { - this.curTimeSpan.textContent = '0.00'; - } else { - this.curTimeSpan.textContent = '00:00'; - } - } - } - }; - - this.interval = undefined; - - this.start = function() { - if (this.playbackObject != undefined && this.interval == undefined) { - if (this.maxTime < 60) { - this.interval = setInterval(function(){interfaceContext.playhead.update();},10); - } else { - this.interval = setInterval(function(){interfaceContext.playhead.update();},100); - } - } - }; - this.stop = function() { - clearInterval(this.interval); - this.interval = undefined; + + this.playhead = new function () { + this.object = document.createElement('div'); + this.object.className = 'playhead'; + this.object.align = 'left'; + var curTime = document.createElement('div'); + curTime.style.width = '50px'; + this.curTimeSpan = document.createElement('span'); + this.curTimeSpan.textContent = '00:00'; + curTime.appendChild(this.curTimeSpan); + this.object.appendChild(curTime); + this.scrubberTrack = document.createElement('div'); + this.scrubberTrack.className = 'playhead-scrub-track'; + + this.scrubberHead = document.createElement('div'); + this.scrubberHead.id = 'playhead-scrubber'; + this.scrubberTrack.appendChild(this.scrubberHead); + this.object.appendChild(this.scrubberTrack); + + this.timePerPixel = 0; + this.maxTime = 0; + + this.playbackObject; + + this.setTimePerPixel = function (audioObject) { + //maxTime must be in seconds + this.playbackObject = audioObject; + this.maxTime = audioObject.buffer.buffer.duration; + var width = 490; //500 - 10, 5 each side of the tracker head + this.timePerPixel = this.maxTime / 490; + if (this.maxTime < 60) { + this.curTimeSpan.textContent = '0.00'; + } else { + this.curTimeSpan.textContent = '00:00'; + } + }; + + this.update = function () { + // Update the playhead position, startPlay must be called + if (this.timePerPixel > 0) { + var time = this.playbackObject.getCurrentPosition(); + if (time > 0 && time < this.maxTime) { + var width = 490; + var pix = Math.floor(time / this.timePerPixel); + this.scrubberHead.style.left = pix + 'px'; + if (this.maxTime > 60.0) { + var secs = time % 60; + var mins = Math.floor((time - secs) / 60); + secs = secs.toString(); + secs = secs.substr(0, 2); + mins = mins.toString(); + this.curTimeSpan.textContent = mins + ':' + secs; + } else { + time = time.toString(); + this.curTimeSpan.textContent = time.substr(0, 4); + } + } else { + this.scrubberHead.style.left = '0px'; + if (this.maxTime < 60) { + this.curTimeSpan.textContent = '0.00'; + } else { + this.curTimeSpan.textContent = '00:00'; + } + } + } + }; + + this.interval = undefined; + + this.start = function () { + if (this.playbackObject != undefined && this.interval == undefined) { + if (this.maxTime < 60) { + this.interval = setInterval(function () { + interfaceContext.playhead.update(); + }, 10); + } else { + this.interval = setInterval(function () { + interfaceContext.playhead.update(); + }, 100); + } + } + }; + this.stop = function () { + clearInterval(this.interval); + this.interval = undefined; this.scrubberHead.style.left = '0px'; - if (this.maxTime < 60) { - this.curTimeSpan.textContent = '0.00'; - } else { - this.curTimeSpan.textContent = '00:00'; - } - }; - }; - - this.volume = new function() - { + if (this.maxTime < 60) { + this.curTimeSpan.textContent = '0.00'; + } else { + this.curTimeSpan.textContent = '00:00'; + } + }; + }; + + this.volume = new function () { // An in-built volume module which can be viewed on page // Includes trackers on page-by-page data // Volume does NOT reset to 0dB on each page load @@ -2845,48 +2776,44 @@ this.valueText = document.createElement('span'); this.valueText.id = 'master-volume-feedback'; this.valueText.textContent = '0dB'; - + this.slider.min = -60; this.slider.max = 12; this.slider.value = 0; this.slider.step = 1; - this.slider.onmousemove = function(event) - { + this.slider.onmousemove = function (event) { interfaceContext.volume.valueDB = event.currentTarget.value; interfaceContext.volume.valueLin = decibelToLinear(interfaceContext.volume.valueDB); - interfaceContext.volume.valueText.textContent = interfaceContext.volume.valueDB+'dB'; + interfaceContext.volume.valueText.textContent = interfaceContext.volume.valueDB + 'dB'; audioEngineContext.outputGain.gain.value = interfaceContext.volume.valueLin; } - this.slider.onmouseup = function(event) - { + this.slider.onmouseup = function (event) { 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'); + storePoint.setAttribute('name', 'volumeTracker'); testState.currentStore.XMLDOM.getElementsByTagName('metric')[0].appendChild(storePoint); - } - else { + } else { storePoint = storePoint[0]; } var node = storage.document.createElement('movement'); - node.setAttribute('test-time',audioEngineContext.timer.getTestTime()); - node.setAttribute('volume',interfaceContext.volume.valueDB); - node.setAttribute('format','dBFS'); + node.setAttribute('test-time', audioEngineContext.timer.getTestTime()); + node.setAttribute('volume', interfaceContext.volume.valueDB); + node.setAttribute('format', 'dBFS'); storePoint.appendChild(node); } - + var title = document.createElement('div'); title.innerHTML = '<span>Master Volume Control</span>'; title.style.fontSize = '0.75em'; title.style.width = "100%"; title.align = 'center'; this.root.appendChild(title); - + this.root.appendChild(this.slider); this.root.appendChild(this.valueText); - - this.resize = function(event) { + + this.resize = function (event) { if (window.innerWidth < 1000) { this.object.className = "master-volume-holder-inline" } else { @@ -2894,21 +2821,21 @@ } } } - + this.calibrationModuleObject = null; - this.calibrationModule = function() { + this.calibrationModule = function () { // This creates an on-page calibration module this.storeDOM = storage.document.createElement("calibration"); storage.root.appendChild(this.storeDOM); // The calibration is a fixed state module this.calibrationNodes = []; this.holder = null; - this.build = function(inject) { + this.build = function (inject) { var f0 = 62.5; this.holder = document.createElement("div"); this.holder.className = "calibration-holder"; this.calibrationNodes = []; - while(f0 < 20000) { + while (f0 < 20000) { var obj = { root: document.createElement("div"), input: document.createElement("input"), @@ -2916,8 +2843,8 @@ gain: audioContext.createGain(), f: f0, parent: this, - handleEvent: function(event) { - switch(event.type) { + handleEvent: function (event) { + switch (event.type) { case "mouseenter": this.oscillator.start(0); break; @@ -2928,7 +2855,7 @@ this.oscillator.frequency.value = this.f; break; case "mousemove": - var value = Math.pow(10,this.input.value/20); + var value = Math.pow(10, this.input.value / 20); if (this.f == 1000) { audioEngineContext.outputGain.gain.value = value; interfaceContext.volume.slider.value = this.input.value; @@ -2938,7 +2865,7 @@ break; } }, - disconnect: function() { + disconnect: function () { this.gain.disconnect(); } } @@ -2946,23 +2873,23 @@ obj.root.appendChild(obj.input); obj.oscillator.connect(obj.gain); obj.gain.connect(audioEngineContext.outputGain); - obj.gain.gain.value = Math.random()*2; + obj.gain.gain.value = Math.random() * 2; obj.input.value = obj.gain.gain.value; - obj.input.setAttribute('orient','vertical'); + obj.input.setAttribute('orient', 'vertical'); obj.input.type = "range"; obj.input.min = -6; obj.input.max = 6; obj.input.step = 0.25; if (f0 != 1000) { - obj.input.value = (Math.random()*12)-6; + obj.input.value = (Math.random() * 12) - 6; } else { obj.input.value = 0; - obj.root.style.backgroundColor="rgb(255,125,125)"; + obj.root.style.backgroundColor = "rgb(255,125,125)"; } - obj.input.addEventListener("mousemove",obj); - obj.input.addEventListener("mouseenter",obj); - obj.input.addEventListener("mouseleave",obj); - obj.gain.gain.value = Math.pow(10,obj.input.value/20); + obj.input.addEventListener("mousemove", obj); + obj.input.addEventListener("mouseenter", obj); + obj.input.addEventListener("mouseleave", obj); + obj.gain.gain.value = Math.pow(10, obj.input.value / 20); obj.oscillator.frequency.value = f0; this.calibrationNodes.push(obj); this.holder.appendChild(obj.root); @@ -2970,175 +2897,148 @@ } inject.appendChild(this.holder); } - this.collect = function() { + this.collect = function () { 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); + 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); } } } - - - // Global Checkers - // These functions will help enforce the checkers - this.checkHiddenAnchor = function() - { - 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'); + + + // Global Checkers + // These functions will help enforce the checkers + this.checkHiddenAnchor = function () { + 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() - { - 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'); + return false; + } + } + } + return true; + }; + + this.checkHiddenReference = function () { + 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; - }; - - this.checkFragmentsFullyPlayed = function () - { - // Checks the entire file has been played back - // NOTE ! This will return true IF playback is Looped!!! - if (audioEngineContext.loopPlayback) - { - console.log("WARNING - Looped source: Cannot check fragments are fully played"); - return true; - } - var check_pass = true; - 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; - var passed = false; - for (var j=0; j<metric.listenTracker.length; j++) - { - var bt = metric.listenTracker[j].getElementsByTagName('testtime'); - var start_time = Number(bt[0].getAttribute('start')); - var stop_time = Number(bt[0].getAttribute('stop')); - var delta = stop_time - start_time; - if (delta >= time) - { - passed = true; - break; - } - } - 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) - { - var str_start = "You have not completely listened to fragments "; - for (var i=0; i<error_obj.length; i++) - { - str_start += error_obj[i]; - if (i != error_obj.length-1) - { - str_start += ', '; - } - } - str_start += ". Please keep listening"; - console.log("[ALERT]: "+str_start); - this.storeErrorNode("[ALERT]: "+str_start); - interfaceContext.lightbox.post("Error",str_start); + interfaceContext.lightbox.post("Message", 'Please keep listening'); + return false; + } + } + } + return true; + }; + + this.checkFragmentsFullyPlayed = function () { + // Checks the entire file has been played back + // NOTE ! This will return true IF playback is Looped!!! + if (audioEngineContext.loopPlayback) { + console.log("WARNING - Looped source: Cannot check fragments are fully played"); + return true; + } + var check_pass = true; + 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; + var passed = false; + for (var j = 0; j < metric.listenTracker.length; j++) { + var bt = metric.listenTracker[j].getElementsByTagName('testtime'); + var start_time = Number(bt[0].getAttribute('start')); + var stop_time = Number(bt[0].getAttribute('stop')); + var delta = stop_time - start_time; + if (delta >= time) { + passed = true; + break; + } + } + 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) { + var str_start = "You have not completely listened to fragments "; + for (var i = 0; i < error_obj.length; i++) { + str_start += error_obj[i]; + if (i != error_obj.length - 1) { + str_start += ', '; + } + } + str_start += ". Please keep listening"; + console.log("[ALERT]: " + str_start); + this.storeErrorNode("[ALERT]: " + str_start); + interfaceContext.lightbox.post("Error", str_start); return false; - } + } return true; - }; - this.checkAllMoved = function() - { - var str = "You have not moved "; - var failed = []; - for (var ao of audioEngineContext.audioObjects) - { - if(ao.metric.wasMoved == false && ao.interfaceDOM.canMove() == true) - { - failed.push(ao.interfaceDOM.getPresentedId()); - } - } - if (failed.length == 0) - { - return true; - } else if (failed.length == 1) - { - str += 'track '+failed[0]; - } else { - str += 'tracks '; - for (var i=0; i<failed.length-1; i++) - { - str += failed[i]+', '; - } - str += 'and '+failed[i]; - } - str +='.'; - interfaceContext.lightbox.post("Error",str); - console.log(str); + }; + this.checkAllMoved = function () { + var str = "You have not moved "; + var failed = []; + for (var ao of audioEngineContext.audioObjects) { + if (ao.metric.wasMoved == false && ao.interfaceDOM.canMove() == true) { + failed.push(ao.interfaceDOM.getPresentedId()); + } + } + if (failed.length == 0) { + return true; + } else if (failed.length == 1) { + str += 'track ' + failed[0]; + } else { + str += 'tracks '; + for (var i = 0; i < failed.length - 1; i++) { + str += failed[i] + ', '; + } + str += 'and ' + failed[i]; + } + str += '.'; + interfaceContext.lightbox.post("Error", str); + console.log(str); this.storeErrorNode(str); - return false; - }; - this.checkAllPlayed = function() - { - var str = "You have not played "; - var failed = []; - for (var ao of audioEngineContext.audioObjects) - { - if(ao.metric.wasListenedTo == false) - { - failed.push(ao.interfaceDOM.getPresentedId()); - } - } - if (failed.length == 0) - { - return true; - } else if (failed.length == 1) - { - str += 'track '+failed[0]; - } else { - str += 'tracks '; - for (var i=0; i<failed.length-1; i++) - { - str += failed[i]+', '; - } - str += 'and '+failed[i]; - } - str +='.'; - interfaceContext.lightbox.post("Error",str); - console.log(str); + return false; + }; + this.checkAllPlayed = function () { + var str = "You have not played "; + var failed = []; + for (var ao of audioEngineContext.audioObjects) { + if (ao.metric.wasListenedTo == false) { + failed.push(ao.interfaceDOM.getPresentedId()); + } + } + if (failed.length == 0) { + return true; + } else if (failed.length == 1) { + str += 'track ' + failed[0]; + } else { + str += 'tracks '; + for (var i = 0; i < failed.length - 1; i++) { + str += failed[i] + ', '; + } + str += 'and ' + failed[i]; + } + str += '.'; + interfaceContext.lightbox.post("Error", str); + console.log(str); this.storeErrorNode(str); - return false; - }; - this.checkScaleRange = function(min, max) { + return false; + }; + this.checkScaleRange = function (min, max) { var page = testState.getCurrentTestPage(); var audioObjects = audioEngineContext.audioObjects; var state = true; @@ -3147,55 +3047,56 @@ var maxRanking = -Infinity; for (var ao of audioObjects) { var rank = ao.interfaceDOM.getValue(); - if (rank < minRanking) {minRanking = rank;} - if (rank > maxRanking) {maxRanking = rank;} + if (rank < minRanking) { + minRanking = rank; + } + if (rank > maxRanking) { + maxRanking = rank; + } } - if (minRanking*100 > min) { - str += "At least one fragment must be below the "+min+" mark."; + if (minRanking * 100 > min) { + str += "At least one fragment must be below the " + min + " mark."; state = false; } - if (maxRanking*100 < max) { - str += "At least one fragment must be above the "+max+" mark." + 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); + interfaceContext.lightbox.post("Error", str); } return state; } - - this.storeErrorNode = function(errorMessage) - { + + this.storeErrorNode = function (errorMessage) { var time = audioEngineContext.timer.getTestTime(); var node = storage.document.createElement('error'); - node.setAttribute('time',time); + node.setAttribute('time', time); node.textContent = errorMessage; testState.currentStore.XMLDOM.appendChild(node); }; } -function Storage() -{ - // Holds results in XML format until ready for collection - this.globalPreTest = null; - this.globalPostTest = null; - this.testPages = []; - this.document = null; - this.root = null; - this.state = 0; - - this.initialise = function(existingStore) - { +function Storage() { + // Holds results in XML format until ready for collection + this.globalPreTest = null; + this.globalPostTest = null; + this.testPages = []; + this.document = null; + this.root = null; + this.state = 0; + + this.initialise = function (existingStore) { if (existingStore == undefined) { // We need to get the sessionKey this.SessionKey.generateKey(); - this.document = document.implementation.createDocument(null,"waetresult",null); + this.document = document.implementation.createDocument(null, "waetresult", null); this.root = this.document.childNodes[0]; var projectDocument = specification.projectXML; - projectDocument.setAttribute('file-name',url); - projectDocument.setAttribute('url',qualifyURL(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()); @@ -3204,68 +3105,72 @@ this.root = existingStore.firstChild; this.SessionKey.key = this.root.getAttribute("key"); } - if (specification.preTest != undefined){this.globalPreTest = new this.surveyNode(this,this.root,specification.preTest);} - if (specification.postTest != undefined){this.globalPostTest = new this.surveyNode(this,this.root,specification.postTest);} - }; - + if (specification.preTest != undefined) { + this.globalPreTest = new this.surveyNode(this, this.root, specification.preTest); + } + if (specification.postTest != undefined) { + this.globalPostTest = new this.surveyNode(this, this.root, specification.postTest); + } + }; + this.SessionKey = { key: null, request: new XMLHttpRequest(), parent: this, - handleEvent: function() { + handleEvent: function () { var parse = new DOMParser(); - var xml = parse.parseFromString(this.request.response,"text/xml"); + var xml = parse.parseFromString(this.request.response, "text/xml"); var shouldGenerateKey = true; if (this.request.response.length == 0) { console.log("Error: Server did not respond"); return; } - if(xml.getElementsByTagName("state").length > 0){ - if (xml.getElementsByTagName("state")[0].textContent == "OK") { - this.key = xml.getAllElementsByTagName("key")[0].textContent; - this.parent.root.setAttribute("key",this.key); - this.parent.root.setAttribute("state","empty"); - shouldGenerateKey = false; - } - } - if(shouldGenerateKey === true){ - this.generateKey(); - } + if (xml.getElementsByTagName("state").length > 0) { + if (xml.getElementsByTagName("state")[0].textContent == "OK") { + this.key = xml.getAllElementsByTagName("key")[0].textContent; + this.parent.root.setAttribute("key", this.key); + this.parent.root.setAttribute("state", "empty"); + shouldGenerateKey = false; + } + } + if (shouldGenerateKey === true) { + this.generateKey(); + } }, - generateKey: function() { + generateKey: function () { var temp_key = randomString(32); var returnURL = ""; if (typeof specification.projectReturn == "string") { - if (specification.projectReturn.substr(0,4) == "http") { + if (specification.projectReturn.substr(0, 4) == "http") { returnURL = specification.projectReturn; } } - this.request.open("GET",returnURL+"php/keygen.php?key="+temp_key,true); - this.request.addEventListener("load",this); + this.request.open("GET", returnURL + "php/keygen.php?key=" + temp_key, true); + this.request.addEventListener("load", this); this.request.send(); }, - update: function() { + update: function () { if (this.key == null) { console.log("Cannot save as key == null"); return; } - this.parent.root.setAttribute("state","update"); + this.parent.root.setAttribute("state", "update"); var xmlhttp = new XMLHttpRequest(); var returnURL = ""; if (typeof specification.projectReturn == "string") { - if (specification.projectReturn.substr(0,4) == "http") { + if (specification.projectReturn.substr(0, 4) == "http") { returnURL = specification.projectReturn; } } - xmlhttp.open("POST",returnURL+"php/save.php?key="+this.key); + xmlhttp.open("POST", returnURL + "php/save.php?key=" + this.key); xmlhttp.setRequestHeader('Content-Type', 'text/xml'); - xmlhttp.onerror = function(){ + xmlhttp.onerror = function () { console.log('Error updating file to server!'); }; var hold = document.createElement("div"); var clone = this.parent.root.cloneNode(true); hold.appendChild(clone); - xmlhttp.onload = function() { + xmlhttp.onload = function () { if (this.status >= 300) { console.log("WARNING - Could not update at this time"); } else { @@ -3274,159 +3179,153 @@ var response = xmlDoc.getElementsByTagName('response')[0]; if (response.getAttribute("state") == "OK") { var file = response.getElementsByTagName("file")[0]; - console.log("Intermediate save: OK, written "+file.getAttribute("bytes")+"B"); + console.log("Intermediate save: OK, written " + file.getAttribute("bytes") + "B"); } else { var message = response.getElementsByTagName("message"); - console.log("Intermediate save: Error! "+message.textContent); + console.log("Intermediate save: Error! " + message.textContent); } } } xmlhttp.send([hold.innerHTML]); } } - - this.createTestPageStore = function(specification) - { - var store = new this.pageNode(this,specification); - this.testPages.push(store); - return this.testPages[this.testPages.length-1]; - }; - - this.surveyNode = function(parent,root,specification) - { - this.specification = specification; - this.parent = parent; + + this.createTestPageStore = function (specification) { + var store = new this.pageNode(this, specification); + this.testPages.push(store); + return this.testPages[this.testPages.length - 1]; + }; + + this.surveyNode = function (parent, root, specification) { + this.specification = specification; + this.parent = parent; this.state = "empty"; - this.XMLDOM = this.parent.document.createElement('survey'); - this.XMLDOM.setAttribute('location',this.specification.location); - this.XMLDOM.setAttribute("state",this.state); - 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); - } - } - root.appendChild(this.XMLDOM); - - this.postResult = function(node) - { - // 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) { - if (surveyresult.getAttribute("ref") == node.specification.id) - { + this.XMLDOM = this.parent.document.createElement('survey'); + this.XMLDOM.setAttribute('location', this.specification.location); + this.XMLDOM.setAttribute("state", this.state); + 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); + } + } + root.appendChild(this.XMLDOM); + + this.postResult = function (node) { + // 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) { + if (surveyresult.getAttribute("ref") == node.specification.id) { break; } surveyresult = surveyresult.nextElementSibling; } - switch(node.specification.type) - { - case "number": - case "question": - var child = this.parent.document.createElement('response'); - child.textContent = node.response; - surveyresult.appendChild(child); - break; - case "radio": - var child = this.parent.document.createElement('response'); - child.setAttribute('name',node.response.name); - child.textContent = node.response.text; - surveyresult.appendChild(child); - break; - case "checkbox": - if (node.response == undefined) { - surveyresult.appendChild(this.parent.document.createElement('response')); + switch (node.specification.type) { + case "number": + case "question": + var child = this.parent.document.createElement('response'); + child.textContent = node.response; + surveyresult.appendChild(child); break; + case "radio": + var child = this.parent.document.createElement('response'); + child.setAttribute('name', node.response.name); + child.textContent = node.response.text; + surveyresult.appendChild(child); + break; + case "checkbox": + if (node.response == undefined) { + surveyresult.appendChild(this.parent.document.createElement('response')); + break; + } + for (var i = 0; i < node.response.length; 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; + } + }; + this.complete = function () { + this.state = "complete"; + this.XMLDOM.setAttribute("state", this.state); + } + }; + + this.pageNode = function (parent, specification) { + // Create one store per test page + this.specification = specification; + this.parent = parent; + this.state = "empty"; + this.XMLDOM = this.parent.document.createElement('page'); + this.XMLDOM.setAttribute('ref', specification.id); + this.XMLDOM.setAttribute('presentedId', specification.presentedId); + this.XMLDOM.setAttribute("state", this.state); + if (specification.preTest != undefined) { + this.preTest = new this.parent.surveyNode(this.parent, this.XMLDOM, this.specification.preTest); + } + if (specification.postTest != undefined) { + this.postTest = new this.parent.surveyNode(this.parent, this.XMLDOM, this.specification.postTest); + } + + // Add any page metrics + var page_metric = this.parent.document.createElement('metric'); + this.XMLDOM.appendChild(page_metric); + + // Add the audioelement + 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) + }; + aeNode.setAttribute('type', element.type); + aeNode.setAttribute('url', element.url); + aeNode.setAttribute('fqurl', qualifyURL(element.url)); + aeNode.setAttribute('gain', element.gain); + if (element.type == 'anchor' || element.type == 'reference') { + if (element.marker > 0) { + aeNode.setAttribute('marker', element.marker); } - for (var i=0; i<node.response.length; 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; - } - }; - this.complete = function() { + } + var ae_metric = this.parent.document.createElement('metric'); + aeNode.appendChild(ae_metric); + this.XMLDOM.appendChild(aeNode); + } + + this.parent.root.appendChild(this.XMLDOM); + + this.complete = function () { this.state = "complete"; - this.XMLDOM.setAttribute("state",this.state); + this.XMLDOM.setAttribute("state", "complete"); } - }; - - this.pageNode = function(parent,specification) - { - // Create one store per test page - this.specification = specification; - this.parent = parent; - this.state = "empty"; - this.XMLDOM = this.parent.document.createElement('page'); - this.XMLDOM.setAttribute('ref',specification.id); - this.XMLDOM.setAttribute('presentedId',specification.presentedId); - this.XMLDOM.setAttribute("state",this.state); - if (specification.preTest != undefined){this.preTest = new this.parent.surveyNode(this.parent,this.XMLDOM,this.specification.preTest);} - if (specification.postTest != undefined){this.postTest = new this.parent.surveyNode(this.parent,this.XMLDOM,this.specification.postTest);} - - // Add any page metrics - var page_metric = this.parent.document.createElement('metric'); - this.XMLDOM.appendChild(page_metric); - - // Add the audioelement - 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)}; - aeNode.setAttribute('type',element.type); - aeNode.setAttribute('url', element.url); - aeNode.setAttribute('fqurl',qualifyURL(element.url)); - aeNode.setAttribute('gain', element.gain); - if (element.type == 'anchor' || element.type == 'reference') - { - if (element.marker > 0) - { - aeNode.setAttribute('marker',element.marker); - } - } - var ae_metric = this.parent.document.createElement('metric'); - aeNode.appendChild(ae_metric); - this.XMLDOM.appendChild(aeNode); - } - - this.parent.root.appendChild(this.XMLDOM); - - this.complete = function() { - this.state = "complete"; - this.XMLDOM.setAttribute("state","complete"); - } - }; - this.update = function() { + }; + this.update = function () { this.SessionKey.update(); } - this.finish = function() - { - if (this.state == 0) - { + this.finish = function () { + if (this.state == 0) { this.update(); - } - this.state = 1; - this.root.setAttribute("state","complete"); - return this.root; - }; + } + this.state = 1; + this.root.setAttribute("state", "complete"); + return this.root; + }; } var window_depedancy_callback; -window_depedancy_callback = window.setInterval(function(){ +window_depedancy_callback = window.setInterval(function () { if (check_dependancies()) { window.clearInterval(window_depedancy_callback); onload(); } else { document.getElementById("topLevelBody").innerHTML = "<h1>Loading Resources</h1>"; } -},100); \ No newline at end of file +}, 100);
--- a/js/specification.js Wed Sep 21 10:20:57 2016 +0100 +++ b/js/specification.js Wed Sep 21 13:21:06 2016 +0100 @@ -495,13 +495,13 @@ // Get the title var title = xml.getElementsByTagName('title'); - if (title.length != 0) { + if (title.length != 0 && title[0].parentElement == xml) { this.title = title[0].textContent; } // Get the Comment Box Prefix var CBP = xml.getElementsByTagName('commentboxprefix'); - if (CBP.length != 0) { + if (CBP.length != 0 && CBP[0].parentElement == xml) { this.commentBoxPrefix = CBP[0].textContent; }
--- a/tests/examples/project.xml Wed Sep 21 10:20:57 2016 +0100 +++ b/tests/examples/project.xml Wed Sep 21 13:21:06 2016 +0100 @@ -94,7 +94,7 @@ </survey> </page> <page id='test-1' hostURL="media/example/" randomiseOrder='true' repeatCount='0' loop='false' label="letter"> - <commentboxprefix>Comment on fragment</commentboxprefix> + <commentboxprefix>Comment on fragment</commentboxprefix> <interface name="preference"> <title>Example Test Question</title> <scales>