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