annotate interfaces/AB.js @ 1316:279930a008ca

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