annotate core.js @ 2110:44eef6a6ca7e

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