Mercurial > hg > webaudioevaluationtool
comparison js/core.js @ 2720:a6a0d2b786af
Merge branch 'vnext' into hot_fix
# Conflicts:
# js/core.js
author | Nicholas Jillings <n.g.r.jillings@se14.qmul.ac.uk> |
---|---|
date | Fri, 14 Apr 2017 16:28:19 +0100 |
parents | 04cc7a27ae64 d30222a70f2d |
children | a087cb7b5972 |
comparison
equal
deleted
inserted
replaced
2676:04cc7a27ae64 | 2720:a6a0d2b786af |
---|---|
2 * core.js | 2 * core.js |
3 * | 3 * |
4 * Main script to run, calls all other core functions and manages loading/store to backend. | 4 * Main script to run, calls all other core functions and manages loading/store to backend. |
5 * Also contains all global variables. | 5 * Also contains all global variables. |
6 */ | 6 */ |
7 | |
8 /*globals window, document, XMLDocument, Element, XMLHttpRequest, DOMParser, console, Blob, $, Promise, navigator */ | |
9 /*globals AudioBuffer, AudioBufferSourceNode */ | |
10 /*globals Specification, calculateLoudness, WAVE, validateXML, showdown, pageXMLSave, loadTest, resizeWindow */ | |
7 | 11 |
8 /* create the web audio API context and store in audioContext*/ | 12 /* create the web audio API context and store in audioContext*/ |
9 var audioContext; // Hold the browser web audio API | 13 var audioContext; // Hold the browser web audio API |
10 var projectXML; // Hold the parsed setup XML | 14 var projectXML; // Hold the parsed setup XML |
11 var schemaXSD; // Hold the parsed schema XSD | 15 var schemaXSD; // Hold the parsed schema XSD |
46 // element nodes must have a name and therefore can pull the schema node | 50 // element nodes must have a name and therefore can pull the schema node |
47 XMLDocument.prototype.getAllElementsByName = function (name) { | 51 XMLDocument.prototype.getAllElementsByName = function (name) { |
48 name = String(name); | 52 name = String(name); |
49 var selected = this.documentElement.getAllElementsByName(name); | 53 var selected = this.documentElement.getAllElementsByName(name); |
50 return selected; | 54 return selected; |
51 } | 55 }; |
52 | 56 |
53 Element.prototype.getAllElementsByName = function (name) { | 57 Element.prototype.getAllElementsByName = function (name) { |
54 name = String(name); | 58 name = String(name); |
55 var selected = []; | 59 var selected = []; |
56 var node = this.firstElementChild; | 60 var node = this.firstElementChild; |
57 while (node != null) { | 61 while (node !== null) { |
58 if (node.getAttribute('name') == name) { | 62 if (node.getAttribute('name') == name) { |
59 selected.push(node); | 63 selected.push(node); |
60 } | 64 } |
61 if (node.childElementCount > 0) { | 65 if (node.childElementCount > 0) { |
62 selected = selected.concat(node.getAllElementsByName(name)); | 66 selected = selected.concat(node.getAllElementsByName(name)); |
63 } | 67 } |
64 node = node.nextElementSibling; | 68 node = node.nextElementSibling; |
65 } | 69 } |
66 return selected; | 70 return selected; |
67 } | 71 }; |
68 | 72 |
69 XMLDocument.prototype.getAllElementsByTagName = function (name) { | 73 XMLDocument.prototype.getAllElementsByTagName = function (name) { |
70 name = String(name); | 74 name = String(name); |
71 var selected = this.documentElement.getAllElementsByTagName(name); | 75 var selected = this.documentElement.getAllElementsByTagName(name); |
72 return selected; | 76 return selected; |
73 } | 77 }; |
74 | 78 |
75 Element.prototype.getAllElementsByTagName = function (name) { | 79 Element.prototype.getAllElementsByTagName = function (name) { |
76 name = String(name); | 80 name = String(name); |
77 var selected = []; | 81 var selected = []; |
78 var node = this.firstElementChild; | 82 var node = this.firstElementChild; |
79 while (node != null) { | 83 while (node !== null) { |
80 if (node.nodeName == name) { | 84 if (node.nodeName == name) { |
81 selected.push(node); | 85 selected.push(node); |
82 } | 86 } |
83 if (node.childElementCount > 0) { | 87 if (node.childElementCount > 0) { |
84 selected = selected.concat(node.getAllElementsByTagName(name)); | 88 selected = selected.concat(node.getAllElementsByTagName(name)); |
85 } | 89 } |
86 node = node.nextElementSibling; | 90 node = node.nextElementSibling; |
87 } | 91 } |
88 return selected; | 92 return selected; |
89 } | 93 }; |
90 | 94 |
91 // Firefox does not have an XMLDocument.prototype.getElementsByName | 95 // Firefox does not have an XMLDocument.prototype.getElementsByName |
92 if (typeof XMLDocument.prototype.getElementsByName != "function") { | 96 if (typeof XMLDocument.prototype.getElementsByName != "function") { |
93 XMLDocument.prototype.getElementsByName = function (name) { | 97 XMLDocument.prototype.getElementsByName = function (name) { |
94 name = String(name); | 98 name = String(name); |
95 var node = this.documentElement.firstElementChild; | 99 var node = this.documentElement.firstElementChild; |
96 var selected = []; | 100 var selected = []; |
97 while (node != null) { | 101 while (node !== null) { |
98 if (node.getAttribute('name') == name) { | 102 if (node.getAttribute('name') == name) { |
99 selected.push(node); | 103 selected.push(node); |
100 } | 104 } |
101 node = node.nextElementSibling; | 105 node = node.nextElementSibling; |
102 } | 106 } |
103 return selected; | 107 return selected; |
104 } | 108 }; |
105 } | 109 } |
106 | 110 |
107 var check_dependancies = function () { | 111 var check_dependancies = function () { |
108 // This will check for the data dependancies | 112 // This will check for the data dependancies |
109 if (typeof (jQuery) != "function") { | 113 if (typeof (jQuery) != "function") { |
120 } | 124 } |
121 if (typeof (validateXML) != "function") { | 125 if (typeof (validateXML) != "function") { |
122 return false; | 126 return false; |
123 } | 127 } |
124 return true; | 128 return true; |
125 } | 129 }; |
126 | 130 |
127 var onload = function () { | 131 var onload = function () { |
128 // Function called once the browser has loaded all files. | 132 // Function called once the browser has loaded all files. |
129 // This should perform any initial commands such as structure / loading documents | 133 // This should perform any initial commands such as structure / loading documents |
130 | 134 |
131 // Create a web audio API context | 135 // Create a web audio API context |
132 // Fixed for cross-browser support | 136 // Fixed for cross-browser support |
133 var AudioContext = window.AudioContext || window.webkitAudioContext; | 137 var AudioContext = window.AudioContext || window.webkitAudioContext; |
134 audioContext = new AudioContext; | 138 audioContext = new AudioContext(); |
135 | 139 |
136 // Create test state | 140 // Create test state |
137 testState = new stateMachine(); | 141 testState = new stateMachine(); |
138 | 142 |
139 // Create the popup interface object | 143 // Create the popup interface object |
150 // Define window callbacks for interface | 154 // Define window callbacks for interface |
151 window.onresize = function (event) { | 155 window.onresize = function (event) { |
152 interfaceContext.resizeWindow(event); | 156 interfaceContext.resizeWindow(event); |
153 }; | 157 }; |
154 | 158 |
155 if (window.location.search.length != 0) { | 159 if (window.location.search.length !== 0) { |
156 var search = window.location.search.split('?')[1]; | 160 var search = window.location.search.split('?')[1]; |
157 // Now split the requests into pairs | 161 // Now split the requests into pairs |
158 var searchQueries = search.split('&'); | 162 var searchQueries = search.split('&'); |
159 | 163 var url; |
160 for (var i in searchQueries) { | 164 for (var i in searchQueries) { |
161 // Split each key-value pair | 165 // Split each key-value pair |
162 searchQueries[i] = searchQueries[i].split('='); | 166 searchQueries[i] = searchQueries[i].split('='); |
163 var key = searchQueries[i][0]; | 167 var key = searchQueries[i][0]; |
164 var value = decodeURIComponent(searchQueries[i][1]); | 168 var value = decodeURIComponent(searchQueries[i][1]); |
165 switch (key) { | 169 switch (key) { |
166 case "url": | 170 case "url": |
167 url = value; | 171 url = value; |
172 specification.url = url; | |
168 break; | 173 break; |
169 case "returnURL": | 174 case "returnURL": |
170 gReturnURL = value; | 175 gReturnURL = value; |
171 break; | 176 break; |
172 case "saveFilenamePrefix": | 177 case "saveFilenamePrefix": |
186 // Load the project document from the given URL, decode the XML and instruct audioEngine to get audio data | 191 // Load the project document from the given URL, decode the XML and instruct audioEngine to get audio data |
187 // If url is null, request client to upload project XML document | 192 // If url is null, request client to upload project XML document |
188 var xmlhttp = new XMLHttpRequest(); | 193 var xmlhttp = new XMLHttpRequest(); |
189 xmlhttp.open("GET", 'xml/test-schema.xsd', true); | 194 xmlhttp.open("GET", 'xml/test-schema.xsd', true); |
190 xmlhttp.onload = function () { | 195 xmlhttp.onload = function () { |
191 schemaXSD = xmlhttp.response; | 196 specification.processSchema(xmlhttp.response); |
192 var parse = new DOMParser(); | |
193 specification.schema = parse.parseFromString(xmlhttp.response, 'text/xml'); | |
194 var r = new XMLHttpRequest(); | 197 var r = new XMLHttpRequest(); |
195 r.open('GET', url, true); | 198 r.open('GET', url, true); |
196 r.onload = function () { | 199 r.onload = function () { |
197 loadProjectSpecCallback(r.response); | 200 loadProjectSpecCallback(r.response); |
198 }; | 201 }; |
202 msg.textContent = "FATAL ERROR"; | 205 msg.textContent = "FATAL ERROR"; |
203 var span = document.createElement("p"); | 206 var span = document.createElement("p"); |
204 span.textContent = "There was an error when loading your XML file. Please check your path in the URL. After the path to this page, there should be '?url=path/to/your/file.xml'. Check the spelling of your filename as well. If you are still having issues, check the log of the python server or your webserver distribution for 404 codes for your file."; | 207 span.textContent = "There was an error when loading your XML file. Please check your path in the URL. After the path to this page, there should be '?url=path/to/your/file.xml'. Check the spelling of your filename as well. If you are still having issues, check the log of the python server or your webserver distribution for 404 codes for your file."; |
205 document.getElementsByTagName('body')[0].appendChild(msg); | 208 document.getElementsByTagName('body')[0].appendChild(msg); |
206 document.getElementsByTagName('body')[0].appendChild(span); | 209 document.getElementsByTagName('body')[0].appendChild(span); |
207 } | 210 }; |
208 r.send(); | 211 r.send(); |
209 }; | 212 }; |
210 xmlhttp.send(); | 213 xmlhttp.send(); |
211 }; | 214 } |
212 | 215 |
213 function loadProjectSpecCallback(response) { | 216 function loadProjectSpecCallback(response) { |
214 // Function called after asynchronous download of XML project specification | 217 // Function called after asynchronous download of XML project specification |
215 //var decode = $.parseXML(response); | 218 //var decode = $.parseXML(response); |
216 //projectXML = $(decode); | 219 //projectXML = $(decode); |
217 | 220 |
218 // Check if XML is new or a resumption | 221 // Check if XML is new or a resumption |
219 var parse = new DOMParser(); | 222 var parse = new DOMParser(); |
220 var responseDocument = parse.parseFromString(response, 'text/xml'); | 223 var responseDocument = parse.parseFromString(response, 'text/xml'); |
221 var errorNode = responseDocument.getElementsByTagName('parsererror'); | 224 var errorNode = responseDocument.getElementsByTagName('parsererror'); |
225 var msg, span; | |
222 if (errorNode.length >= 1) { | 226 if (errorNode.length >= 1) { |
223 var msg = document.createElement("h3"); | 227 msg = document.createElement("h3"); |
224 msg.textContent = "FATAL ERROR"; | 228 msg.textContent = "FATAL ERROR"; |
225 var span = document.createElement("span"); | 229 span = document.createElement("span"); |
226 span.textContent = "The XML parser returned the following errors when decoding your XML file"; | 230 span.textContent = "The XML parser returned the following errors when decoding your XML file"; |
227 document.getElementsByTagName('body')[0].innerHTML = null; | 231 document.getElementsByTagName('body')[0].innerHTML = null; |
228 document.getElementsByTagName('body')[0].appendChild(msg); | 232 document.getElementsByTagName('body')[0].appendChild(msg); |
229 document.getElementsByTagName('body')[0].appendChild(span); | 233 document.getElementsByTagName('body')[0].appendChild(span); |
230 document.getElementsByTagName('body')[0].appendChild(errorNode[0]); | 234 document.getElementsByTagName('body')[0].appendChild(errorNode[0]); |
231 return; | 235 return; |
232 } | 236 } |
233 if (responseDocument == undefined || responseDocument.firstChild == undefined) { | 237 if (responseDocument === undefined || responseDocument.firstChild === undefined) { |
234 var msg = document.createElement("h3"); | 238 msg = document.createElement("h3"); |
235 msg.textContent = "FATAL ERROR"; | 239 msg.textContent = "FATAL ERROR"; |
236 var span = document.createElement("span"); | 240 span = document.createElement("span"); |
237 span.textContent = "The project XML was not decoded properly, try refreshing your browser and clearing caches. If the problem persists, contact the test creator."; | 241 span.textContent = "The project XML was not decoded properly, try refreshing your browser and clearing caches. If the problem persists, contact the test creator."; |
238 document.getElementsByTagName('body')[0].innerHTML = null; | 242 document.getElementsByTagName('body')[0].innerHTML = null; |
239 document.getElementsByTagName('body')[0].appendChild(msg); | 243 document.getElementsByTagName('body')[0].appendChild(msg); |
240 document.getElementsByTagName('body')[0].appendChild(span); | 244 document.getElementsByTagName('body')[0].appendChild(span); |
241 return; | 245 return; |
244 // document is a specification | 248 // document is a specification |
245 | 249 |
246 // Perform XML schema validation | 250 // Perform XML schema validation |
247 var Module = { | 251 var Module = { |
248 xml: response, | 252 xml: response, |
249 schema: schemaXSD, | 253 schema: specification.getSchemaString(), |
250 arguments: ["--noout", "--schema", 'test-schema.xsd', 'document.xml'] | 254 arguments: ["--noout", "--schema", 'test-schema.xsd', 'document.xml'] |
251 }; | 255 }; |
252 projectXML = responseDocument; | 256 projectXML = responseDocument; |
253 var xmllint = validateXML(Module); | 257 var xmllint = validateXML(Module); |
254 console.log(xmllint); | 258 console.log(xmllint); |
255 if (xmllint != 'document.xml validates\n') { | 259 if (xmllint != 'document.xml validates\n') { |
256 document.getElementsByTagName('body')[0].innerHTML = null; | 260 document.getElementsByTagName('body')[0].innerHTML = null; |
257 var msg = document.createElement("h3"); | 261 msg = document.createElement("h3"); |
258 msg.textContent = "FATAL ERROR"; | 262 msg.textContent = "FATAL ERROR"; |
259 var span = document.createElement("h4"); | 263 span = document.createElement("h4"); |
260 span.textContent = "The XML validator returned the following errors when decoding your XML file"; | 264 span.textContent = "The XML validator returned the following errors when decoding your XML file"; |
261 document.getElementsByTagName('body')[0].appendChild(msg); | 265 document.getElementsByTagName('body')[0].appendChild(msg); |
262 document.getElementsByTagName('body')[0].appendChild(span); | 266 document.getElementsByTagName('body')[0].appendChild(span); |
263 xmllint = xmllint.split('\n'); | 267 xmllint = xmllint.split('\n'); |
264 for (var i in xmllint) { | 268 for (var i in xmllint) { |
265 document.getElementsByTagName('body')[0].appendChild(document.createElement('br')); | 269 document.getElementsByTagName('body')[0].appendChild(document.createElement('br')); |
266 var span = document.createElement("span"); | 270 span = document.createElement("span"); |
267 span.textContent = xmllint[i]; | 271 span.textContent = xmllint[i]; |
268 document.getElementsByTagName('body')[0].appendChild(span); | 272 document.getElementsByTagName('body')[0].appendChild(span); |
269 } | 273 } |
270 return; | 274 return; |
271 } | 275 } |
276 | 280 |
277 } else if (responseDocument.firstChild.nodeName == "waetresult") { | 281 } else if (responseDocument.firstChild.nodeName == "waetresult") { |
278 // document is a result | 282 // document is a result |
279 projectXML = document.implementation.createDocument(null, "waet"); | 283 projectXML = document.implementation.createDocument(null, "waet"); |
280 projectXML.firstChild.appendChild(responseDocument.getElementsByTagName('waet')[0].getElementsByTagName("setup")[0].cloneNode(true)); | 284 projectXML.firstChild.appendChild(responseDocument.getElementsByTagName('waet')[0].getElementsByTagName("setup")[0].cloneNode(true)); |
281 var child = responseDocument.firstChild.firstChild; | 285 var child = responseDocument.firstChild.firstChild, |
282 while (child != null) { | 286 copy; |
287 while (child !== null) { | |
283 if (child.nodeName == "survey") { | 288 if (child.nodeName == "survey") { |
284 // One of the global survey elements | 289 // One of the global survey elements |
285 if (child.getAttribute("state") == "complete") { | 290 if (child.getAttribute("state") == "complete") { |
286 // We need to remove this survey from <setup> | 291 // We need to remove this survey from <setup> |
287 var location = child.getAttribute("location"); | 292 var location = child.getAttribute("location"); |
288 var globalSurveys = projectXML.getElementsByTagName("setup")[0].getElementsByTagName("survey")[0]; | 293 var globalSurveys = projectXML.getElementsByTagName("setup")[0].getElementsByTagName("survey")[0]; |
289 while (globalSurveys != null) { | 294 while (globalSurveys !== null) { |
290 if (location == "pre" || location == "before") { | 295 if (location == "pre" || location == "before") { |
291 if (globalSurveys.getAttribute("location") == "pre" || globalSurveys.getAttribute("location") == "before") { | 296 if (globalSurveys.getAttribute("location") == "pre" || globalSurveys.getAttribute("location") == "before") { |
292 projectXML.getElementsByTagName("setup")[0].removeChild(globalSurveys); | 297 projectXML.getElementsByTagName("setup")[0].removeChild(globalSurveys); |
293 break; | 298 break; |
294 } | 299 } |
300 } | 305 } |
301 globalSurveys = globalSurveys.nextElementSibling; | 306 globalSurveys = globalSurveys.nextElementSibling; |
302 } | 307 } |
303 } else { | 308 } else { |
304 // We need to complete this, so it must be regenerated by store | 309 // We need to complete this, so it must be regenerated by store |
305 var copy = child; | 310 copy = child; |
306 child = child.previousElementSibling; | 311 child = child.previousElementSibling; |
307 responseDocument.firstChild.removeChild(copy); | 312 responseDocument.firstChild.removeChild(copy); |
308 } | 313 } |
309 } else if (child.nodeName == "page") { | 314 } else if (child.nodeName == "page") { |
310 if (child.getAttribute("state") == "empty") { | 315 if (child.getAttribute("state") == "empty") { |
311 // We need to complete this page | 316 // We need to complete this page |
312 projectXML.firstChild.appendChild(responseDocument.getElementById(child.getAttribute("ref")).cloneNode(true)); | 317 projectXML.firstChild.appendChild(responseDocument.getElementById(child.getAttribute("ref")).cloneNode(true)); |
313 var copy = child; | 318 copy = child; |
314 child = child.previousElementSibling; | 319 child = child.previousElementSibling; |
315 responseDocument.firstChild.removeChild(copy); | 320 responseDocument.firstChild.removeChild(copy); |
316 } | 321 } |
317 } | 322 } |
318 child = child.nextElementSibling; | 323 child = child.nextElementSibling; |
321 specification.decode(projectXML); | 326 specification.decode(projectXML); |
322 // Use the original | 327 // Use the original |
323 storage.initialise(responseDocument); | 328 storage.initialise(responseDocument); |
324 } | 329 } |
325 /// CHECK FOR SAMPLE RATE COMPATIBILITY | 330 /// CHECK FOR SAMPLE RATE COMPATIBILITY |
326 if (specification.sampleRate != undefined) { | 331 if (isFinite(specification.sampleRate)) { |
327 if (Number(specification.sampleRate) != audioContext.sampleRate) { | 332 if (Number(specification.sampleRate) != audioContext.sampleRate) { |
328 var errStr = 'Sample rates do not match! Requested ' + Number(specification.sampleRate) + ', got ' + audioContext.sampleRate + '. Please set the sample rate to match before completing this test.'; | 333 var errStr = 'Sample rates do not match! Requested ' + Number(specification.sampleRate) + ', got ' + audioContext.sampleRate + '. Please set the sample rate to match before completing this test.'; |
329 interfaceContext.lightbox.post("Error", errStr); | 334 interfaceContext.lightbox.post("Error", errStr); |
330 return; | 335 return; |
331 } | 336 } |
333 | 338 |
334 var getInterfaces = new XMLHttpRequest(); | 339 var getInterfaces = new XMLHttpRequest(); |
335 getInterfaces.open("GET", "interfaces/interfaces.json"); | 340 getInterfaces.open("GET", "interfaces/interfaces.json"); |
336 getInterfaces.onerror = function (e) { | 341 getInterfaces.onerror = function (e) { |
337 throw (e); | 342 throw (e); |
338 } | 343 }; |
339 getInterfaces.onload = function () { | 344 getInterfaces.onload = function () { |
340 if (getInterfaces.status !== 200) { | 345 if (getInterfaces.status !== 200) { |
341 throw (new Error(getInterfaces.status)); | 346 throw (new Error(getInterfaces.status)); |
342 } | 347 } |
343 // Get the current interface | 348 // Get the current interface |
361 css.setAttribute("rel", "stylesheet"); | 366 css.setAttribute("rel", "stylesheet"); |
362 css.setAttribute("type", "text/css"); | 367 css.setAttribute("type", "text/css"); |
363 css.setAttribute("href", v); | 368 css.setAttribute("href", v); |
364 head.appendChild(css); | 369 head.appendChild(css); |
365 }); | 370 }); |
366 } | 371 }; |
367 getInterfaces.send(); | 372 getInterfaces.send(); |
368 | 373 |
369 if (gReturnURL != undefined) { | 374 if (gReturnURL !== undefined) { |
370 console.log("returnURL Overide from " + specification.returnURL + " to " + gReturnURL); | 375 console.log("returnURL Overide from " + specification.returnURL + " to " + gReturnURL); |
371 specification.returnURL = gReturnURL; | 376 specification.returnURL = gReturnURL; |
372 } | 377 } |
373 if (gSaveFilenamePrefix != undefined) { | 378 if (gSaveFilenamePrefix !== undefined) { |
374 specification.saveFilenamePrefix = gSaveFilenamePrefix; | 379 specification.saveFilenamePrefix = gSaveFilenamePrefix; |
375 } | 380 } |
376 | 381 |
377 // Create the audio engine object | 382 // Create the audio engine object |
378 audioEngineContext = new AudioEngine(specification); | 383 audioEngineContext = new AudioEngine(specification); |
413 if (specification.projectReturn.substr(0, 4) == "http") { | 418 if (specification.projectReturn.substr(0, 4) == "http") { |
414 projectReturn = specification.projectReturn; | 419 projectReturn = specification.projectReturn; |
415 } | 420 } |
416 } | 421 } |
417 var saveURL = projectReturn + "php/save.php?key=" + storage.SessionKey.key + saveUrlSuffix; | 422 var saveURL = projectReturn + "php/save.php?key=" + storage.SessionKey.key + saveUrlSuffix; |
418 var xmlhttp = new XMLHttpRequest; | 423 var xmlhttp = new XMLHttpRequest(); |
419 xmlhttp.open("POST", saveURL, true); | 424 xmlhttp.open("POST", saveURL, true); |
420 xmlhttp.setRequestHeader('Content-Type', 'text/xml'); | 425 xmlhttp.setRequestHeader('Content-Type', 'text/xml'); |
421 xmlhttp.onerror = function () { | 426 xmlhttp.onerror = function () { |
422 console.log('Error saving file to server! Presenting download locally'); | 427 console.log('Error saving file to server! Presenting download locally'); |
423 createProjectSave("local"); | 428 createProjectSave("local"); |
515 function samplesToSeconds(samples, fs) { | 520 function samplesToSeconds(samples, fs) { |
516 return samples / fs; | 521 return samples / fs; |
517 } | 522 } |
518 | 523 |
519 function randomString(length) { | 524 function randomString(length) { |
520 var str = "" | 525 var str = ""; |
521 for (var i = 0; i < length; i += 2) { | 526 for (var i = 0; i < length; i += 2) { |
522 var num = Math.floor(Math.random() * 1295); | 527 var num = Math.floor(Math.random() * 1295); |
523 str += num.toString(36); | 528 str += num.toString(36); |
524 } | 529 } |
525 return str; | 530 return str; |
530 // This takes an array of information and randomises the order | 535 // This takes an array of information and randomises the order |
531 var N = input.length; | 536 var N = input.length; |
532 | 537 |
533 var inputSequence = []; // For safety purposes: keep track of randomisation | 538 var inputSequence = []; // For safety purposes: keep track of randomisation |
534 for (var counter = 0; counter < N; ++counter) | 539 for (var counter = 0; counter < N; ++counter) |
535 inputSequence.push(counter) // Fill array | 540 inputSequence.push(counter); // Fill array |
536 var inputSequenceClone = inputSequence.slice(0); | 541 var inputSequenceClone = inputSequence.slice(0); |
537 | 542 |
538 var holdArr = []; | 543 var holdArr = []; |
539 var outputSequence = []; | 544 var outputSequence = []; |
540 for (var n = 0; n < N; n++) { | 545 for (var n = 0; n < N; n++) { |
582 console.log(e); | 587 console.log(e); |
583 popup.buttonProceed.onclick(); | 588 popup.buttonProceed.onclick(); |
584 e.preventDefault(); | 589 e.preventDefault(); |
585 } | 590 } |
586 }); | 591 }); |
592 // Generators & Processors // | |
593 | |
594 function processConditional(node, value) { | |
595 function jumpToId(jumpID) { | |
596 var index = this.popupOptions.findIndex(function (item, index, element) { | |
597 if (item.specification.id == jumpID) { | |
598 return true; | |
599 } else { | |
600 return false; | |
601 } | |
602 }, this); | |
603 this.currentIndex = index - 1; | |
604 } | |
605 var conditionFunction; | |
606 if (node.specification.type === "question") { | |
607 conditionFunction = processQuestionConditional; | |
608 } else if (node.specification.type === "checkbox") { | |
609 conditionFunction = processCheckboxConditional; | |
610 } else if (node.specification.type === "radio") { | |
611 conditionFunction = processRadioConditional; | |
612 } else if (node.specification.type === "number") { | |
613 conditionFunction = processNumberConditional; | |
614 } else if (node.specification.type === "slider") { | |
615 conditionFunction = processSliderConditional; | |
616 } else { | |
617 return; | |
618 } | |
619 for (var i = 0; i < node.specification.conditions.length; i++) { | |
620 var condition = node.specification.conditions[i]; | |
621 var pass = conditionFunction(condition, value); | |
622 var jumpID; | |
623 if (pass) { | |
624 jumpID = condition.jumpToOnPass; | |
625 } else { | |
626 jumpID = condition.jumpToOnFail; | |
627 } | |
628 if (jumpID !== undefined) { | |
629 jumpToId.call(this, jumpID); | |
630 break; | |
631 } | |
632 } | |
633 } | |
634 | |
635 function postQuestion(node) { | |
636 var textArea = document.createElement('textarea'); | |
637 switch (node.specification.boxsize) { | |
638 case 'small': | |
639 textArea.cols = "20"; | |
640 textArea.rows = "1"; | |
641 break; | |
642 case 'normal': | |
643 textArea.cols = "30"; | |
644 textArea.rows = "2"; | |
645 break; | |
646 case 'large': | |
647 textArea.cols = "40"; | |
648 textArea.rows = "5"; | |
649 break; | |
650 case 'huge': | |
651 textArea.cols = "50"; | |
652 textArea.rows = "10"; | |
653 break; | |
654 } | |
655 if (node.response === undefined) { | |
656 node.response = ""; | |
657 } else { | |
658 textArea.value = node.response; | |
659 } | |
660 this.popupResponse.appendChild(textArea); | |
661 textArea.focus(); | |
662 this.popupResponse.style.textAlign = "center"; | |
663 this.popupResponse.style.left = "0%"; | |
664 } | |
665 | |
666 function processQuestionConditional(condition, value) { | |
667 switch (condition.check) { | |
668 case "equals": | |
669 // Deliberately loose check | |
670 if (value == condition.value) { | |
671 return true; | |
672 } | |
673 break; | |
674 case "greaterThan": | |
675 case "lessThan": | |
676 console.log("Survey Element of type 'question' cannot interpret greaterThan/lessThan conditions. IGNORING"); | |
677 break; | |
678 case "contains": | |
679 if (value.includes(condition.value)) { | |
680 return true; | |
681 } | |
682 break; | |
683 } | |
684 return false; | |
685 } | |
686 | |
687 function processQuestion(node) { | |
688 var textArea = this.popupResponse.getElementsByTagName("textarea")[0]; | |
689 if (node.specification.mandatory === true && textArea.value.length === 0) { | |
690 interfaceContext.lightbox.post("Error", "This question is mandatory"); | |
691 return false; | |
692 } | |
693 // Save the text content | |
694 console.log("Question: " + node.specification.statement); | |
695 console.log("Question Response: " + textArea.value); | |
696 node.response = textArea.value; | |
697 processConditional.call(this, node, textArea.value); | |
698 return true; | |
699 } | |
700 | |
701 function postCheckbox(node) { | |
702 if (node.response === undefined) { | |
703 node.response = Array(node.specification.options.length); | |
704 } | |
705 var table = document.createElement("table"); | |
706 table.className = "popup-option-list"; | |
707 table.border = "0"; | |
708 node.response = []; | |
709 node.specification.options.forEach(function (option, index) { | |
710 var tr = document.createElement("tr"); | |
711 table.appendChild(tr); | |
712 var td = document.createElement("td"); | |
713 tr.appendChild(td); | |
714 var input = document.createElement('input'); | |
715 input.id = option.name; | |
716 input.type = 'checkbox'; | |
717 td.appendChild(input); | |
718 | |
719 td = document.createElement("td"); | |
720 tr.appendChild(td); | |
721 var span = document.createElement('span'); | |
722 span.textContent = option.text; | |
723 td.appendChild(span); | |
724 tr = document.createElement('div'); | |
725 tr.setAttribute('name', 'option'); | |
726 tr.className = "popup-option-checbox"; | |
727 if (node.response[index] !== undefined) { | |
728 if (node.response[index].checked === true) { | |
729 input.checked = "true"; | |
730 } | |
731 } | |
732 index++; | |
733 }); | |
734 this.popupResponse.appendChild(table); | |
735 } | |
736 | |
737 function processCheckbox(node) { | |
738 console.log("Checkbox: " + node.specification.statement); | |
739 var inputs = this.popupResponse.getElementsByTagName('input'); | |
740 node.response = []; | |
741 var numChecked = 0, | |
742 i; | |
743 for (i = 0; i < node.specification.options.length; i++) { | |
744 if (inputs[i].checked) { | |
745 numChecked++; | |
746 } | |
747 } | |
748 if (node.specification.min !== undefined) { | |
749 if (node.specification.max === undefined) { | |
750 if (numChecked < node.specification.min) { | |
751 var msg = "You must select at least " + node.specification.min + " option"; | |
752 if (node.specification.min > 1) { | |
753 msg += "s"; | |
754 } | |
755 interfaceContext.lightbox.post("Error", msg); | |
756 return; | |
757 } | |
758 } else { | |
759 if (numChecked < node.specification.min || numChecked > node.specification.max) { | |
760 if (node.specification.min == node.specification.max) { | |
761 interfaceContext.lightbox.post("Error", "You must only select " + node.specification.min); | |
762 } else { | |
763 interfaceContext.lightbox.post("Error", "You must select between " + node.specification.min + " and " + node.specification.max); | |
764 } | |
765 return false; | |
766 } | |
767 } | |
768 } | |
769 for (i = 0; i < node.specification.options.length; i++) { | |
770 node.response.push({ | |
771 name: node.specification.options[i].name, | |
772 text: node.specification.options[i].text, | |
773 checked: inputs[i].checked | |
774 }); | |
775 console.log(node.specification.options[i].name + ": " + inputs[i].checked); | |
776 } | |
777 processConditional.call(this, node, node.response); | |
778 return true; | |
779 } | |
780 | |
781 function processCheckboxConditional(condition, response) { | |
782 switch (condition.check) { | |
783 case "contains": | |
784 for (var i = 0; i < response.length; i++) { | |
785 var value = response[i]; | |
786 if (value.name === condition.value && value.checked) { | |
787 return true; | |
788 } | |
789 } | |
790 break; | |
791 case "equals": | |
792 case "greaterThan": | |
793 case "lessThan": | |
794 console.log("Survey Element of type 'checkbox' cannot interpret equals/greaterThan/lessThan conditions. IGNORING"); | |
795 break; | |
796 default: | |
797 console.log("Unknown condition. IGNORING"); | |
798 break; | |
799 } | |
800 return false; | |
801 } | |
802 | |
803 function postRadio(node) { | |
804 if (node.response === null) { | |
805 node.response = { | |
806 name: "", | |
807 text: "" | |
808 }; | |
809 } | |
810 var table = document.createElement("table"); | |
811 table.className = "popup-option-list"; | |
812 table.border = "0"; | |
813 if (node.response === null || node.response.length === 0) { | |
814 node.response = []; | |
815 } | |
816 node.specification.options.forEach(function (option, index) { | |
817 var tr = document.createElement("tr"); | |
818 table.appendChild(tr); | |
819 var td = document.createElement("td"); | |
820 tr.appendChild(td); | |
821 var input = document.createElement('input'); | |
822 input.id = option.name; | |
823 input.type = 'radio'; | |
824 input.name = node.specification.id; | |
825 td.appendChild(input); | |
826 | |
827 td = document.createElement("td"); | |
828 tr.appendChild(td); | |
829 var span = document.createElement('span'); | |
830 span.textContent = option.text; | |
831 td.appendChild(span); | |
832 tr = document.createElement('div'); | |
833 tr.setAttribute('name', 'option'); | |
834 tr.className = "popup-option-checbox"; | |
835 table.appendChild(tr); | |
836 }); | |
837 this.popupResponse.appendChild(table); | |
838 } | |
839 | |
840 function processRadio(node) { | |
841 var optHold = this.popupResponse; | |
842 console.log("Radio: " + node.specification.statement); | |
843 node.response = null; | |
844 var i = 0; | |
845 var inputs = optHold.getElementsByTagName('input'); | |
846 while (node.response === null) { | |
847 if (i == inputs.length) { | |
848 if (node.specification.mandatory === true) { | |
849 interfaceContext.lightbox.post("Error", "Please select one option"); | |
850 return false; | |
851 } | |
852 break; | |
853 } | |
854 if (inputs[i].checked === true) { | |
855 node.response = node.specification.options[i]; | |
856 console.log("Selected: " + node.specification.options[i].name); | |
857 } | |
858 i++; | |
859 } | |
860 processConditional.call(this, node, node.response); | |
861 return true; | |
862 } | |
863 | |
864 function processRadioConditional(condition, response) { | |
865 switch (condition.check) { | |
866 case "equals": | |
867 if (response === condition.value) { | |
868 return true; | |
869 } | |
870 break; | |
871 case "contains": | |
872 case "greaterThan": | |
873 case "lessThan": | |
874 console.log("Survey Element of type 'radio' cannot interpret contains/greaterThan/lessThan conditions. IGNORING"); | |
875 break; | |
876 default: | |
877 console.log("Unknown condition. IGNORING"); | |
878 break; | |
879 } | |
880 return false; | |
881 } | |
882 | |
883 function postNumber(node) { | |
884 var input = document.createElement('input'); | |
885 input.type = 'textarea'; | |
886 if (node.specification.min !== null) { | |
887 input.min = node.specification.min; | |
888 } | |
889 if (node.specification.max !== null) { | |
890 input.max = node.specification.max; | |
891 } | |
892 if (node.specification.step !== null) { | |
893 input.step = node.specification.step; | |
894 } | |
895 if (node.response !== undefined) { | |
896 input.value = node.response; | |
897 } | |
898 this.popupResponse.appendChild(input); | |
899 this.popupResponse.style.textAlign = "center"; | |
900 this.popupResponse.style.left = "0%"; | |
901 } | |
902 | |
903 function processNumber(node) { | |
904 var input = this.popupContent.getElementsByTagName('input')[0]; | |
905 if (node.mandatory === true && input.value.length === 0) { | |
906 interfaceContext.lightbox.post("Error", 'This question is mandatory. Please enter a number'); | |
907 return false; | |
908 } | |
909 var enteredNumber = Number(input.value); | |
910 if (isNaN(enteredNumber)) { | |
911 interfaceContext.lightbox.post("Error", 'Please enter a valid number'); | |
912 return false; | |
913 } | |
914 if (enteredNumber < node.min && node.min !== null) { | |
915 interfaceContext.lightbox.post("Error", 'Number is below the minimum value of ' + node.min); | |
916 return false; | |
917 } | |
918 if (enteredNumber > node.max && node.max !== null) { | |
919 interfaceContext.lightbox.post("Error", 'Number is above the maximum value of ' + node.max); | |
920 return false; | |
921 } | |
922 node.response = input.value; | |
923 processConditional.call(this, node, node.response); | |
924 return true; | |
925 } | |
926 | |
927 function processNumberConditional(condtion, value) { | |
928 var condition = condition; | |
929 switch (condition.check) { | |
930 case "greaterThan": | |
931 if (value > Number(condition.value)) { | |
932 return true; | |
933 } | |
934 break; | |
935 case "lessThan": | |
936 if (value < Number(condition.value)) { | |
937 return true; | |
938 } | |
939 break; | |
940 case "equals": | |
941 if (value == condition.value) { | |
942 return true; | |
943 } | |
944 break; | |
945 case "contains": | |
946 console.log("Survey Element of type 'number' cannot interpret \"contains\" conditions. IGNORING"); | |
947 break; | |
948 default: | |
949 console.log("Unknown condition. IGNORING"); | |
950 break; | |
951 } | |
952 return false; | |
953 } | |
954 | |
955 function postVideo(node) { | |
956 var video = document.createElement("video"); | |
957 video.src = node.specification.url; | |
958 this.popupResponse.appendChild(video); | |
959 } | |
960 | |
961 function postYoutube(node) { | |
962 var iframe = document.createElement("iframe"); | |
963 iframe.className = "youtube"; | |
964 iframe.src = node.specification.url; | |
965 this.popupResponse.appendChild(iframe); | |
966 } | |
967 | |
968 function postSlider(node) { | |
969 var hold = document.createElement('div'); | |
970 var input = document.createElement('input'); | |
971 input.type = 'range'; | |
972 input.style.width = "90%"; | |
973 if (node.specification.min !== null) { | |
974 input.min = node.specification.min; | |
975 } | |
976 if (node.specification.max !== null) { | |
977 input.max = node.specification.max; | |
978 } | |
979 if (node.response !== undefined) { | |
980 input.value = node.response; | |
981 } | |
982 hold.className = "survey-slider-text-holder"; | |
983 var minText = document.createElement('span'); | |
984 var maxText = document.createElement('span'); | |
985 minText.textContent = node.specification.leftText; | |
986 maxText.textContent = node.specification.rightText; | |
987 hold.appendChild(minText); | |
988 hold.appendChild(maxText); | |
989 this.popupResponse.appendChild(input); | |
990 this.popupResponse.appendChild(hold); | |
991 this.popupResponse.style.textAlign = "center"; | |
992 } | |
993 | |
994 function processSlider(node) { | |
995 var input = this.popupContent.getElementsByTagName('input')[0]; | |
996 node.response = input.value; | |
997 processConditional.call(this, node, node.response); | |
998 return true; | |
999 } | |
1000 | |
1001 function processSliderConditional(condition, value) { | |
1002 switch (condition.check) { | |
1003 case "contains": | |
1004 console.log("Survey Element of type 'number' cannot interpret contains conditions. IGNORING"); | |
1005 break; | |
1006 case "greaterThan": | |
1007 if (value > Number(condition.value)) { | |
1008 return true; | |
1009 } | |
1010 break; | |
1011 case "lessThan": | |
1012 if (value < Number(condition.value)) { | |
1013 return true; | |
1014 } | |
1015 break; | |
1016 case "equals": | |
1017 if (value == condition.value) { | |
1018 return true; | |
1019 } | |
1020 break; | |
1021 default: | |
1022 console.log("Unknown condition. IGNORING"); | |
1023 break; | |
1024 } | |
1025 return false; | |
1026 } | |
587 | 1027 |
588 this.createPopup = function () { | 1028 this.createPopup = function () { |
589 // Create popup window interface | 1029 // Create popup window interface |
590 var insertPoint = document.getElementById("topLevelBody"); | 1030 var insertPoint = document.getElementById("topLevelBody"); |
591 | 1031 |
612 this.hidePopup(); | 1052 this.hidePopup(); |
613 this.popup.style.visibility = 'hidden'; | 1053 this.popup.style.visibility = 'hidden'; |
614 }; | 1054 }; |
615 | 1055 |
616 this.showPopup = function () { | 1056 this.showPopup = function () { |
617 if (this.popup == null) { | 1057 if (this.popup === null) { |
618 this.createPopup(); | 1058 this.createPopup(); |
619 } | 1059 } |
620 this.popup.style.visibility = 'visible'; | 1060 this.popup.style.visibility = 'visible'; |
621 var blank = document.getElementsByClassName('testHalt')[0]; | 1061 var blank = document.getElementsByClassName('testHalt')[0]; |
622 blank.style.visibility = 'visible'; | 1062 blank.style.visibility = 'visible'; |
639 p = new DOMParser(); | 1079 p = new DOMParser(); |
640 this.popupResponse.innerHTML = ""; | 1080 this.popupResponse.innerHTML = ""; |
641 this.popupTitle.innerHTML = ""; | 1081 this.popupTitle.innerHTML = ""; |
642 this.popupTitle.appendChild(p.parseFromString(converter.makeHtml(node.specification.statement), "text/html").getElementsByTagName("body")[0].firstElementChild); | 1082 this.popupTitle.appendChild(p.parseFromString(converter.makeHtml(node.specification.statement), "text/html").getElementsByTagName("body")[0].firstElementChild); |
643 if (node.specification.type == 'question') { | 1083 if (node.specification.type == 'question') { |
644 var textArea = document.createElement('textarea'); | 1084 postQuestion.call(this, node); |
645 switch (node.specification.boxsize) { | |
646 case 'small': | |
647 textArea.cols = "20"; | |
648 textArea.rows = "1"; | |
649 break; | |
650 case 'normal': | |
651 textArea.cols = "30"; | |
652 textArea.rows = "2"; | |
653 break; | |
654 case 'large': | |
655 textArea.cols = "40"; | |
656 textArea.rows = "5"; | |
657 break; | |
658 case 'huge': | |
659 textArea.cols = "50"; | |
660 textArea.rows = "10"; | |
661 break; | |
662 } | |
663 if (node.response == undefined) { | |
664 node.response = ""; | |
665 } else { | |
666 textArea.value = node.response; | |
667 } | |
668 this.popupResponse.appendChild(textArea); | |
669 textArea.focus(); | |
670 this.popupResponse.style.textAlign = "center"; | |
671 this.popupResponse.style.left = "0%"; | |
672 } else if (node.specification.type == 'checkbox') { | 1085 } else if (node.specification.type == 'checkbox') { |
673 if (node.response == undefined) { | 1086 postCheckbox.call(this, node); |
674 node.response = Array(node.specification.options.length); | |
675 } | |
676 var index = 0; | |
677 var table = document.createElement("table"); | |
678 table.className = "popup-option-list"; | |
679 table.border = "0"; | |
680 for (var option of node.specification.options) { | |
681 var tr = document.createElement("tr"); | |
682 table.appendChild(tr); | |
683 var td = document.createElement("td"); | |
684 tr.appendChild(td); | |
685 var input = document.createElement('input'); | |
686 input.id = option.name; | |
687 input.type = 'checkbox'; | |
688 td.appendChild(input); | |
689 | |
690 td = document.createElement("td"); | |
691 tr.appendChild(td); | |
692 var span = document.createElement('span'); | |
693 span.textContent = option.text; | |
694 td.appendChild(span); | |
695 var tr = document.createElement('div'); | |
696 tr.setAttribute('name', 'option'); | |
697 tr.className = "popup-option-checbox"; | |
698 if (node.response[index] != undefined) { | |
699 if (node.response[index].checked == true) { | |
700 input.checked = "true"; | |
701 } | |
702 } | |
703 index++; | |
704 } | |
705 this.popupResponse.appendChild(table); | |
706 } else if (node.specification.type == 'radio') { | 1087 } else if (node.specification.type == 'radio') { |
707 if (node.response == undefined) { | 1088 postRadio.call(this, node); |
708 node.response = { | |
709 name: "", | |
710 text: "" | |
711 }; | |
712 } | |
713 var index = 0; | |
714 var table = document.createElement("table"); | |
715 table.className = "popup-option-list"; | |
716 table.border = "0"; | |
717 for (var option of node.specification.options) { | |
718 var tr = document.createElement("tr"); | |
719 table.appendChild(tr); | |
720 var td = document.createElement("td"); | |
721 tr.appendChild(td); | |
722 var input = document.createElement('input'); | |
723 input.id = option.name; | |
724 input.type = 'radio'; | |
725 input.name = node.specification.id; | |
726 td.appendChild(input); | |
727 | |
728 td = document.createElement("td"); | |
729 tr.appendChild(td); | |
730 var span = document.createElement('span'); | |
731 span.textContent = option.text; | |
732 td.appendChild(span); | |
733 var tr = document.createElement('div'); | |
734 tr.setAttribute('name', 'option'); | |
735 tr.className = "popup-option-checbox"; | |
736 if (node.response[index] != undefined) { | |
737 if (node.response[index].checked == true) { | |
738 input.checked = "true"; | |
739 } | |
740 } | |
741 index++; | |
742 } | |
743 this.popupResponse.appendChild(table); | |
744 } else if (node.specification.type == 'number') { | 1089 } else if (node.specification.type == 'number') { |
745 var input = document.createElement('input'); | 1090 postNumber.call(this, node); |
746 input.type = 'textarea'; | |
747 if (node.specification.min != null) { | |
748 input.min = node.specification.min; | |
749 } | |
750 if (node.specification.max != null) { | |
751 input.max = node.specification.max; | |
752 } | |
753 if (node.specification.step != null) { | |
754 input.step = node.specification.step; | |
755 } | |
756 if (node.response != undefined) { | |
757 input.value = node.response; | |
758 } | |
759 this.popupResponse.appendChild(input); | |
760 this.popupResponse.style.textAlign = "center"; | |
761 this.popupResponse.style.left = "0%"; | |
762 } else if (node.specification.type == "video") { | 1091 } else if (node.specification.type == "video") { |
763 var video = document.createElement("video"); | 1092 postVideo.call(this, node); |
764 video.src = node.specification.url; | |
765 this.popupResponse.appendChild(video); | |
766 } else if (node.specification.type == "youtube") { | 1093 } else if (node.specification.type == "youtube") { |
767 var iframe = document.createElement("iframe"); | 1094 postYoutube.call(this, node); |
768 iframe.className = "youtube"; | |
769 iframe.src = node.specification.url; | |
770 this.popupResponse.appendChild(iframe); | |
771 } else if (node.specification.type == "slider") { | 1095 } else if (node.specification.type == "slider") { |
772 var hold = document.createElement('div'); | 1096 postSlider.call(this, node); |
773 var input = document.createElement('input'); | |
774 input.type = 'range'; | |
775 input.style.width = "90%"; | |
776 if (node.specification.min != null) { | |
777 input.min = node.specification.min; | |
778 } | |
779 if (node.specification.max != null) { | |
780 input.max = node.specification.max; | |
781 } | |
782 if (node.response != undefined) { | |
783 input.value = node.response; | |
784 } | |
785 hold.className = "survey-slider-text-holder"; | |
786 var minText = document.createElement('span'); | |
787 var maxText = document.createElement('span'); | |
788 minText.textContent = node.specification.leftText; | |
789 maxText.textContent = node.specification.rightText; | |
790 hold.appendChild(minText); | |
791 hold.appendChild(maxText); | |
792 this.popupResponse.appendChild(input); | |
793 this.popupResponse.appendChild(hold); | |
794 this.popupResponse.style.textAlign = "center"; | |
795 } | 1097 } |
796 if (this.currentIndex + 1 == this.popupOptions.length) { | 1098 if (this.currentIndex + 1 == this.popupOptions.length) { |
797 if (this.node.location == "pre") { | 1099 if (this.node.location == "pre") { |
798 this.buttonProceed.textContent = 'Start'; | 1100 this.buttonProceed.textContent = 'Start'; |
799 } else { | 1101 } else { |
813 // initialise the popup procedure. | 1115 // initialise the popup procedure. |
814 if (node.options.length > 0) { | 1116 if (node.options.length > 0) { |
815 this.popupOptions = []; | 1117 this.popupOptions = []; |
816 this.node = node; | 1118 this.node = node; |
817 this.store = store; | 1119 this.store = store; |
818 for (var opt of node.options) { | 1120 node.options.forEach(function (opt) { |
819 this.popupOptions.push({ | 1121 this.popupOptions.push({ |
820 specification: opt, | 1122 specification: opt, |
821 response: null | 1123 response: null |
822 }); | 1124 }); |
823 } | 1125 }, this); |
824 this.currentIndex = 0; | 1126 this.currentIndex = 0; |
825 this.showPopup(); | 1127 this.showPopup(); |
826 this.postNode(); | 1128 this.postNode(); |
827 } else { | 1129 } else { |
828 advanceState(); | 1130 advanceState(); |
829 } | 1131 } |
830 }; | 1132 }; |
831 | 1133 |
832 this.proceedClicked = function () { | 1134 this.proceedClicked = function () { |
833 // Each time the popup button is clicked! | 1135 // Each time the popup button is clicked! |
834 if (testState.stateIndex == 0 && specification.calibration) { | 1136 if (testState.stateIndex === 0 && specification.calibration) { |
835 interfaceContext.calibrationModuleObject.collect(); | 1137 interfaceContext.calibrationModuleObject.collect(); |
836 advanceState(); | 1138 advanceState(); |
837 return; | 1139 return; |
838 } | 1140 } |
839 var node = this.popupOptions[this.currentIndex]; | 1141 var node = this.popupOptions[this.currentIndex], |
1142 pass = true; | |
840 if (node.specification.type == 'question') { | 1143 if (node.specification.type == 'question') { |
841 // Must extract the question data | 1144 // Must extract the question data |
842 var textArea = $(popup.popupContent).find('textarea')[0]; | 1145 pass = processQuestion.call(this, node); |
843 if (node.specification.mandatory == true && textArea.value.length == 0) { | |
844 interfaceContext.lightbox.post("Error", "This question is mandatory"); | |
845 return; | |
846 } else { | |
847 // Save the text content | |
848 console.log("Question: " + node.specification.statement); | |
849 console.log("Question Response: " + textArea.value); | |
850 node.response = textArea.value; | |
851 } | |
852 // Perform the conditional | |
853 for (var condition of node.specification.conditions) { | |
854 var pass = false; | |
855 switch (condition.check) { | |
856 case "equals": | |
857 if (textArea.value == condition.value) { | |
858 pass = true; | |
859 } | |
860 break; | |
861 case "greaterThan": | |
862 case "lessThan": | |
863 console.log("Survey Element of type 'question' cannot interpret greaterThan/lessThan conditions. IGNORING"); | |
864 break; | |
865 case "contains": | |
866 if (textArea.value.includes(condition.value)) { | |
867 pass = true; | |
868 } | |
869 break; | |
870 } | |
871 var jumpID; | |
872 if (pass) { | |
873 jumpID = condition.jumpToOnPass; | |
874 } else { | |
875 jumpID = condition.jumpToOnFail; | |
876 } | |
877 if (jumpID != undefined) { | |
878 var index = this.popupOptions.findIndex(function (item, index, element) { | |
879 if (item.specification.id == jumpID) { | |
880 return true; | |
881 } else { | |
882 return false; | |
883 } | |
884 }, this); | |
885 this.currentIndex = index - 1; | |
886 break; | |
887 } | |
888 } | |
889 } else if (node.specification.type == 'checkbox') { | 1146 } else if (node.specification.type == 'checkbox') { |
890 // Must extract checkbox data | 1147 // Must extract checkbox data |
891 console.log("Checkbox: " + node.specification.statement); | 1148 pass = processCheckbox.call(this, node); |
892 var inputs = this.popupResponse.getElementsByTagName('input'); | 1149 } else if (node.specification.type == "radio") { |
893 node.response = []; | |
894 var numChecked = 0; | |
895 for (var i = 0; i < node.specification.options.length; i++) { | |
896 if (inputs[i].checked) { | |
897 numChecked++; | |
898 } | |
899 } | |
900 if (node.specification.min != undefined) { | |
901 if (node.specification.max == undefined) { | |
902 if (numChecked < node.specification.min) { | |
903 var msg = "You must select at least " + node.specification.min + " option"; | |
904 if (node.specification.min > 1) { | |
905 msg += "s"; | |
906 } | |
907 interfaceContext.lightbox.post("Error", msg); | |
908 return; | |
909 } | |
910 } else { | |
911 if (numChecked < node.specification.min || numChecked > node.specification.max) { | |
912 if (node.specification.min == node.specification.max) { | |
913 interfaceContext.lightbox.post("Error", "You must only select " + node.specification.min); | |
914 } else { | |
915 interfaceContext.lightbox.post("Error", "You must select between " + node.specification.min + " and " + node.specification.max); | |
916 } | |
917 return; | |
918 } | |
919 } | |
920 } | |
921 for (var i = 0; i < node.specification.options.length; i++) { | |
922 node.response.push({ | |
923 name: node.specification.options[i].name, | |
924 text: node.specification.options[i].text, | |
925 checked: inputs[i].checked | |
926 }); | |
927 console.log(node.specification.options[i].name + ": " + inputs[i].checked); | |
928 } | |
929 // Perform the conditional | 1150 // Perform the conditional |
930 for (var condition of node.specification.conditions) { | 1151 pass = processRadio.call(this, node); |
931 var pass = false; | 1152 } else if (node.specification.type == "number") { |
932 switch (condition.check) { | |
933 case "equals": | |
934 case "greaterThan": | |
935 case "lessThan": | |
936 console.log("Survey Element of type 'checkbox' cannot interpret equals/greaterThan/lessThan conditions. IGNORING"); | |
937 break; | |
938 case "contains": | |
939 for (var response of node.response) { | |
940 if (response.name == condition.value && response.checked) { | |
941 pass = true; | |
942 break; | |
943 } | |
944 } | |
945 break; | |
946 } | |
947 var jumpID; | |
948 if (pass) { | |
949 jumpID = condition.jumpToOnPass; | |
950 } else { | |
951 jumpID = condition.jumpToOnFail; | |
952 } | |
953 if (jumpID != undefined) { | |
954 var index = this.popupOptions.findIndex(function (item, index, element) { | |
955 if (item.specification.id == jumpID) { | |
956 return true; | |
957 } else { | |
958 return false; | |
959 } | |
960 }, this); | |
961 this.currentIndex = index - 1; | |
962 break; | |
963 } | |
964 } | |
965 } else if (node.specification.type == "radio") { | |
966 var optHold = this.popupResponse; | |
967 console.log("Radio: " + node.specification.statement); | |
968 node.response = null; | |
969 var i = 0; | |
970 var inputs = optHold.getElementsByTagName('input'); | |
971 while (node.response == null) { | |
972 if (i == inputs.length) { | |
973 if (node.specification.mandatory == true) { | |
974 interfaceContext.lightbox.post("Error", "Please select one option"); | |
975 return; | |
976 } | |
977 break; | |
978 } | |
979 if (inputs[i].checked == true) { | |
980 node.response = node.specification.options[i]; | |
981 console.log("Selected: " + node.specification.options[i].name); | |
982 } | |
983 i++; | |
984 } | |
985 // Perform the conditional | 1153 // Perform the conditional |
986 for (var condition of node.specification.conditions) { | 1154 pass = processNumber.call(this, node); |
987 var pass = false; | |
988 switch (condition.check) { | |
989 case "contains": | |
990 case "greaterThan": | |
991 case "lessThan": | |
992 console.log("Survey Element of type 'radio' cannot interpret contains/greaterThan/lessThan conditions. IGNORING"); | |
993 break; | |
994 case "equals": | |
995 if (node.response.name == condition.value) { | |
996 pass = true; | |
997 } | |
998 break; | |
999 } | |
1000 var jumpID; | |
1001 if (pass) { | |
1002 jumpID = condition.jumpToOnPass; | |
1003 } else { | |
1004 jumpID = condition.jumpToOnFail; | |
1005 } | |
1006 if (jumpID != undefined) { | |
1007 var index = this.popupOptions.findIndex(function (item, index, element) { | |
1008 if (item.specification.id == jumpID) { | |
1009 return true; | |
1010 } else { | |
1011 return false; | |
1012 } | |
1013 }, this); | |
1014 this.currentIndex = index - 1; | |
1015 break; | |
1016 } | |
1017 } | |
1018 } else if (node.specification.type == "number") { | |
1019 var input = this.popupContent.getElementsByTagName('input')[0]; | |
1020 if (node.mandatory == true && input.value.length == 0) { | |
1021 interfaceContext.lightbox.post("Error", 'This question is mandatory. Please enter a number'); | |
1022 return; | |
1023 } | |
1024 var enteredNumber = Number(input.value); | |
1025 if (isNaN(enteredNumber)) { | |
1026 interfaceContext.lightbox.post("Error", 'Please enter a valid number'); | |
1027 return; | |
1028 } | |
1029 if (enteredNumber < node.min && node.min != null) { | |
1030 interfaceContext.lightbox.post("Error", 'Number is below the minimum value of ' + node.min); | |
1031 return; | |
1032 } | |
1033 if (enteredNumber > node.max && node.max != null) { | |
1034 interfaceContext.lightbox.post("Error", 'Number is above the maximum value of ' + node.max); | |
1035 return; | |
1036 } | |
1037 node.response = input.value; | |
1038 // Perform the conditional | |
1039 for (var condition of node.specification.conditions) { | |
1040 var pass = false; | |
1041 switch (condition.check) { | |
1042 case "contains": | |
1043 console.log("Survey Element of type 'number' cannot interpret contains conditions. IGNORING"); | |
1044 break; | |
1045 case "greaterThan": | |
1046 if (node.response > Number(condition.value)) { | |
1047 pass = true; | |
1048 } | |
1049 break; | |
1050 case "lessThan": | |
1051 if (node.response < Number(condition.value)) { | |
1052 pass = true; | |
1053 } | |
1054 break; | |
1055 case "equals": | |
1056 if (node.response == condition.value) { | |
1057 pass = true; | |
1058 } | |
1059 break; | |
1060 } | |
1061 var jumpID; | |
1062 if (pass) { | |
1063 jumpID = condition.jumpToOnPass; | |
1064 } else { | |
1065 jumpID = condition.jumpToOnFail; | |
1066 } | |
1067 if (jumpID != undefined) { | |
1068 var index = this.popupOptions.findIndex(function (item, index, element) { | |
1069 if (item.specification.id == jumpID) { | |
1070 return true; | |
1071 } else { | |
1072 return false; | |
1073 } | |
1074 }, this); | |
1075 this.currentIndex = index - 1; | |
1076 break; | |
1077 } | |
1078 } | |
1079 } else if (node.specification.type == 'slider') { | 1155 } else if (node.specification.type == 'slider') { |
1080 var input = this.popupContent.getElementsByTagName('input')[0]; | 1156 pass = processSlider.call(this, node); |
1081 node.response = input.value; | 1157 } |
1082 for (var condition of node.specification.conditions) { | 1158 if (pass === false) { |
1083 var pass = false; | 1159 return; |
1084 switch (condition.check) { | |
1085 case "contains": | |
1086 console.log("Survey Element of type 'number' cannot interpret contains conditions. IGNORING"); | |
1087 break; | |
1088 case "greaterThan": | |
1089 if (node.response > Number(condition.value)) { | |
1090 pass = true; | |
1091 } | |
1092 break; | |
1093 case "lessThan": | |
1094 if (node.response < Number(condition.value)) { | |
1095 pass = true; | |
1096 } | |
1097 break; | |
1098 case "equals": | |
1099 if (node.response == condition.value) { | |
1100 pass = true; | |
1101 } | |
1102 break; | |
1103 } | |
1104 var jumpID; | |
1105 if (pass) { | |
1106 jumpID = condition.jumpToOnPass; | |
1107 } else { | |
1108 jumpID = condition.jumpToOnFail; | |
1109 } | |
1110 if (jumpID != undefined) { | |
1111 var index = this.popupOptions.findIndex(function (item, index, element) { | |
1112 if (item.specification.id == jumpID) { | |
1113 return true; | |
1114 } else { | |
1115 return false; | |
1116 } | |
1117 }, this); | |
1118 this.currentIndex = index - 1; | |
1119 break; | |
1120 } | |
1121 } | |
1122 } | 1160 } |
1123 this.currentIndex++; | 1161 this.currentIndex++; |
1124 if (this.currentIndex < this.popupOptions.length) { | 1162 if (this.currentIndex < this.popupOptions.length) { |
1125 this.postNode(); | 1163 this.postNode(); |
1126 } else { | 1164 } else { |
1127 // Reached the end of the popupOptions | 1165 // Reached the end of the popupOptions |
1128 this.popupTitle.innerHTML = ""; | 1166 this.popupTitle.innerHTML = ""; |
1129 this.popupResponse.innerHTML = ""; | 1167 this.popupResponse.innerHTML = ""; |
1130 this.hidePopup(); | 1168 this.hidePopup(); |
1131 for (var node of this.popupOptions) { | 1169 this.popupOptions.forEach(function (node) { |
1132 this.store.postResult(node); | 1170 this.store.postResult(node); |
1133 } | 1171 }, this); |
1134 this.store.complete(); | 1172 this.store.complete(); |
1135 advanceState(); | 1173 advanceState(); |
1136 } | 1174 } |
1137 }; | 1175 }; |
1138 | 1176 |
1144 } | 1182 } |
1145 }; | 1183 }; |
1146 | 1184 |
1147 this.resize = function (event) { | 1185 this.resize = function (event) { |
1148 // Called on window resize; | 1186 // Called on window resize; |
1149 if (this.popup != null) { | 1187 if (this.popup !== null) { |
1150 this.popup.style.left = (window.innerWidth / 2) - 250 + 'px'; | 1188 this.popup.style.left = (window.innerWidth / 2) - 250 + 'px'; |
1151 this.popup.style.top = (window.innerHeight / 2) - 125 + 'px'; | 1189 this.popup.style.top = (window.innerHeight / 2) - 125 + 'px'; |
1152 var blank = document.getElementsByClassName('testHalt')[0]; | 1190 var blank = document.getElementsByClassName('testHalt')[0]; |
1153 blank.style.width = window.innerWidth; | 1191 blank.style.width = window.innerWidth; |
1154 blank.style.height = window.innerHeight; | 1192 blank.style.height = window.innerHeight; |
1155 } | 1193 } |
1156 }; | 1194 }; |
1157 this.hideNextButton = function () { | 1195 this.hideNextButton = function () { |
1158 this.buttonProceed.style.visibility = "hidden"; | 1196 this.buttonProceed.style.visibility = "hidden"; |
1159 } | 1197 }; |
1160 this.hidePreviousButton = function () { | 1198 this.hidePreviousButton = function () { |
1161 this.buttonPrevious.style.visibility = "hidden"; | 1199 this.buttonPrevious.style.visibility = "hidden"; |
1162 } | 1200 }; |
1163 this.showNextButton = function () { | 1201 this.showNextButton = function () { |
1164 this.buttonProceed.style.visibility = "visible"; | 1202 this.buttonProceed.style.visibility = "visible"; |
1165 } | 1203 }; |
1166 this.showPreviousButton = function () { | 1204 this.showPreviousButton = function () { |
1167 this.buttonPrevious.style.visibility = "visible"; | 1205 this.buttonPrevious.style.visibility = "visible"; |
1168 } | 1206 }; |
1169 } | 1207 } |
1170 | 1208 |
1171 function advanceState() { | 1209 function advanceState() { |
1172 // Just for complete clarity | 1210 // Just for complete clarity |
1173 testState.advanceState(); | 1211 testState.advanceState(); |
1174 } | 1212 } |
1175 | 1213 |
1176 function stateMachine() { | 1214 function stateMachine() { |
1177 // Object prototype for tracking and managing the test state | 1215 // Object prototype for tracking and managing the test state |
1178 | 1216 |
1179 function pickSubPool(pool, numElements) { | 1217 function pickSubPool(pool, numElements) { |
1180 // Assumes each element of pool has function "alwaysInclude" | 1218 // Assumes each element of pool has function "alwaysInclude" |
1181 | 1219 |
1182 // First extract those excluded from picking process | 1220 // First extract those excluded from picking process |
1183 var picked = []; | 1221 var picked = []; |
1187 } | 1225 } |
1188 }); | 1226 }); |
1189 | 1227 |
1190 return picked.concat(randomSubArray(pool, numElements - picked.length)); | 1228 return picked.concat(randomSubArray(pool, numElements - picked.length)); |
1191 } | 1229 } |
1192 | 1230 |
1193 this.stateMap = []; | 1231 this.stateMap = []; |
1194 this.preTestSurvey = null; | 1232 this.preTestSurvey = null; |
1195 this.postTestSurvey = null; | 1233 this.postTestSurvey = null; |
1196 this.stateIndex = null; | 1234 this.stateIndex = null; |
1197 this.currentStateMap = null; | 1235 this.currentStateMap = null; |
1199 this.currentStore = null; | 1237 this.currentStore = null; |
1200 this.initialise = function () { | 1238 this.initialise = function () { |
1201 | 1239 |
1202 // Get the data from Specification | 1240 // Get the data from Specification |
1203 var pagePool = []; | 1241 var pagePool = []; |
1204 for (var page of specification.pages) { | 1242 specification.pages.forEach(function(page){ |
1205 if (page.position !== null || page.alwaysInclude) { | 1243 if (page.position !== null || page.alwaysInclude) { |
1206 page.alwaysInclude = true; | 1244 page.alwaysInclude = true; |
1207 } | 1245 } |
1208 pagePool.push(page) | 1246 pagePool.push(page); |
1209 } | 1247 }); |
1210 if (specification.numPages > 0) { | 1248 if (specification.numPages > 0) { |
1211 specification.randomiseOrder = true; | 1249 specification.randomiseOrder = true; |
1212 pagePool = pickSubPool(pagePool, specification.numPages); | 1250 pagePool = pickSubPool(pagePool, specification.numPages); |
1213 } | 1251 } |
1214 | 1252 |
1215 // Now get the order of pages | 1253 // Now get the order of pages |
1216 var fixed = [] | 1254 var fixed = []; |
1217 for (var page of pagePool) { | 1255 pagePool.forEach(function(page){ |
1218 if (page.position !== null) { | 1256 if (page.position !== null) { |
1219 fixed.push(page); | 1257 fixed.push(page); |
1220 var i = pagePool.indexOf(page); | 1258 var i = pagePool.indexOf(page); |
1221 pagePool.splice(i, 1); | 1259 pagePool.splice(i, 1); |
1222 } | 1260 } |
1223 } | 1261 }); |
1224 | 1262 |
1225 if (specification.randomiseOrder) { | 1263 if (specification.randomiseOrder) { |
1226 pagePool = randomiseOrder(pagePool); | 1264 pagePool = randomiseOrder(pagePool); |
1227 } | 1265 } |
1228 | 1266 |
1229 // Place in the correct order | 1267 // Place in the correct order |
1230 for (var page of fixed) { | 1268 fixed.forEach(function(page) { |
1231 pagePool.splice(page.position, 0, page) | 1269 pagePool.splice(page.position, 0, page); |
1232 } | 1270 }); |
1233 | 1271 |
1234 // Now process the pages | 1272 // Now process the pages |
1235 pagePool.forEach(function (page, i) { | 1273 pagePool.forEach(function (page, i) { |
1236 page.presentedId = i; | 1274 page.presentedId = i; |
1237 this.stateMap.push(page); | 1275 this.stateMap.push(page); |
1238 var elements = page.audioElements | 1276 var elements = page.audioElements; |
1239 if (page.poolSize > 0 || page.randomiseOrder) { | 1277 if (page.poolSize > 0 || page.randomiseOrder) { |
1240 page.randomiseOrder = true; | 1278 page.randomiseOrder = true; |
1241 if (page.poolSize == 0) { | 1279 if (page.poolSize === 0) { |
1242 page.poolSize = elements.length; | 1280 page.poolSize = elements.length; |
1243 } | 1281 } |
1244 elements = pickSubPool(elements, page.poolSize); | 1282 elements = pickSubPool(elements, page.poolSize); |
1245 } | 1283 } |
1246 if (page.randomiseOrder) { | 1284 if (page.randomiseOrder) { |
1247 elements = randomiseOrder(elements); | 1285 elements = randomiseOrder(elements); |
1248 } | 1286 } |
1249 page.audioElements = elements; | 1287 page.audioElements = elements; |
1250 storage.createTestPageStore(page); | 1288 storage.createTestPageStore(page); |
1251 audioEngineContext.loadPageData(page); | 1289 audioEngineContext.loadPageData(page); |
1252 }, this) | 1290 }, this); |
1253 | 1291 |
1254 if (specification.preTest != null) { | 1292 if (specification.preTest !== null) { |
1255 this.preTestSurvey = specification.preTest; | 1293 this.preTestSurvey = specification.preTest; |
1256 } | 1294 } |
1257 if (specification.postTest != null) { | 1295 if (specification.postTest !== null) { |
1258 this.postTestSurvey = specification.postTest; | 1296 this.postTestSurvey = specification.postTest; |
1259 } | 1297 } |
1260 | 1298 |
1261 if (this.stateMap.length > 0) { | 1299 if (this.stateMap.length > 0) { |
1262 if (this.stateIndex != null) { | 1300 if (this.stateIndex !== null) { |
1263 console.log('NOTE - State already initialise'); | 1301 console.log('NOTE - State already initialise'); |
1264 } | 1302 } |
1265 this.stateIndex = -2; | 1303 this.stateIndex = -2; |
1266 console.log('Starting test...'); | 1304 console.log('Starting test...'); |
1267 } else { | 1305 } else { |
1268 console.log('FATAL - StateMap not correctly constructed. EMPTY_STATE_MAP'); | 1306 console.log('FATAL - StateMap not correctly constructed. EMPTY_STATE_MAP'); |
1269 } | 1307 } |
1270 }; | 1308 }; |
1271 this.advanceState = function () { | 1309 this.advanceState = function () { |
1272 if (this.stateIndex == null) { | 1310 if (this.stateIndex === null) { |
1273 this.initialise(); | 1311 this.initialise(); |
1274 } | 1312 } |
1275 if (this.stateIndex > -2) { | 1313 if (this.stateIndex > -2) { |
1276 storage.update(); | 1314 storage.update(); |
1277 } | 1315 } |
1278 if (this.stateIndex == -2) { | 1316 if (this.stateIndex == -2) { |
1279 this.stateIndex++; | 1317 this.stateIndex++; |
1280 if (this.preTestSurvey != null) { | 1318 if (this.preTestSurvey !== undefined) { |
1281 popup.initState(this.preTestSurvey, storage.globalPreTest); | 1319 popup.initState(this.preTestSurvey, storage.globalPreTest); |
1282 } else { | 1320 } else { |
1283 this.advanceState(); | 1321 this.advanceState(); |
1284 } | 1322 } |
1285 } else if (this.stateIndex == -1) { | 1323 } else if (this.stateIndex == -1) { |
1295 } | 1333 } |
1296 } else if (this.stateIndex == this.stateMap.length) { | 1334 } else if (this.stateIndex == this.stateMap.length) { |
1297 // All test pages complete, post test | 1335 // All test pages complete, post test |
1298 console.log('Ending test ...'); | 1336 console.log('Ending test ...'); |
1299 this.stateIndex++; | 1337 this.stateIndex++; |
1300 if (this.postTestSurvey == null) { | 1338 if (this.postTestSurvey === undefined) { |
1301 this.advanceState(); | 1339 this.advanceState(); |
1302 } else { | 1340 } else { |
1303 popup.initState(this.postTestSurvey, storage.globalPostTest); | 1341 popup.initState(this.postTestSurvey, storage.globalPostTest); |
1304 } | 1342 } |
1305 } else if (this.stateIndex > this.stateMap.length) { | 1343 } else if (this.stateIndex > this.stateMap.length) { |
1306 createProjectSave(specification.projectReturn); | 1344 createProjectSave(specification.projectReturn); |
1307 } else { | 1345 } else { |
1308 popup.hidePopup(); | 1346 popup.hidePopup(); |
1309 if (this.currentStateMap == null) { | 1347 if (this.currentStateMap === null) { |
1310 this.currentStateMap = this.stateMap[this.stateIndex]; | 1348 this.currentStateMap = this.stateMap[this.stateIndex]; |
1311 // Find and extract the outside reference | 1349 // Find and extract the outside reference |
1312 var elements = [], | 1350 var elements = [], |
1313 ref = []; | 1351 ref = []; |
1314 var elem; | 1352 var elem = this.currentStateMap.audioElements.pop(); |
1315 while (elem = this.currentStateMap.audioElements.pop()) { | 1353 while (elem) { |
1316 if (elem.type == "outside-reference") { | 1354 if (elem.type == "outside-reference") { |
1317 ref.push(elem); | 1355 ref.push(elem); |
1318 } else { | 1356 } else { |
1319 elements.push(elem); | 1357 elements.push(elem); |
1320 } | 1358 } |
1359 elem = this.currentStateMap.audioElements.pop(); | |
1321 } | 1360 } |
1322 elements = elements.reverse(); | 1361 elements = elements.reverse(); |
1323 if (this.currentStateMap.randomiseOrder) { | 1362 if (this.currentStateMap.randomiseOrder) { |
1324 elements = randomiseOrder(elements); | 1363 elements = randomiseOrder(elements); |
1325 } | 1364 } |
1326 this.currentStateMap.audioElements = elements.concat(ref); | 1365 this.currentStateMap.audioElements = elements.concat(ref); |
1327 | 1366 |
1328 this.currentStore = storage.testPages[this.stateIndex]; | 1367 this.currentStore = storage.testPages[this.stateIndex]; |
1329 if (this.currentStateMap.preTest != null) { | 1368 if (this.currentStateMap.preTest !== undefined) { |
1330 this.currentStatePosition = 'pre'; | 1369 this.currentStatePosition = 'pre'; |
1331 popup.initState(this.currentStateMap.preTest, storage.testPages[this.stateIndex].preTest); | 1370 popup.initState(this.currentStateMap.preTest, storage.testPages[this.stateIndex].preTest); |
1332 } else { | 1371 } else { |
1333 this.currentStatePosition = 'test'; | 1372 this.currentStatePosition = 'test'; |
1334 } | 1373 } |
1341 break; | 1380 break; |
1342 case 'test': | 1381 case 'test': |
1343 this.currentStatePosition = 'post'; | 1382 this.currentStatePosition = 'post'; |
1344 // Save the data | 1383 // Save the data |
1345 this.testPageCompleted(); | 1384 this.testPageCompleted(); |
1346 if (this.currentStateMap.postTest == null) { | 1385 if (this.currentStateMap.postTest === undefined) { |
1347 this.advanceState(); | 1386 this.advanceState(); |
1348 return; | 1387 return; |
1349 } else { | 1388 } else { |
1350 popup.initState(this.currentStateMap.postTest, storage.testPages[this.stateIndex].postTest); | 1389 popup.initState(this.currentStateMap.postTest, storage.testPages[this.stateIndex].postTest); |
1351 } | 1390 } |
1353 case 'post': | 1392 case 'post': |
1354 this.stateIndex++; | 1393 this.stateIndex++; |
1355 this.currentStateMap = null; | 1394 this.currentStateMap = null; |
1356 this.advanceState(); | 1395 this.advanceState(); |
1357 break; | 1396 break; |
1358 }; | 1397 } |
1359 } | 1398 } |
1360 }; | 1399 }; |
1361 | 1400 |
1362 this.testPageCompleted = function () { | 1401 this.testPageCompleted = function () { |
1363 // Function called each time a test page has been completed | 1402 // Function called each time a test page has been completed |
1371 testTime.textContent = audioEngineContext.timer.testDuration; | 1410 testTime.textContent = audioEngineContext.timer.testDuration; |
1372 metric.appendChild(testTime); | 1411 metric.appendChild(testTime); |
1373 } | 1412 } |
1374 | 1413 |
1375 var audioObjects = audioEngineContext.audioObjects; | 1414 var audioObjects = audioEngineContext.audioObjects; |
1376 for (var ao of audioEngineContext.audioObjects) { | 1415 audioEngineContext.audioObjects.forEach(function (ao) { |
1377 ao.exportXMLDOM(); | 1416 ao.exportXMLDOM(); |
1378 } | 1417 }); |
1379 for (var element of interfaceContext.commentQuestions) { | 1418 interfaceContext.commentQuestions.forEach(function (element) { |
1380 element.exportXMLDOM(storePoint); | 1419 element.exportXMLDOM(storePoint); |
1381 } | 1420 }); |
1382 pageXMLSave(storePoint.XMLDOM, this.currentStateMap); | 1421 pageXMLSave(storePoint.XMLDOM, this.currentStateMap); |
1383 storePoint.complete(); | 1422 storePoint.complete(); |
1384 }; | 1423 }; |
1385 | 1424 |
1386 this.getCurrentTestPage = function () { | 1425 this.getCurrentTestPage = function () { |
1387 if (this.stateIndex >= 0 && this.stateIndex < this.stateMap.length) { | 1426 if (this.stateIndex >= 0 && this.stateIndex < this.stateMap.length) { |
1388 return this.currentStateMap; | 1427 return this.currentStateMap; |
1389 } else { | 1428 } else { |
1390 return null; | 1429 return null; |
1391 } | 1430 } |
1392 } | 1431 }; |
1393 this.getCurrentTestPageStore = function () { | 1432 this.getCurrentTestPageStore = function () { |
1394 if (this.stateIndex >= 0 && this.stateIndex < this.stateMap.length) { | 1433 if (this.stateIndex >= 0 && this.stateIndex < this.stateMap.length) { |
1395 return this.currentStore; | 1434 return this.currentStore; |
1396 } else { | 1435 } else { |
1397 return null; | 1436 return null; |
1398 } | 1437 } |
1399 } | 1438 }; |
1400 } | 1439 } |
1401 | 1440 |
1402 function AudioEngine(specification) { | 1441 function AudioEngine(specification) { |
1403 | 1442 |
1404 // Create two output paths, the main outputGain and fooGain. | 1443 // Create two output paths, the main outputGain and fooGain. |
1449 if (this.status >= 2) { | 1488 if (this.status >= 2) { |
1450 this.status = 3; | 1489 this.status = 3; |
1451 } | 1490 } |
1452 for (var i = 0; i < this.users.length; i++) { | 1491 for (var i = 0; i < this.users.length; i++) { |
1453 this.users[i].state = 1; | 1492 this.users[i].state = 1; |
1454 if (this.users[i].interfaceDOM != null) { | 1493 if (this.users[i].interfaceDOM !== null) { |
1455 this.users[i].bufferLoaded(this); | 1494 this.users[i].bufferLoaded(this); |
1456 } | 1495 } |
1457 } | 1496 } |
1458 }; | 1497 }; |
1459 this.setUrls = function (obj) { | 1498 this.setUrls = function (obj) { |
1477 if (urls[i].url == checkUrl) { | 1516 if (urls[i].url == checkUrl) { |
1478 return true; | 1517 return true; |
1479 } | 1518 } |
1480 } | 1519 } |
1481 return false; | 1520 return false; |
1482 } | 1521 }; |
1483 this.getMedia = function () { | 1522 this.getMedia = function () { |
1484 var self = this; | 1523 var self = this; |
1485 var currentUrlIndex = 0; | 1524 var currentUrlIndex = 0; |
1486 | 1525 |
1487 function get(fqurl) { | 1526 function get(fqurl) { |
1521 self.status = 2; | 1560 self.status = 2; |
1522 calculateLoudness(self, "I"); | 1561 calculateLoudness(self, "I"); |
1523 return true; | 1562 return true; |
1524 }, function (e) { | 1563 }, function (e) { |
1525 var waveObj = new WAVE(); | 1564 var waveObj = new WAVE(); |
1526 if (waveObj.open(response) == 0) { | 1565 if (waveObj.open(response) === 0) { |
1527 self.buffer = audioContext.createBuffer(waveObj.num_channels, waveObj.num_samples, waveObj.sample_rate); | 1566 self.buffer = audioContext.createBuffer(waveObj.num_channels, waveObj.num_samples, waveObj.sample_rate); |
1528 for (var c = 0; c < waveObj.num_channels; c++) { | 1567 for (var c = 0; c < waveObj.num_channels; c++) { |
1529 var buffer_ptr = self.buffer.getChannelData(c); | 1568 var buffer_ptr = self.buffer.getChannelData(c); |
1530 for (var n = 0; n < waveObj.num_samples; n++) { | 1569 for (var n = 0; n < waveObj.num_samples; n++) { |
1531 buffer_ptr[n] = waveObj.decoded_data[c][n]; | 1570 buffer_ptr[n] = waveObj.decoded_data[c][n]; |
1532 } | 1571 } |
1533 } | 1572 } |
1534 | |
1535 delete waveObj; | |
1536 } | 1573 } |
1537 if (self.buffer != undefined) { | 1574 if (self.buffer !== undefined) { |
1538 self.status = 2; | 1575 self.status = 2; |
1539 calculateLoudness(self, "I"); | 1576 calculateLoudness(self, "I"); |
1540 return true; | 1577 return true; |
1541 } | 1578 } |
1579 waveObj = undefined; | |
1542 return false; | 1580 return false; |
1543 }); | 1581 }); |
1544 } | 1582 } |
1545 | 1583 |
1546 // Create callback for any error in loading | 1584 // Create callback for any error in loading |
1547 function processError() { | 1585 function processError() { |
1548 this.status = -1; | 1586 this.status = -1; |
1549 for (var i = 0; i < this.users.length; i++) { | 1587 for (var i = 0; i < this.users.length; i++) { |
1550 this.users[i].state = -1; | 1588 this.users[i].state = -1; |
1551 if (this.users[i].interfaceDOM != null) { | 1589 if (this.users[i].interfaceDOM !== null) { |
1552 this.users[i].bufferLoaded(this); | 1590 this.users[i].bufferLoaded(this); |
1553 } | 1591 } |
1554 } | 1592 } |
1555 interfaceContext.lightbox.post("Error", "Could not load resource " + urls[currentUrlIndex].url); | 1593 interfaceContext.lightbox.post("Error", "Could not load resource " + urls[currentUrlIndex].url); |
1556 } | 1594 } |
1557 | 1595 |
1558 function progressCallback(event) { | 1596 function progressCallback(event) { |
1559 if (event.lengthComputable) { | 1597 if (event.lengthComputable) { |
1560 this.progress = event.loaded / event.total; | 1598 this.progress = event.loaded / event.total; |
1561 for (var i = 0; i < this.users.length; i++) { | 1599 for (var i = 0; i < this.users.length; i++) { |
1562 if (this.users[i].interfaceDOM != null) { | 1600 if (this.users[i].interfaceDOM !== null) { |
1563 if (typeof this.users[i].interfaceDOM.updateLoading === "function") { | 1601 if (typeof this.users[i].interfaceDOM.updateLoading === "function") { |
1564 this.users[i].interfaceDOM.updateLoading(this.progress * 100); | 1602 this.users[i].interfaceDOM.updateLoading(this.progress * 100); |
1565 } | 1603 } |
1566 } | 1604 } |
1567 } | 1605 } |
1568 } | 1606 } |
1569 }; | 1607 } |
1570 | 1608 |
1571 this.progress = 0; | 1609 this.progress = 0; |
1572 this.status = 1; | 1610 this.status = 1; |
1573 currentUrlIndex = 0; | 1611 currentUrlIndex = 0; |
1574 get(urls[0].url).then(processAudio.bind(self)).catch(getNextURL.bind(self)); | 1612 get(urls[0].url).then(processAudio.bind(self)).catch(getNextURL.bind(self)); |
1575 }; | 1613 }; |
1576 | 1614 |
1577 this.registerAudioObject = function (audioObject) { | 1615 this.registerAudioObject = function (audioObject) { |
1578 // Called by an audioObject to register to the buffer for use | 1616 // Called by an audioObject to register to the buffer for use |
1579 // First check if already in the register pool | 1617 // First check if already in the register pool |
1580 for (var objects of this.users) { | 1618 this.users.forEach(function (object) { |
1581 if (audioObject.id == objects.id) { | 1619 if (audioObject.id == object.id) { |
1582 return 0; | 1620 return 0; |
1583 } | 1621 } |
1584 } | 1622 }); |
1585 this.users.push(audioObject); | 1623 this.users.push(audioObject); |
1586 if (this.status == 3 || this.status == -1) { | 1624 if (this.status == 3 || this.status == -1) { |
1587 // The buffer is already ready, trigger bufferLoaded | 1625 // The buffer is already ready, trigger bufferLoaded |
1588 audioObject.bufferLoaded(this); | 1626 audioObject.bufferLoaded(this); |
1589 } | 1627 } |
1590 }; | 1628 }; |
1591 | 1629 |
1592 this.copyBuffer = function (preSilenceTime, postSilenceTime) { | 1630 this.copyBuffer = function (preSilenceTime, postSilenceTime) { |
1593 // Copies the entire bufferObj. | 1631 // Copies the entire bufferObj. |
1594 if (preSilenceTime == undefined) { | 1632 if (preSilenceTime === undefined) { |
1595 preSilenceTime = 0; | 1633 preSilenceTime = 0; |
1596 } | 1634 } |
1597 if (postSilenceTime == undefined) { | 1635 if (postSilenceTime === undefined) { |
1598 postSilenceTime = 0; | 1636 postSilenceTime = 0; |
1599 } | 1637 } |
1600 var preSilenceSamples = secondsToSamples(preSilenceTime, this.buffer.sampleRate); | 1638 var preSilenceSamples = secondsToSamples(preSilenceTime, this.buffer.sampleRate); |
1601 var postSilenceSamples = secondsToSamples(postSilenceTime, this.buffer.sampleRate); | 1639 var postSilenceSamples = secondsToSamples(postSilenceTime, this.buffer.sampleRate); |
1602 var newLength = this.buffer.length + preSilenceSamples + postSilenceSamples; | 1640 var newLength = this.buffer.length + preSilenceSamples + postSilenceSamples; |
1603 var copybuffer = audioContext.createBuffer(this.buffer.numberOfChannels, newLength, this.buffer.sampleRate); | 1641 var copybuffer = audioContext.createBuffer(this.buffer.numberOfChannels, newLength, this.buffer.sampleRate); |
1642 var c; | |
1604 // Now we can use some efficient background copy schemes if we are just padding the end | 1643 // Now we can use some efficient background copy schemes if we are just padding the end |
1605 if (preSilenceSamples == 0 && typeof copybuffer.copyToChannel == "function") { | 1644 if (preSilenceSamples === 0 && typeof copybuffer.copyToChannel === "function") { |
1606 for (var c = 0; c < this.buffer.numberOfChannels; c++) { | 1645 for (c = 0; c < this.buffer.numberOfChannels; c++) { |
1607 copybuffer.copyToChannel(this.buffer.getChannelData(c), c); | 1646 copybuffer.copyToChannel(this.buffer.getChannelData(c), c); |
1608 } | 1647 } |
1609 } else { | 1648 } else { |
1610 for (var c = 0; c < this.buffer.numberOfChannels; c++) { | 1649 for (c = 0; c < this.buffer.numberOfChannels; c++) { |
1611 var src = this.buffer.getChannelData(c); | 1650 var src = this.buffer.getChannelData(c); |
1612 var dst = copybuffer.getChannelData(c); | 1651 var dst = copybuffer.getChannelData(c); |
1613 for (var n = 0; n < src.length; n++) | 1652 for (var n = 0; n < src.length; n++) |
1614 dst[n + preSilenceSamples] = src[n]; | 1653 dst[n + preSilenceSamples] = src[n]; |
1615 } | 1654 } |
1616 } | 1655 } |
1617 // Copy in the rest of the buffer information | 1656 // Copy in the rest of the buffer information |
1618 copybuffer.lufs = this.buffer.lufs; | 1657 copybuffer.lufs = this.buffer.lufs; |
1619 copybuffer.playbackGain = this.buffer.playbackGain; | 1658 copybuffer.playbackGain = this.buffer.playbackGain; |
1620 return copybuffer; | 1659 return copybuffer; |
1621 } | 1660 }; |
1622 | 1661 |
1623 this.cropBuffer = function (startTime, stopTime) { | 1662 this.cropBuffer = function (startTime, stopTime) { |
1624 // Copy and return the cropped buffer | 1663 // Copy and return the cropped buffer |
1625 var start_sample = Math.floor(startTime * this.buffer.sampleRate); | 1664 var start_sample = Math.floor(startTime * this.buffer.sampleRate); |
1626 var stop_sample = Math.floor(stopTime * this.buffer.sampleRate); | 1665 var stop_sample = Math.floor(stopTime * this.buffer.sampleRate); |
1637 for (var n = 0; n < newLength; n++) | 1676 for (var n = 0; n < newLength; n++) |
1638 dst[n] = buffer[n + start_sample]; | 1677 dst[n] = buffer[n + start_sample]; |
1639 } | 1678 } |
1640 } | 1679 } |
1641 return copybuffer; | 1680 return copybuffer; |
1642 } | 1681 }; |
1643 }; | 1682 }; |
1644 | 1683 |
1645 this.loadPageData = function (page) { | 1684 this.loadPageData = function (page) { |
1646 // Load the URL from pages | 1685 // Load the URL from pages |
1647 for (var element of page.audioElements) { | 1686 function loadAudioElementData(element) { |
1648 var URL = page.hostURL + element.url; | 1687 var URL = page.hostURL + element.url; |
1649 var buffer = null; | 1688 var buffer = this.buffers.find(function (buffObj) { |
1650 for (var buffObj of this.buffers) { | 1689 return buffObj.hasUrl(URL); |
1651 if (buffObj.hasUrl(URL)) { | 1690 }); |
1652 buffer = buffObj; | 1691 if (buffer === undefined) { |
1653 break; | |
1654 } | |
1655 } | |
1656 if (buffer == null) { | |
1657 buffer = new this.bufferObj(); | 1692 buffer = new this.bufferObj(); |
1658 var urls = [{ | 1693 var urls = [{ |
1659 url: URL, | 1694 url: URL, |
1660 sampleRate: element.sampleRate | 1695 sampleRate: element.sampleRate |
1661 }]; | 1696 }]; |
1668 buffer.setUrls(urls); | 1703 buffer.setUrls(urls); |
1669 buffer.getMedia(); | 1704 buffer.getMedia(); |
1670 this.buffers.push(buffer); | 1705 this.buffers.push(buffer); |
1671 } | 1706 } |
1672 } | 1707 } |
1673 }; | 1708 page.audioElements.forEach(loadAudioElementData, this); |
1709 }; | |
1710 | |
1711 function playNormal(id) { | |
1712 var playTime = audioContext.currentTime + 0.1; | |
1713 var stopTime = playTime + specification.crossFade; | |
1714 this.audioObjects.forEach(function (ao) { | |
1715 if (ao.id === id) { | |
1716 ao.play(playTime); | |
1717 } else { | |
1718 ao.stop(stopTime); | |
1719 } | |
1720 }); | |
1721 } | |
1722 | |
1723 function playLoopSync(id) { | |
1724 var playTime = audioContext.currentTime + 0.1; | |
1725 var stopTime = playTime + specification.crossFade; | |
1726 this.audioObjects.forEach(function (ao) { | |
1727 ao.play(playTime); | |
1728 if (ao.id === id) { | |
1729 ao.loopStart(playTime); | |
1730 } else { | |
1731 ao.loopStop(stopTime); | |
1732 } | |
1733 }); | |
1734 } | |
1674 | 1735 |
1675 this.play = function (id) { | 1736 this.play = function (id) { |
1676 // Start the timer and set the audioEngine state to playing (1) | 1737 // Start the timer and set the audioEngine state to playing (1) |
1677 if (this.status == 0) { | 1738 if (typeof id !== "number" || id < 0 || id > this.audioObjects.length) { |
1678 // Check if all audioObjects are ready | 1739 throw ('FATAL - Passed id was undefined - AudioEngineContext.play(id)'); |
1679 this.bufferReady(id); | 1740 } |
1680 } else { | 1741 if (this.status === 1) { |
1681 this.status = 1; | |
1682 } | |
1683 if (this.status == 1) { | |
1684 this.timer.startTest(); | 1742 this.timer.startTest(); |
1685 if (id == undefined) { | 1743 interfaceContext.playhead.setTimePerPixel(this.audioObjects[id]); |
1686 id = -1; | |
1687 console.error('FATAL - Passed id was undefined - AudioEngineContext.play(id)'); | |
1688 return; | |
1689 } else { | |
1690 interfaceContext.playhead.setTimePerPixel(this.audioObjects[id]); | |
1691 } | |
1692 var setTime = audioContext.currentTime; | |
1693 if (this.synchPlayback && this.loopPlayback) { | 1744 if (this.synchPlayback && this.loopPlayback) { |
1694 // Traditional looped playback | 1745 // Traditional looped playback |
1695 for (var i = 0; i < this.audioObjects.length; i++) { | 1746 playLoopSync.call(this, id); |
1696 this.audioObjects[i].play(audioContext.currentTime); | |
1697 if (id == i) { | |
1698 this.audioObjects[i].loopStart(setTime); | |
1699 } else { | |
1700 this.audioObjects[i].loopStop(setTime + specification.crossFade); | |
1701 } | |
1702 } | |
1703 } else { | 1747 } else { |
1704 for (var i = 0; i < this.audioObjects.length; i++) { | 1748 if (this.bufferReady(id) === false) { |
1705 if (i != id) { | 1749 console.log("Cannot play. Buffer not ready"); |
1706 this.audioObjects[i].stop(setTime + specification.crossFade); | 1750 return; |
1707 } else if (i == id) { | 1751 } |
1708 this.audioObjects[id].play(setTime); | 1752 playNormal.call(this, id); |
1709 } | |
1710 } | |
1711 } | 1753 } |
1712 interfaceContext.playhead.start(); | 1754 interfaceContext.playhead.start(); |
1713 } | 1755 } |
1714 }; | 1756 }; |
1715 | 1757 |
1716 this.stop = function () { | 1758 this.stop = function () { |
1717 // Send stop and reset command to all playback buffers | 1759 // Send stop and reset command to all playback buffers |
1718 if (this.status == 1) { | 1760 if (this.status == 1) { |
1719 var setTime = audioContext.currentTime + 0.1; | 1761 var setTime = audioContext.currentTime + 0.1; |
1720 for (var i = 0; i < this.audioObjects.length; i++) { | 1762 this.audioObjects.forEach(function (a) { |
1721 this.audioObjects[i].stop(setTime); | 1763 a.stop(setTime); |
1722 } | 1764 }); |
1723 interfaceContext.playhead.stop(); | 1765 interfaceContext.playhead.stop(); |
1724 } | 1766 } |
1725 }; | 1767 }; |
1726 | 1768 |
1727 this.newTrack = function (element) { | 1769 this.newTrack = function (element) { |
1728 // Pull data from given URL into new audio buffer | 1770 // Pull data from given URL into new audio buffer |
1729 // URLs must either be from the same source OR be setup to 'Access-Control-Allow-Origin' | 1771 // URLs must either be from the same source OR be setup to 'Access-Control-Allow-Origin' |
1730 | 1772 |
1731 // Create the audioObject with ID of the new track length; | 1773 // Create the audioObject with ID of the new track length; |
1732 audioObjectId = this.audioObjects.length; | 1774 var audioObjectId = this.audioObjects.length; |
1733 this.audioObjects[audioObjectId] = new audioObject(audioObjectId); | 1775 this.audioObjects[audioObjectId] = new audioObject(audioObjectId); |
1734 | 1776 |
1735 // Check if audioObject buffer is currently stored by full URL | 1777 // Check if audioObject buffer is currently stored by full URL |
1736 var URL = testState.currentStateMap.hostURL + element.url; | 1778 var URL = testState.currentStateMap.hostURL + element.url; |
1737 var buffer = null; | 1779 var buffer = this.buffers.find(function (buffObj) { |
1738 for (var i = 0; i < this.buffers.length; i++) { | 1780 return buffObj.hasUrl(URL); |
1739 if (this.buffers[i].hasUrl(URL)) { | 1781 }); |
1740 buffer = this.buffers[i]; | 1782 if (buffer === undefined) { |
1741 break; | |
1742 } | |
1743 } | |
1744 if (buffer == null) { | |
1745 console.log("[WARN]: Buffer was not loaded in pre-test! " + URL); | 1783 console.log("[WARN]: Buffer was not loaded in pre-test! " + URL); |
1746 buffer = new this.bufferObj(); | 1784 buffer = new this.bufferObj(); |
1747 this.buffers.push(buffer); | 1785 this.buffers.push(buffer); |
1748 buffer.getMedia(URL); | 1786 buffer.getMedia(URL); |
1749 } | 1787 } |
1765 this.pageStore = store; | 1803 this.pageStore = store; |
1766 this.pageSpecification = audioHolderObject; | 1804 this.pageSpecification = audioHolderObject; |
1767 this.status = 0; | 1805 this.status = 0; |
1768 this.audioObjectsReady = false; | 1806 this.audioObjectsReady = false; |
1769 this.metric.reset(); | 1807 this.metric.reset(); |
1770 for (var i = 0; i < this.buffers.length; i++) { | 1808 this.buffers.forEach(function (buffer) { |
1771 this.buffers[i].users = []; | 1809 buffer.users = []; |
1772 } | 1810 }); |
1773 this.audioObjects = []; | 1811 this.audioObjects = []; |
1774 this.timer = new timer(); | 1812 this.timer = new timer(); |
1775 this.loopPlayback = audioHolderObject.loop; | 1813 this.loopPlayback = audioHolderObject.loop; |
1776 this.synchPlayback = audioHolderObject.synchronous; | 1814 this.synchPlayback = audioHolderObject.synchronous; |
1777 }; | 1815 }; |
1778 | 1816 |
1779 this.checkAllPlayed = function () { | 1817 this.checkAllPlayed = function () { |
1780 arr = []; | 1818 var arr = []; |
1781 for (var id = 0; id < this.audioObjects.length; id++) { | 1819 for (var id = 0; id < this.audioObjects.length; id++) { |
1782 if (this.audioObjects[id].metric.wasListenedTo == false) { | 1820 if (this.audioObjects[id].metric.wasListenedTo === false) { |
1783 arr.push(this.audioObjects[id].id); | 1821 arr.push(this.audioObjects[id].id); |
1784 } | 1822 } |
1785 } | 1823 } |
1786 return arr; | 1824 return arr; |
1787 }; | 1825 }; |
1788 | 1826 |
1789 this.checkAllReady = function () { | 1827 this.checkAllReady = function () { |
1790 var ready = true; | 1828 var ready = true; |
1791 for (var i = 0; i < this.audioObjects.length; i++) { | 1829 for (var i = 0; i < this.audioObjects.length; i++) { |
1792 if (this.audioObjects[i].state == 0) { | 1830 if (this.audioObjects[i].state === 0) { |
1793 // Track not ready | 1831 // Track not ready |
1794 console.log('WAIT -- audioObject ' + i + ' not ready yet!'); | 1832 console.log('WAIT -- audioObject ' + i + ' not ready yet!'); |
1795 ready = false; | 1833 ready = false; |
1796 }; | 1834 } |
1797 } | 1835 } |
1798 return ready; | 1836 return ready; |
1799 }; | 1837 }; |
1800 | 1838 |
1801 this.setSynchronousLoop = function () { | 1839 this.setSynchronousLoop = function () { |
1808 duration = this.audioObjects[i].buffer.buffer.duration; | 1846 duration = this.audioObjects[i].buffer.buffer.duration; |
1809 maxId = i; | 1847 maxId = i; |
1810 } | 1848 } |
1811 } | 1849 } |
1812 // Extract the audio and zero-pad | 1850 // Extract the audio and zero-pad |
1813 for (var ao of this.audioObjects) { | 1851 this.audioObjects.forEach(function (ao) { |
1814 if (ao.buffer.buffer.duration !== duration) { | 1852 if (ao.buffer.buffer.duration !== duration) { |
1815 ao.buffer.buffer = ao.buffer.copyBuffer(0, duration - ao.buffer.buffer.duration); | 1853 ao.buffer.buffer = ao.buffer.copyBuffer(0, duration - ao.buffer.buffer.duration); |
1816 } | 1854 } |
1817 } | 1855 }); |
1818 }; | 1856 }; |
1819 | 1857 |
1820 this.bufferReady = function (id) { | 1858 this.bufferReady = function (id) { |
1821 if (this.checkAllReady()) { | 1859 if (this.checkAllReady()) { |
1822 if (this.synchPlayback) { | 1860 if (this.synchPlayback) { |
1824 } | 1862 } |
1825 this.status = 1; | 1863 this.status = 1; |
1826 return true; | 1864 return true; |
1827 } | 1865 } |
1828 return false; | 1866 return false; |
1829 } | |
1830 | |
1831 this.exportXML = function () { | |
1832 | |
1833 }; | 1867 }; |
1834 | 1868 |
1835 } | 1869 } |
1836 | 1870 |
1837 function audioObject(id) { | 1871 function audioObject(id) { |
1838 // The main buffer object with common control nodes to the AudioEngine | 1872 // The main buffer object with common control nodes to the AudioEngine |
1839 | 1873 |
1840 this.specification; | 1874 this.specification = undefined; |
1841 this.id = id; | 1875 this.id = id; |
1842 this.state = 0; // 0 - no data, 1 - ready | 1876 this.state = 0; // 0 - no data, 1 - ready |
1843 this.url = null; // Hold the URL given for the output back to the results. | 1877 this.url = null; // Hold the URL given for the output back to the results. |
1844 this.metric = new metricTracker(this); | 1878 this.metric = new metricTracker(this); |
1845 this.storeDOM = null; | 1879 this.storeDOM = null; |
1858 this.outputGain.connect(audioEngineContext.outputGain); | 1892 this.outputGain.connect(audioEngineContext.outputGain); |
1859 audioEngineContext.nullBufferSource.connect(this.outputGain); | 1893 audioEngineContext.nullBufferSource.connect(this.outputGain); |
1860 | 1894 |
1861 // the audiobuffer is not designed for multi-start playback | 1895 // the audiobuffer is not designed for multi-start playback |
1862 // When stopeed, the buffer node is deleted and recreated with the stored buffer. | 1896 // When stopeed, the buffer node is deleted and recreated with the stored buffer. |
1863 this.buffer; | 1897 this.buffer = undefined; |
1864 | 1898 |
1865 this.bufferLoaded = function (callee) { | 1899 this.bufferLoaded = function (callee) { |
1866 // Called by the associated buffer when it has finished loading, will then 'bind' the buffer to the | 1900 // Called by the associated buffer when it has finished loading, will then 'bind' the buffer to the |
1867 // audioObject and trigger the interfaceDOM.enable() function for user feedback | 1901 // audioObject and trigger the interfaceDOM.enable() function for user feedback |
1868 if (callee.status == -1) { | 1902 if (callee.status == -1) { |
1869 // ERROR | 1903 // ERROR |
1870 this.state = -1; | 1904 this.state = -1; |
1871 if (this.interfaceDOM != null) { | 1905 if (this.interfaceDOM !== null) { |
1872 this.interfaceDOM.error(); | 1906 this.interfaceDOM.error(); |
1873 } | 1907 } |
1874 this.buffer = callee; | 1908 this.buffer = callee; |
1875 return; | 1909 return; |
1876 } | 1910 } |
1880 var startTime = this.specification.startTime; | 1914 var startTime = this.specification.startTime; |
1881 var stopTime = this.specification.stopTime; | 1915 var stopTime = this.specification.stopTime; |
1882 var copybuffer = new callee.constructor(); | 1916 var copybuffer = new callee.constructor(); |
1883 | 1917 |
1884 copybuffer.buffer = callee.cropBuffer(startTime || 0, stopTime || callee.buffer.duration); | 1918 copybuffer.buffer = callee.cropBuffer(startTime || 0, stopTime || callee.buffer.duration); |
1885 if (preSilenceTime != 0 || postSilenceTime != 0) { | 1919 if (preSilenceTime !== 0 || postSilenceTime !== 0) { |
1886 copybuffer.buffer = copybuffer.copyBuffer(preSilenceTime, postSilenceTime); | 1920 copybuffer.buffer = copybuffer.copyBuffer(preSilenceTime, postSilenceTime); |
1887 } | 1921 } |
1888 | 1922 |
1889 copybuffer.buffer.lufs = callee.buffer.lufs; | 1923 copybuffer.buffer.lufs = callee.buffer.lufs; |
1890 this.buffer = copybuffer; | 1924 this.buffer = copybuffer; |
1893 if (typeof targetLUFS === "number" && isFinite(targetLUFS)) { | 1927 if (typeof targetLUFS === "number" && isFinite(targetLUFS)) { |
1894 this.buffer.buffer.playbackGain = decibelToLinear(targetLUFS - this.buffer.buffer.lufs); | 1928 this.buffer.buffer.playbackGain = decibelToLinear(targetLUFS - this.buffer.buffer.lufs); |
1895 } else { | 1929 } else { |
1896 this.buffer.buffer.playbackGain = 1.0; | 1930 this.buffer.buffer.playbackGain = 1.0; |
1897 } | 1931 } |
1898 if (this.interfaceDOM != null) { | 1932 if (this.interfaceDOM !== null) { |
1899 this.interfaceDOM.enable(); | 1933 this.interfaceDOM.enable(); |
1900 } | 1934 } |
1901 this.onplayGain = decibelToLinear(this.specification.gain) * (this.buffer.buffer.playbackGain || 1.0); | 1935 this.onplayGain = decibelToLinear(this.specification.gain) * (this.buffer.buffer.playbackGain || 1.0); |
1902 this.storeDOM.setAttribute('playGain', linearToDecibel(this.onplayGain)); | 1936 this.storeDOM.setAttribute('playGain', linearToDecibel(this.onplayGain)); |
1903 this.state = 1; | 1937 this.state = 1; |
1922 this.metric.startListening(audioEngineContext.timer.getTestTime()); | 1956 this.metric.startListening(audioEngineContext.timer.getTestTime()); |
1923 this.interfaceDOM.startPlayback(); | 1957 this.interfaceDOM.startPlayback(); |
1924 }; | 1958 }; |
1925 | 1959 |
1926 this.loopStop = function (setTime) { | 1960 this.loopStop = function (setTime) { |
1927 if (this.outputGain.gain.value != 0.0) { | 1961 if (this.outputGain.gain.value !== 0.0) { |
1928 this.outputGain.gain.linearRampToValueAtTime(0.0, setTime); | 1962 this.outputGain.gain.linearRampToValueAtTime(0.0, setTime); |
1929 this.metric.stopListening(audioEngineContext.timer.getTestTime()); | 1963 this.metric.stopListening(audioEngineContext.timer.getTestTime()); |
1930 } | 1964 } |
1931 this.interfaceDOM.stopPlayback(); | 1965 this.interfaceDOM.stopPlayback(); |
1932 }; | 1966 }; |
1933 | 1967 |
1934 this.play = function (startTime) { | 1968 this.play = function (startTime) { |
1935 if (this.bufferNode == undefined && this.buffer.buffer != undefined) { | 1969 if (this.bufferNode === undefined && this.buffer.buffer !== undefined) { |
1936 this.bufferNode = audioContext.createBufferSource(); | 1970 this.bufferNode = audioContext.createBufferSource(); |
1937 this.bufferNode.owner = this; | 1971 this.bufferNode.owner = this; |
1938 this.bufferNode.connect(this.outputGain); | 1972 this.bufferNode.connect(this.outputGain); |
1939 this.bufferNode.buffer = this.buffer.buffer; | 1973 this.bufferNode.buffer = this.buffer.buffer; |
1940 this.bufferNode.loop = audioEngineContext.loopPlayback; | 1974 this.bufferNode.loop = audioEngineContext.loopPlayback; |
1941 this.bufferNode.onended = function (event) { | 1975 this.bufferNode.onended = function (event) { |
1942 // Safari does not like using 'this' to reference the calling object! | 1976 // Safari does not like using 'this' to reference the calling object! |
1943 //event.currentTarget.owner.metric.stopListening(audioEngineContext.timer.getTestTime(),event.currentTarget.owner.getCurrentPosition()); | 1977 //event.currentTarget.owner.metric.stopListening(audioEngineContext.timer.getTestTime(),event.currentTarget.owner.getCurrentPosition()); |
1944 if (event.currentTarget != null) { | 1978 if (event.currentTarget !== null) { |
1945 event.currentTarget.owner.stop(audioContext.currentTime + 1); | 1979 event.currentTarget.owner.stop(audioContext.currentTime + 1); |
1946 } | 1980 } |
1947 }; | 1981 }; |
1948 this.outputGain.gain.cancelScheduledValues(audioContext.currentTime); | 1982 this.outputGain.gain.cancelScheduledValues(audioContext.currentTime); |
1949 if (!audioEngineContext.loopPlayback || !audioEngineContext.synchPlayback) { | 1983 if (!audioEngineContext.loopPlayback || !audioEngineContext.synchPlayback) { |
1964 } | 1998 } |
1965 }; | 1999 }; |
1966 | 2000 |
1967 this.stop = function (stopTime) { | 2001 this.stop = function (stopTime) { |
1968 this.outputGain.gain.cancelScheduledValues(audioContext.currentTime); | 2002 this.outputGain.gain.cancelScheduledValues(audioContext.currentTime); |
1969 if (this.bufferNode != undefined) { | 2003 if (this.bufferNode !== undefined) { |
1970 this.metric.stopListening(audioEngineContext.timer.getTestTime(), this.getCurrentPosition()); | 2004 this.metric.stopListening(audioEngineContext.timer.getTestTime(), this.getCurrentPosition()); |
1971 this.bufferNode.stop(stopTime); | 2005 this.bufferNode.stop(stopTime); |
1972 this.bufferNode = undefined; | 2006 this.bufferNode = undefined; |
1973 } | 2007 } |
1974 this.outputGain.gain.linearRampToValueAtTime(0.0, stopTime); | 2008 this.outputGain.gain.linearRampToValueAtTime(0.0, stopTime); |
1975 this.interfaceDOM.stopPlayback(); | 2009 this.interfaceDOM.stopPlayback(); |
1976 }; | 2010 }; |
1977 | 2011 |
1978 this.getCurrentPosition = function () { | 2012 this.getCurrentPosition = function () { |
1979 var time = audioEngineContext.timer.getTestTime(); | 2013 var time = audioEngineContext.timer.getTestTime(); |
1980 if (this.bufferNode != undefined) { | 2014 if (this.bufferNode !== undefined) { |
1981 var position = (time - this.bufferNode.playbackStartTime) % this.buffer.buffer.duration; | 2015 var position = (time - this.bufferNode.playbackStartTime) % this.buffer.buffer.duration; |
1982 if (isNaN(position)) { | 2016 if (isNaN(position)) { |
1983 return 0; | 2017 return 0; |
1984 } | 2018 } |
1985 return position; | 2019 return position; |
1995 file.setAttribute('sampleCount', this.buffer.buffer.length); | 2029 file.setAttribute('sampleCount', this.buffer.buffer.length); |
1996 file.setAttribute('duration', this.buffer.buffer.duration); | 2030 file.setAttribute('duration', this.buffer.buffer.duration); |
1997 this.storeDOM.appendChild(file); | 2031 this.storeDOM.appendChild(file); |
1998 if (this.specification.type != 'outside-reference') { | 2032 if (this.specification.type != 'outside-reference') { |
1999 var interfaceXML = this.interfaceDOM.exportXMLDOM(this); | 2033 var interfaceXML = this.interfaceDOM.exportXMLDOM(this); |
2000 if (interfaceXML != null) { | 2034 if (interfaceXML !== null) { |
2001 if (interfaceXML.length == undefined) { | 2035 if (interfaceXML.length === undefined) { |
2002 this.storeDOM.appendChild(interfaceXML); | 2036 this.storeDOM.appendChild(interfaceXML); |
2003 } else { | 2037 } else { |
2004 for (var i = 0; i < interfaceXML.length; i++) { | 2038 for (var i = 0; i < interfaceXML.length; i++) { |
2005 this.storeDOM.appendChild(interfaceXML[i]); | 2039 this.storeDOM.appendChild(interfaceXML[i]); |
2006 } | 2040 } |
2007 } | 2041 } |
2008 } | 2042 } |
2009 if (this.commentDOM != null) { | 2043 if (this.commentDOM !== null) { |
2010 this.storeDOM.appendChild(this.commentDOM.exportXMLDOM(this)); | 2044 this.storeDOM.appendChild(this.commentDOM.exportXMLDOM(this)); |
2011 } | 2045 } |
2012 } | 2046 } |
2013 var nodes = this.metric.exportXMLDOM(); | 2047 this.metric.exportXMLDOM(this.storeDOM.getElementsByTagName('metric')[0]); |
2014 var mroot = this.storeDOM.getElementsByTagName('metric')[0]; | |
2015 for (var i = 0; i < nodes.length; i++) { | |
2016 mroot.appendChild(nodes[i]); | |
2017 } | |
2018 }; | 2048 }; |
2019 } | 2049 } |
2020 | 2050 |
2021 function timer() { | 2051 function timer() { |
2022 /* Timer object used in audioEngine to keep track of session timings | 2052 /* Timer object used in audioEngine to keep track of session timings |
2025 this.testStarted = false; | 2055 this.testStarted = false; |
2026 this.testStartTime = 0; | 2056 this.testStartTime = 0; |
2027 this.testDuration = 0; | 2057 this.testDuration = 0; |
2028 this.minimumTestTime = 0; // No minimum test time | 2058 this.minimumTestTime = 0; // No minimum test time |
2029 this.startTest = function () { | 2059 this.startTest = function () { |
2030 if (this.testStarted == false) { | 2060 if (this.testStarted === false) { |
2031 this.testStartTime = audioContext.currentTime; | 2061 this.testStartTime = audioContext.currentTime; |
2032 this.testStarted = true; | 2062 this.testStarted = true; |
2033 this.updateTestTime(); | 2063 this.updateTestTime(); |
2034 audioEngineContext.metric.initialiseTest(); | 2064 audioEngineContext.metric.initialiseTest(); |
2035 } | 2065 } |
2133 } | 2163 } |
2134 this.movementTracker[this.movementTracker.length] = [time, position]; | 2164 this.movementTracker[this.movementTracker.length] = [time, position]; |
2135 }; | 2165 }; |
2136 | 2166 |
2137 this.startListening = function (time) { | 2167 this.startListening = function (time) { |
2138 if (this.listenHold == false) { | 2168 if (this.listenHold === false) { |
2139 this.wasListenedTo = true; | 2169 this.wasListenedTo = true; |
2140 this.listenStart = time; | 2170 this.listenStart = time; |
2141 this.listenHold = true; | 2171 this.listenHold = true; |
2142 | 2172 |
2143 var evnt = document.createElement('event'); | 2173 var evnt = document.createElement('event'); |
2152 console.log('slider ' + this.parent.id + ' played (' + time + ')'); // DEBUG/SAFETY: show played slider id | 2182 console.log('slider ' + this.parent.id + ' played (' + time + ')'); // DEBUG/SAFETY: show played slider id |
2153 } | 2183 } |
2154 }; | 2184 }; |
2155 | 2185 |
2156 this.stopListening = function (time, bufferStopTime) { | 2186 this.stopListening = function (time, bufferStopTime) { |
2157 if (this.listenHold == true) { | 2187 if (this.listenHold === true) { |
2158 var diff = time - this.listenStart; | 2188 var diff = time - this.listenStart; |
2159 this.listenedTimer += (diff); | 2189 this.listenedTimer += (diff); |
2160 this.listenStart = 0; | 2190 this.listenStart = 0; |
2161 this.listenHold = false; | 2191 this.listenHold = false; |
2162 | 2192 |
2163 var evnt = this.listenTracker[this.listenTracker.length - 1]; | 2193 var evnt = this.listenTracker[this.listenTracker.length - 1]; |
2164 var testTime = evnt.getElementsByTagName('testTime')[0]; | 2194 var testTime = evnt.getElementsByTagName('testTime')[0]; |
2165 var bufferTime = evnt.getElementsByTagName('bufferTime')[0]; | 2195 var bufferTime = evnt.getElementsByTagName('bufferTime')[0]; |
2166 testTime.setAttribute('stop', time); | 2196 testTime.setAttribute('stop', time); |
2167 if (bufferStopTime == undefined) { | 2197 if (bufferStopTime === undefined) { |
2168 bufferTime.setAttribute('stop', this.parent.getCurrentPosition()); | 2198 bufferTime.setAttribute('stop', this.parent.getCurrentPosition()); |
2169 } else { | 2199 } else { |
2170 bufferTime.setAttribute('stop', bufferStopTime); | 2200 bufferTime.setAttribute('stop', bufferStopTime); |
2171 } | 2201 } |
2172 console.log('slider ' + this.parent.id + ' played for (' + diff + ')'); // DEBUG/SAFETY: show played slider id | 2202 console.log('slider ' + this.parent.id + ' played for (' + diff + ')'); // DEBUG/SAFETY: show played slider id |
2173 } | 2203 } |
2174 }; | 2204 }; |
2175 | 2205 |
2176 this.exportXMLDOM = function () { | 2206 function exportElementTimer(parentElement) { |
2177 var storeDOM = []; | 2207 var mElementTimer = storage.document.createElement('metricresult'); |
2208 mElementTimer.setAttribute('name', 'enableElementTimer'); | |
2209 mElementTimer.textContent = this.listenedTimer; | |
2210 parentElement.appendChild(mElementTimer); | |
2211 return mElementTimer; | |
2212 } | |
2213 | |
2214 function exportElementTrack(parentElement) { | |
2215 var elementTrackerFull = storage.document.createElement('metricresult'); | |
2216 elementTrackerFull.setAttribute('name', 'elementTrackerFull'); | |
2217 for (var k = 0; k < this.movementTracker.length; k++) { | |
2218 var timePos = storage.document.createElement('movement'); | |
2219 timePos.setAttribute("time", this.movementTracker[k][0]); | |
2220 timePos.setAttribute("value", this.movementTracker[k][1]); | |
2221 elementTrackerFull.appendChild(timePos); | |
2222 } | |
2223 parentElement.appendChild(elementTrackerFull); | |
2224 return elementTrackerFull; | |
2225 } | |
2226 | |
2227 function exportElementListenTracker(parentElement) { | |
2228 var elementListenTracker = storage.document.createElement('metricresult'); | |
2229 elementListenTracker.setAttribute('name', 'elementListenTracker'); | |
2230 for (var k = 0; k < this.listenTracker.length; k++) { | |
2231 elementListenTracker.appendChild(this.listenTracker[k]); | |
2232 } | |
2233 parentElement.appendChild(elementListenTracker); | |
2234 return elementListenTracker; | |
2235 } | |
2236 | |
2237 function exportElementInitialPosition(parentElement) { | |
2238 var elementInitial = storage.document.createElement('metricresult'); | |
2239 elementInitial.setAttribute('name', 'elementInitialPosition'); | |
2240 elementInitial.textContent = this.initialPosition; | |
2241 parentElement.appendChild(elementInitial); | |
2242 return elementInitial; | |
2243 } | |
2244 | |
2245 function exportFlagListenedTo(parentElement) { | |
2246 var flagListenedTo = storage.document.createElement('metricresult'); | |
2247 flagListenedTo.setAttribute('name', 'elementFlagListenedTo'); | |
2248 flagListenedTo.textContent = this.wasListenedTo; | |
2249 parentElement.appendChild(flagListenedTo); | |
2250 return flagListenedTo; | |
2251 } | |
2252 | |
2253 function exportFlagMoved(parentElement) { | |
2254 var flagMoved = storage.document.createElement('metricresult'); | |
2255 flagMoved.setAttribute('name', 'elementFlagMoved'); | |
2256 flagMoved.textContent = this.wasMoved; | |
2257 parentElement.appendChild(flagMoved); | |
2258 return flagMoved; | |
2259 } | |
2260 | |
2261 function exportFlagComments(parentElement) { | |
2262 var flagComments = storage.document.createElement('metricresult'); | |
2263 flagComments.setAttribute('name', 'elementFlagComments'); | |
2264 if (this.parent.commentDOM === null) { | |
2265 flagComments.textContent = 'false'; | |
2266 } else if (this.parent.commentDOM.textContent.length === 0) { | |
2267 flagComments.textContent = 'false'; | |
2268 } else { | |
2269 flagComments.textContet = 'true'; | |
2270 } | |
2271 parentElement.appendChild(flagComments); | |
2272 return flagComments; | |
2273 } | |
2274 | |
2275 this.exportXMLDOM = function (parentElement) { | |
2276 var elems = []; | |
2178 if (audioEngineContext.metric.enableElementTimer) { | 2277 if (audioEngineContext.metric.enableElementTimer) { |
2179 var mElementTimer = storage.document.createElement('metricresult'); | 2278 elems.push(exportElementTimer.call(this, parentElement)); |
2180 mElementTimer.setAttribute('name', 'enableElementTimer'); | |
2181 mElementTimer.textContent = this.listenedTimer; | |
2182 storeDOM.push(mElementTimer); | |
2183 } | 2279 } |
2184 if (audioEngineContext.metric.enableElementTracker) { | 2280 if (audioEngineContext.metric.enableElementTracker) { |
2185 var elementTrackerFull = storage.document.createElement('metricresult'); | 2281 elems.push(exportElementTrack.call(this, parentElement)); |
2186 elementTrackerFull.setAttribute('name', 'elementTrackerFull'); | |
2187 for (var k = 0; k < this.movementTracker.length; k++) { | |
2188 var timePos = storage.document.createElement('movement'); | |
2189 timePos.setAttribute("time", this.movementTracker[k][0]); | |
2190 timePos.setAttribute("value", this.movementTracker[k][1]); | |
2191 elementTrackerFull.appendChild(timePos); | |
2192 } | |
2193 storeDOM.push(elementTrackerFull); | |
2194 } | 2282 } |
2195 if (audioEngineContext.metric.enableElementListenTracker) { | 2283 if (audioEngineContext.metric.enableElementListenTracker) { |
2196 var elementListenTracker = storage.document.createElement('metricresult'); | 2284 elems.push(exportElementListenTracker.call(this, parentElement)); |
2197 elementListenTracker.setAttribute('name', 'elementListenTracker'); | |
2198 for (var k = 0; k < this.listenTracker.length; k++) { | |
2199 elementListenTracker.appendChild(this.listenTracker[k]); | |
2200 } | |
2201 storeDOM.push(elementListenTracker); | |
2202 } | 2285 } |
2203 if (audioEngineContext.metric.enableElementInitialPosition) { | 2286 if (audioEngineContext.metric.enableElementInitialPosition) { |
2204 var elementInitial = storage.document.createElement('metricresult'); | 2287 elems.push(exportElementInitialPosition.call(this, parentElement)); |
2205 elementInitial.setAttribute('name', 'elementInitialPosition'); | |
2206 elementInitial.textContent = this.initialPosition; | |
2207 storeDOM.push(elementInitial); | |
2208 } | 2288 } |
2209 if (audioEngineContext.metric.enableFlagListenedTo) { | 2289 if (audioEngineContext.metric.enableFlagListenedTo) { |
2210 var flagListenedTo = storage.document.createElement('metricresult'); | 2290 elems.push(exportFlagListenedTo.call(this, parentElement)); |
2211 flagListenedTo.setAttribute('name', 'elementFlagListenedTo'); | |
2212 flagListenedTo.textContent = this.wasListenedTo; | |
2213 storeDOM.push(flagListenedTo); | |
2214 } | 2291 } |
2215 if (audioEngineContext.metric.enableFlagMoved) { | 2292 if (audioEngineContext.metric.enableFlagMoved) { |
2216 var flagMoved = storage.document.createElement('metricresult'); | 2293 elems.push(exportFlagMoved.call(this, parentElement)); |
2217 flagMoved.setAttribute('name', 'elementFlagMoved'); | |
2218 flagMoved.textContent = this.wasMoved; | |
2219 storeDOM.push(flagMoved); | |
2220 } | 2294 } |
2221 if (audioEngineContext.metric.enableFlagComments) { | 2295 if (audioEngineContext.metric.enableFlagComments) { |
2222 var flagComments = storage.document.createElement('metricresult'); | 2296 elems.push(exportFlagComments.call(this, parentElement)); |
2223 flagComments.setAttribute('name', 'elementFlagComments'); | 2297 } |
2224 if (this.parent.commentDOM == null) { | 2298 return elems; |
2225 flag.textContent = 'false'; | |
2226 } else if (this.parent.commentDOM.textContent.length == 0) { | |
2227 flag.textContent = 'false'; | |
2228 } else { | |
2229 flag.textContet = 'true'; | |
2230 } | |
2231 storeDOM.push(flagComments); | |
2232 } | |
2233 return storeDOM; | |
2234 }; | 2299 }; |
2235 } | 2300 } |
2236 | 2301 |
2237 function Interface(specificationObject) { | 2302 function Interface(specificationObject) { |
2238 // This handles the bindings between the interface and the audioEngineContext; | 2303 // This handles the bindings between the interface and the audioEngineContext; |
2254 | 2319 |
2255 this.resizeWindow = function (event) { | 2320 this.resizeWindow = function (event) { |
2256 popup.resize(event); | 2321 popup.resize(event); |
2257 this.volume.resize(); | 2322 this.volume.resize(); |
2258 this.lightbox.resize(); | 2323 this.lightbox.resize(); |
2259 for (var i = 0; i < this.commentBoxes.length; i++) { | 2324 this.commentBoxes.boxes.forEach(function (elem) { |
2260 this.commentBoxes[i].resize(); | 2325 elem.resize(); |
2261 } | 2326 }); |
2262 for (var i = 0; i < this.commentQuestions.length; i++) { | 2327 this.commentQuestions.forEach(function (elem) { |
2263 this.commentQuestions[i].resize(); | 2328 elem.resize(); |
2264 } | 2329 }); |
2265 try { | 2330 try { |
2266 resizeWindow(event); | 2331 resizeWindow(event); |
2267 } catch (err) { | 2332 } catch (err) { |
2268 console.log("Warning - Interface does not have Resize option"); | 2333 console.log("Warning - Interface does not have Resize option"); |
2269 console.log(err); | 2334 console.log(err); |
2308 | 2373 |
2309 hold.appendChild(date); | 2374 hold.appendChild(date); |
2310 hold.appendChild(time); | 2375 hold.appendChild(time); |
2311 return hold; | 2376 return hold; |
2312 | 2377 |
2313 } | 2378 }; |
2314 | 2379 |
2315 this.lightbox = { | 2380 this.lightbox = { |
2316 parent: this, | 2381 parent: this, |
2317 root: document.createElement("div"), | 2382 root: document.createElement("div"), |
2318 content: document.createElement("div"), | 2383 content: document.createElement("div"), |
2350 } | 2415 } |
2351 }, | 2416 }, |
2352 resize: function (event) { | 2417 resize: function (event) { |
2353 this.root.style.left = (window.innerWidth / 2) - 250 + 'px'; | 2418 this.root.style.left = (window.innerWidth / 2) - 250 + 'px'; |
2354 } | 2419 } |
2355 } | 2420 }; |
2356 | 2421 |
2357 this.lightbox.root.appendChild(this.lightbox.content); | 2422 this.lightbox.root.appendChild(this.lightbox.content); |
2358 this.lightbox.root.appendChild(this.lightbox.accept); | 2423 this.lightbox.root.appendChild(this.lightbox.accept); |
2359 this.lightbox.root.className = "popupHolder"; | 2424 this.lightbox.root.className = "popupHolder"; |
2360 this.lightbox.root.id = "lightbox-root"; | 2425 this.lightbox.root.id = "lightbox-root"; |
2366 this.lightbox.blanker.className = "testHalt"; | 2431 this.lightbox.blanker.className = "testHalt"; |
2367 this.lightbox.blanker.id = "lightbox-blanker"; | 2432 this.lightbox.blanker.id = "lightbox-blanker"; |
2368 document.getElementsByTagName("body")[0].appendChild(this.lightbox.root); | 2433 document.getElementsByTagName("body")[0].appendChild(this.lightbox.root); |
2369 document.getElementsByTagName("body")[0].appendChild(this.lightbox.blanker); | 2434 document.getElementsByTagName("body")[0].appendChild(this.lightbox.blanker); |
2370 | 2435 |
2371 this.commentBoxes = new function () { | 2436 this.commentBoxes = (function () { |
2372 this.boxes = []; | 2437 var commentBoxes = {}; |
2373 this.injectPoint = null; | 2438 commentBoxes.boxes = []; |
2374 this.elementCommentBox = function (audioObject) { | 2439 commentBoxes.injectPoint = null; |
2440 commentBoxes.elementCommentBox = function (audioObject) { | |
2375 var element = audioObject.specification; | 2441 var element = audioObject.specification; |
2376 this.audioObject = audioObject; | 2442 this.audioObject = audioObject; |
2377 this.id = audioObject.id; | 2443 this.id = audioObject.id; |
2378 var audioHolderObject = audioObject.specification.parent; | 2444 var audioHolderObject = audioObject.specification.parent; |
2379 // Create document objects to hold the comment boxes | 2445 // Create document objects to hold the comment boxes |
2416 this.trackComment.style.width = boxwidth + "px"; | 2482 this.trackComment.style.width = boxwidth + "px"; |
2417 this.trackCommentBox.style.width = boxwidth - 6 + "px"; | 2483 this.trackCommentBox.style.width = boxwidth - 6 + "px"; |
2418 }; | 2484 }; |
2419 this.resize(); | 2485 this.resize(); |
2420 }; | 2486 }; |
2421 this.createCommentBox = function (audioObject) { | 2487 commentBoxes.createCommentBox = function (audioObject) { |
2422 var node = new this.elementCommentBox(audioObject); | 2488 var node = new this.elementCommentBox(audioObject); |
2423 this.boxes.push(node); | 2489 this.boxes.push(node); |
2424 audioObject.commentDOM = node; | 2490 audioObject.commentDOM = node; |
2425 return node; | 2491 return node; |
2426 }; | 2492 }; |
2427 this.sortCommentBoxes = function () { | 2493 commentBoxes.sortCommentBoxes = function () { |
2428 this.boxes.sort(function (a, b) { | 2494 this.boxes.sort(function (a, b) { |
2429 return a.id - b.id; | 2495 return a.id - b.id; |
2430 }); | 2496 }); |
2431 }; | 2497 }; |
2432 | 2498 |
2433 this.showCommentBoxes = function (inject, sort) { | 2499 commentBoxes.showCommentBoxes = function (inject, sort) { |
2434 this.injectPoint = inject; | 2500 this.injectPoint = inject; |
2435 if (sort) { | 2501 if (sort) { |
2436 this.sortCommentBoxes(); | 2502 this.sortCommentBoxes(); |
2437 } | 2503 } |
2438 for (var box of this.boxes) { | 2504 this.boxes.forEach(function (box) { |
2439 inject.appendChild(box.trackComment); | 2505 inject.appendChild(box.trackComment); |
2440 } | 2506 }); |
2441 }; | 2507 }; |
2442 | 2508 |
2443 this.deleteCommentBoxes = function () { | 2509 commentBoxes.deleteCommentBoxes = function () { |
2444 if (this.injectPoint != null) { | 2510 if (this.injectPoint !== null) { |
2445 for (var box of this.boxes) { | 2511 this.boxes.forEach(function (box) { |
2446 this.injectPoint.removeChild(box.trackComment); | 2512 this.injectPoint.removeChild(box.trackComment); |
2447 } | 2513 }, this); |
2448 this.injectPoint = null; | 2514 this.injectPoint = null; |
2449 } | 2515 } |
2450 this.boxes = []; | 2516 this.boxes = []; |
2451 }; | 2517 }; |
2452 } | 2518 return commentBoxes; |
2519 })(); | |
2453 | 2520 |
2454 this.commentQuestions = []; | 2521 this.commentQuestions = []; |
2455 | 2522 |
2456 this.commentBox = function (commentQuestion) { | 2523 this.commentBox = function (commentQuestion) { |
2457 this.specification = commentQuestion; | 2524 this.specification = commentQuestion; |
2506 this.holder = document.createElement('div'); | 2573 this.holder = document.createElement('div'); |
2507 this.holder.className = 'comment-div'; | 2574 this.holder.className = 'comment-div'; |
2508 // Create a string next to each comment asking for a comment | 2575 // Create a string next to each comment asking for a comment |
2509 this.string = document.createElement('span'); | 2576 this.string = document.createElement('span'); |
2510 this.string.innerHTML = commentQuestion.statement; | 2577 this.string.innerHTML = commentQuestion.statement; |
2511 var br = document.createElement('br'); | |
2512 // Add to the holder. | 2578 // Add to the holder. |
2513 this.holder.appendChild(this.string); | 2579 this.holder.appendChild(this.string); |
2514 this.holder.appendChild(br); | |
2515 this.options = []; | 2580 this.options = []; |
2516 this.inputs = document.createElement('div'); | 2581 this.inputs = document.createElement('div'); |
2517 this.span = document.createElement('div'); | 2582 this.inputs.className = "comment-checkbox-inputs-holder"; |
2518 this.inputs.align = 'center'; | |
2519 this.inputs.style.marginLeft = '12px'; | |
2520 this.inputs.className = "comment-radio-inputs-holder"; | |
2521 this.span.style.marginLeft = '12px'; | |
2522 this.span.align = 'center'; | |
2523 this.span.style.marginTop = '15px'; | |
2524 this.span.className = "comment-radio-span-holder"; | |
2525 | 2583 |
2526 var optCount = commentQuestion.options.length; | 2584 var optCount = commentQuestion.options.length; |
2527 for (var optNode of commentQuestion.options) { | 2585 for (var i = 0; i < optCount; i++) { |
2528 var div = document.createElement('div'); | 2586 var div = document.createElement('div'); |
2529 div.style.width = '80px'; | 2587 div.className = "comment-checkbox-inputs-flex"; |
2530 div.style.float = 'left'; | 2588 |
2589 var span = document.createElement('span'); | |
2590 span.textContent = commentQuestion.options[i].text; | |
2591 span.className = 'comment-radio-span'; | |
2592 div.appendChild(span); | |
2593 | |
2531 var input = document.createElement('input'); | 2594 var input = document.createElement('input'); |
2532 input.type = 'radio'; | 2595 input.type = 'radio'; |
2533 input.name = commentQuestion.id; | 2596 input.name = commentQuestion.id; |
2534 input.setAttribute('setvalue', optNode.name); | 2597 input.setAttribute('setvalue', commentQuestion.options[i].name); |
2535 input.className = 'comment-radio'; | 2598 input.className = 'comment-radio'; |
2536 div.appendChild(input); | 2599 div.appendChild(input); |
2600 | |
2537 this.inputs.appendChild(div); | 2601 this.inputs.appendChild(div); |
2538 | |
2539 | |
2540 div = document.createElement('div'); | |
2541 div.style.width = '80px'; | |
2542 div.style.float = 'left'; | |
2543 div.align = 'center'; | |
2544 var span = document.createElement('span'); | |
2545 span.textContent = optNode.text; | |
2546 span.className = 'comment-radio-span'; | |
2547 div.appendChild(span); | |
2548 this.span.appendChild(div); | |
2549 this.options.push(input); | 2602 this.options.push(input); |
2550 } | 2603 } |
2551 this.holder.appendChild(this.span); | |
2552 this.holder.appendChild(this.inputs); | 2604 this.holder.appendChild(this.inputs); |
2553 | 2605 |
2554 this.exportXMLDOM = function (storePoint) { | 2606 this.exportXMLDOM = function (storePoint) { |
2555 var root = storePoint.parent.document.createElement('comment'); | 2607 var root = storePoint.parent.document.createElement('comment'); |
2556 root.id = this.specification.id; | 2608 root.id = this.specification.id; |
2557 root.setAttribute('type', this.specification.type); | 2609 root.setAttribute('type', this.specification.type); |
2558 var question = document.createElement('question'); | 2610 var question = document.createElement('question'); |
2559 question.textContent = this.string.textContent; | 2611 question.textContent = this.string.textContent; |
2560 var response = document.createElement('response'); | 2612 var response = document.createElement('response'); |
2561 var i = 0; | 2613 var i = 0; |
2562 while (this.options[i].checked == false) { | 2614 while (this.options[i].checked === false) { |
2563 i++; | 2615 i++; |
2564 if (i >= this.options.length) { | 2616 if (i >= this.options.length) { |
2565 break; | 2617 break; |
2566 } | 2618 } |
2567 } | 2619 } |
2584 boxwidth = 600; | 2636 boxwidth = 600; |
2585 } else if (boxwidth < 400) { | 2637 } else if (boxwidth < 400) { |
2586 boxwidth = 400; | 2638 boxwidth = 400; |
2587 } | 2639 } |
2588 this.holder.style.width = boxwidth + "px"; | 2640 this.holder.style.width = boxwidth + "px"; |
2589 var text = this.holder.getElementsByClassName("comment-radio-span-holder")[0]; | |
2590 var options = this.holder.getElementsByClassName("comment-radio-inputs-holder")[0]; | |
2591 var optCount = options.childElementCount; | |
2592 var spanMargin = Math.floor(((boxwidth - 20 - (optCount * 80)) / (optCount)) / 2) + 'px'; | |
2593 var options = options.firstChild; | |
2594 var text = text.firstChild; | |
2595 options.style.marginRight = spanMargin; | |
2596 options.style.marginLeft = spanMargin; | |
2597 text.style.marginRight = spanMargin; | |
2598 text.style.marginLeft = spanMargin; | |
2599 while (options.nextSibling != undefined) { | |
2600 options = options.nextSibling; | |
2601 text = text.nextSibling; | |
2602 options.style.marginRight = spanMargin; | |
2603 options.style.marginLeft = spanMargin; | |
2604 text.style.marginRight = spanMargin; | |
2605 text.style.marginLeft = spanMargin; | |
2606 } | |
2607 }; | 2641 }; |
2608 this.resize(); | 2642 this.resize(); |
2609 }; | 2643 }; |
2610 | 2644 |
2611 this.checkboxBox = function (commentQuestion) { | 2645 this.checkboxBox = function (commentQuestion) { |
2614 this.holder = document.createElement('div'); | 2648 this.holder = document.createElement('div'); |
2615 this.holder.className = 'comment-div'; | 2649 this.holder.className = 'comment-div'; |
2616 // Create a string next to each comment asking for a comment | 2650 // Create a string next to each comment asking for a comment |
2617 this.string = document.createElement('span'); | 2651 this.string = document.createElement('span'); |
2618 this.string.innerHTML = commentQuestion.statement; | 2652 this.string.innerHTML = commentQuestion.statement; |
2619 var br = document.createElement('br'); | |
2620 // Add to the holder. | 2653 // Add to the holder. |
2621 this.holder.appendChild(this.string); | 2654 this.holder.appendChild(this.string); |
2622 this.holder.appendChild(br); | |
2623 this.options = []; | 2655 this.options = []; |
2624 this.inputs = document.createElement('div'); | 2656 this.inputs = document.createElement('div'); |
2625 this.span = document.createElement('div'); | |
2626 this.inputs.align = 'center'; | |
2627 this.inputs.style.marginLeft = '12px'; | |
2628 this.inputs.className = "comment-checkbox-inputs-holder"; | 2657 this.inputs.className = "comment-checkbox-inputs-holder"; |
2629 this.span.style.marginLeft = '12px'; | |
2630 this.span.align = 'center'; | |
2631 this.span.style.marginTop = '15px'; | |
2632 this.span.className = "comment-checkbox-span-holder"; | |
2633 | 2658 |
2634 var optCount = commentQuestion.options.length; | 2659 var optCount = commentQuestion.options.length; |
2635 for (var i = 0; i < optCount; i++) { | 2660 for (var i = 0; i < optCount; i++) { |
2636 var div = document.createElement('div'); | 2661 var div = document.createElement('div'); |
2637 div.style.width = '80px'; | 2662 div.className = "comment-checkbox-inputs-flex"; |
2638 div.style.float = 'left'; | 2663 |
2664 var span = document.createElement('span'); | |
2665 span.textContent = commentQuestion.options[i].text; | |
2666 span.className = 'comment-radio-span'; | |
2667 div.appendChild(span); | |
2668 | |
2639 var input = document.createElement('input'); | 2669 var input = document.createElement('input'); |
2640 input.type = 'checkbox'; | 2670 input.type = 'checkbox'; |
2641 input.name = commentQuestion.id; | 2671 input.name = commentQuestion.id; |
2642 input.setAttribute('setvalue', commentQuestion.options[i].name); | 2672 input.setAttribute('setvalue', commentQuestion.options[i].name); |
2643 input.className = 'comment-radio'; | 2673 input.className = 'comment-radio'; |
2644 div.appendChild(input); | 2674 div.appendChild(input); |
2675 | |
2645 this.inputs.appendChild(div); | 2676 this.inputs.appendChild(div); |
2646 | |
2647 | |
2648 div = document.createElement('div'); | |
2649 div.style.width = '80px'; | |
2650 div.style.float = 'left'; | |
2651 div.align = 'center'; | |
2652 var span = document.createElement('span'); | |
2653 span.textContent = commentQuestion.options[i].text; | |
2654 span.className = 'comment-radio-span'; | |
2655 div.appendChild(span); | |
2656 this.span.appendChild(div); | |
2657 this.options.push(input); | 2677 this.options.push(input); |
2658 } | 2678 } |
2659 this.holder.appendChild(this.span); | |
2660 this.holder.appendChild(this.inputs); | 2679 this.holder.appendChild(this.inputs); |
2661 | 2680 |
2662 this.exportXMLDOM = function (storePoint) { | 2681 this.exportXMLDOM = function (storePoint) { |
2663 var root = storePoint.parent.document.createElement('comment'); | 2682 var root = storePoint.parent.document.createElement('comment'); |
2664 root.id = this.specification.id; | 2683 root.id = this.specification.id; |
2683 boxwidth = 600; | 2702 boxwidth = 600; |
2684 } else if (boxwidth < 400) { | 2703 } else if (boxwidth < 400) { |
2685 boxwidth = 400; | 2704 boxwidth = 400; |
2686 } | 2705 } |
2687 this.holder.style.width = boxwidth + "px"; | 2706 this.holder.style.width = boxwidth + "px"; |
2688 var text = this.holder.getElementsByClassName("comment-checkbox-span-holder")[0]; | |
2689 var options = this.holder.getElementsByClassName("comment-checkbox-inputs-holder")[0]; | |
2690 var optCount = options.childElementCount; | |
2691 var spanMargin = Math.floor(((boxwidth - 20 - (optCount * 80)) / (optCount)) / 2) + 'px'; | |
2692 var options = options.firstChild; | |
2693 var text = text.firstChild; | |
2694 options.style.marginRight = spanMargin; | |
2695 options.style.marginLeft = spanMargin; | |
2696 text.style.marginRight = spanMargin; | |
2697 text.style.marginLeft = spanMargin; | |
2698 while (options.nextSibling != undefined) { | |
2699 options = options.nextSibling; | |
2700 text = text.nextSibling; | |
2701 options.style.marginRight = spanMargin; | |
2702 options.style.marginLeft = spanMargin; | |
2703 text.style.marginRight = spanMargin; | |
2704 text.style.marginLeft = spanMargin; | |
2705 } | |
2706 }; | 2707 }; |
2707 this.resize(); | 2708 this.resize(); |
2708 }; | 2709 }; |
2709 | 2710 |
2710 this.sliderBox = function (commentQuestion) { | 2711 this.sliderBox = function (commentQuestion) { |
2788 this.outsideReferenceHolder = document.createElement('button'); | 2789 this.outsideReferenceHolder = document.createElement('button'); |
2789 this.outsideReferenceHolder.className = 'outside-reference'; | 2790 this.outsideReferenceHolder.className = 'outside-reference'; |
2790 this.outsideReferenceHolder.setAttribute('track-id', index); | 2791 this.outsideReferenceHolder.setAttribute('track-id', index); |
2791 this.outsideReferenceHolder.textContent = this.parent.specification.label || "Reference"; | 2792 this.outsideReferenceHolder.textContent = this.parent.specification.label || "Reference"; |
2792 this.outsideReferenceHolder.disabled = true; | 2793 this.outsideReferenceHolder.disabled = true; |
2793 | 2794 this.handleEvent = function (event) { |
2794 this.outsideReferenceHolder.onclick = function (event) { | 2795 audioEngineContext.play(this.parent.id); |
2795 audioEngineContext.play(event.currentTarget.getAttribute('track-id')); | 2796 }; |
2796 }; | 2797 this.outsideReferenceHolder.addEventListener("click", this); |
2797 inject.appendChild(this.outsideReferenceHolder); | 2798 inject.appendChild(this.outsideReferenceHolder); |
2798 this.enable = function () { | 2799 this.enable = function () { |
2799 if (this.parent.state == 1) { | 2800 if (this.parent.state == 1) { |
2800 this.outsideReferenceHolder.disabled = false; | 2801 this.outsideReferenceHolder.disabled = false; |
2801 } | 2802 } |
2833 }; | 2834 }; |
2834 this.error = function () { | 2835 this.error = function () { |
2835 // audioObject has an error!! | 2836 // audioObject has an error!! |
2836 this.outsideReferenceHolder.textContent = "Error"; | 2837 this.outsideReferenceHolder.textContent = "Error"; |
2837 this.outsideReferenceHolder.style.backgroundColor = "#F00"; | 2838 this.outsideReferenceHolder.style.backgroundColor = "#F00"; |
2838 } | 2839 }; |
2839 } | 2840 }; |
2840 | 2841 |
2841 this.playhead = new function () { | 2842 this.playhead = (function () { |
2842 this.object = document.createElement('div'); | 2843 var playhead ={}; |
2843 this.object.className = 'playhead'; | 2844 playhead.object = document.createElement('div'); |
2844 this.object.align = 'left'; | 2845 playhead.object.className = 'playhead'; |
2846 playhead.object.align = 'left'; | |
2845 var curTime = document.createElement('div'); | 2847 var curTime = document.createElement('div'); |
2846 curTime.style.width = '50px'; | 2848 curTime.style.width = '50px'; |
2847 this.curTimeSpan = document.createElement('span'); | 2849 playhead.curTimeSpan = document.createElement('span'); |
2848 this.curTimeSpan.textContent = '00:00'; | 2850 playhead.curTimeSpan.textContent = '00:00'; |
2849 curTime.appendChild(this.curTimeSpan); | 2851 curTime.appendChild(playhead.curTimeSpan); |
2850 this.object.appendChild(curTime); | 2852 playhead.object.appendChild(curTime); |
2851 this.scrubberTrack = document.createElement('div'); | 2853 playhead.scrubberTrack = document.createElement('div'); |
2852 this.scrubberTrack.className = 'playhead-scrub-track'; | 2854 playhead.scrubberTrack.className = 'playhead-scrub-track'; |
2853 | 2855 |
2854 this.scrubberHead = document.createElement('div'); | 2856 playhead.scrubberHead = document.createElement('div'); |
2855 this.scrubberHead.id = 'playhead-scrubber'; | 2857 playhead.scrubberHead.id = 'playhead-scrubber'; |
2856 this.scrubberTrack.appendChild(this.scrubberHead); | 2858 playhead.scrubberTrack.appendChild(playhead.scrubberHead); |
2857 this.object.appendChild(this.scrubberTrack); | 2859 playhead.object.appendChild(playhead.scrubberTrack); |
2858 | 2860 |
2859 this.timePerPixel = 0; | 2861 playhead.timePerPixel = 0; |
2860 this.maxTime = 0; | 2862 playhead.maxTime = 0; |
2861 | 2863 |
2862 this.playbackObject; | 2864 playhead.playbackObject = undefined; |
2863 | 2865 |
2864 this.setTimePerPixel = function (audioObject) { | 2866 playhead.setTimePerPixel = function (audioObject) { |
2865 //maxTime must be in seconds | 2867 //maxTime must be in seconds |
2866 this.playbackObject = audioObject; | 2868 this.playbackObject = audioObject; |
2867 this.maxTime = audioObject.buffer.buffer.duration; | 2869 this.maxTime = audioObject.buffer.buffer.duration; |
2868 var width = 490; //500 - 10, 5 each side of the tracker head | 2870 var width = 490; //500 - 10, 5 each side of the tracker head |
2869 this.timePerPixel = this.maxTime / 490; | 2871 this.timePerPixel = this.maxTime / 490; |
2872 } else { | 2874 } else { |
2873 this.curTimeSpan.textContent = '00:00'; | 2875 this.curTimeSpan.textContent = '00:00'; |
2874 } | 2876 } |
2875 }; | 2877 }; |
2876 | 2878 |
2877 this.update = function () { | 2879 playhead.update = function () { |
2878 // Update the playhead position, startPlay must be called | 2880 // Update the playhead position, startPlay must be called |
2879 if (this.timePerPixel > 0) { | 2881 if (this.timePerPixel > 0) { |
2880 var time = this.playbackObject.getCurrentPosition(); | 2882 var time = this.playbackObject.getCurrentPosition(); |
2881 if (time > 0 && time < this.maxTime) { | 2883 if (time > 0 && time < this.maxTime) { |
2882 var width = 490; | 2884 var width = 490; |
2902 } | 2904 } |
2903 } | 2905 } |
2904 } | 2906 } |
2905 }; | 2907 }; |
2906 | 2908 |
2907 this.interval = undefined; | 2909 playhead.interval = undefined; |
2908 | 2910 |
2909 this.start = function () { | 2911 playhead.start = function () { |
2910 if (this.playbackObject != undefined && this.interval == undefined) { | 2912 if (this.playbackObject !== undefined && this.interval === undefined) { |
2911 if (this.maxTime < 60) { | 2913 if (this.maxTime < 60) { |
2912 this.interval = setInterval(function () { | 2914 this.interval = window.setInterval(function () { |
2913 interfaceContext.playhead.update(); | 2915 interfaceContext.playhead.update(); |
2914 }, 10); | 2916 }, 10); |
2915 } else { | 2917 } else { |
2916 this.interval = setInterval(function () { | 2918 this.interval = window.setInterval(function () { |
2917 interfaceContext.playhead.update(); | 2919 interfaceContext.playhead.update(); |
2918 }, 100); | 2920 }, 100); |
2919 } | 2921 } |
2920 } | 2922 } |
2921 }; | 2923 }; |
2922 this.stop = function () { | 2924 playhead.stop = function () { |
2923 clearInterval(this.interval); | 2925 window.clearInterval(this.interval); |
2924 this.interval = undefined; | 2926 this.interval = undefined; |
2925 this.scrubberHead.style.left = '0px'; | 2927 this.scrubberHead.style.left = '0px'; |
2926 if (this.maxTime < 60) { | 2928 if (this.maxTime < 60) { |
2927 this.curTimeSpan.textContent = '0.00'; | 2929 this.curTimeSpan.textContent = '0.00'; |
2928 } else { | 2930 } else { |
2929 this.curTimeSpan.textContent = '00:00'; | 2931 this.curTimeSpan.textContent = '00:00'; |
2930 } | 2932 } |
2931 }; | 2933 }; |
2932 }; | 2934 return playhead; |
2933 | 2935 })(); |
2934 this.volume = new function () { | 2936 |
2937 this.volume = (function () { | |
2935 // An in-built volume module which can be viewed on page | 2938 // An in-built volume module which can be viewed on page |
2936 // Includes trackers on page-by-page data | 2939 // Includes trackers on page-by-page data |
2937 // Volume does NOT reset to 0dB on each page load | 2940 // Volume does NOT reset to 0dB on each page load |
2938 this.valueLin = 1.0; | 2941 var volume = {}; |
2939 this.valueDB = 0.0; | 2942 volume.valueLin = 1.0; |
2940 this.root = document.createElement('div'); | 2943 volume.valueDB = 0.0; |
2941 this.root.id = 'master-volume-root'; | 2944 volume.root = document.createElement('div'); |
2942 this.object = document.createElement('div'); | 2945 volume.root.id = 'master-volume-root'; |
2943 this.object.className = 'master-volume-holder-float'; | 2946 volume.object = document.createElement('div'); |
2944 this.object.appendChild(this.root); | 2947 volume.object.className = 'master-volume-holder-float'; |
2945 this.slider = document.createElement('input'); | 2948 volume.object.appendChild(volume.root); |
2946 this.slider.id = 'master-volume-control'; | 2949 volume.slider = document.createElement('input'); |
2947 this.slider.type = 'range'; | 2950 volume.slider.id = 'master-volume-control'; |
2948 this.valueText = document.createElement('span'); | 2951 volume.slider.type = 'range'; |
2949 this.valueText.id = 'master-volume-feedback'; | 2952 volume.valueText = document.createElement('span'); |
2950 this.valueText.textContent = '0dB'; | 2953 volume.valueText.id = 'master-volume-feedback'; |
2951 | 2954 volume.valueText.textContent = '0dB'; |
2952 this.slider.min = -60; | 2955 |
2953 this.slider.max = 12; | 2956 volume.slider.min = -60; |
2954 this.slider.value = 0; | 2957 volume.slider.max = 12; |
2955 this.slider.step = 1; | 2958 volume.slider.value = 0; |
2956 this.handleEvent = function (event) { | 2959 volume.slider.step = 1; |
2960 volume.handleEvent = function (event) { | |
2957 if (event.type == "mousemove") { | 2961 if (event.type == "mousemove") { |
2958 this.valueDB = Number(this.slider.value); | 2962 this.valueDB = Number(this.slider.value); |
2959 this.valueLin = decibelToLinear(this.valueDB); | 2963 this.valueLin = decibelToLinear(this.valueDB); |
2960 this.valueText.textContent = this.valueDB + 'dB'; | 2964 this.valueText.textContent = this.valueDB + 'dB'; |
2961 audioEngineContext.outputGain.gain.value = this.valueLin; | 2965 audioEngineContext.outputGain.gain.value = this.valueLin; |
2965 this.slider.value = this.valueDB; | 2969 this.slider.value = this.valueDB; |
2966 | 2970 |
2967 if (event.stopPropagation) { | 2971 if (event.stopPropagation) { |
2968 event.stopPropagation(); | 2972 event.stopPropagation(); |
2969 } | 2973 } |
2970 } | 2974 }; |
2971 this.onmouseup = function () { | 2975 volume.onmouseup = function () { |
2972 var storePoint = testState.currentStore.XMLDOM.getElementsByTagName('metric')[0].getAllElementsByName('volumeTracker'); | 2976 var storePoint = testState.currentStore.XMLDOM.getElementsByTagName('metric')[0].getAllElementsByName('volumeTracker'); |
2973 if (storePoint.length == 0) { | 2977 if (storePoint.length === 0) { |
2974 storePoint = storage.document.createElement('metricresult'); | 2978 storePoint = storage.document.createElement('metricresult'); |
2975 storePoint.setAttribute('name', 'volumeTracker'); | 2979 storePoint.setAttribute('name', 'volumeTracker'); |
2976 testState.currentStore.XMLDOM.getElementsByTagName('metric')[0].appendChild(storePoint); | 2980 testState.currentStore.XMLDOM.getElementsByTagName('metric')[0].appendChild(storePoint); |
2977 } else { | 2981 } else { |
2978 storePoint = storePoint[0]; | 2982 storePoint = storePoint[0]; |
2980 var node = storage.document.createElement('movement'); | 2984 var node = storage.document.createElement('movement'); |
2981 node.setAttribute('test-time', audioEngineContext.timer.getTestTime()); | 2985 node.setAttribute('test-time', audioEngineContext.timer.getTestTime()); |
2982 node.setAttribute('volume', this.valueDB); | 2986 node.setAttribute('volume', this.valueDB); |
2983 node.setAttribute('format', 'dBFS'); | 2987 node.setAttribute('format', 'dBFS'); |
2984 storePoint.appendChild(node); | 2988 storePoint.appendChild(node); |
2985 } | 2989 }; |
2986 this.slider.addEventListener("mousemove", this); | 2990 volume.slider.addEventListener("mousemove", volume); |
2987 this.root.addEventListener("mouseup", this); | 2991 volume.root.addEventListener("mouseup", volume); |
2988 | 2992 |
2989 var title = document.createElement('div'); | 2993 var title = document.createElement('div'); |
2990 title.innerHTML = '<span>Master Volume Control</span>'; | 2994 title.innerHTML = '<span>Master Volume Control</span>'; |
2991 title.style.fontSize = '0.75em'; | 2995 title.style.fontSize = '0.75em'; |
2992 title.style.width = "100%"; | 2996 title.style.width = "100%"; |
2993 title.align = 'center'; | 2997 title.align = 'center'; |
2994 this.root.appendChild(title); | 2998 volume.root.appendChild(title); |
2995 | 2999 |
2996 this.root.appendChild(this.slider); | 3000 volume.root.appendChild(volume.slider); |
2997 this.root.appendChild(this.valueText); | 3001 volume.root.appendChild(volume.valueText); |
2998 | 3002 |
2999 this.resize = function (event) { | 3003 volume.resize = function (event) { |
3000 if (window.innerWidth < 1000) { | 3004 if (window.innerWidth < 1000) { |
3001 this.object.className = "master-volume-holder-inline" | 3005 this.object.className = "master-volume-holder-inline"; |
3002 } else { | 3006 } else { |
3003 this.object.className = 'master-volume-holder-float'; | 3007 this.object.className = 'master-volume-holder-float'; |
3004 } | 3008 } |
3005 } | 3009 }; |
3006 } | 3010 return volume; |
3011 })(); | |
3007 | 3012 |
3008 this.calibrationModuleObject = null; | 3013 this.calibrationModuleObject = null; |
3009 this.calibrationModule = function () { | 3014 this.calibrationModule = function () { |
3010 // This creates an on-page calibration module | 3015 // This creates an on-page calibration module |
3011 this.storeDOM = storage.document.createElement("calibration"); | 3016 this.storeDOM = storage.document.createElement("calibration"); |
3017 var f0 = 62.5; | 3022 var f0 = 62.5; |
3018 this.holder = document.createElement("div"); | 3023 this.holder = document.createElement("div"); |
3019 this.holder.className = "calibration-holder"; | 3024 this.holder.className = "calibration-holder"; |
3020 this.calibrationNodes = []; | 3025 this.calibrationNodes = []; |
3021 while (f0 < 20000) { | 3026 while (f0 < 20000) { |
3027 /* jshint loopfunc: true */ | |
3022 var obj = { | 3028 var obj = { |
3023 root: document.createElement("div"), | 3029 root: document.createElement("div"), |
3024 input: document.createElement("input"), | 3030 input: document.createElement("input"), |
3025 oscillator: audioContext.createOscillator(), | 3031 oscillator: audioContext.createOscillator(), |
3026 gain: audioContext.createGain(), | 3032 gain: audioContext.createGain(), |
3041 var value = Math.pow(10, this.input.value / 20); | 3047 var value = Math.pow(10, this.input.value / 20); |
3042 if (this.f == 1000) { | 3048 if (this.f == 1000) { |
3043 audioEngineContext.outputGain.gain.value = value; | 3049 audioEngineContext.outputGain.gain.value = value; |
3044 interfaceContext.volume.slider.value = this.input.value; | 3050 interfaceContext.volume.slider.value = this.input.value; |
3045 } else { | 3051 } else { |
3046 this.gain.gain.value = value | 3052 this.gain.gain.value = value; |
3047 } | 3053 } |
3048 break; | 3054 break; |
3049 } | 3055 } |
3050 }, | 3056 }, |
3051 disconnect: function () { | 3057 disconnect: function () { |
3052 this.gain.disconnect(); | 3058 this.gain.disconnect(); |
3053 } | 3059 } |
3054 } | 3060 }; |
3055 obj.root.className = "calibration-slider"; | 3061 obj.root.className = "calibration-slider"; |
3056 obj.root.appendChild(obj.input); | 3062 obj.root.appendChild(obj.input); |
3057 obj.oscillator.connect(obj.gain); | 3063 obj.oscillator.connect(obj.gain); |
3058 obj.gain.connect(audioEngineContext.outputGain); | 3064 obj.gain.connect(audioEngineContext.outputGain); |
3059 obj.gain.gain.value = Math.random() * 2; | 3065 obj.gain.gain.value = Math.random() * 2; |
3077 this.calibrationNodes.push(obj); | 3083 this.calibrationNodes.push(obj); |
3078 this.holder.appendChild(obj.root); | 3084 this.holder.appendChild(obj.root); |
3079 f0 *= 2; | 3085 f0 *= 2; |
3080 } | 3086 } |
3081 inject.appendChild(this.holder); | 3087 inject.appendChild(this.holder); |
3082 } | 3088 }; |
3083 this.collect = function () { | 3089 this.collect = function () { |
3084 for (var obj of this.calibrationNodes) { | 3090 this.calibrationNodes.forEach(function (obj) { |
3085 var node = storage.document.createElement("calibrationresult"); | 3091 var node = storage.document.createElement("calibrationresult"); |
3086 node.setAttribute("frequency", obj.f); | 3092 node.setAttribute("frequency", obj.f); |
3087 node.setAttribute("range-min", obj.input.min); | 3093 node.setAttribute("range-min", obj.input.min); |
3088 node.setAttribute("range-max", obj.input.max); | 3094 node.setAttribute("range-max", obj.input.max); |
3089 node.setAttribute("gain-lin", obj.gain.gain.value); | 3095 node.setAttribute("gain-lin", obj.gain.gain.value); |
3090 this.storeDOM.appendChild(node); | 3096 this.storeDOM.appendChild(node); |
3091 } | 3097 }, this); |
3092 } | 3098 }; |
3093 } | 3099 }; |
3094 | 3100 |
3095 | 3101 |
3096 // Global Checkers | 3102 // Global Checkers |
3097 // These functions will help enforce the checkers | 3103 // These functions will help enforce the checkers |
3098 this.checkHiddenAnchor = function () { | 3104 this.checkHiddenAnchor = function () { |
3099 for (var ao of audioEngineContext.audioObjects) { | 3105 var anchors = audioEngineContext.audioObjects.filter(function (ao) { |
3100 if (ao.specification.type == "anchor") { | 3106 return ao.specification.type === "anchor"; |
3101 if (ao.interfaceDOM.getValue() > (ao.specification.marker / 100) && ao.specification.marker > 0) { | 3107 }); |
3102 // Anchor is not set below | 3108 var state = anchors.some(function (ao) { |
3103 console.log('Anchor node not below marker value'); | 3109 return (ao.interfaceDOM.getValue() > (ao.specification.marker / 100) && ao.specification.marker > 0); |
3104 interfaceContext.lightbox.post("Message", 'Please keep listening'); | 3110 }); |
3105 this.storeErrorNode('Anchor node not below marker value'); | 3111 if (state) { |
3106 return false; | 3112 console.log('Anchor node not below marker value'); |
3107 } | 3113 interfaceContext.lightbox.post("Message", 'Please keep listening'); |
3108 } | 3114 this.storeErrorNode('Anchor node not below marker value'); |
3115 return false; | |
3109 } | 3116 } |
3110 return true; | 3117 return true; |
3111 }; | 3118 }; |
3112 | 3119 |
3113 this.checkHiddenReference = function () { | 3120 this.checkHiddenReference = function () { |
3114 for (var ao of audioEngineContext.audioObjects) { | 3121 var references = audioEngineContext.audioObjects.filter(function (ao) { |
3115 if (ao.specification.type == "reference") { | 3122 return ao.specification.type === "reference"; |
3116 if (ao.interfaceDOM.getValue() < (ao.specification.marker / 100) && ao.specification.marker > 0) { | 3123 }); |
3117 // Anchor is not set below | 3124 var state = references.some(function (ao) { |
3118 console.log('Reference node not above marker value'); | 3125 return (ao.interfaceDOM.getValue() < (ao.specification.marker / 100) && ao.specification.marker > 0); |
3119 this.storeErrorNode('Reference node not above marker value'); | 3126 }); |
3120 interfaceContext.lightbox.post("Message", 'Please keep listening'); | 3127 if (state) { |
3121 return false; | 3128 console.log('Reference node not below marker value'); |
3122 } | 3129 interfaceContext.lightbox.post("Message", 'Please keep listening'); |
3123 } | 3130 this.storeErrorNode('Reference node not below marker value'); |
3131 return false; | |
3124 } | 3132 } |
3125 return true; | 3133 return true; |
3126 }; | 3134 }; |
3127 | 3135 |
3128 this.checkFragmentsFullyPlayed = function () { | 3136 this.checkFragmentsFullyPlayed = function () { |
3131 if (audioEngineContext.loopPlayback) { | 3139 if (audioEngineContext.loopPlayback) { |
3132 console.log("WARNING - Looped source: Cannot check fragments are fully played"); | 3140 console.log("WARNING - Looped source: Cannot check fragments are fully played"); |
3133 return true; | 3141 return true; |
3134 } | 3142 } |
3135 var check_pass = true; | 3143 var check_pass = true; |
3136 var error_obj = []; | 3144 var error_obj = [], |
3137 for (var i = 0; i < audioEngineContext.audioObjects.length; i++) { | 3145 i; |
3146 for (i = 0; i < audioEngineContext.audioObjects.length; i++) { | |
3138 var object = audioEngineContext.audioObjects[i]; | 3147 var object = audioEngineContext.audioObjects[i]; |
3139 var time = object.buffer.buffer.duration; | 3148 var time = object.buffer.buffer.duration; |
3140 var metric = object.metric; | 3149 var metric = object.metric; |
3141 var passed = false; | 3150 var passed = false; |
3142 for (var j = 0; j < metric.listenTracker.length; j++) { | 3151 for (var j = 0; j < metric.listenTracker.length; j++) { |
3147 if (delta >= time) { | 3156 if (delta >= time) { |
3148 passed = true; | 3157 passed = true; |
3149 break; | 3158 break; |
3150 } | 3159 } |
3151 } | 3160 } |
3152 if (passed == false) { | 3161 if (passed === false) { |
3153 check_pass = false; | 3162 check_pass = false; |
3154 console.log("Continue listening to track-" + object.interfaceDOM.getPresentedId()); | 3163 console.log("Continue listening to track-" + object.interfaceDOM.getPresentedId()); |
3155 error_obj.push(object.interfaceDOM.getPresentedId()); | 3164 error_obj.push(object.interfaceDOM.getPresentedId()); |
3156 } | 3165 } |
3157 } | 3166 } |
3158 if (check_pass == false) { | 3167 if (check_pass === false) { |
3159 var str_start = "You have not completely listened to fragments "; | 3168 var str_start = "You have not completely listened to fragments "; |
3160 for (var i = 0; i < error_obj.length; i++) { | 3169 for (i = 0; i < error_obj.length; i++) { |
3161 str_start += error_obj[i]; | 3170 str_start += error_obj[i]; |
3162 if (i != error_obj.length - 1) { | 3171 if (i != error_obj.length - 1) { |
3163 str_start += ', '; | 3172 str_start += ', '; |
3164 } | 3173 } |
3165 } | 3174 } |
3172 return true; | 3181 return true; |
3173 }; | 3182 }; |
3174 this.checkAllMoved = function () { | 3183 this.checkAllMoved = function () { |
3175 var str = "You have not moved "; | 3184 var str = "You have not moved "; |
3176 var failed = []; | 3185 var failed = []; |
3177 for (var ao of audioEngineContext.audioObjects) { | 3186 audioEngineContext.audioObjects.forEach(function (ao) { |
3178 if (ao.metric.wasMoved == false && ao.interfaceDOM.canMove() == true) { | 3187 if (ao.metric.wasMoved === false && ao.interfaceDOM.canMove() === true) { |
3179 failed.push(ao.interfaceDOM.getPresentedId()); | 3188 failed.push(ao.interfaceDOM.getPresentedId()); |
3180 } | 3189 } |
3181 } | 3190 }, this); |
3182 if (failed.length == 0) { | 3191 if (failed.length === 0) { |
3183 return true; | 3192 return true; |
3184 } else if (failed.length == 1) { | 3193 } else if (failed.length == 1) { |
3185 str += 'track ' + failed[0]; | 3194 str += 'track ' + failed[0]; |
3186 } else { | 3195 } else { |
3187 str += 'tracks '; | 3196 str += 'tracks '; |
3197 return false; | 3206 return false; |
3198 }; | 3207 }; |
3199 this.checkAllPlayed = function () { | 3208 this.checkAllPlayed = function () { |
3200 var str = "You have not played "; | 3209 var str = "You have not played "; |
3201 var failed = []; | 3210 var failed = []; |
3202 for (var ao of audioEngineContext.audioObjects) { | 3211 audioEngineContext.audioObjects.forEach(function (ao) { |
3203 if (ao.metric.wasListenedTo == false) { | 3212 if (ao.metric.wasListenedTo === false) { |
3204 failed.push(ao.interfaceDOM.getPresentedId()); | 3213 failed.push(ao.interfaceDOM.getPresentedId()); |
3205 } | 3214 } |
3206 } | 3215 }, this); |
3207 if (failed.length == 0) { | 3216 if (failed.length === 0) { |
3208 return true; | 3217 return true; |
3209 } else if (failed.length == 1) { | 3218 } else if (failed.length == 1) { |
3210 str += 'track ' + failed[0]; | 3219 str += 'track ' + failed[0]; |
3211 } else { | 3220 } else { |
3212 str += 'tracks '; | 3221 str += 'tracks '; |
3234 this.storeErrorNode(str); | 3243 this.storeErrorNode(str); |
3235 return false; | 3244 return false; |
3236 } | 3245 } |
3237 } | 3246 } |
3238 return true; | 3247 return true; |
3239 } | 3248 }; |
3240 this.checkScaleRange = function (min, max) { | 3249 this.checkScaleRange = function () { |
3241 var page = testState.getCurrentTestPage(); | 3250 var page = testState.getCurrentTestPage(); |
3242 var audioObjects = audioEngineContext.audioObjects; | 3251 var interfaceObject = page.interfaces; |
3243 var state = true; | 3252 var state = true; |
3244 var str = "Please keep listening. "; | 3253 var str = "Please keep listening. "; |
3245 var minRanking = Infinity; | 3254 if (interfaceObject === undefined) { |
3246 var maxRanking = -Infinity; | 3255 return true; |
3247 for (var ao of audioObjects) { | 3256 } |
3248 var rank = ao.interfaceDOM.getValue(); | 3257 interfaceObject = interfaceObject[0]; |
3249 if (rank < minRanking) { | 3258 var scales = (function () { |
3250 minRanking = rank; | 3259 var scaleRange = interfaceObject.options.find(function (a) { |
3251 } | 3260 return a.name == "scalerange"; |
3252 if (rank > maxRanking) { | 3261 }); |
3253 maxRanking = rank; | 3262 return { |
3254 } | 3263 min: scaleRange.min, |
3255 } | 3264 max: scaleRange.max |
3256 if (minRanking * 100 > min) { | 3265 }; |
3257 str += "At least one fragment must be below the " + min + " mark."; | 3266 })(); |
3267 var range = audioEngineContext.audioObjects.reduce(function (a, b) { | |
3268 var v = b.interfaceDOM.getValue(); | |
3269 return { | |
3270 min: Math.min(a.min, v), | |
3271 max: Math.max(a.max, v) | |
3272 }; | |
3273 }, { | |
3274 min: 100, | |
3275 max: 0 | |
3276 }); | |
3277 if (range.min > scales.min) { | |
3278 str += "At least one fragment must be below the " + range.min + " mark."; | |
3258 state = false; | 3279 state = false; |
3259 } | 3280 } else if (range.max < scales.max) { |
3260 if (maxRanking * 100 < max) { | 3281 str += "At least one fragment must be above the " + range.max + " mark."; |
3261 str += "At least one fragment must be above the " + max + " mark." | |
3262 state = false; | 3282 state = false; |
3263 } | 3283 } |
3264 if (!state) { | 3284 if (state === false) { |
3265 console.log(str); | 3285 console.log(str); |
3266 this.storeErrorNode(str); | 3286 this.storeErrorNode(str); |
3267 interfaceContext.lightbox.post("Error", str); | 3287 interfaceContext.lightbox.post("Error", str); |
3268 } | 3288 } |
3269 return state; | 3289 return state; |
3270 } | 3290 }; |
3271 | 3291 |
3272 this.storeErrorNode = function (errorMessage) { | 3292 this.storeErrorNode = function (errorMessage) { |
3273 var time = audioEngineContext.timer.getTestTime(); | 3293 var time = audioEngineContext.timer.getTestTime(); |
3274 var node = storage.document.createElement('error'); | 3294 var node = storage.document.createElement('error'); |
3275 node.setAttribute('time', time); | 3295 node.setAttribute('time', time); |
3290 case "letter": | 3310 case "letter": |
3291 return String.fromCharCode((index + offset) % 26 + 97); | 3311 return String.fromCharCode((index + offset) % 26 + 97); |
3292 case "capital": | 3312 case "capital": |
3293 return String.fromCharCode((index + offset) % 26 + 65); | 3313 return String.fromCharCode((index + offset) % 26 + 65); |
3294 case "samediff": | 3314 case "samediff": |
3295 if (index == 0) { | 3315 if (index === 0) { |
3296 return "Same"; | 3316 return "Same"; |
3297 } else if (index == 1) { | 3317 } else if (index == 1) { |
3298 return "Difference"; | 3318 return "Difference"; |
3299 } else { | |
3300 return ""; | |
3301 } | 3319 } |
3320 return ""; | |
3302 case "number": | 3321 case "number": |
3303 return String(index + offset); | 3322 return String(index + offset); |
3304 default: | 3323 default: |
3305 return ""; | 3324 return ""; |
3306 } | 3325 } |
3307 } | 3326 } |
3308 | 3327 |
3309 if (typeof labelStart !== "string" || labelStart.length == 0) { | 3328 if (typeof labelStart !== "string" || labelStart.length === 0) { |
3310 labelStart = String.fromCharCode(0); | 3329 labelStart = String.fromCharCode(0); |
3311 } | 3330 } |
3312 | 3331 |
3313 switch (labelType) { | 3332 switch (labelType) { |
3314 case "letter": | 3333 case "letter": |
3329 labelStart = Number(labelStart); | 3348 labelStart = Number(labelStart); |
3330 if (!isFinite(labelStart)) { | 3349 if (!isFinite(labelStart)) { |
3331 labelStart = 1; | 3350 labelStart = 1; |
3332 } | 3351 } |
3333 break; | 3352 break; |
3334 case "none": | |
3335 default: | 3353 default: |
3336 labelStart = 0; | 3354 labelStart = 0; |
3337 } | 3355 } |
3338 if (typeof index == "number") { | 3356 if (typeof index == "number") { |
3339 return calculateLabel(labelType, index, labelStart); | 3357 return calculateLabel(labelType, index, labelStart); |
3346 } | 3364 } |
3347 return a; | 3365 return a; |
3348 } else { | 3366 } else { |
3349 throw ("Invalid arguments"); | 3367 throw ("Invalid arguments"); |
3350 } | 3368 } |
3351 } | 3369 }; |
3352 | 3370 |
3353 this.getCombinedInterfaces = function (page) { | 3371 this.getCombinedInterfaces = function (page) { |
3354 // Combine the interfaces with the global interface nodes | 3372 // Combine the interfaces with the global interface nodes |
3355 var global = specification.interfaces, | 3373 var global = specification.interfaces, |
3356 local = page.interfaces; | 3374 local = page.interfaces; |
3371 // Use the global default scales | 3389 // Use the global default scales |
3372 locInt.scales = global.scales; | 3390 locInt.scales = global.scales; |
3373 } | 3391 } |
3374 }); | 3392 }); |
3375 return local; | 3393 return local; |
3376 } | 3394 }; |
3377 } | 3395 } |
3378 | 3396 |
3379 function Storage() { | 3397 function Storage() { |
3380 // Holds results in XML format until ready for collection | 3398 // Holds results in XML format until ready for collection |
3381 this.globalPreTest = null; | 3399 this.globalPreTest = null; |
3384 this.document = null; | 3402 this.document = null; |
3385 this.root = null; | 3403 this.root = null; |
3386 this.state = 0; | 3404 this.state = 0; |
3387 | 3405 |
3388 this.initialise = function (existingStore) { | 3406 this.initialise = function (existingStore) { |
3389 if (existingStore == undefined) { | 3407 if (existingStore === undefined) { |
3390 // We need to get the sessionKey | 3408 // We need to get the sessionKey |
3391 this.SessionKey.requestKey(); | 3409 this.SessionKey.requestKey(); |
3392 this.document = document.implementation.createDocument(null, "waetresult", null); | 3410 this.document = document.implementation.createDocument(null, "waetresult", null); |
3393 this.root = this.document.childNodes[0]; | 3411 this.root = this.document.childNodes[0]; |
3394 var projectDocument = specification.projectXML; | 3412 var projectDocument = specification.projectXML; |
3395 projectDocument.setAttribute('file-name', url); | 3413 projectDocument.setAttribute('file-name', specification.url); |
3396 projectDocument.setAttribute('url', qualifyURL(url)); | 3414 projectDocument.setAttribute('url', qualifyURL(specification.url)); |
3397 this.root.appendChild(projectDocument); | 3415 this.root.appendChild(projectDocument); |
3398 this.root.appendChild(interfaceContext.returnDateNode()); | 3416 this.root.appendChild(interfaceContext.returnDateNode()); |
3399 this.root.appendChild(interfaceContext.returnNavigator()); | 3417 this.root.appendChild(interfaceContext.returnNavigator()); |
3400 } else { | 3418 } else { |
3401 this.document = existingStore; | 3419 this.document = existingStore; |
3402 this.root = existingStore.firstChild; | 3420 this.root = existingStore.firstChild; |
3403 this.SessionKey.key = this.root.getAttribute("key"); | 3421 this.SessionKey.key = this.root.getAttribute("key"); |
3404 } | 3422 } |
3405 if (specification.preTest != undefined) { | 3423 if (specification.preTest !== undefined) { |
3406 this.globalPreTest = new this.surveyNode(this, this.root, specification.preTest); | 3424 this.globalPreTest = new this.surveyNode(this, this.root, specification.preTest); |
3407 } | 3425 } |
3408 if (specification.postTest != undefined) { | 3426 if (specification.postTest !== undefined) { |
3409 this.globalPostTest = new this.surveyNode(this, this.root, specification.postTest); | 3427 this.globalPostTest = new this.surveyNode(this, this.root, specification.postTest); |
3410 } | 3428 } |
3411 }; | 3429 }; |
3412 | 3430 |
3413 this.SessionKey = { | 3431 this.SessionKey = { |
3415 request: new XMLHttpRequest(), | 3433 request: new XMLHttpRequest(), |
3416 parent: this, | 3434 parent: this, |
3417 handleEvent: function () { | 3435 handleEvent: function () { |
3418 var parse = new DOMParser(); | 3436 var parse = new DOMParser(); |
3419 var xml = parse.parseFromString(this.request.response, "text/xml"); | 3437 var xml = parse.parseFromString(this.request.response, "text/xml"); |
3420 if (this.request.response.length == 0) { | 3438 if (this.request.response.length === 0) { |
3421 console.error("An unspecified error occured, no server key could be generated"); | 3439 console.error("An unspecified error occured, no server key could be generated"); |
3422 return; | 3440 return; |
3423 } | 3441 } |
3424 if (xml.getElementsByTagName("state").length > 0) { | 3442 if (xml.getElementsByTagName("state").length > 0) { |
3425 if (xml.getElementsByTagName("state")[0].textContent == "OK") { | 3443 if (xml.getElementsByTagName("state")[0].textContent == "OK") { |
3448 this.request.open("GET", returnURL + "php/requestKey.php", true); | 3466 this.request.open("GET", returnURL + "php/requestKey.php", true); |
3449 this.request.addEventListener("load", this); | 3467 this.request.addEventListener("load", this); |
3450 this.request.send(); | 3468 this.request.send(); |
3451 }, | 3469 }, |
3452 update: function () { | 3470 update: function () { |
3453 if (this.key == null) { | 3471 if (this.key === null) { |
3454 console.log("Cannot save as key == null"); | 3472 console.log("Cannot save as key == null"); |
3455 return; | 3473 return; |
3456 } | 3474 } |
3457 this.parent.root.setAttribute("state", "update"); | 3475 this.parent.root.setAttribute("state", "update"); |
3458 var xmlhttp = new XMLHttpRequest(); | 3476 var xmlhttp = new XMLHttpRequest(); |
3483 } else { | 3501 } else { |
3484 var message = response.getElementsByTagName("message"); | 3502 var message = response.getElementsByTagName("message"); |
3485 console.log("Intermediate save: Error! " + message.textContent); | 3503 console.log("Intermediate save: Error! " + message.textContent); |
3486 } | 3504 } |
3487 } | 3505 } |
3488 } | 3506 }; |
3489 xmlhttp.send([hold.innerHTML]); | 3507 xmlhttp.send([hold.innerHTML]); |
3490 } | 3508 } |
3491 } | 3509 }; |
3492 | 3510 |
3493 this.createTestPageStore = function (specification) { | 3511 this.createTestPageStore = function (specification) { |
3494 var store = new this.pageNode(this, specification); | 3512 var store = new this.pageNode(this, specification); |
3495 this.testPages.push(store); | 3513 this.testPages.push(store); |
3496 return this.testPages[this.testPages.length - 1]; | 3514 return this.testPages[this.testPages.length - 1]; |
3501 this.parent = parent; | 3519 this.parent = parent; |
3502 this.state = "empty"; | 3520 this.state = "empty"; |
3503 this.XMLDOM = this.parent.document.createElement('survey'); | 3521 this.XMLDOM = this.parent.document.createElement('survey'); |
3504 this.XMLDOM.setAttribute('location', this.specification.location); | 3522 this.XMLDOM.setAttribute('location', this.specification.location); |
3505 this.XMLDOM.setAttribute("state", this.state); | 3523 this.XMLDOM.setAttribute("state", this.state); |
3506 for (var optNode of this.specification.options) { | 3524 this.specification.options.forEach(function (optNode) { |
3507 if (optNode.type != 'statement') { | 3525 if (optNode.type != 'statement') { |
3508 var node = this.parent.document.createElement('surveyresult'); | 3526 var node = this.parent.document.createElement('surveyresult'); |
3509 node.setAttribute("ref", optNode.id); | 3527 node.setAttribute("ref", optNode.id); |
3510 node.setAttribute('type', optNode.type); | 3528 node.setAttribute('type', optNode.type); |
3511 this.XMLDOM.appendChild(node); | 3529 this.XMLDOM.appendChild(node); |
3512 } | 3530 } |
3513 } | 3531 }, this); |
3514 root.appendChild(this.XMLDOM); | 3532 root.appendChild(this.XMLDOM); |
3515 | 3533 |
3516 this.postResult = function (node) { | 3534 this.postResult = function (node) { |
3535 function postNumber(doc, value) { | |
3536 var child = doc.createElement("response"); | |
3537 child.textContent = value; | |
3538 return child; | |
3539 } | |
3540 | |
3541 function postRadio(doc, node) { | |
3542 var child = doc.createElement('response'); | |
3543 if (node.response !== null) { | |
3544 child.setAttribute('name', node.response.name); | |
3545 child.textContent = node.response.text; | |
3546 } | |
3547 return child; | |
3548 } | |
3549 | |
3550 function postCheckbox(doc, node) { | |
3551 var checkNode = doc.createElement('response'); | |
3552 checkNode.setAttribute('name', node.name); | |
3553 checkNode.setAttribute('checked', node.checked); | |
3554 return checkNode; | |
3555 } | |
3517 // From popup: node is the popupOption node containing both spec. and results | 3556 // From popup: node is the popupOption node containing both spec. and results |
3518 // ID is the position | 3557 // ID is the position |
3519 if (node.specification.type == 'statement') { | 3558 if (node.specification.type == 'statement') { |
3520 return; | 3559 return; |
3521 } | 3560 } |
3522 var surveyresult = this.XMLDOM.firstChild; | 3561 var surveyresult = this.XMLDOM.firstChild; |
3523 while (surveyresult != null) { | 3562 while (surveyresult !== null) { |
3524 if (surveyresult.getAttribute("ref") == node.specification.id) { | 3563 if (surveyresult.getAttribute("ref") == node.specification.id) { |
3525 break; | 3564 break; |
3526 } | 3565 } |
3527 surveyresult = surveyresult.nextElementSibling; | 3566 surveyresult = surveyresult.nextElementSibling; |
3528 } | 3567 } |
3529 switch (node.specification.type) { | 3568 switch (node.specification.type) { |
3530 case "number": | 3569 case "number": |
3531 case "question": | 3570 case "question": |
3532 case "slider": | 3571 case "slider": |
3533 var child = this.parent.document.createElement('response'); | 3572 surveyresult.appendChild(postNumber(this.parent.document, node.response)); |
3534 child.textContent = node.response; | |
3535 surveyresult.appendChild(child); | |
3536 break; | 3573 break; |
3537 case "radio": | 3574 case "radio": |
3538 var child = this.parent.document.createElement('response'); | 3575 surveyresult.appendChild(postRadio(this.parent.document, node)); |
3539 if (node.response !== null) { | |
3540 child.setAttribute('name', node.response.name); | |
3541 child.textContent = node.response.text; | |
3542 } | |
3543 surveyresult.appendChild(child); | |
3544 break; | 3576 break; |
3545 case "checkbox": | 3577 case "checkbox": |
3546 if (node.response == undefined) { | 3578 if (node.response === undefined) { |
3547 surveyresult.appendChild(this.parent.document.createElement('response')); | 3579 surveyresult.appendChild(this.parent.document.createElement('response')); |
3548 break; | 3580 break; |
3549 } | 3581 } |
3550 for (var i = 0; i < node.response.length; i++) { | 3582 for (var i = 0; i < node.response.length; i++) { |
3551 var checkNode = this.parent.document.createElement('response'); | 3583 surveyresult.appendChild(postCheckbox(this.parent.document, node.response[i])); |
3552 checkNode.setAttribute('name', node.response[i].name); | |
3553 checkNode.setAttribute('checked', node.response[i].checked); | |
3554 surveyresult.appendChild(checkNode); | |
3555 } | 3584 } |
3556 break; | 3585 break; |
3557 } | 3586 } |
3558 }; | 3587 }; |
3559 this.complete = function () { | 3588 this.complete = function () { |
3560 this.state = "complete"; | 3589 this.state = "complete"; |
3561 this.XMLDOM.setAttribute("state", this.state); | 3590 this.XMLDOM.setAttribute("state", this.state); |
3562 } | 3591 }; |
3563 }; | 3592 }; |
3564 | 3593 |
3565 this.pageNode = function (parent, specification) { | 3594 this.pageNode = function (parent, specification) { |
3566 // Create one store per test page | 3595 // Create one store per test page |
3567 this.specification = specification; | 3596 this.specification = specification; |
3569 this.state = "empty"; | 3598 this.state = "empty"; |
3570 this.XMLDOM = this.parent.document.createElement('page'); | 3599 this.XMLDOM = this.parent.document.createElement('page'); |
3571 this.XMLDOM.setAttribute('ref', specification.id); | 3600 this.XMLDOM.setAttribute('ref', specification.id); |
3572 this.XMLDOM.setAttribute('presentedId', specification.presentedId); | 3601 this.XMLDOM.setAttribute('presentedId', specification.presentedId); |
3573 this.XMLDOM.setAttribute("state", this.state); | 3602 this.XMLDOM.setAttribute("state", this.state); |
3574 if (specification.preTest != undefined) { | 3603 if (specification.preTest !== undefined) { |
3575 this.preTest = new this.parent.surveyNode(this.parent, this.XMLDOM, this.specification.preTest); | 3604 this.preTest = new this.parent.surveyNode(this.parent, this.XMLDOM, this.specification.preTest); |
3576 } | 3605 } |
3577 if (specification.postTest != undefined) { | 3606 if (specification.postTest !== undefined) { |
3578 this.postTest = new this.parent.surveyNode(this.parent, this.XMLDOM, this.specification.postTest); | 3607 this.postTest = new this.parent.surveyNode(this.parent, this.XMLDOM, this.specification.postTest); |
3579 } | 3608 } |
3580 | 3609 |
3581 // Add any page metrics | 3610 // Add any page metrics |
3582 var page_metric = this.parent.document.createElement('metric'); | 3611 var page_metric = this.parent.document.createElement('metric'); |
3583 this.XMLDOM.appendChild(page_metric); | 3612 this.XMLDOM.appendChild(page_metric); |
3584 | 3613 |
3585 // Add the audioelement | 3614 // Add the audioelement |
3586 for (var element of this.specification.audioElements) { | 3615 this.specification.audioElements.forEach(function (element) { |
3587 var aeNode = this.parent.document.createElement('audioelement'); | 3616 var aeNode = this.parent.document.createElement('audioelement'); |
3588 aeNode.setAttribute('ref', element.id); | 3617 aeNode.setAttribute('ref', element.id); |
3589 if (element.name != undefined) { | 3618 if (element.name !== undefined) { |
3590 aeNode.setAttribute('name', element.name) | 3619 aeNode.setAttribute('name', element.name); |
3591 }; | 3620 } |
3592 aeNode.setAttribute('type', element.type); | 3621 aeNode.setAttribute('type', element.type); |
3593 aeNode.setAttribute('url', element.url); | 3622 aeNode.setAttribute('url', element.url); |
3594 aeNode.setAttribute('fqurl', qualifyURL(element.url)); | 3623 aeNode.setAttribute('fqurl', qualifyURL(element.url)); |
3595 aeNode.setAttribute('gain', element.gain); | 3624 aeNode.setAttribute('gain', element.gain); |
3596 if (element.type == 'anchor' || element.type == 'reference') { | 3625 if (element.type == 'anchor' || element.type == 'reference') { |
3599 } | 3628 } |
3600 } | 3629 } |
3601 var ae_metric = this.parent.document.createElement('metric'); | 3630 var ae_metric = this.parent.document.createElement('metric'); |
3602 aeNode.appendChild(ae_metric); | 3631 aeNode.appendChild(ae_metric); |
3603 this.XMLDOM.appendChild(aeNode); | 3632 this.XMLDOM.appendChild(aeNode); |
3604 } | 3633 }, this); |
3605 | 3634 |
3606 this.parent.root.appendChild(this.XMLDOM); | 3635 this.parent.root.appendChild(this.XMLDOM); |
3607 | 3636 |
3608 this.complete = function () { | 3637 this.complete = function () { |
3609 this.state = "complete"; | 3638 this.state = "complete"; |
3610 this.XMLDOM.setAttribute("state", "complete"); | 3639 this.XMLDOM.setAttribute("state", "complete"); |
3611 } | 3640 }; |
3612 }; | 3641 }; |
3613 this.update = function () { | 3642 this.update = function () { |
3614 this.SessionKey.update(); | 3643 this.SessionKey.update(); |
3615 } | 3644 }; |
3616 this.finish = function () { | 3645 this.finish = function () { |
3617 if (this.state == 0) { | 3646 if (this.state === 0) { |
3618 this.update(); | 3647 this.update(); |
3619 } | 3648 } |
3620 this.state = 1; | 3649 this.state = 1; |
3621 this.root.setAttribute("state", "complete"); | 3650 this.root.setAttribute("state", "complete"); |
3622 return this.root; | 3651 return this.root; |