annotate core.js @ 2152:c02853f9f4bd

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