djmoffat@1099: /** djmoffat@1099: * ape.js djmoffat@1099: * Create the APE interface djmoffat@1099: */ djmoffat@1099: djmoffat@1099: djmoffat@1099: // Once this is loaded and parsed, begin execution djmoffat@1099: loadInterface(); djmoffat@1099: djmoffat@1099: function loadInterface() { djmoffat@1099: djmoffat@1099: // Get the dimensions of the screen available to the page djmoffat@1099: var width = window.innerWidth; djmoffat@1099: var height = window.innerHeight; djmoffat@1099: djmoffat@1099: // The injection point into the HTML page djmoffat@1099: interfaceContext.insertPoint = document.getElementById("topLevelBody"); djmoffat@1099: var testContent = document.createElement('div'); djmoffat@1099: djmoffat@1099: testContent.id = 'testContent'; djmoffat@1099: djmoffat@1099: // Bindings for interfaceContext djmoffat@1099: interfaceContext.checkAllPlayed = function() djmoffat@1099: { djmoffat@1099: hasBeenPlayed = audioEngineContext.checkAllPlayed(); djmoffat@1099: if (hasBeenPlayed.length > 0) // if a fragment has not been played yet djmoffat@1099: { djmoffat@1099: str = ""; djmoffat@1099: if (hasBeenPlayed.length > 1) { djmoffat@1099: for (var i=0; i 1) { djmoffat@1099: var str = ""; djmoffat@1099: for (var i=0; i maxRanking) djmoffat@1099: { djmoffat@1099: maxRanking = ranking; djmoffat@1099: } djmoffat@1099: } djmoffat@1099: if (minRanking > minScale || maxRanking < maxScale) djmoffat@1099: { djmoffat@1099: state = false; djmoffat@1099: str += 'On axis "'+this.interfaceSliders[i].interfaceObject.title+'" you have not used the full width of the scale. '; djmoffat@1099: } djmoffat@1099: } djmoffat@1099: if (state != true) djmoffat@1099: { djmoffat@1099: alert(str); djmoffat@1099: console.log(str); djmoffat@1099: } djmoffat@1099: return state; djmoffat@1099: }; djmoffat@1099: djmoffat@1099: Interface.prototype.objectSelected = null; djmoffat@1099: Interface.prototype.objectMoved = false; djmoffat@1099: Interface.prototype.selectObject = function(object) djmoffat@1099: { djmoffat@1099: if (this.objectSelected == null) djmoffat@1099: { djmoffat@1099: this.objectSelected = object; djmoffat@1099: this.objectMoved = false; djmoffat@1099: } djmoffat@1099: }; djmoffat@1099: Interface.prototype.moveObject = function() djmoffat@1099: { djmoffat@1099: if (this.objectMoved == false) djmoffat@1099: { djmoffat@1099: this.objectMoved = true; djmoffat@1099: } djmoffat@1099: }; djmoffat@1099: Interface.prototype.releaseObject = function() djmoffat@1099: { djmoffat@1099: this.objectSelected = null; djmoffat@1099: this.objectMoved = false; djmoffat@1099: }; djmoffat@1099: Interface.prototype.getSelectedObject = function() djmoffat@1099: { djmoffat@1099: return this.objectSelected; djmoffat@1099: }; djmoffat@1099: Interface.prototype.hasSelectedObjectMoved = function() djmoffat@1099: { djmoffat@1099: return this.objectMoved; djmoffat@1099: }; djmoffat@1099: djmoffat@1099: // Bindings for slider interfaces djmoffat@1099: Interface.prototype.interfaceSliders = []; djmoffat@1099: djmoffat@1099: // Bindings for audioObjects djmoffat@1099: djmoffat@1099: // Create the top div for the Title element djmoffat@1099: var titleAttr = specification.title; djmoffat@1099: var title = document.createElement('div'); djmoffat@1099: title.className = "title"; djmoffat@1099: title.align = "center"; djmoffat@1099: var titleSpan = document.createElement('span'); djmoffat@1099: djmoffat@1099: // Set title to that defined in XML, else set to default djmoffat@1099: if (titleAttr != undefined) { djmoffat@1099: titleSpan.textContent = titleAttr; djmoffat@1099: } else { djmoffat@1099: titleSpan.textContent = 'Listening test'; djmoffat@1099: } djmoffat@1099: // Insert the titleSpan element into the title div element. djmoffat@1099: title.appendChild(titleSpan); djmoffat@1099: djmoffat@1099: // Create Interface buttons! djmoffat@1099: var interfaceButtons = document.createElement('div'); djmoffat@1099: interfaceButtons.id = 'interface-buttons'; djmoffat@1099: djmoffat@1099: // Create playback start/stop points djmoffat@1099: var playback = document.createElement("button"); djmoffat@1099: playback.innerHTML = 'Stop'; djmoffat@1099: playback.id = 'playback-button'; djmoffat@1099: // onclick function. Check if it is playing or not, call the correct function in the djmoffat@1099: // audioEngine, change the button text to reflect the next state. djmoffat@1099: playback.onclick = function() { djmoffat@1099: if (audioEngineContext.status == 1) { djmoffat@1099: audioEngineContext.stop(); djmoffat@1099: this.innerHTML = 'Stop'; djmoffat@1099: var time = audioEngineContext.timer.getTestTime(); djmoffat@1099: console.log('Stopped at ' + time); // DEBUG/SAFETY djmoffat@1099: } djmoffat@1099: }; djmoffat@1099: // Create Submit (save) button djmoffat@1099: var submit = document.createElement("button"); djmoffat@1099: submit.innerHTML = 'Submit'; djmoffat@1099: submit.onclick = buttonSubmitClick; djmoffat@1099: submit.id = 'submit-button'; djmoffat@1099: // Append the interface buttons into the interfaceButtons object. djmoffat@1099: interfaceButtons.appendChild(playback); djmoffat@1099: interfaceButtons.appendChild(submit); djmoffat@1099: djmoffat@1099: var sliderHolder = document.createElement("div"); djmoffat@1099: sliderHolder.id = "slider-holder"; djmoffat@1099: djmoffat@1099: djmoffat@1099: // Global parent for the comment boxes on the page djmoffat@1099: var feedbackHolder = document.createElement('div'); djmoffat@1099: feedbackHolder.id = 'feedbackHolder'; djmoffat@1099: djmoffat@1099: testContent.style.zIndex = 1; djmoffat@1099: interfaceContext.insertPoint.innerHTML = null; // Clear the current schema djmoffat@1099: djmoffat@1099: // Inject into HTML djmoffat@1099: testContent.appendChild(title); // Insert the title djmoffat@1099: testContent.appendChild(interfaceButtons); djmoffat@1099: testContent.appendChild(sliderHolder); djmoffat@1099: testContent.appendChild(feedbackHolder); djmoffat@1099: interfaceContext.insertPoint.appendChild(testContent); djmoffat@1099: djmoffat@1099: // Load the full interface djmoffat@1099: testState.initialise(); djmoffat@1099: testState.advanceState(); djmoffat@1099: djmoffat@1099: } djmoffat@1099: djmoffat@1099: function loadTest(audioHolderObject) djmoffat@1099: { djmoffat@1099: var width = window.innerWidth; djmoffat@1099: var height = window.innerHeight; djmoffat@1099: var id = audioHolderObject.id; djmoffat@1099: djmoffat@1099: interfaceContext.interfaceSliders = []; djmoffat@1099: djmoffat@1099: var feedbackHolder = document.getElementById('feedbackHolder'); djmoffat@1099: var sliderHolder = document.getElementById('slider-holder'); djmoffat@1099: feedbackHolder.innerHTML = null; djmoffat@1099: sliderHolder.innerHTML = null; djmoffat@1099: djmoffat@1099: // Delete outside reference djmoffat@1099: var outsideReferenceHolder = document.getElementById('outside-reference'); djmoffat@1099: if (outsideReferenceHolder != null) { djmoffat@1099: document.getElementById('interface-buttons').removeChild(outsideReferenceHolder); djmoffat@1099: } djmoffat@1099: djmoffat@1099: var interfaceObj = audioHolderObject.interfaces; djmoffat@1099: for (var k=0; k'; djmoffat@1099: var inject = document.getElementById('interface-buttons'); djmoffat@1099: inject.appendChild(pagecountHolder); djmoffat@1099: } else if (interfaceList[k].options[i].type == 'show' && interfaceList[k].options[i].name == 'volume') { djmoffat@1099: if (document.getElementById('master-volume-holder') == null) djmoffat@1099: { djmoffat@1099: feedbackHolder.appendChild(interfaceContext.volume.object); djmoffat@1099: } djmoffat@1099: } djmoffat@1099: } djmoffat@1099: } djmoffat@1099: djmoffat@1099: var commentBoxPrefix = "Comment on fragment"; djmoffat@1099: djmoffat@1099: var commentShow = audioHolderObject.elementComments; djmoffat@1099: djmoffat@1099: var loopPlayback = audioHolderObject.loop; djmoffat@1099: djmoffat@1099: currentTestHolder = document.createElement('audioHolder'); djmoffat@1099: currentTestHolder.id = audioHolderObject.id; djmoffat@1099: currentTestHolder.repeatCount = audioHolderObject.repeatCount; djmoffat@1099: djmoffat@1099: // Find all the audioElements from the audioHolder djmoffat@1099: $(audioHolderObject.audioElements).each(function(index,element){ djmoffat@1099: // Find URL of track djmoffat@1099: // In this jQuery loop, variable 'this' holds the current audioElement. djmoffat@1099: var audioObject = audioEngineContext.newTrack(element); djmoffat@1099: // Check if an outside reference djmoffat@1099: if (element.type == 'outside-reference') djmoffat@1099: { djmoffat@1099: // Construct outside reference; djmoffat@1099: var orNode = new outsideReferenceDOM(audioObject,index,document.getElementById('interface-buttons')); djmoffat@1099: audioObject.bindInterface(orNode); djmoffat@1099: } else { djmoffat@1099: // Create a slider per track djmoffat@1099: var sliderNode = new sliderObject(audioObject,interfaceObj); djmoffat@1099: audioObject.bindInterface(sliderNode); n@1102: interfaceContext.commentBoxes.createCommentBox(audioObject); djmoffat@1099: } djmoffat@1099: }); djmoffat@1099: djmoffat@1099: // Initialse the interfaceSlider object metrics djmoffat@1099: djmoffat@1099: $('.track-slider').mousedown(function(event) { djmoffat@1099: interfaceContext.selectObject($(this)[0]); djmoffat@1099: }); djmoffat@1099: $('.track-slider').on('touchstart',null,function(event) { djmoffat@1099: interfaceContext.selectObject($(this)[0]); djmoffat@1099: }); djmoffat@1099: djmoffat@1099: $('.track-slider').mousemove(function(event) { djmoffat@1099: event.preventDefault(); djmoffat@1099: }); djmoffat@1099: djmoffat@1099: $('.slider').mousemove(function(event) { djmoffat@1099: event.preventDefault(); djmoffat@1099: var obj = interfaceContext.getSelectedObject(); djmoffat@1099: if (obj == null) {return;} djmoffat@1099: $(obj).css("left",event.clientX-6 + "px"); djmoffat@1099: interfaceContext.moveObject(); djmoffat@1099: }); djmoffat@1099: djmoffat@1099: $('.slider').on('touchmove',null,function(event) { djmoffat@1099: event.preventDefault(); djmoffat@1099: var obj = interfaceContext.getSelectedObject(); djmoffat@1099: if (obj == null) {return;} djmoffat@1099: var move = event.originalEvent.targetTouches[0].clientX - 6; djmoffat@1099: $(obj).css("left",move + "px"); djmoffat@1099: interfaceContext.moveObject(); djmoffat@1099: }); djmoffat@1099: djmoffat@1099: $(document).mouseup(function(event){ djmoffat@1099: event.preventDefault(); djmoffat@1099: var obj = interfaceContext.getSelectedObject(); djmoffat@1099: if (obj == null) {return;} djmoffat@1099: var interfaceID = obj.parentElement.getAttribute("interfaceid"); djmoffat@1099: var trackID = obj.getAttribute("trackindex"); djmoffat@1099: if (interfaceContext.hasSelectedObjectMoved() == true) djmoffat@1099: { djmoffat@1099: var l = $(obj).css("left"); djmoffat@1099: var id = obj.getAttribute('trackIndex'); djmoffat@1099: var time = audioEngineContext.timer.getTestTime(); djmoffat@1099: var rate = convSliderPosToRate(obj); djmoffat@1099: audioEngineContext.audioObjects[id].metric.moved(time,rate); djmoffat@1099: interfaceContext.interfaceSliders[interfaceID].metrics[trackID].moved(time,rate); djmoffat@1099: console.log("slider "+id+" moved to "+rate+' ('+time+')'); djmoffat@1099: } else { djmoffat@1099: var id = Number(obj.attributes['trackIndex'].value); djmoffat@1099: //audioEngineContext.metric.sliderPlayed(id); djmoffat@1099: audioEngineContext.play(id); djmoffat@1099: } djmoffat@1099: interfaceContext.releaseObject(); djmoffat@1099: }); djmoffat@1099: djmoffat@1099: $('.slider').on('touchend',null,function(event){ djmoffat@1099: var obj = interfaceContext.getSelectedObject(); djmoffat@1099: if (obj == null) {return;} djmoffat@1099: var interfaceID = obj.parentElement.getAttribute("interfaceid"); djmoffat@1099: var trackID = obj.getAttribute("trackindex"); djmoffat@1099: if (interfaceContext.hasSelectedObjectMoved() == true) djmoffat@1099: { djmoffat@1099: var l = $(obj).css("left"); djmoffat@1099: var id = obj.getAttribute('trackIndex'); djmoffat@1099: var time = audioEngineContext.timer.getTestTime(); djmoffat@1099: var rate = convSliderPosToRate(obj); djmoffat@1099: audioEngineContext.audioObjects[id].metric.moved(time,rate); djmoffat@1099: interfaceContext.interfaceSliders[interfaceID].metrics[trackID].moved(time,rate); djmoffat@1099: console.log("slider "+id+" moved to "+rate+' ('+time+')'); djmoffat@1099: } djmoffat@1099: interfaceContext.releaseObject(); djmoffat@1099: }); djmoffat@1099: djmoffat@1099: djmoffat@1099: if (audioHolderObject.showElementComments) { n@1102: interfaceContext.commentBoxes.showCommentBoxes(feedbackHolder,true); djmoffat@1099: } djmoffat@1099: djmoffat@1099: $(audioHolderObject.commentQuestions).each(function(index,element) { djmoffat@1099: var node = interfaceContext.createCommentQuestion(element); djmoffat@1099: feedbackHolder.appendChild(node.holder); djmoffat@1099: }); djmoffat@1099: djmoffat@1099: djmoffat@1099: //testWaitIndicator(); djmoffat@1099: } djmoffat@1099: djmoffat@1099: function interfaceSliderHolder(interfaceObject) djmoffat@1099: { djmoffat@1099: this.sliders = []; djmoffat@1099: this.metrics = []; djmoffat@1099: this.id = document.getElementsByClassName("sliderCanvasDiv").length; djmoffat@1099: this.name = interfaceObject.name; djmoffat@1099: this.interfaceObject = interfaceObject; djmoffat@1099: this.sliderDOM = document.createElement('div'); djmoffat@1099: this.sliderDOM.className = 'sliderCanvasDiv'; djmoffat@1099: this.sliderDOM.id = 'sliderCanvasHolder-'+this.id; djmoffat@1099: djmoffat@1099: var pagetitle = document.createElement('div'); djmoffat@1099: pagetitle.className = "pageTitle"; djmoffat@1099: pagetitle.align = "center"; djmoffat@1099: var titleSpan = document.createElement('span'); djmoffat@1099: titleSpan.id = "pageTitle-"+this.id; djmoffat@1099: if (interfaceObject.title != undefined && typeof interfaceObject.title == "string") djmoffat@1099: { djmoffat@1099: titleSpan.textContent = interfaceObject.title; djmoffat@1099: } else { djmoffat@1099: titleSpan.textContent = "Axis "+String(this.id+1); djmoffat@1099: } djmoffat@1099: pagetitle.appendChild(titleSpan); djmoffat@1099: this.sliderDOM.appendChild(pagetitle); djmoffat@1099: djmoffat@1099: // Create the slider box to hold the slider elements djmoffat@1099: this.canvas = document.createElement('div'); djmoffat@1099: if (this.name != undefined) djmoffat@1099: this.canvas.id = 'slider-'+this.name; djmoffat@1099: else djmoffat@1099: this.canvas.id = 'slider-'+this.id; djmoffat@1099: this.canvas.setAttribute("interfaceid",this.id); djmoffat@1099: this.canvas.className = 'slider'; djmoffat@1099: this.canvas.align = "left"; djmoffat@1099: this.canvas.addEventListener('dragover',function(event){ djmoffat@1099: event.preventDefault(); djmoffat@1099: event.dataTransfer.effectAllowed = 'none'; djmoffat@1099: event.dataTransfer.dropEffect = 'copy'; djmoffat@1099: return false; djmoffat@1099: },false); djmoffat@1099: var sliderMargin = document.createAttribute('marginsize'); djmoffat@1099: sliderMargin.nodeValue = 42; // Set default margins to 42px either side djmoffat@1099: // Must have a known EXACT width, as this is used later to determine the ratings djmoffat@1099: var w = (Number(sliderMargin.nodeValue)+8)*2; djmoffat@1099: this.canvas.style.width = window.innerWidth - w +"px"; djmoffat@1099: this.canvas.style.marginLeft = sliderMargin.nodeValue +'px'; djmoffat@1099: this.canvas.setAttributeNode(sliderMargin); djmoffat@1099: this.sliderDOM.appendChild(this.canvas); djmoffat@1099: djmoffat@1099: // Create the div to hold any scale objects djmoffat@1099: this.scale = document.createElement('div'); djmoffat@1099: this.scale.className = 'sliderScale'; djmoffat@1099: this.scale.id = 'sliderScaleHolder-'+this.id; djmoffat@1099: this.scale.align = 'left'; djmoffat@1099: this.sliderDOM.appendChild(this.scale); djmoffat@1099: var positionScale = this.canvas.style.width.substr(0,this.canvas.style.width.length-2); djmoffat@1099: var offset = Number(this.canvas.attributes['marginsize'].value); djmoffat@1099: var dest = document.getElementById("slider-holder").appendChild(this.sliderDOM); djmoffat@1099: for (var scaleObj of interfaceObject.scales) djmoffat@1099: { djmoffat@1099: var position = Number(scaleObj.position)*0.01; djmoffat@1099: var pixelPosition = (position*$(this.canvas).width())+offset; djmoffat@1099: var scaleDOM = document.createElement('span'); djmoffat@1099: scaleDOM.textContent = scaleObj.text; djmoffat@1099: scaleDOM.setAttribute('value',position) djmoffat@1099: this.scale.appendChild(scaleDOM); djmoffat@1099: scaleDOM.style.left = Math.floor((pixelPosition-($(scaleDOM).width()/2)))+'px'; djmoffat@1099: } djmoffat@1099: djmoffat@1099: this.createSliderObject = function(audioObject) djmoffat@1099: { djmoffat@1099: var trackObj = document.createElement('div'); djmoffat@1099: trackObj.align = "center"; djmoffat@1099: trackObj.className = 'track-slider track-slider-disabled track-slider-'+audioObject.id; djmoffat@1099: trackObj.id = 'track-slider-'+this.id+'-'+audioObject.id; djmoffat@1099: trackObj.setAttribute('trackIndex',audioObject.id); djmoffat@1099: if (this.name != undefined) { djmoffat@1099: trackObj.setAttribute('interface-name',this.name); djmoffat@1099: } else { djmoffat@1099: trackObj.setAttribute('interface-name',this.id); djmoffat@1099: } djmoffat@1099: var offset = Number(this.canvas.attributes['marginsize'].value); djmoffat@1099: // Distribute it randomnly djmoffat@1099: var w = window.innerWidth - (offset+8)*2; djmoffat@1099: w = Math.random()*w; djmoffat@1099: w = Math.floor(w+(offset+8)); djmoffat@1099: trackObj.style.left = w+'px'; djmoffat@1099: this.canvas.appendChild(trackObj); djmoffat@1099: this.sliders.push(trackObj); djmoffat@1099: this.metrics.push(new metricTracker(this)); djmoffat@1099: trackObj.innerHTML = ''+(this.metrics.length-1)+''; djmoffat@1099: this.metrics[this.metrics.length-1].initialise(convSliderPosToRate(trackObj)); djmoffat@1099: return trackObj; djmoffat@1099: }; djmoffat@1099: djmoffat@1099: this.resize = function(event) djmoffat@1099: { djmoffat@1099: var holdValues = []; djmoffat@1099: for (var index = 0; index < this.sliders.length; index++) djmoffat@1099: { djmoffat@1099: holdValues.push(convSliderPosToRate(this.sliders[index])); djmoffat@1099: } djmoffat@1099: var width = event.target.innerWidth; djmoffat@1099: var sliderDiv = this.canvas; djmoffat@1099: var sliderScaleDiv = this.scale; djmoffat@1099: var marginsize = Number(sliderDiv.attributes['marginsize'].value); djmoffat@1099: var w = (marginsize+8)*2; djmoffat@1099: sliderDiv.style.width = width - w + 'px'; djmoffat@1099: var width = width - w; djmoffat@1099: // Move sliders into new position djmoffat@1099: for (var index = 0; index < this.sliders.length; index++) djmoffat@1099: { djmoffat@1099: var pos = holdValues[index]; djmoffat@1099: var pix = pos * width; djmoffat@1099: this.sliders[index].style.left = pix+marginsize+'px'; djmoffat@1099: } djmoffat@1099: djmoffat@1099: // Move scale labels djmoffat@1099: for (var index = 0; index < this.scale.children.length; index++) djmoffat@1099: { djmoffat@1099: var scaleObj = this.scale.children[index]; djmoffat@1099: var position = Number(scaleObj.attributes['value'].value); djmoffat@1099: var pixelPosition = (position*width)+marginsize; djmoffat@1099: scaleObj.style.left = Math.floor((pixelPosition-($(scaleObj).width()/2)))+'px'; djmoffat@1099: } djmoffat@1099: }; djmoffat@1099: } djmoffat@1099: djmoffat@1099: function sliderObject(audioObject,interfaceObjects) { djmoffat@1099: // Create a new slider object; djmoffat@1099: this.parent = audioObject; djmoffat@1099: this.trackSliderObjects = []; djmoffat@1099: for (var i=0; i saves djmoffat@1099: // Get the current information in store (remember to appendChild your data to it) djmoffat@1099: // pageSpecification is the current page node configuration djmoffat@1099: // To create new XML nodes, use storage.document.createElement(); djmoffat@1099: djmoffat@1099: if (interfaceContext.interfaceSliders.length == 1) djmoffat@1099: { djmoffat@1099: // If there is only one axis, there only needs to be one metric return djmoffat@1099: return; djmoffat@1099: } djmoffat@1099: var audioelements = store.getElementsByTagName("audioelement"); djmoffat@1099: for (var i=0; i