annotate mushra.js @ 755:c73996a0fb21

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