changeset 470:1330c77d212c Dev_main

Moved interfaces into their own sub-directory
author Nicholas Jillings <n.g.r.jillings@se14.qmul.ac.uk>
date Wed, 13 Jan 2016 09:34:46 +0000
parents d39d243e6601
children 3a9b869ba7f8
files AB.css AB.js ape.css ape.js core.js interfaces/AB.css interfaces/AB.js interfaces/ape.css interfaces/ape.js interfaces/mushra.css interfaces/mushra.js mushra.css mushra.js
diffstat 13 files changed, 1939 insertions(+), 1939 deletions(-) [+]
line wrap: on
line diff
--- a/AB.css	Tue Jan 12 18:29:55 2016 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,74 +0,0 @@
-body {
-	/* Set the background colour (note US English spelling) to grey*/
-	background-color: #fff
-}
-
-div.pageTitle {
-	width: auto;
-	height: 20px;
-	margin-top: 20px;
-}
-
-div.pageTitle span{
-	font-size: 1.5em;
-}
-
-div.testHalt {
-	/* Specify any colouring during the test halt for pre/post questions */
-	background-color: rgba(0,0,0,0.5);
-	/* Don't mess with this bit */
-	z-index: 2;
-	width: 100%;
-	height: 100%;
-	position: absolute;
-	left: 0px;
-	top: 0px;
-}
-
-button {
-	/* Specify any button structure or style */
-	min-width: 20px;
-	background-color: #ddd
-}
-
-button.big-button {
-	width: 250px;
-	height: 40px;
-	font-size: 1.2em;
-}
-
-div.comparitor-holder {
-	width: 260px;
-	height: 300px;
-	border:  black 1px solid;
-    float: left;
-	padding-top: 5px;
-    margin: 25px;
-}
-
-div.comparitor-selector {
-	width: 248px;
-	height: 250px;
-	border: black 1px solid;
-	position: relative;
-	background-color: #FF0000;
-}
-
-div.disabled {
-	background-color: #AAA;
-}
-
-div.selected {
-	background-color: #008000;
-}
-
-div.comparitor-selector span {
-	font-size: 4em;
-}
-
-button.comparitor-button {
-	width: 250px;
-	height: 38px;
-	position: relative;
-	margin-top: 5px;
-}
--- a/AB.js	Tue Jan 12 18:29:55 2016 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,320 +0,0 @@
-// Once this is loaded and parsed, begin execution
-loadInterface();
-
-function loadInterface() {
-	// Get the dimensions of the screen available to the page
-	var width = window.innerWidth;
-	var height = window.innerHeight;
-	interfaceContext.insertPoint.innerHTML = null; // Clear the current schema
-	
-	// Custom Comparitor Object
-	Interface.prototype.comparitor = null;
-	
-	// The injection point into the HTML page
-	interfaceContext.insertPoint = document.getElementById("topLevelBody");
-	var testContent = document.createElement('div');
-	testContent.id = 'testContent';
-	
-	// Create the top div for the Title element
-	var titleAttr = specification.title;
-	var title = document.createElement('div');
-	title.className = "title";
-	title.align = "center";
-	var titleSpan = document.createElement('span');
-	
-	// Set title to that defined in XML, else set to default
-	if (titleAttr != undefined) {
-		titleSpan.textContent = titleAttr;
-	} else {
-		titleSpan.textContent =  'Listening test';
-	}
-	// Insert the titleSpan element into the title div element.
-	title.appendChild(titleSpan);
-	
-	var pagetitle = document.createElement('div');
-	pagetitle.className = "pageTitle";
-	pagetitle.align = "center";
-	var titleSpan = document.createElement('span');
-	titleSpan.id = "pageTitle";
-	pagetitle.appendChild(titleSpan);
-	
-	// Create Interface buttons!
-	var interfaceButtons = document.createElement('div');
-	interfaceButtons.id = 'interface-buttons';
-	interfaceButtons.style.height = '25px';
-	
-	// Create playback start/stop points
-	var playback = document.createElement("button");
-	playback.innerHTML = 'Stop';
-	playback.id = 'playback-button';
-	playback.style.float = 'left';
-	// onclick function. Check if it is playing or not, call the correct function in the
-	// audioEngine, change the button text to reflect the next state.
-	playback.onclick = function() {
-		if (audioEngineContext.status == 1) {
-			audioEngineContext.stop();
-			this.innerHTML = 'Stop';
-            var time = audioEngineContext.timer.getTestTime();
-            console.log('Stopped at ' + time); // DEBUG/SAFETY
-		}
-	};
-	// Append the interface buttons into the interfaceButtons object.
-	interfaceButtons.appendChild(playback);
-	
-	// Global parent for the comment boxes on the page
-	var feedbackHolder = document.createElement('div');
-	feedbackHolder.id = 'feedbackHolder';
-	
-	// Construct the AB Boxes
-	var boxes = document.createElement('div');
-	boxes.align = "center";
-	boxes.id = "box-holders";
-    boxes.style.float = "left";
-	
-	var submit = document.createElement('button');
-	submit.id = "submit";
-	submit.onclick = buttonSubmitClick;
-	submit.className = "big-button";
-	submit.textContent = "submit";
-	submit.style.position = "relative";
-	submit.style.left = (window.innerWidth-250)/2 + 'px';
-		
-	feedbackHolder.appendChild(boxes);
-	
-	// Inject into HTML
-	testContent.appendChild(title); // Insert the title
-	testContent.appendChild(pagetitle);
-	testContent.appendChild(interfaceButtons);
-	testContent.appendChild(feedbackHolder);
-	testContent.appendChild(submit);
-	interfaceContext.insertPoint.appendChild(testContent);
-
-	// Load the full interface
-	testState.initialise();
-	testState.advanceState();
-}
-
-function loadTest(audioHolderObject)
-{
-	var feedbackHolder = document.getElementById('feedbackHolder');
-	var interfaceObj = audioHolderObject.interfaces;
-	if (interfaceObj.length > 1)
-	{
-		console.log("WARNING - This interface only supports one <interface> node per page. Using first interface node");
-	}
-	interfaceObj = interfaceObj[0];
-	
-	if(interfaceObj.title != null)
-	{
-		document.getElementById("pageTitle").textContent = interfaceObj.title;
-	}
-	
-	// Populate the comparitor object
-	interfaceContext.comparitor = new Comparitor(audioHolderObject);
-	resizeWindow(null);
-}
-
-function Comparitor(audioHolderObject)
-{	
-	this.comparitorBox = function(audioElement,id,text)
-	{
-		this.parent = audioElement;
-		this.id = id;
-		this.value = 0;
-		this.disabled = true;
-		this.box = document.createElement('div');
-		this.box.className = 'comparitor-holder';
-		this.box.setAttribute('track-id',audioElement.id);
-		this.box.id = 'comparitor-'+text;
-		this.selector = document.createElement('div');
-		this.selector.className = 'comparitor-selector disabled';
-		var selectorText = document.createElement('span');
-		selectorText.textContent = text;
-		this.selector.appendChild(selectorText);
-		this.playback = document.createElement('button');
-		this.playback.className = 'comparitor-button';
-		this.playback.disabled = true;
-		this.playback.textContent = "Listen";
-		this.box.appendChild(this.selector);
-		this.box.appendChild(this.playback);
-		this.selector.onclick = function()
-		{
-			var time = audioEngineContext.timer.getTestTime();
-			if ($(event.currentTarget).hasClass('disabled'))
-			{
-				console.log("Please wait until sample has loaded");
-				return;
-			}
-			if (audioEngineContext.status == 0)
-			{
-				alert("Please listen to the samples before making a selection");
-				console.log("Please listen to the samples before making a selection");
-				return;
-			}
-			$(".comparitor-selector").removeClass('selected');
-			var id = event.currentTarget.parentElement.getAttribute('track-id');
-			interfaceContext.comparitor.selected = id;
-			$(event.currentTarget).addClass('selected');
-			for (var i=0; i<interfaceContext.comparitor.comparitors.length; i++)
-			{
-				var obj = interfaceContext.comparitor.comparitors[i];
-				if (i == id) {
-					obj.value = 1;
-				} else {
-					obj.value = 0;
-				}
-				obj.parent.metric.moved(time,obj.value);
-			}
-			console.log("Selected "+id+' ('+time+')');
-		};
-		this.playback.onclick = function()
-		{
-			$('.comparitor-button').text('Listen');
-			var id = event.currentTarget.parentElement.getAttribute('track-id');
-			audioEngineContext.play(id);
-			$(event.currentTarget).text('Playing');
-		};
-		
-		this.enable = function()
-		{
-			if (this.parent.state == 1)
-			{
-				$(this.selector).removeClass('disabled');
-				this.playback.disabled = false;
-			}
-		};
-		this.updateLoading = function(progress)
-		{
-			if (progress != 100)
-			{
-				progress = String(progress);
-				progress = progress.split('.')[0];
-				this.playback.textContent = progress+'%';
-			} else {
-				this.playback.textContent = "Listen";
-			}
-		};
-		this.exportXMLDOM = function(audioObject)
-		{
-			var node = storage.document.createElement('value');
-			node.textContent = this.value;
-			return node;
-		};
-		this.getValue = function() {
-			return this.value;	
-		};
-		this.getPresentedId = function()
-		{
-			return this.selector.children[0].textContent;
-		};
-		this.canMove = function()
-		{
-			return false;
-		};
-	};
-	
-	this.boxHolders = document.getElementById('box-holders');
-	this.boxHolders.innerHTML = null;
-	this.comparitors = [];
-	this.selected = null;
-	
-	// First generate the Audio Objects for the Audio Engine
-	for (var index=0; index<audioHolderObject.audioElements.length; index++)
-	{
-		var element = audioHolderObject.audioElements[index];
-		if (index == audioHolderObject.outsideReference || element.type == 'outside-reference')
-		{
-			console.log("WARNING - AB cannot have fixed reference");
-		}
-		var audioObject = audioEngineContext.newTrack(element);
-		var node = new this.comparitorBox(audioObject,index,String.fromCharCode(65 + index));
-		audioObject.bindInterface(node);
-		this.comparitors.push(node);
-		this.boxHolders.appendChild(node.box);
-	}
-	return this;
-}
-
-function resizeWindow(event)
-{
-	document.getElementById('submit').style.left = (window.innerWidth-250)/2 + 'px';
-	var numObj = interfaceContext.comparitor.comparitors.length;
-	var boxW = numObj*312;
-    var diff = window.innerWidth - boxW;
-    while (diff < 0)
-    {
-        numObj = Math.ceil(numObj/2);
-        boxW = numObj*312;
-        diff = window.innerWidth - boxW;
-    }
-    document.getElementById('box-holders').style.marginLeft = diff/2 + 'px';
-    document.getElementById('box-holders').style.marginRight = diff/2 + 'px';
-    document.getElementById('box-holders').style.width = boxW + 'px';
-}
-
-function buttonSubmitClick()
-{
-	var checks = [];
-	checks = checks.concat(testState.currentStateMap.interfaces[0].options);
-	checks = checks.concat(specification.interfaces.options);
-	var canContinue = true;
-	
-	for (var i=0; i<checks.length; i++) {
-		if (checks[i].type == 'check')
-		{
-			switch(checks[i].name) {
-			case 'fragmentPlayed':
-				// Check if all fragments have been played
-				var checkState = interfaceContext.checkAllPlayed();
-				if (checkState == false) {canContinue = false;}
-				break;
-			case  'fragmentFullPlayback':
-				// Check all fragments have been played to their full length
-				var checkState = interfaceContext.checkFragmentsFullyPlayed();
-				if (checkState == false) {canContinue = false;}
-				break;
-			case 'fragmentMoved':
-				// Check all fragment sliders have been moved.
-				var checkState = interfaceContext.checkAllMoved();
-				if (checkState == false) {canContinue = false;}
-				break;
-			case 'fragmentComments':
-				// Check all fragment sliders have been moved.
-				var checkState = interfaceContext.checkAllCommented();
-				if (checkState == false) {canContinue = false;}
-				break;
-			default:
-				console.log("WARNING - Check option "+checks[i].check+" is not supported on this interface");
-				break;
-			}
-
-		}
-		if (!canContinue) {break;}
-	}
-	if (canContinue)
-	{
-	    if (audioEngineContext.status == 1) {
-	        var playback = document.getElementById('playback-button');
-	        playback.click();
-	    // This function is called when the submit button is clicked. Will check for any further tests to perform, or any post-test options
-	    } else
-	    {
-	        if (audioEngineContext.timer.testStarted == false)
-	        {
-	            alert('You have not started the test! Please press start to begin the test!');
-	            return;
-	        }
-	    }
-	    testState.advanceState();
-	}
-}
-
-function pageXMLSave(store, pageSpecification)
-{
-	// MANDATORY
-	// Saves a specific test page
-	// You can use this space to add any extra nodes to your XML <audioHolder> saves
-	// Get the current <page> information in store (remember to appendChild your data to it)
-	// pageSpecification is the current page node configuration
-	// To create new XML nodes, use storage.document.createElement();
-}
\ No newline at end of file
--- a/ape.css	Tue Jan 12 18:29:55 2016 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,88 +0,0 @@
-/*
- * Hold any style information for APE interface. Customise if you like to make the interface your own!
- * 
- */
-body {
-	/* Set the background colour (note US English spelling) to grey*/
-	background-color: #ddd
-}
-
-div.title {
-	/* Specify any colouring for the title */
-}
-
-div.pageTitle {
-	width: auto;
-	height: 20px;
-	margin-top: 5px;
-	margin-bottom: 10px;
-}
-
-div.pageTitle span{
-	font-size: 1.5em;
-}
-
-button {
-	/* Specify any button structure or style */
-	min-width: 20px;
-	background-color: #ddd
-}
-
-div.slider {
-	/* Specify any structure for the slider holder interface */
-	background-color: #eee;
-	height: 150px;
-	margin-bottom: 5px;
-	-moz-user-select: -moz-none;
-	-khtml-user-select: none;
-	-webkit-user-select: none;
-}
-
-div.sliderScale {
-	width: 100%;
-	min-height: 30px;
-	-moz-user-select: -moz-none;
-	-khtml-user-select: none;
-	-webkit-user-select: none;
-}
-
-div.sliderScale span {
-	/* Any formatting of text below scale */
-	font-size: 1.2em;
-	min-width: 5px;
-	height: 20px;
-	position: absolute;
-}
-
-div.track-slider {
-	/* Specify any structure for the slider objects */
-	position: absolute;
-	height: inherit;
-	width: 12px;
-	float: left;
-	background-color: rgb(100,200,100);
-	-moz-user-select: -moz-none;
-	-khtml-user-select: none;
-	-webkit-user-select: none;
-}
-
-div.outside-reference {
-	width:120px;
-	padding-left: 55px;
-	margin-left: 100px;
-	height:20px;
-	margin-bottom:5px;
-	background-color: rgb(100,200,100);
-}
-
-div.track-slider-disabled {
-	background-color: rgb(100,100,100);
-}
-
-div.track-slider-playing {
-	background-color: #FF0000;
-}
-
-div.comment-box-playing {
-	background-color: #FFDDDD;
-}
--- a/ape.js	Tue Jan 12 18:29:55 2016 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,826 +0,0 @@
-/**
- *  ape.js
- *  Create the APE interface
- */
-
-
-// Once this is loaded and parsed, begin execution
-loadInterface();
-
-function loadInterface() {
-	
-	// Get the dimensions of the screen available to the page
-	var width = window.innerWidth;
-	var height = window.innerHeight;
-	
-	// The injection point into the HTML page
-	interfaceContext.insertPoint = document.getElementById("topLevelBody");
-	var testContent = document.createElement('div');
-	
-	testContent.id = 'testContent';
-	
-	// Bindings for interfaceContext
-	interfaceContext.checkAllPlayed = function()
-	{
-		hasBeenPlayed = audioEngineContext.checkAllPlayed();
-		if (hasBeenPlayed.length > 0) // if a fragment has not been played yet
-	    {
-	    	str = "";
-	    	if (hasBeenPlayed.length > 1) {
-		    	for (var i=0; i<hasBeenPlayed.length; i++) {
-		    		str = str + hasBeenPlayed[i];
-		    		if (i < hasBeenPlayed.length-2){
-		    			str += ", ";
-		    		} else if (i == hasBeenPlayed.length-2) {
-		    			str += " or ";
-		    		}
-		    	}
-		    	alert('You have not played fragments ' + str + ' yet. Please listen, rate and comment all samples before submitting.');
-	       } else {
-	       		alert('You have not played fragment ' + hasBeenPlayed[0] + ' yet. Please listen, rate and comment all samples before submitting.');
-	       }
-	        return false;
-	    }
-	    return true;
-	};
-	
-	interfaceContext.checkAllMoved = function() {
-		var state = true;
-		var str = 'You have not moved the following sliders. ';
-		for (var i=0; i<this.interfaceSliders.length; i++)
-		{
-			var interfaceTID = [];
-			for (var j=0; j<this.interfaceSliders[i].metrics.length; j++)
-			{
-				if (this.interfaceSliders[i].metrics[j].wasMoved == false)
-				{
-					state = false;
-					interfaceTID.push(j);
-				}
-			}
-			if (interfaceTID.length != 0)
-			{
-				var interfaceName = this.interfaceSliders[i].interfaceObject.title;
-				if (interfaceName == undefined) {
-					str += 'On axis '+String(i+1)+' you must move ';
-				} else {
-					str += 'On axis "'+interfaceName+'" you must move ';
-				}
-				if (interfaceTID.length == 1)
-				{
-					str += 'slider '+interfaceTID[0]+'. ';
-				}
-				else {
-					str += 'sliders ';
-					for (var k=0; k<interfaceTID.length-1; k++)
-					{
-						str += interfaceTID[k]+', ';
-					}
-					str += interfaceTID[interfaceTID.length-1] +'. ';
-				}
-			}
-		}
-		if (state != true)
-		{
-			alert(str);
-			console.log(str);
-		}
-		return state;
-	};
-	
-	Interface.prototype.checkAllCommented = function() {
-		var audioObjs = audioEngineContext.audioObjects;
-		var audioHolder = testState.stateMap[testState.stateIndex];
-		var state = true;
-		if (audioHolder.elementComments) {
-			var strNums = [];
-			for (var i=0; i<audioObjs.length; i++)
-			{
-				if (audioObjs[i].commentDOM.trackCommentBox.value.length == 0) {
-					state = false;
-					strNums.push(i);
-				}
-			}
-			if (state == false) {
-				if (strNums.length > 1) {
-					var str = "";
-			    	for (var i=0; i<strNums.length; i++) {
-			    		str = str + strNums[i];
-			    		if (i < strNums.length-2){
-			    			str += ", ";
-			    		} else if (i == strNums.length-2) {
-			    			str += " or ";
-			    		}
-			    	}
-			    	alert('You have not commented on fragments ' + str + ' yet. Please listen, rate and comment all samples before submitting.');
-		       } else {
-		       		alert('You have not commented on fragment ' + strNums[0] + ' yet. Please listen, rate and comment all samples before submitting.');
-		       }
-			}
-		}
-		return state;
-	};
-	
-	Interface.prototype.checkScaleRange = function()
-	{
-		var audioObjs = audioEngineContext.audioObjects;
-		var audioHolder = testState.stateMap[testState.stateIndex];
-		var state = true;
-		var str = '';
-		for (var i=0; i<this.interfaceSliders.length; i++)
-		{
-			var minScale;
-			var maxScale;
-			var interfaceObject = interfaceContext.interfaceSliders[0].interfaceObject;
-			for (var j=0; j<interfaceObject.options.length; j++)
-			{
-				if (interfaceObject.options[j].check == "scalerange") {
-					minScale = interfaceObject.options[j].min;
-					maxScale = interfaceObject.options[j].max;
-					break;
-				}
-			}
-			var minRanking = convSliderPosToRate(this.interfaceSliders[i].sliders[0]);
-			var maxRanking = minRanking;
-			for (var j=1; j<this.interfaceSliders[i].sliders.length; j++)
-			{
-				var ranking = convSliderPosToRate(this.interfaceSliders[i].sliders[j]);
-				if (ranking < minRanking)
-				{
-					minRanking = ranking;
-				} else if (ranking > maxRanking)
-				{
-					maxRanking = ranking;
-				}
-			}
-			if (minRanking > minScale || maxRanking < maxScale)
-			{
-				state = false;
-				str += 'On axis "'+this.interfaceSliders[i].interfaceObject.title+'" you have not used the full width of the scale. ';
-			}
-		}
-		if (state != true)
-		{
-			alert(str);
-			console.log(str);
-		}
-		return state;
-	};
-	
-	Interface.prototype.objectSelected = null;
-	Interface.prototype.objectMoved = false;
-	Interface.prototype.selectObject = function(object)
-	{
-		if (this.objectSelected == null)
-		{
-			this.objectSelected = object;
-			this.objectMoved = false;
-		}
-	};
-	Interface.prototype.moveObject = function()
-	{
-		if (this.objectMoved == false)
-		{
-			this.objectMoved = true;
-		}
-	};
-	Interface.prototype.releaseObject = function()
-	{
-		this.objectSelected = null;
-		this.objectMoved = false;
-	};
-	Interface.prototype.getSelectedObject = function()
-	{
-		return this.objectSelected;
-	};
-	Interface.prototype.hasSelectedObjectMoved = function()
-	{
-		return this.objectMoved;
-	};
-	
-	// Bindings for slider interfaces
-	Interface.prototype.interfaceSliders = [];
-	
-	// Bindings for audioObjects
-	
-	// Create the top div for the Title element
-	var titleAttr = specification.title;
-	var title = document.createElement('div');
-	title.className = "title";
-	title.align = "center";
-	var titleSpan = document.createElement('span');
-	
-	// Set title to that defined in XML, else set to default
-	if (titleAttr != undefined) {
-		titleSpan.textContent = titleAttr;
-	} else {
-		titleSpan.textContent =  'Listening test';
-	}
-	// Insert the titleSpan element into the title div element.
-	title.appendChild(titleSpan);
-	
-	// Create Interface buttons!
-	var interfaceButtons = document.createElement('div');
-	interfaceButtons.id = 'interface-buttons';
-	
-	// Create playback start/stop points
-	var playback = document.createElement("button");
-	playback.innerHTML = 'Stop';
-	playback.id = 'playback-button';
-	// onclick function. Check if it is playing or not, call the correct function in the
-	// audioEngine, change the button text to reflect the next state.
-	playback.onclick = function() {
-		if (audioEngineContext.status == 1) {
-			audioEngineContext.stop();
-			this.innerHTML = 'Stop';
-            var time = audioEngineContext.timer.getTestTime();
-            console.log('Stopped at ' + time); // DEBUG/SAFETY
-		}
-	};
-	// Create Submit (save) button
-	var submit = document.createElement("button");
-	submit.innerHTML = 'Submit';
-	submit.onclick = buttonSubmitClick;
-	submit.id = 'submit-button';
-	// Append the interface buttons into the interfaceButtons object.
-	interfaceButtons.appendChild(playback);
-	interfaceButtons.appendChild(submit);
-	
-	var sliderHolder = document.createElement("div");
-	sliderHolder.id = "slider-holder";
-	
-	
-	// Global parent for the comment boxes on the page
-	var feedbackHolder = document.createElement('div');
-	feedbackHolder.id = 'feedbackHolder';
-	
-	testContent.style.zIndex = 1;
-	interfaceContext.insertPoint.innerHTML = null; // Clear the current schema
-	
-	// Inject into HTML
-	testContent.appendChild(title); // Insert the title
-	testContent.appendChild(interfaceButtons);
-	testContent.appendChild(sliderHolder);
-	testContent.appendChild(feedbackHolder);
-	interfaceContext.insertPoint.appendChild(testContent);
-
-	// Load the full interface
-	testState.initialise();
-	testState.advanceState();
-	
-}
-
-function loadTest(audioHolderObject)
-{
-	var width = window.innerWidth;
-	var height = window.innerHeight;
-	var id = audioHolderObject.id;
-	
-	interfaceContext.interfaceSliders = [];
-	
-	var feedbackHolder = document.getElementById('feedbackHolder');
-	var sliderHolder = document.getElementById('slider-holder');
-	feedbackHolder.innerHTML = null;
-	sliderHolder.innerHTML = null;
-	
-	// Delete outside reference
-	var outsideReferenceHolder = document.getElementById('outside-reference');
-	if (outsideReferenceHolder != null) {
-		document.getElementById('interface-buttons').removeChild(outsideReferenceHolder);
-	}
-	
-	var interfaceObj = audioHolderObject.interfaces;
-	for (var k=0; k<interfaceObj.length; k++) {
-		// Create the div box to center align
-		interfaceContext.interfaceSliders.push(new interfaceSliderHolder(interfaceObj[k]));
-		for (var i=0; i<interfaceObj[k].options.length; i++)
-		{
-			if (interfaceObj[k].options[i].type == 'option' && interfaceObj[k].options[i].name == 'playhead')
-			{
-				var playbackHolder = document.getElementById('playback-holder');
-				if (playbackHolder == null)
-				{
-					playbackHolder = document.createElement('div');
-					playbackHolder.style.width = "100%";
-					playbackHolder.align = 'center';
-					playbackHolder.appendChild(interfaceContext.playhead.object);
-					feedbackHolder.appendChild(playbackHolder);
-				}
-			} else if (interfaceObj[k].options[i].type == 'option' && interfaceObj[k].options[i].name == 'page-count')
-			{
-				var pagecountHolder = document.getElementById('page-count');
-				if (pagecountHolder == null)
-				{
-					pagecountHolder = document.createElement('div');
-					pagecountHolder.id = 'page-count';
-				}
-				pagecountHolder.innerHTML = '<span>Page '+(audioHolderObject.presentedId+1)+' of '+specification.audioHolders.length+'</span>';
-				var inject = document.getElementById('interface-buttons');
-				inject.appendChild(pagecountHolder);
-			}
-		}
-	}
-	
-	var commentBoxPrefix = "Comment on fragment";
-	
-	var commentShow = audioHolderObject.elementComments;
-	
-	var loopPlayback = audioHolderObject.loop;
-
-	currentTestHolder = document.createElement('audioHolder');
-	currentTestHolder.id = audioHolderObject.id;
-	currentTestHolder.repeatCount = audioHolderObject.repeatCount;
-	
-	// Find all the audioElements from the audioHolder
-	$(audioHolderObject.audioElements).each(function(index,element){
-		// Find URL of track
-		// In this jQuery loop, variable 'this' holds the current audioElement.
-		var audioObject = audioEngineContext.newTrack(element);
-		// Check if an outside reference
-		if (element.type == 'outside-reference')
-		{
-			// Construct outside reference;
-			var orNode = new outsideReferenceDOM(audioObject,index,document.getElementById('interface-buttons'));
-			audioObject.bindInterface(orNode);
-		} else {
-			var node = interfaceContext.createCommentBox(audioObject);
-			// Create a slider per track
-			var sliderNode = new sliderObject(audioObject,interfaceObj);
-			audioObject.bindInterface(sliderNode);
-		}
-	});
-	
-	// Initialse the interfaceSlider object metrics
-	
-	$('.track-slider').mousedown(function(event) {
-		interfaceContext.selectObject($(this)[0]);
-	});
-	$('.track-slider').on('touchstart',null,function(event) {
-		interfaceContext.selectObject($(this)[0]);
-	});
-	
-	$('.track-slider').mousemove(function(event) {
-		event.preventDefault();
-	});
-	
-	$('.slider').mousemove(function(event) {
-		event.preventDefault();
-		var obj = interfaceContext.getSelectedObject();
-		if (obj == null) {return;}
-		$(obj).css("left",event.clientX + "px");
-		interfaceContext.moveObject();
-	});
-	
-	$('.slider').on('touchmove',null,function(event) {
-		event.preventDefault();
-		var obj = interfaceContext.getSelectedObject();
-		if (obj == null) {return;}
-		var move = event.originalEvent.targetTouches[0].clientX - 6;
-		$(obj).css("left",move + "px");
-		interfaceContext.moveObject();
-	});
-
-	$(document).mouseup(function(event){
-		event.preventDefault();
-		var obj = interfaceContext.getSelectedObject();
-		if (obj == null) {return;}
-		var interfaceID = obj.parentElement.getAttribute("interfaceid");
-		var trackID = obj.getAttribute("trackindex");
-		if (interfaceContext.hasSelectedObjectMoved() == true)
-		{
-			var l = $(obj).css("left");
-			var id = obj.getAttribute('trackIndex');
-			var time = audioEngineContext.timer.getTestTime();
-			var rate = convSliderPosToRate(obj);
-			audioEngineContext.audioObjects[id].metric.moved(time,rate);
-			interfaceContext.interfaceSliders[interfaceID].metrics[trackID].moved(time,rate);
-			console.log("slider "+id+" moved to "+rate+' ('+time+')');
-		} else {
-			var id = Number(obj.attributes['trackIndex'].value);
-			//audioEngineContext.metric.sliderPlayed(id);
-			audioEngineContext.play(id);
-	        // Currently playing track red, rest green
-	        
-	        $('.track-slider').removeClass('track-slider-playing');
-	        var name = ".track-slider-"+obj.getAttribute("trackindex");
-	        $(name).addClass('track-slider-playing');
-	        $('.comment-div').removeClass('comment-box-playing');
-	        $('#comment-div-'+id).addClass('comment-box-playing');
-	        var outsideReference = document.getElementById('outside-reference');
-	        if (outsideReference != undefined)
-	        $(outsideReference).removeClass('track-slider-playing');
-		}
-		interfaceContext.releaseObject();
-	});
-	
-	$('.slider').on('touchend',null,function(event){
-		var obj = interfaceContext.getSelectedObject();
-		if (obj == null) {return;}
-		var interfaceID = obj.parentElement.getAttribute("interfaceid");
-		var trackID = obj.getAttribute("trackindex");
-		if (interfaceContext.hasSelectedObjectMoved() == true)
-		{
-			var l = $(obj).css("left");
-			var id = obj.getAttribute('trackIndex');
-			var time = audioEngineContext.timer.getTestTime();
-			var rate = convSliderPosToRate(obj);
-			audioEngineContext.audioObjects[id].metric.moved(time,rate);
-			interfaceContext.interfaceSliders[interfaceID].metrics[trackID].moved(time,rate);
-			console.log("slider "+id+" moved to "+rate+' ('+time+')');
-		}
-		interfaceContext.releaseObject();
-	});
-	
-	
-	if (audioHolderObject.showElementComments) {
-		interfaceContext.showCommentBoxes(feedbackHolder,true);
-	}
-	
-	$(audioHolderObject.commentQuestions).each(function(index,element) {
-		var node = interfaceContext.createCommentQuestion(element);
-		feedbackHolder.appendChild(node.holder);
-	});
-	
-	
-	//testWaitIndicator();
-}
-
-function interfaceSliderHolder(interfaceObject)
-{
-	this.sliders = [];
-	this.metrics = [];
-	this.id = document.getElementsByClassName("sliderCanvasDiv").length;
-	this.name = interfaceObject.name;
-	this.interfaceObject = interfaceObject;
-	this.sliderDOM = document.createElement('div');
-	this.sliderDOM.className = 'sliderCanvasDiv';
-	this.sliderDOM.id = 'sliderCanvasHolder-'+this.id;
-	
-	var pagetitle = document.createElement('div');
-	pagetitle.className = "pageTitle";
-	pagetitle.align = "center";
-	var titleSpan = document.createElement('span');
-	titleSpan.id = "pageTitle-"+this.id;
-	if (interfaceObject.title != undefined && typeof interfaceObject.title == "string")
-	{
-		titleSpan.textContent = interfaceObject.title;
-	} else {
-		titleSpan.textContent = "Axis "+String(this.id+1);
-	}
-	pagetitle.appendChild(titleSpan);
-	this.sliderDOM.appendChild(pagetitle);
-	
-	// Create the slider box to hold the slider elements
-	this.canvas = document.createElement('div');
-	if (this.name != undefined)
-		this.canvas.id = 'slider-'+this.name;
-	else
-		this.canvas.id = 'slider-'+this.id;
-	this.canvas.setAttribute("interfaceid",this.id);
-	this.canvas.className = 'slider';
-	this.canvas.align = "left";
-	this.canvas.addEventListener('dragover',function(event){
-		event.preventDefault();
-		event.dataTransfer.effectAllowed = 'none';
-		event.dataTransfer.dropEffect = 'copy';
-		return false;
-	},false);
-	var sliderMargin = document.createAttribute('marginsize');
-	sliderMargin.nodeValue = 42; // Set default margins to 42px either side
-	// Must have a known EXACT width, as this is used later to determine the ratings
-	var w = (Number(sliderMargin.nodeValue)+8)*2;
-	this.canvas.style.width = window.innerWidth - w +"px";
-	this.canvas.style.marginLeft = sliderMargin.nodeValue +'px';
-	this.canvas.setAttributeNode(sliderMargin);
-	this.sliderDOM.appendChild(this.canvas);
-	
-	// Create the div to hold any scale objects
-	this.scale = document.createElement('div');
-	this.scale.className = 'sliderScale';
-	this.scale.id = 'sliderScaleHolder-'+this.id;
-	this.scale.align = 'left';
-	this.sliderDOM.appendChild(this.scale);
-	var positionScale = this.canvas.style.width.substr(0,this.canvas.style.width.length-2);
-	var offset = Number(this.canvas.attributes['marginsize'].value);
-	for (var scaleObj of interfaceObject.scales)
-	{
-		var value = document.createAttribute('value');
-		var position = Number(scaleObj.position)*0.01;
-		value.nodeValue = position;
-		var pixelPosition = (position*positionScale)+offset;
-		var scaleDOM = document.createElement('span');
-		scaleDOM.textContent = scaleObj.text;
-		this.scale.appendChild(scaleDOM);
-		scaleDOM.style.left = Math.floor((pixelPosition-($(scaleDOM).width()/2)))+'px';
-		scaleDOM.setAttributeNode(value);
-	}
-	
-	var dest = document.getElementById("slider-holder");
-	dest.appendChild(this.sliderDOM);
-	
-	this.createSliderObject = function(audioObject)
-	{
-		var trackObj = document.createElement('div');
-		trackObj.className = 'track-slider track-slider-disabled track-slider-'+audioObject.id;
-		trackObj.id = 'track-slider-'+this.id+'-'+audioObject.id;
-		trackObj.setAttribute('trackIndex',audioObject.id);
-		if (this.name != undefined) {
-			trackObj.setAttribute('interface-name',this.name);
-		} else {
-			trackObj.setAttribute('interface-name',this.id);
-		}
-		var offset = Number(this.canvas.attributes['marginsize'].value);
-		// Distribute it randomnly
-		var w = window.innerWidth - (offset+8)*2;
-		w = Math.random()*w;
-		w = Math.floor(w+(offset+8));
-		trackObj.style.left = w+'px';
-		this.canvas.appendChild(trackObj);
-		this.sliders.push(trackObj);
-		this.metrics.push(new metricTracker(this));
-		trackObj.innerHTML = '<span>'+(this.metrics.length-1)+'</span>';
-		this.metrics[this.metrics.length-1].initialise(convSliderPosToRate(trackObj));
-		return trackObj;
-	};
-	
-	this.resize = function(event)
-	{
-		var holdValues = [];
-		for (var index = 0; index < this.sliders.length; index++)
-		{
-			holdValues.push(convSliderPosToRate(this.sliders[index])); 
-		}
-		var width = event.target.innerWidth;
-		var sliderDiv = this.canvas;
-		var sliderScaleDiv = this.scale;
-		var marginsize = Number(sliderDiv.attributes['marginsize'].value);
-		var w = (marginsize+8)*2;
-		sliderDiv.style.width = width - w + 'px';
-		var width = width - w;
-		// Move sliders into new position
-		for (var index = 0; index < this.sliders.length; index++)
-		{
-			var pos = holdValues[index];
-			var pix = pos * width;
-			this.sliders[index].style.left = pix+marginsize+'px';
-		}
-		
-		// Move scale labels
-		for (var index = 0; index < this.scale.children.length; index++)
-		{
-			var scaleObj = this.scale.children[index];
-			var position = Number(scaleObj.attributes['value'].value);
-			var pixelPosition = (position*width)+marginsize;
-			scaleObj.style.left = Math.floor((pixelPosition-($(scaleObj).width()/2)))+'px';
-		}
-	};
-}
-
-function sliderObject(audioObject,interfaceObjects) {
-	// Create a new slider object;
-	this.parent = audioObject;
-	this.trackSliderObjects = [];
-	for (var i=0; i<interfaceContext.interfaceSliders.length; i++)
-	{
-		var trackObj = interfaceContext.interfaceSliders[i].createSliderObject(audioObject);
-		this.trackSliderObjects.push(trackObj);
-	}
-
-	// Onclick, switch playback to that track
-	
-	this.enable = function() {
-		if (this.parent.state == 1)
-		{
-			$(this.trackSliderObjects).each(function(i,trackObj){
-				$(trackObj).removeClass('track-slider-disabled');
-			});
-		}
-	};
-	this.updateLoading = function(progress)
-	{
-		if (progress != 100)
-		{
-			progress = String(progress);
-			progress = progress.split('.')[0];
-			this.trackSliderObjects[0].children[0].textContent = progress+'%';
-		} else {
-			this.trackSliderObjects[0].children[0].textContent = this.parent.id;
-		}
-	};
-	this.exportXMLDOM = function(audioObject) {
-		// Called by the audioObject holding this element. Must be present
-		var obj = [];
-		$(this.trackSliderObjects).each(function(i,trackObj){
-			var node = storage.document.createElement('value');
-			node.setAttribute("interface-name",trackObj.getAttribute("interface-name"));
-			node.textContent = convSliderPosToRate(trackObj);
-			obj.push(node);
-		});
-		
-		return obj;
-	};
-	this.getValue = function() {
-		return convSliderPosToRate(this.trackSliderObjects[0]);
-	};
-	this.getPresentedId = function()
-	{
-		return this.trackSliderObjects[0].children[0].textContent;
-	};
-	this.canMove = function()
-	{
-		return true;
-	};
-}
-
-function outsideReferenceDOM(audioObject,index,inject)
-{
-	this.parent = audioObject;
-	this.outsideReferenceHolder = document.createElement('div');
-	this.outsideReferenceHolder.id = 'outside-reference';
-	this.outsideReferenceHolder.className = 'outside-reference track-slider-disabled';
-	var outsideReferenceHolderspan = document.createElement('span');
-	outsideReferenceHolderspan.textContent = 'Reference';
-	this.outsideReferenceHolder.appendChild(outsideReferenceHolderspan);
-	this.outsideReferenceHolder.setAttribute('track-id',index);
-	
-	this.outsideReferenceHolder.onclick = function(event)
-	{
-		audioEngineContext.play(event.currentTarget.getAttribute('track-id'));
-		$('.track-slider').removeClass('track-slider-playing');
-        $('.comment-div').removeClass('comment-box-playing');
-        if (event.currentTarget.nodeName == 'DIV') {
-        	$(event.currentTarget).addClass('track-slider-playing');
-        } else {
-        	$(event.currentTarget.parentElement).addClass('track-slider-playing');
-        }
-	};
-	inject.appendChild(this.outsideReferenceHolder);
-	this.enable = function()
-	{
-		if (this.parent.state == 1)
-		{
-			$(this.outsideReferenceHolder).removeClass('track-slider-disabled');
-		}
-	};
-	this.updateLoading = function(progress)
-	{
-		if (progress != 100)
-		{
-			progress = String(progress);
-			progress = progress.split('.')[0];
-			this.outsideReferenceHolder[0].children[0].textContent = progress+'%';
-		} else {
-			this.outsideReferenceHolder[0].children[0].textContent = "Play Reference";
-		}
-	};
-	this.exportXMLDOM = function(audioObject)
-	{
-		return null;
-	};
-	this.getValue = function()
-	{
-		return 0;
-	};
-	this.getPresentedId = function()
-	{
-		return 'reference';
-	};
-	this.canMove = function()
-	{
-		return false;
-	};
-}
-
-function buttonSubmitClick()
-{
-	var checks = [];
-	checks = checks.concat(testState.currentStateMap.interfaces[0].options);
-	checks = checks.concat(specification.interfaces.options);
-	var canContinue = true;
-	
-	// Check that the anchor and reference objects are correctly placed
-	if (interfaceContext.checkHiddenAnchor() == false) {return;}
-	if (interfaceContext.checkHiddenReference() == false) {return;}
-	
-	for (var i=0; i<checks.length; i++) {
-		if (checks[i].type == 'check')
-		{
-			switch(checks[i].name) {
-			case 'fragmentPlayed':
-				// Check if all fragments have been played
-				var checkState = interfaceContext.checkAllPlayed();
-				if (checkState == false) {canContinue = false;}
-				break;
-			case  'fragmentFullPlayback':
-				// Check all fragments have been played to their full length
-				var checkState = interfaceContext.checkFragmentsFullyPlayed();
-				if (checkState == false) {canContinue = false;}
-				break;
-			case 'fragmentMoved':
-				// Check all fragment sliders have been moved.
-				var checkState = interfaceContext.checkAllMoved();
-				if (checkState == false) {canContinue = false;}
-				break;
-			case 'fragmentComments':
-				// Check all fragment sliders have been moved.
-				var checkState = interfaceContext.checkAllCommented();
-				if (checkState == false) {canContinue = false;}
-				break;
-			case 'scalerange':
-				// Check the scale is used to its full width outlined by the node
-				var checkState = interfaceContext.checkScaleRange();
-				if (checkState == false) {canContinue = false;}
-				break;
-			default:
-				console.log("WARNING - Check option "+checks[i].name+" is not supported on this interface");
-				break;
-			}
-
-		}
-		if (!canContinue) {break;}
-	}
-   
-    if (canContinue) {
-	    if (audioEngineContext.status == 1) {
-	        var playback = document.getElementById('playback-button');
-	        playback.click();
-	    // This function is called when the submit button is clicked. Will check for any further tests to perform, or any post-test options
-	    } else
-	    {
-	        if (audioEngineContext.timer.testStarted == false)
-	        {
-	            alert('You have not started the test! Please click a fragment to begin the test!');
-	            return;
-	        }
-	    }
-	    testState.advanceState();
-    } 
-}
-
-function convSliderPosToRate(trackSlider)
-{
-	var slider = trackSlider.parentElement;
-	var w = slider.style.width;
-	var marginsize = Number(slider.attributes['marginsize'].value);
-	var maxPix = w.substr(0,w.length-2);
-	var pix = trackSlider.style.left;
-	pix = pix.substr(0,pix.length-2);
-	var rate = (pix-marginsize)/maxPix;
-	return rate;
-}
-
-function resizeWindow(event){
-	// Function called when the window has been resized.
-	// MANDATORY FUNCTION
-	
-	// Resize the slider objects
-	for (var i=0; i<interfaceContext.interfaceSliders.length; i++)
-	{
-		interfaceContext.interfaceSliders[i].resize(event);
-	}
-}
-
-function pageXMLSave(store, pageSpecification)
-{
-	// MANDATORY
-	// Saves a specific test page
-	// You can use this space to add any extra nodes to your XML <audioHolder> saves
-	// Get the current <page> information in store (remember to appendChild your data to it)
-	// pageSpecification is the current page node configuration
-	// To create new XML nodes, use storage.document.createElement();
-	
-	if (interfaceContext.interfaceSliders.length == 1)
-	{
-		// If there is only one axis, there only needs to be one metric return
-		return;
-	}
-	var audioelements = store.getElementsByTagName("audioelement");
-	for (var i=0; i<audioelements.length; i++)
-	{
-		// Have to append the metric specific nodes
-		if (pageSpecification.outsideReference == null || pageSpecification.outsideReference.id != audioelements[i].id)
-		{
-			var inject = audioelements[i].getElementsByTagName("metric");
-			if (inject.length == 0)
-			{
-				inject = storage.document.createElement("metric");
-			} else {
-				inject = inject[0];
-			}
-			for (var k=0; k<interfaceContext.interfaceSliders.length; k++)
-			{
-				var mrnodes = interfaceContext.interfaceSliders[k].metrics[i].exportXMLDOM(inject);
-				for (var j=0; j<mrnodes.length; j++)
-				{
-					var name = mrnodes[j].getAttribute("name");
-					if (name == "elementTracker" || name == "elementTrackerFull" || name == "elementInitialPosition" || name == "elementFlagMoved")
-					{
-						mrnodes[j].setAttribute("interface-name",interfaceContext.interfaceSliders[k].name);
-						mrnodes[j].setAttribute("interface-id",k);
-						inject.appendChild(mrnodes[j]);
-					}
-				}
-			}
-		}
-	}
-}
\ No newline at end of file
--- a/core.js	Tue Jan 12 18:29:55 2016 +0000
+++ b/core.js	Wed Jan 13 09:34:46 2016 +0000
@@ -141,37 +141,37 @@
 	switch(specification.interface)
 	{
 		case "APE":
-		interfaceJS.setAttribute("src","ape.js");
+		interfaceJS.setAttribute("src","interfaces/ape.js");
 		
 		// APE comes with a css file
 		var css = document.createElement('link');
 		css.rel = 'stylesheet';
 		css.type = 'text/css';
-		css.href = 'ape.css';
+		css.href = 'interfaces/ape.css';
 		
 		document.getElementsByTagName("head")[0].appendChild(css);
 		break;
 		
 		case "MUSHRA":
-		interfaceJS.setAttribute("src","mushra.js");
+		interfaceJS.setAttribute("src","interfaces/mushra.js");
 		
 		// MUSHRA comes with a css file
 		var css = document.createElement('link');
 		css.rel = 'stylesheet';
 		css.type = 'text/css';
-		css.href = 'mushra.css';
+		css.href = 'interfaces/mushra.css';
 		
 		document.getElementsByTagName("head")[0].appendChild(css);
 		break;
 		
 		case "AB":
-		interfaceJS.setAttribute("src","AB.js");
+		interfaceJS.setAttribute("src","interfaces/AB.js");
 		
 		// AB comes with a css file
 		var css = document.createElement('link');
 		css.rel = 'stylesheet';
 		css.type = 'text/css';
-		css.href = 'AB.css';
+		css.href = 'interfaces/AB.css';
 		
 		document.getElementsByTagName("head")[0].appendChild(css);
 	}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/interfaces/AB.css	Wed Jan 13 09:34:46 2016 +0000
@@ -0,0 +1,74 @@
+body {
+	/* Set the background colour (note US English spelling) to grey*/
+	background-color: #fff
+}
+
+div.pageTitle {
+	width: auto;
+	height: 20px;
+	margin-top: 20px;
+}
+
+div.pageTitle span{
+	font-size: 1.5em;
+}
+
+div.testHalt {
+	/* Specify any colouring during the test halt for pre/post questions */
+	background-color: rgba(0,0,0,0.5);
+	/* Don't mess with this bit */
+	z-index: 2;
+	width: 100%;
+	height: 100%;
+	position: absolute;
+	left: 0px;
+	top: 0px;
+}
+
+button {
+	/* Specify any button structure or style */
+	min-width: 20px;
+	background-color: #ddd
+}
+
+button.big-button {
+	width: 250px;
+	height: 40px;
+	font-size: 1.2em;
+}
+
+div.comparitor-holder {
+	width: 260px;
+	height: 300px;
+	border:  black 1px solid;
+    float: left;
+	padding-top: 5px;
+    margin: 25px;
+}
+
+div.comparitor-selector {
+	width: 248px;
+	height: 250px;
+	border: black 1px solid;
+	position: relative;
+	background-color: #FF0000;
+}
+
+div.disabled {
+	background-color: #AAA;
+}
+
+div.selected {
+	background-color: #008000;
+}
+
+div.comparitor-selector span {
+	font-size: 4em;
+}
+
+button.comparitor-button {
+	width: 250px;
+	height: 38px;
+	position: relative;
+	margin-top: 5px;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/interfaces/AB.js	Wed Jan 13 09:34:46 2016 +0000
@@ -0,0 +1,320 @@
+// Once this is loaded and parsed, begin execution
+loadInterface();
+
+function loadInterface() {
+	// Get the dimensions of the screen available to the page
+	var width = window.innerWidth;
+	var height = window.innerHeight;
+	interfaceContext.insertPoint.innerHTML = null; // Clear the current schema
+	
+	// Custom Comparitor Object
+	Interface.prototype.comparitor = null;
+	
+	// The injection point into the HTML page
+	interfaceContext.insertPoint = document.getElementById("topLevelBody");
+	var testContent = document.createElement('div');
+	testContent.id = 'testContent';
+	
+	// Create the top div for the Title element
+	var titleAttr = specification.title;
+	var title = document.createElement('div');
+	title.className = "title";
+	title.align = "center";
+	var titleSpan = document.createElement('span');
+	
+	// Set title to that defined in XML, else set to default
+	if (titleAttr != undefined) {
+		titleSpan.textContent = titleAttr;
+	} else {
+		titleSpan.textContent =  'Listening test';
+	}
+	// Insert the titleSpan element into the title div element.
+	title.appendChild(titleSpan);
+	
+	var pagetitle = document.createElement('div');
+	pagetitle.className = "pageTitle";
+	pagetitle.align = "center";
+	var titleSpan = document.createElement('span');
+	titleSpan.id = "pageTitle";
+	pagetitle.appendChild(titleSpan);
+	
+	// Create Interface buttons!
+	var interfaceButtons = document.createElement('div');
+	interfaceButtons.id = 'interface-buttons';
+	interfaceButtons.style.height = '25px';
+	
+	// Create playback start/stop points
+	var playback = document.createElement("button");
+	playback.innerHTML = 'Stop';
+	playback.id = 'playback-button';
+	playback.style.float = 'left';
+	// onclick function. Check if it is playing or not, call the correct function in the
+	// audioEngine, change the button text to reflect the next state.
+	playback.onclick = function() {
+		if (audioEngineContext.status == 1) {
+			audioEngineContext.stop();
+			this.innerHTML = 'Stop';
+            var time = audioEngineContext.timer.getTestTime();
+            console.log('Stopped at ' + time); // DEBUG/SAFETY
+		}
+	};
+	// Append the interface buttons into the interfaceButtons object.
+	interfaceButtons.appendChild(playback);
+	
+	// Global parent for the comment boxes on the page
+	var feedbackHolder = document.createElement('div');
+	feedbackHolder.id = 'feedbackHolder';
+	
+	// Construct the AB Boxes
+	var boxes = document.createElement('div');
+	boxes.align = "center";
+	boxes.id = "box-holders";
+    boxes.style.float = "left";
+	
+	var submit = document.createElement('button');
+	submit.id = "submit";
+	submit.onclick = buttonSubmitClick;
+	submit.className = "big-button";
+	submit.textContent = "submit";
+	submit.style.position = "relative";
+	submit.style.left = (window.innerWidth-250)/2 + 'px';
+		
+	feedbackHolder.appendChild(boxes);
+	
+	// Inject into HTML
+	testContent.appendChild(title); // Insert the title
+	testContent.appendChild(pagetitle);
+	testContent.appendChild(interfaceButtons);
+	testContent.appendChild(feedbackHolder);
+	testContent.appendChild(submit);
+	interfaceContext.insertPoint.appendChild(testContent);
+
+	// Load the full interface
+	testState.initialise();
+	testState.advanceState();
+}
+
+function loadTest(audioHolderObject)
+{
+	var feedbackHolder = document.getElementById('feedbackHolder');
+	var interfaceObj = audioHolderObject.interfaces;
+	if (interfaceObj.length > 1)
+	{
+		console.log("WARNING - This interface only supports one <interface> node per page. Using first interface node");
+	}
+	interfaceObj = interfaceObj[0];
+	
+	if(interfaceObj.title != null)
+	{
+		document.getElementById("pageTitle").textContent = interfaceObj.title;
+	}
+	
+	// Populate the comparitor object
+	interfaceContext.comparitor = new Comparitor(audioHolderObject);
+	resizeWindow(null);
+}
+
+function Comparitor(audioHolderObject)
+{	
+	this.comparitorBox = function(audioElement,id,text)
+	{
+		this.parent = audioElement;
+		this.id = id;
+		this.value = 0;
+		this.disabled = true;
+		this.box = document.createElement('div');
+		this.box.className = 'comparitor-holder';
+		this.box.setAttribute('track-id',audioElement.id);
+		this.box.id = 'comparitor-'+text;
+		this.selector = document.createElement('div');
+		this.selector.className = 'comparitor-selector disabled';
+		var selectorText = document.createElement('span');
+		selectorText.textContent = text;
+		this.selector.appendChild(selectorText);
+		this.playback = document.createElement('button');
+		this.playback.className = 'comparitor-button';
+		this.playback.disabled = true;
+		this.playback.textContent = "Listen";
+		this.box.appendChild(this.selector);
+		this.box.appendChild(this.playback);
+		this.selector.onclick = function()
+		{
+			var time = audioEngineContext.timer.getTestTime();
+			if ($(event.currentTarget).hasClass('disabled'))
+			{
+				console.log("Please wait until sample has loaded");
+				return;
+			}
+			if (audioEngineContext.status == 0)
+			{
+				alert("Please listen to the samples before making a selection");
+				console.log("Please listen to the samples before making a selection");
+				return;
+			}
+			$(".comparitor-selector").removeClass('selected');
+			var id = event.currentTarget.parentElement.getAttribute('track-id');
+			interfaceContext.comparitor.selected = id;
+			$(event.currentTarget).addClass('selected');
+			for (var i=0; i<interfaceContext.comparitor.comparitors.length; i++)
+			{
+				var obj = interfaceContext.comparitor.comparitors[i];
+				if (i == id) {
+					obj.value = 1;
+				} else {
+					obj.value = 0;
+				}
+				obj.parent.metric.moved(time,obj.value);
+			}
+			console.log("Selected "+id+' ('+time+')');
+		};
+		this.playback.onclick = function()
+		{
+			$('.comparitor-button').text('Listen');
+			var id = event.currentTarget.parentElement.getAttribute('track-id');
+			audioEngineContext.play(id);
+			$(event.currentTarget).text('Playing');
+		};
+		
+		this.enable = function()
+		{
+			if (this.parent.state == 1)
+			{
+				$(this.selector).removeClass('disabled');
+				this.playback.disabled = false;
+			}
+		};
+		this.updateLoading = function(progress)
+		{
+			if (progress != 100)
+			{
+				progress = String(progress);
+				progress = progress.split('.')[0];
+				this.playback.textContent = progress+'%';
+			} else {
+				this.playback.textContent = "Listen";
+			}
+		};
+		this.exportXMLDOM = function(audioObject)
+		{
+			var node = storage.document.createElement('value');
+			node.textContent = this.value;
+			return node;
+		};
+		this.getValue = function() {
+			return this.value;	
+		};
+		this.getPresentedId = function()
+		{
+			return this.selector.children[0].textContent;
+		};
+		this.canMove = function()
+		{
+			return false;
+		};
+	};
+	
+	this.boxHolders = document.getElementById('box-holders');
+	this.boxHolders.innerHTML = null;
+	this.comparitors = [];
+	this.selected = null;
+	
+	// First generate the Audio Objects for the Audio Engine
+	for (var index=0; index<audioHolderObject.audioElements.length; index++)
+	{
+		var element = audioHolderObject.audioElements[index];
+		if (index == audioHolderObject.outsideReference || element.type == 'outside-reference')
+		{
+			console.log("WARNING - AB cannot have fixed reference");
+		}
+		var audioObject = audioEngineContext.newTrack(element);
+		var node = new this.comparitorBox(audioObject,index,String.fromCharCode(65 + index));
+		audioObject.bindInterface(node);
+		this.comparitors.push(node);
+		this.boxHolders.appendChild(node.box);
+	}
+	return this;
+}
+
+function resizeWindow(event)
+{
+	document.getElementById('submit').style.left = (window.innerWidth-250)/2 + 'px';
+	var numObj = interfaceContext.comparitor.comparitors.length;
+	var boxW = numObj*312;
+    var diff = window.innerWidth - boxW;
+    while (diff < 0)
+    {
+        numObj = Math.ceil(numObj/2);
+        boxW = numObj*312;
+        diff = window.innerWidth - boxW;
+    }
+    document.getElementById('box-holders').style.marginLeft = diff/2 + 'px';
+    document.getElementById('box-holders').style.marginRight = diff/2 + 'px';
+    document.getElementById('box-holders').style.width = boxW + 'px';
+}
+
+function buttonSubmitClick()
+{
+	var checks = [];
+	checks = checks.concat(testState.currentStateMap.interfaces[0].options);
+	checks = checks.concat(specification.interfaces.options);
+	var canContinue = true;
+	
+	for (var i=0; i<checks.length; i++) {
+		if (checks[i].type == 'check')
+		{
+			switch(checks[i].name) {
+			case 'fragmentPlayed':
+				// Check if all fragments have been played
+				var checkState = interfaceContext.checkAllPlayed();
+				if (checkState == false) {canContinue = false;}
+				break;
+			case  'fragmentFullPlayback':
+				// Check all fragments have been played to their full length
+				var checkState = interfaceContext.checkFragmentsFullyPlayed();
+				if (checkState == false) {canContinue = false;}
+				break;
+			case 'fragmentMoved':
+				// Check all fragment sliders have been moved.
+				var checkState = interfaceContext.checkAllMoved();
+				if (checkState == false) {canContinue = false;}
+				break;
+			case 'fragmentComments':
+				// Check all fragment sliders have been moved.
+				var checkState = interfaceContext.checkAllCommented();
+				if (checkState == false) {canContinue = false;}
+				break;
+			default:
+				console.log("WARNING - Check option "+checks[i].check+" is not supported on this interface");
+				break;
+			}
+
+		}
+		if (!canContinue) {break;}
+	}
+	if (canContinue)
+	{
+	    if (audioEngineContext.status == 1) {
+	        var playback = document.getElementById('playback-button');
+	        playback.click();
+	    // This function is called when the submit button is clicked. Will check for any further tests to perform, or any post-test options
+	    } else
+	    {
+	        if (audioEngineContext.timer.testStarted == false)
+	        {
+	            alert('You have not started the test! Please press start to begin the test!');
+	            return;
+	        }
+	    }
+	    testState.advanceState();
+	}
+}
+
+function pageXMLSave(store, pageSpecification)
+{
+	// MANDATORY
+	// Saves a specific test page
+	// You can use this space to add any extra nodes to your XML <audioHolder> saves
+	// Get the current <page> information in store (remember to appendChild your data to it)
+	// pageSpecification is the current page node configuration
+	// To create new XML nodes, use storage.document.createElement();
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/interfaces/ape.css	Wed Jan 13 09:34:46 2016 +0000
@@ -0,0 +1,88 @@
+/*
+ * Hold any style information for APE interface. Customise if you like to make the interface your own!
+ * 
+ */
+body {
+	/* Set the background colour (note US English spelling) to grey*/
+	background-color: #ddd
+}
+
+div.title {
+	/* Specify any colouring for the title */
+}
+
+div.pageTitle {
+	width: auto;
+	height: 20px;
+	margin-top: 5px;
+	margin-bottom: 10px;
+}
+
+div.pageTitle span{
+	font-size: 1.5em;
+}
+
+button {
+	/* Specify any button structure or style */
+	min-width: 20px;
+	background-color: #ddd
+}
+
+div.slider {
+	/* Specify any structure for the slider holder interface */
+	background-color: #eee;
+	height: 150px;
+	margin-bottom: 5px;
+	-moz-user-select: -moz-none;
+	-khtml-user-select: none;
+	-webkit-user-select: none;
+}
+
+div.sliderScale {
+	width: 100%;
+	min-height: 30px;
+	-moz-user-select: -moz-none;
+	-khtml-user-select: none;
+	-webkit-user-select: none;
+}
+
+div.sliderScale span {
+	/* Any formatting of text below scale */
+	font-size: 1.2em;
+	min-width: 5px;
+	height: 20px;
+	position: absolute;
+}
+
+div.track-slider {
+	/* Specify any structure for the slider objects */
+	position: absolute;
+	height: inherit;
+	width: 12px;
+	float: left;
+	background-color: rgb(100,200,100);
+	-moz-user-select: -moz-none;
+	-khtml-user-select: none;
+	-webkit-user-select: none;
+}
+
+div.outside-reference {
+	width:120px;
+	padding-left: 55px;
+	margin-left: 100px;
+	height:20px;
+	margin-bottom:5px;
+	background-color: rgb(100,200,100);
+}
+
+div.track-slider-disabled {
+	background-color: rgb(100,100,100);
+}
+
+div.track-slider-playing {
+	background-color: #FF0000;
+}
+
+div.comment-box-playing {
+	background-color: #FFDDDD;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/interfaces/ape.js	Wed Jan 13 09:34:46 2016 +0000
@@ -0,0 +1,826 @@
+/**
+ *  ape.js
+ *  Create the APE interface
+ */
+
+
+// Once this is loaded and parsed, begin execution
+loadInterface();
+
+function loadInterface() {
+	
+	// Get the dimensions of the screen available to the page
+	var width = window.innerWidth;
+	var height = window.innerHeight;
+	
+	// The injection point into the HTML page
+	interfaceContext.insertPoint = document.getElementById("topLevelBody");
+	var testContent = document.createElement('div');
+	
+	testContent.id = 'testContent';
+	
+	// Bindings for interfaceContext
+	interfaceContext.checkAllPlayed = function()
+	{
+		hasBeenPlayed = audioEngineContext.checkAllPlayed();
+		if (hasBeenPlayed.length > 0) // if a fragment has not been played yet
+	    {
+	    	str = "";
+	    	if (hasBeenPlayed.length > 1) {
+		    	for (var i=0; i<hasBeenPlayed.length; i++) {
+		    		str = str + hasBeenPlayed[i];
+		    		if (i < hasBeenPlayed.length-2){
+		    			str += ", ";
+		    		} else if (i == hasBeenPlayed.length-2) {
+		    			str += " or ";
+		    		}
+		    	}
+		    	alert('You have not played fragments ' + str + ' yet. Please listen, rate and comment all samples before submitting.');
+	       } else {
+	       		alert('You have not played fragment ' + hasBeenPlayed[0] + ' yet. Please listen, rate and comment all samples before submitting.');
+	       }
+	        return false;
+	    }
+	    return true;
+	};
+	
+	interfaceContext.checkAllMoved = function() {
+		var state = true;
+		var str = 'You have not moved the following sliders. ';
+		for (var i=0; i<this.interfaceSliders.length; i++)
+		{
+			var interfaceTID = [];
+			for (var j=0; j<this.interfaceSliders[i].metrics.length; j++)
+			{
+				if (this.interfaceSliders[i].metrics[j].wasMoved == false)
+				{
+					state = false;
+					interfaceTID.push(j);
+				}
+			}
+			if (interfaceTID.length != 0)
+			{
+				var interfaceName = this.interfaceSliders[i].interfaceObject.title;
+				if (interfaceName == undefined) {
+					str += 'On axis '+String(i+1)+' you must move ';
+				} else {
+					str += 'On axis "'+interfaceName+'" you must move ';
+				}
+				if (interfaceTID.length == 1)
+				{
+					str += 'slider '+interfaceTID[0]+'. ';
+				}
+				else {
+					str += 'sliders ';
+					for (var k=0; k<interfaceTID.length-1; k++)
+					{
+						str += interfaceTID[k]+', ';
+					}
+					str += interfaceTID[interfaceTID.length-1] +'. ';
+				}
+			}
+		}
+		if (state != true)
+		{
+			alert(str);
+			console.log(str);
+		}
+		return state;
+	};
+	
+	Interface.prototype.checkAllCommented = function() {
+		var audioObjs = audioEngineContext.audioObjects;
+		var audioHolder = testState.stateMap[testState.stateIndex];
+		var state = true;
+		if (audioHolder.elementComments) {
+			var strNums = [];
+			for (var i=0; i<audioObjs.length; i++)
+			{
+				if (audioObjs[i].commentDOM.trackCommentBox.value.length == 0) {
+					state = false;
+					strNums.push(i);
+				}
+			}
+			if (state == false) {
+				if (strNums.length > 1) {
+					var str = "";
+			    	for (var i=0; i<strNums.length; i++) {
+			    		str = str + strNums[i];
+			    		if (i < strNums.length-2){
+			    			str += ", ";
+			    		} else if (i == strNums.length-2) {
+			    			str += " or ";
+			    		}
+			    	}
+			    	alert('You have not commented on fragments ' + str + ' yet. Please listen, rate and comment all samples before submitting.');
+		       } else {
+		       		alert('You have not commented on fragment ' + strNums[0] + ' yet. Please listen, rate and comment all samples before submitting.');
+		       }
+			}
+		}
+		return state;
+	};
+	
+	Interface.prototype.checkScaleRange = function()
+	{
+		var audioObjs = audioEngineContext.audioObjects;
+		var audioHolder = testState.stateMap[testState.stateIndex];
+		var state = true;
+		var str = '';
+		for (var i=0; i<this.interfaceSliders.length; i++)
+		{
+			var minScale;
+			var maxScale;
+			var interfaceObject = interfaceContext.interfaceSliders[0].interfaceObject;
+			for (var j=0; j<interfaceObject.options.length; j++)
+			{
+				if (interfaceObject.options[j].check == "scalerange") {
+					minScale = interfaceObject.options[j].min;
+					maxScale = interfaceObject.options[j].max;
+					break;
+				}
+			}
+			var minRanking = convSliderPosToRate(this.interfaceSliders[i].sliders[0]);
+			var maxRanking = minRanking;
+			for (var j=1; j<this.interfaceSliders[i].sliders.length; j++)
+			{
+				var ranking = convSliderPosToRate(this.interfaceSliders[i].sliders[j]);
+				if (ranking < minRanking)
+				{
+					minRanking = ranking;
+				} else if (ranking > maxRanking)
+				{
+					maxRanking = ranking;
+				}
+			}
+			if (minRanking > minScale || maxRanking < maxScale)
+			{
+				state = false;
+				str += 'On axis "'+this.interfaceSliders[i].interfaceObject.title+'" you have not used the full width of the scale. ';
+			}
+		}
+		if (state != true)
+		{
+			alert(str);
+			console.log(str);
+		}
+		return state;
+	};
+	
+	Interface.prototype.objectSelected = null;
+	Interface.prototype.objectMoved = false;
+	Interface.prototype.selectObject = function(object)
+	{
+		if (this.objectSelected == null)
+		{
+			this.objectSelected = object;
+			this.objectMoved = false;
+		}
+	};
+	Interface.prototype.moveObject = function()
+	{
+		if (this.objectMoved == false)
+		{
+			this.objectMoved = true;
+		}
+	};
+	Interface.prototype.releaseObject = function()
+	{
+		this.objectSelected = null;
+		this.objectMoved = false;
+	};
+	Interface.prototype.getSelectedObject = function()
+	{
+		return this.objectSelected;
+	};
+	Interface.prototype.hasSelectedObjectMoved = function()
+	{
+		return this.objectMoved;
+	};
+	
+	// Bindings for slider interfaces
+	Interface.prototype.interfaceSliders = [];
+	
+	// Bindings for audioObjects
+	
+	// Create the top div for the Title element
+	var titleAttr = specification.title;
+	var title = document.createElement('div');
+	title.className = "title";
+	title.align = "center";
+	var titleSpan = document.createElement('span');
+	
+	// Set title to that defined in XML, else set to default
+	if (titleAttr != undefined) {
+		titleSpan.textContent = titleAttr;
+	} else {
+		titleSpan.textContent =  'Listening test';
+	}
+	// Insert the titleSpan element into the title div element.
+	title.appendChild(titleSpan);
+	
+	// Create Interface buttons!
+	var interfaceButtons = document.createElement('div');
+	interfaceButtons.id = 'interface-buttons';
+	
+	// Create playback start/stop points
+	var playback = document.createElement("button");
+	playback.innerHTML = 'Stop';
+	playback.id = 'playback-button';
+	// onclick function. Check if it is playing or not, call the correct function in the
+	// audioEngine, change the button text to reflect the next state.
+	playback.onclick = function() {
+		if (audioEngineContext.status == 1) {
+			audioEngineContext.stop();
+			this.innerHTML = 'Stop';
+            var time = audioEngineContext.timer.getTestTime();
+            console.log('Stopped at ' + time); // DEBUG/SAFETY
+		}
+	};
+	// Create Submit (save) button
+	var submit = document.createElement("button");
+	submit.innerHTML = 'Submit';
+	submit.onclick = buttonSubmitClick;
+	submit.id = 'submit-button';
+	// Append the interface buttons into the interfaceButtons object.
+	interfaceButtons.appendChild(playback);
+	interfaceButtons.appendChild(submit);
+	
+	var sliderHolder = document.createElement("div");
+	sliderHolder.id = "slider-holder";
+	
+	
+	// Global parent for the comment boxes on the page
+	var feedbackHolder = document.createElement('div');
+	feedbackHolder.id = 'feedbackHolder';
+	
+	testContent.style.zIndex = 1;
+	interfaceContext.insertPoint.innerHTML = null; // Clear the current schema
+	
+	// Inject into HTML
+	testContent.appendChild(title); // Insert the title
+	testContent.appendChild(interfaceButtons);
+	testContent.appendChild(sliderHolder);
+	testContent.appendChild(feedbackHolder);
+	interfaceContext.insertPoint.appendChild(testContent);
+
+	// Load the full interface
+	testState.initialise();
+	testState.advanceState();
+	
+}
+
+function loadTest(audioHolderObject)
+{
+	var width = window.innerWidth;
+	var height = window.innerHeight;
+	var id = audioHolderObject.id;
+	
+	interfaceContext.interfaceSliders = [];
+	
+	var feedbackHolder = document.getElementById('feedbackHolder');
+	var sliderHolder = document.getElementById('slider-holder');
+	feedbackHolder.innerHTML = null;
+	sliderHolder.innerHTML = null;
+	
+	// Delete outside reference
+	var outsideReferenceHolder = document.getElementById('outside-reference');
+	if (outsideReferenceHolder != null) {
+		document.getElementById('interface-buttons').removeChild(outsideReferenceHolder);
+	}
+	
+	var interfaceObj = audioHolderObject.interfaces;
+	for (var k=0; k<interfaceObj.length; k++) {
+		// Create the div box to center align
+		interfaceContext.interfaceSliders.push(new interfaceSliderHolder(interfaceObj[k]));
+		for (var i=0; i<interfaceObj[k].options.length; i++)
+		{
+			if (interfaceObj[k].options[i].type == 'option' && interfaceObj[k].options[i].name == 'playhead')
+			{
+				var playbackHolder = document.getElementById('playback-holder');
+				if (playbackHolder == null)
+				{
+					playbackHolder = document.createElement('div');
+					playbackHolder.style.width = "100%";
+					playbackHolder.align = 'center';
+					playbackHolder.appendChild(interfaceContext.playhead.object);
+					feedbackHolder.appendChild(playbackHolder);
+				}
+			} else if (interfaceObj[k].options[i].type == 'option' && interfaceObj[k].options[i].name == 'page-count')
+			{
+				var pagecountHolder = document.getElementById('page-count');
+				if (pagecountHolder == null)
+				{
+					pagecountHolder = document.createElement('div');
+					pagecountHolder.id = 'page-count';
+				}
+				pagecountHolder.innerHTML = '<span>Page '+(audioHolderObject.presentedId+1)+' of '+specification.audioHolders.length+'</span>';
+				var inject = document.getElementById('interface-buttons');
+				inject.appendChild(pagecountHolder);
+			}
+		}
+	}
+	
+	var commentBoxPrefix = "Comment on fragment";
+	
+	var commentShow = audioHolderObject.elementComments;
+	
+	var loopPlayback = audioHolderObject.loop;
+
+	currentTestHolder = document.createElement('audioHolder');
+	currentTestHolder.id = audioHolderObject.id;
+	currentTestHolder.repeatCount = audioHolderObject.repeatCount;
+	
+	// Find all the audioElements from the audioHolder
+	$(audioHolderObject.audioElements).each(function(index,element){
+		// Find URL of track
+		// In this jQuery loop, variable 'this' holds the current audioElement.
+		var audioObject = audioEngineContext.newTrack(element);
+		// Check if an outside reference
+		if (element.type == 'outside-reference')
+		{
+			// Construct outside reference;
+			var orNode = new outsideReferenceDOM(audioObject,index,document.getElementById('interface-buttons'));
+			audioObject.bindInterface(orNode);
+		} else {
+			var node = interfaceContext.createCommentBox(audioObject);
+			// Create a slider per track
+			var sliderNode = new sliderObject(audioObject,interfaceObj);
+			audioObject.bindInterface(sliderNode);
+		}
+	});
+	
+	// Initialse the interfaceSlider object metrics
+	
+	$('.track-slider').mousedown(function(event) {
+		interfaceContext.selectObject($(this)[0]);
+	});
+	$('.track-slider').on('touchstart',null,function(event) {
+		interfaceContext.selectObject($(this)[0]);
+	});
+	
+	$('.track-slider').mousemove(function(event) {
+		event.preventDefault();
+	});
+	
+	$('.slider').mousemove(function(event) {
+		event.preventDefault();
+		var obj = interfaceContext.getSelectedObject();
+		if (obj == null) {return;}
+		$(obj).css("left",event.clientX + "px");
+		interfaceContext.moveObject();
+	});
+	
+	$('.slider').on('touchmove',null,function(event) {
+		event.preventDefault();
+		var obj = interfaceContext.getSelectedObject();
+		if (obj == null) {return;}
+		var move = event.originalEvent.targetTouches[0].clientX - 6;
+		$(obj).css("left",move + "px");
+		interfaceContext.moveObject();
+	});
+
+	$(document).mouseup(function(event){
+		event.preventDefault();
+		var obj = interfaceContext.getSelectedObject();
+		if (obj == null) {return;}
+		var interfaceID = obj.parentElement.getAttribute("interfaceid");
+		var trackID = obj.getAttribute("trackindex");
+		if (interfaceContext.hasSelectedObjectMoved() == true)
+		{
+			var l = $(obj).css("left");
+			var id = obj.getAttribute('trackIndex');
+			var time = audioEngineContext.timer.getTestTime();
+			var rate = convSliderPosToRate(obj);
+			audioEngineContext.audioObjects[id].metric.moved(time,rate);
+			interfaceContext.interfaceSliders[interfaceID].metrics[trackID].moved(time,rate);
+			console.log("slider "+id+" moved to "+rate+' ('+time+')');
+		} else {
+			var id = Number(obj.attributes['trackIndex'].value);
+			//audioEngineContext.metric.sliderPlayed(id);
+			audioEngineContext.play(id);
+	        // Currently playing track red, rest green
+	        
+	        $('.track-slider').removeClass('track-slider-playing');
+	        var name = ".track-slider-"+obj.getAttribute("trackindex");
+	        $(name).addClass('track-slider-playing');
+	        $('.comment-div').removeClass('comment-box-playing');
+	        $('#comment-div-'+id).addClass('comment-box-playing');
+	        var outsideReference = document.getElementById('outside-reference');
+	        if (outsideReference != undefined)
+	        $(outsideReference).removeClass('track-slider-playing');
+		}
+		interfaceContext.releaseObject();
+	});
+	
+	$('.slider').on('touchend',null,function(event){
+		var obj = interfaceContext.getSelectedObject();
+		if (obj == null) {return;}
+		var interfaceID = obj.parentElement.getAttribute("interfaceid");
+		var trackID = obj.getAttribute("trackindex");
+		if (interfaceContext.hasSelectedObjectMoved() == true)
+		{
+			var l = $(obj).css("left");
+			var id = obj.getAttribute('trackIndex');
+			var time = audioEngineContext.timer.getTestTime();
+			var rate = convSliderPosToRate(obj);
+			audioEngineContext.audioObjects[id].metric.moved(time,rate);
+			interfaceContext.interfaceSliders[interfaceID].metrics[trackID].moved(time,rate);
+			console.log("slider "+id+" moved to "+rate+' ('+time+')');
+		}
+		interfaceContext.releaseObject();
+	});
+	
+	
+	if (audioHolderObject.showElementComments) {
+		interfaceContext.showCommentBoxes(feedbackHolder,true);
+	}
+	
+	$(audioHolderObject.commentQuestions).each(function(index,element) {
+		var node = interfaceContext.createCommentQuestion(element);
+		feedbackHolder.appendChild(node.holder);
+	});
+	
+	
+	//testWaitIndicator();
+}
+
+function interfaceSliderHolder(interfaceObject)
+{
+	this.sliders = [];
+	this.metrics = [];
+	this.id = document.getElementsByClassName("sliderCanvasDiv").length;
+	this.name = interfaceObject.name;
+	this.interfaceObject = interfaceObject;
+	this.sliderDOM = document.createElement('div');
+	this.sliderDOM.className = 'sliderCanvasDiv';
+	this.sliderDOM.id = 'sliderCanvasHolder-'+this.id;
+	
+	var pagetitle = document.createElement('div');
+	pagetitle.className = "pageTitle";
+	pagetitle.align = "center";
+	var titleSpan = document.createElement('span');
+	titleSpan.id = "pageTitle-"+this.id;
+	if (interfaceObject.title != undefined && typeof interfaceObject.title == "string")
+	{
+		titleSpan.textContent = interfaceObject.title;
+	} else {
+		titleSpan.textContent = "Axis "+String(this.id+1);
+	}
+	pagetitle.appendChild(titleSpan);
+	this.sliderDOM.appendChild(pagetitle);
+	
+	// Create the slider box to hold the slider elements
+	this.canvas = document.createElement('div');
+	if (this.name != undefined)
+		this.canvas.id = 'slider-'+this.name;
+	else
+		this.canvas.id = 'slider-'+this.id;
+	this.canvas.setAttribute("interfaceid",this.id);
+	this.canvas.className = 'slider';
+	this.canvas.align = "left";
+	this.canvas.addEventListener('dragover',function(event){
+		event.preventDefault();
+		event.dataTransfer.effectAllowed = 'none';
+		event.dataTransfer.dropEffect = 'copy';
+		return false;
+	},false);
+	var sliderMargin = document.createAttribute('marginsize');
+	sliderMargin.nodeValue = 42; // Set default margins to 42px either side
+	// Must have a known EXACT width, as this is used later to determine the ratings
+	var w = (Number(sliderMargin.nodeValue)+8)*2;
+	this.canvas.style.width = window.innerWidth - w +"px";
+	this.canvas.style.marginLeft = sliderMargin.nodeValue +'px';
+	this.canvas.setAttributeNode(sliderMargin);
+	this.sliderDOM.appendChild(this.canvas);
+	
+	// Create the div to hold any scale objects
+	this.scale = document.createElement('div');
+	this.scale.className = 'sliderScale';
+	this.scale.id = 'sliderScaleHolder-'+this.id;
+	this.scale.align = 'left';
+	this.sliderDOM.appendChild(this.scale);
+	var positionScale = this.canvas.style.width.substr(0,this.canvas.style.width.length-2);
+	var offset = Number(this.canvas.attributes['marginsize'].value);
+	for (var scaleObj of interfaceObject.scales)
+	{
+		var value = document.createAttribute('value');
+		var position = Number(scaleObj.position)*0.01;
+		value.nodeValue = position;
+		var pixelPosition = (position*positionScale)+offset;
+		var scaleDOM = document.createElement('span');
+		scaleDOM.textContent = scaleObj.text;
+		this.scale.appendChild(scaleDOM);
+		scaleDOM.style.left = Math.floor((pixelPosition-($(scaleDOM).width()/2)))+'px';
+		scaleDOM.setAttributeNode(value);
+	}
+	
+	var dest = document.getElementById("slider-holder");
+	dest.appendChild(this.sliderDOM);
+	
+	this.createSliderObject = function(audioObject)
+	{
+		var trackObj = document.createElement('div');
+		trackObj.className = 'track-slider track-slider-disabled track-slider-'+audioObject.id;
+		trackObj.id = 'track-slider-'+this.id+'-'+audioObject.id;
+		trackObj.setAttribute('trackIndex',audioObject.id);
+		if (this.name != undefined) {
+			trackObj.setAttribute('interface-name',this.name);
+		} else {
+			trackObj.setAttribute('interface-name',this.id);
+		}
+		var offset = Number(this.canvas.attributes['marginsize'].value);
+		// Distribute it randomnly
+		var w = window.innerWidth - (offset+8)*2;
+		w = Math.random()*w;
+		w = Math.floor(w+(offset+8));
+		trackObj.style.left = w+'px';
+		this.canvas.appendChild(trackObj);
+		this.sliders.push(trackObj);
+		this.metrics.push(new metricTracker(this));
+		trackObj.innerHTML = '<span>'+(this.metrics.length-1)+'</span>';
+		this.metrics[this.metrics.length-1].initialise(convSliderPosToRate(trackObj));
+		return trackObj;
+	};
+	
+	this.resize = function(event)
+	{
+		var holdValues = [];
+		for (var index = 0; index < this.sliders.length; index++)
+		{
+			holdValues.push(convSliderPosToRate(this.sliders[index])); 
+		}
+		var width = event.target.innerWidth;
+		var sliderDiv = this.canvas;
+		var sliderScaleDiv = this.scale;
+		var marginsize = Number(sliderDiv.attributes['marginsize'].value);
+		var w = (marginsize+8)*2;
+		sliderDiv.style.width = width - w + 'px';
+		var width = width - w;
+		// Move sliders into new position
+		for (var index = 0; index < this.sliders.length; index++)
+		{
+			var pos = holdValues[index];
+			var pix = pos * width;
+			this.sliders[index].style.left = pix+marginsize+'px';
+		}
+		
+		// Move scale labels
+		for (var index = 0; index < this.scale.children.length; index++)
+		{
+			var scaleObj = this.scale.children[index];
+			var position = Number(scaleObj.attributes['value'].value);
+			var pixelPosition = (position*width)+marginsize;
+			scaleObj.style.left = Math.floor((pixelPosition-($(scaleObj).width()/2)))+'px';
+		}
+	};
+}
+
+function sliderObject(audioObject,interfaceObjects) {
+	// Create a new slider object;
+	this.parent = audioObject;
+	this.trackSliderObjects = [];
+	for (var i=0; i<interfaceContext.interfaceSliders.length; i++)
+	{
+		var trackObj = interfaceContext.interfaceSliders[i].createSliderObject(audioObject);
+		this.trackSliderObjects.push(trackObj);
+	}
+
+	// Onclick, switch playback to that track
+	
+	this.enable = function() {
+		if (this.parent.state == 1)
+		{
+			$(this.trackSliderObjects).each(function(i,trackObj){
+				$(trackObj).removeClass('track-slider-disabled');
+			});
+		}
+	};
+	this.updateLoading = function(progress)
+	{
+		if (progress != 100)
+		{
+			progress = String(progress);
+			progress = progress.split('.')[0];
+			this.trackSliderObjects[0].children[0].textContent = progress+'%';
+		} else {
+			this.trackSliderObjects[0].children[0].textContent = this.parent.id;
+		}
+	};
+	this.exportXMLDOM = function(audioObject) {
+		// Called by the audioObject holding this element. Must be present
+		var obj = [];
+		$(this.trackSliderObjects).each(function(i,trackObj){
+			var node = storage.document.createElement('value');
+			node.setAttribute("interface-name",trackObj.getAttribute("interface-name"));
+			node.textContent = convSliderPosToRate(trackObj);
+			obj.push(node);
+		});
+		
+		return obj;
+	};
+	this.getValue = function() {
+		return convSliderPosToRate(this.trackSliderObjects[0]);
+	};
+	this.getPresentedId = function()
+	{
+		return this.trackSliderObjects[0].children[0].textContent;
+	};
+	this.canMove = function()
+	{
+		return true;
+	};
+}
+
+function outsideReferenceDOM(audioObject,index,inject)
+{
+	this.parent = audioObject;
+	this.outsideReferenceHolder = document.createElement('div');
+	this.outsideReferenceHolder.id = 'outside-reference';
+	this.outsideReferenceHolder.className = 'outside-reference track-slider-disabled';
+	var outsideReferenceHolderspan = document.createElement('span');
+	outsideReferenceHolderspan.textContent = 'Reference';
+	this.outsideReferenceHolder.appendChild(outsideReferenceHolderspan);
+	this.outsideReferenceHolder.setAttribute('track-id',index);
+	
+	this.outsideReferenceHolder.onclick = function(event)
+	{
+		audioEngineContext.play(event.currentTarget.getAttribute('track-id'));
+		$('.track-slider').removeClass('track-slider-playing');
+        $('.comment-div').removeClass('comment-box-playing');
+        if (event.currentTarget.nodeName == 'DIV') {
+        	$(event.currentTarget).addClass('track-slider-playing');
+        } else {
+        	$(event.currentTarget.parentElement).addClass('track-slider-playing');
+        }
+	};
+	inject.appendChild(this.outsideReferenceHolder);
+	this.enable = function()
+	{
+		if (this.parent.state == 1)
+		{
+			$(this.outsideReferenceHolder).removeClass('track-slider-disabled');
+		}
+	};
+	this.updateLoading = function(progress)
+	{
+		if (progress != 100)
+		{
+			progress = String(progress);
+			progress = progress.split('.')[0];
+			this.outsideReferenceHolder[0].children[0].textContent = progress+'%';
+		} else {
+			this.outsideReferenceHolder[0].children[0].textContent = "Play Reference";
+		}
+	};
+	this.exportXMLDOM = function(audioObject)
+	{
+		return null;
+	};
+	this.getValue = function()
+	{
+		return 0;
+	};
+	this.getPresentedId = function()
+	{
+		return 'reference';
+	};
+	this.canMove = function()
+	{
+		return false;
+	};
+}
+
+function buttonSubmitClick()
+{
+	var checks = [];
+	checks = checks.concat(testState.currentStateMap.interfaces[0].options);
+	checks = checks.concat(specification.interfaces.options);
+	var canContinue = true;
+	
+	// Check that the anchor and reference objects are correctly placed
+	if (interfaceContext.checkHiddenAnchor() == false) {return;}
+	if (interfaceContext.checkHiddenReference() == false) {return;}
+	
+	for (var i=0; i<checks.length; i++) {
+		if (checks[i].type == 'check')
+		{
+			switch(checks[i].name) {
+			case 'fragmentPlayed':
+				// Check if all fragments have been played
+				var checkState = interfaceContext.checkAllPlayed();
+				if (checkState == false) {canContinue = false;}
+				break;
+			case  'fragmentFullPlayback':
+				// Check all fragments have been played to their full length
+				var checkState = interfaceContext.checkFragmentsFullyPlayed();
+				if (checkState == false) {canContinue = false;}
+				break;
+			case 'fragmentMoved':
+				// Check all fragment sliders have been moved.
+				var checkState = interfaceContext.checkAllMoved();
+				if (checkState == false) {canContinue = false;}
+				break;
+			case 'fragmentComments':
+				// Check all fragment sliders have been moved.
+				var checkState = interfaceContext.checkAllCommented();
+				if (checkState == false) {canContinue = false;}
+				break;
+			case 'scalerange':
+				// Check the scale is used to its full width outlined by the node
+				var checkState = interfaceContext.checkScaleRange();
+				if (checkState == false) {canContinue = false;}
+				break;
+			default:
+				console.log("WARNING - Check option "+checks[i].name+" is not supported on this interface");
+				break;
+			}
+
+		}
+		if (!canContinue) {break;}
+	}
+   
+    if (canContinue) {
+	    if (audioEngineContext.status == 1) {
+	        var playback = document.getElementById('playback-button');
+	        playback.click();
+	    // This function is called when the submit button is clicked. Will check for any further tests to perform, or any post-test options
+	    } else
+	    {
+	        if (audioEngineContext.timer.testStarted == false)
+	        {
+	            alert('You have not started the test! Please click a fragment to begin the test!');
+	            return;
+	        }
+	    }
+	    testState.advanceState();
+    } 
+}
+
+function convSliderPosToRate(trackSlider)
+{
+	var slider = trackSlider.parentElement;
+	var w = slider.style.width;
+	var marginsize = Number(slider.attributes['marginsize'].value);
+	var maxPix = w.substr(0,w.length-2);
+	var pix = trackSlider.style.left;
+	pix = pix.substr(0,pix.length-2);
+	var rate = (pix-marginsize)/maxPix;
+	return rate;
+}
+
+function resizeWindow(event){
+	// Function called when the window has been resized.
+	// MANDATORY FUNCTION
+	
+	// Resize the slider objects
+	for (var i=0; i<interfaceContext.interfaceSliders.length; i++)
+	{
+		interfaceContext.interfaceSliders[i].resize(event);
+	}
+}
+
+function pageXMLSave(store, pageSpecification)
+{
+	// MANDATORY
+	// Saves a specific test page
+	// You can use this space to add any extra nodes to your XML <audioHolder> saves
+	// Get the current <page> information in store (remember to appendChild your data to it)
+	// pageSpecification is the current page node configuration
+	// To create new XML nodes, use storage.document.createElement();
+	
+	if (interfaceContext.interfaceSliders.length == 1)
+	{
+		// If there is only one axis, there only needs to be one metric return
+		return;
+	}
+	var audioelements = store.getElementsByTagName("audioelement");
+	for (var i=0; i<audioelements.length; i++)
+	{
+		// Have to append the metric specific nodes
+		if (pageSpecification.outsideReference == null || pageSpecification.outsideReference.id != audioelements[i].id)
+		{
+			var inject = audioelements[i].getElementsByTagName("metric");
+			if (inject.length == 0)
+			{
+				inject = storage.document.createElement("metric");
+			} else {
+				inject = inject[0];
+			}
+			for (var k=0; k<interfaceContext.interfaceSliders.length; k++)
+			{
+				var mrnodes = interfaceContext.interfaceSliders[k].metrics[i].exportXMLDOM(inject);
+				for (var j=0; j<mrnodes.length; j++)
+				{
+					var name = mrnodes[j].getAttribute("name");
+					if (name == "elementTracker" || name == "elementTrackerFull" || name == "elementInitialPosition" || name == "elementFlagMoved")
+					{
+						mrnodes[j].setAttribute("interface-name",interfaceContext.interfaceSliders[k].name);
+						mrnodes[j].setAttribute("interface-id",k);
+						inject.appendChild(mrnodes[j]);
+					}
+				}
+			}
+		}
+	}
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/interfaces/mushra.css	Wed Jan 13 09:34:46 2016 +0000
@@ -0,0 +1,136 @@
+/*
+ * Hold any style information for MUSHRA interface. Customise if you like to make the interface your own!
+ * 
+ */
+body {
+	/* Set the background colour (note US English spelling) to grey*/
+	background-color: #ddd
+}
+
+div.pageTitle {
+	width: auto;
+	height: 20px;
+	margin: 10px 0px;
+}
+
+div.pageTitle span{
+	font-size: 1.5em;
+}
+
+div.testHalt {
+	/* Specify any colouring during the test halt for pre/post questions */
+	background-color: rgba(0,0,0,0.5);
+	/* Don't mess with this bit */
+	z-index: 2;
+	width: 100%;
+	height: 100%;
+	position: absolute;
+	left: 0px;
+	top: 0px;
+}
+
+button {
+	/* Specify any button structure or style */
+	min-width: 20px;
+	background-color: #ddd
+}
+
+div#slider-holder {
+	height: inherit;
+	position: absolute;
+	left: 0px;
+	z-index: 3;
+}
+
+div#scale-holder {
+	height: inherit;
+	position: absolute;
+	left: 0px;
+	z-index: 2;
+}
+
+div#scale-text-holder {
+	position:relative;
+	width: 100px;
+	float: left;
+}
+div.scale-text {
+	position: absolute;
+}
+
+canvas#scale-canvas {
+	position: relative;
+	float: left;
+}
+
+div.track-slider {
+	float: left;
+	width: 94px;
+	border: solid;
+	border-width: 1px;
+	border-color: black;
+	padding:2px;
+	margin-left: 50px;
+}
+
+button.outside-reference {
+	width:120px;
+	height:20px;
+	margin-bottom:5px;
+	position: absolute;
+}
+
+div.track-slider-playing {
+	background-color: #FFDDDD;
+}
+
+input.track-slider-range {
+	margin: 2px 0px;
+}
+
+input[type=range][orient=vertical]
+{
+    writing-mode: bt-lr; /* IE */
+    -webkit-appearance: slider-vertical; /* WebKit */
+    width: 8px;
+    padding: 0 5px;
+    color: rgb(255, 144, 144);
+}
+
+input[type=range]::-webkit-slider-runnable-track {
+	width: 8px;
+	cursor: pointer;
+	background: #fff;
+	border-radius: 4px;
+	border: 1px solid #000;
+}
+
+input[type=range]::-moz-range-track {
+	width: 8px;
+	cursor: pointer;
+	background: #fff;
+	border-radius: 4px;
+	border: 1px solid #000;
+}
+
+input.track-slider-not-moved[type=range]::-webkit-slider-runnable-track {
+	background: #aaa;
+}
+
+input.track-slider-not-moved[type=range]::-moz-range-track {
+	background: #aaa;
+}
+
+
+input[type=range]::-moz-range-thumb {
+	margin-left: -7px;
+	cursor: pointer;
+	margin-top: -1px;
+	box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d;
+}
+
+input[type=range]::-webkit-slider-thumb {
+	cursor: pointer;
+	margin-top: -1px;
+	margin-left: -4px;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/interfaces/mushra.js	Wed Jan 13 09:34:46 2016 +0000
@@ -0,0 +1,489 @@
+/**
+ *  mushra.js
+ *  Create the MUSHRA interface
+ */
+
+// Once this is loaded and parsed, begin execution
+loadInterface();
+
+function loadInterface() {
+	// Get the dimensions of the screen available to the page
+	var width = window.innerWidth;
+	var height = window.innerHeight;
+	
+	// The injection point into the HTML page
+	interfaceContext.insertPoint = document.getElementById("topLevelBody");
+	var testContent = document.createElement('div');
+	testContent.id = 'testContent';
+	
+	// Create the top div for the Title element
+	var titleAttr = specification.title;
+	var title = document.createElement('div');
+	title.className = "title";
+	title.align = "center";
+	var titleSpan = document.createElement('span');
+	
+	// Set title to that defined in XML, else set to default
+	if (titleAttr != undefined) {
+		titleSpan.textContent = titleAttr;
+	} else {
+		titleSpan.textContent =  'Listening test';
+	}
+	// Insert the titleSpan element into the title div element.
+	title.appendChild(titleSpan);
+	
+	var pagetitle = document.createElement('div');
+	pagetitle.className = "pageTitle";
+	pagetitle.align = "center";
+	var titleSpan = document.createElement('span');
+	titleSpan.id = "pageTitle";
+	pagetitle.appendChild(titleSpan);
+	
+	// Create Interface buttons!
+	var interfaceButtons = document.createElement('div');
+	interfaceButtons.id = 'interface-buttons';
+	interfaceButtons.style.height = '25px';
+	
+	// Create playback start/stop points
+	var playback = document.createElement("button");
+	playback.innerHTML = 'Stop';
+	playback.id = 'playback-button';
+	playback.style.float = 'left';
+	// onclick function. Check if it is playing or not, call the correct function in the
+	// audioEngine, change the button text to reflect the next state.
+	playback.onclick = function() {
+		if (audioEngineContext.status == 1) {
+			audioEngineContext.stop();
+			this.innerHTML = 'Stop';
+            var time = audioEngineContext.timer.getTestTime();
+            console.log('Stopped at ' + time); // DEBUG/SAFETY
+		}
+	};
+	// Create Submit (save) button
+	var submit = document.createElement("button");
+	submit.innerHTML = 'Submit';
+	submit.onclick = buttonSubmitClick;
+	submit.id = 'submit-button';
+	submit.style.float = 'left';
+	// Append the interface buttons into the interfaceButtons object.
+	interfaceButtons.appendChild(playback);
+	interfaceButtons.appendChild(submit);
+	
+	// Create a slider box
+	var sliderBox = document.createElement('div');
+	sliderBox.style.width = "100%";
+	sliderBox.style.height = window.innerHeight - 200+12 + 'px';
+	sliderBox.style.marginBottom = '10px';
+	sliderBox.id = 'slider';
+	var scaleHolder = document.createElement('div');
+	scaleHolder.id = "scale-holder";
+	sliderBox.appendChild(scaleHolder);
+	var scaleText = document.createElement('div');
+	scaleText.id = "scale-text-holder";
+	scaleHolder.appendChild(scaleText);
+	var scaleCanvas = document.createElement('canvas');
+	scaleCanvas.id = "scale-canvas";
+	scaleHolder.appendChild(scaleCanvas);
+	var sliderObjectHolder = document.createElement('div');
+	sliderObjectHolder.id = 'slider-holder';
+	sliderObjectHolder.align = "center";
+	sliderBox.appendChild(sliderObjectHolder);
+	
+	// Global parent for the comment boxes on the page
+	var feedbackHolder = document.createElement('div');
+	feedbackHolder.id = 'feedbackHolder';
+	
+	testContent.style.zIndex = 1;
+	interfaceContext.insertPoint.innerHTML = null; // Clear the current schema
+	
+	// Inject into HTML
+	testContent.appendChild(title); // Insert the title
+	testContent.appendChild(pagetitle);
+	testContent.appendChild(interfaceButtons);
+	testContent.appendChild(sliderBox);
+	testContent.appendChild(feedbackHolder);
+	interfaceContext.insertPoint.appendChild(testContent);
+
+	// Load the full interface
+	testState.initialise();
+	testState.advanceState();
+}
+
+function loadTest(audioHolderObject)
+{
+	var id = audioHolderObject.id;
+	
+	var feedbackHolder = document.getElementById('feedbackHolder');
+	var interfaceObj = audioHolderObject.interfaces;
+	if (interfaceObj.length > 1)
+	{
+		console.log("WARNING - This interface only supports one <interface> node per page. Using first interface node");
+	}
+	interfaceObj = interfaceObj[0];
+	if(interfaceObj.title != null)
+	{
+		document.getElementById("pageTitle").textContent = interfaceObj.title;
+	}
+	
+	// Delete outside reference
+	var outsideReferenceHolder = document.getElementById('outside-reference');
+	if (outsideReferenceHolder != null) {
+		document.getElementById('interface-buttons').removeChild(outsideReferenceHolder);
+	}
+	
+	var sliderBox = document.getElementById('slider-holder');
+	feedbackHolder.innerHTML = null;
+	sliderBox.innerHTML = null;
+	
+	var commentBoxPrefix = "Comment on track";
+	if (interfaceObj.commentBoxPrefix != undefined) {
+		commentBoxPrefix = interfaceObj.commentBoxPrefix;
+	}
+	var loopPlayback = audioHolderObject.loop;
+	
+	currentTestHolder = document.createElement('audioHolder');
+	currentTestHolder.id = audioHolderObject.id;
+	currentTestHolder.repeatCount = audioHolderObject.repeatCount;
+	
+	$(audioHolderObject.commentQuestions).each(function(index,element) {
+		var node = interfaceContext.createCommentQuestion(element);
+		feedbackHolder.appendChild(node.holder);
+	});
+	
+	// Find all the audioElements from the audioHolder
+	var label = 0;
+	$(audioHolderObject.audioElements).each(function(index,element){
+		// Find URL of track
+		// In this jQuery loop, variable 'this' holds the current audioElement.
+		
+		var audioObject = audioEngineContext.newTrack(element);
+		if (element.type == 'outside-reference')
+		{
+			// Construct outside reference;
+			var orNode = new outsideReferenceDOM(audioObject,index,document.getElementById('interface-buttons'));
+			audioObject.bindInterface(orNode);
+		} else {
+			var node = interfaceContext.createCommentBox(audioObject);
+		
+			// Create a slider per track
+			audioObject.bindInterface(new sliderObject(audioObject,label));
+			
+			if (typeof audioHolderObject.initialPosition === "number")
+			{
+				// Set the values
+				audioObject.interfaceDOM.slider.value = audioHolderObject.initalPosition;
+			} else {
+				// Distribute it randomnly
+				audioObject.interfaceDOM.slider.value = Math.random();
+			}
+			sliderBox.appendChild(audioObject.interfaceDOM.holder);
+			audioObject.metric.initialise(audioObject.interfaceDOM.slider.value);
+			label += 1;
+		}
+        
+	});
+	
+	// Auto-align
+	resizeWindow(null);
+}
+
+function sliderObject(audioObject,label)
+{
+	// Constructs the slider object. We use the HTML5 slider object
+	this.parent = audioObject;
+	this.holder = document.createElement('div');
+	this.title = document.createElement('span');
+	this.slider = document.createElement('input');
+	this.play = document.createElement('button');
+	
+	this.holder.className = 'track-slider';
+	this.holder.style.height = window.innerHeight-200 + 'px';
+	this.holder.appendChild(this.title);
+	this.holder.appendChild(this.slider);
+	this.holder.appendChild(this.play);
+	this.holder.align = "center";
+	if (audioObject.id == 0)
+	{
+		this.holder.style.marginLeft = '0px';
+	}
+	this.holder.setAttribute('trackIndex',audioObject.id);
+	
+	this.title.textContent = label;
+	this.title.style.width = "100%";
+	this.title.style.float = "left";
+	
+	this.slider.type = "range";
+	this.slider.className = "track-slider-range track-slider-not-moved";
+	this.slider.min = "0";
+	this.slider.max = "1";
+	this.slider.step = "0.01";
+	this.slider.setAttribute('orient','vertical');
+	this.slider.style.height = window.innerHeight-250 + 'px';
+	this.slider.onchange = function()
+	{
+		var time = audioEngineContext.timer.getTestTime();
+		var id = Number(this.parentNode.getAttribute('trackIndex'));
+		audioEngineContext.audioObjects[id].metric.moved(time,this.value);
+		console.log('slider '+id+' moved to '+this.value+' ('+time+')');
+		$(this).removeClass('track-slider-not-moved');
+	};
+	
+	this.play.textContent = "Loading...";
+	this.play.value = audioObject.id;
+	this.play.style.float = "left";
+	this.play.style.width = "100%";
+	this.play.disabled = true;
+	this.play.onclick = function(event)
+	{
+		var id = Number(event.currentTarget.value);
+		//audioEngineContext.metric.sliderPlayed(id);
+		audioEngineContext.play(id);
+		$(".track-slider").removeClass('track-slider-playing');
+		$(event.currentTarget.parentElement).addClass('track-slider-playing');
+		var outsideReference = document.getElementById('outside-reference');
+		if (outsideReference != null) {
+			$(outsideReference).removeClass('track-slider-playing');
+		}
+	};
+	
+	this.enable = function() {
+		this.play.disabled = false;
+		this.play.textContent = "Play";
+		$(this.slider).removeClass('track-slider-disabled');
+	};
+	
+	this.exportXMLDOM = function(audioObject) {
+		// Called by the audioObject holding this element. Must be present
+		var node = storage.document.createElement('value');
+		node.textContent = this.slider.value;
+		return node;
+	};
+	this.getValue = function() {
+		return this.slider.value;
+	};
+	
+	this.resize = function(event)
+	{
+		this.holder.style.height = window.innerHeight-200 + 'px';
+		this.slider.style.height = window.innerHeight-250 + 'px';
+	};
+	this.updateLoading = function(progress)
+	{
+		progress = String(progress);
+		progress = progress.substr(0,5);
+		this.play.textContent = "Loading: "+progress+"%";
+	};
+	
+	if (this.parent.state == 1)
+	{
+		this.enable();
+	}
+	this.getPresentedId = function()
+	{
+		return this.title.textContent;
+	};
+	this.canMove = function()
+	{
+		return true;
+	};
+}
+
+function outsideReferenceDOM(audioObject,index,inject)
+{
+	this.parent = audioObject;
+	this.outsideReferenceHolder = document.createElement('button');
+	this.outsideReferenceHolder.id = 'outside-reference';
+	this.outsideReferenceHolder.className = 'outside-reference';
+	this.outsideReferenceHolder.setAttribute('track-id',index);
+	this.outsideReferenceHolder.textContent = "Play Reference";
+	this.outsideReferenceHolder.disabled = true;
+	
+	this.outsideReferenceHolder.onclick = function(event)
+	{
+		audioEngineContext.play(event.currentTarget.getAttribute('track-id'));
+		$('.track-slider').removeClass('track-slider-playing');
+        $('.comment-div').removeClass('comment-box-playing');
+        $(event.currentTarget).addClass('track-slider-playing');
+	};
+	inject.appendChild(this.outsideReferenceHolder);
+	this.enable = function()
+	{
+		if (this.parent.state == 1)
+		{
+			this.outsideReferenceHolder.disabled = false;
+		}
+	};
+	this.updateLoading = function(progress)
+	{
+		if (progress != 100)
+		{
+			progress = String(progress);
+			progress = progress.split('.')[0];
+			this.outsideReferenceHolder[0].children[0].textContent = progress+'%';
+		} else {
+			this.outsideReferenceHolder[0].children[0].textContent = "Play Reference";
+		}
+	};
+	this.exportXMLDOM = function(audioObject)
+	{
+		return null;
+	};
+	this.getValue = function()
+	{
+		return 0;
+	};
+	this.getPresentedId = function()
+	{
+		return 'reference';
+	};
+	this.canMove = function()
+	{
+		return false;
+	};
+}
+
+function resizeWindow(event)
+{
+	// Function called when the window has been resized.
+	// MANDATORY FUNCTION
+	
+	var outsideRef = document.getElementById('outside-reference');
+	if(outsideRef != null)
+	{
+		outsideRef.style.left = (window.innerWidth-120)/2 + 'px';
+	}
+	
+	// Auto-align
+	var numObj = document.getElementsByClassName('track-slider').length;
+	var totalWidth = (numObj-1)*150+100;
+	var diff = (window.innerWidth - totalWidth)/2;
+	document.getElementById('slider').style.height = window.innerHeight - 180 + 'px';
+	if (diff <= 0){diff = 0;}
+	document.getElementById('slider-holder').style.marginLeft = diff + 'px';
+	for (var i in audioEngineContext.audioObjects)
+	{
+		if (audioEngineContext.audioObjects[i].specification.type != 'outside-reference'){
+			audioEngineContext.audioObjects[i].interfaceDOM.resize(event);
+		}
+	}
+	document.getElementById('scale-holder').style.marginLeft = (diff-100) + 'px';
+	document.getElementById('scale-text-holder').style.height = window.innerHeight-194 + 'px';
+	var canvas = document.getElementById('scale-canvas');
+	canvas.width = totalWidth;
+	canvas.height = window.innerHeight-194;
+	drawScale();
+}
+
+function drawScale()
+{
+	var interfaceObj = testState.currentStateMap.interfaces[0];
+	var scales = testState.currentStateMap.interfaces[0].scales;
+	scales = scales.sort(function(a,b) {
+		return a.position - b.position;
+	});
+	var canvas = document.getElementById('scale-canvas');
+	var ctx = canvas.getContext("2d");
+	var height = canvas.height;
+	var width = canvas.width;
+	var draw_heights = [24, height-34];
+	var textHolder = document.getElementById('scale-text-holder');
+	textHolder.innerHTML = null;
+	var lastHeight = 0;
+	for (var scale of scales)
+	{
+		var posPercent = scale.position / 100.0;
+		var posPix = (1-posPercent)*(draw_heights[1]-draw_heights[0])+draw_heights[0];
+		ctx.fillStyle = "#000000";
+		ctx.setLineDash([1,2]);
+		ctx.moveTo(0,posPix);
+		ctx.lineTo(width,posPix);
+		ctx.stroke();
+		var text = document.createElement('div');
+		text.align = "right";
+		var textC = document.createElement('span');
+		textC.textContent = scale.text;
+		text.appendChild(textC);
+		text.className = "scale-text";
+		textHolder.appendChild(text);
+		text.style.top = (posPix-9) + 'px';
+		text.style.left = 100 - ($(text).width()+3) + 'px';
+		lastHeight = posPix;
+	}
+}
+
+function buttonSubmitClick() // TODO: Only when all songs have been played!
+{
+	var checks = [];
+	checks = checks.concat(testState.currentStateMap.interfaces[0].options);
+	checks = checks.concat(specification.interfaces.options);
+	var canContinue = true;
+	
+	// Check that the anchor and reference objects are correctly placed
+	if (interfaceContext.checkHiddenAnchor() == false) {return;}
+	if (interfaceContext.checkHiddenReference() == false) {return;}
+	
+	for (var i=0; i<checks.length; i++) {
+		if (checks[i].type == 'check')
+		{
+			switch(checks[i].name) {
+			case 'fragmentPlayed':
+				// Check if all fragments have been played
+				var checkState = interfaceContext.checkAllPlayed();
+				if (checkState == false) {canContinue = false;}
+				break;
+			case  'fragmentFullPlayback':
+				// Check all fragments have been played to their full length
+				var checkState = interfaceContext.checkAllPlayed();
+				if (checkState == false) {canContinue = false;}
+				console.log('NOTE: fragmentFullPlayback not currently implemented, performing check fragmentPlayed instead');
+				break;
+			case 'fragmentMoved':
+				// Check all fragment sliders have been moved.
+				var checkState = interfaceContext.checkAllMoved();
+				if (checkState == false) {canContinue = false;}
+				break;
+			case 'fragmentComments':
+				// Check all fragment sliders have been moved.
+				var checkState = interfaceContext.checkAllCommented();
+				if (checkState == false) {canContinue = false;}
+				break;
+			//case 'scalerange':
+				// Check the scale is used to its full width outlined by the node
+				//var checkState = interfaceContext.checkScaleRange();
+				//if (checkState == false) {canContinue = false;}
+			//	break;
+			default:
+				console.log("WARNING - Check option "+checks[i].check+" is not supported on this interface");
+				break;
+			}
+
+		}
+		if (!canContinue) {break;}
+	}
+	
+    if (canContinue) {
+	    if (audioEngineContext.status == 1) {
+	        var playback = document.getElementById('playback-button');
+	        playback.click();
+	    // This function is called when the submit button is clicked. Will check for any further tests to perform, or any post-test options
+	    } else
+	    {
+	        if (audioEngineContext.timer.testStarted == false)
+	        {
+	            alert('You have not started the test! Please press start to begin the test!');
+	            return;
+	        }
+	    }
+	    testState.advanceState();
+    } 
+}
+
+function pageXMLSave(store, pageSpecification)
+{
+	// MANDATORY
+	// Saves a specific test page
+	// You can use this space to add any extra nodes to your XML <audioHolder> saves
+	// Get the current <page> information in store (remember to appendChild your data to it)
+	// pageSpecification is the current page node configuration
+	// To create new XML nodes, use storage.document.createElement();
+}
--- a/mushra.css	Tue Jan 12 18:29:55 2016 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,136 +0,0 @@
-/*
- * Hold any style information for MUSHRA interface. Customise if you like to make the interface your own!
- * 
- */
-body {
-	/* Set the background colour (note US English spelling) to grey*/
-	background-color: #ddd
-}
-
-div.pageTitle {
-	width: auto;
-	height: 20px;
-	margin: 10px 0px;
-}
-
-div.pageTitle span{
-	font-size: 1.5em;
-}
-
-div.testHalt {
-	/* Specify any colouring during the test halt for pre/post questions */
-	background-color: rgba(0,0,0,0.5);
-	/* Don't mess with this bit */
-	z-index: 2;
-	width: 100%;
-	height: 100%;
-	position: absolute;
-	left: 0px;
-	top: 0px;
-}
-
-button {
-	/* Specify any button structure or style */
-	min-width: 20px;
-	background-color: #ddd
-}
-
-div#slider-holder {
-	height: inherit;
-	position: absolute;
-	left: 0px;
-	z-index: 3;
-}
-
-div#scale-holder {
-	height: inherit;
-	position: absolute;
-	left: 0px;
-	z-index: 2;
-}
-
-div#scale-text-holder {
-	position:relative;
-	width: 100px;
-	float: left;
-}
-div.scale-text {
-	position: absolute;
-}
-
-canvas#scale-canvas {
-	position: relative;
-	float: left;
-}
-
-div.track-slider {
-	float: left;
-	width: 94px;
-	border: solid;
-	border-width: 1px;
-	border-color: black;
-	padding:2px;
-	margin-left: 50px;
-}
-
-button.outside-reference {
-	width:120px;
-	height:20px;
-	margin-bottom:5px;
-	position: absolute;
-}
-
-div.track-slider-playing {
-	background-color: #FFDDDD;
-}
-
-input.track-slider-range {
-	margin: 2px 0px;
-}
-
-input[type=range][orient=vertical]
-{
-    writing-mode: bt-lr; /* IE */
-    -webkit-appearance: slider-vertical; /* WebKit */
-    width: 8px;
-    padding: 0 5px;
-    color: rgb(255, 144, 144);
-}
-
-input[type=range]::-webkit-slider-runnable-track {
-	width: 8px;
-	cursor: pointer;
-	background: #fff;
-	border-radius: 4px;
-	border: 1px solid #000;
-}
-
-input[type=range]::-moz-range-track {
-	width: 8px;
-	cursor: pointer;
-	background: #fff;
-	border-radius: 4px;
-	border: 1px solid #000;
-}
-
-input.track-slider-not-moved[type=range]::-webkit-slider-runnable-track {
-	background: #aaa;
-}
-
-input.track-slider-not-moved[type=range]::-moz-range-track {
-	background: #aaa;
-}
-
-
-input[type=range]::-moz-range-thumb {
-	margin-left: -7px;
-	cursor: pointer;
-	margin-top: -1px;
-	box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d;
-}
-
-input[type=range]::-webkit-slider-thumb {
-	cursor: pointer;
-	margin-top: -1px;
-	margin-left: -4px;
-}
--- a/mushra.js	Tue Jan 12 18:29:55 2016 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,489 +0,0 @@
-/**
- *  mushra.js
- *  Create the MUSHRA interface
- */
-
-// Once this is loaded and parsed, begin execution
-loadInterface();
-
-function loadInterface() {
-	// Get the dimensions of the screen available to the page
-	var width = window.innerWidth;
-	var height = window.innerHeight;
-	
-	// The injection point into the HTML page
-	interfaceContext.insertPoint = document.getElementById("topLevelBody");
-	var testContent = document.createElement('div');
-	testContent.id = 'testContent';
-	
-	// Create the top div for the Title element
-	var titleAttr = specification.title;
-	var title = document.createElement('div');
-	title.className = "title";
-	title.align = "center";
-	var titleSpan = document.createElement('span');
-	
-	// Set title to that defined in XML, else set to default
-	if (titleAttr != undefined) {
-		titleSpan.textContent = titleAttr;
-	} else {
-		titleSpan.textContent =  'Listening test';
-	}
-	// Insert the titleSpan element into the title div element.
-	title.appendChild(titleSpan);
-	
-	var pagetitle = document.createElement('div');
-	pagetitle.className = "pageTitle";
-	pagetitle.align = "center";
-	var titleSpan = document.createElement('span');
-	titleSpan.id = "pageTitle";
-	pagetitle.appendChild(titleSpan);
-	
-	// Create Interface buttons!
-	var interfaceButtons = document.createElement('div');
-	interfaceButtons.id = 'interface-buttons';
-	interfaceButtons.style.height = '25px';
-	
-	// Create playback start/stop points
-	var playback = document.createElement("button");
-	playback.innerHTML = 'Stop';
-	playback.id = 'playback-button';
-	playback.style.float = 'left';
-	// onclick function. Check if it is playing or not, call the correct function in the
-	// audioEngine, change the button text to reflect the next state.
-	playback.onclick = function() {
-		if (audioEngineContext.status == 1) {
-			audioEngineContext.stop();
-			this.innerHTML = 'Stop';
-            var time = audioEngineContext.timer.getTestTime();
-            console.log('Stopped at ' + time); // DEBUG/SAFETY
-		}
-	};
-	// Create Submit (save) button
-	var submit = document.createElement("button");
-	submit.innerHTML = 'Submit';
-	submit.onclick = buttonSubmitClick;
-	submit.id = 'submit-button';
-	submit.style.float = 'left';
-	// Append the interface buttons into the interfaceButtons object.
-	interfaceButtons.appendChild(playback);
-	interfaceButtons.appendChild(submit);
-	
-	// Create a slider box
-	var sliderBox = document.createElement('div');
-	sliderBox.style.width = "100%";
-	sliderBox.style.height = window.innerHeight - 200+12 + 'px';
-	sliderBox.style.marginBottom = '10px';
-	sliderBox.id = 'slider';
-	var scaleHolder = document.createElement('div');
-	scaleHolder.id = "scale-holder";
-	sliderBox.appendChild(scaleHolder);
-	var scaleText = document.createElement('div');
-	scaleText.id = "scale-text-holder";
-	scaleHolder.appendChild(scaleText);
-	var scaleCanvas = document.createElement('canvas');
-	scaleCanvas.id = "scale-canvas";
-	scaleHolder.appendChild(scaleCanvas);
-	var sliderObjectHolder = document.createElement('div');
-	sliderObjectHolder.id = 'slider-holder';
-	sliderObjectHolder.align = "center";
-	sliderBox.appendChild(sliderObjectHolder);
-	
-	// Global parent for the comment boxes on the page
-	var feedbackHolder = document.createElement('div');
-	feedbackHolder.id = 'feedbackHolder';
-	
-	testContent.style.zIndex = 1;
-	interfaceContext.insertPoint.innerHTML = null; // Clear the current schema
-	
-	// Inject into HTML
-	testContent.appendChild(title); // Insert the title
-	testContent.appendChild(pagetitle);
-	testContent.appendChild(interfaceButtons);
-	testContent.appendChild(sliderBox);
-	testContent.appendChild(feedbackHolder);
-	interfaceContext.insertPoint.appendChild(testContent);
-
-	// Load the full interface
-	testState.initialise();
-	testState.advanceState();
-}
-
-function loadTest(audioHolderObject)
-{
-	var id = audioHolderObject.id;
-	
-	var feedbackHolder = document.getElementById('feedbackHolder');
-	var interfaceObj = audioHolderObject.interfaces;
-	if (interfaceObj.length > 1)
-	{
-		console.log("WARNING - This interface only supports one <interface> node per page. Using first interface node");
-	}
-	interfaceObj = interfaceObj[0];
-	if(interfaceObj.title != null)
-	{
-		document.getElementById("pageTitle").textContent = interfaceObj.title;
-	}
-	
-	// Delete outside reference
-	var outsideReferenceHolder = document.getElementById('outside-reference');
-	if (outsideReferenceHolder != null) {
-		document.getElementById('interface-buttons').removeChild(outsideReferenceHolder);
-	}
-	
-	var sliderBox = document.getElementById('slider-holder');
-	feedbackHolder.innerHTML = null;
-	sliderBox.innerHTML = null;
-	
-	var commentBoxPrefix = "Comment on track";
-	if (interfaceObj.commentBoxPrefix != undefined) {
-		commentBoxPrefix = interfaceObj.commentBoxPrefix;
-	}
-	var loopPlayback = audioHolderObject.loop;
-	
-	currentTestHolder = document.createElement('audioHolder');
-	currentTestHolder.id = audioHolderObject.id;
-	currentTestHolder.repeatCount = audioHolderObject.repeatCount;
-	
-	$(audioHolderObject.commentQuestions).each(function(index,element) {
-		var node = interfaceContext.createCommentQuestion(element);
-		feedbackHolder.appendChild(node.holder);
-	});
-	
-	// Find all the audioElements from the audioHolder
-	var label = 0;
-	$(audioHolderObject.audioElements).each(function(index,element){
-		// Find URL of track
-		// In this jQuery loop, variable 'this' holds the current audioElement.
-		
-		var audioObject = audioEngineContext.newTrack(element);
-		if (element.type == 'outside-reference')
-		{
-			// Construct outside reference;
-			var orNode = new outsideReferenceDOM(audioObject,index,document.getElementById('interface-buttons'));
-			audioObject.bindInterface(orNode);
-		} else {
-			var node = interfaceContext.createCommentBox(audioObject);
-		
-			// Create a slider per track
-			audioObject.bindInterface(new sliderObject(audioObject,label));
-			
-			if (typeof audioHolderObject.initialPosition === "number")
-			{
-				// Set the values
-				audioObject.interfaceDOM.slider.value = audioHolderObject.initalPosition;
-			} else {
-				// Distribute it randomnly
-				audioObject.interfaceDOM.slider.value = Math.random();
-			}
-			sliderBox.appendChild(audioObject.interfaceDOM.holder);
-			audioObject.metric.initialise(audioObject.interfaceDOM.slider.value);
-			label += 1;
-		}
-        
-	});
-	
-	// Auto-align
-	resizeWindow(null);
-}
-
-function sliderObject(audioObject,label)
-{
-	// Constructs the slider object. We use the HTML5 slider object
-	this.parent = audioObject;
-	this.holder = document.createElement('div');
-	this.title = document.createElement('span');
-	this.slider = document.createElement('input');
-	this.play = document.createElement('button');
-	
-	this.holder.className = 'track-slider';
-	this.holder.style.height = window.innerHeight-200 + 'px';
-	this.holder.appendChild(this.title);
-	this.holder.appendChild(this.slider);
-	this.holder.appendChild(this.play);
-	this.holder.align = "center";
-	if (audioObject.id == 0)
-	{
-		this.holder.style.marginLeft = '0px';
-	}
-	this.holder.setAttribute('trackIndex',audioObject.id);
-	
-	this.title.textContent = label;
-	this.title.style.width = "100%";
-	this.title.style.float = "left";
-	
-	this.slider.type = "range";
-	this.slider.className = "track-slider-range track-slider-not-moved";
-	this.slider.min = "0";
-	this.slider.max = "1";
-	this.slider.step = "0.01";
-	this.slider.setAttribute('orient','vertical');
-	this.slider.style.height = window.innerHeight-250 + 'px';
-	this.slider.onchange = function()
-	{
-		var time = audioEngineContext.timer.getTestTime();
-		var id = Number(this.parentNode.getAttribute('trackIndex'));
-		audioEngineContext.audioObjects[id].metric.moved(time,this.value);
-		console.log('slider '+id+' moved to '+this.value+' ('+time+')');
-		$(this).removeClass('track-slider-not-moved');
-	};
-	
-	this.play.textContent = "Loading...";
-	this.play.value = audioObject.id;
-	this.play.style.float = "left";
-	this.play.style.width = "100%";
-	this.play.disabled = true;
-	this.play.onclick = function(event)
-	{
-		var id = Number(event.currentTarget.value);
-		//audioEngineContext.metric.sliderPlayed(id);
-		audioEngineContext.play(id);
-		$(".track-slider").removeClass('track-slider-playing');
-		$(event.currentTarget.parentElement).addClass('track-slider-playing');
-		var outsideReference = document.getElementById('outside-reference');
-		if (outsideReference != null) {
-			$(outsideReference).removeClass('track-slider-playing');
-		}
-	};
-	
-	this.enable = function() {
-		this.play.disabled = false;
-		this.play.textContent = "Play";
-		$(this.slider).removeClass('track-slider-disabled');
-	};
-	
-	this.exportXMLDOM = function(audioObject) {
-		// Called by the audioObject holding this element. Must be present
-		var node = storage.document.createElement('value');
-		node.textContent = this.slider.value;
-		return node;
-	};
-	this.getValue = function() {
-		return this.slider.value;
-	};
-	
-	this.resize = function(event)
-	{
-		this.holder.style.height = window.innerHeight-200 + 'px';
-		this.slider.style.height = window.innerHeight-250 + 'px';
-	};
-	this.updateLoading = function(progress)
-	{
-		progress = String(progress);
-		progress = progress.substr(0,5);
-		this.play.textContent = "Loading: "+progress+"%";
-	};
-	
-	if (this.parent.state == 1)
-	{
-		this.enable();
-	}
-	this.getPresentedId = function()
-	{
-		return this.title.textContent;
-	};
-	this.canMove = function()
-	{
-		return true;
-	};
-}
-
-function outsideReferenceDOM(audioObject,index,inject)
-{
-	this.parent = audioObject;
-	this.outsideReferenceHolder = document.createElement('button');
-	this.outsideReferenceHolder.id = 'outside-reference';
-	this.outsideReferenceHolder.className = 'outside-reference';
-	this.outsideReferenceHolder.setAttribute('track-id',index);
-	this.outsideReferenceHolder.textContent = "Play Reference";
-	this.outsideReferenceHolder.disabled = true;
-	
-	this.outsideReferenceHolder.onclick = function(event)
-	{
-		audioEngineContext.play(event.currentTarget.getAttribute('track-id'));
-		$('.track-slider').removeClass('track-slider-playing');
-        $('.comment-div').removeClass('comment-box-playing');
-        $(event.currentTarget).addClass('track-slider-playing');
-	};
-	inject.appendChild(this.outsideReferenceHolder);
-	this.enable = function()
-	{
-		if (this.parent.state == 1)
-		{
-			this.outsideReferenceHolder.disabled = false;
-		}
-	};
-	this.updateLoading = function(progress)
-	{
-		if (progress != 100)
-		{
-			progress = String(progress);
-			progress = progress.split('.')[0];
-			this.outsideReferenceHolder[0].children[0].textContent = progress+'%';
-		} else {
-			this.outsideReferenceHolder[0].children[0].textContent = "Play Reference";
-		}
-	};
-	this.exportXMLDOM = function(audioObject)
-	{
-		return null;
-	};
-	this.getValue = function()
-	{
-		return 0;
-	};
-	this.getPresentedId = function()
-	{
-		return 'reference';
-	};
-	this.canMove = function()
-	{
-		return false;
-	};
-}
-
-function resizeWindow(event)
-{
-	// Function called when the window has been resized.
-	// MANDATORY FUNCTION
-	
-	var outsideRef = document.getElementById('outside-reference');
-	if(outsideRef != null)
-	{
-		outsideRef.style.left = (window.innerWidth-120)/2 + 'px';
-	}
-	
-	// Auto-align
-	var numObj = document.getElementsByClassName('track-slider').length;
-	var totalWidth = (numObj-1)*150+100;
-	var diff = (window.innerWidth - totalWidth)/2;
-	document.getElementById('slider').style.height = window.innerHeight - 180 + 'px';
-	if (diff <= 0){diff = 0;}
-	document.getElementById('slider-holder').style.marginLeft = diff + 'px';
-	for (var i in audioEngineContext.audioObjects)
-	{
-		if (audioEngineContext.audioObjects[i].specification.type != 'outside-reference'){
-			audioEngineContext.audioObjects[i].interfaceDOM.resize(event);
-		}
-	}
-	document.getElementById('scale-holder').style.marginLeft = (diff-100) + 'px';
-	document.getElementById('scale-text-holder').style.height = window.innerHeight-194 + 'px';
-	var canvas = document.getElementById('scale-canvas');
-	canvas.width = totalWidth;
-	canvas.height = window.innerHeight-194;
-	drawScale();
-}
-
-function drawScale()
-{
-	var interfaceObj = testState.currentStateMap.interfaces[0];
-	var scales = testState.currentStateMap.interfaces[0].scales;
-	scales = scales.sort(function(a,b) {
-		return a.position - b.position;
-	});
-	var canvas = document.getElementById('scale-canvas');
-	var ctx = canvas.getContext("2d");
-	var height = canvas.height;
-	var width = canvas.width;
-	var draw_heights = [24, height-34];
-	var textHolder = document.getElementById('scale-text-holder');
-	textHolder.innerHTML = null;
-	var lastHeight = 0;
-	for (var scale of scales)
-	{
-		var posPercent = scale.position / 100.0;
-		var posPix = (1-posPercent)*(draw_heights[1]-draw_heights[0])+draw_heights[0];
-		ctx.fillStyle = "#000000";
-		ctx.setLineDash([1,2]);
-		ctx.moveTo(0,posPix);
-		ctx.lineTo(width,posPix);
-		ctx.stroke();
-		var text = document.createElement('div');
-		text.align = "right";
-		var textC = document.createElement('span');
-		textC.textContent = scale.text;
-		text.appendChild(textC);
-		text.className = "scale-text";
-		textHolder.appendChild(text);
-		text.style.top = (posPix-9) + 'px';
-		text.style.left = 100 - ($(text).width()+3) + 'px';
-		lastHeight = posPix;
-	}
-}
-
-function buttonSubmitClick() // TODO: Only when all songs have been played!
-{
-	var checks = [];
-	checks = checks.concat(testState.currentStateMap.interfaces[0].options);
-	checks = checks.concat(specification.interfaces.options);
-	var canContinue = true;
-	
-	// Check that the anchor and reference objects are correctly placed
-	if (interfaceContext.checkHiddenAnchor() == false) {return;}
-	if (interfaceContext.checkHiddenReference() == false) {return;}
-	
-	for (var i=0; i<checks.length; i++) {
-		if (checks[i].type == 'check')
-		{
-			switch(checks[i].name) {
-			case 'fragmentPlayed':
-				// Check if all fragments have been played
-				var checkState = interfaceContext.checkAllPlayed();
-				if (checkState == false) {canContinue = false;}
-				break;
-			case  'fragmentFullPlayback':
-				// Check all fragments have been played to their full length
-				var checkState = interfaceContext.checkAllPlayed();
-				if (checkState == false) {canContinue = false;}
-				console.log('NOTE: fragmentFullPlayback not currently implemented, performing check fragmentPlayed instead');
-				break;
-			case 'fragmentMoved':
-				// Check all fragment sliders have been moved.
-				var checkState = interfaceContext.checkAllMoved();
-				if (checkState == false) {canContinue = false;}
-				break;
-			case 'fragmentComments':
-				// Check all fragment sliders have been moved.
-				var checkState = interfaceContext.checkAllCommented();
-				if (checkState == false) {canContinue = false;}
-				break;
-			//case 'scalerange':
-				// Check the scale is used to its full width outlined by the node
-				//var checkState = interfaceContext.checkScaleRange();
-				//if (checkState == false) {canContinue = false;}
-			//	break;
-			default:
-				console.log("WARNING - Check option "+checks[i].check+" is not supported on this interface");
-				break;
-			}
-
-		}
-		if (!canContinue) {break;}
-	}
-	
-    if (canContinue) {
-	    if (audioEngineContext.status == 1) {
-	        var playback = document.getElementById('playback-button');
-	        playback.click();
-	    // This function is called when the submit button is clicked. Will check for any further tests to perform, or any post-test options
-	    } else
-	    {
-	        if (audioEngineContext.timer.testStarted == false)
-	        {
-	            alert('You have not started the test! Please press start to begin the test!');
-	            return;
-	        }
-	    }
-	    testState.advanceState();
-    } 
-}
-
-function pageXMLSave(store, pageSpecification)
-{
-	// MANDATORY
-	// Saves a specific test page
-	// You can use this space to add any extra nodes to your XML <audioHolder> saves
-	// Get the current <page> information in store (remember to appendChild your data to it)
-	// pageSpecification is the current page node configuration
-	// To create new XML nodes, use storage.document.createElement();
-}