annotate ape.js @ 429:9bf8ecbcdc8a Dev_main

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