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