Mercurial > hg > webaudioevaluationtool
changeset 1762:64c0614fe3f2
Merge from branch "Dev_main"
author | Nicholas Jillings <nickjillings@users.noreply.github.com> |
---|---|
date | Fri, 05 Jun 2015 12:54:52 +0100 |
parents | c33eef57f0b9 (diff) a5890005e289 (current diff) |
children | 3496198d413c |
files | ape.js core.js example_eval/project.xml test_create/test_create.html |
diffstat | 3 files changed, 658 insertions(+), 409 deletions(-) [+] |
line wrap: on
line diff
--- a/ape.js Thu Jun 04 10:06:42 2015 +0100 +++ b/ape.js Fri Jun 05 12:54:52 2015 +0100 @@ -11,123 +11,29 @@ // Once this is loaded and parsed, begin execution -loadInterface(projectXML); +loadInterface(); -function loadInterface(xmlDoc) { +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 - var insertPoint = document.getElementById("topLevelBody"); + interfaceContext.insertPoint = document.getElementById("topLevelBody"); var testContent = document.createElement('div'); testContent.id = 'testContent'; - - - // Decode parts of the xmlDoc that are needed - // xmlDoc MUST already be parsed by jQuery! - var xmlSetup = xmlDoc.find('setup'); - // Should put in an error function here incase of malprocessed or malformed XML - - // Create pre and post test questions - - var preTest = xmlSetup.find('PreTest'); - var postTest = xmlSetup.find('PostTest'); - preTest = preTest[0]; - postTest = postTest[0]; - - if (preTest == undefined) {preTest = document.createElement("preTest");} - if (postTest == undefined){postTest= document.createElement("postTest");} - - testState.stateMap.push(preTest); - - // Extract the different test XML DOM trees - var audioHolders = xmlDoc.find('audioHolder'); - var testXMLSetups = []; - audioHolders.each(function(index,element) { - var repeatN = element.attributes['repeatCount'].value; - for (var r=0; r<=repeatN; r++) { - testXMLSetups.push(element); - } - }); - - // New check if we need to randomise the test order - var randomise = xmlSetup[0].attributes['randomiseOrder']; - if (randomise != undefined) { - if (randomise.value === 'true'){ - randomise = true; - } else { - randomise = false; - } - } else { - randomise = false; - } - - if (randomise) - { - testXMLSetups = randomiseOrder(testXMLSetups); - } - - $(testXMLSetups).each(function(index,elem){ - testState.stateMap.push(elem); - }) - - testState.stateMap.push(postTest); - - // Obtain the metrics enabled - var metricNode = xmlSetup.find('Metric'); - var metricNode = metricNode.find('metricEnable'); - metricNode.each(function(index,node){ - var enabled = node.textContent; - switch(enabled) - { - case 'testTimer': - sessionMetrics.prototype.enableTestTimer = true; - break; - case 'elementTimer': - sessionMetrics.prototype.enableElementTimer = true; - break; - case 'elementTracker': - sessionMetrics.prototype.enableElementTracker = true; - break; - case 'elementListenTracker': - sessionMetrics.prototype.enableElementListenTracker = true; - break; - case 'elementInitialPosition': - sessionMetrics.prototype.enableElementInitialPosition = true; - break; - case 'elementFlagListenedTo': - sessionMetrics.prototype.enableFlagListenedTo = true; - break; - case 'elementFlagMoved': - sessionMetrics.prototype.enableFlagMoved = true; - break; - case 'elementFlagComments': - sessionMetrics.prototype.enableFlagComments = true; - break; - } - }); + // Create APE specific metric functions audioEngineContext.metric.initialiseTest = function() { }; - audioEngineContext.metric.sliderMoveStart = function(id) - { - if (this.data == -1) - { - this.data = id; - } else { - console.log('ERROR: Metric tracker detecting two moves!'); - this.data = -1; - } - }; audioEngineContext.metric.sliderMoved = function() { - var time = audioEngineContext.timer.getTestTime(); + var id = this.data; this.data = -1; var position = convSliderPosToRate(id); @@ -153,8 +59,10 @@ console.log('slider ' + id + ' played (' + time + ')'); // DEBUG/SAFETY: show played slider id }; + // Bindings for audioObjects + // Create the top div for the Title element - var titleAttr = xmlSetup[0].attributes['title']; + var titleAttr = specification.title; var title = document.createElement('div'); title.className = "title"; title.align = "center"; @@ -162,9 +70,9 @@ // Set title to that defined in XML, else set to default if (titleAttr != undefined) { - titleSpan.innerHTML = titleAttr.value; + titleSpan.textContent = titleAttr; } else { - titleSpan.innerHTML = 'Listening test'; + titleSpan.textContent = 'Listening test'; } // Insert the titleSpan element into the title div element. title.appendChild(titleSpan); @@ -176,24 +84,10 @@ titleSpan.id = "pageTitle"; pagetitle.appendChild(titleSpan); - // Store the return URL path in global projectReturn - projectReturn = xmlSetup[0].attributes['projectReturn']; - if (projectReturn == undefined) { - console.log("WARNING - projectReturn not specified! Will assume null."); - projectReturn = "null"; - } else { - projectReturn = projectReturn.value; - } - // Create Interface buttons! var interfaceButtons = document.createElement('div'); interfaceButtons.id = 'interface-buttons'; - // MANUAL DOWNLOAD POINT - // If project return is null, this MUST be specified as the location to create the download link - var downloadPoint = document.createElement('div'); - downloadPoint.id = 'download-point'; - // Create playback start/stop points var playback = document.createElement("button"); playback.innerHTML = 'Stop'; @@ -216,7 +110,6 @@ // Append the interface buttons into the interfaceButtons object. interfaceButtons.appendChild(playback); interfaceButtons.appendChild(submit); - interfaceButtons.appendChild(downloadPoint); // Now create the slider and HTML5 canvas boxes @@ -254,9 +147,7 @@ feedbackHolder.id = 'feedbackHolder'; testContent.style.zIndex = 1; - insertPoint.innerHTML = null; // Clear the current schema - - currentState = 'preTest'; + interfaceContext.insertPoint.innerHTML = null; // Clear the current schema // Inject into HTML testContent.appendChild(title); // Insert the title @@ -264,7 +155,7 @@ testContent.appendChild(interfaceButtons); testContent.appendChild(sliderBox); testContent.appendChild(feedbackHolder); - insertPoint.appendChild(testContent); + interfaceContext.insertPoint.appendChild(testContent); // Load the full interface testState.initialise(); @@ -272,13 +163,13 @@ } -function loadTest(textXML) +function loadTest(audioHolderObject) { // Reset audioEngineContext.Metric globals for new test audioEngineContext.newTestPage(); - var id = textXML.id; + var id = audioHolderObject.id; var feedbackHolder = document.getElementById('feedbackHolder'); var canvas = document.getElementById('slider'); @@ -286,75 +177,49 @@ canvas.innerHTML = null; // Setup question title - var interfaceObj = $(textXML).find('interface'); - var titleNode = interfaceObj.find('title'); - if (titleNode[0] != undefined) - { - document.getElementById('pageTitle').textContent = titleNode[0].textContent; - } - var positionScale = canvas.style.width.substr(0,canvas.style.width.length-2); - var offset = Number(document.getElementById('slider').attributes['marginsize'].value); - var scale = document.getElementById('sliderScaleHolder'); - scale.innerHTML = null; - interfaceObj.find('scale').each(function(index,scaleObj){ - var value = document.createAttribute('value'); - var position = Number(scaleObj.attributes['position'].value)*0.01; - value.nodeValue = position; - var pixelPosition = (position*positionScale)+offset; - var scaleDOM = document.createElement('span'); - scaleDOM.textContent = scaleObj.textContent; - scale.appendChild(scaleDOM); - scaleDOM.style.left = Math.floor((pixelPosition-($(scaleDOM).width()/2)))+'px'; - scaleDOM.setAttributeNode(value); - }); - - var commentBoxPrefix = interfaceObj.find('commentBoxPrefix'); - if (commentBoxPrefix.length != 0) { - commentBoxPrefix = commentBoxPrefix[0].textContent; - } else { - commentBoxPrefix = "Comment on track"; + var interfaceObj = audioHolderObject.interfaces; + var commentBoxPrefix = "Comment on track"; + if (interfaceObj.length != 0) { + interfaceObj = interfaceObj[0]; + var titleNode = interfaceObj.title; + if (titleNode != undefined) + { + document.getElementById('pageTitle').textContent = titleNode; + } + var positionScale = canvas.style.width.substr(0,canvas.style.width.length-2); + var offset = Number(document.getElementById('slider').attributes['marginsize'].value); + var scale = document.getElementById('sliderScaleHolder'); + scale.innerHTML = null; + $(interfaceObj.scale).each(function(index,scaleObj){ + var value = document.createAttribute('value'); + var position = Number(scaleObj[0])*0.01; + value.nodeValue = position; + var pixelPosition = (position*positionScale)+offset; + var scaleDOM = document.createElement('span'); + scaleDOM.textContent = scaleObj[1]; + scale.appendChild(scaleDOM); + scaleDOM.style.left = Math.floor((pixelPosition-($(scaleDOM).width()/2)))+'px'; + scaleDOM.setAttributeNode(value); + }); + + if (interfaceObj.commentBoxPrefix != undefined) { + commentBoxPrefix = interfaceObj.commentBoxPrefix; + } } - // Extract the hostURL attribute. If not set, create an empty string. - var hostURL = textXML.attributes['hostURL']; - if (hostURL == undefined) { - hostURL = ""; - } else { - hostURL = hostURL.value; - } - // Extract the sampleRate. If set, convert the string to a Number. - var hostFs = textXML.attributes['sampleRate']; - if (hostFs != undefined) { - hostFs = Number(hostFs.value); - } - /// CHECK FOR SAMPLE RATE COMPATIBILITY - if (hostFs != undefined) { - if (Number(hostFs) != audioContext.sampleRate) { - var errStr = 'Sample rates do not match! Requested '+Number(hostFs)+', got '+audioContext.sampleRate+'. Please set the sample rate to match before completing this test.'; + if (audioHolderObject.sampleRate != undefined) { + if (Number(audioHolderObject.sampleRate) != audioContext.sampleRate) { + var errStr = 'Sample rates do not match! Requested '+Number(audioHolderObject.sampleRate)+', got '+audioContext.sampleRate+'. Please set the sample rate to match before completing this test.'; alert(errStr); return; } } - var commentShow = textXML.attributes['elementComments']; - if (commentShow != undefined) { - if (commentShow.value == 'false') {commentShow = false;} - else {commentShow = true;} - } else {commentShow = true;} + var commentShow = audioHolderObject.elementComments; - var loopPlayback = textXML.attributes['loop']; - if (loopPlayback != undefined) - { - loopPlayback = loopPlayback.value; - if (loopPlayback == 'true') { - loopPlayback = true; - } else { - loopPlayback = false; - } - } else { - loopPlayback = false; - } + var loopPlayback = audioHolderObject.loop; + audioEngineContext.loopPlayback = loopPlayback; // Create AudioEngine bindings for playback if (loopPlayback) { @@ -383,117 +248,59 @@ } currentTestHolder = document.createElement('audioHolder'); - currentTestHolder.id = textXML.id; - currentTestHolder.repeatCount = textXML.attributes['repeatCount'].value; + currentTestHolder.id = audioHolderObject.id; + currentTestHolder.repeatCount = audioHolderObject.repeatCount; - var randomise = textXML.attributes['randomiseOrder']; - if (randomise != undefined) {randomise = randomise.value;} - else {randomise = false;} + var randomise = audioHolderObject.randomiseOrder; - var audioElements = $(textXML).find('audioElements'); + var audioElements = audioHolderObject.audioElements; currentTrackOrder = []; - audioElements.each(function(index,element){ - // Find any blind-repeats - // Not implemented yet, but just in case - currentTrackOrder[index] = element; - }); if (randomise) { - currentTrackOrder = randomiseOrder(currentTrackOrder); + audioHolderObject.audioElements = randomiseOrder(audioHolderObject.audioElements); } // Delete any previous audioObjects associated with the audioEngine audioEngineContext.audioObjects = []; // Find all the audioElements from the audioHolder - $(currentTrackOrder).each(function(index,element){ + $(audioHolderObject.audioElements).each(function(index,element){ // Find URL of track // In this jQuery loop, variable 'this' holds the current audioElement. // Now load each audio sample. First create the new track by passing the full URL - var trackURL = hostURL + this.attributes['url'].value; - audioEngineContext.newTrack(trackURL); + var trackURL = audioHolderObject.hostURL + element.url; + var audioObject = audioEngineContext.newTrack(element); if (commentShow) { - // Create document objects to hold the comment boxes - var trackComment = document.createElement('div'); - trackComment.className = 'comment-div'; - trackComment.id = 'comment-div-'+index; - // Create a string next to each comment asking for a comment - var trackString = document.createElement('span'); - trackString.innerHTML = commentBoxPrefix+' '+index; - // Create the HTML5 comment box 'textarea' - var trackCommentBox = document.createElement('textarea'); - trackCommentBox.rows = '4'; - trackCommentBox.cols = '100'; - trackCommentBox.name = 'trackComment'+index; - trackCommentBox.className = 'trackComment'; - var br = document.createElement('br'); - // Add to the holder. - trackComment.appendChild(trackString); - trackComment.appendChild(br); - trackComment.appendChild(trackCommentBox); - feedbackHolder.appendChild(trackComment); + var node = interfaceContext.createCommentBox(audioObject); } // Create a slider per track - - var trackSliderObj = document.createElement('div'); - trackSliderObj.className = 'track-slider'; - trackSliderObj.id = 'track-slider-'+index; - - var trackSliderAOIndex = document.createAttribute('trackIndex'); - trackSliderAOIndex.nodeValue = index; - trackSliderObj.setAttributeNode(trackSliderAOIndex); + audioObject.interfaceDOM = new sliderObject(audioObject); // Distribute it randomnly var w = window.innerWidth - (offset+8)*2; w = Math.random()*w; w = Math.floor(w+(offset+8)); - trackSliderObj.style.left = w+'px'; - trackSliderObj.innerHTML = '<span>'+index+'</span>'; - trackSliderObj.draggable = true; - trackSliderObj.ondragend = dragEnd; - trackSliderObj.ondragstart = function() - { - var id = Number(event.srcElement.attributes['trackIndex'].value); - audioEngineContext.metric.sliderMoveStart(id); - }; + audioObject.interfaceDOM.trackSliderObj.style.left = w+'px'; - // Onclick, switch playback to that track - trackSliderObj.onclick = function() { - // Start the test on first click, that way timings are more accurate. - audioEngineContext.play(); - if (audioEngineContext.audioObjectsReady) { - // Cannot continue to issue play command until audioObjects reported as ready! - // Get the track ID from the object ID - var id = Number(event.srcElement.attributes['trackIndex'].value); - //audioEngineContext.metric.sliderPlayed(id); - audioEngineContext.selectedTrack(id); - // Currently playing track red, rest green - - //document.getElementById('track-slider-'+index).style.backgroundColor = "#FF0000"; - $('.track-slider').removeClass('track-slider-playing'); - $(event.srcElement).addClass('track-slider-playing'); - $('.comment-div').removeClass('comment-box-playing'); - $('#comment-div-'+id).addClass('comment-box-playing'); - } - }; - - canvas.appendChild(trackSliderObj); - audioEngineContext.audioObjects[index].metric.initialised(convSliderPosToRate(index)); + canvas.appendChild(audioObject.interfaceDOM.trackSliderObj); + audioObject.metric.initialised(convSliderPosToRate(audioObject.interfaceDOM.trackSliderObj)); }); + if (commentShow) { + interfaceContext.showCommentBoxes(feedbackHolder,true); + } // Append any commentQuestion boxes - var commentQuestions = $(textXML).find('CommentQuestion'); - $(commentQuestions).each(function(index,element) { + $(audioHolderObject.commentQuestions).each(function(index,element) { // Create document objects to hold the comment boxes var trackComment = document.createElement('div'); trackComment.className = 'comment-div commentQuestion'; - trackComment.id = element.attributes['id'].value; + trackComment.id = element.id; // Create a string next to each comment asking for a comment var trackString = document.createElement('span'); - trackString.innerHTML = element.textContent; + trackString.innerHTML = element.question; // Create the HTML5 comment box 'textarea' var trackCommentBox = document.createElement('textarea'); trackCommentBox.rows = '4'; @@ -512,6 +319,45 @@ testWaitIndicator(); } +function sliderObject(audioObject) { + // Create a new slider object; + this.parent = audioObject; + this.trackSliderObj = document.createElement('div'); + this.trackSliderObj.className = 'track-slider'; + this.trackSliderObj.id = 'track-slider-'+audioObject.id; + + this.trackSliderObj.setAttribute('trackIndex',audioObject.id); + this.trackSliderObj.innerHTML = '<span>'+audioObject.id+'</span>'; + this.trackSliderObj.draggable = true; + this.trackSliderObj.ondragend = dragEnd; + + // Onclick, switch playback to that track + this.trackSliderObj.onclick = function() { + // Start the test on first click, that way timings are more accurate. + audioEngineContext.play(); + if (audioEngineContext.audioObjectsReady) { + // Cannot continue to issue play command until audioObjects reported as ready! + // Get the track ID from the object ID + var id = Number(event.srcElement.attributes['trackIndex'].value); + //audioEngineContext.metric.sliderPlayed(id); + audioEngineContext.selectedTrack(id); + // Currently playing track red, rest green + + //document.getElementById('track-slider-'+index).style.backgroundColor = "#FF0000"; + $('.track-slider').removeClass('track-slider-playing'); + $(event.srcElement).addClass('track-slider-playing'); + $('.comment-div').removeClass('comment-box-playing'); + $('#comment-div-'+id).addClass('comment-box-playing'); + } + }; + + this.exportXMLDOM = function() { + // Called by the audioObject holding this element. Must be present + var node = document.createElement('value'); + node.textContent = convSliderPosToRate(this.trackSliderObj); + return node; + }; +} function dragEnd(ev) { // Function call when a div has been dropped @@ -529,7 +375,9 @@ this.style.left = (w+marginSize) + 'px'; } } - audioEngineContext.metric.sliderMoved(); + var time = audioEngineContext.timer.getTestTime(); + var id = Number(ev.srcElement.getAttribute('trackindex')); + audioEngineContext.audioObjects[id].metric.moved(time,convSliderPosToRate(ev.srcElement)); } function buttonSubmitClick() // TODO: Only when all songs have been played! @@ -569,12 +417,11 @@ } } -function convSliderPosToRate(id) +function convSliderPosToRate(slider) { var w = document.getElementById('slider').style.width; var marginsize = Number(document.getElementById('slider').attributes['marginsize'].value); var maxPix = w.substr(0,w.length-2); - var slider = document.getElementsByClassName('track-slider')[id]; var pix = slider.style.left; pix = pix.substr(0,pix.length-2); var rate = (pix-marginsize)/maxPix; @@ -614,17 +461,13 @@ }); } -function pageXMLSave(store, testXML, testId) +function pageXMLSave(store, testXML) { // Saves a specific test page var xmlDoc = store; // Check if any session wide metrics are enabled - var commentShow = testXML.attributes['elementComments']; - if (commentShow != undefined) { - if (commentShow.value == 'false') {commentShow = false;} - else {commentShow = true;} - } else {commentShow = true;} + var commentShow = testXML.elementComments; var metric = document.createElement('metric'); if (audioEngineContext.metric.enableTestTimer) @@ -635,89 +478,10 @@ metric.appendChild(testTime); } xmlDoc.appendChild(metric); - var trackSliderObjects = document.getElementsByClassName('track-slider'); - var commentObjects = document.getElementsByClassName('comment-div'); - for (var i=0; i<trackSliderObjects.length; i++) + var audioObjects = audioEngineContext.audioObjects; + for (var i=0; i<audioObjects.length; i++) { - var audioElement = document.createElement('audioElement'); - audioElement.id = currentTrackOrder[i].attributes['id'].value; - audioElement.url = currentTrackOrder[i].attributes['url'].value; - var value = document.createElement('value'); - value.innerHTML = convSliderPosToRate(i); - if (commentShow) { - var comment = document.createElement("comment"); - var question = document.createElement("question"); - var response = document.createElement("response"); - question.textContent = commentObjects[i].children[0].textContent; - response.textContent = commentObjects[i].children[2].value; - console.log('Comment ' + i + ': ' + commentObjects[i].children[2].value); // DEBUG/SAFETY - comment.appendChild(question); - comment.appendChild(response); - audioElement.appendChild(comment); - } - audioElement.appendChild(value); - // Check for any per element metrics - var metric = document.createElement('metric'); - var elementMetric = audioEngineContext.audioObjects[i].metric; - if (audioEngineContext.metric.enableElementTimer) { - var elementTimer = document.createElement('metricResult'); - elementTimer.id = 'elementTimer'; - elementTimer.textContent = elementMetric.listenedTimer; - metric.appendChild(elementTimer); - } - if (audioEngineContext.metric.enableElementTracker) { - var elementTrackerFull = document.createElement('metricResult'); - elementTrackerFull.id = 'elementTrackerFull'; - var data = elementMetric.movementTracker; - for (var k=0; k<data.length; k++) - { - var timePos = document.createElement('timePos'); - timePos.id = k; - var time = document.createElement('time'); - time.textContent = data[k][0]; - var position = document.createElement('position'); - position.textContent = data[k][1]; - timePos.appendChild(time); - timePos.appendChild(position); - elementTrackerFull.appendChild(timePos); - } - metric.appendChild(elementTrackerFull); - } - if (audioEngineContext.metric.enableElementListenTracker) { - var elementListenTracker = document.createElement('metricResult'); - elementListenTracker.id = 'elementListenTracker'; - var obj = elementMetric.listenTracker; - for (var k=0; k<obj.length; k++) { - elementListenTracker.appendChild(obj[k]); - } - metric.appendChild(elementListenTracker); - } - if (audioEngineContext.metric.enableElementInitialPosition) { - var elementInitial = document.createElement('metricResult'); - elementInitial.id = 'elementInitialPosition'; - elementInitial.textContent = elementMetric.initialPosition; - metric.appendChild(elementInitial); - } - if (audioEngineContext.metric.enableFlagListenedTo) { - var flagListenedTo = document.createElement('metricResult'); - flagListenedTo.id = 'elementFlagListenedTo'; - flagListenedTo.textContent = elementMetric.wasListenedTo; - metric.appendChild(flagListenedTo); - } - if (audioEngineContext.metric.enableFlagMoved) { - var flagMoved = document.createElement('metricResult'); - flagMoved.id = 'elementFlagMoved'; - flagMoved.textContent = elementMetric.wasMoved; - metric.appendChild(flagMoved); - } - if (audioEngineContext.metric.enableFlagComments) { - var flagComments = document.createElement('metricResult'); - flagComments.id = 'elementFlagComments'; - if (response.textContent.length == 0) {flag.textContent = 'false';} - else {flag.textContet = 'true';} - metric.appendChild(flagComments); - } - audioElement.appendChild(metric); + var audioElement = audioEngineContext.audioObjects[i].exportXMLDOM(); xmlDoc.appendChild(audioElement); } var commentQuestion = document.getElementsByClassName('commentQuestion');
--- a/core.js Thu Jun 04 10:06:42 2015 +0100 +++ b/core.js Fri Jun 05 12:54:52 2015 +0100 @@ -8,9 +8,10 @@ /* create the web audio API context and store in audioContext*/ var audioContext; // Hold the browser web audio API var projectXML; // Hold the parsed setup XML +var specification; +var interfaceContext; var popup; // Hold the interfacePopup object var testState; -var currentState; // Keep track of the current state (pre/post test, which test, final test? first test?) var currentTrackOrder = []; // Hold the current XML tracks in their (randomised) order var audioEngineContext; // The custome AudioEngine object var projectReturn; // Hold the URL for the return @@ -36,6 +37,12 @@ // Create the popup interface object popup = new interfacePopup(); + + // Create the specification object + specification = new Specification(); + + // Create the interface object + interfaceContext = new Interface(specification); }; function interfacePopup() { @@ -46,6 +53,7 @@ this.popupOptions = null; this.currentIndex = null; this.responses = null; + this.createPopup = function(){ // Create popup window interface var insertPoint = document.getElementById("topLevelBody"); @@ -69,12 +77,16 @@ this.popupButton.className = 'popupButton'; this.popupButton.innerHTML = 'Next'; this.popupButton.onclick = function(){popup.buttonClicked();}; + this.popup.style.zIndex = -1; + this.popup.style.visibility = 'hidden'; + blank.style.zIndex = -2; + blank.style.visibility = 'hidden'; insertPoint.appendChild(this.popup); insertPoint.appendChild(blank); }; this.showPopup = function(){ - if (this.popup == null || this.popup == undefined) { + if (this.popup == null) { this.createPopup(); } this.popup.style.zIndex = 3; @@ -96,19 +108,67 @@ // This will take the node from the popupOptions and display it var node = this.popupOptions[this.currentIndex]; this.popupContent.innerHTML = null; - if (node.nodeName == 'statement') { + if (node.type == 'statement') { var span = document.createElement('span'); - span.textContent = node.textContent; + span.textContent = node.statement; this.popupContent.appendChild(span); - } else if (node.nodeName == 'question') { + } else if (node.type == 'question') { var span = document.createElement('span'); - span.textContent = node.textContent; + span.textContent = node.question; var textArea = document.createElement('textarea'); var br = document.createElement('br'); this.popupContent.appendChild(span); this.popupContent.appendChild(br); this.popupContent.appendChild(textArea); this.popupContent.childNodes[2].focus(); + } else if (node.type == 'checkbox') { + var span = document.createElement('span'); + span.textContent = node.statement; + this.popupContent.appendChild(span); + var optHold = document.createElement('div'); + optHold.id = 'option-holder'; + optHold.align = 'left'; + for (var i=0; i<node.options.length; i++) { + var option = node.options[i]; + var input = document.createElement('input'); + input.id = option.id; + input.type = 'checkbox'; + var span = document.createElement('span'); + span.textContent = option.text; + var hold = document.createElement('div'); + hold.setAttribute('name','option'); + hold.style.float = 'left'; + hold.style.padding = '4px'; + hold.appendChild(input); + hold.appendChild(span); + optHold.appendChild(hold); + } + this.popupContent.appendChild(optHold); + } else if (node.type == 'radio') { + var span = document.createElement('span'); + span.textContent = node.statement; + this.popupContent.appendChild(span); + var optHold = document.createElement('div'); + optHold.id = 'option-holder'; + optHold.align = 'none'; + optHold.style.float = 'left'; + optHold.style.width = "100%"; + for (var i=0; i<node.options.length; i++) { + var option = node.options[i]; + var input = document.createElement('input'); + input.id = option.name; + input.type = 'radio'; + input.name = node.id; + var span = document.createElement('span'); + span.textContent = option.text; + var hold = document.createElement('div'); + hold.setAttribute('name','option'); + hold.style.padding = '4px'; + hold.appendChild(input); + hold.appendChild(span); + optHold.appendChild(hold); + } + this.popupContent.appendChild(optHold); } this.popupContent.appendChild(this.popupButton); }; @@ -116,11 +176,11 @@ this.initState = function(node) { //Call this with your preTest and postTest nodes when needed to // initialise the popup procedure. - this.popupOptions = $(node).children(); + this.popupOptions = node.options; if (this.popupOptions.length > 0) { - if (node.nodeName == 'preTest' || node.nodeName == 'PreTest') { + if (node.type == 'pretest') { this.responses = document.createElement('PreTest'); - } else if (node.nodeName == 'postTest' || node.nodeName == 'PostTest') { + } else if (node.type == 'posttest') { this.responses = document.createElement('PostTest'); } else { console.log ('WARNING - popup node neither pre or post!'); @@ -129,34 +189,61 @@ this.currentIndex = 0; this.showPopup(); this.postNode(); + } else { + advanceState(); } }; this.buttonClicked = function() { // Each time the popup button is clicked! var node = this.popupOptions[this.currentIndex]; - if (node.nodeName == 'question') { + if (node.type == 'question') { // Must extract the question data - var mandatory = node.attributes['mandatory']; - if (mandatory == undefined) { - mandatory = false; - } else { - if (mandatory.value == 'true'){mandatory = true;} - else {mandatory = false;} - } var textArea = $(popup.popupContent).find('textarea')[0]; - if (mandatory == true && textArea.value.length == 0) { + if (node.mandatory == true && textArea.value.length == 0) { alert('This question is mandatory'); return; } else { // Save the text content var hold = document.createElement('comment'); - hold.id = node.attributes['id'].value; + hold.id = node.id; hold.innerHTML = textArea.value; console.log("Question: "+ node.textContent); console.log("Question Response: "+ textArea.value); this.responses.appendChild(hold); } + } else if (node.type == 'checkbox') { + // Must extract checkbox data + var optHold = document.getElementById('option-holder'); + var hold = document.createElement('checkbox'); + console.log("Checkbox: "+ node.statement); + hold.id = node.id; + for (var i=0; i<optHold.childElementCount; i++) { + var input = optHold.childNodes[i].getElementsByTagName('input')[0]; + var statement = optHold.childNodes[i].getElementsByTagName('span')[0]; + var response = document.createElement('option'); + response.setAttribute('id',input.id); + response.setAttribute('checked',input.checked); + hold.appendChild(response); + console.log(input.id +': '+ input.checked); + } + this.responses.appendChild(hold); + } else if (node.type == "radio") { + var optHold = document.getElementById('option-holder'); + var hold = document.createElement('radio'); + var responseID = null; + var i=0; + while(responseID == null) { + var input = optHold.childNodes[i].getElementsByTagName('input')[0]; + if (input.checked == true) { + responseID = i; + } + i++; + } + hold.id = node.id; + hold.setAttribute('name',node.options[responseID].name); + hold.textContent = node.options[responseID].text; + this.responses.appendChild(hold); } this.currentIndex++; if (this.currentIndex < this.popupOptions.length) { @@ -198,9 +285,9 @@ this.stateIndex = -1; var that = this; for (var id=0; id<this.stateMap.length; id++){ - var name = this.stateMap[id].nodeName; + var name = this.stateMap[id].type; var obj = document.createElement(name); - if (name == "audioHolder") { + if (name == 'audioHolder') { obj.id = this.stateMap[id].id; } this.stateResults.push(obj); @@ -217,7 +304,7 @@ console.log('Starting test...'); } if (this.currentIndex == null){ - if (this.currentStateMap.nodeName == "audioHolder") { + if (this.currentStateMap.type == "audioHolder") { // Save current page this.testPageCompleted(this.stateResults[this.stateIndex],this.currentStateMap,this.currentTestId); this.currentTestId++; @@ -225,15 +312,15 @@ this.stateIndex++; if (this.stateIndex >= this.stateMap.length) { console.log('Test Completed'); - createProjectSave(projectReturn); + createProjectSave(specification.projectReturn); } else { this.currentStateMap = this.stateMap[this.stateIndex]; - if (this.currentStateMap.nodeName == "audioHolder") { + if (this.currentStateMap.type == "audioHolder") { console.log('Loading test page'); loadTest(this.currentStateMap); this.initialiseInnerState(this.currentStateMap); - } else if (this.currentStateMap.nodeName == "PreTest" || this.currentStateMap.nodeName == "PostTest") { - if (this.currentStateMap.childElementCount >= 1) { + } else if (this.currentStateMap.type == "pretest" || this.currentStateMap.type == "posttest") { + if (this.currentStateMap.options.length >= 1) { popup.initState(this.currentStateMap); } else { this.advanceState(); @@ -251,22 +338,22 @@ // Function called each time a test page has been completed // Can be used to over-rule default behaviour - pageXMLSave(store, testXML, testId); - } + pageXMLSave(store, testXML); + }; - this.initialiseInnerState = function(testXML) { + this.initialiseInnerState = function(node) { // Parses the received testXML for pre and post test options this.currentStateMap = []; - var preTest = $(testXML).find('PreTest')[0]; - var postTest = $(testXML).find('PostTest')[0]; + var preTest = node.preTest; + var postTest = node.postTest; if (preTest == undefined) {preTest = document.createElement("preTest");} if (postTest == undefined){postTest= document.createElement("postTest");} this.currentStateMap.push(preTest); - this.currentStateMap.push(testXML); + this.currentStateMap.push(node); this.currentStateMap.push(postTest); this.currentIndex = -1; this.advanceInnerState(); - } + }; this.advanceInnerState = function() { this.currentIndex++; @@ -275,17 +362,17 @@ this.currentStateMap = this.stateMap[this.stateIndex]; this.advanceState(); } else { - if (this.currentStateMap[this.currentIndex].nodeName == "audioHolder") { + if (this.currentStateMap[this.currentIndex].type == "audioHolder") { console.log("Loading test page"+this.currentTestId); - } else if (this.currentStateMap[this.currentIndex].nodeName == "PreTest") { + } else if (this.currentStateMap[this.currentIndex].type == "pretest") { popup.initState(this.currentStateMap[this.currentIndex]); - } else if (this.currentStateMap[this.currentIndex].nodeName == "PostTest") { + } else if (this.currentStateMap[this.currentIndex].type == "posttest") { popup.initState(this.currentStateMap[this.currentIndex]); } else { this.advanceInnerState(); } } - } + }; this.previousState = function(){}; } @@ -322,16 +409,67 @@ function loadProjectSpecCallback(response) { // Function called after asynchronous download of XML project specification - var decode = $.parseXML(response); - projectXML = $(decode); + //var decode = $.parseXML(response); + //projectXML = $(decode); - // Now extract the setup tag - var xmlSetup = projectXML.find('setup'); + var parse = new DOMParser(); + projectXML = parse.parseFromString(response,'text/xml'); + + // Build the specification + specification.decode(); + + testState.stateMap.push(specification.preTest); + + // New check if we need to randomise the test order + if (specification.randomiseOrder) + { + specification.audioHolders = randomiseOrder(specification.audioHolders); + } + + $(specification.audioHolders).each(function(index,elem){ + testState.stateMap.push(elem); + }); + + testState.stateMap.push(specification.postTest); + + // Obtain the metrics enabled + $(specification.metrics).each(function(index,node){ + var enabled = node.textContent; + switch(node.enabled) + { + case 'testTimer': + sessionMetrics.prototype.enableTestTimer = true; + break; + case 'elementTimer': + sessionMetrics.prototype.enableElementTimer = true; + break; + case 'elementTracker': + sessionMetrics.prototype.enableElementTracker = true; + break; + case 'elementListenTracker': + sessionMetrics.prototype.enableElementListenTracker = true; + break; + case 'elementInitialPosition': + sessionMetrics.prototype.enableElementInitialPosition = true; + break; + case 'elementFlagListenedTo': + sessionMetrics.prototype.enableFlagListenedTo = true; + break; + case 'elementFlagMoved': + sessionMetrics.prototype.enableFlagMoved = true; + break; + case 'elementFlagComments': + sessionMetrics.prototype.enableFlagComments = true; + break; + } + }); + + + // Detect the interface to use and load the relevant javascripts. - var interfaceType = xmlSetup[0].attributes['interface']; var interfaceJS = document.createElement('script'); interfaceJS.setAttribute("type","text/javascript"); - if (interfaceType.value == 'APE') { + if (specification.interfaceType == 'APE') { interfaceJS.setAttribute("src","ape.js"); // APE comes with a css file @@ -365,11 +503,9 @@ a.download = "save.xml"; a.textContent = "Save File"; - var submitDiv = document.getElementById('download-point'); - submitDiv.appendChild(a); popup.showPopup(); popup.popupContent.innerHTML = null; - popup.popupContent.appendChild(submitDiv) + popup.popupContent.appendChild(a); } else { var xmlhttp = new XMLHttpRequest; xmlhttp.open("POST",destURL,true); @@ -386,7 +522,6 @@ }; xmlhttp.send(file); } - return submitDiv; } // Only other global function which must be defined in the interface class. Determines how to create the XML document. @@ -461,7 +596,7 @@ }; - this.newTrack = function(url) { + this.newTrack = function(element) { // Pull data from given URL into new audio buffer // URLs must either be from the same source OR be setup to 'Access-Control-Allow-Origin' @@ -470,7 +605,9 @@ this.audioObjects[audioObjectId] = new audioObject(audioObjectId); // AudioObject will get track itself. - this.audioObjects[audioObjectId].constructTrack(url); + this.audioObjects[audioObjectId].specification = element; + this.audioObjects[audioObjectId].constructTrack(element.parent.hostURL + element.url); + return this.audioObjects[audioObjectId]; }; this.newTestPage = function() { @@ -507,11 +644,16 @@ function audioObject(id) { // The main buffer object with common control nodes to the AudioEngine + this.specification; this.id = id; this.state = 0; // 0 - no data, 1 - ready this.url = null; // Hold the URL given for the output back to the results. this.metric = new metricTracker(this); + // Bindings for GUI + this.interfaceDOM = null; + this.commentDOM = null; + // Create a buffer and external gain control to allow internal patching of effects and volume leveling. this.bufferNode = undefined; this.outputGain = audioContext.createGain(); @@ -529,14 +671,14 @@ this.loopStart = function() { this.outputGain.gain.value = 1.0; this.metric.startListening(audioEngineContext.timer.getTestTime()); - } + }; this.loopStop = function() { if (this.outputGain.gain.value != 0.0) { this.outputGain.gain.value = 0.0; this.metric.stopListening(audioEngineContext.timer.getTestTime()); } - } + }; this.play = function(startTime) { this.bufferNode = audioContext.createBufferSource(); @@ -609,6 +751,15 @@ request.send(); }; + this.exportXMLDOM = function() { + var root = document.createElement('audioElement'); + root.id = this.specification.id; + root.setAttribute('url',this.url); + root.appendChild(this.interfaceDOM.exportXMLDOM()); + root.appendChild(this.commentDOM.exportXMLDOM()); + root.appendChild(this.metric.exportXMLDOM()); + return root; + }; } function timer() @@ -736,6 +887,72 @@ console.log('slider ' + this.parent.id + ' played for (' + diff + ')'); // DEBUG/SAFETY: show played slider id } }; + + this.exportXMLDOM = function() { + var root = document.createElement('metric'); + if (audioEngineContext.metric.enableElementTimer) { + var mElementTimer = document.createElement('metricresult'); + mElementTimer.setAttribute('name','enableElementTimer'); + mElementTimer.textContent = this.listenedTimer; + root.appendChild(mElementTimer); + } + if (audioEngineContext.metric.enableElementTracker) { + var elementTrackerFull = document.createElement('metricResult'); + elementTrackerFull.setAttribute('name','elementTrackerFull'); + for (var k=0; k<this.movementTracker.length; k++) + { + var timePos = document.createElement('timePos'); + timePos.id = k; + var time = document.createElement('time'); + time.textContent = this.movementTracker[k][0]; + var position = document.createElement('position'); + position.textContent = this.movementTracker[k][1]; + timePos.appendChild(time); + timePos.appendChild(position); + elementTrackerFull.appendChild(timePos); + } + root.appendChild(elementTrackerFull); + } + if (audioEngineContext.metric.enableElementListenTracker) { + var elementListenTracker = document.createElement('metricResult'); + elementListenTracker.setAttribute('name','elementListenTracker'); + for (var k=0; k<this.listenTracker.length; k++) { + elementListenTracker.appendChild(this.listenTracker[k]); + } + root.appendChild(elementListenTracker); + } + if (audioEngineContext.metric.enableElementInitialPosition) { + var elementInitial = document.createElement('metricResult'); + elementInitial.setAttribute('name','elementInitialPosition'); + elementInitial.textContent = this.initialPosition; + root.appendChild(elementInitial); + } + if (audioEngineContext.metric.enableFlagListenedTo) { + var flagListenedTo = document.createElement('metricResult'); + flagListenedTo.setAttribute('name','elementFlagListenedTo'); + flagListenedTo.textContent = this.wasListenedTo; + root.appendChild(flagListenedTo); + } + if (audioEngineContext.metric.enableFlagMoved) { + var flagMoved = document.createElement('metricResult'); + flagMoved.setAttribute('name','elementFlagMoved'); + flagMoved.textContent = this.wasMoved; + root.appendChild(flagMoved); + } + if (audioEngineContext.metric.enableFlagComments) { + var flagComments = document.createElement('metricResult'); + flagComments.setAttribute('name','elementFlagComments'); + if (this.parent.commentDOM == null) + {flag.textContent = 'false';} + else if (this.parent.commentDOM.textContent.length == 0) + {flag.textContent = 'false';} + else + {flag.textContet = 'true';} + root.appendChild(flagComments); + } + + return root; + }; } function randomiseOrder(input) @@ -803,17 +1020,24 @@ var hold = document.createElement("div"); hold.id = "testWaitIndicator"; hold.className = "indicator-box"; + hold.style.zIndex = 3; var span = document.createElement("span"); span.textContent = "Please wait! Elements still loading"; hold.appendChild(span); + var blank = document.createElement('div'); + blank.className = 'testHalt'; + blank.id = "testHaltBlank"; var body = document.getElementsByTagName('body')[0]; body.appendChild(hold); + body.appendChild(blank); testWaitTimerIntervalHolder = setInterval(function(){ var ready = audioEngineContext.checkAllReady(); if (ready) { var elem = document.getElementById('testWaitIndicator'); + var blank = document.getElementById('testHaltBlank'); var body = document.getElementsByTagName('body')[0]; body.removeChild(elem); + body.removeChild(blank); clearInterval(testWaitTimerIntervalHolder); } },500,false); @@ -821,3 +1045,253 @@ } var testWaitTimerIntervalHolder = null; + +function Specification() { + // Handles the decoding of the project specification XML into a simple JavaScript Object. + + this.interfaceType; + this.projectReturn; + this.randomiseOrder; + this.collectMetrics; + this.preTest; + this.postTest; + this.metrics =[]; + + this.audioHolders = []; + + this.decode = function() { + // projectXML - DOM Parsed document + var setupNode = projectXML.getElementsByTagName('setup')[0]; + this.interfaceType = setupNode.getAttribute('interface'); + this.projectReturn = setupNode.getAttribute('projectReturn'); + if (setupNode.getAttribute('randomiseOrder') == "true") { + this.randomiseOrder = true; + } else {this.randomiseOrder = false;} + if (setupNode.getAttribute('collectMetrics') == "true") { + this.collectMetrics = true; + } else {this.collectMetrics = false;} + var metricCollection = setupNode.getElementsByTagName('Metric'); + + this.preTest = new this.prepostNode('pretest',setupNode.getElementsByTagName('PreTest')); + this.postTest = new this.prepostNode('posttest',setupNode.getElementsByTagName('PostTest')); + + if (metricCollection.length > 0) { + metricCollection = metricCollection[0].getElementsByTagName('metricEnable'); + for (var i=0; i<metricCollection.length; i++) { + this.metrics.push(new this.metricNode(metricCollection[i].textContent)); + } + } + + var audioHolders = projectXML.getElementsByTagName('audioHolder'); + for (var i=0; i<audioHolders.length; i++) { + this.audioHolders.push(new this.audioHolderNode(this,audioHolders[i])); + } + + }; + + this.prepostNode = function(type,Collection) { + this.type = type; + this.options = []; + + this.OptionNode = function(child) { + + this.childOption = function(element) { + this.type = 'option'; + this.id = element.id; + this.name = element.getAttribute('name'); + this.text = element.textContent; + } + + this.type = child.nodeName; + if (child.nodeName == "question") { + this.id = child.id; + this.mandatory; + if (child.getAttribute('mandatory') == "true") {this.mandatory = true;} + else {this.mandatory = false;} + this.question = child.textContent; + } else if (child.nodeName == "statement") { + this.statement = child.textContent; + } else if (child.nodeName == "checkbox" || child.nodeName == "radio") { + var element = child.firstElementChild; + this.id = child.id; + if (element == null) { + console.log('Malformed' +child.nodeName+ 'entry'); + this.statement = 'Malformed' +child.nodeName+ 'entry'; + this.type = 'statement'; + } else { + this.options = []; + while (element != null) { + if (element.nodeName == 'statement' && this.statement == undefined){ + this.statement = element.textContent; + } else if (element.nodeName == 'option') { + this.options.push(new this.childOption(element)); + } + element = element.nextElementSibling; + } + } + } + }; + + // On construction: + if (Collection.length != 0) { + Collection = Collection[0]; + if (Collection.childElementCount != 0) { + var child = Collection.firstElementChild; + this.options.push(new this.OptionNode(child)); + while (child.nextElementSibling != null) { + child = child.nextElementSibling; + this.options.push(new this.OptionNode(child)); + } + } + } + }; + + this.metricNode = function(name) { + this.enabled = name; + }; + + this.audioHolderNode = function(parent,xml) { + this.type = 'audioHolder'; + this.interfaceNode = function(DOM) { + var title = DOM.getElementsByTagName('title'); + if (title.length == 0) {this.title = null;} + else {this.title = title[0].textContent;} + + var scale = DOM.getElementsByTagName('scale'); + this.scale = []; + for (var i=0; i<scale.length; i++) { + var arr = [null, null]; + arr[0] = scale[i].getAttribute('position'); + arr[1] = scale[i].textContent; + this.scale.push(arr); + } + }; + + this.audioElementNode = function(parent,xml) { + this.url = xml.getAttribute('url'); + this.id = xml.id; + this.parent = parent; + }; + + this.commentQuestionNode = function(xml) { + this.id = xml.id; + if (xml.getAttribute('mandatory') == 'true') {this.mandatory = true;} + else {this.mandatory = false;} + this.question = xml.textContent; + }; + + this.id = xml.id; + this.hostURL = xml.getAttribute('hostURL'); + this.sampleRate = xml.getAttribute('sampleRate'); + if (xml.getAttribute('randomiseOrder') == "true") {this.randomiseOrder = true;} + else {this.randomiseOrder = false;} + this.repeatCount = xml.getAttribute('repeatCount'); + if (xml.getAttribute('loop') == 'true') {this.loop = true;} + else {this.loop == false;} + if (xml.getAttribute('elementComments') == "true") {this.elementComments = true;} + else {this.elementComments = false;} + + this.preTest = new parent.prepostNode('pretest',xml.getElementsByTagName('PreTest')); + this.postTest = new parent.prepostNode('posttest',xml.getElementsByTagName('PostTest')); + + this.interfaces = []; + var interfaceDOM = xml.getElementsByTagName('interface'); + for (var i=0; i<interfaceDOM.length; i++) { + this.interfaces.push(new this.interfaceNode(interfaceDOM[i])); + } + + this.commentBoxPrefix = xml.getElementsByTagName('commentBoxPrefix'); + if (this.commentBoxPrefix.length != 0) { + this.commentBoxPrefix = this.commentBoxPrefix[0].textContent; + } else { + this.commentBoxPrefix = "Comment on track"; + } + + this.audioElements =[]; + var audioElementsDOM = xml.getElementsByTagName('audioElements'); + for (var i=0; i<audioElementsDOM.length; i++) { + this.audioElements.push(new this.audioElementNode(this,audioElementsDOM[i])); + } + + this.commentQuestions = []; + var commentQuestionsDOM = xml.getElementsByTagName('CommentQuestion'); + for (var i=0; i<commentQuestionsDOM.length; i++) { + this.commentQuestions.push(new this.commentQuestionNode(commentQuestionsDOM[i])); + } + }; +} + +function Interface(specificationObject) { + // This handles the bindings between the interface and the audioEngineContext; + this.specification = specificationObject; + this.insertPoint = document.getElementById("topLevelBody"); + + // Bounded by interface!! + // Interface object MUST have an exportXMLDOM method which returns the various DOM levels + // For example, APE returns the slider position normalised in a <value> tag. + this.interfaceObjects = []; + this.interfaceObject = function(){}; + + this.commentBoxes = []; + this.commentBox = function(audioObject) { + var element = audioObject.specification; + this.audioObject = audioObject; + this.id = audioObject.id; + var audioHolderObject = audioObject.specification.parent; + // Create document objects to hold the comment boxes + this.trackComment = document.createElement('div'); + this.trackComment.className = 'comment-div'; + this.trackComment.id = 'comment-div-'+audioObject.id; + // Create a string next to each comment asking for a comment + this.trackString = document.createElement('span'); + this.trackString.innerHTML = audioHolderObject.commentBoxPrefix+' '+audioObject.id; + // Create the HTML5 comment box 'textarea' + this.trackCommentBox = document.createElement('textarea'); + this.trackCommentBox.rows = '4'; + this.trackCommentBox.cols = '100'; + this.trackCommentBox.name = 'trackComment'+audioObject.id; + this.trackCommentBox.className = 'trackComment'; + var br = document.createElement('br'); + // Add to the holder. + this.trackComment.appendChild(this.trackString); + this.trackComment.appendChild(br); + this.trackComment.appendChild(this.trackCommentBox); + + this.exportXMLDOM = function() { + var root = document.createElement('comment'); + if (this.audioObject.specification.parent.elementComments) { + var question = document.createElement('question'); + question.textContent = this.trackString.textContent; + var response = document.createElement('response'); + response.textContent = this.trackCommentBox.value; + root.appendChild(question); + root.appendChild(response); + } + return root; + }; + }; + + this.createCommentBox = function(audioObject) { + var node = new this.commentBox(audioObject); + this.commentBoxes.push(node); + audioObject.commentDOM = node; + return node; + }; + + this.sortCommentBoxes = function() { + var holder = []; + while (this.commentBoxes.length > 0) { + var node = this.commentBoxes.pop(0); + holder[node.id] = node; + } + this.commentBoxes = holder; + }; + + this.showCommentBoxes = function(inject, sort) { + if (sort) {interfaceContext.sortCommentBoxes();} + for (var i=0; i<interfaceContext.commentBoxes.length; i++) { + inject.appendChild(this.commentBoxes[i].trackComment); + } + }; +} +
--- a/example_eval/project.xml Thu Jun 04 10:06:42 2015 +0100 +++ b/example_eval/project.xml Fri Jun 05 12:54:52 2015 +0100 @@ -3,6 +3,21 @@ <setup interface="APE" projectReturn="/save" randomiseOrder='true' collectMetrics='true'> <PreTest> <question id="Location" mandatory="true">Please enter your location.</question> + <checkbox id="experience"> + <statement>Check options which are relevant to you</statement> + <option id="digital">Digital Consoles</option> + <option id="analog">Analog Consoles</option> + <option id="live">Live Mixing</option> + <option id="studio">Studio Mixing</option> + <option id="player">Play an instrument</option> + </checkbox> + <radio id="rating"> + <statement>Please rate this interface</statement> + <option name="bad">Bad</option> + <option name="OK">OK</option> + <option name="Good">Good</option> + <option name="Great">Great</option> + </radio> <statement>Please listen to all mixes</statement> </PreTest> <PostTest> @@ -32,19 +47,15 @@ <audioElements url="1.wav" id="1"/> <audioElements url="2.wav" id="2"/> <audioElements url="3.wav" id="3"/> - <audioElements url="4.wav" id="4"/> + <!--<audioElements url="4.wav" id="4"/> <audioElements url="5.wav" id="5"/> - <!--<audioElements url="6.wav" id="6"/> + <audioElements url="6.wav" id="6"/> <audioElements url="7.wav" id="7"/> <audioElements url="8.wav" id="8"/> <audioElements url="9.wav" id="9"/> <audioElements url="10.wav" id="10"/>--> <CommentQuestion id='mixingExperience'>What is your mixing experience</CommentQuestion> - <!-- - <PreTest> - <statement>Start the Test 3</statement> - </PreTest> - --> + <PreTest/> <PostTest> <question id="genre" mandatory="true">Please enter the genre</question> </PostTest>