annotate core.js @ 1239:a54422902bbd

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