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