comparison core.js @ 771:0796d28701ae

Start tidy up of core.js
author Nicholas Jillings <n.g.r.jillings@se14.qmul.ac.uk>
date Mon, 07 Dec 2015 12:15:43 +0000
parents d2a83474a967
children d07bc42a716c
comparison
equal deleted inserted replaced
770:d2a83474a967 771:0796d28701ae
30 audioContext = new AudioContext; 30 audioContext = new AudioContext;
31 31
32 // Create test state 32 // Create test state
33 testState = new stateMachine(); 33 testState = new stateMachine();
34 34
35 // Create the audio engine object
36 audioEngineContext = new AudioEngine();
37
38 // Create the popup interface object 35 // Create the popup interface object
39 popup = new interfacePopup(); 36 popup = new interfacePopup();
40 37
41 // Create the specification object 38 // Create the specification object
42 specification = new Specification(); 39 specification = new Specification();
43 40
44 // Create the interface object 41 // Create the interface object
45 interfaceContext = new Interface(specification); 42 interfaceContext = new Interface(specification);
46 }; 43 };
44
45 function loadProjectSpec(url) {
46 // Load the project document from the given URL, decode the XML and instruct audioEngine to get audio data
47 // If url is null, request client to upload project XML document
48 var r = new XMLHttpRequest();
49 r.open('GET',url,true);
50 r.onload = function() {
51 loadProjectSpecCallback(r.response);
52 };
53 r.send();
54 };
55
56 function loadProjectSpecCallback(response) {
57 // Function called after asynchronous download of XML project specification
58 //var decode = $.parseXML(response);
59 //projectXML = $(decode);
60
61 var parse = new DOMParser();
62 projectXML = parse.parseFromString(response,'text/xml');
63
64 // Build the specification
65 specification.decode(projectXML);
66
67 // Create the audio engine object
68 audioEngineContext = new AudioEngine(specification);
69
70 testState.stateMap.push(specification.preTest);
71
72 $(specification.audioHolders).each(function(index,elem){
73 testState.stateMap.push(elem);
74 });
75
76 testState.stateMap.push(specification.postTest);
77
78
79
80 // Detect the interface to use and load the relevant javascripts.
81 var interfaceJS = document.createElement('script');
82 interfaceJS.setAttribute("type","text/javascript");
83 if (specification.interfaceType == 'APE') {
84 interfaceJS.setAttribute("src","ape.js");
85
86 // APE comes with a css file
87 var css = document.createElement('link');
88 css.rel = 'stylesheet';
89 css.type = 'text/css';
90 css.href = 'ape.css';
91
92 document.getElementsByTagName("head")[0].appendChild(css);
93 } else if (specification.interfaceType == "MUSHRA")
94 {
95 interfaceJS.setAttribute("src","mushra.js");
96
97 // MUSHRA comes with a css file
98 var css = document.createElement('link');
99 css.rel = 'stylesheet';
100 css.type = 'text/css';
101 css.href = 'mushra.css';
102
103 document.getElementsByTagName("head")[0].appendChild(css);
104 }
105 document.getElementsByTagName("head")[0].appendChild(interfaceJS);
106
107 // Define window callbacks for interface
108 window.onresize = function(event){interfaceContext.resizeWindow(event);};
109 }
110
111 function createProjectSave(destURL) {
112 // Save the data from interface into XML and send to destURL
113 // If destURL is null then download XML in client
114 // Now time to render file locally
115 var xmlDoc = interfaceXMLSave();
116 var parent = document.createElement("div");
117 parent.appendChild(xmlDoc);
118 var file = [parent.innerHTML];
119 if (destURL == "null" || destURL == undefined) {
120 var bb = new Blob(file,{type : 'application/xml'});
121 var dnlk = window.URL.createObjectURL(bb);
122 var a = document.createElement("a");
123 a.hidden = '';
124 a.href = dnlk;
125 a.download = "save.xml";
126 a.textContent = "Save File";
127
128 popup.showPopup();
129 popup.popupContent.innerHTML = null;
130 popup.popupContent.appendChild(a);
131 } else {
132 var xmlhttp = new XMLHttpRequest;
133 xmlhttp.open("POST",destURL,true);
134 xmlhttp.setRequestHeader('Content-Type', 'text/xml');
135 xmlhttp.onerror = function(){
136 console.log('Error saving file to server! Presenting download locally');
137 createProjectSave(null);
138 };
139 xmlhttp.onreadystatechange = function() {
140 console.log(xmlhttp.status);
141 if (xmlhttp.status != 200 && xmlhttp.readyState == 4) {
142 createProjectSave(null);
143 } else {
144 if (xmlhttp.responseXML == null)
145 {
146 return createProjectSave(null);
147 }
148 var response = xmlhttp.responseXML.childNodes[0];
149 if (response.getAttribute('state') == "OK")
150 {
151 var file = response.getElementsByTagName('file')[0];
152 console.log('Save OK: Filename '+file.textContent+','+file.getAttribute('bytes')+'B');
153 popup.showPopup();
154 popup.popupContent.innerHTML = null;
155 popup.popupContent.textContent = "Thank you!";
156 } else {
157 var message = response.getElementsByTagName('message')[0];
158 errorSessionDump(message.textContent);
159 }
160 }
161 };
162 xmlhttp.send(file);
163 }
164 }
165
166 function errorSessionDump(msg){
167 // Create the partial interface XML save
168 // Include error node with message on why the dump occured
169 var xmlDoc = interfaceXMLSave();
170 var err = document.createElement('error');
171 err.textContent = msg;
172 xmlDoc.appendChild(err);
173 var parent = document.createElement("div");
174 parent.appendChild(xmlDoc);
175 var file = [parent.innerHTML];
176 var bb = new Blob(file,{type : 'application/xml'});
177 var dnlk = window.URL.createObjectURL(bb);
178 var a = document.createElement("a");
179 a.hidden = '';
180 a.href = dnlk;
181 a.download = "save.xml";
182 a.textContent = "Save File";
183
184 popup.showPopup();
185 popup.popupContent.innerHTML = "ERROR : "+msg;
186 popup.popupContent.appendChild(a);
187 }
188
189 // Only other global function which must be defined in the interface class. Determines how to create the XML document.
190 function interfaceXMLSave(){
191 // Create the XML string to be exported with results
192 var xmlDoc = document.createElement("BrowserEvaluationResult");
193 var projectDocument = specification.projectXML;
194 projectDocument.setAttribute('file-name',url);
195 xmlDoc.appendChild(projectDocument);
196 xmlDoc.appendChild(returnDateNode());
197 xmlDoc.appendChild(interfaceContext.returnNavigator());
198 for (var i=0; i<testState.stateResults.length; i++)
199 {
200 xmlDoc.appendChild(testState.stateResults[i]);
201 }
202
203 return xmlDoc;
204 }
47 205
48 function interfacePopup() { 206 function interfacePopup() {
49 // Creates an object to manage the popup 207 // Creates an object to manage the popup
50 this.popup = null; 208 this.popup = null;
51 this.popupContent = null; 209 this.popupContent = null;
516 }; 674 };
517 675
518 this.previousState = function(){}; 676 this.previousState = function(){};
519 } 677 }
520 678
521 function testEnded(testId) 679 function AudioEngine(specification) {
522 {
523 pageXMLSave(testId);
524 if (testXMLSetups.length-1 > testId)
525 {
526 // Yes we have another test to perform
527 testId = (Number(testId)+1);
528 currentState = 'testRun-'+testId;
529 loadTest(testId);
530 } else {
531 console.log('Testing Completed!');
532 currentState = 'postTest';
533 // Check for any post tests
534 var xmlSetup = projectXML.find('setup');
535 var postTest = xmlSetup.find('PostTest')[0];
536 popup.initState(postTest);
537 }
538 }
539
540 function loadProjectSpec(url) {
541 // Load the project document from the given URL, decode the XML and instruct audioEngine to get audio data
542 // If url is null, request client to upload project XML document
543 var r = new XMLHttpRequest();
544 r.open('GET',url,true);
545 r.onload = function() {
546 loadProjectSpecCallback(r.response);
547 };
548 r.send();
549 };
550
551 function loadProjectSpecCallback(response) {
552 // Function called after asynchronous download of XML project specification
553 //var decode = $.parseXML(response);
554 //projectXML = $(decode);
555
556 var parse = new DOMParser();
557 projectXML = parse.parseFromString(response,'text/xml');
558
559 // Build the specification
560 specification.decode(projectXML);
561
562 testState.stateMap.push(specification.preTest);
563
564 $(specification.audioHolders).each(function(index,elem){
565 testState.stateMap.push(elem);
566 });
567
568 testState.stateMap.push(specification.postTest);
569
570 // Obtain the metrics enabled
571 $(specification.metrics).each(function(index,node){
572 var enabled = node.textContent;
573 switch(node.enabled)
574 {
575 case 'testTimer':
576 sessionMetrics.prototype.enableTestTimer = true;
577 break;
578 case 'elementTimer':
579 sessionMetrics.prototype.enableElementTimer = true;
580 break;
581 case 'elementTracker':
582 sessionMetrics.prototype.enableElementTracker = true;
583 break;
584 case 'elementListenTracker':
585 sessionMetrics.prototype.enableElementListenTracker = true;
586 break;
587 case 'elementInitialPosition':
588 sessionMetrics.prototype.enableElementInitialPosition = true;
589 break;
590 case 'elementFlagListenedTo':
591 sessionMetrics.prototype.enableFlagListenedTo = true;
592 break;
593 case 'elementFlagMoved':
594 sessionMetrics.prototype.enableFlagMoved = true;
595 break;
596 case 'elementFlagComments':
597 sessionMetrics.prototype.enableFlagComments = true;
598 break;
599 }
600 });
601
602
603
604 // Detect the interface to use and load the relevant javascripts.
605 var interfaceJS = document.createElement('script');
606 interfaceJS.setAttribute("type","text/javascript");
607 if (specification.interfaceType == 'APE') {
608 interfaceJS.setAttribute("src","ape.js");
609
610 // APE comes with a css file
611 var css = document.createElement('link');
612 css.rel = 'stylesheet';
613 css.type = 'text/css';
614 css.href = 'ape.css';
615
616 document.getElementsByTagName("head")[0].appendChild(css);
617 } else if (specification.interfaceType == "MUSHRA")
618 {
619 interfaceJS.setAttribute("src","mushra.js");
620
621 // MUSHRA comes with a css file
622 var css = document.createElement('link');
623 css.rel = 'stylesheet';
624 css.type = 'text/css';
625 css.href = 'mushra.css';
626
627 document.getElementsByTagName("head")[0].appendChild(css);
628 }
629 document.getElementsByTagName("head")[0].appendChild(interfaceJS);
630
631 // Define window callbacks for interface
632 window.onresize = function(event){interfaceContext.resizeWindow(event);};
633 }
634
635 function createProjectSave(destURL) {
636 // Save the data from interface into XML and send to destURL
637 // If destURL is null then download XML in client
638 // Now time to render file locally
639 var xmlDoc = interfaceXMLSave();
640 var parent = document.createElement("div");
641 parent.appendChild(xmlDoc);
642 var file = [parent.innerHTML];
643 if (destURL == "null" || destURL == undefined) {
644 var bb = new Blob(file,{type : 'application/xml'});
645 var dnlk = window.URL.createObjectURL(bb);
646 var a = document.createElement("a");
647 a.hidden = '';
648 a.href = dnlk;
649 a.download = "save.xml";
650 a.textContent = "Save File";
651
652 popup.showPopup();
653 popup.popupContent.innerHTML = null;
654 popup.popupContent.appendChild(a);
655 } else {
656 var xmlhttp = new XMLHttpRequest;
657 xmlhttp.open("POST",destURL,true);
658 xmlhttp.setRequestHeader('Content-Type', 'text/xml');
659 xmlhttp.onerror = function(){
660 console.log('Error saving file to server! Presenting download locally');
661 createProjectSave(null);
662 };
663 xmlhttp.onreadystatechange = function() {
664 console.log(xmlhttp.status);
665 if (xmlhttp.status != 200 && xmlhttp.readyState == 4) {
666 createProjectSave(null);
667 } else {
668 if (xmlhttp.responseXML == null)
669 {
670 return createProjectSave(null);
671 }
672 var response = xmlhttp.responseXML.childNodes[0];
673 if (response.getAttribute('state') == "OK")
674 {
675 var file = response.getElementsByTagName('file')[0];
676 console.log('Save OK: Filename '+file.textContent+','+file.getAttribute('bytes')+'B');
677 popup.showPopup();
678 popup.popupContent.innerHTML = null;
679 popup.popupContent.textContent = "Thank you!";
680 } else {
681 var message = response.getElementsByTagName('message')[0];
682 errorSessionDump(message.textContent);
683 }
684 }
685 };
686 xmlhttp.send(file);
687 }
688 }
689
690 function errorSessionDump(msg){
691 // Create the partial interface XML save
692 // Include error node with message on why the dump occured
693 var xmlDoc = interfaceXMLSave();
694 var err = document.createElement('error');
695 err.textContent = msg;
696 xmlDoc.appendChild(err);
697 var parent = document.createElement("div");
698 parent.appendChild(xmlDoc);
699 var file = [parent.innerHTML];
700 var bb = new Blob(file,{type : 'application/xml'});
701 var dnlk = window.URL.createObjectURL(bb);
702 var a = document.createElement("a");
703 a.hidden = '';
704 a.href = dnlk;
705 a.download = "save.xml";
706 a.textContent = "Save File";
707
708 popup.showPopup();
709 popup.popupContent.innerHTML = "ERROR : "+msg;
710 popup.popupContent.appendChild(a);
711 }
712
713 // Only other global function which must be defined in the interface class. Determines how to create the XML document.
714 function interfaceXMLSave(){
715 // Create the XML string to be exported with results
716 var xmlDoc = document.createElement("BrowserEvaluationResult");
717 var projectDocument = specification.projectXML;
718 projectDocument.setAttribute('file-name',url);
719 xmlDoc.appendChild(projectDocument);
720 xmlDoc.appendChild(returnDateNode());
721 xmlDoc.appendChild(interfaceContext.returnNavigator());
722 for (var i=0; i<testState.stateResults.length; i++)
723 {
724 xmlDoc.appendChild(testState.stateResults[i]);
725 }
726
727 return xmlDoc;
728 }
729
730 function AudioEngine() {
731 680
732 // Create two output paths, the main outputGain and fooGain. 681 // Create two output paths, the main outputGain and fooGain.
733 // Output gain is default to 1 and any items for playback route here 682 // Output gain is default to 1 and any items for playback route here
734 // Foo gain is used for analysis to ensure paths get processed, but are not heard 683 // Foo gain is used for analysis to ensure paths get processed, but are not heard
735 // because web audio will optimise and any route which does not go to the destination gets ignored. 684 // because web audio will optimise and any route which does not go to the destination gets ignored.
745 this.fooGain.connect(audioContext.destination); 694 this.fooGain.connect(audioContext.destination);
746 695
747 // Create the timer Object 696 // Create the timer Object
748 this.timer = new timer(); 697 this.timer = new timer();
749 // Create session metrics 698 // Create session metrics
750 this.metric = new sessionMetrics(this); 699 this.metric = new sessionMetrics(this,specification);
751 700
752 this.loopPlayback = false; 701 this.loopPlayback = false;
753 702
754 // Create store for new audioObjects 703 // Create store for new audioObjects
755 this.audioObjects = []; 704 this.audioObjects = [];
1082 this.updateTestTime(); 1031 this.updateTestTime();
1083 return this.testDuration; 1032 return this.testDuration;
1084 }; 1033 };
1085 } 1034 }
1086 1035
1087 function sessionMetrics(engine) 1036 function sessionMetrics(engine,specification)
1088 { 1037 {
1089 /* Used by audioEngine to link to audioObjects to minimise the timer call timers; 1038 /* Used by audioEngine to link to audioObjects to minimise the timer call timers;
1090 */ 1039 */
1091 this.engine = engine; 1040 this.engine = engine;
1092 this.lastClicked = -1; 1041 this.lastClicked = -1;
1093 this.data = -1; 1042 this.data = -1;
1094 this.reset = function() { 1043 this.reset = function() {
1095 this.lastClicked = -1; 1044 this.lastClicked = -1;
1096 this.data = -1; 1045 this.data = -1;
1097 }; 1046 };
1047
1048 this.enableElementInitialPosition = false;
1049 this.enableElementListenTracker = false;
1050 this.enableElementTimer = false;
1051 this.enableElementTracker = false;
1052 this.enableFlagListenedTo = false;
1053 this.enableFlagMoved = false;
1054 this.enableTestTimer = false;
1055 // Obtain the metrics enabled
1056 for (var i=0; i<specification.metrics.length; i++)
1057 {
1058 var node = specification.metrics[i];
1059 switch(node.enabled)
1060 {
1061 case 'testTimer':
1062 this.enableTestTimer = true;
1063 break;
1064 case 'elementTimer':
1065 this.enableElementTimer = true;
1066 break;
1067 case 'elementTracker':
1068 this.enableElementTracker = true;
1069 break;
1070 case 'elementListenTracker':
1071 this.enableElementListenTracker = true;
1072 break;
1073 case 'elementInitialPosition':
1074 this.enableElementInitialPosition = true;
1075 break;
1076 case 'elementFlagListenedTo':
1077 this.enableFlagListenedTo = true;
1078 break;
1079 case 'elementFlagMoved':
1080 this.enableFlagMoved = true;
1081 break;
1082 case 'elementFlagComments':
1083 this.enableFlagComments = true;
1084 break;
1085 }
1086 }
1098 this.initialiseTest = function(){}; 1087 this.initialiseTest = function(){};
1099 } 1088 }
1100 1089
1101 function metricTracker(caller) 1090 function metricTracker(caller)
1102 { 1091 {
1302 time.setAttributeNode(minute); 1291 time.setAttributeNode(minute);
1303 time.setAttributeNode(secs); 1292 time.setAttributeNode(secs);
1304 1293
1305 hold.appendChild(date); 1294 hold.appendChild(date);
1306 hold.appendChild(time); 1295 hold.appendChild(time);
1307 return hold 1296 return hold;
1308 1297
1309 } 1298 }
1310 1299
1311 function Specification() { 1300 function Specification() {
1312 // Handles the decoding of the project specification XML into a simple JavaScript Object. 1301 // Handles the decoding of the project specification XML into a simple JavaScript Object.