annotate interfaces/ape.js @ 496:cb348f6208b2 Dev_main

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