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