annotate core.js @ 2127:ff9269342c1e

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