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 }