annotate core.js @ 1291:6b2ae5d87a64

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