annotate core.js @ 2147:0eeb17318f7e

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