annotate core.js @ 1290:4594f5ebd758

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