annotate interfaces/ape.js @ 1116:c44fbf72f7f2

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