annotate core.js @ 2185:e9212d745302

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