annotate core.js @ 2191:dd5a8556235c

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