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