changeset 2720:a6a0d2b786af

Merge branch 'vnext' into hot_fix # Conflicts: # js/core.js
author Nicholas Jillings <n.g.r.jillings@se14.qmul.ac.uk>
date Fri, 14 Apr 2017 16:28:19 +0100
parents 04cc7a27ae64 (current diff) d30222a70f2d (diff)
children 2bc86ec0ac6c a087cb7b5972
files js/core.js
diffstat 13 files changed, 1541 insertions(+), 1646 deletions(-) [+]
line wrap: on
line diff
--- a/css/core.css	Fri Apr 14 16:25:26 2017 +0100
+++ b/css/core.css	Fri Apr 14 16:28:19 2017 +0100
@@ -261,3 +261,15 @@
 div.comment-slider-text-holder span {
     margin: 0px 5px;
 }
+div.comment-checkbox-inputs-holder {
+    display: flex;
+    flex-direction: row;
+    justify-content: space-around;
+    margin: 10px 5px;
+}
+div.comment-checkbox-inputs-flex {
+    display: flex;
+    flex-direction: column;
+    justify-content: space-between;
+    align-items: center;
+}
\ No newline at end of file
--- a/interfaces/AB.js	Fri Apr 14 16:25:26 2017 +0100
+++ b/interfaces/AB.js	Fri Apr 14 16:28:19 2017 +0100
@@ -1,6 +1,6 @@
 // Once this is loaded and parsed, begin execution
 loadInterface();
-
+/*globals window, interfaceContext, testState, Interface, audioEngineContext, console, document, specification, $, storage*/
 function loadInterface() {
     // Get the dimensions of the screen available to the page
     var width = window.innerWidth;
@@ -10,34 +10,6 @@
     // Custom comparator Object
     Interface.prototype.comparator = null;
 
-    Interface.prototype.checkScaleRange = function (min, max) {
-        var page = testState.getCurrentTestPage();
-        var audioObjects = audioEngineContext.audioObjects;
-        var state = true;
-        var str = "Please keep listening. ";
-        var minRanking = Infinity;
-        var maxRanking = -Infinity;
-        for (var ao of audioObjects) {
-            var rank = ao.interfaceDOM.getValue();
-            if (rank < minRanking) {
-                minRanking = rank;
-            }
-            if (rank > maxRanking) {
-                maxRanking = rank;
-            }
-        }
-        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);
-        }
-        return state;
-    }
-
     // The injection point into the HTML page
     interfaceContext.insertPoint = document.getElementById("topLevelBody");
     var testContent = document.createElement('div');
@@ -52,7 +24,7 @@
     titleSpan.id = "test-title";
 
     // Set title to that defined in XML, else set to default
-    if (titleAttr != undefined) {
+    if (titleAttr !== undefined) {
         titleSpan.textContent = titleAttr;
     } else {
         titleSpan.textContent = 'Listening test';
@@ -63,7 +35,8 @@
     var pagetitle = document.createElement('div');
     pagetitle.className = "pageTitle";
     pagetitle.align = "center";
-    var titleSpan = document.createElement('span');
+
+    titleSpan = document.createElement('span');
     titleSpan.id = "pageTitle";
     pagetitle.appendChild(titleSpan);
 
@@ -150,10 +123,10 @@
 
     // Set the page title
     if (typeof audioHolderObject.title == "string" && audioHolderObject.title.length > 0) {
-        document.getElementById("test-title").textContent = audioHolderObject.title
+        document.getElementById("test-title").textContent = audioHolderObject.title;
     }
 
-    if (interfaceObj.title != null) {
+    if (interfaceObj.title !== null) {
         document.getElementById("pageTitle").textContent = interfaceObj.title;
     }
 
@@ -182,7 +155,7 @@
             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,7 +167,7 @@
                     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);
@@ -202,7 +175,7 @@
                     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;
@@ -246,51 +219,50 @@
         this.playback.textContent = "Listen";
         this.box.appendChild(this.selector);
         this.box.appendChild(this.playback);
-        this.selector.onclick = function (event) {
+        this.selectorClicked = function () {
+            var i;
             var time = audioEngineContext.timer.getTestTime();
-            if ($(event.currentTarget).hasClass('disabled')) {
+            if (this.parent.state !== 1) {
+                interfaceContext.lightbox.post("Message", "Please wait for the sample to load");
                 console.log("Please wait until sample has loaded");
                 return;
             }
-            if (audioEngineContext.status == 0) {
+            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;
-            if ($(event.currentTarget).hasClass("selected")) {
-                $(".comparator-selector").removeClass('selected');
-                for (var i = 0; i < interfaceContext.comparator.comparators.length; i++) {
-                    var obj = interfaceContext.comparator.comparators[i];
-                    obj.parent.metric.moved(time, 0);
-                    obj.value = 0;
+            interfaceContext.comparator.selected = this.id;
+            $(".comparator-selector").removeClass('selected');
+            $(this.selector).addClass('selected');
+            this.comparator.comparators.forEach(function (a) {
+                if (a !== this) {
+                    a.value = 0;
+                } else {
+                    a.value = 1;
                 }
-            } else {
-                $(".comparator-selector").removeClass('selected');
-                $(event.currentTarget).addClass('selected');
-                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);
-                }
-                console.log("Selected " + id + ' (' + time + ')');
-            }
+                a.parent.metric.moved(time, a.value);
+            }, this);
+            console.log("Selected " + this.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") {
-                audioEngineContext.play(id);
-            } else if (event.currentTarget.getAttribute("playstate") == "playing") {
+        this.playbackClicked = function () {
+            if (this.playback.getAttribute("playstate") == "ready") {
+                audioEngineContext.play(this.id);
+            } else if (this.playback.getAttribute("playstate") == "playing") {
                 audioEngineContext.stop();
             }
 
         };
+        this.handleEvent = function (event) {
+            if (event.currentTarget === this.selector) {
+                this.selectorClicked();
+            } else if (event.currentTarget === this.playback) {
+                this.playbackClicked();
+            }
+        }
+        this.playback.addEventListener("click", this);
+        this.selector.addEventListener("click", this);
 
         this.enable = function () {
             if (this.parent.state == 1) {
@@ -311,7 +283,7 @@
             // audioObject has an error!!
             this.playback.textContent = "Error";
             $(this.playback).addClass("error-colour");
-        }
+        };
         this.startPlayback = function () {
             if (this.parent.specification.parent.playOne || specification.playOne) {
                 $('.comparator-button').text('Wait');
@@ -369,6 +341,11 @@
                 label = interfaceContext.getLabel(labelType, index, audioHolderObject.labelStart);
             }
             var node = new this.comparatorBox(audioObject, index, label);
+            Object.defineProperties(node, {
+                'comparator': {
+                    'value': this
+                }
+            });
             audioObject.bindInterface(node);
             this.comparators.push(node);
             this.boxHolders.appendChild(node.box);
@@ -392,7 +369,7 @@
     document.getElementById('box-holders').style.width = boxW + 'px';
 
     var outsideRef = document.getElementById('outside-reference');
-    if (outsideRef != null) {
+    if (outsideRef !== null) {
         outsideRef.style.left = (window.innerWidth - 120) / 2 + 'px';
     }
 }
@@ -403,41 +380,39 @@
 
     for (var i = 0; i < checks.length; i++) {
         if (checks[i].type == 'check') {
+            var checkState;
             switch (checks[i].name) {
                 case 'fragmentPlayed':
                     // Check if all fragments have been played
-                    var checkState = interfaceContext.checkAllPlayed();
-                    if (checkState == false) {
+                    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) {
+                    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) {
+                    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) {
+                    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;
-                    }
+                    console.log("WARNING - Check 'scalerange' does not make sense in AB/ABX! Ignoring!");
                     break;
                 default:
                     console.log("WARNING - Check option " + checks[i].check + " is not supported on this interface");
@@ -455,7 +430,7 @@
             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) {
+            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;
             }
--- a/interfaces/ABX.js	Fri Apr 14 16:25:26 2017 +0100
+++ b/interfaces/ABX.js	Fri Apr 14 16:28:19 2017 +0100
@@ -4,6 +4,7 @@
  */
 
 // Once this is loaded and parsed, begin execution
+/* globals interfaceContext, Interface, testState, audioEngineContext, console, document, window, feedbackHolder, $, specification, storage*/
 loadInterface();
 
 function loadInterface() {
@@ -12,34 +13,6 @@
 
     interfaceContext.insertPoint.innerHTML = ""; // Clear the current schema
 
-    Interface.prototype.checkScaleRange = function (min, max) {
-        var page = testState.getCurrentTestPage();
-        var audioObjects = audioEngineContext.audioObjects;
-        var state = true;
-        var str = "Please keep listening. ";
-        var minRanking = Infinity;
-        var maxRanking = -Infinity;
-        for (var ao of audioObjects) {
-            var rank = ao.interfaceDOM.getValue();
-            if (rank < minRanking) {
-                minRanking = rank;
-            }
-            if (rank > maxRanking) {
-                maxRanking = rank;
-            }
-        }
-        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);
-        }
-        return state;
-    }
-
     // Custom comparator Object
     Interface.prototype.comparator = null;
 
@@ -57,7 +30,7 @@
     titleSpan.id = "test-title";
 
     // Set title to that defined in XML, else set to default
-    if (titleAttr != undefined) {
+    if (titleAttr !== undefined) {
         titleSpan.textContent = titleAttr;
     } else {
         titleSpan.textContent = 'Listening test';
@@ -68,7 +41,8 @@
     var pagetitle = document.createElement('div');
     pagetitle.className = "pageTitle";
     pagetitle.align = "center";
-    var titleSpan = document.createElement('span');
+
+    titleSpan = document.createElement('span');
     titleSpan.id = "pageTitle";
     pagetitle.appendChild(titleSpan);
 
@@ -131,7 +105,7 @@
     // Load the full interface
     testState.initialise();
     testState.advanceState();
-};
+}
 
 function loadTest(page) {
     // Called each time a new test page is to be build. The page specification node is the only item passed in
@@ -148,10 +122,10 @@
 
     // Set the page title
     if (typeof page.title == "string" && page.title.length > 0) {
-        document.getElementById("test-title").textContent = page.title
+        document.getElementById("test-title").textContent = page.title;
     }
 
-    if (interfaceObj.title != null) {
+    if (interfaceObj.title !== null) {
         document.getElementById("pageTitle").textContent = interfaceObj.title;
     }
 
@@ -163,7 +137,7 @@
             switch (option.name) {
                 case "playhead":
                     var playbackHolder = document.getElementById('playback-holder');
-                    if (playbackHolder == null) {
+                    if (playbackHolder === null) {
                         playbackHolder = document.createElement('div');
                         playbackHolder.style.width = "100%";
                         playbackHolder.style.float = "left";
@@ -174,7 +148,7 @@
                     break;
                 case "page-count":
                     var pagecountHolder = document.getElementById('page-count');
-                    if (pagecountHolder == null) {
+                    if (pagecountHolder === null) {
                         pagecountHolder = document.createElement('div');
                         pagecountHolder.id = 'page-count';
                     }
@@ -183,7 +157,7 @@
                     inject.appendChild(pagecountHolder);
                     break;
                 case "volume":
-                    if (document.getElementById('master-volume-holder') == null) {
+                    if (document.getElementById('master-volume-holder') === null) {
                         feedbackHolder.appendChild(interfaceContext.volume.object);
                     }
                     break;
@@ -231,60 +205,54 @@
         this.playback.textContent = "Listen";
         this.box.appendChild(this.selector);
         this.box.appendChild(this.playback);
-        this.selector.onclick = function (event) {
-            var label = event.currentTarget.children[0].textContent;
+        this.selectorClicked = function (event) {
             if (label == "X" || label == "x") {
                 return;
             }
             var time = audioEngineContext.timer.getTestTime();
-            if ($(event.currentTarget).hasClass('disabled')) {
+            if (this.disabled) {
+                interfaceContext.lightbox.post("Message", "Please wait until sample has loaded");
                 console.log("Please wait until sample has loaded");
                 return;
             }
-            if (audioEngineContext.status == 0) {
+            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;
-            if ($(event.currentTarget).hasClass("selected")) {
-                $(".comparator-selector").removeClass('selected');
-                for (var i = 0; i < interfaceContext.comparator.pair.length; i++) {
-                    var obj = interfaceContext.comparator.pair[i];
-                    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.pair.length; i++) {
-                    var obj = interfaceContext.comparator.pair[i];
-                    if (i == id) {
-                        obj.value = 1;
-                    } else {
-                        obj.value = 0;
-                    }
-                    obj.parent.metric.moved(time, obj.value);
-                }
-                console.log("Selected " + id + ' (' + time + ')');
-            }
+            interfaceContext.comparator.selected = this.id;
+            $(".comparator-selector").removeClass('selected');
+            $(this.selector).addClass('selected');
+            interfaceContext.comparator.pair.forEach(function (obj) {
+                obj.value = 1.0 * obj === this;
+                obj.parent.metric.moved(time, obj.value);
+            });
+            console.log("Selected " + this.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") {
-                audioEngineContext.play(id);
+        this.playbackClicked = function (event) {
+            if (this.playback.getAttribute("playstate") == "ready") {
+                audioEngineContext.play(this.id);
             } else if (event.currentTarget.getAttribute("playstate") == "playing") {
                 audioEngineContext.stop();
             }
 
         };
+        this.handleEvent = function (event) {
+            if (event.currentTarget === this.playback) {
+                this.playbackClicked(event);
+            } else if (event.currentTarget === this.selector) {
+                this.selectorClicked(event);
+            }
+        };
+        this.playback.addEventListener("click", this);
+        this.selector.addEventListener("click", this);
         this.enable = function () {
             // This is used to tell the interface object that playback of this node is ready
             if (this.parent.state == 1) {
                 $(this.selector).removeClass('disabled');
                 this.playback.disabled = false;
+                this.disabled = false;
             }
         };
         this.updateLoading = function (progress) {
@@ -345,7 +313,7 @@
         };
         this.error = function () {
             // If there is an error with the audioObject, this will be called to indicate a failure
-        }
+        };
     };
     // Ensure there are only two comparisons per page
     if (page.audioElements.length != 2) {
@@ -353,31 +321,42 @@
         return;
     }
     // Build the three audio elements
+
+    function buildElement(index, audioObject) {
+        var label;
+        switch (index) {
+            case 0:
+                label = "A";
+                break;
+            case 1:
+                label = "B";
+                break;
+            default:
+                label = "X";
+                break;
+        }
+        var node = new this.interfaceObject(audioObject, label);
+        audioObject.bindInterface(node);
+        return node;
+    }
+
     this.pair = [];
     this.X = null;
     this.boxHolders = document.getElementById('box-holders');
-    for (var index = 0; index < page.audioElements.length; index++) {
-        var element = page.audioElements[index];
+    var node;
+    page.audioElements.forEach(function (element, index) {
         if (element.type != 'normal') {
             console.log("WARNING - ABX can only have normal elements. Page " + page.id + ", Element " + element.id);
             element.type = "normal";
         }
-        var audioObject = audioEngineContext.newTrack(element);
-        var label;
-        if (index == 0) {
-            label = "A";
-        } else {
-            label = "B";
-        }
-        var node = new this.interfaceObject(audioObject, label);
-        audioObject.bindInterface(node);
+        node = buildElement.call(this, index, audioEngineContext.newTrack(element));
         this.pair.push(node);
         this.boxHolders.appendChild(node.box);
-    }
+    }, this);
     var elementId = Math.floor(Math.random() * 2); //Randomly pick A or B to be X
     var element = new page.audioElementNode(specification);
     for (var atr in page.audioElements[elementId]) {
-        eval("element." + atr + " = page.audioElements[elementId]." + atr);
+        element[atr] = page.audioElements[elementId][atr];
     }
     element.id += "-X";
     if (typeof element.name == "string") {
@@ -397,17 +376,9 @@
     aeNode.appendChild(storage.document.createElement('metric'));
     root.appendChild(aeNode);
     // Build the 'X' element
+    var label;
     var audioObject = audioEngineContext.newTrack(element);
-    var label;
-    switch (audioObject.specification.parent.label) {
-        case "letter":
-            label = "x";
-            break;
-        default:
-            label = "X";
-            break;
-    }
-    var node = new this.interfaceObject(audioObject, label);
+    node = buildElement.call(this, 3, audioObject);
     node.box.children[0].classList.add('inactive');
     audioObject.bindInterface(node);
     this.X = node;
@@ -434,42 +405,28 @@
         canContinue = true;
 
     for (var i = 0; i < checks.length; i++) {
+        var checkState = true;
         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;
-                    }
+                    checkState = interfaceContext.checkAllPlayed();
+
                     break;
                 case 'fragmentFullPlayback':
                     // Check all fragments have been played to their full length
-                    var checkState = interfaceContext.checkFragmentsFullyPlayed();
-                    if (checkState == false) {
-                        canContinue = false;
-                    }
+                    checkState = interfaceContext.checkFragmentsFullyPlayed();
                     break;
                 case 'fragmentMoved':
                     // Check all fragment sliders have been moved.
-                    var checkState = interfaceContext.checkAllMoved();
-                    if (checkState == false) {
-                        canContinue = false;
-                    }
+                    checkState = interfaceContext.checkAllMoved();
                     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;
-                    }
+                    console.log("WARNING - Check 'scalerange' does not make sense in AB/ABX! Ignoring!");
                     break;
                 default:
                     console.log("WARNING - Check option " + checks[i].check + " is not supported on this interface");
@@ -477,17 +434,19 @@
             }
 
         }
-        if (!canContinue) {
+        if (checkState === false) {
+            canContinue = false;
             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) {
+            if (audioEngineContext.timer.testStarted === false) {
                 interfaceContext.lightbox.post("Warning", 'You have not started the test! Please listen to a sample to begin the test!');
                 return;
             }
--- a/interfaces/ape.js	Fri Apr 14 16:25:26 2017 +0100
+++ b/interfaces/ape.js	Fri Apr 14 16:28:19 2017 +0100
@@ -3,7 +3,8 @@
  *  Create the APE interface
  */
 
-
+/*globals window,interfaceContext, document, audioEngineContext, console, $, Interface, testState, storage, specification */
+/*globals metricTracker */
 // Once this is loaded and parsed, begin execution
 loadInterface();
 
@@ -21,7 +22,7 @@
 
     // Bindings for interfaceContext
     interfaceContext.checkAllPlayed = function () {
-        hasBeenPlayed = audioEngineContext.checkAllPlayed();
+        var hasBeenPlayed = audioEngineContext.checkAllPlayed();
         if (hasBeenPlayed.length > 0) // if a fragment has not been played yet
         {
             var str = "";
@@ -53,14 +54,14 @@
             var interfaceTID = [];
             for (var j = 0; j < this.interfaceSliders[i].metrics.length; j++) {
                 var ao_id = this.interfaceSliders[i].sliders[j].getAttribute("trackIndex");
-                if (this.interfaceSliders[i].metrics[j].wasMoved == false && audioEngineContext.audioObjects[ao_id].interfaceDOM.canMove()) {
+                if (this.interfaceSliders[i].metrics[j].wasMoved === false && audioEngineContext.audioObjects[ao_id].interfaceDOM.canMove()) {
                     state = false;
                     interfaceTID.push(j);
                 }
             }
-            if (interfaceTID.length != 0) {
+            if (interfaceTID.length !== 0) {
                 var interfaceName = this.interfaceSliders[i].interfaceObject.title;
-                if (interfaceName == undefined) {
+                if (interfaceName === undefined) {
                     str += 'On axis ' + String(i + 1) + ' you must move ';
                 } else {
                     str += 'On axis "' + interfaceName + '" you must move ';
@@ -76,7 +77,7 @@
                 }
             }
         }
-        if (state != true) {
+        if (state !== true) {
             this.storeErrorNode(str);
             interfaceContext.lightbox.post("Message", str);
             console.log(str);
@@ -84,75 +85,38 @@
         return state;
     };
 
-    Interface.prototype.checkAllCommented = function () {
+    interfaceContext.checkScaleRange = 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) {
-                var str = "";
-                if (strNums.length > 1) {
-
-                    for (var i = 0; i < strNums.length; i++) {
-                        var ao_id = audioEngineContext.audioObjects[strNums[i]].interfaceDOM.getPresentedId();
-                        str = str + (ao_id); // start from 1
-                        if (i < strNums.length - 2) {
-                            str += ", ";
-                        } else if (i == strNums.length - 2) {
-                            str += " or ";
-                        }
-                    }
-                    str = 'You have not commented on fragments ' + str + ' yet. Please listen, rate and comment all samples before submitting.';
-                } else {
-                    str = 'You have not commented on fragment ' + (audioEngineContext.audioObjects[strNums[0]].interfaceDOM.getPresentedId()) + ' yet. Please listen, rate and comment all samples before submitting.';
-                }
-                this.storeErrorNode(str);
-                interfaceContext.lightbox.post("Message", str);
-                console.log(str);
-            }
-        }
-        return state;
-    };
-
-    Interface.prototype.checkScaleRange = function () {
-        var audioObjs = audioEngineContext.audioObjects;
-        var audioHolder = testState.stateMap[testState.stateIndex];
+        var interfaceObject = this.interfaceSliders[0].interfaceObject;
         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;
+        this.interfaceSliders.forEach(function (sliderHolder, i) {
+            var scales = (function () {
+                var scaleRange = interfaceObject.options.find(function (a) {
+                    return a.name == "scalerange";
+                });
+                return {
+                    min: scaleRange.min,
+                    max: scaleRange.max
+                };
+            })();
+            var range = sliderHolder.sliders.reduce(function (a, b) {
+                var v = convSliderPosToRate(b) * 100.0;
+                return {
+                    min: Math.min(a.min, v),
+                    max: Math.max(a.max, v)
                 }
+            }, {
+                min: 100,
+                max: 0
+            });
+            if (range.min >= scales.min || range.max <= scales.max) {
+                state = false;
+                str += 'On axis "' + sliderHolder.interfaceObject.title + '" you have not used the full width of the scale. ';
             }
-            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) {
+        });
+        if (state !== true) {
             this.storeErrorNode(str);
             interfaceContext.lightbox.post("Message", str);
             console.log(str);
@@ -163,13 +127,13 @@
     Interface.prototype.objectSelected = null;
     Interface.prototype.objectMoved = false;
     Interface.prototype.selectObject = function (object) {
-        if (this.objectSelected == null) {
+        if (this.objectSelected === null) {
             this.objectSelected = object;
             this.objectMoved = false;
         }
     };
     Interface.prototype.moveObject = function () {
-        if (this.objectMoved == false) {
+        if (this.objectMoved === false) {
             this.objectMoved = true;
         }
     };
@@ -198,7 +162,7 @@
     titleSpan.id = "test-title";
 
     // Set title to that defined in XML, else set to default
-    if (titleAttr != undefined) {
+    if (titleAttr !== undefined) {
         titleSpan.textContent = titleAttr;
     } else {
         titleSpan.textContent = 'Listening test';
@@ -274,12 +238,12 @@
     sliderHolder.innerHTML = "";
 
     // Set labelType if default to number
-    if (audioHolderObject.label == "default" || audioHolderObject.label == "") {
+    if (audioHolderObject.label === "default" || audioHolderObject.label === "") {
         audioHolderObject.label = "number";
     }
     // Set the page title
     if (typeof audioHolderObject.title == "string" && audioHolderObject.title.length > 0) {
-        document.getElementById("test-title").textContent = audioHolderObject.title
+        document.getElementById("test-title").textContent = audioHolderObject.title;
     }
 
 
@@ -287,17 +251,17 @@
     document.getElementById("outside-reference-holder").innerHTML = "";
 
     var interfaceObj = interfaceContext.getCombinedInterfaces(audioHolderObject);
-    for (var k = 0; k < interfaceObj.length; k++) {
+    interfaceObj.forEach(function (interfaceObjectInstance) {
         // Create the div box to center align
-        interfaceContext.interfaceSliders.push(new interfaceSliderHolder(interfaceObj[k]));
-    }
+        interfaceContext.interfaceSliders.push(new interfaceSliderHolder(interfaceObjectInstance));
+    });
     interfaceObj.forEach(function (interface) {
-        for (var option of interface.options) {
+        interface.options.forEach(function (option) {
             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.style.width = "100%";
                             playbackHolder.align = 'center';
@@ -307,7 +271,7 @@
                         break;
                     case "page-count":
                         var pagecountHolder = document.getElementById('page-count');
-                        if (pagecountHolder == null) {
+                        if (pagecountHolder === null) {
                             pagecountHolder = document.createElement('div');
                             pagecountHolder.id = 'page-count';
                         }
@@ -316,7 +280,7 @@
                         inject.appendChild(pagecountHolder);
                         break;
                     case "volume":
-                        if (document.getElementById('master-volume-holder') == null) {
+                        if (document.getElementById('master-volume-holder') === null) {
                             feedbackHolder.appendChild(interfaceContext.volume.object);
                         }
                         break;
@@ -325,7 +289,7 @@
                         break;
                 }
             }
-        }
+        });
     });
 
     var commentBoxPrefix = "Comment on fragment";
@@ -334,7 +298,7 @@
 
     var loopPlayback = audioHolderObject.loop;
 
-    currentTestHolder = document.createElement('audioHolder');
+    var currentTestHolder = document.createElement('audioHolder');
     currentTestHolder.id = audioHolderObject.id;
     currentTestHolder.repeatCount = audioHolderObject.repeatCount;
 
@@ -372,7 +336,7 @@
     $('.slider').mousemove(function (event) {
         event.preventDefault();
         var obj = interfaceContext.getSelectedObject();
-        if (obj == null) {
+        if (obj === null) {
             return;
         }
         var move = event.clientX - 6;
@@ -386,7 +350,7 @@
     $('.slider').on('touchmove', null, function (event) {
         event.preventDefault();
         var obj = interfaceContext.getSelectedObject();
-        if (obj == null) {
+        if (obj === null) {
             return;
         }
         var move = event.originalEvent.targetTouches[0].clientX - 6;
@@ -400,14 +364,15 @@
     $(document).mouseup(function (event) {
         event.preventDefault();
         var obj = interfaceContext.getSelectedObject();
-        if (obj == null) {
+        if (obj === null) {
             return;
         }
         var interfaceID = obj.parentElement.getAttribute("interfaceid");
         var trackID = obj.getAttribute("trackindex");
-        if (interfaceContext.hasSelectedObjectMoved() == true) {
+        var id;
+        if (interfaceContext.hasSelectedObjectMoved() === true) {
             var l = $(obj).css("left");
-            var id = obj.getAttribute('trackIndex');
+            id = obj.getAttribute('trackIndex');
             var time = audioEngineContext.timer.getTestTime();
             var rate = convSliderPosToRate(obj);
             audioEngineContext.audioObjects[id].metric.moved(time, rate);
@@ -415,7 +380,7 @@
             console.log("slider " + id + " moved to " + rate + ' (' + time + ')');
             obj.setAttribute("slider-value", convSliderPosToRate(obj));
         } else {
-            var id = Number(obj.attributes['trackIndex'].value);
+            id = Number(obj.attributes.trackIndex.value);
             //audioEngineContext.metric.sliderPlayed(id);
             audioEngineContext.play(id);
         }
@@ -424,12 +389,12 @@
 
     $('.slider').on('touchend', null, function (event) {
         var obj = interfaceContext.getSelectedObject();
-        if (obj == null) {
+        if (obj === null) {
             return;
         }
         var interfaceID = obj.parentElement.getAttribute("interfaceid");
         var trackID = obj.getAttribute("trackindex");
-        if (interfaceContext.hasSelectedObjectMoved() == true) {
+        if (interfaceContext.hasSelectedObjectMoved() === true) {
             var l = $(obj).css("left");
             var id = obj.getAttribute('trackIndex');
             var time = audioEngineContext.timer.getTestTime();
@@ -446,7 +411,7 @@
         for (var i = 0; i < interfaceList[k].options.length; i++) {
             if (interfaceList[k].options[i].type == 'show' && interfaceList[k].options[i].name == '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%";
@@ -456,7 +421,7 @@
                 }
             } else if (interfaceList[k].options[i].type == 'show' && interfaceList[k].options[i].name == 'page-count') {
                 var pagecountHolder = document.getElementById('page-count');
-                if (pagecountHolder == null) {
+                if (pagecountHolder === null) {
                     pagecountHolder = document.createElement('div');
                     pagecountHolder.id = 'page-count';
                 }
@@ -464,7 +429,7 @@
                 var inject = document.getElementById('interface-buttons');
                 inject.appendChild(pagecountHolder);
             } else if (interfaceList[k].options[i].type == 'show' && interfaceList[k].options[i].name == 'volume') {
-                if (document.getElementById('master-volume-holder') == null) {
+                if (document.getElementById('master-volume-holder') === null) {
                     feedbackHolder.appendChild(interfaceContext.volume.object);
                 }
             } else if (interfaceList[k].options[i].type == 'show' && interfaceList[k].options[i].name == 'comments') {
@@ -497,7 +462,7 @@
     pagetitle.align = "center";
     var titleSpan = document.createElement('span');
     titleSpan.id = "pageTitle-" + this.id;
-    if (interfaceObject.title != undefined && typeof interfaceObject.title == "string") {
+    if (interfaceObject.title !== undefined && typeof interfaceObject.title == "string") {
         titleSpan.textContent = interfaceObject.title;
     } else {
         titleSpan.textContent = "Axis " + String(this.id + 1);
@@ -507,7 +472,7 @@
 
     // Create the slider box to hold the slider elements
     this.canvas = document.createElement('div');
-    if (this.name != undefined)
+    if (this.name !== undefined)
         this.canvas.id = 'slider-' + this.name;
     else
         this.canvas.id = 'slider-' + this.id;
@@ -531,16 +496,16 @@
     var positionScale = this.canvas.style.width.substr(0, this.canvas.style.width.length - 2);
     var offset = 50;
     var dest = document.getElementById("slider-holder").appendChild(this.sliderDOM);
-    for (var scaleObj of interfaceObject.scales) {
+    interfaceObject.scales.forEach(function (scaleObj) {
         var position = Number(scaleObj.position) * 0.01;
         var pixelPosition = (position * $(this.canvas).width()) + offset;
         var scaleDOM = document.createElement('span');
         scaleDOM.className = "ape-marker-text";
         scaleDOM.textContent = scaleObj.text;
-        scaleDOM.setAttribute('value', position)
+        scaleDOM.setAttribute('value', position);
         this.scale.appendChild(scaleDOM);
         scaleDOM.style.left = Math.floor((pixelPosition - ($(scaleDOM).width() / 2))) + 'px';
-    }
+    }, this);
 
     this.createSliderObject = function (audioObject, label) {
         var trackObj = document.createElement('div');
@@ -548,7 +513,7 @@
         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) {
+        if (this.name !== undefined) {
             trackObj.setAttribute('interface-name', this.name);
         } else {
             trackObj.setAttribute('interface-name', this.id);
@@ -572,21 +537,20 @@
     };
 
     this.resize = function (event) {
-        var width = window.innerWidth;
         var sliderDiv = this.canvas;
         var sliderScaleDiv = this.scale;
         var width = $(sliderDiv).width();
         var marginsize = 50;
         // Move sliders into new position
-        for (var index = 0; index < this.sliders.length; index++) {
-            var pix = Number(this.sliders[index].getAttribute("slider-value")) * width;
-            this.sliders[index].style.left = (pix + marginsize) + 'px';
-        }
+        this.sliders.forEach(function (slider, index) {
+            var pix = Number(slider.getAttribute("slider-value")) * width;
+            slider.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 position = Number(scaleObj.attributes.value.value);
             var pixelPosition = (position * width) + marginsize;
             scaleObj.style.left = Math.floor((pixelPosition - ($(scaleObj).width() / 2))) + 'px';
         }
@@ -671,7 +635,7 @@
         // audioObject has an error!!
         this.playback.textContent = "Error";
         $(this.playback).addClass("error-colour");
-    }
+    };
 }
 
 function outsideReferenceDOM(audioObject, index, inject) {
@@ -684,16 +648,13 @@
     this.outsideReferenceHolder.appendChild(outsideReferenceHolderspan);
     this.outsideReferenceHolder.setAttribute('track-id', index);
 
-    this.outsideReferenceHolder.onclick = function (event) {
-        audioEngineContext.play(event.currentTarget.getAttribute('track-id'));
+    this.handleEvent = function (event) {
+        audioEngineContext.play(audioObject.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');
-        }
+        $(this.outsideReferenceHolder).addClass('track-slider-playing');
     };
+    this.outsideReferenceHolder.addEventListener("click", this.handleEvent);
     inject.appendChild(this.outsideReferenceHolder);
     this.enable = function () {
         if (this.parent.state == 1) {
@@ -733,7 +694,7 @@
         // audioObject has an error!!
         this.outsideReferenceHolder.textContent = "Error";
         $(this.outsideReferenceHolder).addClass("error-colour");
-    }
+    };
 }
 
 function buttonSubmitClick() {
@@ -741,50 +702,36 @@
         canContinue = true;
 
     // Check that the anchor and reference objects are correctly placed
-    if (interfaceContext.checkHiddenAnchor() == false) {
+    if (interfaceContext.checkHiddenAnchor() === false) {
         return;
     }
-    if (interfaceContext.checkHiddenReference() == false) {
+    if (interfaceContext.checkHiddenReference() === false) {
         return;
     }
 
     for (var i = 0; i < checks.length; i++) {
         if (checks[i].type == 'check') {
+            var checkState = true;
             switch (checks[i].name) {
                 case 'fragmentPlayed':
                     // Check if all fragments have been played
-                    var checkState = interfaceContext.checkAllPlayed();
-                    if (checkState == false) {
-                        canContinue = false;
-                    }
+                    checkState = interfaceContext.checkAllPlayed();
                     break;
                 case 'fragmentFullPlayback':
                     // Check all fragments have been played to their full length
-                    var checkState = interfaceContext.checkFragmentsFullyPlayed();
-                    if (checkState == false) {
-                        canContinue = false;
-                    }
+                    checkState = interfaceContext.checkFragmentsFullyPlayed();
                     break;
                 case 'fragmentMoved':
                     // Check all fragment sliders have been moved.
-                    var checkState = interfaceContext.checkAllMoved();
-                    if (checkState == false) {
-                        canContinue = false;
-                    }
+                    checkState = interfaceContext.checkAllMoved();
                     break;
                 case 'fragmentComments':
                     // Check all fragment sliders have been moved.
-                    var checkState = interfaceContext.checkAllCommented();
-                    if (checkState == false) {
-                        canContinue = false;
-                    }
+                    checkState = interfaceContext.checkAllCommented();
                     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;
-                    }
+                    checkState = interfaceContext.checkScaleRange();
                     break;
                 default:
                     console.log("WARNING - Check option " + checks[i].name + " is not supported on this interface");
@@ -792,7 +739,8 @@
             }
 
         }
-        if (!canContinue) {
+        if (checkState === false) {
+            canContinue = false;
             break;
         }
     }
@@ -803,7 +751,7 @@
             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) {
+            if (audioEngineContext.timer.testStarted === false) {
                 interfaceContext.lightbox.post("Warning", 'You have not started the test! Please click a fragment to begin the test!');
                 return;
             }
@@ -847,9 +795,9 @@
     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) {
+        if (pageSpecification.outsideReference === undefined || pageSpecification.outsideReference.id != audioelements[i].id) {
             var inject = audioelements[i].getElementsByTagName("metric");
-            if (inject.length == 0) {
+            if (inject.length === 0) {
                 inject = storage.document.createElement("metric");
             } else {
                 inject = inject[0];
@@ -861,7 +809,6 @@
                     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]);
                     }
                 }
             }
--- a/interfaces/discrete.js	Fri Apr 14 16:25:26 2017 +0100
+++ b/interfaces/discrete.js	Fri Apr 14 16:28:19 2017 +0100
@@ -1,3 +1,4 @@
+/* globals interfaceContext, document, window, $, specification, audioEngineContext, console, window, testState, storage */
 // Once this is loaded and parsed, begin execution
 loadInterface();
 
@@ -19,7 +20,7 @@
     titleSpan.id = "test-title";
 
     // Set title to that defined in XML, else set to default
-    if (titleAttr != undefined) {
+    if (titleAttr !== undefined) {
         titleSpan.textContent = titleAttr;
     } else {
         titleSpan.textContent = 'Listening test';
@@ -30,7 +31,8 @@
     var pagetitle = document.createElement('div');
     pagetitle.className = "pageTitle";
     pagetitle.align = "center";
-    var titleSpan = document.createElement('span');
+
+    titleSpan = document.createElement('span');
     titleSpan.id = "pageTitle";
     pagetitle.appendChild(titleSpan);
 
@@ -112,7 +114,7 @@
     // Load the full interface
     testState.initialise();
     testState.advanceState();
-};
+}
 
 function loadTest(page) {
     // Called each time a new test page is to be build. The page specification node is the only item passed in
@@ -128,10 +130,10 @@
 
     // Set the page title
     if (typeof page.title == "string" && page.title.length > 0) {
-        document.getElementById("test-title").textContent = page.title
+        document.getElementById("test-title").textContent = page.title;
     }
 
-    if (interfaceObj.title != null) {
+    if (interfaceObj.title !== null) {
         document.getElementById("pageTitle").textContent = interfaceObj.title;
     }
 
@@ -142,17 +144,17 @@
     sliderBox.innerHTML = "";
 
     var commentBoxPrefix = "Comment on track";
-    if (interfaceObj.commentBoxPrefix != undefined) {
+    if (interfaceObj.commentBoxPrefix !== undefined) {
         commentBoxPrefix = interfaceObj.commentBoxPrefix;
     }
     var loopPlayback = page.loop;
 
-    for (var option of interfaceObj.options) {
+    interfaceObj.options.forEach(function (option) {
         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.style.width = "100%";
                         playbackHolder.align = 'center';
@@ -162,7 +164,7 @@
                     break;
                 case "page-count":
                     var pagecountHolder = document.getElementById('page-count');
-                    if (pagecountHolder == null) {
+                    if (pagecountHolder === null) {
                         pagecountHolder = document.createElement('div');
                         pagecountHolder.id = 'page-count';
                     }
@@ -171,7 +173,7 @@
                     inject.appendChild(pagecountHolder);
                     break;
                 case "volume":
-                    if (document.getElementById('master-volume-holder') == null) {
+                    if (document.getElementById('master-volume-holder') === null) {
                         feedbackHolder.appendChild(interfaceContext.volume.object);
                     }
                     break;
@@ -180,7 +182,7 @@
                     break;
             }
         }
-    }
+    });
 
     // Find all the audioElements from the audioHolder
     var index = 0;
@@ -223,9 +225,9 @@
     // An example node, you can make this however you want for each audioElement.
     // However, every audioObject (audioEngineContext.audioObject) MUST have an interface object with the following
     // You attach them by calling audioObject.bindInterface( )
-    if (interfaceScales == null || interfaceScales.length == 0) {
+    if (interfaceScales === null || interfaceScales.length === 0) {
         console.log("WARNING: The discrete radio's are built depending on the number of scale points specified! Ensure you have some specified. Defaulting to 5 for now!");
-        numOptions = 5;
+        var numOptions = 5;
     }
     this.parent = audioObject;
 
@@ -246,6 +248,23 @@
 
     this.discreteHolder.className = "track-slider-range";
     this.discreteHolder.style.width = window.innerWidth - 500 + 'px';
+    this.radioClicked = function (event) {
+        var time = audioEngineContext.timer.getTestTime();
+        if (audioEngineContext.status === 0) {
+            event.currentTarget.checked = false;
+            return;
+        }
+        var id = this.parent.id;
+        var position = this.getValue();
+        this.parent.metric.moved(time, position);
+        console.log('slider ' + id + ' moved to ' + position + ' (' + time + ')');
+
+    };
+    this.handleEvent = function (event) {
+        if (event.currentTarget.getAttribute("name") === this.parent.specification.id) {
+            this.radioClicked(event);
+        }
+    };
     for (var i = 0; i < interfaceScales.length; i++) {
         var node = document.createElement('input');
         node.setAttribute('type', 'radio');
@@ -256,17 +275,7 @@
         node.setAttribute('id', audioObject.specification.id + '-' + String(i));
         this.discretes.push(node);
         this.discreteHolder.appendChild(node);
-        node.onclick = function (event) {
-            if (audioEngineContext.status == 0) {
-                event.currentTarget.checked = false;
-                return;
-            }
-            var time = audioEngineContext.timer.getTestTime();
-            var id = Number(event.currentTarget.parentNode.parentNode.getAttribute('trackIndex'));
-            var value = event.currentTarget.getAttribute('position') / 100.0;
-            audioEngineContext.audioObjects[id].metric.moved(time, value);
-            console.log('slider ' + id + ' moved to ' + value + ' (' + time + ')');
-        };
+        node.addEventListener("click", this);
     }
 
     this.play.className = 'track-slider-button';
@@ -300,9 +309,9 @@
         this.play.disabled = false;
         this.play.textContent = "Play";
         $(this.slider).removeClass('track-slider-disabled');
-        for (var radio of this.discretes) {
-            radio.disabled = false;
-        }
+        this.discretes.forEach(function (elem) {
+            elem.disabled = false;
+        });
     };
     this.updateLoading = function (progress) {
         // progress is a value from 0 to 100 indicating the current download state of media files
@@ -322,14 +331,14 @@
         $(this.holder).addClass('track-slider-playing');
         var outsideReference = document.getElementById('outside-reference');
         this.play.textContent = "Listening";
-        if (outsideReference != null) {
+        if (outsideReference !== null) {
             $(outsideReference).removeClass('track-slider-playing');
         }
         if (this.parent.specification.parent.playOne || specification.playOne) {
             $('.track-slider-button').text = "Wait";
             $('.track-slider-button').attr("disabled", "true");
         }
-    }
+    };
     this.stopPlayback = function () {
         // Called by audioObject when playback stops
         if (this.play.getAttribute("playstate") == "playing") {
@@ -339,18 +348,17 @@
             this.play.textContent = "Play";
             $('.track-slider-button').removeAttr("disabled");
         }
-    }
+    };
 
     this.getValue = function () {
         // Return the current value of the object. If there is no value, return -1
-        var value = -1;
-        for (var i = 0; i < this.discretes.length; i++) {
-            if (this.discretes[i].checked == true) {
-                value = this.discretes[i].getAttribute('position') / 100.0;
-                break;
-            }
+        var checkedElement = this.discretes.find(function (elem) {
+            return elem.checked;
+        });
+        if (checkedElement === undefined) {
+            return -1;
         }
-        return value;
+        return checkedElement.getAttribute("position") / 100.0;
     };
     this.getPresentedId = function () {
         // Return the presented ID of the object. For instance, the APE has sliders starting from 0. Whilst AB has alphabetical scale
@@ -374,8 +382,8 @@
         // audioObject has an error!!
         this.playback.textContent = "Error";
         $(this.playback).addClass("error-colour");
-    }
-};
+    };
+}
 
 function resizeWindow(event) {
     // Called on every window resize event, use this to scale your page properly
@@ -415,7 +423,7 @@
     textHolder.innerHTML = "";
     ctx.fillStyle = "#000000";
     ctx.setLineDash([1, 4]);
-    for (var scale of scales) {
+    scales.forEach(function (scale) {
         var posPercent = scale.position / 100.0;
         var posPix = Math.round(width * posPercent);
         if (posPix <= 0) {
@@ -437,7 +445,7 @@
         textHolder.appendChild(text);
         text.style.width = $(text.children[0]).width() + 'px';
         text.style.left = (posPix + 150 - ($(text).width() / 2)) + 'px';
-    }
+    });
 }
 
 function buttonSubmitClick() // TODO: Only when all songs have been played!
@@ -446,57 +454,45 @@
         canContinue = true;
 
     // Check that the anchor and reference objects are correctly placed
-    if (interfaceContext.checkHiddenAnchor() == false) {
+    if (interfaceContext.checkHiddenAnchor() === false) {
         return;
     }
-    if (interfaceContext.checkHiddenReference() == false) {
+    if (interfaceContext.checkHiddenReference() === false) {
         return;
     }
 
     for (var i = 0; i < checks.length; i++) {
+        var checkState;
         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;
-                    }
+                    checkState = interfaceContext.checkAllPlayed();
                     break;
                 case 'fragmentFullPlayback':
                     // Check all fragments have been played to their full length
-                    var checkState = interfaceContext.checkAllPlayed();
-                    if (checkState == false) {
-                        canContinue = false;
-                    }
+                    checkState = interfaceContext.checkAllPlayed();
                     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;
-                    }
+                    checkState = interfaceContext.checkAllMoved();
                     break;
                 case 'fragmentComments':
                     // Check all fragment sliders have been moved.
-                    var checkState = interfaceContext.checkAllCommented();
-                    if (checkState == false) {
-                        canContinue = false;
-                    }
+                    checkState = interfaceContext.checkAllCommented();
                     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;
-                    }
+                    checkState = interfaceContext.checkScaleRange(checks[i].min, checks[i].max);
                     break;
                 default:
                     console.log("WARNING - Check option " + checks[i].check + " is not supported on this interface");
                     break;
             }
-
+            if (checkState === false) {
+                canContinue = false;
+            }
         }
         if (!canContinue) {
             break;
@@ -509,7 +505,7 @@
             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) {
+            if (audioEngineContext.timer.testStarted === false) {
                 interfaceContext.lightbox.post("Warning", 'You have not started the test! Please press start to begin the test!');
                 return;
             }
--- a/interfaces/horizontal-sliders.js	Fri Apr 14 16:25:26 2017 +0100
+++ b/interfaces/horizontal-sliders.js	Fri Apr 14 16:28:19 2017 +0100
@@ -1,4 +1,5 @@
 // Once this is loaded and parsed, begin execution
+/*globals interfaceContext, window, document, specification, audioEngineContext, console, testState, $, storage */
 loadInterface();
 
 function loadInterface() {
@@ -19,7 +20,7 @@
     titleSpan.id = "test-title";
 
     // Set title to that defined in XML, else set to default
-    if (titleAttr != undefined) {
+    if (titleAttr !== undefined) {
         titleSpan.textContent = titleAttr;
     } else {
         titleSpan.textContent = 'Listening test';
@@ -30,7 +31,8 @@
     var pagetitle = document.createElement('div');
     pagetitle.className = "pageTitle";
     pagetitle.align = "center";
-    var titleSpan = document.createElement('span');
+
+    titleSpan = document.createElement('span');
     titleSpan.id = "pageTitle";
     pagetitle.appendChild(titleSpan);
 
@@ -111,7 +113,7 @@
     // Load the full interface
     testState.initialise();
     testState.advanceState();
-};
+}
 
 function loadTest(page) {
     // Called each time a new test page is to be build. The page specification node is the only item passed in
@@ -128,10 +130,10 @@
 
     // Set the page title
     if (typeof page.title == "string" && page.title.length > 0) {
-        document.getElementById("test-title").textContent = page.title
+        document.getElementById("test-title").textContent = page.title;
     }
 
-    if (interfaceObj.title != null) {
+    if (interfaceObj.title !== null) {
         document.getElementById("pageTitle").textContent = interfaceObj.title;
     }
 
@@ -142,7 +144,7 @@
     sliderBox.innerHTML = "";
 
     var commentBoxPrefix = "Comment on track";
-    if (interfaceObj.commentBoxPrefix != undefined) {
+    if (interfaceObj.commentBoxPrefix !== undefined) {
         commentBoxPrefix = interfaceObj.commentBoxPrefix;
     }
     var loopPlayback = page.loop;
@@ -186,12 +188,12 @@
         }
 
     });
-    for (var option of interfaceObj.options) {
+    interfaceObj.options.forEach(function (option) {
         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.style.width = "100%";
                         playbackHolder.align = 'center';
@@ -201,7 +203,7 @@
                     break;
                 case "page-count":
                     var pagecountHolder = document.getElementById('page-count');
-                    if (pagecountHolder == null) {
+                    if (pagecountHolder === null) {
                         pagecountHolder = document.createElement('div');
                         pagecountHolder.id = 'page-count';
                     }
@@ -210,7 +212,7 @@
                     inject.appendChild(pagecountHolder);
                     break;
                 case "volume":
-                    if (document.getElementById('master-volume-holder') == null) {
+                    if (document.getElementById('master-volume-holder') === null) {
                         feedbackHolder.appendChild(interfaceContext.volume.object);
                     }
                     break;
@@ -219,7 +221,7 @@
                     break;
             }
         }
-    }
+    });
     // Auto-align
     resizeWindow(null);
 }
@@ -291,7 +293,7 @@
         $(".track-slider").removeClass('track-slider-playing');
         $(this.holder).addClass('track-slider-playing');
         var outsideReference = document.getElementById('outside-reference');
-        if (outsideReference != null) {
+        if (outsideReference !== null) {
             $(outsideReference).removeClass('track-slider-playing');
         }
     };
@@ -326,8 +328,8 @@
         // audioObject has an error!!
         this.playback.textContent = "Error";
         $(this.playback).addClass("error-colour");
-    }
-};
+    };
+}
 
 function resizeWindow(event) {
     // Called on every window resize event, use this to scale your page properly
@@ -367,7 +369,7 @@
     textHolder.innerHTML = "";
     ctx.fillStyle = "#000000";
     ctx.setLineDash([1, 4]);
-    for (var scale of scales) {
+    scales.forEach(function (scale) {
         var posPercent = scale.position / 100.0;
         var posPix = Math.round(width * posPercent);
         if (posPix <= 0) {
@@ -389,7 +391,7 @@
         textHolder.appendChild(text);
         text.style.width = Math.ceil($(text).width()) + 'px';
         text.style.left = (posPix + 100 - ($(text).width() / 2)) + 'px';
-    }
+    });
 }
 
 function buttonSubmitClick() // TODO: Only when all songs have been played!
@@ -398,59 +400,46 @@
         canContinue = true;
 
     // Check that the anchor and reference objects are correctly placed
-    if (interfaceContext.checkHiddenAnchor() == false) {
+    if (interfaceContext.checkHiddenAnchor() === false) {
         return;
     }
-    if (interfaceContext.checkHiddenReference() == false) {
+    if (interfaceContext.checkHiddenReference() === false) {
         return;
     }
 
     for (var i = 0; i < checks.length; i++) {
+        var checkState = true;
         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;
-                    }
+                    checkState = interfaceContext.checkAllPlayed();
                     break;
                 case 'fragmentFullPlayback':
                     // Check all fragments have been played to their full length
-                    var checkState = interfaceContext.checkAllPlayed();
-                    if (checkState == false) {
-                        canContinue = false;
-                    }
+                    checkState = interfaceContext.checkAllPlayed();
                     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;
-                    }
+                    checkState = interfaceContext.checkAllMoved();
                     break;
                 case 'fragmentComments':
                     // Check all fragment sliders have been moved.
-                    var checkState = interfaceContext.checkAllCommented();
-                    if (checkState == false) {
-                        canContinue = false;
-                    }
+                    checkState = interfaceContext.checkAllCommented();
                     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;
-                    }
+                    checkState = interfaceContext.checkScaleRange(checks[i].min, checks[i].max);
+
                     break;
                 default:
                     console.log("WARNING - Check option " + checks[i].check + " is not supported on this interface");
                     break;
             }
-
         }
-        if (!canContinue) {
+        if (checkState === false) {
+            canContinue = false;
             break;
         }
     }
@@ -461,7 +450,7 @@
             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) {
+            if (audioEngineContext.timer.testStarted === false) {
                 interfaceContext.lightbox.post("Warning", 'You have not started the test! Please press start to begin the test!');
                 return;
             }
--- a/interfaces/mushra.js	Fri Apr 14 16:25:26 2017 +0100
+++ b/interfaces/mushra.js	Fri Apr 14 16:28:19 2017 +0100
@@ -2,7 +2,7 @@
  *  mushra.js
  *  Create the MUSHRA interface
  */
-
+/*globals window, interfaceContext, document, $, specification, audioEngineContext, console, testState, storage */
 // Once this is loaded and parsed, begin execution
 loadInterface();
 
@@ -25,7 +25,7 @@
     titleSpan.id = "test-title";
 
     // Set title to that defined in XML, else set to default
-    if (titleAttr != undefined) {
+    if (titleAttr !== undefined) {
         titleSpan.textContent = titleAttr;
     } else {
         titleSpan.textContent = 'Listening test';
@@ -36,7 +36,8 @@
     var pagetitle = document.createElement('div');
     pagetitle.className = "pageTitle";
     pagetitle.align = "center";
-    var titleSpan = document.createElement('span');
+
+    titleSpan = document.createElement('span');
     titleSpan.id = "pageTitle";
     pagetitle.appendChild(titleSpan);
 
@@ -129,10 +130,10 @@
 
     // Set the page title
     if (typeof audioHolderObject.title == "string" && audioHolderObject.title.length > 0) {
-        document.getElementById("test-title").textContent = audioHolderObject.title
+        document.getElementById("test-title").textContent = audioHolderObject.title;
     }
 
-    if (interfaceObj.title != null) {
+    if (interfaceObj.title !== null) {
         document.getElementById("pageTitle").textContent = interfaceObj.title;
     }
 
@@ -144,12 +145,12 @@
     sliderBox.innerHTML = "";
 
     var commentBoxPrefix = "Comment on track";
-    if (interfaceObj.commentBoxPrefix != undefined) {
+    if (interfaceObj.commentBoxPrefix !== undefined) {
         commentBoxPrefix = interfaceObj.commentBoxPrefix;
     }
     var loopPlayback = audioHolderObject.loop;
 
-    currentTestHolder = document.createElement('audioHolder');
+    var currentTestHolder = document.createElement('audioHolder');
     currentTestHolder.id = audioHolderObject.id;
     currentTestHolder.repeatCount = audioHolderObject.repeatCount;
 
@@ -192,18 +193,18 @@
     if (testState.currentStateMap.restrictMovement) {
         $(".track-slider-range").addClass("track-slider-range-disabled");
         $(".track-slider-range").each(function (i, e) {
-            e.disabled = true
+            e.disabled = true;
         });
     }
 
 
     var interfaceOptions = interfaceObj.options;
-    for (var option of interfaceOptions) {
+    interfaceOptions.forEach(function (option) {
         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.style.width = "100%";
                         playbackHolder.align = 'center';
@@ -213,7 +214,7 @@
                     break;
                 case "page-count":
                     var pagecountHolder = document.getElementById('page-count');
-                    if (pagecountHolder == null) {
+                    if (pagecountHolder === null) {
                         pagecountHolder = document.createElement('div');
                         pagecountHolder.id = 'page-count';
                     }
@@ -222,7 +223,7 @@
                     inject.appendChild(pagecountHolder);
                     break;
                 case "volume":
-                    if (document.getElementById('master-volume-holder') == null) {
+                    if (document.getElementById('master-volume-holder') === null) {
                         feedbackHolder.appendChild(interfaceContext.volume.object);
                     }
                     break;
@@ -231,7 +232,7 @@
                     break;
             }
         }
-    }
+    });
 
     $(audioHolderObject.commentQuestions).each(function (index, element) {
         var node = interfaceContext.createCommentQuestion(element);
@@ -257,7 +258,7 @@
     this.holder.appendChild(this.slider);
     this.holder.appendChild(this.play);
     this.holder.align = "center";
-    if (label == 0) {
+    if (label === 0) {
         this.holder.style.marginLeft = '0px';
     }
     this.holder.setAttribute('trackIndex', audioObject.id);
@@ -316,7 +317,7 @@
         $(".track-slider").removeClass('track-slider-playing');
         $(this.holder).addClass('track-slider-playing');
         var outsideReference = document.getElementById('outside-reference');
-        if (outsideReference != null) {
+        if (outsideReference !== null) {
             $(outsideReference).removeClass('track-slider-playing');
         }
         this.play.textContent = "Stop";
@@ -374,7 +375,7 @@
         // audioObject has an error!!
         this.playback.textContent = "Error";
         $(this.playback).addClass("error-colour");
-    }
+    };
 }
 
 function resizeWindow(event) {
@@ -382,7 +383,7 @@
     // MANDATORY FUNCTION
 
     var outsideRef = document.getElementById('outside-reference');
-    if (outsideRef != null) {
+    if (outsideRef !== null) {
         outsideRef.style.left = (window.innerWidth - 120) / 2 + 'px';
     }
 
@@ -428,7 +429,7 @@
     var textHolder = document.getElementById('scale-text-holder');
     textHolder.innerHTML = "";
     var lastHeight = 0;
-    for (var scale of scales) {
+    scales.forEach(function (scale) {
         var posPercent = scale.position / 100.0;
         var posPix = (1 - posPercent) * (draw_heights[1] - draw_heights[0]) + draw_heights[0];
         ctx.fillStyle = "#000000";
@@ -446,7 +447,7 @@
         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!
@@ -455,51 +456,37 @@
         canContinue = true;
 
     // Check that the anchor and reference objects are correctly placed
-    if (interfaceContext.checkHiddenAnchor() == false) {
+    if (interfaceContext.checkHiddenAnchor() === false) {
         return;
     }
-    if (interfaceContext.checkHiddenReference() == false) {
+    if (interfaceContext.checkHiddenReference() === false) {
         return;
     }
 
     for (var i = 0; i < checks.length; i++) {
+        var checkState = true;
         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;
-                    }
+                    checkState = interfaceContext.checkAllPlayed();
                     break;
                 case 'fragmentFullPlayback':
                     // Check all fragments have been played to their full length
-                    var checkState = interfaceContext.checkAllPlayed();
-                    if (checkState == false) {
-                        canContinue = false;
-                    }
+                    checkState = interfaceContext.checkAllPlayed();
                     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;
-                    }
+                    checkState = interfaceContext.checkAllMoved();
                     break;
                 case 'fragmentComments':
                     // Check all fragment sliders have been moved.
-                    var checkState = interfaceContext.checkAllCommented();
-                    if (checkState == false) {
-                        canContinue = false;
-                    }
+                    checkState = interfaceContext.checkAllCommented();
                     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;
-                    }
+                    checkState = interfaceContext.checkScaleRange(checks[i].min, checks[i].max);
                     break;
                 default:
                     console.log("WARNING - Check option " + checks[i].check + " is not supported on this interface");
@@ -507,7 +494,8 @@
             }
 
         }
-        if (!canContinue) {
+        if (checkState === false) {
+            canContinue = false;
             break;
         }
     }
@@ -518,7 +506,7 @@
             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) {
+            if (audioEngineContext.timer.testStarted === false) {
                 interfaceContext.lightbox.post("Message", 'You have not started the test! Please press start to begin the test!');
                 return;
             }
--- a/interfaces/timeline.js	Fri Apr 14 16:25:26 2017 +0100
+++ b/interfaces/timeline.js	Fri Apr 14 16:28:19 2017 +0100
@@ -2,7 +2,7 @@
  * WAET Timeline
  * This interface plots a waveform timeline per audio fragment on a page. Clicking on the fragment will generate a comment box for processing.
  */
-
+/*globals interfaceContext, window, document, console, audioEngineContext, testState, $, storage */
 // Once this is loaded and parsed, begin execution
 loadInterface();
 
@@ -77,7 +77,7 @@
     // Load the full interface
     testState.initialise();
     testState.advanceState();
-};
+}
 
 function loadTest(page) {
     // Called each time a new test page is to be build. The page specification node is the only item passed in
@@ -94,7 +94,7 @@
         document.getElementById("test-title").textContent = page.title;
     }
 
-    if (interfaceObj.title != null) {
+    if (interfaceObj.title !== null) {
         document.getElementById("page-title").textContent = interfaceObj.title;
     }
 
@@ -103,7 +103,7 @@
     outsideReferenceHolder.innerHTML = "";
 
     var commentBoxPrefix = "Comment on track";
-    if (interfaceObj.commentBoxPrefix != undefined) {
+    if (interfaceObj.commentBoxPrefix !== undefined) {
         commentBoxPrefix = interfaceObj.commentBoxPrefix;
     }
     var index = 0;
@@ -116,7 +116,7 @@
         var audioObject = audioEngineContext.newTrack(element);
         if (page.audioElements.type == 'outside-reference') {
             var refNode = interfaceContext.outsideReferenceDOM(audioObject, index, outsideReferenceHolder);
-            audioObject.bindInterface(orNode);
+            audioObject.bindInterface(refNode);
         } else {
             var label = interfaceContext.getLabel(labelType, index, page.labelStart);
             var node = new interfaceObject(audioObject, label);
@@ -169,7 +169,7 @@
             var titleHolder = document.createElement("div");
             titleHolder.className = "comment-entry-header";
             this.title = document.createElement("span");
-            if (str != undefined) {
+            if (str !== undefined) {
                 this.title.textContent = str;
             } else {
                 this.title.textContent = "Time: " + time.toFixed(2) + "s";
@@ -186,7 +186,7 @@
                 handleEvent: function () {
                     this.parent.parent.deleteComment(this.parent);
                 }
-            }
+            };
             this.clear.DOM.textContent = "Delete";
             this.clear.DOM.addEventListener("click", this.clear);
             titleHolder.appendChild(this.clear.DOM);
@@ -199,7 +199,7 @@
                 elem_w = Math.max(elem_w, 190);
                 this.DOM.style.width = elem_w + "px";
                 this.textarea.style.width = (elem_w - 5) + "px";
-            }
+            };
             this.buildXML = function (root) {
                 //storage.document.createElement();
                 var node = storage.document.createElement("comment");
@@ -211,7 +211,7 @@
                 node.appendChild(question);
                 node.appendChild(comment);
                 root.appendChild(node);
-            }
+            };
             this.resize();
         },
         newComment: function (time) {
@@ -240,7 +240,7 @@
                 this.deleteComment(this.list[0]);
             }
         }
-    }
+    };
 
     this.canvas = {
         parent: this,
@@ -283,7 +283,7 @@
             }
         },
         drawWaveform: function () {
-            if (this.parent.parent == undefined || this.parent.parent.buffer == undefined) {
+            if (this.parent.parent === undefined || this.parent.parent.buffer === undefined) {
                 return;
             }
             var buffer = this.parent.parent.buffer.buffer;
@@ -342,7 +342,7 @@
             context.stroke();
         },
         drawMarkers: function () {
-            if (this.parent.parent == undefined || this.parent.parent.buffer == undefined) {
+            if (this.parent.parent === undefined || this.parent.parent.buffer === undefined) {
                 return;
             }
             var context = this.layer3.getContext("2d");
@@ -362,7 +362,7 @@
             var context = canvas.getContext("2d");
             context.clearRect(0, 0, canvas.width, canvas.height);
         }
-    }
+    };
     this.canvas.layer1.className = "timeline-element-canvas canvas-layer1 canvas-disabled";
     this.canvas.layer2.className = "timeline-element-canvas canvas-layer2";
     this.canvas.layer3.className = "timeline-element-canvas canvas-layer3";
@@ -393,7 +393,7 @@
                 audioEngineContext.stop();
             }
         }
-    }
+    };
     this.playButton.DOM.addEventListener("click", this.playButton);
     this.playButton.DOM.className = "timeline-button timeline-button-disabled";
     this.playButton.DOM.disabled = true;
@@ -408,7 +408,7 @@
         root.style.width = w + "px";
         var c_w = w - 100;
         this.canvas.resize(c_w);
-    }
+    };
 
     this.enable = function () {
         // This is used to tell the interface object that playback of this node is ready
@@ -459,8 +459,8 @@
     };
     this.error = function () {
         // If there is an error with the audioObject, this will be called to indicate a failure
-    }
-};
+    };
+}
 
 function resizeWindow(event) {
     // Called on every window resize event, use this to scale your page properly
@@ -470,7 +470,7 @@
 }
 
 function buttonSubmitClick() {
-    if (audioEngineContext.timer.testStarted == false) {
+    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;
     }
@@ -495,7 +495,7 @@
                     console.log("WARNING - Check option " + checks[i].check + " is not supported on this interface");
                     break;
             }
-            if (checkState == false) {
+            if (checkState === false) {
                 canContinue = false;
             }
         }
--- a/js/WAVE.js	Fri Apr 14 16:25:26 2017 +0100
+++ b/js/WAVE.js	Fri Apr 14 16:28:19 2017 +0100
@@ -1,6 +1,7 @@
 // Decode and perform WAVE file byte level manipulation
+/*globals console, Uint8Array, Float32Array, Float64Array */
 
-find_subarray = function (arr, subarr) {
+function find_subarray(arr, subarr) {
     var arr_length = arr.length;
     var subarr_length = subarr.length;
     var last_check_index = arr_length - subarr_length;
@@ -15,7 +16,7 @@
             return i;
         }
     return -1;
-};
+}
 
 function convertToInteger(arr) {
     var value = 0;
@@ -35,24 +36,24 @@
 
 function WAVE() {
     // The WAVE file object
-    this.status == 'WAVE_DECLARED'
+    this.status = 'WAVE_DECLARED';
 
     this.decoded_data = null;
 
     this.RIFF = String(); //ChunkID
-    this.size; //ChunkSize
-    this.FT_Header; //Format
-    this.fmt_marker; //Subchunk1ID
-    this.formatDataLength; //Subchunk1Size
-    this.type; //AudioFormat
-    this.num_channels; //NumChannels
-    this.sample_rate; //SampleRate
-    this.byte_rate; //ByteRate
-    this.block_align; //BlockAlign
-    this.bits_per_sample; //BitsPerSample
-    this.data_header; //Subchunk2ID
-    this.data_size; //Subchunk2Size
-    this.num_samples;
+    this.size = undefined; //ChunkSize
+    this.FT_Header = undefined; //Format
+    this.fmt_marker = undefined; //Subchunk1ID
+    this.formatDataLength = undefined; //Subchunk1Size
+    this.type = undefined; //AudioFormat
+    this.num_channels = undefined; //NumChannels
+    this.sample_rate = undefined; //SampleRate
+    this.byte_rate = undefined; //ByteRate
+    this.block_align = undefined; //BlockAlign
+    this.bits_per_sample = undefined; //BitsPerSample
+    this.data_header = undefined; //Subchunk2ID
+    this.data_size = undefined; //Subchunk2Size
+    this.num_samples = undefined;
 
     this.open = function (IOArrayBuffer) {
         var IOView8 = new Uint8Array(IOArrayBuffer);
--- a/js/core.js	Fri Apr 14 16:25:26 2017 +0100
+++ b/js/core.js	Fri Apr 14 16:28:19 2017 +0100
@@ -5,6 +5,10 @@
  * Also contains all global variables.
  */
 
+/*globals window, document, XMLDocument, Element, XMLHttpRequest, DOMParser, console, Blob, $, Promise, navigator */
+/*globals AudioBuffer, AudioBufferSourceNode */
+/*globals Specification, calculateLoudness, WAVE, validateXML, showdown, pageXMLSave, loadTest, resizeWindow */
+
 /* create the web audio API context and store in audioContext*/
 var audioContext; // Hold the browser web audio API
 var projectXML; // Hold the parsed setup XML
@@ -48,13 +52,13 @@
     name = String(name);
     var selected = this.documentElement.getAllElementsByName(name);
     return selected;
-}
+};
 
 Element.prototype.getAllElementsByName = function (name) {
     name = String(name);
     var selected = [];
     var node = this.firstElementChild;
-    while (node != null) {
+    while (node !== null) {
         if (node.getAttribute('name') == name) {
             selected.push(node);
         }
@@ -64,19 +68,19 @@
         node = node.nextElementSibling;
     }
     return selected;
-}
+};
 
 XMLDocument.prototype.getAllElementsByTagName = function (name) {
     name = String(name);
     var selected = this.documentElement.getAllElementsByTagName(name);
     return selected;
-}
+};
 
 Element.prototype.getAllElementsByTagName = function (name) {
     name = String(name);
     var selected = [];
     var node = this.firstElementChild;
-    while (node != null) {
+    while (node !== null) {
         if (node.nodeName == name) {
             selected.push(node);
         }
@@ -86,7 +90,7 @@
         node = node.nextElementSibling;
     }
     return selected;
-}
+};
 
 // Firefox does not have an XMLDocument.prototype.getElementsByName
 if (typeof XMLDocument.prototype.getElementsByName != "function") {
@@ -94,14 +98,14 @@
         name = String(name);
         var node = this.documentElement.firstElementChild;
         var selected = [];
-        while (node != null) {
+        while (node !== null) {
             if (node.getAttribute('name') == name) {
                 selected.push(node);
             }
             node = node.nextElementSibling;
         }
         return selected;
-    }
+    };
 }
 
 var check_dependancies = function () {
@@ -122,7 +126,7 @@
         return false;
     }
     return true;
-}
+};
 
 var onload = function () {
     // Function called once the browser has loaded all files.
@@ -131,7 +135,7 @@
     // Create a web audio API context
     // Fixed for cross-browser support
     var AudioContext = window.AudioContext || window.webkitAudioContext;
-    audioContext = new AudioContext;
+    audioContext = new AudioContext();
 
     // Create test state
     testState = new stateMachine();
@@ -152,11 +156,11 @@
         interfaceContext.resizeWindow(event);
     };
 
-    if (window.location.search.length != 0) {
+    if (window.location.search.length !== 0) {
         var search = window.location.search.split('?')[1];
         // Now split the requests into pairs
         var searchQueries = search.split('&');
-
+        var url;
         for (var i in searchQueries) {
             // Split each key-value pair
             searchQueries[i] = searchQueries[i].split('=');
@@ -165,6 +169,7 @@
             switch (key) {
                 case "url":
                     url = value;
+                    specification.url = url;
                     break;
                 case "returnURL":
                     gReturnURL = value;
@@ -188,9 +193,7 @@
     var xmlhttp = new XMLHttpRequest();
     xmlhttp.open("GET", 'xml/test-schema.xsd', true);
     xmlhttp.onload = function () {
-        schemaXSD = xmlhttp.response;
-        var parse = new DOMParser();
-        specification.schema = parse.parseFromString(xmlhttp.response, 'text/xml');
+        specification.processSchema(xmlhttp.response);
         var r = new XMLHttpRequest();
         r.open('GET', url, true);
         r.onload = function () {
@@ -204,11 +207,11 @@
             span.textContent = "There was an error when loading your XML file. Please check your path in the URL. After the path to this page, there should be '?url=path/to/your/file.xml'. Check the spelling of your filename as well. If you are still having issues, check the log of the python server or your webserver distribution for 404 codes for your file.";
             document.getElementsByTagName('body')[0].appendChild(msg);
             document.getElementsByTagName('body')[0].appendChild(span);
-        }
+        };
         r.send();
     };
     xmlhttp.send();
-};
+}
 
 function loadProjectSpecCallback(response) {
     // Function called after asynchronous download of XML project specification
@@ -219,10 +222,11 @@
     var parse = new DOMParser();
     var responseDocument = parse.parseFromString(response, 'text/xml');
     var errorNode = responseDocument.getElementsByTagName('parsererror');
+    var msg, span;
     if (errorNode.length >= 1) {
-        var msg = document.createElement("h3");
+        msg = document.createElement("h3");
         msg.textContent = "FATAL ERROR";
-        var span = document.createElement("span");
+        span = document.createElement("span");
         span.textContent = "The XML parser returned the following errors when decoding your XML file";
         document.getElementsByTagName('body')[0].innerHTML = null;
         document.getElementsByTagName('body')[0].appendChild(msg);
@@ -230,10 +234,10 @@
         document.getElementsByTagName('body')[0].appendChild(errorNode[0]);
         return;
     }
-    if (responseDocument == undefined || responseDocument.firstChild == undefined) {
-        var msg = document.createElement("h3");
+    if (responseDocument === undefined || responseDocument.firstChild === undefined) {
+        msg = document.createElement("h3");
         msg.textContent = "FATAL ERROR";
-        var span = document.createElement("span");
+        span = document.createElement("span");
         span.textContent = "The project XML was not decoded properly, try refreshing your browser and clearing caches. If the problem persists, contact the test creator.";
         document.getElementsByTagName('body')[0].innerHTML = null;
         document.getElementsByTagName('body')[0].appendChild(msg);
@@ -246,7 +250,7 @@
         // Perform XML schema validation
         var Module = {
             xml: response,
-            schema: schemaXSD,
+            schema: specification.getSchemaString(),
             arguments: ["--noout", "--schema", 'test-schema.xsd', 'document.xml']
         };
         projectXML = responseDocument;
@@ -254,16 +258,16 @@
         console.log(xmllint);
         if (xmllint != 'document.xml validates\n') {
             document.getElementsByTagName('body')[0].innerHTML = null;
-            var msg = document.createElement("h3");
+            msg = document.createElement("h3");
             msg.textContent = "FATAL ERROR";
-            var span = document.createElement("h4");
+            span = document.createElement("h4");
             span.textContent = "The XML validator returned the following errors when decoding your XML file";
             document.getElementsByTagName('body')[0].appendChild(msg);
             document.getElementsByTagName('body')[0].appendChild(span);
             xmllint = xmllint.split('\n');
             for (var i in xmllint) {
                 document.getElementsByTagName('body')[0].appendChild(document.createElement('br'));
-                var span = document.createElement("span");
+                span = document.createElement("span");
                 span.textContent = xmllint[i];
                 document.getElementsByTagName('body')[0].appendChild(span);
             }
@@ -278,15 +282,16 @@
         // document is a result
         projectXML = document.implementation.createDocument(null, "waet");
         projectXML.firstChild.appendChild(responseDocument.getElementsByTagName('waet')[0].getElementsByTagName("setup")[0].cloneNode(true));
-        var child = responseDocument.firstChild.firstChild;
-        while (child != null) {
+        var child = responseDocument.firstChild.firstChild,
+            copy;
+        while (child !== null) {
             if (child.nodeName == "survey") {
                 // One of the global survey elements
                 if (child.getAttribute("state") == "complete") {
                     // We need to remove this survey from <setup>
                     var location = child.getAttribute("location");
                     var globalSurveys = projectXML.getElementsByTagName("setup")[0].getElementsByTagName("survey")[0];
-                    while (globalSurveys != null) {
+                    while (globalSurveys !== null) {
                         if (location == "pre" || location == "before") {
                             if (globalSurveys.getAttribute("location") == "pre" || globalSurveys.getAttribute("location") == "before") {
                                 projectXML.getElementsByTagName("setup")[0].removeChild(globalSurveys);
@@ -302,7 +307,7 @@
                     }
                 } else {
                     // We need to complete this, so it must be regenerated by store
-                    var copy = child;
+                    copy = child;
                     child = child.previousElementSibling;
                     responseDocument.firstChild.removeChild(copy);
                 }
@@ -310,7 +315,7 @@
                 if (child.getAttribute("state") == "empty") {
                     // We need to complete this page
                     projectXML.firstChild.appendChild(responseDocument.getElementById(child.getAttribute("ref")).cloneNode(true));
-                    var copy = child;
+                    copy = child;
                     child = child.previousElementSibling;
                     responseDocument.firstChild.removeChild(copy);
                 }
@@ -323,7 +328,7 @@
         storage.initialise(responseDocument);
     }
     /// CHECK FOR SAMPLE RATE COMPATIBILITY
-    if (specification.sampleRate != undefined) {
+    if (isFinite(specification.sampleRate)) {
         if (Number(specification.sampleRate) != audioContext.sampleRate) {
             var errStr = 'Sample rates do not match! Requested ' + Number(specification.sampleRate) + ', got ' + audioContext.sampleRate + '. Please set the sample rate to match before completing this test.';
             interfaceContext.lightbox.post("Error", errStr);
@@ -335,7 +340,7 @@
     getInterfaces.open("GET", "interfaces/interfaces.json");
     getInterfaces.onerror = function (e) {
         throw (e);
-    }
+    };
     getInterfaces.onload = function () {
         if (getInterfaces.status !== 200) {
             throw (new Error(getInterfaces.status));
@@ -363,14 +368,14 @@
             css.setAttribute("href", v);
             head.appendChild(css);
         });
-    }
+    };
     getInterfaces.send();
 
-    if (gReturnURL != undefined) {
+    if (gReturnURL !== undefined) {
         console.log("returnURL Overide from " + specification.returnURL + " to " + gReturnURL);
         specification.returnURL = gReturnURL;
     }
-    if (gSaveFilenamePrefix != undefined) {
+    if (gSaveFilenamePrefix !== undefined) {
         specification.saveFilenamePrefix = gSaveFilenamePrefix;
     }
 
@@ -415,7 +420,7 @@
             }
         }
         var saveURL = projectReturn + "php/save.php?key=" + storage.SessionKey.key + saveUrlSuffix;
-        var xmlhttp = new XMLHttpRequest;
+        var xmlhttp = new XMLHttpRequest();
         xmlhttp.open("POST", saveURL, true);
         xmlhttp.setRequestHeader('Content-Type', 'text/xml');
         xmlhttp.onerror = function () {
@@ -517,7 +522,7 @@
 }
 
 function randomString(length) {
-    var str = ""
+    var str = "";
     for (var i = 0; i < length; i += 2) {
         var num = Math.floor(Math.random() * 1295);
         str += num.toString(36);
@@ -532,7 +537,7 @@
 
     var inputSequence = []; // For safety purposes: keep track of randomisation
     for (var counter = 0; counter < N; ++counter)
-        inputSequence.push(counter) // Fill array
+        inputSequence.push(counter); // Fill array
     var inputSequenceClone = inputSequence.slice(0);
 
     var holdArr = [];
@@ -584,6 +589,441 @@
             e.preventDefault();
         }
     });
+    // Generators & Processors //
+
+    function processConditional(node, value) {
+        function jumpToId(jumpID) {
+            var index = this.popupOptions.findIndex(function (item, index, element) {
+                if (item.specification.id == jumpID) {
+                    return true;
+                } else {
+                    return false;
+                }
+            }, this);
+            this.currentIndex = index - 1;
+        }
+        var conditionFunction;
+        if (node.specification.type === "question") {
+            conditionFunction = processQuestionConditional;
+        } else if (node.specification.type === "checkbox") {
+            conditionFunction = processCheckboxConditional;
+        } else if (node.specification.type === "radio") {
+            conditionFunction = processRadioConditional;
+        } else if (node.specification.type === "number") {
+            conditionFunction = processNumberConditional;
+        } else if (node.specification.type === "slider") {
+            conditionFunction = processSliderConditional;
+        } else {
+            return;
+        }
+        for (var i = 0; i < node.specification.conditions.length; i++) {
+            var condition = node.specification.conditions[i];
+            var pass = conditionFunction(condition, value);
+            var jumpID;
+            if (pass) {
+                jumpID = condition.jumpToOnPass;
+            } else {
+                jumpID = condition.jumpToOnFail;
+            }
+            if (jumpID !== undefined) {
+                jumpToId.call(this, jumpID);
+                break;
+            }
+        }
+    }
+
+    function postQuestion(node) {
+        var textArea = document.createElement('textarea');
+        switch (node.specification.boxsize) {
+            case 'small':
+                textArea.cols = "20";
+                textArea.rows = "1";
+                break;
+            case 'normal':
+                textArea.cols = "30";
+                textArea.rows = "2";
+                break;
+            case 'large':
+                textArea.cols = "40";
+                textArea.rows = "5";
+                break;
+            case 'huge':
+                textArea.cols = "50";
+                textArea.rows = "10";
+                break;
+        }
+        if (node.response === undefined) {
+            node.response = "";
+        } else {
+            textArea.value = node.response;
+        }
+        this.popupResponse.appendChild(textArea);
+        textArea.focus();
+        this.popupResponse.style.textAlign = "center";
+        this.popupResponse.style.left = "0%";
+    }
+
+    function processQuestionConditional(condition, value) {
+        switch (condition.check) {
+            case "equals":
+                // Deliberately loose check
+                if (value == condition.value) {
+                    return true;
+                }
+                break;
+            case "greaterThan":
+            case "lessThan":
+                console.log("Survey Element of type 'question' cannot interpret greaterThan/lessThan conditions. IGNORING");
+                break;
+            case "contains":
+                if (value.includes(condition.value)) {
+                    return true;
+                }
+                break;
+        }
+        return false;
+    }
+
+    function processQuestion(node) {
+        var textArea = this.popupResponse.getElementsByTagName("textarea")[0];
+        if (node.specification.mandatory === true && textArea.value.length === 0) {
+            interfaceContext.lightbox.post("Error", "This question is mandatory");
+            return false;
+        }
+        // Save the text content
+        console.log("Question: " + node.specification.statement);
+        console.log("Question Response: " + textArea.value);
+        node.response = textArea.value;
+        processConditional.call(this, node, textArea.value);
+        return true;
+    }
+
+    function postCheckbox(node) {
+        if (node.response === undefined) {
+            node.response = Array(node.specification.options.length);
+        }
+        var table = document.createElement("table");
+        table.className = "popup-option-list";
+        table.border = "0";
+        node.response = [];
+        node.specification.options.forEach(function (option, index) {
+            var tr = document.createElement("tr");
+            table.appendChild(tr);
+            var td = document.createElement("td");
+            tr.appendChild(td);
+            var input = document.createElement('input');
+            input.id = option.name;
+            input.type = 'checkbox';
+            td.appendChild(input);
+
+            td = document.createElement("td");
+            tr.appendChild(td);
+            var span = document.createElement('span');
+            span.textContent = option.text;
+            td.appendChild(span);
+            tr = document.createElement('div');
+            tr.setAttribute('name', 'option');
+            tr.className = "popup-option-checbox";
+            if (node.response[index] !== undefined) {
+                if (node.response[index].checked === true) {
+                    input.checked = "true";
+                }
+            }
+            index++;
+        });
+        this.popupResponse.appendChild(table);
+    }
+
+    function processCheckbox(node) {
+        console.log("Checkbox: " + node.specification.statement);
+        var inputs = this.popupResponse.getElementsByTagName('input');
+        node.response = [];
+        var numChecked = 0,
+            i;
+        for (i = 0; i < node.specification.options.length; i++) {
+            if (inputs[i].checked) {
+                numChecked++;
+            }
+        }
+        if (node.specification.min !== undefined) {
+            if (node.specification.max === undefined) {
+                if (numChecked < node.specification.min) {
+                    var msg = "You must select at least " + node.specification.min + " option";
+                    if (node.specification.min > 1) {
+                        msg += "s";
+                    }
+                    interfaceContext.lightbox.post("Error", msg);
+                    return;
+                }
+            } else {
+                if (numChecked < node.specification.min || numChecked > node.specification.max) {
+                    if (node.specification.min == node.specification.max) {
+                        interfaceContext.lightbox.post("Error", "You must only select " + node.specification.min);
+                    } else {
+                        interfaceContext.lightbox.post("Error", "You must select between " + node.specification.min + " and " + node.specification.max);
+                    }
+                    return false;
+                }
+            }
+        }
+        for (i = 0; i < node.specification.options.length; i++) {
+            node.response.push({
+                name: node.specification.options[i].name,
+                text: node.specification.options[i].text,
+                checked: inputs[i].checked
+            });
+            console.log(node.specification.options[i].name + ": " + inputs[i].checked);
+        }
+        processConditional.call(this, node, node.response);
+        return true;
+    }
+
+    function processCheckboxConditional(condition, response) {
+        switch (condition.check) {
+            case "contains":
+                for (var i = 0; i < response.length; i++) {
+                    var value = response[i];
+                    if (value.name === condition.value && value.checked) {
+                        return true;
+                    }
+                }
+                break;
+            case "equals":
+            case "greaterThan":
+            case "lessThan":
+                console.log("Survey Element of type 'checkbox' cannot interpret equals/greaterThan/lessThan conditions. IGNORING");
+                break;
+            default:
+                console.log("Unknown condition. IGNORING");
+                break;
+        }
+        return false;
+    }
+
+    function postRadio(node) {
+        if (node.response === null) {
+            node.response = {
+                name: "",
+                text: ""
+            };
+        }
+        var table = document.createElement("table");
+        table.className = "popup-option-list";
+        table.border = "0";
+        if (node.response === null || node.response.length === 0) {
+            node.response = [];
+        }
+        node.specification.options.forEach(function (option, index) {
+            var tr = document.createElement("tr");
+            table.appendChild(tr);
+            var td = document.createElement("td");
+            tr.appendChild(td);
+            var input = document.createElement('input');
+            input.id = option.name;
+            input.type = 'radio';
+            input.name = node.specification.id;
+            td.appendChild(input);
+
+            td = document.createElement("td");
+            tr.appendChild(td);
+            var span = document.createElement('span');
+            span.textContent = option.text;
+            td.appendChild(span);
+            tr = document.createElement('div');
+            tr.setAttribute('name', 'option');
+            tr.className = "popup-option-checbox";
+            table.appendChild(tr);
+        });
+        this.popupResponse.appendChild(table);
+    }
+
+    function processRadio(node) {
+        var optHold = this.popupResponse;
+        console.log("Radio: " + node.specification.statement);
+        node.response = null;
+        var i = 0;
+        var inputs = optHold.getElementsByTagName('input');
+        while (node.response === null) {
+            if (i == inputs.length) {
+                if (node.specification.mandatory === true) {
+                    interfaceContext.lightbox.post("Error", "Please select one option");
+                    return false;
+                }
+                break;
+            }
+            if (inputs[i].checked === true) {
+                node.response = node.specification.options[i];
+                console.log("Selected: " + node.specification.options[i].name);
+            }
+            i++;
+        }
+        processConditional.call(this, node, node.response);
+        return true;
+    }
+
+    function processRadioConditional(condition, response) {
+        switch (condition.check) {
+            case "equals":
+                if (response === condition.value) {
+                    return true;
+                }
+                break;
+            case "contains":
+            case "greaterThan":
+            case "lessThan":
+                console.log("Survey Element of type 'radio' cannot interpret contains/greaterThan/lessThan conditions. IGNORING");
+                break;
+            default:
+                console.log("Unknown condition. IGNORING");
+                break;
+        }
+        return false;
+    }
+
+    function postNumber(node) {
+        var input = document.createElement('input');
+        input.type = 'textarea';
+        if (node.specification.min !== null) {
+            input.min = node.specification.min;
+        }
+        if (node.specification.max !== null) {
+            input.max = node.specification.max;
+        }
+        if (node.specification.step !== null) {
+            input.step = node.specification.step;
+        }
+        if (node.response !== undefined) {
+            input.value = node.response;
+        }
+        this.popupResponse.appendChild(input);
+        this.popupResponse.style.textAlign = "center";
+        this.popupResponse.style.left = "0%";
+    }
+
+    function processNumber(node) {
+        var input = this.popupContent.getElementsByTagName('input')[0];
+        if (node.mandatory === true && input.value.length === 0) {
+            interfaceContext.lightbox.post("Error", 'This question is mandatory. Please enter a number');
+            return false;
+        }
+        var enteredNumber = Number(input.value);
+        if (isNaN(enteredNumber)) {
+            interfaceContext.lightbox.post("Error", 'Please enter a valid number');
+            return false;
+        }
+        if (enteredNumber < node.min && node.min !== null) {
+            interfaceContext.lightbox.post("Error", 'Number is below the minimum value of ' + node.min);
+            return false;
+        }
+        if (enteredNumber > node.max && node.max !== null) {
+            interfaceContext.lightbox.post("Error", 'Number is above the maximum value of ' + node.max);
+            return false;
+        }
+        node.response = input.value;
+        processConditional.call(this, node, node.response);
+        return true;
+    }
+
+    function processNumberConditional(condtion, value) {
+        var condition = condition;
+        switch (condition.check) {
+            case "greaterThan":
+                if (value > Number(condition.value)) {
+                    return true;
+                }
+                break;
+            case "lessThan":
+                if (value < Number(condition.value)) {
+                    return true;
+                }
+                break;
+            case "equals":
+                if (value == condition.value) {
+                    return true;
+                }
+                break;
+            case "contains":
+                console.log("Survey Element of type 'number' cannot interpret \"contains\" conditions. IGNORING");
+                break;
+            default:
+                console.log("Unknown condition. IGNORING");
+                break;
+        }
+        return false;
+    }
+
+    function postVideo(node) {
+        var video = document.createElement("video");
+        video.src = node.specification.url;
+        this.popupResponse.appendChild(video);
+    }
+
+    function postYoutube(node) {
+        var iframe = document.createElement("iframe");
+        iframe.className = "youtube";
+        iframe.src = node.specification.url;
+        this.popupResponse.appendChild(iframe);
+    }
+
+    function postSlider(node) {
+        var hold = document.createElement('div');
+        var input = document.createElement('input');
+        input.type = 'range';
+        input.style.width = "90%";
+        if (node.specification.min !== null) {
+            input.min = node.specification.min;
+        }
+        if (node.specification.max !== null) {
+            input.max = node.specification.max;
+        }
+        if (node.response !== undefined) {
+            input.value = node.response;
+        }
+        hold.className = "survey-slider-text-holder";
+        var minText = document.createElement('span');
+        var maxText = document.createElement('span');
+        minText.textContent = node.specification.leftText;
+        maxText.textContent = node.specification.rightText;
+        hold.appendChild(minText);
+        hold.appendChild(maxText);
+        this.popupResponse.appendChild(input);
+        this.popupResponse.appendChild(hold);
+        this.popupResponse.style.textAlign = "center";
+    }
+
+    function processSlider(node) {
+        var input = this.popupContent.getElementsByTagName('input')[0];
+        node.response = input.value;
+        processConditional.call(this, node, node.response);
+        return true;
+    }
+
+    function processSliderConditional(condition, value) {
+        switch (condition.check) {
+            case "contains":
+                console.log("Survey Element of type 'number' cannot interpret contains conditions. IGNORING");
+                break;
+            case "greaterThan":
+                if (value > Number(condition.value)) {
+                    return true;
+                }
+                break;
+            case "lessThan":
+                if (value < Number(condition.value)) {
+                    return true;
+                }
+                break;
+            case "equals":
+                if (value == condition.value) {
+                    return true;
+                }
+                break;
+            default:
+                console.log("Unknown condition. IGNORING");
+                break;
+        }
+        return false;
+    }
 
     this.createPopup = function () {
         // Create popup window interface
@@ -614,7 +1054,7 @@
     };
 
     this.showPopup = function () {
-        if (this.popup == null) {
+        if (this.popup === null) {
             this.createPopup();
         }
         this.popup.style.visibility = 'visible';
@@ -641,157 +1081,19 @@
         this.popupTitle.innerHTML = "";
         this.popupTitle.appendChild(p.parseFromString(converter.makeHtml(node.specification.statement), "text/html").getElementsByTagName("body")[0].firstElementChild);
         if (node.specification.type == 'question') {
-            var textArea = document.createElement('textarea');
-            switch (node.specification.boxsize) {
-                case 'small':
-                    textArea.cols = "20";
-                    textArea.rows = "1";
-                    break;
-                case 'normal':
-                    textArea.cols = "30";
-                    textArea.rows = "2";
-                    break;
-                case 'large':
-                    textArea.cols = "40";
-                    textArea.rows = "5";
-                    break;
-                case 'huge':
-                    textArea.cols = "50";
-                    textArea.rows = "10";
-                    break;
-            }
-            if (node.response == undefined) {
-                node.response = "";
-            } else {
-                textArea.value = node.response;
-            }
-            this.popupResponse.appendChild(textArea);
-            textArea.focus();
-            this.popupResponse.style.textAlign = "center";
-            this.popupResponse.style.left = "0%";
+            postQuestion.call(this, node);
         } else if (node.specification.type == 'checkbox') {
-            if (node.response == undefined) {
-                node.response = Array(node.specification.options.length);
-            }
-            var index = 0;
-            var table = document.createElement("table");
-            table.className = "popup-option-list";
-            table.border = "0";
-            for (var option of node.specification.options) {
-                var tr = document.createElement("tr");
-                table.appendChild(tr);
-                var td = document.createElement("td");
-                tr.appendChild(td);
-                var input = document.createElement('input');
-                input.id = option.name;
-                input.type = 'checkbox';
-                td.appendChild(input);
-
-                td = document.createElement("td");
-                tr.appendChild(td);
-                var span = document.createElement('span');
-                span.textContent = option.text;
-                td.appendChild(span);
-                var tr = document.createElement('div');
-                tr.setAttribute('name', 'option');
-                tr.className = "popup-option-checbox";
-                if (node.response[index] != undefined) {
-                    if (node.response[index].checked == true) {
-                        input.checked = "true";
-                    }
-                }
-                index++;
-            }
-            this.popupResponse.appendChild(table);
+            postCheckbox.call(this, node);
         } else if (node.specification.type == 'radio') {
-            if (node.response == undefined) {
-                node.response = {
-                    name: "",
-                    text: ""
-                };
-            }
-            var index = 0;
-            var table = document.createElement("table");
-            table.className = "popup-option-list";
-            table.border = "0";
-            for (var option of node.specification.options) {
-                var tr = document.createElement("tr");
-                table.appendChild(tr);
-                var td = document.createElement("td");
-                tr.appendChild(td);
-                var input = document.createElement('input');
-                input.id = option.name;
-                input.type = 'radio';
-                input.name = node.specification.id;
-                td.appendChild(input);
-
-                td = document.createElement("td");
-                tr.appendChild(td);
-                var span = document.createElement('span');
-                span.textContent = option.text;
-                td.appendChild(span);
-                var tr = document.createElement('div');
-                tr.setAttribute('name', 'option');
-                tr.className = "popup-option-checbox";
-                if (node.response[index] != undefined) {
-                    if (node.response[index].checked == true) {
-                        input.checked = "true";
-                    }
-                }
-                index++;
-            }
-            this.popupResponse.appendChild(table);
+            postRadio.call(this, node);
         } else if (node.specification.type == 'number') {
-            var input = document.createElement('input');
-            input.type = 'textarea';
-            if (node.specification.min != null) {
-                input.min = node.specification.min;
-            }
-            if (node.specification.max != null) {
-                input.max = node.specification.max;
-            }
-            if (node.specification.step != null) {
-                input.step = node.specification.step;
-            }
-            if (node.response != undefined) {
-                input.value = node.response;
-            }
-            this.popupResponse.appendChild(input);
-            this.popupResponse.style.textAlign = "center";
-            this.popupResponse.style.left = "0%";
+            postNumber.call(this, node);
         } else if (node.specification.type == "video") {
-            var video = document.createElement("video");
-            video.src = node.specification.url;
-            this.popupResponse.appendChild(video);
+            postVideo.call(this, node);
         } else if (node.specification.type == "youtube") {
-            var iframe = document.createElement("iframe");
-            iframe.className = "youtube";
-            iframe.src = node.specification.url;
-            this.popupResponse.appendChild(iframe);
+            postYoutube.call(this, node);
         } else if (node.specification.type == "slider") {
-            var hold = document.createElement('div');
-            var input = document.createElement('input');
-            input.type = 'range';
-            input.style.width = "90%";
-            if (node.specification.min != null) {
-                input.min = node.specification.min;
-            }
-            if (node.specification.max != null) {
-                input.max = node.specification.max;
-            }
-            if (node.response != undefined) {
-                input.value = node.response;
-            }
-            hold.className = "survey-slider-text-holder";
-            var minText = document.createElement('span');
-            var maxText = document.createElement('span');
-            minText.textContent = node.specification.leftText;
-            maxText.textContent = node.specification.rightText;
-            hold.appendChild(minText);
-            hold.appendChild(maxText);
-            this.popupResponse.appendChild(input);
-            this.popupResponse.appendChild(hold);
-            this.popupResponse.style.textAlign = "center";
+            postSlider.call(this, node);
         }
         if (this.currentIndex + 1 == this.popupOptions.length) {
             if (this.node.location == "pre") {
@@ -815,12 +1117,12 @@
             this.popupOptions = [];
             this.node = node;
             this.store = store;
-            for (var opt of node.options) {
+            node.options.forEach(function (opt) {
                 this.popupOptions.push({
                     specification: opt,
                     response: null
                 });
-            }
+            }, this);
             this.currentIndex = 0;
             this.showPopup();
             this.postNode();
@@ -831,294 +1133,30 @@
 
     this.proceedClicked = function () {
         // Each time the popup button is clicked!
-        if (testState.stateIndex == 0 && specification.calibration) {
+        if (testState.stateIndex === 0 && specification.calibration) {
             interfaceContext.calibrationModuleObject.collect();
             advanceState();
             return;
         }
-        var node = this.popupOptions[this.currentIndex];
+        var node = this.popupOptions[this.currentIndex],
+            pass = true;
         if (node.specification.type == 'question') {
             // Must extract the question data
-            var textArea = $(popup.popupContent).find('textarea')[0];
-            if (node.specification.mandatory == true && textArea.value.length == 0) {
-                interfaceContext.lightbox.post("Error", "This question is mandatory");
-                return;
-            } else {
-                // Save the text content
-                console.log("Question: " + node.specification.statement);
-                console.log("Question Response: " + textArea.value);
-                node.response = textArea.value;
-            }
-            // Perform the conditional
-            for (var condition of node.specification.conditions) {
-                var pass = false;
-                switch (condition.check) {
-                    case "equals":
-                        if (textArea.value == condition.value) {
-                            pass = true;
-                        }
-                        break;
-                    case "greaterThan":
-                    case "lessThan":
-                        console.log("Survey Element of type 'question' cannot interpret greaterThan/lessThan conditions. IGNORING");
-                        break;
-                    case "contains":
-                        if (textArea.value.includes(condition.value)) {
-                            pass = true;
-                        }
-                        break;
-                }
-                var jumpID;
-                if (pass) {
-                    jumpID = condition.jumpToOnPass;
-                } else {
-                    jumpID = condition.jumpToOnFail;
-                }
-                if (jumpID != undefined) {
-                    var index = this.popupOptions.findIndex(function (item, index, element) {
-                        if (item.specification.id == jumpID) {
-                            return true;
-                        } else {
-                            return false;
-                        }
-                    }, this);
-                    this.currentIndex = index - 1;
-                    break;
-                }
-            }
+            pass = processQuestion.call(this, node);
         } else if (node.specification.type == 'checkbox') {
             // Must extract checkbox data
-            console.log("Checkbox: " + node.specification.statement);
-            var inputs = this.popupResponse.getElementsByTagName('input');
-            node.response = [];
-            var numChecked = 0;
-            for (var i = 0; i < node.specification.options.length; i++) {
-                if (inputs[i].checked) {
-                    numChecked++;
-                }
-            }
-            if (node.specification.min != undefined) {
-                if (node.specification.max == undefined) {
-                    if (numChecked < node.specification.min) {
-                        var msg = "You must select at least " + node.specification.min + " option";
-                        if (node.specification.min > 1) {
-                            msg += "s";
-                        }
-                        interfaceContext.lightbox.post("Error", msg);
-                        return;
-                    }
-                } else {
-                    if (numChecked < node.specification.min || numChecked > node.specification.max) {
-                        if (node.specification.min == node.specification.max) {
-                            interfaceContext.lightbox.post("Error", "You must only select " + node.specification.min);
-                        } else {
-                            interfaceContext.lightbox.post("Error", "You must select between " + node.specification.min + " and " + node.specification.max);
-                        }
-                        return;
-                    }
-                }
-            }
-            for (var i = 0; i < node.specification.options.length; i++) {
-                node.response.push({
-                    name: node.specification.options[i].name,
-                    text: node.specification.options[i].text,
-                    checked: inputs[i].checked
-                });
-                console.log(node.specification.options[i].name + ": " + inputs[i].checked);
-            }
+            pass = processCheckbox.call(this, node);
+        } else if (node.specification.type == "radio") {
             // Perform the conditional
-            for (var condition of node.specification.conditions) {
-                var pass = false;
-                switch (condition.check) {
-                    case "equals":
-                    case "greaterThan":
-                    case "lessThan":
-                        console.log("Survey Element of type 'checkbox' cannot interpret equals/greaterThan/lessThan conditions. IGNORING");
-                        break;
-                    case "contains":
-                        for (var response of node.response) {
-                            if (response.name == condition.value && response.checked) {
-                                pass = true;
-                                break;
-                            }
-                        }
-                        break;
-                }
-                var jumpID;
-                if (pass) {
-                    jumpID = condition.jumpToOnPass;
-                } else {
-                    jumpID = condition.jumpToOnFail;
-                }
-                if (jumpID != undefined) {
-                    var index = this.popupOptions.findIndex(function (item, index, element) {
-                        if (item.specification.id == jumpID) {
-                            return true;
-                        } else {
-                            return false;
-                        }
-                    }, this);
-                    this.currentIndex = index - 1;
-                    break;
-                }
-            }
-        } else if (node.specification.type == "radio") {
-            var optHold = this.popupResponse;
-            console.log("Radio: " + node.specification.statement);
-            node.response = null;
-            var i = 0;
-            var inputs = optHold.getElementsByTagName('input');
-            while (node.response == null) {
-                if (i == inputs.length) {
-                    if (node.specification.mandatory == true) {
-                        interfaceContext.lightbox.post("Error", "Please select one option");
-                        return;
-                    }
-                    break;
-                }
-                if (inputs[i].checked == true) {
-                    node.response = node.specification.options[i];
-                    console.log("Selected: " + node.specification.options[i].name);
-                }
-                i++;
-            }
+            pass = processRadio.call(this, node);
+        } else if (node.specification.type == "number") {
             // Perform the conditional
-            for (var condition of node.specification.conditions) {
-                var pass = false;
-                switch (condition.check) {
-                    case "contains":
-                    case "greaterThan":
-                    case "lessThan":
-                        console.log("Survey Element of type 'radio' cannot interpret contains/greaterThan/lessThan conditions. IGNORING");
-                        break;
-                    case "equals":
-                        if (node.response.name == condition.value) {
-                            pass = true;
-                        }
-                        break;
-                }
-                var jumpID;
-                if (pass) {
-                    jumpID = condition.jumpToOnPass;
-                } else {
-                    jumpID = condition.jumpToOnFail;
-                }
-                if (jumpID != undefined) {
-                    var index = this.popupOptions.findIndex(function (item, index, element) {
-                        if (item.specification.id == jumpID) {
-                            return true;
-                        } else {
-                            return false;
-                        }
-                    }, this);
-                    this.currentIndex = index - 1;
-                    break;
-                }
-            }
-        } else if (node.specification.type == "number") {
-            var input = this.popupContent.getElementsByTagName('input')[0];
-            if (node.mandatory == true && input.value.length == 0) {
-                interfaceContext.lightbox.post("Error", 'This question is mandatory. Please enter a number');
-                return;
-            }
-            var enteredNumber = Number(input.value);
-            if (isNaN(enteredNumber)) {
-                interfaceContext.lightbox.post("Error", 'Please enter a valid number');
-                return;
-            }
-            if (enteredNumber < node.min && node.min != null) {
-                interfaceContext.lightbox.post("Error", 'Number is below the minimum value of ' + node.min);
-                return;
-            }
-            if (enteredNumber > node.max && node.max != null) {
-                interfaceContext.lightbox.post("Error", 'Number is above the maximum value of ' + node.max);
-                return;
-            }
-            node.response = input.value;
-            // Perform the conditional
-            for (var condition of node.specification.conditions) {
-                var pass = false;
-                switch (condition.check) {
-                    case "contains":
-                        console.log("Survey Element of type 'number' cannot interpret contains conditions. IGNORING");
-                        break;
-                    case "greaterThan":
-                        if (node.response > Number(condition.value)) {
-                            pass = true;
-                        }
-                        break;
-                    case "lessThan":
-                        if (node.response < Number(condition.value)) {
-                            pass = true;
-                        }
-                        break;
-                    case "equals":
-                        if (node.response == condition.value) {
-                            pass = true;
-                        }
-                        break;
-                }
-                var jumpID;
-                if (pass) {
-                    jumpID = condition.jumpToOnPass;
-                } else {
-                    jumpID = condition.jumpToOnFail;
-                }
-                if (jumpID != undefined) {
-                    var index = this.popupOptions.findIndex(function (item, index, element) {
-                        if (item.specification.id == jumpID) {
-                            return true;
-                        } else {
-                            return false;
-                        }
-                    }, this);
-                    this.currentIndex = index - 1;
-                    break;
-                }
-            }
+            pass = processNumber.call(this, node);
         } else if (node.specification.type == 'slider') {
-            var input = this.popupContent.getElementsByTagName('input')[0];
-            node.response = input.value;
-            for (var condition of node.specification.conditions) {
-                var pass = false;
-                switch (condition.check) {
-                    case "contains":
-                        console.log("Survey Element of type 'number' cannot interpret contains conditions. IGNORING");
-                        break;
-                    case "greaterThan":
-                        if (node.response > Number(condition.value)) {
-                            pass = true;
-                        }
-                        break;
-                    case "lessThan":
-                        if (node.response < Number(condition.value)) {
-                            pass = true;
-                        }
-                        break;
-                    case "equals":
-                        if (node.response == condition.value) {
-                            pass = true;
-                        }
-                        break;
-                }
-                var jumpID;
-                if (pass) {
-                    jumpID = condition.jumpToOnPass;
-                } else {
-                    jumpID = condition.jumpToOnFail;
-                }
-                if (jumpID != undefined) {
-                    var index = this.popupOptions.findIndex(function (item, index, element) {
-                        if (item.specification.id == jumpID) {
-                            return true;
-                        } else {
-                            return false;
-                        }
-                    }, this);
-                    this.currentIndex = index - 1;
-                    break;
-                }
-            }
+            pass = processSlider.call(this, node);
+        }
+        if (pass === false) {
+            return;
         }
         this.currentIndex++;
         if (this.currentIndex < this.popupOptions.length) {
@@ -1128,9 +1166,9 @@
             this.popupTitle.innerHTML = "";
             this.popupResponse.innerHTML = "";
             this.hidePopup();
-            for (var node of this.popupOptions) {
+            this.popupOptions.forEach(function (node) {
                 this.store.postResult(node);
-            }
+            }, this);
             this.store.complete();
             advanceState();
         }
@@ -1146,7 +1184,7 @@
 
     this.resize = function (event) {
         // Called on window resize;
-        if (this.popup != null) {
+        if (this.popup !== null) {
             this.popup.style.left = (window.innerWidth / 2) - 250 + 'px';
             this.popup.style.top = (window.innerHeight / 2) - 125 + 'px';
             var blank = document.getElementsByClassName('testHalt')[0];
@@ -1156,16 +1194,16 @@
     };
     this.hideNextButton = function () {
         this.buttonProceed.style.visibility = "hidden";
-    }
+    };
     this.hidePreviousButton = function () {
         this.buttonPrevious.style.visibility = "hidden";
-    }
+    };
     this.showNextButton = function () {
         this.buttonProceed.style.visibility = "visible";
-    }
+    };
     this.showPreviousButton = function () {
         this.buttonPrevious.style.visibility = "visible";
-    }
+    };
 }
 
 function advanceState() {
@@ -1175,7 +1213,7 @@
 
 function stateMachine() {
     // Object prototype for tracking and managing the test state
-
+    
     function pickSubPool(pool, numElements) {
         // Assumes each element of pool has function "alwaysInclude"
 
@@ -1189,7 +1227,7 @@
 
         return picked.concat(randomSubArray(pool, numElements - picked.length));
     }
-
+    
     this.stateMap = [];
     this.preTestSurvey = null;
     this.postTestSurvey = null;
@@ -1201,44 +1239,44 @@
 
         // Get the data from Specification
         var pagePool = [];
-        for (var page of specification.pages) {
+        specification.pages.forEach(function(page){
             if (page.position !== null || page.alwaysInclude) {
                 page.alwaysInclude = true;
             }
-            pagePool.push(page)
-        }
+            pagePool.push(page);
+        });
         if (specification.numPages > 0) {
             specification.randomiseOrder = true;
             pagePool = pickSubPool(pagePool, specification.numPages);
         }
 
         // Now get the order of pages
-        var fixed = []
-        for (var page of pagePool) {
+        var fixed = [];
+        pagePool.forEach(function(page){
             if (page.position !== null) {
                 fixed.push(page);
                 var i = pagePool.indexOf(page);
                 pagePool.splice(i, 1);
             }
-        }
+        });
 
         if (specification.randomiseOrder) {
             pagePool = randomiseOrder(pagePool);
         }
 
         // Place in the correct order
-        for (var page of fixed) {
-            pagePool.splice(page.position, 0, page)
-        }
+        fixed.forEach(function(page) {
+            pagePool.splice(page.position, 0, page);
+        });
 
         // Now process the pages
         pagePool.forEach(function (page, i) {
             page.presentedId = i;
             this.stateMap.push(page);
-            var elements = page.audioElements
+            var elements = page.audioElements;
             if (page.poolSize > 0 || page.randomiseOrder) {
                 page.randomiseOrder = true;
-                if (page.poolSize == 0) {
+                if (page.poolSize === 0) {
                     page.poolSize = elements.length;
                 }
                 elements = pickSubPool(elements, page.poolSize);
@@ -1249,17 +1287,17 @@
             page.audioElements = elements;
             storage.createTestPageStore(page);
             audioEngineContext.loadPageData(page);
-        }, this)
+        }, this);
 
-        if (specification.preTest != null) {
+        if (specification.preTest !== null) {
             this.preTestSurvey = specification.preTest;
         }
-        if (specification.postTest != null) {
+        if (specification.postTest !== null) {
             this.postTestSurvey = specification.postTest;
         }
 
         if (this.stateMap.length > 0) {
-            if (this.stateIndex != null) {
+            if (this.stateIndex !== null) {
                 console.log('NOTE - State already initialise');
             }
             this.stateIndex = -2;
@@ -1269,7 +1307,7 @@
         }
     };
     this.advanceState = function () {
-        if (this.stateIndex == null) {
+        if (this.stateIndex === null) {
             this.initialise();
         }
         if (this.stateIndex > -2) {
@@ -1277,7 +1315,7 @@
         }
         if (this.stateIndex == -2) {
             this.stateIndex++;
-            if (this.preTestSurvey != null) {
+            if (this.preTestSurvey !== undefined) {
                 popup.initState(this.preTestSurvey, storage.globalPreTest);
             } else {
                 this.advanceState();
@@ -1297,7 +1335,7 @@
             // All test pages complete, post test
             console.log('Ending test ...');
             this.stateIndex++;
-            if (this.postTestSurvey == null) {
+            if (this.postTestSurvey === undefined) {
                 this.advanceState();
             } else {
                 popup.initState(this.postTestSurvey, storage.globalPostTest);
@@ -1306,18 +1344,19 @@
             createProjectSave(specification.projectReturn);
         } else {
             popup.hidePopup();
-            if (this.currentStateMap == null) {
+            if (this.currentStateMap === null) {
                 this.currentStateMap = this.stateMap[this.stateIndex];
                 // Find and extract the outside reference
                 var elements = [],
                     ref = [];
-                var elem;
-                while (elem = this.currentStateMap.audioElements.pop()) {
+                var elem = this.currentStateMap.audioElements.pop();
+                while (elem) {
                     if (elem.type == "outside-reference") {
                         ref.push(elem);
                     } else {
                         elements.push(elem);
                     }
+                    elem = this.currentStateMap.audioElements.pop();
                 }
                 elements = elements.reverse();
                 if (this.currentStateMap.randomiseOrder) {
@@ -1326,7 +1365,7 @@
                 this.currentStateMap.audioElements = elements.concat(ref);
 
                 this.currentStore = storage.testPages[this.stateIndex];
-                if (this.currentStateMap.preTest != null) {
+                if (this.currentStateMap.preTest !== undefined) {
                     this.currentStatePosition = 'pre';
                     popup.initState(this.currentStateMap.preTest, storage.testPages[this.stateIndex].preTest);
                 } else {
@@ -1343,7 +1382,7 @@
                     this.currentStatePosition = 'post';
                     // Save the data
                     this.testPageCompleted();
-                    if (this.currentStateMap.postTest == null) {
+                    if (this.currentStateMap.postTest === undefined) {
                         this.advanceState();
                         return;
                     } else {
@@ -1355,7 +1394,7 @@
                     this.currentStateMap = null;
                     this.advanceState();
                     break;
-            };
+            }
         }
     };
 
@@ -1373,12 +1412,12 @@
         }
 
         var audioObjects = audioEngineContext.audioObjects;
-        for (var ao of audioEngineContext.audioObjects) {
+        audioEngineContext.audioObjects.forEach(function (ao) {
             ao.exportXMLDOM();
-        }
-        for (var element of interfaceContext.commentQuestions) {
+        });
+        interfaceContext.commentQuestions.forEach(function (element) {
             element.exportXMLDOM(storePoint);
-        }
+        });
         pageXMLSave(storePoint.XMLDOM, this.currentStateMap);
         storePoint.complete();
     };
@@ -1389,14 +1428,14 @@
         } else {
             return null;
         }
-    }
+    };
     this.getCurrentTestPageStore = function () {
         if (this.stateIndex >= 0 && this.stateIndex < this.stateMap.length) {
             return this.currentStore;
         } else {
             return null;
         }
-    }
+    };
 }
 
 function AudioEngine(specification) {
@@ -1451,7 +1490,7 @@
             }
             for (var i = 0; i < this.users.length; i++) {
                 this.users[i].state = 1;
-                if (this.users[i].interfaceDOM != null) {
+                if (this.users[i].interfaceDOM !== null) {
                     this.users[i].bufferLoaded(this);
                 }
             }
@@ -1479,7 +1518,7 @@
                 }
             }
             return false;
-        }
+        };
         this.getMedia = function () {
             var self = this;
             var currentUrlIndex = 0;
@@ -1523,7 +1562,7 @@
                     return true;
                 }, function (e) {
                     var waveObj = new WAVE();
-                    if (waveObj.open(response) == 0) {
+                    if (waveObj.open(response) === 0) {
                         self.buffer = audioContext.createBuffer(waveObj.num_channels, waveObj.num_samples, waveObj.sample_rate);
                         for (var c = 0; c < waveObj.num_channels; c++) {
                             var buffer_ptr = self.buffer.getChannelData(c);
@@ -1531,14 +1570,13 @@
                                 buffer_ptr[n] = waveObj.decoded_data[c][n];
                             }
                         }
-
-                        delete waveObj;
                     }
-                    if (self.buffer != undefined) {
+                    if (self.buffer !== undefined) {
                         self.status = 2;
                         calculateLoudness(self, "I");
                         return true;
                     }
+                    waveObj = undefined;
                     return false;
                 });
             }
@@ -1548,7 +1586,7 @@
                 this.status = -1;
                 for (var i = 0; i < this.users.length; i++) {
                     this.users[i].state = -1;
-                    if (this.users[i].interfaceDOM != null) {
+                    if (this.users[i].interfaceDOM !== null) {
                         this.users[i].bufferLoaded(this);
                     }
                 }
@@ -1559,14 +1597,14 @@
                 if (event.lengthComputable) {
                     this.progress = event.loaded / event.total;
                     for (var i = 0; i < this.users.length; i++) {
-                        if (this.users[i].interfaceDOM != null) {
+                        if (this.users[i].interfaceDOM !== null) {
                             if (typeof this.users[i].interfaceDOM.updateLoading === "function") {
                                 this.users[i].interfaceDOM.updateLoading(this.progress * 100);
                             }
                         }
                     }
                 }
-            };
+            }
 
             this.progress = 0;
             this.status = 1;
@@ -1577,11 +1615,11 @@
         this.registerAudioObject = function (audioObject) {
             // Called by an audioObject to register to the buffer for use
             // First check if already in the register pool
-            for (var objects of this.users) {
-                if (audioObject.id == objects.id) {
+            this.users.forEach(function (object) {
+                if (audioObject.id == object.id) {
                     return 0;
                 }
-            }
+            });
             this.users.push(audioObject);
             if (this.status == 3 || this.status == -1) {
                 // The buffer is already ready, trigger bufferLoaded
@@ -1591,23 +1629,24 @@
 
         this.copyBuffer = function (preSilenceTime, postSilenceTime) {
             // Copies the entire bufferObj.
-            if (preSilenceTime == undefined) {
+            if (preSilenceTime === undefined) {
                 preSilenceTime = 0;
             }
-            if (postSilenceTime == undefined) {
+            if (postSilenceTime === undefined) {
                 postSilenceTime = 0;
             }
             var preSilenceSamples = secondsToSamples(preSilenceTime, this.buffer.sampleRate);
             var postSilenceSamples = secondsToSamples(postSilenceTime, this.buffer.sampleRate);
             var newLength = this.buffer.length + preSilenceSamples + postSilenceSamples;
             var copybuffer = audioContext.createBuffer(this.buffer.numberOfChannels, newLength, this.buffer.sampleRate);
+            var c;
             // Now we can use some efficient background copy schemes if we are just padding the end
-            if (preSilenceSamples == 0 && typeof copybuffer.copyToChannel == "function") {
-                for (var c = 0; c < this.buffer.numberOfChannels; c++) {
+            if (preSilenceSamples === 0 && typeof copybuffer.copyToChannel === "function") {
+                for (c = 0; c < this.buffer.numberOfChannels; c++) {
                     copybuffer.copyToChannel(this.buffer.getChannelData(c), c);
                 }
             } else {
-                for (var c = 0; c < this.buffer.numberOfChannels; c++) {
+                for (c = 0; c < this.buffer.numberOfChannels; c++) {
                     var src = this.buffer.getChannelData(c);
                     var dst = copybuffer.getChannelData(c);
                     for (var n = 0; n < src.length; n++)
@@ -1618,7 +1657,7 @@
             copybuffer.lufs = this.buffer.lufs;
             copybuffer.playbackGain = this.buffer.playbackGain;
             return copybuffer;
-        }
+        };
 
         this.cropBuffer = function (startTime, stopTime) {
             // Copy and return the cropped buffer
@@ -1639,21 +1678,17 @@
                 }
             }
             return copybuffer;
-        }
+        };
     };
 
     this.loadPageData = function (page) {
         // Load the URL from pages
-        for (var element of page.audioElements) {
+        function loadAudioElementData(element) {
             var URL = page.hostURL + element.url;
-            var buffer = null;
-            for (var buffObj of this.buffers) {
-                if (buffObj.hasUrl(URL)) {
-                    buffer = buffObj;
-                    break;
-                }
-            }
-            if (buffer == null) {
+            var buffer = this.buffers.find(function (buffObj) {
+                return buffObj.hasUrl(URL);
+            });
+            if (buffer === undefined) {
                 buffer = new this.bufferObj();
                 var urls = [{
                     url: URL,
@@ -1670,44 +1705,51 @@
                 this.buffers.push(buffer);
             }
         }
+        page.audioElements.forEach(loadAudioElementData, this);
     };
 
+    function playNormal(id) {
+        var playTime = audioContext.currentTime + 0.1;
+        var stopTime = playTime + specification.crossFade;
+        this.audioObjects.forEach(function (ao) {
+            if (ao.id === id) {
+                ao.play(playTime);
+            } else {
+                ao.stop(stopTime);
+            }
+        });
+    }
+
+    function playLoopSync(id) {
+        var playTime = audioContext.currentTime + 0.1;
+        var stopTime = playTime + specification.crossFade;
+        this.audioObjects.forEach(function (ao) {
+            ao.play(playTime);
+            if (ao.id === id) {
+                ao.loopStart(playTime);
+            } else {
+                ao.loopStop(stopTime);
+            }
+        });
+    }
+
     this.play = function (id) {
         // Start the timer and set the audioEngine state to playing (1)
-        if (this.status == 0) {
-            // Check if all audioObjects are ready
-            this.bufferReady(id);
-        } else {
-            this.status = 1;
+        if (typeof id !== "number" || id < 0 || id > this.audioObjects.length) {
+            throw ('FATAL - Passed id was undefined - AudioEngineContext.play(id)');
         }
-        if (this.status == 1) {
+        if (this.status === 1) {
             this.timer.startTest();
-            if (id == undefined) {
-                id = -1;
-                console.error('FATAL - Passed id was undefined - AudioEngineContext.play(id)');
-                return;
-            } else {
-                interfaceContext.playhead.setTimePerPixel(this.audioObjects[id]);
-            }
-            var setTime = audioContext.currentTime;
+            interfaceContext.playhead.setTimePerPixel(this.audioObjects[id]);
             if (this.synchPlayback && this.loopPlayback) {
                 // Traditional looped playback
-                for (var i = 0; i < this.audioObjects.length; i++) {
-                    this.audioObjects[i].play(audioContext.currentTime);
-                    if (id == i) {
-                        this.audioObjects[i].loopStart(setTime);
-                    } else {
-                        this.audioObjects[i].loopStop(setTime + specification.crossFade);
-                    }
+                playLoopSync.call(this, id);
+            } else {
+                if (this.bufferReady(id) === false) {
+                    console.log("Cannot play. Buffer not ready");
+                    return;
                 }
-            } else {
-                for (var i = 0; i < this.audioObjects.length; i++) {
-                    if (i != id) {
-                        this.audioObjects[i].stop(setTime + specification.crossFade);
-                    } else if (i == id) {
-                        this.audioObjects[id].play(setTime);
-                    }
-                }
+                playNormal.call(this, id);
             }
             interfaceContext.playhead.start();
         }
@@ -1717,9 +1759,9 @@
         // Send stop and reset command to all playback buffers
         if (this.status == 1) {
             var setTime = audioContext.currentTime + 0.1;
-            for (var i = 0; i < this.audioObjects.length; i++) {
-                this.audioObjects[i].stop(setTime);
-            }
+            this.audioObjects.forEach(function (a) {
+                a.stop(setTime);
+            });
             interfaceContext.playhead.stop();
         }
     };
@@ -1729,19 +1771,15 @@
         // URLs must either be from the same source OR be setup to 'Access-Control-Allow-Origin'
 
         // Create the audioObject with ID of the new track length;
-        audioObjectId = this.audioObjects.length;
+        var audioObjectId = this.audioObjects.length;
         this.audioObjects[audioObjectId] = new audioObject(audioObjectId);
 
         // Check if audioObject buffer is currently stored by full URL
         var URL = testState.currentStateMap.hostURL + element.url;
-        var buffer = null;
-        for (var i = 0; i < this.buffers.length; i++) {
-            if (this.buffers[i].hasUrl(URL)) {
-                buffer = this.buffers[i];
-                break;
-            }
-        }
-        if (buffer == null) {
+        var buffer = this.buffers.find(function (buffObj) {
+            return buffObj.hasUrl(URL);
+        });
+        if (buffer === undefined) {
             console.log("[WARN]: Buffer was not loaded in pre-test! " + URL);
             buffer = new this.bufferObj();
             this.buffers.push(buffer);
@@ -1767,9 +1805,9 @@
         this.status = 0;
         this.audioObjectsReady = false;
         this.metric.reset();
-        for (var i = 0; i < this.buffers.length; i++) {
-            this.buffers[i].users = [];
-        }
+        this.buffers.forEach(function (buffer) {
+            buffer.users = [];
+        });
         this.audioObjects = [];
         this.timer = new timer();
         this.loopPlayback = audioHolderObject.loop;
@@ -1777,9 +1815,9 @@
     };
 
     this.checkAllPlayed = function () {
-        arr = [];
+        var arr = [];
         for (var id = 0; id < this.audioObjects.length; id++) {
-            if (this.audioObjects[id].metric.wasListenedTo == false) {
+            if (this.audioObjects[id].metric.wasListenedTo === false) {
                 arr.push(this.audioObjects[id].id);
             }
         }
@@ -1789,11 +1827,11 @@
     this.checkAllReady = function () {
         var ready = true;
         for (var i = 0; i < this.audioObjects.length; i++) {
-            if (this.audioObjects[i].state == 0) {
+            if (this.audioObjects[i].state === 0) {
                 // Track not ready
                 console.log('WAIT -- audioObject ' + i + ' not ready yet!');
                 ready = false;
-            };
+            }
         }
         return ready;
     };
@@ -1810,11 +1848,11 @@
             }
         }
         // Extract the audio and zero-pad
-        for (var ao of this.audioObjects) {
+        this.audioObjects.forEach(function (ao) {
             if (ao.buffer.buffer.duration !== duration) {
                 ao.buffer.buffer = ao.buffer.copyBuffer(0, duration - ao.buffer.buffer.duration);
             }
-        }
+        });
     };
 
     this.bufferReady = function (id) {
@@ -1826,10 +1864,6 @@
             return true;
         }
         return false;
-    }
-
-    this.exportXML = function () {
-
     };
 
 }
@@ -1837,7 +1871,7 @@
 function audioObject(id) {
     // The main buffer object with common control nodes to the AudioEngine
 
-    this.specification;
+    this.specification = undefined;
     this.id = id;
     this.state = 0; // 0 - no data, 1 - ready
     this.url = null; // Hold the URL given for the output back to the results.
@@ -1860,7 +1894,7 @@
 
     // the audiobuffer is not designed for multi-start playback
     // When stopeed, the buffer node is deleted and recreated with the stored buffer.
-    this.buffer;
+    this.buffer = undefined;
 
     this.bufferLoaded = function (callee) {
         // Called by the associated buffer when it has finished loading, will then 'bind' the buffer to the
@@ -1868,7 +1902,7 @@
         if (callee.status == -1) {
             // ERROR
             this.state = -1;
-            if (this.interfaceDOM != null) {
+            if (this.interfaceDOM !== null) {
                 this.interfaceDOM.error();
             }
             this.buffer = callee;
@@ -1882,7 +1916,7 @@
         var copybuffer = new callee.constructor();
 
         copybuffer.buffer = callee.cropBuffer(startTime || 0, stopTime || callee.buffer.duration);
-        if (preSilenceTime != 0 || postSilenceTime != 0) {
+        if (preSilenceTime !== 0 || postSilenceTime !== 0) {
             copybuffer.buffer = copybuffer.copyBuffer(preSilenceTime, postSilenceTime);
         }
 
@@ -1895,7 +1929,7 @@
         } else {
             this.buffer.buffer.playbackGain = 1.0;
         }
-        if (this.interfaceDOM != null) {
+        if (this.interfaceDOM !== null) {
             this.interfaceDOM.enable();
         }
         this.onplayGain = decibelToLinear(this.specification.gain) * (this.buffer.buffer.playbackGain || 1.0);
@@ -1924,7 +1958,7 @@
     };
 
     this.loopStop = function (setTime) {
-        if (this.outputGain.gain.value != 0.0) {
+        if (this.outputGain.gain.value !== 0.0) {
             this.outputGain.gain.linearRampToValueAtTime(0.0, setTime);
             this.metric.stopListening(audioEngineContext.timer.getTestTime());
         }
@@ -1932,7 +1966,7 @@
     };
 
     this.play = function (startTime) {
-        if (this.bufferNode == undefined && this.buffer.buffer != undefined) {
+        if (this.bufferNode === undefined && this.buffer.buffer !== undefined) {
             this.bufferNode = audioContext.createBufferSource();
             this.bufferNode.owner = this;
             this.bufferNode.connect(this.outputGain);
@@ -1941,7 +1975,7 @@
             this.bufferNode.onended = function (event) {
                 // Safari does not like using 'this' to reference the calling object!
                 //event.currentTarget.owner.metric.stopListening(audioEngineContext.timer.getTestTime(),event.currentTarget.owner.getCurrentPosition());
-                if (event.currentTarget != null) {
+                if (event.currentTarget !== null) {
                     event.currentTarget.owner.stop(audioContext.currentTime + 1);
                 }
             };
@@ -1966,7 +2000,7 @@
 
     this.stop = function (stopTime) {
         this.outputGain.gain.cancelScheduledValues(audioContext.currentTime);
-        if (this.bufferNode != undefined) {
+        if (this.bufferNode !== undefined) {
             this.metric.stopListening(audioEngineContext.timer.getTestTime(), this.getCurrentPosition());
             this.bufferNode.stop(stopTime);
             this.bufferNode = undefined;
@@ -1977,7 +2011,7 @@
 
     this.getCurrentPosition = function () {
         var time = audioEngineContext.timer.getTestTime();
-        if (this.bufferNode != undefined) {
+        if (this.bufferNode !== undefined) {
             var position = (time - this.bufferNode.playbackStartTime) % this.buffer.buffer.duration;
             if (isNaN(position)) {
                 return 0;
@@ -1997,8 +2031,8 @@
         this.storeDOM.appendChild(file);
         if (this.specification.type != 'outside-reference') {
             var interfaceXML = this.interfaceDOM.exportXMLDOM(this);
-            if (interfaceXML != null) {
-                if (interfaceXML.length == undefined) {
+            if (interfaceXML !== null) {
+                if (interfaceXML.length === undefined) {
                     this.storeDOM.appendChild(interfaceXML);
                 } else {
                     for (var i = 0; i < interfaceXML.length; i++) {
@@ -2006,15 +2040,11 @@
                     }
                 }
             }
-            if (this.commentDOM != null) {
+            if (this.commentDOM !== null) {
                 this.storeDOM.appendChild(this.commentDOM.exportXMLDOM(this));
             }
         }
-        var nodes = this.metric.exportXMLDOM();
-        var mroot = this.storeDOM.getElementsByTagName('metric')[0];
-        for (var i = 0; i < nodes.length; i++) {
-            mroot.appendChild(nodes[i]);
-        }
+        this.metric.exportXMLDOM(this.storeDOM.getElementsByTagName('metric')[0]);
     };
 }
 
@@ -2027,7 +2057,7 @@
     this.testDuration = 0;
     this.minimumTestTime = 0; // No minimum test time
     this.startTest = function () {
-        if (this.testStarted == false) {
+        if (this.testStarted === false) {
             this.testStartTime = audioContext.currentTime;
             this.testStarted = true;
             this.updateTestTime();
@@ -2135,7 +2165,7 @@
     };
 
     this.startListening = function (time) {
-        if (this.listenHold == false) {
+        if (this.listenHold === false) {
             this.wasListenedTo = true;
             this.listenStart = time;
             this.listenHold = true;
@@ -2154,7 +2184,7 @@
     };
 
     this.stopListening = function (time, bufferStopTime) {
-        if (this.listenHold == true) {
+        if (this.listenHold === true) {
             var diff = time - this.listenStart;
             this.listenedTimer += (diff);
             this.listenStart = 0;
@@ -2164,7 +2194,7 @@
             var testTime = evnt.getElementsByTagName('testTime')[0];
             var bufferTime = evnt.getElementsByTagName('bufferTime')[0];
             testTime.setAttribute('stop', time);
-            if (bufferStopTime == undefined) {
+            if (bufferStopTime === undefined) {
                 bufferTime.setAttribute('stop', this.parent.getCurrentPosition());
             } else {
                 bufferTime.setAttribute('stop', bufferStopTime);
@@ -2173,64 +2203,99 @@
         }
     };
 
-    this.exportXMLDOM = function () {
-        var storeDOM = [];
+    function exportElementTimer(parentElement) {
+        var mElementTimer = storage.document.createElement('metricresult');
+        mElementTimer.setAttribute('name', 'enableElementTimer');
+        mElementTimer.textContent = this.listenedTimer;
+        parentElement.appendChild(mElementTimer);
+        return mElementTimer;
+    }
+
+    function exportElementTrack(parentElement) {
+        var elementTrackerFull = storage.document.createElement('metricresult');
+        elementTrackerFull.setAttribute('name', 'elementTrackerFull');
+        for (var k = 0; k < this.movementTracker.length; k++) {
+            var timePos = storage.document.createElement('movement');
+            timePos.setAttribute("time", this.movementTracker[k][0]);
+            timePos.setAttribute("value", this.movementTracker[k][1]);
+            elementTrackerFull.appendChild(timePos);
+        }
+        parentElement.appendChild(elementTrackerFull);
+        return elementTrackerFull;
+    }
+
+    function exportElementListenTracker(parentElement) {
+        var elementListenTracker = storage.document.createElement('metricresult');
+        elementListenTracker.setAttribute('name', 'elementListenTracker');
+        for (var k = 0; k < this.listenTracker.length; k++) {
+            elementListenTracker.appendChild(this.listenTracker[k]);
+        }
+        parentElement.appendChild(elementListenTracker);
+        return elementListenTracker;
+    }
+
+    function exportElementInitialPosition(parentElement) {
+        var elementInitial = storage.document.createElement('metricresult');
+        elementInitial.setAttribute('name', 'elementInitialPosition');
+        elementInitial.textContent = this.initialPosition;
+        parentElement.appendChild(elementInitial);
+        return elementInitial;
+    }
+
+    function exportFlagListenedTo(parentElement) {
+        var flagListenedTo = storage.document.createElement('metricresult');
+        flagListenedTo.setAttribute('name', 'elementFlagListenedTo');
+        flagListenedTo.textContent = this.wasListenedTo;
+        parentElement.appendChild(flagListenedTo);
+        return flagListenedTo;
+    }
+
+    function exportFlagMoved(parentElement) {
+        var flagMoved = storage.document.createElement('metricresult');
+        flagMoved.setAttribute('name', 'elementFlagMoved');
+        flagMoved.textContent = this.wasMoved;
+        parentElement.appendChild(flagMoved);
+        return flagMoved;
+    }
+
+    function exportFlagComments(parentElement) {
+        var flagComments = storage.document.createElement('metricresult');
+        flagComments.setAttribute('name', 'elementFlagComments');
+        if (this.parent.commentDOM === null) {
+            flagComments.textContent = 'false';
+        } else if (this.parent.commentDOM.textContent.length === 0) {
+            flagComments.textContent = 'false';
+        } else {
+            flagComments.textContet = 'true';
+        }
+        parentElement.appendChild(flagComments);
+        return flagComments;
+    }
+
+    this.exportXMLDOM = function (parentElement) {
+        var elems = [];
         if (audioEngineContext.metric.enableElementTimer) {
-            var mElementTimer = storage.document.createElement('metricresult');
-            mElementTimer.setAttribute('name', 'enableElementTimer');
-            mElementTimer.textContent = this.listenedTimer;
-            storeDOM.push(mElementTimer);
+            elems.push(exportElementTimer.call(this, parentElement));
         }
         if (audioEngineContext.metric.enableElementTracker) {
-            var elementTrackerFull = storage.document.createElement('metricresult');
-            elementTrackerFull.setAttribute('name', 'elementTrackerFull');
-            for (var k = 0; k < this.movementTracker.length; k++) {
-                var timePos = storage.document.createElement('movement');
-                timePos.setAttribute("time", this.movementTracker[k][0]);
-                timePos.setAttribute("value", this.movementTracker[k][1]);
-                elementTrackerFull.appendChild(timePos);
-            }
-            storeDOM.push(elementTrackerFull);
+            elems.push(exportElementTrack.call(this, parentElement));
         }
         if (audioEngineContext.metric.enableElementListenTracker) {
-            var elementListenTracker = storage.document.createElement('metricresult');
-            elementListenTracker.setAttribute('name', 'elementListenTracker');
-            for (var k = 0; k < this.listenTracker.length; k++) {
-                elementListenTracker.appendChild(this.listenTracker[k]);
-            }
-            storeDOM.push(elementListenTracker);
+            elems.push(exportElementListenTracker.call(this, parentElement));
         }
         if (audioEngineContext.metric.enableElementInitialPosition) {
-            var elementInitial = storage.document.createElement('metricresult');
-            elementInitial.setAttribute('name', 'elementInitialPosition');
-            elementInitial.textContent = this.initialPosition;
-            storeDOM.push(elementInitial);
+            elems.push(exportElementInitialPosition.call(this, parentElement));
         }
         if (audioEngineContext.metric.enableFlagListenedTo) {
-            var flagListenedTo = storage.document.createElement('metricresult');
-            flagListenedTo.setAttribute('name', 'elementFlagListenedTo');
-            flagListenedTo.textContent = this.wasListenedTo;
-            storeDOM.push(flagListenedTo);
+            elems.push(exportFlagListenedTo.call(this, parentElement));
         }
         if (audioEngineContext.metric.enableFlagMoved) {
-            var flagMoved = storage.document.createElement('metricresult');
-            flagMoved.setAttribute('name', 'elementFlagMoved');
-            flagMoved.textContent = this.wasMoved;
-            storeDOM.push(flagMoved);
+            elems.push(exportFlagMoved.call(this, parentElement));
         }
         if (audioEngineContext.metric.enableFlagComments) {
-            var flagComments = storage.document.createElement('metricresult');
-            flagComments.setAttribute('name', 'elementFlagComments');
-            if (this.parent.commentDOM == null) {
-                flag.textContent = 'false';
-            } else if (this.parent.commentDOM.textContent.length == 0) {
-                flag.textContent = 'false';
-            } else {
-                flag.textContet = 'true';
-            }
-            storeDOM.push(flagComments);
+            elems.push(exportFlagComments.call(this, parentElement));
         }
-        return storeDOM;
+        return elems;
     };
 }
 
@@ -2256,12 +2321,12 @@
         popup.resize(event);
         this.volume.resize();
         this.lightbox.resize();
-        for (var i = 0; i < this.commentBoxes.length; i++) {
-            this.commentBoxes[i].resize();
-        }
-        for (var i = 0; i < this.commentQuestions.length; i++) {
-            this.commentQuestions[i].resize();
-        }
+        this.commentBoxes.boxes.forEach(function (elem) {
+            elem.resize();
+        });
+        this.commentQuestions.forEach(function (elem) {
+            elem.resize();
+        });
         try {
             resizeWindow(event);
         } catch (err) {
@@ -2310,7 +2375,7 @@
         hold.appendChild(time);
         return hold;
 
-    }
+    };
 
     this.lightbox = {
         parent: this,
@@ -2352,7 +2417,7 @@
         resize: function (event) {
             this.root.style.left = (window.innerWidth / 2) - 250 + 'px';
         }
-    }
+    };
 
     this.lightbox.root.appendChild(this.lightbox.content);
     this.lightbox.root.appendChild(this.lightbox.accept);
@@ -2368,10 +2433,11 @@
     document.getElementsByTagName("body")[0].appendChild(this.lightbox.root);
     document.getElementsByTagName("body")[0].appendChild(this.lightbox.blanker);
 
-    this.commentBoxes = new function () {
-        this.boxes = [];
-        this.injectPoint = null;
-        this.elementCommentBox = function (audioObject) {
+    this.commentBoxes = (function () {
+        var commentBoxes = {};
+        commentBoxes.boxes = [];
+        commentBoxes.injectPoint = null;
+        commentBoxes.elementCommentBox = function (audioObject) {
             var element = audioObject.specification;
             this.audioObject = audioObject;
             this.id = audioObject.id;
@@ -2418,38 +2484,39 @@
             };
             this.resize();
         };
-        this.createCommentBox = function (audioObject) {
+        commentBoxes.createCommentBox = function (audioObject) {
             var node = new this.elementCommentBox(audioObject);
             this.boxes.push(node);
             audioObject.commentDOM = node;
             return node;
         };
-        this.sortCommentBoxes = function () {
+        commentBoxes.sortCommentBoxes = function () {
             this.boxes.sort(function (a, b) {
                 return a.id - b.id;
             });
         };
 
-        this.showCommentBoxes = function (inject, sort) {
+        commentBoxes.showCommentBoxes = function (inject, sort) {
             this.injectPoint = inject;
             if (sort) {
                 this.sortCommentBoxes();
             }
-            for (var box of this.boxes) {
+            this.boxes.forEach(function (box) {
                 inject.appendChild(box.trackComment);
-            }
+            });
         };
 
-        this.deleteCommentBoxes = function () {
-            if (this.injectPoint != null) {
-                for (var box of this.boxes) {
+        commentBoxes.deleteCommentBoxes = function () {
+            if (this.injectPoint !== null) {
+                this.boxes.forEach(function (box) {
                     this.injectPoint.removeChild(box.trackComment);
-                }
+                }, this);
                 this.injectPoint = null;
             }
             this.boxes = [];
         };
-    }
+        return commentBoxes;
+    })();
 
     this.commentQuestions = [];
 
@@ -2508,47 +2575,32 @@
         // Create a string next to each comment asking for a comment
         this.string = document.createElement('span');
         this.string.innerHTML = commentQuestion.statement;
-        var br = document.createElement('br');
         // Add to the holder.
         this.holder.appendChild(this.string);
-        this.holder.appendChild(br);
         this.options = [];
         this.inputs = document.createElement('div');
-        this.span = document.createElement('div');
-        this.inputs.align = 'center';
-        this.inputs.style.marginLeft = '12px';
-        this.inputs.className = "comment-radio-inputs-holder";
-        this.span.style.marginLeft = '12px';
-        this.span.align = 'center';
-        this.span.style.marginTop = '15px';
-        this.span.className = "comment-radio-span-holder";
+        this.inputs.className = "comment-checkbox-inputs-holder";
 
         var optCount = commentQuestion.options.length;
-        for (var optNode of commentQuestion.options) {
+        for (var i = 0; i < optCount; i++) {
             var div = document.createElement('div');
-            div.style.width = '80px';
-            div.style.float = 'left';
+            div.className = "comment-checkbox-inputs-flex";
+            
+            var span = document.createElement('span');
+            span.textContent = commentQuestion.options[i].text;
+            span.className = 'comment-radio-span';
+            div.appendChild(span);
+            
             var input = document.createElement('input');
             input.type = 'radio';
             input.name = commentQuestion.id;
-            input.setAttribute('setvalue', optNode.name);
+            input.setAttribute('setvalue', commentQuestion.options[i].name);
             input.className = 'comment-radio';
             div.appendChild(input);
+            
             this.inputs.appendChild(div);
-
-
-            div = document.createElement('div');
-            div.style.width = '80px';
-            div.style.float = 'left';
-            div.align = 'center';
-            var span = document.createElement('span');
-            span.textContent = optNode.text;
-            span.className = 'comment-radio-span';
-            div.appendChild(span);
-            this.span.appendChild(div);
             this.options.push(input);
         }
-        this.holder.appendChild(this.span);
         this.holder.appendChild(this.inputs);
 
         this.exportXMLDOM = function (storePoint) {
@@ -2559,7 +2611,7 @@
             question.textContent = this.string.textContent;
             var response = document.createElement('response');
             var i = 0;
-            while (this.options[i].checked == false) {
+            while (this.options[i].checked === false) {
                 i++;
                 if (i >= this.options.length) {
                     break;
@@ -2586,24 +2638,6 @@
                 boxwidth = 400;
             }
             this.holder.style.width = boxwidth + "px";
-            var text = this.holder.getElementsByClassName("comment-radio-span-holder")[0];
-            var options = this.holder.getElementsByClassName("comment-radio-inputs-holder")[0];
-            var optCount = options.childElementCount;
-            var spanMargin = Math.floor(((boxwidth - 20 - (optCount * 80)) / (optCount)) / 2) + 'px';
-            var options = options.firstChild;
-            var text = text.firstChild;
-            options.style.marginRight = spanMargin;
-            options.style.marginLeft = spanMargin;
-            text.style.marginRight = spanMargin;
-            text.style.marginLeft = spanMargin;
-            while (options.nextSibling != undefined) {
-                options = options.nextSibling;
-                text = text.nextSibling;
-                options.style.marginRight = spanMargin;
-                options.style.marginLeft = spanMargin;
-                text.style.marginRight = spanMargin;
-                text.style.marginLeft = spanMargin;
-            }
         };
         this.resize();
     };
@@ -2616,47 +2650,32 @@
         // Create a string next to each comment asking for a comment
         this.string = document.createElement('span');
         this.string.innerHTML = commentQuestion.statement;
-        var br = document.createElement('br');
         // Add to the holder.
         this.holder.appendChild(this.string);
-        this.holder.appendChild(br);
         this.options = [];
         this.inputs = document.createElement('div');
-        this.span = document.createElement('div');
-        this.inputs.align = 'center';
-        this.inputs.style.marginLeft = '12px';
         this.inputs.className = "comment-checkbox-inputs-holder";
-        this.span.style.marginLeft = '12px';
-        this.span.align = 'center';
-        this.span.style.marginTop = '15px';
-        this.span.className = "comment-checkbox-span-holder";
 
         var optCount = commentQuestion.options.length;
         for (var i = 0; i < optCount; i++) {
             var div = document.createElement('div');
-            div.style.width = '80px';
-            div.style.float = 'left';
+            div.className = "comment-checkbox-inputs-flex";
+            
+            var span = document.createElement('span');
+            span.textContent = commentQuestion.options[i].text;
+            span.className = 'comment-radio-span';
+            div.appendChild(span);
+            
             var input = document.createElement('input');
             input.type = 'checkbox';
             input.name = commentQuestion.id;
             input.setAttribute('setvalue', commentQuestion.options[i].name);
             input.className = 'comment-radio';
             div.appendChild(input);
+            
             this.inputs.appendChild(div);
-
-
-            div = document.createElement('div');
-            div.style.width = '80px';
-            div.style.float = 'left';
-            div.align = 'center';
-            var span = document.createElement('span');
-            span.textContent = commentQuestion.options[i].text;
-            span.className = 'comment-radio-span';
-            div.appendChild(span);
-            this.span.appendChild(div);
             this.options.push(input);
         }
-        this.holder.appendChild(this.span);
         this.holder.appendChild(this.inputs);
 
         this.exportXMLDOM = function (storePoint) {
@@ -2685,24 +2704,6 @@
                 boxwidth = 400;
             }
             this.holder.style.width = boxwidth + "px";
-            var text = this.holder.getElementsByClassName("comment-checkbox-span-holder")[0];
-            var options = this.holder.getElementsByClassName("comment-checkbox-inputs-holder")[0];
-            var optCount = options.childElementCount;
-            var spanMargin = Math.floor(((boxwidth - 20 - (optCount * 80)) / (optCount)) / 2) + 'px';
-            var options = options.firstChild;
-            var text = text.firstChild;
-            options.style.marginRight = spanMargin;
-            options.style.marginLeft = spanMargin;
-            text.style.marginRight = spanMargin;
-            text.style.marginLeft = spanMargin;
-            while (options.nextSibling != undefined) {
-                options = options.nextSibling;
-                text = text.nextSibling;
-                options.style.marginRight = spanMargin;
-                options.style.marginLeft = spanMargin;
-                text.style.marginRight = spanMargin;
-                text.style.marginLeft = spanMargin;
-            }
         };
         this.resize();
     };
@@ -2790,10 +2791,10 @@
         this.outsideReferenceHolder.setAttribute('track-id', index);
         this.outsideReferenceHolder.textContent = this.parent.specification.label || "Reference";
         this.outsideReferenceHolder.disabled = true;
-
-        this.outsideReferenceHolder.onclick = function (event) {
-            audioEngineContext.play(event.currentTarget.getAttribute('track-id'));
+        this.handleEvent = function (event) {
+            audioEngineContext.play(this.parent.id);
         };
+        this.outsideReferenceHolder.addEventListener("click", this);
         inject.appendChild(this.outsideReferenceHolder);
         this.enable = function () {
             if (this.parent.state == 1) {
@@ -2835,33 +2836,34 @@
             // audioObject has an error!!
             this.outsideReferenceHolder.textContent = "Error";
             this.outsideReferenceHolder.style.backgroundColor = "#F00";
-        }
-    }
+        };
+    };
 
-    this.playhead = new function () {
-        this.object = document.createElement('div');
-        this.object.className = 'playhead';
-        this.object.align = 'left';
+    this.playhead = (function () {
+        var playhead ={};
+        playhead.object = document.createElement('div');
+        playhead.object.className = 'playhead';
+        playhead.object.align = 'left';
         var curTime = document.createElement('div');
         curTime.style.width = '50px';
-        this.curTimeSpan = document.createElement('span');
-        this.curTimeSpan.textContent = '00:00';
-        curTime.appendChild(this.curTimeSpan);
-        this.object.appendChild(curTime);
-        this.scrubberTrack = document.createElement('div');
-        this.scrubberTrack.className = 'playhead-scrub-track';
+        playhead.curTimeSpan = document.createElement('span');
+        playhead.curTimeSpan.textContent = '00:00';
+        curTime.appendChild(playhead.curTimeSpan);
+        playhead.object.appendChild(curTime);
+        playhead.scrubberTrack = document.createElement('div');
+        playhead.scrubberTrack.className = 'playhead-scrub-track';
 
-        this.scrubberHead = document.createElement('div');
-        this.scrubberHead.id = 'playhead-scrubber';
-        this.scrubberTrack.appendChild(this.scrubberHead);
-        this.object.appendChild(this.scrubberTrack);
+        playhead.scrubberHead = document.createElement('div');
+        playhead.scrubberHead.id = 'playhead-scrubber';
+        playhead.scrubberTrack.appendChild(playhead.scrubberHead);
+        playhead.object.appendChild(playhead.scrubberTrack);
 
-        this.timePerPixel = 0;
-        this.maxTime = 0;
+        playhead.timePerPixel = 0;
+        playhead.maxTime = 0;
 
-        this.playbackObject;
+        playhead.playbackObject = undefined;
 
-        this.setTimePerPixel = function (audioObject) {
+        playhead.setTimePerPixel = function (audioObject) {
             //maxTime must be in seconds
             this.playbackObject = audioObject;
             this.maxTime = audioObject.buffer.buffer.duration;
@@ -2874,7 +2876,7 @@
             }
         };
 
-        this.update = function () {
+        playhead.update = function () {
             // Update the playhead position, startPlay must be called
             if (this.timePerPixel > 0) {
                 var time = this.playbackObject.getCurrentPosition();
@@ -2904,23 +2906,23 @@
             }
         };
 
-        this.interval = undefined;
+        playhead.interval = undefined;
 
-        this.start = function () {
-            if (this.playbackObject != undefined && this.interval == undefined) {
+        playhead.start = function () {
+            if (this.playbackObject !== undefined && this.interval === undefined) {
                 if (this.maxTime < 60) {
-                    this.interval = setInterval(function () {
+                    this.interval = window.setInterval(function () {
                         interfaceContext.playhead.update();
                     }, 10);
                 } else {
-                    this.interval = setInterval(function () {
+                    this.interval = window.setInterval(function () {
                         interfaceContext.playhead.update();
                     }, 100);
                 }
             }
         };
-        this.stop = function () {
-            clearInterval(this.interval);
+        playhead.stop = function () {
+            window.clearInterval(this.interval);
             this.interval = undefined;
             this.scrubberHead.style.left = '0px';
             if (this.maxTime < 60) {
@@ -2929,31 +2931,33 @@
                 this.curTimeSpan.textContent = '00:00';
             }
         };
-    };
+        return playhead;
+    })();
 
-    this.volume = new function () {
+    this.volume = (function () {
         // An in-built volume module which can be viewed on page
         // Includes trackers on page-by-page data
         // Volume does NOT reset to 0dB on each page load
-        this.valueLin = 1.0;
-        this.valueDB = 0.0;
-        this.root = document.createElement('div');
-        this.root.id = 'master-volume-root';
-        this.object = document.createElement('div');
-        this.object.className = 'master-volume-holder-float';
-        this.object.appendChild(this.root);
-        this.slider = document.createElement('input');
-        this.slider.id = 'master-volume-control';
-        this.slider.type = 'range';
-        this.valueText = document.createElement('span');
-        this.valueText.id = 'master-volume-feedback';
-        this.valueText.textContent = '0dB';
+        var volume = {};
+        volume.valueLin = 1.0;
+        volume.valueDB = 0.0;
+        volume.root = document.createElement('div');
+        volume.root.id = 'master-volume-root';
+        volume.object = document.createElement('div');
+        volume.object.className = 'master-volume-holder-float';
+        volume.object.appendChild(volume.root);
+        volume.slider = document.createElement('input');
+        volume.slider.id = 'master-volume-control';
+        volume.slider.type = 'range';
+        volume.valueText = document.createElement('span');
+        volume.valueText.id = 'master-volume-feedback';
+        volume.valueText.textContent = '0dB';
 
-        this.slider.min = -60;
-        this.slider.max = 12;
-        this.slider.value = 0;
-        this.slider.step = 1;
-        this.handleEvent = function (event) {
+        volume.slider.min = -60;
+        volume.slider.max = 12;
+        volume.slider.value = 0;
+        volume.slider.step = 1;
+        volume.handleEvent = function (event) {
             if (event.type == "mousemove") {
                 this.valueDB = Number(this.slider.value);
                 this.valueLin = decibelToLinear(this.valueDB);
@@ -2967,10 +2971,10 @@
             if (event.stopPropagation) {
                 event.stopPropagation();
             }
-        }
-        this.onmouseup = function () {
+        };
+        volume.onmouseup = function () {
             var storePoint = testState.currentStore.XMLDOM.getElementsByTagName('metric')[0].getAllElementsByName('volumeTracker');
-            if (storePoint.length == 0) {
+            if (storePoint.length === 0) {
                 storePoint = storage.document.createElement('metricresult');
                 storePoint.setAttribute('name', 'volumeTracker');
                 testState.currentStore.XMLDOM.getElementsByTagName('metric')[0].appendChild(storePoint);
@@ -2982,28 +2986,29 @@
             node.setAttribute('volume', this.valueDB);
             node.setAttribute('format', 'dBFS');
             storePoint.appendChild(node);
-        }
-        this.slider.addEventListener("mousemove", this);
-        this.root.addEventListener("mouseup", this);
+        };
+        volume.slider.addEventListener("mousemove", volume);
+        volume.root.addEventListener("mouseup", volume);
 
         var title = document.createElement('div');
         title.innerHTML = '<span>Master Volume Control</span>';
         title.style.fontSize = '0.75em';
         title.style.width = "100%";
         title.align = 'center';
-        this.root.appendChild(title);
+        volume.root.appendChild(title);
 
-        this.root.appendChild(this.slider);
-        this.root.appendChild(this.valueText);
+        volume.root.appendChild(volume.slider);
+        volume.root.appendChild(volume.valueText);
 
-        this.resize = function (event) {
+        volume.resize = function (event) {
             if (window.innerWidth < 1000) {
-                this.object.className = "master-volume-holder-inline"
+                this.object.className = "master-volume-holder-inline";
             } else {
                 this.object.className = 'master-volume-holder-float';
             }
-        }
-    }
+        };
+        return volume;
+    })();
 
     this.calibrationModuleObject = null;
     this.calibrationModule = function () {
@@ -3019,6 +3024,7 @@
             this.holder.className = "calibration-holder";
             this.calibrationNodes = [];
             while (f0 < 20000) {
+                /* jshint loopfunc: true */
                 var obj = {
                     root: document.createElement("div"),
                     input: document.createElement("input"),
@@ -3043,7 +3049,7 @@
                                     audioEngineContext.outputGain.gain.value = value;
                                     interfaceContext.volume.slider.value = this.input.value;
                                 } else {
-                                    this.gain.gain.value = value
+                                    this.gain.gain.value = value;
                                 }
                                 break;
                         }
@@ -3051,7 +3057,7 @@
                     disconnect: function () {
                         this.gain.disconnect();
                     }
-                }
+                };
                 obj.root.className = "calibration-slider";
                 obj.root.appendChild(obj.input);
                 obj.oscillator.connect(obj.gain);
@@ -3079,48 +3085,50 @@
                 f0 *= 2;
             }
             inject.appendChild(this.holder);
-        }
+        };
         this.collect = function () {
-            for (var obj of this.calibrationNodes) {
+            this.calibrationNodes.forEach(function (obj) {
                 var node = storage.document.createElement("calibrationresult");
                 node.setAttribute("frequency", obj.f);
                 node.setAttribute("range-min", obj.input.min);
                 node.setAttribute("range-max", obj.input.max);
                 node.setAttribute("gain-lin", obj.gain.gain.value);
                 this.storeDOM.appendChild(node);
-            }
-        }
-    }
+            }, this);
+        };
+    };
 
 
     // Global Checkers
     // These functions will help enforce the checkers
     this.checkHiddenAnchor = function () {
-        for (var ao of audioEngineContext.audioObjects) {
-            if (ao.specification.type == "anchor") {
-                if (ao.interfaceDOM.getValue() > (ao.specification.marker / 100) && ao.specification.marker > 0) {
-                    // Anchor is not set below
-                    console.log('Anchor node not below marker value');
-                    interfaceContext.lightbox.post("Message", 'Please keep listening');
-                    this.storeErrorNode('Anchor node not below marker value');
-                    return false;
-                }
-            }
+        var anchors = audioEngineContext.audioObjects.filter(function (ao) {
+            return ao.specification.type === "anchor";
+        });
+        var state = anchors.some(function (ao) {
+            return (ao.interfaceDOM.getValue() > (ao.specification.marker / 100) && ao.specification.marker > 0);
+        });
+        if (state) {
+            console.log('Anchor node not below marker value');
+            interfaceContext.lightbox.post("Message", 'Please keep listening');
+            this.storeErrorNode('Anchor node not below marker value');
+            return false;
         }
         return true;
     };
 
     this.checkHiddenReference = function () {
-        for (var ao of audioEngineContext.audioObjects) {
-            if (ao.specification.type == "reference") {
-                if (ao.interfaceDOM.getValue() < (ao.specification.marker / 100) && ao.specification.marker > 0) {
-                    // Anchor is not set below
-                    console.log('Reference node not above marker value');
-                    this.storeErrorNode('Reference node not above marker value');
-                    interfaceContext.lightbox.post("Message", 'Please keep listening');
-                    return false;
-                }
-            }
+        var references = audioEngineContext.audioObjects.filter(function (ao) {
+            return ao.specification.type === "reference";
+        });
+        var state = references.some(function (ao) {
+            return (ao.interfaceDOM.getValue() < (ao.specification.marker / 100) && ao.specification.marker > 0);
+        });
+        if (state) {
+            console.log('Reference node not below marker value');
+            interfaceContext.lightbox.post("Message", 'Please keep listening');
+            this.storeErrorNode('Reference node not below marker value');
+            return false;
         }
         return true;
     };
@@ -3133,8 +3141,9 @@
             return true;
         }
         var check_pass = true;
-        var error_obj = [];
-        for (var i = 0; i < audioEngineContext.audioObjects.length; i++) {
+        var error_obj = [],
+            i;
+        for (i = 0; i < audioEngineContext.audioObjects.length; i++) {
             var object = audioEngineContext.audioObjects[i];
             var time = object.buffer.buffer.duration;
             var metric = object.metric;
@@ -3149,15 +3158,15 @@
                     break;
                 }
             }
-            if (passed == false) {
+            if (passed === false) {
                 check_pass = false;
                 console.log("Continue listening to track-" + object.interfaceDOM.getPresentedId());
                 error_obj.push(object.interfaceDOM.getPresentedId());
             }
         }
-        if (check_pass == false) {
+        if (check_pass === false) {
             var str_start = "You have not completely listened to fragments ";
-            for (var i = 0; i < error_obj.length; i++) {
+            for (i = 0; i < error_obj.length; i++) {
                 str_start += error_obj[i];
                 if (i != error_obj.length - 1) {
                     str_start += ', ';
@@ -3174,12 +3183,12 @@
     this.checkAllMoved = function () {
         var str = "You have not moved ";
         var failed = [];
-        for (var ao of audioEngineContext.audioObjects) {
-            if (ao.metric.wasMoved == false && ao.interfaceDOM.canMove() == true) {
+        audioEngineContext.audioObjects.forEach(function (ao) {
+            if (ao.metric.wasMoved === false && ao.interfaceDOM.canMove() === true) {
                 failed.push(ao.interfaceDOM.getPresentedId());
             }
-        }
-        if (failed.length == 0) {
+        }, this);
+        if (failed.length === 0) {
             return true;
         } else if (failed.length == 1) {
             str += 'track ' + failed[0];
@@ -3199,12 +3208,12 @@
     this.checkAllPlayed = function () {
         var str = "You have not played ";
         var failed = [];
-        for (var ao of audioEngineContext.audioObjects) {
-            if (ao.metric.wasListenedTo == false) {
+        audioEngineContext.audioObjects.forEach(function (ao) {
+            if (ao.metric.wasListenedTo === false) {
                 failed.push(ao.interfaceDOM.getPresentedId());
             }
-        }
-        if (failed.length == 0) {
+        }, this);
+        if (failed.length === 0) {
             return true;
         } else if (failed.length == 1) {
             str += 'track ' + failed[0];
@@ -3236,38 +3245,49 @@
             }
         }
         return true;
-    }
-    this.checkScaleRange = function (min, max) {
+    };
+    this.checkScaleRange = function () {
         var page = testState.getCurrentTestPage();
-        var audioObjects = audioEngineContext.audioObjects;
+        var interfaceObject = page.interfaces;
         var state = true;
         var str = "Please keep listening. ";
-        var minRanking = Infinity;
-        var maxRanking = -Infinity;
-        for (var ao of audioObjects) {
-            var rank = ao.interfaceDOM.getValue();
-            if (rank < minRanking) {
-                minRanking = rank;
-            }
-            if (rank > maxRanking) {
-                maxRanking = rank;
-            }
+        if (interfaceObject === undefined) {
+            return true;
         }
-        if (minRanking * 100 > min) {
-            str += "At least one fragment must be below the " + min + " mark.";
+        interfaceObject = interfaceObject[0];
+        var scales = (function () {
+            var scaleRange = interfaceObject.options.find(function (a) {
+                return a.name == "scalerange";
+            });
+            return {
+                min: scaleRange.min,
+                max: scaleRange.max
+            };
+        })();
+        var range = audioEngineContext.audioObjects.reduce(function (a, b) {
+            var v = b.interfaceDOM.getValue();
+            return {
+                min: Math.min(a.min, v),
+                max: Math.max(a.max, v)
+            };
+        }, {
+            min: 100,
+            max: 0
+        });
+        if (range.min > scales.min) {
+            str += "At least one fragment must be below the " + range.min + " mark.";
+            state = false;
+        } else if (range.max < scales.max) {
+            str += "At least one fragment must be above the " + range.max + " mark.";
             state = false;
         }
-        if (maxRanking * 100 < max) {
-            str += "At least one fragment must be above the " + max + " mark."
-            state = false;
-        }
-        if (!state) {
+        if (state === false) {
             console.log(str);
             this.storeErrorNode(str);
             interfaceContext.lightbox.post("Error", str);
         }
         return state;
-    }
+    };
 
     this.storeErrorNode = function (errorMessage) {
         var time = audioEngineContext.timer.getTestTime();
@@ -3292,13 +3312,12 @@
                 case "capital":
                     return String.fromCharCode((index + offset) % 26 + 65);
                 case "samediff":
-                    if (index == 0) {
+                    if (index === 0) {
                         return "Same";
                     } else if (index == 1) {
                         return "Difference";
-                    } else {
-                        return "";
                     }
+                    return "";
                 case "number":
                     return String(index + offset);
                 default:
@@ -3306,7 +3325,7 @@
             }
         }
 
-        if (typeof labelStart !== "string" || labelStart.length == 0) {
+        if (typeof labelStart !== "string" || labelStart.length === 0) {
             labelStart = String.fromCharCode(0);
         }
 
@@ -3331,7 +3350,6 @@
                     labelStart = 1;
                 }
                 break;
-            case "none":
             default:
                 labelStart = 0;
         }
@@ -3348,7 +3366,7 @@
         } else {
             throw ("Invalid arguments");
         }
-    }
+    };
 
     this.getCombinedInterfaces = function (page) {
         // Combine the interfaces with the global interface nodes
@@ -3373,7 +3391,7 @@
             }
         });
         return local;
-    }
+    };
 }
 
 function Storage() {
@@ -3386,14 +3404,14 @@
     this.state = 0;
 
     this.initialise = function (existingStore) {
-        if (existingStore == undefined) {
+        if (existingStore === undefined) {
             // We need to get the sessionKey
             this.SessionKey.requestKey();
             this.document = document.implementation.createDocument(null, "waetresult", null);
             this.root = this.document.childNodes[0];
             var projectDocument = specification.projectXML;
-            projectDocument.setAttribute('file-name', url);
-            projectDocument.setAttribute('url', qualifyURL(url));
+            projectDocument.setAttribute('file-name', specification.url);
+            projectDocument.setAttribute('url', qualifyURL(specification.url));
             this.root.appendChild(projectDocument);
             this.root.appendChild(interfaceContext.returnDateNode());
             this.root.appendChild(interfaceContext.returnNavigator());
@@ -3402,10 +3420,10 @@
             this.root = existingStore.firstChild;
             this.SessionKey.key = this.root.getAttribute("key");
         }
-        if (specification.preTest != undefined) {
+        if (specification.preTest !== undefined) {
             this.globalPreTest = new this.surveyNode(this, this.root, specification.preTest);
         }
-        if (specification.postTest != undefined) {
+        if (specification.postTest !== undefined) {
             this.globalPostTest = new this.surveyNode(this, this.root, specification.postTest);
         }
     };
@@ -3417,7 +3435,7 @@
         handleEvent: function () {
             var parse = new DOMParser();
             var xml = parse.parseFromString(this.request.response, "text/xml");
-            if (this.request.response.length == 0) {
+            if (this.request.response.length === 0) {
                 console.error("An unspecified error occured, no server key could be generated");
                 return;
             }
@@ -3450,7 +3468,7 @@
             this.request.send();
         },
         update: function () {
-            if (this.key == null) {
+            if (this.key === null) {
                 console.log("Cannot save as key == null");
                 return;
             }
@@ -3485,10 +3503,10 @@
                         console.log("Intermediate save: Error! " + message.textContent);
                     }
                 }
-            }
+            };
             xmlhttp.send([hold.innerHTML]);
         }
-    }
+    };
 
     this.createTestPageStore = function (specification) {
         var store = new this.pageNode(this, specification);
@@ -3503,24 +3521,45 @@
         this.XMLDOM = this.parent.document.createElement('survey');
         this.XMLDOM.setAttribute('location', this.specification.location);
         this.XMLDOM.setAttribute("state", this.state);
-        for (var optNode of this.specification.options) {
+        this.specification.options.forEach(function (optNode) {
             if (optNode.type != 'statement') {
                 var node = this.parent.document.createElement('surveyresult');
                 node.setAttribute("ref", optNode.id);
                 node.setAttribute('type', optNode.type);
                 this.XMLDOM.appendChild(node);
             }
-        }
+        }, this);
         root.appendChild(this.XMLDOM);
 
         this.postResult = function (node) {
+            function postNumber(doc, value) {
+                var child = doc.createElement("response");
+                child.textContent = value;
+                return child;
+            }
+
+            function postRadio(doc, node) {
+                var child = doc.createElement('response');
+                if (node.response !== null) {
+                    child.setAttribute('name', node.response.name);
+                    child.textContent = node.response.text;
+                }
+                return child;
+            }
+
+            function postCheckbox(doc, node) {
+                var checkNode = doc.createElement('response');
+                checkNode.setAttribute('name', node.name);
+                checkNode.setAttribute('checked', node.checked);
+                return checkNode;
+            }
             // From popup: node is the popupOption node containing both spec. and results
             // ID is the position
             if (node.specification.type == 'statement') {
                 return;
             }
             var surveyresult = this.XMLDOM.firstChild;
-            while (surveyresult != null) {
+            while (surveyresult !== null) {
                 if (surveyresult.getAttribute("ref") == node.specification.id) {
                     break;
                 }
@@ -3530,28 +3569,18 @@
                 case "number":
                 case "question":
                 case "slider":
-                    var child = this.parent.document.createElement('response');
-                    child.textContent = node.response;
-                    surveyresult.appendChild(child);
+                    surveyresult.appendChild(postNumber(this.parent.document, node.response));
                     break;
                 case "radio":
-                    var child = this.parent.document.createElement('response');
-                    if (node.response !== null) {
-                        child.setAttribute('name', node.response.name);
-                        child.textContent = node.response.text;
-                    }
-                    surveyresult.appendChild(child);
+                    surveyresult.appendChild(postRadio(this.parent.document, node));
                     break;
                 case "checkbox":
-                    if (node.response == undefined) {
+                    if (node.response === undefined) {
                         surveyresult.appendChild(this.parent.document.createElement('response'));
                         break;
                     }
                     for (var i = 0; i < node.response.length; i++) {
-                        var checkNode = this.parent.document.createElement('response');
-                        checkNode.setAttribute('name', node.response[i].name);
-                        checkNode.setAttribute('checked', node.response[i].checked);
-                        surveyresult.appendChild(checkNode);
+                        surveyresult.appendChild(postCheckbox(this.parent.document, node.response[i]));
                     }
                     break;
             }
@@ -3559,7 +3588,7 @@
         this.complete = function () {
             this.state = "complete";
             this.XMLDOM.setAttribute("state", this.state);
-        }
+        };
     };
 
     this.pageNode = function (parent, specification) {
@@ -3571,10 +3600,10 @@
         this.XMLDOM.setAttribute('ref', specification.id);
         this.XMLDOM.setAttribute('presentedId', specification.presentedId);
         this.XMLDOM.setAttribute("state", this.state);
-        if (specification.preTest != undefined) {
+        if (specification.preTest !== undefined) {
             this.preTest = new this.parent.surveyNode(this.parent, this.XMLDOM, this.specification.preTest);
         }
-        if (specification.postTest != undefined) {
+        if (specification.postTest !== undefined) {
             this.postTest = new this.parent.surveyNode(this.parent, this.XMLDOM, this.specification.postTest);
         }
 
@@ -3583,12 +3612,12 @@
         this.XMLDOM.appendChild(page_metric);
 
         // Add the audioelement
-        for (var element of this.specification.audioElements) {
+        this.specification.audioElements.forEach(function (element) {
             var aeNode = this.parent.document.createElement('audioelement');
             aeNode.setAttribute('ref', element.id);
-            if (element.name != undefined) {
-                aeNode.setAttribute('name', element.name)
-            };
+            if (element.name !== undefined) {
+                aeNode.setAttribute('name', element.name);
+            }
             aeNode.setAttribute('type', element.type);
             aeNode.setAttribute('url', element.url);
             aeNode.setAttribute('fqurl', qualifyURL(element.url));
@@ -3601,20 +3630,20 @@
             var ae_metric = this.parent.document.createElement('metric');
             aeNode.appendChild(ae_metric);
             this.XMLDOM.appendChild(aeNode);
-        }
+        }, this);
 
         this.parent.root.appendChild(this.XMLDOM);
 
         this.complete = function () {
             this.state = "complete";
             this.XMLDOM.setAttribute("state", "complete");
-        }
+        };
     };
     this.update = function () {
         this.SessionKey.update();
-    }
+    };
     this.finish = function () {
-        if (this.state == 0) {
+        if (this.state === 0) {
             this.update();
         }
         this.state = 1;
--- a/js/loader.js	Fri Apr 14 16:25:26 2017 +0100
+++ b/js/loader.js	Fri Apr 14 16:28:19 2017 +0100
@@ -1,8 +1,8 @@
 // Script to load the relevant JS files if the system supports it
-
+/*globals window, document */
 window.onload = function () {
     // First check if the Web Audio API is supported
-    if (window.AudioContext == undefined && window.webkitAudioContext == undefined) {
+    if (window.AudioContext === undefined && window.webkitAudioContext === undefined) {
         // Display unsuported error message
         var body = document.getElementsByTagName("body")[0];
         body.innerHTML = "<h1>Sorry! Your browser is not supported :(</h1><p>Your browser does not support the HTML5 Web Audio API. Please use one of the following supported browsers instead.<p>";
@@ -26,4 +26,4 @@
             head.appendChild(script);
         }
     }
-}
+};
--- a/js/loudness.js	Fri Apr 14 16:25:26 2017 +0100
+++ b/js/loudness.js	Fri Apr 14 16:28:19 2017 +0100
@@ -5,7 +5,7 @@
  * 	return gain values to correct for a target loudness or match loudness between
  *  multiple objects
  */
-
+/* globals webkitOfflineAudioContext, navigator, audioContext, Float32Array */
 var interval_cal_loudness_event = null;
 
 if (typeof OfflineAudioContext == "undefined") {
@@ -21,16 +21,16 @@
     if (navigator.platform == 'iPad' || navigator.platform == 'iPhone') {
         buffer.ready();
     }
-    if (buffer == undefined) {
+    if (buffer === undefined) {
         return 0;
     }
-    if (timescale == undefined) {
+    if (timescale === undefined) {
         timescale = "I";
     }
-    if (target == undefined) {
+    if (target === undefined) {
         target = -23;
     }
-    if (offlineContext == undefined) {
+    if (offlineContext === undefined) {
         offlineContext = new OfflineAudioContext(audioContext.destination.channelCount, buffer.buffer.duration * audioContext.sampleRate, audioContext.sampleRate);
     }
     // Create the required filters
@@ -77,12 +77,12 @@
 }
 
 function calculateMeanSquared(buffer, frame_dur, frame_overlap) {
-    frame_size = Math.floor(buffer.sampleRate * frame_dur);
-    step_size = Math.floor(frame_size * (1.0 - frame_overlap));
-    num_frames = Math.floor((buffer.length - frame_size) / step_size);
-    num_frames = Math.max(num_frames, 1);
+    var frame_size = Math.floor(buffer.sampleRate * frame_dur);
+    var step_size = Math.floor(frame_size * (1.0 - frame_overlap));
+    var num_frames = Math.floor((buffer.length - frame_size) / step_size);
+    num_frames = Math.max(num_frames, 0);
 
-    MS = Array(buffer.numberOfChannels);
+    var MS = Array(buffer.numberOfChannels);
     for (var c = 0; c < buffer.numberOfChannels; c++) {
         MS[c] = new Float32Array(num_frames);
         var data = buffer.getChannelData(c);
@@ -124,13 +124,14 @@
     var num_frames = source[0].length;
     var num_channels = source.length;
     var LK = Array(num_channels);
-    for (var c = 0; c < num_channels; c++) {
+    var n, c;
+    for (c = 0; c < num_channels; c++) {
         LK[c] = [];
     }
 
-    for (var n = 0; n < num_frames; n++) {
+    for (n = 0; n < num_frames; n++) {
         if (blocks[n] > threshold) {
-            for (var c = 0; c < num_channels; c++) {
+            for (c = 0; c < num_channels; c++) {
                 LK[c].push(source[c][n]);
             }
         }
--- a/js/specification.js	Fri Apr 14 16:25:26 2017 +0100
+++ b/js/specification.js	Fri Apr 14 16:28:19 2017 +0100
@@ -1,38 +1,40 @@
+/* globals document, console, DOMParser */
 function Specification() {
+    var schemaRoot;
+    var schemaString;
     // Handles the decoding of the project specification XML into a simple JavaScript Object.
 
     // <setup> attributes
-    this.interface = null;
-    this.projectReturn = null;
-    this.returnURL = null;
-    this.randomiseOrder = null;
-    this.poolSize = null;
-    this.loudness = null;
-    this.sampleRate = null;
-    this.calibration = null;
-    this.crossFade = null;
-    this.preSilence = null;
-    this.postSilence = null;
-    this.playOne = null;
+    this.interface = undefined;
+    this.projectReturn = undefined;
+    this.returnURL = undefined;
+    this.randomiseOrder = undefined;
+    this.poolSize = undefined;
+    this.loudness = undefined;
+    this.sampleRate = undefined;
+    this.calibration = undefined;
+    this.crossFade = undefined;
+    this.preSilence = undefined;
+    this.postSilence = undefined;
+    this.playOne = undefined;
 
     // nodes
-    this.metrics = null;
+    this.metrics = undefined;
     this.preTest = undefined;
     this.postTest = undefined;
     this.pages = [];
-    this.interfaces = null;
+    this.interfaces = undefined;
     this.errors = [];
-    this.schema = null;
     this.exitText = "Thank you.";
 
-    this.processAttribute = function (attribute, schema, schemaRoot) {
+    var processAttribute = function (attribute, schema) {
         // attribute is the string returned from getAttribute on the XML
         // schema is the <xs:attribute> node
-        if (schema.getAttribute('name') == undefined && schema.getAttribute('ref') != undefined) {
+        if (schema.getAttribute('name') === null && schema.getAttribute('ref') !== undefined) {
             schema = schemaRoot.getAllElementsByName(schema.getAttribute('ref'))[0];
         }
         var defaultOpt = schema.getAttribute('default');
-        if (attribute == null) {
+        if (attribute === null) {
             attribute = defaultOpt;
         }
         var dataType = schema.getAttribute('type');
@@ -51,7 +53,7 @@
                 dataType = "string";
             }
         }
-        if (attribute == null) {
+        if (attribute === null) {
             return attribute;
         }
         switch (dataType) {
@@ -71,7 +73,6 @@
             case "short":
                 attribute = Number(attribute);
                 break;
-            case "string":
             default:
                 attribute = String(attribute);
                 break;
@@ -79,26 +80,44 @@
         return attribute;
     };
 
+    this.processSchema = function (schemaXSD) {
+        if (schemaRoot === undefined) {
+            schemaString = schemaXSD;
+            var parse = new DOMParser();
+            schemaRoot = parse.parseFromString(schemaString, 'text/xml');
+            Object.defineProperties(this, {
+                'schema': {
+                    'value': schemaRoot
+                },
+                'schemaString': {
+                    'value': schemaString
+                }
+            });
+        }
+    };
+    this.getSchema = function () {
+        return schemaRoot;
+    };
+    this.getSchemaString = function () {
+        return schemaString;
+    };
+
     this.decode = function (projectXML) {
+        schemaRoot = this.schema;
         this.errors = [];
         // projectXML - DOM Parsed document
         this.projectXML = projectXML.childNodes[0];
         var setupNode = projectXML.getElementsByTagName('setup')[0];
-        var schemaSetup = this.schema.getAllElementsByName('setup')[0];
+        var schemaSetup = schemaRoot.getAllElementsByName('setup')[0];
         // First decode the attributes
         var attributes = schemaSetup.getAllElementsByTagName('xs:attribute');
-        for (var i = 0; i < attributes.length; i++) {
+        var i;
+        for (i = 0; i < attributes.length; i++) {
             var attributeName = attributes[i].getAttribute('name') || attributes[i].getAttribute('ref');
             var projectAttr = setupNode.getAttribute(attributeName);
-            projectAttr = this.processAttribute(projectAttr, attributes[i], this.schema);
-            switch (typeof projectAttr) {
-                case "number":
-                case "boolean":
-                    eval('this.' + attributeName + ' = ' + projectAttr);
-                    break;
-                case "string":
-                    eval('this.' + attributeName + ' = "' + projectAttr + '"');
-                    break;
+            projectAttr = processAttribute(projectAttr, attributes[i]);
+            if (projectAttr !== null) {
+                this[attributeName] = projectAttr;
             }
 
         }
@@ -114,7 +133,7 @@
 
         // Now process the survey node options
         var survey = setupNode.getElementsByTagName('survey');
-        for (var i = 0; i < survey.length; i++) {
+        for (i = 0; i < survey.length; i++) {
             var location = survey[i].getAttribute('location');
             switch (location) {
                 case 'pre':
@@ -135,7 +154,7 @@
             this.errors.push("Only one <interface> node in the <setup> node allowed! Others except first ingnored!");
         }
         this.interfaces = new this.interfaceNode(this);
-        if (interfaceNode.length != 0) {
+        if (interfaceNode.length !== 0) {
             interfaceNode = interfaceNode[0];
             this.interfaces.decode(this, interfaceNode, this.schema.getAllElementsByName('interface')[1]);
         }
@@ -143,7 +162,7 @@
         // Page tags
         var pageTags = projectXML.getElementsByTagName('page');
         var pageSchema = this.schema.getAllElementsByName('page')[0];
-        for (var i = 0; i < pageTags.length; i++) {
+        for (i = 0; i < pageTags.length; i++) {
             var node = new this.page(this);
             node.decode(this, pageTags[i], pageSchema);
             this.pages.push(node);
@@ -157,21 +176,21 @@
         root.setAttribute("xsi:noNamespaceSchemaLocation", "test-schema.xsd");
         // Build setup node
         var setup = RootDocument.createElement("setup");
-        var schemaSetup = this.schema.getAllElementsByName('setup')[0];
+        var schemaSetup = schemaRoot.getAllElementsByName('setup')[0];
         // First decode the attributes
         var attributes = schemaSetup.getAllElementsByTagName('xs:attribute');
         for (var i = 0; i < attributes.length; i++) {
             var name = attributes[i].getAttribute("name");
-            if (name == undefined) {
+            if (name === undefined) {
                 name = attributes[i].getAttribute("ref");
             }
-            if (eval("this." + name + " != undefined") || attributes[i].getAttribute("use") == "required") {
-                eval("setup.setAttribute('" + name + "',this." + name + ")");
+            if (this[name] !== undefined || attributes[i].getAttribute("use") == "required") {
+                setup.setAttribute(name, this[name]);
             }
         }
         root.appendChild(setup);
         // Survey node
-        if (this.exitText != null) {
+        if (this.exitText !== null) {
             var exitTextNode = RootDocument.createElement('exitText');
             exitTextNode.textContent = this.exitText;
             setup.appendChild(exitTextNode);
@@ -180,17 +199,17 @@
         setup.appendChild(this.postTest.encode(RootDocument));
         setup.appendChild(this.metrics.encode(RootDocument));
         setup.appendChild(this.interfaces.encode(RootDocument));
-        for (var page of this.pages) {
+        this.pages.forEach(function (page) {
             root.appendChild(page.encode(RootDocument));
-        }
+        });
         return RootDocument;
     };
 
     this.surveyNode = function (specification) {
-        this.location = null;
+        this.location = undefined;
         this.options = [];
-        this.parent = null;
-        this.schema = specification.schema.getAllElementsByName('survey')[0];
+        this.parent = undefined;
+        this.schema = schemaRoot.getAllElementsByName('survey')[0];
         this.specification = specification;
 
         this.OptionNode = function (specification) {
@@ -208,23 +227,18 @@
             this.conditions = [];
 
             this.decode = function (parent, child) {
-                this.schema = specification.schema.getAllElementsByName(child.nodeName)[0];
+                this.schema = schemaRoot.getAllElementsByName(child.nodeName)[0];
                 var attributeMap = this.schema.getAllElementsByTagName('xs:attribute');
-                for (var i in attributeMap) {
-                    if (isNaN(Number(i)) == true) {
+                var i;
+                for (i in attributeMap) {
+                    if (isNaN(Number(i)) === true) {
                         break;
                     }
                     var attributeName = attributeMap[i].getAttribute('name') || attributeMap[i].getAttribute('ref');
                     var projectAttr = child.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;
+                    projectAttr = processAttribute(projectAttr, attributeMap[i]);
+                    if (projectAttr !== null) {
+                        this[attributeName] = projectAttr;
                     }
                 }
                 if (child.nodeName == 'surveyentry') {
@@ -239,13 +253,13 @@
                 this.statement = child.getElementsByTagName('statement')[0].textContent;
                 if (this.type == "checkbox" || this.type == "radio") {
                     var children = child.getElementsByTagName('option');
-                    if (children.length == null) {
+                    if (children.length === null) {
                         console.log('Malformed' + child.nodeName + 'entry');
                         this.statement = 'Malformed' + child.nodeName + 'entry';
                         this.type = 'statement';
                     } else {
                         this.options = [];
-                        for (var i = 0; i < children.length; i++) {
+                        for (i = 0; i < children.length; i++) {
                             this.options.push({
                                 name: children[i].getAttribute('name'),
                                 text: children[i].textContent
@@ -265,14 +279,14 @@
                     }
                 }
                 var conditionElements = child.getElementsByTagName("conditional");
-                for (var i = 0; i < conditionElements.length; i++) {
+                for (i = 0; i < conditionElements.length; i++) {
                     var condition = conditionElements[i];
                     var obj = {
                         check: condition.getAttribute("check"),
                         value: condition.getAttribute("value"),
                         jumpToOnPass: condition.getAttribute("jumpToOnPass"),
                         jumpToOnFail: condition.getAttribute("jumpToOnFail")
-                    }
+                    };
                     this.conditions.push(obj);
                 }
             };
@@ -283,28 +297,28 @@
                 statement.textContent = this.statement;
                 node.appendChild(statement);
                 node.id = this.id;
-                if (this.name != undefined) {
+                if (this.name !== undefined) {
                     node.setAttribute("name", this.name);
                 }
-                if (this.mandatory != undefined) {
+                if (this.mandatory !== undefined) {
                     node.setAttribute("mandatory", this.mandatory);
                 }
                 node.id = this.id;
-                if (this.name != undefined) {
+                if (this.name !== undefined) {
                     node.setAttribute("name", this.name);
                 }
                 switch (this.type) {
                     case "checkbox":
-                        if (this.min != undefined) {
+                        if (this.min !== undefined) {
                             node.setAttribute("min", this.min);
                         } else {
                             node.setAttribute("min", "0");
                         }
-                        if (this.max != undefined) {
+                        if (this.max !== undefined) {
                             node.setAttribute("max", this.max);
                         } else {
                             node.setAttribute("max", "undefined");
-                        }
+                        } /* falls through */
                     case "radio":
                         for (var i = 0; i < this.options.length; i++) {
                             var option = this.options[i];
@@ -315,27 +329,27 @@
                         }
                         break;
                     case "number":
-                        if (this.min != undefined) {
+                        if (this.min !== undefined) {
                             node.setAttribute("min", this.min);
                         }
-                        if (this.max != undefined) {
+                        if (this.max !== undefined) {
                             node.setAttribute("max", this.max);
                         }
                         break;
                     case "question":
-                        if (this.boxsize != undefined) {
+                        if (this.boxsize !== undefined) {
                             node.setAttribute("boxsize", this.boxsize);
                         }
-                        if (this.mandatory != undefined) {
+                        if (this.mandatory !== undefined) {
                             node.setAttribute("mandatory", this.mandatory);
                         }
                         break;
                     case "video":
-                        if (this.mandatory != undefined) {
+                        if (this.mandatory !== undefined) {
                             node.setAttribute("mandatory", this.mandatory);
-                        }
+                        } /* falls through */
                     case "youtube":
-                        if (this.url != undefined) {
+                        if (this.url !== undefined) {
                             node.setAttribute("url", this.url);
                         }
                         break;
@@ -352,17 +366,18 @@
                             maxText.textContent = this.rightText;
                             node.appendChild(maxText);
                         }
+                        break;
                     default:
                         break;
                 }
-                for (var condition of this.conditions) {
+                this.conditions.forEach(function (condition) {
                     var conditionDOM = doc.createElement("conditional");
                     conditionDOM.setAttribute("check", condition.check);
                     conditionDOM.setAttribute("value", condition.value);
                     conditionDOM.setAttribute("jumpToOnPass", condition.jumpToOnPass);
                     conditionDOM.setAttribute("jumpToOnFail", condition.jumpToOnFail);
                     node.appendChild(conditionDOM);
-                }
+                });
                 return node;
             };
         };
@@ -374,14 +389,14 @@
             } else if (this.location == 'after') {
                 this.location = 'post';
             }
-            var child = xml.firstElementChild
+            var child = xml.firstElementChild;
             while (child) {
                 var node = new this.OptionNode(this.specification);
                 node.decode(parent, child);
                 this.options.push(node);
                 child = child.nextElementSibling;
             }
-            if (this.options.length == 0) {
+            if (this.options.length === 0) {
                 console.log("Empty survey node");
                 console.log(this);
             }
@@ -397,11 +412,11 @@
     };
 
     this.interfaceNode = function (specification) {
-        this.title = null;
-        this.name = null;
+        this.title = undefined;
+        this.name = undefined;
         this.options = [];
         this.scales = [];
-        this.schema = specification.schema.getAllElementsByName('interface')[1];
+        this.schema = schemaRoot.getAllElementsByName('interface')[1];
 
         this.decode = function (parent, xml) {
             this.name = xml.getAttribute('name');
@@ -413,25 +428,16 @@
             // Extract interfaceoption node schema
             var interfaceOptionNodeSchema = this.schema.getAllElementsByName('interfaceoption')[0];
             var attributeMap = interfaceOptionNodeSchema.getAllElementsByTagName('xs:attribute');
-            for (var i = 0; i < interfaceOptionNodes.length; i++) {
+            var i, j;
+            for (i = 0; i < interfaceOptionNodes.length; i++) {
                 var ioNode = interfaceOptionNodes[i];
                 var option = {};
-                for (var j = 0; j < attributeMap.length; j++) {
+                for (j = 0; j < attributeMap.length; j++) {
                     var attributeName = attributeMap[j].getAttribute('name') || attributeMap[j].getAttribute('ref');
                     var projectAttr = ioNode.getAttribute(attributeName);
-                    if (parent.processAttribute) {
-                        parent.processAttribute(projectAttr, attributeMap[j], parent.schema)
-                    } else {
-                        parent.parent.processAttribute(projectAttr, attributeMap[j], parent.parent.schema)
-                    }
-                    switch (typeof projectAttr) {
-                        case "number":
-                        case "boolean":
-                            eval('option.' + attributeName + ' = ' + projectAttr);
-                            break;
-                        case "string":
-                            eval('option.' + attributeName + ' = "' + projectAttr + '"');
-                            break;
+                    projectAttr = processAttribute(projectAttr, attributeMap[j]);
+                    if (projectAttr !== null) {
+                        option[attributeName] = projectAttr;
                     }
                 }
                 this.options.push(option);
@@ -442,7 +448,7 @@
             if (scaleParent.length == 1) {
                 scaleParent = scaleParent[0];
                 var scalelabels = scaleParent.getAllElementsByTagName('scalelabel');
-                for (var i = 0; i < scalelabels.length; i++) {
+                for (i = 0; i < scalelabels.length; i++) {
                     this.scales.push({
                         text: scalelabels[i].textContent,
                         position: Number(scalelabels[i].getAttribute('position'))
@@ -453,27 +459,27 @@
 
         this.encode = function (doc) {
             var node = doc.createElement("interface");
-            if (typeof name == "string" && name.length > 0)
+            if (typeof this.name == "string" && this.name.length > 0)
                 node.setAttribute("name", this.name);
             if (typeof this.title == "string") {
                 var titleNode = doc.createElement("title");
                 titleNode.textContent = this.title;
                 node.appendChild(titleNode);
             }
-            for (var option of this.options) {
+            this.options.forEach(function (option) {
                 var child = doc.createElement("interfaceoption");
                 child.setAttribute("type", option.type);
                 child.setAttribute("name", option.name);
                 node.appendChild(child);
-            }
-            if (this.scales.length != 0) {
+            });
+            if (this.scales.length !== 0) {
                 var scales = doc.createElement("scales");
-                for (var scale of this.scales) {
+                this.scales.forEach(function (scale) {
                     var child = doc.createElement("scalelabel");
                     child.setAttribute("position", scale.position);
                     child.textContent = scale.text;
                     scales.appendChild(child);
-                }
+                });
                 node.appendChild(scales);
             }
             return node;
@@ -485,16 +491,16 @@
         this.decode = function (parent, xml) {
             var children = xml.getElementsByTagName('metricenable');
             for (var i in children) {
-                if (isNaN(Number(i)) == true) {
+                if (isNaN(Number(i)) === true) {
                     break;
                 }
                 this.enabled.push(children[i].textContent);
             }
-        }
+        };
         this.encode = function (doc) {
             var node = doc.createElement('metric');
             for (var i in this.enabled) {
-                if (isNaN(Number(i)) == true) {
+                if (isNaN(Number(i)) === true) {
                     break;
                 }
                 var child = doc.createElement('metricenable');
@@ -502,8 +508,8 @@
                 node.appendChild(child);
             }
             return node;
-        }
-    }
+        };
+    };
 
     this.page = function (specification) {
         this.presentedId = undefined;
@@ -512,57 +518,52 @@
         this.hostURL = undefined;
         this.randomiseOrder = undefined;
         this.loop = undefined;
-        this.outsideReference = null;
-        this.loudness = null;
-        this.label = null;
-        this.labelStart = "";
-        this.preTest = null;
-        this.postTest = null;
+        this.outsideReference = undefined;
+        this.loudness = undefined;
+        this.label = undefined;
+        this.labelStart = undefined;
+        this.preTest = undefined;
+        this.postTest = undefined;
         this.interfaces = [];
-        this.playOne = null;
-        this.restrictMovement = null;
-        this.position = null;
+        this.playOne = undefined;
+        this.restrictMovement = undefined;
+        this.position = undefined;
         this.commentBoxPrefix = "Comment on track";
         this.audioElements = [];
         this.commentQuestions = [];
-        this.schema = specification.schema.getAllElementsByName("page")[0];
+        this.schema = schemaRoot.getAllElementsByName("page")[0];
         this.specification = specification;
-        this.parent = null;
+        this.parent = undefined;
 
         this.decode = function (parent, xml) {
             this.parent = parent;
             var attributeMap = this.schema.getAllElementsByTagName('xs:attribute');
-            for (var i = 0; i < attributeMap.length; i++) {
+            var i, node;
+            for (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;
+                projectAttr = processAttribute(projectAttr, attributeMap[i]);
+                if (projectAttr !== null) {
+                    this[attributeName] = projectAttr;
                 }
             }
 
             // Get the title
             var title = xml.getElementsByTagName('title');
-            if (title.length != 0 && title[0].parentElement == xml) {
+            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 && CBP[0].parentElement == xml) {
+            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);
+            for (i = 0; i < interfaceNode.length; i++) {
+                node = new parent.interfaceNode(this.specification);
                 node.decode(this, interfaceNode[i], parent.schema.getAllElementsByName('interface')[1]);
                 this.interfaces.push(node);
             }
@@ -570,17 +571,17 @@
             // 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++) {
+            for (i = 0; i < survey.length; i++) {
                 var location = survey[i].getAttribute('location');
                 if (location == 'pre' || location == 'before') {
-                    if (this.preTest != null) {
+                    if (this.preTest !== undefined) {
                         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) {
+                    if (this.postTest !== undefined) {
                         this.errors.push("Already a post/after test survey defined! Ignoring second!!");
                     } else {
                         this.postTest = new parent.surveyNode(this.specification);
@@ -591,19 +592,19 @@
 
             // 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);
+            for (i = 0; i < audioElements.length; i++) {
+                var audioNode = new this.audioElementNode(this.specification);
+                audioNode.decode(this, audioElements[i]);
+                this.audioElements.push(audioNode);
             }
 
             // Now decode the commentquestions
             var cqNode = xml.getElementsByTagName('commentquestions');
-            if (cqNode.length != 0) {
+            if (cqNode.length !== 0) {
                 cqNode = cqNode[0];
                 var commentQuestion = cqNode.firstElementChild;
                 while (commentQuestion) {
-                    var node = new this.commentQuestionNode(this.specification);
+                    node = new this.commentQuestionNode(this.specification);
                     node.decode(parent, commentQuestion);
                     this.commentQuestions.push(node);
                     commentQuestion = commentQuestion.nextElementSibling;
@@ -615,32 +616,30 @@
             var AHNode = root.createElement("page");
             // First decode the attributes
             var attributes = this.schema.getAllElementsByTagName('xs:attribute');
-            for (var i = 0; i < attributes.length; i++) {
+            var i;
+            for (i = 0; i < attributes.length; i++) {
                 var name = attributes[i].getAttribute("name");
-                if (name == undefined) {
+                if (name === null) {
                     name = attributes[i].getAttribute("ref");
                 }
-                if (eval("this." + name + " != undefined") || attributes[i].getAttribute("use") == "required") {
-                    eval("AHNode.setAttribute('" + name + "',this." + name + ")");
+                if (this[name] !== undefined || attributes[i].getAttribute("use") == "required") {
+                    AHNode.setAttribute(name, this[name]);
                 }
             }
-            if (this.loudness != null) {
-                AHNode.setAttribute("loudness", this.loudness);
-            }
             // <commentboxprefix>
             var commentboxprefix = root.createElement("commentboxprefix");
             commentboxprefix.textContent = this.commentBoxPrefix;
             AHNode.appendChild(commentboxprefix);
 
-            for (var i = 0; i < this.interfaces.length; i++) {
+            for (i = 0; i < this.interfaces.length; i++) {
                 AHNode.appendChild(this.interfaces[i].encode(root));
             }
 
-            for (var i = 0; i < this.audioElements.length; i++) {
+            for (i = 0; i < this.audioElements.length; i++) {
                 AHNode.appendChild(this.audioElements[i].encode(root));
             }
             // Create <CommentQuestion>
-            for (var i = 0; i < this.commentQuestions.length; i++) {
+            for (i = 0; i < this.commentQuestions.length; i++) {
                 AHNode.appendChild(this.commentQuestions[i].encode(root));
             }
 
@@ -650,14 +649,17 @@
         };
 
         this.commentQuestionNode = function (specification) {
-            this.id = null;
+            this.id = undefined;
             this.name = undefined;
             this.type = undefined;
             this.statement = undefined;
-            this.schema = specification.schema.getAllElementsByName('commentquestion')[0];
+            this.schema = schemaRoot.getAllElementsByName('commentquestion')[0];
             this.decode = function (parent, xml) {
                 this.id = xml.id;
                 this.name = xml.getAttribute('name');
+                if (this.name === null) {
+                    this.name = undefined;
+                }
                 switch (xml.nodeName) {
                     case "commentradio":
                         this.type = "radio";
@@ -674,9 +676,10 @@
                         this.step = undefined;
                         break;
                     case "commentquestion":
-                    default:
                         this.type = "question";
                         break;
+                    default:
+                        throw ("Unknown comment type " + xml.nodeName);
                 }
                 this.statement = xml.getElementsByTagName('statement')[0].textContent;
                 if (this.type == "radio" || this.type == "checkbox") {
@@ -693,12 +696,12 @@
                     this.min = Number(xml.getAttribute("min"));
                     this.max = Number(xml.getAttribute("max"));
                     this.step = Number(xml.getAttribute("step"));
-                    if (this.step == undefined) {
+                    if (this.step === undefined) {
                         this.step = 1;
                     }
                     this.value = Number(xml.getAttribute("value"));
-                    if (this.value == undefined) {
-                        this.value = min;
+                    if (this.value === undefined) {
+                        this.value = this.min;
                     }
                     this.leftText = xml.getElementsByTagName("minText");
                     if (this.leftText && this.leftText.length > 0) {
@@ -728,25 +731,26 @@
                         node = root.createElement("commentslider");
                         break;
                     case "question":
-                    default:
                         node = root.createElement("commentquestion");
                         break;
+                    default:
+                        throw ("Unknown type " + this.type);
                 }
                 node.id = this.id;
                 node.setAttribute("type", this.type);
-                if (this.name != undefined) {
+                if (this.name !== undefined) {
                     node.setAttribute("name", this.name);
                 }
                 var statement = root.createElement("statement");
                 statement.textContent = this.statement;
                 node.appendChild(statement);
                 if (this.type == "radio" || this.type == "checkbox") {
-                    for (var option of this.options) {
+                    this.options.forEach(function (option) {
                         var child = root.createElement("option");
                         child.setAttribute("name", option.name);
                         child.textContent = option.text;
                         node.appendChild(child);
-                    }
+                    });
                 }
                 if (this.type == "slider") {
                     node.setAttribute("min", this.min);
@@ -773,36 +777,30 @@
         };
 
         this.audioElementNode = function (specification) {
-            this.url = null;
-            this.id = null;
-            this.name = null;
-            this.parent = null;
-            this.type = null;
-            this.marker = null;
+            this.url = undefined;
+            this.id = undefined;
+            this.name = undefined;
+            this.parent = undefined;
+            this.type = undefined;
+            this.marker = undefined;
             this.enforce = false;
             this.gain = 0.0;
-            this.label = null;
+            this.label = undefined;
             this.startTime = undefined;
             this.stopTime = undefined;
             this.sampleRate = undefined;
             this.alternatives = [];
-            this.schema = specification.schema.getAllElementsByName('audioelement')[0];;
-            this.parent = null;
+            this.schema = schemaRoot.getAllElementsByName('audioelement')[0];
+            this.parent = undefined;
             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.parent.processAttribute(projectAttr, attributeMap[i], parent.parent.schema);
-                    switch (typeof projectAttr) {
-                        case "number":
-                        case "boolean":
-                            eval('this.' + attributeName + ' = ' + projectAttr);
-                            break;
-                        case "string":
-                            eval('this.' + attributeName + ' = "' + projectAttr + '"');
-                            break;
+                    projectAttr = processAttribute(projectAttr, attributeMap[i]);
+                    if (projectAttr !== null) {
+                        this[attributeName] = projectAttr;
                     }
                 }
                 // Get the alternative nodes
@@ -823,11 +821,11 @@
                 var attributes = this.schema.getAllElementsByTagName('xs:attribute');
                 for (var i = 0; i < attributes.length; i++) {
                     var name = attributes[i].getAttribute("name");
-                    if (name == undefined) {
+                    if (name === null) {
                         name = attributes[i].getAttribute("ref");
                     }
-                    if (eval("this." + name + " != undefined") || attributes[i].getAttribute("use") == "required") {
-                        eval("AENode.setAttribute('" + name + "',this." + name + ")");
+                    if (this[name] !== undefined || attributes[i].getAttribute("use") == "required") {
+                        AENode.setAttribute(name, this[name]);
                     }
                 }
                 this.alternatives.forEach(function (alt) {