annotate interfaces/ape.js @ 1103:2051868b21f0

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