annotate interfaces/AB.js @ 1116:c44fbf72f7f2

All interfaces support comment boxes. Comment box identification matches presented tag (for instance, AB will be Comment on fragment A, rather than 1). Tighter buffer loading protocol, audioObjects register with the buffer rather than checking for buffer existence (which can be buggy depending on the buffer state). Buffers now have a state to ensure exact location in loading chain (downloading, decoding, LUFS, ready).
author Nicholas Jillings <n.g.r.jillings@se14.qmul.ac.uk>
date Fri, 29 Jan 2016 11:11:57 +0000
parents
children c0022a09c4f6
rev   line source
n@1116 1 // Once this is loaded and parsed, begin execution
n@1116 2 loadInterface();
n@1116 3
n@1116 4 function loadInterface() {
n@1116 5 // Get the dimensions of the screen available to the page
n@1116 6 var width = window.innerWidth;
n@1116 7 var height = window.innerHeight;
n@1116 8 interfaceContext.insertPoint.innerHTML = null; // Clear the current schema
n@1116 9
n@1116 10 // Custom Comparitor Object
n@1116 11 Interface.prototype.comparitor = null;
n@1116 12
n@1116 13 // The injection point into the HTML page
n@1116 14 interfaceContext.insertPoint = document.getElementById("topLevelBody");
n@1116 15 var testContent = document.createElement('div');
n@1116 16 testContent.id = 'testContent';
n@1116 17
n@1116 18 // Create the top div for the Title element
n@1116 19 var titleAttr = specification.title;
n@1116 20 var title = document.createElement('div');
n@1116 21 title.className = "title";
n@1116 22 title.align = "center";
n@1116 23 var titleSpan = document.createElement('span');
n@1116 24
n@1116 25 // Set title to that defined in XML, else set to default
n@1116 26 if (titleAttr != undefined) {
n@1116 27 titleSpan.textContent = titleAttr;
n@1116 28 } else {
n@1116 29 titleSpan.textContent = 'Listening test';
n@1116 30 }
n@1116 31 // Insert the titleSpan element into the title div element.
n@1116 32 title.appendChild(titleSpan);
n@1116 33
n@1116 34 var pagetitle = document.createElement('div');
n@1116 35 pagetitle.className = "pageTitle";
n@1116 36 pagetitle.align = "center";
n@1116 37 var titleSpan = document.createElement('span');
n@1116 38 titleSpan.id = "pageTitle";
n@1116 39 pagetitle.appendChild(titleSpan);
n@1116 40
n@1116 41 // Create Interface buttons!
n@1116 42 var interfaceButtons = document.createElement('div');
n@1116 43 interfaceButtons.id = 'interface-buttons';
n@1116 44 interfaceButtons.style.height = '25px';
n@1116 45
n@1116 46 // Create playback start/stop points
n@1116 47 var playback = document.createElement("button");
n@1116 48 playback.innerHTML = 'Stop';
n@1116 49 playback.id = 'playback-button';
n@1116 50 playback.style.float = 'left';
n@1116 51 // onclick function. Check if it is playing or not, call the correct function in the
n@1116 52 // audioEngine, change the button text to reflect the next state.
n@1116 53 playback.onclick = function() {
n@1116 54 if (audioEngineContext.status == 1) {
n@1116 55 audioEngineContext.stop();
n@1116 56 this.innerHTML = 'Stop';
n@1116 57 var time = audioEngineContext.timer.getTestTime();
n@1116 58 console.log('Stopped at ' + time); // DEBUG/SAFETY
n@1116 59 }
n@1116 60 };
n@1116 61 // Append the interface buttons into the interfaceButtons object.
n@1116 62 interfaceButtons.appendChild(playback);
n@1116 63
n@1116 64 // Global parent for the comment boxes on the page
n@1116 65 var feedbackHolder = document.createElement('div');
n@1116 66 feedbackHolder.id = 'feedbackHolder';
n@1116 67
n@1116 68 // Construct the AB Boxes
n@1116 69 var boxes = document.createElement('div');
n@1116 70 boxes.align = "center";
n@1116 71 boxes.id = "box-holders";
n@1116 72 boxes.style.float = "left";
n@1116 73
n@1116 74 var submit = document.createElement('button');
n@1116 75 submit.id = "submit";
n@1116 76 submit.onclick = buttonSubmitClick;
n@1116 77 submit.className = "big-button";
n@1116 78 submit.textContent = "submit";
n@1116 79 submit.style.position = "relative";
n@1116 80 submit.style.left = (window.innerWidth-250)/2 + 'px';
n@1116 81
n@1116 82 feedbackHolder.appendChild(boxes);
n@1116 83
n@1116 84 // Inject into HTML
n@1116 85 testContent.appendChild(title); // Insert the title
n@1116 86 testContent.appendChild(pagetitle);
n@1116 87 testContent.appendChild(interfaceButtons);
n@1116 88 testContent.appendChild(feedbackHolder);
n@1116 89 testContent.appendChild(submit);
n@1116 90 interfaceContext.insertPoint.appendChild(testContent);
n@1116 91
n@1116 92 // Load the full interface
n@1116 93 testState.initialise();
n@1116 94 testState.advanceState();
n@1116 95 }
n@1116 96
n@1116 97 function loadTest(audioHolderObject)
n@1116 98 {
n@1116 99 var feedbackHolder = document.getElementById('feedbackHolder');
n@1116 100 var interfaceObj = audioHolderObject.interfaces;
n@1116 101 if (interfaceObj.length > 1)
n@1116 102 {
n@1116 103 console.log("WARNING - This interface only supports one <interface> node per page. Using first interface node");
n@1116 104 }
n@1116 105 interfaceObj = interfaceObj[0];
n@1116 106
n@1116 107 if(interfaceObj.title != null)
n@1116 108 {
n@1116 109 document.getElementById("pageTitle").textContent = interfaceObj.title;
n@1116 110 }
n@1116 111
n@1116 112 var interfaceOptions = specification.interfaces.options.concat(interfaceObj.options);
n@1116 113 for (var option of interfaceOptions)
n@1116 114 {
n@1116 115 if (option.type == "show")
n@1116 116 {
n@1116 117 switch(option.name) {
n@1116 118 case "playhead":
n@1116 119 var playbackHolder = document.getElementById('playback-holder');
n@1116 120 if (playbackHolder == null)
n@1116 121 {
n@1116 122 playbackHolder = document.createElement('div');
n@1116 123 playbackHolder.style.width = "100%";
n@1116 124 playbackHolder.style.float = "left";
n@1116 125 playbackHolder.align = 'center';
n@1116 126 playbackHolder.appendChild(interfaceContext.playhead.object);
n@1116 127 feedbackHolder.appendChild(playbackHolder);
n@1116 128 }
n@1116 129 break;
n@1116 130 case "page-count":
n@1116 131 var pagecountHolder = document.getElementById('page-count');
n@1116 132 if (pagecountHolder == null)
n@1116 133 {
n@1116 134 pagecountHolder = document.createElement('div');
n@1116 135 pagecountHolder.id = 'page-count';
n@1116 136 }
n@1116 137 pagecountHolder.innerHTML = '<span>Page '+(audioHolderObject.presentedId+1)+' of '+specification.pages.length+'</span>';
n@1116 138 var inject = document.getElementById('interface-buttons');
n@1116 139 inject.appendChild(pagecountHolder);
n@1116 140 break;
n@1116 141 case "volume":
n@1116 142 if (document.getElementById('master-volume-holder') == null)
n@1116 143 {
n@1116 144 feedbackHolder.appendChild(interfaceContext.volume.object);
n@1116 145 }
n@1116 146 break;
n@1116 147 }
n@1116 148 }
n@1116 149 }
n@1116 150
n@1116 151 // Populate the comparitor object
n@1116 152 interfaceContext.comparitor = new Comparitor(audioHolderObject);
n@1116 153 if (audioHolderObject.showElementComments)
n@1116 154 {
n@1116 155 var commentHolder = document.createElement('div');
n@1116 156 commentHolder.id = 'commentHolder';
n@1116 157 document.getElementById('testContent').appendChild(commentHolder);
n@1116 158 // Generate one comment box per presented page
n@1116 159 for (var element of audioEngineContext.audioObjects)
n@1116 160 {
n@1116 161 interfaceContext.createCommentBox(element);
n@1116 162 }
n@1116 163 interfaceContext.showCommentBoxes(commentHolder,true);
n@1116 164 }
n@1116 165 resizeWindow(null);
n@1116 166 }
n@1116 167
n@1116 168 function Comparitor(audioHolderObject)
n@1116 169 {
n@1116 170 this.comparitorBox = function(audioElement,id,text)
n@1116 171 {
n@1116 172 this.parent = audioElement;
n@1116 173 this.id = id;
n@1116 174 this.value = 0;
n@1116 175 this.disabled = true;
n@1116 176 this.box = document.createElement('div');
n@1116 177 this.box.className = 'comparitor-holder';
n@1116 178 this.box.setAttribute('track-id',audioElement.id);
n@1116 179 this.box.id = 'comparitor-'+text;
n@1116 180 this.selector = document.createElement('div');
n@1116 181 this.selector.className = 'comparitor-selector disabled';
n@1116 182 var selectorText = document.createElement('span');
n@1116 183 selectorText.textContent = text;
n@1116 184 this.selector.appendChild(selectorText);
n@1116 185 this.playback = document.createElement('button');
n@1116 186 this.playback.className = 'comparitor-button';
n@1116 187 this.playback.disabled = true;
n@1116 188 this.playback.textContent = "Listen";
n@1116 189 this.box.appendChild(this.selector);
n@1116 190 this.box.appendChild(this.playback);
n@1116 191 this.selector.onclick = function(event)
n@1116 192 {
n@1116 193 var time = audioEngineContext.timer.getTestTime();
n@1116 194 if ($(event.currentTarget).hasClass('disabled'))
n@1116 195 {
n@1116 196 console.log("Please wait until sample has loaded");
n@1116 197 return;
n@1116 198 }
n@1116 199 if (audioEngineContext.status == 0)
n@1116 200 {
n@1116 201 alert("Please listen to the samples before making a selection");
n@1116 202 console.log("Please listen to the samples before making a selection");
n@1116 203 return;
n@1116 204 }
n@1116 205 $(".comparitor-selector").removeClass('selected');
n@1116 206 var id = event.currentTarget.parentElement.getAttribute('track-id');
n@1116 207 interfaceContext.comparitor.selected = id;
n@1116 208 $(event.currentTarget).addClass('selected');
n@1116 209 for (var i=0; i<interfaceContext.comparitor.comparitors.length; i++)
n@1116 210 {
n@1116 211 var obj = interfaceContext.comparitor.comparitors[i];
n@1116 212 if (i == id) {
n@1116 213 obj.value = 1;
n@1116 214 } else {
n@1116 215 obj.value = 0;
n@1116 216 }
n@1116 217 obj.parent.metric.moved(time,obj.value);
n@1116 218 }
n@1116 219 console.log("Selected "+id+' ('+time+')');
n@1116 220 };
n@1116 221 this.playback.onclick = function(event)
n@1116 222 {
n@1116 223 var id = event.currentTarget.parentElement.getAttribute('track-id');
n@1116 224 audioEngineContext.play(id);
n@1116 225 };
n@1116 226
n@1116 227 this.enable = function()
n@1116 228 {
n@1116 229 if (this.parent.state == 1)
n@1116 230 {
n@1116 231 $(this.selector).removeClass('disabled');
n@1116 232 this.playback.disabled = false;
n@1116 233 }
n@1116 234 };
n@1116 235 this.updateLoading = function(progress)
n@1116 236 {
n@1116 237 if (progress != 100)
n@1116 238 {
n@1116 239 progress = String(progress);
n@1116 240 progress = progress.split('.')[0];
n@1116 241 this.playback.textContent = progress+'%';
n@1116 242 } else {
n@1116 243 this.playback.textContent = "Listen";
n@1116 244 }
n@1116 245 };
n@1116 246 this.startPlayback = function()
n@1116 247 {
n@1116 248 $('.comparitor-button').text('Listen');
n@1116 249 $(this.playback).text('Playing');
n@1116 250 };
n@1116 251 this.stopPlayback = function()
n@1116 252 {
n@1116 253 $(this.playback).text('Listen');
n@1116 254 };
n@1116 255 this.exportXMLDOM = function(audioObject)
n@1116 256 {
n@1116 257 var node = storage.document.createElement('value');
n@1116 258 node.textContent = this.value;
n@1116 259 return node;
n@1116 260 };
n@1116 261 this.getValue = function() {
n@1116 262 return this.value;
n@1116 263 };
n@1116 264 this.getPresentedId = function()
n@1116 265 {
n@1116 266 return this.selector.children[0].textContent;
n@1116 267 };
n@1116 268 this.canMove = function()
n@1116 269 {
n@1116 270 return false;
n@1116 271 };
n@1116 272 };
n@1116 273
n@1116 274 this.boxHolders = document.getElementById('box-holders');
n@1116 275 this.boxHolders.innerHTML = null;
n@1116 276 this.comparitors = [];
n@1116 277 this.selected = null;
n@1116 278
n@1116 279 // First generate the Audio Objects for the Audio Engine
n@1116 280 for (var index=0; index<audioHolderObject.audioElements.length; index++)
n@1116 281 {
n@1116 282 var element = audioHolderObject.audioElements[index];
n@1116 283 if (index == audioHolderObject.outsideReference || element.type == 'outside-reference')
n@1116 284 {
n@1116 285 console.log("WARNING - AB cannot have fixed reference");
n@1116 286 }
n@1116 287 var audioObject = audioEngineContext.newTrack(element);
n@1116 288 var node = new this.comparitorBox(audioObject,index,String.fromCharCode(65 + index));
n@1116 289 audioObject.bindInterface(node);
n@1116 290 this.comparitors.push(node);
n@1116 291 this.boxHolders.appendChild(node.box);
n@1116 292 }
n@1116 293 return this;
n@1116 294 }
n@1116 295
n@1116 296 function resizeWindow(event)
n@1116 297 {
n@1116 298 document.getElementById('submit').style.left = (window.innerWidth-250)/2 + 'px';
n@1116 299 var numObj = interfaceContext.comparitor.comparitors.length;
n@1116 300 var boxW = numObj*312;
n@1116 301 var diff = window.innerWidth - boxW;
n@1116 302 while (diff < 0)
n@1116 303 {
n@1116 304 numObj = Math.ceil(numObj/2);
n@1116 305 boxW = numObj*312;
n@1116 306 diff = window.innerWidth - boxW;
n@1116 307 }
n@1116 308 document.getElementById('box-holders').style.marginLeft = diff/2 + 'px';
n@1116 309 document.getElementById('box-holders').style.marginRight = diff/2 + 'px';
n@1116 310 document.getElementById('box-holders').style.width = boxW + 'px';
n@1116 311 }
n@1116 312
n@1116 313 function buttonSubmitClick()
n@1116 314 {
n@1116 315 var checks = [];
n@1116 316 checks = checks.concat(testState.currentStateMap.interfaces[0].options);
n@1116 317 checks = checks.concat(specification.interfaces.options);
n@1116 318 var canContinue = true;
n@1116 319
n@1116 320 for (var i=0; i<checks.length; i++) {
n@1116 321 if (checks[i].type == 'check')
n@1116 322 {
n@1116 323 switch(checks[i].name) {
n@1116 324 case 'fragmentPlayed':
n@1116 325 // Check if all fragments have been played
n@1116 326 var checkState = interfaceContext.checkAllPlayed();
n@1116 327 if (checkState == false) {canContinue = false;}
n@1116 328 break;
n@1116 329 case 'fragmentFullPlayback':
n@1116 330 // Check all fragments have been played to their full length
n@1116 331 var checkState = interfaceContext.checkFragmentsFullyPlayed();
n@1116 332 if (checkState == false) {canContinue = false;}
n@1116 333 break;
n@1116 334 case 'fragmentMoved':
n@1116 335 // Check all fragment sliders have been moved.
n@1116 336 var checkState = interfaceContext.checkAllMoved();
n@1116 337 if (checkState == false) {canContinue = false;}
n@1116 338 break;
n@1116 339 case 'fragmentComments':
n@1116 340 // Check all fragment sliders have been moved.
n@1116 341 var checkState = interfaceContext.checkAllCommented();
n@1116 342 if (checkState == false) {canContinue = false;}
n@1116 343 break;
n@1116 344 default:
n@1116 345 console.log("WARNING - Check option "+checks[i].check+" is not supported on this interface");
n@1116 346 break;
n@1116 347 }
n@1116 348
n@1116 349 }
n@1116 350 if (!canContinue) {break;}
n@1116 351 }
n@1116 352 if (canContinue)
n@1116 353 {
n@1116 354 if (audioEngineContext.status == 1) {
n@1116 355 var playback = document.getElementById('playback-button');
n@1116 356 playback.click();
n@1116 357 // This function is called when the submit button is clicked. Will check for any further tests to perform, or any post-test options
n@1116 358 } else
n@1116 359 {
n@1116 360 if (audioEngineContext.timer.testStarted == false)
n@1116 361 {
n@1116 362 alert('You have not started the test! Please press start to begin the test!');
n@1116 363 return;
n@1116 364 }
n@1116 365 }
n@1116 366 testState.advanceState();
n@1116 367 }
n@1116 368 }
n@1116 369
n@1116 370 function pageXMLSave(store, pageSpecification)
n@1116 371 {
n@1116 372 // MANDATORY
n@1116 373 // Saves a specific test page
n@1116 374 // You can use this space to add any extra nodes to your XML <audioHolder> saves
n@1116 375 // Get the current <page> information in store (remember to appendChild your data to it)
n@1116 376 // pageSpecification is the current page node configuration
n@1116 377 // To create new XML nodes, use storage.document.createElement();
n@1116 378 }