annotate ape.js @ 964:9c09cb530ec1

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