annotate core.js @ 2165:e75107c82178

Fixed errors caused by specifcation nesting, specifically if <page> interface nodes have <interfaceoption> nodes.
author Nicholas Jillings <nickjillings@users.noreply.github.com>
date Fri, 18 Mar 2016 14:52:30 +0000
parents 894d9bfd4177
children 96a830d58da7
rev   line source
nickjillings@1682 1 /**
nickjillings@1682 2 * core.js
nickjillings@1682 3 *
nickjillings@1682 4 * Main script to run, calls all other core functions and manages loading/store to backend.
nickjillings@1682 5 * Also contains all global variables.
nickjillings@1682 6 */
nickjillings@1682 7
nickjillings@1682 8 /* create the web audio API context and store in audioContext*/
nickjillings@1643 9 var audioContext; // Hold the browser web audio API
nickjillings@1643 10 var projectXML; // Hold the parsed setup XML
nickjillings@1324 11 var schemaXSD; // Hold the parsed schema XSD
nickjillings@1581 12 var specification;
nickjillings@1582 13 var interfaceContext;
nickjillings@1324 14 var storage;
nickjillings@1622 15 var popup; // Hold the interfacePopup object
nickjillings@1634 16 var testState;
nickjillings@1655 17 var currentTrackOrder = []; // Hold the current XML tracks in their (randomised) order
nickjillings@1643 18 var audioEngineContext; // The custome AudioEngine object
nickjillings@1643 19 var projectReturn; // Hold the URL for the return
nickjillings@2013 20
nickjillings@1318 21
nickjillings@1318 22 // Add a prototype to the bufferSourceNode to reference to the audioObject holding it
nickjillings@1318 23 AudioBufferSourceNode.prototype.owner = undefined;
nickjillings@2101 24 // Add a prototype to the bufferSourceNode to hold when the object was given a play command
nickjillings@2101 25 AudioBufferSourceNode.prototype.playbackStartTime = undefined;
nickjillings@1318 26 // Add a prototype to the bufferNode to hold the desired LINEAR gain
nickjillings@1320 27 AudioBuffer.prototype.playbackGain = undefined;
nickjillings@1318 28 // Add a prototype to the bufferNode to hold the computed LUFS loudness
nickjillings@1318 29 AudioBuffer.prototype.lufs = undefined;
nickjillings@1318 30
nickjillings@1348 31 // Firefox does not have an XMLDocument.prototype.getElementsByName
nickjillings@1348 32 // and there is no searchAll style command, this custom function will
nickjillings@1348 33 // search all children recusrively for the name. Used for XSD where all
nickjillings@1348 34 // element nodes must have a name and therefore can pull the schema node
nickjillings@1348 35 XMLDocument.prototype.getAllElementsByName = function(name)
nickjillings@1348 36 {
nickjillings@1348 37 name = String(name);
nickjillings@1348 38 var selected = this.documentElement.getAllElementsByName(name);
nickjillings@1348 39 return selected;
nickjillings@1348 40 }
nickjillings@1348 41
nickjillings@1348 42 Element.prototype.getAllElementsByName = function(name)
nickjillings@1348 43 {
nickjillings@1348 44 name = String(name);
nickjillings@1348 45 var selected = [];
nickjillings@1348 46 var node = this.firstElementChild;
nickjillings@1348 47 while(node != null)
nickjillings@1348 48 {
nickjillings@1348 49 if (node.getAttribute('name') == name)
nickjillings@1348 50 {
nickjillings@1348 51 selected.push(node);
nickjillings@1348 52 }
nickjillings@1348 53 if (node.childElementCount > 0)
nickjillings@1348 54 {
nickjillings@1348 55 selected = selected.concat(node.getAllElementsByName(name));
nickjillings@1348 56 }
nickjillings@1348 57 node = node.nextElementSibling;
nickjillings@1348 58 }
nickjillings@1348 59 return selected;
nickjillings@1348 60 }
nickjillings@1348 61
nickjillings@1348 62 XMLDocument.prototype.getAllElementsByTagName = function(name)
nickjillings@1348 63 {
nickjillings@1348 64 name = String(name);
nickjillings@1348 65 var selected = this.documentElement.getAllElementsByTagName(name);
nickjillings@1348 66 return selected;
nickjillings@1348 67 }
nickjillings@1348 68
nickjillings@1348 69 Element.prototype.getAllElementsByTagName = function(name)
nickjillings@1348 70 {
nickjillings@1348 71 name = String(name);
nickjillings@1348 72 var selected = [];
nickjillings@1348 73 var node = this.firstElementChild;
nickjillings@1348 74 while(node != null)
nickjillings@1348 75 {
nickjillings@1348 76 if (node.nodeName == name)
nickjillings@1348 77 {
nickjillings@1348 78 selected.push(node);
nickjillings@1348 79 }
nickjillings@1348 80 if (node.childElementCount > 0)
nickjillings@1348 81 {
nickjillings@1348 82 selected = selected.concat(node.getAllElementsByTagName(name));
nickjillings@1348 83 }
nickjillings@1348 84 node = node.nextElementSibling;
nickjillings@1348 85 }
nickjillings@1348 86 return selected;
nickjillings@1348 87 }
nickjillings@1348 88
nickjillings@1348 89 // Firefox does not have an XMLDocument.prototype.getElementsByName
nickjillings@1348 90 if (typeof XMLDocument.prototype.getElementsByName != "function") {
nickjillings@1348 91 XMLDocument.prototype.getElementsByName = function(name)
nickjillings@1348 92 {
nickjillings@1348 93 name = String(name);
nickjillings@1348 94 var node = this.documentElement.firstElementChild;
nickjillings@1348 95 var selected = [];
nickjillings@1348 96 while(node != null)
nickjillings@1348 97 {
nickjillings@1348 98 if (node.getAttribute('name') == name)
nickjillings@1348 99 {
nickjillings@1348 100 selected.push(node);
nickjillings@1348 101 }
nickjillings@1348 102 node = node.nextElementSibling;
nickjillings@1348 103 }
nickjillings@1348 104 return selected;
nickjillings@1348 105 }
nickjillings@1348 106 }
nickjillings@1348 107
nickjillings@1682 108 window.onload = function() {
nickjillings@1682 109 // Function called once the browser has loaded all files.
nickjillings@1682 110 // This should perform any initial commands such as structure / loading documents
nickjillings@1682 111
nickjillings@1682 112 // Create a web audio API context
nickjillings@1701 113 // Fixed for cross-browser support
nickjillings@1701 114 var AudioContext = window.AudioContext || window.webkitAudioContext;
nickjillings@1688 115 audioContext = new AudioContext;
nickjillings@1682 116
nickjillings@1634 117 // Create test state
nickjillings@1634 118 testState = new stateMachine();
nickjillings@1634 119
nickjillings@1622 120 // Create the popup interface object
nickjillings@1622 121 popup = new interfacePopup();
nickjillings@1370 122
nickjillings@1370 123 // Create the specification object
nickjillings@1581 124 specification = new Specification();
nickjillings@1582 125
nickjillings@1582 126 // Create the interface object
nickjillings@1582 127 interfaceContext = new Interface(specification);
nickjillings@1324 128
nickjillings@1324 129 // Create the storage object
nickjillings@1324 130 storage = new Storage();
nickjillings@1410 131 // Define window callbacks for interface
nickjillings@1410 132 window.onresize = function(event){interfaceContext.resizeWindow(event);};
nickjillings@1697 133 };
nickjillings@1682 134
nickjillings@1408 135 function loadProjectSpec(url) {
nickjillings@1408 136 // Load the project document from the given URL, decode the XML and instruct audioEngine to get audio data
nickjillings@1408 137 // If url is null, request client to upload project XML document
nickjillings@1324 138 var xmlhttp = new XMLHttpRequest();
nickjillings@1324 139 xmlhttp.open("GET",'test-schema.xsd',true);
nickjillings@1324 140 xmlhttp.onload = function()
nickjillings@1324 141 {
nickjillings@1324 142 schemaXSD = xmlhttp.response;
nickjillings@1324 143 var parse = new DOMParser();
nickjillings@1324 144 specification.schema = parse.parseFromString(xmlhttp.response,'text/xml');
nickjillings@1324 145 var r = new XMLHttpRequest();
nickjillings@1324 146 r.open('GET',url,true);
nickjillings@1324 147 r.onload = function() {
nickjillings@1324 148 loadProjectSpecCallback(r.response);
nickjillings@1324 149 };
nickjillings@2110 150 r.onerror = function() {
nickjillings@2110 151 document.getElementsByTagName('body')[0].innerHTML = null;
nickjillings@2110 152 var msg = document.createElement("h3");
nickjillings@2110 153 msg.textContent = "FATAL ERROR";
nickjillings@2110 154 var span = document.createElement("p");
nickjillings@2110 155 span.textContent = "There was an error when loading your XML file. Please check your path in the URL. After the path to this page, there should be '?url=path/to/your/file.xml'. Check the spelling of your filename as well. If you are still having issues, check the log of the python server or your webserver distribution for 404 codes for your file.";
nickjillings@2110 156 document.getElementsByTagName('body')[0].appendChild(msg);
nickjillings@2110 157 document.getElementsByTagName('body')[0].appendChild(span);
nickjillings@2110 158 }
nickjillings@1324 159 r.send();
nickjillings@1408 160 };
nickjillings@1324 161 xmlhttp.send();
nickjillings@1408 162 };
nickjillings@1408 163
nickjillings@1408 164 function loadProjectSpecCallback(response) {
nickjillings@1408 165 // Function called after asynchronous download of XML project specification
nickjillings@1408 166 //var decode = $.parseXML(response);
nickjillings@1408 167 //projectXML = $(decode);
nickjillings@1408 168
nickjillings@2147 169 // Check if XML is new or a resumption
nickjillings@2147 170 var parse = new DOMParser();
nickjillings@2147 171 var responseDocument = parse.parseFromString(response,'text/xml');
nickjillings@2147 172 var errorNode = responseDocument.getElementsByTagName('parsererror');
nickjillings@1443 173 if (errorNode.length >= 1)
nickjillings@1443 174 {
nickjillings@1443 175 var msg = document.createElement("h3");
nickjillings@1443 176 msg.textContent = "FATAL ERROR";
nickjillings@1443 177 var span = document.createElement("span");
nickjillings@1443 178 span.textContent = "The XML parser returned the following errors when decoding your XML file";
nickjillings@1445 179 document.getElementsByTagName('body')[0].innerHTML = null;
nickjillings@1443 180 document.getElementsByTagName('body')[0].appendChild(msg);
nickjillings@1443 181 document.getElementsByTagName('body')[0].appendChild(span);
nickjillings@1443 182 document.getElementsByTagName('body')[0].appendChild(errorNode[0]);
nickjillings@1443 183 return;
nickjillings@1443 184 }
nickjillings@2147 185 if (responseDocument.children[0].nodeName == "waet") {
nickjillings@2147 186 // document is a specification
nickjillings@2147 187
nickjillings@2147 188 // Perform XML schema validation
nickjillings@2147 189 var Module = {
nickjillings@2147 190 xml: response,
nickjillings@2147 191 schema: schemaXSD,
nickjillings@2147 192 arguments:["--noout", "--schema", 'test-schema.xsd','document.xml']
nickjillings@2147 193 };
nickjillings@2147 194 projectXML = responseDocument;
nickjillings@2147 195 var xmllint = validateXML(Module);
nickjillings@2147 196 console.log(xmllint);
nickjillings@2147 197 if(xmllint != 'document.xml validates\n')
nickjillings@2147 198 {
nickjillings@2147 199 document.getElementsByTagName('body')[0].innerHTML = null;
nickjillings@2147 200 var msg = document.createElement("h3");
nickjillings@2147 201 msg.textContent = "FATAL ERROR";
nickjillings@2147 202 var span = document.createElement("h4");
nickjillings@2147 203 span.textContent = "The XML validator returned the following errors when decoding your XML file";
nickjillings@2147 204 document.getElementsByTagName('body')[0].appendChild(msg);
nickjillings@2147 205 document.getElementsByTagName('body')[0].appendChild(span);
nickjillings@2147 206 xmllint = xmllint.split('\n');
nickjillings@2147 207 for (var i in xmllint)
nickjillings@2147 208 {
nickjillings@2147 209 document.getElementsByTagName('body')[0].appendChild(document.createElement('br'));
nickjillings@2147 210 var span = document.createElement("span");
nickjillings@2147 211 span.textContent = xmllint[i];
nickjillings@2147 212 document.getElementsByTagName('body')[0].appendChild(span);
nickjillings@2147 213 }
nickjillings@2147 214 return;
nickjillings@2147 215 }
nickjillings@2149 216 // Build the specification
nickjillings@2149 217 specification.decode(projectXML);
nickjillings@2147 218 // Generate the session-key
nickjillings@2147 219 storage.initialise();
nickjillings@2147 220
nickjillings@2147 221 } else if (responseDocument.children[0].nodeName == "waetresult") {
nickjillings@2147 222 // document is a result
nickjillings@1294 223 projectXML = document.implementation.createDocument(null,"waet");
nickjillings@1294 224 projectXML.children[0].appendChild(responseDocument.getElementsByTagName('waet')[0].getElementsByTagName("setup")[0].cloneNode(true));
nickjillings@1294 225 var child = responseDocument.children[0].children[0];
nickjillings@1294 226 while (child != null) {
nickjillings@1294 227 if (child.nodeName == "survey") {
nickjillings@1294 228 // One of the global survey elements
nickjillings@1294 229 if (child.getAttribute("state") == "complete") {
nickjillings@1294 230 // We need to remove this survey from <setup>
nickjillings@1294 231 var location = child.getAttribute("location");
nickjillings@1294 232 var globalSurveys = projectXML.getElementsByTagName("setup")[0].getElementsByTagName("survey")[0];
nickjillings@1294 233 while(globalSurveys != null) {
nickjillings@1294 234 if (location == "pre" || location == "before") {
nickjillings@1294 235 if (globalSurveys.getAttribute("location") == "pre" || globalSurveys.getAttribute("location") == "before") {
nickjillings@1294 236 projectXML.getElementsByTagName("setup")[0].removeChild(globalSurveys);
nickjillings@1294 237 break;
nickjillings@1294 238 }
nickjillings@1294 239 } else {
nickjillings@1294 240 if (globalSurveys.getAttribute("location") == "post" || globalSurveys.getAttribute("location") == "after") {
nickjillings@1294 241 projectXML.getElementsByTagName("setup")[0].removeChild(globalSurveys);
nickjillings@1294 242 break;
nickjillings@1294 243 }
nickjillings@1294 244 }
nickjillings@1294 245 globalSurveys = globalSurveys.nextElementSibling;
nickjillings@1294 246 }
nickjillings@1294 247 } else {
nickjillings@1294 248 // We need to complete this, so it must be regenerated by store
nickjillings@1294 249 var copy = child;
nickjillings@1294 250 child = child.previousElementSibling;
nickjillings@1294 251 responseDocument.children[0].removeChild(copy);
nickjillings@1294 252 }
nickjillings@1294 253 } else if (child.nodeName == "page") {
nickjillings@1294 254 if (child.getAttribute("state") == "empty") {
nickjillings@1294 255 // We need to complete this page
nickjillings@1294 256 projectXML.children[0].appendChild(responseDocument.getElementById(child.getAttribute("ref")).cloneNode(true));
nickjillings@1294 257 var copy = child;
nickjillings@1294 258 child = child.previousElementSibling;
nickjillings@1294 259 responseDocument.children[0].removeChild(copy);
nickjillings@1294 260 }
nickjillings@1294 261 }
nickjillings@1294 262 child = child.nextElementSibling;
nickjillings@1294 263 }
nickjillings@2149 264 // Build the specification
nickjillings@2149 265 specification.decode(projectXML);
nickjillings@1294 266 // Use the original
nickjillings@1294 267 storage.initialise(responseDocument);
nickjillings@2147 268 }
nickjillings@1339 269 /// CHECK FOR SAMPLE RATE COMPATIBILITY
nickjillings@1339 270 if (specification.sampleRate != undefined) {
nickjillings@1339 271 if (Number(specification.sampleRate) != audioContext.sampleRate) {
nickjillings@1339 272 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.';
nickjillings@1339 273 alert(errStr);
nickjillings@1339 274 return;
nickjillings@1339 275 }
nickjillings@1339 276 }
nickjillings@1408 277
nickjillings@1408 278 // Detect the interface to use and load the relevant javascripts.
nickjillings@1408 279 var interfaceJS = document.createElement('script');
nickjillings@1408 280 interfaceJS.setAttribute("type","text/javascript");
nickjillings@1329 281 switch(specification.interface)
nickjillings@1329 282 {
nickjillings@1329 283 case "APE":
nickjillings@2161 284 interfaceJS.setAttribute("src","interfaces/ape.js");
nickjillings@2161 285
nickjillings@2161 286 // APE comes with a css file
nickjillings@2161 287 var css = document.createElement('link');
nickjillings@2161 288 css.rel = 'stylesheet';
nickjillings@2161 289 css.type = 'text/css';
nickjillings@2161 290 css.href = 'interfaces/ape.css';
nickjillings@2161 291
nickjillings@2161 292 document.getElementsByTagName("head")[0].appendChild(css);
nickjillings@2161 293 break;
nickjillings@2161 294
nickjillings@1329 295 case "MUSHRA":
nickjillings@2161 296 interfaceJS.setAttribute("src","interfaces/mushra.js");
nickjillings@2161 297
nickjillings@2161 298 // MUSHRA comes with a css file
nickjillings@2161 299 var css = document.createElement('link');
nickjillings@2161 300 css.rel = 'stylesheet';
nickjillings@2161 301 css.type = 'text/css';
nickjillings@2161 302 css.href = 'interfaces/mushra.css';
nickjillings@2161 303
nickjillings@2161 304 document.getElementsByTagName("head")[0].appendChild(css);
nickjillings@2161 305 break;
nickjillings@1329 306
nickjillings@1329 307 case "AB":
nickjillings@2161 308 interfaceJS.setAttribute("src","interfaces/AB.js");
nickjillings@2161 309
nickjillings@2161 310 // AB comes with a css file
nickjillings@2161 311 var css = document.createElement('link');
nickjillings@2161 312 css.rel = 'stylesheet';
nickjillings@2161 313 css.type = 'text/css';
nickjillings@2161 314 css.href = 'interfaces/AB.css';
nickjillings@2161 315
nickjillings@2161 316 document.getElementsByTagName("head")[0].appendChild(css);
nickjillings@2161 317 break;
nickjillings@2161 318
nickjillings@2161 319 case "ABX":
nickjillings@2161 320 interfaceJS.setAttribute("src","interfaces/ABX.js");
nickjillings@2161 321
nickjillings@2161 322 // AB comes with a css file
nickjillings@2161 323 var css = document.createElement('link');
nickjillings@2161 324 css.rel = 'stylesheet';
nickjillings@2161 325 css.type = 'text/css';
nickjillings@2161 326 css.href = 'interfaces/ABX.css';
nickjillings@2161 327
nickjillings@2161 328 document.getElementsByTagName("head")[0].appendChild(css);
nickjillings@2161 329 break;
nickjillings@2161 330
nickjillings@1345 331 case "Bipolar":
nickjillings@1345 332 case "ACR":
nickjillings@1345 333 case "DCR":
nickjillings@1345 334 case "CCR":
nickjillings@1343 335 case "ABC":
nickjillings@2161 336 // Above enumerate to horizontal sliders
nickjillings@2161 337 interfaceJS.setAttribute("src","interfaces/horizontal-sliders.js");
nickjillings@2161 338
nickjillings@2161 339 // horizontal-sliders comes with a css file
nickjillings@2161 340 var css = document.createElement('link');
nickjillings@2161 341 css.rel = 'stylesheet';
nickjillings@2161 342 css.type = 'text/css';
nickjillings@2161 343 css.href = 'interfaces/horizontal-sliders.css';
nickjillings@2161 344
nickjillings@2161 345 document.getElementsByTagName("head")[0].appendChild(css);
nickjillings@2161 346 break;
nickjillings@1345 347 case "discrete":
nickjillings@1345 348 case "likert":
nickjillings@2161 349 // Above enumerate to horizontal discrete radios
nickjillings@2161 350 interfaceJS.setAttribute("src","interfaces/discrete.js");
nickjillings@2161 351
nickjillings@2161 352 // horizontal-sliders comes with a css file
nickjillings@2161 353 var css = document.createElement('link');
nickjillings@2161 354 css.rel = 'stylesheet';
nickjillings@2161 355 css.type = 'text/css';
nickjillings@2161 356 css.href = 'interfaces/discrete.css';
nickjillings@2161 357
nickjillings@2161 358 document.getElementsByTagName("head")[0].appendChild(css);
nickjillings@2161 359 break;
nickjillings@1408 360 }
nickjillings@1408 361 document.getElementsByTagName("head")[0].appendChild(interfaceJS);
nickjillings@1408 362
nickjillings@1410 363 // Create the audio engine object
nickjillings@1410 364 audioEngineContext = new AudioEngine(specification);
nickjillings@1408 365 }
nickjillings@1408 366
nickjillings@1408 367 function createProjectSave(destURL) {
nickjillings@1408 368 // Save the data from interface into XML and send to destURL
nickjillings@1408 369 // If destURL is null then download XML in client
nickjillings@1408 370 // Now time to render file locally
nickjillings@1408 371 var xmlDoc = interfaceXMLSave();
nickjillings@1408 372 var parent = document.createElement("div");
nickjillings@1408 373 parent.appendChild(xmlDoc);
nickjillings@1408 374 var file = [parent.innerHTML];
nickjillings@2150 375 if (destURL == "local") {
nickjillings@1408 376 var bb = new Blob(file,{type : 'application/xml'});
nickjillings@1408 377 var dnlk = window.URL.createObjectURL(bb);
nickjillings@1408 378 var a = document.createElement("a");
nickjillings@1408 379 a.hidden = '';
nickjillings@1408 380 a.href = dnlk;
nickjillings@1408 381 a.download = "save.xml";
nickjillings@1408 382 a.textContent = "Save File";
nickjillings@1408 383
nickjillings@1408 384 popup.showPopup();
nickjillings@1332 385 popup.popupContent.innerHTML = "</span>Please save the file below to give to your test supervisor</span><br>";
nickjillings@1408 386 popup.popupContent.appendChild(a);
nickjillings@1408 387 } else {
nickjillings@1408 388 var xmlhttp = new XMLHttpRequest;
nickjillings@1293 389 xmlhttp.open("POST","\save.php?key="+storage.SessionKey.key,true);
nickjillings@1408 390 xmlhttp.setRequestHeader('Content-Type', 'text/xml');
nickjillings@1408 391 xmlhttp.onerror = function(){
nickjillings@1408 392 console.log('Error saving file to server! Presenting download locally');
nickjillings@1293 393 createProjectSave("local");
nickjillings@1408 394 };
nickjillings@2150 395 xmlhttp.onload = function() {
nickjillings@2150 396 console.log(xmlhttp);
nickjillings@2150 397 if (this.status >= 300) {
nickjillings@2150 398 console.log("WARNING - Could not update at this time");
nickjillings@1293 399 createProjectSave("local");
nickjillings@2150 400 } else {
nickjillings@2150 401 var parser = new DOMParser();
nickjillings@2150 402 var xmlDoc = parser.parseFromString(xmlhttp.responseText, "application/xml");
nickjillings@2150 403 var response = xmlDoc.getElementsByTagName('response')[0];
nickjillings@2150 404 if (response.getAttribute("state") == "OK") {
nickjillings@2150 405 var file = response.getElementsByTagName("file")[0];
nickjillings@2150 406 console.log("Save: OK, written "+file.getAttribute("bytes")+"B");
nickjillings@2155 407 popup.popupContent.textContent = "Thank you. Your session has been saved.";
nickjillings@2150 408 } else {
nickjillings@2150 409 var message = response.getElementsByTagName("message");
nickjillings@2150 410 console.log("Save: Error! "+message.textContent);
nickjillings@2150 411 createProjectSave("local");
nickjillings@2150 412 }
nickjillings@2150 413 }
nickjillings@2150 414 };
nickjillings@1408 415 xmlhttp.send(file);
nickjillings@1332 416 popup.showPopup();
nickjillings@1332 417 popup.popupContent.innerHTML = null;
nickjillings@1332 418 popup.popupContent.textContent = "Submitting. Please Wait";
nickjillings@2106 419 popup.hideNextButton();
nickjillings@2106 420 popup.hidePreviousButton();
nickjillings@1408 421 }
nickjillings@1408 422 }
nickjillings@1408 423
nickjillings@1408 424 function errorSessionDump(msg){
nickjillings@1408 425 // Create the partial interface XML save
nickjillings@1408 426 // Include error node with message on why the dump occured
nickjillings@1443 427 popup.showPopup();
nickjillings@1443 428 popup.popupContent.innerHTML = null;
nickjillings@1443 429 var err = document.createElement('error');
nickjillings@1443 430 var parent = document.createElement("div");
nickjillings@1443 431 if (typeof msg === "object")
nickjillings@1443 432 {
nickjillings@1443 433 err.appendChild(msg);
nickjillings@1443 434 popup.popupContent.appendChild(msg);
nickjillings@1443 435
nickjillings@1443 436 } else {
nickjillings@1443 437 err.textContent = msg;
nickjillings@1443 438 popup.popupContent.innerHTML = "ERROR : "+msg;
nickjillings@1443 439 }
nickjillings@1408 440 var xmlDoc = interfaceXMLSave();
nickjillings@1408 441 xmlDoc.appendChild(err);
nickjillings@1408 442 parent.appendChild(xmlDoc);
nickjillings@1408 443 var file = [parent.innerHTML];
nickjillings@1408 444 var bb = new Blob(file,{type : 'application/xml'});
nickjillings@1408 445 var dnlk = window.URL.createObjectURL(bb);
nickjillings@1408 446 var a = document.createElement("a");
nickjillings@1408 447 a.hidden = '';
nickjillings@1408 448 a.href = dnlk;
nickjillings@1408 449 a.download = "save.xml";
nickjillings@1408 450 a.textContent = "Save File";
nickjillings@1408 451
nickjillings@1443 452
nickjillings@1443 453
nickjillings@1408 454 popup.popupContent.appendChild(a);
nickjillings@1408 455 }
nickjillings@1408 456
nickjillings@1408 457 // Only other global function which must be defined in the interface class. Determines how to create the XML document.
nickjillings@1408 458 function interfaceXMLSave(){
nickjillings@1408 459 // Create the XML string to be exported with results
nickjillings@1324 460 return storage.finish();
nickjillings@1408 461 }
nickjillings@1408 462
nickjillings@1426 463 function linearToDecibel(gain)
nickjillings@1426 464 {
nickjillings@1426 465 return 20.0*Math.log10(gain);
nickjillings@1426 466 }
nickjillings@1426 467
nickjillings@1426 468 function decibelToLinear(gain)
nickjillings@1426 469 {
nickjillings@1426 470 return Math.pow(10,gain/20.0);
nickjillings@1426 471 }
nickjillings@1426 472
nickjillings@2147 473 function randomString(length) {
nickjillings@2147 474 return Math.round((Math.pow(36, length + 1) - Math.random() * Math.pow(36, length))).toString(36).slice(1);
nickjillings@2147 475 }
nickjillings@2147 476
nickjillings@1622 477 function interfacePopup() {
nickjillings@1622 478 // Creates an object to manage the popup
nickjillings@1622 479 this.popup = null;
nickjillings@1622 480 this.popupContent = null;
nickjillings@1526 481 this.popupTitle = null;
nickjillings@1526 482 this.popupResponse = null;
nickjillings@1574 483 this.buttonProceed = null;
nickjillings@2034 484 this.buttonPrevious = null;
nickjillings@1622 485 this.popupOptions = null;
nickjillings@1622 486 this.currentIndex = null;
nickjillings@1324 487 this.node = null;
nickjillings@1324 488 this.store = null;
nickjillings@1422 489 $(window).keypress(function(e){
nickjillings@1422 490 if (e.keyCode == 13 && popup.popup.style.visibility == 'visible')
nickjillings@1422 491 {
nickjillings@1422 492 console.log(e);
nickjillings@1422 493 popup.buttonProceed.onclick();
nickjillings@1424 494 e.preventDefault();
nickjillings@1422 495 }
nickjillings@1422 496 });
nickjillings@1581 497
nickjillings@1622 498 this.createPopup = function(){
nickjillings@1622 499 // Create popup window interface
nickjillings@1622 500 var insertPoint = document.getElementById("topLevelBody");
nickjillings@1622 501
nickjillings@1379 502 this.popup = document.getElementById('popupHolder');
nickjillings@1622 503 this.popup.style.left = (window.innerWidth/2)-250 + 'px';
nickjillings@1622 504 this.popup.style.top = (window.innerHeight/2)-125 + 'px';
nickjillings@1622 505
nickjillings@1379 506 this.popupContent = document.getElementById('popupContent');
nickjillings@1622 507
nickjillings@1379 508 this.popupTitle = document.getElementById('popupTitle');
nickjillings@1526 509
nickjillings@1379 510 this.popupResponse = document.getElementById('popupResponse');
nickjillings@1526 511
nickjillings@1379 512 this.buttonProceed = document.getElementById('popup-proceed');
nickjillings@1574 513 this.buttonProceed.onclick = function(){popup.proceedClicked();};
nickjillings@2034 514
nickjillings@1379 515 this.buttonPrevious = document.getElementById('popup-previous');
nickjillings@2034 516 this.buttonPrevious.onclick = function(){popup.previousClick();};
nickjillings@2034 517
nickjillings@1379 518 this.hidePopup();
nickjillings@1379 519
nickjillings@1581 520 this.popup.style.zIndex = -1;
nickjillings@1581 521 this.popup.style.visibility = 'hidden';
nickjillings@1622 522 };
nickjillings@1621 523
nickjillings@1622 524 this.showPopup = function(){
nickjillings@1581 525 if (this.popup == null) {
nickjillings@1622 526 this.createPopup();
nickjillings@1622 527 }
nickjillings@1622 528 this.popup.style.zIndex = 3;
nickjillings@1622 529 this.popup.style.visibility = 'visible';
nickjillings@1622 530 var blank = document.getElementsByClassName('testHalt')[0];
nickjillings@1622 531 blank.style.zIndex = 2;
nickjillings@1622 532 blank.style.visibility = 'visible';
nickjillings@1622 533 };
nickjillings@1622 534
nickjillings@1622 535 this.hidePopup = function(){
nickjillings@1622 536 this.popup.style.zIndex = -1;
nickjillings@1622 537 this.popup.style.visibility = 'hidden';
nickjillings@1622 538 var blank = document.getElementsByClassName('testHalt')[0];
nickjillings@1622 539 blank.style.zIndex = -2;
nickjillings@1622 540 blank.style.visibility = 'hidden';
nickjillings@1526 541 this.buttonPrevious.style.visibility = 'inherit';
nickjillings@1622 542 };
nickjillings@1622 543
nickjillings@1622 544 this.postNode = function() {
nickjillings@1622 545 // This will take the node from the popupOptions and display it
nickjillings@1622 546 var node = this.popupOptions[this.currentIndex];
nickjillings@1526 547 this.popupResponse.innerHTML = null;
nickjillings@1324 548 this.popupTitle.textContent = node.specification.statement;
nickjillings@1324 549 if (node.specification.type == 'question') {
nickjillings@1622 550 var textArea = document.createElement('textarea');
nickjillings@1324 551 switch (node.specification.boxsize) {
nickjillings@2030 552 case 'small':
nickjillings@2030 553 textArea.cols = "20";
nickjillings@2030 554 textArea.rows = "1";
nickjillings@2030 555 break;
nickjillings@2030 556 case 'normal':
nickjillings@2030 557 textArea.cols = "30";
nickjillings@2030 558 textArea.rows = "2";
nickjillings@2030 559 break;
nickjillings@2030 560 case 'large':
nickjillings@2030 561 textArea.cols = "40";
nickjillings@2030 562 textArea.rows = "5";
nickjillings@2030 563 break;
nickjillings@2030 564 case 'huge':
nickjillings@2030 565 textArea.cols = "50";
nickjillings@2030 566 textArea.rows = "10";
nickjillings@2030 567 break;
nickjillings@2030 568 }
nickjillings@1361 569 if (node.response == undefined) {
nickjillings@1361 570 node.response = "";
nickjillings@1361 571 } else {
nickjillings@1361 572 textArea.value = node.response;
nickjillings@1361 573 }
nickjillings@1526 574 this.popupResponse.appendChild(textArea);
nickjillings@1526 575 textArea.focus();
nickjillings@1382 576 this.popupResponse.style.textAlign="center";
nickjillings@1382 577 this.popupResponse.style.left="0%";
nickjillings@1324 578 } else if (node.specification.type == 'checkbox') {
nickjillings@1361 579 if (node.response == undefined) {
nickjillings@1361 580 node.response = Array(node.specification.options.length);
nickjillings@1361 581 }
nickjillings@1361 582 var index = 0;
nickjillings@1382 583 var max_w = 0;
nickjillings@1324 584 for (var option of node.specification.options) {
nickjillings@1588 585 var input = document.createElement('input');
nickjillings@2093 586 input.id = option.name;
nickjillings@1588 587 input.type = 'checkbox';
nickjillings@1588 588 var span = document.createElement('span');
nickjillings@1588 589 span.textContent = option.text;
nickjillings@1588 590 var hold = document.createElement('div');
nickjillings@1588 591 hold.setAttribute('name','option');
nickjillings@1588 592 hold.style.padding = '4px';
nickjillings@1588 593 hold.appendChild(input);
nickjillings@1588 594 hold.appendChild(span);
nickjillings@1324 595 this.popupResponse.appendChild(hold);
nickjillings@1361 596 if (node.response[index] != undefined){
nickjillings@1361 597 if (node.response[index].checked == true) {
nickjillings@1361 598 input.checked = "true";
nickjillings@1361 599 }
nickjillings@1361 600 }
nickjillings@1382 601 var w = $(span).width();
nickjillings@1382 602 if (w > max_w)
nickjillings@1382 603 max_w = w;
nickjillings@1361 604 index++;
nickjillings@1588 605 }
nickjillings@1382 606 max_w += 12;
nickjillings@1382 607 this.popupResponse.style.textAlign="";
nickjillings@1382 608 var leftP = ((max_w/500)/2)*100;
nickjillings@1382 609 this.popupResponse.style.left=leftP+"%";
nickjillings@1324 610 } else if (node.specification.type == 'radio') {
nickjillings@1361 611 if (node.response == undefined) {
nickjillings@1361 612 node.response = {name: "", text: ""};
nickjillings@1361 613 }
nickjillings@1361 614 var index = 0;
nickjillings@1382 615 var max_w = 0;
nickjillings@1324 616 for (var option of node.specification.options) {
nickjillings@1589 617 var input = document.createElement('input');
nickjillings@1589 618 input.id = option.name;
nickjillings@1589 619 input.type = 'radio';
nickjillings@1324 620 input.name = node.specification.id;
nickjillings@1589 621 var span = document.createElement('span');
nickjillings@1589 622 span.textContent = option.text;
nickjillings@1589 623 var hold = document.createElement('div');
nickjillings@1589 624 hold.setAttribute('name','option');
nickjillings@1589 625 hold.style.padding = '4px';
nickjillings@1589 626 hold.appendChild(input);
nickjillings@1589 627 hold.appendChild(span);
nickjillings@1324 628 this.popupResponse.appendChild(hold);
nickjillings@1361 629 if (input.id == node.response.name) {
nickjillings@1361 630 input.checked = "true";
nickjillings@1361 631 }
nickjillings@1382 632 var w = $(span).width();
nickjillings@1382 633 if (w > max_w)
nickjillings@1382 634 max_w = w;
nickjillings@1589 635 }
nickjillings@1382 636 max_w += 12;
nickjillings@1382 637 this.popupResponse.style.textAlign="";
nickjillings@1382 638 var leftP = ((max_w/500)/2)*100;
nickjillings@1382 639 this.popupResponse.style.left=leftP+"%";
nickjillings@1324 640 } else if (node.specification.type == 'number') {
nickjillings@1573 641 var input = document.createElement('input');
nickjillings@2044 642 input.type = 'textarea';
nickjillings@1324 643 if (node.min != null) {input.min = node.specification.min;}
nickjillings@1324 644 if (node.max != null) {input.max = node.specification.max;}
nickjillings@1324 645 if (node.step != null) {input.step = node.specification.step;}
nickjillings@1361 646 if (node.response != undefined) {
nickjillings@1361 647 input.value = node.response;
nickjillings@1361 648 }
nickjillings@1526 649 this.popupResponse.appendChild(input);
nickjillings@1382 650 this.popupResponse.style.textAlign="center";
nickjillings@1382 651 this.popupResponse.style.left="0%";
nickjillings@1622 652 }
nickjillings@2034 653 if(this.currentIndex+1 == this.popupOptions.length) {
nickjillings@1324 654 if (this.node.location == "pre") {
nickjillings@1531 655 this.buttonProceed.textContent = 'Start';
nickjillings@1531 656 } else {
nickjillings@1531 657 this.buttonProceed.textContent = 'Submit';
nickjillings@1531 658 }
nickjillings@2034 659 } else {
nickjillings@2034 660 this.buttonProceed.textContent = 'Next';
nickjillings@2034 661 }
nickjillings@2034 662 if(this.currentIndex > 0)
nickjillings@1526 663 this.buttonPrevious.style.visibility = 'visible';
nickjillings@1526 664 else
nickjillings@1526 665 this.buttonPrevious.style.visibility = 'hidden';
nickjillings@2015 666 };
nickjillings@1622 667
nickjillings@1324 668 this.initState = function(node,store) {
nickjillings@1622 669 //Call this with your preTest and postTest nodes when needed to
nickjillings@1622 670 // initialise the popup procedure.
nickjillings@1324 671 if (node.options.length > 0) {
nickjillings@1324 672 this.popupOptions = [];
nickjillings@1324 673 this.node = node;
nickjillings@1324 674 this.store = store;
nickjillings@1324 675 for (var opt of node.options)
nickjillings@1324 676 {
nickjillings@1324 677 this.popupOptions.push({
nickjillings@1324 678 specification: opt,
nickjillings@1324 679 response: null
nickjillings@1324 680 });
nickjillings@1324 681 }
nickjillings@1622 682 this.currentIndex = 0;
nickjillings@1622 683 this.showPopup();
nickjillings@1622 684 this.postNode();
nickjillings@1581 685 } else {
nickjillings@1581 686 advanceState();
nickjillings@1622 687 }
nickjillings@2015 688 };
nickjillings@1622 689
nickjillings@1574 690 this.proceedClicked = function() {
nickjillings@1622 691 // Each time the popup button is clicked!
nickjillings@1622 692 var node = this.popupOptions[this.currentIndex];
nickjillings@1324 693 if (node.specification.type == 'question') {
nickjillings@1622 694 // Must extract the question data
nickjillings@1622 695 var textArea = $(popup.popupContent).find('textarea')[0];
nickjillings@1324 696 if (node.specification.mandatory == true && textArea.value.length == 0) {
nickjillings@1622 697 alert('This question is mandatory');
nickjillings@1622 698 return;
nickjillings@1622 699 } else {
nickjillings@1622 700 // Save the text content
nickjillings@1324 701 console.log("Question: "+ node.specification.statement);
nickjillings@1623 702 console.log("Question Response: "+ textArea.value);
nickjillings@1324 703 node.response = textArea.value;
nickjillings@1622 704 }
nickjillings@1324 705 } else if (node.specification.type == 'checkbox') {
nickjillings@1588 706 // Must extract checkbox data
nickjillings@1326 707 console.log("Checkbox: "+ node.specification.statement);
nickjillings@1324 708 var inputs = this.popupResponse.getElementsByTagName('input');
nickjillings@1324 709 node.response = [];
nickjillings@1324 710 for (var i=0; i<node.specification.options.length; i++) {
nickjillings@1324 711 node.response.push({
nickjillings@1324 712 name: node.specification.options[i].name,
nickjillings@1324 713 text: node.specification.options[i].text,
nickjillings@1324 714 checked: inputs[i].checked
nickjillings@1324 715 });
nickjillings@1326 716 console.log(node.specification.options[i].name+": "+ inputs[i].checked);
nickjillings@1324 717 }
nickjillings@1324 718 } else if (node.specification.type == "radio") {
nickjillings@1526 719 var optHold = this.popupResponse;
nickjillings@1324 720 console.log("Radio: "+ node.specification.statement);
nickjillings@1324 721 node.response = null;
nickjillings@1589 722 var i=0;
nickjillings@1324 723 var inputs = optHold.getElementsByTagName('input');
nickjillings@1324 724 while(node.response == null) {
nickjillings@1324 725 if (i == inputs.length)
nickjillings@1324 726 {
nickjillings@1324 727 if (node.specification.mandatory == true)
nickjillings@1324 728 {
nickjillings@1324 729 alert("This radio is mandatory");
nickjillings@1324 730 } else {
nickjillings@1324 731 node.response = -1;
nickjillings@1324 732 }
nickjillings@1324 733 return;
nickjillings@1324 734 }
nickjillings@1324 735 if (inputs[i].checked == true) {
nickjillings@1324 736 node.response = node.specification.options[i];
nickjillings@1324 737 console.log("Selected: "+ node.specification.options[i].name);
nickjillings@1589 738 }
nickjillings@1589 739 i++;
nickjillings@1589 740 }
nickjillings@1324 741 } else if (node.specification.type == "number") {
nickjillings@1573 742 var input = this.popupContent.getElementsByTagName('input')[0];
nickjillings@1573 743 if (node.mandatory == true && input.value.length == 0) {
nickjillings@1574 744 alert('This question is mandatory. Please enter a number');
nickjillings@1574 745 return;
nickjillings@1574 746 }
nickjillings@1574 747 var enteredNumber = Number(input.value);
nickjillings@2044 748 if (isNaN(enteredNumber)) {
nickjillings@1574 749 alert('Please enter a valid number');
nickjillings@1574 750 return;
nickjillings@1574 751 }
nickjillings@1574 752 if (enteredNumber < node.min && node.min != null) {
nickjillings@1574 753 alert('Number is below the minimum value of '+node.min);
nickjillings@1574 754 return;
nickjillings@1574 755 }
nickjillings@1574 756 if (enteredNumber > node.max && node.max != null) {
nickjillings@1574 757 alert('Number is above the maximum value of '+node.max);
nickjillings@1573 758 return;
nickjillings@1573 759 }
nickjillings@1324 760 node.response = input.value;
nickjillings@1622 761 }
nickjillings@1622 762 this.currentIndex++;
nickjillings@1622 763 if (this.currentIndex < this.popupOptions.length) {
nickjillings@1622 764 this.postNode();
nickjillings@1622 765 } else {
nickjillings@1622 766 // Reached the end of the popupOptions
nickjillings@1622 767 this.hidePopup();
nickjillings@1324 768 for (var node of this.popupOptions)
nickjillings@1324 769 {
nickjillings@1324 770 this.store.postResult(node);
nickjillings@1634 771 }
nickjillings@1294 772 this.store.complete();
nickjillings@1622 773 advanceState();
nickjillings@1622 774 }
nickjillings@2015 775 };
nickjillings@2034 776
nickjillings@2034 777 this.previousClick = function() {
nickjillings@2034 778 // Triggered when the 'Back' button is clicked in the survey
nickjillings@2034 779 if (this.currentIndex > 0) {
nickjillings@2034 780 this.currentIndex--;
nickjillings@2034 781 this.postNode();
nickjillings@2034 782 }
nickjillings@2034 783 };
nickjillings@1421 784
nickjillings@1421 785 this.resize = function(event)
nickjillings@1421 786 {
nickjillings@1421 787 // Called on window resize;
nickjillings@1344 788 if (this.popup != null) {
nickjillings@1344 789 this.popup.style.left = (window.innerWidth/2)-250 + 'px';
nickjillings@1344 790 this.popup.style.top = (window.innerHeight/2)-125 + 'px';
nickjillings@1344 791 var blank = document.getElementsByClassName('testHalt')[0];
nickjillings@1344 792 blank.style.width = window.innerWidth;
nickjillings@1344 793 blank.style.height = window.innerHeight;
nickjillings@1344 794 }
nickjillings@1421 795 };
nickjillings@2106 796 this.hideNextButton = function() {
nickjillings@2106 797 this.buttonProceed.style.visibility = "hidden";
nickjillings@2106 798 }
nickjillings@2106 799 this.hidePreviousButton = function() {
nickjillings@2106 800 this.buttonPrevious.style.visibility = "hidden";
nickjillings@2106 801 }
nickjillings@2106 802 this.showNextButton = function() {
nickjillings@2106 803 this.buttonProceed.style.visibility = "visible";
nickjillings@2106 804 }
nickjillings@2106 805 this.showPreviousButton = function() {
nickjillings@2106 806 this.buttonPrevious.style.visibility = "visible";
nickjillings@2106 807 }
nickjillings@1621 808 }
nickjillings@1621 809
nickjillings@1622 810 function advanceState()
nickjillings@1621 811 {
nickjillings@1634 812 // Just for complete clarity
nickjillings@1634 813 testState.advanceState();
nickjillings@1634 814 }
nickjillings@1634 815
nickjillings@1634 816 function stateMachine()
nickjillings@1634 817 {
nickjillings@1634 818 // Object prototype for tracking and managing the test state
nickjillings@1634 819 this.stateMap = [];
nickjillings@1324 820 this.preTestSurvey = null;
nickjillings@1324 821 this.postTestSurvey = null;
nickjillings@1634 822 this.stateIndex = null;
nickjillings@1324 823 this.currentStateMap = null;
nickjillings@1324 824 this.currentStatePosition = null;
nickjillings@1354 825 this.currentStore = null;
nickjillings@1634 826 this.initialise = function(){
nickjillings@1324 827
nickjillings@1324 828 // Get the data from Specification
nickjillings@1324 829 var pageHolder = [];
nickjillings@1324 830 for (var page of specification.pages)
nickjillings@1324 831 {
nickjillings@1380 832 var repeat = page.repeatCount;
nickjillings@1380 833 while(repeat >= 0)
nickjillings@1380 834 {
nickjillings@1380 835 pageHolder.push(page);
nickjillings@1380 836 repeat--;
nickjillings@1380 837 }
nickjillings@1324 838 }
nickjillings@1324 839 if (specification.randomiseOrder)
nickjillings@1324 840 {
nickjillings@1324 841 pageHolder = randomiseOrder(pageHolder);
nickjillings@1324 842 }
nickjillings@1324 843 for (var i=0; i<pageHolder.length; i++)
nickjillings@1324 844 {
nickjillings@1324 845 pageHolder[i].presentedId = i;
nickjillings@1324 846 }
nickjillings@1324 847 for (var i=0; i<specification.pages.length; i++)
nickjillings@1324 848 {
nickjillings@2124 849 if (specification.testPages <= i && specification.testPages != 0) {break;}
nickjillings@1324 850 this.stateMap.push(pageHolder[i]);
nickjillings@2149 851 storage.createTestPageStore(pageHolder[i]);
nickjillings@2152 852 for (var element of pageHolder[i].audioElements) {
nickjillings@2152 853 var URL = pageHolder[i].hostURL + element.url;
nickjillings@2152 854 var buffer = null;
nickjillings@2152 855 for (var buffObj of audioEngineContext.buffers) {
nickjillings@2152 856 if (URL == buffObj.url) {
nickjillings@2152 857 buffer = buffObj;
nickjillings@2152 858 break;
nickjillings@2152 859 }
nickjillings@2152 860 }
nickjillings@2152 861 if (buffer == null) {
nickjillings@2152 862 buffer = new audioEngineContext.bufferObj();
nickjillings@2152 863 buffer.getMedia(URL);
nickjillings@2152 864 audioEngineContext.buffers.push(buffer);
nickjillings@2152 865 }
nickjillings@2152 866 }
nickjillings@1324 867 }
nickjillings@2124 868
nickjillings@1324 869 if (specification.preTest != null) {this.preTestSurvey = specification.preTest;}
nickjillings@1324 870 if (specification.postTest != null) {this.postTestSurvey = specification.postTest;}
nickjillings@1324 871
nickjillings@1634 872 if (this.stateMap.length > 0) {
nickjillings@1634 873 if(this.stateIndex != null) {
nickjillings@1634 874 console.log('NOTE - State already initialise');
nickjillings@1634 875 }
nickjillings@1634 876 this.stateIndex = -1;
nickjillings@1634 877 } else {
b@2059 878 console.log('FATAL - StateMap not correctly constructed. EMPTY_STATE_MAP');
nickjillings@1622 879 }
nickjillings@1634 880 };
nickjillings@1634 881 this.advanceState = function(){
nickjillings@1634 882 if (this.stateIndex == null) {
nickjillings@1634 883 this.initialise();
nickjillings@1634 884 }
nickjillings@2150 885 storage.update();
nickjillings@1634 886 if (this.stateIndex == -1) {
nickjillings@1342 887 this.stateIndex++;
nickjillings@1634 888 console.log('Starting test...');
nickjillings@1324 889 if (this.preTestSurvey != null)
nickjillings@1324 890 {
nickjillings@1324 891 popup.initState(this.preTestSurvey,storage.globalPreTest);
nickjillings@1342 892 } else {
nickjillings@1342 893 this.advanceState();
nickjillings@1318 894 }
nickjillings@1324 895 } else if (this.stateIndex == this.stateMap.length)
nickjillings@1324 896 {
nickjillings@1324 897 // All test pages complete, post test
nickjillings@1324 898 console.log('Ending test ...');
nickjillings@1324 899 this.stateIndex++;
nickjillings@1324 900 if (this.postTestSurvey == null) {
nickjillings@1324 901 this.advanceState();
nickjillings@1318 902 } else {
nickjillings@1324 903 popup.initState(this.postTestSurvey,storage.globalPostTest);
nickjillings@1324 904 }
nickjillings@1324 905 } else if (this.stateIndex > this.stateMap.length)
nickjillings@1324 906 {
nickjillings@1324 907 createProjectSave(specification.projectReturn);
nickjillings@1634 908 }
nickjillings@1324 909 else
nickjillings@1324 910 {
nickjillings@1324 911 if (this.currentStateMap == null)
nickjillings@1324 912 {
nickjillings@1318 913 this.currentStateMap = this.stateMap[this.stateIndex];
nickjillings@1334 914 if (this.currentStateMap.randomiseOrder)
nickjillings@1334 915 {
nickjillings@1334 916 this.currentStateMap.audioElements = randomiseOrder(this.currentStateMap.audioElements);
nickjillings@1334 917 }
nickjillings@2149 918 this.currentStore = storage.testPages[this.stateIndex];
nickjillings@1324 919 if (this.currentStateMap.preTest != null)
nickjillings@1324 920 {
nickjillings@1324 921 this.currentStatePosition = 'pre';
nickjillings@1324 922 popup.initState(this.currentStateMap.preTest,storage.testPages[this.stateIndex].preTest);
nickjillings@1318 923 } else {
nickjillings@1324 924 this.currentStatePosition = 'test';
nickjillings@1324 925 }
nickjillings@1324 926 interfaceContext.newPage(this.currentStateMap,storage.testPages[this.stateIndex]);
nickjillings@1324 927 return;
nickjillings@1634 928 }
nickjillings@1324 929 switch(this.currentStatePosition)
nickjillings@1324 930 {
nickjillings@1324 931 case 'pre':
nickjillings@1324 932 this.currentStatePosition = 'test';
nickjillings@1324 933 break;
nickjillings@1324 934 case 'test':
nickjillings@1324 935 this.currentStatePosition = 'post';
nickjillings@1324 936 // Save the data
nickjillings@1324 937 this.testPageCompleted();
nickjillings@1324 938 if (this.currentStateMap.postTest == null)
nickjillings@1324 939 {
nickjillings@1318 940 this.advanceState();
nickjillings@1324 941 return;
nickjillings@1634 942 } else {
nickjillings@1324 943 popup.initState(this.currentStateMap.postTest,storage.testPages[this.stateIndex].postTest);
nickjillings@1634 944 }
nickjillings@1324 945 break;
nickjillings@1324 946 case 'post':
nickjillings@1324 947 this.stateIndex++;
nickjillings@1324 948 this.currentStateMap = null;
nickjillings@1324 949 this.advanceState();
nickjillings@1324 950 break;
nickjillings@1324 951 };
nickjillings@1634 952 }
nickjillings@1634 953 };
nickjillings@1634 954
nickjillings@1324 955 this.testPageCompleted = function() {
nickjillings@1634 956 // Function called each time a test page has been completed
nickjillings@1324 957 var storePoint = storage.testPages[this.stateIndex];
nickjillings@1324 958 // First get the test metric
nickjillings@1324 959
nickjillings@1324 960 var metric = storePoint.XMLDOM.getElementsByTagName('metric')[0];
nickjillings@1412 961 if (audioEngineContext.metric.enableTestTimer)
nickjillings@1412 962 {
nickjillings@1324 963 var testTime = storePoint.parent.document.createElement('metricresult');
nickjillings@1412 964 testTime.id = 'testTime';
nickjillings@1412 965 testTime.textContent = audioEngineContext.timer.testDuration;
nickjillings@1412 966 metric.appendChild(testTime);
nickjillings@1412 967 }
nickjillings@1324 968
nickjillings@1412 969 var audioObjects = audioEngineContext.audioObjects;
nickjillings@1324 970 for (var ao of audioEngineContext.audioObjects)
nickjillings@1412 971 {
nickjillings@1324 972 ao.exportXMLDOM();
nickjillings@1412 973 }
nickjillings@1324 974 for (var element of interfaceContext.commentQuestions)
nickjillings@1324 975 {
nickjillings@1324 976 element.exportXMLDOM(storePoint);
nickjillings@1324 977 }
nickjillings@1324 978 pageXMLSave(storePoint.XMLDOM, this.currentStateMap);
nickjillings@1294 979 storePoint.complete();
nickjillings@1576 980 };
nickjillings@1621 981 }
nickjillings@1621 982
nickjillings@1408 983 function AudioEngine(specification) {
nickjillings@1682 984
nickjillings@1682 985 // Create two output paths, the main outputGain and fooGain.
nickjillings@1682 986 // Output gain is default to 1 and any items for playback route here
nickjillings@1682 987 // Foo gain is used for analysis to ensure paths get processed, but are not heard
nickjillings@1682 988 // because web audio will optimise and any route which does not go to the destination gets ignored.
nickjillings@1682 989 this.outputGain = audioContext.createGain();
nickjillings@1682 990 this.fooGain = audioContext.createGain();
nickjillings@1682 991 this.fooGain.gain = 0;
nickjillings@1682 992
nickjillings@1688 993 // Use this to detect playback state: 0 - stopped, 1 - playing
nickjillings@1688 994 this.status = 0;
nickjillings@1688 995
nickjillings@1682 996 // Connect both gains to output
nickjillings@1682 997 this.outputGain.connect(audioContext.destination);
nickjillings@1682 998 this.fooGain.connect(audioContext.destination);
nickjillings@1682 999
nickjillings@1659 1000 // Create the timer Object
nickjillings@1659 1001 this.timer = new timer();
nickjillings@1659 1002 // Create session metrics
nickjillings@1408 1003 this.metric = new sessionMetrics(this,specification);
nickjillings@1659 1004
nickjillings@1667 1005 this.loopPlayback = false;
nickjillings@1667 1006
nickjillings@1324 1007 this.pageStore = null;
nickjillings@1324 1008
nickjillings@1682 1009 // Create store for new audioObjects
nickjillings@1682 1010 this.audioObjects = [];
nickjillings@1682 1011
nickjillings@1410 1012 this.buffers = [];
nickjillings@1430 1013 this.bufferObj = function()
nickjillings@1410 1014 {
nickjillings@1430 1015 this.url = null;
nickjillings@1410 1016 this.buffer = null;
nickjillings@1410 1017 this.xmlRequest = new XMLHttpRequest();
nickjillings@1396 1018 this.xmlRequest.parent = this;
nickjillings@1410 1019 this.users = [];
nickjillings@1316 1020 this.progress = 0;
nickjillings@1316 1021 this.status = 0;
nickjillings@1342 1022 this.ready = function()
nickjillings@1342 1023 {
nickjillings@1316 1024 if (this.status >= 2)
nickjillings@1316 1025 {
nickjillings@1316 1026 this.status = 3;
nickjillings@1316 1027 }
nickjillings@1342 1028 for (var i=0; i<this.users.length; i++)
nickjillings@1342 1029 {
nickjillings@1342 1030 this.users[i].state = 1;
nickjillings@1342 1031 if (this.users[i].interfaceDOM != null)
nickjillings@1342 1032 {
nickjillings@1342 1033 this.users[i].bufferLoaded(this);
nickjillings@1342 1034 }
nickjillings@1342 1035 }
nickjillings@1342 1036 };
nickjillings@1430 1037 this.getMedia = function(url) {
nickjillings@1430 1038 this.url = url;
nickjillings@1430 1039 this.xmlRequest.open('GET',this.url,true);
nickjillings@1430 1040 this.xmlRequest.responseType = 'arraybuffer';
nickjillings@1430 1041
nickjillings@1430 1042 var bufferObj = this;
nickjillings@1430 1043
nickjillings@1430 1044 // Create callback to decode the data asynchronously
nickjillings@1430 1045 this.xmlRequest.onloadend = function() {
nickjillings@1353 1046 // Use inbuilt WAVE decoder first
nickjillings@2113 1047 if (this.status == -1) {return;}
nickjillings@1353 1048 var waveObj = new WAVE();
nickjillings@1353 1049 if (waveObj.open(bufferObj.xmlRequest.response) == 0)
nickjillings@1353 1050 {
nickjillings@1353 1051 bufferObj.buffer = audioContext.createBuffer(waveObj.num_channels,waveObj.num_samples,waveObj.sample_rate);
nickjillings@1353 1052 for (var c=0; c<waveObj.num_channels; c++)
nickjillings@1353 1053 {
nickjillings@1353 1054 var buffer_ptr = bufferObj.buffer.getChannelData(c);
nickjillings@1353 1055 for (var n=0; n<waveObj.num_samples; n++)
nickjillings@1353 1056 {
nickjillings@1353 1057 buffer_ptr[n] = waveObj.decoded_data[c][n];
nickjillings@1353 1058 }
nickjillings@1353 1059 }
nickjillings@1316 1060
nickjillings@1353 1061 delete waveObj;
nickjillings@1353 1062 } else {
nickjillings@1353 1063 audioContext.decodeAudioData(bufferObj.xmlRequest.response, function(decodedData) {
nickjillings@1353 1064 bufferObj.buffer = decodedData;
nickjillings@1353 1065 }, function(e){
nickjillings@1353 1066 // Should only be called if there was an error, but sometimes gets called continuously
nickjillings@1353 1067 // Check here if the error is genuine
nickjillings@1353 1068 if (bufferObj.xmlRequest.response == undefined) {
nickjillings@1353 1069 // Genuine error
nickjillings@1353 1070 console.log('FATAL - Error loading buffer on '+audioObj.id);
nickjillings@1353 1071 if (request.status == 404)
nickjillings@1353 1072 {
nickjillings@1353 1073 console.log('FATAL - Fragment '+audioObj.id+' 404 error');
nickjillings@1353 1074 console.log('URL: '+audioObj.url);
nickjillings@1353 1075 errorSessionDump('Fragment '+audioObj.id+' 404 error');
nickjillings@1353 1076 }
nickjillings@2113 1077 this.parent.status = -1;
nickjillings@1353 1078 }
nickjillings@1353 1079 });
nickjillings@1353 1080 }
nickjillings@1353 1081 if (bufferObj.buffer != undefined)
nickjillings@1353 1082 {
nickjillings@1316 1083 bufferObj.status = 2;
nickjillings@1353 1084 calculateLoudness(bufferObj,"I");
nickjillings@1353 1085 }
nickjillings@1430 1086 };
nickjillings@2113 1087
nickjillings@2113 1088 // Create callback for any error in loading
nickjillings@2113 1089 this.xmlRequest.onerror = function() {
nickjillings@2113 1090 this.parent.status = -1;
nickjillings@2113 1091 for (var i=0; i<this.parent.users.length; i++)
nickjillings@2113 1092 {
nickjillings@2113 1093 this.parent.users[i].state = -1;
nickjillings@2113 1094 if (this.parent.users[i].interfaceDOM != null)
nickjillings@2113 1095 {
nickjillings@2113 1096 this.parent.users[i].bufferLoaded(this);
nickjillings@2113 1097 }
nickjillings@2113 1098 }
nickjillings@2113 1099 }
nickjillings@2113 1100
nickjillings@1433 1101 this.progress = 0;
nickjillings@1433 1102 this.progressCallback = function(event){
nickjillings@1433 1103 if (event.lengthComputable)
nickjillings@1433 1104 {
nickjillings@1396 1105 this.parent.progress = event.loaded / event.total;
nickjillings@1396 1106 for (var i=0; i<this.parent.users.length; i++)
nickjillings@1396 1107 {
nickjillings@1396 1108 if(this.parent.users[i].interfaceDOM != null)
nickjillings@1396 1109 {
nickjillings@1396 1110 if (typeof this.parent.users[i].interfaceDOM.updateLoading === "function")
nickjillings@1396 1111 {
nickjillings@1396 1112 this.parent.users[i].interfaceDOM.updateLoading(this.parent.progress*100);
nickjillings@1396 1113 }
nickjillings@1396 1114 }
nickjillings@1396 1115 }
nickjillings@1433 1116 }
nickjillings@1433 1117 };
nickjillings@1433 1118 this.xmlRequest.addEventListener("progress", this.progressCallback);
nickjillings@1316 1119 this.status = 1;
nickjillings@1430 1120 this.xmlRequest.send();
nickjillings@1410 1121 };
nickjillings@1316 1122
nickjillings@1316 1123 this.registerAudioObject = function(audioObject)
nickjillings@1316 1124 {
nickjillings@1316 1125 // Called by an audioObject to register to the buffer for use
nickjillings@1316 1126 // First check if already in the register pool
nickjillings@1316 1127 for (var objects of this.users)
nickjillings@1316 1128 {
nickjillings@1316 1129 if (audioObject.id == objects.id){return 0;}
nickjillings@1316 1130 }
nickjillings@1316 1131 this.users.push(audioObject);
nickjillings@2113 1132 if (this.status == 3 || this.status == -1)
nickjillings@1316 1133 {
nickjillings@1316 1134 // The buffer is already ready, trigger bufferLoaded
nickjillings@1316 1135 audioObject.bufferLoaded(this);
nickjillings@1316 1136 }
nickjillings@1316 1137 }
nickjillings@1410 1138 };
nickjillings@1410 1139
nickjillings@1565 1140 this.play = function(id) {
nickjillings@1620 1141 // Start the timer and set the audioEngine state to playing (1)
nickjillings@1523 1142 if (this.status == 0 && this.loopPlayback) {
nickjillings@1620 1143 // Check if all audioObjects are ready
nickjillings@1523 1144 if(this.checkAllReady())
nickjillings@1523 1145 {
nickjillings@1565 1146 this.status = 1;
nickjillings@1523 1147 this.setSynchronousLoop();
nickjillings@1565 1148 }
nickjillings@1565 1149 }
nickjillings@1523 1150 else
nickjillings@1523 1151 {
nickjillings@1523 1152 this.status = 1;
nickjillings@1523 1153 }
nickjillings@1565 1154 if (this.status== 1) {
nickjillings@1523 1155 this.timer.startTest();
nickjillings@1567 1156 if (id == undefined) {
nickjillings@1567 1157 id = -1;
nickjillings@1523 1158 console.log('FATAL - Passed id was undefined - AudioEngineContext.play(id)');
nickjillings@1523 1159 return;
nickjillings@1567 1160 } else {
nickjillings@1567 1161 interfaceContext.playhead.setTimePerPixel(this.audioObjects[id]);
nickjillings@1567 1162 }
nickjillings@1565 1163 if (this.loopPlayback) {
nickjillings@2099 1164 var setTime = audioContext.currentTime;
nickjillings@1565 1165 for (var i=0; i<this.audioObjects.length; i++)
nickjillings@1565 1166 {
nickjillings@2099 1167 this.audioObjects[i].play(setTime);
nickjillings@1565 1168 if (id == i) {
nickjillings@1360 1169 this.audioObjects[i].loopStart(setTime);
nickjillings@1565 1170 } else {
nickjillings@1360 1171 this.audioObjects[i].loopStop(setTime);
nickjillings@1636 1172 }
nickjillings@1636 1173 }
nickjillings@1565 1174 } else {
nickjillings@1360 1175 var setTime = audioContext.currentTime+0.1;
nickjillings@1565 1176 for (var i=0; i<this.audioObjects.length; i++)
nickjillings@1565 1177 {
nickjillings@1565 1178 if (i != id) {
nickjillings@1360 1179 this.audioObjects[i].stop(setTime);
nickjillings@1565 1180 } else if (i == id) {
nickjillings@1360 1181 this.audioObjects[id].play(setTime);
nickjillings@1565 1182 }
nickjillings@1565 1183 }
nickjillings@1620 1184 }
nickjillings@1567 1185 interfaceContext.playhead.start();
nickjillings@1620 1186 }
nickjillings@1620 1187 };
nickjillings@1682 1188
nickjillings@1620 1189 this.stop = function() {
nickjillings@1378 1190 // Send stop and reset command to all playback buffers
nickjillings@1620 1191 if (this.status == 1) {
nickjillings@1360 1192 var setTime = audioContext.currentTime+0.1;
nickjillings@1620 1193 for (var i=0; i<this.audioObjects.length; i++)
nickjillings@1620 1194 {
nickjillings@1360 1195 this.audioObjects[i].stop(setTime);
nickjillings@1620 1196 }
nickjillings@1567 1197 interfaceContext.playhead.stop();
nickjillings@1620 1198 }
nickjillings@1620 1199 };
nickjillings@1689 1200
nickjillings@1582 1201 this.newTrack = function(element) {
nickjillings@1682 1202 // Pull data from given URL into new audio buffer
nickjillings@1682 1203 // URLs must either be from the same source OR be setup to 'Access-Control-Allow-Origin'
nickjillings@1688 1204
nickjillings@1682 1205 // Create the audioObject with ID of the new track length;
nickjillings@1659 1206 audioObjectId = this.audioObjects.length;
nickjillings@1682 1207 this.audioObjects[audioObjectId] = new audioObject(audioObjectId);
nickjillings@1688 1208
nickjillings@1410 1209 // Check if audioObject buffer is currently stored by full URL
nickjillings@1324 1210 var URL = testState.currentStateMap.hostURL + element.url;
nickjillings@1410 1211 var buffer = null;
nickjillings@1410 1212 for (var i=0; i<this.buffers.length; i++)
nickjillings@1410 1213 {
nickjillings@1410 1214 if (URL == this.buffers[i].url)
nickjillings@1410 1215 {
nickjillings@1410 1216 buffer = this.buffers[i];
nickjillings@1410 1217 break;
nickjillings@1410 1218 }
nickjillings@1410 1219 }
nickjillings@1410 1220 if (buffer == null)
nickjillings@1410 1221 {
nickjillings@1426 1222 console.log("[WARN]: Buffer was not loaded in pre-test! "+URL);
nickjillings@1430 1223 buffer = new this.bufferObj();
nickjillings@1316 1224 this.buffers.push(buffer);
nickjillings@1430 1225 buffer.getMedia(URL);
nickjillings@1410 1226 }
nickjillings@1582 1227 this.audioObjects[audioObjectId].specification = element;
nickjillings@1426 1228 this.audioObjects[audioObjectId].url = URL;
nickjillings@1324 1229 // Obtain store node
nickjillings@1324 1230 var aeNodes = this.pageStore.XMLDOM.getElementsByTagName('audioelement');
nickjillings@1324 1231 for (var i=0; i<aeNodes.length; i++)
nickjillings@1410 1232 {
nickjillings@1294 1233 if(aeNodes[i].getAttribute("ref") == element.id)
nickjillings@1324 1234 {
nickjillings@1324 1235 this.audioObjects[audioObjectId].storeDOM = aeNodes[i];
nickjillings@1324 1236 break;
nickjillings@1324 1237 }
nickjillings@1410 1238 }
nickjillings@1316 1239 buffer.registerAudioObject(this.audioObjects[audioObjectId]);
nickjillings@1579 1240 return this.audioObjects[audioObjectId];
nickjillings@1697 1241 };
nickjillings@1682 1242
nickjillings@1369 1243 this.newTestPage = function(audioHolderObject,store) {
nickjillings@1324 1244 this.pageStore = store;
nickjillings@2127 1245 this.status = 0;
nickjillings@1620 1246 this.audioObjectsReady = false;
nickjillings@1620 1247 this.metric.reset();
nickjillings@1410 1248 for (var i=0; i < this.buffers.length; i++)
nickjillings@1410 1249 {
nickjillings@1410 1250 this.buffers[i].users = [];
nickjillings@1410 1251 }
nickjillings@1620 1252 this.audioObjects = [];
nickjillings@1369 1253 this.timer = new timer();
nickjillings@1369 1254 this.loopPlayback = audioHolderObject.loop;
nickjillings@1620 1255 };
nickjillings@1620 1256
nickjillings@1614 1257 this.checkAllPlayed = function() {
nickjillings@1614 1258 arr = [];
nickjillings@1614 1259 for (var id=0; id<this.audioObjects.length; id++) {
nickjillings@2008 1260 if (this.audioObjects[id].metric.wasListenedTo == false) {
nickjillings@1614 1261 arr.push(this.audioObjects[id].id);
nickjillings@1614 1262 }
nickjillings@1614 1263 }
nickjillings@1614 1264 return arr;
nickjillings@1614 1265 };
nickjillings@1614 1266
nickjillings@1620 1267 this.checkAllReady = function() {
nickjillings@1620 1268 var ready = true;
nickjillings@1620 1269 for (var i=0; i<this.audioObjects.length; i++) {
nickjillings@1620 1270 if (this.audioObjects[i].state == 0) {
nickjillings@1620 1271 // Track not ready
nickjillings@1620 1272 console.log('WAIT -- audioObject '+i+' not ready yet!');
nickjillings@1620 1273 ready = false;
nickjillings@1620 1274 };
nickjillings@1620 1275 }
nickjillings@1620 1276 return ready;
nickjillings@1620 1277 };
nickjillings@1620 1278
nickjillings@1535 1279 this.setSynchronousLoop = function() {
nickjillings@1535 1280 // Pads the signals so they are all exactly the same length
nickjillings@1523 1281 var length = 0;
nickjillings@1523 1282 var maxId;
nickjillings@1523 1283 for (var i=0; i<this.audioObjects.length; i++)
nickjillings@1535 1284 {
nickjillings@1413 1285 if (length < this.audioObjects[i].buffer.buffer.length)
nickjillings@1535 1286 {
nickjillings@1413 1287 length = this.audioObjects[i].buffer.buffer.length;
nickjillings@1523 1288 maxId = i;
nickjillings@1535 1289 }
nickjillings@1523 1290 }
nickjillings@1404 1291 // Extract the audio and zero-pad
nickjillings@1430 1292 for (var i=0; i<this.audioObjects.length; i++)
nickjillings@1523 1293 {
nickjillings@1413 1294 var orig = this.audioObjects[i].buffer.buffer;
nickjillings@1523 1295 var hold = audioContext.createBuffer(orig.numberOfChannels,length,orig.sampleRate);
nickjillings@1523 1296 for (var c=0; c<orig.numberOfChannels; c++)
nickjillings@1535 1297 {
nickjillings@1523 1298 var inData = hold.getChannelData(c);
nickjillings@1523 1299 var outData = orig.getChannelData(c);
nickjillings@1523 1300 for (var n=0; n<orig.length; n++)
nickjillings@1523 1301 {inData[n] = outData[n];}
nickjillings@1535 1302 }
nickjillings@1320 1303 hold.playbackGain = orig.playbackGain;
nickjillings@1430 1304 hold.lufs = orig.lufs;
nickjillings@1413 1305 this.audioObjects[i].buffer.buffer = hold;
nickjillings@1535 1306 }
nickjillings@1535 1307 };
nickjillings@1370 1308
nickjillings@1370 1309 this.exportXML = function()
nickjillings@1370 1310 {
nickjillings@1370 1311
nickjillings@1370 1312 };
nickjillings@1535 1313
nickjillings@1682 1314 }
nickjillings@1682 1315
nickjillings@1682 1316 function audioObject(id) {
nickjillings@1682 1317 // The main buffer object with common control nodes to the AudioEngine
nickjillings@1682 1318
nickjillings@1582 1319 this.specification;
nickjillings@1682 1320 this.id = id;
nickjillings@1682 1321 this.state = 0; // 0 - no data, 1 - ready
nickjillings@1704 1322 this.url = null; // Hold the URL given for the output back to the results.
nickjillings@1602 1323 this.metric = new metricTracker(this);
nickjillings@1324 1324 this.storeDOM = null;
nickjillings@1682 1325
nickjillings@1577 1326 // Bindings for GUI
nickjillings@1583 1327 this.interfaceDOM = null;
nickjillings@1577 1328 this.commentDOM = null;
nickjillings@1577 1329
nickjillings@1682 1330 // Create a buffer and external gain control to allow internal patching of effects and volume leveling.
nickjillings@1667 1331 this.bufferNode = undefined;
nickjillings@1682 1332 this.outputGain = audioContext.createGain();
nickjillings@1682 1333
nickjillings@1324 1334 this.onplayGain = 1.0;
nickjillings@1689 1335
nickjillings@1682 1336 // Connect buffer to the audio graph
nickjillings@1682 1337 this.outputGain.connect(audioEngineContext.outputGain);
nickjillings@1682 1338
nickjillings@1682 1339 // the audiobuffer is not designed for multi-start playback
nickjillings@1682 1340 // When stopeed, the buffer node is deleted and recreated with the stored buffer.
nickjillings@1682 1341 this.buffer;
nickjillings@1434 1342
nickjillings@1434 1343 this.bufferLoaded = function(callee)
nickjillings@1434 1344 {
nickjillings@1434 1345 // Called by the associated buffer when it has finished loading, will then 'bind' the buffer to the
nickjillings@1434 1346 // audioObject and trigger the interfaceDOM.enable() function for user feedback
nickjillings@2113 1347 if (callee.status == -1) {
nickjillings@2113 1348 // ERROR
nickjillings@2113 1349 this.state = -1;
nickjillings@2113 1350 if (this.interfaceDOM != null) {this.interfaceDOM.error();}
nickjillings@2113 1351 this.buffer = callee;
nickjillings@2113 1352 return;
nickjillings@2113 1353 }
nickjillings@1434 1354 if (audioEngineContext.loopPlayback){
nickjillings@1434 1355 // First copy the buffer into this.buffer
nickjillings@1434 1356 this.buffer = new audioEngineContext.bufferObj();
nickjillings@1434 1357 this.buffer.url = callee.url;
nickjillings@1434 1358 this.buffer.buffer = audioContext.createBuffer(callee.buffer.numberOfChannels, callee.buffer.length, callee.buffer.sampleRate);
nickjillings@1434 1359 for (var c=0; c<callee.buffer.numberOfChannels; c++)
nickjillings@1434 1360 {
nickjillings@1434 1361 var src = callee.buffer.getChannelData(c);
nickjillings@1434 1362 var dst = this.buffer.buffer.getChannelData(c);
nickjillings@1434 1363 for (var n=0; n<src.length; n++)
nickjillings@1434 1364 {
nickjillings@1434 1365 dst[n] = src[n];
nickjillings@1434 1366 }
nickjillings@1434 1367 }
nickjillings@1434 1368 } else {
nickjillings@1434 1369 this.buffer = callee;
nickjillings@1434 1370 }
nickjillings@1434 1371 this.state = 1;
nickjillings@1320 1372 this.buffer.buffer.playbackGain = callee.buffer.playbackGain;
nickjillings@1434 1373 this.buffer.buffer.lufs = callee.buffer.lufs;
nickjillings@1348 1374 var targetLUFS = this.specification.parent.loudness || specification.loudness;
nickjillings@1434 1375 if (typeof targetLUFS === "number")
nickjillings@1434 1376 {
nickjillings@1320 1377 this.buffer.buffer.playbackGain = decibelToLinear(targetLUFS - this.buffer.buffer.lufs);
nickjillings@1434 1378 } else {
nickjillings@1320 1379 this.buffer.buffer.playbackGain = 1.0;
nickjillings@1434 1380 }
nickjillings@1434 1381 if (this.interfaceDOM != null) {
nickjillings@1434 1382 this.interfaceDOM.enable();
nickjillings@1434 1383 }
nickjillings@1324 1384 this.onplayGain = decibelToLinear(this.specification.gain)*this.buffer.buffer.playbackGain;
nickjillings@1324 1385 this.storeDOM.setAttribute('playGain',linearToDecibel(this.onplayGain));
nickjillings@1318 1386 };
nickjillings@1325 1387
nickjillings@1325 1388 this.bindInterface = function(interfaceObject)
nickjillings@1325 1389 {
nickjillings@1325 1390 this.interfaceDOM = interfaceObject;
nickjillings@1325 1391 this.metric.initialise(interfaceObject.getValue());
nickjillings@1325 1392 if (this.state == 1)
nickjillings@1325 1393 {
nickjillings@1325 1394 this.interfaceDOM.enable();
nickjillings@2113 1395 } else if (this.state == -1) {
nickjillings@2113 1396 // ERROR
nickjillings@2113 1397 this.interfaceDOM.error();
nickjillings@2113 1398 return;
nickjillings@2113 1399 }
nickjillings@1338 1400 this.storeDOM.setAttribute('presentedId',interfaceObject.getPresentedId());
nickjillings@1434 1401 };
b@1639 1402
nickjillings@1360 1403 this.loopStart = function(setTime) {
nickjillings@1360 1404 this.outputGain.gain.linearRampToValueAtTime(this.onplayGain,setTime);
nickjillings@1637 1405 this.metric.startListening(audioEngineContext.timer.getTestTime());
nickjillings@1360 1406 this.interfaceDOM.startPlayback();
nickjillings@1577 1407 };
nickjillings@1637 1408
nickjillings@1360 1409 this.loopStop = function(setTime) {
nickjillings@1637 1410 if (this.outputGain.gain.value != 0.0) {
nickjillings@1360 1411 this.outputGain.gain.linearRampToValueAtTime(0.0,setTime);
nickjillings@1637 1412 this.metric.stopListening(audioEngineContext.timer.getTestTime());
nickjillings@1637 1413 }
nickjillings@1360 1414 this.interfaceDOM.stopPlayback();
nickjillings@1577 1415 };
nickjillings@1637 1416
nickjillings@1682 1417 this.play = function(startTime) {
nickjillings@1410 1418 if (this.bufferNode == undefined && this.buffer.buffer != undefined) {
nickjillings@1565 1419 this.bufferNode = audioContext.createBufferSource();
nickjillings@1565 1420 this.bufferNode.owner = this;
nickjillings@1565 1421 this.bufferNode.connect(this.outputGain);
nickjillings@1410 1422 this.bufferNode.buffer = this.buffer.buffer;
nickjillings@1565 1423 this.bufferNode.loop = audioEngineContext.loopPlayback;
nickjillings@1522 1424 this.bufferNode.onended = function(event) {
nickjillings@1565 1425 // Safari does not like using 'this' to reference the calling object!
nickjillings@1458 1426 //event.currentTarget.owner.metric.stopListening(audioEngineContext.timer.getTestTime(),event.currentTarget.owner.getCurrentPosition());
nickjillings@2120 1427 if (event.currentTarget != null) {
nickjillings@2120 1428 event.currentTarget.owner.stop(audioContext.currentTime+1);
nickjillings@2120 1429 }
nickjillings@1565 1430 };
nickjillings@1565 1431 if (this.bufferNode.loop == false) {
nickjillings@1565 1432 this.metric.startListening(audioEngineContext.timer.getTestTime());
nickjillings@1360 1433 this.outputGain.gain.setValueAtTime(this.onplayGain,startTime);
nickjillings@1360 1434 this.interfaceDOM.startPlayback();
nickjillings@1360 1435 } else {
nickjillings@1360 1436 this.outputGain.gain.setValueAtTime(0.0,startTime);
nickjillings@1360 1437 }
nickjillings@1565 1438 this.bufferNode.start(startTime);
nickjillings@2126 1439 this.bufferNode.playbackStartTime = audioEngineContext.timer.getTestTime();
nickjillings@1617 1440 }
nickjillings@1697 1441 };
nickjillings@1682 1442
nickjillings@1360 1443 this.stop = function(stopTime) {
nickjillings@1360 1444 this.outputGain.gain.cancelScheduledValues(audioContext.currentTime);
nickjillings@2002 1445 if (this.bufferNode != undefined)
nickjillings@2002 1446 {
nickjillings@1566 1447 this.metric.stopListening(audioEngineContext.timer.getTestTime(),this.getCurrentPosition());
nickjillings@1360 1448 this.bufferNode.stop(stopTime);
nickjillings@2002 1449 this.bufferNode = undefined;
nickjillings@2002 1450 }
nickjillings@1360 1451 this.outputGain.gain.value = 0.0;
nickjillings@1360 1452 this.interfaceDOM.stopPlayback();
nickjillings@1697 1453 };
nickjillings@2019 1454
nickjillings@2019 1455 this.getCurrentPosition = function() {
nickjillings@2019 1456 var time = audioEngineContext.timer.getTestTime();
nickjillings@2019 1457 if (this.bufferNode != undefined) {
nickjillings@2138 1458 var position = (time - this.bufferNode.playbackStartTime)%this.buffer.buffer.duration;
nickjillings@2138 1459 if (isNaN(position)){return 0;}
nickjillings@2138 1460 return position;
nickjillings@2019 1461 } else {
nickjillings@2019 1462 return 0;
nickjillings@2019 1463 }
nickjillings@2019 1464 };
nickjillings@1688 1465
nickjillings@1583 1466 this.exportXMLDOM = function() {
nickjillings@1324 1467 var file = storage.document.createElement('file');
nickjillings@1416 1468 file.setAttribute('sampleRate',this.buffer.buffer.sampleRate);
nickjillings@1416 1469 file.setAttribute('channels',this.buffer.buffer.numberOfChannels);
nickjillings@1416 1470 file.setAttribute('sampleCount',this.buffer.buffer.length);
nickjillings@1416 1471 file.setAttribute('duration',this.buffer.buffer.duration);
nickjillings@1324 1472 this.storeDOM.appendChild(file);
nickjillings@1324 1473 if (this.specification.type != 'outside-reference') {
nickjillings@1413 1474 var interfaceXML = this.interfaceDOM.exportXMLDOM(this);
nickjillings@1340 1475 if (interfaceXML != null)
nickjillings@1340 1476 {
nickjillings@1340 1477 if (interfaceXML.length == undefined) {
nickjillings@1340 1478 this.storeDOM.appendChild(interfaceXML);
nickjillings@1340 1479 } else {
nickjillings@1340 1480 for (var i=0; i<interfaceXML.length; i++)
nickjillings@1340 1481 {
nickjillings@1340 1482 this.storeDOM.appendChild(interfaceXML[i]);
nickjillings@1340 1483 }
nickjillings@1413 1484 }
nickjillings@1413 1485 }
nickjillings@1330 1486 if (this.commentDOM != null) {
nickjillings@1330 1487 this.storeDOM.appendChild(this.commentDOM.exportXMLDOM(this));
nickjillings@1529 1488 }
nickjillings@2050 1489 }
nickjillings@1324 1490 var nodes = this.metric.exportXMLDOM();
nickjillings@1324 1491 var mroot = this.storeDOM.getElementsByTagName('metric')[0];
nickjillings@1324 1492 for (var i=0; i<nodes.length; i++)
nickjillings@1324 1493 {
nickjillings@1324 1494 mroot.appendChild(nodes[i]);
nickjillings@1324 1495 }
nickjillings@1583 1496 };
nickjillings@1659 1497 }
nickjillings@1659 1498
nickjillings@1659 1499 function timer()
nickjillings@1659 1500 {
nickjillings@1659 1501 /* Timer object used in audioEngine to keep track of session timings
nickjillings@1659 1502 * Uses the timer of the web audio API, so sample resolution
nickjillings@1659 1503 */
nickjillings@1659 1504 this.testStarted = false;
nickjillings@1659 1505 this.testStartTime = 0;
nickjillings@1659 1506 this.testDuration = 0;
nickjillings@1659 1507 this.minimumTestTime = 0; // No minimum test time
nickjillings@1659 1508 this.startTest = function()
nickjillings@1659 1509 {
nickjillings@1659 1510 if (this.testStarted == false)
nickjillings@1659 1511 {
nickjillings@1659 1512 this.testStartTime = audioContext.currentTime;
nickjillings@1659 1513 this.testStarted = true;
nickjillings@1659 1514 this.updateTestTime();
nickjillings@1662 1515 audioEngineContext.metric.initialiseTest();
nickjillings@1659 1516 }
nickjillings@1659 1517 };
nickjillings@1659 1518 this.stopTest = function()
nickjillings@1659 1519 {
nickjillings@1659 1520 if (this.testStarted)
nickjillings@1659 1521 {
nickjillings@1659 1522 this.testDuration = this.getTestTime();
nickjillings@1659 1523 this.testStarted = false;
nickjillings@1659 1524 } else {
nickjillings@1659 1525 console.log('ERR: Test tried to end before beginning');
nickjillings@1659 1526 }
nickjillings@1659 1527 };
nickjillings@1659 1528 this.updateTestTime = function()
nickjillings@1659 1529 {
nickjillings@1659 1530 if (this.testStarted)
nickjillings@1659 1531 {
nickjillings@1659 1532 this.testDuration = audioContext.currentTime - this.testStartTime;
nickjillings@1659 1533 }
nickjillings@1659 1534 };
nickjillings@1659 1535 this.getTestTime = function()
nickjillings@1659 1536 {
nickjillings@1659 1537 this.updateTestTime();
nickjillings@1659 1538 return this.testDuration;
nickjillings@1659 1539 };
nickjillings@1659 1540 }
nickjillings@1659 1541
nickjillings@1408 1542 function sessionMetrics(engine,specification)
nickjillings@1659 1543 {
nickjillings@1659 1544 /* Used by audioEngine to link to audioObjects to minimise the timer call timers;
nickjillings@1659 1545 */
nickjillings@1659 1546 this.engine = engine;
nickjillings@1659 1547 this.lastClicked = -1;
nickjillings@1659 1548 this.data = -1;
nickjillings@1620 1549 this.reset = function() {
nickjillings@1620 1550 this.lastClicked = -1;
nickjillings@1620 1551 this.data = -1;
nickjillings@1620 1552 };
nickjillings@1408 1553
nickjillings@1408 1554 this.enableElementInitialPosition = false;
nickjillings@1408 1555 this.enableElementListenTracker = false;
nickjillings@1408 1556 this.enableElementTimer = false;
nickjillings@1408 1557 this.enableElementTracker = false;
nickjillings@1408 1558 this.enableFlagListenedTo = false;
nickjillings@1408 1559 this.enableFlagMoved = false;
nickjillings@1408 1560 this.enableTestTimer = false;
nickjillings@1408 1561 // Obtain the metrics enabled
nickjillings@1324 1562 for (var i=0; i<specification.metrics.enabled.length; i++)
nickjillings@1408 1563 {
nickjillings@1324 1564 var node = specification.metrics.enabled[i];
nickjillings@1324 1565 switch(node)
nickjillings@1408 1566 {
nickjillings@1408 1567 case 'testTimer':
nickjillings@1408 1568 this.enableTestTimer = true;
nickjillings@1408 1569 break;
nickjillings@1408 1570 case 'elementTimer':
nickjillings@1408 1571 this.enableElementTimer = true;
nickjillings@1408 1572 break;
nickjillings@1408 1573 case 'elementTracker':
nickjillings@1408 1574 this.enableElementTracker = true;
nickjillings@1408 1575 break;
nickjillings@1408 1576 case 'elementListenTracker':
nickjillings@1408 1577 this.enableElementListenTracker = true;
nickjillings@1408 1578 break;
nickjillings@1408 1579 case 'elementInitialPosition':
nickjillings@1408 1580 this.enableElementInitialPosition = true;
nickjillings@1408 1581 break;
nickjillings@1408 1582 case 'elementFlagListenedTo':
nickjillings@1408 1583 this.enableFlagListenedTo = true;
nickjillings@1408 1584 break;
nickjillings@1408 1585 case 'elementFlagMoved':
nickjillings@1408 1586 this.enableFlagMoved = true;
nickjillings@1408 1587 break;
nickjillings@1408 1588 case 'elementFlagComments':
nickjillings@1408 1589 this.enableFlagComments = true;
nickjillings@1408 1590 break;
nickjillings@1408 1591 }
nickjillings@1408 1592 }
nickjillings@1662 1593 this.initialiseTest = function(){};
nickjillings@1659 1594 }
nickjillings@1659 1595
nickjillings@1602 1596 function metricTracker(caller)
nickjillings@1659 1597 {
nickjillings@1659 1598 /* Custom object to track and collect metric data
nickjillings@1659 1599 * Used only inside the audioObjects object.
nickjillings@1659 1600 */
nickjillings@1659 1601
nickjillings@1659 1602 this.listenedTimer = 0;
nickjillings@1659 1603 this.listenStart = 0;
nickjillings@1617 1604 this.listenHold = false;
nickjillings@1661 1605 this.initialPosition = -1;
nickjillings@1659 1606 this.movementTracker = [];
nickjillings@2019 1607 this.listenTracker =[];
nickjillings@1659 1608 this.wasListenedTo = false;
nickjillings@1659 1609 this.wasMoved = false;
nickjillings@1659 1610 this.hasComments = false;
nickjillings@1602 1611 this.parent = caller;
nickjillings@1659 1612
nickjillings@1324 1613 this.initialise = function(position)
nickjillings@1659 1614 {
nickjillings@1661 1615 if (this.initialPosition == -1) {
nickjillings@1661 1616 this.initialPosition = position;
nickjillings@1325 1617 this.moved(0,position);
nickjillings@1661 1618 }
nickjillings@1659 1619 };
nickjillings@1659 1620
nickjillings@1659 1621 this.moved = function(time,position)
nickjillings@1659 1622 {
nickjillings@1325 1623 if (time > 0) {this.wasMoved = true;}
nickjillings@1659 1624 this.movementTracker[this.movementTracker.length] = [time, position];
nickjillings@1659 1625 };
nickjillings@1659 1626
nickjillings@1637 1627 this.startListening = function(time)
nickjillings@1659 1628 {
nickjillings@1617 1629 if (this.listenHold == false)
nickjillings@1659 1630 {
nickjillings@1659 1631 this.wasListenedTo = true;
nickjillings@1659 1632 this.listenStart = time;
nickjillings@1617 1633 this.listenHold = true;
nickjillings@2019 1634
nickjillings@2019 1635 var evnt = document.createElement('event');
nickjillings@2019 1636 var testTime = document.createElement('testTime');
nickjillings@2019 1637 testTime.setAttribute('start',time);
nickjillings@2019 1638 var bufferTime = document.createElement('bufferTime');
nickjillings@2019 1639 bufferTime.setAttribute('start',this.parent.getCurrentPosition());
nickjillings@2019 1640 evnt.appendChild(testTime);
nickjillings@2019 1641 evnt.appendChild(bufferTime);
nickjillings@2019 1642 this.listenTracker.push(evnt);
nickjillings@2019 1643
nickjillings@1602 1644 console.log('slider ' + this.parent.id + ' played (' + time + ')'); // DEBUG/SAFETY: show played slider id
nickjillings@1602 1645 }
nickjillings@1602 1646 };
nickjillings@1637 1647
nickjillings@1566 1648 this.stopListening = function(time,bufferStopTime)
nickjillings@1637 1649 {
nickjillings@1637 1650 if (this.listenHold == true)
nickjillings@1637 1651 {
nickjillings@2019 1652 var diff = time - this.listenStart;
nickjillings@2019 1653 this.listenedTimer += (diff);
nickjillings@1659 1654 this.listenStart = 0;
nickjillings@1617 1655 this.listenHold = false;
nickjillings@2019 1656
nickjillings@2019 1657 var evnt = this.listenTracker[this.listenTracker.length-1];
nickjillings@2019 1658 var testTime = evnt.getElementsByTagName('testTime')[0];
nickjillings@2019 1659 var bufferTime = evnt.getElementsByTagName('bufferTime')[0];
nickjillings@2019 1660 testTime.setAttribute('stop',time);
nickjillings@1566 1661 if (bufferStopTime == undefined) {
nickjillings@1566 1662 bufferTime.setAttribute('stop',this.parent.getCurrentPosition());
nickjillings@1566 1663 } else {
nickjillings@1566 1664 bufferTime.setAttribute('stop',bufferStopTime);
nickjillings@1566 1665 }
nickjillings@2019 1666 console.log('slider ' + this.parent.id + ' played for (' + diff + ')'); // DEBUG/SAFETY: show played slider id
nickjillings@1659 1667 }
nickjillings@1659 1668 };
nickjillings@1577 1669
nickjillings@1577 1670 this.exportXMLDOM = function() {
nickjillings@1324 1671 var storeDOM = [];
nickjillings@1577 1672 if (audioEngineContext.metric.enableElementTimer) {
nickjillings@1324 1673 var mElementTimer = storage.document.createElement('metricresult');
nickjillings@1577 1674 mElementTimer.setAttribute('name','enableElementTimer');
nickjillings@1577 1675 mElementTimer.textContent = this.listenedTimer;
nickjillings@1324 1676 storeDOM.push(mElementTimer);
nickjillings@1577 1677 }
nickjillings@1577 1678 if (audioEngineContext.metric.enableElementTracker) {
nickjillings@1324 1679 var elementTrackerFull = storage.document.createElement('metricResult');
nickjillings@1577 1680 elementTrackerFull.setAttribute('name','elementTrackerFull');
nickjillings@1577 1681 for (var k=0; k<this.movementTracker.length; k++)
nickjillings@1577 1682 {
nickjillings@2138 1683 var timePos = storage.document.createElement('movement');
nickjillings@2138 1684 timePos.setAttribute("time",this.movementTracker[k][0]);
nickjillings@2138 1685 timePos.setAttribute("value",this.movementTracker[k][1]);
nickjillings@1577 1686 elementTrackerFull.appendChild(timePos);
nickjillings@1577 1687 }
nickjillings@1324 1688 storeDOM.push(elementTrackerFull);
nickjillings@1577 1689 }
nickjillings@1577 1690 if (audioEngineContext.metric.enableElementListenTracker) {
nickjillings@1324 1691 var elementListenTracker = storage.document.createElement('metricResult');
nickjillings@1577 1692 elementListenTracker.setAttribute('name','elementListenTracker');
nickjillings@1577 1693 for (var k=0; k<this.listenTracker.length; k++) {
nickjillings@1577 1694 elementListenTracker.appendChild(this.listenTracker[k]);
nickjillings@1577 1695 }
nickjillings@1324 1696 storeDOM.push(elementListenTracker);
nickjillings@1577 1697 }
nickjillings@1577 1698 if (audioEngineContext.metric.enableElementInitialPosition) {
nickjillings@1324 1699 var elementInitial = storage.document.createElement('metricResult');
nickjillings@1577 1700 elementInitial.setAttribute('name','elementInitialPosition');
nickjillings@1577 1701 elementInitial.textContent = this.initialPosition;
nickjillings@1324 1702 storeDOM.push(elementInitial);
nickjillings@1577 1703 }
nickjillings@1577 1704 if (audioEngineContext.metric.enableFlagListenedTo) {
nickjillings@1324 1705 var flagListenedTo = storage.document.createElement('metricResult');
nickjillings@1577 1706 flagListenedTo.setAttribute('name','elementFlagListenedTo');
nickjillings@1577 1707 flagListenedTo.textContent = this.wasListenedTo;
nickjillings@1324 1708 storeDOM.push(flagListenedTo);
nickjillings@1577 1709 }
nickjillings@1577 1710 if (audioEngineContext.metric.enableFlagMoved) {
nickjillings@1324 1711 var flagMoved = storage.document.createElement('metricResult');
nickjillings@1577 1712 flagMoved.setAttribute('name','elementFlagMoved');
nickjillings@1577 1713 flagMoved.textContent = this.wasMoved;
nickjillings@1324 1714 storeDOM.push(flagMoved);
nickjillings@1577 1715 }
nickjillings@1577 1716 if (audioEngineContext.metric.enableFlagComments) {
nickjillings@1324 1717 var flagComments = storage.document.createElement('metricResult');
nickjillings@1577 1718 flagComments.setAttribute('name','elementFlagComments');
nickjillings@1577 1719 if (this.parent.commentDOM == null)
nickjillings@1577 1720 {flag.textContent = 'false';}
nickjillings@1577 1721 else if (this.parent.commentDOM.textContent.length == 0)
nickjillings@1577 1722 {flag.textContent = 'false';}
nickjillings@1577 1723 else
nickjillings@1577 1724 {flag.textContet = 'true';}
nickjillings@1324 1725 storeDOM.push(flagComments);
nickjillings@1577 1726 }
nickjillings@1324 1727 return storeDOM;
nickjillings@1577 1728 };
nickjillings@1664 1729 }
nickjillings@1664 1730
nickjillings@1664 1731 function randomiseOrder(input)
nickjillings@1664 1732 {
nickjillings@1664 1733 // This takes an array of information and randomises the order
nickjillings@1664 1734 var N = input.length;
b@2037 1735
b@2037 1736 var inputSequence = []; // For safety purposes: keep track of randomisation
b@2037 1737 for (var counter = 0; counter < N; ++counter)
b@2037 1738 inputSequence.push(counter) // Fill array
b@2037 1739 var inputSequenceClone = inputSequence.slice(0);
b@2037 1740
nickjillings@1664 1741 var holdArr = [];
b@2037 1742 var outputSequence = [];
nickjillings@1664 1743 for (var n=0; n<N; n++)
nickjillings@1664 1744 {
nickjillings@1664 1745 // First pick a random number
nickjillings@1664 1746 var r = Math.random();
nickjillings@1664 1747 // Multiply and floor by the number of elements left
nickjillings@1664 1748 r = Math.floor(r*input.length);
nickjillings@1664 1749 // Pick out that element and delete from the array
nickjillings@1664 1750 holdArr.push(input.splice(r,1)[0]);
b@2037 1751 // Do the same with sequence
b@2037 1752 outputSequence.push(inputSequence.splice(r,1)[0]);
nickjillings@1664 1753 }
b@2037 1754 console.log(inputSequenceClone.toString()); // print original array to console
b@2037 1755 console.log(outputSequence.toString()); // print randomised array to console
nickjillings@1664 1756 return holdArr;
nickjillings@1631 1757 }
nickjillings@1631 1758
nickjillings@1631 1759 function returnDateNode()
nickjillings@1631 1760 {
nickjillings@1631 1761 // Create an XML Node for the Date and Time a test was conducted
nickjillings@1631 1762 // Structure is
nickjillings@1631 1763 // <datetime>
nickjillings@1631 1764 // <date year="##" month="##" day="##">DD/MM/YY</date>
nickjillings@1631 1765 // <time hour="##" minute="##" sec="##">HH:MM:SS</time>
nickjillings@1631 1766 // </datetime>
nickjillings@1631 1767 var dateTime = new Date();
nickjillings@1631 1768 var year = document.createAttribute('year');
nickjillings@1631 1769 var month = document.createAttribute('month');
nickjillings@1631 1770 var day = document.createAttribute('day');
nickjillings@1631 1771 var hour = document.createAttribute('hour');
nickjillings@1631 1772 var minute = document.createAttribute('minute');
nickjillings@1631 1773 var secs = document.createAttribute('secs');
nickjillings@1631 1774
nickjillings@1631 1775 year.nodeValue = dateTime.getFullYear();
nickjillings@1631 1776 month.nodeValue = dateTime.getMonth()+1;
nickjillings@1631 1777 day.nodeValue = dateTime.getDate();
nickjillings@1631 1778 hour.nodeValue = dateTime.getHours();
nickjillings@1631 1779 minute.nodeValue = dateTime.getMinutes();
nickjillings@1631 1780 secs.nodeValue = dateTime.getSeconds();
nickjillings@1631 1781
nickjillings@1631 1782 var hold = document.createElement("datetime");
nickjillings@1631 1783 var date = document.createElement("date");
nickjillings@1631 1784 date.textContent = year.nodeValue+'/'+month.nodeValue+'/'+day.nodeValue;
nickjillings@1631 1785 var time = document.createElement("time");
nickjillings@1631 1786 time.textContent = hour.nodeValue+':'+minute.nodeValue+':'+secs.nodeValue;
nickjillings@1631 1787
nickjillings@1631 1788 date.setAttributeNode(year);
nickjillings@1631 1789 date.setAttributeNode(month);
nickjillings@1631 1790 date.setAttributeNode(day);
nickjillings@1631 1791 time.setAttributeNode(hour);
nickjillings@1631 1792 time.setAttributeNode(minute);
nickjillings@1631 1793 time.setAttributeNode(secs);
nickjillings@1631 1794
nickjillings@1631 1795 hold.appendChild(date);
nickjillings@1631 1796 hold.appendChild(time);
nickjillings@1408 1797 return hold;
nickjillings@1631 1798
nickjillings@1604 1799 }
nickjillings@1604 1800
nickjillings@1580 1801 function Specification() {
nickjillings@1580 1802 // Handles the decoding of the project specification XML into a simple JavaScript Object.
nickjillings@1580 1803
nickjillings@1324 1804 this.interface = null;
nickjillings@1373 1805 this.projectReturn = "null";
nickjillings@1324 1806 this.randomiseOrder = null;
nickjillings@1324 1807 this.testPages = null;
nickjillings@1324 1808 this.pages = [];
nickjillings@1324 1809 this.metrics = null;
nickjillings@1324 1810 this.interfaces = null;
nickjillings@1324 1811 this.loudness = null;
nickjillings@1324 1812 this.errors = [];
nickjillings@1324 1813 this.schema = null;
nickjillings@1318 1814
nickjillings@2165 1815 this.processAttribute = function(attribute,schema,schemaRoot)
nickjillings@1406 1816 {
nickjillings@1324 1817 // attribute is the string returned from getAttribute on the XML
nickjillings@1324 1818 // schema is the <xs:attribute> node
nickjillings@1324 1819 if (schema.getAttribute('name') == undefined && schema.getAttribute('ref') != undefined)
nickjillings@1406 1820 {
nickjillings@2165 1821 schema = schemaRoot.getAllElementsByName(schema.getAttribute('ref'))[0];
nickjillings@1324 1822 }
nickjillings@1324 1823 var defaultOpt = schema.getAttribute('default');
nickjillings@1324 1824 if (attribute == null) {
nickjillings@1324 1825 attribute = defaultOpt;
nickjillings@1324 1826 }
nickjillings@1324 1827 var dataType = schema.getAttribute('type');
nickjillings@1324 1828 if (typeof dataType == "string") { dataType = dataType.substr(3);}
nickjillings@1324 1829 else {dataType = "string";}
nickjillings@1324 1830 if (attribute == null)
nickjillings@1324 1831 {
nickjillings@1324 1832 return attribute;
nickjillings@1324 1833 }
nickjillings@1324 1834 switch(dataType)
nickjillings@1324 1835 {
nickjillings@1324 1836 case "boolean":
nickjillings@1324 1837 if (attribute == 'true'){attribute = true;}else{attribute=false;}
nickjillings@1324 1838 break;
nickjillings@1324 1839 case "negativeInteger":
nickjillings@1324 1840 case "positiveInteger":
nickjillings@1324 1841 case "nonNegativeInteger":
nickjillings@1324 1842 case "nonPositiveInteger":
nickjillings@1324 1843 case "integer":
nickjillings@1324 1844 case "decimal":
nickjillings@1324 1845 case "short":
nickjillings@1324 1846 attribute = Number(attribute);
nickjillings@1324 1847 break;
nickjillings@1324 1848 case "string":
nickjillings@1324 1849 default:
nickjillings@1324 1850 attribute = String(attribute);
nickjillings@1324 1851 break;
nickjillings@1324 1852 }
nickjillings@1324 1853 return attribute;
nickjillings@1406 1854 };
nickjillings@1411 1855
nickjillings@1406 1856 this.decode = function(projectXML) {
nickjillings@1324 1857 this.errors = [];
nickjillings@1580 1858 // projectXML - DOM Parsed document
nickjillings@2052 1859 this.projectXML = projectXML.childNodes[0];
nickjillings@1580 1860 var setupNode = projectXML.getElementsByTagName('setup')[0];
nickjillings@1348 1861 var schemaSetup = this.schema.getAllElementsByName('setup')[0];
nickjillings@1324 1862 // First decode the attributes
nickjillings@1348 1863 var attributes = schemaSetup.getAllElementsByTagName('xs:attribute');
nickjillings@1324 1864 for (var i in attributes)
nickjillings@1520 1865 {
nickjillings@1324 1866 if (isNaN(Number(i)) == true){break;}
nickjillings@2165 1867 var attributeName = attributes[i].getAttribute('name') || attributes[i].getAttribute('ref');
nickjillings@1324 1868 var projectAttr = setupNode.getAttribute(attributeName);
nickjillings@2165 1869 projectAttr = this.processAttribute(projectAttr,attributes[i],this.schema);
nickjillings@1324 1870 switch(typeof projectAttr)
nickjillings@1432 1871 {
nickjillings@1324 1872 case "number":
nickjillings@1324 1873 case "boolean":
nickjillings@1324 1874 eval('this.'+attributeName+' = '+projectAttr);
nickjillings@1324 1875 break;
nickjillings@1324 1876 case "string":
nickjillings@1324 1877 eval('this.'+attributeName+' = "'+projectAttr+'"');
nickjillings@1324 1878 break;
nickjillings@1432 1879 }
nickjillings@1324 1880
nickjillings@1406 1881 }
nickjillings@1406 1882
nickjillings@1370 1883 this.metrics = new this.metricNode();
nickjillings@1580 1884
nickjillings@1324 1885 this.metrics.decode(this,setupNode.getElementsByTagName('metric')[0]);
nickjillings@1324 1886
nickjillings@1324 1887 // Now process the survey node options
nickjillings@1324 1888 var survey = setupNode.getElementsByTagName('survey');
nickjillings@1324 1889 for (var i in survey) {
nickjillings@1324 1890 if (isNaN(Number(i)) == true){break;}
nickjillings@1324 1891 var location = survey[i].getAttribute('location');
nickjillings@1324 1892 if (location == 'pre' || location == 'before')
nickjillings@1324 1893 {
nickjillings@1324 1894 if (this.preTest != null){this.errors.push("Already a pre/before test survey defined! Ignoring second!!");}
nickjillings@1324 1895 else {
nickjillings@1324 1896 this.preTest = new this.surveyNode();
nickjillings@1370 1897 this.preTest.decode(this,survey[i]);
nickjillings@1324 1898 }
nickjillings@1324 1899 } else if (location == 'post' || location == 'after') {
nickjillings@1324 1900 if (this.postTest != null){this.errors.push("Already a post/after test survey defined! Ignoring second!!");}
nickjillings@1324 1901 else {
nickjillings@1324 1902 this.postTest = new this.surveyNode();
nickjillings@1370 1903 this.postTest.decode(this,survey[i]);
nickjillings@1324 1904 }
nickjillings@1580 1905 }
nickjillings@1580 1906 }
nickjillings@1580 1907
nickjillings@1324 1908 var interfaceNode = setupNode.getElementsByTagName('interface');
nickjillings@1324 1909 if (interfaceNode.length > 1)
nickjillings@1324 1910 {
nickjillings@1324 1911 this.errors.push("Only one <interface> node in the <setup> node allowed! Others except first ingnored!");
nickjillings@1324 1912 }
nickjillings@1324 1913 this.interfaces = new this.interfaceNode();
nickjillings@1324 1914 if (interfaceNode.length != 0)
nickjillings@1324 1915 {
nickjillings@1324 1916 interfaceNode = interfaceNode[0];
nickjillings@1348 1917 this.interfaces.decode(this,interfaceNode,this.schema.getAllElementsByName('interface')[1]);
nickjillings@1556 1918 }
nickjillings@1556 1919
nickjillings@1324 1920 // Page tags
nickjillings@1324 1921 var pageTags = projectXML.getElementsByTagName('page');
nickjillings@1348 1922 var pageSchema = this.schema.getAllElementsByName('page')[0];
nickjillings@1324 1923 for (var i=0; i<pageTags.length; i++)
nickjillings@1520 1924 {
nickjillings@1324 1925 var node = new this.page();
nickjillings@1324 1926 node.decode(this,pageTags[i],pageSchema);
nickjillings@1324 1927 this.pages.push(node);
nickjillings@1520 1928 }
nickjillings@1580 1929 };
nickjillings@1580 1930
nickjillings@1406 1931 this.encode = function()
nickjillings@1406 1932 {
nickjillings@1372 1933 var RootDocument = document.implementation.createDocument(null,"waet");
nickjillings@1372 1934 var root = RootDocument.children[0];
nickjillings@1372 1935 root.setAttribute("xmlns:xsi","http://www.w3.org/2001/XMLSchema-instance");
nickjillings@1372 1936 root.setAttribute("xsi:noNamespaceSchemaLocation","test-schema.xsd");
nickjillings@1324 1937 // Build setup node
nickjillings@1372 1938 var setup = RootDocument.createElement("setup");
nickjillings@1372 1939 var schemaSetup = this.schema.getAllElementsByName('setup')[0];
nickjillings@1372 1940 // First decode the attributes
nickjillings@1372 1941 var attributes = schemaSetup.getAllElementsByTagName('xs:attribute');
nickjillings@1372 1942 for (var i=0; i<attributes.length; i++)
nickjillings@1372 1943 {
nickjillings@1372 1944 var name = attributes[i].getAttribute("name");
nickjillings@1372 1945 if (name == undefined) {
nickjillings@1372 1946 name = attributes[i].getAttribute("ref");
nickjillings@1372 1947 }
nickjillings@1372 1948 if(eval("this."+name+" != undefined") || attributes[i].getAttribute("use") == "required")
nickjillings@1372 1949 {
nickjillings@1372 1950 eval("setup.setAttribute('"+name+"',this."+name+")");
nickjillings@1372 1951 }
nickjillings@1372 1952 }
nickjillings@1372 1953 root.appendChild(setup);
nickjillings@1372 1954 // Survey node
nickjillings@1372 1955 setup.appendChild(this.preTest.encode(RootDocument));
nickjillings@1372 1956 setup.appendChild(this.postTest.encode(RootDocument));
nickjillings@1372 1957 setup.appendChild(this.metrics.encode(RootDocument));
nickjillings@1372 1958 setup.appendChild(this.interfaces.encode(RootDocument));
nickjillings@1372 1959 for (var page of this.pages)
nickjillings@1372 1960 {
nickjillings@1372 1961 root.appendChild(page.encode(RootDocument));
nickjillings@1372 1962 }
nickjillings@1372 1963 return RootDocument;
nickjillings@1406 1964 };
nickjillings@1406 1965
nickjillings@1324 1966 this.surveyNode = function() {
nickjillings@1324 1967 this.location = null;
nickjillings@1580 1968 this.options = [];
nickjillings@2165 1969 this.parent = null;
nickjillings@1370 1970 this.schema = specification.schema.getAllElementsByName('survey')[0];
nickjillings@1580 1971
nickjillings@1406 1972 this.OptionNode = function() {
nickjillings@1406 1973 this.type = undefined;
nickjillings@1370 1974 this.schema = specification.schema.getAllElementsByName('surveyentry')[0];
nickjillings@1406 1975 this.id = undefined;
nickjillings@2156 1976 this.name = undefined;
nickjillings@1406 1977 this.mandatory = undefined;
nickjillings@1406 1978 this.statement = undefined;
nickjillings@1406 1979 this.boxsize = undefined;
nickjillings@1406 1980 this.options = [];
nickjillings@1406 1981 this.min = undefined;
nickjillings@1406 1982 this.max = undefined;
nickjillings@1406 1983 this.step = undefined;
nickjillings@1406 1984
nickjillings@1370 1985 this.decode = function(parent,child)
nickjillings@1406 1986 {
nickjillings@1370 1987 var attributeMap = this.schema.getAllElementsByTagName('xs:attribute');
nickjillings@1324 1988 for (var i in attributeMap){
nickjillings@1324 1989 if(isNaN(Number(i)) == true){break;}
nickjillings@1324 1990 var attributeName = attributeMap[i].getAttribute('name') || attributeMap[i].getAttribute('ref');
nickjillings@1324 1991 var projectAttr = child.getAttribute(attributeName);
nickjillings@2165 1992 projectAttr = parent.processAttribute(projectAttr,attributeMap[i],parent.schema);
nickjillings@1324 1993 switch(typeof projectAttr)
nickjillings@1324 1994 {
nickjillings@1324 1995 case "number":
nickjillings@1324 1996 case "boolean":
nickjillings@1324 1997 eval('this.'+attributeName+' = '+projectAttr);
nickjillings@1324 1998 break;
nickjillings@1324 1999 case "string":
nickjillings@1324 2000 eval('this.'+attributeName+' = "'+projectAttr+'"');
nickjillings@1324 2001 break;
nickjillings@1406 2002 }
nickjillings@1324 2003 }
nickjillings@1324 2004 this.statement = child.getElementsByTagName('statement')[0].textContent;
nickjillings@1324 2005 if (this.type == "checkbox" || this.type == "radio") {
nickjillings@1324 2006 var children = child.getElementsByTagName('option');
nickjillings@1324 2007 if (children.length == null) {
nickjillings@1406 2008 console.log('Malformed' +child.nodeName+ 'entry');
nickjillings@1406 2009 this.statement = 'Malformed' +child.nodeName+ 'entry';
nickjillings@1406 2010 this.type = 'statement';
nickjillings@1406 2011 } else {
nickjillings@1406 2012 this.options = [];
nickjillings@1324 2013 for (var i in children)
nickjillings@1324 2014 {
nickjillings@1324 2015 if (isNaN(Number(i))==true){break;}
nickjillings@1324 2016 this.options.push({
nickjillings@1324 2017 name: children[i].getAttribute('name'),
nickjillings@1324 2018 text: children[i].textContent
nickjillings@1324 2019 });
nickjillings@1406 2020 }
nickjillings@1406 2021 }
nickjillings@2030 2022 }
nickjillings@1406 2023 };
nickjillings@1406 2024
nickjillings@1372 2025 this.exportXML = function(doc)
nickjillings@1406 2026 {
nickjillings@2111 2027 var node = doc.createElement('surveyentry');
nickjillings@1324 2028 node.setAttribute('type',this.type);
nickjillings@1372 2029 var statement = doc.createElement('statement');
nickjillings@1324 2030 statement.textContent = this.statement;
nickjillings@1324 2031 node.appendChild(statement);
nickjillings@2162 2032 node.id = this.id;
nickjillings@2162 2033 if (this.name != undefined) { node.setAttribute("name",this.name);}
nickjillings@2162 2034 if (this.mandatory != undefined) { node.setAttribute("mandatory",this.mandatory);}
nickjillings@2162 2035 node.id = this.id;
nickjillings@2162 2036 if (this.name != undefined) {node.setAttribute("name",this.name);}
nickjillings@2162 2037 switch(this.type)
nickjillings@2162 2038 {
nickjillings@2156 2039 case "checkbox":
nickjillings@2156 2040 case "radio":
nickjillings@2156 2041 for (var i=0; i<this.options.length; i++)
nickjillings@2156 2042 {
nickjillings@2156 2043 var option = this.options[i];
nickjillings@2156 2044 var optionNode = doc.createElement("option");
nickjillings@2156 2045 optionNode.setAttribute("name",option.name);
nickjillings@2156 2046 optionNode.textContent = option.text;
nickjillings@2156 2047 node.appendChild(optionNode);
nickjillings@2156 2048 }
nickjillings@2162 2049 case "number":
nickjillings@2162 2050 if (this.min != undefined) {node.setAttribute("min", this.min);}
nickjillings@2162 2051 if (this.max != undefined) {node.setAttribute("max", this.max);}
nickjillings@2162 2052 case "question":
nickjillings@2162 2053 if (this.boxsize != undefined) {node.setAttribute("boxsize",this.boxsize);}
nickjillings@2162 2054 if (this.mandatory != undefined) {node.setAttribute("mandatory",this.mandatory);}
nickjillings@2162 2055 default:
nickjillings@2156 2056 break;
nickjillings@2156 2057 }
nickjillings@1406 2058 return node;
nickjillings@1406 2059 };
nickjillings@1406 2060 };
nickjillings@1370 2061 this.decode = function(parent,xml) {
nickjillings@2165 2062 this.parent = parent;
nickjillings@1324 2063 this.location = xml.getAttribute('location');
nickjillings@1324 2064 if (this.location == 'before'){this.location = 'pre';}
nickjillings@1324 2065 else if (this.location == 'after'){this.location = 'post';}
nickjillings@1324 2066 for (var i in xml.children)
nickjillings@1324 2067 {
nickjillings@1324 2068 if(isNaN(Number(i))==true){break;}
nickjillings@1406 2069 var node = new this.OptionNode();
nickjillings@1370 2070 node.decode(parent,xml.children[i]);
nickjillings@1406 2071 this.options.push(node);
nickjillings@1324 2072 }
nickjillings@1324 2073 };
nickjillings@1372 2074 this.encode = function(doc) {
nickjillings@1372 2075 var node = doc.createElement('survey');
nickjillings@1324 2076 node.setAttribute('location',this.location);
nickjillings@1324 2077 for (var i=0; i<this.options.length; i++)
nickjillings@1324 2078 {
nickjillings@1372 2079 node.appendChild(this.options[i].exportXML(doc));
nickjillings@1324 2080 }
nickjillings@1324 2081 return node;
nickjillings@1324 2082 };
nickjillings@1324 2083 };
nickjillings@1324 2084
nickjillings@1324 2085 this.interfaceNode = function()
nickjillings@1324 2086 {
nickjillings@1324 2087 this.title = null;
nickjillings@1324 2088 this.name = null;
nickjillings@1324 2089 this.options = [];
nickjillings@1324 2090 this.scales = [];
nickjillings@1370 2091 this.schema = specification.schema.getAllElementsByName('interface')[1];
nickjillings@1324 2092
nickjillings@1370 2093 this.decode = function(parent,xml) {
nickjillings@1324 2094 this.name = xml.getAttribute('name');
nickjillings@1324 2095 var titleNode = xml.getElementsByTagName('title');
nickjillings@1324 2096 if (titleNode.length == 1)
nickjillings@1324 2097 {
nickjillings@1324 2098 this.title = titleNode[0].textContent;
nickjillings@1324 2099 }
nickjillings@1324 2100 var interfaceOptionNodes = xml.getElementsByTagName('interfaceoption');
nickjillings@1324 2101 // Extract interfaceoption node schema
nickjillings@1370 2102 var interfaceOptionNodeSchema = this.schema.getAllElementsByName('interfaceoption')[0];
nickjillings@1348 2103 var attributeMap = interfaceOptionNodeSchema.getAllElementsByTagName('xs:attribute');
nickjillings@1324 2104 for (var i=0; i<interfaceOptionNodes.length; i++)
nickjillings@1324 2105 {
nickjillings@1324 2106 var ioNode = interfaceOptionNodes[i];
nickjillings@1324 2107 var option = {};
nickjillings@1324 2108 for (var j=0; j<attributeMap.length; j++)
nickjillings@1324 2109 {
nickjillings@1324 2110 var attributeName = attributeMap[j].getAttribute('name') || attributeMap[j].getAttribute('ref');
nickjillings@1324 2111 var projectAttr = ioNode.getAttribute(attributeName);
nickjillings@2165 2112 if(parent.processAttribute) {
nickjillings@2165 2113 parent.processAttribute(projectAttr, attributeMap[j], parent.schema)
nickjillings@2165 2114 } else {
nickjillings@2165 2115 parent.parent.processAttribute(projectAttr, attributeMap[j], parent.parent.schema)
nickjillings@2165 2116 }
nickjillings@1324 2117 switch(typeof projectAttr)
nickjillings@1324 2118 {
nickjillings@1324 2119 case "number":
nickjillings@1324 2120 case "boolean":
nickjillings@1324 2121 eval('option.'+attributeName+' = '+projectAttr);
nickjillings@1324 2122 break;
nickjillings@1324 2123 case "string":
nickjillings@1324 2124 eval('option.'+attributeName+' = "'+projectAttr+'"');
nickjillings@1324 2125 break;
nickjillings@1324 2126 }
nickjillings@1324 2127 }
nickjillings@1324 2128 this.options.push(option);
nickjillings@1324 2129 }
nickjillings@1324 2130
nickjillings@1324 2131 // Now the scales nodes
nickjillings@1324 2132 var scaleParent = xml.getElementsByTagName('scales');
nickjillings@1324 2133 if (scaleParent.length == 1) {
nickjillings@1324 2134 scaleParent = scaleParent[0];
nickjillings@1324 2135 for (var i=0; i<scaleParent.children.length; i++) {
nickjillings@1324 2136 var child = scaleParent.children[i];
nickjillings@1324 2137 this.scales.push({
nickjillings@1324 2138 text: child.textContent,
nickjillings@1324 2139 position: Number(child.getAttribute('position'))
nickjillings@1324 2140 });
nickjillings@1406 2141 }
nickjillings@1580 2142 }
nickjillings@1580 2143 };
nickjillings@1324 2144
nickjillings@1372 2145 this.encode = function(doc) {
nickjillings@1372 2146 var node = doc.createElement("interface");
nickjillings@1372 2147 if (typeof name == "string")
nickjillings@1372 2148 node.setAttribute("name",this.name);
nickjillings@1372 2149 for (var option of this.options)
nickjillings@1372 2150 {
nickjillings@1372 2151 var child = doc.createElement("interfaceoption");
nickjillings@1372 2152 child.setAttribute("type",option.type);
nickjillings@1372 2153 child.setAttribute("name",option.name);
nickjillings@1372 2154 node.appendChild(child);
nickjillings@1372 2155 }
nickjillings@1372 2156 if (this.scales.length != 0) {
nickjillings@1372 2157 var scales = doc.createElement("scales");
nickjillings@1372 2158 for (var scale of this.scales)
nickjillings@1372 2159 {
nickjillings@1372 2160 var child = doc.createElement("scalelabel");
nickjillings@1372 2161 child.setAttribute("position",scale.position);
nickjillings@1372 2162 child.textContent = scale.text;
nickjillings@1372 2163 scales.appendChild(child);
nickjillings@1372 2164 }
nickjillings@1372 2165 node.appendChild(scales);
nickjillings@1372 2166 }
nickjillings@1372 2167 return node;
nickjillings@1324 2168 };
nickjillings@1580 2169 };
nickjillings@1580 2170
nickjillings@1370 2171 this.metricNode = function() {
nickjillings@1370 2172 this.enabled = [];
nickjillings@1370 2173 this.decode = function(parent, xml) {
nickjillings@1370 2174 var children = xml.getElementsByTagName('metricenable');
nickjillings@1370 2175 for (var i in children) {
nickjillings@1370 2176 if (isNaN(Number(i)) == true){break;}
nickjillings@1370 2177 this.enabled.push(children[i].textContent);
nickjillings@1370 2178 }
nickjillings@1370 2179 }
nickjillings@1372 2180 this.encode = function(doc) {
nickjillings@1372 2181 var node = doc.createElement('metric');
nickjillings@1370 2182 for (var i in this.enabled)
nickjillings@1370 2183 {
nickjillings@1370 2184 if (isNaN(Number(i)) == true){break;}
nickjillings@1372 2185 var child = doc.createElement('metricenable');
nickjillings@1370 2186 child.textContent = this.enabled[i];
nickjillings@1370 2187 node.appendChild(child);
nickjillings@1370 2188 }
nickjillings@1370 2189 return node;
nickjillings@1370 2190 }
nickjillings@1370 2191 }
nickjillings@1370 2192
nickjillings@1324 2193 this.page = function() {
nickjillings@1406 2194 this.presentedId = undefined;
nickjillings@1406 2195 this.id = undefined;
nickjillings@1406 2196 this.hostURL = undefined;
nickjillings@1406 2197 this.randomiseOrder = undefined;
nickjillings@1406 2198 this.loop = undefined;
nickjillings@1324 2199 this.showElementComments = undefined;
nickjillings@1406 2200 this.outsideReference = null;
nickjillings@1432 2201 this.loudness = null;
nickjillings@1295 2202 this.label = null;
nickjillings@1324 2203 this.preTest = null;
nickjillings@1324 2204 this.postTest = null;
nickjillings@1406 2205 this.interfaces = [];
nickjillings@1406 2206 this.commentBoxPrefix = "Comment on track";
nickjillings@1406 2207 this.audioElements = [];
nickjillings@1406 2208 this.commentQuestions = [];
nickjillings@1370 2209 this.schema = specification.schema.getAllElementsByName("page")[0];
nickjillings@2165 2210 this.parent = null;
nickjillings@1406 2211
nickjillings@1406 2212 this.decode = function(parent,xml)
nickjillings@1406 2213 {
nickjillings@2165 2214 this.parent = parent;
nickjillings@1348 2215 var attributeMap = this.schema.getAllElementsByTagName('xs:attribute');
nickjillings@1324 2216 for (var i=0; i<attributeMap.length; i++)
nickjillings@1432 2217 {
nickjillings@1324 2218 var attributeName = attributeMap[i].getAttribute('name') || attributeMap[i].getAttribute('ref');
nickjillings@1324 2219 var projectAttr = xml.getAttribute(attributeName);
nickjillings@2165 2220 projectAttr = parent.processAttribute(projectAttr,attributeMap[i],parent.schema);
nickjillings@1324 2221 switch(typeof projectAttr)
nickjillings@1395 2222 {
nickjillings@1324 2223 case "number":
nickjillings@1324 2224 case "boolean":
nickjillings@1324 2225 eval('this.'+attributeName+' = '+projectAttr);
nickjillings@1324 2226 break;
nickjillings@1324 2227 case "string":
nickjillings@1324 2228 eval('this.'+attributeName+' = "'+projectAttr+'"');
nickjillings@1324 2229 break;
nickjillings@1406 2230 }
nickjillings@1406 2231 }
nickjillings@1406 2232
nickjillings@1324 2233 // Get the Comment Box Prefix
nickjillings@1324 2234 var CBP = xml.getElementsByTagName('commentboxprefix');
nickjillings@1324 2235 if (CBP.length != 0) {
nickjillings@1324 2236 this.commentBoxPrefix = CBP[0].textContent;
nickjillings@1441 2237 }
nickjillings@1441 2238
nickjillings@1324 2239 // Now decode the interfaces
nickjillings@1324 2240 var interfaceNode = xml.getElementsByTagName('interface');
nickjillings@1324 2241 for (var i=0; i<interfaceNode.length; i++)
nickjillings@1324 2242 {
nickjillings@1324 2243 var node = new parent.interfaceNode();
nickjillings@1348 2244 node.decode(this,interfaceNode[i],parent.schema.getAllElementsByName('interface')[1]);
nickjillings@1324 2245 this.interfaces.push(node);
nickjillings@1324 2246 }
nickjillings@1411 2247
nickjillings@1324 2248 // Now process the survey node options
nickjillings@1324 2249 var survey = xml.getElementsByTagName('survey');
nickjillings@1348 2250 var surveySchema = parent.schema.getAllElementsByName('survey')[0];
nickjillings@1324 2251 for (var i in survey) {
nickjillings@1324 2252 if (isNaN(Number(i)) == true){break;}
nickjillings@1324 2253 var location = survey[i].getAttribute('location');
nickjillings@1324 2254 if (location == 'pre' || location == 'before')
nickjillings@1324 2255 {
nickjillings@1324 2256 if (this.preTest != null){this.errors.push("Already a pre/before test survey defined! Ignoring second!!");}
nickjillings@1324 2257 else {
nickjillings@1324 2258 this.preTest = new parent.surveyNode();
nickjillings@1324 2259 this.preTest.decode(parent,survey[i],surveySchema);
nickjillings@1324 2260 }
nickjillings@1324 2261 } else if (location == 'post' || location == 'after') {
nickjillings@1324 2262 if (this.postTest != null){this.errors.push("Already a post/after test survey defined! Ignoring second!!");}
nickjillings@1324 2263 else {
nickjillings@1324 2264 this.postTest = new parent.surveyNode();
nickjillings@1324 2265 this.postTest.decode(parent,survey[i],surveySchema);
nickjillings@1324 2266 }
nickjillings@1324 2267 }
nickjillings@1324 2268 }
nickjillings@1324 2269
nickjillings@1324 2270 // Now process the audioelement tags
nickjillings@1324 2271 var audioElements = xml.getElementsByTagName('audioelement');
nickjillings@1324 2272 for (var i=0; i<audioElements.length; i++)
nickjillings@1324 2273 {
nickjillings@1324 2274 var node = new this.audioElementNode();
nickjillings@1370 2275 node.decode(this,audioElements[i]);
nickjillings@1324 2276 this.audioElements.push(node);
nickjillings@1324 2277 }
nickjillings@1324 2278
nickjillings@1324 2279 // Now decode the commentquestions
nickjillings@1324 2280 var commentQuestions = xml.getElementsByTagName('commentquestion');
nickjillings@1324 2281 for (var i=0; i<commentQuestions.length; i++)
nickjillings@1324 2282 {
nickjillings@1406 2283 var node = new this.commentQuestionNode();
nickjillings@1370 2284 node.decode(parent,commentQuestions[i]);
nickjillings@1406 2285 this.commentQuestions.push(node);
nickjillings@1580 2286 }
nickjillings@1580 2287 };
nickjillings@1580 2288
nickjillings@1406 2289 this.encode = function(root)
nickjillings@1406 2290 {
nickjillings@1372 2291 var AHNode = root.createElement("page");
nickjillings@1372 2292 // First decode the attributes
nickjillings@1372 2293 var attributes = this.schema.getAllElementsByTagName('xs:attribute');
nickjillings@1372 2294 for (var i=0; i<attributes.length; i++)
nickjillings@1372 2295 {
nickjillings@1372 2296 var name = attributes[i].getAttribute("name");
nickjillings@1372 2297 if (name == undefined) {
nickjillings@1372 2298 name = attributes[i].getAttribute("ref");
nickjillings@1372 2299 }
nickjillings@1372 2300 if(eval("this."+name+" != undefined") || attributes[i].getAttribute("use") == "required")
nickjillings@1372 2301 {
nickjillings@1372 2302 eval("AHNode.setAttribute('"+name+"',this."+name+")");
nickjillings@1372 2303 }
nickjillings@1372 2304 }
nickjillings@1432 2305 if(this.loudness != null) {AHNode.setAttribute("loudness",this.loudness);}
nickjillings@1372 2306 // <commentboxprefix>
nickjillings@1372 2307 var commentboxprefix = root.createElement("commentboxprefix");
nickjillings@1372 2308 commentboxprefix.textContent = this.commentBoxPrefix;
nickjillings@1372 2309 AHNode.appendChild(commentboxprefix);
nickjillings@1372 2310
nickjillings@1406 2311 for (var i=0; i<this.interfaces.length; i++)
nickjillings@1404 2312 {
nickjillings@1406 2313 AHNode.appendChild(this.interfaces[i].encode(root));
nickjillings@1406 2314 }
nickjillings@1558 2315
nickjillings@1406 2316 for (var i=0; i<this.audioElements.length; i++) {
nickjillings@1406 2317 AHNode.appendChild(this.audioElements[i].encode(root));
nickjillings@1406 2318 }
nickjillings@1406 2319 // Create <CommentQuestion>
nickjillings@1406 2320 for (var i=0; i<this.commentQuestions.length; i++)
nickjillings@1455 2321 {
nickjillings@1372 2322 AHNode.appendChild(this.commentQuestions[i].encode(root));
nickjillings@1406 2323 }
nickjillings@1406 2324
nickjillings@1372 2325 AHNode.appendChild(this.preTest.encode(root));
nickjillings@1372 2326 AHNode.appendChild(this.postTest.encode(root));
nickjillings@1406 2327 return AHNode;
nickjillings@1406 2328 };
nickjillings@1406 2329
nickjillings@1324 2330 this.commentQuestionNode = function() {
nickjillings@1324 2331 this.id = null;
nickjillings@2156 2332 this.name = undefined;
nickjillings@1324 2333 this.type = undefined;
nickjillings@1406 2334 this.options = [];
nickjillings@1324 2335 this.statement = undefined;
nickjillings@1370 2336 this.schema = specification.schema.getAllElementsByName('commentquestion')[0];
nickjillings@1370 2337 this.decode = function(parent,xml)
nickjillings@1406 2338 {
nickjillings@1324 2339 this.id = xml.id;
nickjillings@2156 2340 this.name = xml.getAttribute('name');
nickjillings@1324 2341 this.type = xml.getAttribute('type');
nickjillings@1324 2342 this.statement = xml.getElementsByTagName('statement')[0].textContent;
nickjillings@1324 2343 var optNodes = xml.getElementsByTagName('option');
nickjillings@1324 2344 for (var i=0; i<optNodes.length; i++)
nickjillings@1324 2345 {
nickjillings@1324 2346 var optNode = optNodes[i];
nickjillings@1324 2347 this.options.push({
nickjillings@1324 2348 name: optNode.getAttribute('name'),
nickjillings@1324 2349 text: optNode.textContent
nickjillings@1324 2350 });
nickjillings@1406 2351 }
nickjillings@1406 2352 };
nickjillings@1324 2353
nickjillings@1406 2354 this.encode = function(root)
nickjillings@1406 2355 {
nickjillings@1372 2356 var node = root.createElement("commentquestion");
nickjillings@1372 2357 node.id = this.id;
nickjillings@1372 2358 node.setAttribute("type",this.type);
nickjillings@2156 2359 if (this.name != undefined){node.setAttribute("name",this.name);}
nickjillings@1372 2360 var statement = root.createElement("statement");
nickjillings@1372 2361 statement.textContent = this.statement;
nickjillings@1372 2362 node.appendChild(statement);
nickjillings@1372 2363 for (var option of this.options)
nickjillings@1372 2364 {
nickjillings@1372 2365 var child = root.createElement("option");
nickjillings@1372 2366 child.setAttribute("name",option.name);
nickjillings@1372 2367 child.textContent = option.text;
nickjillings@1372 2368 node.appendChild(child);
nickjillings@1372 2369 }
nickjillings@1372 2370 return node;
nickjillings@1406 2371 };
nickjillings@1406 2372 };
nickjillings@1406 2373
nickjillings@1406 2374 this.audioElementNode = function() {
nickjillings@1406 2375 this.url = null;
nickjillings@1406 2376 this.id = null;
nickjillings@2156 2377 this.name = null;
nickjillings@1406 2378 this.parent = null;
nickjillings@1324 2379 this.type = null;
nickjillings@1310 2380 this.marker = null;
nickjillings@1406 2381 this.enforce = false;
nickjillings@2130 2382 this.gain = 0.0;
nickjillings@1370 2383 this.schema = specification.schema.getAllElementsByName('audioelement')[0];;
nickjillings@1324 2384 this.parent = null;
nickjillings@1406 2385 this.decode = function(parent,xml)
nickjillings@1406 2386 {
nickjillings@1406 2387 this.parent = parent;
nickjillings@1348 2388 var attributeMap = this.schema.getAllElementsByTagName('xs:attribute');
nickjillings@1324 2389 for (var i=0; i<attributeMap.length; i++)
nickjillings@1426 2390 {
nickjillings@1324 2391 var attributeName = attributeMap[i].getAttribute('name') || attributeMap[i].getAttribute('ref');
nickjillings@1324 2392 var projectAttr = xml.getAttribute(attributeName);
nickjillings@2165 2393 projectAttr = parent.parent.processAttribute(projectAttr,attributeMap[i],parent.parent.schema);
nickjillings@1324 2394 switch(typeof projectAttr)
nickjillings@1406 2395 {
nickjillings@1324 2396 case "number":
nickjillings@1324 2397 case "boolean":
nickjillings@1324 2398 eval('this.'+attributeName+' = '+projectAttr);
nickjillings@1324 2399 break;
nickjillings@1324 2400 case "string":
nickjillings@1324 2401 eval('this.'+attributeName+' = "'+projectAttr+'"');
nickjillings@1324 2402 break;
nickjillings@1455 2403 }
nickjillings@1455 2404 }
nickjillings@1324 2405
nickjillings@1406 2406 };
nickjillings@1406 2407 this.encode = function(root)
nickjillings@1406 2408 {
nickjillings@1372 2409 var AENode = root.createElement("audioelement");
nickjillings@1372 2410 var attributes = this.schema.getAllElementsByTagName('xs:attribute');
nickjillings@1372 2411 for (var i=0; i<attributes.length; i++)
nickjillings@1372 2412 {
nickjillings@1372 2413 var name = attributes[i].getAttribute("name");
nickjillings@1372 2414 if (name == undefined) {
nickjillings@1372 2415 name = attributes[i].getAttribute("ref");
nickjillings@1372 2416 }
nickjillings@1372 2417 if(eval("this."+name+" != undefined") || attributes[i].getAttribute("use") == "required")
nickjillings@1372 2418 {
nickjillings@1372 2419 eval("AENode.setAttribute('"+name+"',this."+name+")");
nickjillings@1372 2420 }
nickjillings@1372 2421 }
nickjillings@1406 2422 return AENode;
nickjillings@1406 2423 };
nickjillings@1580 2424 };
nickjillings@1580 2425 };
nickjillings@1580 2426 }
nickjillings@1406 2427
nickjillings@1582 2428 function Interface(specificationObject) {
nickjillings@1580 2429 // This handles the bindings between the interface and the audioEngineContext;
nickjillings@1582 2430 this.specification = specificationObject;
nickjillings@1582 2431 this.insertPoint = document.getElementById("topLevelBody");
nickjillings@1580 2432
nickjillings@1324 2433 this.newPage = function(audioHolderObject,store)
nickjillings@1407 2434 {
nickjillings@1369 2435 audioEngineContext.newTestPage(audioHolderObject,store);
nickjillings@2117 2436 interfaceContext.commentBoxes.deleteCommentBoxes();
nickjillings@1407 2437 interfaceContext.deleteCommentQuestions();
nickjillings@1324 2438 loadTest(audioHolderObject,store);
nickjillings@1407 2439 };
nickjillings@1407 2440
nickjillings@1582 2441 // Bounded by interface!!
nickjillings@1582 2442 // Interface object MUST have an exportXMLDOM method which returns the various DOM levels
nickjillings@1582 2443 // For example, APE returns the slider position normalised in a <value> tag.
nickjillings@1582 2444 this.interfaceObjects = [];
nickjillings@1582 2445 this.interfaceObject = function(){};
nickjillings@1582 2446
nickjillings@1525 2447 this.resizeWindow = function(event)
nickjillings@1525 2448 {
nickjillings@1421 2449 popup.resize(event);
nickjillings@1525 2450 for(var i=0; i<this.commentBoxes.length; i++)
nickjillings@1525 2451 {this.commentBoxes[i].resize();}
nickjillings@1525 2452 for(var i=0; i<this.commentQuestions.length; i++)
nickjillings@1525 2453 {this.commentQuestions[i].resize();}
nickjillings@1525 2454 try
nickjillings@1525 2455 {
nickjillings@1525 2456 resizeWindow(event);
nickjillings@1525 2457 }
nickjillings@1525 2458 catch(err)
nickjillings@1525 2459 {
nickjillings@1525 2460 console.log("Warning - Interface does not have Resize option");
nickjillings@1525 2461 console.log(err);
nickjillings@1525 2462 }
nickjillings@1525 2463 };
nickjillings@1525 2464
nickjillings@1465 2465 this.returnNavigator = function()
nickjillings@1465 2466 {
nickjillings@1362 2467 var node = storage.document.createElement("navigator");
nickjillings@1362 2468 var platform = storage.document.createElement("platform");
nickjillings@1465 2469 platform.textContent = navigator.platform;
nickjillings@1362 2470 var vendor = storage.document.createElement("vendor");
nickjillings@1465 2471 vendor.textContent = navigator.vendor;
nickjillings@1362 2472 var userAgent = storage.document.createElement("uagent");
nickjillings@1465 2473 userAgent.textContent = navigator.userAgent;
nickjillings@1362 2474 var screen = storage.document.createElement("window");
nickjillings@1362 2475 screen.setAttribute('innerWidth',window.innerWidth);
nickjillings@1362 2476 screen.setAttribute('innerHeight',window.innerHeight);
nickjillings@1465 2477 node.appendChild(platform);
nickjillings@1465 2478 node.appendChild(vendor);
nickjillings@1465 2479 node.appendChild(userAgent);
nickjillings@1362 2480 node.appendChild(screen);
nickjillings@1465 2481 return node;
nickjillings@1465 2482 };
nickjillings@1465 2483
nickjillings@2117 2484 this.commentBoxes = new function() {
nickjillings@2117 2485 this.boxes = [];
nickjillings@2117 2486 this.injectPoint = null;
nickjillings@2117 2487 this.elementCommentBox = function(audioObject) {
nickjillings@2117 2488 var element = audioObject.specification;
nickjillings@2117 2489 this.audioObject = audioObject;
nickjillings@2117 2490 this.id = audioObject.id;
nickjillings@2117 2491 var audioHolderObject = audioObject.specification.parent;
nickjillings@2117 2492 // Create document objects to hold the comment boxes
nickjillings@2117 2493 this.trackComment = document.createElement('div');
nickjillings@2117 2494 this.trackComment.className = 'comment-div';
nickjillings@2117 2495 this.trackComment.id = 'comment-div-'+audioObject.id;
nickjillings@2117 2496 // Create a string next to each comment asking for a comment
nickjillings@2117 2497 this.trackString = document.createElement('span');
nickjillings@2117 2498 this.trackString.innerHTML = audioHolderObject.commentBoxPrefix+' '+audioObject.interfaceDOM.getPresentedId();
nickjillings@2117 2499 // Create the HTML5 comment box 'textarea'
nickjillings@2117 2500 this.trackCommentBox = document.createElement('textarea');
nickjillings@2117 2501 this.trackCommentBox.rows = '4';
nickjillings@2117 2502 this.trackCommentBox.cols = '100';
nickjillings@2117 2503 this.trackCommentBox.name = 'trackComment'+audioObject.id;
nickjillings@2117 2504 this.trackCommentBox.className = 'trackComment';
nickjillings@2117 2505 var br = document.createElement('br');
nickjillings@2117 2506 // Add to the holder.
nickjillings@2117 2507 this.trackComment.appendChild(this.trackString);
nickjillings@2117 2508 this.trackComment.appendChild(br);
nickjillings@2117 2509 this.trackComment.appendChild(this.trackCommentBox);
nickjillings@2117 2510
nickjillings@2117 2511 this.exportXMLDOM = function() {
nickjillings@2117 2512 var root = document.createElement('comment');
nickjillings@2117 2513 var question = document.createElement('question');
nickjillings@2117 2514 question.textContent = this.trackString.textContent;
nickjillings@2117 2515 var response = document.createElement('response');
nickjillings@2117 2516 response.textContent = this.trackCommentBox.value;
nickjillings@2117 2517 console.log("Comment frag-"+this.id+": "+response.textContent);
nickjillings@2117 2518 root.appendChild(question);
nickjillings@2117 2519 root.appendChild(response);
nickjillings@2117 2520 return root;
nickjillings@2117 2521 };
nickjillings@2117 2522 this.resize = function()
nickjillings@2117 2523 {
nickjillings@2117 2524 var boxwidth = (window.innerWidth-100)/2;
nickjillings@2117 2525 if (boxwidth >= 600)
nickjillings@2117 2526 {
nickjillings@2117 2527 boxwidth = 600;
nickjillings@2117 2528 }
nickjillings@2117 2529 else if (boxwidth < 400)
nickjillings@2117 2530 {
nickjillings@2117 2531 boxwidth = 400;
nickjillings@2117 2532 }
nickjillings@2117 2533 this.trackComment.style.width = boxwidth+"px";
nickjillings@2117 2534 this.trackCommentBox.style.width = boxwidth-6+"px";
nickjillings@2117 2535 };
nickjillings@2117 2536 this.resize();
nickjillings@2117 2537 };
nickjillings@2117 2538 this.createCommentBox = function(audioObject) {
nickjillings@2117 2539 var node = new this.elementCommentBox(audioObject);
nickjillings@2117 2540 this.boxes.push(node);
nickjillings@2117 2541 audioObject.commentDOM = node;
nickjillings@2117 2542 return node;
nickjillings@2117 2543 };
nickjillings@2117 2544 this.sortCommentBoxes = function() {
nickjillings@2117 2545 this.boxes.sort(function(a,b){return a.id - b.id;});
nickjillings@2117 2546 };
nickjillings@2117 2547
nickjillings@2117 2548 this.showCommentBoxes = function(inject, sort) {
nickjillings@2117 2549 this.injectPoint = inject;
nickjillings@2117 2550 if (sort) {this.sortCommentBoxes();}
nickjillings@2117 2551 for (var box of this.boxes) {
nickjillings@2117 2552 inject.appendChild(box.trackComment);
nickjillings@2117 2553 }
nickjillings@2117 2554 };
nickjillings@2117 2555
nickjillings@2117 2556 this.deleteCommentBoxes = function() {
nickjillings@2117 2557 if (this.injectPoint != null) {
nickjillings@2117 2558 for (var box of this.boxes) {
nickjillings@2117 2559 this.injectPoint.removeChild(box.trackComment);
nickjillings@2117 2560 }
nickjillings@2117 2561 this.injectPoint = null;
nickjillings@2117 2562 }
nickjillings@2117 2563 this.boxes = [];
nickjillings@2117 2564 };
nickjillings@2117 2565 }
nickjillings@1582 2566
nickjillings@2032 2567 this.commentQuestions = [];
nickjillings@2032 2568
nickjillings@2032 2569 this.commentBox = function(commentQuestion) {
nickjillings@2032 2570 this.specification = commentQuestion;
nickjillings@2032 2571 // Create document objects to hold the comment boxes
nickjillings@2032 2572 this.holder = document.createElement('div');
nickjillings@2032 2573 this.holder.className = 'comment-div';
nickjillings@2032 2574 // Create a string next to each comment asking for a comment
nickjillings@2032 2575 this.string = document.createElement('span');
nickjillings@1324 2576 this.string.innerHTML = commentQuestion.statement;
nickjillings@2032 2577 // Create the HTML5 comment box 'textarea'
nickjillings@2032 2578 this.textArea = document.createElement('textarea');
nickjillings@2032 2579 this.textArea.rows = '4';
nickjillings@2032 2580 this.textArea.cols = '100';
nickjillings@2032 2581 this.textArea.className = 'trackComment';
nickjillings@2032 2582 var br = document.createElement('br');
nickjillings@2032 2583 // Add to the holder.
nickjillings@2032 2584 this.holder.appendChild(this.string);
nickjillings@2032 2585 this.holder.appendChild(br);
nickjillings@2032 2586 this.holder.appendChild(this.textArea);
nickjillings@2032 2587
nickjillings@2097 2588 this.exportXMLDOM = function(storePoint) {
nickjillings@2097 2589 var root = storePoint.parent.document.createElement('comment');
nickjillings@2032 2590 root.id = this.specification.id;
nickjillings@2032 2591 root.setAttribute('type',this.specification.type);
b@2059 2592 console.log("Question: "+this.string.textContent);
b@2059 2593 console.log("Response: "+root.textContent);
nickjillings@2097 2594 var question = storePoint.parent.document.createElement('question');
nickjillings@2097 2595 question.textContent = this.string.textContent;
nickjillings@2097 2596 var response = storePoint.parent.document.createElement('response');
nickjillings@2097 2597 response.textContent = this.textArea.value;
nickjillings@2097 2598 root.appendChild(question);
nickjillings@2097 2599 root.appendChild(response);
nickjillings@2097 2600 storePoint.XMLDOM.appendChild(root);
nickjillings@2032 2601 return root;
nickjillings@2032 2602 };
nickjillings@1525 2603 this.resize = function()
nickjillings@1525 2604 {
nickjillings@1525 2605 var boxwidth = (window.innerWidth-100)/2;
nickjillings@1525 2606 if (boxwidth >= 600)
nickjillings@1525 2607 {
nickjillings@1525 2608 boxwidth = 600;
nickjillings@1525 2609 }
nickjillings@1525 2610 else if (boxwidth < 400)
nickjillings@1525 2611 {
nickjillings@1525 2612 boxwidth = 400;
nickjillings@1525 2613 }
nickjillings@1525 2614 this.holder.style.width = boxwidth+"px";
nickjillings@1525 2615 this.textArea.style.width = boxwidth-6+"px";
nickjillings@1525 2616 };
nickjillings@1525 2617 this.resize();
nickjillings@2032 2618 };
nickjillings@2032 2619
nickjillings@2032 2620 this.radioBox = function(commentQuestion) {
nickjillings@2032 2621 this.specification = commentQuestion;
nickjillings@2032 2622 // Create document objects to hold the comment boxes
nickjillings@2032 2623 this.holder = document.createElement('div');
nickjillings@2032 2624 this.holder.className = 'comment-div';
nickjillings@2032 2625 // Create a string next to each comment asking for a comment
nickjillings@2032 2626 this.string = document.createElement('span');
nickjillings@2032 2627 this.string.innerHTML = commentQuestion.statement;
nickjillings@2032 2628 var br = document.createElement('br');
nickjillings@2032 2629 // Add to the holder.
nickjillings@2032 2630 this.holder.appendChild(this.string);
nickjillings@2032 2631 this.holder.appendChild(br);
nickjillings@2032 2632 this.options = [];
nickjillings@2032 2633 this.inputs = document.createElement('div');
nickjillings@2032 2634 this.span = document.createElement('div');
nickjillings@2032 2635 this.inputs.align = 'center';
nickjillings@2032 2636 this.inputs.style.marginLeft = '12px';
nickjillings@2032 2637 this.span.style.marginLeft = '12px';
nickjillings@2032 2638 this.span.align = 'center';
nickjillings@2032 2639 this.span.style.marginTop = '15px';
nickjillings@2032 2640
nickjillings@2032 2641 var optCount = commentQuestion.options.length;
nickjillings@1324 2642 for (var optNode of commentQuestion.options)
nickjillings@2032 2643 {
nickjillings@2032 2644 var div = document.createElement('div');
nickjillings@1524 2645 div.style.width = '80px';
nickjillings@2032 2646 div.style.float = 'left';
nickjillings@2032 2647 var input = document.createElement('input');
nickjillings@2032 2648 input.type = 'radio';
nickjillings@2032 2649 input.name = commentQuestion.id;
nickjillings@1324 2650 input.setAttribute('setvalue',optNode.name);
nickjillings@2032 2651 input.className = 'comment-radio';
nickjillings@2032 2652 div.appendChild(input);
nickjillings@2032 2653 this.inputs.appendChild(div);
nickjillings@2032 2654
nickjillings@2032 2655
nickjillings@2032 2656 div = document.createElement('div');
nickjillings@1524 2657 div.style.width = '80px';
nickjillings@2032 2658 div.style.float = 'left';
nickjillings@2032 2659 div.align = 'center';
nickjillings@2032 2660 var span = document.createElement('span');
nickjillings@1324 2661 span.textContent = optNode.text;
nickjillings@2032 2662 span.className = 'comment-radio-span';
nickjillings@2032 2663 div.appendChild(span);
nickjillings@2032 2664 this.span.appendChild(div);
nickjillings@2032 2665 this.options.push(input);
nickjillings@2032 2666 }
nickjillings@2032 2667 this.holder.appendChild(this.span);
nickjillings@2032 2668 this.holder.appendChild(this.inputs);
nickjillings@2032 2669
nickjillings@2097 2670 this.exportXMLDOM = function(storePoint) {
nickjillings@2097 2671 var root = storePoint.parent.document.createElement('comment');
nickjillings@2032 2672 root.id = this.specification.id;
nickjillings@2032 2673 root.setAttribute('type',this.specification.type);
nickjillings@2032 2674 var question = document.createElement('question');
nickjillings@2032 2675 question.textContent = this.string.textContent;
nickjillings@2032 2676 var response = document.createElement('response');
nickjillings@2032 2677 var i=0;
nickjillings@2032 2678 while(this.options[i].checked == false) {
nickjillings@2032 2679 i++;
nickjillings@2032 2680 if (i >= this.options.length) {
nickjillings@2032 2681 break;
nickjillings@2032 2682 }
nickjillings@2032 2683 }
nickjillings@2032 2684 if (i >= this.options.length) {
nickjillings@2032 2685 response.textContent = 'null';
nickjillings@2032 2686 } else {
nickjillings@2032 2687 response.textContent = this.options[i].getAttribute('setvalue');
nickjillings@2032 2688 response.setAttribute('number',i);
nickjillings@2032 2689 }
nickjillings@1572 2690 console.log('Comment: '+question.textContent);
nickjillings@1572 2691 console.log('Response: '+response.textContent);
nickjillings@2032 2692 root.appendChild(question);
nickjillings@2032 2693 root.appendChild(response);
nickjillings@2097 2694 storePoint.XMLDOM.appendChild(root);
nickjillings@2032 2695 return root;
nickjillings@2032 2696 };
nickjillings@1525 2697 this.resize = function()
nickjillings@1525 2698 {
nickjillings@1525 2699 var boxwidth = (window.innerWidth-100)/2;
nickjillings@1525 2700 if (boxwidth >= 600)
nickjillings@1525 2701 {
nickjillings@1525 2702 boxwidth = 600;
nickjillings@1525 2703 }
nickjillings@1525 2704 else if (boxwidth < 400)
nickjillings@1525 2705 {
nickjillings@1525 2706 boxwidth = 400;
nickjillings@1525 2707 }
nickjillings@1525 2708 this.holder.style.width = boxwidth+"px";
nickjillings@1525 2709 var text = this.holder.children[2];
nickjillings@1525 2710 var options = this.holder.children[3];
nickjillings@1525 2711 var optCount = options.children.length;
nickjillings@1525 2712 var spanMargin = Math.floor(((boxwidth-20-(optCount*80))/(optCount))/2)+'px';
nickjillings@1525 2713 var options = options.firstChild;
nickjillings@1525 2714 var text = text.firstChild;
nickjillings@1525 2715 options.style.marginRight = spanMargin;
nickjillings@1525 2716 options.style.marginLeft = spanMargin;
nickjillings@1525 2717 text.style.marginRight = spanMargin;
nickjillings@1525 2718 text.style.marginLeft = spanMargin;
nickjillings@1525 2719 while(options.nextSibling != undefined)
nickjillings@1525 2720 {
nickjillings@1525 2721 options = options.nextSibling;
nickjillings@1525 2722 text = text.nextSibling;
nickjillings@1525 2723 options.style.marginRight = spanMargin;
nickjillings@1525 2724 options.style.marginLeft = spanMargin;
nickjillings@1525 2725 text.style.marginRight = spanMargin;
nickjillings@1525 2726 text.style.marginLeft = spanMargin;
nickjillings@1525 2727 }
nickjillings@1525 2728 };
nickjillings@1525 2729 this.resize();
nickjillings@2032 2730 };
nickjillings@2032 2731
nickjillings@1572 2732 this.checkboxBox = function(commentQuestion) {
nickjillings@1572 2733 this.specification = commentQuestion;
nickjillings@1572 2734 // Create document objects to hold the comment boxes
nickjillings@1572 2735 this.holder = document.createElement('div');
nickjillings@1572 2736 this.holder.className = 'comment-div';
nickjillings@1572 2737 // Create a string next to each comment asking for a comment
nickjillings@1572 2738 this.string = document.createElement('span');
nickjillings@1572 2739 this.string.innerHTML = commentQuestion.statement;
nickjillings@1572 2740 var br = document.createElement('br');
nickjillings@1572 2741 // Add to the holder.
nickjillings@1572 2742 this.holder.appendChild(this.string);
nickjillings@1572 2743 this.holder.appendChild(br);
nickjillings@1572 2744 this.options = [];
nickjillings@1572 2745 this.inputs = document.createElement('div');
nickjillings@1572 2746 this.span = document.createElement('div');
nickjillings@1572 2747 this.inputs.align = 'center';
nickjillings@1572 2748 this.inputs.style.marginLeft = '12px';
nickjillings@1572 2749 this.span.style.marginLeft = '12px';
nickjillings@1572 2750 this.span.align = 'center';
nickjillings@1572 2751 this.span.style.marginTop = '15px';
nickjillings@1572 2752
nickjillings@1572 2753 var optCount = commentQuestion.options.length;
nickjillings@1572 2754 for (var i=0; i<optCount; i++)
nickjillings@1572 2755 {
nickjillings@1572 2756 var div = document.createElement('div');
nickjillings@1524 2757 div.style.width = '80px';
nickjillings@1572 2758 div.style.float = 'left';
nickjillings@1572 2759 var input = document.createElement('input');
nickjillings@1572 2760 input.type = 'checkbox';
nickjillings@1572 2761 input.name = commentQuestion.id;
nickjillings@1572 2762 input.setAttribute('setvalue',commentQuestion.options[i].name);
nickjillings@1572 2763 input.className = 'comment-radio';
nickjillings@1572 2764 div.appendChild(input);
nickjillings@1572 2765 this.inputs.appendChild(div);
nickjillings@1572 2766
nickjillings@1572 2767
nickjillings@1572 2768 div = document.createElement('div');
nickjillings@1524 2769 div.style.width = '80px';
nickjillings@1572 2770 div.style.float = 'left';
nickjillings@1572 2771 div.align = 'center';
nickjillings@1572 2772 var span = document.createElement('span');
nickjillings@1572 2773 span.textContent = commentQuestion.options[i].text;
nickjillings@1572 2774 span.className = 'comment-radio-span';
nickjillings@1572 2775 div.appendChild(span);
nickjillings@1572 2776 this.span.appendChild(div);
nickjillings@1572 2777 this.options.push(input);
nickjillings@1572 2778 }
nickjillings@1572 2779 this.holder.appendChild(this.span);
nickjillings@1572 2780 this.holder.appendChild(this.inputs);
nickjillings@1572 2781
nickjillings@2097 2782 this.exportXMLDOM = function(storePoint) {
nickjillings@2097 2783 var root = storePoint.parent.document.createElement('comment');
nickjillings@1572 2784 root.id = this.specification.id;
nickjillings@1572 2785 root.setAttribute('type',this.specification.type);
nickjillings@1572 2786 var question = document.createElement('question');
nickjillings@1572 2787 question.textContent = this.string.textContent;
nickjillings@1572 2788 root.appendChild(question);
nickjillings@1572 2789 console.log('Comment: '+question.textContent);
nickjillings@1572 2790 for (var i=0; i<this.options.length; i++) {
nickjillings@1572 2791 var response = document.createElement('response');
nickjillings@1572 2792 response.textContent = this.options[i].checked;
nickjillings@1572 2793 response.setAttribute('name',this.options[i].getAttribute('setvalue'));
nickjillings@1572 2794 root.appendChild(response);
nickjillings@1572 2795 console.log('Response '+response.getAttribute('name') +': '+response.textContent);
nickjillings@1572 2796 }
nickjillings@2097 2797 storePoint.XMLDOM.appendChild(root);
nickjillings@1572 2798 return root;
nickjillings@1572 2799 };
nickjillings@1525 2800 this.resize = function()
nickjillings@1525 2801 {
nickjillings@1525 2802 var boxwidth = (window.innerWidth-100)/2;
nickjillings@1525 2803 if (boxwidth >= 600)
nickjillings@1525 2804 {
nickjillings@1525 2805 boxwidth = 600;
nickjillings@1525 2806 }
nickjillings@1525 2807 else if (boxwidth < 400)
nickjillings@1525 2808 {
nickjillings@1525 2809 boxwidth = 400;
nickjillings@1525 2810 }
nickjillings@1525 2811 this.holder.style.width = boxwidth+"px";
nickjillings@1525 2812 var text = this.holder.children[2];
nickjillings@1525 2813 var options = this.holder.children[3];
nickjillings@1525 2814 var optCount = options.children.length;
nickjillings@1525 2815 var spanMargin = Math.floor(((boxwidth-20-(optCount*80))/(optCount))/2)+'px';
nickjillings@1525 2816 var options = options.firstChild;
nickjillings@1525 2817 var text = text.firstChild;
nickjillings@1525 2818 options.style.marginRight = spanMargin;
nickjillings@1525 2819 options.style.marginLeft = spanMargin;
nickjillings@1525 2820 text.style.marginRight = spanMargin;
nickjillings@1525 2821 text.style.marginLeft = spanMargin;
nickjillings@1525 2822 while(options.nextSibling != undefined)
nickjillings@1525 2823 {
nickjillings@1525 2824 options = options.nextSibling;
nickjillings@1525 2825 text = text.nextSibling;
nickjillings@1525 2826 options.style.marginRight = spanMargin;
nickjillings@1525 2827 options.style.marginLeft = spanMargin;
nickjillings@1525 2828 text.style.marginRight = spanMargin;
nickjillings@1525 2829 text.style.marginLeft = spanMargin;
nickjillings@1525 2830 }
nickjillings@1525 2831 };
nickjillings@1525 2832 this.resize();
nickjillings@1572 2833 };
nickjillings@1570 2834
nickjillings@2032 2835 this.createCommentQuestion = function(element) {
nickjillings@2032 2836 var node;
nickjillings@1324 2837 if (element.type == 'question') {
nickjillings@2032 2838 node = new this.commentBox(element);
nickjillings@2032 2839 } else if (element.type == 'radio') {
nickjillings@2032 2840 node = new this.radioBox(element);
nickjillings@1572 2841 } else if (element.type == 'checkbox') {
nickjillings@1572 2842 node = new this.checkboxBox(element);
nickjillings@2032 2843 }
nickjillings@2032 2844 this.commentQuestions.push(node);
nickjillings@2032 2845 return node;
nickjillings@2032 2846 };
nickjillings@1564 2847
nickjillings@2051 2848 this.deleteCommentQuestions = function()
nickjillings@2051 2849 {
nickjillings@2051 2850 this.commentQuestions = [];
nickjillings@2051 2851 };
nickjillings@2051 2852
nickjillings@1564 2853 this.playhead = new function()
nickjillings@1564 2854 {
nickjillings@1564 2855 this.object = document.createElement('div');
nickjillings@1564 2856 this.object.className = 'playhead';
nickjillings@1564 2857 this.object.align = 'left';
nickjillings@1564 2858 var curTime = document.createElement('div');
nickjillings@1564 2859 curTime.style.width = '50px';
nickjillings@1564 2860 this.curTimeSpan = document.createElement('span');
nickjillings@1564 2861 this.curTimeSpan.textContent = '00:00';
nickjillings@1564 2862 curTime.appendChild(this.curTimeSpan);
nickjillings@1564 2863 this.object.appendChild(curTime);
nickjillings@1564 2864 this.scrubberTrack = document.createElement('div');
nickjillings@1564 2865 this.scrubberTrack.className = 'playhead-scrub-track';
nickjillings@1564 2866
nickjillings@1564 2867 this.scrubberHead = document.createElement('div');
nickjillings@1564 2868 this.scrubberHead.id = 'playhead-scrubber';
nickjillings@1564 2869 this.scrubberTrack.appendChild(this.scrubberHead);
nickjillings@1564 2870 this.object.appendChild(this.scrubberTrack);
nickjillings@1564 2871
nickjillings@1564 2872 this.timePerPixel = 0;
nickjillings@1564 2873 this.maxTime = 0;
nickjillings@1564 2874
nickjillings@1567 2875 this.playbackObject;
nickjillings@1567 2876
nickjillings@1567 2877 this.setTimePerPixel = function(audioObject) {
nickjillings@1564 2878 //maxTime must be in seconds
nickjillings@1567 2879 this.playbackObject = audioObject;
nickjillings@1410 2880 this.maxTime = audioObject.buffer.buffer.duration;
nickjillings@1564 2881 var width = 490; //500 - 10, 5 each side of the tracker head
nickjillings@1567 2882 this.timePerPixel = this.maxTime/490;
nickjillings@1567 2883 if (this.maxTime < 60) {
nickjillings@1564 2884 this.curTimeSpan.textContent = '0.00';
nickjillings@1564 2885 } else {
nickjillings@1564 2886 this.curTimeSpan.textContent = '00:00';
nickjillings@1564 2887 }
nickjillings@1564 2888 };
nickjillings@1564 2889
nickjillings@1567 2890 this.update = function() {
nickjillings@1564 2891 // Update the playhead position, startPlay must be called
nickjillings@1564 2892 if (this.timePerPixel > 0) {
nickjillings@1567 2893 var time = this.playbackObject.getCurrentPosition();
nickjillings@1367 2894 if (time > 0 && time < this.maxTime) {
nickjillings@1530 2895 var width = 490;
nickjillings@1530 2896 var pix = Math.floor(time/this.timePerPixel);
nickjillings@1530 2897 this.scrubberHead.style.left = pix+'px';
nickjillings@1530 2898 if (this.maxTime > 60.0) {
nickjillings@1530 2899 var secs = time%60;
nickjillings@1530 2900 var mins = Math.floor((time-secs)/60);
nickjillings@1530 2901 secs = secs.toString();
nickjillings@1530 2902 secs = secs.substr(0,2);
nickjillings@1530 2903 mins = mins.toString();
nickjillings@1530 2904 this.curTimeSpan.textContent = mins+':'+secs;
nickjillings@1530 2905 } else {
nickjillings@1530 2906 time = time.toString();
nickjillings@1530 2907 this.curTimeSpan.textContent = time.substr(0,4);
nickjillings@1530 2908 }
nickjillings@1564 2909 } else {
nickjillings@1530 2910 this.scrubberHead.style.left = '0px';
nickjillings@1530 2911 if (this.maxTime < 60) {
nickjillings@1530 2912 this.curTimeSpan.textContent = '0.00';
nickjillings@1530 2913 } else {
nickjillings@1530 2914 this.curTimeSpan.textContent = '00:00';
nickjillings@1530 2915 }
nickjillings@1564 2916 }
nickjillings@1564 2917 }
nickjillings@1564 2918 };
nickjillings@1567 2919
nickjillings@1567 2920 this.interval = undefined;
nickjillings@1567 2921
nickjillings@1567 2922 this.start = function() {
nickjillings@1567 2923 if (this.playbackObject != undefined && this.interval == undefined) {
nickjillings@1530 2924 if (this.maxTime < 60) {
nickjillings@1530 2925 this.interval = setInterval(function(){interfaceContext.playhead.update();},10);
nickjillings@1530 2926 } else {
nickjillings@1530 2927 this.interval = setInterval(function(){interfaceContext.playhead.update();},100);
nickjillings@1530 2928 }
nickjillings@1567 2929 }
nickjillings@1567 2930 };
nickjillings@1567 2931 this.stop = function() {
nickjillings@1567 2932 clearInterval(this.interval);
nickjillings@1567 2933 this.interval = undefined;
nickjillings@2101 2934 this.scrubberHead.style.left = '0px';
nickjillings@1530 2935 if (this.maxTime < 60) {
nickjillings@1530 2936 this.curTimeSpan.textContent = '0.00';
nickjillings@1530 2937 } else {
nickjillings@1530 2938 this.curTimeSpan.textContent = '00:00';
nickjillings@1530 2939 }
nickjillings@1567 2940 };
nickjillings@1564 2941 };
nickjillings@1354 2942
nickjillings@1354 2943 this.volume = new function()
nickjillings@1354 2944 {
nickjillings@1354 2945 // An in-built volume module which can be viewed on page
nickjillings@1354 2946 // Includes trackers on page-by-page data
nickjillings@1354 2947 // Volume does NOT reset to 0dB on each page load
nickjillings@1354 2948 this.valueLin = 1.0;
nickjillings@1354 2949 this.valueDB = 0.0;
nickjillings@1354 2950 this.object = document.createElement('div');
nickjillings@1354 2951 this.object.id = 'master-volume-holder';
nickjillings@1354 2952 this.slider = document.createElement('input');
nickjillings@1354 2953 this.slider.id = 'master-volume-control';
nickjillings@1354 2954 this.slider.type = 'range';
nickjillings@1354 2955 this.valueText = document.createElement('span');
nickjillings@1354 2956 this.valueText.id = 'master-volume-feedback';
nickjillings@1354 2957 this.valueText.textContent = '0dB';
nickjillings@1354 2958
nickjillings@1354 2959 this.slider.min = -60;
nickjillings@1354 2960 this.slider.max = 12;
nickjillings@1354 2961 this.slider.value = 0;
nickjillings@1354 2962 this.slider.step = 1;
nickjillings@1354 2963 this.slider.onmousemove = function(event)
nickjillings@1354 2964 {
nickjillings@1354 2965 interfaceContext.volume.valueDB = event.currentTarget.value;
nickjillings@1354 2966 interfaceContext.volume.valueLin = decibelToLinear(interfaceContext.volume.valueDB);
nickjillings@1354 2967 interfaceContext.volume.valueText.textContent = interfaceContext.volume.valueDB+'dB';
nickjillings@1354 2968 audioEngineContext.outputGain.gain.value = interfaceContext.volume.valueLin;
nickjillings@1354 2969 }
nickjillings@1354 2970 this.slider.onmouseup = function(event)
nickjillings@1354 2971 {
nickjillings@2100 2972 var storePoint = testState.currentStore.XMLDOM.getElementsByTagName('metric')[0].getAllElementsByName('volumeTracker');
nickjillings@1354 2973 if (storePoint.length == 0)
nickjillings@1354 2974 {
nickjillings@1354 2975 storePoint = storage.document.createElement('metricresult');
nickjillings@1354 2976 storePoint.setAttribute('name','volumeTracker');
nickjillings@2100 2977 testState.currentStore.XMLDOM.getElementsByTagName('metric')[0].appendChild(storePoint);
nickjillings@1354 2978 }
nickjillings@1354 2979 else {
nickjillings@1354 2980 storePoint = storePoint[0];
nickjillings@1354 2981 }
nickjillings@1354 2982 var node = storage.document.createElement('movement');
nickjillings@1354 2983 node.setAttribute('test-time',audioEngineContext.timer.getTestTime());
nickjillings@1354 2984 node.setAttribute('volume',interfaceContext.volume.valueDB);
nickjillings@1354 2985 node.setAttribute('format','dBFS');
nickjillings@1354 2986 storePoint.appendChild(node);
nickjillings@1354 2987 }
nickjillings@1354 2988
nickjillings@1355 2989 var title = document.createElement('div');
nickjillings@1355 2990 title.innerHTML = '<span>Master Volume Control</span>';
nickjillings@1355 2991 title.style.fontSize = '0.75em';
nickjillings@1355 2992 title.style.width = "100%";
nickjillings@1355 2993 title.align = 'center';
nickjillings@1355 2994 this.object.appendChild(title);
nickjillings@1355 2995
nickjillings@1354 2996 this.object.appendChild(this.slider);
nickjillings@1354 2997 this.object.appendChild(this.valueText);
nickjillings@1354 2998 }
nickjillings@2049 2999 // Global Checkers
nickjillings@2049 3000 // These functions will help enforce the checkers
nickjillings@2049 3001 this.checkHiddenAnchor = function()
nickjillings@2049 3002 {
nickjillings@1324 3003 for (var ao of audioEngineContext.audioObjects)
nickjillings@2049 3004 {
nickjillings@1324 3005 if (ao.specification.type == "anchor")
nickjillings@2049 3006 {
nickjillings@1325 3007 if (ao.interfaceDOM.getValue() > (ao.specification.marker/100) && ao.specification.marker > 0) {
nickjillings@1324 3008 // Anchor is not set below
nickjillings@1324 3009 console.log('Anchor node not below marker value');
nickjillings@1324 3010 alert('Please keep listening');
nickjillings@1367 3011 this.storeErrorNode('Anchor node not below marker value');
nickjillings@1324 3012 return false;
nickjillings@1324 3013 }
nickjillings@2049 3014 }
nickjillings@2049 3015 }
nickjillings@2049 3016 return true;
nickjillings@2049 3017 };
nickjillings@2049 3018
nickjillings@2049 3019 this.checkHiddenReference = function()
nickjillings@2049 3020 {
nickjillings@1324 3021 for (var ao of audioEngineContext.audioObjects)
nickjillings@2049 3022 {
nickjillings@1324 3023 if (ao.specification.type == "reference")
nickjillings@2049 3024 {
nickjillings@1325 3025 if (ao.interfaceDOM.getValue() < (ao.specification.marker/100) && ao.specification.marker > 0) {
nickjillings@1324 3026 // Anchor is not set below
nickjillings@1367 3027 console.log('Reference node not above marker value');
nickjillings@1367 3028 this.storeErrorNode('Reference node not above marker value');
nickjillings@1324 3029 alert('Please keep listening');
nickjillings@1324 3030 return false;
nickjillings@1324 3031 }
nickjillings@2049 3032 }
nickjillings@2049 3033 }
nickjillings@2049 3034 return true;
nickjillings@2049 3035 };
nickjillings@1474 3036
nickjillings@1474 3037 this.checkFragmentsFullyPlayed = function ()
nickjillings@1474 3038 {
nickjillings@1474 3039 // Checks the entire file has been played back
nickjillings@1474 3040 // NOTE ! This will return true IF playback is Looped!!!
nickjillings@1474 3041 if (audioEngineContext.loopPlayback)
nickjillings@1474 3042 {
nickjillings@1474 3043 console.log("WARNING - Looped source: Cannot check fragments are fully played");
nickjillings@1474 3044 return true;
nickjillings@1474 3045 }
nickjillings@1474 3046 var check_pass = true;
nickjillings@1474 3047 var error_obj = [];
nickjillings@1474 3048 for (var i = 0; i<audioEngineContext.audioObjects.length; i++)
nickjillings@1474 3049 {
nickjillings@1474 3050 var object = audioEngineContext.audioObjects[i];
nickjillings@1393 3051 var time = object.buffer.buffer.duration;
nickjillings@1474 3052 var metric = object.metric;
nickjillings@1474 3053 var passed = false;
nickjillings@1474 3054 for (var j=0; j<metric.listenTracker.length; j++)
nickjillings@1474 3055 {
nickjillings@1474 3056 var bt = metric.listenTracker[j].getElementsByTagName('buffertime');
nickjillings@1474 3057 var start_time = Number(bt[0].getAttribute('start'));
nickjillings@1474 3058 var stop_time = Number(bt[0].getAttribute('stop'));
nickjillings@1474 3059 var delta = stop_time - start_time;
nickjillings@1474 3060 if (delta >= time)
nickjillings@1474 3061 {
nickjillings@1474 3062 passed = true;
nickjillings@1474 3063 break;
nickjillings@1474 3064 }
nickjillings@1474 3065 }
nickjillings@1474 3066 if (passed == false)
nickjillings@1474 3067 {
nickjillings@1474 3068 check_pass = false;
nickjillings@1290 3069 console.log("Continue listening to track-"+object.interfaceDOM.getPresentedId());
nickjillings@1290 3070 error_obj.push(object.interfaceDOM.getPresentedId());
nickjillings@1474 3071 }
nickjillings@1474 3072 }
nickjillings@1474 3073 if (check_pass == false)
nickjillings@1474 3074 {
nickjillings@1393 3075 var str_start = "You have not completely listened to fragments ";
nickjillings@1474 3076 for (var i=0; i<error_obj.length; i++)
nickjillings@1474 3077 {
nickjillings@1474 3078 str_start += error_obj[i];
nickjillings@1474 3079 if (i != error_obj.length-1)
nickjillings@1474 3080 {
nickjillings@1474 3081 str_start += ', ';
nickjillings@1474 3082 }
nickjillings@1474 3083 }
nickjillings@1474 3084 str_start += ". Please keep listening";
nickjillings@1474 3085 console.log("[ALERT]: "+str_start);
nickjillings@1367 3086 this.storeErrorNode("[ALERT]: "+str_start);
nickjillings@1474 3087 alert(str_start);
nickjillings@1474 3088 }
nickjillings@1474 3089 };
nickjillings@1399 3090 this.checkAllMoved = function()
nickjillings@1399 3091 {
nickjillings@1399 3092 var str = "You have not moved ";
nickjillings@1399 3093 var failed = [];
nickjillings@1340 3094 for (var ao of audioEngineContext.audioObjects)
nickjillings@1399 3095 {
nickjillings@1340 3096 if(ao.metric.wasMoved == false && ao.interfaceDOM.canMove() == true)
nickjillings@1399 3097 {
nickjillings@1340 3098 failed.push(ao.interfaceDOM.getPresentedId());
nickjillings@1399 3099 }
nickjillings@1399 3100 }
nickjillings@1399 3101 if (failed.length == 0)
nickjillings@1399 3102 {
nickjillings@1399 3103 return true;
nickjillings@1399 3104 } else if (failed.length == 1)
nickjillings@1399 3105 {
nickjillings@1399 3106 str += 'track '+failed[0];
nickjillings@1399 3107 } else {
nickjillings@1399 3108 str += 'tracks ';
nickjillings@1399 3109 for (var i=0; i<failed.length-1; i++)
nickjillings@1399 3110 {
nickjillings@1399 3111 str += failed[i]+', ';
nickjillings@1399 3112 }
nickjillings@1399 3113 str += 'and '+failed[i];
nickjillings@1399 3114 }
nickjillings@1399 3115 str +='.';
nickjillings@1399 3116 alert(str);
nickjillings@1399 3117 console.log(str);
nickjillings@1367 3118 this.storeErrorNode(str);
nickjillings@1399 3119 return false;
nickjillings@1399 3120 };
nickjillings@1399 3121 this.checkAllPlayed = function()
nickjillings@1399 3122 {
nickjillings@1399 3123 var str = "You have not played ";
nickjillings@1399 3124 var failed = [];
nickjillings@1340 3125 for (var ao of audioEngineContext.audioObjects)
nickjillings@1399 3126 {
nickjillings@1340 3127 if(ao.metric.wasListenedTo == false)
nickjillings@1399 3128 {
nickjillings@1340 3129 failed.push(ao.interfaceDOM.getPresentedId());
nickjillings@1399 3130 }
nickjillings@1399 3131 }
nickjillings@1399 3132 if (failed.length == 0)
nickjillings@1399 3133 {
nickjillings@1399 3134 return true;
nickjillings@1399 3135 } else if (failed.length == 1)
nickjillings@1399 3136 {
nickjillings@1399 3137 str += 'track '+failed[0];
nickjillings@1399 3138 } else {
nickjillings@1399 3139 str += 'tracks ';
nickjillings@1399 3140 for (var i=0; i<failed.length-1; i++)
nickjillings@1399 3141 {
nickjillings@1399 3142 str += failed[i]+', ';
nickjillings@1399 3143 }
nickjillings@1399 3144 str += 'and '+failed[i];
nickjillings@1399 3145 }
nickjillings@1399 3146 str +='.';
nickjillings@1399 3147 alert(str);
nickjillings@1399 3148 console.log(str);
nickjillings@1367 3149 this.storeErrorNode(str);
nickjillings@1399 3150 return false;
nickjillings@1399 3151 };
nickjillings@1367 3152
nickjillings@1367 3153 this.storeErrorNode = function(errorMessage)
nickjillings@1367 3154 {
nickjillings@1367 3155 var time = audioEngineContext.timer.getTestTime();
nickjillings@1367 3156 var node = storage.document.createElement('error');
nickjillings@1367 3157 node.setAttribute('time',time);
nickjillings@1367 3158 node.textContent = errorMessage;
nickjillings@1367 3159 testState.currentStore.XMLDOM.appendChild(node);
nickjillings@1367 3160 };
nickjillings@1324 3161 }
nickjillings@1324 3162
nickjillings@1324 3163 function Storage()
nickjillings@1324 3164 {
nickjillings@1324 3165 // Holds results in XML format until ready for collection
nickjillings@1324 3166 this.globalPreTest = null;
nickjillings@1324 3167 this.globalPostTest = null;
nickjillings@1324 3168 this.testPages = [];
nickjillings@1294 3169 this.document = null;
nickjillings@1294 3170 this.root = null;
nickjillings@1324 3171 this.state = 0;
nickjillings@1324 3172
nickjillings@1294 3173 this.initialise = function(existingStore)
nickjillings@1324 3174 {
nickjillings@1294 3175 if (existingStore == undefined) {
nickjillings@2147 3176 // We need to get the sessionKey
nickjillings@2147 3177 this.SessionKey.generateKey();
nickjillings@1294 3178 this.document = document.implementation.createDocument(null,"waetresult");
nickjillings@1294 3179 this.root = this.document.childNodes[0];
nickjillings@2150 3180 var projectDocument = specification.projectXML;
nickjillings@2150 3181 projectDocument.setAttribute('file-name',url);
nickjillings@2150 3182 this.root.appendChild(projectDocument);
nickjillings@2150 3183 this.root.appendChild(returnDateNode());
nickjillings@2150 3184 this.root.appendChild(interfaceContext.returnNavigator());
nickjillings@2147 3185 } else {
nickjillings@1294 3186 this.document = existingStore;
nickjillings@1294 3187 this.root = existingStore.children[0];
nickjillings@1294 3188 this.SessionKey.key = this.root.getAttribute("key");
nickjillings@2147 3189 }
nickjillings@2150 3190 if (specification.preTest != undefined){this.globalPreTest = new this.surveyNode(this,this.root,specification.preTest);}
nickjillings@1294 3191 if (specification.postTest != undefined){this.globalPostTest = new this.surveyNode(this,this.root,specification.postTest);}
nickjillings@1324 3192 };
nickjillings@2147 3193
nickjillings@2147 3194 this.SessionKey = {
nickjillings@2147 3195 key: null,
nickjillings@2147 3196 request: new XMLHttpRequest(),
nickjillings@2147 3197 parent: this,
nickjillings@2147 3198 handleEvent: function() {
nickjillings@2147 3199 var parse = new DOMParser();
nickjillings@2147 3200 var xml = parse.parseFromString(this.request.response,"text/xml");
nickjillings@2147 3201 if (xml.getAllElementsByTagName("state")[0].textContent == "OK") {
nickjillings@2147 3202 this.key = xml.getAllElementsByTagName("key")[0].textContent;
nickjillings@2150 3203 this.parent.root.setAttribute("key",this.key);
nickjillings@2150 3204 this.parent.root.setAttribute("state","empty");
nickjillings@2147 3205 } else {
nickjillings@2147 3206 this.generateKey();
nickjillings@2147 3207 }
nickjillings@2147 3208 },
nickjillings@2147 3209 generateKey: function() {
nickjillings@2147 3210 var temp_key = randomString(32);
nickjillings@2147 3211 this.request.open("GET","keygen.php?key="+temp_key,true);
nickjillings@2147 3212 this.request.addEventListener("load",this);
nickjillings@2147 3213 this.request.send();
nickjillings@2149 3214 },
nickjillings@2150 3215 update: function() {
nickjillings@2150 3216 this.parent.root.setAttribute("state","update");
nickjillings@2150 3217 var xmlhttp = new XMLHttpRequest();
nickjillings@2150 3218 xmlhttp.open("POST",specification.projectReturn+"?key="+this.key);
nickjillings@2150 3219 xmlhttp.setRequestHeader('Content-Type', 'text/xml');
nickjillings@2150 3220 xmlhttp.onerror = function(){
nickjillings@2150 3221 console.log('Error updating file to server!');
nickjillings@2150 3222 };
nickjillings@2150 3223 var hold = document.createElement("div");
nickjillings@2150 3224 var clone = this.parent.root.cloneNode(true);
nickjillings@2150 3225 hold.appendChild(clone);
nickjillings@2150 3226 xmlhttp.onload = function() {
nickjillings@2150 3227 if (this.status >= 300) {
nickjillings@2150 3228 console.log("WARNING - Could not update at this time");
nickjillings@2150 3229 } else {
nickjillings@2150 3230 var parser = new DOMParser();
nickjillings@2150 3231 var xmlDoc = parser.parseFromString(xmlhttp.responseText, "application/xml");
nickjillings@2150 3232 var response = xmlDoc.getElementsByTagName('response')[0];
nickjillings@2150 3233 if (response.getAttribute("state") == "OK") {
nickjillings@2150 3234 var file = response.getElementsByTagName("file")[0];
nickjillings@2150 3235 console.log("Intermediate save: OK, written "+file.getAttribute("bytes")+"B");
nickjillings@2150 3236 } else {
nickjillings@2150 3237 var message = response.getElementsByTagName("message");
nickjillings@2150 3238 console.log("Intermediate save: Error! "+message.textContent);
nickjillings@2150 3239 }
nickjillings@2149 3240 }
nickjillings@2149 3241 }
nickjillings@2150 3242 xmlhttp.send([hold.innerHTML]);
nickjillings@2147 3243 }
nickjillings@2147 3244 }
nickjillings@1324 3245
nickjillings@1324 3246 this.createTestPageStore = function(specification)
nickjillings@1324 3247 {
nickjillings@1324 3248 var store = new this.pageNode(this,specification);
nickjillings@1324 3249 this.testPages.push(store);
nickjillings@1324 3250 return this.testPages[this.testPages.length-1];
nickjillings@1324 3251 };
nickjillings@1324 3252
nickjillings@1324 3253 this.surveyNode = function(parent,root,specification)
nickjillings@1324 3254 {
nickjillings@1324 3255 this.specification = specification;
nickjillings@1324 3256 this.parent = parent;
nickjillings@1294 3257 this.state = "empty";
nickjillings@1324 3258 this.XMLDOM = this.parent.document.createElement('survey');
nickjillings@1324 3259 this.XMLDOM.setAttribute('location',this.specification.location);
nickjillings@1294 3260 this.XMLDOM.setAttribute("state",this.state);
nickjillings@1324 3261 for (var optNode of this.specification.options)
nickjillings@1324 3262 {
nickjillings@1324 3263 if (optNode.type != 'statement')
nickjillings@1324 3264 {
nickjillings@1324 3265 var node = this.parent.document.createElement('surveyresult');
nickjillings@1294 3266 node.setAttribute("ref",optNode.id);
nickjillings@1324 3267 node.setAttribute('type',optNode.type);
nickjillings@1324 3268 this.XMLDOM.appendChild(node);
nickjillings@1324 3269 }
nickjillings@1324 3270 }
nickjillings@1324 3271 root.appendChild(this.XMLDOM);
nickjillings@1324 3272
nickjillings@1324 3273 this.postResult = function(node)
nickjillings@1324 3274 {
nickjillings@1324 3275 // From popup: node is the popupOption node containing both spec. and results
nickjillings@1324 3276 // ID is the position
nickjillings@1324 3277 if (node.specification.type == 'statement'){return;}
nickjillings@1294 3278 var surveyresult = this.XMLDOM.children[0];
nickjillings@1294 3279 while(surveyresult != null) {
nickjillings@1294 3280 if (surveyresult.getAttribute("ref") == node.specification.id)
nickjillings@1294 3281 {
nickjillings@1294 3282 break;
nickjillings@1294 3283 }
nickjillings@1294 3284 surveyresult = surveyresult.nextElementSibling;
nickjillings@1294 3285 }
nickjillings@1324 3286 switch(node.specification.type)
nickjillings@1324 3287 {
nickjillings@1324 3288 case "number":
nickjillings@1324 3289 case "question":
nickjillings@1324 3290 var child = this.parent.document.createElement('response');
nickjillings@1324 3291 child.textContent = node.response;
nickjillings@1324 3292 surveyresult.appendChild(child);
nickjillings@1324 3293 break;
nickjillings@1324 3294 case "radio":
nickjillings@1324 3295 var child = this.parent.document.createElement('response');
nickjillings@1324 3296 child.setAttribute('name',node.response.name);
nickjillings@1324 3297 child.textContent = node.response.text;
nickjillings@1324 3298 surveyresult.appendChild(child);
nickjillings@1324 3299 break;
nickjillings@1324 3300 case "checkbox":
nickjillings@1324 3301 for (var i=0; i<node.response.length; i++)
nickjillings@1324 3302 {
nickjillings@1324 3303 var checkNode = this.parent.document.createElement('response');
nickjillings@1347 3304 checkNode.setAttribute('name',node.response[i].name);
nickjillings@1347 3305 checkNode.setAttribute('checked',node.response[i].checked);
nickjillings@1326 3306 surveyresult.appendChild(checkNode);
nickjillings@1324 3307 }
nickjillings@1324 3308 break;
nickjillings@1324 3309 }
nickjillings@1324 3310 };
nickjillings@1294 3311 this.complete = function() {
nickjillings@1294 3312 this.state = "complete";
nickjillings@1294 3313 this.XMLDOM.setAttribute("state",this.state);
nickjillings@1294 3314 }
nickjillings@1324 3315 };
nickjillings@1324 3316
nickjillings@1324 3317 this.pageNode = function(parent,specification)
nickjillings@1324 3318 {
nickjillings@1324 3319 // Create one store per test page
nickjillings@1324 3320 this.specification = specification;
nickjillings@1324 3321 this.parent = parent;
nickjillings@1294 3322 this.state = "empty";
nickjillings@1324 3323 this.XMLDOM = this.parent.document.createElement('page');
nickjillings@1294 3324 this.XMLDOM.setAttribute('ref',specification.id);
nickjillings@1324 3325 this.XMLDOM.setAttribute('presentedId',specification.presentedId);
nickjillings@1294 3326 this.XMLDOM.setAttribute("state",this.state);
nickjillings@1345 3327 if (specification.preTest != undefined){this.preTest = new this.parent.surveyNode(this.parent,this.XMLDOM,this.specification.preTest);}
nickjillings@1345 3328 if (specification.postTest != undefined){this.postTest = new this.parent.surveyNode(this.parent,this.XMLDOM,this.specification.postTest);}
nickjillings@1324 3329
nickjillings@1324 3330 // Add any page metrics
nickjillings@1324 3331 var page_metric = this.parent.document.createElement('metric');
nickjillings@1324 3332 this.XMLDOM.appendChild(page_metric);
nickjillings@1324 3333
nickjillings@1324 3334 // Add the audioelement
nickjillings@1324 3335 for (var element of this.specification.audioElements)
nickjillings@1324 3336 {
nickjillings@1324 3337 var aeNode = this.parent.document.createElement('audioelement');
nickjillings@1294 3338 aeNode.setAttribute('ref',element.id);
nickjillings@1294 3339 if (element.name != undefined){aeNode.setAttribute('name',element.name)};
nickjillings@1324 3340 aeNode.setAttribute('type',element.type);
nickjillings@1324 3341 aeNode.setAttribute('url', element.url);
nickjillings@1324 3342 aeNode.setAttribute('gain', element.gain);
nickjillings@1324 3343 if (element.type == 'anchor' || element.type == 'reference')
nickjillings@1324 3344 {
nickjillings@1324 3345 if (element.marker > 0)
nickjillings@1324 3346 {
nickjillings@1324 3347 aeNode.setAttribute('marker',element.marker);
nickjillings@1324 3348 }
nickjillings@1324 3349 }
nickjillings@1324 3350 var ae_metric = this.parent.document.createElement('metric');
nickjillings@1324 3351 aeNode.appendChild(ae_metric);
nickjillings@1324 3352 this.XMLDOM.appendChild(aeNode);
nickjillings@1324 3353 }
nickjillings@1324 3354
nickjillings@1324 3355 this.parent.root.appendChild(this.XMLDOM);
nickjillings@1294 3356
nickjillings@1294 3357 this.complete = function() {
nickjillings@1294 3358 this.state = "complete";
nickjillings@1294 3359 this.XMLDOM.setAttribute("state","complete");
nickjillings@1294 3360 }
nickjillings@1324 3361 };
nickjillings@2150 3362 this.update = function() {
nickjillings@2150 3363 this.SessionKey.update();
nickjillings@2150 3364 }
nickjillings@1324 3365 this.finish = function()
nickjillings@1324 3366 {
nickjillings@1324 3367 if (this.state == 0)
nickjillings@1324 3368 {
nickjillings@2150 3369 this.update();
nickjillings@1324 3370 }
nickjillings@1324 3371 this.state = 1;
nickjillings@1324 3372 return this.root;
nickjillings@1324 3373 };
nickjillings@1324 3374 }