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