changeset 2916:300bb2bbba4a

Merge branch 'master' of https://github.com/BrechtDeMan/WebAudioEvaluationTool
author www-data <www-data@sucuk.dcs.qmul.ac.uk>
date Tue, 01 Aug 2017 15:23:47 +0100
parents 0e4b84497040 (current diff) 03a66e54cdff (diff)
children db5e601861e6
files
diffstat 9 files changed, 314 insertions(+), 214 deletions(-) [+]
line wrap: on
line diff
--- a/interfaces/mushra.js	Thu Jul 27 21:00:36 2017 +0100
+++ b/interfaces/mushra.js	Tue Aug 01 15:23:47 2017 +0100
@@ -179,7 +179,7 @@
             audioObject.bindInterface(orNode);
         } else {
             // Create a slider per track
-            var label = interfaceContext.getLabel(labelType, index, audioHolderObject.labelStart);
+            var label = element.label || interfaceContext.getLabel(labelType, index, audioHolderObject.labelStart);
             var sliderObj = new sliderObject(audioObject, label);
 
             if (typeof audioHolderObject.initialPosition === "number") {
--- a/js/core.js	Thu Jul 27 21:00:36 2017 +0100
+++ b/js/core.js	Tue Aug 01 15:23:47 2017 +0100
@@ -555,7 +555,7 @@
     this.store = null;
     var lastNodeStart;
     $(window).keypress(function (e) {
-        if (e.keyCode == 13 && popup.popup.style.visibility == 'visible') {
+        if (e.keyCode == 13 && popup.popup.style.visibility == 'visible' && interfaceContext.lightbox.isVisible() === false) {
             console.log(e);
             popup.buttonProceed.onclick();
             e.preventDefault();
@@ -1216,6 +1216,39 @@
     this.currentStore = null;
     this.initialise = function () {
 
+        function randomiseElements(page) {
+            // Get the elements which are fixed / labelled
+            var fixed = [],
+                or = [],
+                remainder = [];
+            page.audioElements.forEach(function (a) {
+                if (a.label.length > 0 || a.postion !== undefined) {
+                    fixed.push(a);
+                } else if (a.type === "outside-reference") {
+                    or.push(a);
+                } else {
+                    remainder.push(a);
+                }
+            })
+            if (page.poolSize > 0 || page.randomiseOrder) {
+                page.randomiseOrder = true;
+                if (page.poolSize === 0) {
+                    page.poolSize = page.audioElements.length;
+                }
+                page.poolSize -= fixed.length;
+                remainder = pickSubPool(remainder, page.poolSize);
+            }
+            // Randomise the remainders
+            if (page.randomiseOrder) {
+                remainder = randomiseOrder(remainder);
+            }
+            fixed = fixed.concat(remainder);
+            page.audioElements = fixed.concat(or);
+            page.audioElements.forEach(function (a, i) {
+                a.position = i;
+            });
+        }
+
         // Get the data from Specification
         var pagePool = [];
         specification.pages.forEach(function (page) {
@@ -1253,17 +1286,7 @@
             page.presentedId = i;
             this.stateMap.push(page);
             var elements = page.audioElements;
-            if (page.poolSize > 0 || page.randomiseOrder) {
-                page.randomiseOrder = true;
-                if (page.poolSize === 0) {
-                    page.poolSize = elements.length;
-                }
-                elements = pickSubPool(elements, page.poolSize);
-            }
-            if (page.randomiseOrder) {
-                elements = randomiseOrder(elements);
-            }
-            page.audioElements = elements;
+            randomiseElements(page);
             storage.createTestPageStore(page);
             audioEngineContext.loadPageData(page);
         }, this);
@@ -1325,23 +1348,6 @@
             popup.hidePopup();
             if (this.currentStateMap === null) {
                 this.currentStateMap = this.stateMap[this.stateIndex];
-                // Find and extract the outside reference
-                var elements = [],
-                    ref = [];
-                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) {
-                    elements = randomiseOrder(elements);
-                }
-                this.currentStateMap.audioElements = elements.concat(ref);
 
                 this.currentStore = storage.testPages[this.stateIndex];
                 if (this.currentStateMap.preTest !== undefined) {
@@ -2401,6 +2407,7 @@
         show: function () {
             this.root.style.visibility = "visible";
             this.blanker.style.visibility = "visible";
+            this.accept.focus();
         },
         clear: function () {
             this.root.style.visibility = "";
@@ -2414,6 +2421,9 @@
         },
         resize: function (event) {
             this.root.style.left = (window.innerWidth / 2) - 250 + 'px';
+        },
+        isVisible: function () {
+            return this.root.style.visibility == "visible";
         }
     };
 
--- a/test_create.html	Thu Jul 27 21:00:36 2017 +0100
+++ b/test_create.html	Tue Aug 01 15:23:47 2017 +0100
@@ -12,13 +12,14 @@
     <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
     <script type="text/javascript" src="js/xmllint.js"></script>
 
-    <title>WAET 1.2.1 Test Creator</title>
+    <title>WAET 1.2.2 Test Creator</title>
 </head>
 
 <body ng-app="creator" ng-controller="view">
     <div class="container">
         <div id="pageRoot">
-            <h1>Web Audio Evaluation Tool - Test Creator</h1>
+            <h1>Web Audio Evaluation Tool</h1>
+            <h3>Test Creator <span class="label label-primary">v1.2.2</span></h3>
         </div>
         <button type="button" class="btn btn-info" ng-click="validate()">Validate</button>
         <button type="button" class="btn btn-success" ng-click="exportXML()" ng-disabled="validated == false">Export XML</button>
@@ -45,7 +46,9 @@
             <div class="attributes">
                 <div class="attribute">
                     <span>Interface: </span>
-                    <input type="text" ng-model="specification.interface" required/>
+                    <select type="text" ng-model="specification.interface" required>
+                        <option value="{{i.name}}" ng-repeat="i in availableInterfaceModules">{{i.name}}</option>
+                    </select>
                 </div>
                 <div class="attribute" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="If you would like to save to a server other than your hosting server, you can place the full WAET URL here">
                     <span>Save URL: </span>
@@ -53,7 +56,7 @@
                 </div>
                 <div class="attribute" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="Once the test is completed and save confirmed, the browser will redirect to this page, if not defined.">
                     <span>Exit URL: </span>
-                    <input type="text" ng-model="specification.returnURL" />
+                    <input type="text" ng-model="specification.returnURL" placeholder="{{placeholder('returnURL')}}" />
                 </div>
                 <div class="attribute" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="Randomise the page order">
                     <span>Randomise Page Order: </span>
@@ -61,15 +64,15 @@
                 </div>
                 <div class="attribute" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="Set the number of pages to present to the user. This includes repeated pages. Set to '0' or blank to ignore. Randomise page order must be selected.">
                     <span>Page Pool Size: </span>
-                    <input type="number" ng-model="specification.poolSize" min="0" />
+                    <input type="number" ng-model="specification.poolSize" min="0" placeholder="{{placeholder('poolSize')}}" />
                 </div>
                 <div class="attribute" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="Automatically analyse and normalsie audio to this target LUFS. If unsure, use -25LUFS. 0 or blank disables normalisation">
                     <span>Loudness Normalisation (LUFS): </span>
-                    <input type="number" ng-model="specification.loudness" max="0" />
+                    <input type="number" ng-model="specification.loudness" max="0" placeholder="{{placeholder('loudness')}}" />
                 </div>
                 <div class="attribute" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="Only perform the test if the browser reported sampling rate matches this.">
                     <span>Fixed Sampling Rate: </span>
-                    <input type="number" ng-model="specification.sampleRate" min="0" />
+                    <input type="number" ng-model="specification.sampleRate" min="0" placeholder="{{placeholder('sampleRate')}}" />
                 </div>
                 <div class="attribute" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="Show a 'method of adjustment' audio calibration before testing.">
                     <span>Pre-Test audio calibration: </span>
@@ -77,15 +80,15 @@
                 </div>
                 <div class="attribute" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="Default cross-fade time when switching between elements. Can be over-ridden on each page">
                     <span>Global Cross-fade time: </span>
-                    <input type="number" ng-model="specification.crossFade" min="0" step="0.1" />
+                    <input type="number" ng-model="specification.crossFade" min="0" step="0.1" placeholder="{{placeholder('crossFade')}}" />
                 </div>
                 <div class="attribute" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="Default pre-play element silence. Can be over-ridden on each page and element">
                     <span>Global Fragment Pre-Silence: </span>
-                    <input type="number" ng-model="specification.preSilence" min="0" step="0.1" />
+                    <input type="number" ng-model="specification.preSilence" min="0" step="0.1" placeholder="{{placeholder('preSilence')}}" />
                 </div>
                 <div class="attribute" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="Default post-play element silence. Can be over-ridden on each page and element">
                     <span>Global Fragment Post-Silence: </span>
-                    <input type="number" ng-model="specification.preSilence" min="0" step="0.1" />
+                    <input type="number" ng-model="specification.postSilence" min="0" step="0.1" placeholder="{{placeholder('postSilence')}}" />
                 </div>
                 <div class="attribute" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="Disable switching of audio elements">
                     <span>Play audio one-at-a-time: </span>
@@ -93,11 +96,11 @@
                 </div>
                 <div class="attribute" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="Minimum number of times an audio fragment must be played">
                     <span>Minimum number of fragment plays</span>
-                    <input type="number" ng-model="specification.minNumberPlays" min="0" max="{{specification.maxNumberPlays}}" />
+                    <input type="number" ng-model="specification.minNumberPlays" min="0" max="{{specification.maxNumberPlays}}" placeholder="{{placeholder('minNumberPlays')}}" />
                 </div>
                 <div class="attribute" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="Maximum number of times an audio fragment can be played">
                     <span>Maximum number of fragment plays</span>
-                    <input type="number" ng-model="specification.maxNumberPlays" min="{{specification.minNumberPlays || 0}}" />
+                    <input type="number" ng-model="specification.maxNumberPlays" min="{{specification.minNumberPlays || 0}}" placeholder="{{placeholder('maxNumberPlays')}}" />
                 </div>
             </div>
             <div class="node">
@@ -140,11 +143,11 @@
             <div id="globalpresurvey" class="node" ng-controller="survey" ng-init="survey = specification.preTest">
                 <h2>Pre Test Survey</h2>
                 <button type="button" class="btn btn-success" ng-click="addSurveyEntry()">Add Entry</button>
-                <div class="node" ng-repeat="opt in survey.options" ng-controller="surveyOption">
+                <div class="node surveyentry" ng-repeat="opt in survey.options" ng-controller="surveyOption">
                     <h3>Survey Entry</h3>
                     <button type="button" class="btn btn-danger" ng-click="removeSurveyEntry(opt);">Delete Entry</button>
                     <div class="attributes">
-                        <div class="attribute">
+                        <div class="attribute" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="Type of survey entry.">
                             <span>Survey Type: </span>
                             <select ng-model="opt.type">
                                 <option value="question">Question</option>
@@ -157,23 +160,23 @@
                                 <option value="youtube">YouTube</option>
                             </select>
                         </div>
-                        <div class="attribute">
+                        <div class="attribute" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="Unique across the entire session of all ID entries. ">
                             <span>Unique Survey Entry ID:</span>
                             <input type="text" ng-model="opt.id" required/>
                         </div>
-                        <div class="attribute">
+                        <div class="attribute" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="Friendly name. Will be used in test results parsers instead of the ID, if defined.">
                             <span>Entry Name:</span>
                             <input type="text" ng-model="opt.name" />
                         </div>
-                        <div class="attribute" ng-show="['question', 'checkbox', 'radio', 'number'].indexOf(opt.type) >= 0">
+                        <div class="attribute" ng-show="['question', 'checkbox', 'radio', 'number'].indexOf(opt.type) >= 0" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="An answer must be given to continue with the test.">
                             <span>Mandatory:</span>
                             <input type="checkbox" ng-model="opt.mandatory" />
                         </div>
-                        <div class="attribute">
+                        <div class="attribute" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="Minimum time in seconds before continuing.">
                             <span>Minimum Wait Time (s):</span>
                             <input type="number" ng-model="opt.minWait" min="0" />
                         </div>
-                        <div class="attribute" ng-show="opt.type == 'question'">
+                        <div class="attribute" ng-show="opt.type == 'question'" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="Size of the displayed text box. Does not limit entry but may discourage (or encourage) longer ansewrs if bigger.">
                             <span>Box Size:</span>
                             <select ng-model="opt.boxsize">
                                 <option value="small">Small</option>
@@ -182,28 +185,28 @@
                                 <option value="huge">Huge</option>
                             </select>
                         </div>
-                        <div class="attribute" ng-show="['checkbox', 'radio'].indexOf(opt.type) >= 0">
+                        <div class="attribute" ng-show="['checkbox', 'radio'].indexOf(opt.type) >= 0" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="The minimum number of options that must be selected before continuing">
                             <span>Minimum Selected:</span>
                             <input type="number" ng-model="opt.min" min="0" />
                         </div>
-                        <div class="attribute" ng-show="['checkbox', 'radio'].indexOf(opt.type) >= 0">
+                        <div class="attribute" ng-show="['checkbox', 'radio'].indexOf(opt.type) >= 0" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="Maximum number of options that can be selected to continue.">
                             <span>Maximum Selected:</span>
                             <input type="number" ng-model="opt.max" max="{{opt.options.length}}" />
                         </div>
-                        <div class="attribute" ng-show="['slider', 'number'].indexOf(opt.type) >= 0">
+                        <div class="attribute" ng-show="['slider', 'number'].indexOf(opt.type) >= 0" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="Minimum numerical value">
                             <span>Minimum Value:</span>
                             <input type="number" ng-model="opt.min" />
                         </div>
-                        <div class="attribute" ng-show="['slider', 'number'].indexOf(opt.type) >= 0">
+                        <div class="attribute" ng-show="['slider', 'number'].indexOf(opt.type) >= 0" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="Maximum numerical value">
                             <span>Maximum Value:</span>
                             <input type="number" ng-model="opt.max" />
                         </div>
-                        <div class="attribute" ng-show="['video', 'youtube'].indexOf(opt.type) >= 0">
+                        <div class="attribute" ng-show="['video', 'youtube'].indexOf(opt.type) >= 0" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="URL of the video to embed.">
                             <span>Video URL:</span>
                             <input type="text" ng-model="opt.url" />
                         </div>
                     </div>
-                    <div class="node">
+                    <div class="node" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="Statement / Question to pose to the subject.">
                         <h4>Statement</h4>
                         <textarea ng-model="opt.statement"></textarea>
                     </div>
@@ -271,11 +274,11 @@
             <div id="globalpostsurvey" class="node" ng-controller="survey" ng-init="survey = specification.postTest">
                 <h2>Post Test Survey</h2>
                 <button type="button" class="btn btn-success" ng-click="addSurveyEntry()">Add Entry</button>
-                <div class="node" ng-repeat="opt in survey.options" ng-controller="surveyOption">
+                <div class="node surveyentry" ng-repeat="opt in survey.options" ng-controller="surveyOption">
                     <h3>Survey Entry</h3>
                     <button type="button" class="btn btn-danger" ng-click="removeSurveyEntry(opt);">Delete Entry</button>
                     <div class="attributes">
-                        <div class="attribute">
+                        <div class="attribute" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="Type of survey entry.">
                             <span>Survey Type: </span>
                             <select ng-model="opt.type">
                                 <option value="question">Question</option>
@@ -288,23 +291,23 @@
                                 <option value="youtube">YouTube</option>
                             </select>
                         </div>
-                        <div class="attribute">
+                        <div class="attribute" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="Unique across the entire session of all ID entries. ">
                             <span>Unique Survey Entry ID:</span>
                             <input type="text" ng-model="opt.id" required/>
                         </div>
-                        <div class="attribute">
+                        <div class="attribute" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="Friendly name. Will be used in test results parsers instead of the ID, if defined.">
                             <span>Entry Name:</span>
                             <input type="text" ng-model="opt.name" />
                         </div>
-                        <div class="attribute" ng-show="['question', 'checkbox', 'radio', 'number'].indexOf(opt.type) >= 0">
+                        <div class="attribute" ng-show="['question', 'checkbox', 'radio', 'number'].indexOf(opt.type) >= 0" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="An answer must be given to continue with the test.">
                             <span>Mandatory:</span>
                             <input type="checkbox" ng-model="opt.mandatory" />
                         </div>
-                        <div class="attribute">
+                        <div class="attribute" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="Minimum time in seconds before continuing.">
                             <span>Minimum Wait Time (s):</span>
                             <input type="number" ng-model="opt.minWait" min="0" />
                         </div>
-                        <div class="attribute" ng-show="opt.type == 'question'">
+                        <div class="attribute" ng-show="opt.type == 'question'" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="Size of the displayed text box. Does not limit entry but may discourage (or encourage) longer ansewrs if bigger.">
                             <span>Box Size:</span>
                             <select ng-model="opt.boxsize">
                                 <option value="small">Small</option>
@@ -313,28 +316,28 @@
                                 <option value="huge">Huge</option>
                             </select>
                         </div>
-                        <div class="attribute" ng-show="['checkbox', 'radio'].indexOf(opt.type) >= 0">
+                        <div class="attribute" ng-show="['checkbox', 'radio'].indexOf(opt.type) >= 0" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="The minimum number of options that must be selected before continuing">
                             <span>Minimum Selected:</span>
                             <input type="number" ng-model="opt.min" min="0" />
                         </div>
-                        <div class="attribute" ng-show="['checkbox', 'radio'].indexOf(opt.type) >= 0">
+                        <div class="attribute" ng-show="['checkbox', 'radio'].indexOf(opt.type) >= 0" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="Maximum number of options that can be selected to continue.">
                             <span>Maximum Selected:</span>
                             <input type="number" ng-model="opt.max" max="{{opt.options.length}}" />
                         </div>
-                        <div class="attribute" ng-show="['slider', 'number'].indexOf(opt.type) >= 0">
+                        <div class="attribute" ng-show="['slider', 'number'].indexOf(opt.type) >= 0" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="Minimum numerical value">
                             <span>Minimum Value:</span>
                             <input type="number" ng-model="opt.min" />
                         </div>
-                        <div class="attribute" ng-show="['slider', 'number'].indexOf(opt.type) >= 0">
+                        <div class="attribute" ng-show="['slider', 'number'].indexOf(opt.type) >= 0" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="Maximum numerical value">
                             <span>Maximum Value:</span>
                             <input type="number" ng-model="opt.max" />
                         </div>
-                        <div class="attribute" ng-show="['video', 'youtube'].indexOf(opt.type) >= 0">
+                        <div class="attribute" ng-show="['video', 'youtube'].indexOf(opt.type) >= 0" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="URL of the video to embed.">
                             <span>Video URL:</span>
                             <input type="text" ng-model="opt.url" />
                         </div>
                     </div>
-                    <div class="node">
+                    <div class="node" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="Statement / Question to pose to the subject.">
                         <h4>Statement</h4>
                         <textarea ng-model="opt.statement"></textarea>
                     </div>
@@ -403,23 +406,23 @@
                 <h2>Interface (Globals)</h2>
                 <div class="node interfaceOptions">
                     <div class="attributes">
-                        <div class="attribute" name="fragmentPlayed" type="check">
+                        <div class="attribute" name="fragmentPlayed" type="check" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="A page can only be submitted if all fragments have been played">
                             <span>Check all fragments played: </span>
                             <input type="checkbox" ng-click="enableInterfaceOption($event)" />
                         </div>
-                        <div class="attribute" name="fragmentFullPlayback" type="check">
+                        <div class="attribute" name="fragmentFullPlayback" type="check" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="A page can only be submitted if all fragments have been played completely.">
                             <span>Check all fragments fully played: </span>
                             <input type="checkbox" ng-click="enableInterfaceOption($event)" />
                         </div>
-                        <div class="attribute" name="fragmentMoved" type="check">
+                        <div class="attribute" name="fragmentMoved" type="check" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="A page can only be submitted if all fragments have been moved">
                             <span>Check all fragments have been moved: </span>
                             <input type="checkbox" ng-click="enableInterfaceOption($event)" />
                         </div>
-                        <div class="attribute" name="fragmentComments" type="check">
+                        <div class="attribute" name="fragmentComments" type="check" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="A page can only be submitted if all fragments have their comment boxes completed.">
                             <span>Check all fragments have comments: </span>
                             <input type="checkbox" ng-click="enableInterfaceOption($event)" />
                         </div>
-                        <div class="attribute" name="scalerange" type="check">
+                        <div class="attribute" name="scalerange" type="check" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="A page can only be submitted if there are fragments above and below the maximum and minimum ranges.">
                             <span>Enforce a scale usage: </span>
                             <input type="checkbox" ng-click="enableInterfaceOption($event)" />
                             <span>Minimum:</span>
@@ -427,19 +430,19 @@
                             <span>Maximum:</span>
                             <input type="number" min="0" max="100" name="max" />
                         </div>
-                        <div class="attribute" name="volume" type="show">
+                        <div class="attribute" name="volume" type="show" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="Show the master volume control on each page.">
                             <span>Show master volume control: </span>
                             <input type="checkbox" ng-click="enableInterfaceOption($event)" />
                         </div>
-                        <div class="attribute" name="playhead" type="show">
+                        <div class="attribute" name="playhead" type="show" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="Show the playhead for the fragments.">
                             <span>Show playhead: </span>
                             <input type="checkbox" ng-click="enableInterfaceOption($event)" />
                         </div>
-                        <div class="attribute" name="page-count" type="show">
+                        <div class="attribute" name="page-count" type="show" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="Show number of completed and remaining pages.">
                             <span>Show Page Count: </span>
                             <input type="checkbox" ng-click="enableInterfaceOption($event)" />
                         </div>
-                        <div class="attribute" name="comments" type="show">
+                        <div class="attribute" name="comments" type="show" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="Show comment boxes for each fragment.">
                             <span>Show Fragment Comments: </span>
                             <input type="checkbox" ng-click="enableInterfaceOption($event)" />
                         </div>
@@ -467,8 +470,8 @@
                     <input type="checkbox" ng-model="page.randomiseOrder" />
                 </div>
                 <div class="attribute" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="Specify if this page should be repeated and how many times. Please note, that if page-pooling is also selected then it 'may' not repeat as many times.">
-                    <span>Repeat Page N-times: </span>
-                    <input type="number" ng-model="page.repeatCount" value="0" step="1" />
+                    <span>Numer of repetitions: </span>
+                    <input type="number" ng-model="page.repeatCount" value="0" step="1" placeholder="{{placeholder('repeatCount')}}" />
                 </div>
                 <div class="attribute" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="Loop audio playback until manually stopped or the page submit button is pressed">
                     <span>Loop audio: </span>
@@ -480,7 +483,7 @@
                 </div>
                 <div class="attribute" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="Over-ride global loudness normalisation">
                     <span>Loudness (page): </span>
-                    <input type="number" ng-model="page.loudness" max="0" />
+                    <input type="number" ng-model="page.loudness" max="0" placeholder="{{specification.loudness || placeholder('loudness')}}" />
                 </div>
                 <div class="attribute" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="Label type to display on the fragments.">
                     <span>Label type: </span>
@@ -499,7 +502,7 @@
                 </div>
                 <div class="attribute" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="Select a subgroup of the given audio fragments to display. 0 or blank means all fragments will be displayed.">
                     <span>Fragment pool size: </span>
-                    <input type="number" ng-model="page.poolSize" min="0" max="page.audioElements.length" />
+                    <input type="number" ng-model="page.poolSize" min="0" max="page.audioElements.length" placeholder="{{placeholder('poolSize')}}" />
                 </div>
                 <div class="attribute" ng-show="specification.poolSize > 0" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="Always display this page, even after sub-pooling of pages">
                     <span>Always include page: </span>
@@ -511,11 +514,11 @@
                 </div>
                 <div class="attribute" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="Over-ride global pre-silence">
                     <span>Fragment pre-silence: </span>
-                    <input type="number" ng-model="page.preSilence" min="0" step="0.1" />
+                    <input type="number" ng-model="page.preSilence" min="0" step="0.1" placeholder="{{specification.preSilence ||  placeholder('preSilence')}}" />
                 </div>
                 <div class="attribute" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="Over-ride global post-silence">
                     <span>Fragment post-silence: </span>
-                    <input type="number" ng-model="page.postSilence" min="0" step="0.1" />
+                    <input type="number" ng-model="page.postSilence" min="0" step="0.1" placeholder="{{specification.postSilence ||  placeholder('postSilence')}}" />
                 </div>
                 <div class="attribute" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="Disable switching of audio">
                     <span>Cannot interupt audio: </span>
@@ -527,11 +530,11 @@
                 </div>
                 <div class="attribute" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="Over-ride global minimum number of fragment plays">
                     <span>Minimum number of fragment plays</span>
-                    <input type="number" ng-model="page.minNumberPlays" min="0" max="{{page.maxNumberPlays || specification.maxNumberPlays}}" />
+                    <input type="number" ng-model="page.minNumberPlays" min="0" max="{{page.maxNumberPlays || specification.maxNumberPlays}}" placeholder="{{specification.minNumberPlays ||  placeholder('minNumberPlays')}}" />
                 </div>
                 <div class="attribute" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="Over-ride global maximum number of fragment plays">
                     <span>Maximum number of fragment plays</span>
-                    <input type="number" ng-model="page.maxNumberPlays" min="{{page.minNumberPlays || specification.minNumberPlays || 0}}" />
+                    <input type="number" ng-model="page.maxNumberPlays" min="{{page.minNumberPlays || specification.minNumberPlays || 0}}" placeholder="{{specification.maxNumberPlays ||  placeholder('maxNumberPlays')}}" />
                 </div>
             </div>
             <div class="node" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="Set the title of the page">
@@ -548,11 +551,11 @@
             <div class="node" ng-controller="survey" ng-init="survey = page.preTest">
                 <h2>Pre Page Survey</h2>
                 <button type="button" class="btn btn-success" ng-click="addSurveyEntry()">Add Entry</button>
-                <div class="node" ng-repeat="opt in survey.options" ng-controller="surveyOption">
+                <div class="node surveyentry" ng-repeat="opt in survey.options" ng-controller="surveyOption">
                     <h3>Survey Entry</h3>
                     <button type="button" class="btn btn-danger" ng-click="removeSurveyEntry(opt);">Delete Entry</button>
                     <div class="attributes">
-                        <div class="attribute">
+                        <div class="attribute" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="Type of survey entry.">
                             <span>Survey Type: </span>
                             <select ng-model="opt.type">
                                 <option value="question">Question</option>
@@ -565,23 +568,23 @@
                                 <option value="youtube">YouTube</option>
                             </select>
                         </div>
-                        <div class="attribute">
+                        <div class="attribute" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="Unique across the entire session of all ID entries. ">
                             <span>Unique Survey Entry ID:</span>
                             <input type="text" ng-model="opt.id" required/>
                         </div>
-                        <div class="attribute">
+                        <div class="attribute" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="Friendly name. Will be used in test results parsers instead of the ID, if defined.">
                             <span>Entry Name:</span>
                             <input type="text" ng-model="opt.name" />
                         </div>
-                        <div class="attribute" ng-show="['question', 'checkbox', 'radio', 'number'].indexOf(opt.type) >= 0">
+                        <div class="attribute" ng-show="['question', 'checkbox', 'radio', 'number'].indexOf(opt.type) >= 0" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="An answer must be given to continue with the test.">
                             <span>Mandatory:</span>
                             <input type="checkbox" ng-model="opt.mandatory" />
                         </div>
-                        <div class="attribute">
+                        <div class="attribute" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="Minimum time in seconds before continuing.">
                             <span>Minimum Wait Time (s):</span>
                             <input type="number" ng-model="opt.minWait" min="0" />
                         </div>
-                        <div class="attribute" ng-show="opt.type == 'question'">
+                        <div class="attribute" ng-show="opt.type == 'question'" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="Size of the displayed text box. Does not limit entry but may discourage (or encourage) longer ansewrs if bigger.">
                             <span>Box Size:</span>
                             <select ng-model="opt.boxsize">
                                 <option value="small">Small</option>
@@ -590,28 +593,28 @@
                                 <option value="huge">Huge</option>
                             </select>
                         </div>
-                        <div class="attribute" ng-show="['checkbox', 'radio'].indexOf(opt.type) >= 0">
+                        <div class="attribute" ng-show="['checkbox', 'radio'].indexOf(opt.type) >= 0" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="The minimum number of options that must be selected before continuing">
                             <span>Minimum Selected:</span>
                             <input type="number" ng-model="opt.min" min="0" />
                         </div>
-                        <div class="attribute" ng-show="['checkbox', 'radio'].indexOf(opt.type) >= 0">
+                        <div class="attribute" ng-show="['checkbox', 'radio'].indexOf(opt.type) >= 0" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="Maximum number of options that can be selected to continue.">
                             <span>Maximum Selected:</span>
                             <input type="number" ng-model="opt.max" max="{{opt.options.length}}" />
                         </div>
-                        <div class="attribute" ng-show="['slider', 'number'].indexOf(opt.type) >= 0">
+                        <div class="attribute" ng-show="['slider', 'number'].indexOf(opt.type) >= 0" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="Minimum numerical value">
                             <span>Minimum Value:</span>
                             <input type="number" ng-model="opt.min" />
                         </div>
-                        <div class="attribute" ng-show="['slider', 'number'].indexOf(opt.type) >= 0">
+                        <div class="attribute" ng-show="['slider', 'number'].indexOf(opt.type) >= 0" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="Maximum numerical value">
                             <span>Maximum Value:</span>
                             <input type="number" ng-model="opt.max" />
                         </div>
-                        <div class="attribute" ng-show="['video', 'youtube'].indexOf(opt.type) >= 0">
+                        <div class="attribute" ng-show="['video', 'youtube'].indexOf(opt.type) >= 0" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="URL of the video to embed.">
                             <span>Video URL:</span>
                             <input type="text" ng-model="opt.url" />
                         </div>
                     </div>
-                    <div class="node">
+                    <div class="node" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="Statement / Question to pose to the subject.">
                         <h4>Statement</h4>
                         <textarea ng-model="opt.statement"></textarea>
                     </div>
@@ -679,11 +682,11 @@
             <div class="node" ng-controller="survey" ng-init="survey = page.postTest">
                 <h2>Post Page Survey</h2>
                 <button type="button" class="btn btn-success" ng-click="addSurveyEntry()">Add Entry</button>
-                <div class="node" ng-repeat="opt in survey.options" ng-controller="surveyOption">
+                <div class="node surveyentry" ng-repeat="opt in survey.options" ng-controller="surveyOption">
                     <h3>Survey Entry</h3>
                     <button type="button" class="btn btn-danger" ng-click="removeSurveyEntry(opt);">Delete Entry</button>
                     <div class="attributes">
-                        <div class="attribute">
+                        <div class="attribute" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="Type of survey entry.">
                             <span>Survey Type: </span>
                             <select ng-model="opt.type">
                                 <option value="question">Question</option>
@@ -696,23 +699,23 @@
                                 <option value="youtube">YouTube</option>
                             </select>
                         </div>
-                        <div class="attribute">
+                        <div class="attribute" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="Unique across the entire session of all ID entries. ">
                             <span>Unique Survey Entry ID:</span>
-                            <input type="text" ng-model="opt.id" required />
+                            <input type="text" ng-model="opt.id" required/>
                         </div>
-                        <div class="attribute">
+                        <div class="attribute" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="Friendly name. Will be used in test results parsers instead of the ID, if defined.">
                             <span>Entry Name:</span>
                             <input type="text" ng-model="opt.name" />
                         </div>
-                        <div class="attribute" ng-show="['question', 'checkbox', 'radio', 'number'].indexOf(opt.type) >= 0">
+                        <div class="attribute" ng-show="['question', 'checkbox', 'radio', 'number'].indexOf(opt.type) >= 0" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="An answer must be given to continue with the test.">
                             <span>Mandatory:</span>
                             <input type="checkbox" ng-model="opt.mandatory" />
                         </div>
-                        <div class="attribute">
+                        <div class="attribute" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="Minimum time in seconds before continuing.">
                             <span>Minimum Wait Time (s):</span>
                             <input type="number" ng-model="opt.minWait" min="0" />
                         </div>
-                        <div class="attribute" ng-show="opt.type == 'question'">
+                        <div class="attribute" ng-show="opt.type == 'question'" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="Size of the displayed text box. Does not limit entry but may discourage (or encourage) longer ansewrs if bigger.">
                             <span>Box Size:</span>
                             <select ng-model="opt.boxsize">
                                 <option value="small">Small</option>
@@ -721,28 +724,28 @@
                                 <option value="huge">Huge</option>
                             </select>
                         </div>
-                        <div class="attribute" ng-show="['checkbox', 'radio'].indexOf(opt.type) >= 0">
+                        <div class="attribute" ng-show="['checkbox', 'radio'].indexOf(opt.type) >= 0" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="The minimum number of options that must be selected before continuing">
                             <span>Minimum Selected:</span>
                             <input type="number" ng-model="opt.min" min="0" />
                         </div>
-                        <div class="attribute" ng-show="['checkbox', 'radio'].indexOf(opt.type) >= 0">
+                        <div class="attribute" ng-show="['checkbox', 'radio'].indexOf(opt.type) >= 0" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="Maximum number of options that can be selected to continue.">
                             <span>Maximum Selected:</span>
                             <input type="number" ng-model="opt.max" max="{{opt.options.length}}" />
                         </div>
-                        <div class="attribute" ng-show="['slider', 'number'].indexOf(opt.type) >= 0">
+                        <div class="attribute" ng-show="['slider', 'number'].indexOf(opt.type) >= 0" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="Minimum numerical value">
                             <span>Minimum Value:</span>
                             <input type="number" ng-model="opt.min" />
                         </div>
-                        <div class="attribute" ng-show="['slider', 'number'].indexOf(opt.type) >= 0">
+                        <div class="attribute" ng-show="['slider', 'number'].indexOf(opt.type) >= 0" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="Maximum numerical value">
                             <span>Maximum Value:</span>
                             <input type="number" ng-model="opt.max" />
                         </div>
-                        <div class="attribute" ng-show="['video', 'youtube'].indexOf(opt.type) >= 0">
+                        <div class="attribute" ng-show="['video', 'youtube'].indexOf(opt.type) >= 0" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="URL of the video to embed.">
                             <span>Video URL:</span>
                             <input type="text" ng-model="opt.url" />
                         </div>
                     </div>
-                    <div class="node">
+                    <div class="node" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="Statement / Question to pose to the subject.">
                         <h4>Statement</h4>
                         <textarea ng-model="opt.statement"></textarea>
                     </div>
@@ -808,7 +811,7 @@
                 </div>
             </div>
             <button type="button" class="btn btn-success" ng-show="specification.interface == 'APE' || page.interfaces.length == 0" ng-click="addInterface()">Add Interface/Axis</button>
-            <div class="node" ng-repeat="interface in page.interfaces" ng-controller="interfaceNode">
+            <div class="node interface" ng-repeat="interface in page.interfaces" ng-controller="interfaceNode">
                 <h2>Interface</h2>
                 <button type="button" class="btn btn-danger" ng-click="removeInterface(interface)">Remove Interface/Axis</button>
                 <div class="node interfaceOptions">
@@ -955,15 +958,15 @@
                 </div>
             </div>
             <button type="button" class="btn btn-success" ng-click="addAudioElement()">Add Fragment</button>
-            <div class="node" ng-repeat="fragment in page.audioElements">
+            <div class="node audioelement" ng-repeat="fragment in page.audioElements">
                 <h3>Audio Fragment</h3>
                 <button type="button" class="btn btn-danger" ng-click="removeAudioElement(fragment)">Remove Fragment</button>
                 <div class="attributes">
-                    <div class="attribute">
+                    <div class="attribute" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="The unique ID of this fragment. Must be unique across the entire session">
                         <span>Unique ID: </span>
                         <input type="text" ng-model="fragment.id" required/>
                     </div>
-                    <div class="attribute">
+                    <div class="attribute" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="URL of the fragment.">
                         <span>URL: </span>
                         <input type="text" ng-model="fragment.url" required/>
                         <span>Full URL: </span><span style="font-weight=600">{{page.hostURL}}{{fragment.url}}</span>
@@ -991,15 +994,15 @@
                     </div>
                     <div class="attribute" ng-show="fragment.type == 'anchor'">
                         <span>Anchor must be below: </span>
-                        <input type="number" ng-model="fragment.marker" min="0" max="100" />
+                        <input type="number" ng-model="fragment.marker" min="0" max="100" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="If this is set, the page cannot be submitted if this fragment is above this point." />
                     </div>
                     <div class="attribute" ng-show="fragment.type == 'reference'">
                         <span>Reference must be above: </span>
-                        <input type="number" ng-model="fragment.marker" min="0" max="100" />
+                        <input type="number" ng-model="fragment.marker" min="0" max="100" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="If this is set, the page cannot be submitted if this fragment is below this point." />
                     </div>
                     <div class="attribute" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="Over-ride global and page loudness">
                         <span>Loudness: </span>
-                        <input type="number" ng-model="fragment.loudness" max="0" />
+                        <input type="number" ng-model="fragment.loudness" max="0" placeholder="{{page.loudness || specification.loudness || ''}}" />
                     </div>
                     <div class="attribute" ng-show="page.poolSize > 0" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="hover" data-content="Always include this fragment after any sub-pooling">
                         <span>Always include fragment: </span>
@@ -1044,13 +1047,13 @@
     <div id="popupHolder" ng-show="popupVisible">
         <div ng-controller="introduction" class="popup" ng-show="popupVisible">
             <div class="popupTitle" ng-switch="state">
-                <span ng-switch-when="0">Test Creator</span>
+                <span ng-switch-when="0">Test Creator <span class="label label-primary">v1.2.2</span></span>
                 <span ng-switch-when="1">Create New Test</span>
             </div>
             <div class="popupContent container-fluid" ng-switch="state">
                 <div ng-switch-when="0">
                     <div>
-                        <span>Welcome to the WAET test creator tool. This will allow you to create a new test from scratch to suit your testing needs. If you wish to update a test file, please drag and drop the XML document into the area below for processing, otherwise press 'Next' to start a new test. This tool generates files for the WAET 1.2.1 version.</span>
+                        <span>Welcome to the WAET test creator tool. This will allow you to create a new test from scratch to suit your testing needs. If you wish to update a test file, please drag and drop the XML document into the area below for processing, otherwise press 'Next' to start a new test. This tool generates files for the WAET 1.2.2 version.</span>
                     </div>
                     <div>
                         <input type="file" id="files" ng-model="files" onchange="handleFiles(event)" />
@@ -1062,7 +1065,7 @@
                     </div>
                     <div class="row">
                         <div class="col-md-6" style="overflow-y: scroll;height: 333px;">
-                            <div class="new-test" ng-repeat="i in testSpecifications.interfaces" ng-mouseover="mouseover(i.name)" ng-click="initialise(i.name)">
+                            <div class="new-test" ng-repeat="i in testSpecifications.interfaces" ng-mouseover="mouseover(i.name)" ng-click="select(i.name)">
                                 <label style="cursor:pointer">
                                     <input type="radio" name="new-test" value="{{i.name}}" id="i.name" style="cursor:pointer" /> {{i.name}}
                                 </label>
--- a/test_create/interfaces/specifications.json	Thu Jul 27 21:00:36 2017 +0100
+++ b/test_create/interfaces/specifications.json	Tue Aug 01 15:23:47 2017 +0100
@@ -10,56 +10,6 @@
             "show": [],
             "elements": []
         }, {
-            "name": "MUSHRA",
-            "interface": "MUSHRA",
-            "description": {
-                "en": "Multi-stimulus with hidden reference and anchor. Each fragment is shown on its own vertical slider. One fragment must be labelled as a reference and another labelled as an anchor. One external reference must also be shown."
-            },
-            "scales": ["ACR"],
-            "checks": [{
-                "name": "fragmentMoved",
-                "support": "none"
-            }, {
-                "name": "fragmentPlayed",
-                "support": "none"
-            }, {
-                "name": "fragmentFullPlayback",
-                "support": "none"
-            }, {
-                "name": "fragmentComments",
-                "support": "none"
-            }, {
-                "name": "scalerange",
-                "support": "none"
-            }],
-            "show": [{
-                "name": "volume",
-                "support": "none"
-            }, {
-                "name": "page-count",
-                "support": "none"
-            }, {
-                "name": "playhead",
-                "support": "none"
-            }, {
-                "name": "comments",
-                "support": "none"
-            }],
-            "elements": [{
-                "anchor": {
-                    "min": 1,
-                    "max": "undefined"
-                },
-                "reference": {
-                    "min": 1,
-                    "max": "undefined"
-                },
-                "outsidereference": {
-                    "min": 1,
-                    "max": "undefined"
-                }
-            }]
-        }, {
             "name": "Vertical Sliders",
             "interface": "MUSHRA",
             "description": {
@@ -78,7 +28,7 @@
                 "en": "Each element is given a horizontal scale broken into a number of discrete choices. The number of choices is defined by the scale markers."
             }
         }, {
-            "name": "Rank",
+            "name": "Rank/Ordinal",
             "interface": "ordinal",
             "description": {
                 "en": "Each stimulus is placed on a discrete scale equalling the number of fragments. The fragments are then ranked based on the question posed. Only one element can occupy a rank position"
@@ -114,38 +64,19 @@
                 "support": "none"
             }]
         }, {
-            "name": "ABC/HR",
+            "name": "ABC/HR (ITU-R BS.1116)",
             "interface": "MUSHRA",
             "description": {
-                "en": "Each stimulus is placed on a vertical slider. The scale is fixed with the labels 'Imperceptible' to 'Very Annoying'"
+                "en": "Double-blind triple-stimulus with hidden reference. Each fragment is shown on its own vertical slider. One fragment must be labelled as a reference and placed as fragment A. This must be copied again and then the tested difference as the third fragment. ITU-R BS.1116-3"
             },
-            "scales": ["ABC"],
-            "checks": [{
-                "name": "fragmentMoved",
-                "support": "none"
-            }, {
-                "name": "fragmentPlayed",
-                "support": "none"
-            }, {
-                "name": "fragmentFullPlayback",
-                "support": "none"
-            }, {
-                "name": "fragmentComments",
-                "support": "none"
-            }],
-            "show": [{
-                "name": "volume",
-                "support": "none"
-            }, {
-                "name": "page-count",
-                "support": "none"
-            }, {
-                "name": "playhead",
-                "support": "none"
-            }, {
-                "name": "comments",
-                "support": "none"
-            }]
+            "template": "./tests/templates/itur1116.xml"
+        }, {
+            "name": "MUSHRA (ITU-R BS.1534)",
+            "interface": "MUSHRA",
+            "description": {
+                "en": "Multiple Stimulus with hiddren reference and anchor. At least four fragments must be shown, an outside reference, hidden reference, hidden anchor and the test candidate. ITU-R BS.1534-3"
+            },
+            "template": "./tests/templates/itur1534.xml"
         }, {
             "name": "Bipolar",
             "interface": "horizontal",
--- a/test_create/style.css	Thu Jul 27 21:00:36 2017 +0100
+++ b/test_create/style.css	Tue Aug 01 15:23:47 2017 +0100
@@ -74,10 +74,10 @@
     width: 80%;
 }
 .node > h1,
-h2,
-h3,
-h4,
-h5 {
+.node > h2,
+.node > h3,
+.node > h4,
+.node > h5 {
     text-align: center;
 }
 .attribute {
--- a/test_create/test_core.js	Thu Jul 27 21:00:36 2017 +0100
+++ b/test_create/test_core.js	Tue Aug 01 15:23:47 2017 +0100
@@ -35,10 +35,14 @@
 
 window.onload = function () {
     // Get the test interface specifications
+    toggleDropdowns();
+};
+
+function toggleDropdowns() {
     $(function () {
         $('[data-toggle="popover"]').popover();
     });
-};
+}
 
 function handleFiles(event) {
     var s = angular.element(event.currentTarget).scope();
@@ -82,6 +86,11 @@
         specification.processSchema(text);
         $s.globalSchema = specification.getSchema();
     });
+    $s.availableInterfaceModules = [];
+    get("interfaces/interfaces.json").then(JSON.parse).then(function (d) {
+        $s.availableInterfaceModules = d.interfaces;
+        $s.$apply();
+    });
     $s.specification = specification;
     $s.selectedTestPrototype = undefined;
     $s.setTestPrototype = function (obj) {
@@ -112,10 +121,12 @@
         });
         var dnlk = window.URL.createObjectURL(bb);
         var a = document.createElement("a");
+        document.body.appendChild(a)
         a.href = dnlk;
         a.download = "test.xml";
         a.click();
         window.URL.revokeObjectURL(dnlk);
+        document.body.removeChild(a);
     };
     $s.validated = false;
     $s.showValidationMessages = false;
@@ -149,14 +160,28 @@
     $s.hideValidationMessages = function () {
         $s.showValidationMessages = false;
     }
+    $s.$watch(function () {
+        return document.querySelectorAll("div.pageNode").length;
+    }, $w.toggleDropdowns);
+    $s.$watch(function () {
+        return document.querySelectorAll("div.surveyentry").length;
+    }, $w.toggleDropdowns);
+    $s.$watch(function () {
+        return document.querySelectorAll("div.interface").length;
+    }, $w.toggleDropdowns);
+    $s.$watch(function () {
+        return document.querySelectorAll("div.audioelement").length;
+    }, $w.toggleDropdowns);
 }]);
 
 AngularInterface.controller("introduction", ['$scope', '$element', '$window', function ($s, $e, $w) {
     $s.state = 0;
+    $s.selected = undefined;
     $s.next = function () {
         $s.state++;
         if ($s.state > 1 || $s.file) {
             $s.hidePopup();
+            $s.initialise($s.selected);
         }
     };
     $s.back = function () {
@@ -177,19 +202,32 @@
         if (obj === undefined) {
             throw ("Cannot find specification");
         }
-        $s.setTestPrototype(obj);
+        if (typeof obj.template === "string") {
+            get(obj.template).then(function (data) {
+                $s.parseFile(data);
+            }, function (err) {})
+        } else {
+            $s.setTestPrototype(obj);
+        }
     };
-    // Get the test interface specifications
+    $s.select = function (name) {
+            $s.selected = name;
+        }
+        // Get the test interface specifications
     $s.file = undefined;
     $s.description = "";
 
+    $s.parseFile = function (f) {
+        var p = new DOMParser();
+        specification.decode(p.parseFromString(f, "text/xml"));
+        $s.$apply();
+    }
+
     $s.handleFiles = function ($event) {
         $s.file = $event.currentTarget.files[0];
         var r = new FileReader();
         r.onload = function () {
-            var p = new DOMParser();
-            specification.decode(p.parseFromString(r.result, "text/xml"));
-            $s.$apply();
+            $s.parseFile(r.result);
         };
         r.readAsText($s.file);
     };
@@ -240,6 +278,17 @@
     $s.configure = function () {}
 
     $s.$watch("selectedTestPrototype", $s.configure);
+
+    $s.placeholder = function (name) {
+        if ($s.schema) {
+            var spec = $s.schema.querySelector("attribute[name=\"" + name + "\"]") || $w.specification.schema.querySelector("attribute[name=\"" + name + "\"]");
+            var attr = spec.getAttribute("default");
+            if (attr === null) {
+                return "Not set";
+            }
+            return attr;
+        }
+    }
 }]);
 
 AngularInterface.controller("survey", ['$scope', '$element', '$window', function ($s, $e, $w) {
@@ -413,6 +462,8 @@
     $s.configure();
 }]);
 AngularInterface.controller("page", ['$scope', '$element', '$window', function ($s, $e, $w) {
+    $s.schema = $w.specification.schema.querySelector("element[name=\"page\"]");
+    $s.page.label = $s.page.label || "default";
     $s.addInterface = function () {
         $s.page.addInterface();
     };
@@ -450,4 +501,13 @@
         }
         $s.page.audioElements.splice(index, 1);
     };
+
+    $s.placeholder = function (name) {
+        var spec = $s.schema.querySelector("attribute[name=\"" + name + "\"]") || $w.specification.schema.querySelector("attribute[name=\"" + name + "\"]");
+        var attr = spec.getAttribute("default");
+        if (attr === null) {
+            return "Not set";
+        }
+        return attr;
+    }
 }]);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/templates/itur1116.xml	Tue Aug 01 15:23:47 2017 +0100
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+    <waet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="test-schema.xsd">
+        <setup interface="MUSHRA" projectReturn="save.php" randomiseOrder="true" loudness="-23">
+            <exitText>Thank you for participating in this listening test. You may now exit the test environment</exitText>
+            <metric>
+                <metricenable>testTimer</metricenable>
+                <metricenable>elementTimer</metricenable>
+                <metricenable>elementInitialPosition</metricenable>
+                <metricenable>elementTracker</metricenable>
+                <metricenable>elementFlagListenedTo</metricenable>
+                <metricenable>elementFlagMoved</metricenable>
+                <metricenable>elementListenTracker</metricenable>
+            </metric>
+            <interface>
+                <interfaceoption type="check" name="fragmentMoved" />
+            </interface>
+        </setup>
+        <page id="training" randomiseOrder="true" repeatCount="0" loop="true" synchronous="true" position="0" label="capital">
+            <interface>
+                <title>Quality</title>
+                <scales>
+                    <scalelabel position="100">Imperceptible</scalelabel>
+                    <scalelabel position="75">Perceptible but not annoying</scalelabel>
+                    <scalelabel position="50">Slightly annoying</scalelabel>
+                    <scalelabel position="25">Annoying</scalelabel>
+                    <scalelabel position="0">Very annoying</scalelabel>
+                </scales>
+            </interface>
+            <audioelement url="0.wav" id="training-A" type="reference" label="A" position="0" />
+            <audioelement url="1.wav" id="training-1" />
+            <audioelement url="0.wav" id="training-2" />
+        </page>
+        <page id="test-0" randomiseOrder="true" repeatCount="0" loop="true" synchronous="true" position="0" label="capital">
+            <interface>
+                <title>Quality</title>
+                <scales>
+                    <scalelabel position="100">Imperceptible</scalelabel>
+                    <scalelabel position="75">Perceptible but not annoying</scalelabel>
+                    <scalelabel position="50">Slightly annoying</scalelabel>
+                    <scalelabel position="25">Annoying</scalelabel>
+                    <scalelabel position="0">Very annoying</scalelabel>
+                </scales>
+            </interface>
+            <audioelement url="0.wav" id="test-0-A" type="reference" label="A" position="0" />
+            <audioelement url="1.wav" id="test-0-1" />
+            <audioelement url="0.wav" id="test-0-2" />
+        </page>
+    </waet>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/templates/itur1534.xml	Tue Aug 01 15:23:47 2017 +0100
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+    <waet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="test-schema.xsd">
+        <setup interface="MUSHRA" projectReturn="save.php" randomiseOrder="true" loudness="-23">
+            <exitText>Thank you for participating in this listening test. You may now exit the test environment</exitText>
+            <metric>
+                <metricenable>testTimer</metricenable>
+                <metricenable>elementTimer</metricenable>
+                <metricenable>elementInitialPosition</metricenable>
+                <metricenable>elementTracker</metricenable>
+                <metricenable>elementFlagListenedTo</metricenable>
+                <metricenable>elementFlagMoved</metricenable>
+                <metricenable>elementListenTracker</metricenable>
+            </metric>
+            <interface>
+                <interfaceoption type="check" name="fragmentMoved" />
+            </interface>
+        </setup>
+        <page id="training" randomiseOrder="true" repeatCount="0" loop="true" synchronous="true" position="0" label="capital">
+            <interface>
+                <title>Quality</title>
+                <scales>
+                    <scalelabel position="100">Imperceptible</scalelabel>
+                    <scalelabel position="75">Perceptible but not annoying</scalelabel>
+                    <scalelabel position="50">Slightly annoying</scalelabel>
+                    <scalelabel position="25">Annoying</scalelabel>
+                    <scalelabel position="0">Very annoying</scalelabel>
+                </scales>
+            </interface>
+            <audioelement url="0.wav" id="training-0" type="reference" />
+            <audioelement url="1.wav" id="training-1" type="anchor" />
+            <audioelement url="0.wav" id="training-2" />
+        </page>
+        <page id="test-0" randomiseOrder="true" repeatCount="0" loop="true" synchronous="true" position="0" label="capital">
+            <interface>
+                <title>Quality</title>
+                <scales>
+                    <scalelabel position="100">Imperceptible</scalelabel>
+                    <scalelabel position="75">Perceptible but not annoying</scalelabel>
+                    <scalelabel position="50">Slightly annoying</scalelabel>
+                    <scalelabel position="25">Annoying</scalelabel>
+                    <scalelabel position="0">Very annoying</scalelabel>
+                </scales>
+            </interface>
+            <audioelement url="0.wav" id="test-0-0" type="reference" />
+            <audioelement url="1.wav" id="test-0-1" type="anchor" />
+            <audioelement url="0.wav" id="test-0-2" />
+        </page>
+    </waet>
--- a/xml/test-schema.xsd	Thu Jul 27 21:00:36 2017 +0100
+++ b/xml/test-schema.xsd	Tue Aug 01 15:23:47 2017 +0100
@@ -12,14 +12,14 @@
         <xs:attribute name="poolSize" type="xs:nonNegativeInteger" default="0" />
         <xs:attribute name="alwaysInclude" type="xs:boolean" default="false" />
 
-        <xs:attribute name="preSilence">
+        <xs:attribute name="preSilence" default="0.0">
             <xs:simpleType>
                 <xs:restriction base="xs:decimal">
                     <xs:minInclusive value="0.0" />
                 </xs:restriction>
             </xs:simpleType>
         </xs:attribute>
-        <xs:attribute name="postSilence">
+        <xs:attribute name="postSilence" default="0.0">
             <xs:simpleType>
                 <xs:restriction base="xs:decimal">
                     <xs:minInclusive value="0.0" />