annotate core.js @ 1293:c753706731f2

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