nickjillings@1341: // Once this is loaded and parsed, begin execution nickjillings@1341: loadInterface(); nickjillings@1341: nickjillings@1341: function loadInterface() { nicholas@2576: // Get the dimensions of the screen available to the page nicholas@2576: var width = window.innerWidth; nicholas@2576: var height = window.innerHeight; nicholas@2576: interfaceContext.insertPoint.innerHTML = ""; // Clear the current schema nicholas@2576: nicholas@2576: // Custom comparator Object nicholas@2576: Interface.prototype.comparator = null; nicholas@2576: nicholas@2576: Interface.prototype.checkScaleRange = function (min, max) { nicholas@2359: var page = testState.getCurrentTestPage(); nicholas@2359: var audioObjects = audioEngineContext.audioObjects; nicholas@2359: var state = true; nicholas@2359: var str = "Please keep listening. "; nicholas@2359: var minRanking = Infinity; nicholas@2359: var maxRanking = -Infinity; nicholas@2359: for (var ao of audioObjects) { nicholas@2359: var rank = ao.interfaceDOM.getValue(); nicholas@2576: if (rank < minRanking) { nicholas@2576: minRanking = rank; nicholas@2576: } nicholas@2576: if (rank > maxRanking) { nicholas@2576: maxRanking = rank; nicholas@2576: } nicholas@2359: } nicholas@2576: if (maxRanking * 100 < max) { nicholas@2359: str += "At least one fragment must be selected." nicholas@2359: state = false; nicholas@2359: } nicholas@2359: if (!state) { nicholas@2359: console.log(str); nicholas@2359: this.storeErrorNode(str); nicholas@2576: interfaceContext.lightbox.post("Message", str); nicholas@2359: } nicholas@2359: return state; nicholas@2359: } nicholas@2576: nicholas@2576: // The injection point into the HTML page nicholas@2576: interfaceContext.insertPoint = document.getElementById("topLevelBody"); nicholas@2576: var testContent = document.createElement('div'); nicholas@2576: testContent.id = 'testContent'; nicholas@2576: nicholas@2576: // Create the top div for the Title element nicholas@2576: var titleAttr = specification.title; nicholas@2576: var title = document.createElement('div'); nicholas@2576: title.className = "title"; nicholas@2576: title.align = "center"; nicholas@2576: var titleSpan = document.createElement('span'); nicholas@2470: titleSpan.id = "test-title"; nicholas@2576: nicholas@2576: // Set title to that defined in XML, else set to default nicholas@2576: if (titleAttr != undefined) { nicholas@2576: titleSpan.textContent = titleAttr; nicholas@2576: } else { nicholas@2576: titleSpan.textContent = 'Listening test'; nicholas@2576: } nicholas@2576: // Insert the titleSpan element into the title div element. nicholas@2576: title.appendChild(titleSpan); nicholas@2576: nicholas@2576: var pagetitle = document.createElement('div'); nicholas@2576: pagetitle.className = "pageTitle"; nicholas@2576: pagetitle.align = "center"; nicholas@2576: var titleSpan = document.createElement('span'); nicholas@2576: titleSpan.id = "pageTitle"; nicholas@2576: pagetitle.appendChild(titleSpan); nicholas@2576: nicholas@2576: // Create Interface buttons! nicholas@2576: var interfaceButtons = document.createElement('div'); nicholas@2576: interfaceButtons.id = 'interface-buttons'; nicholas@2576: interfaceButtons.style.height = '25px'; nicholas@2576: nicholas@2576: // Create playback start/stop points nicholas@2576: var playback = document.createElement("button"); nicholas@2576: playback.innerHTML = 'Stop'; nicholas@2576: playback.id = 'playback-button'; nicholas@2576: playback.style.float = 'left'; nicholas@2576: // onclick function. Check if it is playing or not, call the correct function in the nicholas@2576: // audioEngine, change the button text to reflect the next state. nicholas@2576: playback.onclick = function () { nicholas@2576: if (audioEngineContext.status == 1) { nicholas@2576: audioEngineContext.stop(); nicholas@2576: this.innerHTML = 'Stop'; nickjillings@1341: var time = audioEngineContext.timer.getTestTime(); nickjillings@1341: console.log('Stopped at ' + time); // DEBUG/SAFETY nicholas@2576: } nicholas@2576: }; nicholas@2576: // Append the interface buttons into the interfaceButtons object. nicholas@2576: interfaceButtons.appendChild(playback); nicholas@2576: nicholas@2576: // Global parent for the comment boxes on the page nicholas@2576: var feedbackHolder = document.createElement('div'); nicholas@2576: feedbackHolder.id = 'feedbackHolder'; nicholas@2576: nicholas@2396: // Create outside reference holder nicholas@2396: var outsideRef = document.createElement("div"); nicholas@2396: outsideRef.id = "outside-reference-holder"; nicholas@2576: nicholas@2576: // Construct the AB Boxes nicholas@2576: var boxes = document.createElement('div'); nicholas@2576: boxes.align = "center"; nicholas@2576: boxes.id = "box-holders"; nickjillings@1341: boxes.style.float = "left"; nicholas@2576: nicholas@2576: var submit = document.createElement('button'); nicholas@2576: submit.id = "submit"; nicholas@2576: submit.onclick = buttonSubmitClick; nicholas@2576: submit.className = "big-button"; nicholas@2576: submit.textContent = "submit"; nicholas@2576: submit.style.position = "relative"; nicholas@2576: submit.style.left = (window.innerWidth - 250) / 2 + 'px'; nicholas@2576: nicholas@2576: feedbackHolder.appendChild(boxes); nicholas@2576: nicholas@2475: // Create holder for comment boxes nicholas@2475: var comments = document.createElement("div"); nicholas@2475: comments.id = "comment-box-holder"; nicholas@2576: nicholas@2576: // Inject into HTML nicholas@2576: testContent.appendChild(title); // Insert the title nicholas@2576: testContent.appendChild(pagetitle); nicholas@2576: testContent.appendChild(interfaceButtons); nicholas@2396: testContent.appendChild(outsideRef); nicholas@2576: testContent.appendChild(feedbackHolder); nicholas@2576: testContent.appendChild(submit); nicholas@2475: testContent.appendChild(comments); nicholas@2576: interfaceContext.insertPoint.appendChild(testContent); nickjillings@1341: nicholas@2576: // Load the full interface nicholas@2576: testState.initialise(); nicholas@2576: testState.advanceState(); nickjillings@1341: } nickjillings@1341: nicholas@2576: function loadTest(audioHolderObject) { nicholas@2576: var feedbackHolder = document.getElementById('feedbackHolder'); nicholas@2576: var interfaceObj = audioHolderObject.interfaces; nicholas@2576: if (interfaceObj.length > 1) { nicholas@2576: console.log("WARNING - This interface only supports one node per page. Using first interface node"); nicholas@2576: } nicholas@2576: interfaceObj = interfaceObj[0]; nicholas@2576: nicholas@2475: var commentHolder = document.getElementById('comment-box-holder'); nicholas@2475: commentHolder.innerHTML = ""; nicholas@2576: nicholas@2396: // Delete outside reference nicholas@2576: var outsideReferenceHolder = document.getElementById("outside-reference-holder"); nicholas@2396: outsideReferenceHolder.innerHTML = ""; nicholas@2576: nicholas@2470: // Set the page title nicholas@2470: if (typeof audioHolderObject.title == "string" && audioHolderObject.title.length > 0) { nicholas@2470: document.getElementById("test-title").textContent = audioHolderObject.title nicholas@2470: } nicholas@2576: nicholas@2576: if (interfaceObj.title != null) { nicholas@2576: document.getElementById("pageTitle").textContent = interfaceObj.title; nicholas@2576: } nicholas@2576: nickjillings@1356: var interfaceOptions = specification.interfaces.options.concat(interfaceObj.options); nicholas@2394: // Clear the interfaceElements nicholas@2394: { nicholas@2394: var node = document.getElementById('playback-holder'); nicholas@2576: if (node) { nicholas@2576: feedbackHolder.removeChild(node); nicholas@2576: } nicholas@2394: node = document.getElementById('page-count'); nicholas@2576: if (node) { nicholas@2576: document.getElementById('interface-buttons').removeChild(node); nicholas@2576: } nicholas@2394: node = document.getElementById('master-volume-holder-float'); nicholas@2576: if (node) { nicholas@2576: feedbackHolder.removeChild(node); nicholas@2576: } nicholas@2394: } nicholas@2576: n@2407: // Populate the comparator object nicholas@2576: interfaceContext.comparator = new comparator(audioHolderObject); nicholas@2576: nicholas@2576: for (var option of interfaceOptions) { nicholas@2576: if (option.type == "show") { nicholas@2576: switch (option.name) { nickjillings@1356: case "playhead": nickjillings@1356: var playbackHolder = document.getElementById('playback-holder'); nicholas@2576: if (playbackHolder == null) { nickjillings@1356: playbackHolder = document.createElement('div'); nicholas@2394: playbackHolder.id = 'playback-holder'; nickjillings@1356: playbackHolder.style.width = "100%"; nickjillings@1356: playbackHolder.style.float = "left"; nickjillings@1356: playbackHolder.align = 'center'; nickjillings@1356: playbackHolder.appendChild(interfaceContext.playhead.object); nickjillings@1356: feedbackHolder.appendChild(playbackHolder); nickjillings@1356: } nickjillings@1356: break; nickjillings@1356: case "page-count": nickjillings@1356: var pagecountHolder = document.getElementById('page-count'); nicholas@2576: if (pagecountHolder == null) { nickjillings@1356: pagecountHolder = document.createElement('div'); nickjillings@1356: pagecountHolder.id = 'page-count'; nicholas@2393: document.getElementById('interface-buttons').appendChild(pagecountHolder); nickjillings@1356: } nicholas@2576: pagecountHolder.innerHTML = 'Page ' + (testState.stateIndex + 1) + ' of ' + testState.stateMap.length + ''; nickjillings@1356: break; nickjillings@1356: case "volume": nicholas@2576: if (document.getElementById('master-volume-holder-float') == null) { nickjillings@1356: feedbackHolder.appendChild(interfaceContext.volume.object); nickjillings@1356: } nickjillings@1356: break; n@2407: case "comments": n@2407: // Generate one comment box per presented page nicholas@2576: for (var element of audioEngineContext.audioObjects) { n@2407: interfaceContext.commentBoxes.createCommentBox(element); n@2407: } nicholas@2576: interfaceContext.commentBoxes.showCommentBoxes(commentHolder, true); n@2407: break; nickjillings@1356: } nickjillings@1356: } nickjillings@1356: } nicholas@2576: nicholas@2576: $(audioHolderObject.commentQuestions).each(function (index, element) { nicholas@2576: var node = interfaceContext.createCommentQuestion(element); nicholas@2576: commentHolder.appendChild(node.holder); nicholas@2576: }); nicholas@2576: nicholas@2576: resizeWindow(null); nickjillings@1341: } nickjillings@1341: nicholas@2576: function comparator(audioHolderObject) { nicholas@2576: this.comparatorBox = function (audioElement, id, text) { nicholas@2576: this.parent = audioElement; nicholas@2576: this.id = id; nicholas@2576: this.value = 0; nicholas@2576: this.disabled = true; nicholas@2576: this.box = document.createElement('div'); nicholas@2576: this.box.className = 'comparator-holder'; nicholas@2576: this.box.setAttribute('track-id', audioElement.id); nicholas@2576: this.box.id = 'comparator-' + text; nicholas@2576: this.selector = document.createElement('div'); nicholas@2576: this.selector.className = 'comparator-selector disabled'; nicholas@2576: var selectorText = document.createElement('span'); nicholas@2576: selectorText.textContent = text; nicholas@2576: this.selector.appendChild(selectorText); nicholas@2576: this.playback = document.createElement('button'); nicholas@2576: this.playback.className = 'comparator-button'; nicholas@2576: this.playback.disabled = true; nicholas@2576: this.playback.textContent = "Listen"; nicholas@2576: this.box.appendChild(this.selector); nicholas@2576: this.box.appendChild(this.playback); nicholas@2576: this.selector.onclick = function (event) { nicholas@2576: var time = audioEngineContext.timer.getTestTime(); nicholas@2576: if ($(event.currentTarget).hasClass('disabled')) { nicholas@2576: console.log("Please wait until sample has loaded"); nicholas@2576: return; nickjillings@2112: } nicholas@2576: if (audioEngineContext.status == 0) { nicholas@2576: interfaceContext.lightbox.post("Message", "Please listen to the samples before making a selection"); nicholas@2576: console.log("Please listen to the samples before making a selection"); nicholas@2576: return; nicholas@2576: } nicholas@2576: var id = event.currentTarget.parentElement.getAttribute('track-id'); nicholas@2576: interfaceContext.comparator.selected = id; nickjillings@2112: if ($(event.currentTarget).hasClass("selected")) { nickjillings@2112: $(".comparator-selector").removeClass('selected'); nicholas@2576: for (var i = 0; i < interfaceContext.comparator.comparators.length; i++) { nicholas@2307: var obj = interfaceContext.comparator.comparators[i]; nicholas@2576: obj.parent.metric.moved(time, 0); nicholas@2307: obj.value = 0; nickjillings@2112: } nickjillings@2112: } else { nickjillings@2112: $(".comparator-selector").removeClass('selected'); nickjillings@2112: $(event.currentTarget).addClass('selected'); nicholas@2576: for (var i = 0; i < interfaceContext.comparator.comparators.length; i++) { nickjillings@2112: var obj = interfaceContext.comparator.comparators[i]; nickjillings@2112: if (i == id) { nickjillings@2112: obj.value = 1; nickjillings@2112: } else { nickjillings@2112: obj.value = 0; nickjillings@2112: } nicholas@2576: obj.parent.metric.moved(time, obj.value); nickjillings@2112: } nicholas@2576: console.log("Selected " + id + ' (' + time + ')'); nickjillings@2112: } nicholas@2576: }; nicholas@2576: this.playback.setAttribute("playstate", "ready"); nicholas@2576: this.playback.onclick = function (event) { nicholas@2576: var id = event.currentTarget.parentElement.getAttribute('track-id'); nicholas@2576: if (event.currentTarget.getAttribute("playstate") == "ready") { nickjillings@1376: audioEngineContext.play(id); nickjillings@1376: } else if (event.currentTarget.getAttribute("playstate") == "playing") { nickjillings@1376: audioEngineContext.stop(); nickjillings@1376: } nicholas@2576: nicholas@2576: }; nicholas@2576: nicholas@2576: this.enable = function () { nicholas@2576: if (this.parent.state == 1) { nicholas@2576: $(this.selector).removeClass('disabled'); nicholas@2576: this.playback.disabled = false; nicholas@2576: } nicholas@2576: }; nicholas@2576: this.updateLoading = function (progress) { nicholas@2576: if (progress != 100) { nicholas@2576: progress = String(progress); nicholas@2576: progress = progress.split('.')[0]; nicholas@2576: this.playback.textContent = progress + '%'; nicholas@2576: } else { nicholas@2576: this.playback.textContent = "Play"; nicholas@2576: } nicholas@2576: }; nicholas@2576: this.error = function () { nickjillings@2113: // audioObject has an error!! nickjillings@2113: this.playback.textContent = "Error"; nickjillings@2113: $(this.playback).addClass("error-colour"); nickjillings@2113: } nicholas@2576: this.startPlayback = function () { n@2426: if (this.parent.specification.parent.playOne || specification.playOne) { n@2426: $('.comparator-button').text('Wait'); nicholas@2576: <<<<<<< HEAD n@2426: $('.comparator-button').attr("disabled","true"); nicholas@2503: $(this.playback).removeAttr("disabled"); nicholas@2576: ======= nicholas@2576: $('.comparator-button').attr("disabled", "true"); nicholas@2576: $(this.playback).css("disabled", "false"); nicholas@2576: >>>>>>> 0425ea2... Formatting of AB. WIP for #37 n@2426: } else { n@2426: $('.comparator-button').text('Listen'); n@2426: } nickjillings@1376: $(this.playback).text('Stop'); nicholas@2576: this.playback.setAttribute("playstate", "playing"); nickjillings@1360: }; nicholas@2576: this.stopPlayback = function () { n@2426: if (this.playback.getAttribute("playstate") == "playing") { n@2426: $('.comparator-button').text('Listen'); n@2426: $('.comparator-button').removeAttr("disabled"); nicholas@2576: this.playback.setAttribute("playstate", "ready"); n@2426: } nickjillings@1360: }; nicholas@2576: this.exportXMLDOM = function (audioObject) { nicholas@2576: var node = storage.document.createElement('value'); nicholas@2576: node.textContent = this.value; nicholas@2576: return node; nicholas@2576: }; nicholas@2576: this.getValue = function () { nicholas@2576: return this.value; nicholas@2576: }; nicholas@2576: this.getPresentedId = function () { nicholas@2576: return this.selector.children[0].textContent; nicholas@2576: }; nicholas@2576: this.canMove = function () { nicholas@2576: return false; nicholas@2576: }; nicholas@2576: }; nicholas@2576: nicholas@2576: this.boxHolders = document.getElementById('box-holders'); nicholas@2576: this.boxHolders.innerHTML = ""; nicholas@2576: this.comparators = []; nicholas@2576: this.selected = null; nicholas@2576: nicholas@2576: // First generate the Audio Objects for the Audio Engine nicholas@2576: var label = audioHolderObject.labelStart; nicholas@2576: if (label == "") { nicholas@2576: switch (audioHolderObject.label) { nicholas@2576: case "number": nicholas@2576: label = "1"; nicholas@2576: break; nicholas@2576: case "letter": nicholas@2576: label = "a"; nicholas@2576: break; nicholas@2576: case "none": nicholas@2576: label = ""; nicholas@2576: break; nicholas@2576: case "capital": nicholas@2576: default: nicholas@2576: label = "A"; nicholas@2576: break; nicholas@2576: } nicholas@2576: } else { nicholas@2576: switch (audioHolderObject.label) { nicholas@2576: case "number": nicholas@2576: if (!isFinite(Number(label))) { nicholas@2576: label = "1"; nicholas@2576: } nicholas@2576: break; nicholas@2576: case "letter": nicholas@2576: if (label.charCodeAt(0) < 97 || label.charCodeAt(0) > 122) { nicholas@2576: label = "a"; nicholas@2576: } nicholas@2576: break; nicholas@2576: case "none": nicholas@2576: label = ""; nicholas@2576: break; nicholas@2576: case "capital": nicholas@2576: default: nicholas@2576: if (label.charCodeAt(0) < 65 || label.charCodeAt(0) > 90) { nicholas@2576: label = "A"; nicholas@2576: } nicholas@2576: break; nicholas@2576: } nicholas@2576: } nicholas@2576: for (var index = 0; index < audioHolderObject.audioElements.length; index++) { nicholas@2576: var element = audioHolderObject.audioElements[index]; nickjillings@2177: var audioObject = audioEngineContext.newTrack(element); nicholas@2576: if (index == audioHolderObject.outsideReference || element.type == 'outside-reference') { nicholas@2576: var orNode = new interfaceContext.outsideReferenceDOM(audioObject, index, document.getElementById("outside-reference-holder")); nicholas@2576: audioObject.bindInterface(orNode); nickjillings@2177: } else { nicholas@2576: var node = new this.comparatorBox(audioObject, index, label); nicholas@2576: switch (audioHolderObject.label) { nickjillings@2177: case "none": nickjillings@2177: label = ""; nickjillings@2177: break; nickjillings@2177: case "number": nicholas@2576: label = (Number(label) + 1).toString(10); nickjillings@2177: break; nickjillings@2177: case "letter": nicholas@2576: label = String.fromCharCode((label.charCodeAt(0) - 96) % 26 + 97); nickjillings@2177: break; nicholas@2576: case "capital": nickjillings@2177: default: nicholas@2576: label = String.fromCharCode((label.charCodeAt(0) - 64) % 26 + 65); nickjillings@2177: break; nickjillings@2177: } nickjillings@2177: audioObject.bindInterface(node); nickjillings@2177: this.comparators.push(node); nickjillings@2177: this.boxHolders.appendChild(node.box); nickjillings@1295: } nicholas@2576: } nicholas@2576: return this; nickjillings@1341: } nickjillings@1341: nicholas@2576: function resizeWindow(event) { nicholas@2576: document.getElementById('submit').style.left = (window.innerWidth - 250) / 2 + 'px'; nicholas@2576: var numObj = interfaceContext.comparator.comparators.length; nicholas@2576: var boxW = numObj * 312; nickjillings@1341: var diff = window.innerWidth - boxW; nicholas@2576: while (diff < 0) { nicholas@2576: numObj = Math.ceil(numObj / 2); nicholas@2576: boxW = numObj * 312; nickjillings@1341: diff = window.innerWidth - boxW; nickjillings@1341: } nicholas@2576: document.getElementById('box-holders').style.marginLeft = diff / 2 + 'px'; nicholas@2576: document.getElementById('box-holders').style.marginRight = diff / 2 + 'px'; nickjillings@1341: document.getElementById('box-holders').style.width = boxW + 'px'; nicholas@2576: nickjillings@2177: var outsideRef = document.getElementById('outside-reference'); nicholas@2576: if (outsideRef != null) { nicholas@2576: outsideRef.style.left = (window.innerWidth - 120) / 2 + 'px'; nicholas@2576: } nickjillings@1341: } nickjillings@1341: nicholas@2576: function buttonSubmitClick() { nicholas@2576: var checks = []; nicholas@2576: checks = checks.concat(testState.currentStateMap.interfaces[0].options); nicholas@2576: checks = checks.concat(specification.interfaces.options); nicholas@2576: var canContinue = true; nickjillings@1341: nicholas@2576: for (var i = 0; i < checks.length; i++) { nicholas@2576: if (checks[i].type == 'check') { nicholas@2576: switch (checks[i].name) { nicholas@2576: case 'fragmentPlayed': nicholas@2576: // Check if all fragments have been played nicholas@2576: var checkState = interfaceContext.checkAllPlayed(); nicholas@2576: if (checkState == false) { nicholas@2576: canContinue = false; nicholas@2576: } nicholas@2576: break; nicholas@2576: case 'fragmentFullPlayback': nicholas@2576: // Check all fragments have been played to their full length nicholas@2576: var checkState = interfaceContext.checkFragmentsFullyPlayed(); nicholas@2576: if (checkState == false) { nicholas@2576: canContinue = false; nicholas@2576: } nicholas@2576: break; nicholas@2576: case 'fragmentMoved': nicholas@2576: // Check all fragment sliders have been moved. nicholas@2576: var checkState = interfaceContext.checkAllMoved(); nicholas@2576: if (checkState == false) { nicholas@2576: canContinue = false; nicholas@2576: } nicholas@2576: break; nicholas@2576: case 'fragmentComments': nicholas@2576: // Check all fragment sliders have been moved. nicholas@2576: var checkState = interfaceContext.checkAllCommented(); nicholas@2576: if (checkState == false) { nicholas@2576: canContinue = false; nicholas@2576: } nicholas@2576: break; nicholas@2576: case 'scalerange': nicholas@2576: // Check the scale has been used effectively nicholas@2576: var checkState = interfaceContext.checkScaleRange(checks[i].min, checks[i].max); nicholas@2576: if (checkState == false) { nicholas@2576: canContinue = false; nicholas@2576: } nicholas@2576: break; nicholas@2576: default: nicholas@2576: console.log("WARNING - Check option " + checks[i].check + " is not supported on this interface"); nicholas@2576: break; nicholas@2576: } nicholas@2576: nicholas@2576: } nicholas@2576: if (!canContinue) { nicholas@2576: break; nicholas@2576: } nicholas@2576: } nicholas@2576: if (canContinue) { nicholas@2576: if (audioEngineContext.status == 1) { nicholas@2576: var playback = document.getElementById('playback-button'); nicholas@2576: playback.click(); nicholas@2576: // This function is called when the submit button is clicked. Will check for any further tests to perform, or any post-test options nicholas@2576: } else { nicholas@2576: if (audioEngineContext.timer.testStarted == false) { nicholas@2576: interfaceContext.lightbox.post("Warning", 'You have not started the test! Please click play on a sample to begin the test!'); nicholas@2576: return; nicholas@2576: } nicholas@2576: } nicholas@2576: testState.advanceState(); nicholas@2576: } nickjillings@1341: } nickjillings@1341: nicholas@2576: function pageXMLSave(store, pageSpecification) { nicholas@2576: // MANDATORY nicholas@2576: // Saves a specific test page nicholas@2576: // You can use this space to add any extra nodes to your XML saves nicholas@2576: // Get the current information in store (remember to appendChild your data to it) nicholas@2576: // pageSpecification is the current page node configuration nicholas@2576: // To create new XML nodes, use storage.document.createElement(); nicholas@2576: }