annotate ape.js @ 51:cada56696a15 Dev_main

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