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