Mercurial > hg > webaudioevaluationtool
view ape.js @ 950:05e7edb032b9
Fix Bug #1241 and #1213: Added checks each time new test page is loaded that all audioObjects have decoded. Writes to browser console WAIT and does not issue any play command if any audioObjects not ready.
author | Nicholas Jillings <n.g.r.jillings@se14.qmul.ac.uk> |
---|---|
date | Mon, 25 May 2015 11:14:12 +0100 |
parents | 48e05b7a16e0 |
children | 55bf2500f278 |
line wrap: on
line source
/** * ape.js * Create the APE interface */ var currentState; // Keep track of the current state (pre/post test, which test, final test? first test?) // preTest - In preTest state // testRun-ID - In test running, test Id number at the end 'testRun-2' // testRunPost-ID - Post test of test ID // testRunPre-ID - Pre-test of test ID // postTest - End of test, final submission! // Once this is loaded and parsed, begin execution loadInterface(projectXML); function loadInterface(xmlDoc) { // 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"); 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 // Extract the different test XML DOM trees var audioHolders = xmlDoc.find('audioHolder'); audioHolders.each(function(index,element) { var repeatN = element.attributes['repeatCount'].value; for (var r=0; r<=repeatN; r++) { testXMLSetups[testXMLSetups.length] = 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); } // 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 'elementInitalPosition': 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() { var sliders = document.getElementsByClassName('track-slider'); for (var i=0; i<sliders.length; i++) { audioEngineContext.audioObjects[i].metric.initialised(convSliderPosToRate(i)); } }; 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); if (audioEngineContext.timer.testStarted) { audioEngineContext.audioObjects[id].metric.moved(time,position); } }; audioEngineContext.metric.sliderPlayed = function(id) { var time = audioEngineContext.timer.getTestTime(); if (audioEngineContext.timer.testStarted) { if (this.lastClicked >= 0) { audioEngineContext.audioObjects[this.lastClicked].metric.listening(time); } this.lastClicked = id; audioEngineContext.audioObjects[id].metric.listening(time); } }; // Create the top div for the Title element var titleAttr = xmlSetup[0].attributes['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.innerHTML = titleAttr.value; } else { titleSpan.innerHTML = '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); // Store the return URL path in global projectReturn projectReturn = xmlSetup[0].attributes['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'; 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'; } }; // 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); interfaceButtons.appendChild(downloadPoint); // Now create the slider and HTML5 canvas boxes // Create the div box to center align var sliderBox = document.createElement('div'); sliderBox.className = 'sliderCanvasDiv'; sliderBox.id = 'sliderCanvasHolder'; sliderBox.align = 'center'; // Create the slider box to hold the slider elements var canvas = document.createElement('div'); canvas.id = 'slider'; // Must have a known EXACT width, as this is used later to determine the ratings canvas.style.width = width - 100 +"px"; canvas.align = "left"; sliderBox.appendChild(canvas); // Create the div to hold any scale objects var scale = document.createElement('div'); scale.className = 'sliderScale'; scale.id = 'sliderScaleHolder'; scale.align = 'left'; sliderBox.appendChild(scale); // Global parent for the comment boxes on the page var feedbackHolder = document.createElement('div'); feedbackHolder.id = 'feedbackHolder'; testContent.style.zIndex = 1; insertPoint.innerHTML = null; // Clear the current schema // Create pre and post test questions var blank = document.createElement('div'); blank.className = 'testHalt'; var popupHolder = document.createElement('div'); popupHolder.id = 'popupHolder'; popupHolder.className = 'popupHolder'; popupHolder.style.position = 'absolute'; popupHolder.style.left = (window.innerWidth/2)-250 + 'px'; popupHolder.style.top = (window.innerHeight/2)-125 + 'px'; insertPoint.appendChild(popupHolder); insertPoint.appendChild(blank); hidePopup(); var preTest = xmlSetup.find('PreTest'); var postTest = xmlSetup.find('PostTest'); preTest = preTest[0]; postTest = postTest[0]; currentState = 'preTest'; // Create Pre-Test Box if (preTest != undefined && preTest.childElementCount >= 1) { showPopup(); preTestPopupStart(preTest); } // Inject into HTML testContent.appendChild(title); // Insert the title testContent.appendChild(pagetitle); testContent.appendChild(interfaceButtons); testContent.appendChild(sliderBox); testContent.appendChild(feedbackHolder); insertPoint.appendChild(testContent); // Load the full interface } function loadTest(id) { // Reset audioEngineContext.Metric globals for new test audioEngineContext.newTestPage(); // Used to load a specific test page var textXML = testXMLSetups[id]; var feedbackHolder = document.getElementById('feedbackHolder'); var canvas = document.getElementById('slider'); feedbackHolder.innerHTML = null; 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 = 50-8; // Half the offset of the slider (window width -100) minus the body padding of 8 // TODO: AUTOMATE ABOVE!! var scale = document.getElementById('sliderScaleHolder'); scale.innerHTML = null; interfaceObj.find('scale').each(function(index,scaleObj){ var position = Number(scaleObj.attributes['position'].value)*0.01; 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'; }); // 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.'; alert(errStr); return; } } var commentShow = textXML.attributes['elementComments']; if (commentShow != undefined) { if (commentShow.value == 'false') {commentShow = false;} else {commentShow = true;} } else {commentShow = true;} var loopPlayback = textXML.attributes['loop']; if (loopPlayback != undefined) { loopPlayback = loopPlayback.value; if (loopPlayback == 'true') { loopPlayback = true; } else { loopPlayback = false; } } else { loopPlayback = false; } audioEngineContext.loopPlayback = loopPlayback; loopPlayback = false; // Create AudioEngine bindings for playback if (loopPlayback) { audioEngineContext.selectedTrack = function(id) { for (var i=0; i<this.audioObjects.length; i++) { if (id == i) { this.audioObjects[i].outputGain.gain.value = 1.0; } else { this.audioObjects[i].outputGain.gain.value = 0.0; } } }; } else { audioEngineContext.selectedTrack = function(id) { for (var i=0; i<this.audioObjects.length; i++) { this.audioObjects[i].outputGain.gain.value = 0.0; this.audioObjects[i].stop(); } if (this.status == 1) { this.audioObjects[id].outputGain.gain.value = 1.0; this.audioObjects[id].play(audioContext.currentTime+0.01); } }; } currentTestHolder = document.createElement('audioHolder'); currentTestHolder.id = textXML.id; currentTestHolder.repeatCount = textXML.attributes['repeatCount'].value; var currentPreTestHolder = document.createElement('preTest'); var currentPostTestHolder = document.createElement('postTest'); currentTestHolder.appendChild(currentPreTestHolder); currentTestHolder.appendChild(currentPostTestHolder); var randomise = textXML.attributes['randomiseOrder']; if (randomise != undefined) {randomise = randomise.value;} else {randomise = false;} var audioElements = $(textXML).find('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); } // Delete any previous audioObjects associated with the audioEngine audioEngineContext.audioObjects = []; // Find all the audioElements from the audioHolder $(currentTrackOrder).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); if (commentShow) { // Create document objects to hold the comment boxes var trackComment = document.createElement('div'); trackComment.className = 'comment-div'; // Create a string next to each comment asking for a comment var trackString = document.createElement('span'); trackString.innerHTML = 'Comment on track '+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); } // Create a slider per track var trackSliderObj = document.createElement('div'); trackSliderObj.className = 'track-slider'; trackSliderObj.id = 'track-slider-'+index; // Distribute it randomnly var w = window.innerWidth - 100; w = Math.random()*w; trackSliderObj.style.left = Math.floor(w)+50+'px'; trackSliderObj.innerHTML = '<span>'+index+'</span>'; trackSliderObj.draggable = true; trackSliderObj.ondragend = dragEnd; trackSliderObj.ondragstart = function() { var id = Number(this.id.substr(13,2)); // Maximum theoretical tracks is 99! audioEngineContext.metric.sliderMoveStart(id); }; // Onclick, switch playback to that track trackSliderObj.onclick = function() { // Start the test on first click, that way timings are more accurate. audioEngineContext.play(); // Get the track ID from the object ID var id = Number(this.id.substr(13,2)); // Maximum theoretical tracks is 99! //audioEngineContext.metric.sliderPlayed(id); audioEngineContext.selectedTrack(id); // Currently playing track red, rest green document.getElementById('track-slider-'+index).style.backgroundColor = "#FF0000"; for (var i = 0; i<$(currentTrackOrder).length; i++) { if (i!=index) // Make all other sliders green { document.getElementById('track-slider-'+i).style.backgroundColor = "rgb(100,200,100)"; } } }; canvas.appendChild(trackSliderObj); }); // Append any commentQuestion boxes var commentQuestions = $(textXML).find('CommentQuestion'); $(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; // Create a string next to each comment asking for a comment var trackString = document.createElement('span'); trackString.innerHTML = element.textContent; // Create the HTML5 comment box 'textarea' var trackCommentBox = document.createElement('textarea'); trackCommentBox.rows = '4'; trackCommentBox.cols = '100'; trackCommentBox.name = 'commentQuestion'+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); }); // Now process any pre-test commands var preTest = $(testXMLSetups[id]).find('PreTest')[0]; if (preTest.childElementCount > 0) { currentState = 'testRunPre-'+id; preTestPopupStart(preTest); showPopup(); } else { currentState = 'testRun-'+id; } } function preTestPopupStart(preTest) { var popupHolder = document.getElementById('popupHolder'); popupHolder.innerHTML = null; // Parse the first box var preTestOption = document.createElement('div'); preTestOption.id = 'preTest'; preTestOption.style.marginTop = '25px'; preTestOption.align = "center"; var child = $(preTest).children()[0]; if (child.nodeName == 'statement') { preTestOption.innerHTML = '<span>'+child.textContent+'</span>'; } else if (child.nodeName == 'question') { var textHold = document.createElement('span'); textHold.innerHTML = child.textContent; var textEnter = document.createElement('textarea'); textEnter.id = child.attributes['id'].value + 'response'; var br = document.createElement('br'); preTestOption.innerHTML = null; preTestOption.appendChild(textHold); preTestOption.appendChild(br); preTestOption.appendChild(textEnter); } var nextButton = document.createElement('button'); nextButton.className = 'popupButton'; nextButton.value = '0'; nextButton.innerHTML = 'Next'; nextButton.onclick = popupButtonClick; popupHolder.appendChild(preTestOption); popupHolder.appendChild(nextButton); } function popupButtonClick() { // Global call from the 'Next' button click if (currentState == 'preTest') { // At the start of the preTest routine! var xmlTree = projectXML.find('setup'); var preTest = xmlTree.find('PreTest')[0]; this.value = preTestButtonClick(preTest,this.value); } else if (currentState.substr(0,10) == 'testRunPre') { //Specific test pre-test var testId = currentState.substr(11,currentState.length-10); var preTest = $(testXMLSetups[testId]).find('PreTest')[0]; this.value = preTestButtonClick(preTest,this.value); } else if (currentState.substr(0,11) == 'testRunPost') { // Specific test post-test var testId = currentState.substr(12,currentState.length-11); var preTest = $(testXMLSetups[testId]).find('PostTest')[0]; this.value = preTestButtonClick(preTest,this.value); } else if (currentState == 'postTest') { // At the end of the test, running global post test var xmlTree = projectXML.find('setup'); var PostTest = xmlTree.find('PostTest')[0]; this.value = preTestButtonClick(PostTest,this.value); } } function preTestButtonClick(preTest,index) { // Called on click of pre-test button // Need to find and parse preTest again! var preTestOption = document.getElementById('preTest'); // Check if current state is a question! if ($(preTest).children()[index].nodeName == 'question') { var questionId = $(preTest).children()[index].attributes['id'].value; var questionHold = document.createElement('comment'); var questionResponse = document.getElementById(questionId + 'response'); var mandatory = $(preTest).children()[index].attributes['mandatory']; if (mandatory != undefined){ if (mandatory.value == 'true') {mandatory = true;} else {mandatory = false;} } else {mandatory = false;} if (mandatory == true && questionResponse.value.length == 0) { return index; } questionHold.id = questionId; questionHold.innerHTML = questionResponse.value; postPopupResponse(questionHold); } index++; if (index < preTest.childElementCount) { // More to process var child = $(preTest).children()[index]; if (child.nodeName == 'statement') { preTestOption.innerHTML = '<span>'+child.textContent+'</span>'; } else if (child.nodeName == 'question') { var textHold = document.createElement('span'); textHold.innerHTML = child.textContent; var textEnter = document.createElement('textarea'); textEnter.id = child.attributes['id'].value + 'response'; var br = document.createElement('br'); preTestOption.innerHTML = null; preTestOption.appendChild(textHold); preTestOption.appendChild(br); preTestOption.appendChild(textEnter); } } else { // Time to clear preTestOption.innerHTML = null; if (currentState != 'postTest') { hidePopup(); // Progress the state! advanceState(); } else { a = createProjectSave(projectReturn); preTestOption.appendChild(a); } } return index; } function postPopupResponse(response) { if (currentState == 'preTest') { preTestQuestions.appendChild(response); } else if (currentState == 'postTest') { postTestQuestions.appendChild(response); } else { // Inside a specific test if (currentState.substr(0,10) == 'testRunPre') { // Pre Test var store = $(currentTestHolder).find('preTest'); } else { // Post Test var store = $(currentTestHolder).find('postTest'); } store[0].appendChild(response); } } function showPopup() { var popupHolder = document.getElementById('popupHolder'); popupHolder.style.zIndex = 3; popupHolder.style.visibility = 'visible'; var blank = document.getElementsByClassName('testHalt')[0]; blank.style.zIndex = 2; blank.style.visibility = 'visible'; } function hidePopup() { var popupHolder = document.getElementById('popupHolder'); popupHolder.style.zIndex = -1; popupHolder.style.visibility = 'hidden'; var blank = document.getElementsByClassName('testHalt')[0]; blank.style.zIndex = -2; blank.style.visibility = 'hidden'; } function dragEnd(ev) { // Function call when a div has been dropped var slider = document.getElementById('slider'); var w = slider.style.width; w = Number(w.substr(0,w.length-2)); var x = ev.x; if (x >= 42 && x < w+42) { this.style.left = (x)+'px'; } else { if (x<42) { this.style.left = '42px'; } else { this.style.left = (w+42) + 'px'; } } audioEngineContext.metric.sliderMoved(); } function advanceState() { console.log(currentState); if (currentState == 'preTest') { // End of pre-test, begin the test loadTest(0); } else if (currentState.substr(0,10) == 'testRunPre') { // Start the test var testId = currentState.substr(11,currentState.length-10); currentState = 'testRun-'+testId; //audioEngineContext.timer.startTest(); //audioEngineContext.play(); } else if (currentState.substr(0,11) == 'testRunPost') { var testId = currentState.substr(12,currentState.length-11); testEnded(testId); } else if (currentState.substr(0,7) == 'testRun') { var testId = currentState.substr(8,currentState.length-7); // Check if we have any post tests to perform var postXML = $(testXMLSetups[testId]).find('PostTest')[0]; if (postXML == undefined || postXML.childElementCount == 0) { testEnded(testId); } else if (postXML.childElementCount > 0) { currentState = 'testRunPost-'+testId; showPopup(); preTestPopupStart(postXML); } else { // No post tests, check if we have another test to perform instead } } console.log(currentState); } function testEnded(testId) { pageXMLSave(testId); if (testXMLSetups.length-1 > testId) { // Yes we have another test to perform testId = (Number(testId)+1); currentState = 'testRun-'+testId; loadTest(testId); } else { console.log('Testing Completed!'); currentState = 'postTest'; // Check for any post tests var xmlSetup = projectXML.find('setup'); var postTest = xmlSetup.find('PostTest')[0]; showPopup(); preTestPopupStart(postTest); } } function buttonSubmitClick() // TODO: Only when all songs have been played! { hasBeenPlayed = audioEngineContext.checkAllPlayed(); if (hasBeenPlayed.length == 0) { 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; } } if (currentState.substr(0,7) == 'testRun') { hasBeenPlayed = []; // clear array to prepare for next test audioEngineContext.timer.stopTest(); advanceState(); } } else // 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; } } function convSliderPosToRate(id) { var w = document.getElementById('slider').style.width; 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-42)/maxPix; return rate; } function pageXMLSave(testId) { // Saves a specific test page var xmlDoc = currentTestHolder; // Check if any session wide metrics are enabled var commentShow = testXMLSetups[testId].attributes['elementComments']; if (commentShow != undefined) { if (commentShow.value == 'false') {commentShow = false;} else {commentShow = true;} } else {commentShow = true;} var metric = document.createElement('metric'); if (audioEngineContext.metric.enableTestTimer) { var testTime = document.createElement('metricResult'); testTime.id = 'testTime'; testTime.textContent = audioEngineContext.timer.testDuration; 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 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; 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.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); xmlDoc.appendChild(audioElement); } var commentQuestion = document.getElementsByClassName('commentQuestion'); for (var i=0; i<commentQuestion.length; i++) { var cqHolder = document.createElement('CommentQuestion'); var comment = document.createElement('comment'); var question = document.createElement('question'); cqHolder.id = commentQuestion[i].id; comment.textContent = commentQuestion[i].children[2].value; question.textContent = commentQuestion[i].children[0].textContent; cqHolder.appendChild(question); cqHolder.appendChild(comment); xmlDoc.appendChild(cqHolder); } testResultsHolders[testId] = xmlDoc; } // Only other global function which must be defined in the interface class. Determines how to create the XML document. function interfaceXMLSave(){ // Create the XML string to be exported with results var xmlDoc = document.createElement("BrowserEvaluationResult"); for (var i=0; i<testResultsHolders.length; i++) { xmlDoc.appendChild(testResultsHolders[i]); } // Append Pre/Post Questions xmlDoc.appendChild(preTestQuestions); xmlDoc.appendChild(postTestQuestions); return xmlDoc; }