annotate mushra.js @ 1450:bc074d4ee760

Resolved #1394: Python returns same XML responses as the PHP server
author Nicholas Jillings <nickjillings@users.noreply.github.com>
date Fri, 27 Nov 2015 12:09:49 +0000
parents
children 81246d594793
rev   line source
nickjillings@1450 1 /**
nickjillings@1450 2 * mushra.js
nickjillings@1450 3 * Create the MUSHRA interface
nickjillings@1450 4 */
nickjillings@1450 5
nickjillings@1450 6 // Once this is loaded and parsed, begin execution
nickjillings@1450 7 loadInterface();
nickjillings@1450 8
nickjillings@1450 9 function loadInterface() {
nickjillings@1450 10 // Get the dimensions of the screen available to the page
nickjillings@1450 11 var width = window.innerWidth;
nickjillings@1450 12 var height = window.innerHeight;
nickjillings@1450 13
nickjillings@1450 14 // The injection point into the HTML page
nickjillings@1450 15 interfaceContext.insertPoint = document.getElementById("topLevelBody");
nickjillings@1450 16 var testContent = document.createElement('div');
nickjillings@1450 17 testContent.id = 'testContent';
nickjillings@1450 18
nickjillings@1450 19 // Create the top div for the Title element
nickjillings@1450 20 var titleAttr = specification.title;
nickjillings@1450 21 var title = document.createElement('div');
nickjillings@1450 22 title.className = "title";
nickjillings@1450 23 title.align = "center";
nickjillings@1450 24 var titleSpan = document.createElement('span');
nickjillings@1450 25
nickjillings@1450 26 // Set title to that defined in XML, else set to default
nickjillings@1450 27 if (titleAttr != undefined) {
nickjillings@1450 28 titleSpan.textContent = titleAttr;
nickjillings@1450 29 } else {
nickjillings@1450 30 titleSpan.textContent = 'Listening test';
nickjillings@1450 31 }
nickjillings@1450 32 // Insert the titleSpan element into the title div element.
nickjillings@1450 33 title.appendChild(titleSpan);
nickjillings@1450 34
nickjillings@1450 35 var pagetitle = document.createElement('div');
nickjillings@1450 36 pagetitle.className = "pageTitle";
nickjillings@1450 37 pagetitle.align = "center";
nickjillings@1450 38 var titleSpan = document.createElement('span');
nickjillings@1450 39 titleSpan.id = "pageTitle";
nickjillings@1450 40 pagetitle.appendChild(titleSpan);
nickjillings@1450 41
nickjillings@1450 42 // Create Interface buttons!
nickjillings@1450 43 var interfaceButtons = document.createElement('div');
nickjillings@1450 44 interfaceButtons.id = 'interface-buttons';
nickjillings@1450 45
nickjillings@1450 46 // Create playback start/stop points
nickjillings@1450 47 var playback = document.createElement("button");
nickjillings@1450 48 playback.innerHTML = 'Stop';
nickjillings@1450 49 playback.id = 'playback-button';
nickjillings@1450 50 // onclick function. Check if it is playing or not, call the correct function in the
nickjillings@1450 51 // audioEngine, change the button text to reflect the next state.
nickjillings@1450 52 playback.onclick = function() {
nickjillings@1450 53 if (audioEngineContext.status == 1) {
nickjillings@1450 54 audioEngineContext.stop();
nickjillings@1450 55 this.innerHTML = 'Stop';
nickjillings@1450 56 var time = audioEngineContext.timer.getTestTime();
nickjillings@1450 57 console.log('Stopped at ' + time); // DEBUG/SAFETY
nickjillings@1450 58 }
nickjillings@1450 59 };
nickjillings@1450 60 // Create Submit (save) button
nickjillings@1450 61 var submit = document.createElement("button");
nickjillings@1450 62 submit.innerHTML = 'Submit';
nickjillings@1450 63 submit.onclick = buttonSubmitClick;
nickjillings@1450 64 submit.id = 'submit-button';
nickjillings@1450 65 // Append the interface buttons into the interfaceButtons object.
nickjillings@1450 66 interfaceButtons.appendChild(playback);
nickjillings@1450 67 interfaceButtons.appendChild(submit);
nickjillings@1450 68
nickjillings@1450 69 // Create a slider box
nickjillings@1450 70 var sliderBox = document.createElement('div');
nickjillings@1450 71 sliderBox.style.width = "100%";
nickjillings@1450 72 sliderBox.style.height = window.innerHeight - 180 + 'px';
nickjillings@1450 73 sliderBox.id = 'slider';
nickjillings@1450 74 sliderBox.align = "center";
nickjillings@1450 75
nickjillings@1450 76 // Global parent for the comment boxes on the page
nickjillings@1450 77 var feedbackHolder = document.createElement('div');
nickjillings@1450 78 feedbackHolder.id = 'feedbackHolder';
nickjillings@1450 79
nickjillings@1450 80 testContent.style.zIndex = 1;
nickjillings@1450 81 interfaceContext.insertPoint.innerHTML = null; // Clear the current schema
nickjillings@1450 82
nickjillings@1450 83 // Inject into HTML
nickjillings@1450 84 testContent.appendChild(title); // Insert the title
nickjillings@1450 85 testContent.appendChild(pagetitle);
nickjillings@1450 86 testContent.appendChild(interfaceButtons);
nickjillings@1450 87 testContent.appendChild(sliderBox);
nickjillings@1450 88 testContent.appendChild(feedbackHolder);
nickjillings@1450 89 interfaceContext.insertPoint.appendChild(testContent);
nickjillings@1450 90
nickjillings@1450 91 // Load the full interface
nickjillings@1450 92 testState.initialise();
nickjillings@1450 93 testState.advanceState();
nickjillings@1450 94 }
nickjillings@1450 95
nickjillings@1450 96 function loadTest(audioHolderObject)
nickjillings@1450 97 {
nickjillings@1450 98 // Reset audioEngineContext.Metric globals for new test
nickjillings@1450 99 audioEngineContext.newTestPage();
nickjillings@1450 100
nickjillings@1450 101 // Delete any previous audioObjects associated with the audioEngine
nickjillings@1450 102 audioEngineContext.audioObjects = [];
nickjillings@1450 103 interfaceContext.deleteCommentBoxes();
nickjillings@1450 104 interfaceContext.deleteCommentQuestions();
nickjillings@1450 105
nickjillings@1450 106 var id = audioHolderObject.id;
nickjillings@1450 107
nickjillings@1450 108 var feedbackHolder = document.getElementById('feedbackHolder');
nickjillings@1450 109 var interfaceObj = audioHolderObject.interfaces;
nickjillings@1450 110
nickjillings@1450 111 var sliderBox = document.getElementById('slider');
nickjillings@1450 112 feedbackHolder.innerHTML = null;
nickjillings@1450 113 sliderBox.innerHTML = null;
nickjillings@1450 114
nickjillings@1450 115 var commentBoxPrefix = "Comment on track";
nickjillings@1450 116 if (interfaceObj.commentBoxPrefix != undefined) {
nickjillings@1450 117 commentBoxPrefix = interfaceObj.commentBoxPrefix;
nickjillings@1450 118 }
nickjillings@1450 119
nickjillings@1450 120 /// CHECK FOR SAMPLE RATE COMPATIBILITY
nickjillings@1450 121 if (audioHolderObject.sampleRate != undefined) {
nickjillings@1450 122 if (Number(audioHolderObject.sampleRate) != audioContext.sampleRate) {
nickjillings@1450 123 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.';
nickjillings@1450 124 alert(errStr);
nickjillings@1450 125 return;
nickjillings@1450 126 }
nickjillings@1450 127 }
nickjillings@1450 128
nickjillings@1450 129 var loopPlayback = audioHolderObject.loop;
nickjillings@1450 130
nickjillings@1450 131 audioEngineContext.loopPlayback = loopPlayback;
nickjillings@1450 132
nickjillings@1450 133 currentTestHolder = document.createElement('audioHolder');
nickjillings@1450 134 currentTestHolder.id = audioHolderObject.id;
nickjillings@1450 135 currentTestHolder.repeatCount = audioHolderObject.repeatCount;
nickjillings@1450 136
nickjillings@1450 137 $(audioHolderObject.commentQuestions).each(function(index,element) {
nickjillings@1450 138 var node = interfaceContext.createCommentQuestion(element);
nickjillings@1450 139 feedbackHolder.appendChild(node.holder);
nickjillings@1450 140 });
nickjillings@1450 141
nickjillings@1450 142 // Find all the audioElements from the audioHolder
nickjillings@1450 143 $(audioHolderObject.audioElements).each(function(index,element){
nickjillings@1450 144 // Find URL of track
nickjillings@1450 145 // In this jQuery loop, variable 'this' holds the current audioElement.
nickjillings@1450 146
nickjillings@1450 147 // Now load each audio sample. First create the new track by passing the full URL
nickjillings@1450 148 var trackURL = audioHolderObject.hostURL + element.url;
nickjillings@1450 149 var audioObject = audioEngineContext.newTrack(element);
nickjillings@1450 150
nickjillings@1450 151 var node = interfaceContext.createCommentBox(audioObject);
nickjillings@1450 152
nickjillings@1450 153 // Create a slider per track
nickjillings@1450 154 audioObject.interfaceDOM = new sliderObject(audioObject);
nickjillings@1450 155
nickjillings@1450 156 // Distribute it randomnly
nickjillings@1450 157 audioObject.interfaceDOM.slider.value = Math.random();
nickjillings@1450 158
nickjillings@1450 159 sliderBox.appendChild(audioObject.interfaceDOM.holder);
nickjillings@1450 160 audioObject.metric.initialised(audioObject.interfaceDOM.slider.value);
nickjillings@1450 161
nickjillings@1450 162 });
nickjillings@1450 163
nickjillings@1450 164 // Auto-align
nickjillings@1450 165 var numObj = audioHolderObject.audioElements.length;
nickjillings@1450 166 var totalWidth = (numObj-1)*150+100;
nickjillings@1450 167 var diff = (window.innerWidth - totalWidth)/2;
nickjillings@1450 168 audioEngineContext.audioObjects[0].interfaceDOM.holder.style.marginLeft = diff + 'px';
nickjillings@1450 169 }
nickjillings@1450 170
nickjillings@1450 171 function sliderObject(audioObject)
nickjillings@1450 172 {
nickjillings@1450 173 // Constructs the slider object. We use the HTML5 slider object
nickjillings@1450 174 this.parent = audioObject;
nickjillings@1450 175 this.holder = document.createElement('div');
nickjillings@1450 176 this.title = document.createElement('span');
nickjillings@1450 177 this.slider = document.createElement('input');
nickjillings@1450 178 this.play = document.createElement('button');
nickjillings@1450 179
nickjillings@1450 180 this.holder.className = 'track-slider';
nickjillings@1450 181 this.holder.style.height = window.innerHeight-200 + 'px';
nickjillings@1450 182 this.holder.appendChild(this.title);
nickjillings@1450 183 this.holder.appendChild(this.slider);
nickjillings@1450 184 this.holder.appendChild(this.play);
nickjillings@1450 185 this.holder.align = "center";
nickjillings@1450 186 this.holder.style.marginLeft = "50px";
nickjillings@1450 187 this.holder.setAttribute('trackIndex',audioObject.id);
nickjillings@1450 188
nickjillings@1450 189 this.title.textContent = audioObject.id;
nickjillings@1450 190 this.title.style.width = "100%";
nickjillings@1450 191 this.title.style.float = "left";
nickjillings@1450 192
nickjillings@1450 193 this.slider.type = "range";
nickjillings@1450 194 this.slider.min = "0";
nickjillings@1450 195 this.slider.max = "1";
nickjillings@1450 196 this.slider.step = "0.01";
nickjillings@1450 197 this.slider.setAttribute('orient','vertical');
nickjillings@1450 198 this.slider.style.float = "left";
nickjillings@1450 199 this.slider.style.width = "100%";
nickjillings@1450 200 this.slider.style.height = window.innerHeight-250 + 'px';
nickjillings@1450 201 this.slider.onchange = function()
nickjillings@1450 202 {
nickjillings@1450 203 var time = audioEngineContext.timer.getTestTime();
nickjillings@1450 204 var id = Number(this.parentNode.getAttribute('trackIndex'));
nickjillings@1450 205 audioEngineContext.audioObjects[id].metric.moved(time,this.value);
nickjillings@1450 206 console.log('slider '+id+' moved to '+this.value+' ('+time+')');
nickjillings@1450 207 };
nickjillings@1450 208
nickjillings@1450 209 this.play.textContent = "Play";
nickjillings@1450 210 this.play.value = audioObject.id;
nickjillings@1450 211 this.play.style.float = "left";
nickjillings@1450 212 this.play.style.width = "100%";
nickjillings@1450 213 this.play.onclick = function()
nickjillings@1450 214 {
nickjillings@1450 215 audioEngineContext.play();
nickjillings@1450 216 if (audioEngineContext.audioObjectsReady) {
nickjillings@1450 217 var id = Number(event.srcElement.value);
nickjillings@1450 218 //audioEngineContext.metric.sliderPlayed(id);
nickjillings@1450 219 audioEngineContext.play(id);
nickjillings@1450 220 }
nickjillings@1450 221 };
nickjillings@1450 222
nickjillings@1450 223 this.enable = function() {
nickjillings@1450 224 if (this.parent.state == 1)
nickjillings@1450 225 {
nickjillings@1450 226 $(this.slider).removeClass('track-slider-disabled');
nickjillings@1450 227 }
nickjillings@1450 228 };
nickjillings@1450 229
nickjillings@1450 230 this.exportXMLDOM = function(audioObject) {
nickjillings@1450 231 // Called by the audioObject holding this element. Must be present
nickjillings@1450 232 var node = document.createElement('value');
nickjillings@1450 233 node.textContent = this.slider.value;
nickjillings@1450 234 return node;
nickjillings@1450 235 };
nickjillings@1450 236 this.getValue = function() {
nickjillings@1450 237 return this.slider.value;
nickjillings@1450 238 };
nickjillings@1450 239 }
nickjillings@1450 240
nickjillings@1450 241
nickjillings@1450 242 function buttonSubmitClick() // TODO: Only when all songs have been played!
nickjillings@1450 243 {
nickjillings@1450 244 var checks = testState.currentStateMap[testState.currentIndex].interfaces[0].options;
nickjillings@1450 245 var canContinue = true;
nickjillings@1450 246
nickjillings@1450 247 // Check that the anchor and reference objects are correctly placed
nickjillings@1450 248 if (interfaceContext.checkHiddenAnchor() == false) {return;}
nickjillings@1450 249 if (interfaceContext.checkHiddenReference() == false) {return;}
nickjillings@1450 250 /*
nickjillings@1450 251 for (var i=0; i<checks.length; i++) {
nickjillings@1450 252 if (checks[i].type == 'check')
nickjillings@1450 253 {
nickjillings@1450 254 switch(checks[i].check) {
nickjillings@1450 255 case 'fragmentPlayed':
nickjillings@1450 256 // Check if all fragments have been played
nickjillings@1450 257 var checkState = interfaceContext.checkAllPlayed();
nickjillings@1450 258 if (checkState == false) {canContinue = false;}
nickjillings@1450 259 break;
nickjillings@1450 260 case 'fragmentFullPlayback':
nickjillings@1450 261 // Check all fragments have been played to their full length
nickjillings@1450 262 var checkState = interfaceContext.checkAllPlayed();
nickjillings@1450 263 if (checkState == false) {canContinue = false;}
nickjillings@1450 264 console.log('NOTE: fragmentFullPlayback not currently implemented, performing check fragmentPlayed instead');
nickjillings@1450 265 break;
nickjillings@1450 266 case 'fragmentMoved':
nickjillings@1450 267 // Check all fragment sliders have been moved.
nickjillings@1450 268 var checkState = interfaceContext.checkAllMoved();
nickjillings@1450 269 if (checkState == false) {canContinue = false;}
nickjillings@1450 270 break;
nickjillings@1450 271 case 'fragmentComments':
nickjillings@1450 272 // Check all fragment sliders have been moved.
nickjillings@1450 273 var checkState = interfaceContext.checkAllCommented();
nickjillings@1450 274 if (checkState == false) {canContinue = false;}
nickjillings@1450 275 break;
nickjillings@1450 276 case 'scalerange':
nickjillings@1450 277 // Check the scale is used to its full width outlined by the node
nickjillings@1450 278 var checkState = interfaceContext.checkScaleRange();
nickjillings@1450 279 if (checkState == false) {canContinue = false;}
nickjillings@1450 280 break;
nickjillings@1450 281 }
nickjillings@1450 282
nickjillings@1450 283 }
nickjillings@1450 284 if (!canContinue) {break;}
nickjillings@1450 285 }
nickjillings@1450 286 */
nickjillings@1450 287 if (canContinue) {
nickjillings@1450 288 if (audioEngineContext.status == 1) {
nickjillings@1450 289 var playback = document.getElementById('playback-button');
nickjillings@1450 290 playback.click();
nickjillings@1450 291 // This function is called when the submit button is clicked. Will check for any further tests to perform, or any post-test options
nickjillings@1450 292 } else
nickjillings@1450 293 {
nickjillings@1450 294 if (audioEngineContext.timer.testStarted == false)
nickjillings@1450 295 {
nickjillings@1450 296 alert('You have not started the test! Please press start to begin the test!');
nickjillings@1450 297 return;
nickjillings@1450 298 }
nickjillings@1450 299 }
nickjillings@1450 300 testState.advanceState();
nickjillings@1450 301 }
nickjillings@1450 302 }
nickjillings@1450 303
nickjillings@1450 304 function pageXMLSave(store, testXML)
nickjillings@1450 305 {
nickjillings@1450 306 // Saves a specific test page
nickjillings@1450 307 var xmlDoc = store;
nickjillings@1450 308 // Check if any session wide metrics are enabled
nickjillings@1450 309
nickjillings@1450 310 var commentShow = testXML.elementComments;
nickjillings@1450 311
nickjillings@1450 312 var metric = document.createElement('metric');
nickjillings@1450 313 if (audioEngineContext.metric.enableTestTimer)
nickjillings@1450 314 {
nickjillings@1450 315 var testTime = document.createElement('metricResult');
nickjillings@1450 316 testTime.id = 'testTime';
nickjillings@1450 317 testTime.textContent = audioEngineContext.timer.testDuration;
nickjillings@1450 318 metric.appendChild(testTime);
nickjillings@1450 319 }
nickjillings@1450 320 xmlDoc.appendChild(metric);
nickjillings@1450 321 var audioObjects = audioEngineContext.audioObjects;
nickjillings@1450 322 for (var i=0; i<audioObjects.length; i++)
nickjillings@1450 323 {
nickjillings@1450 324 var audioElement = audioEngineContext.audioObjects[i].exportXMLDOM();
nickjillings@1450 325 audioElement.setAttribute('presentedId',i);
nickjillings@1450 326 xmlDoc.appendChild(audioElement);
nickjillings@1450 327 }
nickjillings@1450 328
nickjillings@1450 329 $(interfaceContext.commentQuestions).each(function(index,element){
nickjillings@1450 330 var node = element.exportXMLDOM();
nickjillings@1450 331 xmlDoc.appendChild(node);
nickjillings@1450 332 });
nickjillings@1450 333 store = xmlDoc;
nickjillings@1450 334 }