comparison interfaces/ABX.js @ 2695:211364181d16

JSHint ABX (#180)
author Nicholas Jillings <n.g.r.jillings@se14.qmul.ac.uk>
date Sat, 11 Mar 2017 18:38:12 +0000
parents 22efb2d04bc9
children 536cb44c7292
comparison
equal deleted inserted replaced
2694:1ccc083552d5 2695:211364181d16
2 * WAET Blank Template 2 * WAET Blank Template
3 * Use this to start building your custom interface 3 * Use this to start building your custom interface
4 */ 4 */
5 5
6 // Once this is loaded and parsed, begin execution 6 // Once this is loaded and parsed, begin execution
7 /* globals interfaceContext, Interface, testState, audioEngineContext, console, document, window, feedbackHolder, $, specification, storage*/
7 loadInterface(); 8 loadInterface();
8 9
9 function loadInterface() { 10 function loadInterface() {
10 // Use this to do any one-time page / element construction. For instance, placing any stationary text objects, 11 // Use this to do any one-time page / element construction. For instance, placing any stationary text objects,
11 // holding div's, or setting up any nodes which are present for the entire test sequence 12 // holding div's, or setting up any nodes which are present for the entire test sequence
27 if (rank > maxRanking) { 28 if (rank > maxRanking) {
28 maxRanking = rank; 29 maxRanking = rank;
29 } 30 }
30 } 31 }
31 if (maxRanking * 100 < max) { 32 if (maxRanking * 100 < max) {
32 str += "At least one fragment must be selected." 33 str += "At least one fragment must be selected.";
33 state = false; 34 state = false;
34 } 35 }
35 if (!state) { 36 if (!state) {
36 console.log(str); 37 console.log(str);
37 this.storeErrorNode(str); 38 this.storeErrorNode(str);
38 interfaceContext.lightbox.post("Message", str); 39 interfaceContext.lightbox.post("Message", str);
39 } 40 }
40 return state; 41 return state;
41 } 42 };
42 43
43 // Custom comparator Object 44 // Custom comparator Object
44 Interface.prototype.comparator = null; 45 Interface.prototype.comparator = null;
45 46
46 // The injection point into the HTML page 47 // The injection point into the HTML page
55 title.align = "center"; 56 title.align = "center";
56 var titleSpan = document.createElement('span'); 57 var titleSpan = document.createElement('span');
57 titleSpan.id = "test-title"; 58 titleSpan.id = "test-title";
58 59
59 // Set title to that defined in XML, else set to default 60 // Set title to that defined in XML, else set to default
60 if (titleAttr != undefined) { 61 if (titleAttr !== undefined) {
61 titleSpan.textContent = titleAttr; 62 titleSpan.textContent = titleAttr;
62 } else { 63 } else {
63 titleSpan.textContent = 'Listening test'; 64 titleSpan.textContent = 'Listening test';
64 } 65 }
65 // Insert the titleSpan element into the title div element. 66 // Insert the titleSpan element into the title div element.
66 title.appendChild(titleSpan); 67 title.appendChild(titleSpan);
67 68
68 var pagetitle = document.createElement('div'); 69 var pagetitle = document.createElement('div');
69 pagetitle.className = "pageTitle"; 70 pagetitle.className = "pageTitle";
70 pagetitle.align = "center"; 71 pagetitle.align = "center";
71 var titleSpan = document.createElement('span'); 72
73 titleSpan = document.createElement('span');
72 titleSpan.id = "pageTitle"; 74 titleSpan.id = "pageTitle";
73 pagetitle.appendChild(titleSpan); 75 pagetitle.appendChild(titleSpan);
74 76
75 // Create Interface buttons! 77 // Create Interface buttons!
76 var interfaceButtons = document.createElement('div'); 78 var interfaceButtons = document.createElement('div');
129 interfaceContext.insertPoint.appendChild(testContent); 131 interfaceContext.insertPoint.appendChild(testContent);
130 132
131 // Load the full interface 133 // Load the full interface
132 testState.initialise(); 134 testState.initialise();
133 testState.advanceState(); 135 testState.advanceState();
134 }; 136 }
135 137
136 function loadTest(page) { 138 function loadTest(page) {
137 // Called each time a new test page is to be build. The page specification node is the only item passed in 139 // Called each time a new test page is to be build. The page specification node is the only item passed in
138 document.getElementById('box-holders').innerHTML = ""; 140 document.getElementById('box-holders').innerHTML = "";
139 141
146 var commentHolder = document.getElementById("comment-box-holder"); 148 var commentHolder = document.getElementById("comment-box-holder");
147 commentHolder.innerHTML = ""; 149 commentHolder.innerHTML = "";
148 150
149 // Set the page title 151 // Set the page title
150 if (typeof page.title == "string" && page.title.length > 0) { 152 if (typeof page.title == "string" && page.title.length > 0) {
151 document.getElementById("test-title").textContent = page.title 153 document.getElementById("test-title").textContent = page.title;
152 } 154 }
153 155
154 if (interfaceObj.title != null) { 156 if (interfaceObj.title !== null) {
155 document.getElementById("pageTitle").textContent = interfaceObj.title; 157 document.getElementById("pageTitle").textContent = interfaceObj.title;
156 } 158 }
157 159
158 interfaceContext.comparator = new comparator(page); 160 interfaceContext.comparator = new comparator(page);
159 161
161 for (var option of interfaceOptions) { 163 for (var option of interfaceOptions) {
162 if (option.type == "show") { 164 if (option.type == "show") {
163 switch (option.name) { 165 switch (option.name) {
164 case "playhead": 166 case "playhead":
165 var playbackHolder = document.getElementById('playback-holder'); 167 var playbackHolder = document.getElementById('playback-holder');
166 if (playbackHolder == null) { 168 if (playbackHolder === null) {
167 playbackHolder = document.createElement('div'); 169 playbackHolder = document.createElement('div');
168 playbackHolder.style.width = "100%"; 170 playbackHolder.style.width = "100%";
169 playbackHolder.style.float = "left"; 171 playbackHolder.style.float = "left";
170 playbackHolder.align = 'center'; 172 playbackHolder.align = 'center';
171 playbackHolder.appendChild(interfaceContext.playhead.object); 173 playbackHolder.appendChild(interfaceContext.playhead.object);
172 feedbackHolder.appendChild(playbackHolder); 174 feedbackHolder.appendChild(playbackHolder);
173 } 175 }
174 break; 176 break;
175 case "page-count": 177 case "page-count":
176 var pagecountHolder = document.getElementById('page-count'); 178 var pagecountHolder = document.getElementById('page-count');
177 if (pagecountHolder == null) { 179 if (pagecountHolder === null) {
178 pagecountHolder = document.createElement('div'); 180 pagecountHolder = document.createElement('div');
179 pagecountHolder.id = 'page-count'; 181 pagecountHolder.id = 'page-count';
180 } 182 }
181 pagecountHolder.innerHTML = '<span>Page ' + (testState.stateIndex + 1) + ' of ' + testState.stateMap.length + '</span>'; 183 pagecountHolder.innerHTML = '<span>Page ' + (testState.stateIndex + 1) + ' of ' + testState.stateMap.length + '</span>';
182 var inject = document.getElementById('interface-buttons'); 184 var inject = document.getElementById('interface-buttons');
183 inject.appendChild(pagecountHolder); 185 inject.appendChild(pagecountHolder);
184 break; 186 break;
185 case "volume": 187 case "volume":
186 if (document.getElementById('master-volume-holder') == null) { 188 if (document.getElementById('master-volume-holder') === null) {
187 feedbackHolder.appendChild(interfaceContext.volume.object); 189 feedbackHolder.appendChild(interfaceContext.volume.object);
188 } 190 }
189 break; 191 break;
190 case "comments": 192 case "comments":
191 // Generate one comment box per presented page 193 // Generate one comment box per presented page
229 this.playback.className = 'comparator-button'; 231 this.playback.className = 'comparator-button';
230 this.playback.disabled = true; 232 this.playback.disabled = true;
231 this.playback.textContent = "Listen"; 233 this.playback.textContent = "Listen";
232 this.box.appendChild(this.selector); 234 this.box.appendChild(this.selector);
233 this.box.appendChild(this.playback); 235 this.box.appendChild(this.playback);
234 this.selector.onclick = function (event) { 236 this.selectorClicked = function (event) {
235 var label = event.currentTarget.children[0].textContent;
236 if (label == "X" || label == "x") { 237 if (label == "X" || label == "x") {
237 return; 238 return;
238 } 239 }
239 var time = audioEngineContext.timer.getTestTime(); 240 var time = audioEngineContext.timer.getTestTime();
240 if ($(event.currentTarget).hasClass('disabled')) { 241 if (this.disabled) {
242 interfaceContext.lightbox.post("Message", "Please wait until sample has loaded");
241 console.log("Please wait until sample has loaded"); 243 console.log("Please wait until sample has loaded");
242 return; 244 return;
243 } 245 }
244 if (audioEngineContext.status == 0) { 246 if (audioEngineContext.status === 0) {
245 interfaceContext.lightbox.post("Message", "Please listen to the samples before making a selection"); 247 interfaceContext.lightbox.post("Message", "Please listen to the samples before making a selection");
246 console.log("Please listen to the samples before making a selection"); 248 console.log("Please listen to the samples before making a selection");
247 return; 249 return;
248 } 250 }
249 var id = event.currentTarget.parentElement.getAttribute('track-id'); 251 interfaceContext.comparator.selected = this.id;
250 interfaceContext.comparator.selected = id; 252 $(".comparator-selector").removeClass('selected');
251 if ($(event.currentTarget).hasClass("selected")) { 253 $(this.selector).addClass('selected');
252 $(".comparator-selector").removeClass('selected'); 254 interfaceContext.comparator.pair.forEach(function (obj) {
253 for (var i = 0; i < interfaceContext.comparator.pair.length; i++) { 255 obj.value = 1.0 * obj === this;
254 var obj = interfaceContext.comparator.pair[i]; 256 obj.parent.metric.moved(time, obj.value);
255 obj.parent.metric.moved(time, 0); 257 });
256 obj.value = 0; 258 console.log("Selected " + this.id + ' (' + time + ')');
257 }
258 } else {
259 $(".comparator-selector").removeClass('selected');
260 $(event.currentTarget).addClass('selected');
261 for (var i = 0; i < interfaceContext.comparator.pair.length; i++) {
262 var obj = interfaceContext.comparator.pair[i];
263 if (i == id) {
264 obj.value = 1;
265 } else {
266 obj.value = 0;
267 }
268 obj.parent.metric.moved(time, obj.value);
269 }
270 console.log("Selected " + id + ' (' + time + ')');
271 }
272 }; 259 };
273 this.playback.setAttribute("playstate", "ready"); 260 this.playback.setAttribute("playstate", "ready");
274 this.playback.onclick = function (event) { 261 this.playbackClicked = function (event) {
275 var id = event.currentTarget.parentElement.getAttribute('track-id'); 262 if (this.playback.getAttribute("playstate") == "ready") {
276 if (event.currentTarget.getAttribute("playstate") == "ready") { 263 audioEngineContext.play(this.id);
277 audioEngineContext.play(id);
278 } else if (event.currentTarget.getAttribute("playstate") == "playing") { 264 } else if (event.currentTarget.getAttribute("playstate") == "playing") {
279 audioEngineContext.stop(); 265 audioEngineContext.stop();
280 } 266 }
281 267
282 }; 268 };
269 this.handleEvent = function (event) {
270 if (event.currentTarget === this.playback) {
271 this.playbackClicked(event);
272 } else if (event.currentTarget === this.selector) {
273 this.selectorClicked(event);
274 }
275 };
276 this.playback.addEventListener("click", this);
277 this.selector.addEventListener("click", this);
283 this.enable = function () { 278 this.enable = function () {
284 // This is used to tell the interface object that playback of this node is ready 279 // This is used to tell the interface object that playback of this node is ready
285 if (this.parent.state == 1) { 280 if (this.parent.state == 1) {
286 $(this.selector).removeClass('disabled'); 281 $(this.selector).removeClass('disabled');
287 this.playback.disabled = false; 282 this.playback.disabled = false;
283 this.disabled = false;
288 } 284 }
289 }; 285 };
290 this.updateLoading = function (progress) { 286 this.updateLoading = function (progress) {
291 // progress is a value from 0 to 100 indicating the current download state of media files 287 // progress is a value from 0 to 100 indicating the current download state of media files
292 if (progress != 100) { 288 if (progress != 100) {
343 return node; 339 return node;
344 340
345 }; 341 };
346 this.error = function () { 342 this.error = function () {
347 // If there is an error with the audioObject, this will be called to indicate a failure 343 // If there is an error with the audioObject, this will be called to indicate a failure
348 } 344 };
349 }; 345 };
350 // Ensure there are only two comparisons per page 346 // Ensure there are only two comparisons per page
351 if (page.audioElements.length != 2) { 347 if (page.audioElements.length != 2) {
352 console.error('FATAL - There must be 2 <audioelement> nodes on each <page>: ' + page.id); 348 console.error('FATAL - There must be 2 <audioelement> nodes on each <page>: ' + page.id);
353 return; 349 return;
354 } 350 }
355 // Build the three audio elements 351 // Build the three audio elements
352
353 function buildElement(index, audioObject) {
354 var label;
355 switch (index) {
356 case 0:
357 label = "A";
358 break;
359 case 1:
360 label = "B";
361 break;
362 default:
363 label = "X";
364 break;
365 }
366 var node = new this.interfaceObject(audioObject, label);
367 audioObject.bindInterface(node);
368 return node;
369 }
370
356 this.pair = []; 371 this.pair = [];
357 this.X = null; 372 this.X = null;
358 this.boxHolders = document.getElementById('box-holders'); 373 this.boxHolders = document.getElementById('box-holders');
359 for (var index = 0; index < page.audioElements.length; index++) { 374 var node;
360 var element = page.audioElements[index]; 375 page.audioElements.forEach(function (element, index) {
361 if (element.type != 'normal') { 376 if (element.type != 'normal') {
362 console.log("WARNING - ABX can only have normal elements. Page " + page.id + ", Element " + element.id); 377 console.log("WARNING - ABX can only have normal elements. Page " + page.id + ", Element " + element.id);
363 element.type = "normal"; 378 element.type = "normal";
364 } 379 }
365 var audioObject = audioEngineContext.newTrack(element); 380 node = buildElement.call(this, index, audioEngineContext.newTrack(element));
366 var label;
367 if (index == 0) {
368 label = "A";
369 } else {
370 label = "B";
371 }
372 var node = new this.interfaceObject(audioObject, label);
373 audioObject.bindInterface(node);
374 this.pair.push(node); 381 this.pair.push(node);
375 this.boxHolders.appendChild(node.box); 382 this.boxHolders.appendChild(node.box);
376 } 383 }, this);
377 var elementId = Math.floor(Math.random() * 2); //Randomly pick A or B to be X 384 var elementId = Math.floor(Math.random() * 2); //Randomly pick A or B to be X
378 var element = new page.audioElementNode(specification); 385 var element = new page.audioElementNode(specification);
379 for (var atr in page.audioElements[elementId]) { 386 for (var atr in page.audioElements[elementId]) {
380 eval("element." + atr + " = page.audioElements[elementId]." + atr); 387 element[atr] = page.audioElements[elementId][atr];
381 } 388 }
382 element.id += "-X"; 389 element.id += "-X";
383 if (typeof element.name == "string") { 390 if (typeof element.name == "string") {
384 element.name += "-X"; 391 element.name += "-X";
385 } 392 }
395 aeNode.setAttribute('url', element.url); 402 aeNode.setAttribute('url', element.url);
396 aeNode.setAttribute('gain', element.gain); 403 aeNode.setAttribute('gain', element.gain);
397 aeNode.appendChild(storage.document.createElement('metric')); 404 aeNode.appendChild(storage.document.createElement('metric'));
398 root.appendChild(aeNode); 405 root.appendChild(aeNode);
399 // Build the 'X' element 406 // Build the 'X' element
407 var label;
400 var audioObject = audioEngineContext.newTrack(element); 408 var audioObject = audioEngineContext.newTrack(element);
401 var label; 409 node = buildElement.call(this, 3, audioObject);
402 switch (audioObject.specification.parent.label) {
403 case "letter":
404 label = "x";
405 break;
406 default:
407 label = "X";
408 break;
409 }
410 var node = new this.interfaceObject(audioObject, label);
411 node.box.children[0].classList.add('inactive'); 410 node.box.children[0].classList.add('inactive');
412 audioObject.bindInterface(node); 411 audioObject.bindInterface(node);
413 this.X = node; 412 this.X = node;
414 this.boxHolders.appendChild(node.box); 413 this.boxHolders.appendChild(node.box);
415 } 414 }
432 function buttonSubmitClick() { 431 function buttonSubmitClick() {
433 var checks = testState.currentStateMap.interfaces[0].options, 432 var checks = testState.currentStateMap.interfaces[0].options,
434 canContinue = true; 433 canContinue = true;
435 434
436 for (var i = 0; i < checks.length; i++) { 435 for (var i = 0; i < checks.length; i++) {
436 var checkState;
437 if (checks[i].type == 'check') { 437 if (checks[i].type == 'check') {
438 switch (checks[i].name) { 438 switch (checks[i].name) {
439 case 'fragmentPlayed': 439 case 'fragmentPlayed':
440 // Check if all fragments have been played 440 // Check if all fragments have been played
441 var checkState = interfaceContext.checkAllPlayed(); 441 checkState = interfaceContext.checkAllPlayed();
442 if (checkState == false) { 442 if (checkState === false) {
443 canContinue = false; 443 canContinue = false;
444 } 444 }
445 break; 445 break;
446 case 'fragmentFullPlayback': 446 case 'fragmentFullPlayback':
447 // Check all fragments have been played to their full length 447 // Check all fragments have been played to their full length
448 var checkState = interfaceContext.checkFragmentsFullyPlayed(); 448 checkState = interfaceContext.checkFragmentsFullyPlayed();
449 if (checkState == false) { 449 if (checkState === false) {
450 canContinue = false; 450 canContinue = false;
451 } 451 }
452 break; 452 break;
453 case 'fragmentMoved': 453 case 'fragmentMoved':
454 // Check all fragment sliders have been moved. 454 // Check all fragment sliders have been moved.
455 var checkState = interfaceContext.checkAllMoved(); 455 checkState = interfaceContext.checkAllMoved();
456 if (checkState == false) { 456 if (checkState === false) {
457 canContinue = false; 457 canContinue = false;
458 } 458 }
459 break; 459 break;
460 case 'fragmentComments': 460 case 'fragmentComments':
461 // Check all fragment sliders have been moved. 461 // Check all fragment sliders have been moved.
462 var checkState = interfaceContext.checkAllCommented(); 462 checkState = interfaceContext.checkAllCommented();
463 if (checkState == false) { 463 if (checkState === false) {
464 canContinue = false; 464 canContinue = false;
465 } 465 }
466 break; 466 break;
467 case 'scalerange': 467 case 'scalerange':
468 // Check the scale has been used effectively 468 // Check the scale has been used effectively
469 var checkState = interfaceContext.checkScaleRange(checks[i].min, checks[i].max); 469 checkState = interfaceContext.checkScaleRange(checks[i].min, checks[i].max);
470 if (checkState == false) { 470 if (checkState === false) {
471 canContinue = false; 471 canContinue = false;
472 } 472 }
473 break; 473 break;
474 default: 474 default:
475 console.log("WARNING - Check option " + checks[i].check + " is not supported on this interface"); 475 console.log("WARNING - Check option " + checks[i].check + " is not supported on this interface");
485 if (audioEngineContext.status == 1) { 485 if (audioEngineContext.status == 1) {
486 var playback = document.getElementById('playback-button'); 486 var playback = document.getElementById('playback-button');
487 playback.click(); 487 playback.click();
488 // This function is called when the submit button is clicked. Will check for any further tests to perform, or any post-test options 488 // This function is called when the submit button is clicked. Will check for any further tests to perform, or any post-test options
489 } else { 489 } else {
490 if (audioEngineContext.timer.testStarted == false) { 490 if (audioEngineContext.timer.testStarted === false) {
491 interfaceContext.lightbox.post("Warning", 'You have not started the test! Please listen to a sample to begin the test!'); 491 interfaceContext.lightbox.post("Warning", 'You have not started the test! Please listen to a sample to begin the test!');
492 return; 492 return;
493 } 493 }
494 } 494 }
495 testState.advanceState(); 495 testState.advanceState();