Mercurial > hg > webaudioevaluationtool
comparison core.js @ 1324:95b40955f79a
--UNSTABLE-- Major revision. Updated Specification including verification. Added storage collector for XML results. Popup more stable.
author | Nicholas Jillings <nickjillings@users.noreply.github.com> |
---|---|
date | Wed, 06 Jan 2016 10:36:37 +0000 |
parents | dd7f5b4d3b4e |
children | 3f65e594154d |
comparison
equal
deleted
inserted
replaced
1323:de870e126edd | 1324:95b40955f79a |
---|---|
6 */ | 6 */ |
7 | 7 |
8 /* create the web audio API context and store in audioContext*/ | 8 /* create the web audio API context and store in audioContext*/ |
9 var audioContext; // Hold the browser web audio API | 9 var audioContext; // Hold the browser web audio API |
10 var projectXML; // Hold the parsed setup XML | 10 var projectXML; // Hold the parsed setup XML |
11 var schemaXSD; // Hold the parsed schema XSD | |
11 var specification; | 12 var specification; |
12 var interfaceContext; | 13 var interfaceContext; |
14 var storage; | |
13 var popup; // Hold the interfacePopup object | 15 var popup; // Hold the interfacePopup object |
14 var testState; | 16 var testState; |
15 var currentTrackOrder = []; // Hold the current XML tracks in their (randomised) order | 17 var currentTrackOrder = []; // Hold the current XML tracks in their (randomised) order |
16 var audioEngineContext; // The custome AudioEngine object | 18 var audioEngineContext; // The custome AudioEngine object |
17 var projectReturn; // Hold the URL for the return | 19 var projectReturn; // Hold the URL for the return |
42 // Create the specification object | 44 // Create the specification object |
43 specification = new Specification(); | 45 specification = new Specification(); |
44 | 46 |
45 // Create the interface object | 47 // Create the interface object |
46 interfaceContext = new Interface(specification); | 48 interfaceContext = new Interface(specification); |
49 | |
50 // Create the storage object | |
51 storage = new Storage(); | |
47 // Define window callbacks for interface | 52 // Define window callbacks for interface |
48 window.onresize = function(event){interfaceContext.resizeWindow(event);}; | 53 window.onresize = function(event){interfaceContext.resizeWindow(event);}; |
49 }; | 54 }; |
50 | 55 |
51 function loadProjectSpec(url) { | 56 function loadProjectSpec(url) { |
52 // Load the project document from the given URL, decode the XML and instruct audioEngine to get audio data | 57 // Load the project document from the given URL, decode the XML and instruct audioEngine to get audio data |
53 // If url is null, request client to upload project XML document | 58 // If url is null, request client to upload project XML document |
54 var r = new XMLHttpRequest(); | 59 var xmlhttp = new XMLHttpRequest(); |
55 r.open('GET',url,true); | 60 xmlhttp.open("GET",'test-schema.xsd',true); |
56 r.onload = function() { | 61 xmlhttp.onload = function() |
57 loadProjectSpecCallback(r.response); | 62 { |
58 }; | 63 schemaXSD = xmlhttp.response; |
59 r.send(); | 64 var parse = new DOMParser(); |
65 specification.schema = parse.parseFromString(xmlhttp.response,'text/xml'); | |
66 var r = new XMLHttpRequest(); | |
67 r.open('GET',url,true); | |
68 r.onload = function() { | |
69 loadProjectSpecCallback(r.response); | |
70 }; | |
71 r.send(); | |
72 }; | |
73 xmlhttp.send(); | |
60 }; | 74 }; |
61 | 75 |
62 function loadProjectSpecCallback(response) { | 76 function loadProjectSpecCallback(response) { |
63 // Function called after asynchronous download of XML project specification | 77 // Function called after asynchronous download of XML project specification |
64 //var decode = $.parseXML(response); | 78 //var decode = $.parseXML(response); |
65 //projectXML = $(decode); | 79 //projectXML = $(decode); |
80 | |
81 // First perform XML schema validation | |
82 var Module = { | |
83 xml: response, | |
84 schema: schemaXSD, | |
85 arguments:["--noout", "--schema", 'test-schema.xsd','document.xml'] | |
86 }; | |
87 | |
88 var xmllint = validateXML(Module); | |
89 console.log(xmllint); | |
90 if(xmllint != 'document.xml validates\n') | |
91 { | |
92 document.getElementsByTagName('body')[0].innerHTML = null; | |
93 var msg = document.createElement("h3"); | |
94 msg.textContent = "FATAL ERROR"; | |
95 var span = document.createElement("h4"); | |
96 span.textContent = "The XML validator returned the following errors when decoding your XML file"; | |
97 document.getElementsByTagName('body')[0].appendChild(msg); | |
98 document.getElementsByTagName('body')[0].appendChild(span); | |
99 xmllint = xmllint.split('\n'); | |
100 for (var i in xmllint) | |
101 { | |
102 document.getElementsByTagName('body')[0].appendChild(document.createElement('br')); | |
103 var span = document.createElement("span"); | |
104 span.textContent = xmllint[i]; | |
105 document.getElementsByTagName('body')[0].appendChild(span); | |
106 } | |
107 return; | |
108 } | |
66 | 109 |
67 var parse = new DOMParser(); | 110 var parse = new DOMParser(); |
68 projectXML = parse.parseFromString(response,'text/xml'); | 111 projectXML = parse.parseFromString(response,'text/xml'); |
69 var errorNode = projectXML.getElementsByTagName('parsererror'); | 112 var errorNode = projectXML.getElementsByTagName('parsererror'); |
70 if (errorNode.length >= 1) | 113 if (errorNode.length >= 1) |
80 return; | 123 return; |
81 } | 124 } |
82 | 125 |
83 // Build the specification | 126 // Build the specification |
84 specification.decode(projectXML); | 127 specification.decode(projectXML); |
128 storage.initialise(); | |
85 | 129 |
86 // Detect the interface to use and load the relevant javascripts. | 130 // Detect the interface to use and load the relevant javascripts. |
87 var interfaceJS = document.createElement('script'); | 131 var interfaceJS = document.createElement('script'); |
88 interfaceJS.setAttribute("type","text/javascript"); | 132 interfaceJS.setAttribute("type","text/javascript"); |
89 if (specification.interfaceType == 'APE') { | 133 if (specification.interface == 'APE') { |
90 interfaceJS.setAttribute("src","ape.js"); | 134 interfaceJS.setAttribute("src","ape.js"); |
91 | 135 |
92 // APE comes with a css file | 136 // APE comes with a css file |
93 var css = document.createElement('link'); | 137 var css = document.createElement('link'); |
94 css.rel = 'stylesheet'; | 138 css.rel = 'stylesheet'; |
95 css.type = 'text/css'; | 139 css.type = 'text/css'; |
96 css.href = 'ape.css'; | 140 css.href = 'ape.css'; |
97 | 141 |
98 document.getElementsByTagName("head")[0].appendChild(css); | 142 document.getElementsByTagName("head")[0].appendChild(css); |
99 } else if (specification.interfaceType == "MUSHRA") | 143 } else if (specification.interface == "MUSHRA") |
100 { | 144 { |
101 interfaceJS.setAttribute("src","mushra.js"); | 145 interfaceJS.setAttribute("src","mushra.js"); |
102 | 146 |
103 // MUSHRA comes with a css file | 147 // MUSHRA comes with a css file |
104 var css = document.createElement('link'); | 148 var css = document.createElement('link'); |
111 document.getElementsByTagName("head")[0].appendChild(interfaceJS); | 155 document.getElementsByTagName("head")[0].appendChild(interfaceJS); |
112 | 156 |
113 // Create the audio engine object | 157 // Create the audio engine object |
114 audioEngineContext = new AudioEngine(specification); | 158 audioEngineContext = new AudioEngine(specification); |
115 | 159 |
116 testState.stateMap.push(specification.preTest); | 160 $(specification.pages).each(function(index,elem){ |
117 | |
118 $(specification.audioHolders).each(function(index,elem){ | |
119 testState.stateMap.push(elem); | |
120 $(elem.audioElements).each(function(i,audioElem){ | 161 $(elem.audioElements).each(function(i,audioElem){ |
121 var URL = audioElem.parent.hostURL + audioElem.url; | 162 var URL = elem.hostURL + audioElem.url; |
122 var buffer = null; | 163 var buffer = null; |
123 for (var i=0; i<audioEngineContext.buffers.length; i++) | 164 for (var i=0; i<audioEngineContext.buffers.length; i++) |
124 { | 165 { |
125 if (URL == audioEngineContext.buffers[i].url) | 166 if (URL == audioEngineContext.buffers[i].url) |
126 { | 167 { |
134 buffer.getMedia(URL); | 175 buffer.getMedia(URL); |
135 audioEngineContext.buffers.push(buffer); | 176 audioEngineContext.buffers.push(buffer); |
136 } | 177 } |
137 }); | 178 }); |
138 }); | 179 }); |
139 | |
140 testState.stateMap.push(specification.postTest); | |
141 } | 180 } |
142 | 181 |
143 function createProjectSave(destURL) { | 182 function createProjectSave(destURL) { |
144 // Save the data from interface into XML and send to destURL | 183 // Save the data from interface into XML and send to destURL |
145 // If destURL is null then download XML in client | 184 // If destURL is null then download XML in client |
173 if (xmlhttp.status != 200 && xmlhttp.readyState == 4) { | 212 if (xmlhttp.status != 200 && xmlhttp.readyState == 4) { |
174 createProjectSave(null); | 213 createProjectSave(null); |
175 } else { | 214 } else { |
176 if (xmlhttp.responseXML == null) | 215 if (xmlhttp.responseXML == null) |
177 { | 216 { |
178 return createProjectSave(null); | 217 createProjectSave('null'); |
179 } | 218 } |
180 var response = xmlhttp.responseXML.childNodes[0]; | 219 var response = xmlhttp.responseXML.childNodes[0]; |
181 if (response.getAttribute('state') == "OK") | 220 if (response.getAttribute('state') == "OK") |
182 { | 221 { |
183 var file = response.getElementsByTagName('file')[0]; | 222 var file = response.getElementsByTagName('file')[0]; |
229 } | 268 } |
230 | 269 |
231 // Only other global function which must be defined in the interface class. Determines how to create the XML document. | 270 // Only other global function which must be defined in the interface class. Determines how to create the XML document. |
232 function interfaceXMLSave(){ | 271 function interfaceXMLSave(){ |
233 // Create the XML string to be exported with results | 272 // Create the XML string to be exported with results |
234 var xmlDoc = document.createElement("BrowserEvaluationResult"); | 273 return storage.finish(); |
235 var projectDocument = specification.projectXML; | |
236 projectDocument.setAttribute('file-name',url); | |
237 xmlDoc.appendChild(projectDocument); | |
238 xmlDoc.appendChild(returnDateNode()); | |
239 xmlDoc.appendChild(interfaceContext.returnNavigator()); | |
240 for (var i=0; i<testState.stateResults.length; i++) | |
241 { | |
242 xmlDoc.appendChild(testState.stateResults[i]); | |
243 } | |
244 | |
245 return xmlDoc; | |
246 } | 274 } |
247 | 275 |
248 function linearToDecibel(gain) | 276 function linearToDecibel(gain) |
249 { | 277 { |
250 return 20.0*Math.log10(gain); | 278 return 20.0*Math.log10(gain); |
263 this.popupResponse = null; | 291 this.popupResponse = null; |
264 this.buttonProceed = null; | 292 this.buttonProceed = null; |
265 this.buttonPrevious = null; | 293 this.buttonPrevious = null; |
266 this.popupOptions = null; | 294 this.popupOptions = null; |
267 this.currentIndex = null; | 295 this.currentIndex = null; |
268 this.responses = null; | 296 this.node = null; |
297 this.store = null; | |
269 $(window).keypress(function(e){ | 298 $(window).keypress(function(e){ |
270 if (e.keyCode == 13 && popup.popup.style.visibility == 'visible') | 299 if (e.keyCode == 13 && popup.popup.style.visibility == 'visible') |
271 { | 300 { |
272 console.log(e); | 301 console.log(e); |
273 popup.buttonProceed.onclick(); | 302 popup.buttonProceed.onclick(); |
364 | 393 |
365 this.postNode = function() { | 394 this.postNode = function() { |
366 // This will take the node from the popupOptions and display it | 395 // This will take the node from the popupOptions and display it |
367 var node = this.popupOptions[this.currentIndex]; | 396 var node = this.popupOptions[this.currentIndex]; |
368 this.popupResponse.innerHTML = null; | 397 this.popupResponse.innerHTML = null; |
369 if (node.type == 'statement') { | 398 this.popupTitle.textContent = node.specification.statement; |
370 this.popupTitle.textContent = null; | 399 if (node.specification.type == 'question') { |
371 var statement = document.createElement('span'); | |
372 statement.textContent = node.statement; | |
373 this.popupResponse.appendChild(statement); | |
374 } else if (node.type == 'question') { | |
375 this.popupTitle.textContent = node.question; | |
376 var textArea = document.createElement('textarea'); | 400 var textArea = document.createElement('textarea'); |
377 switch (node.boxsize) { | 401 switch (node.specification.boxsize) { |
378 case 'small': | 402 case 'small': |
379 textArea.cols = "20"; | 403 textArea.cols = "20"; |
380 textArea.rows = "1"; | 404 textArea.rows = "1"; |
381 break; | 405 break; |
382 case 'normal': | 406 case 'normal': |
392 textArea.rows = "10"; | 416 textArea.rows = "10"; |
393 break; | 417 break; |
394 } | 418 } |
395 this.popupResponse.appendChild(textArea); | 419 this.popupResponse.appendChild(textArea); |
396 textArea.focus(); | 420 textArea.focus(); |
397 } else if (node.type == 'checkbox') { | 421 } else if (node.specification.type == 'checkbox') { |
398 this.popupTitle.textContent = node.statement; | 422 for (var option of node.specification.options) { |
399 var optHold = this.popupResponse; | |
400 for (var i=0; i<node.options.length; i++) { | |
401 var option = node.options[i]; | |
402 var input = document.createElement('input'); | 423 var input = document.createElement('input'); |
403 input.id = option.name; | 424 input.id = option.name; |
404 input.type = 'checkbox'; | 425 input.type = 'checkbox'; |
405 var span = document.createElement('span'); | 426 var span = document.createElement('span'); |
406 span.textContent = option.text; | 427 span.textContent = option.text; |
407 var hold = document.createElement('div'); | 428 var hold = document.createElement('div'); |
408 hold.setAttribute('name','option'); | 429 hold.setAttribute('name','option'); |
409 hold.style.padding = '4px'; | 430 hold.style.padding = '4px'; |
410 hold.appendChild(input); | 431 hold.appendChild(input); |
411 hold.appendChild(span); | 432 hold.appendChild(span); |
412 optHold.appendChild(hold); | 433 this.popupResponse.appendChild(hold); |
413 } | 434 } |
414 } else if (node.type == 'radio') { | 435 } else if (node.specification.type == 'radio') { |
415 this.popupTitle.textContent = node.statement; | 436 for (var option of node.specification.options) { |
416 var optHold = this.popupResponse; | |
417 for (var i=0; i<node.options.length; i++) { | |
418 var option = node.options[i]; | |
419 var input = document.createElement('input'); | 437 var input = document.createElement('input'); |
420 input.id = option.name; | 438 input.id = option.name; |
421 input.type = 'radio'; | 439 input.type = 'radio'; |
422 input.name = node.id; | 440 input.name = node.specification.id; |
423 var span = document.createElement('span'); | 441 var span = document.createElement('span'); |
424 span.textContent = option.text; | 442 span.textContent = option.text; |
425 var hold = document.createElement('div'); | 443 var hold = document.createElement('div'); |
426 hold.setAttribute('name','option'); | 444 hold.setAttribute('name','option'); |
427 hold.style.padding = '4px'; | 445 hold.style.padding = '4px'; |
428 hold.appendChild(input); | 446 hold.appendChild(input); |
429 hold.appendChild(span); | 447 hold.appendChild(span); |
430 optHold.appendChild(hold); | 448 this.popupResponse.appendChild(hold); |
431 } | 449 } |
432 } else if (node.type == 'number') { | 450 } else if (node.specification.type == 'number') { |
433 this.popupTitle.textContent = node.statement; | |
434 var input = document.createElement('input'); | 451 var input = document.createElement('input'); |
435 input.type = 'textarea'; | 452 input.type = 'textarea'; |
436 if (node.min != null) {input.min = node.min;} | 453 if (node.min != null) {input.min = node.specification.min;} |
437 if (node.max != null) {input.max = node.max;} | 454 if (node.max != null) {input.max = node.specification.max;} |
438 if (node.step != null) {input.step = node.step;} | 455 if (node.step != null) {input.step = node.specification.step;} |
439 this.popupResponse.appendChild(input); | 456 this.popupResponse.appendChild(input); |
440 } | 457 } |
441 var content_height = Number(this.popup.offsetHeight.toFixed()); | 458 var content_height = Number(this.popup.offsetHeight.toFixed()); |
442 content_height -= Number(this.popupContent.offsetHeight.toFixed()); | 459 content_height -= Number(this.popupContent.offsetHeight.toFixed()); |
443 content_height -=Number(this.buttonProceed.offsetHeight.toFixed()); | 460 content_height -=Number(this.buttonProceed.offsetHeight.toFixed()); |
444 content_height = content_height + "px"; | 461 content_height = content_height + "px"; |
445 this.buttonProceed.style.top = content_height; | 462 this.buttonProceed.style.top = content_height; |
446 this.buttonPrevious.style.top = content_height; | 463 this.buttonPrevious.style.top = content_height; |
447 if(this.currentIndex+1 == this.popupOptions.length) { | 464 if(this.currentIndex+1 == this.popupOptions.length) { |
448 if (this.responses.nodeName == "PRETEST") { | 465 if (this.node.location == "pre") { |
449 this.buttonProceed.textContent = 'Start'; | 466 this.buttonProceed.textContent = 'Start'; |
450 } else { | 467 } else { |
451 this.buttonProceed.textContent = 'Submit'; | 468 this.buttonProceed.textContent = 'Submit'; |
452 } | 469 } |
453 } else { | 470 } else { |
457 this.buttonPrevious.style.visibility = 'visible'; | 474 this.buttonPrevious.style.visibility = 'visible'; |
458 else | 475 else |
459 this.buttonPrevious.style.visibility = 'hidden'; | 476 this.buttonPrevious.style.visibility = 'hidden'; |
460 }; | 477 }; |
461 | 478 |
462 this.initState = function(node) { | 479 this.initState = function(node,store) { |
463 //Call this with your preTest and postTest nodes when needed to | 480 //Call this with your preTest and postTest nodes when needed to |
464 // initialise the popup procedure. | 481 // initialise the popup procedure. |
465 this.popupOptions = node.options; | 482 if (node.options.length > 0) { |
466 if (this.popupOptions.length > 0) { | 483 this.popupOptions = []; |
467 if (node.type == 'pretest') { | 484 this.node = node; |
468 this.responses = document.createElement('PreTest'); | 485 this.store = store; |
469 } else if (node.type == 'posttest') { | 486 for (var opt of node.options) |
470 this.responses = document.createElement('PostTest'); | 487 { |
471 } else { | 488 this.popupOptions.push({ |
472 console.log ('WARNING - popup node neither pre or post!'); | 489 specification: opt, |
473 this.responses = document.createElement('responses'); | 490 response: null |
474 } | 491 }); |
492 } | |
475 this.currentIndex = 0; | 493 this.currentIndex = 0; |
476 this.showPopup(); | 494 this.showPopup(); |
477 this.postNode(); | 495 this.postNode(); |
478 } else { | 496 } else { |
479 advanceState(); | 497 advanceState(); |
481 }; | 499 }; |
482 | 500 |
483 this.proceedClicked = function() { | 501 this.proceedClicked = function() { |
484 // Each time the popup button is clicked! | 502 // Each time the popup button is clicked! |
485 var node = this.popupOptions[this.currentIndex]; | 503 var node = this.popupOptions[this.currentIndex]; |
486 if (node.type == 'question') { | 504 if (node.specification.type == 'question') { |
487 // Must extract the question data | 505 // Must extract the question data |
488 var textArea = $(popup.popupContent).find('textarea')[0]; | 506 var textArea = $(popup.popupContent).find('textarea')[0]; |
489 if (node.mandatory == true && textArea.value.length == 0) { | 507 if (node.specification.mandatory == true && textArea.value.length == 0) { |
490 alert('This question is mandatory'); | 508 alert('This question is mandatory'); |
491 return; | 509 return; |
492 } else { | 510 } else { |
493 // Save the text content | 511 // Save the text content |
494 var hold = document.createElement('comment'); | 512 console.log("Question: "+ node.specification.statement); |
495 hold.id = node.id; | |
496 hold.innerHTML = textArea.value; | |
497 console.log("Question: "+ node.question); | |
498 console.log("Question Response: "+ textArea.value); | 513 console.log("Question Response: "+ textArea.value); |
499 this.responses.appendChild(hold); | 514 node.response = textArea.value; |
500 } | 515 } |
501 } else if (node.type == 'checkbox') { | 516 } else if (node.specification.type == 'checkbox') { |
502 // Must extract checkbox data | 517 // Must extract checkbox data |
518 console.log("Checkbox: "+ node.statement); | |
519 var inputs = this.popupResponse.getElementsByTagName('input'); | |
520 node.response = []; | |
521 for (var i=0; i<node.specification.options.length; i++) { | |
522 node.response.push({ | |
523 name: node.specification.options[i].name, | |
524 text: node.specification.options[i].text, | |
525 checked: inputs[i].checked | |
526 }); | |
527 } | |
528 } else if (node.specification.type == "radio") { | |
503 var optHold = this.popupResponse; | 529 var optHold = this.popupResponse; |
504 var hold = document.createElement('checkbox'); | 530 console.log("Radio: "+ node.specification.statement); |
505 console.log("Checkbox: "+ node.statement); | 531 node.response = null; |
506 hold.id = node.id; | |
507 for (var i=0; i<optHold.childElementCount; i++) { | |
508 var input = optHold.childNodes[i].getElementsByTagName('input')[0]; | |
509 var statement = optHold.childNodes[i].getElementsByTagName('span')[0]; | |
510 var response = document.createElement('option'); | |
511 response.setAttribute('name',input.id); | |
512 response.textContent = input.checked; | |
513 hold.appendChild(response); | |
514 console.log(input.id +': '+ input.checked); | |
515 } | |
516 this.responses.appendChild(hold); | |
517 } else if (node.type == "radio") { | |
518 var optHold = this.popupResponse; | |
519 var hold = document.createElement('radio'); | |
520 console.log("Checkbox: "+ node.statement); | |
521 var responseID = null; | |
522 var i=0; | 532 var i=0; |
523 while(responseID == null) { | 533 var inputs = optHold.getElementsByTagName('input'); |
524 var input = optHold.childNodes[i].getElementsByTagName('input')[0]; | 534 while(node.response == null) { |
525 if (input.checked == true) { | 535 if (i == inputs.length) |
526 responseID = i; | 536 { |
527 console.log("Selected: "+ node.options[i].name); | 537 if (node.specification.mandatory == true) |
538 { | |
539 alert("This radio is mandatory"); | |
540 } else { | |
541 node.response = -1; | |
542 } | |
543 return; | |
544 } | |
545 if (inputs[i].checked == true) { | |
546 node.response = node.specification.options[i]; | |
547 console.log("Selected: "+ node.specification.options[i].name); | |
528 } | 548 } |
529 i++; | 549 i++; |
530 } | 550 } |
531 hold.id = node.id; | 551 } else if (node.specification.type == "number") { |
532 hold.setAttribute('name',node.options[responseID].name); | |
533 hold.textContent = node.options[responseID].text; | |
534 this.responses.appendChild(hold); | |
535 } else if (node.type == "number") { | |
536 var input = this.popupContent.getElementsByTagName('input')[0]; | 552 var input = this.popupContent.getElementsByTagName('input')[0]; |
537 if (node.mandatory == true && input.value.length == 0) { | 553 if (node.mandatory == true && input.value.length == 0) { |
538 alert('This question is mandatory. Please enter a number'); | 554 alert('This question is mandatory. Please enter a number'); |
539 return; | 555 return; |
540 } | 556 } |
549 } | 565 } |
550 if (enteredNumber > node.max && node.max != null) { | 566 if (enteredNumber > node.max && node.max != null) { |
551 alert('Number is above the maximum value of '+node.max); | 567 alert('Number is above the maximum value of '+node.max); |
552 return; | 568 return; |
553 } | 569 } |
554 var hold = document.createElement('number'); | 570 node.response = input.value; |
555 hold.id = node.id; | |
556 hold.textContent = input.value; | |
557 this.responses.appendChild(hold); | |
558 } | 571 } |
559 this.currentIndex++; | 572 this.currentIndex++; |
560 if (this.currentIndex < this.popupOptions.length) { | 573 if (this.currentIndex < this.popupOptions.length) { |
561 this.postNode(); | 574 this.postNode(); |
562 } else { | 575 } else { |
563 // Reached the end of the popupOptions | 576 // Reached the end of the popupOptions |
564 this.hidePopup(); | 577 this.hidePopup(); |
565 if (this.responses.nodeName == testState.stateResults[testState.stateIndex].nodeName) { | 578 for (var node of this.popupOptions) |
566 testState.stateResults[testState.stateIndex] = this.responses; | 579 { |
567 } else { | 580 this.store.postResult(node); |
568 testState.stateResults[testState.stateIndex].appendChild(this.responses); | |
569 } | 581 } |
570 advanceState(); | 582 advanceState(); |
571 } | 583 } |
572 }; | 584 }; |
573 | 585 |
630 | 642 |
631 function stateMachine() | 643 function stateMachine() |
632 { | 644 { |
633 // Object prototype for tracking and managing the test state | 645 // Object prototype for tracking and managing the test state |
634 this.stateMap = []; | 646 this.stateMap = []; |
647 this.preTestSurvey = null; | |
648 this.postTestSurvey = null; | |
635 this.stateIndex = null; | 649 this.stateIndex = null; |
636 this.currentStateMap = []; | 650 this.currentStateMap = null; |
637 this.currentIndex = null; | 651 this.currentStatePosition = null; |
638 this.currentTestId = 0; | 652 this.currentTestId = 0; |
639 this.stateResults = []; | 653 this.stateResults = []; |
640 this.timerCallBackHolders = null; | 654 this.timerCallBackHolders = null; |
641 this.initialise = function(){ | 655 this.initialise = function(){ |
656 | |
657 // Get the data from Specification | |
658 var pageHolder = []; | |
659 for (var page of specification.pages) | |
660 { | |
661 pageHolder.push(page); | |
662 } | |
663 if (specification.randomiseOrder) | |
664 { | |
665 pageHolder = randomiseOrder(pageHolder); | |
666 } | |
667 for (var i=0; i<pageHolder.length; i++) | |
668 { | |
669 pageHolder[i].presentedId = i; | |
670 } | |
671 for (var i=0; i<specification.pages.length; i++) | |
672 { | |
673 if (specification.testPages < i && specification.testPages != 0) {break;} | |
674 this.stateMap.push(pageHolder[i]); | |
675 | |
676 } | |
677 if (specification.preTest != null) {this.preTestSurvey = specification.preTest;} | |
678 if (specification.postTest != null) {this.postTestSurvey = specification.postTest;} | |
679 | |
642 if (this.stateMap.length > 0) { | 680 if (this.stateMap.length > 0) { |
643 if(this.stateIndex != null) { | 681 if(this.stateIndex != null) { |
644 console.log('NOTE - State already initialise'); | 682 console.log('NOTE - State already initialise'); |
645 } | 683 } |
646 this.stateIndex = -1; | 684 this.stateIndex = -1; |
664 if (this.stateIndex == null) { | 702 if (this.stateIndex == null) { |
665 this.initialise(); | 703 this.initialise(); |
666 } | 704 } |
667 if (this.stateIndex == -1) { | 705 if (this.stateIndex == -1) { |
668 console.log('Starting test...'); | 706 console.log('Starting test...'); |
669 } | 707 if (this.preTestSurvey != null) |
670 if (this.currentIndex == null){ | 708 { |
671 if (this.currentStateMap.type == "audioHolder") { | 709 popup.initState(this.preTestSurvey,storage.globalPreTest); |
672 // Save current page | |
673 this.testPageCompleted(this.stateResults[this.stateIndex],this.currentStateMap,this.currentTestId); | |
674 this.currentTestId++; | |
675 } | 710 } |
676 this.stateIndex++; | 711 this.stateIndex++; |
677 if (this.stateIndex >= this.stateMap.length) { | 712 } else if (this.stateIndex == this.stateMap.length) |
678 console.log('Test Completed'); | 713 { |
679 createProjectSave(specification.projectReturn); | 714 // All test pages complete, post test |
715 console.log('Ending test ...'); | |
716 this.stateIndex++; | |
717 if (this.postTestSurvey == null) { | |
718 this.advanceState(); | |
680 } else { | 719 } else { |
720 popup.initState(this.postTestSurvey,storage.globalPostTest); | |
721 } | |
722 } else if (this.stateIndex > this.stateMap.length) | |
723 { | |
724 createProjectSave(specification.projectReturn); | |
725 } | |
726 else | |
727 { | |
728 if (this.currentStateMap == null) | |
729 { | |
681 this.currentStateMap = this.stateMap[this.stateIndex]; | 730 this.currentStateMap = this.stateMap[this.stateIndex]; |
682 if (this.currentStateMap.type == "audioHolder") { | 731 storage.createTestPageStore(this.currentStateMap); |
683 console.log('Loading test page'); | 732 if (this.currentStateMap.preTest != null) |
684 interfaceContext.newPage(this.currentStateMap); | 733 { |
685 this.initialiseInnerState(this.currentStateMap); | 734 this.currentStatePosition = 'pre'; |
686 } else if (this.currentStateMap.type == "pretest" || this.currentStateMap.type == "posttest") { | 735 popup.initState(this.currentStateMap.preTest,storage.testPages[this.stateIndex].preTest); |
687 if (this.currentStateMap.options.length >= 1) { | |
688 popup.initState(this.currentStateMap); | |
689 } else { | |
690 this.advanceState(); | |
691 } | |
692 } else { | 736 } else { |
737 this.currentStatePosition = 'test'; | |
738 } | |
739 interfaceContext.newPage(this.currentStateMap,storage.testPages[this.stateIndex]); | |
740 return; | |
741 } | |
742 switch(this.currentStatePosition) | |
743 { | |
744 case 'pre': | |
745 this.currentStatePosition = 'test'; | |
746 break; | |
747 case 'test': | |
748 this.currentStatePosition = 'post'; | |
749 // Save the data | |
750 this.testPageCompleted(); | |
751 if (this.currentStateMap.postTest == null) | |
752 { | |
693 this.advanceState(); | 753 this.advanceState(); |
694 } | 754 return; |
695 } | 755 } else { |
696 } else { | 756 popup.initState(this.currentStateMap.postTest,storage.testPages[this.stateIndex].postTest); |
697 this.advanceInnerState(); | 757 } |
698 } | 758 break; |
699 }; | 759 case 'post': |
700 | 760 this.stateIndex++; |
701 this.testPageCompleted = function(store, testXML, testId) { | 761 this.currentStateMap = null; |
762 this.advanceState(); | |
763 break; | |
764 }; | |
765 } | |
766 }; | |
767 | |
768 this.testPageCompleted = function() { | |
702 // Function called each time a test page has been completed | 769 // Function called each time a test page has been completed |
703 var metric = document.createElement('metric'); | 770 var storePoint = storage.testPages[this.stateIndex]; |
771 // First get the test metric | |
772 | |
773 var metric = storePoint.XMLDOM.getElementsByTagName('metric')[0]; | |
704 if (audioEngineContext.metric.enableTestTimer) | 774 if (audioEngineContext.metric.enableTestTimer) |
705 { | 775 { |
706 var testTime = document.createElement('metricResult'); | 776 var testTime = storePoint.parent.document.createElement('metricresult'); |
707 testTime.id = 'testTime'; | 777 testTime.id = 'testTime'; |
708 testTime.textContent = audioEngineContext.timer.testDuration; | 778 testTime.textContent = audioEngineContext.timer.testDuration; |
709 metric.appendChild(testTime); | 779 metric.appendChild(testTime); |
710 } | 780 } |
711 store.appendChild(metric); | 781 |
712 var audioObjects = audioEngineContext.audioObjects; | 782 var audioObjects = audioEngineContext.audioObjects; |
713 for (var i=0; i<audioObjects.length; i++) | 783 for (var ao of audioEngineContext.audioObjects) |
714 { | 784 { |
715 var audioElement = audioEngineContext.audioObjects[i].exportXMLDOM(); | 785 ao.exportXMLDOM(); |
716 audioElement.setAttribute('presentedId',i); | 786 } |
717 store.appendChild(audioElement); | 787 for (var element of interfaceContext.commentQuestions) |
718 } | 788 { |
719 $(interfaceContext.commentQuestions).each(function(index,element){ | 789 element.exportXMLDOM(storePoint); |
720 var node = element.exportXMLDOM(); | 790 } |
721 store.appendChild(node); | 791 pageXMLSave(storePoint.XMLDOM, this.currentStateMap); |
722 }); | 792 }; |
723 pageXMLSave(store, testXML); | |
724 }; | |
725 | |
726 this.initialiseInnerState = function(node) { | |
727 // Parses the received testXML for pre and post test options | |
728 this.currentStateMap = []; | |
729 var preTest = node.preTest; | |
730 var postTest = node.postTest; | |
731 if (preTest == undefined) {preTest = document.createElement("preTest");} | |
732 if (postTest == undefined){postTest= document.createElement("postTest");} | |
733 this.currentStateMap.push(preTest); | |
734 this.currentStateMap.push(node); | |
735 this.currentStateMap.push(postTest); | |
736 this.currentIndex = -1; | |
737 this.advanceInnerState(); | |
738 }; | |
739 | |
740 this.advanceInnerState = function() { | |
741 this.currentIndex++; | |
742 if (this.currentIndex >= this.currentStateMap.length) { | |
743 this.currentIndex = null; | |
744 this.currentStateMap = this.stateMap[this.stateIndex]; | |
745 this.advanceState(); | |
746 } else { | |
747 if (this.currentStateMap[this.currentIndex].type == "audioHolder") { | |
748 console.log("Loading test page"+this.currentTestId); | |
749 } else if (this.currentStateMap[this.currentIndex].type == "pretest") { | |
750 popup.initState(this.currentStateMap[this.currentIndex]); | |
751 } else if (this.currentStateMap[this.currentIndex].type == "posttest") { | |
752 popup.initState(this.currentStateMap[this.currentIndex]); | |
753 } else { | |
754 this.advanceInnerState(); | |
755 } | |
756 } | |
757 }; | |
758 | |
759 this.previousState = function(){}; | |
760 } | 793 } |
761 | 794 |
762 function AudioEngine(specification) { | 795 function AudioEngine(specification) { |
763 | 796 |
764 // Create two output paths, the main outputGain and fooGain. | 797 // Create two output paths, the main outputGain and fooGain. |
780 this.timer = new timer(); | 813 this.timer = new timer(); |
781 // Create session metrics | 814 // Create session metrics |
782 this.metric = new sessionMetrics(this,specification); | 815 this.metric = new sessionMetrics(this,specification); |
783 | 816 |
784 this.loopPlayback = false; | 817 this.loopPlayback = false; |
818 | |
819 this.pageStore = null; | |
785 | 820 |
786 // Create store for new audioObjects | 821 // Create store for new audioObjects |
787 this.audioObjects = []; | 822 this.audioObjects = []; |
788 | 823 |
789 this.buffers = []; | 824 this.buffers = []; |
889 { | 924 { |
890 if (i != id) { | 925 if (i != id) { |
891 this.audioObjects[i].outputGain.gain.value = 0.0; | 926 this.audioObjects[i].outputGain.gain.value = 0.0; |
892 this.audioObjects[i].stop(); | 927 this.audioObjects[i].stop(); |
893 } else if (i == id) { | 928 } else if (i == id) { |
894 this.audioObjects[id].outputGain.gain.value = this.audioObjects[id].specification.gain*this.audioObjects[id].buffer.buffer.playbackGain; | 929 this.audioObjects[id].outputGain.gain.value = this.audioObjects[id].onplayGain; |
895 this.audioObjects[id].play(audioContext.currentTime+0.01); | 930 this.audioObjects[id].play(audioContext.currentTime+0.01); |
896 } | 931 } |
897 } | 932 } |
898 } | 933 } |
899 interfaceContext.playhead.start(); | 934 interfaceContext.playhead.start(); |
919 // Create the audioObject with ID of the new track length; | 954 // Create the audioObject with ID of the new track length; |
920 audioObjectId = this.audioObjects.length; | 955 audioObjectId = this.audioObjects.length; |
921 this.audioObjects[audioObjectId] = new audioObject(audioObjectId); | 956 this.audioObjects[audioObjectId] = new audioObject(audioObjectId); |
922 | 957 |
923 // Check if audioObject buffer is currently stored by full URL | 958 // Check if audioObject buffer is currently stored by full URL |
924 var URL = element.parent.hostURL + element.url; | 959 var URL = testState.currentStateMap.hostURL + element.url; |
925 var buffer = null; | 960 var buffer = null; |
926 for (var i=0; i<this.buffers.length; i++) | 961 for (var i=0; i<this.buffers.length; i++) |
927 { | 962 { |
928 if (URL == this.buffers[i].url) | 963 if (URL == this.buffers[i].url) |
929 { | 964 { |
939 this.buffers.push(buffer); | 974 this.buffers.push(buffer); |
940 } | 975 } |
941 this.audioObjects[audioObjectId].specification = element; | 976 this.audioObjects[audioObjectId].specification = element; |
942 this.audioObjects[audioObjectId].url = URL; | 977 this.audioObjects[audioObjectId].url = URL; |
943 buffer.users.push(this.audioObjects[audioObjectId]); | 978 buffer.users.push(this.audioObjects[audioObjectId]); |
979 // Obtain store node | |
980 var aeNodes = this.pageStore.XMLDOM.getElementsByTagName('audioelement'); | |
981 for (var i=0; i<aeNodes.length; i++) | |
982 { | |
983 if(aeNodes[i].id == element.id) | |
984 { | |
985 this.audioObjects[audioObjectId].storeDOM = aeNodes[i]; | |
986 break; | |
987 } | |
988 } | |
944 if (buffer.buffer != null) | 989 if (buffer.buffer != null) |
945 { | 990 { |
946 this.audioObjects[audioObjectId].bufferLoaded(buffer); | 991 this.audioObjects[audioObjectId].bufferLoaded(buffer); |
947 } | 992 } |
948 return this.audioObjects[audioObjectId]; | 993 return this.audioObjects[audioObjectId]; |
949 }; | 994 }; |
950 | 995 |
951 this.newTestPage = function() { | 996 this.newTestPage = function(store) { |
997 this.pageStore = store; | |
952 this.state = 0; | 998 this.state = 0; |
953 this.audioObjectsReady = false; | 999 this.audioObjectsReady = false; |
954 this.metric.reset(); | 1000 this.metric.reset(); |
955 for (var i=0; i < this.buffers.length; i++) | 1001 for (var i=0; i < this.buffers.length; i++) |
956 { | 1002 { |
1019 this.specification; | 1065 this.specification; |
1020 this.id = id; | 1066 this.id = id; |
1021 this.state = 0; // 0 - no data, 1 - ready | 1067 this.state = 0; // 0 - no data, 1 - ready |
1022 this.url = null; // Hold the URL given for the output back to the results. | 1068 this.url = null; // Hold the URL given for the output back to the results. |
1023 this.metric = new metricTracker(this); | 1069 this.metric = new metricTracker(this); |
1070 this.storeDOM = null; | |
1024 | 1071 |
1025 // Bindings for GUI | 1072 // Bindings for GUI |
1026 this.interfaceDOM = null; | 1073 this.interfaceDOM = null; |
1027 this.commentDOM = null; | 1074 this.commentDOM = null; |
1028 | 1075 |
1030 this.bufferNode = undefined; | 1077 this.bufferNode = undefined; |
1031 this.outputGain = audioContext.createGain(); | 1078 this.outputGain = audioContext.createGain(); |
1032 | 1079 |
1033 // Default output gain to be zero | 1080 // Default output gain to be zero |
1034 this.outputGain.gain.value = 0.0; | 1081 this.outputGain.gain.value = 0.0; |
1082 this.onplayGain = 1.0; | |
1035 | 1083 |
1036 // Connect buffer to the audio graph | 1084 // Connect buffer to the audio graph |
1037 this.outputGain.connect(audioEngineContext.outputGain); | 1085 this.outputGain.connect(audioEngineContext.outputGain); |
1038 | 1086 |
1039 // the audiobuffer is not designed for multi-start playback | 1087 // the audiobuffer is not designed for multi-start playback |
1072 this.buffer.buffer.playbackGain = 1.0; | 1120 this.buffer.buffer.playbackGain = 1.0; |
1073 } | 1121 } |
1074 if (this.interfaceDOM != null) { | 1122 if (this.interfaceDOM != null) { |
1075 this.interfaceDOM.enable(); | 1123 this.interfaceDOM.enable(); |
1076 } | 1124 } |
1125 this.onplayGain = decibelToLinear(this.specification.gain)*this.buffer.buffer.playbackGain; | |
1126 | |
1127 this.storeDOM.setAttribute('presentedId',this.id); | |
1128 this.storeDOM.setAttribute('playGain',linearToDecibel(this.onplayGain)); | |
1077 }; | 1129 }; |
1078 | 1130 |
1079 this.loopStart = function() { | 1131 this.loopStart = function() { |
1080 this.outputGain.gain.value = this.specification.gain*this.buffer.buffer.playbackGain; | 1132 this.outputGain.gain.value = this.onplayGain; |
1081 this.metric.startListening(audioEngineContext.timer.getTestTime()); | 1133 this.metric.startListening(audioEngineContext.timer.getTestTime()); |
1082 }; | 1134 }; |
1083 | 1135 |
1084 this.loopStop = function() { | 1136 this.loopStop = function() { |
1085 if (this.outputGain.gain.value != 0.0) { | 1137 if (this.outputGain.gain.value != 0.0) { |
1136 return 0; | 1188 return 0; |
1137 } | 1189 } |
1138 }; | 1190 }; |
1139 | 1191 |
1140 this.exportXMLDOM = function() { | 1192 this.exportXMLDOM = function() { |
1141 var root = document.createElement('audioElement'); | 1193 var file = storage.document.createElement('file'); |
1142 root.id = this.specification.id; | |
1143 root.setAttribute('url',this.specification.url); | |
1144 var file = document.createElement('file'); | |
1145 file.setAttribute('sampleRate',this.buffer.buffer.sampleRate); | 1194 file.setAttribute('sampleRate',this.buffer.buffer.sampleRate); |
1146 file.setAttribute('channels',this.buffer.buffer.numberOfChannels); | 1195 file.setAttribute('channels',this.buffer.buffer.numberOfChannels); |
1147 file.setAttribute('sampleCount',this.buffer.buffer.length); | 1196 file.setAttribute('sampleCount',this.buffer.buffer.length); |
1148 file.setAttribute('duration',this.buffer.buffer.duration); | 1197 file.setAttribute('duration',this.buffer.buffer.duration); |
1149 root.appendChild(file); | 1198 this.storeDOM.appendChild(file); |
1150 if (this.specification.type != 'outsidereference') { | 1199 if (this.specification.type != 'outside-reference') { |
1151 var interfaceXML = this.interfaceDOM.exportXMLDOM(this); | 1200 var interfaceXML = this.interfaceDOM.exportXMLDOM(this); |
1152 if (interfaceXML.length == undefined) { | 1201 if (interfaceXML.length == undefined) { |
1153 root.appendChild(interfaceXML); | 1202 this.storeDOM.appendChild(interfaceXML); |
1154 } else { | 1203 } else { |
1155 for (var i=0; i<interfaceXML.length; i++) | 1204 for (var i=0; i<interfaceXML.length; i++) |
1156 { | 1205 { |
1157 root.appendChild(interfaceXML[i]); | 1206 this.storeDOM.appendChild(interfaceXML[i]); |
1158 } | 1207 } |
1159 } | 1208 } |
1160 root.appendChild(this.commentDOM.exportXMLDOM(this)); | 1209 this.storeDOM.appendChild(this.commentDOM.exportXMLDOM(this)); |
1161 if(this.specification.type == 'anchor') { | 1210 } |
1162 root.setAttribute('anchor',true); | 1211 var nodes = this.metric.exportXMLDOM(); |
1163 } else if(this.specification.type == 'reference') { | 1212 var mroot = this.storeDOM.getElementsByTagName('metric')[0]; |
1164 root.setAttribute('reference',true); | 1213 for (var i=0; i<nodes.length; i++) |
1165 } | 1214 { |
1166 } | 1215 mroot.appendChild(nodes[i]); |
1167 root.appendChild(this.metric.exportXMLDOM()); | 1216 } |
1168 return root; | |
1169 }; | 1217 }; |
1170 } | 1218 } |
1171 | 1219 |
1172 function timer() | 1220 function timer() |
1173 { | 1221 { |
1230 this.enableElementTracker = false; | 1278 this.enableElementTracker = false; |
1231 this.enableFlagListenedTo = false; | 1279 this.enableFlagListenedTo = false; |
1232 this.enableFlagMoved = false; | 1280 this.enableFlagMoved = false; |
1233 this.enableTestTimer = false; | 1281 this.enableTestTimer = false; |
1234 // Obtain the metrics enabled | 1282 // Obtain the metrics enabled |
1235 for (var i=0; i<specification.metrics.length; i++) | 1283 for (var i=0; i<specification.metrics.enabled.length; i++) |
1236 { | 1284 { |
1237 var node = specification.metrics[i]; | 1285 var node = specification.metrics.enabled[i]; |
1238 switch(node.enabled) | 1286 switch(node) |
1239 { | 1287 { |
1240 case 'testTimer': | 1288 case 'testTimer': |
1241 this.enableTestTimer = true; | 1289 this.enableTestTimer = true; |
1242 break; | 1290 break; |
1243 case 'elementTimer': | 1291 case 'elementTimer': |
1281 this.wasListenedTo = false; | 1329 this.wasListenedTo = false; |
1282 this.wasMoved = false; | 1330 this.wasMoved = false; |
1283 this.hasComments = false; | 1331 this.hasComments = false; |
1284 this.parent = caller; | 1332 this.parent = caller; |
1285 | 1333 |
1286 this.initialised = function(position) | 1334 this.initialise = function(position) |
1287 { | 1335 { |
1288 if (this.initialPosition == -1) { | 1336 if (this.initialPosition == -1) { |
1289 this.initialPosition = position; | 1337 this.initialPosition = position; |
1290 } | 1338 } |
1291 }; | 1339 }; |
1338 console.log('slider ' + this.parent.id + ' played for (' + diff + ')'); // DEBUG/SAFETY: show played slider id | 1386 console.log('slider ' + this.parent.id + ' played for (' + diff + ')'); // DEBUG/SAFETY: show played slider id |
1339 } | 1387 } |
1340 }; | 1388 }; |
1341 | 1389 |
1342 this.exportXMLDOM = function() { | 1390 this.exportXMLDOM = function() { |
1343 var root = document.createElement('metric'); | 1391 var storeDOM = []; |
1344 if (audioEngineContext.metric.enableElementTimer) { | 1392 if (audioEngineContext.metric.enableElementTimer) { |
1345 var mElementTimer = document.createElement('metricresult'); | 1393 var mElementTimer = storage.document.createElement('metricresult'); |
1346 mElementTimer.setAttribute('name','enableElementTimer'); | 1394 mElementTimer.setAttribute('name','enableElementTimer'); |
1347 mElementTimer.textContent = this.listenedTimer; | 1395 mElementTimer.textContent = this.listenedTimer; |
1348 root.appendChild(mElementTimer); | 1396 storeDOM.push(mElementTimer); |
1349 } | 1397 } |
1350 if (audioEngineContext.metric.enableElementTracker) { | 1398 if (audioEngineContext.metric.enableElementTracker) { |
1351 var elementTrackerFull = document.createElement('metricResult'); | 1399 var elementTrackerFull = storage.document.createElement('metricResult'); |
1352 elementTrackerFull.setAttribute('name','elementTrackerFull'); | 1400 elementTrackerFull.setAttribute('name','elementTrackerFull'); |
1353 for (var k=0; k<this.movementTracker.length; k++) | 1401 for (var k=0; k<this.movementTracker.length; k++) |
1354 { | 1402 { |
1355 var timePos = document.createElement('timePos'); | 1403 var timePos = storage.document.createElement('timePos'); |
1356 timePos.id = k; | 1404 timePos.id = k; |
1357 var time = document.createElement('time'); | 1405 var time = storage.document.createElement('time'); |
1358 time.textContent = this.movementTracker[k][0]; | 1406 time.textContent = this.movementTracker[k][0]; |
1359 var position = document.createElement('position'); | 1407 var position = document.createElement('position'); |
1360 position.textContent = this.movementTracker[k][1]; | 1408 position.textContent = this.movementTracker[k][1]; |
1361 timePos.appendChild(time); | 1409 timePos.appendChild(time); |
1362 timePos.appendChild(position); | 1410 timePos.appendChild(position); |
1363 elementTrackerFull.appendChild(timePos); | 1411 elementTrackerFull.appendChild(timePos); |
1364 } | 1412 } |
1365 root.appendChild(elementTrackerFull); | 1413 storeDOM.push(elementTrackerFull); |
1366 } | 1414 } |
1367 if (audioEngineContext.metric.enableElementListenTracker) { | 1415 if (audioEngineContext.metric.enableElementListenTracker) { |
1368 var elementListenTracker = document.createElement('metricResult'); | 1416 var elementListenTracker = storage.document.createElement('metricResult'); |
1369 elementListenTracker.setAttribute('name','elementListenTracker'); | 1417 elementListenTracker.setAttribute('name','elementListenTracker'); |
1370 for (var k=0; k<this.listenTracker.length; k++) { | 1418 for (var k=0; k<this.listenTracker.length; k++) { |
1371 elementListenTracker.appendChild(this.listenTracker[k]); | 1419 elementListenTracker.appendChild(this.listenTracker[k]); |
1372 } | 1420 } |
1373 root.appendChild(elementListenTracker); | 1421 storeDOM.push(elementListenTracker); |
1374 } | 1422 } |
1375 if (audioEngineContext.metric.enableElementInitialPosition) { | 1423 if (audioEngineContext.metric.enableElementInitialPosition) { |
1376 var elementInitial = document.createElement('metricResult'); | 1424 var elementInitial = storage.document.createElement('metricResult'); |
1377 elementInitial.setAttribute('name','elementInitialPosition'); | 1425 elementInitial.setAttribute('name','elementInitialPosition'); |
1378 elementInitial.textContent = this.initialPosition; | 1426 elementInitial.textContent = this.initialPosition; |
1379 root.appendChild(elementInitial); | 1427 storeDOM.push(elementInitial); |
1380 } | 1428 } |
1381 if (audioEngineContext.metric.enableFlagListenedTo) { | 1429 if (audioEngineContext.metric.enableFlagListenedTo) { |
1382 var flagListenedTo = document.createElement('metricResult'); | 1430 var flagListenedTo = storage.document.createElement('metricResult'); |
1383 flagListenedTo.setAttribute('name','elementFlagListenedTo'); | 1431 flagListenedTo.setAttribute('name','elementFlagListenedTo'); |
1384 flagListenedTo.textContent = this.wasListenedTo; | 1432 flagListenedTo.textContent = this.wasListenedTo; |
1385 root.appendChild(flagListenedTo); | 1433 storeDOM.push(flagListenedTo); |
1386 } | 1434 } |
1387 if (audioEngineContext.metric.enableFlagMoved) { | 1435 if (audioEngineContext.metric.enableFlagMoved) { |
1388 var flagMoved = document.createElement('metricResult'); | 1436 var flagMoved = storage.document.createElement('metricResult'); |
1389 flagMoved.setAttribute('name','elementFlagMoved'); | 1437 flagMoved.setAttribute('name','elementFlagMoved'); |
1390 flagMoved.textContent = this.wasMoved; | 1438 flagMoved.textContent = this.wasMoved; |
1391 root.appendChild(flagMoved); | 1439 storeDOM.push(flagMoved); |
1392 } | 1440 } |
1393 if (audioEngineContext.metric.enableFlagComments) { | 1441 if (audioEngineContext.metric.enableFlagComments) { |
1394 var flagComments = document.createElement('metricResult'); | 1442 var flagComments = storage.document.createElement('metricResult'); |
1395 flagComments.setAttribute('name','elementFlagComments'); | 1443 flagComments.setAttribute('name','elementFlagComments'); |
1396 if (this.parent.commentDOM == null) | 1444 if (this.parent.commentDOM == null) |
1397 {flag.textContent = 'false';} | 1445 {flag.textContent = 'false';} |
1398 else if (this.parent.commentDOM.textContent.length == 0) | 1446 else if (this.parent.commentDOM.textContent.length == 0) |
1399 {flag.textContent = 'false';} | 1447 {flag.textContent = 'false';} |
1400 else | 1448 else |
1401 {flag.textContet = 'true';} | 1449 {flag.textContet = 'true';} |
1402 root.appendChild(flagComments); | 1450 storeDOM.push(flagComments); |
1403 } | 1451 } |
1404 | 1452 return storeDOM; |
1405 return root; | |
1406 }; | 1453 }; |
1407 } | 1454 } |
1408 | 1455 |
1409 function randomiseOrder(input) | 1456 function randomiseOrder(input) |
1410 { | 1457 { |
1477 } | 1524 } |
1478 | 1525 |
1479 function Specification() { | 1526 function Specification() { |
1480 // Handles the decoding of the project specification XML into a simple JavaScript Object. | 1527 // Handles the decoding of the project specification XML into a simple JavaScript Object. |
1481 | 1528 |
1482 this.interfaceType = null; | 1529 this.interface = null; |
1483 this.commonInterface = new function() | 1530 this.projectReturn = null; |
1484 { | 1531 this.randomiseOrder = null; |
1485 this.options = []; | 1532 this.testPages = null; |
1486 this.optionNode = function(input) | 1533 this.pages = []; |
1487 { | 1534 this.metrics = null; |
1488 var name = input.getAttribute('name'); | 1535 this.interfaces = null; |
1489 this.type = name; | 1536 this.loudness = null; |
1490 if(this.type == "option") | 1537 this.errors = []; |
1491 { | 1538 this.schema = null; |
1492 this.name = input.id; | |
1493 } else if (this.type == "check") | |
1494 { | |
1495 this.check = input.id; | |
1496 } | |
1497 }; | |
1498 }; | |
1499 | 1539 |
1500 this.randomiseOrder = function(input) | 1540 this.randomiseOrder = function(input) |
1501 { | 1541 { |
1502 // This takes an array of information and randomises the order | 1542 // This takes an array of information and randomises the order |
1503 var N = input.length; | 1543 var N = input.length; |
1522 } | 1562 } |
1523 console.log(inputSequenceClone.toString()); // print original array to console | 1563 console.log(inputSequenceClone.toString()); // print original array to console |
1524 console.log(outputSequence.toString()); // print randomised array to console | 1564 console.log(outputSequence.toString()); // print randomised array to console |
1525 return holdArr; | 1565 return holdArr; |
1526 }; | 1566 }; |
1527 this.projectReturn = null; | 1567 |
1528 this.randomiseOrder = null; | 1568 this.processAttribute = function(attribute,schema) |
1529 this.collectMetrics = null; | 1569 { |
1530 this.testPages = null; | 1570 // attribute is the string returned from getAttribute on the XML |
1531 this.audioHolders = []; | 1571 // schema is the <xs:attribute> node |
1532 this.metrics = []; | 1572 if (schema.getAttribute('name') == undefined && schema.getAttribute('ref') != undefined) |
1533 this.loudness = null; | 1573 { |
1574 schema = this.schema.getElementsByName(schema.getAttribute('ref'))[0]; | |
1575 } | |
1576 var defaultOpt = schema.getAttribute('default'); | |
1577 if (attribute == null) { | |
1578 attribute = defaultOpt; | |
1579 } | |
1580 var dataType = schema.getAttribute('type'); | |
1581 if (typeof dataType == "string") { dataType = dataType.substr(3);} | |
1582 else {dataType = "string";} | |
1583 if (attribute == null) | |
1584 { | |
1585 return attribute; | |
1586 } | |
1587 switch(dataType) | |
1588 { | |
1589 case "boolean": | |
1590 if (attribute == 'true'){attribute = true;}else{attribute=false;} | |
1591 break; | |
1592 case "negativeInteger": | |
1593 case "positiveInteger": | |
1594 case "nonNegativeInteger": | |
1595 case "nonPositiveInteger": | |
1596 case "integer": | |
1597 case "decimal": | |
1598 case "short": | |
1599 attribute = Number(attribute); | |
1600 break; | |
1601 case "string": | |
1602 default: | |
1603 attribute = String(attribute); | |
1604 break; | |
1605 } | |
1606 return attribute; | |
1607 }; | |
1534 | 1608 |
1535 this.decode = function(projectXML) { | 1609 this.decode = function(projectXML) { |
1610 this.errors = []; | |
1536 // projectXML - DOM Parsed document | 1611 // projectXML - DOM Parsed document |
1537 this.projectXML = projectXML.childNodes[0]; | 1612 this.projectXML = projectXML.childNodes[0]; |
1538 var setupNode = projectXML.getElementsByTagName('setup')[0]; | 1613 var setupNode = projectXML.getElementsByTagName('setup')[0]; |
1539 this.interfaceType = setupNode.getAttribute('interface'); | 1614 var schemaSetup = this.schema.getElementsByName('setup')[0]; |
1540 this.projectReturn = setupNode.getAttribute('projectReturn'); | 1615 // First decode the attributes |
1541 this.testPages = setupNode.getAttribute('testPages'); | 1616 var attributes = schemaSetup.getElementsByTagName('attribute'); |
1542 if (setupNode.getAttribute('randomiseOrder') == "true") { | 1617 for (var i in attributes) |
1543 this.randomiseOrder = true; | 1618 { |
1544 } else {this.randomiseOrder = false;} | 1619 if (isNaN(Number(i)) == true){break;} |
1545 if (setupNode.getAttribute('collectMetrics') == "true") { | 1620 var attributeName = attributes[i].getAttribute('name'); |
1546 this.collectMetrics = true; | 1621 var projectAttr = setupNode.getAttribute(attributeName); |
1547 } else {this.collectMetrics = false;} | 1622 projectAttr = this.processAttribute(projectAttr,attributes[i]); |
1548 if (isNaN(Number(this.testPages)) || this.testPages == undefined) | 1623 switch(typeof projectAttr) |
1549 { | 1624 { |
1550 this.testPages = null; | 1625 case "number": |
1551 } else { | 1626 case "boolean": |
1552 this.testPages = Number(this.testPages); | 1627 eval('this.'+attributeName+' = '+projectAttr); |
1553 if (this.testPages == 0) {this.testPages = null;} | 1628 break; |
1554 } | 1629 case "string": |
1555 if (setupNode.getAttribute('loudness') != null) | 1630 eval('this.'+attributeName+' = "'+projectAttr+'"'); |
1556 { | 1631 break; |
1557 var XMLloudness = setupNode.getAttribute('loudness'); | 1632 } |
1558 if (isNaN(Number(XMLloudness)) == false) | 1633 |
1559 { | 1634 } |
1560 this.loudness = Number(XMLloudness); | 1635 |
1561 } | 1636 this.metrics = { |
1562 } | 1637 enabled: [], |
1563 var metricCollection = setupNode.getElementsByTagName('Metric'); | 1638 decode: function(parent, xml) { |
1564 | 1639 var children = xml.getElementsByTagName('metricenable'); |
1565 var setupPreTestNode = setupNode.getElementsByTagName('PreTest'); | 1640 for (var i in children) { |
1566 if (setupPreTestNode.length != 0) | 1641 if (isNaN(Number(i)) == true){break;} |
1567 { | 1642 this.enabled.push(children[i].textContent); |
1568 setupPreTestNode = setupPreTestNode[0]; | 1643 } |
1569 this.preTest.construct(setupPreTestNode); | 1644 }, |
1570 } | 1645 encode: function(root) { |
1571 | 1646 var node = root.createElement('metric'); |
1572 var setupPostTestNode = setupNode.getElementsByTagName('PostTest'); | 1647 for (var i in this.enabled) |
1573 if (setupPostTestNode.length != 0) | |
1574 { | |
1575 setupPostTestNode = setupPostTestNode[0]; | |
1576 this.postTest.construct(setupPostTestNode); | |
1577 } | |
1578 | |
1579 if (metricCollection.length > 0) { | |
1580 metricCollection = metricCollection[0].getElementsByTagName('metricEnable'); | |
1581 for (var i=0; i<metricCollection.length; i++) { | |
1582 this.metrics.push(new this.metricNode(metricCollection[i].textContent)); | |
1583 } | |
1584 } | |
1585 | |
1586 var commonInterfaceNode = setupNode.getElementsByTagName('interface'); | |
1587 if (commonInterfaceNode.length > 0) { | |
1588 commonInterfaceNode = commonInterfaceNode[0]; | |
1589 } else { | |
1590 commonInterfaceNode = undefined; | |
1591 } | |
1592 | |
1593 this.commonInterface = new function() { | |
1594 this.OptionNode = function(child) { | |
1595 this.type = child.nodeName; | |
1596 if (this.type == 'option') | |
1597 { | 1648 { |
1598 this.name = child.getAttribute('name'); | 1649 if (isNaN(Number(i)) == true){break;} |
1599 } | 1650 var child = root.createElement('metricenable'); |
1600 else if (this.type == 'check') { | 1651 child.textContent = this.enabled[i]; |
1601 this.check = child.getAttribute('name'); | 1652 node.appendChild(child); |
1602 if (this.check == 'scalerange') { | 1653 } |
1603 this.min = child.getAttribute('min'); | 1654 return node; |
1604 this.max = child.getAttribute('max'); | |
1605 if (this.min == null) {this.min = 1;} | |
1606 else if (Number(this.min) > 1 && this.min != null) { | |
1607 this.min = Number(this.min)/100; | |
1608 } else { | |
1609 this.min = Number(this.min); | |
1610 } | |
1611 if (this.max == null) {this.max = 0;} | |
1612 else if (Number(this.max) > 1 && this.max != null) { | |
1613 this.max = Number(this.max)/100; | |
1614 } else { | |
1615 this.max = Number(this.max); | |
1616 } | |
1617 } | |
1618 } else if (this.type == 'anchor' || this.type == 'reference') { | |
1619 this.value = Number(child.textContent); | |
1620 this.enforce = child.getAttribute('enforce'); | |
1621 if (this.enforce == 'true') {this.enforce = true;} | |
1622 else {this.enforce = false;} | |
1623 } | |
1624 }; | |
1625 this.options = []; | |
1626 if (commonInterfaceNode != undefined) { | |
1627 var child = commonInterfaceNode.firstElementChild; | |
1628 while (child != undefined) { | |
1629 this.options.push(new this.OptionNode(child)); | |
1630 child = child.nextElementSibling; | |
1631 } | |
1632 } | 1655 } |
1633 }; | 1656 }; |
1634 | 1657 |
1635 var audioHolders = projectXML.getElementsByTagName('audioHolder'); | 1658 this.metrics.decode(this,setupNode.getElementsByTagName('metric')[0]); |
1636 for (var i=0; i<audioHolders.length; i++) { | 1659 |
1637 var node = new this.audioHolderNode(this); | 1660 // Now process the survey node options |
1638 node.decode(this,audioHolders[i]); | 1661 var survey = setupNode.getElementsByTagName('survey'); |
1639 this.audioHolders.push(node); | 1662 var surveySchema = specification.schema.getElementsByName('survey')[0]; |
1640 } | 1663 for (var i in survey) { |
1641 | 1664 if (isNaN(Number(i)) == true){break;} |
1642 // New check if we need to randomise the test order | 1665 var location = survey[i].getAttribute('location'); |
1643 if (this.randomiseOrder && typeof randomiseOrder === "function") | 1666 if (location == 'pre' || location == 'before') |
1644 { | 1667 { |
1645 this.audioHolders = randomiseOrder(this.audioHolders); | 1668 if (this.preTest != null){this.errors.push("Already a pre/before test survey defined! Ignoring second!!");} |
1646 for (var i=0; i<this.audioHolders.length; i++) | 1669 else { |
1647 { | 1670 this.preTest = new this.surveyNode(); |
1648 this.audioHolders[i].presentedId = i; | 1671 this.preTest.decode(this,survey[i],surveySchema); |
1649 } | 1672 } |
1650 } | 1673 } else if (location == 'post' || location == 'after') { |
1651 | 1674 if (this.postTest != null){this.errors.push("Already a post/after test survey defined! Ignoring second!!");} |
1652 if (this.testPages != null || this.testPages != undefined) | 1675 else { |
1653 { | 1676 this.postTest = new this.surveyNode(); |
1654 if (this.testPages > audioHolders.length) | 1677 this.postTest.decode(this,survey[i],surveySchema); |
1655 { | 1678 } |
1656 console.log('Warning: You have specified '+audioHolders.length+' tests but requested '+this.testPages+' be completed!'); | 1679 } |
1657 this.testPages = audioHolders.length; | 1680 } |
1658 } | 1681 |
1659 var aH = this.audioHolders; | 1682 var interfaceNode = setupNode.getElementsByTagName('interface'); |
1660 this.audioHolders = []; | 1683 if (interfaceNode.length > 1) |
1661 for (var i=0; i<this.testPages; i++) | 1684 { |
1662 { | 1685 this.errors.push("Only one <interface> node in the <setup> node allowed! Others except first ingnored!"); |
1663 this.audioHolders.push(aH[i]); | 1686 } |
1664 } | 1687 this.interfaces = new this.interfaceNode(); |
1688 if (interfaceNode.length != 0) | |
1689 { | |
1690 interfaceNode = interfaceNode[0]; | |
1691 this.interfaces.decode(this,interfaceNode,this.schema.getElementsByName('interface')[1]); | |
1692 } | |
1693 | |
1694 // Page tags | |
1695 var pageTags = projectXML.getElementsByTagName('page'); | |
1696 var pageSchema = this.schema.getElementsByName('page')[0]; | |
1697 for (var i=0; i<pageTags.length; i++) | |
1698 { | |
1699 var node = new this.page(); | |
1700 node.decode(this,pageTags[i],pageSchema); | |
1701 this.pages.push(node); | |
1665 } | 1702 } |
1666 }; | 1703 }; |
1667 | 1704 |
1668 this.encode = function() | 1705 this.encode = function() |
1669 { | 1706 { |
1670 var root = document.implementation.createDocument(null,"BrowserEvalProjectDocument"); | 1707 var root = document.implementation.createDocument(null,"waet"); |
1671 // First get all the <setup> tag compiled | 1708 |
1672 var setupNode = root.createElement("setup"); | 1709 // Build setup node |
1673 setupNode.setAttribute('interface',this.interfaceType); | 1710 |
1674 setupNode.setAttribute('projectReturn',this.projectReturn); | |
1675 setupNode.setAttribute('randomiseOrder',this.randomiseOrder); | |
1676 setupNode.setAttribute('collectMetrics',this.collectMetrics); | |
1677 setupNode.setAttribute('testPages',this.testPages); | |
1678 if(this.loudness != null) {AHNode.setAttribute("loudness",this.loudness);} | |
1679 | |
1680 var setupPreTest = root.createElement("PreTest"); | |
1681 for (var i=0; i<this.preTest.options.length; i++) | |
1682 { | |
1683 setupPreTest.appendChild(this.preTest.options[i].exportXML(root)); | |
1684 } | |
1685 | |
1686 var setupPostTest = root.createElement("PostTest"); | |
1687 for (var i=0; i<this.postTest.options.length; i++) | |
1688 { | |
1689 setupPostTest.appendChild(this.postTest.options[i].exportXML(root)); | |
1690 } | |
1691 | |
1692 setupNode.appendChild(setupPreTest); | |
1693 setupNode.appendChild(setupPostTest); | |
1694 | |
1695 // <Metric> tag | |
1696 var Metric = root.createElement("Metric"); | |
1697 for (var i=0; i<this.metrics.length; i++) | |
1698 { | |
1699 var metricEnable = root.createElement("metricEnable"); | |
1700 metricEnable.textContent = this.metrics[i].enabled; | |
1701 Metric.appendChild(metricEnable); | |
1702 } | |
1703 setupNode.appendChild(Metric); | |
1704 | |
1705 // <interface> tag | |
1706 var CommonInterface = root.createElement("interface"); | |
1707 for (var i=0; i<this.commonInterface.options.length; i++) | |
1708 { | |
1709 var CIObj = this.commonInterface.options[i]; | |
1710 var CINode = root.createElement(CIObj.type); | |
1711 if (CIObj.type == "check") {CINode.setAttribute("name",CIObj.check);} | |
1712 else {CINode.setAttribute("name",CIObj.name);} | |
1713 CommonInterface.appendChild(CINode); | |
1714 } | |
1715 setupNode.appendChild(CommonInterface); | |
1716 | |
1717 root.getElementsByTagName("BrowserEvalProjectDocument")[0].appendChild(setupNode); | |
1718 // Time for the <audioHolder> tags | |
1719 for (var ahIndex = 0; ahIndex < this.audioHolders.length; ahIndex++) | |
1720 { | |
1721 var node = this.audioHolders[ahIndex].encode(root); | |
1722 root.getElementsByTagName("BrowserEvalProjectDocument")[0].appendChild(node); | |
1723 } | |
1724 return root; | 1711 return root; |
1725 }; | 1712 }; |
1726 | 1713 |
1727 this.prepostNode = function(type) { | 1714 this.surveyNode = function() { |
1728 this.type = type; | 1715 this.location = null; |
1729 this.options = []; | 1716 this.options = []; |
1717 this.schema = null; | |
1730 | 1718 |
1731 this.OptionNode = function() { | 1719 this.OptionNode = function() { |
1732 | |
1733 this.childOption = function() { | |
1734 this.type = 'option'; | |
1735 this.id = null; | |
1736 this.name = undefined; | |
1737 this.text = null; | |
1738 }; | |
1739 | |
1740 this.type = undefined; | 1720 this.type = undefined; |
1721 this.schema = undefined; | |
1741 this.id = undefined; | 1722 this.id = undefined; |
1742 this.mandatory = undefined; | 1723 this.mandatory = undefined; |
1743 this.question = undefined; | |
1744 this.statement = undefined; | 1724 this.statement = undefined; |
1745 this.boxsize = undefined; | 1725 this.boxsize = undefined; |
1746 this.options = []; | 1726 this.options = []; |
1747 this.min = undefined; | 1727 this.min = undefined; |
1748 this.max = undefined; | 1728 this.max = undefined; |
1749 this.step = undefined; | 1729 this.step = undefined; |
1750 | 1730 |
1751 this.decode = function(child) | 1731 this.decode = function(parent,child,schema) |
1752 { | 1732 { |
1753 this.type = child.nodeName; | 1733 this.schema = schema; |
1754 if (child.nodeName == "question") { | 1734 var attributeMap = schema.getElementsByTagName('attribute'); |
1755 this.id = child.id; | 1735 for (var i in attributeMap){ |
1756 this.mandatory; | 1736 if(isNaN(Number(i)) == true){break;} |
1757 if (child.getAttribute('mandatory') == "true") {this.mandatory = true;} | 1737 var attributeName = attributeMap[i].getAttribute('name') || attributeMap[i].getAttribute('ref'); |
1758 else {this.mandatory = false;} | 1738 var projectAttr = child.getAttribute(attributeName); |
1759 this.question = child.textContent; | 1739 projectAttr = parent.processAttribute(projectAttr,attributeMap[i]); |
1760 if (child.getAttribute('boxsize') == null) { | 1740 switch(typeof projectAttr) |
1761 this.boxsize = 'normal'; | 1741 { |
1762 } else { | 1742 case "number": |
1763 this.boxsize = child.getAttribute('boxsize'); | 1743 case "boolean": |
1744 eval('this.'+attributeName+' = '+projectAttr); | |
1745 break; | |
1746 case "string": | |
1747 eval('this.'+attributeName+' = "'+projectAttr+'"'); | |
1748 break; | |
1764 } | 1749 } |
1765 } else if (child.nodeName == "statement") { | 1750 } |
1766 this.statement = child.textContent; | 1751 this.statement = child.getElementsByTagName('statement')[0].textContent; |
1767 } else if (child.nodeName == "checkbox" || child.nodeName == "radio") { | 1752 if (this.type == "checkbox" || this.type == "radio") { |
1768 var element = child.firstElementChild; | 1753 var children = child.getElementsByTagName('option'); |
1769 this.id = child.id; | 1754 if (children.length == null) { |
1770 if (element == null) { | |
1771 console.log('Malformed' +child.nodeName+ 'entry'); | 1755 console.log('Malformed' +child.nodeName+ 'entry'); |
1772 this.statement = 'Malformed' +child.nodeName+ 'entry'; | 1756 this.statement = 'Malformed' +child.nodeName+ 'entry'; |
1773 this.type = 'statement'; | 1757 this.type = 'statement'; |
1774 } else { | 1758 } else { |
1775 this.options = []; | 1759 this.options = []; |
1776 while (element != null) { | 1760 for (var i in children) |
1777 if (element.nodeName == 'statement' && this.statement == undefined){ | 1761 { |
1778 this.statement = element.textContent; | 1762 if (isNaN(Number(i))==true){break;} |
1779 } else if (element.nodeName == 'option') { | 1763 this.options.push({ |
1780 var node = new this.childOption(); | 1764 name: children[i].getAttribute('name'), |
1781 if(element.getAttribute('id') != null) { | 1765 text: children[i].textContent |
1782 console.log(child.nodeName + ' node attribute id is deprecated, use name instead'); | 1766 }); |
1783 node.name = element.id; | |
1784 } | |
1785 node.name = element.getAttribute('name'); | |
1786 node.text = element.textContent; | |
1787 this.options.push(node); | |
1788 } | |
1789 element = element.nextElementSibling; | |
1790 } | 1767 } |
1791 } | 1768 } |
1792 } else if (child.nodeName == "number") { | |
1793 this.statement = child.textContent; | |
1794 this.id = child.id; | |
1795 this.min = child.getAttribute('min'); | |
1796 this.max = child.getAttribute('max'); | |
1797 this.step = child.getAttribute('step'); | |
1798 } | 1769 } |
1799 }; | 1770 }; |
1800 | 1771 |
1801 this.exportXML = function(root) | 1772 this.exportXML = function(root) |
1802 { | 1773 { |
1803 var node = root.createElement(this.type); | 1774 var node = root.createElement('surveyelement'); |
1775 node.setAttribute('type',this.type); | |
1776 var statement = root.createElement('statement'); | |
1777 statement.textContent = this.statement; | |
1778 node.appendChild(statement); | |
1804 switch(this.type) | 1779 switch(this.type) |
1805 { | 1780 { |
1806 case "statement": | 1781 case "statement": |
1807 node.textContent = this.statement; | |
1808 break; | 1782 break; |
1809 case "question": | 1783 case "question": |
1810 node.id = this.id; | 1784 node.id = this.id; |
1811 node.setAttribute("mandatory",this.mandatory); | 1785 node.setAttribute("mandatory",this.mandatory); |
1812 node.setAttribute("boxsize",this.boxsize); | 1786 node.setAttribute("boxsize",this.boxsize); |
1813 node.textContent = this.question; | |
1814 break; | 1787 break; |
1815 case "number": | 1788 case "number": |
1816 node.id = this.id; | 1789 node.id = this.id; |
1817 node.setAttribute("mandatory",this.mandatory); | 1790 node.setAttribute("mandatory",this.mandatory); |
1818 node.setAttribute("min", this.min); | 1791 node.setAttribute("min", this.min); |
1819 node.setAttribute("max", this.max); | 1792 node.setAttribute("max", this.max); |
1820 node.setAttribute("step", this.step); | 1793 node.setAttribute("step", this.step); |
1821 node.textContent = this.statement; | |
1822 break; | 1794 break; |
1823 case "checkbox": | 1795 case "checkbox": |
1824 node.id = this.id; | |
1825 var statement = root.createElement("statement"); | |
1826 statement.textContent = this.statement; | |
1827 node.appendChild(statement); | |
1828 for (var i=0; i<this.options.length; i++) | |
1829 { | |
1830 var option = this.options[i]; | |
1831 var optionNode = root.createElement("option"); | |
1832 optionNode.id = option.id; | |
1833 optionNode.textContent = option.text; | |
1834 node.appendChild(optionNode); | |
1835 } | |
1836 break; | |
1837 case "radio": | 1796 case "radio": |
1838 node.id = this.id; | 1797 node.id = this.id; |
1839 var statement = root.createElement("statement"); | |
1840 statement.textContent = this.statement; | |
1841 node.appendChild(statement); | |
1842 for (var i=0; i<this.options.length; i++) | 1798 for (var i=0; i<this.options.length; i++) |
1843 { | 1799 { |
1844 var option = this.options[i]; | 1800 var option = this.options[i]; |
1845 var optionNode = root.createElement("option"); | 1801 var optionNode = root.createElement("option"); |
1846 optionNode.setAttribute("name",option.name); | 1802 optionNode.setAttribute("name",option.name); |
1850 break; | 1806 break; |
1851 } | 1807 } |
1852 return node; | 1808 return node; |
1853 }; | 1809 }; |
1854 }; | 1810 }; |
1855 this.construct = function(Collection) | 1811 this.decode = function(parent,xml,schema) { |
1856 { | 1812 this.schema = schema; |
1857 if (Collection.childElementCount != 0) { | 1813 this.location = xml.getAttribute('location'); |
1858 var child = Collection.firstElementChild; | 1814 if (this.location == 'before'){this.location = 'pre';} |
1815 else if (this.location == 'after'){this.location = 'post';} | |
1816 var surveyentrySchema = schema.getElementsByTagName('element')[0]; | |
1817 for (var i in xml.children) | |
1818 { | |
1819 if(isNaN(Number(i))==true){break;} | |
1859 var node = new this.OptionNode(); | 1820 var node = new this.OptionNode(); |
1860 node.decode(child); | 1821 node.decode(parent,xml.children[i],surveyentrySchema); |
1861 this.options.push(node); | 1822 this.options.push(node); |
1862 while (child.nextElementSibling != null) { | |
1863 child = child.nextElementSibling; | |
1864 node = new this.OptionNode(); | |
1865 node.decode(child); | |
1866 this.options.push(node); | |
1867 } | |
1868 } | 1823 } |
1869 }; | 1824 }; |
1870 }; | 1825 this.encode = function(root) { |
1871 this.preTest = new this.prepostNode("pretest"); | 1826 var node = root.createElement('survey'); |
1872 this.postTest = new this.prepostNode("posttest"); | 1827 node.setAttribute('location',this.location); |
1873 | 1828 for (var i=0; i<this.options.length; i++) |
1874 this.metricNode = function(name) { | 1829 { |
1875 this.enabled = name; | 1830 node.appendChild(this.options[i].exportXML()); |
1876 }; | 1831 } |
1877 | 1832 return node; |
1878 this.audioHolderNode = function(parent) { | 1833 }; |
1879 this.type = 'audioHolder'; | 1834 }; |
1835 | |
1836 this.interfaceNode = function() | |
1837 { | |
1838 this.title = null; | |
1839 this.name = null; | |
1840 this.options = []; | |
1841 this.scales = []; | |
1842 this.schema = null; | |
1843 | |
1844 this.decode = function(parent,xml,schema) { | |
1845 this.schema = schema; | |
1846 this.name = xml.getAttribute('name'); | |
1847 var titleNode = xml.getElementsByTagName('title'); | |
1848 if (titleNode.length == 1) | |
1849 { | |
1850 this.title = titleNode[0].textContent; | |
1851 } | |
1852 var interfaceOptionNodes = xml.getElementsByTagName('interfaceoption'); | |
1853 // Extract interfaceoption node schema | |
1854 var interfaceOptionNodeSchema = schema.getElementsByTagName('element'); | |
1855 for (var i=0; i<interfaceOptionNodeSchema.length; i++) { | |
1856 if (interfaceOptionNodeSchema[i].getAttribute('name') == 'interfaceoption') { | |
1857 interfaceOptionNodeSchema = interfaceOptionNodeSchema[i]; | |
1858 break; | |
1859 } | |
1860 } | |
1861 var attributeMap = interfaceOptionNodeSchema.getElementsByTagName('attribute'); | |
1862 for (var i=0; i<interfaceOptionNodes.length; i++) | |
1863 { | |
1864 var ioNode = interfaceOptionNodes[i]; | |
1865 var option = {}; | |
1866 for (var j=0; j<attributeMap.length; j++) | |
1867 { | |
1868 var attributeName = attributeMap[j].getAttribute('name') || attributeMap[j].getAttribute('ref'); | |
1869 var projectAttr = ioNode.getAttribute(attributeName); | |
1870 projectAttr = parent.processAttribute(projectAttr,attributeMap[j]); | |
1871 switch(typeof projectAttr) | |
1872 { | |
1873 case "number": | |
1874 case "boolean": | |
1875 eval('option.'+attributeName+' = '+projectAttr); | |
1876 break; | |
1877 case "string": | |
1878 eval('option.'+attributeName+' = "'+projectAttr+'"'); | |
1879 break; | |
1880 } | |
1881 } | |
1882 this.options.push(option); | |
1883 } | |
1884 | |
1885 // Now the scales nodes | |
1886 var scaleParent = xml.getElementsByTagName('scales'); | |
1887 if (scaleParent.length == 1) { | |
1888 scaleParent = scaleParent[0]; | |
1889 for (var i=0; i<scaleParent.children.length; i++) { | |
1890 var child = scaleParent.children[i]; | |
1891 this.scales.push({ | |
1892 text: child.textContent, | |
1893 position: Number(child.getAttribute('position')) | |
1894 }); | |
1895 } | |
1896 } | |
1897 }; | |
1898 | |
1899 this.encode = function(root) { | |
1900 | |
1901 }; | |
1902 }; | |
1903 | |
1904 this.page = function() { | |
1880 this.presentedId = undefined; | 1905 this.presentedId = undefined; |
1881 this.id = undefined; | 1906 this.id = undefined; |
1882 this.hostURL = undefined; | 1907 this.hostURL = undefined; |
1883 this.sampleRate = undefined; | |
1884 this.randomiseOrder = undefined; | 1908 this.randomiseOrder = undefined; |
1885 this.loop = undefined; | 1909 this.loop = undefined; |
1886 this.elementComments = undefined; | 1910 this.showElementComments = undefined; |
1887 this.outsideReference = null; | 1911 this.outsideReference = null; |
1888 this.loudness = null; | 1912 this.loudness = null; |
1889 this.initialPosition = null; | 1913 this.preTest = null; |
1890 this.preTest = new parent.prepostNode("pretest"); | 1914 this.postTest = null; |
1891 this.postTest = new parent.prepostNode("pretest"); | |
1892 this.interfaces = []; | 1915 this.interfaces = []; |
1893 this.commentBoxPrefix = "Comment on track"; | 1916 this.commentBoxPrefix = "Comment on track"; |
1894 this.audioElements = []; | 1917 this.audioElements = []; |
1895 this.commentQuestions = []; | 1918 this.commentQuestions = []; |
1896 | 1919 this.schema = null; |
1897 this.decode = function(parent,xml) | 1920 |
1898 { | 1921 this.decode = function(parent,xml,schema) |
1899 this.presentedId = parent.audioHolders.length; | 1922 { |
1900 this.id = xml.id; | 1923 this.schema = schema; |
1901 this.hostURL = xml.getAttribute('hostURL'); | 1924 var attributeMap = this.schema.getElementsByTagName('attribute'); |
1902 this.sampleRate = xml.getAttribute('sampleRate'); | 1925 for (var i=0; i<attributeMap.length; i++) |
1903 if (xml.getAttribute('randomiseOrder') == "true") {this.randomiseOrder = true;} | 1926 { |
1904 else {this.randomiseOrder = false;} | 1927 var attributeName = attributeMap[i].getAttribute('name') || attributeMap[i].getAttribute('ref'); |
1905 this.repeatCount = xml.getAttribute('repeatCount'); | 1928 var projectAttr = xml.getAttribute(attributeName); |
1906 if (xml.getAttribute('loop') == 'true') {this.loop = true;} | 1929 projectAttr = parent.processAttribute(projectAttr,attributeMap[i]); |
1907 else {this.loop == false;} | 1930 switch(typeof projectAttr) |
1908 if (xml.getAttribute('elementComments') == "true") {this.elementComments = true;} | |
1909 else {this.elementComments = false;} | |
1910 if (typeof parent.loudness === "number") | |
1911 { | |
1912 this.loudness = parent.loudness; | |
1913 } | |
1914 if (typeof xml.getAttribute('initial-position') === "string") | |
1915 { | |
1916 var xmlInitialPosition = Number(xml.getAttribute('initial-position')); | |
1917 if (isNaN(xmlInitialPosition) == false) | |
1918 { | 1931 { |
1919 if (xmlInitialPosition > 1) | 1932 case "number": |
1920 { | 1933 case "boolean": |
1921 xmlInitialPosition /= 100; | 1934 eval('this.'+attributeName+' = '+projectAttr); |
1935 break; | |
1936 case "string": | |
1937 eval('this.'+attributeName+' = "'+projectAttr+'"'); | |
1938 break; | |
1939 } | |
1940 } | |
1941 | |
1942 // Get the Comment Box Prefix | |
1943 var CBP = xml.getElementsByTagName('commentboxprefix'); | |
1944 if (CBP.length != 0) { | |
1945 this.commentBoxPrefix = CBP[0].textContent; | |
1946 } | |
1947 | |
1948 // Now decode the interfaces | |
1949 var interfaceNode = xml.getElementsByTagName('interface'); | |
1950 for (var i=0; i<interfaceNode.length; i++) | |
1951 { | |
1952 var node = new parent.interfaceNode(); | |
1953 node.decode(this,interfaceNode[i],parent.schema.getElementsByName('interface')[1]); | |
1954 this.interfaces.push(node); | |
1955 } | |
1956 | |
1957 // Now process the survey node options | |
1958 var survey = xml.getElementsByTagName('survey'); | |
1959 var surveySchema = parent.schema.getElementsByName('survey')[0]; | |
1960 for (var i in survey) { | |
1961 if (isNaN(Number(i)) == true){break;} | |
1962 var location = survey[i].getAttribute('location'); | |
1963 if (location == 'pre' || location == 'before') | |
1964 { | |
1965 if (this.preTest != null){this.errors.push("Already a pre/before test survey defined! Ignoring second!!");} | |
1966 else { | |
1967 this.preTest = new parent.surveyNode(); | |
1968 this.preTest.decode(parent,survey[i],surveySchema); | |
1922 } | 1969 } |
1923 this.initialPosition = xmlInitialPosition; | 1970 } else if (location == 'post' || location == 'after') { |
1924 } | 1971 if (this.postTest != null){this.errors.push("Already a post/after test survey defined! Ignoring second!!");} |
1925 } | 1972 else { |
1926 if (xml.getAttribute('loudness') != null) | 1973 this.postTest = new parent.surveyNode(); |
1927 { | 1974 this.postTest.decode(parent,survey[i],surveySchema); |
1928 var XMLloudness = xml.getAttribute('loudness'); | 1975 } |
1929 if (isNaN(Number(XMLloudness)) == false) | 1976 } |
1930 { | |
1931 this.loudness = Number(XMLloudness); | |
1932 } | |
1933 } | |
1934 var setupPreTestNode = xml.getElementsByTagName('PreTest'); | |
1935 if (setupPreTestNode.length != 0) | |
1936 { | |
1937 setupPreTestNode = setupPreTestNode[0]; | |
1938 this.preTest.construct(setupPreTestNode); | |
1939 } | 1977 } |
1940 | 1978 |
1941 var setupPostTestNode = xml.getElementsByTagName('PostTest'); | 1979 // Now process the audioelement tags |
1942 if (setupPostTestNode.length != 0) | 1980 var audioElements = xml.getElementsByTagName('audioelement'); |
1943 { | 1981 var audioElementSchema = parent.schema.getElementsByName('audioelement')[0]; |
1944 setupPostTestNode = setupPostTestNode[0]; | 1982 for (var i=0; i<audioElements.length; i++) |
1945 this.postTest.construct(setupPostTestNode); | 1983 { |
1984 var node = new this.audioElementNode(); | |
1985 node.decode(this,audioElements[i],audioElementSchema); | |
1986 this.audioElements.push(node); | |
1946 } | 1987 } |
1947 | 1988 |
1948 var interfaceDOM = xml.getElementsByTagName('interface'); | 1989 // Now decode the commentquestions |
1949 for (var i=0; i<interfaceDOM.length; i++) { | 1990 var commentQuestions = xml.getElementsByTagName('commentquestion'); |
1950 var node = new this.interfaceNode(); | 1991 var commentQuestionSchema = parent.schema.getElementsByName('commentquestion')[0]; |
1951 node.decode(interfaceDOM[i]); | 1992 for (var i=0; i<commentQuestions.length; i++) |
1952 this.interfaces.push(node); | 1993 { |
1953 } | |
1954 this.commentBoxPrefix = xml.getElementsByTagName('commentBoxPrefix'); | |
1955 if (this.commentBoxPrefix.length != 0) { | |
1956 this.commentBoxPrefix = this.commentBoxPrefix[0].textContent; | |
1957 } else { | |
1958 this.commentBoxPrefix = "Comment on track"; | |
1959 } | |
1960 var audioElementsDOM = xml.getElementsByTagName('audioElements'); | |
1961 var outsideReferenceHolder = null; | |
1962 for (var i=0; i<audioElementsDOM.length; i++) { | |
1963 var node = new this.audioElementNode(); | |
1964 node.decode(this,audioElementsDOM[i]); | |
1965 if (audioElementsDOM[i].getAttribute('type') == 'outsidereference') { | |
1966 if (this.outsideReference == null) { | |
1967 outsideReferenceHolder = node; | |
1968 this.outsideReference = i; | |
1969 } else { | |
1970 console.log('Error only one audioelement can be of type outsidereference per audioholder'); | |
1971 this.audioElements.push(node); | |
1972 console.log('Element id '+audioElementsDOM[i].id+' made into normal node'); | |
1973 } | |
1974 } else { | |
1975 this.audioElements.push(node); | |
1976 } | |
1977 } | |
1978 | |
1979 if (this.randomiseOrder == true && typeof randomiseOrder === "function") | |
1980 { | |
1981 this.audioElements = randomiseOrder(this.audioElements); | |
1982 } | |
1983 if (outsideReferenceHolder != null) | |
1984 { | |
1985 this.audioElements.push(outsideReferenceHolder); | |
1986 this.outsideReference = this.audioElements.length-1; | |
1987 } | |
1988 | |
1989 | |
1990 var commentQuestionsDOM = xml.getElementsByTagName('CommentQuestion'); | |
1991 for (var i=0; i<commentQuestionsDOM.length; i++) { | |
1992 var node = new this.commentQuestionNode(); | 1994 var node = new this.commentQuestionNode(); |
1993 node.decode(commentQuestionsDOM[i]); | 1995 node.decode(parent,commentQuestions[i],commentQuestionSchema); |
1994 this.commentQuestions.push(node); | 1996 this.commentQuestions.push(node); |
1995 } | 1997 } |
1996 }; | 1998 }; |
1997 | 1999 |
1998 this.encode = function(root) | 2000 this.encode = function(root) |
2038 AHNode.appendChild(AHPreTest); | 2040 AHNode.appendChild(AHPreTest); |
2039 AHNode.appendChild(AHPostTest); | 2041 AHNode.appendChild(AHPostTest); |
2040 return AHNode; | 2042 return AHNode; |
2041 }; | 2043 }; |
2042 | 2044 |
2043 this.interfaceNode = function() { | 2045 this.commentQuestionNode = function() { |
2044 this.title = undefined; | 2046 this.id = null; |
2047 this.type = undefined; | |
2045 this.options = []; | 2048 this.options = []; |
2046 this.scale = []; | 2049 this.statement = undefined; |
2047 this.name = undefined; | 2050 this.schema = null; |
2048 this.decode = function(DOM) | 2051 this.decode = function(parent,xml,schema) |
2049 { | 2052 { |
2050 var title = DOM.getElementsByTagName('title'); | 2053 this.id = xml.id; |
2051 if (title.length == 0) {this.title = null;} | 2054 this.type = xml.getAttribute('type'); |
2052 else {this.title = title[0].textContent;} | 2055 this.statement = xml.getElementsByTagName('statement')[0].textContent; |
2053 var name = DOM.getAttribute("name"); | 2056 var optNodes = xml.getElementsByTagName('option'); |
2054 if (name != undefined) {this.name = name;} | 2057 for (var i=0; i<optNodes.length; i++) |
2055 this.options = parent.commonInterface.options; | 2058 { |
2056 var scale = DOM.getElementsByTagName('scale'); | 2059 var optNode = optNodes[i]; |
2057 this.scale = []; | 2060 this.options.push({ |
2058 for (var i=0; i<scale.length; i++) { | 2061 name: optNode.getAttribute('name'), |
2059 var arr = [null, null]; | 2062 text: optNode.textContent |
2060 arr[0] = scale[i].getAttribute('position'); | 2063 }); |
2061 arr[1] = scale[i].textContent; | |
2062 this.scale.push(arr); | |
2063 } | 2064 } |
2064 }; | 2065 }; |
2066 | |
2065 this.encode = function(root) | 2067 this.encode = function(root) |
2066 { | 2068 { |
2067 var node = root.createElement("interface"); | 2069 |
2068 if (this.title != undefined) | |
2069 { | |
2070 var title = root.createElement("title"); | |
2071 title.textContent = this.title; | |
2072 node.appendChild(title); | |
2073 } | |
2074 for (var i=0; i<this.options.length; i++) | |
2075 { | |
2076 var optionNode = root.createElement(this.options[i].type); | |
2077 if (this.options[i].type == "option") | |
2078 { | |
2079 optionNode.setAttribute("name",this.options[i].name); | |
2080 } else if (this.options[i].type == "check") { | |
2081 optionNode.setAttribute("check",this.options[i].check); | |
2082 } else if (this.options[i].type == "scalerange") { | |
2083 optionNode.setAttribute("min",this.options[i].min*100); | |
2084 optionNode.setAttribute("max",this.options[i].max*100); | |
2085 } | |
2086 node.appendChild(optionNode); | |
2087 } | |
2088 for (var i=0; i<this.scale.length; i++) { | |
2089 var scale = root.createElement("scale"); | |
2090 scale.setAttribute("position",this.scale[i][0]); | |
2091 scale.textContent = this.scale[i][1]; | |
2092 node.appendChild(scale); | |
2093 } | |
2094 return node; | |
2095 }; | 2070 }; |
2096 }; | 2071 }; |
2097 | 2072 |
2098 this.audioElementNode = function() { | 2073 this.audioElementNode = function() { |
2099 this.url = null; | 2074 this.url = null; |
2100 this.id = null; | 2075 this.id = null; |
2101 this.parent = null; | 2076 this.parent = null; |
2102 this.type = "normal"; | 2077 this.type = null; |
2103 this.marker = false; | 2078 this.marker = false; |
2104 this.enforce = false; | 2079 this.enforce = false; |
2105 this.gain = 1.0; | 2080 this.gain = 1.0; |
2106 this.decode = function(parent,xml) | 2081 this.schema = null; |
2107 { | 2082 this.parent = null; |
2108 this.url = xml.getAttribute('url'); | 2083 this.decode = function(parent,xml,schema) |
2109 this.id = xml.id; | 2084 { |
2085 this.schema = schema; | |
2110 this.parent = parent; | 2086 this.parent = parent; |
2111 this.type = xml.getAttribute('type'); | 2087 var attributeMap = this.schema.getElementsByTagName('attribute'); |
2112 var gain = xml.getAttribute('gain'); | 2088 for (var i=0; i<attributeMap.length; i++) |
2113 if (isNaN(gain) == false && gain != null) | |
2114 { | 2089 { |
2115 this.gain = decibelToLinear(Number(gain)); | 2090 var attributeName = attributeMap[i].getAttribute('name') || attributeMap[i].getAttribute('ref'); |
2116 } | 2091 var projectAttr = xml.getAttribute(attributeName); |
2117 if (this.type == null) {this.type = "normal";} | 2092 projectAttr = specification.processAttribute(projectAttr,attributeMap[i]); |
2118 if (this.type == 'anchor') {this.anchor = true;} | 2093 switch(typeof projectAttr) |
2119 else {this.anchor = false;} | |
2120 if (this.type == 'reference') {this.reference = true;} | |
2121 else {this.reference = false;} | |
2122 if (this.anchor == true || this.reference == true) | |
2123 { | |
2124 this.marker = xml.getAttribute('marker'); | |
2125 if (this.marker != undefined) | |
2126 { | 2094 { |
2127 this.marker = Number(this.marker); | 2095 case "number": |
2128 if (isNaN(this.marker) == false) | 2096 case "boolean": |
2129 { | 2097 eval('this.'+attributeName+' = '+projectAttr); |
2130 if (this.marker > 1) | 2098 break; |
2131 { this.marker /= 100.0;} | 2099 case "string": |
2132 if (this.marker >= 0 && this.marker <= 1) | 2100 eval('this.'+attributeName+' = "'+projectAttr+'"'); |
2133 { | 2101 break; |
2134 this.enforce = true; | |
2135 return; | |
2136 } else { | |
2137 console.log("ERROR - Marker of audioElement "+this.id+" is not between 0 and 1 (float) or 0 and 100 (integer)!"); | |
2138 console.log("ERROR - Marker not enforced!"); | |
2139 } | |
2140 } else { | |
2141 console.log("ERROR - Marker of audioElement "+this.id+" is not a number!"); | |
2142 console.log("ERROR - Marker not enforced!"); | |
2143 } | |
2144 } | 2102 } |
2145 } | 2103 } |
2104 | |
2146 }; | 2105 }; |
2147 this.encode = function(root) | 2106 this.encode = function(root) |
2148 { | 2107 { |
2149 var AENode = root.createElement("audioElements"); | 2108 var AENode = root.createElement("audioElements"); |
2150 AENode.id = this.id; | 2109 AENode.id = this.id; |
2156 AENode.setAttribute("marker",this.marker*100); | 2115 AENode.setAttribute("marker",this.marker*100); |
2157 } | 2116 } |
2158 return AENode; | 2117 return AENode; |
2159 }; | 2118 }; |
2160 }; | 2119 }; |
2161 | |
2162 this.commentQuestionNode = function(xml) { | |
2163 this.id = null; | |
2164 this.type = undefined; | |
2165 this.question = undefined; | |
2166 this.options = []; | |
2167 this.statement = undefined; | |
2168 | |
2169 this.childOption = function() { | |
2170 this.type = 'option'; | |
2171 this.name = null; | |
2172 this.text = null; | |
2173 }; | |
2174 this.exportXML = function(root) | |
2175 { | |
2176 var CQNode = root.createElement("CommentQuestion"); | |
2177 CQNode.id = this.id; | |
2178 CQNode.setAttribute("type",this.type); | |
2179 switch(this.type) | |
2180 { | |
2181 case "text": | |
2182 CQNode.textContent = this.question; | |
2183 break; | |
2184 case "radio": | |
2185 var statement = root.createElement("statement"); | |
2186 statement.textContent = this.statement; | |
2187 CQNode.appendChild(statement); | |
2188 for (var i=0; i<this.options.length; i++) | |
2189 { | |
2190 var optionNode = root.createElement("option"); | |
2191 optionNode.setAttribute("name",this.options[i].name); | |
2192 optionNode.textContent = this.options[i].text; | |
2193 CQNode.appendChild(optionNode); | |
2194 } | |
2195 break; | |
2196 case "checkbox": | |
2197 var statement = root.createElement("statement"); | |
2198 statement.textContent = this.statement; | |
2199 CQNode.appendChild(statement); | |
2200 for (var i=0; i<this.options.length; i++) | |
2201 { | |
2202 var optionNode = root.createElement("option"); | |
2203 optionNode.setAttribute("name",this.options[i].name); | |
2204 optionNode.textContent = this.options[i].text; | |
2205 CQNode.appendChild(optionNode); | |
2206 } | |
2207 break; | |
2208 } | |
2209 return CQNode; | |
2210 }; | |
2211 this.decode = function(xml) { | |
2212 this.id = xml.id; | |
2213 if (xml.getAttribute('mandatory') == 'true') {this.mandatory = true;} | |
2214 else {this.mandatory = false;} | |
2215 this.type = xml.getAttribute('type'); | |
2216 if (this.type == undefined) {this.type = 'text';} | |
2217 switch (this.type) { | |
2218 case 'text': | |
2219 this.question = xml.textContent; | |
2220 break; | |
2221 case 'radio': | |
2222 var child = xml.firstElementChild; | |
2223 this.options = []; | |
2224 while (child != undefined) { | |
2225 if (child.nodeName == 'statement' && this.statement == undefined) { | |
2226 this.statement = child.textContent; | |
2227 } else if (child.nodeName == 'option') { | |
2228 var node = new this.childOption(); | |
2229 node.name = child.getAttribute('name'); | |
2230 node.text = child.textContent; | |
2231 this.options.push(node); | |
2232 } | |
2233 child = child.nextElementSibling; | |
2234 } | |
2235 break; | |
2236 case 'checkbox': | |
2237 var child = xml.firstElementChild; | |
2238 this.options = []; | |
2239 while (child != undefined) { | |
2240 if (child.nodeName == 'statement' && this.statement == undefined) { | |
2241 this.statement = child.textContent; | |
2242 } else if (child.nodeName == 'option') { | |
2243 var node = new this.childOption(); | |
2244 node.name = child.getAttribute('name'); | |
2245 node.text = child.textContent; | |
2246 this.options.push(node); | |
2247 } | |
2248 child = child.nextElementSibling; | |
2249 } | |
2250 break; | |
2251 } | |
2252 }; | |
2253 }; | |
2254 }; | 2120 }; |
2255 } | 2121 } |
2256 | 2122 |
2257 function Interface(specificationObject) { | 2123 function Interface(specificationObject) { |
2258 // This handles the bindings between the interface and the audioEngineContext; | 2124 // This handles the bindings between the interface and the audioEngineContext; |
2259 this.specification = specificationObject; | 2125 this.specification = specificationObject; |
2260 this.insertPoint = document.getElementById("topLevelBody"); | 2126 this.insertPoint = document.getElementById("topLevelBody"); |
2261 | 2127 |
2262 this.newPage = function(audioHolderObject) | 2128 this.newPage = function(audioHolderObject,store) |
2263 { | 2129 { |
2264 audioEngineContext.newTestPage(); | 2130 audioEngineContext.newTestPage(store); |
2265 /// CHECK FOR SAMPLE RATE COMPATIBILITY | 2131 /// CHECK FOR SAMPLE RATE COMPATIBILITY |
2266 if (audioHolderObject.sampleRate != undefined) { | 2132 if (audioHolderObject.sampleRate != undefined) { |
2267 if (Number(audioHolderObject.sampleRate) != audioContext.sampleRate) { | 2133 if (Number(audioHolderObject.sampleRate) != audioContext.sampleRate) { |
2268 var errStr = 'Sample rates do not match! Requested '+Number(audioHolderObject.sampleRate)+', got '+audioContext.sampleRate+'. Please set the sample rate to match before completing this test.'; | 2134 var errStr = 'Sample rates do not match! Requested '+Number(audioHolderObject.sampleRate)+', got '+audioContext.sampleRate+'. Please set the sample rate to match before completing this test.'; |
2269 alert(errStr); | 2135 alert(errStr); |
2274 audioEngineContext.loopPlayback = audioHolderObject.loop; | 2140 audioEngineContext.loopPlayback = audioHolderObject.loop; |
2275 // Delete any previous audioObjects associated with the audioEngine | 2141 // Delete any previous audioObjects associated with the audioEngine |
2276 audioEngineContext.audioObjects = []; | 2142 audioEngineContext.audioObjects = []; |
2277 interfaceContext.deleteCommentBoxes(); | 2143 interfaceContext.deleteCommentBoxes(); |
2278 interfaceContext.deleteCommentQuestions(); | 2144 interfaceContext.deleteCommentQuestions(); |
2279 loadTest(audioHolderObject); | 2145 loadTest(audioHolderObject,store); |
2280 }; | 2146 }; |
2281 | 2147 |
2282 // Bounded by interface!! | 2148 // Bounded by interface!! |
2283 // Interface object MUST have an exportXMLDOM method which returns the various DOM levels | 2149 // Interface object MUST have an exportXMLDOM method which returns the various DOM levels |
2284 // For example, APE returns the slider position normalised in a <value> tag. | 2150 // For example, APE returns the slider position normalised in a <value> tag. |
2380 // Create document objects to hold the comment boxes | 2246 // Create document objects to hold the comment boxes |
2381 this.holder = document.createElement('div'); | 2247 this.holder = document.createElement('div'); |
2382 this.holder.className = 'comment-div'; | 2248 this.holder.className = 'comment-div'; |
2383 // Create a string next to each comment asking for a comment | 2249 // Create a string next to each comment asking for a comment |
2384 this.string = document.createElement('span'); | 2250 this.string = document.createElement('span'); |
2385 this.string.innerHTML = commentQuestion.question; | 2251 this.string.innerHTML = commentQuestion.statement; |
2386 // Create the HTML5 comment box 'textarea' | 2252 // Create the HTML5 comment box 'textarea' |
2387 this.textArea = document.createElement('textarea'); | 2253 this.textArea = document.createElement('textarea'); |
2388 this.textArea.rows = '4'; | 2254 this.textArea.rows = '4'; |
2389 this.textArea.cols = '100'; | 2255 this.textArea.cols = '100'; |
2390 this.textArea.className = 'trackComment'; | 2256 this.textArea.className = 'trackComment'; |
2440 this.span.style.marginLeft = '12px'; | 2306 this.span.style.marginLeft = '12px'; |
2441 this.span.align = 'center'; | 2307 this.span.align = 'center'; |
2442 this.span.style.marginTop = '15px'; | 2308 this.span.style.marginTop = '15px'; |
2443 | 2309 |
2444 var optCount = commentQuestion.options.length; | 2310 var optCount = commentQuestion.options.length; |
2445 for (var i=0; i<optCount; i++) | 2311 for (var optNode of commentQuestion.options) |
2446 { | 2312 { |
2447 var div = document.createElement('div'); | 2313 var div = document.createElement('div'); |
2448 div.style.width = '80px'; | 2314 div.style.width = '80px'; |
2449 div.style.float = 'left'; | 2315 div.style.float = 'left'; |
2450 var input = document.createElement('input'); | 2316 var input = document.createElement('input'); |
2451 input.type = 'radio'; | 2317 input.type = 'radio'; |
2452 input.name = commentQuestion.id; | 2318 input.name = commentQuestion.id; |
2453 input.setAttribute('setvalue',commentQuestion.options[i].name); | 2319 input.setAttribute('setvalue',optNode.name); |
2454 input.className = 'comment-radio'; | 2320 input.className = 'comment-radio'; |
2455 div.appendChild(input); | 2321 div.appendChild(input); |
2456 this.inputs.appendChild(div); | 2322 this.inputs.appendChild(div); |
2457 | 2323 |
2458 | 2324 |
2459 div = document.createElement('div'); | 2325 div = document.createElement('div'); |
2460 div.style.width = '80px'; | 2326 div.style.width = '80px'; |
2461 div.style.float = 'left'; | 2327 div.style.float = 'left'; |
2462 div.align = 'center'; | 2328 div.align = 'center'; |
2463 var span = document.createElement('span'); | 2329 var span = document.createElement('span'); |
2464 span.textContent = commentQuestion.options[i].text; | 2330 span.textContent = optNode.text; |
2465 span.className = 'comment-radio-span'; | 2331 span.className = 'comment-radio-span'; |
2466 div.appendChild(span); | 2332 div.appendChild(span); |
2467 this.span.appendChild(div); | 2333 this.span.appendChild(div); |
2468 this.options.push(input); | 2334 this.options.push(input); |
2469 } | 2335 } |
2660 this.commentBoxes = []; | 2526 this.commentBoxes = []; |
2661 }; | 2527 }; |
2662 | 2528 |
2663 this.createCommentQuestion = function(element) { | 2529 this.createCommentQuestion = function(element) { |
2664 var node; | 2530 var node; |
2665 if (element.type == 'text') { | 2531 if (element.type == 'question') { |
2666 node = new this.commentBox(element); | 2532 node = new this.commentBox(element); |
2667 } else if (element.type == 'radio') { | 2533 } else if (element.type == 'radio') { |
2668 node = new this.radioBox(element); | 2534 node = new this.radioBox(element); |
2669 } else if (element.type == 'checkbox') { | 2535 } else if (element.type == 'checkbox') { |
2670 node = new this.checkboxBox(element); | 2536 node = new this.checkboxBox(element); |
2769 | 2635 |
2770 // Global Checkers | 2636 // Global Checkers |
2771 // These functions will help enforce the checkers | 2637 // These functions will help enforce the checkers |
2772 this.checkHiddenAnchor = function() | 2638 this.checkHiddenAnchor = function() |
2773 { | 2639 { |
2774 var audioHolder = testState.currentStateMap[testState.currentIndex]; | 2640 for (var ao of audioEngineContext.audioObjects) |
2775 if (audioHolder.anchorId != null) | 2641 { |
2776 { | 2642 if (ao.specification.type == "anchor") |
2777 var audioObject = audioEngineContext.audioObjects[audioHolder.anchorId]; | 2643 { |
2778 if (audioObject.interfaceDOM.getValue() > audioObject.specification.marker && audioObject.interfaceDOM.enforce == true) | 2644 if (ao.interfaceDOM.getValue() > ao.specification.marker && ao.interfaceDOM.enforce == true) { |
2779 { | 2645 // Anchor is not set below |
2780 // Anchor is not set below | 2646 console.log('Anchor node not below marker value'); |
2781 console.log('Anchor node not below marker value'); | 2647 alert('Please keep listening'); |
2782 alert('Please keep listening'); | 2648 return false; |
2783 return false; | 2649 } |
2784 } | 2650 } |
2785 } | 2651 } |
2786 return true; | 2652 return true; |
2787 }; | 2653 }; |
2788 | 2654 |
2789 this.checkHiddenReference = function() | 2655 this.checkHiddenReference = function() |
2790 { | 2656 { |
2791 var audioHolder = testState.currentStateMap[testState.currentIndex]; | 2657 for (var ao of audioEngineContext.audioObjects) |
2792 if (audioHolder.referenceId != null) | 2658 { |
2793 { | 2659 if (ao.specification.type == "reference") |
2794 var audioObject = audioEngineContext.audioObjects[audioHolder.referenceId]; | 2660 { |
2795 if (audioObject.interfaceDOM.getValue() < audioObject.specification.marker && audioObject.interfaceDOM.enforce == true) | 2661 if (ao.interfaceDOM.getValue() < ao.specification.marker && ao.interfaceDOM.enforce == true) { |
2796 { | 2662 // Anchor is not set below |
2797 // Anchor is not set below | 2663 console.log('Reference node not below marker value'); |
2798 console.log('Reference node not above marker value'); | 2664 alert('Please keep listening'); |
2799 alert('Please keep listening'); | 2665 return false; |
2800 return false; | 2666 } |
2801 } | 2667 } |
2802 } | 2668 } |
2803 return true; | 2669 return true; |
2804 }; | 2670 }; |
2805 | 2671 |
2914 alert(str); | 2780 alert(str); |
2915 console.log(str); | 2781 console.log(str); |
2916 return false; | 2782 return false; |
2917 }; | 2783 }; |
2918 } | 2784 } |
2785 | |
2786 function Storage() | |
2787 { | |
2788 // Holds results in XML format until ready for collection | |
2789 this.globalPreTest = null; | |
2790 this.globalPostTest = null; | |
2791 this.testPages = []; | |
2792 this.document = document.implementation.createDocument(null,"waetresult"); | |
2793 this.root = this.document.children[0]; | |
2794 this.state = 0; | |
2795 | |
2796 this.initialise = function() | |
2797 { | |
2798 this.globalPreTest = new this.surveyNode(this,this.root,specification.preTest); | |
2799 this.globalPostTest = new this.surveyNode(this,this.root,specification.postTest); | |
2800 }; | |
2801 | |
2802 this.createTestPageStore = function(specification) | |
2803 { | |
2804 var store = new this.pageNode(this,specification); | |
2805 this.testPages.push(store); | |
2806 return this.testPages[this.testPages.length-1]; | |
2807 }; | |
2808 | |
2809 this.surveyNode = function(parent,root,specification) | |
2810 { | |
2811 this.specification = specification; | |
2812 this.parent = parent; | |
2813 this.XMLDOM = this.parent.document.createElement('survey'); | |
2814 this.XMLDOM.setAttribute('location',this.specification.location); | |
2815 for (var optNode of this.specification.options) | |
2816 { | |
2817 if (optNode.type != 'statement') | |
2818 { | |
2819 var node = this.parent.document.createElement('surveyresult'); | |
2820 node.id = optNode.id; | |
2821 node.setAttribute('type',optNode.type); | |
2822 this.XMLDOM.appendChild(node); | |
2823 } | |
2824 } | |
2825 root.appendChild(this.XMLDOM); | |
2826 | |
2827 this.postResult = function(node) | |
2828 { | |
2829 // From popup: node is the popupOption node containing both spec. and results | |
2830 // ID is the position | |
2831 if (node.specification.type == 'statement'){return;} | |
2832 var surveyresult = this.parent.document.getElementById(node.specification.id); | |
2833 switch(node.specification.type) | |
2834 { | |
2835 case "number": | |
2836 case "question": | |
2837 var child = this.parent.document.createElement('response'); | |
2838 child.textContent = node.response; | |
2839 surveyresult.appendChild(child); | |
2840 break; | |
2841 case "radio": | |
2842 var child = this.parent.document.createElement('response'); | |
2843 child.setAttribute('name',node.response.name); | |
2844 child.textContent = node.response.text; | |
2845 surveyresult.appendChild(child); | |
2846 break; | |
2847 case "checkbox": | |
2848 for (var i=0; i<node.response.length; i++) | |
2849 { | |
2850 var checkNode = this.parent.document.createElement('response'); | |
2851 child.setAttribute('name',node.response.name); | |
2852 child.setAttribute('checked',node.response.checked); | |
2853 child.textContent = node.response.text; | |
2854 surveyresult.appendChild(child); | |
2855 } | |
2856 break; | |
2857 } | |
2858 }; | |
2859 }; | |
2860 | |
2861 this.pageNode = function(parent,specification) | |
2862 { | |
2863 // Create one store per test page | |
2864 this.specification = specification; | |
2865 this.parent = parent; | |
2866 this.XMLDOM = this.parent.document.createElement('page'); | |
2867 this.XMLDOM.setAttribute('id',specification.id); | |
2868 this.XMLDOM.setAttribute('presentedId',specification.presentedId); | |
2869 this.preTest = new this.parent.surveyNode(parent,this.XMLDOM,specification.preTest); | |
2870 this.postTest = new this.parent.surveyNode(parent,this.XMLDOM,specification.postTest); | |
2871 | |
2872 // Add any page metrics | |
2873 var page_metric = this.parent.document.createElement('metric'); | |
2874 this.XMLDOM.appendChild(page_metric); | |
2875 | |
2876 // Add the audioelement | |
2877 for (var element of this.specification.audioElements) | |
2878 { | |
2879 var aeNode = this.parent.document.createElement('audioelement'); | |
2880 aeNode.id = element.id; | |
2881 aeNode.setAttribute('type',element.type); | |
2882 aeNode.setAttribute('url', element.url); | |
2883 aeNode.setAttribute('gain', element.gain); | |
2884 if (element.type == 'anchor' || element.type == 'reference') | |
2885 { | |
2886 if (element.marker > 0) | |
2887 { | |
2888 aeNode.setAttribute('marker',element.marker); | |
2889 } | |
2890 } | |
2891 var ae_metric = this.parent.document.createElement('metric'); | |
2892 aeNode.appendChild(ae_metric); | |
2893 this.XMLDOM.appendChild(aeNode); | |
2894 } | |
2895 | |
2896 // Add any commentQuestions | |
2897 for (var element of this.specification.commentQuestions) | |
2898 { | |
2899 var cqNode = this.parent.document.createElement('commentquestion'); | |
2900 cqNode.id = element.id; | |
2901 cqNode.setAttribute('type',element.type); | |
2902 var statement = this.parent.document.createElement('statement'); | |
2903 statement.textContent = cqNode.statement; | |
2904 cqNode.appendChild(statement); | |
2905 var response = this.parent.document.createElement('response'); | |
2906 cqNode.appendChild(response); | |
2907 this.XMLDOM.appendChild(cqNode); | |
2908 } | |
2909 | |
2910 this.parent.root.appendChild(this.XMLDOM); | |
2911 }; | |
2912 this.finish = function() | |
2913 { | |
2914 if (this.state == 0) | |
2915 { | |
2916 var projectDocument = specification.projectXML; | |
2917 projectDocument.setAttribute('file-name',url); | |
2918 this.root.appendChild(projectDocument); | |
2919 this.root.appendChild(returnDateNode()); | |
2920 this.root.appendChild(interfaceContext.returnNavigator()); | |
2921 } | |
2922 this.state = 1; | |
2923 return this.root; | |
2924 }; | |
2925 } |