nickjillings@1683: /**
nickjillings@1683: * ape.js
nickjillings@1683: * Create the APE interface
nickjillings@1683: */
nickjillings@1683:
nickjillings@1683: // Once this is loaded and parsed, begin execution
nickjillings@1683: loadInterface(projectXML);
nickjillings@1683:
nickjillings@1683: function loadInterface(xmlDoc) {
nickjillings@1683:
nickjillings@1697: // Get the dimensions of the screen available to the page
nickjillings@1683: var width = window.innerWidth;
nickjillings@1683: var height = window.innerHeight;
nickjillings@1683:
nickjillings@1683: // Set background to grey #ddd
nickjillings@1683: document.getElementsByTagName('body')[0].style.backgroundColor = '#ddd';
nickjillings@1683:
nickjillings@1683: // The injection point into the HTML page
nickjillings@1683: var insertPoint = document.getElementById("topLevelBody");
nickjillings@1702: var testContent = document.createElement('div');
nickjillings@1702: testContent.id = 'testContent';
nickjillings@1683:
nickjillings@1688:
nickjillings@1683: // Decode parts of the xmlDoc that are needed
nickjillings@1683: // xmlDoc MUST already be parsed by jQuery!
nickjillings@1683: var xmlSetup = xmlDoc.find('setup');
nickjillings@1683: // Should put in an error function here incase of malprocessed or malformed XML
nickjillings@1683:
nickjillings@1683: // Create the top div for the Title element
nickjillings@1683: var titleAttr = xmlSetup[0].attributes['title'];
nickjillings@1683: var title = document.createElement('div');
nickjillings@1683: title.className = "title";
nickjillings@1683: title.align = "center";
nickjillings@1683: var titleSpan = document.createElement('span');
nickjillings@1683:
nickjillings@1683: // Set title to that defined in XML, else set to default
nickjillings@1683: if (titleAttr != undefined) {
nickjillings@1683: titleSpan.innerText = titleAttr.value;
nickjillings@1683: } else {
nickjillings@1683: titleSpan.innerText = 'APE Tool';
nickjillings@1683: }
nickjillings@1683: // Insert the titleSpan element into the title div element.
nickjillings@1683: title.appendChild(titleSpan);
nickjillings@1683:
nickjillings@1688: // Store the return URL path in global projectReturn
nickjillings@1688: projectReturn = xmlSetup[0].attributes['projectReturn'].value;
nickjillings@1688:
nickjillings@1688: // Create Interface buttons!
nickjillings@1688: var interfaceButtons = document.createElement('div');
nickjillings@1688: interfaceButtons.id = 'interface-buttons';
nickjillings@1688:
nickjillings@1688: // MANUAL DOWNLOAD POINT
nickjillings@1688: // If project return is null, this MUST be specified as the location to create the download link
nickjillings@1688: var downloadPoint = document.createElement('div');
nickjillings@1688: downloadPoint.id = 'download-point';
nickjillings@1688:
nickjillings@1688: // Create playback start/stop points
nickjillings@1688: var playback = document.createElement("button");
nickjillings@1688: playback.innerText = 'Start';
nickjillings@1697: // onclick function. Check if it is playing or not, call the correct function in the
nickjillings@1697: // audioEngine, change the button text to reflect the next state.
nickjillings@1688: playback.onclick = function() {
nickjillings@1688: if (audioEngineContext.status == 0) {
nickjillings@1688: audioEngineContext.play();
nickjillings@1688: this.innerText = 'Stop';
nickjillings@1688: } else {
nickjillings@1688: audioEngineContext.stop();
nickjillings@1688: this.innerText = 'Start';
nickjillings@1688: }
nickjillings@1697: };
nickjillings@1688: // Create Submit (save) button
nickjillings@1688: var submit = document.createElement("button");
nickjillings@1688: submit.innerText = 'Submit';
nickjillings@1688: submit.onclick = function() {
nickjillings@1696: // TODO: Update this for postTest tags
nickjillings@1688: createProjectSave(projectReturn)
nickjillings@1697: };
nickjillings@1697: // Append the interface buttons into the interfaceButtons object.
nickjillings@1688: interfaceButtons.appendChild(playback);
nickjillings@1688: interfaceButtons.appendChild(submit);
nickjillings@1688: interfaceButtons.appendChild(downloadPoint);
nickjillings@1688:
nickjillings@1683: // Now create the slider and HTML5 canvas boxes
nickjillings@1683:
nickjillings@1697: // Create the div box to center align
nickjillings@1683: var sliderBox = document.createElement('div');
nickjillings@1683: sliderBox.className = 'sliderCanvasDiv';
nickjillings@1697: sliderBox.id = 'sliderCanvasHolder';
nickjillings@1683: sliderBox.align = 'center';
nickjillings@1683:
nickjillings@1697: // Create the slider box to hold the slider elements
nickjillings@1686: var canvas = document.createElement('div');
nickjillings@1683: canvas.id = 'slider';
nickjillings@1697: // Must have a known EXACT width, as this is used later to determine the ratings
nickjillings@1686: canvas.style.width = width - 100 +"px";
nickjillings@1686: canvas.style.height = 150 + "px";
nickjillings@1697: canvas.style.marginBottom = "25px";
nickjillings@1683: canvas.style.backgroundColor = '#eee';
nickjillings@1686: canvas.align = "left";
nickjillings@1683: sliderBox.appendChild(canvas);
nickjillings@1697:
nickjillings@1697: // Global parent for the comment boxes on the page
nickjillings@1684: var feedbackHolder = document.createElement('div');
nickjillings@1697: // Find the parent audioHolder object.
nickjillings@1697: var audioHolder = xmlDoc.find('audioHolder');
nickjillings@1697: audioHolder = audioHolder[0]; // Remove from one field array
nickjillings@1697: // Extract the hostURL attribute. If not set, create an empty string.
nickjillings@1697: var hostURL = audioHolder.attributes['hostURL'];
nickjillings@1688: if (hostURL == undefined) {
nickjillings@1688: hostURL = "";
nickjillings@1688: } else {
nickjillings@1688: hostURL = hostURL.value;
nickjillings@1688: }
nickjillings@1697: // Extract the sampleRate. If set, convert the string to a Number.
nickjillings@1697: var hostFs = audioHolder.attributes['sampleRate'];
nickjillings@1696: if (hostFs != undefined) {
nickjillings@1696: hostFs = Number(hostFs.value);
nickjillings@1688: }
nickjillings@1688:
nickjillings@1688: /// CHECK FOR SAMPLE RATE COMPATIBILITY
nickjillings@1696: if (hostFs != undefined) {
nickjillings@1688: if (Number(hostFs) != audioContext.sampleRate) {
nickjillings@1688: var errStr = 'Sample rates do not match! Requested '+Number(hostFs)+', got '+audioContext.sampleRate+'. Please set the sample rate to match before completing this test.';
nickjillings@1688: alert(errStr);
nickjillings@1688: return;
nickjillings@1688: }
nickjillings@1688: }
nickjillings@1697: // Find all the audioElements from the audioHolder
nickjillings@1697: var audioElements = $(audioHolder).find('audioElements');
nickjillings@1697: audioElements.each(function(index,element){
nickjillings@1688: // Find URL of track
nickjillings@1697: // In this jQuery loop, variable 'this' holds the current audioElement.
nickjillings@1697:
nickjillings@1697: // Now load each audio sample. First create the new track by passing the full URL
nickjillings@1688: var trackURL = hostURL + this.attributes['url'].value;
nickjillings@1688: audioEngineContext.newTrack(trackURL);
nickjillings@1697: // Create document objects to hold the comment boxes
nickjillings@1697: var trackComment = document.createElement('div');
nickjillings@1697: // Create a string next to each comment asking for a comment
nickjillings@1697: var trackString = document.createElement('span');
nickjillings@1697: trackString.innerText = 'Comment on track '+index;
nickjillings@1697: // Create the HTML5 comment box 'textarea'
nickjillings@1697: var trackCommentBox = document.createElement('textarea');
nickjillings@1697: trackCommentBox.rows = '4';
nickjillings@1697: trackCommentBox.cols = '100';
nickjillings@1697: trackCommentBox.name = 'trackComment'+index;
nickjillings@1697: trackCommentBox.className = 'trackComment';
nickjillings@1697: // Add to the holder.
nickjillings@1697: trackComment.appendChild(trackString);
nickjillings@1697: trackComment.appendChild(trackCommentBox);
nickjillings@1697: feedbackHolder.appendChild(trackComment);
nickjillings@1697:
nickjillings@1686: // Create a slider per track
nickjillings@1686:
nickjillings@1686: var trackSliderObj = document.createElement('div');
nickjillings@1686: trackSliderObj.className = 'track-slider';
nickjillings@1686: trackSliderObj.id = 'track-slider-'+index;
nickjillings@1686: trackSliderObj.style.position = 'absolute';
nickjillings@1686: // Distribute it randomnly
nickjillings@1686: var w = window.innerWidth - 100;
nickjillings@1686: w = Math.random()*w;
nickjillings@1686: trackSliderObj.style.left = Math.floor(w)+50+'px';
nickjillings@1686: trackSliderObj.style.height = "150px";
nickjillings@1686: trackSliderObj.style.width = "10px";
nickjillings@1686: trackSliderObj.style.backgroundColor = 'rgb(100,200,100)';
nickjillings@1686: trackSliderObj.innerHTML = ''+index+'';
nickjillings@1686: trackSliderObj.style.float = "left";
nickjillings@1686: trackSliderObj.draggable = true;
nickjillings@1686: trackSliderObj.ondragend = dragEnd;
nickjillings@1689:
nickjillings@1689: // Onclick, switch playback to that track
nickjillings@1689: trackSliderObj.onclick = function() {
nickjillings@1689: // Get the track ID from the object ID
nickjillings@1689: var id = Number(this.id.substr(13,2)); // Maximum theoretical tracks is 99!
nickjillings@1689: audioEngineContext.selectedTrack(id);
nickjillings@1696: };
nickjillings@1689:
nickjillings@1686: canvas.appendChild(trackSliderObj);
nickjillings@1696: });
nickjillings@1684:
nickjillings@1683:
nickjillings@1702: // Create pre and post test questions
nickjillings@1702:
nickjillings@1683: // Inject into HTML
nickjillings@1683: insertPoint.innerHTML = null; // Clear the current schema
nickjillings@1702: testContent.appendChild(title); // Insert the title
nickjillings@1702: testContent.appendChild(interfaceButtons);
nickjillings@1702: testContent.appendChild(sliderBox);
nickjillings@1702: testContent.appendChild(feedbackHolder);
nickjillings@1702: insertPoint.appendChild(testContent);
nickjillings@1702:
nickjillings@1702: var preTest = xmlDoc.find('PreTest');
nickjillings@1702: var postTest = xmlDoc.find('PostTest');
nickjillings@1702: preTest = preTest[0];
nickjillings@1702: postTest = postTest[0];
nickjillings@1702: if (preTest != undefined || postTest != undefined)
nickjillings@1702: {
nickjillings@1702: testContent.style.zIndex = 1;
nickjillings@1702: var blank = document.createElement('div');
nickjillings@1702: blank.id = 'testHalt';
nickjillings@1702: blank.style.zIndex = 2;
nickjillings@1702: blank.style.width = window.innerWidth + 'px';
nickjillings@1702: blank.style.height = window.innerHeight + 'px';
nickjillings@1702: blank.style.position = 'absolute';
nickjillings@1702: blank.style.top = '0';
nickjillings@1702: blank.style.left = '0';
nickjillings@1702: insertPoint.appendChild(blank);
nickjillings@1702: }
nickjillings@1702:
nickjillings@1702: // Create Pre-Test Box
nickjillings@1702: if (preTest != undefined && preTest.children.length >= 1)
nickjillings@1702: {
nickjillings@1702:
nickjillings@1702: var preTestHolder = document.createElement('div');
nickjillings@1702: preTestHolder.id = 'preTestHolder';
nickjillings@1702: preTestHolder.style.zIndex = 2;
nickjillings@1702: preTestHolder.style.width = '500px';
nickjillings@1702: preTestHolder.style.height = '250px';
nickjillings@1702: preTestHolder.style.backgroundColor = '#fff';
nickjillings@1702: preTestHolder.style.position = 'absolute';
nickjillings@1702: preTestHolder.style.left = (window.innerWidth/2)-250 + 'px';
nickjillings@1702: preTestHolder.style.top = (window.innerHeight/2)-125 + 'px';
nickjillings@1702: // Parse the first box
nickjillings@1702: var preTestOption = document.createElement('div');
nickjillings@1702: preTestOption.id = 'preTest';
nickjillings@1702: preTestOption.style.marginTop = '25px';
nickjillings@1702: preTestOption.align = "center";
nickjillings@1702: var child = preTest.children[0];
nickjillings@1702: if (child.nodeName == 'statement')
nickjillings@1702: {
nickjillings@1702: preTestOption.innerHTML = ''+child.innerHTML+'';
nickjillings@1702: } else if (child.nodeName == 'question')
nickjillings@1702: {
nickjillings@1703: var questionId = child.attributes['id'].value;
nickjillings@1702: var textHold = document.createElement('span');
nickjillings@1702: textHold.innerHTML = child.innerHTML;
nickjillings@1703: textHold.id = questionId + 'response';
nickjillings@1702: var textEnter = document.createElement('textarea');
nickjillings@1702: preTestOption.appendChild(textHold);
nickjillings@1702: preTestOption.appendChild(textEnter);
nickjillings@1702: }
nickjillings@1702: var nextButton = document.createElement('button');
nickjillings@1702: nextButton.id = 'preTestNext';
nickjillings@1702: nextButton.value = '1';
nickjillings@1702: nextButton.innerHTML = 'next';
nickjillings@1702: nextButton.style.position = 'relative';
nickjillings@1702: nextButton.style.left = '450px';
nickjillings@1702: nextButton.style.top = '175px';
nickjillings@1702: nextButton.onclick = function() {
nickjillings@1702: // Need to find and parse preTest again!
nickjillings@1702: var preTest = projectXML.find('PreTest')[0];
nickjillings@1703: // Check if current state is a question!
nickjillings@1703: if (preTest.children[this.value-1].nodeName == 'question') {
nickjillings@1703: var questionId = preTest.children[this.value-1].attributes['id'].value;
nickjillings@1703: var questionHold = document.createElement('comment');
nickjillings@1703: var questionResponse = document.getElementById(questionId + 'response');
nickjillings@1703: questionHold.id = questionId;
nickjillings@1703: questionHold.innerHTML = questionResponse.value;
nickjillings@1703: preTestQuestions.appendChild(questionHold);
nickjillings@1703: }
nickjillings@1702: if (this.value < preTest.children.length)
nickjillings@1702: {
nickjillings@1702: // More to process
nickjillings@1702: var child = preTest.children[this.value];
nickjillings@1702: if (child.nodeName == 'statement')
nickjillings@1702: {
nickjillings@1702: preTestOption.innerHTML = ''+child.innerHTML+'';
nickjillings@1702: } else if (child.nodeName == 'question')
nickjillings@1702: {
nickjillings@1702: var textHold = document.createElement('span');
nickjillings@1702: textHold.innerHTML = child.innerHTML;
nickjillings@1702: var textEnter = document.createElement('textarea');
nickjillings@1703: textEnter.id = child.attributes['id'].value + 'response';
nickjillings@1703: preTestOption.innerHTML = null;
nickjillings@1702: preTestOption.appendChild(textHold);
nickjillings@1702: preTestOption.appendChild(textEnter);
nickjillings@1702: }
nickjillings@1702: } else {
nickjillings@1702: // Time to clear
nickjillings@1702: preTestHolder.style.zIndex = -1;
nickjillings@1702: preTestHolder.style.visibility = 'hidden';
nickjillings@1702: var blank = document.getElementById('testHalt');
nickjillings@1702: blank.style.zIndex = -2;
nickjillings@1702: blank.style.visibility = 'hidden';
nickjillings@1702: }
nickjillings@1702: this.value++;
nickjillings@1702: };
nickjillings@1702:
nickjillings@1702: preTestHolder.appendChild(preTestOption);
nickjillings@1702: preTestHolder.appendChild(nextButton);
nickjillings@1702: insertPoint.appendChild(preTestHolder);
nickjillings@1702: }
nickjillings@1702:
nickjillings@1683: }
nickjillings@1686:
nickjillings@1686: function dragEnd(ev) {
nickjillings@1686: // Function call when a div has been dropped
nickjillings@1686: if (ev.x >= 50 && ev.x < window.innerWidth-50) {
nickjillings@1686: this.style.left = (ev.x)+'px';
nickjillings@1686: } else {
nickjillings@1686: if (ev.x<50) {
nickjillings@1686: this.style.left = '50px';
nickjillings@1686: } else {
nickjillings@1686: this.style.left = window.innerWidth-50 + 'px';
nickjillings@1686: }
nickjillings@1686: }
nickjillings@1686: }
nickjillings@1688:
nickjillings@1688: // Only other global function which must be defined in the interface class. Determines how to create the XML document.
nickjillings@1688: function interfaceXMLSave(){
nickjillings@1688: // Create the XML string to be exported with results
nickjillings@1688: var xmlDoc = document.createElement("BrowserEvaluationResult");
nickjillings@1688: var trackSliderObjects = document.getElementsByClassName('track-slider');
nickjillings@1688: var commentObjects = document.getElementsByClassName('trackComment');
nickjillings@1688: var rateMin = 50;
nickjillings@1688: var rateMax = window.innerWidth-50;
nickjillings@1688: for (var i=0; i