annotate ape.js @ 129:0e13112d8501

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