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