comparison ape.js @ 753:66d732c2bc14

Index page now links to example APE project, example MUSHRA project, test creator, analysis page, citing info, GNU license, and instructions. Instructions and example project contain info on checkboxes.
author Brecht De Man <BrechtDeMan@users.noreply.github.com>
date Fri, 18 Dec 2015 18:26:46 +0000
parents
children 8540d153caec
comparison
equal deleted inserted replaced
-1:000000000000 753:66d732c2bc14
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 var interfaceObj = audioHolderObject.interfaces;
287 for (var k=0; k<interfaceObj.length; k++) {
288 // Create the div box to center align
289 interfaceContext.interfaceSliders.push(new interfaceSliderHolder(interfaceObj[k]));
290 for (var i=0; i<interfaceObj[k].options.length; i++)
291 {
292 if (interfaceObj[k].options[i].type == 'option' && interfaceObj[k].options[i].name == 'playhead')
293 {
294 var playbackHolder = document.getElementById('playback-holder');
295 if (playbackHolder == null)
296 {
297 playbackHolder = document.createElement('div');
298 playbackHolder.style.width = "100%";
299 playbackHolder.align = 'center';
300 playbackHolder.appendChild(interfaceContext.playhead.object);
301 feedbackHolder.appendChild(playbackHolder);
302 }
303 } else if (interfaceObj[k].options[i].type == 'option' && interfaceObj[k].options[i].name == 'page-count')
304 {
305 var pagecountHolder = document.getElementById('page-count');
306 if (pagecountHolder == null)
307 {
308 pagecountHolder = document.createElement('div');
309 pagecountHolder.id = 'page-count';
310 }
311 pagecountHolder.innerHTML = '<span>Page '+(audioHolderObject.presentedId+1)+' of '+specification.audioHolders.length+'</span>';
312 var inject = document.getElementById('interface-buttons');
313 inject.appendChild(pagecountHolder);
314 }
315 }
316 }
317
318 var commentBoxPrefix = "Comment on track";
319
320 var commentShow = audioHolderObject.elementComments;
321
322 var loopPlayback = audioHolderObject.loop;
323
324 currentTestHolder = document.createElement('audioHolder');
325 currentTestHolder.id = audioHolderObject.id;
326 currentTestHolder.repeatCount = audioHolderObject.repeatCount;
327
328 // Find all the audioElements from the audioHolder
329 $(audioHolderObject.audioElements).each(function(index,element){
330 // Find URL of track
331 // In this jQuery loop, variable 'this' holds the current audioElement.
332
333 // Check if an outside reference
334 if (index == audioHolderObject.outsideReference)
335 {
336 return;
337 }
338
339 // Now load each audio sample. First create the new track by passing the full URL
340 var trackURL = audioHolderObject.hostURL + element.url;
341 var audioObject = audioEngineContext.newTrack(element);
342
343 var node = interfaceContext.createCommentBox(audioObject);
344
345 // Create a slider per track
346 audioObject.interfaceDOM = new sliderObject(audioObject,interfaceObj);
347 audioObject.metric.initialPosition = convSliderPosToRate(audioObject.interfaceDOM.trackSliderObjects[0]);
348 if (audioObject.state == 1)
349 {
350 audioObject.interfaceDOM.enable();
351 }
352
353 });
354
355 $('.track-slider').mousedown(function(event) {
356 interfaceContext.selectObject($(this)[0]);
357 });
358
359 $('.track-slider').mousemove(function(event) {
360 event.preventDefault();
361 });
362
363 $('.slider').mousemove(function(event) {
364 event.preventDefault();
365 var obj = interfaceContext.getSelectedObject();
366 if (obj == null) {return;}
367 $(obj).css("left",event.clientX + "px");
368 interfaceContext.moveObject();
369 });
370
371 $(document).mouseup(function(event){
372 event.preventDefault();
373 var obj = interfaceContext.getSelectedObject();
374 if (obj == null) {return;}
375 var interfaceID = obj.parentElement.getAttribute("interfaceid");
376 var trackID = obj.getAttribute("trackindex");
377 if (interfaceContext.hasSelectedObjectMoved() == true)
378 {
379 var l = $(obj).css("left");
380 var id = obj.getAttribute('trackIndex');
381 var time = audioEngineContext.timer.getTestTime();
382 var rate = convSliderPosToRate(obj);
383 audioEngineContext.audioObjects[id].metric.moved(time,rate);
384 interfaceContext.interfaceSliders[interfaceID].metrics[trackID].moved(time,rate);
385 console.log("slider "+id+" moved to "+rate+' ('+time+')');
386 } else {
387 var id = Number(obj.attributes['trackIndex'].value);
388 //audioEngineContext.metric.sliderPlayed(id);
389 audioEngineContext.play(id);
390 // Currently playing track red, rest green
391
392 $('.track-slider').removeClass('track-slider-playing');
393 var name = ".track-slider-"+obj.getAttribute("trackindex");
394 $(name).addClass('track-slider-playing');
395 $('.comment-div').removeClass('comment-box-playing');
396 $('#comment-div-'+id).addClass('comment-box-playing');
397 var outsideReference = document.getElementById('outside-reference');
398 if (outsideReference != undefined)
399 $(outsideReference).removeClass('track-slider-playing');
400 }
401 interfaceContext.releaseObject();
402 });
403
404
405 if (commentShow) {
406 interfaceContext.showCommentBoxes(feedbackHolder,true);
407 }
408
409 $(audioHolderObject.commentQuestions).each(function(index,element) {
410 var node = interfaceContext.createCommentQuestion(element);
411 feedbackHolder.appendChild(node.holder);
412 });
413
414 // Construct outside reference;
415 if (audioHolderObject.outsideReference != null) {
416 var outsideReferenceHolder = document.createElement('div');
417 outsideReferenceHolder.id = 'outside-reference';
418 outsideReferenceHolder.className = 'outside-reference';
419 outsideReferenceHolderspan = document.createElement('span');
420 outsideReferenceHolderspan.textContent = 'Reference';
421 outsideReferenceHolder.appendChild(outsideReferenceHolderspan);
422
423 var audioObject = audioEngineContext.newTrack(audioHolderObject.audioElements[audioHolderObject.outsideReference]);
424
425 outsideReferenceHolder.onclick = function(event)
426 {
427 audioEngineContext.play(audioEngineContext.audioObjects.length-1);
428 $('.track-slider').removeClass('track-slider-playing');
429 $('.comment-div').removeClass('comment-box-playing');
430 if (event.currentTarget.nodeName == 'DIV') {
431 $(event.currentTarget).addClass('track-slider-playing');
432 } else {
433 $(event.currentTarget.parentElement).addClass('track-slider-playing');
434 }
435 };
436
437 document.getElementById('interface-buttons').appendChild(outsideReferenceHolder);
438 }
439
440
441 //testWaitIndicator();
442 }
443
444 function interfaceSliderHolder(interfaceObject)
445 {
446 this.sliders = [];
447 this.metrics = [];
448 this.id = document.getElementsByClassName("sliderCanvasDiv").length;
449 this.name = interfaceObject.name;
450 this.interfaceObject = interfaceObject;
451 this.sliderDOM = document.createElement('div');
452 this.sliderDOM.className = 'sliderCanvasDiv';
453 this.sliderDOM.id = 'sliderCanvasHolder-'+this.id;
454
455 var pagetitle = document.createElement('div');
456 pagetitle.className = "pageTitle";
457 pagetitle.align = "center";
458 var titleSpan = document.createElement('span');
459 titleSpan.id = "pageTitle-"+this.id;
460 if (interfaceObject.title != undefined && typeof interfaceObject.title == "string")
461 {
462 titleSpan.textContent = interfaceObject.title;
463 } else {
464 titleSpan.textContent = "Axis "+String(this.id+1);
465 }
466 pagetitle.appendChild(titleSpan);
467 this.sliderDOM.appendChild(pagetitle);
468
469 // Create the slider box to hold the slider elements
470 this.canvas = document.createElement('div');
471 if (this.name != undefined)
472 this.canvas.id = 'slider-'+this.name;
473 else
474 this.canvas.id = 'slider-'+this.id;
475 this.canvas.setAttribute("interfaceid",this.id);
476 this.canvas.className = 'slider';
477 this.canvas.align = "left";
478 this.canvas.addEventListener('dragover',function(event){
479 event.preventDefault();
480 event.dataTransfer.effectAllowed = 'none';
481 event.dataTransfer.dropEffect = 'copy';
482 return false;
483 },false);
484 var sliderMargin = document.createAttribute('marginsize');
485 sliderMargin.nodeValue = 42; // Set default margins to 42px either side
486 // Must have a known EXACT width, as this is used later to determine the ratings
487 var w = (Number(sliderMargin.nodeValue)+8)*2;
488 this.canvas.style.width = window.innerWidth - w +"px";
489 this.canvas.style.marginLeft = sliderMargin.nodeValue +'px';
490 this.canvas.setAttributeNode(sliderMargin);
491 this.sliderDOM.appendChild(this.canvas);
492
493 // Create the div to hold any scale objects
494 this.scale = document.createElement('div');
495 this.scale.className = 'sliderScale';
496 this.scale.id = 'sliderScaleHolder-'+this.id;
497 this.scale.align = 'left';
498 this.sliderDOM.appendChild(this.scale);
499 var positionScale = this.canvas.style.width.substr(0,this.canvas.style.width.length-2);
500 var offset = Number(this.canvas.attributes['marginsize'].value);
501 for (var index=0; index<interfaceObject.scale.length; index++)
502 {
503 var scaleObj = interfaceObject.scale[index];
504 var value = document.createAttribute('value');
505 var position = Number(scaleObj[0])*0.01;
506 value.nodeValue = position;
507 var pixelPosition = (position*positionScale)+offset;
508 var scaleDOM = document.createElement('span');
509 scaleDOM.textContent = scaleObj[1];
510 this.scale.appendChild(scaleDOM);
511 scaleDOM.style.left = Math.floor((pixelPosition-($(scaleDOM).width()/2)))+'px';
512 scaleDOM.setAttributeNode(value);
513 }
514
515 var dest = document.getElementById("slider-holder");
516 dest.appendChild(this.sliderDOM);
517
518 this.createSliderObject = function(audioObject)
519 {
520 var trackObj = document.createElement('div');
521 trackObj.className = 'track-slider track-slider-disabled track-slider-'+audioObject.id;
522 trackObj.id = 'track-slider-'+this.id+'-'+audioObject.id;
523 trackObj.setAttribute('trackIndex',audioObject.id);
524 trackObj.innerHTML = '<span>'+audioObject.id+'</span>';
525 if (this.name != undefined) {
526 trackObj.setAttribute('interface-name',this.name);
527 } else {
528 trackObj.setAttribute('interface-name',this.id);
529 }
530 var offset = Number(this.canvas.attributes['marginsize'].value);
531 // Distribute it randomnly
532 var w = window.innerWidth - (offset+8)*2;
533 w = Math.random()*w;
534 w = Math.floor(w+(offset+8));
535 trackObj.style.left = w+'px';
536 this.canvas.appendChild(trackObj);
537 this.sliders.push(trackObj);
538 this.metrics.push(new metricTracker(this));
539 this.metrics[this.metrics.length-1].initialPosition = convSliderPosToRate(trackObj);
540 return trackObj;
541 };
542
543 this.resize = function(event)
544 {
545 var holdValues = [];
546 for (var index = 0; index < this.sliders.length; index++)
547 {
548 holdValues.push(convSliderPosToRate(this.sliders[index]));
549 }
550 var width = event.target.innerWidth;
551 var sliderDiv = this.canvas;
552 var sliderScaleDiv = this.scale;
553 var marginsize = Number(sliderDiv.attributes['marginsize'].value);
554 var w = (marginsize+8)*2;
555 sliderDiv.style.width = width - w + 'px';
556 var width = width - w;
557 // Move sliders into new position
558 for (var index = 0; index < this.sliders.length; index++)
559 {
560 var pos = holdValues[index];
561 var pix = pos * width;
562 this.sliders[index].style.left = pix+marginsize+'px';
563 }
564
565 // Move scale labels
566 for (var index = 0; index < this.scale.children.length; index++)
567 {
568 var scaleObj = this.scale.children[index];
569 var position = Number(scaleObj.attributes['value'].value);
570 var pixelPosition = (position*width)+marginsize;
571 scaleObj.style.left = Math.floor((pixelPosition-($(scaleObj).width()/2)))+'px';
572 }
573 };
574 }
575
576 function sliderObject(audioObject,interfaceObjects) {
577 // Create a new slider object;
578 this.parent = audioObject;
579 this.trackSliderObjects = [];
580 for (var i=0; i<interfaceContext.interfaceSliders.length; i++)
581 {
582 var trackObj = interfaceContext.interfaceSliders[i].createSliderObject(audioObject);
583 this.trackSliderObjects.push(trackObj);
584 }
585
586 // Onclick, switch playback to that track
587
588 this.enable = function() {
589 if (this.parent.state == 1)
590 {
591 $(this.trackSliderObjects).each(function(i,trackObj){
592 $(trackObj).removeClass('track-slider-disabled');
593 });
594 }
595 };
596 this.updateLoading = function(progress)
597 {
598 if (progress != 100)
599 {
600 progress = String(progress);
601 progress = progress.split('.')[0];
602 this.trackSliderObjects[0].children[0].textContent = progress+'%';
603 } else {
604 this.trackSliderObjects[0].children[0].textContent = this.parent.id;
605 }
606 };
607 this.exportXMLDOM = function(audioObject) {
608 // Called by the audioObject holding this element. Must be present
609 var obj = [];
610 $(this.trackSliderObjects).each(function(i,trackObj){
611 var node = document.createElement('value');
612 node.setAttribute("inteerface-name",trackObj.getAttribute("interface-name"));
613 node.textContent = convSliderPosToRate(trackObj);
614 obj.push(node);
615 });
616
617 return obj;
618 };
619 this.getValue = function() {
620 return convSliderPosToRate(this.trackSliderObj);
621 };
622 }
623
624 function buttonSubmitClick()
625 {
626 var checks = testState.currentStateMap[testState.currentIndex].interfaces[0].options;
627 var canContinue = true;
628
629 // Check that the anchor and reference objects are correctly placed
630 if (interfaceContext.checkHiddenAnchor() == false) {return;}
631 if (interfaceContext.checkHiddenReference() == false) {return;}
632
633 for (var i=0; i<checks.length; i++) {
634 if (checks[i].type == 'check')
635 {
636 switch(checks[i].check) {
637 case 'fragmentPlayed':
638 // Check if all fragments have been played
639 var checkState = interfaceContext.checkAllPlayed();
640 if (checkState == false) {canContinue = false;}
641 break;
642 case 'fragmentFullPlayback':
643 // Check all fragments have been played to their full length
644 var checkState = interfaceContext.checkFragmentsFullyPlayed();
645 if (checkState == false) {canContinue = false;}
646 break;
647 case 'fragmentMoved':
648 // Check all fragment sliders have been moved.
649 var checkState = interfaceContext.checkAllMoved();
650 if (checkState == false) {canContinue = false;}
651 break;
652 case 'fragmentComments':
653 // Check all fragment sliders have been moved.
654 var checkState = interfaceContext.checkAllCommented();
655 if (checkState == false) {canContinue = false;}
656 break;
657 case 'scalerange':
658 // Check the scale is used to its full width outlined by the node
659 var checkState = interfaceContext.checkScaleRange();
660 if (checkState == false) {canContinue = false;}
661 break;
662 default:
663 console.log("WARNING - Check option "+checks[i].check+" is not supported on this interface");
664 break;
665 }
666
667 }
668 if (!canContinue) {break;}
669 }
670
671 if (canContinue) {
672 if (audioEngineContext.status == 1) {
673 var playback = document.getElementById('playback-button');
674 playback.click();
675 // This function is called when the submit button is clicked. Will check for any further tests to perform, or any post-test options
676 } else
677 {
678 if (audioEngineContext.timer.testStarted == false)
679 {
680 alert('You have not started the test! Please click a fragment to begin the test!');
681 return;
682 }
683 }
684 testState.advanceState();
685 }
686 }
687
688 function convSliderPosToRate(trackSlider)
689 {
690 var slider = trackSlider.parentElement;
691 var w = slider.style.width;
692 var marginsize = Number(slider.attributes['marginsize'].value);
693 var maxPix = w.substr(0,w.length-2);
694 var pix = trackSlider.style.left;
695 pix = pix.substr(0,pix.length-2);
696 var rate = (pix-marginsize)/maxPix;
697 return rate;
698 }
699
700 function resizeWindow(event){
701 // Function called when the window has been resized.
702 // MANDATORY FUNCTION
703
704 // Resize the slider objects
705 for (var i=0; i<interfaceContext.interfaceSliders.length; i++)
706 {
707 interfaceContext.interfaceSliders[i].resize(event);
708 }
709 }
710
711 function pageXMLSave(store, testXML)
712 {
713 // MANDATORY
714 // Saves a specific test page
715 // You can use this space to add any extra nodes to your XML <audioHolder> saves
716 // Get the current <audioHolder> information in store (remember to appendChild your data to it)
717 if (interfaceContext.interfaceSliders.length == 1)
718 {
719 // If there is only one axis, there only needs to be one metric return
720 return;
721 }
722 var audioelements = store.getElementsByTagName("audioelement");
723 for (var i=0; i<audioelements.length; i++)
724 {
725 // Have to append the metric specific nodes
726 if (testXML.outsideReference == null || testXML.outsideReference.id != audioelements[i].id)
727 {
728 var inject = audioelements[i].getElementsByTagName("metric");
729 if (inject.length == 0)
730 {
731 inject = document.createElement("metric");
732 } else {
733 inject = inject[0];
734 }
735 for (var k=0; k<interfaceContext.interfaceSliders.length; k++)
736 {
737 var node = interfaceContext.interfaceSliders[k].metrics[i].exportXMLDOM();
738 var mrnodes = node.getElementsByTagName("metricresult");
739 for (var j=0; j<mrnodes.length; j++)
740 {
741 var name = mrnodes[j].getAttribute("name");
742 if (name == "elementTracker" || name == "elementTrackerFull" || name == "elementInitialPosition" || name == "elementFlagMoved")
743 {
744 mrnodes[j].setAttribute("interface-name",interfaceContext.interfaceSliders[k].name);
745 mrnodes[j].setAttribute("interface-id",k);
746 inject.appendChild(mrnodes[j]);
747 }
748 }
749 }
750 }
751 }
752 }