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@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@1683: // Inject into HTML
nickjillings@1683: insertPoint.innerHTML = null; // Clear the current schema
nickjillings@1683: insertPoint.appendChild(title); // Insert the title
nickjillings@1688: insertPoint.appendChild(interfaceButtons);
nickjillings@1683: insertPoint.appendChild(sliderBox);
nickjillings@1684: insertPoint.appendChild(feedbackHolder);
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