changeset 538:6d652a6c80ed Dev_main

Merge into dev_main
author Nicholas Jillings <n.g.r.jillings@se14.qmul.ac.uk>
date Mon, 22 Feb 2016 12:33:56 +0000
parents 2a723261ee3e (diff) 16bf7d5fa618 (current diff)
children 4a69b3ba474d
files core.js
diffstat 15 files changed, 484 insertions(+), 23 deletions(-) [+]
line wrap: on
line diff
--- a/.hgignore	Sun Feb 21 11:18:08 2016 +0000
+++ b/.hgignore	Mon Feb 22 12:33:56 2016 +0000
@@ -49,3 +49,5 @@
 re:^docs/DMRN+10/img/\._TestCreate\.png$
 re:^docs/DMRN+10/img/\._APE\.png$
 re:^docs/DMRN+10/img/\._MUSHRA\.png$
+example_eval/samples/*
+	
--- a/analyse.html	Sun Feb 21 11:18:08 2016 +0000
+++ b/analyse.html	Mon Feb 22 12:33:56 2016 +0000
@@ -24,7 +24,7 @@
 			xmlFileFolder = "saves";
 			// array of XML files
 			// THIS IS WHERE YOU SPECIFY RESULT XML FILES TO ANALYSE
-			var xmlFiles = ['test-0.xml','test-1.xml','test-2.xml','test-3.xml']; 
+			var xmlFiles = ['test-2.xml']; 
 							
 
 			//TODO: make retrieval of file names automatic / drag files on here
@@ -284,6 +284,7 @@
 				  xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
 				  }
 				xmlhttp.open("GET",xmlFileName,false);
+				xmlhttp.overrideMimeType('text/xml');
 				xmlhttp.send();
 				return xmlhttp.responseXML; 
 			}
@@ -300,11 +301,11 @@
 						fileNameArray.push(xmlFiles[fileIndex]);
 						
 						// get root of XML file
-						root = xml.getElementsByTagName('browserevaluationresult')[0];
+						root = xml.getElementsByTagName('waetresult')[0];
 						
 						// get subject ID, add to array if not already there
-						pretest = root.getElementsByTagName('pretest')[0];
-						subjectID = pretest.getElementsByTagName('comment')[0];
+						pretestSurveyResult = root.getElementsByTagName('surveyresult')[0];
+						subjectID = pretestSurveyResult.getElementsByTagName('comment')[0];
 						if (subjectID){
 							if (subjectID.getAttribute('id')!='sessionId') { // warning in console when not available
 								console.log(xmlFiles[fileIndex]+': no SessionID available');
@@ -378,7 +379,7 @@
 					// which songs did they do
 					if (xml != null) { // if file exists
 						// get root of XML file
-						root = xml.getElementsByTagName('browserevaluationresult')[0];
+						root = xml.getElementsByTagName('waetresult')[0];
 						// go over all audioholders
 						// document.getElementById('div_survey_'+xmlFileName).innerHTML += '<strong>Audioholders: </strong>';
 						// audioholderNodes = root.getElementsByTagName('audioholder');
@@ -415,7 +416,7 @@
 
 			function makePlots() { //TODO: split into different functions
 				// TEMPORARY
-				makeTimeline(xmlFileFolder+"/"+xmlFiles[7]);
+				makeTimeline(xmlFileFolder+"/"+xmlFiles[0]);
 
 				// create value array
 				var ratings = [];  // 3D matrix of ratings (audioholder, audioelement, subject)
@@ -432,7 +433,7 @@
 					xml = readXML(xmlFileName); 
 					if (xml != null) { // if file exists
 						// get root of XML file
-						root = xml.getElementsByTagName('browserevaluationresult')[0];
+						root = xml.getElementsByTagName('waetresult')[0];
 						// go over all audioholders
 						audioholderNodes = root.getElementsByTagName('audioholder');
 						for (audioholderIndex = 0; audioholderIndex < audioholderNodes.length; audioholderIndex++) { 
@@ -594,7 +595,7 @@
 					return; // do nothing; exit function
 				}
 				// get root of XML file
-				root = xml.getElementsByTagName('browserevaluationresult')[0];
+				root = xml.getElementsByTagName('waetresult')[0];
 
 				audioholder_time = 0; 
 				previous_audioholder_time = 0; // time spent before current audioholder
--- a/core.js	Sun Feb 21 11:18:08 2016 +0000
+++ b/core.js	Mon Feb 22 12:33:56 2016 +0000
@@ -344,11 +344,13 @@
 			if (xmlhttp.status != 200 && xmlhttp.readyState == 4) {
 				createProjectSave(null);
 			} else {
-				if (xmlhttp.responseXML == null)
+				var parser = new DOMParser();
+				var xmlDoc = parser.parseFromString(xmlhttp.responseText, "application/xml");
+				if (xmlDoc == null)
 				{
 					createProjectSave('null');
 				}
-				var response = xmlhttp.responseXML.childNodes[0];
+				var response = xmlDoc.childNodes[0];
 				if (response.getAttribute('state') == "OK")
 				{
 					var file = response.getElementsByTagName('file')[0];
@@ -2254,7 +2256,7 @@
 			this.id = null;
 			this.parent = null;
 			this.type = null;
-			this.marker = false;
+			this.marker = null;
 			this.enforce = false;
 			this.gain = 1.0;
 			this.schema = specification.schema.getAllElementsByName('audioelement')[0];;
--- a/save.php	Sun Feb 21 11:18:08 2016 +0000
+++ b/save.php	Mon Feb 22 12:33:56 2016 +0000
@@ -1,6 +1,7 @@
 <?php
 	header('Access-Control-Allow-Origin: *');
 	header("Content-type: text/xml");
+	error_reporting(0);
 	$postText = file_get_contents('php://input');
 	$sha1_hash = sha1($postText);
 	$datetime = date('ymdHis');
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/comment_parser.html	Mon Feb 22 12:33:56 2016 +0000
@@ -0,0 +1,72 @@
+<html lang="en">
+    <head>
+        <meta charset="utf-8" />
+        <script type="text/javascript">
+            function getXML()
+            {
+                var XMLHttp = new XMLHttpRequest();
+                XMLHttp.open("GET","comment_parser.php?format=XML",true);
+                XMLHttp.onload = function() {
+                    // Now we have the XML data, extract
+                    var parse = new DOMParser();
+                    var ajax = parse.parseFromString(XMLHttp.response,'text/xml');
+                    
+                    var parent = document.createElement("div");
+                    parent.appendChild(ajax.children[0]);
+                    var file = [parent.innerHTML];
+                    var bb = new Blob(file,{type : 'application/xml'});
+                    generateLink(bb,".xml");
+                }
+                XMLHttp.send();
+            }
+            
+            function getJSON()
+            {
+                var XMLHttp = new XMLHttpRequest();
+                XMLHttp.open("GET","comment_parser.php?format=JSON",true);
+                XMLHttp.onload = function() {
+                    // Now we have the XML data, extract
+                    var file = [XMLHttp.response];
+                    var bb = new Blob(file,{type : 'application/json'});
+                    generateLink(bb,".json");
+                }
+                XMLHttp.send();
+            }
+            
+            function getCSV()
+            {
+                var XMLHttp = new XMLHttpRequest();
+                XMLHttp.open("GET","comment_parser.php?format=CSV",true);
+                XMLHttp.onload = function() {
+                    // Now we have the XML data, extract
+                    var file = [XMLHttp.response];
+                    var bb = new Blob(file,{type : 'text/csv'});
+                    generateLink(bb,".csv");
+                }
+                XMLHttp.send();
+            }
+            
+            function generateLink(blobfile,fmt)
+            {
+                var dnlk = window.URL.createObjectURL(blobfile);
+                var a = document.createElement("a");
+                a.hidden = '';
+                a.href = dnlk;
+                a.download = "save"+fmt;
+                a.textContent = "Save File";
+                document.getElementById("download").appendChild(a);
+            }
+        </script>
+    </head>
+    <body>
+        <h1>WAET Test Results Analysis</h1>
+        <h2>Comment Extraction</h2>
+        <p>All of the XMLs in the server 'saves/' directory are automatically parsed and downloaded, extracting only the comments. Simply select the comments you wish to extract below and your desired data format.</p>
+        <div id="download"></div>
+        <div>
+            <button onclick="getXML();">XML</button>
+            <button onclick="getJSON();">JSON</button>
+            <button onclick="getCSV();">CSV</button>
+        </div>
+    </body>
+</html>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/comment_parser.php	Mon Feb 22 12:33:56 2016 +0000
@@ -0,0 +1,146 @@
+<?php
+// Comment Parser for PHP
+class audioElement {
+    function __construct($id) {
+        $this->id = $id;
+        $this->comments = array();
+    }
+    function addComment($str) {
+        array_push($this->comments,$str);
+    }
+}
+
+class testPage {
+    function __construct($id) {
+        $this->id = $id;
+        $this->elements = array();
+    }
+}
+// XML Saves location - assumes it will be saves/
+$saves = glob("../saves/*.xml");
+$comment_struct = array();
+if (is_array($saves))
+{
+    foreach($saves as $filename) {
+        $xml_string = file_get_contents($filename, FILE_TEXT);
+        $xml_object = simplexml_load_string($xml_string);
+        if ($xml_object == false) {
+            echo "<h1>FATAL</h1> <span>could not parse file ".$filename.": </span>";
+            foreach(libxml_get_errors() as $error) {
+                echo "<br>", $error->message;
+            }
+        } else {
+            // Iterate over each audioHolder node
+            foreach($xml_object->page as $pageInstance)
+            {
+                // Find the page in the comment_struct
+                $page_struct = null;
+                foreach($comment_struct as $comment_struct_page)
+                {
+                    if ($pageInstance['id'] == $comment_struct_page->id)
+                    {
+                        $page_struct = $comment_struct_page;
+                        break;
+                    }
+                }
+                if ($page_struct == null) {
+                    array_push($comment_struct,new testPage($pageInstance['id']));
+                    $page_struct = $comment_struct[count($comment_struct)-1];
+                }
+                // Get the audioelements of the page
+                foreach($pageInstance->audioelement as $fragment)
+                {
+                    // Find the page in the comment_struct
+                    $element_struct = null;
+                    foreach($page_struct->elements as $page_struct_element)
+                    {
+                        if ($fragment['id'] == $page_struct_element->id)
+                        {
+                            $element_struct = $page_struct_element;
+                            break;
+                        }
+                    }
+                    if ($element_struct == null) {
+                        array_push($page_struct->elements,new audioElement($fragment['id']));
+                        $element_struct = $page_struct->elements[count($page_struct->elements)-1];
+                    }
+                    $element_struct->addComment($fragment->comment->response);
+                }
+            }
+        }
+    }
+    // Now we have a sub <xml> containing all comment data
+    switch($_GET['format']) {
+        case "XML":
+            // Convert to an XML
+            $doc_struct = new SimpleXMLElement('<waetprocess/>');
+            foreach($comment_struct as $page_struct)
+            {
+                $doc_page = $doc_struct->addChild("page");
+                $doc_page->addAttribute("id",$page_struct->id);
+                foreach($page_struct->elements as $element_struct)
+                {
+                    $doc_element = $doc_page->addChild("audioelement");
+                    $doc_element->addAttribute("id",$element_struct->id);
+                    foreach($element_struct->comments as $comment)
+                    {
+                        $doc_comment = $doc_element->addChild("comment",$comment);
+                    }
+                }
+            }
+            echo $doc_struct->asXML();
+            break;
+        case "JSON":
+            // Convert to JSON
+            $doc_string = '{ "pages": [';
+            for($page_index = 0; $page_index < count($comment_struct); $page_index++ )
+            {
+                $page_struct = $comment_struct[$page_index];
+                $doc_page = '{"id": "'.$page_struct->id.'", "elements": [';
+                for($element_index = 0; $element_index < count($page_struct->elements); $element_index++ )
+                {
+                    $element_struct = $page_struct->elements[$element_index];
+                    $doc_element = '{"id": "'.$element_struct->id.'", "comments": [';
+                    for($comment_index = 0; $comment_index < count($element_struct->comments); $comment_index++ )
+                    {
+                        $doc_comment = '"'.$element_struct->comments[$comment_index].'"';
+                        if ($comment_index < count($element_struct->comments)-1) {
+                            $doc_comment = $doc_comment.',';
+                        }
+                        $doc_element = $doc_element.$doc_comment;
+                    }
+                    $doc_element = $doc_element.']}';
+                    if ($element_index < count($page_struct->elements)-1) {
+                        $doc_element = $doc_element.',';
+                    }
+                    $doc_page = $doc_page.$doc_element;
+                }
+                $doc_page = $doc_page.']}';
+                if ($page_index < count($comment_struct)-1) {
+                    $doc_page = $doc_page.',';
+                }
+                $doc_string = $doc_string.$doc_page;
+            }
+            $doc_string = $doc_string."]}";
+            echo $doc_string;
+            break;
+        case "CSV":
+            // Conver to CSV
+            // The CSV has three columns: page, element, comment
+            $doc_string = "page,element,comment"."\r\n";
+            foreach($comment_struct as $page_struct)
+            {
+                foreach($page_struct->elements as $element_struct)
+                {
+                    foreach($element_struct->comments as $comment)
+                    {
+                        $doc_string = $doc_string.$page_struct->id.",".$element_struct->id.",".$comment."\r\n";
+                    }
+                }
+            }
+            echo $doc_string;
+    }
+} else {
+    echo "FATAL - No saved XML files discovered";
+}
+?>
\ No newline at end of file
--- a/scripts/comment_parser.py	Sun Feb 21 11:18:08 2016 +0000
+++ b/scripts/comment_parser.py	Mon Feb 22 12:33:56 2016 +0000
@@ -41,11 +41,11 @@
         root = tree.getroot()
         
         # get list of all page names
-        for audioholder in root.findall("./audioholder"):   # iterate over pages
+        for audioholder in root.findall("./page"):   # iterate over pages
             page_name = audioholder.get('id')               # get page name
             
             if page_name is None: # ignore 'empty' audio_holders
-                print "WARNING: " + file + " contains empty audio holder. (comment_parser.py)"
+                print "WARNING: " + file + " contains empty page. (comment_parser.py)"
                 break
 
             # create folder [page_name] if not yet created
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/score_parser.php	Mon Feb 22 12:33:56 2016 +0000
@@ -0,0 +1,192 @@
+<?php
+// Value parser for WAET XML
+// testPage --> axis --> element --> value
+class nestedObject {
+    function __construct($id) {
+        $this->id = $id;
+        $this->nest = array();
+        $this->type = null;
+        $this->num = 0;
+    }
+    function addNewChild($id) {
+        if ($this->type == null) {
+            $this->type = "nest";
+        }
+        if ($this->type == "nest") {
+            $obj = new nestedObject($id);
+            array_push($this->nest,$obj);
+            $this->num = count($this->nest);
+            return $this->nest[$this->num-1];
+        }
+        return null;
+    }
+    function findChild($checkId) {
+        if ($this->type == "nest"){
+            foreach($this->nest as $child)
+            {
+                if (strcmp($checkId,$child->id) == 0) {
+                    return $child;
+                }
+            }
+        }
+        return null;
+    }
+    function addValue($val) {
+        if ($this->type == null) {
+            $this->type = "value";
+        }
+        if ($this->type == "value") {
+            array_push($this->nest,$val);
+            $this->num = count($this->nest);
+            return $this->nest[$this->num-1];
+        }
+        return null;
+    }
+}
+
+// Build the root nest object to hold the testPages
+$root = new nestedObject("root");
+
+// XML Saves location - assumes it will be saves/
+$saves = glob("../saves/*.xml");
+if (is_array($saves))
+{
+    foreach($saves as $filename) {
+        $xml_string = file_get_contents($filename, FILE_TEXT);
+        $xml_object = simplexml_load_string($xml_string);
+        if ($xml_object == false) {
+            echo "<h1>FATAL</h1> <span>could not parse file ".$filename.": </span>";
+            foreach(libxml_get_errors() as $error) {
+                echo "<br>", $error->message;
+            }
+        } else {
+            // Iterate over each $page node
+            foreach($xml_object->page as $pageInstance)
+            {
+                // Find in the nest
+                $pageInstanceId = $pageInstance['id'];
+                $page_nest = $root->findChild($pageInstanceId);
+                if ($page_nest == null) {
+                    $page_nest = $root->addNewChild($pageInstanceId);
+                }
+                
+                // Iterate over each $element node
+                foreach($pageInstance->audioelement as $element) {
+                    
+                    // Now get the <value> tags
+                    foreach($element->value as $value) {
+                        $axis_nest = null;
+                        $axisName = "default";
+                        if (isset($value['interface-name']))
+                        {
+                            // Find the axis nest
+                            $axisName = $value['interface-name'];
+                        }
+                        
+                        $axis_nest = $page_nest->findChild($axisName);
+                        if ($axis_nest == null) {
+                            $axis_nest = $page_nest->addNewChild($axisName);
+                        }
+                        
+                        // Find our specific element tag
+                        $elementId = $element['id'];
+                        $element_nest = $axis_nest->findChild($elementId);
+                        if ($element_nest == null) {
+                            $element_nest = $axis_nest->addNewChild($elementId);
+                        }
+                        // Now push our value
+                        $element_nest->addValue($value);
+                    }
+                }
+            }
+        }
+    }
+    // We now have a structure in $root. EXPORT IT
+    switch($_GET['format']) {
+        case "XML":
+            // Convert to XML
+            $doc_root = new SimpleXMLElement('<waetprocess/>');
+            foreach($root->nest as $page) {
+                $doc_page = $doc_root->addChild("page");
+                $doc_page->addAttribute("id",$page->id);
+                foreach($page->nest as $axis) {
+                    $doc_axis = $doc_page->addChild("interface");
+                    $doc_axis->addAttribute("name",$axis->id);
+                    foreach($axis->nest as $element) {
+                        $doc_element = $doc_axis->addChild("audioelement");
+                        $doc_element->addAttribute("id",$element->id);
+                        foreach($element->nest as $value) {
+                            $doc_value = $doc_element->addChild("value",$value);
+                        }
+                    }
+                }
+            }
+            echo $doc_root->asXML();
+            break;
+        case "JSON":
+            // Convert to JSON
+            $doc_root = '{ "pages": [';
+            for ($pageIndex = 0; $pageIndex < $root->num; $pageIndex++)
+            {
+                $page = $root->nest[$pageIndex];
+                $doc_page = '{ "id": "'.$page->id.'", "axis": [';
+                for($axisIndex = 0; $axisIndex < $page->num; $axisIndex++)
+                {
+                    $axis = $page->nest[$axisIndex];
+                    $doc_axis = '{ "name": "'.$axis->id.'", "elements": [';
+                    for($elementIndex = 0; $elementIndex < $axis->num; $elementIndex++)
+                    {
+                        $element = $axis->nest[$elementIndex];
+                        $doc_element = '{ "id": "'.$element->id.'", "values": [';
+                        for ($valueIndex = 0; $valueIndex < $element->num; $valueIndex++)
+                        {
+                            $doc_element = $doc_element."".strval($element->nest[$valueIndex]);
+                            if ($valueIndex < $element->num-1) {
+                                $doc_element = $doc_element.', ';
+                            }
+                        }
+                        $doc_element = $doc_element.']}';
+                        if ($elementIndex < $axis->num-1) {
+                            $doc_element = $doc_element.', ';
+                        }
+                        $doc_axis = $doc_axis.$doc_element;
+                    }
+                    $doc_axis = $doc_axis.']}';
+                    if ($axisIndex < $page->num-1) {
+                        $doc_axis = $doc_axis.', ';
+                    }
+                    $doc_page = $doc_page.$doc_axis;
+                }
+                $doc_page = $doc_page.']}';
+                if ($pageIndex < $root->num-1) {
+                    $doc_page = $doc_page.', ';
+                }
+                $doc_root = $doc_root.$doc_page;
+            }
+            $doc_root = $doc_root.']}';
+            echo $doc_root;
+            break;
+        case "CSV":
+            // Convert to CSV
+            // CSV Columts: page, axis, element, value
+            $doc_string = "page,axis,element,value"."\r\n";
+            foreach($root->nest as $page){
+                foreach($page->nest as $axis) {
+                    foreach($axis->nest as $element) {
+                        foreach($element->nest as $value) {
+                            $doc_string = $doc_string.$page->id;
+                            $doc_string = $doc_string.$axis->id;
+                            $doc_string = $doc_string.$element->id;
+                            $doc_string = $doc_string.$value;
+                            $doc_string = $doc_string."\r\n";
+                        }
+                    }
+                }
+            }
+            echo $doc_string;
+    }
+} else {
+    echo "FATAL - No saved XML files discovered";
+}
+
+?>
\ No newline at end of file
--- a/scripts/score_parser.py	Sun Feb 21 11:18:08 2016 +0000
+++ b/scripts/score_parser.py	Mon Feb 22 12:33:56 2016 +0000
@@ -43,7 +43,7 @@
         subject_id = file_name[:-4] # file name (without extension) as subject ID
 
         # get list of all pages this subject evaluated
-        for audioholder in root.findall("./audioholder"):    # iterate over pages
+        for audioholder in root.findall("./page"):    # iterate over pages
             page_name = audioholder.get('id') # get page name
                        
             if page_name is None: # ignore 'empty' audio_holders
--- a/test-schema.xsd	Sun Feb 21 11:18:08 2016 +0000
+++ b/test-schema.xsd	Mon Feb 22 12:33:56 2016 +0000
@@ -168,7 +168,7 @@
   <xs:element name="survey">
     <xs:complexType>
       <xs:sequence>
-        <xs:element name="surveyentry" maxOccurs="unbounded">
+        <xs:element name="surveyentry" minOccurs="0" maxOccurs="unbounded">
           <xs:complexType>
             <xs:sequence>
               <xs:element ref="statement" minOccurs="1" maxOccurs="1"/>
--- a/test_create/attributes.json	Sun Feb 21 11:18:08 2016 +0000
+++ b/test_create/attributes.json	Mon Feb 22 12:33:56 2016 +0000
@@ -6,7 +6,7 @@
     "projectReturn": "Results Return URL",
     "randomiseOrder": "Randomise Order",
     "testPages": "Test Pages",
-    "loudness": "Target Loudess (LUFS)",
+    "loudness": "Target Loudness (LUFS)",
     "sampleRate": "Required Sample Rate",
     "hostURL": "Element URL Prefix",
     "repeatCount": "Repeat Count",
@@ -20,4 +20,4 @@
     "gain": "Gain (dB)",
     "marker": "Marker",
     "boxsize": "Box Size"
-}
\ No newline at end of file
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test_create/custom.css	Mon Feb 22 12:33:56 2016 +0000
@@ -0,0 +1,23 @@
+
+div#content > div.node{
+    background-color: rgb(200,228,151);
+}
+
+div#content > div#setup{
+    background-color: coral;
+}
+
+input:disabled+span{
+    text-decoration: line-through;
+}
+
+div.attribute{
+    float: none;
+}
+div.attribute input{
+    max-width: 100%;
+    width: 300px;
+}
+div.attribute input[type=radio], div.attribute input[type=checkbox]{
+    width: 10px;
+}
\ No newline at end of file
--- a/test_create/style.css	Sun Feb 21 11:18:08 2016 +0000
+++ b/test_create/style.css	Mon Feb 22 12:33:56 2016 +0000
@@ -70,6 +70,16 @@
     color: rgb(100,100,100);
 }
 
+
+div#content > div.node{
+    background-color: rgb(200,228,151);
+}
+
+div#content > div#setup{
+    background-color: coral;
+}
+
+
 div.node {
     float: left;
     padding: 10px;
@@ -87,7 +97,7 @@
 }
 div.node-attributes {
     min-width: 92%;
-    float: left;
+    float: none;
     padding: 10px;
 }
 div.attribute {
@@ -103,9 +113,16 @@
     min-width: 92%;
 }
 div.attribute input {
-    max-width: 100px;
+    max-width: 100%;
+    width: 300px;
     margin-right: 10px;
 }
 div.attribute input[type=number] {
     width: 80px;
+}
+div.attribute input[type=radio], div.attribute input[type=checkbox]{
+    width: 10px;
+}
+input:disabled+span{
+    text-decoration: line-through;
 }
\ No newline at end of file
--- a/test_create/test_core.js	Sun Feb 21 11:18:08 2016 +0000
+++ b/test_create/test_core.js	Mon Feb 22 12:33:56 2016 +0000
@@ -1182,9 +1182,9 @@
             obj.text.textContent = checkText.children[i].textContent;
             metric.children.push(obj);
             metric.childrenDOM.appendChild(obj.root);
-            for (var i=0; i<specification.metrics.enabled.length; i++)
+            for (var j=0; j<specification.metrics.enabled.length; j++)
             {
-                if (specification.metrics.enabled[i] == obj.name)
+                if (specification.metrics.enabled[j] == obj.name)
                 {
                     obj.input.checked = true;
                     break;
@@ -1872,6 +1872,9 @@
         }
         
         // Build the components
+        if (this.specification.interfaces.length == 0) {
+            this.specification.interfaces.push(new specification.interfaceNode());
+        }
         for (var interfaceObj of this.specification.interfaces)
         {
             var newInterface = new this.parent.interfaceNode(this.parent,interfaceObj);
--- a/test_create/test_create.html	Sun Feb 21 11:18:08 2016 +0000
+++ b/test_create/test_create.html	Mon Feb 22 12:33:56 2016 +0000
@@ -2,7 +2,9 @@
 <head>
     <!-- This defines the test creator tool for the Web Audio Evaluation Toolbox -->
     <link rel='stylesheet' type="text/css" href="style.css"/>
+    <link rel='stylesheet' type="text/css" href="custom.css"/>
     <script type="text/javascript">
+        window.onbeforeunload = function (e) {var message = 'If you leave the page now, any unsaved changes will be lost', e = e || window.event; if (e) { e.returnValue = message;}return message;};
         // Copy of Specifiation node from Core.js
         function Specification() {
             // Handles the decoding of the project specification XML into a simple JavaScript Object.
@@ -572,7 +574,7 @@
                     this.id = null;
                     this.parent = null;
                     this.type = null;
-                    this.marker = false;
+                    this.marker = null;
                     this.enforce = false;
                     this.gain = 1.0;
                     this.schema = specification.schema.getAllElementsByName('audioelement')[0];;
@@ -632,4 +634,4 @@
     <div id="blanket"></div>
     <div id="content"></div>
 </body>
-</html>
\ No newline at end of file
+</html>