annotate analyse.html @ 1390:1e85294554fe

Index page now links to example APE project, example MUSHRA project, test creator, analysis page, citing info, GNU license, and instructions. Instructions and example project contain info on checkboxes.
author Brecht De Man <b.deman@qmul.ac.uk>
date Fri, 18 Dec 2015 18:26:46 +0000
parents
children babd5366db49 888292c88c33
rev   line source
b@1390 1 <!DOCTYPE html>
b@1390 2 <html lang="en">
b@1390 3 <head>
b@1390 4 <meta charset="utf-8">
b@1390 5
b@1390 6 <!-- Always force latest IE rendering engine (even in intranet) & Chrome Frame
b@1390 7 Remove this if you use the .htaccess -->
b@1390 8 <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
b@1390 9
b@1390 10 <title>Analysis</title>
b@1390 11 <meta name="description" content="Show results from subjective evaluation">
b@1390 12 <meta name="author" content="Brecht De Man">
b@1390 13
b@1390 14 <script type="text/javascript" src="https://www.google.com/jsapi"></script>
b@1390 15 <script type="text/javascript">
b@1390 16 // To aid 'one-page set-up' all scripts and CSS must be included directly in this file!
b@1390 17
b@1390 18 //google.load("visualization", "1", {packages:["corechart"]});
b@1390 19
b@1390 20 /*************
b@1390 21 * SETUP *
b@1390 22 *************/
b@1390 23 // folder where to find the XML files
b@1390 24 xmlFileFolder = "analysis_test";
b@1390 25 // array of XML files
b@1390 26 var xmlFiles = ['McG-A-2013-09.xml', 'McG-A-2014-03.xml', 'McG-A-2014-12.xml', 'McG-B-2013-09.xml',
b@1390 27 'McG-B-2014-03.xml', 'McG-B-2014-12.xml', 'McG-C-2013-09.xml', 'McG-C-2014-03.xml', 'McG-C-2014-12.xml',
b@1390 28 'McG-D-2013-09.xml', 'McG-D-2014-03.xml', 'McG-D-2014-12.xml', 'McG-E-2013-09.xml', 'McG-E-2014-03.xml',
b@1390 29 'McG-E-2014-12.xml', 'McG-F-2013-09.xml', 'McG-F-2014-03.xml', 'McG-F-2014-12.xml', 'McG-G-2014-03.xml',
b@1390 30 'McG-G-2014-12.xml', 'McG-H-2013-09.xml', 'McG-H-2014-03.xml', 'McG-H-2014-12.xml', 'McG-I-2013-09.xml',
b@1390 31 'McG-I-2014-03.xml', 'McG-J-2013-09.xml', 'McG-J-2014-03.xml', 'McG-K-2013-09.xml', 'McG-K-2014-03.xml',
b@1390 32 'McG-L-2013-09.xml', 'McG-L-2014-03.xml', 'McG-M-2013-09.xml', 'McG-M-2014-03.xml', 'McG-N-2013-09.xml',
b@1390 33 'McG-N-2014-03.xml', 'McG-O-2013-09.xml', 'McG-O-2014-03.xml', 'McG-P-2013-09.xml', 'McG-P-2014-03.xml',
b@1390 34 'McG-pro1-2013-09.xml', 'McG-pro1-2014-03.xml', 'McG-pro1-2014-12.xml', 'McG-pro2-2013-09.xml',
b@1390 35 'McG-pro2-2014-03.xml', 'McG-pro2-2014-12.xml', 'McG-Q-2014-12.xml', 'McG-R-2014-12.xml',
b@1390 36 'McG-S-2014-12.xml', 'McG-subA-2013-09.xml', 'McG-subA-2014-03.xml', 'McG-subB-2014-03.xml',
b@1390 37 'McG-subB-2014-12.xml', 'McG-subC-2013-09.xml', 'McG-subC-2014-03.xml', 'McG-subC-2014-12.xml',
b@1390 38 'McG-subD-2013-09.xml', 'McG-subD-2014-12.xml', 'McG-subE-2014-12.xml', 'McG-subG-2014-12.xml',
b@1390 39 'McG-subH-2013-09.xml', 'McG-T-2014-12.xml', 'McG-U-2014-12.xml', 'McG-V-2014-12.xml',
b@1390 40 'McG-W-2014-12.xml', 'McG-X-2014-12.xml', 'MG1-2013-09.xml', 'MG2-2013-09.xml', 'MG3-2013-09.xml',
b@1390 41 'MG4-2013-09.xml', 'MG5-2013-09.xml', 'MG6-2013-09.xml', 'MG7-2013-09.xml', 'MG8-2013-09.xml',
b@1390 42 'MG9-2013-09.xml', 'QM-1-1.xml', 'QM-1-2.xml', 'QM-10-1.xml', 'QM-11-1.xml', 'QM-11-2.xml', 'QM-12-1.xml', 'QM-12-2.xml',
b@1390 43 'QM-13-1.xml', 'QM-14-1.xml', 'QM-15-1.xml', 'QM-16-1.xml', 'QM-17-1.xml', 'QM-18-1.xml', 'QM-18-2.xml',
b@1390 44 'QM-18-3.xml', 'QM-19-1.xml', 'QM-2-1.xml', 'QM-2-2.xml', 'QM-2-3.xml', 'QM-20-1.xml', 'QM-20-2.xml',
b@1390 45 'QM-20-3.xml', 'QM-21-1.xml', 'QM-21-2.xml', 'QM-3-1.xml', 'QM-3-2.xml', 'QM-3-3.xml', 'QM-4-1.xml', 'QM-5-1.xml',
b@1390 46 'QM-5-2.xml', 'QM-6-1.xml', 'QM-6-2.xml', 'QM-7-1.xml', 'QM-7-2.xml', 'QM-8-1.xml', 'QM-9-1.xml',
b@1390 47 'PXL-L1.xml','PXL-L2.xml','PXL-L3.xml','PXL-L4.xml','PXL-L5.xml','PXL-S1.xml','PXL-S2.xml','PXL-S3.xml',
b@1390 48 'PXL-S4.xml','PXL-S5.xml','PXL-S6.xml','PXL-S7.xml','PXL-pro.xml','DU-A1.xml','DU-A2.xml','DU-B1.xml',
b@1390 49 'DU-B2.xml','DU-C1.xml','DU-C2.xml','DU-D1.xml','DU-D2.xml','DU-E1.xml','DU-F1.xml','DU-F2.xml','DU-G1.xml',
b@1390 50 'DU-G2.xml','DU-H1.xml','DU-H2.xml','DU-I2.xml','DU-J2.xml','DU-K1.xml','DU-K2.xml','DU-L1.xml','DU-L2.xml',
b@1390 51 'DU-M1.xml','DU-M2.xml','DU-N1.xml','DU-O1.xml','DU-O2.xml','DU-P1.xml','DU-P2.xml','DU-Q1.xml','DU-Q2.xml',
b@1390 52 'DU-R1.xml','DU-R2.xml','DU-S1.xml','DU-S2.xml','DU-T1.xml','DU-T2.xml','DU-U1.xml','DU-U2.xml','DU-U3.xml'];
b@1390 53 //['QM-1-1.xml','QM-2-1.xml','QM-2-2.xml','QM-2-3.xml','QM-3-1.xml','QM-3-2.xml','QM-4-1.xml','QM-5-1.xml','QM-5-2.xml','QM-6-1.xml','QM-6-2.xml','QM-7-1.xml','QM-7-2.xml','QM-8-1.xml','QM-9-1.xml','QM-10-1.xml','QM-11-1.xml','QM-12-1.xml','QM-12-2.xml','QM-13-1.xml','QM-14-1.xml','QM-15-1.xml','QM-16-1.xml','QM-17-1.xml','QM-18-1.xml','QM-18-2.xml','QM-18-3.xml','QM-19-1.xml','QM-20-1.xml','QM-20-2.xml','QM-20-3.xml','QM-21-1.xml','QM-21-2.xml'];
b@1390 54 //['McG-A-2014-03.xml','McG-B-2014-03.xml','McG-C-2014-03.xml','McG-D-2014-03.xml','McG-E-2014-03.xml','McG-F-2014-03.xml','McG-G-2014-03.xml','McG-H-2014-03.xml'];
b@1390 55
b@1390 56 //TODO: make retrieval of file names automatic / drag files on here
b@1390 57
b@1390 58 /****************
b@1390 59 * VARIABLES *
b@1390 60 ****************/
b@1390 61
b@1390 62 // Counters
b@1390 63 // How many files, audioholders, audioelementes and statements annotated (don't count current one)
b@1390 64 var numberOfFiles = -1;
b@1390 65 var numberOfaudioholders = -1;
b@1390 66 var numberOfaudioelementes = -1;
b@1390 67 var numberOfStatements = -1;
b@1390 68 var numberOfSkippedComments = 0;
b@1390 69
b@1390 70 // Object arrays
b@1390 71 var fileNameArray = [];
b@1390 72 var subjectArray = [];
b@1390 73 var audioholderArray = [];
b@1390 74 var audioelementArray = [];
b@1390 75
b@1390 76 // End of (file, audioholder, audioelement) flags
b@1390 77 var newFile = true;
b@1390 78 var newAudioHolder = true;
b@1390 79 var newAudioElement = true;
b@1390 80
b@1390 81 var fileCounter = 0; // file index
b@1390 82 var audioholderCounter=0; // audioholder index (current XML file)
b@1390 83 var audioelementCounter=0; // audioelement index (current audioholder)
b@1390 84 var statementNumber=0; // total number of statements
b@1390 85
b@1390 86 var root; // root of XML file
b@1390 87 var commentInFull = ''; // full comment
b@1390 88
b@1390 89 var playAudio = true; // whether corresponding audio should be played back
b@1390 90
b@1390 91 // // Measuring time
b@1390 92 // var lastTimeMeasured = -1; //
b@1390 93 // var durationLastAnnotation = -1; // duration of last annotation
b@1390 94 // var timeArray = [];
b@1390 95 // var MIN_TIME = 1.0; // minimum time counted as significant
b@1390 96 // var measurementPaused = false; // whether time measurement is paused
b@1390 97 // var timeInBuffer = 0; //
b@1390 98
b@1390 99 var topLevel;
b@1390 100 window.onload = function() {
b@1390 101 // Initialise page
b@1390 102 topLevel = document.getElementById('topLevelBody');
b@1390 103 var setup = document.createElement('div');
b@1390 104 setup.id = 'setupTagDiv';
b@1390 105 loadAllFiles();
b@1390 106 printSurveyData()
b@1390 107 //makePlots();
b@1390 108 // measure time at this point:
b@1390 109 lastTimeMeasured = new Date().getTime(); // in milliseconds
b@1390 110 };
b@1390 111
b@1390 112 // Assert function
b@1390 113 function assert(condition, message) {
b@1390 114 if (!condition) {
b@1390 115 message = message || "Assertion failed";
b@1390 116 if (typeof Error !== "undefined") {
b@1390 117 throw new Error(message);
b@1390 118 }
b@1390 119 throw message; // Fallback
b@1390 120 }
b@1390 121 }
b@1390 122
b@1390 123 function median(values) { // TODO: replace code by '50th percentile' - should be the same?
b@1390 124 values.sort( function(a,b) {return a - b;} );
b@1390 125 var half = Math.floor(values.length/2);
b@1390 126 if(values.length % 2)
b@1390 127 return values[half];
b@1390 128 else
b@1390 129 return (values[half-1] + values[half]) / 2.0;
b@1390 130 }
b@1390 131
b@1390 132 function percentile(values, n) {
b@1390 133 values.sort( function(a,b) {return a - b;} );
b@1390 134 // get ordinal rank
b@1390 135 var rank = Math.min(Math.floor(values.length*n/100), values.length-1);
b@1390 136 return values[rank];
b@1390 137 }
b@1390 138
b@1390 139 /***********************
b@1390 140 * TIME MEASUREMENT *
b@1390 141 ************************/
b@1390 142
b@1390 143 // measure time since last time this function was called
b@1390 144 function timeSinceLastCall() {
b@1390 145 // current time
b@1390 146 var currentTime = new Date().getTime();
b@1390 147 // calculate time difference
b@1390 148 var timeDifference = currentTime - lastTimeMeasured + timeInBuffer;
b@1390 149 // clear buffer (for pausing)
b@1390 150 timeInBuffer = 0;
b@1390 151 // remember last measured time
b@1390 152 lastTimeMeasured = currentTime;
b@1390 153 return timeDifference;
b@1390 154 }
b@1390 155
b@1390 156 // pause time measurement
b@1390 157 function pauseTimeMeasurement() {
b@1390 158 // UN-PAUSE
b@1390 159 if (measurementPaused) { // already paused
b@1390 160 // button shows 'pause' again
b@1390 161 document.getElementById('pauseButton').innerHTML = 'Pause';
b@1390 162 // toggle state
b@1390 163 measurementPaused = false;
b@1390 164 // resume time measurement
b@1390 165 lastTimeMeasured = new Date().getTime(); // reset time, discard time while paused
b@1390 166 } else { // PAUSE
b@1390 167 // button shows 'resume'
b@1390 168 document.getElementById('pauseButton').innerHTML = 'Resume';
b@1390 169 // toggle state
b@1390 170 measurementPaused = true;
b@1390 171 // pause time measurement
b@1390 172 timeInBuffer = timeSinceLastCall();
b@1390 173 }
b@1390 174 }
b@1390 175
b@1390 176 // show elapsed time on interface
b@1390 177 function showTimeElapsedInSeconds() {
b@1390 178 // if paused: un-pause
b@1390 179 if (measurementPaused) {
b@1390 180 pauseTimeMeasurement();
b@1390 181 }
b@1390 182
b@1390 183 // time of last annotation
b@1390 184 var lastAnnotationTime = timeSinceLastCall()/1000;
b@1390 185 document.getElementById('timeDisplay').innerHTML = lastAnnotationTime.toFixed(2);
b@1390 186 // average time over last ... annotations
b@1390 187 var avgAnnotationTime;
b@1390 188 var numberOfElementsToAverage =
b@1390 189 document.getElementById('numberOfTimeAverages').value;
b@1390 190 if (isPositiveInteger(numberOfElementsToAverage)) {
b@1390 191 avgAnnotationTime =
b@1390 192 calculateAverageTime(lastAnnotationTime,
b@1390 193 Number(numberOfElementsToAverage));
b@1390 194 } else {
b@1390 195 // change text field content to 'ALL'
b@1390 196 document.getElementById('numberOfTimeAverages').value = 'ALL';
b@1390 197 avgAnnotationTime = calculateAverageTime(lastAnnotationTime, -1);
b@1390 198 }
b@1390 199 document.getElementById('timeAverageDisplay').innerHTML = avgAnnotationTime.toFixed(2);
b@1390 200 }
b@1390 201
b@1390 202 // auxiliary function: is string a positive integer?
b@1390 203 // http://stackoverflow.com/questions/10834796/...
b@1390 204 // validate-that-a-string-is-a-positive-integer
b@1390 205 function isPositiveInteger(str) {
b@1390 206 var n = ~~Number(str);
b@1390 207 return String(n) === str && n >= 0;
b@1390 208 }
b@1390 209
b@1390 210 // calculate average time
b@1390 211 function calculateAverageTime(newTimeMeasurementInSeconds,numberOfPoints) {
b@1390 212 // append last measurement time to time array, if significant
b@1390 213 if (newTimeMeasurementInSeconds > MIN_TIME) {
b@1390 214 timeArray.push(newTimeMeasurementInSeconds);
b@1390 215 }
b@1390 216 // average over last N elements of this array
b@1390 217 if (numberOfPoints < 0 || numberOfPoints>=timeArray.length) { // calculate average over all
b@1390 218 var sum = 0;
b@1390 219 for (var i = 0; i < timeArray.length; i++) {
b@1390 220 sum += timeArray[i];
b@1390 221 }
b@1390 222 averageOfTimes = sum/timeArray.length;
b@1390 223 } else { // calculate average over specified number of times measured last
b@1390 224 var sum = 0;
b@1390 225 for (var i = timeArray.length-numberOfPoints; i < timeArray.length; i++) {
b@1390 226 sum += timeArray[i];
b@1390 227 }
b@1390 228 averageOfTimes = sum/numberOfPoints;
b@1390 229 }
b@1390 230 return averageOfTimes;
b@1390 231 }
b@1390 232
b@1390 233
b@1390 234 /********************************
b@1390 235 * PLAYBACK OF AUDIO *
b@1390 236 ********************************/
b@1390 237
b@1390 238 //PLAYaudioelement
b@1390 239 // Keep track of whether audio should be played
b@1390 240 function playFlagChanged(){
b@1390 241 playAudio = playFlag.checked; // global variable
b@1390 242
b@1390 243 if (!playAudio){ // if audio needs to stop
b@1390 244 audio.pause(); // stop audio - if anything is playing
b@1390 245 currently_playing = ''; // back to empty string so playaudioelement knows nothing's playing
b@1390 246 }
b@1390 247 }
b@1390 248
b@1390 249 // audioholder that's currently playing
b@1390 250 var currently_playing_audioholder = ''; // at first: empty string
b@1390 251 var currently_playing_audioelement = '';
b@1390 252 var audio;
b@1390 253
b@1390 254 // Play audioelement of audioholder if available, from start or from same position
b@1390 255 function playaudioelement(audioholderName, audioelementerName){
b@1390 256 if (playAudio) { // if enabled
b@1390 257 // get corresponding file from folder
b@1390 258 var file_location = 'audio/'+audioholderName + '/' + audioelementerName + '.mp3'; // fixed path and file name format
b@1390 259
b@1390 260 // if not available, show error/warning message
b@1390 261 //TODO ...
b@1390 262
b@1390 263 // if nothing playing yet, start playing
b@1390 264 if (currently_playing_audioholder == ''){ // signal that nothing is playing
b@1390 265 //playSound(audioBuffer);
b@1390 266 audio = new Audio(file_location);
b@1390 267 audio.loop = true; // loop when end is reached
b@1390 268 audio.play();
b@1390 269 currently_playing_audioholder = audioholderName;
b@1390 270 currently_playing_audioelement = audioelementerName;
b@1390 271 } else if (currently_playing_audioholder != audioholderName) {
b@1390 272 // if different audioholder playing, stop that and start playing
b@1390 273 audio.pause(); // stop audio
b@1390 274 audio = new Audio(file_location); // load new file
b@1390 275 audio.loop = true; // loop when end is reached
b@1390 276 audio.play(); // play audio from the start
b@1390 277 currently_playing_audioholder = audioholderName;
b@1390 278 currently_playing_audioelement = audioelementerName;
b@1390 279 } else if (currently_playing_audioelement != audioelementerName) {
b@1390 280 // if same audioholder playing, start playing from where it left off
b@1390 281 skipTime = audio.currentTime; // time to skip to
b@1390 282 audio.pause(); // stop audio
b@1390 283 audio = new Audio(file_location);
b@1390 284 audio.addEventListener('loadedmetadata', function() {
b@1390 285 this.currentTime = skipTime;
b@1390 286 console.log('Loaded '+audioholderName+'-'+audioelementerName+', playing from '+skipTime);
b@1390 287 }, false); // skip to same time when audio is loaded!
b@1390 288 audio.loop = true; // loop when end is reached
b@1390 289 audio.play(); // play from that time
b@1390 290 audio.currentTime = skipTime;
b@1390 291 currently_playing_audioholder = audioholderName;
b@1390 292 currently_playing_audioelement = audioelementerName;
b@1390 293 }
b@1390 294 // if same audioelement playing: keep on playing (i.e. do nothing)
b@1390 295 }
b@1390 296 }
b@1390 297
b@1390 298 /********************
b@1390 299 * READING FILES *
b@1390 300 ********************/
b@1390 301
b@1390 302 // Read necessary data from XML file
b@1390 303 function readXML(xmlFileName){
b@1390 304 if (window.XMLHttpRequest)
b@1390 305 {// code for IE7+, Firefox, Chrome, Opera, Safari
b@1390 306 xmlhttp=new XMLHttpRequest();
b@1390 307 }
b@1390 308 else
b@1390 309 {// code for IE6, IE5
b@1390 310 xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
b@1390 311 }
b@1390 312 xmlhttp.open("GET",xmlFileName,false);
b@1390 313 xmlhttp.send();
b@1390 314 return xmlhttp.responseXML;
b@1390 315 }
b@1390 316
b@1390 317 // go over all files and compute relevant statistics
b@1390 318 function loadAllFiles() {
b@1390 319 // retrieve information from XMLs
b@1390 320
b@1390 321 for (fileIndex = 0; fileIndex < xmlFiles.length; fileIndex++) {
b@1390 322 xmlFileName = xmlFileFolder+"/"+xmlFiles[fileIndex];
b@1390 323 xml = readXML(xmlFileName);
b@1390 324 if (xml != null) { // if file exists
b@1390 325 // append file name to array of file names
b@1390 326 fileNameArray.push(xmlFiles[fileIndex]);
b@1390 327
b@1390 328 // get root of XML file
b@1390 329 root = xml.getElementsByTagName('browserevaluationresult')[0];
b@1390 330
b@1390 331 // get subject ID, add to array if not already there
b@1390 332 pretest = root.getElementsByTagName('pretest')[0];
b@1390 333 subjectID = pretest.getElementsByTagName('comment')[0];
b@1390 334 if (subjectID){
b@1390 335 if (subjectID.getAttribute('id')!='sessionId') { // warning in console when not available
b@1390 336 console.log(xmlFiles[fileIndex]+': no SessionID available');
b@1390 337 }
b@1390 338 if (subjectArray.indexOf(subjectID.textContent) == -1) { // if not already in array
b@1390 339 subjectArray.push(subjectID.textContent); // append to array
b@1390 340 }
b@1390 341 }
b@1390 342
b@1390 343 // go over all audioholders, add to array if not already there
b@1390 344 audioholderNodes = root.getElementsByTagName('audioholder');
b@1390 345 // go over audioholderNodes and append audioholder name when not present yet
b@1390 346 for (audioholderIndex = 0; audioholderIndex < audioholderNodes.length; audioholderIndex++) {
b@1390 347 audioholderName = audioholderNodes[audioholderIndex].getAttribute('id');
b@1390 348 if (audioholderArray.indexOf(audioholderName) == -1) { // if not already in array
b@1390 349 audioholderArray.push(audioholderName); // append to array
b@1390 350 }
b@1390 351 // within each audioholder, go over all audioelement IDs, add to array if not already there
b@1390 352 audioelementNodes = audioholderNodes[audioholderIndex].getElementsByTagName('audioelement');
b@1390 353 for (audioelementIndex = 0; audioelementIndex < audioelementNodes.length; audioelementIndex++) {
b@1390 354 audioelementName = audioelementNodes[audioelementIndex].getAttribute('id');
b@1390 355 if (audioelementArray.indexOf(audioelementName) == -1) { // if not already in array
b@1390 356 audioelementArray.push(audioelementName); // append to array
b@1390 357 }
b@1390 358 }
b@1390 359 }
b@1390 360 // count occurrences of each audioholder
b@1390 361 // ...
b@1390 362 }
b@1390 363 else {
b@1390 364 console.log('XML file '+xmlFileName+' not found.');
b@1390 365 }
b@1390 366 }
b@1390 367
b@1390 368 // sort alphabetically
b@1390 369 fileNameArray.sort();
b@1390 370 subjectArray.sort();
b@1390 371 audioholderArray.sort();
b@1390 372 audioelementArray.sort();
b@1390 373
b@1390 374 // display all information in HTML
b@1390 375 // show XML file folder
b@1390 376 document.getElementById('xmlFileFolder_span').innerHTML = "\""+xmlFileFolder+"/\"";
b@1390 377 // show number of files
b@1390 378 document.getElementById('numberOfFiles_span').innerHTML = fileNameArray.length;
b@1390 379 // show list of subject names
b@1390 380 document.getElementById('subjectArray_span').innerHTML = subjectArray.toString();
b@1390 381 // show list of audioholders
b@1390 382 document.getElementById('audioholderArray_span').innerHTML = audioholderArray.toString();
b@1390 383 // show list of audioelementes
b@1390 384 document.getElementById('audioelementArray_span').innerHTML = audioelementArray.toString();
b@1390 385 }
b@1390 386
b@1390 387 function printSurveyData() {
b@1390 388 // print some fields from the survey for different people
b@1390 389
b@1390 390 // go over all XML files
b@1390 391 for (fileIndex = 0; fileIndex < xmlFiles.length; fileIndex++) {
b@1390 392 xmlFileName = xmlFileFolder+"/"+xmlFiles[fileIndex];
b@1390 393 xml = readXML(xmlFileName);
b@1390 394 // make a div
b@1390 395 var div = document.createElement('div');
b@1390 396 document.body.appendChild(div);
b@1390 397 div.id = 'div_survey_'+xmlFileName;
b@1390 398 div.style.width = '1100px';
b@1390 399 //div.style.height = '350px';
b@1390 400
b@1390 401 // title for that div (subject id)
b@1390 402 document.getElementById('div_survey_'+xmlFileName).innerHTML = '<h2>'+xmlFileName+'</h2>';
b@1390 403
b@1390 404 // which songs did they do
b@1390 405 if (xml != null) { // if file exists
b@1390 406 // get root of XML file
b@1390 407 root = xml.getElementsByTagName('browserevaluationresult')[0];
b@1390 408 // go over all audioholders
b@1390 409 // document.getElementById('div_survey_'+xmlFileName).innerHTML += '<strong>Audioholders: </strong>';
b@1390 410 // audioholderNodes = root.getElementsByTagName('audioholder');
b@1390 411 // for (audioholderIndex = 0; audioholderIndex < audioholderNodes.length-1; audioholderIndex++) {
b@1390 412 // document.getElementById('div_survey_'+xmlFileName).innerHTML += audioholderNodes[audioholderIndex].getAttribute('id')+', ';
b@1390 413 // }
b@1390 414 // document.getElementById('div_survey_'+xmlFileName).innerHTML += audioholderNodes[audioholderNodes.length-1].getAttribute('id');
b@1390 415
b@1390 416 // survey responses (each if available)
b@1390 417 // get posttest node for total test
b@1390 418 childNodes = root.childNodes;
b@1390 419 posttestnode = null;
b@1390 420 for (idx = 0; idx < childNodes.length; idx++){
b@1390 421 if (childNodes[childNodes.length-idx-1].tagName == 'posttest') {
b@1390 422 posttestnode = childNodes[childNodes.length-idx-1];
b@1390 423 break;
b@1390 424 }
b@1390 425 }
b@1390 426
b@1390 427 // mix experience
b@1390 428 if (posttestnode) {
b@1390 429 posttestcomments = posttestnode.getElementsByTagName('comment');
b@1390 430 for (idx=0; idx < posttestcomments.length; idx++){
b@1390 431 commentsToPrint = ['generalExperience', 'interfaceExperience'];
b@1390 432 idAttribute = posttestcomments[idx].getAttribute('id');
b@1390 433 if (commentsToPrint.indexOf(idAttribute) >= 0) { // if exists?
b@1390 434 document.getElementById('div_survey_'+xmlFileName).innerHTML += '<br><strong>'+idAttribute+': </strong>'+posttestcomments[idx].textContent;
b@1390 435 }
b@1390 436 }
b@1390 437 }
b@1390 438 }
b@1390 439 }
b@1390 440 }
b@1390 441
b@1390 442 function makePlots() { //TODO: split into different functions
b@1390 443 // TEMPORARY
b@1390 444 makeTimeline(xmlFileFolder+"/"+xmlFiles[7]);
b@1390 445
b@1390 446 // create value array
b@1390 447 var ratings = []; // 3D matrix of ratings (audioholder, audioelement, subject)
b@1390 448 for (audioholderIndex = 0; audioholderIndex < audioholderArray.length; audioholderIndex++) {
b@1390 449 ratings.push([]);
b@1390 450 for (audioelementIndex = 0; audioelementIndex < audioelementArray.length; audioelementIndex++) {
b@1390 451 ratings[audioholderIndex].push([]);
b@1390 452 }
b@1390 453 }
b@1390 454
b@1390 455 // go over all XML files
b@1390 456 for (fileIndex = 0; fileIndex < xmlFiles.length; fileIndex++) {
b@1390 457 xmlFileName = xmlFileFolder+"/"+xmlFiles[fileIndex];
b@1390 458 xml = readXML(xmlFileName);
b@1390 459 if (xml != null) { // if file exists
b@1390 460 // get root of XML file
b@1390 461 root = xml.getElementsByTagName('browserevaluationresult')[0];
b@1390 462 // go over all audioholders
b@1390 463 audioholderNodes = root.getElementsByTagName('audioholder');
b@1390 464 for (audioholderIndex = 0; audioholderIndex < audioholderNodes.length; audioholderIndex++) {
b@1390 465 audioholderName = audioholderNodes[audioholderIndex].getAttribute('id');
b@1390 466 audioelementNodes = audioholderNodes[audioholderIndex].getElementsByTagName('audioelement');
b@1390 467 // go over all audioelements
b@1390 468 for (audioelementIndex = 0; audioelementIndex < audioelementNodes.length; audioelementIndex++) {
b@1390 469 audioelementName = audioelementNodes[audioelementIndex].getAttribute('id');
b@1390 470 // get value
b@1390 471 var value = audioelementNodes[audioelementIndex].getElementsByTagName("value")[0].textContent;
b@1390 472 if (value) { // if not empty, null, undefined...
b@1390 473 ratingValue = parseFloat(value);
b@1390 474 // add to matrix at proper position
b@1390 475 aHidx = audioholderArray.indexOf(audioholderName);
b@1390 476 aEidx = audioelementArray.indexOf(audioelementName);
b@1390 477 ratings[aHidx][aEidx].push(ratingValue);
b@1390 478 }
b@1390 479 }
b@1390 480 }
b@1390 481
b@1390 482 // go over all audioholders
b@1390 483
b@1390 484 // go over all audioelements within audioholder, see if present in idMatrix, add if not
b@1390 485 // add corresponding rating to 'ratings', at position corresponding with position in idMatrix
b@1390 486 }
b@1390 487 }
b@1390 488
b@1390 489 for (audioholderIndex = 0; audioholderIndex < audioholderArray.length; audioholderIndex++) {
b@1390 490 audioholderName = audioholderArray[audioholderIndex]; // for this song
b@1390 491 tickArray = []
b@1390 492
b@1390 493 raw_data = [['SubjectID', 'Rating']];
b@1390 494 audioElIdx = 0;
b@1390 495 for (audioelementIndex = 0; audioelementIndex<ratings[audioholderIndex].length; audioelementIndex++){
b@1390 496 if (ratings[audioholderIndex][audioelementIndex].length>0) {
b@1390 497 audioElIdx++; // increase if not empty
b@1390 498 // make tick label
b@1390 499 tickArray.push({v:audioElIdx, f: audioelementArray[audioelementIndex]});
b@1390 500 }
b@1390 501 for (subject = 0; subject<ratings[audioholderIndex][audioelementIndex].length; subject++){
b@1390 502 // add subject-value pair for each subject
b@1390 503 raw_data.push([audioElIdx, ratings[audioholderIndex][audioelementIndex][subject]]);
b@1390 504 }
b@1390 505 }
b@1390 506
b@1390 507 // create plot (one per song)
b@1390 508 var data = google.visualization.arrayToDataTable(raw_data);
b@1390 509
b@1390 510 var options = {
b@1390 511 title: audioholderName,
b@1390 512 hAxis: {title: 'audioelement ID', minValue: 0, maxValue: audioElIdx+1,
b@1390 513 ticks: tickArray},
b@1390 514 vAxis: {title: 'Rating', minValue: 0, maxValue: 1},
b@1390 515 seriesType: 'scatter',
b@1390 516 legend: 'none'
b@1390 517 };
b@1390 518 var div = document.createElement('div');
b@1390 519 document.body.appendChild(div);
b@1390 520 div.id = 'div_'+audioholderName;
b@1390 521 div.style.width = '1100px';
b@1390 522 div.style.height = '350px';
b@1390 523 var chart = new google.visualization.ComboChart(document.getElementById('div_'+audioholderName));
b@1390 524 chart.draw(data, options);
b@1390 525
b@1390 526 // box plots
b@1390 527 var div = document.createElement('div');
b@1390 528 document.body.appendChild(div);
b@1390 529 div.id = 'div_box_'+audioholderName;
b@1390 530 div.style.width = '1100px';
b@1390 531 div.style.height = '350px';
b@1390 532 // Get median, percentiles, maximum and minimum; outliers.
b@1390 533 pctl25 = [];
b@1390 534 pctl75 = [];
b@1390 535 med = [];
b@1390 536 min = [];
b@1390 537 max = [];
b@1390 538 outlierArray = [];
b@1390 539 max_n_outliers = 0; // maximum number of outliers for one audioelement
b@1390 540 for (audioelementIndex = 0; audioelementIndex<ratings[audioholderIndex].length; audioelementIndex++){
b@1390 541 med.push(median(ratings[audioholderIndex][audioelementIndex])); // median
b@1390 542 pctl25.push(percentile(ratings[audioholderIndex][audioelementIndex], 25)); // 25th percentile
b@1390 543 pctl75.push(percentile(ratings[audioholderIndex][audioelementIndex], 75)); // 75th percentile
b@1390 544 IQR = pctl75[pctl75.length-1]-pctl25[pctl25.length-1];
b@1390 545 // outliers: range of values which is above pctl75+1.5*IQR or below pctl25-1.5*IQR
b@1390 546 outliers = [];
b@1390 547 rest = [];
b@1390 548 for (idx = 0; idx<ratings[audioholderIndex][audioelementIndex].length; idx++){
b@1390 549 if (ratings[audioholderIndex][audioelementIndex][idx] > pctl75[pctl75.length-1]+1.5*IQR ||
b@1390 550 ratings[audioholderIndex][audioelementIndex][idx] < pctl25[pctl25.length-1]-1.5*IQR){
b@1390 551 outliers.push(ratings[audioholderIndex][audioelementIndex][idx]);
b@1390 552 }
b@1390 553 else {
b@1390 554 rest.push(ratings[audioholderIndex][audioelementIndex][idx]);
b@1390 555 }
b@1390 556 }
b@1390 557 outlierArray.push(outliers);
b@1390 558 max_n_outliers = Math.max(max_n_outliers, outliers.length); // update max mber
b@1390 559 // max: maximum value which is not outlier
b@1390 560 max.push(Math.max.apply(null, rest));
b@1390 561 // min: minimum value which is not outlier
b@1390 562 min.push(Math.min.apply(null, rest));
b@1390 563 }
b@1390 564
b@1390 565 // Build data array
b@1390 566 boxplot_data = [['ID', 'Span', '', '', '', 'Median']];
b@1390 567 for (idx = 0; idx < max_n_outliers; idx++) {
b@1390 568 boxplot_data[0].push('Outlier');
b@1390 569 }
b@1390 570 for (audioelementIndex = 0; audioelementIndex<ratings[audioholderIndex].length; audioelementIndex++){
b@1390 571 if (ratings[audioholderIndex][audioelementIndex].length>0) { // if rating array not empty for this audioelement
b@1390 572 data_array = [
b@1390 573 audioelementArray[audioelementIndex], // name
b@1390 574 min[audioelementIndex], // minimum
b@1390 575 pctl75[audioelementIndex],
b@1390 576 pctl25[audioelementIndex],
b@1390 577 max[audioelementIndex], // maximum
b@1390 578 med[audioelementIndex]
b@1390 579 ];
b@1390 580 for (idx = 0; idx < max_n_outliers; idx++) {
b@1390 581 if (idx<outlierArray[audioelementIndex].length){
b@1390 582 data_array.push(outlierArray[audioelementIndex][idx]);
b@1390 583 }
b@1390 584 else {
b@1390 585 data_array.push(null);
b@1390 586 }
b@1390 587 }
b@1390 588 boxplot_data.push(data_array);
b@1390 589 }
b@1390 590 }
b@1390 591
b@1390 592 // Create and populate the data table.
b@1390 593 var data = google.visualization.arrayToDataTable(boxplot_data);
b@1390 594 // Create and draw the visualization.
b@1390 595 var ac = new google.visualization.ComboChart(document.getElementById('div_box_'+audioholderName));
b@1390 596 ac.draw(data, {
b@1390 597 title : audioholderName,
b@1390 598 //width: 600,
b@1390 599 //height: 400,
b@1390 600 vAxis: {title: "Rating"},
b@1390 601 hAxis: {title: "audioelement ID"},
b@1390 602 seriesType: "line",
b@1390 603 pointSize: 5,
b@1390 604 lineWidth: 0,
b@1390 605 colors: ['black'],
b@1390 606 series: { 0: {type: "candlesticks", color: 'blue'}, // box plot shape
b@1390 607 1: {type: "line", pointSize: 10, lineWidth: 0, color: 'red' } }, // median
b@1390 608 legend: 'none'
b@1390 609 });
b@1390 610 }
b@1390 611 }
b@1390 612
b@1390 613 function makeTimeline(xmlFileName){ // WIP
b@1390 614 // Based on the XML file name, take time data and plot playback and marker movements
b@1390 615
b@1390 616 // read XML file and check if exists
b@1390 617 xml = readXML(xmlFileName);
b@1390 618 if (!xml) { // if file does not exist
b@1390 619 console.log('XML file '+xml+'does not exist. ('+xmlFileName+')')
b@1390 620 return; // do nothing; exit function
b@1390 621 }
b@1390 622 // get root of XML file
b@1390 623 root = xml.getElementsByTagName('browserevaluationresult')[0];
b@1390 624
b@1390 625 audioholder_time = 0;
b@1390 626 previous_audioholder_time = 0; // time spent before current audioholder
b@1390 627 time_offset = 0; // test starts at zero
b@1390 628
b@1390 629 // go over all audioholders
b@1390 630 audioholderNodes = root.getElementsByTagName('audioholder');
b@1390 631 for (audioholderIndex = 0; audioholderIndex < audioholderNodes.length; audioholderIndex++) {
b@1390 632 audioholderName = audioholderNodes[audioholderIndex].getAttribute('id');
b@1390 633 if (!audioholderName) {
b@1390 634 console.log('audioholder name is empty; go to next one. ('+xmlFileName+')');
b@1390 635 break;
b@1390 636 }
b@1390 637
b@1390 638 // subtract total audioholder length from subsequent audioholder event times
b@1390 639 audioholder_children = audioholderNodes[audioholderIndex].childNodes;
b@1390 640 foundIt = false;
b@1390 641 console.log(audioholder_children[2].getElementsByTagName("metricResult")) // not working!
b@1390 642 for (idx = 0; idx<audioholder_children.length; idx++) { // go over children
b@1390 643
b@1390 644 if (audioholder_children[idx].getElementsByTagName('metricResult').length) {
b@1390 645 console.log(audioholder_children[idx].getElementsByTagName('metricResult')[0]);
b@1390 646 if (audioholder_children[idx].getElementsByTagName('metricResult')[0].getAttribute('id') == "testTime"){
b@1390 647 audioholder_time = parseFloat(audioholder_children[idx].getElementsByTagName('metricResult')[0].textContent);
b@1390 648 console.log(audioholder_time);
b@1390 649 foundIt = true;
b@1390 650 }
b@1390 651 }
b@1390 652 }
b@1390 653 if (!foundIt) {
b@1390 654 console.log("Skipping audioholder without total time specified from "+xmlFileName+"."); // always hitting this
b@1390 655 break;
b@1390 656 }
b@1390 657
b@1390 658 audioelementNodes = audioholderNodes[audioholderIndex].getElementsByTagName('audioelement');
b@1390 659
b@1390 660 // make div
b@1390 661
b@1390 662 // draw chart
b@1390 663
b@1390 664 // legend with audioelement names
b@1390 665 }
b@1390 666 }
b@1390 667
b@1390 668 </script>
b@1390 669
b@1390 670
b@1390 671
b@1390 672 <style>
b@1390 673 div {
b@1390 674 padding: 2px;
b@1390 675 margin-top: 2px;
b@1390 676 margin-bottom: 2px;
b@1390 677 }
b@1390 678 div.head{
b@1390 679 margin-left: 10px;
b@1390 680 border: black;
b@1390 681 border-width: 2px;
b@1390 682 border-style: solid;
b@1390 683 }
b@1390 684 div.attrib{
b@1390 685 margin-left:25px;
b@1390 686 border: black;
b@1390 687 border-width: 2px;
b@1390 688 border-style: dashed;
b@1390 689 margin-bottom: 10px;
b@1390 690 }
b@1390 691 div#headerMatter{
b@1390 692 background-color: #FFFFCC;
b@1390 693 }
b@1390 694 div#currentStatement{
b@1390 695 font-size:3.0em;
b@1390 696 font-weight: bold;
b@1390 697
b@1390 698 }
b@1390 699 div#debugDisplay {
b@1390 700 color: #CCCCCC;
b@1390 701 font-size:0.3em;
b@1390 702 }
b@1390 703 span#scoreDisplay {
b@1390 704 font-weight: bold;
b@1390 705 }
b@1390 706 div#wrapper {
b@1390 707 width: 780px;
b@1390 708 border: 1px solid black;
b@1390 709 overflow: hidden; /* add this to contain floated children */
b@1390 710 }
b@1390 711 div#instrumentSection {
b@1390 712 width: 250px;
b@1390 713 border: 1px solid red;
b@1390 714 display: inline-block;
b@1390 715 }
b@1390 716 div#featureSection {
b@1390 717 width: 250px;
b@1390 718 border: 1px solid green;
b@1390 719 display: inline-block;
b@1390 720 }
b@1390 721 div#valenceSection {
b@1390 722 width: 250px;
b@1390 723 border: 1px solid blue;
b@1390 724 display: inline-block;
b@1390 725 }
b@1390 726 button#previousComment{
b@1390 727 width: 120px;
b@1390 728 height: 150px;
b@1390 729 font-size:1.5em;
b@1390 730 }
b@1390 731 button#nextComment{
b@1390 732 width: 666px;
b@1390 733 height: 150px;
b@1390 734 font-size:1.5em;
b@1390 735 }
b@1390 736 ul
b@1390 737 {
b@1390 738 list-style-type: none; /* no bullet points */
b@1390 739 margin-left: -20px; /* less indent */
b@1390 740 margin-top: 0px;
b@1390 741 margin-bottom: 5px;
b@1390 742 }
b@1390 743 </style>
b@1390 744
b@1390 745 </head>
b@1390 746
b@1390 747 <body>
b@1390 748 <h1>Subjective evaluation results</h1>
b@1390 749
b@1390 750 <div id="debugDisplay">
b@1390 751 XML file folder: <span id="xmlFileFolder_span"></span>
b@1390 752 </div>
b@1390 753
b@1390 754 <div id="headerMatter">
b@1390 755 <div>
b@1390 756 <strong>Result XML files:</strong> <span id="numberOfFiles_span"></span>
b@1390 757 </div>
b@1390 758 <div>
b@1390 759 <strong>Audioholders in dataset:</strong> <span id="audioholderArray_span"></span>
b@1390 760 </div>
b@1390 761 <div>
b@1390 762 <strong>Subjects in dataset:</strong> <span id="subjectArray_span"></span>
b@1390 763 </div>
b@1390 764 <div>
b@1390 765 <strong>Audioelements in dataset:</strong> <span id="audioelementArray_span"></span>
b@1390 766 </div>
b@1390 767 <br>
b@1390 768 </div>
b@1390 769 <br>
b@1390 770
b@1390 771 <!-- Show time elapsed
b@1390 772 The last annotation took <strong><span id="timeDisplay">(N/A)</span></strong> seconds.
b@1390 773 <br>-->
b@1390 774
b@1390 775 </body>
b@1390 776 </html>