annotate core.js @ 1316:279930a008ca

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