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@503
|
1938 var node = doc.createElement('surveyelement');
|
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@374
|
1948 node.id = this.id;
|
n@374
|
1949 node.setAttribute("mandatory",this.mandatory);
|
n@374
|
1950 node.setAttribute("boxsize",this.boxsize);
|
n@374
|
1951 break;
|
n@374
|
1952 case "number":
|
n@374
|
1953 node.id = this.id;
|
n@374
|
1954 node.setAttribute("mandatory",this.mandatory);
|
n@374
|
1955 node.setAttribute("min", this.min);
|
n@374
|
1956 node.setAttribute("max", this.max);
|
n@374
|
1957 node.setAttribute("step", this.step);
|
n@374
|
1958 break;
|
n@374
|
1959 case "checkbox":
|
n@374
|
1960 case "radio":
|
n@374
|
1961 node.id = this.id;
|
n@374
|
1962 for (var i=0; i<this.options.length; i++)
|
n@374
|
1963 {
|
n@374
|
1964 var option = this.options[i];
|
n@503
|
1965 var optionNode = doc.createElement("option");
|
n@374
|
1966 optionNode.setAttribute("name",option.name);
|
n@374
|
1967 optionNode.textContent = option.text;
|
n@374
|
1968 node.appendChild(optionNode);
|
n@374
|
1969 }
|
n@374
|
1970 break;
|
nicholas@188
|
1971 }
|
n@374
|
1972 return node;
|
n@374
|
1973 };
|
n@374
|
1974 };
|
n@501
|
1975 this.decode = function(parent,xml) {
|
n@453
|
1976 this.location = xml.getAttribute('location');
|
n@453
|
1977 if (this.location == 'before'){this.location = 'pre';}
|
n@453
|
1978 else if (this.location == 'after'){this.location = 'post';}
|
n@453
|
1979 for (var i in xml.children)
|
n@453
|
1980 {
|
n@453
|
1981 if(isNaN(Number(i))==true){break;}
|
n@374
|
1982 var node = new this.OptionNode();
|
n@501
|
1983 node.decode(parent,xml.children[i]);
|
n@374
|
1984 this.options.push(node);
|
n@453
|
1985 }
|
n@453
|
1986 };
|
n@503
|
1987 this.encode = function(doc) {
|
n@503
|
1988 var node = doc.createElement('survey');
|
n@453
|
1989 node.setAttribute('location',this.location);
|
n@453
|
1990 for (var i=0; i<this.options.length; i++)
|
n@453
|
1991 {
|
n@503
|
1992 node.appendChild(this.options[i].exportXML(doc));
|
n@453
|
1993 }
|
n@453
|
1994 return node;
|
n@453
|
1995 };
|
n@453
|
1996 };
|
n@453
|
1997
|
n@453
|
1998 this.interfaceNode = function()
|
n@453
|
1999 {
|
n@453
|
2000 this.title = null;
|
n@453
|
2001 this.name = null;
|
n@453
|
2002 this.options = [];
|
n@453
|
2003 this.scales = [];
|
n@501
|
2004 this.schema = specification.schema.getAllElementsByName('interface')[1];
|
n@453
|
2005
|
n@501
|
2006 this.decode = function(parent,xml) {
|
n@453
|
2007 this.name = xml.getAttribute('name');
|
n@453
|
2008 var titleNode = xml.getElementsByTagName('title');
|
n@453
|
2009 if (titleNode.length == 1)
|
n@453
|
2010 {
|
n@453
|
2011 this.title = titleNode[0].textContent;
|
n@453
|
2012 }
|
n@453
|
2013 var interfaceOptionNodes = xml.getElementsByTagName('interfaceoption');
|
n@453
|
2014 // Extract interfaceoption node schema
|
n@501
|
2015 var interfaceOptionNodeSchema = this.schema.getAllElementsByName('interfaceoption')[0];
|
n@477
|
2016 var attributeMap = interfaceOptionNodeSchema.getAllElementsByTagName('xs:attribute');
|
n@453
|
2017 for (var i=0; i<interfaceOptionNodes.length; i++)
|
n@453
|
2018 {
|
n@453
|
2019 var ioNode = interfaceOptionNodes[i];
|
n@453
|
2020 var option = {};
|
n@453
|
2021 for (var j=0; j<attributeMap.length; j++)
|
n@453
|
2022 {
|
n@453
|
2023 var attributeName = attributeMap[j].getAttribute('name') || attributeMap[j].getAttribute('ref');
|
n@453
|
2024 var projectAttr = ioNode.getAttribute(attributeName);
|
n@453
|
2025 projectAttr = parent.processAttribute(projectAttr,attributeMap[j]);
|
n@453
|
2026 switch(typeof projectAttr)
|
n@453
|
2027 {
|
n@453
|
2028 case "number":
|
n@453
|
2029 case "boolean":
|
n@453
|
2030 eval('option.'+attributeName+' = '+projectAttr);
|
n@453
|
2031 break;
|
n@453
|
2032 case "string":
|
n@453
|
2033 eval('option.'+attributeName+' = "'+projectAttr+'"');
|
n@453
|
2034 break;
|
n@453
|
2035 }
|
n@453
|
2036 }
|
n@453
|
2037 this.options.push(option);
|
n@453
|
2038 }
|
n@453
|
2039
|
n@453
|
2040 // Now the scales nodes
|
n@453
|
2041 var scaleParent = xml.getElementsByTagName('scales');
|
n@453
|
2042 if (scaleParent.length == 1) {
|
n@453
|
2043 scaleParent = scaleParent[0];
|
n@453
|
2044 for (var i=0; i<scaleParent.children.length; i++) {
|
n@453
|
2045 var child = scaleParent.children[i];
|
n@453
|
2046 this.scales.push({
|
n@453
|
2047 text: child.textContent,
|
n@453
|
2048 position: Number(child.getAttribute('position'))
|
n@453
|
2049 });
|
n@374
|
2050 }
|
n@180
|
2051 }
|
n@180
|
2052 };
|
n@453
|
2053
|
n@503
|
2054 this.encode = function(doc) {
|
n@503
|
2055 var node = doc.createElement("interface");
|
n@503
|
2056 if (typeof name == "string")
|
n@503
|
2057 node.setAttribute("name",this.name);
|
n@503
|
2058 for (var option of this.options)
|
n@503
|
2059 {
|
n@503
|
2060 var child = doc.createElement("interfaceoption");
|
n@503
|
2061 child.setAttribute("type",option.type);
|
n@503
|
2062 child.setAttribute("name",option.name);
|
n@503
|
2063 node.appendChild(child);
|
n@503
|
2064 }
|
n@503
|
2065 if (this.scales.length != 0) {
|
n@503
|
2066 var scales = doc.createElement("scales");
|
n@503
|
2067 for (var scale of this.scales)
|
n@503
|
2068 {
|
n@503
|
2069 var child = doc.createElement("scalelabel");
|
n@503
|
2070 child.setAttribute("position",scale.position);
|
n@503
|
2071 child.textContent = scale.text;
|
n@503
|
2072 scales.appendChild(child);
|
n@503
|
2073 }
|
n@503
|
2074 node.appendChild(scales);
|
n@503
|
2075 }
|
n@503
|
2076 return node;
|
n@453
|
2077 };
|
n@180
|
2078 };
|
n@180
|
2079
|
n@501
|
2080 this.metricNode = function() {
|
n@501
|
2081 this.enabled = [];
|
n@501
|
2082 this.decode = function(parent, xml) {
|
n@501
|
2083 var children = xml.getElementsByTagName('metricenable');
|
n@501
|
2084 for (var i in children) {
|
n@501
|
2085 if (isNaN(Number(i)) == true){break;}
|
n@501
|
2086 this.enabled.push(children[i].textContent);
|
n@501
|
2087 }
|
n@501
|
2088 }
|
n@503
|
2089 this.encode = function(doc) {
|
n@503
|
2090 var node = doc.createElement('metric');
|
n@501
|
2091 for (var i in this.enabled)
|
n@501
|
2092 {
|
n@501
|
2093 if (isNaN(Number(i)) == true){break;}
|
n@503
|
2094 var child = doc.createElement('metricenable');
|
n@501
|
2095 child.textContent = this.enabled[i];
|
n@501
|
2096 node.appendChild(child);
|
n@501
|
2097 }
|
n@501
|
2098 return node;
|
n@501
|
2099 }
|
n@501
|
2100 }
|
n@501
|
2101
|
n@453
|
2102 this.page = function() {
|
n@374
|
2103 this.presentedId = undefined;
|
n@374
|
2104 this.id = undefined;
|
n@374
|
2105 this.hostURL = undefined;
|
n@374
|
2106 this.randomiseOrder = undefined;
|
n@374
|
2107 this.loop = undefined;
|
n@453
|
2108 this.showElementComments = undefined;
|
n@374
|
2109 this.outsideReference = null;
|
n@410
|
2110 this.loudness = null;
|
n@453
|
2111 this.preTest = null;
|
n@453
|
2112 this.postTest = null;
|
n@374
|
2113 this.interfaces = [];
|
n@374
|
2114 this.commentBoxPrefix = "Comment on track";
|
n@374
|
2115 this.audioElements = [];
|
n@374
|
2116 this.commentQuestions = [];
|
n@501
|
2117 this.schema = specification.schema.getAllElementsByName("page")[0];
|
n@374
|
2118
|
n@501
|
2119 this.decode = function(parent,xml)
|
n@374
|
2120 {
|
n@477
|
2121 var attributeMap = this.schema.getAllElementsByTagName('xs:attribute');
|
n@453
|
2122 for (var i=0; i<attributeMap.length; i++)
|
n@410
|
2123 {
|
n@453
|
2124 var attributeName = attributeMap[i].getAttribute('name') || attributeMap[i].getAttribute('ref');
|
n@453
|
2125 var projectAttr = xml.getAttribute(attributeName);
|
n@453
|
2126 projectAttr = parent.processAttribute(projectAttr,attributeMap[i]);
|
n@453
|
2127 switch(typeof projectAttr)
|
nicholas@417
|
2128 {
|
n@453
|
2129 case "number":
|
n@453
|
2130 case "boolean":
|
n@453
|
2131 eval('this.'+attributeName+' = '+projectAttr);
|
n@453
|
2132 break;
|
n@453
|
2133 case "string":
|
n@453
|
2134 eval('this.'+attributeName+' = "'+projectAttr+'"');
|
n@453
|
2135 break;
|
n@374
|
2136 }
|
n@374
|
2137 }
|
n@374
|
2138
|
n@453
|
2139 // Get the Comment Box Prefix
|
n@453
|
2140 var CBP = xml.getElementsByTagName('commentboxprefix');
|
n@453
|
2141 if (CBP.length != 0) {
|
n@453
|
2142 this.commentBoxPrefix = CBP[0].textContent;
|
n@427
|
2143 }
|
n@427
|
2144
|
n@453
|
2145 // Now decode the interfaces
|
n@453
|
2146 var interfaceNode = xml.getElementsByTagName('interface');
|
n@453
|
2147 for (var i=0; i<interfaceNode.length; i++)
|
n@453
|
2148 {
|
n@453
|
2149 var node = new parent.interfaceNode();
|
n@477
|
2150 node.decode(this,interfaceNode[i],parent.schema.getAllElementsByName('interface')[1]);
|
n@453
|
2151 this.interfaces.push(node);
|
n@453
|
2152 }
|
n@380
|
2153
|
n@453
|
2154 // Now process the survey node options
|
n@453
|
2155 var survey = xml.getElementsByTagName('survey');
|
n@477
|
2156 var surveySchema = parent.schema.getAllElementsByName('survey')[0];
|
n@453
|
2157 for (var i in survey) {
|
n@453
|
2158 if (isNaN(Number(i)) == true){break;}
|
n@453
|
2159 var location = survey[i].getAttribute('location');
|
n@453
|
2160 if (location == 'pre' || location == 'before')
|
n@453
|
2161 {
|
n@453
|
2162 if (this.preTest != null){this.errors.push("Already a pre/before test survey defined! Ignoring second!!");}
|
n@453
|
2163 else {
|
n@453
|
2164 this.preTest = new parent.surveyNode();
|
n@453
|
2165 this.preTest.decode(parent,survey[i],surveySchema);
|
n@453
|
2166 }
|
n@453
|
2167 } else if (location == 'post' || location == 'after') {
|
n@453
|
2168 if (this.postTest != null){this.errors.push("Already a post/after test survey defined! Ignoring second!!");}
|
n@453
|
2169 else {
|
n@453
|
2170 this.postTest = new parent.surveyNode();
|
n@453
|
2171 this.postTest.decode(parent,survey[i],surveySchema);
|
n@453
|
2172 }
|
n@453
|
2173 }
|
n@453
|
2174 }
|
n@453
|
2175
|
n@453
|
2176 // Now process the audioelement tags
|
n@453
|
2177 var audioElements = xml.getElementsByTagName('audioelement');
|
n@453
|
2178 for (var i=0; i<audioElements.length; i++)
|
n@453
|
2179 {
|
n@453
|
2180 var node = new this.audioElementNode();
|
n@501
|
2181 node.decode(this,audioElements[i]);
|
n@453
|
2182 this.audioElements.push(node);
|
n@453
|
2183 }
|
n@453
|
2184
|
n@453
|
2185 // Now decode the commentquestions
|
n@453
|
2186 var commentQuestions = xml.getElementsByTagName('commentquestion');
|
n@453
|
2187 for (var i=0; i<commentQuestions.length; i++)
|
n@453
|
2188 {
|
n@374
|
2189 var node = new this.commentQuestionNode();
|
n@501
|
2190 node.decode(parent,commentQuestions[i]);
|
n@374
|
2191 this.commentQuestions.push(node);
|
n@180
|
2192 }
|
n@180
|
2193 };
|
n@180
|
2194
|
n@374
|
2195 this.encode = function(root)
|
n@374
|
2196 {
|
n@503
|
2197 var AHNode = root.createElement("page");
|
n@503
|
2198 // First decode the attributes
|
n@503
|
2199 var attributes = this.schema.getAllElementsByTagName('xs:attribute');
|
n@503
|
2200 for (var i=0; i<attributes.length; i++)
|
n@503
|
2201 {
|
n@503
|
2202 var name = attributes[i].getAttribute("name");
|
n@503
|
2203 if (name == undefined) {
|
n@503
|
2204 name = attributes[i].getAttribute("ref");
|
n@503
|
2205 }
|
n@503
|
2206 if(eval("this."+name+" != undefined") || attributes[i].getAttribute("use") == "required")
|
n@503
|
2207 {
|
n@503
|
2208 eval("AHNode.setAttribute('"+name+"',this."+name+")");
|
n@503
|
2209 }
|
n@503
|
2210 }
|
n@410
|
2211 if(this.loudness != null) {AHNode.setAttribute("loudness",this.loudness);}
|
n@503
|
2212 // <commentboxprefix>
|
n@503
|
2213 var commentboxprefix = root.createElement("commentboxprefix");
|
n@503
|
2214 commentboxprefix.textContent = this.commentBoxPrefix;
|
n@503
|
2215 AHNode.appendChild(commentboxprefix);
|
n@503
|
2216
|
n@374
|
2217 for (var i=0; i<this.interfaces.length; i++)
|
n@324
|
2218 {
|
n@374
|
2219 AHNode.appendChild(this.interfaces[i].encode(root));
|
n@374
|
2220 }
|
n@374
|
2221
|
n@374
|
2222 for (var i=0; i<this.audioElements.length; i++) {
|
n@374
|
2223 AHNode.appendChild(this.audioElements[i].encode(root));
|
n@374
|
2224 }
|
n@374
|
2225 // Create <CommentQuestion>
|
n@374
|
2226 for (var i=0; i<this.commentQuestions.length; i++)
|
n@374
|
2227 {
|
n@503
|
2228 AHNode.appendChild(this.commentQuestions[i].encode(root));
|
n@374
|
2229 }
|
n@374
|
2230
|
n@503
|
2231 AHNode.appendChild(this.preTest.encode(root));
|
n@503
|
2232 AHNode.appendChild(this.postTest.encode(root));
|
n@374
|
2233 return AHNode;
|
n@374
|
2234 };
|
n@374
|
2235
|
n@453
|
2236 this.commentQuestionNode = function() {
|
n@453
|
2237 this.id = null;
|
n@453
|
2238 this.type = undefined;
|
n@374
|
2239 this.options = [];
|
n@453
|
2240 this.statement = undefined;
|
n@501
|
2241 this.schema = specification.schema.getAllElementsByName('commentquestion')[0];
|
n@501
|
2242 this.decode = function(parent,xml)
|
n@374
|
2243 {
|
n@453
|
2244 this.id = xml.id;
|
n@453
|
2245 this.type = xml.getAttribute('type');
|
n@453
|
2246 this.statement = xml.getElementsByTagName('statement')[0].textContent;
|
n@453
|
2247 var optNodes = xml.getElementsByTagName('option');
|
n@453
|
2248 for (var i=0; i<optNodes.length; i++)
|
n@453
|
2249 {
|
n@453
|
2250 var optNode = optNodes[i];
|
n@453
|
2251 this.options.push({
|
n@453
|
2252 name: optNode.getAttribute('name'),
|
n@453
|
2253 text: optNode.textContent
|
n@453
|
2254 });
|
n@374
|
2255 }
|
n@374
|
2256 };
|
n@453
|
2257
|
n@374
|
2258 this.encode = function(root)
|
n@374
|
2259 {
|
n@503
|
2260 var node = root.createElement("commentquestion");
|
n@503
|
2261 node.id = this.id;
|
n@503
|
2262 node.setAttribute("type",this.type);
|
n@503
|
2263 var statement = root.createElement("statement");
|
n@503
|
2264 statement.textContent = this.statement;
|
n@503
|
2265 node.appendChild(statement);
|
n@503
|
2266 for (var option of this.options)
|
n@503
|
2267 {
|
n@503
|
2268 var child = root.createElement("option");
|
n@503
|
2269 child.setAttribute("name",option.name);
|
n@503
|
2270 child.textContent = option.text;
|
n@503
|
2271 node.appendChild(child);
|
n@503
|
2272 }
|
n@503
|
2273 return node;
|
n@374
|
2274 };
|
n@374
|
2275 };
|
n@374
|
2276
|
n@374
|
2277 this.audioElementNode = function() {
|
n@374
|
2278 this.url = null;
|
n@374
|
2279 this.id = null;
|
n@374
|
2280 this.parent = null;
|
n@453
|
2281 this.type = null;
|
n@525
|
2282 this.marker = null;
|
n@374
|
2283 this.enforce = false;
|
n@400
|
2284 this.gain = 1.0;
|
n@501
|
2285 this.schema = specification.schema.getAllElementsByName('audioelement')[0];;
|
n@453
|
2286 this.parent = null;
|
n@501
|
2287 this.decode = function(parent,xml)
|
n@374
|
2288 {
|
n@374
|
2289 this.parent = parent;
|
n@477
|
2290 var attributeMap = this.schema.getAllElementsByTagName('xs:attribute');
|
n@453
|
2291 for (var i=0; i<attributeMap.length; i++)
|
n@400
|
2292 {
|
n@453
|
2293 var attributeName = attributeMap[i].getAttribute('name') || attributeMap[i].getAttribute('ref');
|
n@453
|
2294 var projectAttr = xml.getAttribute(attributeName);
|
n@453
|
2295 projectAttr = specification.processAttribute(projectAttr,attributeMap[i]);
|
n@453
|
2296 switch(typeof projectAttr)
|
n@374
|
2297 {
|
n@453
|
2298 case "number":
|
n@453
|
2299 case "boolean":
|
n@453
|
2300 eval('this.'+attributeName+' = '+projectAttr);
|
n@453
|
2301 break;
|
n@453
|
2302 case "string":
|
n@453
|
2303 eval('this.'+attributeName+' = "'+projectAttr+'"');
|
n@453
|
2304 break;
|
n@324
|
2305 }
|
n@324
|
2306 }
|
n@453
|
2307
|
n@374
|
2308 };
|
n@374
|
2309 this.encode = function(root)
|
n@374
|
2310 {
|
n@503
|
2311 var AENode = root.createElement("audioelement");
|
n@503
|
2312 var attributes = this.schema.getAllElementsByTagName('xs:attribute');
|
n@503
|
2313 for (var i=0; i<attributes.length; i++)
|
n@503
|
2314 {
|
n@503
|
2315 var name = attributes[i].getAttribute("name");
|
n@503
|
2316 if (name == undefined) {
|
n@503
|
2317 name = attributes[i].getAttribute("ref");
|
n@503
|
2318 }
|
n@503
|
2319 if(eval("this."+name+" != undefined") || attributes[i].getAttribute("use") == "required")
|
n@503
|
2320 {
|
n@503
|
2321 eval("AENode.setAttribute('"+name+"',this."+name+")");
|
n@503
|
2322 }
|
n@503
|
2323 }
|
n@374
|
2324 return AENode;
|
n@374
|
2325 };
|
n@180
|
2326 };
|
n@180
|
2327 };
|
n@180
|
2328 }
|
n@374
|
2329
|
n@182
|
2330 function Interface(specificationObject) {
|
n@180
|
2331 // This handles the bindings between the interface and the audioEngineContext;
|
n@182
|
2332 this.specification = specificationObject;
|
n@182
|
2333 this.insertPoint = document.getElementById("topLevelBody");
|
n@180
|
2334
|
n@453
|
2335 this.newPage = function(audioHolderObject,store)
|
n@375
|
2336 {
|
n@500
|
2337 audioEngineContext.newTestPage(audioHolderObject,store);
|
n@375
|
2338 interfaceContext.deleteCommentBoxes();
|
n@375
|
2339 interfaceContext.deleteCommentQuestions();
|
n@453
|
2340 loadTest(audioHolderObject,store);
|
n@375
|
2341 };
|
n@375
|
2342
|
n@182
|
2343 // Bounded by interface!!
|
n@182
|
2344 // Interface object MUST have an exportXMLDOM method which returns the various DOM levels
|
n@182
|
2345 // For example, APE returns the slider position normalised in a <value> tag.
|
n@182
|
2346 this.interfaceObjects = [];
|
n@182
|
2347 this.interfaceObject = function(){};
|
n@182
|
2348
|
n@302
|
2349 this.resizeWindow = function(event)
|
n@302
|
2350 {
|
n@395
|
2351 popup.resize(event);
|
n@302
|
2352 for(var i=0; i<this.commentBoxes.length; i++)
|
n@302
|
2353 {this.commentBoxes[i].resize();}
|
n@302
|
2354 for(var i=0; i<this.commentQuestions.length; i++)
|
n@302
|
2355 {this.commentQuestions[i].resize();}
|
n@302
|
2356 try
|
n@302
|
2357 {
|
n@302
|
2358 resizeWindow(event);
|
n@302
|
2359 }
|
n@302
|
2360 catch(err)
|
n@302
|
2361 {
|
n@302
|
2362 console.log("Warning - Interface does not have Resize option");
|
n@302
|
2363 console.log(err);
|
n@302
|
2364 }
|
n@302
|
2365 };
|
n@302
|
2366
|
n@356
|
2367 this.returnNavigator = function()
|
n@356
|
2368 {
|
n@491
|
2369 var node = storage.document.createElement("navigator");
|
n@491
|
2370 var platform = storage.document.createElement("platform");
|
n@356
|
2371 platform.textContent = navigator.platform;
|
n@491
|
2372 var vendor = storage.document.createElement("vendor");
|
n@356
|
2373 vendor.textContent = navigator.vendor;
|
n@491
|
2374 var userAgent = storage.document.createElement("uagent");
|
n@356
|
2375 userAgent.textContent = navigator.userAgent;
|
n@491
|
2376 var screen = storage.document.createElement("window");
|
n@491
|
2377 screen.setAttribute('innerWidth',window.innerWidth);
|
n@491
|
2378 screen.setAttribute('innerHeight',window.innerHeight);
|
n@356
|
2379 node.appendChild(platform);
|
n@356
|
2380 node.appendChild(vendor);
|
n@356
|
2381 node.appendChild(userAgent);
|
n@491
|
2382 node.appendChild(screen);
|
n@356
|
2383 return node;
|
n@356
|
2384 };
|
n@356
|
2385
|
n@182
|
2386 this.commentBoxes = [];
|
n@193
|
2387 this.elementCommentBox = function(audioObject) {
|
n@182
|
2388 var element = audioObject.specification;
|
n@183
|
2389 this.audioObject = audioObject;
|
n@182
|
2390 this.id = audioObject.id;
|
n@182
|
2391 var audioHolderObject = audioObject.specification.parent;
|
n@182
|
2392 // Create document objects to hold the comment boxes
|
n@182
|
2393 this.trackComment = document.createElement('div');
|
n@182
|
2394 this.trackComment.className = 'comment-div';
|
n@182
|
2395 this.trackComment.id = 'comment-div-'+audioObject.id;
|
n@182
|
2396 // Create a string next to each comment asking for a comment
|
n@183
|
2397 this.trackString = document.createElement('span');
|
n@496
|
2398 this.trackString.innerHTML = audioHolderObject.commentBoxPrefix+' '+audioObject.interfaceDOM.getPresentedId();
|
n@182
|
2399 // Create the HTML5 comment box 'textarea'
|
n@183
|
2400 this.trackCommentBox = document.createElement('textarea');
|
n@183
|
2401 this.trackCommentBox.rows = '4';
|
n@183
|
2402 this.trackCommentBox.cols = '100';
|
n@183
|
2403 this.trackCommentBox.name = 'trackComment'+audioObject.id;
|
n@183
|
2404 this.trackCommentBox.className = 'trackComment';
|
n@182
|
2405 var br = document.createElement('br');
|
n@182
|
2406 // Add to the holder.
|
n@183
|
2407 this.trackComment.appendChild(this.trackString);
|
n@182
|
2408 this.trackComment.appendChild(br);
|
n@183
|
2409 this.trackComment.appendChild(this.trackCommentBox);
|
n@183
|
2410
|
n@183
|
2411 this.exportXMLDOM = function() {
|
n@183
|
2412 var root = document.createElement('comment');
|
n@520
|
2413 var question = document.createElement('question');
|
n@520
|
2414 question.textContent = this.trackString.textContent;
|
n@520
|
2415 var response = document.createElement('response');
|
n@520
|
2416 response.textContent = this.trackCommentBox.value;
|
n@520
|
2417 console.log("Comment frag-"+this.id+": "+response.textContent);
|
n@520
|
2418 root.appendChild(question);
|
n@520
|
2419 root.appendChild(response);
|
n@183
|
2420 return root;
|
n@183
|
2421 };
|
n@302
|
2422 this.resize = function()
|
n@302
|
2423 {
|
n@302
|
2424 var boxwidth = (window.innerWidth-100)/2;
|
n@302
|
2425 if (boxwidth >= 600)
|
n@302
|
2426 {
|
n@302
|
2427 boxwidth = 600;
|
n@302
|
2428 }
|
n@302
|
2429 else if (boxwidth < 400)
|
n@302
|
2430 {
|
n@302
|
2431 boxwidth = 400;
|
n@302
|
2432 }
|
n@302
|
2433 this.trackComment.style.width = boxwidth+"px";
|
n@302
|
2434 this.trackCommentBox.style.width = boxwidth-6+"px";
|
n@302
|
2435 };
|
n@302
|
2436 this.resize();
|
n@182
|
2437 };
|
n@182
|
2438
|
n@193
|
2439 this.commentQuestions = [];
|
n@193
|
2440
|
n@193
|
2441 this.commentBox = function(commentQuestion) {
|
n@193
|
2442 this.specification = commentQuestion;
|
n@193
|
2443 // Create document objects to hold the comment boxes
|
n@193
|
2444 this.holder = document.createElement('div');
|
n@193
|
2445 this.holder.className = 'comment-div';
|
n@193
|
2446 // Create a string next to each comment asking for a comment
|
n@193
|
2447 this.string = document.createElement('span');
|
n@453
|
2448 this.string.innerHTML = commentQuestion.statement;
|
n@193
|
2449 // Create the HTML5 comment box 'textarea'
|
n@193
|
2450 this.textArea = document.createElement('textarea');
|
n@193
|
2451 this.textArea.rows = '4';
|
n@193
|
2452 this.textArea.cols = '100';
|
n@193
|
2453 this.textArea.className = 'trackComment';
|
n@193
|
2454 var br = document.createElement('br');
|
n@193
|
2455 // Add to the holder.
|
n@193
|
2456 this.holder.appendChild(this.string);
|
n@193
|
2457 this.holder.appendChild(br);
|
n@193
|
2458 this.holder.appendChild(this.textArea);
|
n@193
|
2459
|
n@520
|
2460 this.exportXMLDOM = function(storePoint) {
|
n@520
|
2461 var root = storePoint.parent.document.createElement('comment');
|
n@193
|
2462 root.id = this.specification.id;
|
n@193
|
2463 root.setAttribute('type',this.specification.type);
|
b@254
|
2464 console.log("Question: "+this.string.textContent);
|
b@254
|
2465 console.log("Response: "+root.textContent);
|
n@520
|
2466 var question = storePoint.parent.document.createElement('question');
|
n@520
|
2467 question.textContent = this.string.textContent;
|
n@520
|
2468 var response = storePoint.parent.document.createElement('response');
|
n@520
|
2469 response.textContent = this.textArea.value;
|
n@520
|
2470 root.appendChild(question);
|
n@520
|
2471 root.appendChild(response);
|
n@520
|
2472 storePoint.XMLDOM.appendChild(root);
|
n@193
|
2473 return root;
|
n@193
|
2474 };
|
n@302
|
2475 this.resize = function()
|
n@302
|
2476 {
|
n@302
|
2477 var boxwidth = (window.innerWidth-100)/2;
|
n@302
|
2478 if (boxwidth >= 600)
|
n@302
|
2479 {
|
n@302
|
2480 boxwidth = 600;
|
n@302
|
2481 }
|
n@302
|
2482 else if (boxwidth < 400)
|
n@302
|
2483 {
|
n@302
|
2484 boxwidth = 400;
|
n@302
|
2485 }
|
n@302
|
2486 this.holder.style.width = boxwidth+"px";
|
n@302
|
2487 this.textArea.style.width = boxwidth-6+"px";
|
n@302
|
2488 };
|
n@302
|
2489 this.resize();
|
n@193
|
2490 };
|
n@193
|
2491
|
n@193
|
2492 this.radioBox = function(commentQuestion) {
|
n@193
|
2493 this.specification = commentQuestion;
|
n@193
|
2494 // Create document objects to hold the comment boxes
|
n@193
|
2495 this.holder = document.createElement('div');
|
n@193
|
2496 this.holder.className = 'comment-div';
|
n@193
|
2497 // Create a string next to each comment asking for a comment
|
n@193
|
2498 this.string = document.createElement('span');
|
n@193
|
2499 this.string.innerHTML = commentQuestion.statement;
|
n@193
|
2500 var br = document.createElement('br');
|
n@193
|
2501 // Add to the holder.
|
n@193
|
2502 this.holder.appendChild(this.string);
|
n@193
|
2503 this.holder.appendChild(br);
|
n@193
|
2504 this.options = [];
|
n@193
|
2505 this.inputs = document.createElement('div');
|
n@193
|
2506 this.span = document.createElement('div');
|
n@193
|
2507 this.inputs.align = 'center';
|
n@193
|
2508 this.inputs.style.marginLeft = '12px';
|
n@193
|
2509 this.span.style.marginLeft = '12px';
|
n@193
|
2510 this.span.align = 'center';
|
n@193
|
2511 this.span.style.marginTop = '15px';
|
n@193
|
2512
|
n@193
|
2513 var optCount = commentQuestion.options.length;
|
n@453
|
2514 for (var optNode of commentQuestion.options)
|
n@193
|
2515 {
|
n@193
|
2516 var div = document.createElement('div');
|
n@301
|
2517 div.style.width = '80px';
|
n@193
|
2518 div.style.float = 'left';
|
n@193
|
2519 var input = document.createElement('input');
|
n@193
|
2520 input.type = 'radio';
|
n@193
|
2521 input.name = commentQuestion.id;
|
n@453
|
2522 input.setAttribute('setvalue',optNode.name);
|
n@193
|
2523 input.className = 'comment-radio';
|
n@193
|
2524 div.appendChild(input);
|
n@193
|
2525 this.inputs.appendChild(div);
|
n@193
|
2526
|
n@193
|
2527
|
n@193
|
2528 div = document.createElement('div');
|
n@301
|
2529 div.style.width = '80px';
|
n@193
|
2530 div.style.float = 'left';
|
n@193
|
2531 div.align = 'center';
|
n@193
|
2532 var span = document.createElement('span');
|
n@453
|
2533 span.textContent = optNode.text;
|
n@193
|
2534 span.className = 'comment-radio-span';
|
n@193
|
2535 div.appendChild(span);
|
n@193
|
2536 this.span.appendChild(div);
|
n@193
|
2537 this.options.push(input);
|
n@193
|
2538 }
|
n@193
|
2539 this.holder.appendChild(this.span);
|
n@193
|
2540 this.holder.appendChild(this.inputs);
|
n@193
|
2541
|
n@520
|
2542 this.exportXMLDOM = function(storePoint) {
|
n@520
|
2543 var root = storePoint.parent.document.createElement('comment');
|
n@193
|
2544 root.id = this.specification.id;
|
n@193
|
2545 root.setAttribute('type',this.specification.type);
|
n@193
|
2546 var question = document.createElement('question');
|
n@193
|
2547 question.textContent = this.string.textContent;
|
n@193
|
2548 var response = document.createElement('response');
|
n@193
|
2549 var i=0;
|
n@193
|
2550 while(this.options[i].checked == false) {
|
n@193
|
2551 i++;
|
n@193
|
2552 if (i >= this.options.length) {
|
n@193
|
2553 break;
|
n@193
|
2554 }
|
n@193
|
2555 }
|
n@193
|
2556 if (i >= this.options.length) {
|
n@193
|
2557 response.textContent = 'null';
|
n@193
|
2558 } else {
|
n@193
|
2559 response.textContent = this.options[i].getAttribute('setvalue');
|
n@193
|
2560 response.setAttribute('number',i);
|
n@193
|
2561 }
|
n@195
|
2562 console.log('Comment: '+question.textContent);
|
n@195
|
2563 console.log('Response: '+response.textContent);
|
n@193
|
2564 root.appendChild(question);
|
n@193
|
2565 root.appendChild(response);
|
n@520
|
2566 storePoint.XMLDOM.appendChild(root);
|
n@193
|
2567 return root;
|
n@193
|
2568 };
|
n@302
|
2569 this.resize = function()
|
n@302
|
2570 {
|
n@302
|
2571 var boxwidth = (window.innerWidth-100)/2;
|
n@302
|
2572 if (boxwidth >= 600)
|
n@302
|
2573 {
|
n@302
|
2574 boxwidth = 600;
|
n@302
|
2575 }
|
n@302
|
2576 else if (boxwidth < 400)
|
n@302
|
2577 {
|
n@302
|
2578 boxwidth = 400;
|
n@302
|
2579 }
|
n@302
|
2580 this.holder.style.width = boxwidth+"px";
|
n@302
|
2581 var text = this.holder.children[2];
|
n@302
|
2582 var options = this.holder.children[3];
|
n@302
|
2583 var optCount = options.children.length;
|
n@302
|
2584 var spanMargin = Math.floor(((boxwidth-20-(optCount*80))/(optCount))/2)+'px';
|
n@302
|
2585 var options = options.firstChild;
|
n@302
|
2586 var text = text.firstChild;
|
n@302
|
2587 options.style.marginRight = spanMargin;
|
n@302
|
2588 options.style.marginLeft = spanMargin;
|
n@302
|
2589 text.style.marginRight = spanMargin;
|
n@302
|
2590 text.style.marginLeft = spanMargin;
|
n@302
|
2591 while(options.nextSibling != undefined)
|
n@302
|
2592 {
|
n@302
|
2593 options = options.nextSibling;
|
n@302
|
2594 text = text.nextSibling;
|
n@302
|
2595 options.style.marginRight = spanMargin;
|
n@302
|
2596 options.style.marginLeft = spanMargin;
|
n@302
|
2597 text.style.marginRight = spanMargin;
|
n@302
|
2598 text.style.marginLeft = spanMargin;
|
n@302
|
2599 }
|
n@302
|
2600 };
|
n@302
|
2601 this.resize();
|
n@193
|
2602 };
|
n@193
|
2603
|
n@195
|
2604 this.checkboxBox = function(commentQuestion) {
|
n@195
|
2605 this.specification = commentQuestion;
|
n@195
|
2606 // Create document objects to hold the comment boxes
|
n@195
|
2607 this.holder = document.createElement('div');
|
n@195
|
2608 this.holder.className = 'comment-div';
|
n@195
|
2609 // Create a string next to each comment asking for a comment
|
n@195
|
2610 this.string = document.createElement('span');
|
n@195
|
2611 this.string.innerHTML = commentQuestion.statement;
|
n@195
|
2612 var br = document.createElement('br');
|
n@195
|
2613 // Add to the holder.
|
n@195
|
2614 this.holder.appendChild(this.string);
|
n@195
|
2615 this.holder.appendChild(br);
|
n@195
|
2616 this.options = [];
|
n@195
|
2617 this.inputs = document.createElement('div');
|
n@195
|
2618 this.span = document.createElement('div');
|
n@195
|
2619 this.inputs.align = 'center';
|
n@195
|
2620 this.inputs.style.marginLeft = '12px';
|
n@195
|
2621 this.span.style.marginLeft = '12px';
|
n@195
|
2622 this.span.align = 'center';
|
n@195
|
2623 this.span.style.marginTop = '15px';
|
n@195
|
2624
|
n@195
|
2625 var optCount = commentQuestion.options.length;
|
n@195
|
2626 for (var i=0; i<optCount; i++)
|
n@195
|
2627 {
|
n@195
|
2628 var div = document.createElement('div');
|
n@301
|
2629 div.style.width = '80px';
|
n@195
|
2630 div.style.float = 'left';
|
n@195
|
2631 var input = document.createElement('input');
|
n@195
|
2632 input.type = 'checkbox';
|
n@195
|
2633 input.name = commentQuestion.id;
|
n@195
|
2634 input.setAttribute('setvalue',commentQuestion.options[i].name);
|
n@195
|
2635 input.className = 'comment-radio';
|
n@195
|
2636 div.appendChild(input);
|
n@195
|
2637 this.inputs.appendChild(div);
|
n@195
|
2638
|
n@195
|
2639
|
n@195
|
2640 div = document.createElement('div');
|
n@301
|
2641 div.style.width = '80px';
|
n@195
|
2642 div.style.float = 'left';
|
n@195
|
2643 div.align = 'center';
|
n@195
|
2644 var span = document.createElement('span');
|
n@195
|
2645 span.textContent = commentQuestion.options[i].text;
|
n@195
|
2646 span.className = 'comment-radio-span';
|
n@195
|
2647 div.appendChild(span);
|
n@195
|
2648 this.span.appendChild(div);
|
n@195
|
2649 this.options.push(input);
|
n@195
|
2650 }
|
n@195
|
2651 this.holder.appendChild(this.span);
|
n@195
|
2652 this.holder.appendChild(this.inputs);
|
n@195
|
2653
|
n@520
|
2654 this.exportXMLDOM = function(storePoint) {
|
n@520
|
2655 var root = storePoint.parent.document.createElement('comment');
|
n@195
|
2656 root.id = this.specification.id;
|
n@195
|
2657 root.setAttribute('type',this.specification.type);
|
n@195
|
2658 var question = document.createElement('question');
|
n@195
|
2659 question.textContent = this.string.textContent;
|
n@195
|
2660 root.appendChild(question);
|
n@195
|
2661 console.log('Comment: '+question.textContent);
|
n@195
|
2662 for (var i=0; i<this.options.length; i++) {
|
n@195
|
2663 var response = document.createElement('response');
|
n@195
|
2664 response.textContent = this.options[i].checked;
|
n@195
|
2665 response.setAttribute('name',this.options[i].getAttribute('setvalue'));
|
n@195
|
2666 root.appendChild(response);
|
n@195
|
2667 console.log('Response '+response.getAttribute('name') +': '+response.textContent);
|
n@195
|
2668 }
|
n@520
|
2669 storePoint.XMLDOM.appendChild(root);
|
n@195
|
2670 return root;
|
n@195
|
2671 };
|
n@302
|
2672 this.resize = function()
|
n@302
|
2673 {
|
n@302
|
2674 var boxwidth = (window.innerWidth-100)/2;
|
n@302
|
2675 if (boxwidth >= 600)
|
n@302
|
2676 {
|
n@302
|
2677 boxwidth = 600;
|
n@302
|
2678 }
|
n@302
|
2679 else if (boxwidth < 400)
|
n@302
|
2680 {
|
n@302
|
2681 boxwidth = 400;
|
n@302
|
2682 }
|
n@302
|
2683 this.holder.style.width = boxwidth+"px";
|
n@302
|
2684 var text = this.holder.children[2];
|
n@302
|
2685 var options = this.holder.children[3];
|
n@302
|
2686 var optCount = options.children.length;
|
n@302
|
2687 var spanMargin = Math.floor(((boxwidth-20-(optCount*80))/(optCount))/2)+'px';
|
n@302
|
2688 var options = options.firstChild;
|
n@302
|
2689 var text = text.firstChild;
|
n@302
|
2690 options.style.marginRight = spanMargin;
|
n@302
|
2691 options.style.marginLeft = spanMargin;
|
n@302
|
2692 text.style.marginRight = spanMargin;
|
n@302
|
2693 text.style.marginLeft = spanMargin;
|
n@302
|
2694 while(options.nextSibling != undefined)
|
n@302
|
2695 {
|
n@302
|
2696 options = options.nextSibling;
|
n@302
|
2697 text = text.nextSibling;
|
n@302
|
2698 options.style.marginRight = spanMargin;
|
n@302
|
2699 options.style.marginLeft = spanMargin;
|
n@302
|
2700 text.style.marginRight = spanMargin;
|
n@302
|
2701 text.style.marginLeft = spanMargin;
|
n@302
|
2702 }
|
n@302
|
2703 };
|
n@302
|
2704 this.resize();
|
n@195
|
2705 };
|
n@193
|
2706
|
n@182
|
2707 this.createCommentBox = function(audioObject) {
|
n@193
|
2708 var node = new this.elementCommentBox(audioObject);
|
n@182
|
2709 this.commentBoxes.push(node);
|
n@182
|
2710 audioObject.commentDOM = node;
|
n@182
|
2711 return node;
|
n@182
|
2712 };
|
n@182
|
2713
|
n@182
|
2714 this.sortCommentBoxes = function() {
|
n@467
|
2715 this.commentBoxes.sort(function(a,b){return a.id - b.id;});
|
n@182
|
2716 };
|
n@182
|
2717
|
n@182
|
2718 this.showCommentBoxes = function(inject, sort) {
|
n@182
|
2719 if (sort) {interfaceContext.sortCommentBoxes();}
|
n@467
|
2720 for (var box of interfaceContext.commentBoxes) {
|
n@467
|
2721 inject.appendChild(box.trackComment);
|
n@182
|
2722 }
|
n@182
|
2723 };
|
n@193
|
2724
|
nicholas@211
|
2725 this.deleteCommentBoxes = function() {
|
nicholas@211
|
2726 this.commentBoxes = [];
|
nicholas@237
|
2727 };
|
nicholas@211
|
2728
|
n@193
|
2729 this.createCommentQuestion = function(element) {
|
n@193
|
2730 var node;
|
n@453
|
2731 if (element.type == 'question') {
|
n@193
|
2732 node = new this.commentBox(element);
|
n@193
|
2733 } else if (element.type == 'radio') {
|
n@193
|
2734 node = new this.radioBox(element);
|
n@195
|
2735 } else if (element.type == 'checkbox') {
|
n@195
|
2736 node = new this.checkboxBox(element);
|
n@193
|
2737 }
|
n@193
|
2738 this.commentQuestions.push(node);
|
n@193
|
2739 return node;
|
n@193
|
2740 };
|
n@201
|
2741
|
nicholas@237
|
2742 this.deleteCommentQuestions = function()
|
nicholas@237
|
2743 {
|
nicholas@237
|
2744 this.commentQuestions = [];
|
nicholas@237
|
2745 };
|
nicholas@237
|
2746
|
n@201
|
2747 this.playhead = new function()
|
n@201
|
2748 {
|
n@201
|
2749 this.object = document.createElement('div');
|
n@201
|
2750 this.object.className = 'playhead';
|
n@201
|
2751 this.object.align = 'left';
|
n@201
|
2752 var curTime = document.createElement('div');
|
n@201
|
2753 curTime.style.width = '50px';
|
n@201
|
2754 this.curTimeSpan = document.createElement('span');
|
n@201
|
2755 this.curTimeSpan.textContent = '00:00';
|
n@201
|
2756 curTime.appendChild(this.curTimeSpan);
|
n@201
|
2757 this.object.appendChild(curTime);
|
n@201
|
2758 this.scrubberTrack = document.createElement('div');
|
n@201
|
2759 this.scrubberTrack.className = 'playhead-scrub-track';
|
n@201
|
2760
|
n@201
|
2761 this.scrubberHead = document.createElement('div');
|
n@201
|
2762 this.scrubberHead.id = 'playhead-scrubber';
|
n@201
|
2763 this.scrubberTrack.appendChild(this.scrubberHead);
|
n@201
|
2764 this.object.appendChild(this.scrubberTrack);
|
n@201
|
2765
|
n@201
|
2766 this.timePerPixel = 0;
|
n@201
|
2767 this.maxTime = 0;
|
n@201
|
2768
|
n@204
|
2769 this.playbackObject;
|
n@204
|
2770
|
n@204
|
2771 this.setTimePerPixel = function(audioObject) {
|
n@201
|
2772 //maxTime must be in seconds
|
n@204
|
2773 this.playbackObject = audioObject;
|
n@379
|
2774 this.maxTime = audioObject.buffer.buffer.duration;
|
n@201
|
2775 var width = 490; //500 - 10, 5 each side of the tracker head
|
n@204
|
2776 this.timePerPixel = this.maxTime/490;
|
n@204
|
2777 if (this.maxTime < 60) {
|
n@201
|
2778 this.curTimeSpan.textContent = '0.00';
|
n@201
|
2779 } else {
|
n@201
|
2780 this.curTimeSpan.textContent = '00:00';
|
n@201
|
2781 }
|
n@201
|
2782 };
|
n@201
|
2783
|
n@204
|
2784 this.update = function() {
|
n@201
|
2785 // Update the playhead position, startPlay must be called
|
n@201
|
2786 if (this.timePerPixel > 0) {
|
n@204
|
2787 var time = this.playbackObject.getCurrentPosition();
|
n@498
|
2788 if (time > 0 && time < this.maxTime) {
|
nicholas@267
|
2789 var width = 490;
|
nicholas@267
|
2790 var pix = Math.floor(time/this.timePerPixel);
|
nicholas@267
|
2791 this.scrubberHead.style.left = pix+'px';
|
nicholas@267
|
2792 if (this.maxTime > 60.0) {
|
nicholas@267
|
2793 var secs = time%60;
|
nicholas@267
|
2794 var mins = Math.floor((time-secs)/60);
|
nicholas@267
|
2795 secs = secs.toString();
|
nicholas@267
|
2796 secs = secs.substr(0,2);
|
nicholas@267
|
2797 mins = mins.toString();
|
nicholas@267
|
2798 this.curTimeSpan.textContent = mins+':'+secs;
|
nicholas@267
|
2799 } else {
|
nicholas@267
|
2800 time = time.toString();
|
nicholas@267
|
2801 this.curTimeSpan.textContent = time.substr(0,4);
|
nicholas@267
|
2802 }
|
n@201
|
2803 } else {
|
nicholas@267
|
2804 this.scrubberHead.style.left = '0px';
|
nicholas@267
|
2805 if (this.maxTime < 60) {
|
nicholas@267
|
2806 this.curTimeSpan.textContent = '0.00';
|
nicholas@267
|
2807 } else {
|
nicholas@267
|
2808 this.curTimeSpan.textContent = '00:00';
|
nicholas@267
|
2809 }
|
n@201
|
2810 }
|
n@201
|
2811 }
|
n@201
|
2812 };
|
n@204
|
2813
|
n@204
|
2814 this.interval = undefined;
|
n@204
|
2815
|
n@204
|
2816 this.start = function() {
|
n@204
|
2817 if (this.playbackObject != undefined && this.interval == undefined) {
|
nicholas@267
|
2818 if (this.maxTime < 60) {
|
nicholas@267
|
2819 this.interval = setInterval(function(){interfaceContext.playhead.update();},10);
|
nicholas@267
|
2820 } else {
|
nicholas@267
|
2821 this.interval = setInterval(function(){interfaceContext.playhead.update();},100);
|
nicholas@267
|
2822 }
|
n@204
|
2823 }
|
n@204
|
2824 };
|
n@204
|
2825 this.stop = function() {
|
n@204
|
2826 clearInterval(this.interval);
|
n@204
|
2827 this.interval = undefined;
|
n@527
|
2828 this.scrubberHead.style.left = '0px';
|
nicholas@267
|
2829 if (this.maxTime < 60) {
|
nicholas@267
|
2830 this.curTimeSpan.textContent = '0.00';
|
nicholas@267
|
2831 } else {
|
nicholas@267
|
2832 this.curTimeSpan.textContent = '00:00';
|
nicholas@267
|
2833 }
|
n@204
|
2834 };
|
n@201
|
2835 };
|
n@483
|
2836
|
n@483
|
2837 this.volume = new function()
|
n@483
|
2838 {
|
n@483
|
2839 // An in-built volume module which can be viewed on page
|
n@483
|
2840 // Includes trackers on page-by-page data
|
n@483
|
2841 // Volume does NOT reset to 0dB on each page load
|
n@483
|
2842 this.valueLin = 1.0;
|
n@483
|
2843 this.valueDB = 0.0;
|
n@483
|
2844 this.object = document.createElement('div');
|
n@483
|
2845 this.object.id = 'master-volume-holder';
|
n@483
|
2846 this.slider = document.createElement('input');
|
n@483
|
2847 this.slider.id = 'master-volume-control';
|
n@483
|
2848 this.slider.type = 'range';
|
n@483
|
2849 this.valueText = document.createElement('span');
|
n@483
|
2850 this.valueText.id = 'master-volume-feedback';
|
n@483
|
2851 this.valueText.textContent = '0dB';
|
n@483
|
2852
|
n@483
|
2853 this.slider.min = -60;
|
n@483
|
2854 this.slider.max = 12;
|
n@483
|
2855 this.slider.value = 0;
|
n@483
|
2856 this.slider.step = 1;
|
n@483
|
2857 this.slider.onmousemove = function(event)
|
n@483
|
2858 {
|
n@483
|
2859 interfaceContext.volume.valueDB = event.currentTarget.value;
|
n@483
|
2860 interfaceContext.volume.valueLin = decibelToLinear(interfaceContext.volume.valueDB);
|
n@483
|
2861 interfaceContext.volume.valueText.textContent = interfaceContext.volume.valueDB+'dB';
|
n@483
|
2862 audioEngineContext.outputGain.gain.value = interfaceContext.volume.valueLin;
|
n@483
|
2863 }
|
n@483
|
2864 this.slider.onmouseup = function(event)
|
n@483
|
2865 {
|
n@526
|
2866 var storePoint = testState.currentStore.XMLDOM.getElementsByTagName('metric')[0].getAllElementsByName('volumeTracker');
|
n@483
|
2867 if (storePoint.length == 0)
|
n@483
|
2868 {
|
n@483
|
2869 storePoint = storage.document.createElement('metricresult');
|
n@483
|
2870 storePoint.setAttribute('name','volumeTracker');
|
n@526
|
2871 testState.currentStore.XMLDOM.getElementsByTagName('metric')[0].appendChild(storePoint);
|
n@483
|
2872 }
|
n@483
|
2873 else {
|
n@483
|
2874 storePoint = storePoint[0];
|
n@483
|
2875 }
|
n@483
|
2876 var node = storage.document.createElement('movement');
|
n@483
|
2877 node.setAttribute('test-time',audioEngineContext.timer.getTestTime());
|
n@483
|
2878 node.setAttribute('volume',interfaceContext.volume.valueDB);
|
n@483
|
2879 node.setAttribute('format','dBFS');
|
n@483
|
2880 storePoint.appendChild(node);
|
n@483
|
2881 }
|
n@483
|
2882
|
n@484
|
2883 var title = document.createElement('div');
|
n@484
|
2884 title.innerHTML = '<span>Master Volume Control</span>';
|
n@484
|
2885 title.style.fontSize = '0.75em';
|
n@484
|
2886 title.style.width = "100%";
|
n@484
|
2887 title.align = 'center';
|
n@484
|
2888 this.object.appendChild(title);
|
n@484
|
2889
|
n@483
|
2890 this.object.appendChild(this.slider);
|
n@483
|
2891 this.object.appendChild(this.valueText);
|
n@483
|
2892 }
|
nicholas@235
|
2893 // Global Checkers
|
nicholas@235
|
2894 // These functions will help enforce the checkers
|
nicholas@235
|
2895 this.checkHiddenAnchor = function()
|
nicholas@235
|
2896 {
|
n@453
|
2897 for (var ao of audioEngineContext.audioObjects)
|
nicholas@235
|
2898 {
|
n@453
|
2899 if (ao.specification.type == "anchor")
|
nicholas@235
|
2900 {
|
n@454
|
2901 if (ao.interfaceDOM.getValue() > (ao.specification.marker/100) && ao.specification.marker > 0) {
|
n@453
|
2902 // Anchor is not set below
|
n@453
|
2903 console.log('Anchor node not below marker value');
|
n@453
|
2904 alert('Please keep listening');
|
n@498
|
2905 this.storeErrorNode('Anchor node not below marker value');
|
n@453
|
2906 return false;
|
n@453
|
2907 }
|
nicholas@235
|
2908 }
|
nicholas@235
|
2909 }
|
nicholas@235
|
2910 return true;
|
nicholas@235
|
2911 };
|
nicholas@235
|
2912
|
nicholas@235
|
2913 this.checkHiddenReference = function()
|
nicholas@235
|
2914 {
|
n@453
|
2915 for (var ao of audioEngineContext.audioObjects)
|
nicholas@235
|
2916 {
|
n@453
|
2917 if (ao.specification.type == "reference")
|
nicholas@235
|
2918 {
|
n@454
|
2919 if (ao.interfaceDOM.getValue() < (ao.specification.marker/100) && ao.specification.marker > 0) {
|
n@453
|
2920 // Anchor is not set below
|
n@498
|
2921 console.log('Reference node not above marker value');
|
n@498
|
2922 this.storeErrorNode('Reference node not above marker value');
|
n@453
|
2923 alert('Please keep listening');
|
n@453
|
2924 return false;
|
n@453
|
2925 }
|
nicholas@235
|
2926 }
|
nicholas@235
|
2927 }
|
nicholas@235
|
2928 return true;
|
nicholas@235
|
2929 };
|
n@366
|
2930
|
n@366
|
2931 this.checkFragmentsFullyPlayed = function ()
|
n@366
|
2932 {
|
n@366
|
2933 // Checks the entire file has been played back
|
n@366
|
2934 // NOTE ! This will return true IF playback is Looped!!!
|
n@366
|
2935 if (audioEngineContext.loopPlayback)
|
n@366
|
2936 {
|
n@366
|
2937 console.log("WARNING - Looped source: Cannot check fragments are fully played");
|
n@366
|
2938 return true;
|
n@366
|
2939 }
|
n@366
|
2940 var check_pass = true;
|
n@366
|
2941 var error_obj = [];
|
n@366
|
2942 for (var i = 0; i<audioEngineContext.audioObjects.length; i++)
|
n@366
|
2943 {
|
n@366
|
2944 var object = audioEngineContext.audioObjects[i];
|
nicholas@415
|
2945 var time = object.buffer.buffer.duration;
|
n@366
|
2946 var metric = object.metric;
|
n@366
|
2947 var passed = false;
|
n@366
|
2948 for (var j=0; j<metric.listenTracker.length; j++)
|
n@366
|
2949 {
|
n@366
|
2950 var bt = metric.listenTracker[j].getElementsByTagName('buffertime');
|
n@366
|
2951 var start_time = Number(bt[0].getAttribute('start'));
|
n@366
|
2952 var stop_time = Number(bt[0].getAttribute('stop'));
|
n@366
|
2953 var delta = stop_time - start_time;
|
n@366
|
2954 if (delta >= time)
|
n@366
|
2955 {
|
n@366
|
2956 passed = true;
|
n@366
|
2957 break;
|
n@366
|
2958 }
|
n@366
|
2959 }
|
n@366
|
2960 if (passed == false)
|
n@366
|
2961 {
|
n@366
|
2962 check_pass = false;
|
n@469
|
2963 console.log("Continue listening to track-"+audioEngineContext.audioObjects.interfaceDOM.getPresentedId());
|
n@469
|
2964 error_obj.push(audioEngineContext.audioObjects.interfaceDOM.getPresentedId());
|
n@366
|
2965 }
|
n@366
|
2966 }
|
n@366
|
2967 if (check_pass == false)
|
n@366
|
2968 {
|
nicholas@415
|
2969 var str_start = "You have not completely listened to fragments ";
|
n@366
|
2970 for (var i=0; i<error_obj.length; i++)
|
n@366
|
2971 {
|
n@366
|
2972 str_start += error_obj[i];
|
n@366
|
2973 if (i != error_obj.length-1)
|
n@366
|
2974 {
|
n@366
|
2975 str_start += ', ';
|
n@366
|
2976 }
|
n@366
|
2977 }
|
n@366
|
2978 str_start += ". Please keep listening";
|
n@366
|
2979 console.log("[ALERT]: "+str_start);
|
n@498
|
2980 this.storeErrorNode("[ALERT]: "+str_start);
|
n@366
|
2981 alert(str_start);
|
n@366
|
2982 }
|
n@366
|
2983 };
|
nicholas@421
|
2984 this.checkAllMoved = function()
|
nicholas@421
|
2985 {
|
nicholas@421
|
2986 var str = "You have not moved ";
|
nicholas@421
|
2987 var failed = [];
|
n@469
|
2988 for (var ao of audioEngineContext.audioObjects)
|
nicholas@421
|
2989 {
|
n@469
|
2990 if(ao.metric.wasMoved == false && ao.interfaceDOM.canMove() == true)
|
nicholas@421
|
2991 {
|
n@469
|
2992 failed.push(ao.interfaceDOM.getPresentedId());
|
nicholas@421
|
2993 }
|
nicholas@421
|
2994 }
|
nicholas@421
|
2995 if (failed.length == 0)
|
nicholas@421
|
2996 {
|
nicholas@421
|
2997 return true;
|
nicholas@421
|
2998 } else if (failed.length == 1)
|
nicholas@421
|
2999 {
|
nicholas@421
|
3000 str += 'track '+failed[0];
|
nicholas@421
|
3001 } else {
|
nicholas@421
|
3002 str += 'tracks ';
|
nicholas@421
|
3003 for (var i=0; i<failed.length-1; i++)
|
nicholas@421
|
3004 {
|
nicholas@421
|
3005 str += failed[i]+', ';
|
nicholas@421
|
3006 }
|
nicholas@421
|
3007 str += 'and '+failed[i];
|
nicholas@421
|
3008 }
|
nicholas@421
|
3009 str +='.';
|
nicholas@421
|
3010 alert(str);
|
nicholas@421
|
3011 console.log(str);
|
n@498
|
3012 this.storeErrorNode(str);
|
nicholas@421
|
3013 return false;
|
nicholas@421
|
3014 };
|
nicholas@421
|
3015 this.checkAllPlayed = function()
|
nicholas@421
|
3016 {
|
nicholas@421
|
3017 var str = "You have not played ";
|
nicholas@421
|
3018 var failed = [];
|
n@469
|
3019 for (var ao of audioEngineContext.audioObjects)
|
nicholas@421
|
3020 {
|
n@469
|
3021 if(ao.metric.wasListenedTo == false)
|
nicholas@421
|
3022 {
|
n@469
|
3023 failed.push(ao.interfaceDOM.getPresentedId());
|
nicholas@421
|
3024 }
|
nicholas@421
|
3025 }
|
nicholas@421
|
3026 if (failed.length == 0)
|
nicholas@421
|
3027 {
|
nicholas@421
|
3028 return true;
|
nicholas@421
|
3029 } else if (failed.length == 1)
|
nicholas@421
|
3030 {
|
nicholas@421
|
3031 str += 'track '+failed[0];
|
nicholas@421
|
3032 } else {
|
nicholas@421
|
3033 str += 'tracks ';
|
nicholas@421
|
3034 for (var i=0; i<failed.length-1; i++)
|
nicholas@421
|
3035 {
|
nicholas@421
|
3036 str += failed[i]+', ';
|
nicholas@421
|
3037 }
|
nicholas@421
|
3038 str += 'and '+failed[i];
|
nicholas@421
|
3039 }
|
nicholas@421
|
3040 str +='.';
|
nicholas@421
|
3041 alert(str);
|
nicholas@421
|
3042 console.log(str);
|
n@498
|
3043 this.storeErrorNode(str);
|
nicholas@421
|
3044 return false;
|
nicholas@421
|
3045 };
|
n@498
|
3046
|
n@498
|
3047 this.storeErrorNode = function(errorMessage)
|
n@498
|
3048 {
|
n@498
|
3049 var time = audioEngineContext.timer.getTestTime();
|
n@498
|
3050 var node = storage.document.createElement('error');
|
n@498
|
3051 node.setAttribute('time',time);
|
n@498
|
3052 node.textContent = errorMessage;
|
n@498
|
3053 testState.currentStore.XMLDOM.appendChild(node);
|
n@498
|
3054 };
|
n@453
|
3055 }
|
n@453
|
3056
|
n@453
|
3057 function Storage()
|
n@453
|
3058 {
|
n@453
|
3059 // Holds results in XML format until ready for collection
|
n@453
|
3060 this.globalPreTest = null;
|
n@453
|
3061 this.globalPostTest = null;
|
n@453
|
3062 this.testPages = [];
|
n@453
|
3063 this.document = document.implementation.createDocument(null,"waetresult");
|
n@453
|
3064 this.root = this.document.children[0];
|
n@453
|
3065 this.state = 0;
|
n@453
|
3066
|
n@453
|
3067 this.initialise = function()
|
n@453
|
3068 {
|
n@471
|
3069 if (specification.preTest != undefined){this.globalPreTest = new this.surveyNode(this,this.root,specification.preTest);}
|
n@471
|
3070 if (specification.postTest != undefined){this.globalPostTest = new this.surveyNode(this,this.root,specification.postTest);}
|
n@453
|
3071 };
|
n@453
|
3072
|
n@453
|
3073 this.createTestPageStore = function(specification)
|
n@453
|
3074 {
|
n@453
|
3075 var store = new this.pageNode(this,specification);
|
n@453
|
3076 this.testPages.push(store);
|
n@453
|
3077 return this.testPages[this.testPages.length-1];
|
n@453
|
3078 };
|
n@453
|
3079
|
n@453
|
3080 this.surveyNode = function(parent,root,specification)
|
n@453
|
3081 {
|
n@453
|
3082 this.specification = specification;
|
n@453
|
3083 this.parent = parent;
|
n@453
|
3084 this.XMLDOM = this.parent.document.createElement('survey');
|
n@453
|
3085 this.XMLDOM.setAttribute('location',this.specification.location);
|
n@453
|
3086 for (var optNode of this.specification.options)
|
n@453
|
3087 {
|
n@453
|
3088 if (optNode.type != 'statement')
|
n@453
|
3089 {
|
n@453
|
3090 var node = this.parent.document.createElement('surveyresult');
|
n@453
|
3091 node.id = optNode.id;
|
n@453
|
3092 node.setAttribute('type',optNode.type);
|
n@453
|
3093 this.XMLDOM.appendChild(node);
|
n@453
|
3094 }
|
n@453
|
3095 }
|
n@453
|
3096 root.appendChild(this.XMLDOM);
|
n@453
|
3097
|
n@453
|
3098 this.postResult = function(node)
|
n@453
|
3099 {
|
n@453
|
3100 // From popup: node is the popupOption node containing both spec. and results
|
n@453
|
3101 // ID is the position
|
n@453
|
3102 if (node.specification.type == 'statement'){return;}
|
n@453
|
3103 var surveyresult = this.parent.document.getElementById(node.specification.id);
|
n@453
|
3104 switch(node.specification.type)
|
n@453
|
3105 {
|
n@453
|
3106 case "number":
|
n@453
|
3107 case "question":
|
n@453
|
3108 var child = this.parent.document.createElement('response');
|
n@453
|
3109 child.textContent = node.response;
|
n@453
|
3110 surveyresult.appendChild(child);
|
n@453
|
3111 break;
|
n@453
|
3112 case "radio":
|
n@453
|
3113 var child = this.parent.document.createElement('response');
|
n@453
|
3114 child.setAttribute('name',node.response.name);
|
n@453
|
3115 child.textContent = node.response.text;
|
n@453
|
3116 surveyresult.appendChild(child);
|
n@453
|
3117 break;
|
n@453
|
3118 case "checkbox":
|
n@453
|
3119 for (var i=0; i<node.response.length; i++)
|
n@453
|
3120 {
|
n@453
|
3121 var checkNode = this.parent.document.createElement('response');
|
n@476
|
3122 checkNode.setAttribute('name',node.response[i].name);
|
n@476
|
3123 checkNode.setAttribute('checked',node.response[i].checked);
|
n@455
|
3124 surveyresult.appendChild(checkNode);
|
n@453
|
3125 }
|
n@453
|
3126 break;
|
n@453
|
3127 }
|
n@453
|
3128 };
|
n@453
|
3129 };
|
n@453
|
3130
|
n@453
|
3131 this.pageNode = function(parent,specification)
|
n@453
|
3132 {
|
n@453
|
3133 // Create one store per test page
|
n@453
|
3134 this.specification = specification;
|
n@453
|
3135 this.parent = parent;
|
n@453
|
3136 this.XMLDOM = this.parent.document.createElement('page');
|
n@453
|
3137 this.XMLDOM.setAttribute('id',specification.id);
|
n@453
|
3138 this.XMLDOM.setAttribute('presentedId',specification.presentedId);
|
n@474
|
3139 if (specification.preTest != undefined){this.preTest = new this.parent.surveyNode(this.parent,this.XMLDOM,this.specification.preTest);}
|
n@474
|
3140 if (specification.postTest != undefined){this.postTest = new this.parent.surveyNode(this.parent,this.XMLDOM,this.specification.postTest);}
|
n@453
|
3141
|
n@453
|
3142 // Add any page metrics
|
n@453
|
3143 var page_metric = this.parent.document.createElement('metric');
|
n@453
|
3144 this.XMLDOM.appendChild(page_metric);
|
n@453
|
3145
|
n@453
|
3146 // Add the audioelement
|
n@453
|
3147 for (var element of this.specification.audioElements)
|
n@453
|
3148 {
|
n@453
|
3149 var aeNode = this.parent.document.createElement('audioelement');
|
n@453
|
3150 aeNode.id = element.id;
|
n@453
|
3151 aeNode.setAttribute('type',element.type);
|
n@453
|
3152 aeNode.setAttribute('url', element.url);
|
n@453
|
3153 aeNode.setAttribute('gain', element.gain);
|
n@453
|
3154 if (element.type == 'anchor' || element.type == 'reference')
|
n@453
|
3155 {
|
n@453
|
3156 if (element.marker > 0)
|
n@453
|
3157 {
|
n@453
|
3158 aeNode.setAttribute('marker',element.marker);
|
n@453
|
3159 }
|
n@453
|
3160 }
|
n@453
|
3161 var ae_metric = this.parent.document.createElement('metric');
|
n@453
|
3162 aeNode.appendChild(ae_metric);
|
n@453
|
3163 this.XMLDOM.appendChild(aeNode);
|
n@453
|
3164 }
|
n@453
|
3165
|
n@453
|
3166 this.parent.root.appendChild(this.XMLDOM);
|
n@453
|
3167 };
|
n@453
|
3168 this.finish = function()
|
n@453
|
3169 {
|
n@453
|
3170 if (this.state == 0)
|
n@453
|
3171 {
|
n@453
|
3172 var projectDocument = specification.projectXML;
|
n@453
|
3173 projectDocument.setAttribute('file-name',url);
|
n@453
|
3174 this.root.appendChild(projectDocument);
|
n@453
|
3175 this.root.appendChild(returnDateNode());
|
n@453
|
3176 this.root.appendChild(interfaceContext.returnNavigator());
|
n@453
|
3177 }
|
n@453
|
3178 this.state = 1;
|
n@453
|
3179 return this.root;
|
n@453
|
3180 };
|
n@453
|
3181 }
|