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