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