Mercurial > hg > webaudioevaluationtool
comparison interfaces/ape.js @ 1316:279930a008ca
All interfaces support comment boxes. Comment box identification matches presented tag (for instance, AB will be Comment on fragment A, rather than 1). Tighter buffer loading protocol, audioObjects register with the buffer rather than checking for buffer existence (which can be buggy depending on the buffer state). Buffers now have a state to ensure exact location in loading chain (downloading, decoding, LUFS, ready).
author | Nicholas Jillings <nickjillings@users.noreply.github.com> |
---|---|
date | Fri, 29 Jan 2016 11:11:57 +0000 |
parents | |
children | f7da0ea7a4c1 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 1316:279930a008ca |
---|---|
1 /** | |
2 * ape.js | |
3 * Create the APE interface | |
4 */ | |
5 | |
6 | |
7 // Once this is loaded and parsed, begin execution | |
8 loadInterface(); | |
9 | |
10 function loadInterface() { | |
11 | |
12 // Get the dimensions of the screen available to the page | |
13 var width = window.innerWidth; | |
14 var height = window.innerHeight; | |
15 | |
16 // The injection point into the HTML page | |
17 interfaceContext.insertPoint = document.getElementById("topLevelBody"); | |
18 var testContent = document.createElement('div'); | |
19 | |
20 testContent.id = 'testContent'; | |
21 | |
22 // Bindings for interfaceContext | |
23 interfaceContext.checkAllPlayed = function() | |
24 { | |
25 hasBeenPlayed = audioEngineContext.checkAllPlayed(); | |
26 if (hasBeenPlayed.length > 0) // if a fragment has not been played yet | |
27 { | |
28 str = ""; | |
29 if (hasBeenPlayed.length > 1) { | |
30 for (var i=0; i<hasBeenPlayed.length; i++) { | |
31 str = str + hasBeenPlayed[i]; | |
32 if (i < hasBeenPlayed.length-2){ | |
33 str += ", "; | |
34 } else if (i == hasBeenPlayed.length-2) { | |
35 str += " or "; | |
36 } | |
37 } | |
38 alert('You have not played fragments ' + str + ' yet. Please listen, rate and comment all samples before submitting.'); | |
39 } else { | |
40 alert('You have not played fragment ' + hasBeenPlayed[0] + ' yet. Please listen, rate and comment all samples before submitting.'); | |
41 } | |
42 return false; | |
43 } | |
44 return true; | |
45 }; | |
46 | |
47 interfaceContext.checkAllMoved = function() { | |
48 var state = true; | |
49 var str = 'You have not moved the following sliders. '; | |
50 for (var i=0; i<this.interfaceSliders.length; i++) | |
51 { | |
52 var interfaceTID = []; | |
53 for (var j=0; j<this.interfaceSliders[i].metrics.length; j++) | |
54 { | |
55 if (this.interfaceSliders[i].metrics[j].wasMoved == false) | |
56 { | |
57 state = false; | |
58 interfaceTID.push(j); | |
59 } | |
60 } | |
61 if (interfaceTID.length != 0) | |
62 { | |
63 var interfaceName = this.interfaceSliders[i].interfaceObject.title; | |
64 if (interfaceName == undefined) { | |
65 str += 'On axis '+String(i+1)+' you must move '; | |
66 } else { | |
67 str += 'On axis "'+interfaceName+'" you must move '; | |
68 } | |
69 if (interfaceTID.length == 1) | |
70 { | |
71 str += 'slider '+interfaceTID[0]+'. '; | |
72 } | |
73 else { | |
74 str += 'sliders '; | |
75 for (var k=0; k<interfaceTID.length-1; k++) | |
76 { | |
77 str += interfaceTID[k]+', '; | |
78 } | |
79 str += interfaceTID[interfaceTID.length-1] +'. '; | |
80 } | |
81 } | |
82 } | |
83 if (state != true) | |
84 { | |
85 alert(str); | |
86 console.log(str); | |
87 } | |
88 return state; | |
89 }; | |
90 | |
91 Interface.prototype.checkAllCommented = function() { | |
92 var audioObjs = audioEngineContext.audioObjects; | |
93 var audioHolder = testState.stateMap[testState.stateIndex]; | |
94 var state = true; | |
95 if (audioHolder.elementComments) { | |
96 var strNums = []; | |
97 for (var i=0; i<audioObjs.length; i++) | |
98 { | |
99 if (audioObjs[i].commentDOM.trackCommentBox.value.length == 0) { | |
100 state = false; | |
101 strNums.push(i); | |
102 } | |
103 } | |
104 if (state == false) { | |
105 if (strNums.length > 1) { | |
106 var str = ""; | |
107 for (var i=0; i<strNums.length; i++) { | |
108 str = str + strNums[i]; | |
109 if (i < strNums.length-2){ | |
110 str += ", "; | |
111 } else if (i == strNums.length-2) { | |
112 str += " or "; | |
113 } | |
114 } | |
115 alert('You have not commented on fragments ' + str + ' yet. Please listen, rate and comment all samples before submitting.'); | |
116 } else { | |
117 alert('You have not commented on fragment ' + strNums[0] + ' yet. Please listen, rate and comment all samples before submitting.'); | |
118 } | |
119 } | |
120 } | |
121 return state; | |
122 }; | |
123 | |
124 Interface.prototype.checkScaleRange = function() | |
125 { | |
126 var audioObjs = audioEngineContext.audioObjects; | |
127 var audioHolder = testState.stateMap[testState.stateIndex]; | |
128 var state = true; | |
129 var str = ''; | |
130 for (var i=0; i<this.interfaceSliders.length; i++) | |
131 { | |
132 var minScale; | |
133 var maxScale; | |
134 var interfaceObject = interfaceContext.interfaceSliders[0].interfaceObject; | |
135 for (var j=0; j<interfaceObject.options.length; j++) | |
136 { | |
137 if (interfaceObject.options[j].check == "scalerange") { | |
138 minScale = interfaceObject.options[j].min; | |
139 maxScale = interfaceObject.options[j].max; | |
140 break; | |
141 } | |
142 } | |
143 var minRanking = convSliderPosToRate(this.interfaceSliders[i].sliders[0]); | |
144 var maxRanking = minRanking; | |
145 for (var j=1; j<this.interfaceSliders[i].sliders.length; j++) | |
146 { | |
147 var ranking = convSliderPosToRate(this.interfaceSliders[i].sliders[j]); | |
148 if (ranking < minRanking) | |
149 { | |
150 minRanking = ranking; | |
151 } else if (ranking > maxRanking) | |
152 { | |
153 maxRanking = ranking; | |
154 } | |
155 } | |
156 if (minRanking > minScale || maxRanking < maxScale) | |
157 { | |
158 state = false; | |
159 str += 'On axis "'+this.interfaceSliders[i].interfaceObject.title+'" you have not used the full width of the scale. '; | |
160 } | |
161 } | |
162 if (state != true) | |
163 { | |
164 alert(str); | |
165 console.log(str); | |
166 } | |
167 return state; | |
168 }; | |
169 | |
170 Interface.prototype.objectSelected = null; | |
171 Interface.prototype.objectMoved = false; | |
172 Interface.prototype.selectObject = function(object) | |
173 { | |
174 if (this.objectSelected == null) | |
175 { | |
176 this.objectSelected = object; | |
177 this.objectMoved = false; | |
178 } | |
179 }; | |
180 Interface.prototype.moveObject = function() | |
181 { | |
182 if (this.objectMoved == false) | |
183 { | |
184 this.objectMoved = true; | |
185 } | |
186 }; | |
187 Interface.prototype.releaseObject = function() | |
188 { | |
189 this.objectSelected = null; | |
190 this.objectMoved = false; | |
191 }; | |
192 Interface.prototype.getSelectedObject = function() | |
193 { | |
194 return this.objectSelected; | |
195 }; | |
196 Interface.prototype.hasSelectedObjectMoved = function() | |
197 { | |
198 return this.objectMoved; | |
199 }; | |
200 | |
201 // Bindings for slider interfaces | |
202 Interface.prototype.interfaceSliders = []; | |
203 | |
204 // Bindings for audioObjects | |
205 | |
206 // Create the top div for the Title element | |
207 var titleAttr = specification.title; | |
208 var title = document.createElement('div'); | |
209 title.className = "title"; | |
210 title.align = "center"; | |
211 var titleSpan = document.createElement('span'); | |
212 | |
213 // Set title to that defined in XML, else set to default | |
214 if (titleAttr != undefined) { | |
215 titleSpan.textContent = titleAttr; | |
216 } else { | |
217 titleSpan.textContent = 'Listening test'; | |
218 } | |
219 // Insert the titleSpan element into the title div element. | |
220 title.appendChild(titleSpan); | |
221 | |
222 // Create Interface buttons! | |
223 var interfaceButtons = document.createElement('div'); | |
224 interfaceButtons.id = 'interface-buttons'; | |
225 | |
226 // Create playback start/stop points | |
227 var playback = document.createElement("button"); | |
228 playback.innerHTML = 'Stop'; | |
229 playback.id = 'playback-button'; | |
230 // onclick function. Check if it is playing or not, call the correct function in the | |
231 // audioEngine, change the button text to reflect the next state. | |
232 playback.onclick = function() { | |
233 if (audioEngineContext.status == 1) { | |
234 audioEngineContext.stop(); | |
235 this.innerHTML = 'Stop'; | |
236 var time = audioEngineContext.timer.getTestTime(); | |
237 console.log('Stopped at ' + time); // DEBUG/SAFETY | |
238 } | |
239 }; | |
240 // Create Submit (save) button | |
241 var submit = document.createElement("button"); | |
242 submit.innerHTML = 'Submit'; | |
243 submit.onclick = buttonSubmitClick; | |
244 submit.id = 'submit-button'; | |
245 // Append the interface buttons into the interfaceButtons object. | |
246 interfaceButtons.appendChild(playback); | |
247 interfaceButtons.appendChild(submit); | |
248 | |
249 var sliderHolder = document.createElement("div"); | |
250 sliderHolder.id = "slider-holder"; | |
251 | |
252 | |
253 // Global parent for the comment boxes on the page | |
254 var feedbackHolder = document.createElement('div'); | |
255 feedbackHolder.id = 'feedbackHolder'; | |
256 | |
257 testContent.style.zIndex = 1; | |
258 interfaceContext.insertPoint.innerHTML = null; // Clear the current schema | |
259 | |
260 // Inject into HTML | |
261 testContent.appendChild(title); // Insert the title | |
262 testContent.appendChild(interfaceButtons); | |
263 testContent.appendChild(sliderHolder); | |
264 testContent.appendChild(feedbackHolder); | |
265 interfaceContext.insertPoint.appendChild(testContent); | |
266 | |
267 // Load the full interface | |
268 testState.initialise(); | |
269 testState.advanceState(); | |
270 | |
271 } | |
272 | |
273 function loadTest(audioHolderObject) | |
274 { | |
275 var width = window.innerWidth; | |
276 var height = window.innerHeight; | |
277 var id = audioHolderObject.id; | |
278 | |
279 interfaceContext.interfaceSliders = []; | |
280 | |
281 var feedbackHolder = document.getElementById('feedbackHolder'); | |
282 var sliderHolder = document.getElementById('slider-holder'); | |
283 feedbackHolder.innerHTML = null; | |
284 sliderHolder.innerHTML = null; | |
285 | |
286 // Delete outside reference | |
287 var outsideReferenceHolder = document.getElementById('outside-reference'); | |
288 if (outsideReferenceHolder != null) { | |
289 document.getElementById('interface-buttons').removeChild(outsideReferenceHolder); | |
290 } | |
291 | |
292 var interfaceObj = audioHolderObject.interfaces; | |
293 for (var k=0; k<interfaceObj.length; k++) { | |
294 // Create the div box to center align | |
295 interfaceContext.interfaceSliders.push(new interfaceSliderHolder(interfaceObj[k])); | |
296 } | |
297 | |
298 var interfaceList = audioHolderObject.interfaces.concat(specification.interfaces); | |
299 for (var k=0; k<interfaceList.length; k++) | |
300 { | |
301 for (var i=0; i<interfaceList[k].options.length; i++) | |
302 { | |
303 if (interfaceList[k].options[i].type == 'show' && interfaceList[k].options[i].name == 'playhead') | |
304 { | |
305 var playbackHolder = document.getElementById('playback-holder'); | |
306 if (playbackHolder == null) | |
307 { | |
308 playbackHolder = document.createElement('div'); | |
309 playbackHolder.style.width = "100%"; | |
310 playbackHolder.align = 'center'; | |
311 playbackHolder.appendChild(interfaceContext.playhead.object); | |
312 feedbackHolder.appendChild(playbackHolder); | |
313 } | |
314 } else if (interfaceList[k].options[i].type == 'show' && interfaceList[k].options[i].name == 'page-count') | |
315 { | |
316 var pagecountHolder = document.getElementById('page-count'); | |
317 if (pagecountHolder == null) | |
318 { | |
319 pagecountHolder = document.createElement('div'); | |
320 pagecountHolder.id = 'page-count'; | |
321 } | |
322 pagecountHolder.innerHTML = '<span>Page '+(audioHolderObject.presentedId+1)+' of '+specification.pages.length+'</span>'; | |
323 var inject = document.getElementById('interface-buttons'); | |
324 inject.appendChild(pagecountHolder); | |
325 } else if (interfaceList[k].options[i].type == 'show' && interfaceList[k].options[i].name == 'volume') { | |
326 if (document.getElementById('master-volume-holder') == null) | |
327 { | |
328 feedbackHolder.appendChild(interfaceContext.volume.object); | |
329 } | |
330 } | |
331 } | |
332 } | |
333 | |
334 var commentBoxPrefix = "Comment on fragment"; | |
335 | |
336 var commentShow = audioHolderObject.elementComments; | |
337 | |
338 var loopPlayback = audioHolderObject.loop; | |
339 | |
340 currentTestHolder = document.createElement('audioHolder'); | |
341 currentTestHolder.id = audioHolderObject.id; | |
342 currentTestHolder.repeatCount = audioHolderObject.repeatCount; | |
343 | |
344 // Find all the audioElements from the audioHolder | |
345 $(audioHolderObject.audioElements).each(function(index,element){ | |
346 // Find URL of track | |
347 // In this jQuery loop, variable 'this' holds the current audioElement. | |
348 var audioObject = audioEngineContext.newTrack(element); | |
349 // Check if an outside reference | |
350 if (element.type == 'outside-reference') | |
351 { | |
352 // Construct outside reference; | |
353 var orNode = new outsideReferenceDOM(audioObject,index,document.getElementById('interface-buttons')); | |
354 audioObject.bindInterface(orNode); | |
355 } else { | |
356 // Create a slider per track | |
357 var sliderNode = new sliderObject(audioObject,interfaceObj); | |
358 audioObject.bindInterface(sliderNode); | |
359 interfaceContext.createCommentBox(audioObject); | |
360 } | |
361 }); | |
362 | |
363 // Initialse the interfaceSlider object metrics | |
364 | |
365 $('.track-slider').mousedown(function(event) { | |
366 interfaceContext.selectObject($(this)[0]); | |
367 }); | |
368 $('.track-slider').on('touchstart',null,function(event) { | |
369 interfaceContext.selectObject($(this)[0]); | |
370 }); | |
371 | |
372 $('.track-slider').mousemove(function(event) { | |
373 event.preventDefault(); | |
374 }); | |
375 | |
376 $('.slider').mousemove(function(event) { | |
377 event.preventDefault(); | |
378 var obj = interfaceContext.getSelectedObject(); | |
379 if (obj == null) {return;} | |
380 $(obj).css("left",event.clientX + "px"); | |
381 interfaceContext.moveObject(); | |
382 }); | |
383 | |
384 $('.slider').on('touchmove',null,function(event) { | |
385 event.preventDefault(); | |
386 var obj = interfaceContext.getSelectedObject(); | |
387 if (obj == null) {return;} | |
388 var move = event.originalEvent.targetTouches[0].clientX - 6; | |
389 $(obj).css("left",move + "px"); | |
390 interfaceContext.moveObject(); | |
391 }); | |
392 | |
393 $(document).mouseup(function(event){ | |
394 event.preventDefault(); | |
395 var obj = interfaceContext.getSelectedObject(); | |
396 if (obj == null) {return;} | |
397 var interfaceID = obj.parentElement.getAttribute("interfaceid"); | |
398 var trackID = obj.getAttribute("trackindex"); | |
399 if (interfaceContext.hasSelectedObjectMoved() == true) | |
400 { | |
401 var l = $(obj).css("left"); | |
402 var id = obj.getAttribute('trackIndex'); | |
403 var time = audioEngineContext.timer.getTestTime(); | |
404 var rate = convSliderPosToRate(obj); | |
405 audioEngineContext.audioObjects[id].metric.moved(time,rate); | |
406 interfaceContext.interfaceSliders[interfaceID].metrics[trackID].moved(time,rate); | |
407 console.log("slider "+id+" moved to "+rate+' ('+time+')'); | |
408 } else { | |
409 var id = Number(obj.attributes['trackIndex'].value); | |
410 //audioEngineContext.metric.sliderPlayed(id); | |
411 audioEngineContext.play(id); | |
412 } | |
413 interfaceContext.releaseObject(); | |
414 }); | |
415 | |
416 $('.slider').on('touchend',null,function(event){ | |
417 var obj = interfaceContext.getSelectedObject(); | |
418 if (obj == null) {return;} | |
419 var interfaceID = obj.parentElement.getAttribute("interfaceid"); | |
420 var trackID = obj.getAttribute("trackindex"); | |
421 if (interfaceContext.hasSelectedObjectMoved() == true) | |
422 { | |
423 var l = $(obj).css("left"); | |
424 var id = obj.getAttribute('trackIndex'); | |
425 var time = audioEngineContext.timer.getTestTime(); | |
426 var rate = convSliderPosToRate(obj); | |
427 audioEngineContext.audioObjects[id].metric.moved(time,rate); | |
428 interfaceContext.interfaceSliders[interfaceID].metrics[trackID].moved(time,rate); | |
429 console.log("slider "+id+" moved to "+rate+' ('+time+')'); | |
430 } | |
431 interfaceContext.releaseObject(); | |
432 }); | |
433 | |
434 | |
435 if (audioHolderObject.showElementComments) { | |
436 interfaceContext.showCommentBoxes(feedbackHolder,true); | |
437 } | |
438 | |
439 $(audioHolderObject.commentQuestions).each(function(index,element) { | |
440 var node = interfaceContext.createCommentQuestion(element); | |
441 feedbackHolder.appendChild(node.holder); | |
442 }); | |
443 | |
444 | |
445 //testWaitIndicator(); | |
446 } | |
447 | |
448 function interfaceSliderHolder(interfaceObject) | |
449 { | |
450 this.sliders = []; | |
451 this.metrics = []; | |
452 this.id = document.getElementsByClassName("sliderCanvasDiv").length; | |
453 this.name = interfaceObject.name; | |
454 this.interfaceObject = interfaceObject; | |
455 this.sliderDOM = document.createElement('div'); | |
456 this.sliderDOM.className = 'sliderCanvasDiv'; | |
457 this.sliderDOM.id = 'sliderCanvasHolder-'+this.id; | |
458 | |
459 var pagetitle = document.createElement('div'); | |
460 pagetitle.className = "pageTitle"; | |
461 pagetitle.align = "center"; | |
462 var titleSpan = document.createElement('span'); | |
463 titleSpan.id = "pageTitle-"+this.id; | |
464 if (interfaceObject.title != undefined && typeof interfaceObject.title == "string") | |
465 { | |
466 titleSpan.textContent = interfaceObject.title; | |
467 } else { | |
468 titleSpan.textContent = "Axis "+String(this.id+1); | |
469 } | |
470 pagetitle.appendChild(titleSpan); | |
471 this.sliderDOM.appendChild(pagetitle); | |
472 | |
473 // Create the slider box to hold the slider elements | |
474 this.canvas = document.createElement('div'); | |
475 if (this.name != undefined) | |
476 this.canvas.id = 'slider-'+this.name; | |
477 else | |
478 this.canvas.id = 'slider-'+this.id; | |
479 this.canvas.setAttribute("interfaceid",this.id); | |
480 this.canvas.className = 'slider'; | |
481 this.canvas.align = "left"; | |
482 this.canvas.addEventListener('dragover',function(event){ | |
483 event.preventDefault(); | |
484 event.dataTransfer.effectAllowed = 'none'; | |
485 event.dataTransfer.dropEffect = 'copy'; | |
486 return false; | |
487 },false); | |
488 var sliderMargin = document.createAttribute('marginsize'); | |
489 sliderMargin.nodeValue = 42; // Set default margins to 42px either side | |
490 // Must have a known EXACT width, as this is used later to determine the ratings | |
491 var w = (Number(sliderMargin.nodeValue)+8)*2; | |
492 this.canvas.style.width = window.innerWidth - w +"px"; | |
493 this.canvas.style.marginLeft = sliderMargin.nodeValue +'px'; | |
494 this.canvas.setAttributeNode(sliderMargin); | |
495 this.sliderDOM.appendChild(this.canvas); | |
496 | |
497 // Create the div to hold any scale objects | |
498 this.scale = document.createElement('div'); | |
499 this.scale.className = 'sliderScale'; | |
500 this.scale.id = 'sliderScaleHolder-'+this.id; | |
501 this.scale.align = 'left'; | |
502 this.sliderDOM.appendChild(this.scale); | |
503 var positionScale = this.canvas.style.width.substr(0,this.canvas.style.width.length-2); | |
504 var offset = Number(this.canvas.attributes['marginsize'].value); | |
505 for (var scaleObj of interfaceObject.scales) | |
506 { | |
507 var value = document.createAttribute('value'); | |
508 var position = Number(scaleObj.position)*0.01; | |
509 value.nodeValue = position; | |
510 var pixelPosition = (position*positionScale)+offset; | |
511 var scaleDOM = document.createElement('span'); | |
512 scaleDOM.textContent = scaleObj.text; | |
513 this.scale.appendChild(scaleDOM); | |
514 scaleDOM.style.left = Math.floor((pixelPosition-($(scaleDOM).width()/2)))+'px'; | |
515 scaleDOM.setAttributeNode(value); | |
516 } | |
517 | |
518 var dest = document.getElementById("slider-holder"); | |
519 dest.appendChild(this.sliderDOM); | |
520 | |
521 this.createSliderObject = function(audioObject) | |
522 { | |
523 var trackObj = document.createElement('div'); | |
524 trackObj.className = 'track-slider track-slider-disabled track-slider-'+audioObject.id; | |
525 trackObj.id = 'track-slider-'+this.id+'-'+audioObject.id; | |
526 trackObj.setAttribute('trackIndex',audioObject.id); | |
527 if (this.name != undefined) { | |
528 trackObj.setAttribute('interface-name',this.name); | |
529 } else { | |
530 trackObj.setAttribute('interface-name',this.id); | |
531 } | |
532 var offset = Number(this.canvas.attributes['marginsize'].value); | |
533 // Distribute it randomnly | |
534 var w = window.innerWidth - (offset+8)*2; | |
535 w = Math.random()*w; | |
536 w = Math.floor(w+(offset+8)); | |
537 trackObj.style.left = w+'px'; | |
538 this.canvas.appendChild(trackObj); | |
539 this.sliders.push(trackObj); | |
540 this.metrics.push(new metricTracker(this)); | |
541 trackObj.innerHTML = '<span>'+(this.metrics.length-1)+'</span>'; | |
542 this.metrics[this.metrics.length-1].initialise(convSliderPosToRate(trackObj)); | |
543 return trackObj; | |
544 }; | |
545 | |
546 this.resize = function(event) | |
547 { | |
548 var holdValues = []; | |
549 for (var index = 0; index < this.sliders.length; index++) | |
550 { | |
551 holdValues.push(convSliderPosToRate(this.sliders[index])); | |
552 } | |
553 var width = event.target.innerWidth; | |
554 var sliderDiv = this.canvas; | |
555 var sliderScaleDiv = this.scale; | |
556 var marginsize = Number(sliderDiv.attributes['marginsize'].value); | |
557 var w = (marginsize+8)*2; | |
558 sliderDiv.style.width = width - w + 'px'; | |
559 var width = width - w; | |
560 // Move sliders into new position | |
561 for (var index = 0; index < this.sliders.length; index++) | |
562 { | |
563 var pos = holdValues[index]; | |
564 var pix = pos * width; | |
565 this.sliders[index].style.left = pix+marginsize+'px'; | |
566 } | |
567 | |
568 // Move scale labels | |
569 for (var index = 0; index < this.scale.children.length; index++) | |
570 { | |
571 var scaleObj = this.scale.children[index]; | |
572 var position = Number(scaleObj.attributes['value'].value); | |
573 var pixelPosition = (position*width)+marginsize; | |
574 scaleObj.style.left = Math.floor((pixelPosition-($(scaleObj).width()/2)))+'px'; | |
575 } | |
576 }; | |
577 } | |
578 | |
579 function sliderObject(audioObject,interfaceObjects) { | |
580 // Create a new slider object; | |
581 this.parent = audioObject; | |
582 this.trackSliderObjects = []; | |
583 for (var i=0; i<interfaceContext.interfaceSliders.length; i++) | |
584 { | |
585 var trackObj = interfaceContext.interfaceSliders[i].createSliderObject(audioObject); | |
586 this.trackSliderObjects.push(trackObj); | |
587 } | |
588 | |
589 // Onclick, switch playback to that track | |
590 | |
591 this.enable = function() { | |
592 if (this.parent.state == 1) | |
593 { | |
594 $(this.trackSliderObjects).each(function(i,trackObj){ | |
595 $(trackObj).removeClass('track-slider-disabled'); | |
596 }); | |
597 } | |
598 }; | |
599 this.updateLoading = function(progress) | |
600 { | |
601 if (progress != 100) | |
602 { | |
603 progress = String(progress); | |
604 progress = progress.split('.')[0]; | |
605 this.trackSliderObjects[0].children[0].textContent = progress+'%'; | |
606 } else { | |
607 this.trackSliderObjects[0].children[0].textContent = this.parent.id; | |
608 } | |
609 }; | |
610 this.startPlayback = function() | |
611 { | |
612 $('.track-slider').removeClass('track-slider-playing'); | |
613 var name = ".track-slider-"+this.parent.id; | |
614 $(name).addClass('track-slider-playing'); | |
615 $('.comment-div').removeClass('comment-box-playing'); | |
616 $('#comment-div-'+this.parent.id).addClass('comment-box-playing'); | |
617 var outsideReference = document.getElementById('outside-reference'); | |
618 if (outsideReference != undefined) | |
619 $(outsideReference).removeClass('track-slider-playing'); | |
620 }; | |
621 this.stopPlayback = function() | |
622 { | |
623 var name = ".track-slider-"+this.parent.id; | |
624 $(name).removeClass('track-slider-playing'); | |
625 $('#comment-div-'+this.parent.id).removeClass('comment-box-playing'); | |
626 }; | |
627 this.exportXMLDOM = function(audioObject) { | |
628 // Called by the audioObject holding this element. Must be present | |
629 var obj = []; | |
630 $(this.trackSliderObjects).each(function(i,trackObj){ | |
631 var node = storage.document.createElement('value'); | |
632 node.setAttribute("interface-name",trackObj.getAttribute("interface-name")); | |
633 node.textContent = convSliderPosToRate(trackObj); | |
634 obj.push(node); | |
635 }); | |
636 | |
637 return obj; | |
638 }; | |
639 this.getValue = function() { | |
640 return convSliderPosToRate(this.trackSliderObjects[0]); | |
641 }; | |
642 this.getPresentedId = function() | |
643 { | |
644 return this.trackSliderObjects[0].children[0].textContent; | |
645 }; | |
646 this.canMove = function() | |
647 { | |
648 return true; | |
649 }; | |
650 } | |
651 | |
652 function outsideReferenceDOM(audioObject,index,inject) | |
653 { | |
654 this.parent = audioObject; | |
655 this.outsideReferenceHolder = document.createElement('div'); | |
656 this.outsideReferenceHolder.id = 'outside-reference'; | |
657 this.outsideReferenceHolder.className = 'outside-reference track-slider-disabled'; | |
658 var outsideReferenceHolderspan = document.createElement('span'); | |
659 outsideReferenceHolderspan.textContent = 'Reference'; | |
660 this.outsideReferenceHolder.appendChild(outsideReferenceHolderspan); | |
661 this.outsideReferenceHolder.setAttribute('track-id',index); | |
662 | |
663 this.outsideReferenceHolder.onclick = function(event) | |
664 { | |
665 audioEngineContext.play(event.currentTarget.getAttribute('track-id')); | |
666 $('.track-slider').removeClass('track-slider-playing'); | |
667 $('.comment-div').removeClass('comment-box-playing'); | |
668 if (event.currentTarget.nodeName == 'DIV') { | |
669 $(event.currentTarget).addClass('track-slider-playing'); | |
670 } else { | |
671 $(event.currentTarget.parentElement).addClass('track-slider-playing'); | |
672 } | |
673 }; | |
674 inject.appendChild(this.outsideReferenceHolder); | |
675 this.enable = function() | |
676 { | |
677 if (this.parent.state == 1) | |
678 { | |
679 $(this.outsideReferenceHolder).removeClass('track-slider-disabled'); | |
680 } | |
681 }; | |
682 this.updateLoading = function(progress) | |
683 { | |
684 if (progress != 100) | |
685 { | |
686 progress = String(progress); | |
687 progress = progress.split('.')[0]; | |
688 this.outsideReferenceHolder[0].children[0].textContent = progress+'%'; | |
689 } else { | |
690 this.outsideReferenceHolder[0].children[0].textContent = "Play Reference"; | |
691 } | |
692 }; | |
693 this.startPlayback = function() | |
694 { | |
695 $('.track-slider').removeClass('track-slider-playing'); | |
696 $(this.outsideReferenceHolder).addClass('track-slider-playing'); | |
697 $('.comment-div').removeClass('comment-box-playing'); | |
698 }; | |
699 this.stopPlayback = function() | |
700 { | |
701 $(this.outsideReferenceHolder).removeClass('track-slider-playing'); | |
702 }; | |
703 this.exportXMLDOM = function(audioObject) | |
704 { | |
705 return null; | |
706 }; | |
707 this.getValue = function() | |
708 { | |
709 return 0; | |
710 }; | |
711 this.getPresentedId = function() | |
712 { | |
713 return 'reference'; | |
714 }; | |
715 this.canMove = function() | |
716 { | |
717 return false; | |
718 }; | |
719 } | |
720 | |
721 function buttonSubmitClick() | |
722 { | |
723 var checks = []; | |
724 checks = checks.concat(testState.currentStateMap.interfaces[0].options); | |
725 checks = checks.concat(specification.interfaces.options); | |
726 var canContinue = true; | |
727 | |
728 // Check that the anchor and reference objects are correctly placed | |
729 if (interfaceContext.checkHiddenAnchor() == false) {return;} | |
730 if (interfaceContext.checkHiddenReference() == false) {return;} | |
731 | |
732 for (var i=0; i<checks.length; i++) { | |
733 if (checks[i].type == 'check') | |
734 { | |
735 switch(checks[i].name) { | |
736 case 'fragmentPlayed': | |
737 // Check if all fragments have been played | |
738 var checkState = interfaceContext.checkAllPlayed(); | |
739 if (checkState == false) {canContinue = false;} | |
740 break; | |
741 case 'fragmentFullPlayback': | |
742 // Check all fragments have been played to their full length | |
743 var checkState = interfaceContext.checkFragmentsFullyPlayed(); | |
744 if (checkState == false) {canContinue = false;} | |
745 break; | |
746 case 'fragmentMoved': | |
747 // Check all fragment sliders have been moved. | |
748 var checkState = interfaceContext.checkAllMoved(); | |
749 if (checkState == false) {canContinue = false;} | |
750 break; | |
751 case 'fragmentComments': | |
752 // Check all fragment sliders have been moved. | |
753 var checkState = interfaceContext.checkAllCommented(); | |
754 if (checkState == false) {canContinue = false;} | |
755 break; | |
756 case 'scalerange': | |
757 // Check the scale is used to its full width outlined by the node | |
758 var checkState = interfaceContext.checkScaleRange(); | |
759 if (checkState == false) {canContinue = false;} | |
760 break; | |
761 default: | |
762 console.log("WARNING - Check option "+checks[i].name+" is not supported on this interface"); | |
763 break; | |
764 } | |
765 | |
766 } | |
767 if (!canContinue) {break;} | |
768 } | |
769 | |
770 if (canContinue) { | |
771 if (audioEngineContext.status == 1) { | |
772 var playback = document.getElementById('playback-button'); | |
773 playback.click(); | |
774 // This function is called when the submit button is clicked. Will check for any further tests to perform, or any post-test options | |
775 } else | |
776 { | |
777 if (audioEngineContext.timer.testStarted == false) | |
778 { | |
779 alert('You have not started the test! Please click a fragment to begin the test!'); | |
780 return; | |
781 } | |
782 } | |
783 testState.advanceState(); | |
784 } | |
785 } | |
786 | |
787 function convSliderPosToRate(trackSlider) | |
788 { | |
789 var slider = trackSlider.parentElement; | |
790 var w = slider.style.width; | |
791 var marginsize = Number(slider.attributes['marginsize'].value); | |
792 var maxPix = w.substr(0,w.length-2); | |
793 var pix = trackSlider.style.left; | |
794 pix = pix.substr(0,pix.length-2); | |
795 var rate = (pix-marginsize)/maxPix; | |
796 return rate; | |
797 } | |
798 | |
799 function resizeWindow(event){ | |
800 // Function called when the window has been resized. | |
801 // MANDATORY FUNCTION | |
802 | |
803 // Resize the slider objects | |
804 for (var i=0; i<interfaceContext.interfaceSliders.length; i++) | |
805 { | |
806 interfaceContext.interfaceSliders[i].resize(event); | |
807 } | |
808 } | |
809 | |
810 function pageXMLSave(store, pageSpecification) | |
811 { | |
812 // MANDATORY | |
813 // Saves a specific test page | |
814 // You can use this space to add any extra nodes to your XML <audioHolder> saves | |
815 // Get the current <page> information in store (remember to appendChild your data to it) | |
816 // pageSpecification is the current page node configuration | |
817 // To create new XML nodes, use storage.document.createElement(); | |
818 | |
819 if (interfaceContext.interfaceSliders.length == 1) | |
820 { | |
821 // If there is only one axis, there only needs to be one metric return | |
822 return; | |
823 } | |
824 var audioelements = store.getElementsByTagName("audioelement"); | |
825 for (var i=0; i<audioelements.length; i++) | |
826 { | |
827 // Have to append the metric specific nodes | |
828 if (pageSpecification.outsideReference == null || pageSpecification.outsideReference.id != audioelements[i].id) | |
829 { | |
830 var inject = audioelements[i].getElementsByTagName("metric"); | |
831 if (inject.length == 0) | |
832 { | |
833 inject = storage.document.createElement("metric"); | |
834 } else { | |
835 inject = inject[0]; | |
836 } | |
837 for (var k=0; k<interfaceContext.interfaceSliders.length; k++) | |
838 { | |
839 var mrnodes = interfaceContext.interfaceSliders[k].metrics[i].exportXMLDOM(inject); | |
840 for (var j=0; j<mrnodes.length; j++) | |
841 { | |
842 var name = mrnodes[j].getAttribute("name"); | |
843 if (name == "elementTracker" || name == "elementTrackerFull" || name == "elementInitialPosition" || name == "elementFlagMoved") | |
844 { | |
845 mrnodes[j].setAttribute("interface-name",interfaceContext.interfaceSliders[k].name); | |
846 mrnodes[j].setAttribute("interface-id",k); | |
847 inject.appendChild(mrnodes[j]); | |
848 } | |
849 } | |
850 } | |
851 } | |
852 } | |
853 } |