annotate interfaces/ape.js @ 3009:1ced6e0cb9ac

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