me@1942: /** me@1942: * ape.js me@1942: * Create the APE interface me@1942: */ me@1942: me@1942: me@1942: // Once this is loaded and parsed, begin execution me@1942: loadInterface(); me@1942: me@1942: function loadInterface() { me@1942: me@1942: // Get the dimensions of the screen available to the page me@1942: var width = window.innerWidth; me@1942: var height = window.innerHeight; me@1942: me@1942: // The injection point into the HTML page me@1942: interfaceContext.insertPoint = document.getElementById("topLevelBody"); me@1942: var testContent = document.createElement('div'); me@1942: me@1942: testContent.id = 'testContent'; me@1942: me@1942: me@1942: // Create APE specific metric functions me@1942: audioEngineContext.metric.initialiseTest = function() me@1942: { me@1942: }; me@1942: me@1942: audioEngineContext.metric.sliderMoved = function() me@1942: { me@1942: var id = this.data; me@1942: this.data = -1; me@1942: var position = convSliderPosToRate(id); me@1942: console.log('slider ' + id + ': '+ position + ' (' + time + ')'); // DEBUG/SAFETY: show position and slider id me@1942: if (audioEngineContext.timer.testStarted) me@1942: { me@1942: audioEngineContext.audioObjects[id].metric.moved(time,position); me@1942: } me@1942: }; me@1942: me@1942: audioEngineContext.metric.sliderPlayed = function(id) me@1942: { me@1942: var time = audioEngineContext.timer.getTestTime(); me@1942: if (audioEngineContext.timer.testStarted) me@1942: { me@1942: if (this.lastClicked >= 0) me@1942: { me@1942: audioEngineContext.audioObjects[this.lastClicked].metric.listening(time); me@1942: } me@1942: this.lastClicked = id; me@1942: audioEngineContext.audioObjects[id].metric.listening(time); me@1942: } me@1942: console.log('slider ' + id + ' played (' + time + ')'); // DEBUG/SAFETY: show played slider id me@1942: }; me@1942: me@1942: // Bindings for interfaceContext me@1942: Interface.prototype.checkAllPlayed = function() me@1942: { me@1942: hasBeenPlayed = audioEngineContext.checkAllPlayed(); me@1942: if (hasBeenPlayed.length > 0) // if a fragment has not been played yet me@1942: { me@1942: str = ""; me@1942: if (hasBeenPlayed.length > 1) { me@1942: for (var i=0; i 1) { me@1942: var str = ""; me@1942: for (var i=0; i 1) { me@1942: var str = ""; me@1942: for (var i=0; i maxRanking) { maxRanking = ranking;} me@1942: } me@1942: } me@1942: if (minRanking > minScale || maxRanking < maxScale) { me@1942: alert('Please use the full width of the scale'); me@1942: return false; me@1942: } else { me@1942: return true; me@1942: } me@1942: }; me@1942: me@1942: // Bindings for audioObjects me@1942: me@1942: // Create the top div for the Title element me@1942: var titleAttr = specification.title; me@1942: var title = document.createElement('div'); me@1942: title.className = "title"; me@1942: title.align = "center"; me@1942: var titleSpan = document.createElement('span'); me@1942: me@1942: // Set title to that defined in XML, else set to default me@1942: if (titleAttr != undefined) { me@1942: titleSpan.textContent = titleAttr; me@1942: } else { me@1942: titleSpan.textContent = 'Listening test'; me@1942: } me@1942: // Insert the titleSpan element into the title div element. me@1942: title.appendChild(titleSpan); me@1942: me@1942: var pagetitle = document.createElement('div'); me@1942: pagetitle.className = "pageTitle"; me@1942: pagetitle.align = "center"; me@1942: var titleSpan = document.createElement('span'); me@1942: titleSpan.id = "pageTitle"; me@1942: pagetitle.appendChild(titleSpan); me@1942: me@1942: // Create Interface buttons! me@1942: var interfaceButtons = document.createElement('div'); me@1942: interfaceButtons.id = 'interface-buttons'; me@1942: me@1942: // Create playback start/stop points me@1942: var playback = document.createElement("button"); me@1942: playback.innerHTML = 'Stop'; me@1942: playback.id = 'playback-button'; me@1942: // onclick function. Check if it is playing or not, call the correct function in the me@1942: // audioEngine, change the button text to reflect the next state. me@1942: playback.onclick = function() { me@1942: if (audioEngineContext.status == 1) { me@1942: audioEngineContext.stop(); me@1942: this.innerHTML = 'Stop'; me@1942: var time = audioEngineContext.timer.getTestTime(); me@1942: console.log('Stopped at ' + time); // DEBUG/SAFETY me@1942: } me@1942: }; me@1942: // Create Submit (save) button me@1942: var submit = document.createElement("button"); me@1942: submit.innerHTML = 'Submit'; me@1942: submit.onclick = buttonSubmitClick; me@1942: submit.id = 'submit-button'; me@1942: // Append the interface buttons into the interfaceButtons object. me@1942: interfaceButtons.appendChild(playback); me@1942: interfaceButtons.appendChild(submit); me@1942: me@1942: // Now create the slider and HTML5 canvas boxes me@1942: me@1942: // Create the div box to center align me@1942: var sliderBox = document.createElement('div'); me@1942: sliderBox.className = 'sliderCanvasDiv'; me@1942: sliderBox.id = 'sliderCanvasHolder'; me@1942: me@1942: // Create the slider box to hold the slider elements me@1942: var canvas = document.createElement('div'); me@1942: canvas.id = 'slider'; me@1942: canvas.align = "left"; me@1942: canvas.addEventListener('dragover',function(event){ me@1942: event.preventDefault(); me@1942: event.dataTransfer.effectAllowed = 'none'; me@1942: event.dataTransfer.dropEffect = 'copy'; me@1942: return false; me@1942: },false); me@1942: var sliderMargin = document.createAttribute('marginsize'); me@1942: sliderMargin.nodeValue = 42; // Set default margins to 42px either side me@1942: // Must have a known EXACT width, as this is used later to determine the ratings me@1942: var w = (Number(sliderMargin.nodeValue)+8)*2; me@1942: canvas.style.width = width - w +"px"; me@1942: canvas.style.marginLeft = sliderMargin.nodeValue +'px'; me@1942: canvas.setAttributeNode(sliderMargin); me@1942: sliderBox.appendChild(canvas); me@1942: me@1942: // Create the div to hold any scale objects me@1942: var scale = document.createElement('div'); me@1942: scale.className = 'sliderScale'; me@1942: scale.id = 'sliderScaleHolder'; me@1942: scale.align = 'left'; me@1942: sliderBox.appendChild(scale); me@1942: me@1942: // Global parent for the comment boxes on the page me@1942: var feedbackHolder = document.createElement('div'); me@1942: feedbackHolder.id = 'feedbackHolder'; me@1942: me@1942: testContent.style.zIndex = 1; me@1942: interfaceContext.insertPoint.innerHTML = null; // Clear the current schema me@1942: me@1942: // Inject into HTML me@1942: testContent.appendChild(title); // Insert the title me@1942: testContent.appendChild(pagetitle); me@1942: testContent.appendChild(interfaceButtons); me@1942: testContent.appendChild(sliderBox); me@1942: testContent.appendChild(feedbackHolder); me@1942: interfaceContext.insertPoint.appendChild(testContent); me@1942: me@1942: // Load the full interface me@1942: testState.initialise(); me@1942: testState.advanceState(); me@1942: me@1942: } me@1942: me@1942: function loadTest(audioHolderObject) me@1942: { me@1942: me@1942: // Reset audioEngineContext.Metric globals for new test me@1942: audioEngineContext.newTestPage(); me@1942: me@1942: var id = audioHolderObject.id; me@1942: me@1942: var feedbackHolder = document.getElementById('feedbackHolder'); me@1942: var canvas = document.getElementById('slider'); me@1942: feedbackHolder.innerHTML = null; me@1942: canvas.innerHTML = null; me@1942: me@1942: var interfaceObj = audioHolderObject.interfaces; me@1942: for (var k=0; k'; me@1942: var inject = document.getElementById('interface-buttons'); me@1942: inject.appendChild(pagecountHolder); me@1942: } me@1942: } me@1942: } me@1942: // Setup question title me@1942: me@1942: var commentBoxPrefix = "Comment on track"; me@1942: if (interfaceObj.length != 0) { me@1942: interfaceObj = interfaceObj[0]; me@1942: var titleNode = interfaceObj.title; me@1942: if (titleNode != undefined) me@1942: { me@1942: document.getElementById('pageTitle').textContent = titleNode; me@1942: } me@1942: var positionScale = canvas.style.width.substr(0,canvas.style.width.length-2); me@1942: var offset = Number(document.getElementById('slider').attributes['marginsize'].value); me@1942: var scale = document.getElementById('sliderScaleHolder'); me@1942: scale.innerHTML = null; me@1942: $(interfaceObj.scale).each(function(index,scaleObj){ me@1942: var value = document.createAttribute('value'); me@1942: var position = Number(scaleObj[0])*0.01; me@1942: value.nodeValue = position; me@1942: var pixelPosition = (position*positionScale)+offset; me@1942: var scaleDOM = document.createElement('span'); me@1942: scaleDOM.textContent = scaleObj[1]; me@1942: scale.appendChild(scaleDOM); me@1942: scaleDOM.style.left = Math.floor((pixelPosition-($(scaleDOM).width()/2)))+'px'; me@1942: scaleDOM.setAttributeNode(value); me@1942: }); me@1942: me@1942: if (interfaceObj.commentBoxPrefix != undefined) { me@1942: commentBoxPrefix = interfaceObj.commentBoxPrefix; me@1942: } me@1942: } me@1942: me@1942: /// CHECK FOR SAMPLE RATE COMPATIBILITY me@1942: if (audioHolderObject.sampleRate != undefined) { me@1942: if (Number(audioHolderObject.sampleRate) != audioContext.sampleRate) { me@1942: var errStr = 'Sample rates do not match! Requested '+Number(audioHolderObject.sampleRate)+', got '+audioContext.sampleRate+'. Please set the sample rate to match before completing this test.'; me@1942: alert(errStr); me@1942: return; me@1942: } me@1942: } me@1942: me@1942: var commentShow = audioHolderObject.elementComments; me@1942: me@1942: var loopPlayback = audioHolderObject.loop; me@1942: me@1942: audioEngineContext.loopPlayback = loopPlayback; me@1942: // Create AudioEngine bindings for playback me@1942: audioEngineContext.selectedTrack = function(id) { me@1942: console.log('Deprecated'); me@1942: }; me@1942: me@1942: currentTestHolder = document.createElement('audioHolder'); me@1942: currentTestHolder.id = audioHolderObject.id; me@1942: currentTestHolder.repeatCount = audioHolderObject.repeatCount; me@1942: me@1942: // Delete any previous audioObjects associated with the audioEngine me@1942: audioEngineContext.audioObjects = []; me@1942: interfaceContext.deleteCommentBoxes(); me@1942: interfaceContext.deleteCommentQuestions(); me@1942: me@1942: // Find all the audioElements from the audioHolder me@1942: $(audioHolderObject.audioElements).each(function(index,element){ me@1942: // Find URL of track me@1942: // In this jQuery loop, variable 'this' holds the current audioElement. me@1942: me@1942: // Now load each audio sample. First create the new track by passing the full URL me@1942: var trackURL = audioHolderObject.hostURL + element.url; me@1942: var audioObject = audioEngineContext.newTrack(element); me@1942: me@1942: var node = interfaceContext.createCommentBox(audioObject); me@1942: me@1942: // Create a slider per track me@1942: audioObject.interfaceDOM = new sliderObject(audioObject); me@1942: me@1942: // Distribute it randomnly me@1942: var w = window.innerWidth - (offset+8)*2; me@1942: w = Math.random()*w; me@1942: w = Math.floor(w+(offset+8)); me@1942: audioObject.interfaceDOM.trackSliderObj.style.left = w+'px'; me@1942: me@1942: canvas.appendChild(audioObject.interfaceDOM.trackSliderObj); me@1942: audioObject.metric.initialised(convSliderPosToRate(audioObject.interfaceDOM.trackSliderObj)); me@1942: me@1942: }); me@1942: if (commentShow) { me@1942: interfaceContext.showCommentBoxes(feedbackHolder,true); me@1942: } me@1942: me@1942: $(audioHolderObject.commentQuestions).each(function(index,element) { me@1942: var node = interfaceContext.createCommentQuestion(element); me@1942: feedbackHolder.appendChild(node.holder); me@1942: }); me@1942: me@1942: // Construct outside reference; me@1942: if (audioHolderObject.outsideReference != null) { me@1942: var outsideReferenceHolder = document.createElement('div'); me@1942: outsideReferenceHolder.id = 'outside-reference'; me@1942: outsideReferenceHolderspan = document.createElement('span'); me@1942: outsideReferenceHolderspan.textContent = 'Reference'; me@1942: outsideReferenceHolder.appendChild(outsideReferenceHolderspan); me@1942: me@1942: var audioObject = audioEngineContext.newTrack(audioHolderObject.outsideReference); me@1942: me@1942: outsideReferenceHolder.onclick = function(event) me@1942: { me@1942: audioEngineContext.play(audioEngineContext.audioObjects.length-1); me@1942: $('.track-slider').removeClass('track-slider-playing'); me@1942: $('.comment-div').removeClass('comment-box-playing'); me@1942: if (event.currentTarget.nodeName == 'DIV') { me@1942: $(event.currentTarget).addClass('track-slider-playing'); me@1942: } else { me@1942: $(event.currentTarget.parentElement).addClass('track-slider-playing'); me@1942: } me@1942: }; me@1942: me@1942: document.getElementById('interface-buttons').appendChild(outsideReferenceHolder); me@1942: } me@1942: me@1942: me@1942: //testWaitIndicator(); me@1942: } me@1942: me@1942: function sliderObject(audioObject) { me@1942: // Create a new slider object; me@1942: this.parent = audioObject; me@1942: this.trackSliderObj = document.createElement('div'); me@1942: this.trackSliderObj.className = 'track-slider track-slider-disabled'; me@1942: this.trackSliderObj.id = 'track-slider-'+audioObject.id; me@1942: me@1942: this.trackSliderObj.setAttribute('trackIndex',audioObject.id); me@1942: this.trackSliderObj.innerHTML = ''+audioObject.id+''; me@1942: this.trackSliderObj.draggable = true; me@1942: this.trackSliderObj.ondragend = dragEnd; me@1942: me@1942: this.trackSliderObj.ondragstart = function(event){ me@1942: event.dataTransfer.setData('Text',null); me@1942: }; me@1942: me@1942: this.trackSliderObj.ondrop = function(event) me@1942: { me@1942: if(event.stopPropagation) {event.stopPropagation();} me@1942: return false; me@1942: }; me@1942: me@1942: // Onclick, switch playback to that track me@1942: this.trackSliderObj.onclick = function(event) { me@1942: // Cannot continue to issue play command until audioObjects reported as ready! me@1942: // Get the track ID from the object ID me@1942: var element; me@1942: if (event.currentTarget.nodeName == "SPAN") { me@1942: element = event.currentTarget.parentNode; me@1942: } else { me@1942: element = event.currentTarget; me@1942: } me@1942: var id = Number(element.attributes['trackIndex'].value); me@1942: //audioEngineContext.metric.sliderPlayed(id); me@1942: audioEngineContext.play(id); me@1942: // Currently playing track red, rest green me@1942: me@1942: //document.getElementById('track-slider-'+index).style.backgroundColor = "#FF0000"; me@1942: $('.track-slider').removeClass('track-slider-playing'); me@1942: $(element).addClass('track-slider-playing'); me@1942: $('.comment-div').removeClass('comment-box-playing'); me@1942: $('#comment-div-'+id).addClass('comment-box-playing'); me@1942: var outsideReference = document.getElementById('outside-reference'); me@1942: if (outsideReference != undefined) me@1942: $(outsideReference).removeClass('track-slider-playing'); me@1942: }; me@1942: me@1942: this.enable = function() { me@1942: if (this.parent.state == 1) me@1942: { me@1942: $(this.trackSliderObj).removeClass('track-slider-disabled'); me@1942: } me@1942: }; me@1942: me@1942: this.exportXMLDOM = function(audioObject) { me@1942: // Called by the audioObject holding this element. Must be present me@1942: var node = document.createElement('value'); me@1942: node.textContent = convSliderPosToRate(this.trackSliderObj); me@1942: return node; me@1942: }; me@1942: this.getValue = function() { me@1942: return convSliderPosToRate(this.trackSliderObj); me@1942: }; me@1942: } me@1942: me@1942: function dragEnd(ev) { me@1942: // Function call when a div has been dropped me@1942: var slider = document.getElementById('slider'); me@1942: var marginSize = Number(slider.attributes['marginsize'].value); me@1942: var w = slider.style.width; me@1942: w = Number(w.substr(0,w.length-2)); me@1942: var x = ev.screenX; me@1942: me@1942: x += Math.abs(window.screenX); me@1942: x = x % window.outerWidth; me@1942: me@1942: if (x >= marginSize && x < w+marginSize) { me@1942: this.style.left = (x)+'px'; me@1942: } else { me@1942: if (x