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