Mercurial > hg > webaudioevaluationtool
changeset 1341:bbb6462cf446
Moved interfaces into their own sub-directory
author | Nicholas Jillings <nickjillings@users.noreply.github.com> |
---|---|
date | Wed, 13 Jan 2016 09:34:46 +0000 |
parents | aedaf70eeee0 |
children | 397e96ee781a |
files | AB.css AB.js ape.css ape.js core.js interfaces/AB.css interfaces/AB.js interfaces/ape.css interfaces/ape.js interfaces/mushra.css interfaces/mushra.js mushra.css mushra.js |
diffstat | 13 files changed, 1939 insertions(+), 1939 deletions(-) [+] |
line wrap: on
line diff
--- a/AB.css Tue Jan 12 18:29:55 2016 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,74 +0,0 @@ -body { - /* Set the background colour (note US English spelling) to grey*/ - background-color: #fff -} - -div.pageTitle { - width: auto; - height: 20px; - margin-top: 20px; -} - -div.pageTitle span{ - font-size: 1.5em; -} - -div.testHalt { - /* Specify any colouring during the test halt for pre/post questions */ - background-color: rgba(0,0,0,0.5); - /* Don't mess with this bit */ - z-index: 2; - width: 100%; - height: 100%; - position: absolute; - left: 0px; - top: 0px; -} - -button { - /* Specify any button structure or style */ - min-width: 20px; - background-color: #ddd -} - -button.big-button { - width: 250px; - height: 40px; - font-size: 1.2em; -} - -div.comparitor-holder { - width: 260px; - height: 300px; - border: black 1px solid; - float: left; - padding-top: 5px; - margin: 25px; -} - -div.comparitor-selector { - width: 248px; - height: 250px; - border: black 1px solid; - position: relative; - background-color: #FF0000; -} - -div.disabled { - background-color: #AAA; -} - -div.selected { - background-color: #008000; -} - -div.comparitor-selector span { - font-size: 4em; -} - -button.comparitor-button { - width: 250px; - height: 38px; - position: relative; - margin-top: 5px; -}
--- a/AB.js Tue Jan 12 18:29:55 2016 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,320 +0,0 @@ -// Once this is loaded and parsed, begin execution -loadInterface(); - -function loadInterface() { - // Get the dimensions of the screen available to the page - var width = window.innerWidth; - var height = window.innerHeight; - interfaceContext.insertPoint.innerHTML = null; // Clear the current schema - - // Custom Comparitor Object - Interface.prototype.comparitor = null; - - // The injection point into the HTML page - interfaceContext.insertPoint = document.getElementById("topLevelBody"); - var testContent = document.createElement('div'); - testContent.id = 'testContent'; - - // Create the top div for the Title element - var titleAttr = specification.title; - var title = document.createElement('div'); - title.className = "title"; - title.align = "center"; - var titleSpan = document.createElement('span'); - - // Set title to that defined in XML, else set to default - if (titleAttr != undefined) { - titleSpan.textContent = titleAttr; - } else { - titleSpan.textContent = 'Listening test'; - } - // Insert the titleSpan element into the title div element. - title.appendChild(titleSpan); - - var pagetitle = document.createElement('div'); - pagetitle.className = "pageTitle"; - pagetitle.align = "center"; - var titleSpan = document.createElement('span'); - titleSpan.id = "pageTitle"; - pagetitle.appendChild(titleSpan); - - // Create Interface buttons! - var interfaceButtons = document.createElement('div'); - interfaceButtons.id = 'interface-buttons'; - interfaceButtons.style.height = '25px'; - - // Create playback start/stop points - var playback = document.createElement("button"); - playback.innerHTML = 'Stop'; - playback.id = 'playback-button'; - playback.style.float = 'left'; - // onclick function. Check if it is playing or not, call the correct function in the - // audioEngine, change the button text to reflect the next state. - playback.onclick = function() { - if (audioEngineContext.status == 1) { - audioEngineContext.stop(); - this.innerHTML = 'Stop'; - var time = audioEngineContext.timer.getTestTime(); - console.log('Stopped at ' + time); // DEBUG/SAFETY - } - }; - // Append the interface buttons into the interfaceButtons object. - interfaceButtons.appendChild(playback); - - // Global parent for the comment boxes on the page - var feedbackHolder = document.createElement('div'); - feedbackHolder.id = 'feedbackHolder'; - - // Construct the AB Boxes - var boxes = document.createElement('div'); - boxes.align = "center"; - boxes.id = "box-holders"; - boxes.style.float = "left"; - - var submit = document.createElement('button'); - submit.id = "submit"; - submit.onclick = buttonSubmitClick; - submit.className = "big-button"; - submit.textContent = "submit"; - submit.style.position = "relative"; - submit.style.left = (window.innerWidth-250)/2 + 'px'; - - feedbackHolder.appendChild(boxes); - - // Inject into HTML - testContent.appendChild(title); // Insert the title - testContent.appendChild(pagetitle); - testContent.appendChild(interfaceButtons); - testContent.appendChild(feedbackHolder); - testContent.appendChild(submit); - interfaceContext.insertPoint.appendChild(testContent); - - // Load the full interface - testState.initialise(); - testState.advanceState(); -} - -function loadTest(audioHolderObject) -{ - var feedbackHolder = document.getElementById('feedbackHolder'); - var interfaceObj = audioHolderObject.interfaces; - if (interfaceObj.length > 1) - { - console.log("WARNING - This interface only supports one <interface> node per page. Using first interface node"); - } - interfaceObj = interfaceObj[0]; - - if(interfaceObj.title != null) - { - document.getElementById("pageTitle").textContent = interfaceObj.title; - } - - // Populate the comparitor object - interfaceContext.comparitor = new Comparitor(audioHolderObject); - resizeWindow(null); -} - -function Comparitor(audioHolderObject) -{ - this.comparitorBox = function(audioElement,id,text) - { - this.parent = audioElement; - this.id = id; - this.value = 0; - this.disabled = true; - this.box = document.createElement('div'); - this.box.className = 'comparitor-holder'; - this.box.setAttribute('track-id',audioElement.id); - this.box.id = 'comparitor-'+text; - this.selector = document.createElement('div'); - this.selector.className = 'comparitor-selector disabled'; - var selectorText = document.createElement('span'); - selectorText.textContent = text; - this.selector.appendChild(selectorText); - this.playback = document.createElement('button'); - this.playback.className = 'comparitor-button'; - this.playback.disabled = true; - this.playback.textContent = "Listen"; - this.box.appendChild(this.selector); - this.box.appendChild(this.playback); - this.selector.onclick = function() - { - var time = audioEngineContext.timer.getTestTime(); - if ($(event.currentTarget).hasClass('disabled')) - { - console.log("Please wait until sample has loaded"); - return; - } - if (audioEngineContext.status == 0) - { - alert("Please listen to the samples before making a selection"); - console.log("Please listen to the samples before making a selection"); - return; - } - $(".comparitor-selector").removeClass('selected'); - var id = event.currentTarget.parentElement.getAttribute('track-id'); - interfaceContext.comparitor.selected = id; - $(event.currentTarget).addClass('selected'); - for (var i=0; i<interfaceContext.comparitor.comparitors.length; i++) - { - var obj = interfaceContext.comparitor.comparitors[i]; - if (i == id) { - obj.value = 1; - } else { - obj.value = 0; - } - obj.parent.metric.moved(time,obj.value); - } - console.log("Selected "+id+' ('+time+')'); - }; - this.playback.onclick = function() - { - $('.comparitor-button').text('Listen'); - var id = event.currentTarget.parentElement.getAttribute('track-id'); - audioEngineContext.play(id); - $(event.currentTarget).text('Playing'); - }; - - this.enable = function() - { - if (this.parent.state == 1) - { - $(this.selector).removeClass('disabled'); - this.playback.disabled = false; - } - }; - this.updateLoading = function(progress) - { - if (progress != 100) - { - progress = String(progress); - progress = progress.split('.')[0]; - this.playback.textContent = progress+'%'; - } else { - this.playback.textContent = "Listen"; - } - }; - this.exportXMLDOM = function(audioObject) - { - var node = storage.document.createElement('value'); - node.textContent = this.value; - return node; - }; - this.getValue = function() { - return this.value; - }; - this.getPresentedId = function() - { - return this.selector.children[0].textContent; - }; - this.canMove = function() - { - return false; - }; - }; - - this.boxHolders = document.getElementById('box-holders'); - this.boxHolders.innerHTML = null; - this.comparitors = []; - this.selected = null; - - // First generate the Audio Objects for the Audio Engine - for (var index=0; index<audioHolderObject.audioElements.length; index++) - { - var element = audioHolderObject.audioElements[index]; - if (index == audioHolderObject.outsideReference || element.type == 'outside-reference') - { - console.log("WARNING - AB cannot have fixed reference"); - } - var audioObject = audioEngineContext.newTrack(element); - var node = new this.comparitorBox(audioObject,index,String.fromCharCode(65 + index)); - audioObject.bindInterface(node); - this.comparitors.push(node); - this.boxHolders.appendChild(node.box); - } - return this; -} - -function resizeWindow(event) -{ - document.getElementById('submit').style.left = (window.innerWidth-250)/2 + 'px'; - var numObj = interfaceContext.comparitor.comparitors.length; - var boxW = numObj*312; - var diff = window.innerWidth - boxW; - while (diff < 0) - { - numObj = Math.ceil(numObj/2); - boxW = numObj*312; - diff = window.innerWidth - boxW; - } - document.getElementById('box-holders').style.marginLeft = diff/2 + 'px'; - document.getElementById('box-holders').style.marginRight = diff/2 + 'px'; - document.getElementById('box-holders').style.width = boxW + 'px'; -} - -function buttonSubmitClick() -{ - var checks = []; - checks = checks.concat(testState.currentStateMap.interfaces[0].options); - checks = checks.concat(specification.interfaces.options); - var canContinue = true; - - for (var i=0; i<checks.length; i++) { - if (checks[i].type == 'check') - { - switch(checks[i].name) { - case 'fragmentPlayed': - // Check if all fragments have been played - var checkState = interfaceContext.checkAllPlayed(); - if (checkState == false) {canContinue = false;} - break; - case 'fragmentFullPlayback': - // Check all fragments have been played to their full length - var checkState = interfaceContext.checkFragmentsFullyPlayed(); - if (checkState == false) {canContinue = false;} - break; - case 'fragmentMoved': - // Check all fragment sliders have been moved. - var checkState = interfaceContext.checkAllMoved(); - if (checkState == false) {canContinue = false;} - break; - case 'fragmentComments': - // Check all fragment sliders have been moved. - var checkState = interfaceContext.checkAllCommented(); - if (checkState == false) {canContinue = false;} - break; - default: - console.log("WARNING - Check option "+checks[i].check+" is not supported on this interface"); - break; - } - - } - if (!canContinue) {break;} - } - if (canContinue) - { - if (audioEngineContext.status == 1) { - var playback = document.getElementById('playback-button'); - playback.click(); - // This function is called when the submit button is clicked. Will check for any further tests to perform, or any post-test options - } else - { - if (audioEngineContext.timer.testStarted == false) - { - alert('You have not started the test! Please press start to begin the test!'); - return; - } - } - testState.advanceState(); - } -} - -function pageXMLSave(store, pageSpecification) -{ - // MANDATORY - // Saves a specific test page - // You can use this space to add any extra nodes to your XML <audioHolder> saves - // Get the current <page> information in store (remember to appendChild your data to it) - // pageSpecification is the current page node configuration - // To create new XML nodes, use storage.document.createElement(); -} \ No newline at end of file
--- a/ape.css Tue Jan 12 18:29:55 2016 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,88 +0,0 @@ -/* - * Hold any style information for APE interface. Customise if you like to make the interface your own! - * - */ -body { - /* Set the background colour (note US English spelling) to grey*/ - background-color: #ddd -} - -div.title { - /* Specify any colouring for the title */ -} - -div.pageTitle { - width: auto; - height: 20px; - margin-top: 5px; - margin-bottom: 10px; -} - -div.pageTitle span{ - font-size: 1.5em; -} - -button { - /* Specify any button structure or style */ - min-width: 20px; - background-color: #ddd -} - -div.slider { - /* Specify any structure for the slider holder interface */ - background-color: #eee; - height: 150px; - margin-bottom: 5px; - -moz-user-select: -moz-none; - -khtml-user-select: none; - -webkit-user-select: none; -} - -div.sliderScale { - width: 100%; - min-height: 30px; - -moz-user-select: -moz-none; - -khtml-user-select: none; - -webkit-user-select: none; -} - -div.sliderScale span { - /* Any formatting of text below scale */ - font-size: 1.2em; - min-width: 5px; - height: 20px; - position: absolute; -} - -div.track-slider { - /* Specify any structure for the slider objects */ - position: absolute; - height: inherit; - width: 12px; - float: left; - background-color: rgb(100,200,100); - -moz-user-select: -moz-none; - -khtml-user-select: none; - -webkit-user-select: none; -} - -div.outside-reference { - width:120px; - padding-left: 55px; - margin-left: 100px; - height:20px; - margin-bottom:5px; - background-color: rgb(100,200,100); -} - -div.track-slider-disabled { - background-color: rgb(100,100,100); -} - -div.track-slider-playing { - background-color: #FF0000; -} - -div.comment-box-playing { - background-color: #FFDDDD; -}
--- a/ape.js Tue Jan 12 18:29:55 2016 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,826 +0,0 @@ -/** - * ape.js - * Create the APE interface - */ - - -// Once this is loaded and parsed, begin execution -loadInterface(); - -function loadInterface() { - - // Get the dimensions of the screen available to the page - var width = window.innerWidth; - var height = window.innerHeight; - - // The injection point into the HTML page - interfaceContext.insertPoint = document.getElementById("topLevelBody"); - var testContent = document.createElement('div'); - - testContent.id = 'testContent'; - - // Bindings for interfaceContext - interfaceContext.checkAllPlayed = function() - { - hasBeenPlayed = audioEngineContext.checkAllPlayed(); - if (hasBeenPlayed.length > 0) // if a fragment has not been played yet - { - str = ""; - if (hasBeenPlayed.length > 1) { - for (var i=0; i<hasBeenPlayed.length; i++) { - str = str + hasBeenPlayed[i]; - if (i < hasBeenPlayed.length-2){ - str += ", "; - } else if (i == hasBeenPlayed.length-2) { - str += " or "; - } - } - alert('You have not played fragments ' + str + ' yet. Please listen, rate and comment all samples before submitting.'); - } else { - alert('You have not played fragment ' + hasBeenPlayed[0] + ' yet. Please listen, rate and comment all samples before submitting.'); - } - return false; - } - return true; - }; - - interfaceContext.checkAllMoved = function() { - var state = true; - var str = 'You have not moved the following sliders. '; - for (var i=0; i<this.interfaceSliders.length; i++) - { - var interfaceTID = []; - for (var j=0; j<this.interfaceSliders[i].metrics.length; j++) - { - if (this.interfaceSliders[i].metrics[j].wasMoved == false) - { - state = false; - interfaceTID.push(j); - } - } - if (interfaceTID.length != 0) - { - var interfaceName = this.interfaceSliders[i].interfaceObject.title; - if (interfaceName == undefined) { - str += 'On axis '+String(i+1)+' you must move '; - } else { - str += 'On axis "'+interfaceName+'" you must move '; - } - if (interfaceTID.length == 1) - { - str += 'slider '+interfaceTID[0]+'. '; - } - else { - str += 'sliders '; - for (var k=0; k<interfaceTID.length-1; k++) - { - str += interfaceTID[k]+', '; - } - str += interfaceTID[interfaceTID.length-1] +'. '; - } - } - } - if (state != true) - { - alert(str); - console.log(str); - } - return state; - }; - - Interface.prototype.checkAllCommented = function() { - var audioObjs = audioEngineContext.audioObjects; - var audioHolder = testState.stateMap[testState.stateIndex]; - var state = true; - if (audioHolder.elementComments) { - var strNums = []; - for (var i=0; i<audioObjs.length; i++) - { - if (audioObjs[i].commentDOM.trackCommentBox.value.length == 0) { - state = false; - strNums.push(i); - } - } - if (state == false) { - if (strNums.length > 1) { - var str = ""; - for (var i=0; i<strNums.length; i++) { - str = str + strNums[i]; - if (i < strNums.length-2){ - str += ", "; - } else if (i == strNums.length-2) { - str += " or "; - } - } - alert('You have not commented on fragments ' + str + ' yet. Please listen, rate and comment all samples before submitting.'); - } else { - alert('You have not commented on fragment ' + strNums[0] + ' yet. Please listen, rate and comment all samples before submitting.'); - } - } - } - return state; - }; - - Interface.prototype.checkScaleRange = function() - { - var audioObjs = audioEngineContext.audioObjects; - var audioHolder = testState.stateMap[testState.stateIndex]; - var state = true; - var str = ''; - for (var i=0; i<this.interfaceSliders.length; i++) - { - var minScale; - var maxScale; - var interfaceObject = interfaceContext.interfaceSliders[0].interfaceObject; - for (var j=0; j<interfaceObject.options.length; j++) - { - if (interfaceObject.options[j].check == "scalerange") { - minScale = interfaceObject.options[j].min; - maxScale = interfaceObject.options[j].max; - break; - } - } - var minRanking = convSliderPosToRate(this.interfaceSliders[i].sliders[0]); - var maxRanking = minRanking; - for (var j=1; j<this.interfaceSliders[i].sliders.length; j++) - { - var ranking = convSliderPosToRate(this.interfaceSliders[i].sliders[j]); - if (ranking < minRanking) - { - minRanking = ranking; - } else if (ranking > maxRanking) - { - maxRanking = ranking; - } - } - if (minRanking > minScale || maxRanking < maxScale) - { - state = false; - str += 'On axis "'+this.interfaceSliders[i].interfaceObject.title+'" you have not used the full width of the scale. '; - } - } - if (state != true) - { - alert(str); - console.log(str); - } - return state; - }; - - Interface.prototype.objectSelected = null; - Interface.prototype.objectMoved = false; - Interface.prototype.selectObject = function(object) - { - if (this.objectSelected == null) - { - this.objectSelected = object; - this.objectMoved = false; - } - }; - Interface.prototype.moveObject = function() - { - if (this.objectMoved == false) - { - this.objectMoved = true; - } - }; - Interface.prototype.releaseObject = function() - { - this.objectSelected = null; - this.objectMoved = false; - }; - Interface.prototype.getSelectedObject = function() - { - return this.objectSelected; - }; - Interface.prototype.hasSelectedObjectMoved = function() - { - return this.objectMoved; - }; - - // Bindings for slider interfaces - Interface.prototype.interfaceSliders = []; - - // Bindings for audioObjects - - // Create the top div for the Title element - var titleAttr = specification.title; - var title = document.createElement('div'); - title.className = "title"; - title.align = "center"; - var titleSpan = document.createElement('span'); - - // Set title to that defined in XML, else set to default - if (titleAttr != undefined) { - titleSpan.textContent = titleAttr; - } else { - titleSpan.textContent = 'Listening test'; - } - // Insert the titleSpan element into the title div element. - title.appendChild(titleSpan); - - // Create Interface buttons! - var interfaceButtons = document.createElement('div'); - interfaceButtons.id = 'interface-buttons'; - - // Create playback start/stop points - var playback = document.createElement("button"); - playback.innerHTML = 'Stop'; - playback.id = 'playback-button'; - // onclick function. Check if it is playing or not, call the correct function in the - // audioEngine, change the button text to reflect the next state. - playback.onclick = function() { - if (audioEngineContext.status == 1) { - audioEngineContext.stop(); - this.innerHTML = 'Stop'; - var time = audioEngineContext.timer.getTestTime(); - console.log('Stopped at ' + time); // DEBUG/SAFETY - } - }; - // Create Submit (save) button - var submit = document.createElement("button"); - submit.innerHTML = 'Submit'; - submit.onclick = buttonSubmitClick; - submit.id = 'submit-button'; - // Append the interface buttons into the interfaceButtons object. - interfaceButtons.appendChild(playback); - interfaceButtons.appendChild(submit); - - var sliderHolder = document.createElement("div"); - sliderHolder.id = "slider-holder"; - - - // Global parent for the comment boxes on the page - var feedbackHolder = document.createElement('div'); - feedbackHolder.id = 'feedbackHolder'; - - testContent.style.zIndex = 1; - interfaceContext.insertPoint.innerHTML = null; // Clear the current schema - - // Inject into HTML - testContent.appendChild(title); // Insert the title - testContent.appendChild(interfaceButtons); - testContent.appendChild(sliderHolder); - testContent.appendChild(feedbackHolder); - interfaceContext.insertPoint.appendChild(testContent); - - // Load the full interface - testState.initialise(); - testState.advanceState(); - -} - -function loadTest(audioHolderObject) -{ - var width = window.innerWidth; - var height = window.innerHeight; - var id = audioHolderObject.id; - - interfaceContext.interfaceSliders = []; - - var feedbackHolder = document.getElementById('feedbackHolder'); - var sliderHolder = document.getElementById('slider-holder'); - feedbackHolder.innerHTML = null; - sliderHolder.innerHTML = null; - - // Delete outside reference - var outsideReferenceHolder = document.getElementById('outside-reference'); - if (outsideReferenceHolder != null) { - document.getElementById('interface-buttons').removeChild(outsideReferenceHolder); - } - - var interfaceObj = audioHolderObject.interfaces; - for (var k=0; k<interfaceObj.length; k++) { - // Create the div box to center align - interfaceContext.interfaceSliders.push(new interfaceSliderHolder(interfaceObj[k])); - for (var i=0; i<interfaceObj[k].options.length; i++) - { - if (interfaceObj[k].options[i].type == 'option' && interfaceObj[k].options[i].name == 'playhead') - { - var playbackHolder = document.getElementById('playback-holder'); - if (playbackHolder == null) - { - playbackHolder = document.createElement('div'); - playbackHolder.style.width = "100%"; - playbackHolder.align = 'center'; - playbackHolder.appendChild(interfaceContext.playhead.object); - feedbackHolder.appendChild(playbackHolder); - } - } else if (interfaceObj[k].options[i].type == 'option' && interfaceObj[k].options[i].name == 'page-count') - { - var pagecountHolder = document.getElementById('page-count'); - if (pagecountHolder == null) - { - pagecountHolder = document.createElement('div'); - pagecountHolder.id = 'page-count'; - } - pagecountHolder.innerHTML = '<span>Page '+(audioHolderObject.presentedId+1)+' of '+specification.audioHolders.length+'</span>'; - var inject = document.getElementById('interface-buttons'); - inject.appendChild(pagecountHolder); - } - } - } - - var commentBoxPrefix = "Comment on fragment"; - - var commentShow = audioHolderObject.elementComments; - - var loopPlayback = audioHolderObject.loop; - - currentTestHolder = document.createElement('audioHolder'); - currentTestHolder.id = audioHolderObject.id; - currentTestHolder.repeatCount = audioHolderObject.repeatCount; - - // Find all the audioElements from the audioHolder - $(audioHolderObject.audioElements).each(function(index,element){ - // Find URL of track - // In this jQuery loop, variable 'this' holds the current audioElement. - var audioObject = audioEngineContext.newTrack(element); - // Check if an outside reference - if (element.type == 'outside-reference') - { - // Construct outside reference; - var orNode = new outsideReferenceDOM(audioObject,index,document.getElementById('interface-buttons')); - audioObject.bindInterface(orNode); - } else { - var node = interfaceContext.createCommentBox(audioObject); - // Create a slider per track - var sliderNode = new sliderObject(audioObject,interfaceObj); - audioObject.bindInterface(sliderNode); - } - }); - - // Initialse the interfaceSlider object metrics - - $('.track-slider').mousedown(function(event) { - interfaceContext.selectObject($(this)[0]); - }); - $('.track-slider').on('touchstart',null,function(event) { - interfaceContext.selectObject($(this)[0]); - }); - - $('.track-slider').mousemove(function(event) { - event.preventDefault(); - }); - - $('.slider').mousemove(function(event) { - event.preventDefault(); - var obj = interfaceContext.getSelectedObject(); - if (obj == null) {return;} - $(obj).css("left",event.clientX + "px"); - interfaceContext.moveObject(); - }); - - $('.slider').on('touchmove',null,function(event) { - event.preventDefault(); - var obj = interfaceContext.getSelectedObject(); - if (obj == null) {return;} - var move = event.originalEvent.targetTouches[0].clientX - 6; - $(obj).css("left",move + "px"); - interfaceContext.moveObject(); - }); - - $(document).mouseup(function(event){ - event.preventDefault(); - var obj = interfaceContext.getSelectedObject(); - if (obj == null) {return;} - var interfaceID = obj.parentElement.getAttribute("interfaceid"); - var trackID = obj.getAttribute("trackindex"); - if (interfaceContext.hasSelectedObjectMoved() == true) - { - var l = $(obj).css("left"); - var id = obj.getAttribute('trackIndex'); - var time = audioEngineContext.timer.getTestTime(); - var rate = convSliderPosToRate(obj); - audioEngineContext.audioObjects[id].metric.moved(time,rate); - interfaceContext.interfaceSliders[interfaceID].metrics[trackID].moved(time,rate); - console.log("slider "+id+" moved to "+rate+' ('+time+')'); - } else { - var id = Number(obj.attributes['trackIndex'].value); - //audioEngineContext.metric.sliderPlayed(id); - audioEngineContext.play(id); - // Currently playing track red, rest green - - $('.track-slider').removeClass('track-slider-playing'); - var name = ".track-slider-"+obj.getAttribute("trackindex"); - $(name).addClass('track-slider-playing'); - $('.comment-div').removeClass('comment-box-playing'); - $('#comment-div-'+id).addClass('comment-box-playing'); - var outsideReference = document.getElementById('outside-reference'); - if (outsideReference != undefined) - $(outsideReference).removeClass('track-slider-playing'); - } - interfaceContext.releaseObject(); - }); - - $('.slider').on('touchend',null,function(event){ - var obj = interfaceContext.getSelectedObject(); - if (obj == null) {return;} - var interfaceID = obj.parentElement.getAttribute("interfaceid"); - var trackID = obj.getAttribute("trackindex"); - if (interfaceContext.hasSelectedObjectMoved() == true) - { - var l = $(obj).css("left"); - var id = obj.getAttribute('trackIndex'); - var time = audioEngineContext.timer.getTestTime(); - var rate = convSliderPosToRate(obj); - audioEngineContext.audioObjects[id].metric.moved(time,rate); - interfaceContext.interfaceSliders[interfaceID].metrics[trackID].moved(time,rate); - console.log("slider "+id+" moved to "+rate+' ('+time+')'); - } - interfaceContext.releaseObject(); - }); - - - if (audioHolderObject.showElementComments) { - interfaceContext.showCommentBoxes(feedbackHolder,true); - } - - $(audioHolderObject.commentQuestions).each(function(index,element) { - var node = interfaceContext.createCommentQuestion(element); - feedbackHolder.appendChild(node.holder); - }); - - - //testWaitIndicator(); -} - -function interfaceSliderHolder(interfaceObject) -{ - this.sliders = []; - this.metrics = []; - this.id = document.getElementsByClassName("sliderCanvasDiv").length; - this.name = interfaceObject.name; - this.interfaceObject = interfaceObject; - this.sliderDOM = document.createElement('div'); - this.sliderDOM.className = 'sliderCanvasDiv'; - this.sliderDOM.id = 'sliderCanvasHolder-'+this.id; - - var pagetitle = document.createElement('div'); - pagetitle.className = "pageTitle"; - pagetitle.align = "center"; - var titleSpan = document.createElement('span'); - titleSpan.id = "pageTitle-"+this.id; - if (interfaceObject.title != undefined && typeof interfaceObject.title == "string") - { - titleSpan.textContent = interfaceObject.title; - } else { - titleSpan.textContent = "Axis "+String(this.id+1); - } - pagetitle.appendChild(titleSpan); - this.sliderDOM.appendChild(pagetitle); - - // Create the slider box to hold the slider elements - this.canvas = document.createElement('div'); - if (this.name != undefined) - this.canvas.id = 'slider-'+this.name; - else - this.canvas.id = 'slider-'+this.id; - this.canvas.setAttribute("interfaceid",this.id); - this.canvas.className = 'slider'; - this.canvas.align = "left"; - this.canvas.addEventListener('dragover',function(event){ - event.preventDefault(); - event.dataTransfer.effectAllowed = 'none'; - event.dataTransfer.dropEffect = 'copy'; - return false; - },false); - var sliderMargin = document.createAttribute('marginsize'); - sliderMargin.nodeValue = 42; // Set default margins to 42px either side - // Must have a known EXACT width, as this is used later to determine the ratings - var w = (Number(sliderMargin.nodeValue)+8)*2; - this.canvas.style.width = window.innerWidth - w +"px"; - this.canvas.style.marginLeft = sliderMargin.nodeValue +'px'; - this.canvas.setAttributeNode(sliderMargin); - this.sliderDOM.appendChild(this.canvas); - - // Create the div to hold any scale objects - this.scale = document.createElement('div'); - this.scale.className = 'sliderScale'; - this.scale.id = 'sliderScaleHolder-'+this.id; - this.scale.align = 'left'; - this.sliderDOM.appendChild(this.scale); - var positionScale = this.canvas.style.width.substr(0,this.canvas.style.width.length-2); - var offset = Number(this.canvas.attributes['marginsize'].value); - for (var scaleObj of interfaceObject.scales) - { - var value = document.createAttribute('value'); - var position = Number(scaleObj.position)*0.01; - value.nodeValue = position; - var pixelPosition = (position*positionScale)+offset; - var scaleDOM = document.createElement('span'); - scaleDOM.textContent = scaleObj.text; - this.scale.appendChild(scaleDOM); - scaleDOM.style.left = Math.floor((pixelPosition-($(scaleDOM).width()/2)))+'px'; - scaleDOM.setAttributeNode(value); - } - - var dest = document.getElementById("slider-holder"); - dest.appendChild(this.sliderDOM); - - this.createSliderObject = function(audioObject) - { - var trackObj = document.createElement('div'); - trackObj.className = 'track-slider track-slider-disabled track-slider-'+audioObject.id; - trackObj.id = 'track-slider-'+this.id+'-'+audioObject.id; - trackObj.setAttribute('trackIndex',audioObject.id); - if (this.name != undefined) { - trackObj.setAttribute('interface-name',this.name); - } else { - trackObj.setAttribute('interface-name',this.id); - } - var offset = Number(this.canvas.attributes['marginsize'].value); - // Distribute it randomnly - var w = window.innerWidth - (offset+8)*2; - w = Math.random()*w; - w = Math.floor(w+(offset+8)); - trackObj.style.left = w+'px'; - this.canvas.appendChild(trackObj); - this.sliders.push(trackObj); - this.metrics.push(new metricTracker(this)); - trackObj.innerHTML = '<span>'+(this.metrics.length-1)+'</span>'; - this.metrics[this.metrics.length-1].initialise(convSliderPosToRate(trackObj)); - return trackObj; - }; - - this.resize = function(event) - { - var holdValues = []; - for (var index = 0; index < this.sliders.length; index++) - { - holdValues.push(convSliderPosToRate(this.sliders[index])); - } - var width = event.target.innerWidth; - var sliderDiv = this.canvas; - var sliderScaleDiv = this.scale; - var marginsize = Number(sliderDiv.attributes['marginsize'].value); - var w = (marginsize+8)*2; - sliderDiv.style.width = width - w + 'px'; - var width = width - w; - // Move sliders into new position - for (var index = 0; index < this.sliders.length; index++) - { - var pos = holdValues[index]; - var pix = pos * width; - this.sliders[index].style.left = pix+marginsize+'px'; - } - - // Move scale labels - for (var index = 0; index < this.scale.children.length; index++) - { - var scaleObj = this.scale.children[index]; - var position = Number(scaleObj.attributes['value'].value); - var pixelPosition = (position*width)+marginsize; - scaleObj.style.left = Math.floor((pixelPosition-($(scaleObj).width()/2)))+'px'; - } - }; -} - -function sliderObject(audioObject,interfaceObjects) { - // Create a new slider object; - this.parent = audioObject; - this.trackSliderObjects = []; - for (var i=0; i<interfaceContext.interfaceSliders.length; i++) - { - var trackObj = interfaceContext.interfaceSliders[i].createSliderObject(audioObject); - this.trackSliderObjects.push(trackObj); - } - - // Onclick, switch playback to that track - - this.enable = function() { - if (this.parent.state == 1) - { - $(this.trackSliderObjects).each(function(i,trackObj){ - $(trackObj).removeClass('track-slider-disabled'); - }); - } - }; - this.updateLoading = function(progress) - { - if (progress != 100) - { - progress = String(progress); - progress = progress.split('.')[0]; - this.trackSliderObjects[0].children[0].textContent = progress+'%'; - } else { - this.trackSliderObjects[0].children[0].textContent = this.parent.id; - } - }; - this.exportXMLDOM = function(audioObject) { - // Called by the audioObject holding this element. Must be present - var obj = []; - $(this.trackSliderObjects).each(function(i,trackObj){ - var node = storage.document.createElement('value'); - node.setAttribute("interface-name",trackObj.getAttribute("interface-name")); - node.textContent = convSliderPosToRate(trackObj); - obj.push(node); - }); - - return obj; - }; - this.getValue = function() { - return convSliderPosToRate(this.trackSliderObjects[0]); - }; - this.getPresentedId = function() - { - return this.trackSliderObjects[0].children[0].textContent; - }; - this.canMove = function() - { - return true; - }; -} - -function outsideReferenceDOM(audioObject,index,inject) -{ - this.parent = audioObject; - this.outsideReferenceHolder = document.createElement('div'); - this.outsideReferenceHolder.id = 'outside-reference'; - this.outsideReferenceHolder.className = 'outside-reference track-slider-disabled'; - var outsideReferenceHolderspan = document.createElement('span'); - outsideReferenceHolderspan.textContent = 'Reference'; - this.outsideReferenceHolder.appendChild(outsideReferenceHolderspan); - this.outsideReferenceHolder.setAttribute('track-id',index); - - this.outsideReferenceHolder.onclick = function(event) - { - audioEngineContext.play(event.currentTarget.getAttribute('track-id')); - $('.track-slider').removeClass('track-slider-playing'); - $('.comment-div').removeClass('comment-box-playing'); - if (event.currentTarget.nodeName == 'DIV') { - $(event.currentTarget).addClass('track-slider-playing'); - } else { - $(event.currentTarget.parentElement).addClass('track-slider-playing'); - } - }; - inject.appendChild(this.outsideReferenceHolder); - this.enable = function() - { - if (this.parent.state == 1) - { - $(this.outsideReferenceHolder).removeClass('track-slider-disabled'); - } - }; - this.updateLoading = function(progress) - { - if (progress != 100) - { - progress = String(progress); - progress = progress.split('.')[0]; - this.outsideReferenceHolder[0].children[0].textContent = progress+'%'; - } else { - this.outsideReferenceHolder[0].children[0].textContent = "Play Reference"; - } - }; - this.exportXMLDOM = function(audioObject) - { - return null; - }; - this.getValue = function() - { - return 0; - }; - this.getPresentedId = function() - { - return 'reference'; - }; - this.canMove = function() - { - return false; - }; -} - -function buttonSubmitClick() -{ - var checks = []; - checks = checks.concat(testState.currentStateMap.interfaces[0].options); - checks = checks.concat(specification.interfaces.options); - var canContinue = true; - - // Check that the anchor and reference objects are correctly placed - if (interfaceContext.checkHiddenAnchor() == false) {return;} - if (interfaceContext.checkHiddenReference() == false) {return;} - - for (var i=0; i<checks.length; i++) { - if (checks[i].type == 'check') - { - switch(checks[i].name) { - case 'fragmentPlayed': - // Check if all fragments have been played - var checkState = interfaceContext.checkAllPlayed(); - if (checkState == false) {canContinue = false;} - break; - case 'fragmentFullPlayback': - // Check all fragments have been played to their full length - var checkState = interfaceContext.checkFragmentsFullyPlayed(); - if (checkState == false) {canContinue = false;} - break; - case 'fragmentMoved': - // Check all fragment sliders have been moved. - var checkState = interfaceContext.checkAllMoved(); - if (checkState == false) {canContinue = false;} - break; - case 'fragmentComments': - // Check all fragment sliders have been moved. - var checkState = interfaceContext.checkAllCommented(); - if (checkState == false) {canContinue = false;} - break; - case 'scalerange': - // Check the scale is used to its full width outlined by the node - var checkState = interfaceContext.checkScaleRange(); - if (checkState == false) {canContinue = false;} - break; - default: - console.log("WARNING - Check option "+checks[i].name+" is not supported on this interface"); - break; - } - - } - if (!canContinue) {break;} - } - - if (canContinue) { - if (audioEngineContext.status == 1) { - var playback = document.getElementById('playback-button'); - playback.click(); - // This function is called when the submit button is clicked. Will check for any further tests to perform, or any post-test options - } else - { - if (audioEngineContext.timer.testStarted == false) - { - alert('You have not started the test! Please click a fragment to begin the test!'); - return; - } - } - testState.advanceState(); - } -} - -function convSliderPosToRate(trackSlider) -{ - var slider = trackSlider.parentElement; - var w = slider.style.width; - var marginsize = Number(slider.attributes['marginsize'].value); - var maxPix = w.substr(0,w.length-2); - var pix = trackSlider.style.left; - pix = pix.substr(0,pix.length-2); - var rate = (pix-marginsize)/maxPix; - return rate; -} - -function resizeWindow(event){ - // Function called when the window has been resized. - // MANDATORY FUNCTION - - // Resize the slider objects - for (var i=0; i<interfaceContext.interfaceSliders.length; i++) - { - interfaceContext.interfaceSliders[i].resize(event); - } -} - -function pageXMLSave(store, pageSpecification) -{ - // MANDATORY - // Saves a specific test page - // You can use this space to add any extra nodes to your XML <audioHolder> saves - // Get the current <page> information in store (remember to appendChild your data to it) - // pageSpecification is the current page node configuration - // To create new XML nodes, use storage.document.createElement(); - - if (interfaceContext.interfaceSliders.length == 1) - { - // If there is only one axis, there only needs to be one metric return - return; - } - var audioelements = store.getElementsByTagName("audioelement"); - for (var i=0; i<audioelements.length; i++) - { - // Have to append the metric specific nodes - if (pageSpecification.outsideReference == null || pageSpecification.outsideReference.id != audioelements[i].id) - { - var inject = audioelements[i].getElementsByTagName("metric"); - if (inject.length == 0) - { - inject = storage.document.createElement("metric"); - } else { - inject = inject[0]; - } - for (var k=0; k<interfaceContext.interfaceSliders.length; k++) - { - var mrnodes = interfaceContext.interfaceSliders[k].metrics[i].exportXMLDOM(inject); - for (var j=0; j<mrnodes.length; j++) - { - var name = mrnodes[j].getAttribute("name"); - if (name == "elementTracker" || name == "elementTrackerFull" || name == "elementInitialPosition" || name == "elementFlagMoved") - { - mrnodes[j].setAttribute("interface-name",interfaceContext.interfaceSliders[k].name); - mrnodes[j].setAttribute("interface-id",k); - inject.appendChild(mrnodes[j]); - } - } - } - } - } -} \ No newline at end of file
--- a/core.js Tue Jan 12 18:29:55 2016 +0000 +++ b/core.js Wed Jan 13 09:34:46 2016 +0000 @@ -141,37 +141,37 @@ switch(specification.interface) { case "APE": - interfaceJS.setAttribute("src","ape.js"); + interfaceJS.setAttribute("src","interfaces/ape.js"); // APE comes with a css file var css = document.createElement('link'); css.rel = 'stylesheet'; css.type = 'text/css'; - css.href = 'ape.css'; + css.href = 'interfaces/ape.css'; document.getElementsByTagName("head")[0].appendChild(css); break; case "MUSHRA": - interfaceJS.setAttribute("src","mushra.js"); + interfaceJS.setAttribute("src","interfaces/mushra.js"); // MUSHRA comes with a css file var css = document.createElement('link'); css.rel = 'stylesheet'; css.type = 'text/css'; - css.href = 'mushra.css'; + css.href = 'interfaces/mushra.css'; document.getElementsByTagName("head")[0].appendChild(css); break; case "AB": - interfaceJS.setAttribute("src","AB.js"); + interfaceJS.setAttribute("src","interfaces/AB.js"); // AB comes with a css file var css = document.createElement('link'); css.rel = 'stylesheet'; css.type = 'text/css'; - css.href = 'AB.css'; + css.href = 'interfaces/AB.css'; document.getElementsByTagName("head")[0].appendChild(css); }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/interfaces/AB.css Wed Jan 13 09:34:46 2016 +0000 @@ -0,0 +1,74 @@ +body { + /* Set the background colour (note US English spelling) to grey*/ + background-color: #fff +} + +div.pageTitle { + width: auto; + height: 20px; + margin-top: 20px; +} + +div.pageTitle span{ + font-size: 1.5em; +} + +div.testHalt { + /* Specify any colouring during the test halt for pre/post questions */ + background-color: rgba(0,0,0,0.5); + /* Don't mess with this bit */ + z-index: 2; + width: 100%; + height: 100%; + position: absolute; + left: 0px; + top: 0px; +} + +button { + /* Specify any button structure or style */ + min-width: 20px; + background-color: #ddd +} + +button.big-button { + width: 250px; + height: 40px; + font-size: 1.2em; +} + +div.comparitor-holder { + width: 260px; + height: 300px; + border: black 1px solid; + float: left; + padding-top: 5px; + margin: 25px; +} + +div.comparitor-selector { + width: 248px; + height: 250px; + border: black 1px solid; + position: relative; + background-color: #FF0000; +} + +div.disabled { + background-color: #AAA; +} + +div.selected { + background-color: #008000; +} + +div.comparitor-selector span { + font-size: 4em; +} + +button.comparitor-button { + width: 250px; + height: 38px; + position: relative; + margin-top: 5px; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/interfaces/AB.js Wed Jan 13 09:34:46 2016 +0000 @@ -0,0 +1,320 @@ +// Once this is loaded and parsed, begin execution +loadInterface(); + +function loadInterface() { + // Get the dimensions of the screen available to the page + var width = window.innerWidth; + var height = window.innerHeight; + interfaceContext.insertPoint.innerHTML = null; // Clear the current schema + + // Custom Comparitor Object + Interface.prototype.comparitor = null; + + // The injection point into the HTML page + interfaceContext.insertPoint = document.getElementById("topLevelBody"); + var testContent = document.createElement('div'); + testContent.id = 'testContent'; + + // Create the top div for the Title element + var titleAttr = specification.title; + var title = document.createElement('div'); + title.className = "title"; + title.align = "center"; + var titleSpan = document.createElement('span'); + + // Set title to that defined in XML, else set to default + if (titleAttr != undefined) { + titleSpan.textContent = titleAttr; + } else { + titleSpan.textContent = 'Listening test'; + } + // Insert the titleSpan element into the title div element. + title.appendChild(titleSpan); + + var pagetitle = document.createElement('div'); + pagetitle.className = "pageTitle"; + pagetitle.align = "center"; + var titleSpan = document.createElement('span'); + titleSpan.id = "pageTitle"; + pagetitle.appendChild(titleSpan); + + // Create Interface buttons! + var interfaceButtons = document.createElement('div'); + interfaceButtons.id = 'interface-buttons'; + interfaceButtons.style.height = '25px'; + + // Create playback start/stop points + var playback = document.createElement("button"); + playback.innerHTML = 'Stop'; + playback.id = 'playback-button'; + playback.style.float = 'left'; + // onclick function. Check if it is playing or not, call the correct function in the + // audioEngine, change the button text to reflect the next state. + playback.onclick = function() { + if (audioEngineContext.status == 1) { + audioEngineContext.stop(); + this.innerHTML = 'Stop'; + var time = audioEngineContext.timer.getTestTime(); + console.log('Stopped at ' + time); // DEBUG/SAFETY + } + }; + // Append the interface buttons into the interfaceButtons object. + interfaceButtons.appendChild(playback); + + // Global parent for the comment boxes on the page + var feedbackHolder = document.createElement('div'); + feedbackHolder.id = 'feedbackHolder'; + + // Construct the AB Boxes + var boxes = document.createElement('div'); + boxes.align = "center"; + boxes.id = "box-holders"; + boxes.style.float = "left"; + + var submit = document.createElement('button'); + submit.id = "submit"; + submit.onclick = buttonSubmitClick; + submit.className = "big-button"; + submit.textContent = "submit"; + submit.style.position = "relative"; + submit.style.left = (window.innerWidth-250)/2 + 'px'; + + feedbackHolder.appendChild(boxes); + + // Inject into HTML + testContent.appendChild(title); // Insert the title + testContent.appendChild(pagetitle); + testContent.appendChild(interfaceButtons); + testContent.appendChild(feedbackHolder); + testContent.appendChild(submit); + interfaceContext.insertPoint.appendChild(testContent); + + // Load the full interface + testState.initialise(); + testState.advanceState(); +} + +function loadTest(audioHolderObject) +{ + var feedbackHolder = document.getElementById('feedbackHolder'); + var interfaceObj = audioHolderObject.interfaces; + if (interfaceObj.length > 1) + { + console.log("WARNING - This interface only supports one <interface> node per page. Using first interface node"); + } + interfaceObj = interfaceObj[0]; + + if(interfaceObj.title != null) + { + document.getElementById("pageTitle").textContent = interfaceObj.title; + } + + // Populate the comparitor object + interfaceContext.comparitor = new Comparitor(audioHolderObject); + resizeWindow(null); +} + +function Comparitor(audioHolderObject) +{ + this.comparitorBox = function(audioElement,id,text) + { + this.parent = audioElement; + this.id = id; + this.value = 0; + this.disabled = true; + this.box = document.createElement('div'); + this.box.className = 'comparitor-holder'; + this.box.setAttribute('track-id',audioElement.id); + this.box.id = 'comparitor-'+text; + this.selector = document.createElement('div'); + this.selector.className = 'comparitor-selector disabled'; + var selectorText = document.createElement('span'); + selectorText.textContent = text; + this.selector.appendChild(selectorText); + this.playback = document.createElement('button'); + this.playback.className = 'comparitor-button'; + this.playback.disabled = true; + this.playback.textContent = "Listen"; + this.box.appendChild(this.selector); + this.box.appendChild(this.playback); + this.selector.onclick = function() + { + var time = audioEngineContext.timer.getTestTime(); + if ($(event.currentTarget).hasClass('disabled')) + { + console.log("Please wait until sample has loaded"); + return; + } + if (audioEngineContext.status == 0) + { + alert("Please listen to the samples before making a selection"); + console.log("Please listen to the samples before making a selection"); + return; + } + $(".comparitor-selector").removeClass('selected'); + var id = event.currentTarget.parentElement.getAttribute('track-id'); + interfaceContext.comparitor.selected = id; + $(event.currentTarget).addClass('selected'); + for (var i=0; i<interfaceContext.comparitor.comparitors.length; i++) + { + var obj = interfaceContext.comparitor.comparitors[i]; + if (i == id) { + obj.value = 1; + } else { + obj.value = 0; + } + obj.parent.metric.moved(time,obj.value); + } + console.log("Selected "+id+' ('+time+')'); + }; + this.playback.onclick = function() + { + $('.comparitor-button').text('Listen'); + var id = event.currentTarget.parentElement.getAttribute('track-id'); + audioEngineContext.play(id); + $(event.currentTarget).text('Playing'); + }; + + this.enable = function() + { + if (this.parent.state == 1) + { + $(this.selector).removeClass('disabled'); + this.playback.disabled = false; + } + }; + this.updateLoading = function(progress) + { + if (progress != 100) + { + progress = String(progress); + progress = progress.split('.')[0]; + this.playback.textContent = progress+'%'; + } else { + this.playback.textContent = "Listen"; + } + }; + this.exportXMLDOM = function(audioObject) + { + var node = storage.document.createElement('value'); + node.textContent = this.value; + return node; + }; + this.getValue = function() { + return this.value; + }; + this.getPresentedId = function() + { + return this.selector.children[0].textContent; + }; + this.canMove = function() + { + return false; + }; + }; + + this.boxHolders = document.getElementById('box-holders'); + this.boxHolders.innerHTML = null; + this.comparitors = []; + this.selected = null; + + // First generate the Audio Objects for the Audio Engine + for (var index=0; index<audioHolderObject.audioElements.length; index++) + { + var element = audioHolderObject.audioElements[index]; + if (index == audioHolderObject.outsideReference || element.type == 'outside-reference') + { + console.log("WARNING - AB cannot have fixed reference"); + } + var audioObject = audioEngineContext.newTrack(element); + var node = new this.comparitorBox(audioObject,index,String.fromCharCode(65 + index)); + audioObject.bindInterface(node); + this.comparitors.push(node); + this.boxHolders.appendChild(node.box); + } + return this; +} + +function resizeWindow(event) +{ + document.getElementById('submit').style.left = (window.innerWidth-250)/2 + 'px'; + var numObj = interfaceContext.comparitor.comparitors.length; + var boxW = numObj*312; + var diff = window.innerWidth - boxW; + while (diff < 0) + { + numObj = Math.ceil(numObj/2); + boxW = numObj*312; + diff = window.innerWidth - boxW; + } + document.getElementById('box-holders').style.marginLeft = diff/2 + 'px'; + document.getElementById('box-holders').style.marginRight = diff/2 + 'px'; + document.getElementById('box-holders').style.width = boxW + 'px'; +} + +function buttonSubmitClick() +{ + var checks = []; + checks = checks.concat(testState.currentStateMap.interfaces[0].options); + checks = checks.concat(specification.interfaces.options); + var canContinue = true; + + for (var i=0; i<checks.length; i++) { + if (checks[i].type == 'check') + { + switch(checks[i].name) { + case 'fragmentPlayed': + // Check if all fragments have been played + var checkState = interfaceContext.checkAllPlayed(); + if (checkState == false) {canContinue = false;} + break; + case 'fragmentFullPlayback': + // Check all fragments have been played to their full length + var checkState = interfaceContext.checkFragmentsFullyPlayed(); + if (checkState == false) {canContinue = false;} + break; + case 'fragmentMoved': + // Check all fragment sliders have been moved. + var checkState = interfaceContext.checkAllMoved(); + if (checkState == false) {canContinue = false;} + break; + case 'fragmentComments': + // Check all fragment sliders have been moved. + var checkState = interfaceContext.checkAllCommented(); + if (checkState == false) {canContinue = false;} + break; + default: + console.log("WARNING - Check option "+checks[i].check+" is not supported on this interface"); + break; + } + + } + if (!canContinue) {break;} + } + if (canContinue) + { + if (audioEngineContext.status == 1) { + var playback = document.getElementById('playback-button'); + playback.click(); + // This function is called when the submit button is clicked. Will check for any further tests to perform, or any post-test options + } else + { + if (audioEngineContext.timer.testStarted == false) + { + alert('You have not started the test! Please press start to begin the test!'); + return; + } + } + testState.advanceState(); + } +} + +function pageXMLSave(store, pageSpecification) +{ + // MANDATORY + // Saves a specific test page + // You can use this space to add any extra nodes to your XML <audioHolder> saves + // Get the current <page> information in store (remember to appendChild your data to it) + // pageSpecification is the current page node configuration + // To create new XML nodes, use storage.document.createElement(); +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/interfaces/ape.css Wed Jan 13 09:34:46 2016 +0000 @@ -0,0 +1,88 @@ +/* + * Hold any style information for APE interface. Customise if you like to make the interface your own! + * + */ +body { + /* Set the background colour (note US English spelling) to grey*/ + background-color: #ddd +} + +div.title { + /* Specify any colouring for the title */ +} + +div.pageTitle { + width: auto; + height: 20px; + margin-top: 5px; + margin-bottom: 10px; +} + +div.pageTitle span{ + font-size: 1.5em; +} + +button { + /* Specify any button structure or style */ + min-width: 20px; + background-color: #ddd +} + +div.slider { + /* Specify any structure for the slider holder interface */ + background-color: #eee; + height: 150px; + margin-bottom: 5px; + -moz-user-select: -moz-none; + -khtml-user-select: none; + -webkit-user-select: none; +} + +div.sliderScale { + width: 100%; + min-height: 30px; + -moz-user-select: -moz-none; + -khtml-user-select: none; + -webkit-user-select: none; +} + +div.sliderScale span { + /* Any formatting of text below scale */ + font-size: 1.2em; + min-width: 5px; + height: 20px; + position: absolute; +} + +div.track-slider { + /* Specify any structure for the slider objects */ + position: absolute; + height: inherit; + width: 12px; + float: left; + background-color: rgb(100,200,100); + -moz-user-select: -moz-none; + -khtml-user-select: none; + -webkit-user-select: none; +} + +div.outside-reference { + width:120px; + padding-left: 55px; + margin-left: 100px; + height:20px; + margin-bottom:5px; + background-color: rgb(100,200,100); +} + +div.track-slider-disabled { + background-color: rgb(100,100,100); +} + +div.track-slider-playing { + background-color: #FF0000; +} + +div.comment-box-playing { + background-color: #FFDDDD; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/interfaces/ape.js Wed Jan 13 09:34:46 2016 +0000 @@ -0,0 +1,826 @@ +/** + * ape.js + * Create the APE interface + */ + + +// Once this is loaded and parsed, begin execution +loadInterface(); + +function loadInterface() { + + // Get the dimensions of the screen available to the page + var width = window.innerWidth; + var height = window.innerHeight; + + // The injection point into the HTML page + interfaceContext.insertPoint = document.getElementById("topLevelBody"); + var testContent = document.createElement('div'); + + testContent.id = 'testContent'; + + // Bindings for interfaceContext + interfaceContext.checkAllPlayed = function() + { + hasBeenPlayed = audioEngineContext.checkAllPlayed(); + if (hasBeenPlayed.length > 0) // if a fragment has not been played yet + { + str = ""; + if (hasBeenPlayed.length > 1) { + for (var i=0; i<hasBeenPlayed.length; i++) { + str = str + hasBeenPlayed[i]; + if (i < hasBeenPlayed.length-2){ + str += ", "; + } else if (i == hasBeenPlayed.length-2) { + str += " or "; + } + } + alert('You have not played fragments ' + str + ' yet. Please listen, rate and comment all samples before submitting.'); + } else { + alert('You have not played fragment ' + hasBeenPlayed[0] + ' yet. Please listen, rate and comment all samples before submitting.'); + } + return false; + } + return true; + }; + + interfaceContext.checkAllMoved = function() { + var state = true; + var str = 'You have not moved the following sliders. '; + for (var i=0; i<this.interfaceSliders.length; i++) + { + var interfaceTID = []; + for (var j=0; j<this.interfaceSliders[i].metrics.length; j++) + { + if (this.interfaceSliders[i].metrics[j].wasMoved == false) + { + state = false; + interfaceTID.push(j); + } + } + if (interfaceTID.length != 0) + { + var interfaceName = this.interfaceSliders[i].interfaceObject.title; + if (interfaceName == undefined) { + str += 'On axis '+String(i+1)+' you must move '; + } else { + str += 'On axis "'+interfaceName+'" you must move '; + } + if (interfaceTID.length == 1) + { + str += 'slider '+interfaceTID[0]+'. '; + } + else { + str += 'sliders '; + for (var k=0; k<interfaceTID.length-1; k++) + { + str += interfaceTID[k]+', '; + } + str += interfaceTID[interfaceTID.length-1] +'. '; + } + } + } + if (state != true) + { + alert(str); + console.log(str); + } + return state; + }; + + Interface.prototype.checkAllCommented = function() { + var audioObjs = audioEngineContext.audioObjects; + var audioHolder = testState.stateMap[testState.stateIndex]; + var state = true; + if (audioHolder.elementComments) { + var strNums = []; + for (var i=0; i<audioObjs.length; i++) + { + if (audioObjs[i].commentDOM.trackCommentBox.value.length == 0) { + state = false; + strNums.push(i); + } + } + if (state == false) { + if (strNums.length > 1) { + var str = ""; + for (var i=0; i<strNums.length; i++) { + str = str + strNums[i]; + if (i < strNums.length-2){ + str += ", "; + } else if (i == strNums.length-2) { + str += " or "; + } + } + alert('You have not commented on fragments ' + str + ' yet. Please listen, rate and comment all samples before submitting.'); + } else { + alert('You have not commented on fragment ' + strNums[0] + ' yet. Please listen, rate and comment all samples before submitting.'); + } + } + } + return state; + }; + + Interface.prototype.checkScaleRange = function() + { + var audioObjs = audioEngineContext.audioObjects; + var audioHolder = testState.stateMap[testState.stateIndex]; + var state = true; + var str = ''; + for (var i=0; i<this.interfaceSliders.length; i++) + { + var minScale; + var maxScale; + var interfaceObject = interfaceContext.interfaceSliders[0].interfaceObject; + for (var j=0; j<interfaceObject.options.length; j++) + { + if (interfaceObject.options[j].check == "scalerange") { + minScale = interfaceObject.options[j].min; + maxScale = interfaceObject.options[j].max; + break; + } + } + var minRanking = convSliderPosToRate(this.interfaceSliders[i].sliders[0]); + var maxRanking = minRanking; + for (var j=1; j<this.interfaceSliders[i].sliders.length; j++) + { + var ranking = convSliderPosToRate(this.interfaceSliders[i].sliders[j]); + if (ranking < minRanking) + { + minRanking = ranking; + } else if (ranking > maxRanking) + { + maxRanking = ranking; + } + } + if (minRanking > minScale || maxRanking < maxScale) + { + state = false; + str += 'On axis "'+this.interfaceSliders[i].interfaceObject.title+'" you have not used the full width of the scale. '; + } + } + if (state != true) + { + alert(str); + console.log(str); + } + return state; + }; + + Interface.prototype.objectSelected = null; + Interface.prototype.objectMoved = false; + Interface.prototype.selectObject = function(object) + { + if (this.objectSelected == null) + { + this.objectSelected = object; + this.objectMoved = false; + } + }; + Interface.prototype.moveObject = function() + { + if (this.objectMoved == false) + { + this.objectMoved = true; + } + }; + Interface.prototype.releaseObject = function() + { + this.objectSelected = null; + this.objectMoved = false; + }; + Interface.prototype.getSelectedObject = function() + { + return this.objectSelected; + }; + Interface.prototype.hasSelectedObjectMoved = function() + { + return this.objectMoved; + }; + + // Bindings for slider interfaces + Interface.prototype.interfaceSliders = []; + + // Bindings for audioObjects + + // Create the top div for the Title element + var titleAttr = specification.title; + var title = document.createElement('div'); + title.className = "title"; + title.align = "center"; + var titleSpan = document.createElement('span'); + + // Set title to that defined in XML, else set to default + if (titleAttr != undefined) { + titleSpan.textContent = titleAttr; + } else { + titleSpan.textContent = 'Listening test'; + } + // Insert the titleSpan element into the title div element. + title.appendChild(titleSpan); + + // Create Interface buttons! + var interfaceButtons = document.createElement('div'); + interfaceButtons.id = 'interface-buttons'; + + // Create playback start/stop points + var playback = document.createElement("button"); + playback.innerHTML = 'Stop'; + playback.id = 'playback-button'; + // onclick function. Check if it is playing or not, call the correct function in the + // audioEngine, change the button text to reflect the next state. + playback.onclick = function() { + if (audioEngineContext.status == 1) { + audioEngineContext.stop(); + this.innerHTML = 'Stop'; + var time = audioEngineContext.timer.getTestTime(); + console.log('Stopped at ' + time); // DEBUG/SAFETY + } + }; + // Create Submit (save) button + var submit = document.createElement("button"); + submit.innerHTML = 'Submit'; + submit.onclick = buttonSubmitClick; + submit.id = 'submit-button'; + // Append the interface buttons into the interfaceButtons object. + interfaceButtons.appendChild(playback); + interfaceButtons.appendChild(submit); + + var sliderHolder = document.createElement("div"); + sliderHolder.id = "slider-holder"; + + + // Global parent for the comment boxes on the page + var feedbackHolder = document.createElement('div'); + feedbackHolder.id = 'feedbackHolder'; + + testContent.style.zIndex = 1; + interfaceContext.insertPoint.innerHTML = null; // Clear the current schema + + // Inject into HTML + testContent.appendChild(title); // Insert the title + testContent.appendChild(interfaceButtons); + testContent.appendChild(sliderHolder); + testContent.appendChild(feedbackHolder); + interfaceContext.insertPoint.appendChild(testContent); + + // Load the full interface + testState.initialise(); + testState.advanceState(); + +} + +function loadTest(audioHolderObject) +{ + var width = window.innerWidth; + var height = window.innerHeight; + var id = audioHolderObject.id; + + interfaceContext.interfaceSliders = []; + + var feedbackHolder = document.getElementById('feedbackHolder'); + var sliderHolder = document.getElementById('slider-holder'); + feedbackHolder.innerHTML = null; + sliderHolder.innerHTML = null; + + // Delete outside reference + var outsideReferenceHolder = document.getElementById('outside-reference'); + if (outsideReferenceHolder != null) { + document.getElementById('interface-buttons').removeChild(outsideReferenceHolder); + } + + var interfaceObj = audioHolderObject.interfaces; + for (var k=0; k<interfaceObj.length; k++) { + // Create the div box to center align + interfaceContext.interfaceSliders.push(new interfaceSliderHolder(interfaceObj[k])); + for (var i=0; i<interfaceObj[k].options.length; i++) + { + if (interfaceObj[k].options[i].type == 'option' && interfaceObj[k].options[i].name == 'playhead') + { + var playbackHolder = document.getElementById('playback-holder'); + if (playbackHolder == null) + { + playbackHolder = document.createElement('div'); + playbackHolder.style.width = "100%"; + playbackHolder.align = 'center'; + playbackHolder.appendChild(interfaceContext.playhead.object); + feedbackHolder.appendChild(playbackHolder); + } + } else if (interfaceObj[k].options[i].type == 'option' && interfaceObj[k].options[i].name == 'page-count') + { + var pagecountHolder = document.getElementById('page-count'); + if (pagecountHolder == null) + { + pagecountHolder = document.createElement('div'); + pagecountHolder.id = 'page-count'; + } + pagecountHolder.innerHTML = '<span>Page '+(audioHolderObject.presentedId+1)+' of '+specification.audioHolders.length+'</span>'; + var inject = document.getElementById('interface-buttons'); + inject.appendChild(pagecountHolder); + } + } + } + + var commentBoxPrefix = "Comment on fragment"; + + var commentShow = audioHolderObject.elementComments; + + var loopPlayback = audioHolderObject.loop; + + currentTestHolder = document.createElement('audioHolder'); + currentTestHolder.id = audioHolderObject.id; + currentTestHolder.repeatCount = audioHolderObject.repeatCount; + + // Find all the audioElements from the audioHolder + $(audioHolderObject.audioElements).each(function(index,element){ + // Find URL of track + // In this jQuery loop, variable 'this' holds the current audioElement. + var audioObject = audioEngineContext.newTrack(element); + // Check if an outside reference + if (element.type == 'outside-reference') + { + // Construct outside reference; + var orNode = new outsideReferenceDOM(audioObject,index,document.getElementById('interface-buttons')); + audioObject.bindInterface(orNode); + } else { + var node = interfaceContext.createCommentBox(audioObject); + // Create a slider per track + var sliderNode = new sliderObject(audioObject,interfaceObj); + audioObject.bindInterface(sliderNode); + } + }); + + // Initialse the interfaceSlider object metrics + + $('.track-slider').mousedown(function(event) { + interfaceContext.selectObject($(this)[0]); + }); + $('.track-slider').on('touchstart',null,function(event) { + interfaceContext.selectObject($(this)[0]); + }); + + $('.track-slider').mousemove(function(event) { + event.preventDefault(); + }); + + $('.slider').mousemove(function(event) { + event.preventDefault(); + var obj = interfaceContext.getSelectedObject(); + if (obj == null) {return;} + $(obj).css("left",event.clientX + "px"); + interfaceContext.moveObject(); + }); + + $('.slider').on('touchmove',null,function(event) { + event.preventDefault(); + var obj = interfaceContext.getSelectedObject(); + if (obj == null) {return;} + var move = event.originalEvent.targetTouches[0].clientX - 6; + $(obj).css("left",move + "px"); + interfaceContext.moveObject(); + }); + + $(document).mouseup(function(event){ + event.preventDefault(); + var obj = interfaceContext.getSelectedObject(); + if (obj == null) {return;} + var interfaceID = obj.parentElement.getAttribute("interfaceid"); + var trackID = obj.getAttribute("trackindex"); + if (interfaceContext.hasSelectedObjectMoved() == true) + { + var l = $(obj).css("left"); + var id = obj.getAttribute('trackIndex'); + var time = audioEngineContext.timer.getTestTime(); + var rate = convSliderPosToRate(obj); + audioEngineContext.audioObjects[id].metric.moved(time,rate); + interfaceContext.interfaceSliders[interfaceID].metrics[trackID].moved(time,rate); + console.log("slider "+id+" moved to "+rate+' ('+time+')'); + } else { + var id = Number(obj.attributes['trackIndex'].value); + //audioEngineContext.metric.sliderPlayed(id); + audioEngineContext.play(id); + // Currently playing track red, rest green + + $('.track-slider').removeClass('track-slider-playing'); + var name = ".track-slider-"+obj.getAttribute("trackindex"); + $(name).addClass('track-slider-playing'); + $('.comment-div').removeClass('comment-box-playing'); + $('#comment-div-'+id).addClass('comment-box-playing'); + var outsideReference = document.getElementById('outside-reference'); + if (outsideReference != undefined) + $(outsideReference).removeClass('track-slider-playing'); + } + interfaceContext.releaseObject(); + }); + + $('.slider').on('touchend',null,function(event){ + var obj = interfaceContext.getSelectedObject(); + if (obj == null) {return;} + var interfaceID = obj.parentElement.getAttribute("interfaceid"); + var trackID = obj.getAttribute("trackindex"); + if (interfaceContext.hasSelectedObjectMoved() == true) + { + var l = $(obj).css("left"); + var id = obj.getAttribute('trackIndex'); + var time = audioEngineContext.timer.getTestTime(); + var rate = convSliderPosToRate(obj); + audioEngineContext.audioObjects[id].metric.moved(time,rate); + interfaceContext.interfaceSliders[interfaceID].metrics[trackID].moved(time,rate); + console.log("slider "+id+" moved to "+rate+' ('+time+')'); + } + interfaceContext.releaseObject(); + }); + + + if (audioHolderObject.showElementComments) { + interfaceContext.showCommentBoxes(feedbackHolder,true); + } + + $(audioHolderObject.commentQuestions).each(function(index,element) { + var node = interfaceContext.createCommentQuestion(element); + feedbackHolder.appendChild(node.holder); + }); + + + //testWaitIndicator(); +} + +function interfaceSliderHolder(interfaceObject) +{ + this.sliders = []; + this.metrics = []; + this.id = document.getElementsByClassName("sliderCanvasDiv").length; + this.name = interfaceObject.name; + this.interfaceObject = interfaceObject; + this.sliderDOM = document.createElement('div'); + this.sliderDOM.className = 'sliderCanvasDiv'; + this.sliderDOM.id = 'sliderCanvasHolder-'+this.id; + + var pagetitle = document.createElement('div'); + pagetitle.className = "pageTitle"; + pagetitle.align = "center"; + var titleSpan = document.createElement('span'); + titleSpan.id = "pageTitle-"+this.id; + if (interfaceObject.title != undefined && typeof interfaceObject.title == "string") + { + titleSpan.textContent = interfaceObject.title; + } else { + titleSpan.textContent = "Axis "+String(this.id+1); + } + pagetitle.appendChild(titleSpan); + this.sliderDOM.appendChild(pagetitle); + + // Create the slider box to hold the slider elements + this.canvas = document.createElement('div'); + if (this.name != undefined) + this.canvas.id = 'slider-'+this.name; + else + this.canvas.id = 'slider-'+this.id; + this.canvas.setAttribute("interfaceid",this.id); + this.canvas.className = 'slider'; + this.canvas.align = "left"; + this.canvas.addEventListener('dragover',function(event){ + event.preventDefault(); + event.dataTransfer.effectAllowed = 'none'; + event.dataTransfer.dropEffect = 'copy'; + return false; + },false); + var sliderMargin = document.createAttribute('marginsize'); + sliderMargin.nodeValue = 42; // Set default margins to 42px either side + // Must have a known EXACT width, as this is used later to determine the ratings + var w = (Number(sliderMargin.nodeValue)+8)*2; + this.canvas.style.width = window.innerWidth - w +"px"; + this.canvas.style.marginLeft = sliderMargin.nodeValue +'px'; + this.canvas.setAttributeNode(sliderMargin); + this.sliderDOM.appendChild(this.canvas); + + // Create the div to hold any scale objects + this.scale = document.createElement('div'); + this.scale.className = 'sliderScale'; + this.scale.id = 'sliderScaleHolder-'+this.id; + this.scale.align = 'left'; + this.sliderDOM.appendChild(this.scale); + var positionScale = this.canvas.style.width.substr(0,this.canvas.style.width.length-2); + var offset = Number(this.canvas.attributes['marginsize'].value); + for (var scaleObj of interfaceObject.scales) + { + var value = document.createAttribute('value'); + var position = Number(scaleObj.position)*0.01; + value.nodeValue = position; + var pixelPosition = (position*positionScale)+offset; + var scaleDOM = document.createElement('span'); + scaleDOM.textContent = scaleObj.text; + this.scale.appendChild(scaleDOM); + scaleDOM.style.left = Math.floor((pixelPosition-($(scaleDOM).width()/2)))+'px'; + scaleDOM.setAttributeNode(value); + } + + var dest = document.getElementById("slider-holder"); + dest.appendChild(this.sliderDOM); + + this.createSliderObject = function(audioObject) + { + var trackObj = document.createElement('div'); + trackObj.className = 'track-slider track-slider-disabled track-slider-'+audioObject.id; + trackObj.id = 'track-slider-'+this.id+'-'+audioObject.id; + trackObj.setAttribute('trackIndex',audioObject.id); + if (this.name != undefined) { + trackObj.setAttribute('interface-name',this.name); + } else { + trackObj.setAttribute('interface-name',this.id); + } + var offset = Number(this.canvas.attributes['marginsize'].value); + // Distribute it randomnly + var w = window.innerWidth - (offset+8)*2; + w = Math.random()*w; + w = Math.floor(w+(offset+8)); + trackObj.style.left = w+'px'; + this.canvas.appendChild(trackObj); + this.sliders.push(trackObj); + this.metrics.push(new metricTracker(this)); + trackObj.innerHTML = '<span>'+(this.metrics.length-1)+'</span>'; + this.metrics[this.metrics.length-1].initialise(convSliderPosToRate(trackObj)); + return trackObj; + }; + + this.resize = function(event) + { + var holdValues = []; + for (var index = 0; index < this.sliders.length; index++) + { + holdValues.push(convSliderPosToRate(this.sliders[index])); + } + var width = event.target.innerWidth; + var sliderDiv = this.canvas; + var sliderScaleDiv = this.scale; + var marginsize = Number(sliderDiv.attributes['marginsize'].value); + var w = (marginsize+8)*2; + sliderDiv.style.width = width - w + 'px'; + var width = width - w; + // Move sliders into new position + for (var index = 0; index < this.sliders.length; index++) + { + var pos = holdValues[index]; + var pix = pos * width; + this.sliders[index].style.left = pix+marginsize+'px'; + } + + // Move scale labels + for (var index = 0; index < this.scale.children.length; index++) + { + var scaleObj = this.scale.children[index]; + var position = Number(scaleObj.attributes['value'].value); + var pixelPosition = (position*width)+marginsize; + scaleObj.style.left = Math.floor((pixelPosition-($(scaleObj).width()/2)))+'px'; + } + }; +} + +function sliderObject(audioObject,interfaceObjects) { + // Create a new slider object; + this.parent = audioObject; + this.trackSliderObjects = []; + for (var i=0; i<interfaceContext.interfaceSliders.length; i++) + { + var trackObj = interfaceContext.interfaceSliders[i].createSliderObject(audioObject); + this.trackSliderObjects.push(trackObj); + } + + // Onclick, switch playback to that track + + this.enable = function() { + if (this.parent.state == 1) + { + $(this.trackSliderObjects).each(function(i,trackObj){ + $(trackObj).removeClass('track-slider-disabled'); + }); + } + }; + this.updateLoading = function(progress) + { + if (progress != 100) + { + progress = String(progress); + progress = progress.split('.')[0]; + this.trackSliderObjects[0].children[0].textContent = progress+'%'; + } else { + this.trackSliderObjects[0].children[0].textContent = this.parent.id; + } + }; + this.exportXMLDOM = function(audioObject) { + // Called by the audioObject holding this element. Must be present + var obj = []; + $(this.trackSliderObjects).each(function(i,trackObj){ + var node = storage.document.createElement('value'); + node.setAttribute("interface-name",trackObj.getAttribute("interface-name")); + node.textContent = convSliderPosToRate(trackObj); + obj.push(node); + }); + + return obj; + }; + this.getValue = function() { + return convSliderPosToRate(this.trackSliderObjects[0]); + }; + this.getPresentedId = function() + { + return this.trackSliderObjects[0].children[0].textContent; + }; + this.canMove = function() + { + return true; + }; +} + +function outsideReferenceDOM(audioObject,index,inject) +{ + this.parent = audioObject; + this.outsideReferenceHolder = document.createElement('div'); + this.outsideReferenceHolder.id = 'outside-reference'; + this.outsideReferenceHolder.className = 'outside-reference track-slider-disabled'; + var outsideReferenceHolderspan = document.createElement('span'); + outsideReferenceHolderspan.textContent = 'Reference'; + this.outsideReferenceHolder.appendChild(outsideReferenceHolderspan); + this.outsideReferenceHolder.setAttribute('track-id',index); + + this.outsideReferenceHolder.onclick = function(event) + { + audioEngineContext.play(event.currentTarget.getAttribute('track-id')); + $('.track-slider').removeClass('track-slider-playing'); + $('.comment-div').removeClass('comment-box-playing'); + if (event.currentTarget.nodeName == 'DIV') { + $(event.currentTarget).addClass('track-slider-playing'); + } else { + $(event.currentTarget.parentElement).addClass('track-slider-playing'); + } + }; + inject.appendChild(this.outsideReferenceHolder); + this.enable = function() + { + if (this.parent.state == 1) + { + $(this.outsideReferenceHolder).removeClass('track-slider-disabled'); + } + }; + this.updateLoading = function(progress) + { + if (progress != 100) + { + progress = String(progress); + progress = progress.split('.')[0]; + this.outsideReferenceHolder[0].children[0].textContent = progress+'%'; + } else { + this.outsideReferenceHolder[0].children[0].textContent = "Play Reference"; + } + }; + this.exportXMLDOM = function(audioObject) + { + return null; + }; + this.getValue = function() + { + return 0; + }; + this.getPresentedId = function() + { + return 'reference'; + }; + this.canMove = function() + { + return false; + }; +} + +function buttonSubmitClick() +{ + var checks = []; + checks = checks.concat(testState.currentStateMap.interfaces[0].options); + checks = checks.concat(specification.interfaces.options); + var canContinue = true; + + // Check that the anchor and reference objects are correctly placed + if (interfaceContext.checkHiddenAnchor() == false) {return;} + if (interfaceContext.checkHiddenReference() == false) {return;} + + for (var i=0; i<checks.length; i++) { + if (checks[i].type == 'check') + { + switch(checks[i].name) { + case 'fragmentPlayed': + // Check if all fragments have been played + var checkState = interfaceContext.checkAllPlayed(); + if (checkState == false) {canContinue = false;} + break; + case 'fragmentFullPlayback': + // Check all fragments have been played to their full length + var checkState = interfaceContext.checkFragmentsFullyPlayed(); + if (checkState == false) {canContinue = false;} + break; + case 'fragmentMoved': + // Check all fragment sliders have been moved. + var checkState = interfaceContext.checkAllMoved(); + if (checkState == false) {canContinue = false;} + break; + case 'fragmentComments': + // Check all fragment sliders have been moved. + var checkState = interfaceContext.checkAllCommented(); + if (checkState == false) {canContinue = false;} + break; + case 'scalerange': + // Check the scale is used to its full width outlined by the node + var checkState = interfaceContext.checkScaleRange(); + if (checkState == false) {canContinue = false;} + break; + default: + console.log("WARNING - Check option "+checks[i].name+" is not supported on this interface"); + break; + } + + } + if (!canContinue) {break;} + } + + if (canContinue) { + if (audioEngineContext.status == 1) { + var playback = document.getElementById('playback-button'); + playback.click(); + // This function is called when the submit button is clicked. Will check for any further tests to perform, or any post-test options + } else + { + if (audioEngineContext.timer.testStarted == false) + { + alert('You have not started the test! Please click a fragment to begin the test!'); + return; + } + } + testState.advanceState(); + } +} + +function convSliderPosToRate(trackSlider) +{ + var slider = trackSlider.parentElement; + var w = slider.style.width; + var marginsize = Number(slider.attributes['marginsize'].value); + var maxPix = w.substr(0,w.length-2); + var pix = trackSlider.style.left; + pix = pix.substr(0,pix.length-2); + var rate = (pix-marginsize)/maxPix; + return rate; +} + +function resizeWindow(event){ + // Function called when the window has been resized. + // MANDATORY FUNCTION + + // Resize the slider objects + for (var i=0; i<interfaceContext.interfaceSliders.length; i++) + { + interfaceContext.interfaceSliders[i].resize(event); + } +} + +function pageXMLSave(store, pageSpecification) +{ + // MANDATORY + // Saves a specific test page + // You can use this space to add any extra nodes to your XML <audioHolder> saves + // Get the current <page> information in store (remember to appendChild your data to it) + // pageSpecification is the current page node configuration + // To create new XML nodes, use storage.document.createElement(); + + if (interfaceContext.interfaceSliders.length == 1) + { + // If there is only one axis, there only needs to be one metric return + return; + } + var audioelements = store.getElementsByTagName("audioelement"); + for (var i=0; i<audioelements.length; i++) + { + // Have to append the metric specific nodes + if (pageSpecification.outsideReference == null || pageSpecification.outsideReference.id != audioelements[i].id) + { + var inject = audioelements[i].getElementsByTagName("metric"); + if (inject.length == 0) + { + inject = storage.document.createElement("metric"); + } else { + inject = inject[0]; + } + for (var k=0; k<interfaceContext.interfaceSliders.length; k++) + { + var mrnodes = interfaceContext.interfaceSliders[k].metrics[i].exportXMLDOM(inject); + for (var j=0; j<mrnodes.length; j++) + { + var name = mrnodes[j].getAttribute("name"); + if (name == "elementTracker" || name == "elementTrackerFull" || name == "elementInitialPosition" || name == "elementFlagMoved") + { + mrnodes[j].setAttribute("interface-name",interfaceContext.interfaceSliders[k].name); + mrnodes[j].setAttribute("interface-id",k); + inject.appendChild(mrnodes[j]); + } + } + } + } + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/interfaces/mushra.css Wed Jan 13 09:34:46 2016 +0000 @@ -0,0 +1,136 @@ +/* + * Hold any style information for MUSHRA interface. Customise if you like to make the interface your own! + * + */ +body { + /* Set the background colour (note US English spelling) to grey*/ + background-color: #ddd +} + +div.pageTitle { + width: auto; + height: 20px; + margin: 10px 0px; +} + +div.pageTitle span{ + font-size: 1.5em; +} + +div.testHalt { + /* Specify any colouring during the test halt for pre/post questions */ + background-color: rgba(0,0,0,0.5); + /* Don't mess with this bit */ + z-index: 2; + width: 100%; + height: 100%; + position: absolute; + left: 0px; + top: 0px; +} + +button { + /* Specify any button structure or style */ + min-width: 20px; + background-color: #ddd +} + +div#slider-holder { + height: inherit; + position: absolute; + left: 0px; + z-index: 3; +} + +div#scale-holder { + height: inherit; + position: absolute; + left: 0px; + z-index: 2; +} + +div#scale-text-holder { + position:relative; + width: 100px; + float: left; +} +div.scale-text { + position: absolute; +} + +canvas#scale-canvas { + position: relative; + float: left; +} + +div.track-slider { + float: left; + width: 94px; + border: solid; + border-width: 1px; + border-color: black; + padding:2px; + margin-left: 50px; +} + +button.outside-reference { + width:120px; + height:20px; + margin-bottom:5px; + position: absolute; +} + +div.track-slider-playing { + background-color: #FFDDDD; +} + +input.track-slider-range { + margin: 2px 0px; +} + +input[type=range][orient=vertical] +{ + writing-mode: bt-lr; /* IE */ + -webkit-appearance: slider-vertical; /* WebKit */ + width: 8px; + padding: 0 5px; + color: rgb(255, 144, 144); +} + +input[type=range]::-webkit-slider-runnable-track { + width: 8px; + cursor: pointer; + background: #fff; + border-radius: 4px; + border: 1px solid #000; +} + +input[type=range]::-moz-range-track { + width: 8px; + cursor: pointer; + background: #fff; + border-radius: 4px; + border: 1px solid #000; +} + +input.track-slider-not-moved[type=range]::-webkit-slider-runnable-track { + background: #aaa; +} + +input.track-slider-not-moved[type=range]::-moz-range-track { + background: #aaa; +} + + +input[type=range]::-moz-range-thumb { + margin-left: -7px; + cursor: pointer; + margin-top: -1px; + box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d; +} + +input[type=range]::-webkit-slider-thumb { + cursor: pointer; + margin-top: -1px; + margin-left: -4px; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/interfaces/mushra.js Wed Jan 13 09:34:46 2016 +0000 @@ -0,0 +1,489 @@ +/** + * mushra.js + * Create the MUSHRA interface + */ + +// Once this is loaded and parsed, begin execution +loadInterface(); + +function loadInterface() { + // Get the dimensions of the screen available to the page + var width = window.innerWidth; + var height = window.innerHeight; + + // The injection point into the HTML page + interfaceContext.insertPoint = document.getElementById("topLevelBody"); + var testContent = document.createElement('div'); + testContent.id = 'testContent'; + + // Create the top div for the Title element + var titleAttr = specification.title; + var title = document.createElement('div'); + title.className = "title"; + title.align = "center"; + var titleSpan = document.createElement('span'); + + // Set title to that defined in XML, else set to default + if (titleAttr != undefined) { + titleSpan.textContent = titleAttr; + } else { + titleSpan.textContent = 'Listening test'; + } + // Insert the titleSpan element into the title div element. + title.appendChild(titleSpan); + + var pagetitle = document.createElement('div'); + pagetitle.className = "pageTitle"; + pagetitle.align = "center"; + var titleSpan = document.createElement('span'); + titleSpan.id = "pageTitle"; + pagetitle.appendChild(titleSpan); + + // Create Interface buttons! + var interfaceButtons = document.createElement('div'); + interfaceButtons.id = 'interface-buttons'; + interfaceButtons.style.height = '25px'; + + // Create playback start/stop points + var playback = document.createElement("button"); + playback.innerHTML = 'Stop'; + playback.id = 'playback-button'; + playback.style.float = 'left'; + // onclick function. Check if it is playing or not, call the correct function in the + // audioEngine, change the button text to reflect the next state. + playback.onclick = function() { + if (audioEngineContext.status == 1) { + audioEngineContext.stop(); + this.innerHTML = 'Stop'; + var time = audioEngineContext.timer.getTestTime(); + console.log('Stopped at ' + time); // DEBUG/SAFETY + } + }; + // Create Submit (save) button + var submit = document.createElement("button"); + submit.innerHTML = 'Submit'; + submit.onclick = buttonSubmitClick; + submit.id = 'submit-button'; + submit.style.float = 'left'; + // Append the interface buttons into the interfaceButtons object. + interfaceButtons.appendChild(playback); + interfaceButtons.appendChild(submit); + + // Create a slider box + var sliderBox = document.createElement('div'); + sliderBox.style.width = "100%"; + sliderBox.style.height = window.innerHeight - 200+12 + 'px'; + sliderBox.style.marginBottom = '10px'; + sliderBox.id = 'slider'; + var scaleHolder = document.createElement('div'); + scaleHolder.id = "scale-holder"; + sliderBox.appendChild(scaleHolder); + var scaleText = document.createElement('div'); + scaleText.id = "scale-text-holder"; + scaleHolder.appendChild(scaleText); + var scaleCanvas = document.createElement('canvas'); + scaleCanvas.id = "scale-canvas"; + scaleHolder.appendChild(scaleCanvas); + var sliderObjectHolder = document.createElement('div'); + sliderObjectHolder.id = 'slider-holder'; + sliderObjectHolder.align = "center"; + sliderBox.appendChild(sliderObjectHolder); + + // Global parent for the comment boxes on the page + var feedbackHolder = document.createElement('div'); + feedbackHolder.id = 'feedbackHolder'; + + testContent.style.zIndex = 1; + interfaceContext.insertPoint.innerHTML = null; // Clear the current schema + + // Inject into HTML + testContent.appendChild(title); // Insert the title + testContent.appendChild(pagetitle); + testContent.appendChild(interfaceButtons); + testContent.appendChild(sliderBox); + testContent.appendChild(feedbackHolder); + interfaceContext.insertPoint.appendChild(testContent); + + // Load the full interface + testState.initialise(); + testState.advanceState(); +} + +function loadTest(audioHolderObject) +{ + var id = audioHolderObject.id; + + var feedbackHolder = document.getElementById('feedbackHolder'); + var interfaceObj = audioHolderObject.interfaces; + if (interfaceObj.length > 1) + { + console.log("WARNING - This interface only supports one <interface> node per page. Using first interface node"); + } + interfaceObj = interfaceObj[0]; + if(interfaceObj.title != null) + { + document.getElementById("pageTitle").textContent = interfaceObj.title; + } + + // Delete outside reference + var outsideReferenceHolder = document.getElementById('outside-reference'); + if (outsideReferenceHolder != null) { + document.getElementById('interface-buttons').removeChild(outsideReferenceHolder); + } + + var sliderBox = document.getElementById('slider-holder'); + feedbackHolder.innerHTML = null; + sliderBox.innerHTML = null; + + var commentBoxPrefix = "Comment on track"; + if (interfaceObj.commentBoxPrefix != undefined) { + commentBoxPrefix = interfaceObj.commentBoxPrefix; + } + var loopPlayback = audioHolderObject.loop; + + currentTestHolder = document.createElement('audioHolder'); + currentTestHolder.id = audioHolderObject.id; + currentTestHolder.repeatCount = audioHolderObject.repeatCount; + + $(audioHolderObject.commentQuestions).each(function(index,element) { + var node = interfaceContext.createCommentQuestion(element); + feedbackHolder.appendChild(node.holder); + }); + + // Find all the audioElements from the audioHolder + var label = 0; + $(audioHolderObject.audioElements).each(function(index,element){ + // Find URL of track + // In this jQuery loop, variable 'this' holds the current audioElement. + + var audioObject = audioEngineContext.newTrack(element); + if (element.type == 'outside-reference') + { + // Construct outside reference; + var orNode = new outsideReferenceDOM(audioObject,index,document.getElementById('interface-buttons')); + audioObject.bindInterface(orNode); + } else { + var node = interfaceContext.createCommentBox(audioObject); + + // Create a slider per track + audioObject.bindInterface(new sliderObject(audioObject,label)); + + if (typeof audioHolderObject.initialPosition === "number") + { + // Set the values + audioObject.interfaceDOM.slider.value = audioHolderObject.initalPosition; + } else { + // Distribute it randomnly + audioObject.interfaceDOM.slider.value = Math.random(); + } + sliderBox.appendChild(audioObject.interfaceDOM.holder); + audioObject.metric.initialise(audioObject.interfaceDOM.slider.value); + label += 1; + } + + }); + + // Auto-align + resizeWindow(null); +} + +function sliderObject(audioObject,label) +{ + // Constructs the slider object. We use the HTML5 slider object + this.parent = audioObject; + this.holder = document.createElement('div'); + this.title = document.createElement('span'); + this.slider = document.createElement('input'); + this.play = document.createElement('button'); + + this.holder.className = 'track-slider'; + this.holder.style.height = window.innerHeight-200 + 'px'; + this.holder.appendChild(this.title); + this.holder.appendChild(this.slider); + this.holder.appendChild(this.play); + this.holder.align = "center"; + if (audioObject.id == 0) + { + this.holder.style.marginLeft = '0px'; + } + this.holder.setAttribute('trackIndex',audioObject.id); + + this.title.textContent = label; + this.title.style.width = "100%"; + this.title.style.float = "left"; + + this.slider.type = "range"; + this.slider.className = "track-slider-range track-slider-not-moved"; + this.slider.min = "0"; + this.slider.max = "1"; + this.slider.step = "0.01"; + this.slider.setAttribute('orient','vertical'); + this.slider.style.height = window.innerHeight-250 + 'px'; + this.slider.onchange = function() + { + var time = audioEngineContext.timer.getTestTime(); + var id = Number(this.parentNode.getAttribute('trackIndex')); + audioEngineContext.audioObjects[id].metric.moved(time,this.value); + console.log('slider '+id+' moved to '+this.value+' ('+time+')'); + $(this).removeClass('track-slider-not-moved'); + }; + + this.play.textContent = "Loading..."; + this.play.value = audioObject.id; + this.play.style.float = "left"; + this.play.style.width = "100%"; + this.play.disabled = true; + this.play.onclick = function(event) + { + var id = Number(event.currentTarget.value); + //audioEngineContext.metric.sliderPlayed(id); + audioEngineContext.play(id); + $(".track-slider").removeClass('track-slider-playing'); + $(event.currentTarget.parentElement).addClass('track-slider-playing'); + var outsideReference = document.getElementById('outside-reference'); + if (outsideReference != null) { + $(outsideReference).removeClass('track-slider-playing'); + } + }; + + this.enable = function() { + this.play.disabled = false; + this.play.textContent = "Play"; + $(this.slider).removeClass('track-slider-disabled'); + }; + + this.exportXMLDOM = function(audioObject) { + // Called by the audioObject holding this element. Must be present + var node = storage.document.createElement('value'); + node.textContent = this.slider.value; + return node; + }; + this.getValue = function() { + return this.slider.value; + }; + + this.resize = function(event) + { + this.holder.style.height = window.innerHeight-200 + 'px'; + this.slider.style.height = window.innerHeight-250 + 'px'; + }; + this.updateLoading = function(progress) + { + progress = String(progress); + progress = progress.substr(0,5); + this.play.textContent = "Loading: "+progress+"%"; + }; + + if (this.parent.state == 1) + { + this.enable(); + } + this.getPresentedId = function() + { + return this.title.textContent; + }; + this.canMove = function() + { + return true; + }; +} + +function outsideReferenceDOM(audioObject,index,inject) +{ + this.parent = audioObject; + this.outsideReferenceHolder = document.createElement('button'); + this.outsideReferenceHolder.id = 'outside-reference'; + this.outsideReferenceHolder.className = 'outside-reference'; + this.outsideReferenceHolder.setAttribute('track-id',index); + this.outsideReferenceHolder.textContent = "Play Reference"; + this.outsideReferenceHolder.disabled = true; + + this.outsideReferenceHolder.onclick = function(event) + { + audioEngineContext.play(event.currentTarget.getAttribute('track-id')); + $('.track-slider').removeClass('track-slider-playing'); + $('.comment-div').removeClass('comment-box-playing'); + $(event.currentTarget).addClass('track-slider-playing'); + }; + inject.appendChild(this.outsideReferenceHolder); + this.enable = function() + { + if (this.parent.state == 1) + { + this.outsideReferenceHolder.disabled = false; + } + }; + this.updateLoading = function(progress) + { + if (progress != 100) + { + progress = String(progress); + progress = progress.split('.')[0]; + this.outsideReferenceHolder[0].children[0].textContent = progress+'%'; + } else { + this.outsideReferenceHolder[0].children[0].textContent = "Play Reference"; + } + }; + this.exportXMLDOM = function(audioObject) + { + return null; + }; + this.getValue = function() + { + return 0; + }; + this.getPresentedId = function() + { + return 'reference'; + }; + this.canMove = function() + { + return false; + }; +} + +function resizeWindow(event) +{ + // Function called when the window has been resized. + // MANDATORY FUNCTION + + var outsideRef = document.getElementById('outside-reference'); + if(outsideRef != null) + { + outsideRef.style.left = (window.innerWidth-120)/2 + 'px'; + } + + // Auto-align + var numObj = document.getElementsByClassName('track-slider').length; + var totalWidth = (numObj-1)*150+100; + var diff = (window.innerWidth - totalWidth)/2; + document.getElementById('slider').style.height = window.innerHeight - 180 + 'px'; + if (diff <= 0){diff = 0;} + document.getElementById('slider-holder').style.marginLeft = diff + 'px'; + for (var i in audioEngineContext.audioObjects) + { + if (audioEngineContext.audioObjects[i].specification.type != 'outside-reference'){ + audioEngineContext.audioObjects[i].interfaceDOM.resize(event); + } + } + document.getElementById('scale-holder').style.marginLeft = (diff-100) + 'px'; + document.getElementById('scale-text-holder').style.height = window.innerHeight-194 + 'px'; + var canvas = document.getElementById('scale-canvas'); + canvas.width = totalWidth; + canvas.height = window.innerHeight-194; + drawScale(); +} + +function drawScale() +{ + var interfaceObj = testState.currentStateMap.interfaces[0]; + var scales = testState.currentStateMap.interfaces[0].scales; + scales = scales.sort(function(a,b) { + return a.position - b.position; + }); + var canvas = document.getElementById('scale-canvas'); + var ctx = canvas.getContext("2d"); + var height = canvas.height; + var width = canvas.width; + var draw_heights = [24, height-34]; + var textHolder = document.getElementById('scale-text-holder'); + textHolder.innerHTML = null; + var lastHeight = 0; + for (var scale of scales) + { + var posPercent = scale.position / 100.0; + var posPix = (1-posPercent)*(draw_heights[1]-draw_heights[0])+draw_heights[0]; + ctx.fillStyle = "#000000"; + ctx.setLineDash([1,2]); + ctx.moveTo(0,posPix); + ctx.lineTo(width,posPix); + ctx.stroke(); + var text = document.createElement('div'); + text.align = "right"; + var textC = document.createElement('span'); + textC.textContent = scale.text; + text.appendChild(textC); + text.className = "scale-text"; + textHolder.appendChild(text); + text.style.top = (posPix-9) + 'px'; + text.style.left = 100 - ($(text).width()+3) + 'px'; + lastHeight = posPix; + } +} + +function buttonSubmitClick() // TODO: Only when all songs have been played! +{ + var checks = []; + checks = checks.concat(testState.currentStateMap.interfaces[0].options); + checks = checks.concat(specification.interfaces.options); + var canContinue = true; + + // Check that the anchor and reference objects are correctly placed + if (interfaceContext.checkHiddenAnchor() == false) {return;} + if (interfaceContext.checkHiddenReference() == false) {return;} + + for (var i=0; i<checks.length; i++) { + if (checks[i].type == 'check') + { + switch(checks[i].name) { + case 'fragmentPlayed': + // Check if all fragments have been played + var checkState = interfaceContext.checkAllPlayed(); + if (checkState == false) {canContinue = false;} + break; + case 'fragmentFullPlayback': + // Check all fragments have been played to their full length + var checkState = interfaceContext.checkAllPlayed(); + if (checkState == false) {canContinue = false;} + console.log('NOTE: fragmentFullPlayback not currently implemented, performing check fragmentPlayed instead'); + break; + case 'fragmentMoved': + // Check all fragment sliders have been moved. + var checkState = interfaceContext.checkAllMoved(); + if (checkState == false) {canContinue = false;} + break; + case 'fragmentComments': + // Check all fragment sliders have been moved. + var checkState = interfaceContext.checkAllCommented(); + if (checkState == false) {canContinue = false;} + break; + //case 'scalerange': + // Check the scale is used to its full width outlined by the node + //var checkState = interfaceContext.checkScaleRange(); + //if (checkState == false) {canContinue = false;} + // break; + default: + console.log("WARNING - Check option "+checks[i].check+" is not supported on this interface"); + break; + } + + } + if (!canContinue) {break;} + } + + if (canContinue) { + if (audioEngineContext.status == 1) { + var playback = document.getElementById('playback-button'); + playback.click(); + // This function is called when the submit button is clicked. Will check for any further tests to perform, or any post-test options + } else + { + if (audioEngineContext.timer.testStarted == false) + { + alert('You have not started the test! Please press start to begin the test!'); + return; + } + } + testState.advanceState(); + } +} + +function pageXMLSave(store, pageSpecification) +{ + // MANDATORY + // Saves a specific test page + // You can use this space to add any extra nodes to your XML <audioHolder> saves + // Get the current <page> information in store (remember to appendChild your data to it) + // pageSpecification is the current page node configuration + // To create new XML nodes, use storage.document.createElement(); +}
--- a/mushra.css Tue Jan 12 18:29:55 2016 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,136 +0,0 @@ -/* - * Hold any style information for MUSHRA interface. Customise if you like to make the interface your own! - * - */ -body { - /* Set the background colour (note US English spelling) to grey*/ - background-color: #ddd -} - -div.pageTitle { - width: auto; - height: 20px; - margin: 10px 0px; -} - -div.pageTitle span{ - font-size: 1.5em; -} - -div.testHalt { - /* Specify any colouring during the test halt for pre/post questions */ - background-color: rgba(0,0,0,0.5); - /* Don't mess with this bit */ - z-index: 2; - width: 100%; - height: 100%; - position: absolute; - left: 0px; - top: 0px; -} - -button { - /* Specify any button structure or style */ - min-width: 20px; - background-color: #ddd -} - -div#slider-holder { - height: inherit; - position: absolute; - left: 0px; - z-index: 3; -} - -div#scale-holder { - height: inherit; - position: absolute; - left: 0px; - z-index: 2; -} - -div#scale-text-holder { - position:relative; - width: 100px; - float: left; -} -div.scale-text { - position: absolute; -} - -canvas#scale-canvas { - position: relative; - float: left; -} - -div.track-slider { - float: left; - width: 94px; - border: solid; - border-width: 1px; - border-color: black; - padding:2px; - margin-left: 50px; -} - -button.outside-reference { - width:120px; - height:20px; - margin-bottom:5px; - position: absolute; -} - -div.track-slider-playing { - background-color: #FFDDDD; -} - -input.track-slider-range { - margin: 2px 0px; -} - -input[type=range][orient=vertical] -{ - writing-mode: bt-lr; /* IE */ - -webkit-appearance: slider-vertical; /* WebKit */ - width: 8px; - padding: 0 5px; - color: rgb(255, 144, 144); -} - -input[type=range]::-webkit-slider-runnable-track { - width: 8px; - cursor: pointer; - background: #fff; - border-radius: 4px; - border: 1px solid #000; -} - -input[type=range]::-moz-range-track { - width: 8px; - cursor: pointer; - background: #fff; - border-radius: 4px; - border: 1px solid #000; -} - -input.track-slider-not-moved[type=range]::-webkit-slider-runnable-track { - background: #aaa; -} - -input.track-slider-not-moved[type=range]::-moz-range-track { - background: #aaa; -} - - -input[type=range]::-moz-range-thumb { - margin-left: -7px; - cursor: pointer; - margin-top: -1px; - box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d; -} - -input[type=range]::-webkit-slider-thumb { - cursor: pointer; - margin-top: -1px; - margin-left: -4px; -}
--- a/mushra.js Tue Jan 12 18:29:55 2016 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,489 +0,0 @@ -/** - * mushra.js - * Create the MUSHRA interface - */ - -// Once this is loaded and parsed, begin execution -loadInterface(); - -function loadInterface() { - // Get the dimensions of the screen available to the page - var width = window.innerWidth; - var height = window.innerHeight; - - // The injection point into the HTML page - interfaceContext.insertPoint = document.getElementById("topLevelBody"); - var testContent = document.createElement('div'); - testContent.id = 'testContent'; - - // Create the top div for the Title element - var titleAttr = specification.title; - var title = document.createElement('div'); - title.className = "title"; - title.align = "center"; - var titleSpan = document.createElement('span'); - - // Set title to that defined in XML, else set to default - if (titleAttr != undefined) { - titleSpan.textContent = titleAttr; - } else { - titleSpan.textContent = 'Listening test'; - } - // Insert the titleSpan element into the title div element. - title.appendChild(titleSpan); - - var pagetitle = document.createElement('div'); - pagetitle.className = "pageTitle"; - pagetitle.align = "center"; - var titleSpan = document.createElement('span'); - titleSpan.id = "pageTitle"; - pagetitle.appendChild(titleSpan); - - // Create Interface buttons! - var interfaceButtons = document.createElement('div'); - interfaceButtons.id = 'interface-buttons'; - interfaceButtons.style.height = '25px'; - - // Create playback start/stop points - var playback = document.createElement("button"); - playback.innerHTML = 'Stop'; - playback.id = 'playback-button'; - playback.style.float = 'left'; - // onclick function. Check if it is playing or not, call the correct function in the - // audioEngine, change the button text to reflect the next state. - playback.onclick = function() { - if (audioEngineContext.status == 1) { - audioEngineContext.stop(); - this.innerHTML = 'Stop'; - var time = audioEngineContext.timer.getTestTime(); - console.log('Stopped at ' + time); // DEBUG/SAFETY - } - }; - // Create Submit (save) button - var submit = document.createElement("button"); - submit.innerHTML = 'Submit'; - submit.onclick = buttonSubmitClick; - submit.id = 'submit-button'; - submit.style.float = 'left'; - // Append the interface buttons into the interfaceButtons object. - interfaceButtons.appendChild(playback); - interfaceButtons.appendChild(submit); - - // Create a slider box - var sliderBox = document.createElement('div'); - sliderBox.style.width = "100%"; - sliderBox.style.height = window.innerHeight - 200+12 + 'px'; - sliderBox.style.marginBottom = '10px'; - sliderBox.id = 'slider'; - var scaleHolder = document.createElement('div'); - scaleHolder.id = "scale-holder"; - sliderBox.appendChild(scaleHolder); - var scaleText = document.createElement('div'); - scaleText.id = "scale-text-holder"; - scaleHolder.appendChild(scaleText); - var scaleCanvas = document.createElement('canvas'); - scaleCanvas.id = "scale-canvas"; - scaleHolder.appendChild(scaleCanvas); - var sliderObjectHolder = document.createElement('div'); - sliderObjectHolder.id = 'slider-holder'; - sliderObjectHolder.align = "center"; - sliderBox.appendChild(sliderObjectHolder); - - // Global parent for the comment boxes on the page - var feedbackHolder = document.createElement('div'); - feedbackHolder.id = 'feedbackHolder'; - - testContent.style.zIndex = 1; - interfaceContext.insertPoint.innerHTML = null; // Clear the current schema - - // Inject into HTML - testContent.appendChild(title); // Insert the title - testContent.appendChild(pagetitle); - testContent.appendChild(interfaceButtons); - testContent.appendChild(sliderBox); - testContent.appendChild(feedbackHolder); - interfaceContext.insertPoint.appendChild(testContent); - - // Load the full interface - testState.initialise(); - testState.advanceState(); -} - -function loadTest(audioHolderObject) -{ - var id = audioHolderObject.id; - - var feedbackHolder = document.getElementById('feedbackHolder'); - var interfaceObj = audioHolderObject.interfaces; - if (interfaceObj.length > 1) - { - console.log("WARNING - This interface only supports one <interface> node per page. Using first interface node"); - } - interfaceObj = interfaceObj[0]; - if(interfaceObj.title != null) - { - document.getElementById("pageTitle").textContent = interfaceObj.title; - } - - // Delete outside reference - var outsideReferenceHolder = document.getElementById('outside-reference'); - if (outsideReferenceHolder != null) { - document.getElementById('interface-buttons').removeChild(outsideReferenceHolder); - } - - var sliderBox = document.getElementById('slider-holder'); - feedbackHolder.innerHTML = null; - sliderBox.innerHTML = null; - - var commentBoxPrefix = "Comment on track"; - if (interfaceObj.commentBoxPrefix != undefined) { - commentBoxPrefix = interfaceObj.commentBoxPrefix; - } - var loopPlayback = audioHolderObject.loop; - - currentTestHolder = document.createElement('audioHolder'); - currentTestHolder.id = audioHolderObject.id; - currentTestHolder.repeatCount = audioHolderObject.repeatCount; - - $(audioHolderObject.commentQuestions).each(function(index,element) { - var node = interfaceContext.createCommentQuestion(element); - feedbackHolder.appendChild(node.holder); - }); - - // Find all the audioElements from the audioHolder - var label = 0; - $(audioHolderObject.audioElements).each(function(index,element){ - // Find URL of track - // In this jQuery loop, variable 'this' holds the current audioElement. - - var audioObject = audioEngineContext.newTrack(element); - if (element.type == 'outside-reference') - { - // Construct outside reference; - var orNode = new outsideReferenceDOM(audioObject,index,document.getElementById('interface-buttons')); - audioObject.bindInterface(orNode); - } else { - var node = interfaceContext.createCommentBox(audioObject); - - // Create a slider per track - audioObject.bindInterface(new sliderObject(audioObject,label)); - - if (typeof audioHolderObject.initialPosition === "number") - { - // Set the values - audioObject.interfaceDOM.slider.value = audioHolderObject.initalPosition; - } else { - // Distribute it randomnly - audioObject.interfaceDOM.slider.value = Math.random(); - } - sliderBox.appendChild(audioObject.interfaceDOM.holder); - audioObject.metric.initialise(audioObject.interfaceDOM.slider.value); - label += 1; - } - - }); - - // Auto-align - resizeWindow(null); -} - -function sliderObject(audioObject,label) -{ - // Constructs the slider object. We use the HTML5 slider object - this.parent = audioObject; - this.holder = document.createElement('div'); - this.title = document.createElement('span'); - this.slider = document.createElement('input'); - this.play = document.createElement('button'); - - this.holder.className = 'track-slider'; - this.holder.style.height = window.innerHeight-200 + 'px'; - this.holder.appendChild(this.title); - this.holder.appendChild(this.slider); - this.holder.appendChild(this.play); - this.holder.align = "center"; - if (audioObject.id == 0) - { - this.holder.style.marginLeft = '0px'; - } - this.holder.setAttribute('trackIndex',audioObject.id); - - this.title.textContent = label; - this.title.style.width = "100%"; - this.title.style.float = "left"; - - this.slider.type = "range"; - this.slider.className = "track-slider-range track-slider-not-moved"; - this.slider.min = "0"; - this.slider.max = "1"; - this.slider.step = "0.01"; - this.slider.setAttribute('orient','vertical'); - this.slider.style.height = window.innerHeight-250 + 'px'; - this.slider.onchange = function() - { - var time = audioEngineContext.timer.getTestTime(); - var id = Number(this.parentNode.getAttribute('trackIndex')); - audioEngineContext.audioObjects[id].metric.moved(time,this.value); - console.log('slider '+id+' moved to '+this.value+' ('+time+')'); - $(this).removeClass('track-slider-not-moved'); - }; - - this.play.textContent = "Loading..."; - this.play.value = audioObject.id; - this.play.style.float = "left"; - this.play.style.width = "100%"; - this.play.disabled = true; - this.play.onclick = function(event) - { - var id = Number(event.currentTarget.value); - //audioEngineContext.metric.sliderPlayed(id); - audioEngineContext.play(id); - $(".track-slider").removeClass('track-slider-playing'); - $(event.currentTarget.parentElement).addClass('track-slider-playing'); - var outsideReference = document.getElementById('outside-reference'); - if (outsideReference != null) { - $(outsideReference).removeClass('track-slider-playing'); - } - }; - - this.enable = function() { - this.play.disabled = false; - this.play.textContent = "Play"; - $(this.slider).removeClass('track-slider-disabled'); - }; - - this.exportXMLDOM = function(audioObject) { - // Called by the audioObject holding this element. Must be present - var node = storage.document.createElement('value'); - node.textContent = this.slider.value; - return node; - }; - this.getValue = function() { - return this.slider.value; - }; - - this.resize = function(event) - { - this.holder.style.height = window.innerHeight-200 + 'px'; - this.slider.style.height = window.innerHeight-250 + 'px'; - }; - this.updateLoading = function(progress) - { - progress = String(progress); - progress = progress.substr(0,5); - this.play.textContent = "Loading: "+progress+"%"; - }; - - if (this.parent.state == 1) - { - this.enable(); - } - this.getPresentedId = function() - { - return this.title.textContent; - }; - this.canMove = function() - { - return true; - }; -} - -function outsideReferenceDOM(audioObject,index,inject) -{ - this.parent = audioObject; - this.outsideReferenceHolder = document.createElement('button'); - this.outsideReferenceHolder.id = 'outside-reference'; - this.outsideReferenceHolder.className = 'outside-reference'; - this.outsideReferenceHolder.setAttribute('track-id',index); - this.outsideReferenceHolder.textContent = "Play Reference"; - this.outsideReferenceHolder.disabled = true; - - this.outsideReferenceHolder.onclick = function(event) - { - audioEngineContext.play(event.currentTarget.getAttribute('track-id')); - $('.track-slider').removeClass('track-slider-playing'); - $('.comment-div').removeClass('comment-box-playing'); - $(event.currentTarget).addClass('track-slider-playing'); - }; - inject.appendChild(this.outsideReferenceHolder); - this.enable = function() - { - if (this.parent.state == 1) - { - this.outsideReferenceHolder.disabled = false; - } - }; - this.updateLoading = function(progress) - { - if (progress != 100) - { - progress = String(progress); - progress = progress.split('.')[0]; - this.outsideReferenceHolder[0].children[0].textContent = progress+'%'; - } else { - this.outsideReferenceHolder[0].children[0].textContent = "Play Reference"; - } - }; - this.exportXMLDOM = function(audioObject) - { - return null; - }; - this.getValue = function() - { - return 0; - }; - this.getPresentedId = function() - { - return 'reference'; - }; - this.canMove = function() - { - return false; - }; -} - -function resizeWindow(event) -{ - // Function called when the window has been resized. - // MANDATORY FUNCTION - - var outsideRef = document.getElementById('outside-reference'); - if(outsideRef != null) - { - outsideRef.style.left = (window.innerWidth-120)/2 + 'px'; - } - - // Auto-align - var numObj = document.getElementsByClassName('track-slider').length; - var totalWidth = (numObj-1)*150+100; - var diff = (window.innerWidth - totalWidth)/2; - document.getElementById('slider').style.height = window.innerHeight - 180 + 'px'; - if (diff <= 0){diff = 0;} - document.getElementById('slider-holder').style.marginLeft = diff + 'px'; - for (var i in audioEngineContext.audioObjects) - { - if (audioEngineContext.audioObjects[i].specification.type != 'outside-reference'){ - audioEngineContext.audioObjects[i].interfaceDOM.resize(event); - } - } - document.getElementById('scale-holder').style.marginLeft = (diff-100) + 'px'; - document.getElementById('scale-text-holder').style.height = window.innerHeight-194 + 'px'; - var canvas = document.getElementById('scale-canvas'); - canvas.width = totalWidth; - canvas.height = window.innerHeight-194; - drawScale(); -} - -function drawScale() -{ - var interfaceObj = testState.currentStateMap.interfaces[0]; - var scales = testState.currentStateMap.interfaces[0].scales; - scales = scales.sort(function(a,b) { - return a.position - b.position; - }); - var canvas = document.getElementById('scale-canvas'); - var ctx = canvas.getContext("2d"); - var height = canvas.height; - var width = canvas.width; - var draw_heights = [24, height-34]; - var textHolder = document.getElementById('scale-text-holder'); - textHolder.innerHTML = null; - var lastHeight = 0; - for (var scale of scales) - { - var posPercent = scale.position / 100.0; - var posPix = (1-posPercent)*(draw_heights[1]-draw_heights[0])+draw_heights[0]; - ctx.fillStyle = "#000000"; - ctx.setLineDash([1,2]); - ctx.moveTo(0,posPix); - ctx.lineTo(width,posPix); - ctx.stroke(); - var text = document.createElement('div'); - text.align = "right"; - var textC = document.createElement('span'); - textC.textContent = scale.text; - text.appendChild(textC); - text.className = "scale-text"; - textHolder.appendChild(text); - text.style.top = (posPix-9) + 'px'; - text.style.left = 100 - ($(text).width()+3) + 'px'; - lastHeight = posPix; - } -} - -function buttonSubmitClick() // TODO: Only when all songs have been played! -{ - var checks = []; - checks = checks.concat(testState.currentStateMap.interfaces[0].options); - checks = checks.concat(specification.interfaces.options); - var canContinue = true; - - // Check that the anchor and reference objects are correctly placed - if (interfaceContext.checkHiddenAnchor() == false) {return;} - if (interfaceContext.checkHiddenReference() == false) {return;} - - for (var i=0; i<checks.length; i++) { - if (checks[i].type == 'check') - { - switch(checks[i].name) { - case 'fragmentPlayed': - // Check if all fragments have been played - var checkState = interfaceContext.checkAllPlayed(); - if (checkState == false) {canContinue = false;} - break; - case 'fragmentFullPlayback': - // Check all fragments have been played to their full length - var checkState = interfaceContext.checkAllPlayed(); - if (checkState == false) {canContinue = false;} - console.log('NOTE: fragmentFullPlayback not currently implemented, performing check fragmentPlayed instead'); - break; - case 'fragmentMoved': - // Check all fragment sliders have been moved. - var checkState = interfaceContext.checkAllMoved(); - if (checkState == false) {canContinue = false;} - break; - case 'fragmentComments': - // Check all fragment sliders have been moved. - var checkState = interfaceContext.checkAllCommented(); - if (checkState == false) {canContinue = false;} - break; - //case 'scalerange': - // Check the scale is used to its full width outlined by the node - //var checkState = interfaceContext.checkScaleRange(); - //if (checkState == false) {canContinue = false;} - // break; - default: - console.log("WARNING - Check option "+checks[i].check+" is not supported on this interface"); - break; - } - - } - if (!canContinue) {break;} - } - - if (canContinue) { - if (audioEngineContext.status == 1) { - var playback = document.getElementById('playback-button'); - playback.click(); - // This function is called when the submit button is clicked. Will check for any further tests to perform, or any post-test options - } else - { - if (audioEngineContext.timer.testStarted == false) - { - alert('You have not started the test! Please press start to begin the test!'); - return; - } - } - testState.advanceState(); - } -} - -function pageXMLSave(store, pageSpecification) -{ - // MANDATORY - // Saves a specific test page - // You can use this space to add any extra nodes to your XML <audioHolder> saves - // Get the current <page> information in store (remember to appendChild your data to it) - // pageSpecification is the current page node configuration - // To create new XML nodes, use storage.document.createElement(); -}