annotate core.js @ 2149:90212e9e9295

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