annotate core.js @ 2145:bcc677a7c759

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