annotate core.js @ 2162:894d9bfd4177

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