Mercurial > hg > webaudioevaluationtool
comparison ape.js @ 937:b5262d076f0b
Merge 'default' and 'Dev_main' branch
author | Brecht De Man <BrechtDeMan@users.noreply.github.com> |
---|---|
date | Sun, 17 May 2015 18:40:56 +0100 |
parents | |
children | 759c2ace3c65 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 937:b5262d076f0b |
---|---|
1 /** | |
2 * ape.js | |
3 * Create the APE interface | |
4 */ | |
5 | |
6 var currentState; // Keep track of the current state (pre/post test, which test, final test? first test?) | |
7 // preTest - In preTest state | |
8 // testRun-ID - In test running, test Id number at the end 'testRun-2' | |
9 // testRunPost-ID - Post test of test ID | |
10 // testRunPre-ID - Pre-test of test ID | |
11 // postTest - End of test, final submission! | |
12 | |
13 | |
14 // Create empty array to log whether different samples have been played | |
15 var hasBeenPlayed = []; // HERE? | |
16 | |
17 | |
18 // Once this is loaded and parsed, begin execution | |
19 loadInterface(projectXML); | |
20 | |
21 function loadInterface(xmlDoc) { | |
22 | |
23 // Get the dimensions of the screen available to the page | |
24 var width = window.innerWidth; | |
25 var height = window.innerHeight; | |
26 | |
27 // The injection point into the HTML page | |
28 var insertPoint = document.getElementById("topLevelBody"); | |
29 var testContent = document.createElement('div'); | |
30 testContent.id = 'testContent'; | |
31 | |
32 | |
33 // Decode parts of the xmlDoc that are needed | |
34 // xmlDoc MUST already be parsed by jQuery! | |
35 var xmlSetup = xmlDoc.find('setup'); | |
36 // Should put in an error function here incase of malprocessed or malformed XML | |
37 | |
38 // Extract the different test XML DOM trees | |
39 var audioHolders = xmlDoc.find('audioHolder'); | |
40 audioHolders.each(function(index,element) { | |
41 var repeatN = element.attributes['repeatCount'].value; | |
42 for (var r=0; r<=repeatN; r++) { | |
43 testXMLSetups[testXMLSetups.length] = element; | |
44 } | |
45 }); | |
46 | |
47 // New check if we need to randomise the test order | |
48 var randomise = xmlSetup[0].attributes['randomiseOrder']; | |
49 if (randomise != undefined) { | |
50 randomise = Boolean(randomise.value); | |
51 } else { | |
52 randomise = false; | |
53 } | |
54 if (randomise) | |
55 { | |
56 testXMLSetups = randomiseOrder(testXMLSetups); | |
57 } | |
58 | |
59 // Obtain the metrics enabled | |
60 var metricNode = xmlSetup.find('Metric'); | |
61 var metricNode = metricNode.find('metricEnable'); | |
62 metricNode.each(function(index,node){ | |
63 var enabled = node.textContent; | |
64 switch(enabled) | |
65 { | |
66 case 'testTimer': | |
67 sessionMetrics.prototype.enableTestTimer = true; | |
68 break; | |
69 case 'elementTimer': | |
70 sessionMetrics.prototype.enableElementTimer = true; | |
71 break; | |
72 case 'elementTracker': | |
73 sessionMetrics.prototype.enableElementTracker = true; | |
74 break; | |
75 case 'elementInitalPosition': | |
76 sessionMetrics.prototype.enableElementInitialPosition = true; | |
77 break; | |
78 case 'elementFlagListenedTo': | |
79 sessionMetrics.prototype.enableFlagListenedTo = true; | |
80 break; | |
81 case 'elementFlagMoved': | |
82 sessionMetrics.prototype.enableFlagMoved = true; | |
83 break; | |
84 case 'elementFlagComments': | |
85 sessionMetrics.prototype.enableFlagComments = true; | |
86 break; | |
87 } | |
88 }); | |
89 | |
90 // Create APE specific metric functions | |
91 audioEngineContext.metric.initialiseTest = function() | |
92 { | |
93 var sliders = document.getElementsByClassName('track-slider'); | |
94 for (var i=0; i<sliders.length; i++) | |
95 { | |
96 audioEngineContext.audioObjects[i].metric.initialised(convSliderPosToRate(i)); | |
97 } | |
98 }; | |
99 | |
100 audioEngineContext.metric.sliderMoveStart = function(id) | |
101 { | |
102 if (this.data == -1) | |
103 { | |
104 this.data = id; | |
105 } else { | |
106 console.log('ERROR: Metric tracker detecting two moves!'); | |
107 this.data = -1; | |
108 } | |
109 }; | |
110 audioEngineContext.metric.sliderMoved = function() | |
111 { | |
112 var time = audioEngineContext.timer.getTestTime(); | |
113 var id = this.data; | |
114 this.data = -1; | |
115 var position = convSliderPosToRate(id); | |
116 if (audioEngineContext.timer.testStarted) | |
117 { | |
118 audioEngineContext.audioObjects[id].metric.moved(time,position); | |
119 } | |
120 }; | |
121 | |
122 audioEngineContext.metric.sliderPlayed = function(id) | |
123 { | |
124 var time = audioEngineContext.timer.getTestTime(); | |
125 if (audioEngineContext.timer.testStarted) | |
126 { | |
127 if (this.lastClicked >= 0) | |
128 { | |
129 audioEngineContext.audioObjects[this.lastClicked].metric.listening(time); | |
130 } | |
131 this.lastClicked = id; | |
132 audioEngineContext.audioObjects[id].metric.listening(time); | |
133 } | |
134 }; | |
135 | |
136 // Create the top div for the Title element | |
137 var titleAttr = xmlSetup[0].attributes['title']; | |
138 var title = document.createElement('div'); | |
139 title.className = "title"; | |
140 title.align = "center"; | |
141 var titleSpan = document.createElement('span'); | |
142 | |
143 // Set title to that defined in XML, else set to default | |
144 if (titleAttr != undefined) { | |
145 titleSpan.innerHTML = titleAttr.value; | |
146 } else { | |
147 titleSpan.innerHTML = 'Listening test'; | |
148 } | |
149 // Insert the titleSpan element into the title div element. | |
150 title.appendChild(titleSpan); | |
151 | |
152 var pagetitle = document.createElement('div'); | |
153 pagetitle.className = "pageTitle"; | |
154 pagetitle.align = "center"; | |
155 var titleSpan = document.createElement('span'); | |
156 titleSpan.id = "pageTitle"; | |
157 pagetitle.appendChild(titleSpan); | |
158 | |
159 // Store the return URL path in global projectReturn | |
160 projectReturn = xmlSetup[0].attributes['projectReturn'].value; | |
161 | |
162 // Create Interface buttons! | |
163 var interfaceButtons = document.createElement('div'); | |
164 interfaceButtons.id = 'interface-buttons'; | |
165 | |
166 // MANUAL DOWNLOAD POINT | |
167 // If project return is null, this MUST be specified as the location to create the download link | |
168 var downloadPoint = document.createElement('div'); | |
169 downloadPoint.id = 'download-point'; | |
170 | |
171 // Create playback start/stop points | |
172 var playback = document.createElement("button"); | |
173 playback.innerHTML = 'Stop'; | |
174 playback.id = 'playback-button'; | |
175 // onclick function. Check if it is playing or not, call the correct function in the | |
176 // audioEngine, change the button text to reflect the next state. | |
177 playback.onclick = function() { | |
178 if (audioEngineContext.status == 1) { | |
179 audioEngineContext.stop(); | |
180 this.innerHTML = 'Stop'; | |
181 } | |
182 }; | |
183 // Create Submit (save) button | |
184 var submit = document.createElement("button"); | |
185 submit.innerHTML = 'Submit'; | |
186 submit.onclick = buttonSubmitClick; | |
187 submit.id = 'submit-button'; | |
188 // Append the interface buttons into the interfaceButtons object. | |
189 interfaceButtons.appendChild(playback); | |
190 interfaceButtons.appendChild(submit); | |
191 interfaceButtons.appendChild(downloadPoint); | |
192 | |
193 // Now create the slider and HTML5 canvas boxes | |
194 | |
195 // Create the div box to center align | |
196 var sliderBox = document.createElement('div'); | |
197 sliderBox.className = 'sliderCanvasDiv'; | |
198 sliderBox.id = 'sliderCanvasHolder'; | |
199 sliderBox.align = 'center'; | |
200 | |
201 // Create the slider box to hold the slider elements | |
202 var canvas = document.createElement('div'); | |
203 canvas.id = 'slider'; | |
204 // Must have a known EXACT width, as this is used later to determine the ratings | |
205 canvas.style.width = width - 100 +"px"; | |
206 canvas.align = "left"; | |
207 sliderBox.appendChild(canvas); | |
208 | |
209 // Create the div to hold any scale objects | |
210 var scale = document.createElement('div'); | |
211 scale.className = 'sliderScale'; | |
212 scale.id = 'sliderScaleHolder'; | |
213 scale.align = 'left'; | |
214 sliderBox.appendChild(scale); | |
215 | |
216 // Global parent for the comment boxes on the page | |
217 var feedbackHolder = document.createElement('div'); | |
218 feedbackHolder.id = 'feedbackHolder'; | |
219 | |
220 testContent.style.zIndex = 1; | |
221 insertPoint.innerHTML = null; // Clear the current schema | |
222 | |
223 // Create pre and post test questions | |
224 var blank = document.createElement('div'); | |
225 blank.className = 'testHalt'; | |
226 | |
227 var popupHolder = document.createElement('div'); | |
228 popupHolder.id = 'popupHolder'; | |
229 popupHolder.className = 'popupHolder'; | |
230 popupHolder.style.position = 'absolute'; | |
231 popupHolder.style.left = (window.innerWidth/2)-250 + 'px'; | |
232 popupHolder.style.top = (window.innerHeight/2)-125 + 'px'; | |
233 insertPoint.appendChild(popupHolder); | |
234 insertPoint.appendChild(blank); | |
235 hidePopup(); | |
236 | |
237 var preTest = xmlSetup.find('PreTest'); | |
238 var postTest = xmlSetup.find('PostTest'); | |
239 preTest = preTest[0]; | |
240 postTest = postTest[0]; | |
241 | |
242 currentState = 'preTest'; | |
243 | |
244 // Create Pre-Test Box | |
245 if (preTest != undefined && preTest.children.length >= 1) | |
246 { | |
247 showPopup(); | |
248 preTestPopupStart(preTest); | |
249 } | |
250 | |
251 // Inject into HTML | |
252 testContent.appendChild(title); // Insert the title | |
253 testContent.appendChild(pagetitle); | |
254 testContent.appendChild(interfaceButtons); | |
255 testContent.appendChild(sliderBox); | |
256 testContent.appendChild(feedbackHolder); | |
257 insertPoint.appendChild(testContent); | |
258 | |
259 // Load the full interface | |
260 | |
261 } | |
262 | |
263 function loadTest(id) | |
264 { | |
265 // Used to load a specific test page | |
266 var textXML = testXMLSetups[id]; | |
267 | |
268 var feedbackHolder = document.getElementById('feedbackHolder'); | |
269 var canvas = document.getElementById('slider'); | |
270 feedbackHolder.innerHTML = null; | |
271 canvas.innerHTML = null; | |
272 | |
273 // Setup question title | |
274 var interfaceObj = $(textXML).find('interface'); | |
275 var titleNode = interfaceObj.find('title'); | |
276 if (titleNode[0] != undefined) | |
277 { | |
278 document.getElementById('pageTitle').textContent = titleNode[0].textContent; | |
279 } | |
280 var positionScale = canvas.style.width.substr(0,canvas.style.width.length-2); | |
281 var offset = 50-8; // Half the offset of the slider (window width -100) minus the body padding of 8 | |
282 // TODO: AUTOMATE ABOVE!! | |
283 var scale = document.getElementById('sliderScaleHolder'); | |
284 scale.innerHTML = null; | |
285 interfaceObj.find('scale').each(function(index,scaleObj){ | |
286 var position = Number(scaleObj.attributes['position'].value)*0.01; | |
287 var pixelPosition = (position*positionScale)+offset; | |
288 var scaleDOM = document.createElement('span'); | |
289 scaleDOM.textContent = scaleObj.textContent; | |
290 scale.appendChild(scaleDOM); | |
291 scaleDOM.style.left = Math.floor((pixelPosition-($(scaleDOM).width()/2)))+'px'; | |
292 }); | |
293 | |
294 // Extract the hostURL attribute. If not set, create an empty string. | |
295 var hostURL = textXML.attributes['hostURL']; | |
296 if (hostURL == undefined) { | |
297 hostURL = ""; | |
298 } else { | |
299 hostURL = hostURL.value; | |
300 } | |
301 // Extract the sampleRate. If set, convert the string to a Number. | |
302 var hostFs = textXML.attributes['sampleRate']; | |
303 if (hostFs != undefined) { | |
304 hostFs = Number(hostFs.value); | |
305 } | |
306 | |
307 /// CHECK FOR SAMPLE RATE COMPATIBILITY | |
308 if (hostFs != undefined) { | |
309 if (Number(hostFs) != audioContext.sampleRate) { | |
310 var errStr = 'Sample rates do not match! Requested '+Number(hostFs)+', got '+audioContext.sampleRate+'. Please set the sample rate to match before completing this test.'; | |
311 alert(errStr); | |
312 return; | |
313 } | |
314 } | |
315 | |
316 var commentShow = textXML.attributes['elementComments']; | |
317 if (commentShow != undefined) { | |
318 if (commentShow.value == 'false') {commentShow = false;} | |
319 else {commentShow = true;} | |
320 } else {commentShow = true;} | |
321 | |
322 var loopPlayback = textXML.attributes['loop']; | |
323 if (loopPlayback != undefined) | |
324 { | |
325 loopPlayback = loopPlayback.value; | |
326 if (loopPlayback == 'true') { | |
327 loopPlayback = true; | |
328 } else { | |
329 loopPlayback = false; | |
330 } | |
331 } else { | |
332 loopPlayback = false; | |
333 } | |
334 audioEngineContext.loopPlayback = loopPlayback; | |
335 | |
336 // Create AudioEngine bindings for playback | |
337 if (loopPlayback) { | |
338 audioEngineContext.play = function() { | |
339 // Send play command to all playback buffers for synchronised start | |
340 // Also start timer callbacks to detect if playback has finished | |
341 if (this.status == 0) { | |
342 this.timer.startTest(); | |
343 // First get current clock | |
344 var timer = audioContext.currentTime; | |
345 // Add 3 seconds | |
346 timer += 3.0; | |
347 // Send play to all tracks | |
348 for (var i=0; i<this.audioObjects.length; i++) | |
349 { | |
350 this.audioObjects[i].play(timer); | |
351 } | |
352 this.status = 1; | |
353 } | |
354 }; | |
355 | |
356 audioEngineContext.stop = function() { | |
357 // Send stop and reset command to all playback buffers | |
358 if (this.status == 1) { | |
359 if (this.loopPlayback) { | |
360 for (var i=0; i<this.audioObjects.length; i++) | |
361 { | |
362 this.audioObjects[i].stop(); | |
363 } | |
364 } | |
365 this.status = 0; | |
366 } | |
367 }; | |
368 | |
369 audioEngineContext.selectedTrack = function(id) { | |
370 for (var i=0; i<this.audioObjects.length; i++) | |
371 { | |
372 if (id == i) { | |
373 this.audioObjects[i].outputGain.gain.value = 1.0; | |
374 } else { | |
375 this.audioObjects[i].outputGain.gain.value = 0.0; | |
376 } | |
377 } | |
378 }; | |
379 } else { | |
380 audioEngineContext.play = function() { | |
381 // Send play command to all playback buffers for synchronised start | |
382 // Also start timer callbacks to detect if playback has finished | |
383 if (this.status == 0) { | |
384 this.timer.startTest(); | |
385 this.status = 1; | |
386 } | |
387 }; | |
388 | |
389 audioEngineContext.stop = function() { | |
390 // Send stop and reset command to all playback buffers | |
391 if (this.status == 1) { | |
392 for (var i=0; i<this.audioObjects.length; i++) | |
393 { | |
394 this.audioObjects[i].stop(); | |
395 } | |
396 this.status = 0; | |
397 } | |
398 }; | |
399 | |
400 audioEngineContext.selectedTrack = function(id) { | |
401 for (var i=0; i<this.audioObjects.length; i++) | |
402 { | |
403 this.audioObjects[i].outputGain.gain.value = 0.0; | |
404 this.audioObjects[i].stop(); | |
405 } | |
406 if (this.status == 1) { | |
407 this.audioObjects[id].outputGain.gain.value = 1.0; | |
408 this.audioObjects[id].play(audioContext.currentTime+0.01); | |
409 } | |
410 }; | |
411 } | |
412 | |
413 currentTestHolder = document.createElement('audioHolder'); | |
414 currentTestHolder.id = textXML.id; | |
415 currentTestHolder.repeatCount = textXML.attributes['repeatCount'].value; | |
416 var currentPreTestHolder = document.createElement('preTest'); | |
417 var currentPostTestHolder = document.createElement('postTest'); | |
418 currentTestHolder.appendChild(currentPreTestHolder); | |
419 currentTestHolder.appendChild(currentPostTestHolder); | |
420 | |
421 var randomise = textXML.attributes['randomiseOrder']; | |
422 if (randomise != undefined) {randomise = randomise.value;} | |
423 else {randomise = false;} | |
424 | |
425 var audioElements = $(textXML).find('audioElements'); | |
426 currentTrackOrder = []; | |
427 audioElements.each(function(index,element){ | |
428 // Find any blind-repeats | |
429 // Not implemented yet, but just in case | |
430 currentTrackOrder[index] = element; | |
431 }); | |
432 if (randomise) { | |
433 currentTrackOrder = randomiseOrder(currentTrackOrder); | |
434 } | |
435 | |
436 // Delete any previous audioObjects associated with the audioEngine | |
437 audioEngineContext.audioObjects = []; | |
438 | |
439 // Find all the audioElements from the audioHolder | |
440 $(currentTrackOrder).each(function(index,element){ | |
441 // Find URL of track | |
442 // In this jQuery loop, variable 'this' holds the current audioElement. | |
443 | |
444 // Now load each audio sample. First create the new track by passing the full URL | |
445 var trackURL = hostURL + this.attributes['url'].value; | |
446 audioEngineContext.newTrack(trackURL); | |
447 | |
448 if (commentShow) { | |
449 // Create document objects to hold the comment boxes | |
450 var trackComment = document.createElement('div'); | |
451 trackComment.className = 'comment-div'; | |
452 // Create a string next to each comment asking for a comment | |
453 var trackString = document.createElement('span'); | |
454 trackString.innerHTML = 'Comment on track '+index; | |
455 // Create the HTML5 comment box 'textarea' | |
456 var trackCommentBox = document.createElement('textarea'); | |
457 trackCommentBox.rows = '4'; | |
458 trackCommentBox.cols = '100'; | |
459 trackCommentBox.name = 'trackComment'+index; | |
460 trackCommentBox.className = 'trackComment'; | |
461 var br = document.createElement('br'); | |
462 // Add to the holder. | |
463 trackComment.appendChild(trackString); | |
464 trackComment.appendChild(br); | |
465 trackComment.appendChild(trackCommentBox); | |
466 feedbackHolder.appendChild(trackComment); | |
467 } | |
468 | |
469 // Create a slider per track | |
470 | |
471 var trackSliderObj = document.createElement('div'); | |
472 trackSliderObj.className = 'track-slider'; | |
473 trackSliderObj.id = 'track-slider-'+index; | |
474 // Distribute it randomnly | |
475 var w = window.innerWidth - 100; | |
476 w = Math.random()*w; | |
477 trackSliderObj.style.left = Math.floor(w)+50+'px'; | |
478 trackSliderObj.innerHTML = '<span>'+index+'</span>'; | |
479 trackSliderObj.draggable = true; | |
480 trackSliderObj.ondragend = dragEnd; | |
481 trackSliderObj.ondragstart = function() | |
482 { | |
483 var id = Number(this.id.substr(13,2)); // Maximum theoretical tracks is 99! | |
484 audioEngineContext.metric.sliderMoveStart(id); | |
485 }; | |
486 | |
487 // Onclick, switch playback to that track | |
488 trackSliderObj.onclick = function() { | |
489 // Get the track ID from the object ID | |
490 var id = Number(this.id.substr(13,2)); // Maximum theoretical tracks is 99! | |
491 audioEngineContext.metric.sliderPlayed(id); | |
492 audioEngineContext.selectedTrack(id); | |
493 // Currently playing track red, rest green | |
494 document.getElementById('track-slider-'+index).style.backgroundColor = "#FF0000"; | |
495 for (var i = 0; i<$(currentTrackOrder).length; i++) | |
496 { | |
497 if (i!=index) // Make all other sliders green | |
498 { | |
499 document.getElementById('track-slider-'+i).style.backgroundColor = "rgb(100,200,100)"; | |
500 } | |
501 | |
502 } | |
503 audioEngineContext.play(); | |
504 hasBeenPlayed[index] = true; // mark as played | |
505 }; | |
506 | |
507 canvas.appendChild(trackSliderObj); | |
508 | |
509 // Add corresponding element to array to check whether sound has been played | |
510 hasBeenPlayed.push(false); | |
511 }); | |
512 | |
513 // Append any commentQuestion boxes | |
514 var commentQuestions = $(textXML).find('CommentQuestion'); | |
515 $(commentQuestions).each(function(index,element) { | |
516 // Create document objects to hold the comment boxes | |
517 var trackComment = document.createElement('div'); | |
518 trackComment.className = 'comment-div commentQuestion'; | |
519 trackComment.id = element.attributes['id'].value; | |
520 // Create a string next to each comment asking for a comment | |
521 var trackString = document.createElement('span'); | |
522 trackString.innerHTML = element.textContent; | |
523 // Create the HTML5 comment box 'textarea' | |
524 var trackCommentBox = document.createElement('textarea'); | |
525 trackCommentBox.rows = '4'; | |
526 trackCommentBox.cols = '100'; | |
527 trackCommentBox.name = 'commentQuestion'+index; | |
528 trackCommentBox.className = 'trackComment'; | |
529 var br = document.createElement('br'); | |
530 // Add to the holder. | |
531 trackComment.appendChild(trackString); | |
532 trackComment.appendChild(br); | |
533 trackComment.appendChild(trackCommentBox); | |
534 feedbackHolder.appendChild(trackComment); | |
535 }); | |
536 | |
537 // Now process any pre-test commands | |
538 | |
539 var preTest = $(testXMLSetups[id]).find('PreTest')[0]; | |
540 if (preTest.children.length > 0) | |
541 { | |
542 currentState = 'testRunPre-'+id; | |
543 preTestPopupStart(preTest); | |
544 showPopup(); | |
545 } else { | |
546 currentState = 'testRun-'+id; | |
547 } | |
548 } | |
549 | |
550 function preTestPopupStart(preTest) | |
551 { | |
552 var popupHolder = document.getElementById('popupHolder'); | |
553 popupHolder.innerHTML = null; | |
554 // Parse the first box | |
555 var preTestOption = document.createElement('div'); | |
556 preTestOption.id = 'preTest'; | |
557 preTestOption.style.marginTop = '25px'; | |
558 preTestOption.align = "center"; | |
559 var child = preTest.children[0]; | |
560 if (child.nodeName == 'statement') | |
561 { | |
562 preTestOption.innerHTML = '<span>'+child.innerHTML+'</span>'; | |
563 } else if (child.nodeName == 'question') | |
564 { | |
565 var questionId = child.attributes['id'].value; | |
566 var textHold = document.createElement('span'); | |
567 textHold.innerHTML = child.innerHTML; | |
568 textHold.id = questionId + 'response'; | |
569 var textEnter = document.createElement('textarea'); | |
570 preTestOption.appendChild(textHold); | |
571 preTestOption.appendChild(textEnter); | |
572 } | |
573 var nextButton = document.createElement('button'); | |
574 nextButton.className = 'popupButton'; | |
575 nextButton.value = '0'; | |
576 nextButton.innerHTML = 'Next'; | |
577 nextButton.onclick = popupButtonClick; | |
578 | |
579 popupHolder.appendChild(preTestOption); | |
580 popupHolder.appendChild(nextButton); | |
581 } | |
582 | |
583 function popupButtonClick() | |
584 { | |
585 // Global call from the 'Next' button click | |
586 if (currentState == 'preTest') | |
587 { | |
588 // At the start of the preTest routine! | |
589 var xmlTree = projectXML.find('setup'); | |
590 var preTest = xmlTree.find('PreTest')[0]; | |
591 this.value = preTestButtonClick(preTest,this.value); | |
592 } else if (currentState.substr(0,10) == 'testRunPre') | |
593 { | |
594 //Specific test pre-test | |
595 var testId = currentState.substr(11,currentState.length-10); | |
596 var preTest = $(testXMLSetups[testId]).find('PreTest')[0]; | |
597 this.value = preTestButtonClick(preTest,this.value); | |
598 } else if (currentState.substr(0,11) == 'testRunPost') | |
599 { | |
600 // Specific test post-test | |
601 var testId = currentState.substr(12,currentState.length-11); | |
602 var preTest = $(testXMLSetups[testId]).find('PostTest')[0]; | |
603 this.value = preTestButtonClick(preTest,this.value); | |
604 } else if (currentState == 'postTest') | |
605 { | |
606 // At the end of the test, running global post test | |
607 var xmlTree = projectXML.find('setup'); | |
608 var PostTest = xmlTree.find('PostTest')[0]; | |
609 this.value = preTestButtonClick(PostTest,this.value); | |
610 } | |
611 } | |
612 | |
613 function preTestButtonClick(preTest,index) | |
614 { | |
615 // Called on click of pre-test button | |
616 // Need to find and parse preTest again! | |
617 var preTestOption = document.getElementById('preTest'); | |
618 // Check if current state is a question! | |
619 if (preTest.children[index].nodeName == 'question') { | |
620 var questionId = preTest.children[index].attributes['id'].value; | |
621 var questionHold = document.createElement('comment'); | |
622 var questionResponse = document.getElementById(questionId + 'response'); | |
623 var mandatory = preTest.children[index].attributes['mandatory']; | |
624 if (mandatory != undefined){ | |
625 if (mandatory.value == 'true') {mandatory = true;} | |
626 else {mandatory = false;} | |
627 } else {mandatory = false;} | |
628 if (mandatory == true && questionResponse.value.length == 0) { | |
629 return index; | |
630 } | |
631 questionHold.id = questionId; | |
632 questionHold.innerHTML = questionResponse.value; | |
633 postPopupResponse(questionHold); | |
634 } | |
635 index++; | |
636 if (index < preTest.children.length) | |
637 { | |
638 // More to process | |
639 var child = preTest.children[index]; | |
640 if (child.nodeName == 'statement') | |
641 { | |
642 preTestOption.innerHTML = '<span>'+child.innerHTML+'</span>'; | |
643 } else if (child.nodeName == 'question') | |
644 { | |
645 var textHold = document.createElement('span'); | |
646 textHold.innerHTML = child.innerHTML; | |
647 var textEnter = document.createElement('textarea'); | |
648 textEnter.id = child.attributes['id'].value + 'response'; | |
649 var br = document.createElement('br'); | |
650 preTestOption.innerHTML = null; | |
651 preTestOption.appendChild(textHold); | |
652 preTestOption.appendChild(br); | |
653 preTestOption.appendChild(textEnter); | |
654 } | |
655 } else { | |
656 // Time to clear | |
657 preTestOption.innerHTML = null; | |
658 if (currentState != 'postTest') { | |
659 hidePopup(); | |
660 // Progress the state! | |
661 advanceState(); | |
662 } else { | |
663 a = createProjectSave(projectReturn); | |
664 preTestOption.appendChild(a); | |
665 } | |
666 } | |
667 return index; | |
668 } | |
669 | |
670 function postPopupResponse(response) | |
671 { | |
672 if (currentState == 'preTest') { | |
673 preTestQuestions.appendChild(response); | |
674 } else if (currentState == 'postTest') { | |
675 postTestQuestions.appendChild(response); | |
676 } else { | |
677 // Inside a specific test | |
678 if (currentState.substr(0,10) == 'testRunPre') { | |
679 // Pre Test | |
680 var store = $(currentTestHolder).find('preTest'); | |
681 } else { | |
682 // Post Test | |
683 var store = $(currentTestHolder).find('postTest'); | |
684 } | |
685 store[0].appendChild(response); | |
686 } | |
687 } | |
688 | |
689 function showPopup() | |
690 { | |
691 var popupHolder = document.getElementById('popupHolder'); | |
692 popupHolder.style.zIndex = 3; | |
693 popupHolder.style.visibility = 'visible'; | |
694 var blank = document.getElementsByClassName('testHalt')[0]; | |
695 blank.style.zIndex = 2; | |
696 blank.style.visibility = 'visible'; | |
697 } | |
698 | |
699 function hidePopup() | |
700 { | |
701 var popupHolder = document.getElementById('popupHolder'); | |
702 popupHolder.style.zIndex = -1; | |
703 popupHolder.style.visibility = 'hidden'; | |
704 var blank = document.getElementsByClassName('testHalt')[0]; | |
705 blank.style.zIndex = -2; | |
706 blank.style.visibility = 'hidden'; | |
707 } | |
708 | |
709 function dragEnd(ev) { | |
710 // Function call when a div has been dropped | |
711 var slider = document.getElementById('slider'); | |
712 var w = slider.style.width; | |
713 w = Number(w.substr(0,w.length-2)); | |
714 var x = ev.x; | |
715 if (x >= 42 && x < w+42) { | |
716 this.style.left = (x)+'px'; | |
717 } else { | |
718 if (x<42) { | |
719 this.style.left = '42px'; | |
720 } else { | |
721 this.style.left = (w+42) + 'px'; | |
722 } | |
723 } | |
724 audioEngineContext.metric.sliderMoved(); | |
725 } | |
726 | |
727 function advanceState() | |
728 { | |
729 console.log(currentState); | |
730 if (currentState == 'preTest') | |
731 { | |
732 // End of pre-test, begin the test | |
733 loadTest(0); | |
734 } else if (currentState.substr(0,10) == 'testRunPre') | |
735 { | |
736 // Start the test | |
737 var testId = currentState.substr(11,currentState.length-10); | |
738 currentState = 'testRun-'+testId; | |
739 } else if (currentState.substr(0,11) == 'testRunPost') | |
740 { | |
741 var testId = currentState.substr(12,currentState.length-11); | |
742 testEnded(testId); | |
743 } else if (currentState.substr(0,7) == 'testRun') | |
744 { | |
745 var testId = currentState.substr(8,currentState.length-7); | |
746 // Check if we have any post tests to perform | |
747 var postXML = $(testXMLSetups[testId]).find('PostTest')[0]; | |
748 if (postXML == undefined) { | |
749 testEnded(testId); | |
750 } | |
751 else if (postXML.children.length > 0) | |
752 { | |
753 currentState = 'testRunPost-'+testId; | |
754 showPopup(); | |
755 preTestPopupStart(postXML); | |
756 } | |
757 else { | |
758 | |
759 | |
760 // No post tests, check if we have another test to perform instead | |
761 | |
762 } | |
763 } | |
764 console.log(currentState); | |
765 } | |
766 | |
767 function testEnded(testId) | |
768 { | |
769 pageXMLSave(testId); | |
770 if (testXMLSetups.length-1 > testId) | |
771 { | |
772 // Yes we have another test to perform | |
773 testId = (Number(testId)+1); | |
774 currentState = 'testRun-'+testId; | |
775 loadTest(testId); | |
776 } else { | |
777 console.log('Testing Completed!'); | |
778 currentState = 'postTest'; | |
779 // Check for any post tests | |
780 var xmlSetup = projectXML.find('setup'); | |
781 var postTest = xmlSetup.find('PostTest')[0]; | |
782 showPopup(); | |
783 preTestPopupStart(postTest); | |
784 } | |
785 } | |
786 | |
787 function buttonSubmitClick() // TODO: Only when all songs have been played! | |
788 { | |
789 if (hasBeenPlayed.indexOf(false)==-1) | |
790 { | |
791 if (audioEngineContext.status == 1) { | |
792 var playback = document.getElementById('playback-button'); | |
793 playback.click(); | |
794 // This function is called when the submit button is clicked. Will check for any further tests to perform, or any post-test options | |
795 } else | |
796 { | |
797 if (audioEngineContext.timer.testStarted == false) | |
798 { | |
799 alert('You have not started the test! Please press start to begin the test!'); | |
800 return; | |
801 } | |
802 } | |
803 if (currentState.substr(0,7) == 'testRun') | |
804 { | |
805 hasBeenPlayed = []; // clear array to prepare for next test | |
806 audioEngineContext.timer.stopTest(); | |
807 advanceState(); | |
808 } | |
809 } else // if a fragment has not been played yet | |
810 { | |
811 alert('You have not played fragment ' + hasBeenPlayed.indexOf(false) + ' yet. Please listen, rate and comment all samples before submitting.'); | |
812 return; | |
813 } | |
814 } | |
815 | |
816 function convSliderPosToRate(id) | |
817 { | |
818 var w = document.getElementById('slider').style.width; | |
819 var maxPix = w.substr(0,w.length-2); | |
820 var slider = document.getElementsByClassName('track-slider')[id]; | |
821 var pix = slider.style.left; | |
822 pix = pix.substr(0,pix.length-2); | |
823 var rate = (pix-42)/maxPix; | |
824 return rate; | |
825 } | |
826 | |
827 function pageXMLSave(testId) | |
828 { | |
829 // Saves a specific test page | |
830 var xmlDoc = currentTestHolder; | |
831 // Check if any session wide metrics are enabled | |
832 | |
833 var commentShow = testXMLSetups[testId].attributes['elementComments']; | |
834 if (commentShow != undefined) { | |
835 if (commentShow.value == 'false') {commentShow = false;} | |
836 else {commentShow = true;} | |
837 } else {commentShow = true;} | |
838 | |
839 var metric = document.createElement('metric'); | |
840 if (audioEngineContext.metric.enableTestTimer) | |
841 { | |
842 var testTime = document.createElement('metricResult'); | |
843 testTime.id = 'testTime'; | |
844 testTime.textContent = audioEngineContext.timer.testDuration; | |
845 metric.appendChild(testTime); | |
846 } | |
847 xmlDoc.appendChild(metric); | |
848 var trackSliderObjects = document.getElementsByClassName('track-slider'); | |
849 var commentObjects = document.getElementsByClassName('comment-div'); | |
850 for (var i=0; i<trackSliderObjects.length; i++) | |
851 { | |
852 var audioElement = document.createElement('audioElement'); | |
853 audioElement.id = currentTrackOrder[i].attributes['id'].value; | |
854 audioElement.url = currentTrackOrder[i].attributes['url'].value; | |
855 var value = document.createElement('value'); | |
856 value.innerHTML = convSliderPosToRate(i); | |
857 if (commentShow) { | |
858 var comment = document.createElement("comment"); | |
859 var question = document.createElement("question"); | |
860 var response = document.createElement("response"); | |
861 question.textContent = commentObjects[i].children[0].textContent; | |
862 response.textContent = commentObjects[i].children[2].value; | |
863 comment.appendChild(question); | |
864 comment.appendChild(response); | |
865 audioElement.appendChild(comment); | |
866 } | |
867 audioElement.appendChild(value); | |
868 // Check for any per element metrics | |
869 var metric = document.createElement('metric'); | |
870 var elementMetric = audioEngineContext.audioObjects[i].metric; | |
871 if (audioEngineContext.metric.enableElementTimer) { | |
872 var elementTimer = document.createElement('metricResult'); | |
873 elementTimer.id = 'elementTimer'; | |
874 elementTimer.textContent = elementMetric.listenedTimer; | |
875 metric.appendChild(elementTimer); | |
876 } | |
877 if (audioEngineContext.metric.enableElementTracker) { | |
878 var elementTrackerFull = document.createElement('metricResult'); | |
879 elementTrackerFull.id = 'elementTrackerFull'; | |
880 var data = elementMetric.movementTracker; | |
881 for (var k=0; k<data.length; k++) | |
882 { | |
883 var timePos = document.createElement('timePos'); | |
884 timePos.id = k; | |
885 var time = document.createElement('time'); | |
886 time.textContent = data[k][0]; | |
887 var position = document.createElement('position'); | |
888 position.textContent = data[k][1]; | |
889 timePos.appendChild(time); | |
890 timePos.appendChild(position); | |
891 elementTrackerFull.appendChild(timePos); | |
892 } | |
893 metric.appendChild(elementTrackerFull); | |
894 } | |
895 if (audioEngineContext.metric.enableElementInitialPosition) { | |
896 var elementInitial = document.createElement('metricResult'); | |
897 elementInitial.id = 'elementInitialPosition'; | |
898 elementInitial.textContent = elementMetric.initialPosition; | |
899 metric.appendChild(elementInitial); | |
900 } | |
901 if (audioEngineContext.metric.enableFlagListenedTo) { | |
902 var flagListenedTo = document.createElement('metricResult'); | |
903 flagListenedTo.id = 'elementFlagListenedTo'; | |
904 flagListenedTo.textContent = elementMetric.wasListenedTo; | |
905 metric.appendChild(flagListenedTo); | |
906 } | |
907 if (audioEngineContext.metric.enableFlagMoved) { | |
908 var flagMoved = document.createElement('metricResult'); | |
909 flagMoved.id = 'elementFlagMoved'; | |
910 flagMoved.textContent = elementMetric.wasMoved; | |
911 metric.appendChild(flagMoved); | |
912 } | |
913 if (audioEngineContext.metric.enableFlagComments) { | |
914 var flagComments = document.createElement('metricResult'); | |
915 flagComments.id = 'elementFlagComments'; | |
916 if (response.textContent.length == 0) {flag.textContent = 'false';} | |
917 else {flag.textContet = 'true';} | |
918 metric.appendChild(flagComments); | |
919 } | |
920 audioElement.appendChild(metric); | |
921 xmlDoc.appendChild(audioElement); | |
922 } | |
923 var commentQuestion = document.getElementsByClassName('commentQuestion'); | |
924 for (var i=0; i<commentQuestion.length; i++) | |
925 { | |
926 var cqHolder = document.createElement('CommentQuestion'); | |
927 var comment = document.createElement('comment'); | |
928 var question = document.createElement('question'); | |
929 cqHolder.id = commentQuestion[i].id; | |
930 comment.textContent = commentQuestion[i].children[2].value; | |
931 question.textContent = commentQuestion[i].children[0].textContent; | |
932 cqHolder.appendChild(question); | |
933 cqHolder.appendChild(comment); | |
934 xmlDoc.appendChild(cqHolder); | |
935 } | |
936 testResultsHolders[testId] = xmlDoc; | |
937 } | |
938 | |
939 // Only other global function which must be defined in the interface class. Determines how to create the XML document. | |
940 function interfaceXMLSave(){ | |
941 // Create the XML string to be exported with results | |
942 var xmlDoc = document.createElement("BrowserEvaluationResult"); | |
943 for (var i=0; i<testResultsHolders.length; i++) | |
944 { | |
945 xmlDoc.appendChild(testResultsHolders[i]); | |
946 } | |
947 // Append Pre/Post Questions | |
948 xmlDoc.appendChild(preTestQuestions); | |
949 xmlDoc.appendChild(postTestQuestions); | |
950 | |
951 return xmlDoc; | |
952 } |