annotate core.js @ 2155:9abb207d2f6a

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