changeset 1249:ba6b9e1aaef5

Merge
author Nicholas Jillings <n.g.r.jillings@se14.qmul.ac.uk>
date Thu, 10 Mar 2016 17:08:10 +0000
parents fbdfdcc07f3a (diff) f04d8ababef7 (current diff)
children 31d2390b25a6
files CITING.txt README.txt core.js docs/AESPosterComp/AESposter.tex docs/AESPosterComp/img/APE.png docs/AESPosterComp/img/MUSHRA.png docs/AESPosterComp/sections/analysis.tex docs/AESPosterComp/sections/footer.tex docs/AESPosterComp/sections/header.tex docs/AESPosterComp/sections/infrastructure.tex docs/AESPosterComp/sections/interfaces.tex docs/AESPosterComp/sections/intro.tex docs/AESPosterComp/sections/testcreate.tex docs/WAC2016/WAC2016.bib docs/WAC2016/WAC2016.pdf docs/WAC2016/WAC2016.tex docs/WAC2016/sig-alternate.cls docs/WAC2016/waccopyright.sty example_eval/AB_example.xml example_eval/mushra_example.xml example_eval/project.xml interfaces/AB.js interfaces/ape.css interfaces/ape.js interfaces/discrete.js interfaces/horizontal-sliders.js interfaces/mushra.js keygen.php pythonServer.py save.php test-schema.xsd test_create/style.css test_create/test_create.html
diffstat 12 files changed, 224 insertions(+), 54 deletions(-) [+]
line wrap: on
line diff
--- a/core.js	Wed Mar 09 15:00:19 2016 +0000
+++ b/core.js	Thu Mar 10 17:08:10 2016 +0000
@@ -220,12 +220,51 @@
         
     } else if (responseDocument.children[0].nodeName == "waetresult") {
         // document is a result
-        projectXML = responseDocument.getElementsByTagName('waet')[0];
+        projectXML = document.implementation.createDocument(null,"waet");
+        projectXML.children[0].appendChild(responseDocument.getElementsByTagName('waet')[0].getElementsByTagName("setup")[0].cloneNode(true));
+        var child = responseDocument.children[0].children[0];
+        while (child != null) {
+            if (child.nodeName == "survey") {
+                // One of the global survey elements
+                if (child.getAttribute("state") == "complete") {
+                    // We need to remove this survey from <setup>
+                    var location = child.getAttribute("location");
+                    var globalSurveys = projectXML.getElementsByTagName("setup")[0].getElementsByTagName("survey")[0];
+                    while(globalSurveys != null) {
+                        if (location == "pre" || location == "before") {
+                            if (globalSurveys.getAttribute("location") == "pre" || globalSurveys.getAttribute("location") == "before") {
+                                projectXML.getElementsByTagName("setup")[0].removeChild(globalSurveys);
+                                break;
+                            }
+                        } else {
+                            if (globalSurveys.getAttribute("location") == "post" || globalSurveys.getAttribute("location") == "after") {
+                                projectXML.getElementsByTagName("setup")[0].removeChild(globalSurveys);
+                                break;
+                            }
+                        }
+                        globalSurveys = globalSurveys.nextElementSibling;
+                    }
+                } else {
+                    // We need to complete this, so it must be regenerated by store
+                    var copy = child;
+                    child = child.previousElementSibling;
+                    responseDocument.children[0].removeChild(copy);
+                }
+            } else if (child.nodeName == "page") {
+                if (child.getAttribute("state") == "empty") {
+                    // We need to complete this page
+                    projectXML.children[0].appendChild(responseDocument.getElementById(child.getAttribute("ref")).cloneNode(true));
+                    var copy = child;
+                    child = child.previousElementSibling;
+                    responseDocument.children[0].removeChild(copy);
+                }
+            }
+            child = child.nextElementSibling;
+        }
         // Build the specification
 	    specification.decode(projectXML);
-        // Use the session-key
-        var sessionKey = responseDocument.children[0].getAttribute(key);
-        storage.initialise(sessionKey);
+        // Use the original
+        storage.initialise(responseDocument);
     }
 	/// CHECK FOR SAMPLE RATE COMPATIBILITY
 	if (specification.sampleRate != undefined) {
@@ -334,16 +373,17 @@
 		popup.popupContent.appendChild(a);
 	} else {
 		var xmlhttp = new XMLHttpRequest;
-		xmlhttp.open("POST",destURL,true);
+		xmlhttp.open("POST","\save.php?key="+storage.SessionKey.key,true);
 		xmlhttp.setRequestHeader('Content-Type', 'text/xml');
 		xmlhttp.onerror = function(){
 			console.log('Error saving file to server! Presenting download locally');
-			createProjectSave(null);
+			createProjectSave("local");
 		};
 		xmlhttp.onload = function() {
             console.log(xmlhttp);
             if (this.status >= 300) {
                 console.log("WARNING - Could not update at this time");
+                createProjectSave("local");
             } else {
                 var parser = new DOMParser();
                 var xmlDoc = parser.parseFromString(xmlhttp.responseText, "application/xml");
@@ -716,6 +756,7 @@
 			{
 				this.store.postResult(node);
 			}
+            this.store.complete();
 			advanceState();
 		}
 	};
@@ -922,6 +963,7 @@
 			element.exportXMLDOM(storePoint);
 		}
 		pageXMLSave(storePoint.XMLDOM, this.currentStateMap);
+        storePoint.complete();
 	};
 }
 
@@ -1175,7 +1217,7 @@
 		var aeNodes = this.pageStore.XMLDOM.getElementsByTagName('audioelement');
 		for (var i=0; i<aeNodes.length; i++)
 		{
-			if(aeNodes[i].id == element.id)
+			if(aeNodes[i].getAttribute("ref") == element.id)
 			{
 				this.audioObjects[audioObjectId].storeDOM = aeNodes[i];
 				break;
@@ -2138,6 +2180,7 @@
 		this.showElementComments = undefined;
 		this.outsideReference = null;
 		this.loudness = null;
+        this.label = null;
 		this.preTest = null;
 		this.postTest = null;
 		this.interfaces = [];
@@ -3002,8 +3045,8 @@
 			if (passed == false)
 			{
 				check_pass = false;
-				console.log("Continue listening to track-"+audioEngineContext.audioObjects.interfaceDOM.getPresentedId());
-				error_obj.push(audioEngineContext.audioObjects.interfaceDOM.getPresentedId());
+				console.log("Continue listening to track-"+object.interfaceDOM.getPresentedId());
+				error_obj.push(object.interfaceDOM.getPresentedId());
 			}
 		}
 		if (check_pass == false)
@@ -3102,25 +3145,29 @@
 	this.globalPreTest = null;
 	this.globalPostTest = null;
 	this.testPages = [];
-	this.document = document.implementation.createDocument(null,"waetresult");
-	this.root = this.document.childNodes[0];
+	this.document = null;
+	this.root = null;
 	this.state = 0;
 	
-	this.initialise = function(sessionKey)
+	this.initialise = function(existingStore)
 	{
-        if (sessionKey == undefined) {
+        if (existingStore == undefined) {
             // We need to get the sessionKey
             this.SessionKey.generateKey();
+            this.document = document.implementation.createDocument(null,"waetresult");
+            this.root = this.document.childNodes[0];
             var projectDocument = specification.projectXML;
             projectDocument.setAttribute('file-name',url);
             this.root.appendChild(projectDocument);
             this.root.appendChild(returnDateNode());
             this.root.appendChild(interfaceContext.returnNavigator());
         } else {
-            this.SessionKey.key = sessionKey;
+            this.document = existingStore;
+            this.root = existingStore.children[0];
+            this.SessionKey.key = this.root.getAttribute("key");
         }
         if (specification.preTest != undefined){this.globalPreTest = new this.surveyNode(this,this.root,specification.preTest);}
-		if (specification.postTest != undefined){this.globalPostTest = new this.surveyNode(this,this.root,specification.postTest);}
+        if (specification.postTest != undefined){this.globalPostTest = new this.surveyNode(this,this.root,specification.postTest);}
 	};
     
     this.SessionKey = {
@@ -3186,14 +3233,16 @@
 	{
 		this.specification = specification;
 		this.parent = parent;
+        this.state = "empty";
 		this.XMLDOM = this.parent.document.createElement('survey');
 		this.XMLDOM.setAttribute('location',this.specification.location);
+        this.XMLDOM.setAttribute("state",this.state);
 		for (var optNode of this.specification.options)
 		{
 			if (optNode.type != 'statement')
 			{
 				var node = this.parent.document.createElement('surveyresult');
-				node.id = optNode.id;
+				node.setAttribute("ref",optNode.id);
 				node.setAttribute('type',optNode.type);
 				this.XMLDOM.appendChild(node);
 			}
@@ -3205,7 +3254,14 @@
 			// From popup: node is the popupOption node containing both spec. and results
 			// ID is the position
 			if (node.specification.type == 'statement'){return;}
-			var surveyresult = this.parent.document.getElementById(node.specification.id);
+			var surveyresult = this.XMLDOM.children[0];
+            while(surveyresult != null) {
+                if (surveyresult.getAttribute("ref") == node.specification.id)
+                {
+                    break;
+                }
+                surveyresult = surveyresult.nextElementSibling;
+            }
 			switch(node.specification.type)
 			{
 			case "number":
@@ -3231,6 +3287,10 @@
 				break;
 			}
 		};
+        this.complete = function() {
+            this.state = "complete";
+            this.XMLDOM.setAttribute("state",this.state);
+        }
 	};
 	
 	this.pageNode = function(parent,specification)
@@ -3238,9 +3298,11 @@
 		// Create one store per test page
 		this.specification = specification;
 		this.parent = parent;
+        this.state = "empty";
 		this.XMLDOM = this.parent.document.createElement('page');
-		this.XMLDOM.setAttribute('id',specification.id);
+		this.XMLDOM.setAttribute('ref',specification.id);
 		this.XMLDOM.setAttribute('presentedId',specification.presentedId);
+        this.XMLDOM.setAttribute("state",this.state);
 		if (specification.preTest != undefined){this.preTest = new this.parent.surveyNode(this.parent,this.XMLDOM,this.specification.preTest);}
 		if (specification.postTest != undefined){this.postTest = new this.parent.surveyNode(this.parent,this.XMLDOM,this.specification.postTest);}
 		
@@ -3252,7 +3314,8 @@
 		for (var element of this.specification.audioElements)
 		{
 			var aeNode = this.parent.document.createElement('audioelement');
-			aeNode.id = element.id;
+			aeNode.setAttribute('ref',element.id);
+            if (element.name != undefined){aeNode.setAttribute('name',element.name)};
 			aeNode.setAttribute('type',element.type);
 			aeNode.setAttribute('url', element.url);
 			aeNode.setAttribute('gain', element.gain);
@@ -3269,6 +3332,11 @@
 		}
 		
 		this.parent.root.appendChild(this.XMLDOM);
+        
+        this.complete = function() {
+            this.state = "complete";
+            this.XMLDOM.setAttribute("state","complete");
+        }
 	};
     this.update = function() {
         this.SessionKey.update();
--- a/example_eval/AB_example.xml	Wed Mar 09 15:00:19 2016 +0000
+++ b/example_eval/AB_example.xml	Thu Mar 10 17:08:10 2016 +0000
@@ -13,7 +13,7 @@
 				<option name="hwdesigner">Designing or building audio hardware</option>
 				<option name="researcher">Research in the field of audio</option>
 			</surveyentry>
-			<surveyentry type="statement">
+			<surveyentry type="statement" id="test-intro">
 				<statement>This is an example of an 'AB'-style test, with two pages, using the test stimuli in 'example_eval/'.</statement>
 			</surveyentry>
 		</survey>
@@ -31,7 +31,7 @@
 				<option name="good">Good</option>
 				<option name="great">Great</option>
 			</surveyentry>
-			<surveyentry type="statement">
+			<surveyentry type="statement" id="test-thank-you">
 				<statement>Thank you for taking this listening test. Please click 'submit' and your results will appear in the 'saves/' folder.</statement>
 			</surveyentry>
 		</survey>
@@ -60,7 +60,7 @@
 		<audioelement url="0.wav" id="track-0"/>
 		<audioelement url="1.wav" id="track-1"/>
 		<survey location="before">
-			<surveyentry type="statement">
+			<surveyentry type="statement" id="test-0-intro">
 				<statement>A two way comparison using randomised element order, automatic loudness and synchronised looping.</statement>
 			</surveyentry>
 		</survey>
@@ -83,7 +83,7 @@
 		<audioelement url="5.wav" id="track-7"/>
 		<audioelement url="6.wav" id="track-8"/>
 		<survey location="before">
-			<surveyentry type="statement">
+			<surveyentry type="statement" id="test-1-intro">
 				<statement>A 7 way comparison using randomised element order and synchronised looping.</statement>
 			</surveyentry>
 		</survey>
--- a/example_eval/mushra_example.xml	Wed Mar 09 15:00:19 2016 +0000
+++ b/example_eval/mushra_example.xml	Thu Mar 10 17:08:10 2016 +0000
@@ -13,7 +13,7 @@
 				<option name="hwdesigner">Designing or building audio hardware</option>
 				<option name="researcher">Research in the field of audio</option>
 			</surveyentry>
-			<surveyentry type="statement">
+			<surveyentry type="statement" id="test-intro">
 				<statement>This is an example of an 'APE'-style test, with two pages, using the test stimuli in 'example_eval/'.</statement>
 			</surveyentry>
 		</survey>
@@ -31,7 +31,7 @@
 				<option name="good">Good</option>
 				<option name="great">Great</option>
 			</surveyentry>
-			<surveyentry type="statement">
+			<surveyentry type="statement" id="thankyou">
 				<statement>Thank you for taking this listening test. Please click 'submit' and your results will appear in the 'saves/' folder.</statement>
 			</surveyentry>
 		</survey>
@@ -71,7 +71,7 @@
 		<audioelement url="3.wav" id="track-3"/>
 		<audioelement url="4.wav" id="track-4"/>
 		<survey location="before">
-			<surveyentry type="statement">
+			<surveyentry type="statement" id="test-0-intro">
 				<statement>Example of a 'MUSHRA' style interface with hidden anchor 'zero' (which needs to be below 20%), looping of the samples, randomisation of marker labels, mandatory moving of every sample, and a forced scale usage of at least 25%-75% using a Comparison Category Rating Scale.</statement>
 			</surveyentry>
 		</survey>
@@ -118,7 +118,7 @@
 			<option name="heavy">Heavy</option>
 		</commentquestion>
 		<survey location="before">
-			<surveyentry type="statement">
+			<surveyentry type="statement" id="test-1-intro">
 				<statement>Example of a 'MUSHRA' style interface with hidden anchor 'zero' (which needs to be below 20%), looping of the samples, randomisation of marker labels, mandatory moving of every sample, and a forced scale usage of at least 25%-75%.</statement>
 			</surveyentry>
 		</survey>
--- a/example_eval/project.xml	Wed Mar 09 15:00:19 2016 +0000
+++ b/example_eval/project.xml	Thu Mar 10 17:08:10 2016 +0000
@@ -13,7 +13,7 @@
 				<option name="hwdesigner">Designing or building audio hardware</option>
 				<option name="researcher">Research in the field of audio</option>
 			</surveyentry>
-			<surveyentry type="statement">
+			<surveyentry type="statement" id="test-intro">
 				<statement>This is an example of an 'APE'-style test, with two pages, using the test stimuli in 'example_eval/'.</statement>
 			</surveyentry>
 		</survey>
@@ -31,7 +31,7 @@
 				<option name="good">Good</option>
 				<option name="great">Great</option>
 			</surveyentry>
-			<surveyentry type="statement">
+			<surveyentry type="statement" id="thankyou">
 				<statement>Thank you for taking this listening test. Please click 'submit' and your results will appear in the 'saves/' folder.</statement>
 			</surveyentry>
 		</survey>
@@ -76,7 +76,7 @@
 		<audioelement url="3.wav" id="track-3"/>
 		<audioelement url="4.wav" id="track-4"/>
 		<survey location="before">
-			<surveyentry type="statement">
+			<surveyentry type="statement" id="test-0-intro">
 				<statement>Example of an 'APE' style interface with hidden anchor 'zero' (which needs to be below 20%), looping of the samples, randomisation of marker labels, mandatory moving of every sample, and a forced scale usage of at least 25%-75%.</statement>
 			</surveyentry>
 		</survey>
@@ -86,7 +86,7 @@
 			</surveyentry>
 		</survey>
 	</page>
-	<page id='test-1' hostURL="example_eval/" randomiseOrder='true' repeatCount='0' loop='false' showElementComments='true'>
+	<page id='test-1' hostURL="example_eval/" randomiseOrder='true' repeatCount='0' loop='false' showElementComments='true' label="none">
 		<commentboxprefix>Comment on fragment</commentboxprefix>
 		<interface name="preference">
 			<title>Example Test Question</title>
@@ -123,7 +123,7 @@
 			<option name="heavy">Heavy</option>
 		</commentquestion>
 		<survey location="before">
-			<surveyentry type="statement">
+			<surveyentry type="statement" id="test-1-intro">
 				<statement>Example of an 'APE' style interface with hidden anchor 'zero' (which needs to be below 20%), looping of the samples, randomisation of marker labels, mandatory moving of every sample, and a forced scale usage of at least 25%-75%.</statement>
 			</surveyentry>
 		</survey>
--- a/interfaces/AB.js	Wed Mar 09 15:00:19 2016 +0000
+++ b/interfaces/AB.js	Thu Mar 10 17:08:10 2016 +0000
@@ -308,7 +308,22 @@
 			console.log("WARNING - AB cannot have fixed reference");
 		}
 		var audioObject = audioEngineContext.newTrack(element);
-		var node = new this.comparatorBox(audioObject,index,String.fromCharCode(65 + index));
+        var label;
+        switch(audioObject.specification.parent.label) {
+            case "none":
+                label = "";
+                break;
+            case "number":
+                label = ""+index;
+                break;
+            case "letter":
+                label = String.fromCharCode(97 + index);
+                break;
+            default:
+                label = String.fromCharCode(65 + index);
+                break;
+        }
+		var node = new this.comparatorBox(audioObject,index,label);
 		audioObject.bindInterface(node);
 		this.comparators.push(node);
 		this.boxHolders.appendChild(node.box);
--- a/interfaces/ape.css	Wed Mar 09 15:00:19 2016 +0000
+++ b/interfaces/ape.css	Thu Mar 10 17:08:10 2016 +0000
@@ -64,6 +64,7 @@
 	-moz-user-select: -moz-none;
 	-khtml-user-select: none;
 	-webkit-user-select: none;
+    border: 1px solid black;
 }
 
 div.outside-reference {
--- a/interfaces/ape.js	Wed Mar 09 15:00:19 2016 +0000
+++ b/interfaces/ape.js	Thu Mar 10 17:08:10 2016 +0000
@@ -239,7 +239,7 @@
 	};
 	// Create Submit (save) button
 	var submit = document.createElement("button");
-	submit.innerHTML = 'Submit';
+	submit.innerHTML = 'Next';
 	submit.onclick = buttonSubmitClick;
 	submit.id = 'submit-button';
 	// Append the interface buttons into the interfaceButtons object.
@@ -535,7 +535,22 @@
 		this.canvas.appendChild(trackObj);
 		this.sliders.push(trackObj);
 		this.metrics.push(new metricTracker(this));
-		trackObj.innerHTML = '<span>'+(this.metrics.length-1)+'</span>';
+		var label = document.createElement("label");
+        switch(audioObject.specification.parent.label) {
+            case "letter":
+                label.textContent = String.fromCharCode(97 + this.metrics.length-1);
+                break;
+            case "capital":
+                label.textContent = String.fromCharCode(65 + this.metrics.length-1);
+                break;
+            case "none":
+                label.textContent = "";
+                break;
+            default:
+                label.textContent = ""+this.metrics.length;
+                break;
+        }
+        trackObj.appendChild(label);
 		this.metrics[this.metrics.length-1].initialise(convSliderPosToRate(trackObj));
 		return trackObj;
 	};
--- a/interfaces/discrete.js	Wed Mar 09 15:00:19 2016 +0000
+++ b/interfaces/discrete.js	Thu Mar 10 17:08:10 2016 +0000
@@ -55,7 +55,7 @@
 	};
 	// Create Submit (save) button
 	var submit = document.createElement("button");
-	submit.innerHTML = 'Submit';
+	submit.innerHTML = 'Next';
 	submit.onclick = buttonSubmitClick;
 	submit.id = 'submit-button';
 	submit.style.float = 'left';
@@ -184,7 +184,7 @@
 	});
 	
 	// Find all the audioElements from the audioHolder
-	var label = 0;
+	var index = 0;
 	var interfaceScales = testState.currentStateMap.interfaces[0].scales;
 	$(page.audioElements).each(function(index,element){
 		// Find URL of track
@@ -198,11 +198,25 @@
 			audioObject.bindInterface(orNode);
 		} else {
 			// Create a slider per track
+            switch(audioObject.specification.parent.label) {
+                case "none":
+                    label = "";
+                    break;
+                case "letter":
+                    label = String.fromCharCode(97 + index);
+                    break;
+                case "capital":
+                    label = String.fromCharCode(65 + index);
+                    break;
+                default:
+                    label = ""+index;
+                    break;
+            }
 			var sliderObj = new discreteObject(audioObject,label,interfaceScales);
 			sliderBox.appendChild(sliderObj.holder);
 			audioObject.bindInterface(sliderObj);
             interfaceContext.commentBoxes.createCommentBox(audioObject);
-			label += 1;
+			index += 1;
 		}
         
 	});
--- a/interfaces/horizontal-sliders.js	Wed Mar 09 15:00:19 2016 +0000
+++ b/interfaces/horizontal-sliders.js	Thu Mar 10 17:08:10 2016 +0000
@@ -55,7 +55,7 @@
 	};
 	// Create Submit (save) button
 	var submit = document.createElement("button");
-	submit.innerHTML = 'Submit';
+	submit.innerHTML = 'Next';
 	submit.onclick = buttonSubmitClick;
 	submit.id = 'submit-button';
 	submit.style.float = 'left';
@@ -185,7 +185,7 @@
 	});
 	
 	// Find all the audioElements from the audioHolder
-	var label = 0;
+	var index = 0;
 	$(page.audioElements).each(function(index,element){
 		// Find URL of track
 		// In this jQuery loop, variable 'this' holds the current audioElement.
@@ -198,6 +198,20 @@
 			audioObject.bindInterface(orNode);
 		} else {
 			// Create a slider per track
+            switch(audioObject.specification.parent.label) {
+                case "none":
+                    label = "";
+                    break;
+                case "letter":
+                    label = String.fromCharCode(97 + index);
+                    break;
+                case "capital":
+                    label = String.fromCharCode(65 + index);
+                    break;
+                default:
+                    label = ""+index;
+                    break;
+            }
 			var sliderObj = new sliderObject(audioObject,label);
 			
 			if (typeof page.initialPosition === "number")
@@ -211,7 +225,7 @@
 			sliderBox.appendChild(sliderObj.holder);
 			audioObject.bindInterface(sliderObj);
             interfaceContext.commentBoxes.createCommentBox(audioObject);
-			label += 1;
+			index += 1;
 		}
         
 	});
--- a/interfaces/mushra.js	Wed Mar 09 15:00:19 2016 +0000
+++ b/interfaces/mushra.js	Thu Mar 10 17:08:10 2016 +0000
@@ -61,7 +61,7 @@
 	};
 	// Create Submit (save) button
 	var submit = document.createElement("button");
-	submit.innerHTML = 'Submit';
+	submit.innerHTML = 'Next';
 	submit.onclick = buttonSubmitClick;
 	submit.id = 'submit-button';
 	submit.style.float = 'left';
@@ -183,7 +183,7 @@
 	currentTestHolder.repeatCount = audioHolderObject.repeatCount;
 	
 	// Find all the audioElements from the audioHolder
-	var label = 0;
+	var index = 0;
 	$(audioHolderObject.audioElements).each(function(index,element){
 		// Find URL of track
 		// In this jQuery loop, variable 'this' holds the current audioElement.
@@ -196,6 +196,20 @@
 			audioObject.bindInterface(orNode);
 		} else {
 			// Create a slider per track
+            switch(audioObject.specification.parent.label) {
+                case "none":
+                    label = "";
+                    break;
+                case "letter":
+                    label = String.fromCharCode(97 + index);
+                    break;
+                case "capital":
+                    label = String.fromCharCode(65 + index);
+                    break;
+                default:
+                    label = ""+index;
+                    break;
+            }
 			var sliderObj = new sliderObject(audioObject,label);
 			
 			if (typeof audioHolderObject.initialPosition === "number")
@@ -209,7 +223,7 @@
 			sliderBox.appendChild(sliderObj.holder);
 			audioObject.bindInterface(sliderObj);
             interfaceContext.commentBoxes.createCommentBox(audioObject);
-			label += 1;
+			index += 1;
 		}
         
 	});
--- a/save.php	Wed Mar 09 15:00:19 2016 +0000
+++ b/save.php	Thu Mar 10 17:08:10 2016 +0000
@@ -1,11 +1,5 @@
 <?php
 	error_reporting(0);
-    try{
-        date_default_timezone_get();
-    }
-    catch(Exception $e){
-        date_default_timezone_set('UTC'); // Sets to UTC if not specified anywhere in .ini
-    }
 	header('Access-Control-Allow-Origin: *');
 	header("Content-type: text/xml");
 	$postText = file_get_contents('php://input');
@@ -23,13 +17,13 @@
 	if ($wbytes === FALSE)
 	{
 		// FileWrite failed
-		$xml = '<response state="error"><message>Could not write file "saves/'.$xmlfile.'"</message></response>';
+		$xml = '<response state="error"><message>Could not write file "'.$filename.'"</message></response>';
 		echo $xml;
 		return;
 	}
 	fclose($fileHandle);
 	
-	// Return JSON confirmation data
-	$xml = '<response state="OK"><message>OK</message><file bytes="'.$wbytes.'">"saves/'.$xmlfile.'"</file></response>';
+	// Return XML confirmation data
+	$xml = '<response state="OK"><message>OK</message><file bytes="'.$wbytes.'">"'.$filename.'"</file></response>';
 	echo $xml;
-?>
+?>
\ No newline at end of file
--- a/test-schema.xsd	Wed Mar 09 15:00:19 2016 +0000
+++ b/test-schema.xsd	Thu Mar 10 17:08:10 2016 +0000
@@ -9,6 +9,21 @@
   <xs:attribute name="id" type="xs:ID"/>
   <xs:attribute name="mandatory" type="xs:boolean"/>
   <xs:attribute name="name" type="xs:string"/>
+
+  <xs:attribute name="preSilence" default="0">
+      <xs:simpleType>
+          <xs:restriction base="xs:decimal">
+              <xs:minInclusive value="0.0"/>
+          </xs:restriction>
+      </xs:simpleType>
+  </xs:attribute>
+  <xs:attribute name="postSilence" default="0">
+      <xs:simpleType>
+          <xs:restriction base="xs:decimal">
+              <xs:minInclusive value="0.0"/>
+          </xs:restriction>
+      </xs:simpleType>
+  </xs:attribute>
   
   <!-- define complex elements-->
   <xs:element name="waet">
@@ -23,6 +38,7 @@
   <xs:element name="setup">
     <xs:complexType>
       <xs:sequence>
+        <xs:element name="exitText" type="xs:string" minOccurs="0" maxOccurs="1"/>
         <xs:element ref="survey" minOccurs="0" maxOccurs="2"/>
         <xs:element ref="metric" maxOccurs="1"/>
         <xs:element ref="interface" maxOccurs="1"/>
@@ -33,6 +49,9 @@
       <xs:attribute name="testPages" type="xs:nonNegativeInteger" default="0"/>
       <xs:attribute name="loudness" type="xs:nonPositiveInteger" use="optional"/>
       <xs:attribute name="sampleRate" type="xs:positiveInteger" use="optional"/>
+      <xs:attribute name="calibration" type="xs:boolean" default="false"/>
+      <xs:attribute ref="preSilence"/>
+      <xs:attribute ref="postSilence"/>
     </xs:complexType>
   </xs:element>
 
@@ -51,8 +70,22 @@
       <xs:attribute name="randomiseOrder" type="xs:boolean" default="false"/>
       <xs:attribute name="repeatCount" type="xs:nonNegativeInteger" default="0"/>
       <xs:attribute name="loop" type="xs:boolean" default="false"/>
+      <xs:attribute name="synchronous" type="xs:boolean" default="false"/>
       <xs:attribute name="showElementComments" type="xs:boolean" default="false"/>
       <xs:attribute name="loudness" type="xs:nonPositiveInteger" use="optional"/>
+      <xs:attribute name="label" use="optional" default="default">
+          <xs:simpleType>
+              <xs:restriction base="xs:string">
+                  <xs:enumeration value="default"/>
+                  <xs:enumeration value="none"/>
+                  <xs:enumeration value="number"/>
+                  <xs:enumeration value="letter"/>
+                  <xs:enumeration value="capital"/>
+              </xs:restriction>
+          </xs:simpleType>
+      </xs:attribute>
+      <xs:attribute ref="preSilence"/>
+      <xs:attribute ref="postSilence"/>
     </xs:complexType>
   </xs:element>
 
@@ -136,6 +169,8 @@
         </xs:simpleType>
       </xs:attribute>
       <xs:attribute name="loudness" type="xs:nonPositiveInteger" use="optional"/>
+      <xs:attribute ref="preSilence"/>
+      <xs:attribute ref="postSilence"/>
     </xs:complexType>
   </xs:element>