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