annotate ape.js @ 926:f427b5805b69

Merge in main
author Nicholas Jillings <nicholas.jillings@eecs.qmul.ac.uk>
date Sat, 30 May 2015 11:39:53 +0100
parents
children d141519a99f2
rev   line source
nicholas@926 1 /**
nicholas@926 2 * ape.js
nicholas@926 3 * Create the APE interface
nicholas@926 4 */
nicholas@926 5
nicholas@926 6 // preTest - In preTest state
nicholas@926 7 // testRun-ID - In test running, test Id number at the end 'testRun-2'
nicholas@926 8 // testRunPost-ID - Post test of test ID
nicholas@926 9 // testRunPre-ID - Pre-test of test ID
nicholas@926 10 // postTest - End of test, final submission!
nicholas@926 11
nicholas@926 12
nicholas@926 13 // Once this is loaded and parsed, begin execution
nicholas@926 14 loadInterface(projectXML);
nicholas@926 15
nicholas@926 16 function loadInterface(xmlDoc) {
nicholas@926 17
nicholas@926 18 // Get the dimensions of the screen available to the page
nicholas@926 19 var width = window.innerWidth;
nicholas@926 20 var height = window.innerHeight;
nicholas@926 21
nicholas@926 22 // The injection point into the HTML page
nicholas@926 23 var insertPoint = document.getElementById("topLevelBody");
nicholas@926 24 var testContent = document.createElement('div');
nicholas@926 25
nicholas@926 26 testContent.id = 'testContent';
nicholas@926 27
nicholas@926 28
nicholas@926 29 // Decode parts of the xmlDoc that are needed
nicholas@926 30 // xmlDoc MUST already be parsed by jQuery!
nicholas@926 31 var xmlSetup = xmlDoc.find('setup');
nicholas@926 32 // Should put in an error function here incase of malprocessed or malformed XML
nicholas@926 33
nicholas@926 34 // Create pre and post test questions
nicholas@926 35
nicholas@926 36 var preTest = xmlSetup.find('PreTest');
nicholas@926 37 var postTest = xmlSetup.find('PostTest');
nicholas@926 38 preTest = preTest[0];
nicholas@926 39 postTest = postTest[0];
nicholas@926 40
nicholas@926 41 if (preTest == undefined) {preTest = document.createElement("preTest");}
nicholas@926 42 if (postTest == undefined){postTest= document.createElement("postTest");}
nicholas@926 43
nicholas@926 44 testState.stateMap.push(preTest);
nicholas@926 45
nicholas@926 46 // Extract the different test XML DOM trees
nicholas@926 47 var audioHolders = xmlDoc.find('audioHolder');
nicholas@926 48 var testXMLSetups = [];
nicholas@926 49 audioHolders.each(function(index,element) {
nicholas@926 50 var repeatN = element.attributes['repeatCount'].value;
nicholas@926 51 for (var r=0; r<=repeatN; r++) {
nicholas@926 52 testXMLSetups.push(element);
nicholas@926 53 }
nicholas@926 54 });
nicholas@926 55
nicholas@926 56 // New check if we need to randomise the test order
nicholas@926 57 var randomise = xmlSetup[0].attributes['randomiseOrder'];
nicholas@926 58 if (randomise != undefined) {
nicholas@926 59 if (randomise.value === 'true'){
nicholas@926 60 randomise = true;
nicholas@926 61 } else {
nicholas@926 62 randomise = false;
nicholas@926 63 }
nicholas@926 64 } else {
nicholas@926 65 randomise = false;
nicholas@926 66 }
nicholas@926 67
nicholas@926 68 if (randomise)
nicholas@926 69 {
nicholas@926 70 testXMLSetups = randomiseOrder(testXMLSetups);
nicholas@926 71 }
nicholas@926 72
nicholas@926 73 $(testXMLSetups).each(function(index,elem){
nicholas@926 74 testState.stateMap.push(elem);
nicholas@926 75 })
nicholas@926 76
nicholas@926 77 testState.stateMap.push(postTest);
nicholas@926 78
nicholas@926 79 // Obtain the metrics enabled
nicholas@926 80 var metricNode = xmlSetup.find('Metric');
nicholas@926 81 var metricNode = metricNode.find('metricEnable');
nicholas@926 82 metricNode.each(function(index,node){
nicholas@926 83 var enabled = node.textContent;
nicholas@926 84 switch(enabled)
nicholas@926 85 {
nicholas@926 86 case 'testTimer':
nicholas@926 87 sessionMetrics.prototype.enableTestTimer = true;
nicholas@926 88 break;
nicholas@926 89 case 'elementTimer':
nicholas@926 90 sessionMetrics.prototype.enableElementTimer = true;
nicholas@926 91 break;
nicholas@926 92 case 'elementTracker':
nicholas@926 93 sessionMetrics.prototype.enableElementTracker = true;
nicholas@926 94 break;
nicholas@926 95 case 'elementInitalPosition':
nicholas@926 96 sessionMetrics.prototype.enableElementInitialPosition = true;
nicholas@926 97 break;
nicholas@926 98 case 'elementFlagListenedTo':
nicholas@926 99 sessionMetrics.prototype.enableFlagListenedTo = true;
nicholas@926 100 break;
nicholas@926 101 case 'elementFlagMoved':
nicholas@926 102 sessionMetrics.prototype.enableFlagMoved = true;
nicholas@926 103 break;
nicholas@926 104 case 'elementFlagComments':
nicholas@926 105 sessionMetrics.prototype.enableFlagComments = true;
nicholas@926 106 break;
nicholas@926 107 }
nicholas@926 108 });
nicholas@926 109
nicholas@926 110 // Create APE specific metric functions
nicholas@926 111 audioEngineContext.metric.initialiseTest = function()
nicholas@926 112 {
nicholas@926 113 };
nicholas@926 114
nicholas@926 115 audioEngineContext.metric.sliderMoveStart = function(id)
nicholas@926 116 {
nicholas@926 117 if (this.data == -1)
nicholas@926 118 {
nicholas@926 119 this.data = id;
nicholas@926 120 } else {
nicholas@926 121 console.log('ERROR: Metric tracker detecting two moves!');
nicholas@926 122 this.data = -1;
nicholas@926 123 }
nicholas@926 124 };
nicholas@926 125 audioEngineContext.metric.sliderMoved = function()
nicholas@926 126 {
nicholas@926 127 var time = audioEngineContext.timer.getTestTime();
nicholas@926 128 var id = this.data;
nicholas@926 129 this.data = -1;
nicholas@926 130 var position = convSliderPosToRate(id);
nicholas@926 131 console.log('slider ' + id + ': '+ position + ' (' + time + ')'); // DEBUG/SAFETY: show position and slider id
nicholas@926 132 if (audioEngineContext.timer.testStarted)
nicholas@926 133 {
nicholas@926 134 audioEngineContext.audioObjects[id].metric.moved(time,position);
nicholas@926 135 }
nicholas@926 136 };
nicholas@926 137
nicholas@926 138 audioEngineContext.metric.sliderPlayed = function(id)
nicholas@926 139 {
nicholas@926 140 var time = audioEngineContext.timer.getTestTime();
nicholas@926 141 if (audioEngineContext.timer.testStarted)
nicholas@926 142 {
nicholas@926 143 if (this.lastClicked >= 0)
nicholas@926 144 {
nicholas@926 145 audioEngineContext.audioObjects[this.lastClicked].metric.listening(time);
nicholas@926 146 }
nicholas@926 147 this.lastClicked = id;
nicholas@926 148 audioEngineContext.audioObjects[id].metric.listening(time);
nicholas@926 149 }
nicholas@926 150 console.log('slider ' + id + ' played (' + time + ')'); // DEBUG/SAFETY: show played slider id
nicholas@926 151 };
nicholas@926 152
nicholas@926 153 // Create the top div for the Title element
nicholas@926 154 var titleAttr = xmlSetup[0].attributes['title'];
nicholas@926 155 var title = document.createElement('div');
nicholas@926 156 title.className = "title";
nicholas@926 157 title.align = "center";
nicholas@926 158 var titleSpan = document.createElement('span');
nicholas@926 159
nicholas@926 160 // Set title to that defined in XML, else set to default
nicholas@926 161 if (titleAttr != undefined) {
nicholas@926 162 titleSpan.innerHTML = titleAttr.value;
nicholas@926 163 } else {
nicholas@926 164 titleSpan.innerHTML = 'Listening test';
nicholas@926 165 }
nicholas@926 166 // Insert the titleSpan element into the title div element.
nicholas@926 167 title.appendChild(titleSpan);
nicholas@926 168
nicholas@926 169 var pagetitle = document.createElement('div');
nicholas@926 170 pagetitle.className = "pageTitle";
nicholas@926 171 pagetitle.align = "center";
nicholas@926 172 var titleSpan = document.createElement('span');
nicholas@926 173 titleSpan.id = "pageTitle";
nicholas@926 174 pagetitle.appendChild(titleSpan);
nicholas@926 175
nicholas@926 176 // Store the return URL path in global projectReturn
nicholas@926 177 projectReturn = xmlSetup[0].attributes['projectReturn'];
nicholas@926 178 if (projectReturn == undefined) {
nicholas@926 179 console.log("WARNING - projectReturn not specified! Will assume null.");
nicholas@926 180 projectReturn = "null";
nicholas@926 181 } else {
nicholas@926 182 projectReturn = projectReturn.value;
nicholas@926 183 }
nicholas@926 184
nicholas@926 185 // Create Interface buttons!
nicholas@926 186 var interfaceButtons = document.createElement('div');
nicholas@926 187 interfaceButtons.id = 'interface-buttons';
nicholas@926 188
nicholas@926 189 // MANUAL DOWNLOAD POINT
nicholas@926 190 // If project return is null, this MUST be specified as the location to create the download link
nicholas@926 191 var downloadPoint = document.createElement('div');
nicholas@926 192 downloadPoint.id = 'download-point';
nicholas@926 193
nicholas@926 194 // Create playback start/stop points
nicholas@926 195 var playback = document.createElement("button");
nicholas@926 196 playback.innerHTML = 'Stop';
nicholas@926 197 playback.id = 'playback-button';
nicholas@926 198 // onclick function. Check if it is playing or not, call the correct function in the
nicholas@926 199 // audioEngine, change the button text to reflect the next state.
nicholas@926 200 playback.onclick = function() {
nicholas@926 201 if (audioEngineContext.status == 1) {
nicholas@926 202 audioEngineContext.stop();
nicholas@926 203 this.innerHTML = 'Stop';
nicholas@926 204 var time = audioEngineContext.timer.getTestTime();
nicholas@926 205 console.log('Stopped at ' + time); // DEBUG/SAFETY
nicholas@926 206 }
nicholas@926 207 };
nicholas@926 208 // Create Submit (save) button
nicholas@926 209 var submit = document.createElement("button");
nicholas@926 210 submit.innerHTML = 'Submit';
nicholas@926 211 submit.onclick = buttonSubmitClick;
nicholas@926 212 submit.id = 'submit-button';
nicholas@926 213 // Append the interface buttons into the interfaceButtons object.
nicholas@926 214 interfaceButtons.appendChild(playback);
nicholas@926 215 interfaceButtons.appendChild(submit);
nicholas@926 216 interfaceButtons.appendChild(downloadPoint);
nicholas@926 217
nicholas@926 218 // Now create the slider and HTML5 canvas boxes
nicholas@926 219
nicholas@926 220 // Create the div box to center align
nicholas@926 221 var sliderBox = document.createElement('div');
nicholas@926 222 sliderBox.className = 'sliderCanvasDiv';
nicholas@926 223 sliderBox.id = 'sliderCanvasHolder';
nicholas@926 224
nicholas@926 225 // Create the slider box to hold the slider elements
nicholas@926 226 var canvas = document.createElement('div');
nicholas@926 227 canvas.id = 'slider';
nicholas@926 228 canvas.align = "left";
nicholas@926 229 canvas.addEventListener('dragover',function(event){
nicholas@926 230 event.preventDefault();
nicholas@926 231 return false;
nicholas@926 232 },false);
nicholas@926 233 var sliderMargin = document.createAttribute('marginsize');
nicholas@926 234 sliderMargin.nodeValue = 42; // Set default margins to 42px either side
nicholas@926 235 // Must have a known EXACT width, as this is used later to determine the ratings
nicholas@926 236 var w = (Number(sliderMargin.nodeValue)+8)*2;
nicholas@926 237 canvas.style.width = width - w +"px";
nicholas@926 238 canvas.style.marginLeft = sliderMargin.nodeValue +'px';
nicholas@926 239 canvas.setAttributeNode(sliderMargin);
nicholas@926 240 sliderBox.appendChild(canvas);
nicholas@926 241
nicholas@926 242 // Create the div to hold any scale objects
nicholas@926 243 var scale = document.createElement('div');
nicholas@926 244 scale.className = 'sliderScale';
nicholas@926 245 scale.id = 'sliderScaleHolder';
nicholas@926 246 scale.align = 'left';
nicholas@926 247 sliderBox.appendChild(scale);
nicholas@926 248
nicholas@926 249 // Global parent for the comment boxes on the page
nicholas@926 250 var feedbackHolder = document.createElement('div');
nicholas@926 251 feedbackHolder.id = 'feedbackHolder';
nicholas@926 252
nicholas@926 253 testContent.style.zIndex = 1;
nicholas@926 254 insertPoint.innerHTML = null; // Clear the current schema
nicholas@926 255
nicholas@926 256 currentState = 'preTest';
nicholas@926 257
nicholas@926 258 // Inject into HTML
nicholas@926 259 testContent.appendChild(title); // Insert the title
nicholas@926 260 testContent.appendChild(pagetitle);
nicholas@926 261 testContent.appendChild(interfaceButtons);
nicholas@926 262 testContent.appendChild(sliderBox);
nicholas@926 263 testContent.appendChild(feedbackHolder);
nicholas@926 264 insertPoint.appendChild(testContent);
nicholas@926 265
nicholas@926 266 // Load the full interface
nicholas@926 267 testState.initialise();
nicholas@926 268 testState.advanceState();
nicholas@926 269
nicholas@926 270 testWaitIndicator();
nicholas@926 271 }
nicholas@926 272
nicholas@926 273 function loadTest(textXML)
nicholas@926 274 {
nicholas@926 275
nicholas@926 276 // Reset audioEngineContext.Metric globals for new test
nicholas@926 277 audioEngineContext.newTestPage();
nicholas@926 278
nicholas@926 279 var id = textXML.id;
nicholas@926 280
nicholas@926 281 var feedbackHolder = document.getElementById('feedbackHolder');
nicholas@926 282 var canvas = document.getElementById('slider');
nicholas@926 283 feedbackHolder.innerHTML = null;
nicholas@926 284 canvas.innerHTML = null;
nicholas@926 285
nicholas@926 286 // Setup question title
nicholas@926 287 var interfaceObj = $(textXML).find('interface');
nicholas@926 288 var titleNode = interfaceObj.find('title');
nicholas@926 289 if (titleNode[0] != undefined)
nicholas@926 290 {
nicholas@926 291 document.getElementById('pageTitle').textContent = titleNode[0].textContent;
nicholas@926 292 }
nicholas@926 293 var positionScale = canvas.style.width.substr(0,canvas.style.width.length-2);
nicholas@926 294 var offset = Number(document.getElementById('slider').attributes['marginsize'].value);
nicholas@926 295 var scale = document.getElementById('sliderScaleHolder');
nicholas@926 296 scale.innerHTML = null;
nicholas@926 297 interfaceObj.find('scale').each(function(index,scaleObj){
nicholas@926 298 var value = document.createAttribute('value');
nicholas@926 299 var position = Number(scaleObj.attributes['position'].value)*0.01;
nicholas@926 300 value.nodeValue = position;
nicholas@926 301 var pixelPosition = (position*positionScale)+offset;
nicholas@926 302 var scaleDOM = document.createElement('span');
nicholas@926 303 scaleDOM.textContent = scaleObj.textContent;
nicholas@926 304 scale.appendChild(scaleDOM);
nicholas@926 305 scaleDOM.style.left = Math.floor((pixelPosition-($(scaleDOM).width()/2)))+'px';
nicholas@926 306 scaleDOM.setAttributeNode(value);
nicholas@926 307 });
nicholas@926 308
nicholas@926 309 // Extract the hostURL attribute. If not set, create an empty string.
nicholas@926 310 var hostURL = textXML.attributes['hostURL'];
nicholas@926 311 if (hostURL == undefined) {
nicholas@926 312 hostURL = "";
nicholas@926 313 } else {
nicholas@926 314 hostURL = hostURL.value;
nicholas@926 315 }
nicholas@926 316 // Extract the sampleRate. If set, convert the string to a Number.
nicholas@926 317 var hostFs = textXML.attributes['sampleRate'];
nicholas@926 318 if (hostFs != undefined) {
nicholas@926 319 hostFs = Number(hostFs.value);
nicholas@926 320 }
nicholas@926 321
nicholas@926 322 /// CHECK FOR SAMPLE RATE COMPATIBILITY
nicholas@926 323 if (hostFs != undefined) {
nicholas@926 324 if (Number(hostFs) != audioContext.sampleRate) {
nicholas@926 325 var errStr = 'Sample rates do not match! Requested '+Number(hostFs)+', got '+audioContext.sampleRate+'. Please set the sample rate to match before completing this test.';
nicholas@926 326 alert(errStr);
nicholas@926 327 return;
nicholas@926 328 }
nicholas@926 329 }
nicholas@926 330
nicholas@926 331 var commentShow = textXML.attributes['elementComments'];
nicholas@926 332 if (commentShow != undefined) {
nicholas@926 333 if (commentShow.value == 'false') {commentShow = false;}
nicholas@926 334 else {commentShow = true;}
nicholas@926 335 } else {commentShow = true;}
nicholas@926 336
nicholas@926 337 var loopPlayback = textXML.attributes['loop'];
nicholas@926 338 if (loopPlayback != undefined)
nicholas@926 339 {
nicholas@926 340 loopPlayback = loopPlayback.value;
nicholas@926 341 if (loopPlayback == 'true') {
nicholas@926 342 loopPlayback = true;
nicholas@926 343 } else {
nicholas@926 344 loopPlayback = false;
nicholas@926 345 }
nicholas@926 346 } else {
nicholas@926 347 loopPlayback = false;
nicholas@926 348 }
nicholas@926 349 audioEngineContext.loopPlayback = loopPlayback;
nicholas@926 350 // Create AudioEngine bindings for playback
nicholas@926 351 if (loopPlayback) {
nicholas@926 352 audioEngineContext.selectedTrack = function(id) {
nicholas@926 353 for (var i=0; i<this.audioObjects.length; i++)
nicholas@926 354 {
nicholas@926 355 if (id == i) {
nicholas@926 356 this.audioObjects[i].loopStart();
nicholas@926 357 } else {
nicholas@926 358 this.audioObjects[i].loopStop();
nicholas@926 359 }
nicholas@926 360 }
nicholas@926 361 };
nicholas@926 362 } else {
nicholas@926 363 audioEngineContext.selectedTrack = function(id) {
nicholas@926 364 for (var i=0; i<this.audioObjects.length; i++)
nicholas@926 365 {
nicholas@926 366 this.audioObjects[i].outputGain.gain.value = 0.0;
nicholas@926 367 this.audioObjects[i].stop();
nicholas@926 368 }
nicholas@926 369 if (this.status == 1) {
nicholas@926 370 this.audioObjects[id].outputGain.gain.value = 1.0;
nicholas@926 371 this.audioObjects[id].play(audioContext.currentTime+0.01);
nicholas@926 372 }
nicholas@926 373 };
nicholas@926 374 }
nicholas@926 375
nicholas@926 376 currentTestHolder = document.createElement('audioHolder');
nicholas@926 377 currentTestHolder.id = textXML.id;
nicholas@926 378 currentTestHolder.repeatCount = textXML.attributes['repeatCount'].value;
nicholas@926 379
nicholas@926 380 var randomise = textXML.attributes['randomiseOrder'];
nicholas@926 381 if (randomise != undefined) {randomise = randomise.value;}
nicholas@926 382 else {randomise = false;}
nicholas@926 383
nicholas@926 384 var audioElements = $(textXML).find('audioElements');
nicholas@926 385 currentTrackOrder = [];
nicholas@926 386 audioElements.each(function(index,element){
nicholas@926 387 // Find any blind-repeats
nicholas@926 388 // Not implemented yet, but just in case
nicholas@926 389 currentTrackOrder[index] = element;
nicholas@926 390 });
nicholas@926 391 if (randomise) {
nicholas@926 392 currentTrackOrder = randomiseOrder(currentTrackOrder);
nicholas@926 393 }
nicholas@926 394
nicholas@926 395 // Delete any previous audioObjects associated with the audioEngine
nicholas@926 396 audioEngineContext.audioObjects = [];
nicholas@926 397
nicholas@926 398 // Find all the audioElements from the audioHolder
nicholas@926 399 $(currentTrackOrder).each(function(index,element){
nicholas@926 400 // Find URL of track
nicholas@926 401 // In this jQuery loop, variable 'this' holds the current audioElement.
nicholas@926 402
nicholas@926 403 // Now load each audio sample. First create the new track by passing the full URL
nicholas@926 404 var trackURL = hostURL + this.attributes['url'].value;
nicholas@926 405 audioEngineContext.newTrack(trackURL);
nicholas@926 406
nicholas@926 407 if (commentShow) {
nicholas@926 408 // Create document objects to hold the comment boxes
nicholas@926 409 var trackComment = document.createElement('div');
nicholas@926 410 trackComment.className = 'comment-div';
nicholas@926 411 // Create a string next to each comment asking for a comment
nicholas@926 412 var trackString = document.createElement('span');
nicholas@926 413 trackString.innerHTML = 'Comment on track '+index;
nicholas@926 414 // Create the HTML5 comment box 'textarea'
nicholas@926 415 var trackCommentBox = document.createElement('textarea');
nicholas@926 416 trackCommentBox.rows = '4';
nicholas@926 417 trackCommentBox.cols = '100';
nicholas@926 418 trackCommentBox.name = 'trackComment'+index;
nicholas@926 419 trackCommentBox.className = 'trackComment';
nicholas@926 420 var br = document.createElement('br');
nicholas@926 421 // Add to the holder.
nicholas@926 422 trackComment.appendChild(trackString);
nicholas@926 423 trackComment.appendChild(br);
nicholas@926 424 trackComment.appendChild(trackCommentBox);
nicholas@926 425 feedbackHolder.appendChild(trackComment);
nicholas@926 426 }
nicholas@926 427
nicholas@926 428 // Create a slider per track
nicholas@926 429
nicholas@926 430 var trackSliderObj = document.createElement('div');
nicholas@926 431 trackSliderObj.className = 'track-slider';
nicholas@926 432 trackSliderObj.id = 'track-slider-'+index;
nicholas@926 433 // Distribute it randomnly
nicholas@926 434 var w = window.innerWidth - (offset+8)*2;
nicholas@926 435 w = Math.random()*w;
nicholas@926 436 w = Math.floor(w+(offset+8));
nicholas@926 437 trackSliderObj.style.left = w+'px';
nicholas@926 438 trackSliderObj.innerHTML = '<span>'+index+'</span>';
nicholas@926 439 trackSliderObj.draggable = true;
nicholas@926 440 trackSliderObj.ondragend = dragEnd;
nicholas@926 441 trackSliderObj.ondragstart = function()
nicholas@926 442 {
nicholas@926 443 var id = Number(this.id.substr(13,2)); // Maximum theoretical tracks is 99!
nicholas@926 444 audioEngineContext.metric.sliderMoveStart(id);
nicholas@926 445 };
nicholas@926 446
nicholas@926 447 // Onclick, switch playback to that track
nicholas@926 448 trackSliderObj.onclick = function() {
nicholas@926 449 // Start the test on first click, that way timings are more accurate.
nicholas@926 450 audioEngineContext.play();
nicholas@926 451 if (audioEngineContext.audioObjectsReady) {
nicholas@926 452 // Cannot continue to issue play command until audioObjects reported as ready!
nicholas@926 453 // Get the track ID from the object ID
nicholas@926 454 var id = Number(this.id.substr(13,2)); // Maximum theoretical tracks is 99!
nicholas@926 455 //audioEngineContext.metric.sliderPlayed(id);
nicholas@926 456 audioEngineContext.selectedTrack(id);
nicholas@926 457 // Currently playing track red, rest green
nicholas@926 458 document.getElementById('track-slider-'+index).style.backgroundColor = "#FF0000";
nicholas@926 459 for (var i = 0; i<$(currentTrackOrder).length; i++)
nicholas@926 460 {
nicholas@926 461 if (i!=index) // Make all other sliders green
nicholas@926 462 {
nicholas@926 463 document.getElementById('track-slider-'+i).style.backgroundColor = "rgb(100,200,100)";
nicholas@926 464 }
nicholas@926 465
nicholas@926 466 }
nicholas@926 467 }
nicholas@926 468 };
nicholas@926 469
nicholas@926 470 canvas.appendChild(trackSliderObj);
nicholas@926 471 audioEngineContext.audioObjects[index].metric.initialised(convSliderPosToRate(index));
nicholas@926 472
nicholas@926 473 });
nicholas@926 474
nicholas@926 475 // Append any commentQuestion boxes
nicholas@926 476 var commentQuestions = $(textXML).find('CommentQuestion');
nicholas@926 477 $(commentQuestions).each(function(index,element) {
nicholas@926 478 // Create document objects to hold the comment boxes
nicholas@926 479 var trackComment = document.createElement('div');
nicholas@926 480 trackComment.className = 'comment-div commentQuestion';
nicholas@926 481 trackComment.id = element.attributes['id'].value;
nicholas@926 482 // Create a string next to each comment asking for a comment
nicholas@926 483 var trackString = document.createElement('span');
nicholas@926 484 trackString.innerHTML = element.textContent;
nicholas@926 485 // Create the HTML5 comment box 'textarea'
nicholas@926 486 var trackCommentBox = document.createElement('textarea');
nicholas@926 487 trackCommentBox.rows = '4';
nicholas@926 488 trackCommentBox.cols = '100';
nicholas@926 489 trackCommentBox.name = 'commentQuestion'+index;
nicholas@926 490 trackCommentBox.className = 'trackComment';
nicholas@926 491 var br = document.createElement('br');
nicholas@926 492 // Add to the holder.
nicholas@926 493 trackComment.appendChild(trackString);
nicholas@926 494 trackComment.appendChild(br);
nicholas@926 495 trackComment.appendChild(trackCommentBox);
nicholas@926 496 feedbackHolder.appendChild(trackComment);
nicholas@926 497 });
nicholas@926 498 }
nicholas@926 499
nicholas@926 500
nicholas@926 501 function dragEnd(ev) {
nicholas@926 502 // Function call when a div has been dropped
nicholas@926 503 var slider = document.getElementById('slider');
nicholas@926 504 var marginSize = Number(slider.attributes['marginsize'].value);
nicholas@926 505 var w = slider.style.width;
nicholas@926 506 w = Number(w.substr(0,w.length-2));
nicholas@926 507 var x = ev.x;
nicholas@926 508 if (x >= marginSize && x < w+marginSize) {
nicholas@926 509 this.style.left = (x)+'px';
nicholas@926 510 } else {
nicholas@926 511 if (x<marginSize) {
nicholas@926 512 this.style.left = marginSize+'px';
nicholas@926 513 } else {
nicholas@926 514 this.style.left = (w+marginSize) + 'px';
nicholas@926 515 }
nicholas@926 516 }
nicholas@926 517 audioEngineContext.metric.sliderMoved();
nicholas@926 518 }
nicholas@926 519
nicholas@926 520 function buttonSubmitClick() // TODO: Only when all songs have been played!
nicholas@926 521 {
nicholas@926 522 hasBeenPlayed = audioEngineContext.checkAllPlayed();
nicholas@926 523 if (hasBeenPlayed.length == 0) {
nicholas@926 524 if (audioEngineContext.status == 1) {
nicholas@926 525 var playback = document.getElementById('playback-button');
nicholas@926 526 playback.click();
nicholas@926 527 // This function is called when the submit button is clicked. Will check for any further tests to perform, or any post-test options
nicholas@926 528 } else
nicholas@926 529 {
nicholas@926 530 if (audioEngineContext.timer.testStarted == false)
nicholas@926 531 {
nicholas@926 532 alert('You have not started the test! Please press start to begin the test!');
nicholas@926 533 return;
nicholas@926 534 }
nicholas@926 535 }
nicholas@926 536 testState.advanceState();
nicholas@926 537 } else // if a fragment has not been played yet
nicholas@926 538 {
nicholas@926 539 str = "";
nicholas@926 540 if (hasBeenPlayed.length > 1) {
nicholas@926 541 for (var i=0; i<hasBeenPlayed.length; i++) {
nicholas@926 542 str = str + hasBeenPlayed[i];
nicholas@926 543 if (i < hasBeenPlayed.length-2){
nicholas@926 544 str += ", ";
nicholas@926 545 } else if (i == hasBeenPlayed.length-2) {
nicholas@926 546 str += " or ";
nicholas@926 547 }
nicholas@926 548 }
nicholas@926 549 alert('You have not played fragments ' + str + ' yet. Please listen, rate and comment all samples before submitting.');
nicholas@926 550 } else {
nicholas@926 551 alert('You have not played fragment ' + hasBeenPlayed[0] + ' yet. Please listen, rate and comment all samples before submitting.');
nicholas@926 552 }
nicholas@926 553 return;
nicholas@926 554 }
nicholas@926 555 }
nicholas@926 556
nicholas@926 557 function convSliderPosToRate(id)
nicholas@926 558 {
nicholas@926 559 var w = document.getElementById('slider').style.width;
nicholas@926 560 var marginsize = Number(document.getElementById('slider').attributes['marginsize'].value);
nicholas@926 561 var maxPix = w.substr(0,w.length-2);
nicholas@926 562 var slider = document.getElementsByClassName('track-slider')[id];
nicholas@926 563 var pix = slider.style.left;
nicholas@926 564 pix = pix.substr(0,pix.length-2);
nicholas@926 565 var rate = (pix-marginsize)/maxPix;
nicholas@926 566 return rate;
nicholas@926 567 }
nicholas@926 568
nicholas@926 569 function resizeWindow(event){
nicholas@926 570 // Function called when the window has been resized.
nicholas@926 571 // MANDATORY FUNCTION
nicholas@926 572
nicholas@926 573 // Store the slider marker values
nicholas@926 574 var holdValues = [];
nicholas@926 575 $(".track-slider").each(function(index,sliderObj){
nicholas@926 576 holdValues.push(convSliderPosToRate(index));
nicholas@926 577 });
nicholas@926 578
nicholas@926 579 var width = event.target.innerWidth;
nicholas@926 580 var canvas = document.getElementById('sliderCanvasHolder');
nicholas@926 581 var sliderDiv = canvas.children[0];
nicholas@926 582 var sliderScaleDiv = canvas.children[1];
nicholas@926 583 var marginsize = Number(sliderDiv.attributes['marginsize'].value);
nicholas@926 584 var w = (marginsize+8)*2;
nicholas@926 585 sliderDiv.style.width = width - w + 'px';
nicholas@926 586 var width = width - w;
nicholas@926 587 // Move sliders into new position
nicholas@926 588 $(".track-slider").each(function(index,sliderObj){
nicholas@926 589 var pos = holdValues[index];
nicholas@926 590 var pix = pos * width;
nicholas@926 591 sliderObj.style.left = pix+marginsize+'px';
nicholas@926 592 });
nicholas@926 593
nicholas@926 594 // Move scale labels
nicholas@926 595 $(sliderScaleDiv.children).each(function(index,scaleObj){
nicholas@926 596 var position = Number(scaleObj.attributes['value'].value);
nicholas@926 597 var pixelPosition = (position*width)+marginsize;
nicholas@926 598 scaleObj.style.left = Math.floor((pixelPosition-($(scaleObj).width()/2)))+'px';
nicholas@926 599 });
nicholas@926 600 }
nicholas@926 601
nicholas@926 602 function pageXMLSave(store, testXML, testId)
nicholas@926 603 {
nicholas@926 604 // Saves a specific test page
nicholas@926 605 var xmlDoc = store;
nicholas@926 606 // Check if any session wide metrics are enabled
nicholas@926 607
nicholas@926 608 var commentShow = testXML.attributes['elementComments'];
nicholas@926 609 if (commentShow != undefined) {
nicholas@926 610 if (commentShow.value == 'false') {commentShow = false;}
nicholas@926 611 else {commentShow = true;}
nicholas@926 612 } else {commentShow = true;}
nicholas@926 613
nicholas@926 614 var metric = document.createElement('metric');
nicholas@926 615 if (audioEngineContext.metric.enableTestTimer)
nicholas@926 616 {
nicholas@926 617 var testTime = document.createElement('metricResult');
nicholas@926 618 testTime.id = 'testTime';
nicholas@926 619 testTime.textContent = audioEngineContext.timer.testDuration;
nicholas@926 620 metric.appendChild(testTime);
nicholas@926 621 }
nicholas@926 622 xmlDoc.appendChild(metric);
nicholas@926 623 var trackSliderObjects = document.getElementsByClassName('track-slider');
nicholas@926 624 var commentObjects = document.getElementsByClassName('comment-div');
nicholas@926 625 for (var i=0; i<trackSliderObjects.length; i++)
nicholas@926 626 {
nicholas@926 627 var audioElement = document.createElement('audioElement');
nicholas@926 628 audioElement.id = currentTrackOrder[i].attributes['id'].value;
nicholas@926 629 audioElement.url = currentTrackOrder[i].attributes['url'].value;
nicholas@926 630 var value = document.createElement('value');
nicholas@926 631 value.innerHTML = convSliderPosToRate(i);
nicholas@926 632 if (commentShow) {
nicholas@926 633 var comment = document.createElement("comment");
nicholas@926 634 var question = document.createElement("question");
nicholas@926 635 var response = document.createElement("response");
nicholas@926 636 question.textContent = commentObjects[i].children[0].textContent;
nicholas@926 637 response.textContent = commentObjects[i].children[2].value;
nicholas@926 638 console.log('Comment ' + i + ': ' + commentObjects[i].children[2].value); // DEBUG/SAFETY
nicholas@926 639 comment.appendChild(question);
nicholas@926 640 comment.appendChild(response);
nicholas@926 641 audioElement.appendChild(comment);
nicholas@926 642 }
nicholas@926 643 audioElement.appendChild(value);
nicholas@926 644 // Check for any per element metrics
nicholas@926 645 var metric = document.createElement('metric');
nicholas@926 646 var elementMetric = audioEngineContext.audioObjects[i].metric;
nicholas@926 647 if (audioEngineContext.metric.enableElementTimer) {
nicholas@926 648 var elementTimer = document.createElement('metricResult');
nicholas@926 649 elementTimer.id = 'elementTimer';
nicholas@926 650 elementTimer.textContent = elementMetric.listenedTimer;
nicholas@926 651 metric.appendChild(elementTimer);
nicholas@926 652 }
nicholas@926 653 if (audioEngineContext.metric.enableElementTracker) {
nicholas@926 654 var elementTrackerFull = document.createElement('metricResult');
nicholas@926 655 elementTrackerFull.id = 'elementTrackerFull';
nicholas@926 656 var data = elementMetric.movementTracker;
nicholas@926 657 for (var k=0; k<data.length; k++)
nicholas@926 658 {
nicholas@926 659 var timePos = document.createElement('timePos');
nicholas@926 660 timePos.id = k;
nicholas@926 661 var time = document.createElement('time');
nicholas@926 662 time.textContent = data[k][0];
nicholas@926 663 var position = document.createElement('position');
nicholas@926 664 position.textContent = data[k][1];
nicholas@926 665 timePos.appendChild(time);
nicholas@926 666 timePos.appendChild(position);
nicholas@926 667 elementTrackerFull.appendChild(timePos);
nicholas@926 668 }
nicholas@926 669 metric.appendChild(elementTrackerFull);
nicholas@926 670 }
nicholas@926 671 if (audioEngineContext.metric.enableElementInitialPosition) {
nicholas@926 672 var elementInitial = document.createElement('metricResult');
nicholas@926 673 elementInitial.id = 'elementInitialPosition';
nicholas@926 674 elementInitial.textContent = elementMetric.initialPosition;
nicholas@926 675 metric.appendChild(elementInitial);
nicholas@926 676 }
nicholas@926 677 if (audioEngineContext.metric.enableFlagListenedTo) {
nicholas@926 678 var flagListenedTo = document.createElement('metricResult');
nicholas@926 679 flagListenedTo.id = 'elementFlagListenedTo';
nicholas@926 680 flagListenedTo.textContent = elementMetric.wasListenedTo;
nicholas@926 681 metric.appendChild(flagListenedTo);
nicholas@926 682 }
nicholas@926 683 if (audioEngineContext.metric.enableFlagMoved) {
nicholas@926 684 var flagMoved = document.createElement('metricResult');
nicholas@926 685 flagMoved.id = 'elementFlagMoved';
nicholas@926 686 flagMoved.textContent = elementMetric.wasMoved;
nicholas@926 687 metric.appendChild(flagMoved);
nicholas@926 688 }
nicholas@926 689 if (audioEngineContext.metric.enableFlagComments) {
nicholas@926 690 var flagComments = document.createElement('metricResult');
nicholas@926 691 flagComments.id = 'elementFlagComments';
nicholas@926 692 if (response.textContent.length == 0) {flag.textContent = 'false';}
nicholas@926 693 else {flag.textContet = 'true';}
nicholas@926 694 metric.appendChild(flagComments);
nicholas@926 695 }
nicholas@926 696 audioElement.appendChild(metric);
nicholas@926 697 xmlDoc.appendChild(audioElement);
nicholas@926 698 }
nicholas@926 699 var commentQuestion = document.getElementsByClassName('commentQuestion');
nicholas@926 700 for (var i=0; i<commentQuestion.length; i++)
nicholas@926 701 {
nicholas@926 702 var cqHolder = document.createElement('CommentQuestion');
nicholas@926 703 var comment = document.createElement('comment');
nicholas@926 704 var question = document.createElement('question');
nicholas@926 705 cqHolder.id = commentQuestion[i].id;
nicholas@926 706 comment.textContent = commentQuestion[i].children[2].value;
nicholas@926 707 question.textContent = commentQuestion[i].children[0].textContent;
nicholas@926 708 console.log('Question ' + i + ': ' + commentQuestion[i].children[2].value); // DEBUG/SAFETY
nicholas@926 709 cqHolder.appendChild(question);
nicholas@926 710 cqHolder.appendChild(comment);
nicholas@926 711 xmlDoc.appendChild(cqHolder);
nicholas@926 712 }
nicholas@926 713 store = xmlDoc;
nicholas@926 714 }