changeset 2575:249a1152e525

Merge branch 'master' into Dev_main # Conflicts: # interfaces/AB.js # js/core.js # js/specification.js
author Nicholas Jillings <nicholas.jillings@mail.bcu.ac.uk>
date Tue, 18 Oct 2016 15:49:58 +0100
parents b6bc57a2a681 (current diff) 9b536838a962 (diff)
children d002a342cfaf
files interfaces/AB.js js/core.js js/specification.js tests/examples/project.xml
diffstat 8 files changed, 463 insertions(+), 467 deletions(-) [+]
line wrap: on
line diff
--- a/interfaces/AB.js	Tue Oct 18 15:28:24 2016 +0100
+++ b/interfaces/AB.js	Tue Oct 18 15:49:58 2016 +0100
@@ -2,15 +2,15 @@
 loadInterface();
 
 function loadInterface() {
-    // Get the dimensions of the screen available to the page
-    var width = window.innerWidth;
-    var height = window.innerHeight;
-    interfaceContext.insertPoint.innerHTML = ""; // Clear the current schema
-
-    // Custom comparator Object
-    Interface.prototype.comparator = null;
-
-    Interface.prototype.checkScaleRange = function (min, max) {
+	// Get the dimensions of the screen available to the page
+	var width = window.innerWidth;
+	var height = window.innerHeight;
+	interfaceContext.insertPoint.innerHTML = ""; // Clear the current schema
+	
+	// Custom comparator Object
+	Interface.prototype.comparator = null;
+    
+    Interface.prototype.checkScaleRange = function(min, max) {
         var page = testState.getCurrentTestPage();
         var audioObjects = audioEngineContext.audioObjects;
         var state = true;
@@ -19,170 +19,166 @@
         var maxRanking = -Infinity;
         for (var ao of audioObjects) {
             var rank = ao.interfaceDOM.getValue();
-            if (rank < minRanking) {
-                minRanking = rank;
-            }
-            if (rank > maxRanking) {
-                maxRanking = rank;
-            }
+            if (rank < minRanking) {minRanking = rank;}
+            if (rank > maxRanking) {maxRanking = rank;}
         }
-        if (maxRanking * 100 < max) {
+        if (maxRanking*100 < max) {
             str += "At least one fragment must be selected."
             state = false;
         }
         if (!state) {
             console.log(str);
             this.storeErrorNode(str);
-            interfaceContext.lightbox.post("Message", str);
+            interfaceContext.lightbox.post("Message",str);
         }
         return state;
     }
-
-    // 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');
+	
+	// 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');
     titleSpan.id = "test-title";
-
-    // 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';
+	
+	// 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';
-
+		}
+	};
+	// 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';
+    
     // Create outside reference holder
     var outsideRef = document.createElement("div");
     outsideRef.id = "outside-reference-holder";
-
-    // Construct the AB Boxes
-    var boxes = document.createElement('div');
-    boxes.align = "center";
-    boxes.id = "box-holders";
+	
+	// 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);
-
+	
+	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);
+    
     // Create holder for comment boxes
     var comments = document.createElement("div");
     comments.id = "comment-box-holder";
+	
+	// Inject into HTML
+	testContent.appendChild(title); // Insert the title
+	testContent.appendChild(pagetitle);
+	testContent.appendChild(interfaceButtons);
+    testContent.appendChild(outsideRef);
+	testContent.appendChild(feedbackHolder);
+	testContent.appendChild(submit);
+    testContent.appendChild(comments);
+	interfaceContext.insertPoint.appendChild(testContent);
 
-    // Inject into HTML
-    testContent.appendChild(title); // Insert the title
-    testContent.appendChild(pagetitle);
-    testContent.appendChild(interfaceButtons);
-    testContent.appendChild(outsideRef);
-    testContent.appendChild(feedbackHolder);
-    testContent.appendChild(submit);
-    testContent.appendChild(comments);
-    interfaceContext.insertPoint.appendChild(testContent);
-
-    // Load the full interface
-    testState.initialise();
-    testState.advanceState();
+	// 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];
-
+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];
+    
     var commentHolder = document.getElementById('comment-box-holder');
     commentHolder.innerHTML = "";
-
+    
     // Delete outside reference
-    var outsideReferenceHolder = document.getElementById("outside-reference-holder");
+	var outsideReferenceHolder = document.getElementById("outside-reference-holder");
     outsideReferenceHolder.innerHTML = "";
-
+    
     // Set the page title
     if (typeof audioHolderObject.title == "string" && audioHolderObject.title.length > 0) {
         document.getElementById("test-title").textContent = audioHolderObject.title
     }
-
-    if (interfaceObj.title != null) {
-        document.getElementById("pageTitle").textContent = interfaceObj.title;
-    }
-
+	
+	if(interfaceObj.title != null)
+	{
+		document.getElementById("pageTitle").textContent = interfaceObj.title;
+	}
+    
     var interfaceOptions = specification.interfaces.options.concat(interfaceObj.options);
     // Clear the interfaceElements
     {
         var node = document.getElementById('playback-holder');
-        if (node) {
-            feedbackHolder.removeChild(node);
-        }
+        if (node){feedbackHolder.removeChild(node);}
         node = document.getElementById('page-count');
-        if (node) {
-            document.getElementById('interface-buttons').removeChild(node);
-        }
+        if (node){document.getElementById('interface-buttons').removeChild(node);}
         node = document.getElementById('master-volume-holder-float');
-        if (node) {
-            feedbackHolder.removeChild(node);
-        }
+        if (node){feedbackHolder.removeChild(node);}
     }
-
+    
     // Populate the comparator object
-    interfaceContext.comparator = new comparator(audioHolderObject);
-
-    for (var option of interfaceOptions) {
-        if (option.type == "show") {
-            switch (option.name) {
+	interfaceContext.comparator = new comparator(audioHolderObject);
+    
+    for (var option of interfaceOptions)
+    {
+        if (option.type == "show")
+        {
+            switch(option.name) {
                 case "playhead":
                     var playbackHolder = document.getElementById('playback-holder');
-                    if (playbackHolder == null) {
+                    if (playbackHolder == null)
+                    {
                         playbackHolder = document.createElement('div');
                         playbackHolder.id = 'playback-holder';
                         playbackHolder.style.width = "100%";
@@ -194,329 +190,309 @@
                     break;
                 case "page-count":
                     var pagecountHolder = document.getElementById('page-count');
-                    if (pagecountHolder == null) {
+                    if (pagecountHolder == null)
+                    {
                         pagecountHolder = document.createElement('div');
                         pagecountHolder.id = 'page-count';
                         document.getElementById('interface-buttons').appendChild(pagecountHolder);
                     }
-                    pagecountHolder.innerHTML = '<span>Page ' + (testState.stateIndex + 1) + ' of ' + testState.stateMap.length + '</span>';
+                    pagecountHolder.innerHTML = '<span>Page '+(testState.stateIndex+1)+' of '+testState.stateMap.length+'</span>';
                     break;
                 case "volume":
-                    if (document.getElementById('master-volume-holder-float') == null) {
+                    if (document.getElementById('master-volume-holder-float') == null)
+                    {
                         feedbackHolder.appendChild(interfaceContext.volume.object);
                     }
                     break;
                 case "comments":
                     // Generate one comment box per presented page
-                    for (var element of audioEngineContext.audioObjects) {
+                    for (var element of audioEngineContext.audioObjects)
+                    {
                         interfaceContext.commentBoxes.createCommentBox(element);
                     }
-                    interfaceContext.commentBoxes.showCommentBoxes(commentHolder, true);
+                    interfaceContext.commentBoxes.showCommentBoxes(commentHolder,true);
                     break;
             }
         }
     }
-
-    $(audioHolderObject.commentQuestions).each(function (index, element) {
-        var node = interfaceContext.createCommentQuestion(element);
-        commentHolder.appendChild(node.holder);
-    });
-
-    resizeWindow(null);
+    
+    $(audioHolderObject.commentQuestions).each(function(index,element) {
+		var node = interfaceContext.createCommentQuestion(element);
+		commentHolder.appendChild(node.holder);
+	});
+    
+	resizeWindow(null);
 }
 
-function comparator(audioHolderObject) {
-    this.comparatorBox = function (audioElement, id, text) {
-        this.parent = audioElement;
-        this.id = id;
-        this.value = 0;
-        this.disabled = true;
-        this.box = document.createElement('div');
-        this.box.className = 'comparator-holder';
-        this.box.setAttribute('track-id', audioElement.id);
-        this.box.id = 'comparator-' + text;
-        this.selector = document.createElement('div');
-        this.selector.className = 'comparator-selector disabled';
-        var selectorText = document.createElement('span');
-        selectorText.textContent = text;
-        this.selector.appendChild(selectorText);
-        this.playback = document.createElement('button');
-        this.playback.className = 'comparator-button';
-        this.playback.disabled = true;
-        this.playback.textContent = "Listen";
-        this.box.appendChild(this.selector);
-        this.box.appendChild(this.playback);
-        this.selector.onclick = function (event) {
-            var time = audioEngineContext.timer.getTestTime();
-            if ($(event.currentTarget).hasClass('disabled')) {
-                console.log("Please wait until sample has loaded");
-                return;
+function comparator(audioHolderObject)
+{	
+	this.comparatorBox = function(audioElement,id,text)
+	{
+		this.parent = audioElement;
+		this.id = id;
+		this.value = 0;
+		this.disabled = true;
+		this.box = document.createElement('div');
+		this.box.className = 'comparator-holder';
+		this.box.setAttribute('track-id',audioElement.id);
+		this.box.id = 'comparator-'+text;
+		this.selector = document.createElement('div');
+		this.selector.className = 'comparator-selector disabled';
+		var selectorText = document.createElement('span');
+		selectorText.textContent = text;
+		this.selector.appendChild(selectorText);
+		this.playback = document.createElement('button');
+		this.playback.className = 'comparator-button';
+		this.playback.disabled = true;
+		this.playback.textContent = "Listen";
+		this.box.appendChild(this.selector);
+		this.box.appendChild(this.playback);
+		this.selector.onclick = function(event)
+		{
+			var time = audioEngineContext.timer.getTestTime();
+			if ($(event.currentTarget).hasClass('disabled'))
+			{
+				console.log("Please wait until sample has loaded");
+				return;
+			}
+			if (audioEngineContext.status == 0)
+			{
+				interfaceContext.lightbox.post("Message","Please listen to the samples before making a selection");
+				console.log("Please listen to the samples before making a selection");
+				return;
             }
-            if (audioEngineContext.status == 0) {
-                interfaceContext.lightbox.post("Message", "Please listen to the samples before making a selection");
-                console.log("Please listen to the samples before making a selection");
-                return;
-            }
-            var id = event.currentTarget.parentElement.getAttribute('track-id');
-            interfaceContext.comparator.selected = id;
+			var id = event.currentTarget.parentElement.getAttribute('track-id');
+			interfaceContext.comparator.selected = id;
             if ($(event.currentTarget).hasClass("selected")) {
                 $(".comparator-selector").removeClass('selected');
-                for (var i = 0; i < interfaceContext.comparator.comparators.length; i++) {
+                for (var i=0; i<interfaceContext.comparator.comparators.length; i++)
+                {
                     var obj = interfaceContext.comparator.comparators[i];
-                    obj.parent.metric.moved(time, 0);
+                    obj.parent.metric.moved(time,0);
                     obj.value = 0;
                 }
             } else {
                 $(".comparator-selector").removeClass('selected');
                 $(event.currentTarget).addClass('selected');
-                for (var i = 0; i < interfaceContext.comparator.comparators.length; i++) {
+                for (var i=0; i<interfaceContext.comparator.comparators.length; i++)
+                {
                     var obj = interfaceContext.comparator.comparators[i];
                     if (i == id) {
                         obj.value = 1;
                     } else {
                         obj.value = 0;
                     }
-                    obj.parent.metric.moved(time, obj.value);
+                    obj.parent.metric.moved(time,obj.value);
                 }
-                console.log("Selected " + id + ' (' + time + ')');
+                console.log("Selected "+id+' ('+time+')');
             }
-        };
-        this.playback.setAttribute("playstate", "ready");
-        this.playback.onclick = function (event) {
-            var id = event.currentTarget.parentElement.getAttribute('track-id');
-            if (event.currentTarget.getAttribute("playstate") == "ready") {
+		};
+        this.playback.setAttribute("playstate","ready");
+		this.playback.onclick = function(event)
+		{
+			var id = event.currentTarget.parentElement.getAttribute('track-id');
+            if (event.currentTarget.getAttribute("playstate") == "ready")
+            {
                 audioEngineContext.play(id);
             } else if (event.currentTarget.getAttribute("playstate") == "playing") {
                 audioEngineContext.stop();
             }
-
-        };
-
-        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 = "Play";
-            }
-        };
-        this.error = function () {
+			
+		};
+		
+		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 = "Play";
+			}
+		};
+        this.error = function() {
             // audioObject has an error!!
             this.playback.textContent = "Error";
             $(this.playback).addClass("error-colour");
         }
-        this.startPlayback = function () {
+        this.startPlayback = function()
+        {
             if (this.parent.specification.parent.playOne || specification.playOne) {
                 $('.comparator-button').text('Wait');
-                $('.comparator-button').attr("disabled", "true");
-                $(this.playback).css("disabled", "false");
+                $('.comparator-button').attr("disabled","true");
+                $(this.playback).removeAttr("disabled");
             } else {
                 $('.comparator-button').text('Listen');
             }
             $(this.playback).text('Stop');
-            this.playback.setAttribute("playstate", "playing");
+            this.playback.setAttribute("playstate","playing");
         };
-        this.stopPlayback = function () {
+        this.stopPlayback = function()
+        {
             if (this.playback.getAttribute("playstate") == "playing") {
                 $('.comparator-button').text('Listen');
                 $('.comparator-button').removeAttr("disabled");
-                this.playback.setAttribute("playstate", "ready");
+                this.playback.setAttribute("playstate","ready");
             }
         };
-        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 = "";
-    this.comparators = [];
-    this.selected = null;
-
-    // First generate the Audio Objects for the Audio Engine
-    var label = audioHolderObject.labelStart;
-    if (label == "") {
-        switch (audioHolderObject.label) {
-            case "number":
-                label = "1";
-                break;
-            case "letter":
-                label = "a";
-                break;
-            case "none":
-                label = "";
-                break;
-            case "capital":
-            default:
-                label = "A";
-                break;
-        }
-    } else {
-        switch (audioHolderObject.label) {
-            case "number":
-                if (!isFinite(Number(label))) {
-                    label = "1";
-                }
-                break;
-            case "letter":
-                if (label.charCodeAt(0) < 97 || label.charCodeAt(0) > 122) {
-                    label = "a";
-                }
-                break;
-            case "none":
-                label = "";
-                break;
-            case "capital":
-            default:
-                if (label.charCodeAt(0) < 65 || label.charCodeAt(0) > 90) {
-                    label = "A";
-                }
-                break;
-        }
-    }
-    for (var index = 0; index < audioHolderObject.audioElements.length; index++) {
-        var element = audioHolderObject.audioElements[index];
+		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 = "";
+	this.comparators = [];
+	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];
         var audioObject = audioEngineContext.newTrack(element);
-        if (index == audioHolderObject.outsideReference || element.type == 'outside-reference') {
-            var orNode = new interfaceContext.outsideReferenceDOM(audioObject, index, document.getElementById("outside-reference-holder"));
-            audioObject.bindInterface(orNode);
+		if (index == audioHolderObject.outsideReference || element.type == 'outside-reference')
+		{
+            var orNode = new interfaceContext.outsideReferenceDOM(audioObject,index,document.getElementById("outside-reference-holder"));
+			audioObject.bindInterface(orNode);
         } else {
-            var node = new this.comparatorBox(audioObject, index, label);
-            switch (audioHolderObject.label) {
+            var label;
+            switch(audioObject.specification.parent.label) {
                 case "none":
                     label = "";
                     break;
                 case "number":
-                    label = (Number(label) + 1).toString(10);
+                    label = ""+index;
                     break;
                 case "letter":
-                    label = String.fromCharCode((label.charCodeAt(0) - 96) % 26 + 97);
+                    label = String.fromCharCode(97 + index);
                     break;
-                case "capital":
                 default:
-                    label = String.fromCharCode((label.charCodeAt(0) - 64) % 26 + 65);
+                    label = String.fromCharCode(65 + index);
                     break;
             }
+            var node = new this.comparatorBox(audioObject,index,label);
             audioObject.bindInterface(node);
             this.comparators.push(node);
             this.boxHolders.appendChild(node.box);
         }
-    }
-    return this;
+	}
+	return this;
 }
 
-function resizeWindow(event) {
-    document.getElementById('submit').style.left = (window.innerWidth - 250) / 2 + 'px';
-    var numObj = interfaceContext.comparator.comparators.length;
-    var boxW = numObj * 312;
+function resizeWindow(event)
+{
+	document.getElementById('submit').style.left = (window.innerWidth-250)/2 + 'px';
+	var numObj = interfaceContext.comparator.comparators.length;
+	var boxW = numObj*312;
     var diff = window.innerWidth - boxW;
-    while (diff < 0) {
-        numObj = Math.ceil(numObj / 2);
-        boxW = numObj * 312;
+    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.marginLeft = diff/2 + 'px';
+    document.getElementById('box-holders').style.marginRight = diff/2 + 'px';
     document.getElementById('box-holders').style.width = boxW + 'px';
-
+    
     var outsideRef = document.getElementById('outside-reference');
-    if (outsideRef != null) {
-        outsideRef.style.left = (window.innerWidth - 120) / 2 + 'px';
-    }
+	if(outsideRef != null)
+	{
+		outsideRef.style.left = (window.innerWidth-120)/2 + 'px';
+	}
 }
 
-function buttonSubmitClick() {
-    var checks = [];
-    checks = checks.concat(testState.currentStateMap.interfaces[0].options);
-    checks = checks.concat(specification.interfaces.options);
-    var canContinue = true;
+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;
+            case 'scalerange':
+                // Check the scale has been used effectively
+                var checkState = interfaceContext.checkScaleRange(checks[i].min,checks[i].max);
+                if (checkState == false) {canContinue = false;}
+				break;
+			default:
+				console.log("WARNING - Check option "+checks[i].check+" is not supported on this interface");
+				break;
+			}
 
-    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 has been used effectively
-                    var checkState = interfaceContext.checkScaleRange(checks[i].min, checks[i].max);
-                    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) {
-                interfaceContext.lightbox.post("Warning", 'You have not started the test! Please click play on a sample to begin the test!');
-                return;
-            }
-        }
-        testState.advanceState();
-    }
+		}
+		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)
+	        {
+	            interfaceContext.lightbox.post("Warning",'You have not started the test! Please click play on a sample 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();
-}
+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/interfaces/ABX.js	Tue Oct 18 15:28:24 2016 +0100
+++ b/interfaces/ABX.js	Tue Oct 18 15:49:58 2016 +0100
@@ -323,7 +323,7 @@
             if (this.parent.specification.parent.playOne || specification.playOne) {
                 $('.comparator-button').text('Wait');
                 $('.comparator-button').attr("disabled","true");
-                $(this.playback).css("disabled","false");
+                $(this.playback).removeAttr("disabled");
             } else {
                 $('.comparator-button').text('Listen');
             }
--- a/js/core.js	Tue Oct 18 15:28:24 2016 +0100
+++ b/js/core.js	Tue Oct 18 15:49:58 2016 +0100
@@ -1113,7 +1113,8 @@
             this.postNode();
         } else {
             // Reached the end of the popupOptions
-            this.popupContent.innerHTML = "";
+            this.popupTitle.textContent = "";
+            this.popupResponse.innerHTML = "";
             this.hidePopup();
             for (var node of this.popupOptions) {
                 this.store.postResult(node);
@@ -1387,7 +1388,7 @@
     // because web audio will optimise and any route which does not go to the destination gets ignored.
     this.outputGain = audioContext.createGain();
     this.fooGain = audioContext.createGain();
-    this.fooGain.gain = 0;
+    this.fooGain.gain.value = 0;
 
     // Use this to detect playback state: 0 - stopped, 1 - playing
     this.status = 0;
@@ -1407,6 +1408,14 @@
 
     this.pageStore = null;
 
+    // Chrome 53+ Error solution
+    // Empty buffer for keep-alive
+    var nullBuffer = audioContext.createBuffer(1, audioContext.sampleRate, audioContext.sampleRate);
+    this.nullBufferSource = audioContext.createBufferSource();
+    this.nullBufferSource.buffer = nullBuffer;
+    this.nullBufferSource.loop = true;
+    this.nullBufferSource.start(0);
+
     // Create store for new audioObjects
     this.audioObjects = [];
 
@@ -1559,7 +1568,7 @@
                 } else {
                     var dst = copybuffer.getChannelData(c);
                     for (var n = 0; n < newLength; n++)
-                        dst[n] = src[n + start_sample];
+                        dst[n] = buffer[n + start_sample];
                 }
             }
             return copybuffer;
@@ -1770,6 +1779,7 @@
 
     // Connect buffer to the audio graph
     this.outputGain.connect(audioEngineContext.outputGain);
+    audioEngineContext.nullBufferSource.connect(this.outputGain);
 
     // the audiobuffer is not designed for multi-start playback
     // When stopeed, the buffer node is deleted and recreated with the stored buffer.
@@ -1793,16 +1803,14 @@
         var startTime = this.specification.startTime;
         var stopTime = this.specification.stopTime;
         var copybuffer = new callee.constructor();
-        if (isFinite(startTime) || isFinite(stopTime)) {
-            copybuffer.buffer = callee.cropBuffer(startTime, stopTime);
+
+        copybuffer.buffer = callee.cropBuffer(startTime || 0, stopTime || callee.buffer.duration);
+        if (preSilenceTime != 0 || postSilenceTime != 0) {
+            copybuffer.buffer = copybuffer.copyBuffer(preSilenceTime, postSilenceTime);
         }
-        if (preSilenceTime != 0 || postSilenceTime != 0) {
-            if (copybuffer.buffer == undefined) {
-                copybuffer.buffer = callee.copyBuffer(preSilenceTime, postSilenceTime);
-            } else {
-                copybuffer.buffer = copybuffer.copyBuffer(preSilenceTime, postSilenceTime);
-            }
-        }
+
+        copybuffer.lufs = callee.buffer.lufs;
+        this.buffer = copybuffer;
 
         var targetLUFS = this.specification.parent.loudness || specification.loudness;
         if (typeof targetLUFS === "number" && isFinite(targetLUFS)) {
@@ -1860,14 +1868,21 @@
                     event.currentTarget.owner.stop(audioContext.currentTime + 1);
                 }
             };
+            this.outputGain.gain.cancelScheduledValues(audioContext.currentTime);
             if (!audioEngineContext.loopPlayback || !audioEngineContext.synchPlayback) {
                 this.metric.startListening(audioEngineContext.timer.getTestTime());
-                this.outputGain.gain.setValueAtTime(this.onplayGain, 0.0);
+                this.outputGain.gain.setValueAtTime(this.onplayGain, startTime);
                 this.interfaceDOM.startPlayback();
             } else {
                 this.outputGain.gain.setValueAtTime(0.0, startTime);
             }
-            this.bufferNode.start(startTime, this.specification.startTime || 0, this.specification.stopTime - this.specification.startTime || this.buffer.buffer.duration);
+            if (audioEngineContext.loopPlayback) {
+                this.bufferNode.loopStart = this.specification.startTime || 0;
+                this.bufferNode.loopEnd = this.specification.stopTime - this.specification.startTime || this.buffer.buffer.duration;
+                this.bufferNode.start(startTime);
+            } else {
+                this.bufferNode.start(startTime, this.specification.startTime || 0, this.specification.stopTime - this.specification.startTime || this.buffer.buffer.duration);
+            }
             this.bufferNode.playbackStartTime = audioEngineContext.timer.getTestTime();
         }
     };
@@ -1879,7 +1894,7 @@
             this.bufferNode.stop(stopTime);
             this.bufferNode = undefined;
         }
-        this.outputGain.gain.value = 0.0;
+        this.outputGain.gain.setValueAtTime(0.0, stopTime);
         this.interfaceDOM.stopPlayback();
     };
 
--- a/js/specification.js	Tue Oct 18 15:28:24 2016 +0100
+++ b/js/specification.js	Tue Oct 18 15:49:58 2016 +0100
@@ -495,83 +495,88 @@
 
         this.decode = function (parent, xml) {
             this.parent = parent;
-            var attributeMap = this.schema.getAllElementsByTagName('xs:attribute');
-            for (var i = 0; i < attributeMap.length; i++) {
-                var attributeName = attributeMap[i].getAttribute('name') || attributeMap[i].getAttribute('ref');
-                var projectAttr = xml.getAttribute(attributeName);
-                projectAttr = parent.processAttribute(projectAttr, attributeMap[i], parent.schema);
-                switch (typeof projectAttr) {
-                    case "number":
-                    case "boolean":
-                        eval('this.' + attributeName + ' = ' + projectAttr);
-                        break;
-                    case "string":
-                        eval('this.' + attributeName + ' = "' + projectAttr + '"');
-                        break;
-                }
-            }
-
+			var attributeMap = this.schema.getAllElementsByTagName('xs:attribute');
+			for (var i=0; i<attributeMap.length; i++)
+			{
+				var attributeName = attributeMap[i].getAttribute('name') || attributeMap[i].getAttribute('ref');
+				var projectAttr = xml.getAttribute(attributeName);
+				projectAttr = parent.processAttribute(projectAttr,attributeMap[i],parent.schema);
+				switch(typeof projectAttr)
+				{
+				case "number":
+				case "boolean":
+					eval('this.'+attributeName+' = '+projectAttr);
+					break;
+				case "string":
+					eval('this.'+attributeName+' = "'+projectAttr+'"');
+					break;
+				}
+			}
+            
             // Get the title
             var title = xml.getElementsByTagName('title');
-            if (title.length != 0) {
+            if (title.length != 0 && title[0].parentElement == xml) {
                 this.title = title[0].textContent;
             }
-
-            // Get the Comment Box Prefix
-            var CBP = xml.getElementsByTagName('commentboxprefix');
-            if (CBP.length != 0) {
-                this.commentBoxPrefix = CBP[0].textContent;
-            }
-
-            // Now decode the interfaces
-            var interfaceNode = xml.getElementsByTagName('interface');
-            for (var i = 0; i < interfaceNode.length; i++) {
-                var node = new parent.interfaceNode(this.specification);
-                node.decode(this, interfaceNode[i], parent.schema.getAllElementsByName('interface')[1]);
-                this.interfaces.push(node);
-            }
-
-            // Now process the survey node options
-            var survey = xml.getElementsByTagName('survey');
-            var surveySchema = parent.schema.getAllElementsByName('survey')[0];
-            for (var i = 0; i < survey.length; i++) {
-                var location = survey[i].getAttribute('location');
-                if (location == 'pre' || location == 'before') {
-                    if (this.preTest != null) {
-                        this.errors.push("Already a pre/before test survey defined! Ignoring second!!");
-                    } else {
-                        this.preTest = new parent.surveyNode(this.specification);
-                        this.preTest.decode(parent, survey[i], surveySchema);
-                    }
-                } else if (location == 'post' || location == 'after') {
-                    if (this.postTest != null) {
-                        this.errors.push("Already a post/after test survey defined! Ignoring second!!");
-                    } else {
-                        this.postTest = new parent.surveyNode(this.specification);
-                        this.postTest.decode(parent, survey[i], surveySchema);
-                    }
-                }
-            }
-
-            // Now process the audioelement tags
-            var audioElements = xml.getElementsByTagName('audioelement');
-            for (var i = 0; i < audioElements.length; i++) {
-                var node = new this.audioElementNode(this.specification);
-                node.decode(this, audioElements[i]);
-                this.audioElements.push(node);
-            }
-
-            // Now decode the commentquestions
-            var commentQuestions = xml.getElementsByTagName('commentquestion');
-            for (var i = 0; i < commentQuestions.length; i++) {
-                var node = new this.commentQuestionNode(this.specification);
-                node.decode(parent, commentQuestions[i]);
-                this.commentQuestions.push(node);
-            }
-        };
-
-        this.encode = function (root) {
-            var AHNode = root.createElement("page");
+			
+			// Get the Comment Box Prefix
+			var CBP = xml.getElementsByTagName('commentboxprefix');
+			if (CBP.length != 0 && CBP[0].parentElement == xml) {
+				this.commentBoxPrefix = CBP[0].textContent;
+			}
+			
+			// Now decode the interfaces
+			var interfaceNode = xml.getElementsByTagName('interface');
+			for (var i=0; i<interfaceNode.length; i++)
+			{
+				var node = new parent.interfaceNode(this.specification);
+				node.decode(this,interfaceNode[i],parent.schema.getAllElementsByName('interface')[1]);
+				this.interfaces.push(node);
+			}
+			
+			// Now process the survey node options
+			var survey = xml.getElementsByTagName('survey');
+			var surveySchema = parent.schema.getAllElementsByName('survey')[0];
+			for (var i=0; i<survey.length; i++){
+				var location = survey[i].getAttribute('location');
+				if (location == 'pre' || location == 'before')
+				{
+					if (this.preTest != null){this.errors.push("Already a pre/before test survey defined! Ignoring second!!");}
+					else {
+						this.preTest = new parent.surveyNode(this.specification);
+						this.preTest.decode(parent,survey[i],surveySchema);
+					}
+				} else if (location == 'post' || location == 'after') {
+					if (this.postTest != null){this.errors.push("Already a post/after test survey defined! Ignoring second!!");}
+					else {
+						this.postTest = new parent.surveyNode(this.specification);
+						this.postTest.decode(parent,survey[i],surveySchema);
+					}
+				}
+			}
+			
+			// Now process the audioelement tags
+			var audioElements = xml.getElementsByTagName('audioelement');
+			for (var i=0; i<audioElements.length; i++)
+			{
+				var node = new this.audioElementNode(this.specification);
+				node.decode(this,audioElements[i]);
+				this.audioElements.push(node);
+			}
+			
+			// Now decode the commentquestions
+			var commentQuestions = xml.getElementsByTagName('commentquestion');
+			for (var i=0; i<commentQuestions.length; i++)
+			{
+				var node = new this.commentQuestionNode(this.specification);
+				node.decode(parent,commentQuestions[i]);
+				this.commentQuestions.push(node);
+			}
+		};
+		
+		this.encode = function(root)
+		{
+			var AHNode = root.createElement("page");
             // First decode the attributes
             var attributes = this.schema.getAllElementsByTagName('xs:attribute');
             for (var i = 0; i < attributes.length; i++) {
--- a/tests/examples/ABX_example.xml	Tue Oct 18 15:28:24 2016 +0100
+++ b/tests/examples/ABX_example.xml	Tue Oct 18 15:49:58 2016 +0100
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <waet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="test-schema.xsd">
-	<setup interface="ABX" projectReturn="save.php" randomiseOrder='true' poolSize="2" loudness="-23" sampleRate="44100">
+	<setup interface="ABX" projectReturn="save.php" randomiseOrder='true' poolSize="2" loudness="-23">
 		<survey location="before">
 			<surveyentry type="question" id="sessionId" mandatory="true">
 				<statement>Please enter your name.</statement>
--- a/tests/examples/AB_example.xml	Tue Oct 18 15:28:24 2016 +0100
+++ b/tests/examples/AB_example.xml	Tue Oct 18 15:49:58 2016 +0100
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <waet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="test-schema.xsd">
-	<setup interface="AB" projectReturn="save.php" randomiseOrder='true' poolSize="2" loudness="-23" sampleRate="44100" playOne="true">
+	<setup interface="AB" projectReturn="save.php" randomiseOrder='true' poolSize="2" loudness="-23" playOne="true">
 		<survey location="before">
 			<surveyentry type="question" id="sessionId" mandatory="true">
 				<statement>Please enter your name.</statement>
--- a/tests/examples/mushra_example.xml	Tue Oct 18 15:28:24 2016 +0100
+++ b/tests/examples/mushra_example.xml	Tue Oct 18 15:49:58 2016 +0100
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <waet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="test-schema.xsd">
-	<setup interface="MUSHRA" projectReturn="save.php" randomiseOrder='true' poolSize="2" loudness="-23" sampleRate="44100">
+	<setup interface="MUSHRA" projectReturn="save.php" randomiseOrder='true' poolSize="2" loudness="-23">
         <exitText>Thank you for looking at WAET. You can modify the successful completion text as well!</exitText>
 		<survey location="before">
 			<surveyentry type="question" id="sessionId" mandatory="true">
--- a/tests/examples/project.xml	Tue Oct 18 15:28:24 2016 +0100
+++ b/tests/examples/project.xml	Tue Oct 18 15:49:58 2016 +0100
@@ -94,7 +94,7 @@
 		</survey>
 	</page>
 	<page id='test-1' hostURL="media/example/" randomiseOrder='true' repeatCount='0' loop='false' label="letter">
-		<commentboxprefix>Comment on fragment</commentboxprefix>
+        <commentboxprefix>Comment on fragment</commentboxprefix>
 		<interface name="preference">
 			<title>Example Test Question</title>
 			<scales>