annotate interfaces/timeline.js @ 2808:d5d374b399cb

Some restructuring of the timeline CSS/JS
author Nicholas Jillings <nicholas.jillings@mail.bcu.ac.uk>
date Tue, 25 Apr 2017 10:55:39 +0100
parents 510a64224eec
children 3a2dbff86da9
rev   line source
nicholas@2479 1 /**
nicholas@2479 2 * WAET Timeline
nicholas@2479 3 * This interface plots a waveform timeline per audio fragment on a page. Clicking on the fragment will generate a comment box for processing.
nicholas@2479 4 */
nicholas@2702 5 /*globals interfaceContext, window, document, console, audioEngineContext, testState, $, storage */
nicholas@2479 6 // Once this is loaded and parsed, begin execution
nicholas@2479 7 loadInterface();
nicholas@2479 8
nicholas@2479 9 function loadInterface() {
nicholas@2481 10 // Use this to do any one-time page / element construction. For instance, placing any stationary text objects,
nicholas@2481 11 // holding div's, or setting up any nodes which are present for the entire test sequence
nicholas@2481 12
nicholas@2479 13 interfaceContext.insertPoint.innerHTML = ""; // Clear the current schema
nicholas@2481 14
nicholas@2479 15 interfaceContext.insertPoint = document.getElementById("topLevelBody");
nicholas@2479 16 var testContent = document.createElement("div");
nicholas@2481 17
nicholas@2479 18 // Create the top div and Title element
nicholas@2479 19 var title = document.createElement("div");
nicholas@2479 20 title.className = "title";
nicholas@2479 21 title.align = "center";
nicholas@2479 22 var titleSpan = document.createElement("span");
nicholas@2479 23 titleSpan.id = "test-title";
nicholas@2479 24 titleSpan.textContent = "Listening Test";
nicholas@2479 25 title.appendChild(titleSpan);
nicholas@2481 26
nicholas@2479 27 var pagetitle = document.createElement("div");
nicholas@2479 28 pagetitle.className = "pageTitle";
nicholas@2479 29 pagetitle.align = "center";
nicholas@2479 30 titleSpan = document.createElement("span");
nicholas@2479 31 titleSpan.id = "page-title";
nicholas@2479 32 pagetitle.appendChild(titleSpan);
nicholas@2481 33
nicholas@2479 34 // Create Interface buttons
nicholas@2479 35 var interfaceButtons = document.createElement("div");
nicholas@2479 36 interfaceButtons.id = 'interface-buttons';
nicholas@2479 37 interfaceButtons.style.height = "25px";
nicholas@2481 38
nicholas@2479 39 // Create playback start/stop points
nicholas@2479 40 var playback = document.createElement("button");
nicholas@2479 41 playback.innerHTML = "Stop";
nicholas@2479 42 playback.id = "playback-button";
nicholas@2481 43 playback.onclick = function () {
nicholas@2479 44 if (audioEngineContext.status == 1) {
nicholas@2479 45 audioEngineContext.stop();
nicholas@2479 46 this.innerHTML = "Stop";
nicholas@2479 47 var time = audioEngineContext.timer.getTestTime();
nicholas@2481 48 console.log("Stopped at " + time);
nicholas@2479 49 }
nicholas@2479 50 };
nicholas@2479 51 // Create Submit (save) button
nicholas@2481 52 var submit = document.createElement("button");
nicholas@2481 53 submit.innerHTML = 'Next';
nicholas@2481 54 submit.onclick = buttonSubmitClick;
nicholas@2481 55 submit.id = 'submit-button';
nicholas@2481 56 submit.style.float = 'left';
nicholas@2481 57 // Append the interface buttons into the interfaceButtons object.
nicholas@2557 58 interfaceButtons.appendChild(submit);
nicholas@2481 59 interfaceButtons.appendChild(playback);
nicholas@2481 60
nicholas@2479 61 // Create outside reference holder
nicholas@2479 62 var outsideRef = document.createElement("div");
nicholas@2479 63 outsideRef.id = "outside-reference-holder";
nicholas@2481 64
nicholas@2479 65 // Create content point
nicholas@2479 66 var content = document.createElement("div");
nicholas@2479 67 content.id = "timeline-test-content";
nicholas@2481 68
nicholas@2479 69 //Inject
nicholas@2479 70 testContent.appendChild(title);
nicholas@2479 71 testContent.appendChild(pagetitle);
nicholas@2479 72 testContent.appendChild(interfaceButtons);
nicholas@2479 73 testContent.appendChild(outsideRef);
nicholas@2479 74 testContent.appendChild(content);
nicholas@2479 75 interfaceContext.insertPoint.appendChild(testContent);
nicholas@2481 76
nicholas@2479 77 // Load the full interface
nicholas@2481 78 testState.initialise();
nicholas@2481 79 testState.advanceState();
nicholas@2702 80 }
nicholas@2479 81
nicholas@2481 82 function loadTest(page) {
nicholas@2481 83 // Called each time a new test page is to be build. The page specification node is the only item passed in
nicholas@2479 84 var content = document.getElementById("timeline-test-content");
nicholas@2479 85 content.innerHTML = "";
nicholas@2651 86 var interfaceObj = interfaceContext.getCombinedInterfaces(page);
nicholas@2481 87 if (interfaceObj.length > 1) {
nicholas@2479 88 console.log("WARNING - This interface only supports one <interface> node per page. Using first interface node");
nicholas@2479 89 }
nicholas@2479 90 interfaceObj = interfaceObj[0];
nicholas@2481 91
nicholas@2479 92 //Set the page title
nicholas@2481 93 if (typeof page.title == "string" && page.title.length > 0) {
nicholas@2479 94 document.getElementById("test-title").textContent = page.title;
nicholas@2479 95 }
nicholas@2481 96
nicholas@2702 97 if (interfaceObj.title !== null) {
nicholas@2479 98 document.getElementById("page-title").textContent = interfaceObj.title;
nicholas@2479 99 }
nicholas@2481 100
nicholas@2801 101 if (interfaceObj.image !== undefined || page.audioElements.some(function (elem) {
nicholas@2801 102 return elem.image !== undefined;
nicholas@2801 103 })) {
nicholas@2807 104 document.getElementById("timeline-test-content").parentElement.insertBefore(interfaceContext.imageHolder.root, document.getElementById("timeline-test-content"));
nicholas@2801 105 interfaceContext.imageHolder.setImage(interfaceObj.image);
nicholas@2801 106 }
nicholas@2801 107
nicholas@2479 108 // Delete outside reference
nicholas@2481 109 var outsideReferenceHolder = document.getElementById("outside-reference-holder");
nicholas@2479 110 outsideReferenceHolder.innerHTML = "";
nicholas@2481 111
nicholas@2479 112 var commentBoxPrefix = "Comment on track";
nicholas@2702 113 if (interfaceObj.commentBoxPrefix !== undefined) {
nicholas@2481 114 commentBoxPrefix = interfaceObj.commentBoxPrefix;
nicholas@2481 115 }
nicholas@2607 116 var index = 0;
nicholas@2607 117 var interfaceScales = testState.currentStateMap.interfaces[0].scales;
nicholas@2607 118 var labelType = page.label;
nicholas@2607 119 if (labelType == "default") {
nicholas@2607 120 labelType = "number";
nicholas@2607 121 }
nicholas@2607 122 $(page.audioElements).each(function (pageIndex, element) {
nicholas@2479 123 var audioObject = audioEngineContext.newTrack(element);
nicholas@2479 124 if (page.audioElements.type == 'outside-reference') {
nicholas@2481 125 var refNode = interfaceContext.outsideReferenceDOM(audioObject, index, outsideReferenceHolder);
nicholas@2702 126 audioObject.bindInterface(refNode);
nicholas@2479 127 } else {
nicholas@2607 128 var label = interfaceContext.getLabel(labelType, index, page.labelStart);
nicholas@2481 129 var node = new interfaceObject(audioObject, label);
nicholas@2481 130
nicholas@2479 131 content.appendChild(node.DOM);
nicholas@2479 132 audioObject.bindInterface(node);
nicholas@2479 133 }
nicholas@2479 134 });
nicholas@2481 135
nicholas@2479 136 resizeWindow();
nicholas@2479 137 }
nicholas@2479 138
nicholas@2481 139 function interfaceObject(audioObject, labelstr) {
nicholas@2481 140 // Each audio object has a waveform guide and self-generated comments
nicholas@2479 141 this.parent = audioObject;
nicholas@2479 142 this.DOM = document.createElement("div");
nicholas@2479 143 this.DOM.className = "timeline-element";
nicholas@2479 144 this.DOM.id = audioObject.specification.id;
nicholas@2481 145
nicholas@2479 146 var root = document.createElement("div");
nicholas@2479 147 root.className = "timeline-element-content";
nicholas@2479 148 this.DOM.appendChild(root);
nicholas@2481 149
nicholas@2479 150 var label = document.createElement("div");
nicholas@2479 151 label.style.textAlign = "center";
nicholas@2479 152 var labelSpan = document.createElement("span");
nicholas@2481 153 labelSpan.textContent = "Fragment " + labelstr;
nicholas@2479 154 label.appendChild(labelSpan);
nicholas@2479 155 root.appendChild(label);
nicholas@2481 156
nicholas@2479 157 var canvasHolder = document.createElement("div");
nicholas@2479 158 canvasHolder.className = "timeline-element-canvas-holder";
nicholas@2479 159 var buttonHolder = document.createElement("div");
nicholas@2479 160 buttonHolder.className = "timeline-element-button-holder";
nicholas@2479 161 var commentHolder = document.createElement("div");
nicholas@2479 162 commentHolder.className = "timeline-element-comment-holder";
nicholas@2481 163
nicholas@2479 164 root.appendChild(canvasHolder);
nicholas@2479 165 root.appendChild(buttonHolder);
nicholas@2479 166 root.appendChild(commentHolder);
nicholas@2481 167
nicholas@2479 168 this.comments = {
nicholas@2479 169 parent: this,
nicholas@2479 170 list: [],
nicholas@2481 171 Comment: function (parent, time, str) {
nicholas@2479 172 this.parent = parent;
nicholas@2479 173 this.time = time;
nicholas@2479 174 this.DOM = document.createElement("div");
nicholas@2480 175 this.DOM.className = "comment-entry";
nicholas@2480 176 var titleHolder = document.createElement("div");
nicholas@2480 177 titleHolder.className = "comment-entry-header";
nicholas@2479 178 this.title = document.createElement("span");
nicholas@2702 179 if (str !== undefined) {
nicholas@2479 180 this.title.textContent = str;
nicholas@2479 181 } else {
nicholas@2481 182 this.title.textContent = "Time: " + time.toFixed(2) + "s";
nicholas@2479 183 }
nicholas@2480 184 titleHolder.appendChild(this.title);
nicholas@2479 185 this.textarea = document.createElement("textarea");
nicholas@2480 186 this.textarea.className = "comment-entry-text";
nicholas@2480 187 this.DOM.appendChild(titleHolder);
nicholas@2479 188 this.DOM.appendChild(this.textarea);
nicholas@2481 189
nicholas@2480 190 this.clear = {
nicholas@2480 191 DOM: document.createElement("button"),
nicholas@2480 192 parent: this,
nicholas@2481 193 handleEvent: function () {
nicholas@2480 194 this.parent.parent.deleteComment(this.parent);
nicholas@2480 195 }
nicholas@2702 196 };
nicholas@2480 197 this.clear.DOM.textContent = "Delete";
nicholas@2481 198 this.clear.DOM.addEventListener("click", this.clear);
nicholas@2480 199 titleHolder.appendChild(this.clear.DOM);
nicholas@2481 200
nicholas@2481 201 this.resize = function () {
nicholas@2479 202 var w = window.innerWidth;
nicholas@2481 203 w = Math.min(w, 800);
nicholas@2481 204 w = Math.max(w, 200);
nicholas@2479 205 var elem_w = w / 2.5;
nicholas@2481 206 elem_w = Math.max(elem_w, 190);
nicholas@2481 207 this.DOM.style.width = elem_w + "px";
nicholas@2481 208 this.textarea.style.width = (elem_w - 5) + "px";
nicholas@2702 209 };
nicholas@2481 210 this.buildXML = function (root) {
nicholas@2480 211 //storage.document.createElement();
nicholas@2480 212 var node = storage.document.createElement("comment");
nicholas@2480 213 var question = storage.document.createElement("question");
nicholas@2480 214 var comment = storage.document.createElement("response");
nicholas@2481 215 node.setAttribute("time", this.time);
nicholas@2480 216 question.textContent = this.title.textContent;
nicholas@2480 217 comment.textContent = this.textarea.value;
nicholas@2480 218 node.appendChild(question);
nicholas@2480 219 node.appendChild(comment);
nicholas@2480 220 root.appendChild(node);
nicholas@2702 221 };
nicholas@2479 222 this.resize();
nicholas@2479 223 },
nicholas@2481 224 newComment: function (time) {
nicholas@2481 225 var node = new this.Comment(this, time);
nicholas@2479 226 this.list.push(node);
nicholas@2479 227 commentHolder.appendChild(node.DOM);
nicholas@2479 228 return node;
nicholas@2479 229 },
nicholas@2481 230 deleteComment: function (comment) {
nicholas@2481 231 var index = this.list.findIndex(function (element, index, array) {
nicholas@2481 232 if (element == comment) {
nicholas@2481 233 return true;
nicholas@2481 234 }
nicholas@2481 235 return false;
nicholas@2481 236 }, comment);
nicholas@2480 237 if (index == -1) {
nicholas@2480 238 return false;
nicholas@2480 239 }
nicholas@2481 240 var node = this.list.splice(index, 1);
nicholas@2480 241 comment.DOM.remove();
nicholas@2480 242 this.parent.canvas.drawMarkers();
nicholas@2480 243 return true;
nicholas@2479 244 },
nicholas@2481 245 clearList: function () {
nicholas@2481 246 while (this.list.length > 0) {
nicholas@2480 247 this.deleteComment(this.list[0]);
nicholas@2480 248 }
nicholas@2479 249 }
nicholas@2702 250 };
nicholas@2481 251
nicholas@2479 252 this.canvas = {
nicholas@2479 253 parent: this,
nicholas@2479 254 comments: this.comments,
nicholas@2479 255 layer1: document.createElement("canvas"),
nicholas@2479 256 layer2: document.createElement("canvas"),
nicholas@2479 257 layer3: document.createElement("canvas"),
nicholas@2479 258 layer4: document.createElement("canvas"),
nicholas@2808 259 resize: function () {
nicholas@2808 260 var w = $(this.layer1.parentElement).width();
nicholas@2479 261 this.layer1.width = w;
nicholas@2479 262 this.layer2.width = w;
nicholas@2479 263 this.layer3.width = w;
nicholas@2479 264 this.layer4.width = w;
nicholas@2481 265 this.layer1.style.width = w + "px";
nicholas@2481 266 this.layer2.style.width = w + "px";
nicholas@2481 267 this.layer3.style.width = w + "px";
nicholas@2481 268 this.layer4.style.width = w + "px";
nicholas@2480 269 this.drawWaveform();
nicholas@2480 270 this.drawMarkers();
nicholas@2479 271 },
nicholas@2481 272 handleEvent: function (event) {
nicholas@2481 273 switch (event.currentTarget) {
nicholas@2479 274 case this.layer1:
nicholas@2481 275 switch (event.type) {
nicholas@2479 276 case "mousemove":
nicholas@2479 277 this.drawMouse(event);
nicholas@2479 278 break;
nicholas@2479 279 case "mouseleave":
nicholas@2479 280 this.clearCanvas(this.layer1);
nicholas@2479 281 break;
nicholas@2479 282 case "click":
nicholas@2479 283 var rect = this.layer1.getBoundingClientRect();
nicholas@2479 284 var pixX = event.clientX - rect.left;
nicholas@2481 285 var tpp = this.parent.parent.buffer.buffer.duration / this.layer1.width;
nicholas@2481 286 this.comments.newComment(pixX * tpp);
nicholas@2479 287 this.drawMarkers();
nicholas@2479 288 break;
nicholas@2479 289 }
nicholas@2479 290 break;
nicholas@2479 291 }
nicholas@2479 292 },
nicholas@2481 293 drawWaveform: function () {
nicholas@2702 294 if (this.parent.parent === undefined || this.parent.parent.buffer === undefined) {
nicholas@2480 295 return;
nicholas@2480 296 }
nicholas@2479 297 var buffer = this.parent.parent.buffer.buffer;
nicholas@2479 298 var context = this.layer4.getContext("2d");
nicholas@2479 299 context.lineWidth = 1;
nicholas@2479 300 context.strokeStyle = "#888";
nicholas@2481 301 context.clearRect(0, 0, this.layer4.width, this.layer4.height);
nicholas@2479 302 var data = buffer.getChannelData(0);
nicholas@2481 303 var t_per_pixel = buffer.duration / this.layer4.width;
nicholas@2481 304 var s_per_pixel = data.length / this.layer4.width;
nicholas@2479 305 var pixX = 0;
nicholas@2479 306 while (pixX < this.layer4.width) {
nicholas@2481 307 var start = Math.floor(s_per_pixel * pixX);
nicholas@2481 308 var end = Math.min(Math.ceil(s_per_pixel * (pixX + 1)), data.length);
nicholas@2481 309 var frame = data.subarray(start, end);
nicholas@2479 310 var min = frame[0];
nicholas@2479 311 var max = min;
nicholas@2481 312 for (var n = 0; n < frame.length; n++) {
nicholas@2481 313 if (frame[n] < min) {
nicholas@2481 314 min = frame[n];
nicholas@2481 315 }
nicholas@2481 316 if (frame[n] > max) {
nicholas@2481 317 max = frame[n];
nicholas@2481 318 }
nicholas@2479 319 }
nicholas@2479 320 // Assuming min/max normalised between [-1, 1] to map to [150, 0]
nicholas@2479 321 context.beginPath();
nicholas@2481 322 context.moveTo(pixX + 0.5, (min + 1) * -75 + 150);
nicholas@2481 323 context.lineTo(pixX + 0.5, (max + 1) * -75 + 150);
nicholas@2479 324 context.stroke();
nicholas@2479 325 pixX++;
nicholas@2479 326 }
nicholas@2479 327 },
nicholas@2481 328 drawMouse: function (event) {
nicholas@2479 329 var context = this.layer1.getContext("2d");
nicholas@2481 330 context.clearRect(0, 0, this.layer1.width, this.layer1.height);
nicholas@2479 331 var rect = this.layer1.getBoundingClientRect();
nicholas@2479 332 var pixX = event.clientX - rect.left;
nicholas@2481 333 pixX = Math.floor(pixX) - 0.5;
nicholas@2479 334 context.strokeStyle = "#800";
nicholas@2479 335 context.beginPath();
nicholas@2481 336 context.moveTo(pixX, 0);
nicholas@2481 337 context.lineTo(pixX, this.layer1.height);
nicholas@2479 338 context.stroke();
nicholas@2479 339 },
nicholas@2481 340 drawTicker: function () {
nicholas@2479 341 var context = this.layer2.getContext("2d");
nicholas@2481 342 context.clearRect(0, 0, this.layer2.width, this.layer2.height);
nicholas@2479 343 var time = this.parent.parent.getCurrentPosition();
nicholas@2479 344 var ratio = time / this.parent.parent.buffer.buffer.duration;
nicholas@2481 345 var pixX = Math.floor(ratio * this.layer2.width) + 0.5;
nicholas@2479 346 context.strokeStyle = "#080";
nicholas@2479 347 context.beginPath();
nicholas@2481 348 context.moveTo(pixX, 0);
nicholas@2481 349 context.lineTo(pixX, this.layer2.height);
nicholas@2479 350 context.stroke();
nicholas@2479 351 },
nicholas@2481 352 drawMarkers: function () {
nicholas@2702 353 if (this.parent.parent === undefined || this.parent.parent.buffer === undefined) {
nicholas@2480 354 return;
nicholas@2480 355 }
nicholas@2479 356 var context = this.layer3.getContext("2d");
nicholas@2481 357 context.clearRect(0, 0, this.layer3.width, this.layer3.height);
nicholas@2479 358 context.strokeStyle = "#008";
nicholas@2481 359 var tpp = this.parent.parent.buffer.buffer.duration / this.layer1.width;
nicholas@2481 360 for (var i = 0; i < this.comments.list.length; i++) {
nicholas@2479 361 var comment = this.comments.list[i];
nicholas@2481 362 var pixX = Math.floor(comment.time / tpp) + 0.5;
nicholas@2479 363 context.beginPath();
nicholas@2481 364 context.moveTo(pixX, 0);
nicholas@2481 365 context.lineTo(pixX, this.layer3.height);
nicholas@2479 366 context.stroke();
nicholas@2479 367 }
nicholas@2479 368 },
nicholas@2481 369 clearCanvas: function (canvas) {
nicholas@2479 370 var context = canvas.getContext("2d");
nicholas@2481 371 context.clearRect(0, 0, canvas.width, canvas.height);
nicholas@2479 372 }
nicholas@2702 373 };
nicholas@2479 374 this.canvas.layer1.className = "timeline-element-canvas canvas-layer1 canvas-disabled";
nicholas@2479 375 this.canvas.layer2.className = "timeline-element-canvas canvas-layer2";
nicholas@2479 376 this.canvas.layer3.className = "timeline-element-canvas canvas-layer3";
nicholas@2479 377 this.canvas.layer4.className = "timeline-element-canvas canvas-layer3";
nicholas@2479 378 this.canvas.layer1.height = "150";
nicholas@2479 379 this.canvas.layer2.height = "150";
nicholas@2479 380 this.canvas.layer3.height = "150";
nicholas@2479 381 this.canvas.layer4.height = "150";
nicholas@2479 382 canvasHolder.appendChild(this.canvas.layer1);
nicholas@2479 383 canvasHolder.appendChild(this.canvas.layer2);
nicholas@2479 384 canvasHolder.appendChild(this.canvas.layer3);
nicholas@2479 385 canvasHolder.appendChild(this.canvas.layer4);
nicholas@2481 386 this.canvas.layer1.addEventListener("mousemove", this.canvas);
nicholas@2481 387 this.canvas.layer1.addEventListener("mouseleave", this.canvas);
nicholas@2481 388 this.canvas.layer1.addEventListener("click", this.canvas);
nicholas@2481 389
nicholas@2479 390 var canvasIntervalID = null;
nicholas@2481 391
nicholas@2479 392 this.playButton = {
nicholas@2479 393 parent: this,
nicholas@2479 394 DOM: document.createElement("button"),
nicholas@2481 395 handleEvent: function (event) {
nicholas@2479 396 var id = this.parent.parent.id;
nicholas@2479 397 var str = this.DOM.textContent;
nicholas@2479 398 if (str == "Play") {
nicholas@2479 399 audioEngineContext.play(id);
nicholas@2479 400 } else if (str == "Stop") {
nicholas@2479 401 audioEngineContext.stop();
nicholas@2479 402 }
nicholas@2479 403 }
nicholas@2702 404 };
nicholas@2481 405 this.playButton.DOM.addEventListener("click", this.playButton);
nicholas@2479 406 this.playButton.DOM.className = "timeline-button timeline-button-disabled";
nicholas@2479 407 this.playButton.DOM.disabled = true;
nicholas@2479 408 this.playButton.DOM.textContent = "Wait";
nicholas@2481 409
nicholas@2479 410 buttonHolder.appendChild(this.playButton.DOM);
nicholas@2481 411
nicholas@2481 412 this.resize = function () {
nicholas@2808 413 this.canvas.resize();
nicholas@2702 414 };
nicholas@2481 415
nicholas@2481 416 this.enable = function () {
nicholas@2481 417 // This is used to tell the interface object that playback of this node is ready
nicholas@2481 418 this.canvas.layer1.addEventListener("click", this.canvas);
nicholas@2479 419 this.canvas.layer1.className = "timeline-element-canvas canvas-layer1";
nicholas@2479 420 this.playButton.DOM.className = "timeline-button timeline-button-play";
nicholas@2479 421 this.playButton.DOM.textContent = "Play";
nicholas@2479 422 this.playButton.DOM.disabled = false;
nicholas@2481 423
nicholas@2479 424 this.canvas.drawWaveform();
nicholas@2481 425 };
nicholas@2481 426 this.updateLoading = function (progress) {
nicholas@2481 427 // progress is a value from 0 to 100 indicating the current download state of media files
nicholas@2479 428 progress = String(progress);
nicholas@2481 429 progress = progress.substr(0, 5);
nicholas@2481 430 this.playButton.DOM.textContent = "Loading: " + progress + '%';
nicholas@2481 431 };
nicholas@2481 432 this.startPlayback = function () {
nicholas@2479 433 // Called when playback has begun
nicholas@2481 434 canvasIntervalID = window.setInterval(this.canvas.drawTicker.bind(this.canvas), 100);
nicholas@2479 435 this.playButton.DOM.textContent = "Stop";
nicholas@2726 436 interfaceContext.commentBoxes.highlightById(audioObject.id);
nicholas@2479 437 };
nicholas@2481 438 this.stopPlayback = function () {
nicholas@2479 439 // Called when playback has stopped. This gets called even if playback never started!
nicholas@2479 440 window.clearInterval(canvasIntervalID);
nicholas@2479 441 this.canvas.clearCanvas(this.canvas.layer2);
nicholas@2479 442 this.playButton.DOM.textContent = "Play";
nicholas@2726 443 var box = interfaceContext.commentBoxes.boxes.find(function (a) {
nicholas@2726 444 return a.id === audioObject.id;
nicholas@2726 445 });
nicholas@2726 446 if (box) {
nicholas@2726 447 box.highlight(false);
nicholas@2726 448 }
nicholas@2479 449 };
nicholas@2481 450 this.getValue = function () {
nicholas@2481 451 // Return the current value of the object. If there is no value, return 0
nicholas@2479 452 return 0;
nicholas@2481 453 };
nicholas@2481 454 this.getPresentedId = function () {
nicholas@2481 455 // Return the presented ID of the object. For instance, the APE has sliders starting from 0. Whilst AB has alphabetical scale
nicholas@2479 456 return labelSpan.textContent;
nicholas@2481 457 };
nicholas@2481 458 this.canMove = function () {
nicholas@2481 459 // Return either true or false if the interface object can be moved. AB / Reference cannot, whilst sliders can and therefore have a continuous scale.
nicholas@2481 460 // These are checked primarily if the interface check option 'fragmentMoved' is enabled.
nicholas@2479 461 return false;
nicholas@2481 462 };
nicholas@2481 463 this.exportXMLDOM = function (audioObject) {
nicholas@2481 464 // Called by the audioObject holding this element to export the interface <value> node.
nicholas@2481 465 // If there is no value node (such as outside reference), return null
nicholas@2481 466 // If there are multiple value nodes (such as multiple scale / 2D scales), return an array of nodes with each value node having an 'interfaceName' attribute
nicholas@2481 467 // Use storage.document.createElement('value'); to generate the XML node.
nicholas@2481 468 return null;
nicholas@2481 469 };
nicholas@2481 470 this.error = function () {
nicholas@2479 471 // If there is an error with the audioObject, this will be called to indicate a failure
nicholas@2702 472 };
nicholas@2702 473 }
nicholas@2479 474
nicholas@2481 475 function resizeWindow(event) {
nicholas@2481 476 // Called on every window resize event, use this to scale your page properly
nicholas@2481 477 for (var i = 0; i < audioEngineContext.audioObjects.length; i++) {
nicholas@2479 478 audioEngineContext.audioObjects[i].interfaceDOM.resize();
nicholas@2479 479 }
nicholas@2479 480 }
nicholas@2479 481
nicholas@2481 482 function buttonSubmitClick() {
nicholas@2702 483 if (audioEngineContext.timer.testStarted === false) {
nicholas@2481 484 interfaceContext.lightbox.post("Warning", 'You have not started the test! Please click play on a sample to begin the test!');
nicholas@2481 485 return;
nicholas@2481 486 }
nicholas@2724 487 var checks = testState.currentStateMap.interfaces[0].options,
nicholas@2651 488 canContinue = true;
nicholas@2481 489 for (var i = 0; i < checks.length; i++) {
nicholas@2481 490 var checkState = true;
nicholas@2481 491 if (checks[i].type == 'check') {
nicholas@2481 492 switch (checks[i].name) {
nicholas@2481 493 case 'fragmentPlayed':
nicholas@2481 494 //Check if all fragments have been played
n@2790 495 checkState = interfaceContext.checkAllPlayed(checks[i].errorMessage);
nicholas@2481 496 break;
nicholas@2481 497 case 'fragmentFullPlayback':
nicholas@2481 498 //Check if all fragments have played to their full length
n@2790 499 checkState = interfaceContext.checkFragmentsFullyPlayed(checks[i].errorMessage);
nicholas@2481 500 break;
nicholas@2481 501 case 'fragmentComments':
n@2790 502 checkState = interfaceContext.checkAllCommented(checks[i].errorMessage);
nicholas@2481 503 break;
nicholas@2481 504 default:
nicholas@2481 505 console.log("WARNING - Check option " + checks[i].check + " is not supported on this interface");
nicholas@2481 506 break;
nicholas@2481 507 }
nicholas@2702 508 if (checkState === false) {
nicholas@2569 509 canContinue = false;
nicholas@2481 510 }
nicholas@2481 511 }
nicholas@2481 512 if (!canContinue) {
nicholas@2481 513 return;
nicholas@2481 514 }
nicholas@2481 515 }
nicholas@2481 516
nicholas@2481 517 if (canContinue) {
nicholas@2481 518 if (audioEngineContext.status == 1) {
nicholas@2481 519 var playback = document.getElementById('playback-button');
nicholas@2481 520 playback.click();
nicholas@2481 521 // This function is called when the submit button is clicked. Will check for any further tests to perform, or any post-test options
nicholas@2481 522 }
nicholas@2481 523 testState.advanceState();
nicholas@2481 524 }
nicholas@2479 525 }
nicholas@2479 526
nicholas@2481 527 function pageXMLSave(store, pageSpecification) {
nicholas@2481 528 // MANDATORY
nicholas@2481 529 // Saves a specific test page
nicholas@2481 530 // You can use this space to add any extra nodes to your XML <audioHolder> saves
nicholas@2481 531 // Get the current <page> information in store (remember to appendChild your data to it)
nicholas@2481 532 // pageSpecification is the current page node configuration
nicholas@2481 533 // To create new XML nodes, use storage.document.createElement();
nicholas@2481 534
nicholas@2481 535 for (var i = 0; i < audioEngineContext.audioObjects.length; i++) {
nicholas@2480 536 var id = audioEngineContext.audioObjects[i].specification.id;
nicholas@2480 537 var commentsList = audioEngineContext.audioObjects[i].interfaceDOM.comments.list;
nicholas@2480 538 var root = audioEngineContext.audioObjects[i].storeDOM;
nicholas@2481 539 for (var j = 0; j < commentsList.length; j++) {
nicholas@2480 540 commentsList[j].buildXML(root);
nicholas@2480 541 }
nicholas@2480 542 }
nicholas@2481 543 }