Mercurial > hg > webaudioevaluationtool
changeset 2585:b1ba05ef1e7a
Merge branch 'master' into Dev_main
author | Nicholas Jillings <nicholas.jillings@mail.bcu.ac.uk> |
---|---|
date | Thu, 03 Nov 2016 11:04:26 +0000 |
parents | 2b02df54211e (diff) dab27ff8cbfa (current diff) |
children | 42446b5eeee8 |
files | tests/examples/ABX_example.xml |
diffstat | 13 files changed, 1861 insertions(+), 1168 deletions(-) [+] |
line wrap: on
line diff
--- a/css/core.css Thu Nov 03 11:02:56 2016 +0000 +++ b/css/core.css Thu Nov 03 11:04:26 2016 +0000 @@ -3,60 +3,53 @@ */ div.title { - width: 100%; - height: 50px; - margin-bottom: 10px; - font-size: 2em; + width: 100%; + height: 50px; + margin-bottom: 10px; + font-size: 2em; } - div.indicator-box { - position: absolute; - left: 150px; - top: 10px; - width: 300px; - height: 60px; - padding: 20px; - border-radius: 10px; - background-color: rgb(100,200,200); + position: absolute; + left: 150px; + top: 10px; + width: 300px; + height: 60px; + padding: 20px; + border-radius: 10px; + background-color: rgb(100, 200, 200); } - div.comment-div { - border:1px solid #444444; - max-width: 600px; - min-width: 400px; - float: left; - margin: 5px 10px 5px 5px; - height: 90px; + border: 1px solid #444444; + max-width: 600px; + min-width: 400px; + float: left; + margin: 5px 10px 5px 5px; + height: 90px; border-radius: 10px; } - div.comment-div span { - margin-left: 15px; + margin-left: 15px; } - div.popupHolder { - width: 500px; - min-height: 250px; - max-height: 400px; - background-color: #fff; - border-radius: 10px; - box-shadow: 0px 0px 50px #000; - z-index: 10; - position: fixed; + width: 500px; + min-height: 250px; + max-height: 400px; + background-color: #fff; + border-radius: 10px; + box-shadow: 0px 0px 50px #000; + z-index: 10; + position: fixed; } - div#popupContent { margin-top: 20px; margin-bottom: 35px; overflow: auto; } - div#popupContent iframe { width: 100%; border: 0px none; height: 290px; } - div#popupTitleHolder { width: inherit; min-height: 25px; @@ -66,11 +59,9 @@ padding: 8px; text-align: center; } - #popupTitle { white-space: pre-line; } - div#popupResponse { width: inherit; min-height: 50px; @@ -78,20 +69,18 @@ overflow: auto; position: relative; } - button.popupButton { - /* Button for popup window + /* Button for popup window */ - width: 50px; - height: 25px; - position: absolute; - border-radius: 5px; - border: #444; - border-width: 1px; - border-style: solid; - background-color: #fff; + width: 50px; + height: 25px; + position: absolute; + border-radius: 5px; + border: #444; + border-width: 1px; + border-style: solid; + background-color: #fff; } - div.popup-option-checbox { /* Popup window checkbox */ padding: 5px; @@ -99,46 +88,44 @@ width: -moz-fit-content; width: -webkit-fit-content; } - -div.popup-option-checbox input{ +div.popup-option-checbox input { /* Popup window checkbox */ margin-right: 15px; } - table.popup-option-list { margin: auto; } - table.popup-option-list tr { padding: 5px; } - table.popup-option-list tr td { padding: 5px; } - +div.survey-slider-text-holder { + display: flex; + flex-direction: row; + justify-content: space-between; + padding: 0px 15px; +} button#popup-proceed { bottom: 10px; right: 10px; } - button#popup-previous { bottom: 10px; left: 10px; } - div.testHalt { - /* Specify any colouring during the test halt for pre/post questions */ - background-color: rgba(0,0,0,0.5); - /* Don't mess with this bit */ - z-index: 9; - width: 100%; - height: 100%; - position: fixed; - left: 0px; - top: 0px; + /* Specify any colouring during the test halt for pre/post questions */ + background-color: rgba(0, 0, 0, 0.5); + /* Don't mess with this bit */ + z-index: 9; + width: 100%; + height: 100%; + position: fixed; + left: 0px; + top: 0px; } - div#lightbox-root { visibility: hidden; z-index: 20; @@ -146,80 +133,69 @@ min-height: 50px; max-height: 250px; } - div.lightbox-error { margin: 25px; margin-bottom: 50px; padding: 5px; border-radius: 5px; - background-color: rgb(255,220,220); - border: 2px rgb(200,0,0) solid; + background-color: rgb(255, 220, 220); + border: 2px rgb(200, 0, 0) solid; } - div.lightbox-warning { margin: 25px; margin-bottom: 50px; padding: 5px; border-radius: 5px; - background-color: rgb(255,255,220); - border: 2px rgb(255,250,0) solid; + background-color: rgb(255, 255, 220); + border: 2px rgb(255, 250, 0) solid; } - div.lightbox-message { margin: 25px; margin-bottom: 50px; padding: 5px; border-radius: 5px; - background-color: rgb(200,220,255); - border: 2px rgb(50,100,250) solid; + background-color: rgb(200, 220, 255); + border: 2px rgb(50, 100, 250) solid; } - div#lightbox-blanker { visibility: hidden; z-index: 19; } - button.outside-reference { - width:120px; - height:20px; - margin-bottom:5px; - position: absolute; + width: 120px; + height: 20px; + margin-bottom: 5px; + position: absolute; } - textarea.trackComment { - max-width: 594px; - min-width: 350px; - max-height: 60px; + max-width: 594px; + min-width: 350px; + max-height: 60px; resize: none; } - div.playhead { - width: 500px; - height: 50px; - background-color: #eee; - border-radius: 10px; - padding: 10px; + width: 500px; + height: 50px; + background-color: #eee; + border-radius: 10px; + padding: 10px; } - div.playhead-scrub-track { - width: 100%; - height: 10px; - border-style: solid; - border-width: 1px; + width: 100%; + height: 10px; + border-style: solid; + border-width: 1px; } - div#playhead-scrubber { - width: 10px; - height: 10px; - position: relative; - background-color: #000; + width: 10px; + height: 10px; + position: relative; + background-color: #000; } - div.master-volume-holder-inline { width: 100%; padding: 5px; } - div.master-volume-holder-float { position: absolute; top: 20px; @@ -227,15 +203,13 @@ width: 250px%; padding: 5px; } - div#master-volume-root { - margin:auto; + margin: auto; border: black 1px solid; border-radius: 5px; width: 250px; height: 40px; } - input#master-volume-control { width: 200px; height: 25px; @@ -243,14 +217,12 @@ margin: 0px; padding: 0px; } - span#master-volume-feedback { width: 45px; height: 25px; margin-left: 5px; float: left; } - div.error-colour { background-color: #FF8F8F; } @@ -258,13 +230,11 @@ background-color: #FF8F8F; color: black; } - div.calibration-holder { text-align: center; align-content: center; height: auto; } - div.calibration-slider { width: 50px; margin: 2px; @@ -272,12 +242,23 @@ align-content: center; float: left; } - -div.calibration-slider input[type=range][orient=vertical] -{ - writing-mode: bt-lr; /* IE */ - -webkit-appearance: slider-vertical; /* WebKit */ +div.calibration-slider input[type=range][orient=vertical] { + writing-mode: bt-lr; + /* IE */ + -webkit-appearance: slider-vertical; + /* WebKit */ width: 8px; padding: 0 5px; height: 290px; } + +/* Comment Boxes */ + +div.comment-slider-text-holder { + display: flex; + flex-direction: row; + justify-content: space-between; +} +div.comment-slider-text-holder span { + margin: 0px 5px; +}
--- a/interfaces/AB.js Thu Nov 03 11:02:56 2016 +0000 +++ b/interfaces/AB.js Thu Nov 03 11:04:26 2016 +0000 @@ -2,15 +2,15 @@ loadInterface(); function loadInterface() { - // Get the dimensions of the screen available to the page - var width = window.innerWidth; - var height = window.innerHeight; - interfaceContext.insertPoint.innerHTML = ""; // Clear the current schema - - // Custom comparator Object - Interface.prototype.comparator = null; - - Interface.prototype.checkScaleRange = function(min, max) { + // Get the dimensions of the screen available to the page + var width = window.innerWidth; + var height = window.innerHeight; + interfaceContext.insertPoint.innerHTML = ""; // Clear the current schema + + // Custom comparator Object + Interface.prototype.comparator = null; + + Interface.prototype.checkScaleRange = function (min, max) { var page = testState.getCurrentTestPage(); var audioObjects = audioEngineContext.audioObjects; var state = true; @@ -19,166 +19,170 @@ var maxRanking = -Infinity; for (var ao of audioObjects) { var rank = ao.interfaceDOM.getValue(); - if (rank < minRanking) {minRanking = rank;} - if (rank > maxRanking) {maxRanking = rank;} + if (rank < minRanking) { + minRanking = rank; + } + if (rank > maxRanking) { + maxRanking = rank; + } } - if (maxRanking*100 < max) { + if (maxRanking * 100 < max) { str += "At least one fragment must be selected." state = false; } if (!state) { console.log(str); this.storeErrorNode(str); - interfaceContext.lightbox.post("Message",str); + interfaceContext.lightbox.post("Message", str); } return state; } - - // The injection point into the HTML page - interfaceContext.insertPoint = document.getElementById("topLevelBody"); - var testContent = document.createElement('div'); - testContent.id = 'testContent'; - - // Create the top div for the Title element - var titleAttr = specification.title; - var title = document.createElement('div'); - title.className = "title"; - title.align = "center"; - var titleSpan = document.createElement('span'); + + // The injection point into the HTML page + interfaceContext.insertPoint = document.getElementById("topLevelBody"); + var testContent = document.createElement('div'); + testContent.id = 'testContent'; + + // Create the top div for the Title element + var titleAttr = specification.title; + var title = document.createElement('div'); + title.className = "title"; + title.align = "center"; + var titleSpan = document.createElement('span'); titleSpan.id = "test-title"; - - // Set title to that defined in XML, else set to default - if (titleAttr != undefined) { - titleSpan.textContent = titleAttr; - } else { - titleSpan.textContent = 'Listening test'; - } - // Insert the titleSpan element into the title div element. - title.appendChild(titleSpan); - - var pagetitle = document.createElement('div'); - pagetitle.className = "pageTitle"; - pagetitle.align = "center"; - var titleSpan = document.createElement('span'); - titleSpan.id = "pageTitle"; - pagetitle.appendChild(titleSpan); - - // Create Interface buttons! - var interfaceButtons = document.createElement('div'); - interfaceButtons.id = 'interface-buttons'; - interfaceButtons.style.height = '25px'; - - // Create playback start/stop points - var playback = document.createElement("button"); - playback.innerHTML = 'Stop'; - playback.id = 'playback-button'; - playback.style.float = 'left'; - // onclick function. Check if it is playing or not, call the correct function in the - // audioEngine, change the button text to reflect the next state. - playback.onclick = function() { - if (audioEngineContext.status == 1) { - audioEngineContext.stop(); - this.innerHTML = 'Stop'; + + // Set title to that defined in XML, else set to default + if (titleAttr != undefined) { + titleSpan.textContent = titleAttr; + } else { + titleSpan.textContent = 'Listening test'; + } + // Insert the titleSpan element into the title div element. + title.appendChild(titleSpan); + + var pagetitle = document.createElement('div'); + pagetitle.className = "pageTitle"; + pagetitle.align = "center"; + var titleSpan = document.createElement('span'); + titleSpan.id = "pageTitle"; + pagetitle.appendChild(titleSpan); + + // Create Interface buttons! + var interfaceButtons = document.createElement('div'); + interfaceButtons.id = 'interface-buttons'; + interfaceButtons.style.height = '25px'; + + // Create playback start/stop points + var playback = document.createElement("button"); + playback.innerHTML = 'Stop'; + playback.id = 'playback-button'; + playback.style.float = 'left'; + // onclick function. Check if it is playing or not, call the correct function in the + // audioEngine, change the button text to reflect the next state. + playback.onclick = function () { + if (audioEngineContext.status == 1) { + audioEngineContext.stop(); + this.innerHTML = 'Stop'; var time = audioEngineContext.timer.getTestTime(); console.log('Stopped at ' + time); // DEBUG/SAFETY - } - }; - // Append the interface buttons into the interfaceButtons object. - interfaceButtons.appendChild(playback); - - // Global parent for the comment boxes on the page - var feedbackHolder = document.createElement('div'); - feedbackHolder.id = 'feedbackHolder'; - + } + }; + // Append the interface buttons into the interfaceButtons object. + interfaceButtons.appendChild(playback); + + // Global parent for the comment boxes on the page + var feedbackHolder = document.createElement('div'); + feedbackHolder.id = 'feedbackHolder'; + // Create outside reference holder var outsideRef = document.createElement("div"); outsideRef.id = "outside-reference-holder"; - - // Construct the AB Boxes - var boxes = document.createElement('div'); - boxes.align = "center"; - boxes.id = "box-holders"; + + // Construct the AB Boxes + var boxes = document.createElement('div'); + boxes.align = "center"; + boxes.id = "box-holders"; boxes.style.float = "left"; - - var submit = document.createElement('button'); - submit.id = "submit"; - submit.onclick = buttonSubmitClick; - submit.className = "big-button"; - submit.textContent = "submit"; - submit.style.position = "relative"; - submit.style.left = (window.innerWidth-250)/2 + 'px'; - - feedbackHolder.appendChild(boxes); - + + var submit = document.createElement('button'); + submit.id = "submit"; + submit.onclick = buttonSubmitClick; + submit.className = "big-button"; + submit.textContent = "submit"; + submit.style.position = "relative"; + submit.style.left = (window.innerWidth - 250) / 2 + 'px'; + + feedbackHolder.appendChild(boxes); + // Create holder for comment boxes var comments = document.createElement("div"); comments.id = "comment-box-holder"; - - // Inject into HTML - testContent.appendChild(title); // Insert the title - testContent.appendChild(pagetitle); - testContent.appendChild(interfaceButtons); + + // Inject into HTML + testContent.appendChild(title); // Insert the title + testContent.appendChild(pagetitle); + testContent.appendChild(interfaceButtons); testContent.appendChild(outsideRef); - testContent.appendChild(feedbackHolder); - testContent.appendChild(submit); + testContent.appendChild(feedbackHolder); + testContent.appendChild(submit); testContent.appendChild(comments); - interfaceContext.insertPoint.appendChild(testContent); + interfaceContext.insertPoint.appendChild(testContent); - // Load the full interface - testState.initialise(); - testState.advanceState(); + // Load the full interface + testState.initialise(); + testState.advanceState(); } -function loadTest(audioHolderObject) -{ - var feedbackHolder = document.getElementById('feedbackHolder'); - var interfaceObj = audioHolderObject.interfaces; - if (interfaceObj.length > 1) - { - console.log("WARNING - This interface only supports one <interface> node per page. Using first interface node"); - } - interfaceObj = interfaceObj[0]; - +function loadTest(audioHolderObject) { + var feedbackHolder = document.getElementById('feedbackHolder'); + var interfaceObj = audioHolderObject.interfaces; + if (interfaceObj.length > 1) { + console.log("WARNING - This interface only supports one <interface> node per page. Using first interface node"); + } + interfaceObj = interfaceObj[0]; + var commentHolder = document.getElementById('comment-box-holder'); commentHolder.innerHTML = ""; - + // Delete outside reference - var outsideReferenceHolder = document.getElementById("outside-reference-holder"); + var outsideReferenceHolder = document.getElementById("outside-reference-holder"); outsideReferenceHolder.innerHTML = ""; - + // Set the page title if (typeof audioHolderObject.title == "string" && audioHolderObject.title.length > 0) { document.getElementById("test-title").textContent = audioHolderObject.title } - - if(interfaceObj.title != null) - { - document.getElementById("pageTitle").textContent = interfaceObj.title; - } - + + if (interfaceObj.title != null) { + document.getElementById("pageTitle").textContent = interfaceObj.title; + } + var interfaceOptions = specification.interfaces.options.concat(interfaceObj.options); // Clear the interfaceElements { var node = document.getElementById('playback-holder'); - if (node){feedbackHolder.removeChild(node);} + if (node) { + feedbackHolder.removeChild(node); + } node = document.getElementById('page-count'); - if (node){document.getElementById('interface-buttons').removeChild(node);} + if (node) { + document.getElementById('interface-buttons').removeChild(node); + } node = document.getElementById('master-volume-holder-float'); - if (node){feedbackHolder.removeChild(node);} + if (node) { + feedbackHolder.removeChild(node); + } } - + // Populate the comparator object - interfaceContext.comparator = new comparator(audioHolderObject); - - for (var option of interfaceOptions) - { - if (option.type == "show") - { - switch(option.name) { + interfaceContext.comparator = new comparator(audioHolderObject); + + for (var option of interfaceOptions) { + if (option.type == "show") { + switch (option.name) { case "playhead": var playbackHolder = document.getElementById('playback-holder'); - if (playbackHolder == null) - { + if (playbackHolder == null) { playbackHolder = document.createElement('div'); playbackHolder.id = 'playback-holder'; playbackHolder.style.width = "100%"; @@ -190,309 +194,329 @@ break; case "page-count": var pagecountHolder = document.getElementById('page-count'); - if (pagecountHolder == null) - { + if (pagecountHolder == null) { pagecountHolder = document.createElement('div'); pagecountHolder.id = 'page-count'; document.getElementById('interface-buttons').appendChild(pagecountHolder); } - pagecountHolder.innerHTML = '<span>Page '+(testState.stateIndex+1)+' of '+testState.stateMap.length+'</span>'; + pagecountHolder.innerHTML = '<span>Page ' + (testState.stateIndex + 1) + ' of ' + testState.stateMap.length + '</span>'; break; case "volume": - if (document.getElementById('master-volume-holder-float') == null) - { + if (document.getElementById('master-volume-holder-float') == null) { feedbackHolder.appendChild(interfaceContext.volume.object); } break; case "comments": // Generate one comment box per presented page - for (var element of audioEngineContext.audioObjects) - { + for (var element of audioEngineContext.audioObjects) { interfaceContext.commentBoxes.createCommentBox(element); } - interfaceContext.commentBoxes.showCommentBoxes(commentHolder,true); + interfaceContext.commentBoxes.showCommentBoxes(commentHolder, true); break; } } } - - $(audioHolderObject.commentQuestions).each(function(index,element) { - var node = interfaceContext.createCommentQuestion(element); - commentHolder.appendChild(node.holder); - }); - - resizeWindow(null); + + $(audioHolderObject.commentQuestions).each(function (index, element) { + var node = interfaceContext.createCommentQuestion(element); + commentHolder.appendChild(node.holder); + }); + + resizeWindow(null); } -function comparator(audioHolderObject) -{ - this.comparatorBox = function(audioElement,id,text) - { - this.parent = audioElement; - this.id = id; - this.value = 0; - this.disabled = true; - this.box = document.createElement('div'); - this.box.className = 'comparator-holder'; - this.box.setAttribute('track-id',audioElement.id); - this.box.id = 'comparator-'+text; - this.selector = document.createElement('div'); - this.selector.className = 'comparator-selector disabled'; - var selectorText = document.createElement('span'); - selectorText.textContent = text; - this.selector.appendChild(selectorText); - this.playback = document.createElement('button'); - this.playback.className = 'comparator-button'; - this.playback.disabled = true; - this.playback.textContent = "Listen"; - this.box.appendChild(this.selector); - this.box.appendChild(this.playback); - this.selector.onclick = function(event) - { - var time = audioEngineContext.timer.getTestTime(); - if ($(event.currentTarget).hasClass('disabled')) - { - console.log("Please wait until sample has loaded"); - return; - } - if (audioEngineContext.status == 0) - { - interfaceContext.lightbox.post("Message","Please listen to the samples before making a selection"); - console.log("Please listen to the samples before making a selection"); - return; +function comparator(audioHolderObject) { + this.comparatorBox = function (audioElement, id, text) { + this.parent = audioElement; + this.id = id; + this.value = 0; + this.disabled = true; + this.box = document.createElement('div'); + this.box.className = 'comparator-holder'; + this.box.setAttribute('track-id', audioElement.id); + this.box.id = 'comparator-' + text; + this.selector = document.createElement('div'); + this.selector.className = 'comparator-selector disabled'; + var selectorText = document.createElement('span'); + selectorText.textContent = text; + this.selector.appendChild(selectorText); + this.playback = document.createElement('button'); + this.playback.className = 'comparator-button'; + this.playback.disabled = true; + this.playback.textContent = "Listen"; + this.box.appendChild(this.selector); + this.box.appendChild(this.playback); + this.selector.onclick = function (event) { + var time = audioEngineContext.timer.getTestTime(); + if ($(event.currentTarget).hasClass('disabled')) { + console.log("Please wait until sample has loaded"); + return; } - var id = event.currentTarget.parentElement.getAttribute('track-id'); - interfaceContext.comparator.selected = id; + if (audioEngineContext.status == 0) { + interfaceContext.lightbox.post("Message", "Please listen to the samples before making a selection"); + console.log("Please listen to the samples before making a selection"); + return; + } + var id = event.currentTarget.parentElement.getAttribute('track-id'); + interfaceContext.comparator.selected = id; if ($(event.currentTarget).hasClass("selected")) { $(".comparator-selector").removeClass('selected'); - for (var i=0; i<interfaceContext.comparator.comparators.length; i++) - { + for (var i = 0; i < interfaceContext.comparator.comparators.length; i++) { var obj = interfaceContext.comparator.comparators[i]; - obj.parent.metric.moved(time,0); + obj.parent.metric.moved(time, 0); obj.value = 0; } } else { $(".comparator-selector").removeClass('selected'); $(event.currentTarget).addClass('selected'); - for (var i=0; i<interfaceContext.comparator.comparators.length; i++) - { + for (var i = 0; i < interfaceContext.comparator.comparators.length; i++) { var obj = interfaceContext.comparator.comparators[i]; if (i == id) { obj.value = 1; } else { obj.value = 0; } - obj.parent.metric.moved(time,obj.value); + obj.parent.metric.moved(time, obj.value); } - console.log("Selected "+id+' ('+time+')'); + console.log("Selected " + id + ' (' + time + ')'); } - }; - this.playback.setAttribute("playstate","ready"); - this.playback.onclick = function(event) - { - var id = event.currentTarget.parentElement.getAttribute('track-id'); - if (event.currentTarget.getAttribute("playstate") == "ready") - { + }; + this.playback.setAttribute("playstate", "ready"); + this.playback.onclick = function (event) { + var id = event.currentTarget.parentElement.getAttribute('track-id'); + if (event.currentTarget.getAttribute("playstate") == "ready") { audioEngineContext.play(id); } else if (event.currentTarget.getAttribute("playstate") == "playing") { audioEngineContext.stop(); } - - }; - - this.enable = function() - { - if (this.parent.state == 1) - { - $(this.selector).removeClass('disabled'); - this.playback.disabled = false; - } - }; - this.updateLoading = function(progress) - { - if (progress != 100) - { - progress = String(progress); - progress = progress.split('.')[0]; - this.playback.textContent = progress+'%'; - } else { - this.playback.textContent = "Play"; - } - }; - this.error = function() { + + }; + + this.enable = function () { + if (this.parent.state == 1) { + $(this.selector).removeClass('disabled'); + this.playback.disabled = false; + } + }; + this.updateLoading = function (progress) { + if (progress != 100) { + progress = String(progress); + progress = progress.split('.')[0]; + this.playback.textContent = progress + '%'; + } else { + this.playback.textContent = "Play"; + } + }; + this.error = function () { // audioObject has an error!! this.playback.textContent = "Error"; $(this.playback).addClass("error-colour"); } - this.startPlayback = function() - { + this.startPlayback = function () { if (this.parent.specification.parent.playOne || specification.playOne) { $('.comparator-button').text('Wait'); - $('.comparator-button').attr("disabled","true"); - $(this.playback).removeAttr("disabled"); + $('.comparator-button').attr("disabled", "true"); + $(this.playback).css("disabled", "false"); } else { $('.comparator-button').text('Listen'); } $(this.playback).text('Stop'); - this.playback.setAttribute("playstate","playing"); + this.playback.setAttribute("playstate", "playing"); }; - this.stopPlayback = function() - { + this.stopPlayback = function () { if (this.playback.getAttribute("playstate") == "playing") { $('.comparator-button').text('Listen'); $('.comparator-button').removeAttr("disabled"); - this.playback.setAttribute("playstate","ready"); + this.playback.setAttribute("playstate", "ready"); } }; - this.exportXMLDOM = function(audioObject) - { - var node = storage.document.createElement('value'); - node.textContent = this.value; - return node; - }; - this.getValue = function() { - return this.value; - }; - this.getPresentedId = function() - { - return this.selector.children[0].textContent; - }; - this.canMove = function() - { - return false; - }; - }; - - this.boxHolders = document.getElementById('box-holders'); - this.boxHolders.innerHTML = ""; - this.comparators = []; - this.selected = null; - - // First generate the Audio Objects for the Audio Engine - for (var index=0; index<audioHolderObject.audioElements.length; index++) - { - var element = audioHolderObject.audioElements[index]; + this.exportXMLDOM = function (audioObject) { + var node = storage.document.createElement('value'); + node.textContent = this.value; + return node; + }; + this.getValue = function () { + return this.value; + }; + this.getPresentedId = function () { + return this.selector.children[0].textContent; + }; + this.canMove = function () { + return false; + }; + }; + + this.boxHolders = document.getElementById('box-holders'); + this.boxHolders.innerHTML = ""; + this.comparators = []; + this.selected = null; + + // First generate the Audio Objects for the Audio Engine + var label = audioHolderObject.labelStart; + if (label == "") { + switch (audioHolderObject.label) { + case "number": + label = "1"; + break; + case "letter": + label = "a"; + break; + case "none": + label = ""; + break; + case "capital": + default: + label = "A"; + break; + } + } else { + switch (audioHolderObject.label) { + case "number": + if (!isFinite(Number(label))) { + label = "1"; + } + break; + case "letter": + if (label.charCodeAt(0) < 97 || label.charCodeAt(0) > 122) { + label = "a"; + } + break; + case "none": + label = ""; + break; + case "capital": + default: + if (label.charCodeAt(0) < 65 || label.charCodeAt(0) > 90) { + label = "A"; + } + break; + } + } + for (var index = 0; index < audioHolderObject.audioElements.length; index++) { + var element = audioHolderObject.audioElements[index]; var audioObject = audioEngineContext.newTrack(element); - if (index == audioHolderObject.outsideReference || element.type == 'outside-reference') - { - var orNode = new interfaceContext.outsideReferenceDOM(audioObject,index,document.getElementById("outside-reference-holder")); - audioObject.bindInterface(orNode); + if (index == audioHolderObject.outsideReference || element.type == 'outside-reference') { + var orNode = new interfaceContext.outsideReferenceDOM(audioObject, index, document.getElementById("outside-reference-holder")); + audioObject.bindInterface(orNode); } else { - var label; - switch(audioObject.specification.parent.label) { + var node = new this.comparatorBox(audioObject, index, label); + switch (audioHolderObject.label) { case "none": label = ""; break; case "number": - label = ""+index; + label = (Number(label) + 1).toString(10); break; case "letter": - label = String.fromCharCode(97 + index); + label = String.fromCharCode((label.charCodeAt(0) - 96) % 26 + 97); break; + case "capital": default: - label = String.fromCharCode(65 + index); + label = String.fromCharCode((label.charCodeAt(0) - 64) % 26 + 65); break; } - var node = new this.comparatorBox(audioObject,index,label); audioObject.bindInterface(node); this.comparators.push(node); this.boxHolders.appendChild(node.box); } - } - return this; + } + return this; } -function resizeWindow(event) -{ - document.getElementById('submit').style.left = (window.innerWidth-250)/2 + 'px'; - var numObj = interfaceContext.comparator.comparators.length; - var boxW = numObj*312; +function resizeWindow(event) { + document.getElementById('submit').style.left = (window.innerWidth - 250) / 2 + 'px'; + var numObj = interfaceContext.comparator.comparators.length; + var boxW = numObj * 312; var diff = window.innerWidth - boxW; - while (diff < 0) - { - numObj = Math.ceil(numObj/2); - boxW = numObj*312; + while (diff < 0) { + numObj = Math.ceil(numObj / 2); + boxW = numObj * 312; diff = window.innerWidth - boxW; } - document.getElementById('box-holders').style.marginLeft = diff/2 + 'px'; - document.getElementById('box-holders').style.marginRight = diff/2 + 'px'; + document.getElementById('box-holders').style.marginLeft = diff / 2 + 'px'; + document.getElementById('box-holders').style.marginRight = diff / 2 + 'px'; document.getElementById('box-holders').style.width = boxW + 'px'; - + var outsideRef = document.getElementById('outside-reference'); - if(outsideRef != null) - { - outsideRef.style.left = (window.innerWidth-120)/2 + 'px'; - } + if (outsideRef != null) { + outsideRef.style.left = (window.innerWidth - 120) / 2 + 'px'; + } } -function buttonSubmitClick() -{ - var checks = []; - checks = checks.concat(testState.currentStateMap.interfaces[0].options); - checks = checks.concat(specification.interfaces.options); - var canContinue = true; - - for (var i=0; i<checks.length; i++) { - if (checks[i].type == 'check') - { - switch(checks[i].name) { - case 'fragmentPlayed': - // Check if all fragments have been played - var checkState = interfaceContext.checkAllPlayed(); - if (checkState == false) {canContinue = false;} - break; - case 'fragmentFullPlayback': - // Check all fragments have been played to their full length - var checkState = interfaceContext.checkFragmentsFullyPlayed(); - if (checkState == false) {canContinue = false;} - break; - case 'fragmentMoved': - // Check all fragment sliders have been moved. - var checkState = interfaceContext.checkAllMoved(); - if (checkState == false) {canContinue = false;} - break; - case 'fragmentComments': - // Check all fragment sliders have been moved. - var checkState = interfaceContext.checkAllCommented(); - if (checkState == false) {canContinue = false;} - break; - case 'scalerange': - // Check the scale has been used effectively - var checkState = interfaceContext.checkScaleRange(checks[i].min,checks[i].max); - if (checkState == false) {canContinue = false;} - break; - default: - console.log("WARNING - Check option "+checks[i].check+" is not supported on this interface"); - break; - } +function buttonSubmitClick() { + var checks = []; + checks = checks.concat(testState.currentStateMap.interfaces[0].options); + checks = checks.concat(specification.interfaces.options); + var canContinue = true; - } - if (!canContinue) {break;} - } - if (canContinue) - { - if (audioEngineContext.status == 1) { - var playback = document.getElementById('playback-button'); - playback.click(); - // This function is called when the submit button is clicked. Will check for any further tests to perform, or any post-test options - } else - { - if (audioEngineContext.timer.testStarted == false) - { - interfaceContext.lightbox.post("Warning",'You have not started the test! Please click play on a sample to begin the test!'); - return; - } - } - testState.advanceState(); - } + for (var i = 0; i < checks.length; i++) { + if (checks[i].type == 'check') { + switch (checks[i].name) { + case 'fragmentPlayed': + // Check if all fragments have been played + var checkState = interfaceContext.checkAllPlayed(); + if (checkState == false) { + canContinue = false; + } + break; + case 'fragmentFullPlayback': + // Check all fragments have been played to their full length + var checkState = interfaceContext.checkFragmentsFullyPlayed(); + if (checkState == false) { + canContinue = false; + } + break; + case 'fragmentMoved': + // Check all fragment sliders have been moved. + var checkState = interfaceContext.checkAllMoved(); + if (checkState == false) { + canContinue = false; + } + break; + case 'fragmentComments': + // Check all fragment sliders have been moved. + var checkState = interfaceContext.checkAllCommented(); + if (checkState == false) { + canContinue = false; + } + break; + case 'scalerange': + // Check the scale has been used effectively + var checkState = interfaceContext.checkScaleRange(checks[i].min, checks[i].max); + if (checkState == false) { + canContinue = false; + } + break; + default: + console.log("WARNING - Check option " + checks[i].check + " is not supported on this interface"); + break; + } + + } + if (!canContinue) { + break; + } + } + if (canContinue) { + if (audioEngineContext.status == 1) { + var playback = document.getElementById('playback-button'); + playback.click(); + // This function is called when the submit button is clicked. Will check for any further tests to perform, or any post-test options + } else { + if (audioEngineContext.timer.testStarted == false) { + interfaceContext.lightbox.post("Warning", 'You have not started the test! Please click play on a sample to begin the test!'); + return; + } + } + testState.advanceState(); + } } -function pageXMLSave(store, pageSpecification) -{ - // MANDATORY - // Saves a specific test page - // You can use this space to add any extra nodes to your XML <audioHolder> saves - // Get the current <page> information in store (remember to appendChild your data to it) - // pageSpecification is the current page node configuration - // To create new XML nodes, use storage.document.createElement(); -} \ No newline at end of file +function pageXMLSave(store, pageSpecification) { + // MANDATORY + // Saves a specific test page + // You can use this space to add any extra nodes to your XML <audioHolder> saves + // Get the current <page> information in store (remember to appendChild your data to it) + // pageSpecification is the current page node configuration + // To create new XML nodes, use storage.document.createElement(); +}
--- a/interfaces/timeline.js Thu Nov 03 11:02:56 2016 +0000 +++ b/interfaces/timeline.js Thu Nov 03 11:04:26 2016 +0000 @@ -506,7 +506,7 @@ break; } if (checkState == false) { - canContinue == false; + canContinue = false; } } if (!canContinue) {
--- a/js/core.js Thu Nov 03 11:02:56 2016 +0000 +++ b/js/core.js Thu Nov 03 11:04:26 2016 +0000 @@ -799,13 +799,13 @@ } else if (node.specification.type == 'number') { var input = document.createElement('input'); input.type = 'textarea'; - if (node.min != null) { + if (node.specification.min != null) { input.min = node.specification.min; } - if (node.max != null) { + if (node.specification.max != null) { input.max = node.specification.max; } - if (node.step != null) { + if (node.specification.step != null) { input.step = node.specification.step; } if (node.response != undefined) { @@ -823,6 +823,30 @@ iframe.className = "youtube"; iframe.src = node.specification.url; this.popupResponse.appendChild(iframe); + } else if (node.specification.type == "slider") { + var hold = document.createElement('div'); + var input = document.createElement('input'); + input.type = 'range'; + input.style.width = "90%"; + if (node.specification.min != null) { + input.min = node.specification.min; + } + if (node.specification.max != null) { + input.max = node.specification.max; + } + if (node.response != undefined) { + input.value = node.response; + } + hold.className = "survey-slider-text-holder"; + var minText = document.createElement('span'); + var maxText = document.createElement('span'); + minText.textContent = node.specification.leftText; + maxText.textContent = node.specification.rightText; + hold.appendChild(minText); + hold.appendChild(maxText); + this.popupResponse.appendChild(input); + this.popupResponse.appendChild(hold); + this.popupResponse.style.textAlign = "center"; } if (this.currentIndex + 1 == this.popupOptions.length) { if (this.node.location == "pre") { @@ -922,6 +946,33 @@ console.log("Checkbox: " + node.specification.statement); var inputs = this.popupResponse.getElementsByTagName('input'); node.response = []; + var numChecked = 0; + for (var i = 0; i < node.specification.options.length; i++) { + if (inputs[i].checked) { + numChecked++; + } + } + if (node.specification.min != undefined) { + if (node.specification.max == undefined) { + if (numChecked < node.specification.min) { + var msg = "You must select at least " + node.specification.min + " option"; + if (node.specification.min > 1) { + msg += "s"; + } + interfaceContext.lightbox.post("Error", msg); + return; + } + } else { + if (numChecked < node.specification.min || numChecked > node.specification.max) { + if (node.specification.min == node.specification.max) { + interfaceContext.lightbox.post("Error", "You must only select " + node.specification.min); + } else { + interfaceContext.lightbox.post("Error", "You must select between " + node.specification.min + " and " + node.specification.max); + } + return; + } + } + } for (var i = 0; i < node.specification.options.length; i++) { node.response.push({ name: node.specification.options[i].name, @@ -1080,6 +1131,49 @@ break; } } + } else if (node.specification.type == 'slider') { + var input = this.popupContent.getElementsByTagName('input')[0]; + node.response = input.value; + for (var condition of node.specification.conditions) { + var pass = false; + switch (condition.check) { + case "contains": + console.log("Survey Element of type 'number' cannot interpret contains conditions. IGNORING"); + break; + case "greaterThan": + if (node.response > Number(condition.value)) { + pass = true; + } + break; + case "lessThan": + if (node.response < Number(condition.value)) { + pass = true; + } + break; + case "equals": + if (node.response == condition.value) { + pass = true; + } + break; + } + var jumpID; + if (pass) { + jumpID = condition.jumpToOnPass; + } else { + jumpID = condition.jumpToOnFail; + } + if (jumpID != undefined) { + var index = this.popupOptions.findIndex(function (item, index, element) { + if (item.specification.id == jumpID) { + return true; + } else { + return false; + } + }, this); + this.currentIndex = index - 1; + break; + } + } } this.currentIndex++; if (this.currentIndex < this.popupOptions.length) { @@ -1695,21 +1789,20 @@ }; this.setSynchronousLoop = function () { - // Pads the signals so they are all exactly the same length - // Get the length of the longest signal. - var length = 0; + // Pads the signals so they are all exactly the same duration + // Get the duration of the longest signal. + var duration = 0; var maxId; for (var i = 0; i < this.audioObjects.length; i++) { - if (length < this.audioObjects[i].buffer.buffer.length) { - length = this.audioObjects[i].buffer.buffer.length; + if (duration < this.audioObjects[i].buffer.buffer.duration) { + duration = this.audioObjects[i].buffer.buffer.duration; maxId = i; } } // Extract the audio and zero-pad for (var ao of this.audioObjects) { - var lengthDiff = length - ao.buffer.buffer.length; - if (lengthDiff > 0) { - ao.buffer.buffer = ao.buffer.copyBuffer(0, samplesToSeconds(lengthDiff, ao.buffer.buffer.sampleRate)); + if (ao.buffer.buffer.duration !== duration) { + ao.buffer.buffer = ao.buffer.copyBuffer(0, duration - ao.buffer.buffer.duration); } } }; @@ -2604,6 +2697,63 @@ this.resize(); }; + this.sliderBox = function (commentQuestion) { + this.specification = commentQuestion; + this.holder = document.createElement("div"); + this.holder.className = 'comment-div'; + this.string = document.createElement("span"); + this.string.innerHTML = commentQuestion.statement; + this.slider = document.createElement("input"); + this.slider.type = "range"; + this.slider.min = commentQuestion.min; + this.slider.max = commentQuestion.max; + this.slider.step = commentQuestion.step; + this.slider.value = commentQuestion.value; + var br = document.createElement('br'); + + var textHolder = document.createElement("div"); + textHolder.className = "comment-slider-text-holder"; + + this.leftText = document.createElement("span"); + this.leftText.textContent = commentQuestion.leftText; + this.rightText = document.createElement("span"); + this.rightText.textContent = commentQuestion.rightText; + textHolder.appendChild(this.leftText); + textHolder.appendChild(this.rightText); + + this.holder.appendChild(this.string); + this.holder.appendChild(br); + this.holder.appendChild(this.slider); + this.holder.appendChild(textHolder); + + this.exportXMLDOM = function (storePoint) { + var root = storePoint.parent.document.createElement('comment'); + root.id = this.specification.id; + root.setAttribute('type', this.specification.type); + console.log("Question: " + this.string.textContent); + console.log("Response: " + this.slider.value); + var question = storePoint.parent.document.createElement('question'); + question.textContent = this.string.textContent; + var response = storePoint.parent.document.createElement('response'); + response.textContent = this.slider.value; + root.appendChild(question); + root.appendChild(response); + storePoint.XMLDOM.appendChild(root); + return root; + }; + this.resize = function () { + var boxwidth = (window.innerWidth - 100) / 2; + if (boxwidth >= 600) { + boxwidth = 600; + } else if (boxwidth < 400) { + boxwidth = 400; + } + this.holder.style.width = boxwidth + "px"; + this.slider.style.width = boxwidth - 24 + "px"; + }; + this.resize(); + }; + this.createCommentQuestion = function (element) { var node; if (element.type == 'question') { @@ -2612,6 +2762,8 @@ node = new this.radioBox(element); } else if (element.type == 'checkbox') { node = new this.checkboxBox(element); + } else if (element.type == 'slider') { + node = new this.sliderBox(element); } this.commentQuestions.push(node); return node; @@ -3242,14 +3394,17 @@ switch (node.specification.type) { case "number": case "question": + case "slider": var child = this.parent.document.createElement('response'); child.textContent = node.response; surveyresult.appendChild(child); break; case "radio": var child = this.parent.document.createElement('response'); - child.setAttribute('name', node.response.name); - child.textContent = node.response.text; + if (node.response !== null) { + child.setAttribute('name', node.response.name); + child.textContent = node.response.text; + } surveyresult.appendChild(child); break; case "checkbox":
--- a/js/specification.js Thu Nov 03 11:02:56 2016 +0000 +++ b/js/specification.js Thu Nov 03 11:04:26 2016 +0000 @@ -1,12 +1,12 @@ function Specification() { - // Handles the decoding of the project specification XML into a simple JavaScript Object. - + // Handles the decoding of the project specification XML into a simple JavaScript Object. + // <setup> attributes - this.interface = null; - this.projectReturn = null; + this.interface = null; + this.projectReturn = null; this.returnURL = null; - this.randomiseOrder = null; - this.poolSize = null; + this.randomiseOrder = null; + this.poolSize = null; this.loudness = null; this.sampleRate = null; this.calibration = null; @@ -14,32 +14,31 @@ this.preSilence = null; this.postSilence = null; this.playOne = null; - + // nodes this.metrics = null; this.preTest = undefined; this.postTest = undefined; - this.pages = []; - this.interfaces = null; - this.errors = []; - this.schema = null; + this.pages = []; + this.interfaces = null; + this.errors = []; + this.schema = null; this.exitText = "Thank you."; - - this.processAttribute = function(attribute,schema,schemaRoot) - { - // attribute is the string returned from getAttribute on the XML - // schema is the <xs:attribute> node - if (schema.getAttribute('name') == undefined && schema.getAttribute('ref') != undefined) - { - schema = schemaRoot.getAllElementsByName(schema.getAttribute('ref'))[0]; - } - var defaultOpt = schema.getAttribute('default'); - if (attribute == null) { - attribute = defaultOpt; - } - var dataType = schema.getAttribute('type'); - if (typeof dataType == "string") { dataType = dataType.substr(3);} - else { + + this.processAttribute = function (attribute, schema, schemaRoot) { + // attribute is the string returned from getAttribute on the XML + // schema is the <xs:attribute> node + if (schema.getAttribute('name') == undefined && schema.getAttribute('ref') != undefined) { + schema = schemaRoot.getAllElementsByName(schema.getAttribute('ref'))[0]; + } + var defaultOpt = schema.getAttribute('default'); + if (attribute == null) { + attribute = defaultOpt; + } + var dataType = schema.getAttribute('type'); + if (typeof dataType == "string") { + dataType = dataType.substr(3); + } else { var rest = schema.getAllElementsByTagName("xs:restriction").concat(schema.getAllElementsByTagName("xs:enumeration")); if (rest.length > 0) { dataType = rest[0].getAttribute("base"); @@ -52,129 +51,122 @@ dataType = "string"; } } - if (attribute == null) - { - return attribute; - } - switch(dataType) - { - case "boolean": - if (attribute == 'true'){attribute = true;}else{attribute=false;} - break; - case "negativeInteger": - case "positiveInteger": - case "nonNegativeInteger": - case "nonPositiveInteger": - case "integer": - case "decimal": - case "short": - attribute = Number(attribute); - break; - case "string": - default: - attribute = String(attribute); - break; - } - return attribute; - }; - - this.decode = function(projectXML) { - this.errors = []; - // projectXML - DOM Parsed document - this.projectXML = projectXML.childNodes[0]; - var setupNode = projectXML.getElementsByTagName('setup')[0]; - var schemaSetup = this.schema.getAllElementsByName('setup')[0]; - // First decode the attributes - var attributes = schemaSetup.getAllElementsByTagName('xs:attribute'); - for (var i=0; i<attributes.length; i++) - { - var attributeName = attributes[i].getAttribute('name') || attributes[i].getAttribute('ref'); - var projectAttr = setupNode.getAttribute(attributeName); - projectAttr = this.processAttribute(projectAttr,attributes[i],this.schema); - switch(typeof projectAttr) - { - case "number": - case "boolean": - eval('this.'+attributeName+' = '+projectAttr); - break; - case "string": - eval('this.'+attributeName+' = "'+projectAttr+'"'); - break; - } - - } - + if (attribute == null) { + return attribute; + } + switch (dataType) { + case "boolean": + if (attribute == 'true') { + attribute = true; + } else { + attribute = false; + } + break; + case "negativeInteger": + case "positiveInteger": + case "nonNegativeInteger": + case "nonPositiveInteger": + case "integer": + case "decimal": + case "short": + attribute = Number(attribute); + break; + case "string": + default: + attribute = String(attribute); + break; + } + return attribute; + }; + + this.decode = function (projectXML) { + this.errors = []; + // projectXML - DOM Parsed document + this.projectXML = projectXML.childNodes[0]; + var setupNode = projectXML.getElementsByTagName('setup')[0]; + var schemaSetup = this.schema.getAllElementsByName('setup')[0]; + // First decode the attributes + var attributes = schemaSetup.getAllElementsByTagName('xs:attribute'); + for (var i = 0; i < attributes.length; i++) { + var attributeName = attributes[i].getAttribute('name') || attributes[i].getAttribute('ref'); + var projectAttr = setupNode.getAttribute(attributeName); + projectAttr = this.processAttribute(projectAttr, attributes[i], this.schema); + switch (typeof projectAttr) { + case "number": + case "boolean": + eval('this.' + attributeName + ' = ' + projectAttr); + break; + case "string": + eval('this.' + attributeName + ' = "' + projectAttr + '"'); + break; + } + + } + var exitTextNode = setupNode.getElementsByTagName('exitText'); if (exitTextNode.length == 1) { this.exitText = exitTextNode[0].textContent; } - - this.metrics = new this.metricNode(); - - this.metrics.decode(this,setupNode.getElementsByTagName('metric')[0]); - - // Now process the survey node options - var survey = setupNode.getElementsByTagName('survey'); - for (var i=0; i<survey.length; i++){ - var location = survey[i].getAttribute('location'); - switch(location) - { + + this.metrics = new this.metricNode(); + + this.metrics.decode(this, setupNode.getElementsByTagName('metric')[0]); + + // Now process the survey node options + var survey = setupNode.getElementsByTagName('survey'); + for (var i = 0; i < survey.length; i++) { + var location = survey[i].getAttribute('location'); + switch (location) { case 'pre': case 'before': - this.preTest = new this.surveyNode(this); - this.preTest.decode(this,survey[i]); + this.preTest = new this.surveyNode(this); + this.preTest.decode(this, survey[i]); break; case 'post': case 'after': this.postTest = new this.surveyNode(this); - this.postTest.decode(this,survey[i]); + this.postTest.decode(this, survey[i]); break; } - } - - var interfaceNode = setupNode.getElementsByTagName('interface'); - if (interfaceNode.length > 1) - { - this.errors.push("Only one <interface> node in the <setup> node allowed! Others except first ingnored!"); - } - this.interfaces = new this.interfaceNode(this); - if (interfaceNode.length != 0) - { - interfaceNode = interfaceNode[0]; - this.interfaces.decode(this,interfaceNode,this.schema.getAllElementsByName('interface')[1]); - } - - // Page tags - var pageTags = projectXML.getElementsByTagName('page'); - var pageSchema = this.schema.getAllElementsByName('page')[0]; - for (var i=0; i<pageTags.length; i++) - { - var node = new this.page(this); - node.decode(this,pageTags[i],pageSchema); - this.pages.push(node); - } - }; - - this.encode = function() - { - var RootDocument = document.implementation.createDocument(null,"waet"); - var root = RootDocument.firstChild; - root.setAttribute("xmlns:xsi","http://www.w3.org/2001/XMLSchema-instance"); - root.setAttribute("xsi:noNamespaceSchemaLocation","test-schema.xsd"); - // Build setup node + } + + var interfaceNode = setupNode.getElementsByTagName('interface'); + if (interfaceNode.length > 1) { + this.errors.push("Only one <interface> node in the <setup> node allowed! Others except first ingnored!"); + } + this.interfaces = new this.interfaceNode(this); + if (interfaceNode.length != 0) { + interfaceNode = interfaceNode[0]; + this.interfaces.decode(this, interfaceNode, this.schema.getAllElementsByName('interface')[1]); + } + + // Page tags + var pageTags = projectXML.getElementsByTagName('page'); + var pageSchema = this.schema.getAllElementsByName('page')[0]; + for (var i = 0; i < pageTags.length; i++) { + var node = new this.page(this); + node.decode(this, pageTags[i], pageSchema); + this.pages.push(node); + } + }; + + this.encode = function () { + var RootDocument = document.implementation.createDocument(null, "waet"); + var root = RootDocument.firstChild; + root.setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance"); + root.setAttribute("xsi:noNamespaceSchemaLocation", "test-schema.xsd"); + // Build setup node var setup = RootDocument.createElement("setup"); var schemaSetup = this.schema.getAllElementsByName('setup')[0]; // First decode the attributes var attributes = schemaSetup.getAllElementsByTagName('xs:attribute'); - for (var i=0; i<attributes.length; i++) - { + for (var i = 0; i < attributes.length; i++) { var name = attributes[i].getAttribute("name"); if (name == undefined) { name = attributes[i].getAttribute("ref"); } - if(eval("this."+name+" != undefined") || attributes[i].getAttribute("use") == "required") - { - eval("setup.setAttribute('"+name+"',this."+name+")"); + if (eval("this." + name + " != undefined") || attributes[i].getAttribute("use") == "required") { + eval("setup.setAttribute('" + name + "',this." + name + ")"); } } root.appendChild(setup); @@ -188,73 +180,92 @@ setup.appendChild(this.postTest.encode(RootDocument)); setup.appendChild(this.metrics.encode(RootDocument)); setup.appendChild(this.interfaces.encode(RootDocument)); - for (var page of this.pages) - { + for (var page of this.pages) { root.appendChild(page.encode(RootDocument)); } - return RootDocument; - }; - - this.surveyNode = function(specification) { - this.location = null; - this.options = []; + return RootDocument; + }; + + this.surveyNode = function (specification) { + this.location = null; + this.options = []; this.parent = null; - this.schema = specification.schema.getAllElementsByName('survey')[0]; + this.schema = specification.schema.getAllElementsByName('survey')[0]; this.specification = specification; - - this.OptionNode = function(specification) { - this.type = undefined; - this.schema = specification.schema.getAllElementsByName('surveyentry')[0]; - this.id = undefined; + + this.OptionNode = function (specification) { + this.type = undefined; + this.schema = undefined; + this.id = undefined; this.name = undefined; - this.mandatory = undefined; - this.statement = undefined; - this.boxsize = undefined; - this.options = []; - this.min = undefined; - this.max = undefined; - this.step = undefined; + this.mandatory = undefined; + this.statement = undefined; + this.boxsize = undefined; + this.options = []; + this.min = undefined; + this.max = undefined; + this.step = undefined; this.conditions = []; - - this.decode = function(parent,child) - { - var attributeMap = this.schema.getAllElementsByTagName('xs:attribute'); - for (var i in attributeMap){ - if(isNaN(Number(i)) == true){break;} - var attributeName = attributeMap[i].getAttribute('name') || attributeMap[i].getAttribute('ref'); - var projectAttr = child.getAttribute(attributeName); - projectAttr = parent.processAttribute(projectAttr,attributeMap[i],parent.schema); - switch(typeof projectAttr) - { - case "number": - case "boolean": - eval('this.'+attributeName+' = '+projectAttr); - break; - case "string": - eval('this.'+attributeName+' = "'+projectAttr+'"'); - break; - } - } - this.statement = child.getElementsByTagName('statement')[0].textContent; - if (this.type == "checkbox" || this.type == "radio") { - var children = child.getElementsByTagName('option'); - if (children.length == null) { - console.log('Malformed' +child.nodeName+ 'entry'); - this.statement = 'Malformed' +child.nodeName+ 'entry'; - this.type = 'statement'; - } else { - this.options = []; - for (var i=0; i<children.length; i++) - { - this.options.push({ - name: children[i].getAttribute('name'), - text: children[i].textContent - }); - } - } - } + + this.decode = function (parent, child) { + this.schema = specification.schema.getAllElementsByName(child.nodeName)[0]; + var attributeMap = this.schema.getAllElementsByTagName('xs:attribute'); + for (var i in attributeMap) { + if (isNaN(Number(i)) == true) { + break; + } + var attributeName = attributeMap[i].getAttribute('name') || attributeMap[i].getAttribute('ref'); + var projectAttr = child.getAttribute(attributeName); + projectAttr = parent.processAttribute(projectAttr, attributeMap[i], parent.schema); + switch (typeof projectAttr) { + case "number": + case "boolean": + eval('this.' + attributeName + ' = ' + projectAttr); + break; + case "string": + eval('this.' + attributeName + ' = "' + projectAttr + '"'); + break; + } + } + if (child.nodeName == 'surveyentry') { + console.log("NOTE - Use of <surveyelement> is now deprecated. Whilst these will still work, newer nodes and tighter error checking will not be enforced"); + console.log("Please use the newer, type specifc nodes"); + if (!this.type) { + throw ("Type not specified"); + } + } else { + this.type = child.nodeName.split('survey')[1]; + } + this.statement = child.getElementsByTagName('statement')[0].textContent; + if (this.type == "checkbox" || this.type == "radio") { + var children = child.getElementsByTagName('option'); + if (children.length == null) { + console.log('Malformed' + child.nodeName + 'entry'); + this.statement = 'Malformed' + child.nodeName + 'entry'; + this.type = 'statement'; + } else { + this.options = []; + for (var i = 0; i < children.length; i++) { + this.options.push({ + name: children[i].getAttribute('name'), + text: children[i].textContent + }); + } + } + } else if (this.type == "slider") { + this.leftText = ""; + this.rightText = ""; + var minText = child.getElementsByTagName("minText"); + var maxText = child.getElementsByTagName("maxText"); + if (minText.length > 0) { + this.leftText = minText[0].textContent; + } + if (maxText.length > 0) { + this.rightText = maxText[0].textContent; + } + } var conditionElements = child.getElementsByTagName("conditional"); - for (var i=0; i<conditionElements.length; i++) { + for (var i = 0; i < conditionElements.length; i++) { var condition = conditionElements[i]; var obj = { check: condition.getAttribute("check"), @@ -264,185 +275,228 @@ } this.conditions.push(obj); } - }; - - this.exportXML = function(doc) - { - var node = doc.createElement('surveyentry'); - node.setAttribute('type',this.type); - var statement = doc.createElement('statement'); - statement.textContent = this.statement; - node.appendChild(statement); + }; + + this.exportXML = function (doc) { + var node = doc.createElement('survey' + this.type); + var statement = doc.createElement('statement'); + statement.textContent = this.statement; + node.appendChild(statement); node.id = this.id; - if (this.name != undefined) { node.setAttribute("name",this.name);} - if (this.mandatory != undefined) { node.setAttribute("mandatory",this.mandatory);} + if (this.name != undefined) { + node.setAttribute("name", this.name); + } + if (this.mandatory != undefined) { + node.setAttribute("mandatory", this.mandatory); + } node.id = this.id; - if (this.name != undefined) {node.setAttribute("name",this.name);} - switch(this.type) - { + if (this.name != undefined) { + node.setAttribute("name", this.name); + } + switch (this.type) { case "checkbox": + if (this.min != undefined) { + node.setAttribute("min", this.min); + } else { + node.setAttribute("min", "0"); + } + if (this.max != undefined) { + node.setAttribute("max", this.max); + } else { + node.setAttribute("max", "undefined"); + } case "radio": - for (var i=0; i<this.options.length; i++) - { + for (var i = 0; i < this.options.length; i++) { var option = this.options[i]; var optionNode = doc.createElement("option"); - optionNode.setAttribute("name",option.name); + optionNode.setAttribute("name", option.name); optionNode.textContent = option.text; node.appendChild(optionNode); } + break; case "number": - if (this.min != undefined) {node.setAttribute("min", this.min);} - if (this.max != undefined) {node.setAttribute("max", this.max);} + if (this.min != undefined) { + node.setAttribute("min", this.min); + } + if (this.max != undefined) { + node.setAttribute("max", this.max); + } + break; case "question": - if (this.boxsize != undefined) {node.setAttribute("boxsize",this.boxsize);} - if (this.mandatory != undefined) {node.setAttribute("mandatory",this.mandatory);} + if (this.boxsize != undefined) { + node.setAttribute("boxsize", this.boxsize); + } + if (this.mandatory != undefined) { + node.setAttribute("mandatory", this.mandatory); + } + break; + case "video": + if (this.mandatory != undefined) { + node.setAttribute("mandatory", this.mandatory); + } + case "youtube": + if (this.url != undefined) { + node.setAttribute("url", this.url); + } + break; + case "slider": + node.setAttribute("min", this.min); + node.setAttribute("max", this.max); + if (this.leftText) { + var minText = doc.createElement("minText"); + minText.textContent = this.leftText; + node.appendChild(minText); + } + if (this.rightText) { + var maxText = doc.createElement("maxText"); + maxText.textContent = this.rightText; + node.appendChild(maxText); + } default: break; } for (var condition of this.conditions) { var conditionDOM = doc.createElement("conditional"); - conditionDOM.setAttribute("check",condition.check); - conditionDOM.setAttribute("value",condition.value); - conditionDOM.setAttribute("jumpToOnPass",condition.jumpToOnPass); - conditionDOM.setAttribute("jumpToOnFail",condition.jumpToOnFail); + conditionDOM.setAttribute("check", condition.check); + conditionDOM.setAttribute("value", condition.value); + conditionDOM.setAttribute("jumpToOnPass", condition.jumpToOnPass); + conditionDOM.setAttribute("jumpToOnFail", condition.jumpToOnFail); node.appendChild(conditionDOM); } - return node; - }; - }; - this.decode = function(parent,xml) { + return node; + }; + }; + this.decode = function (parent, xml) { this.parent = parent; - this.location = xml.getAttribute('location'); - if (this.location == 'before'){this.location = 'pre';} - else if (this.location == 'after'){this.location = 'post';} - var children = xml.getAllElementsByTagName('surveyentry'); - for (var i=0; i<children.length; i++) - { - var node = new this.OptionNode(this.specification); - node.decode(parent,children[i]); - this.options.push(node); - } + this.location = xml.getAttribute('location'); + if (this.location == 'before') { + this.location = 'pre'; + } else if (this.location == 'after') { + this.location = 'post'; + } + var child = xml.firstElementChild + while (child) { + var node = new this.OptionNode(this.specification); + node.decode(parent, child); + this.options.push(node); + child = child.nextElementSibling; + } if (this.options.length == 0) { console.log("Empty survey node"); console.log(this); } - }; - this.encode = function(doc) { - var node = doc.createElement('survey'); - node.setAttribute('location',this.location); - for (var i=0; i<this.options.length; i++) - { - node.appendChild(this.options[i].exportXML(doc)); - } - return node; - }; - }; - - this.interfaceNode = function(specification) - { - this.title = null; - this.name = null; - this.options = []; - this.scales = []; - this.schema = specification.schema.getAllElementsByName('interface')[1]; - - this.decode = function(parent,xml) { - this.name = xml.getAttribute('name'); - var titleNode = xml.getElementsByTagName('title'); - if (titleNode.length == 1) - { - this.title = titleNode[0].textContent; - } - var interfaceOptionNodes = xml.getElementsByTagName('interfaceoption'); - // Extract interfaceoption node schema - var interfaceOptionNodeSchema = this.schema.getAllElementsByName('interfaceoption')[0]; - var attributeMap = interfaceOptionNodeSchema.getAllElementsByTagName('xs:attribute'); - for (var i=0; i<interfaceOptionNodes.length; i++) - { - var ioNode = interfaceOptionNodes[i]; - var option = {}; - for (var j=0; j<attributeMap.length; j++) - { - var attributeName = attributeMap[j].getAttribute('name') || attributeMap[j].getAttribute('ref'); - var projectAttr = ioNode.getAttribute(attributeName); - if(parent.processAttribute) { + }; + this.encode = function (doc) { + var node = doc.createElement('survey'); + node.setAttribute('location', this.location); + for (var i = 0; i < this.options.length; i++) { + node.appendChild(this.options[i].exportXML(doc)); + } + return node; + }; + }; + + this.interfaceNode = function (specification) { + this.title = null; + this.name = null; + this.options = []; + this.scales = []; + this.schema = specification.schema.getAllElementsByName('interface')[1]; + + this.decode = function (parent, xml) { + this.name = xml.getAttribute('name'); + var titleNode = xml.getElementsByTagName('title'); + if (titleNode.length == 1) { + this.title = titleNode[0].textContent; + } + var interfaceOptionNodes = xml.getElementsByTagName('interfaceoption'); + // Extract interfaceoption node schema + var interfaceOptionNodeSchema = this.schema.getAllElementsByName('interfaceoption')[0]; + var attributeMap = interfaceOptionNodeSchema.getAllElementsByTagName('xs:attribute'); + for (var i = 0; i < interfaceOptionNodes.length; i++) { + var ioNode = interfaceOptionNodes[i]; + var option = {}; + for (var j = 0; j < attributeMap.length; j++) { + var attributeName = attributeMap[j].getAttribute('name') || attributeMap[j].getAttribute('ref'); + var projectAttr = ioNode.getAttribute(attributeName); + if (parent.processAttribute) { parent.processAttribute(projectAttr, attributeMap[j], parent.schema) } else { parent.parent.processAttribute(projectAttr, attributeMap[j], parent.parent.schema) } - switch(typeof projectAttr) - { - case "number": - case "boolean": - eval('option.'+attributeName+' = '+projectAttr); - break; - case "string": - eval('option.'+attributeName+' = "'+projectAttr+'"'); - break; - } - } - this.options.push(option); - } - - // Now the scales nodes - var scaleParent = xml.getElementsByTagName('scales'); - if (scaleParent.length == 1) { - scaleParent = scaleParent[0]; + switch (typeof projectAttr) { + case "number": + case "boolean": + eval('option.' + attributeName + ' = ' + projectAttr); + break; + case "string": + eval('option.' + attributeName + ' = "' + projectAttr + '"'); + break; + } + } + this.options.push(option); + } + + // Now the scales nodes + var scaleParent = xml.getElementsByTagName('scales'); + if (scaleParent.length == 1) { + scaleParent = scaleParent[0]; var scalelabels = scaleParent.getAllElementsByTagName('scalelabel'); - for (var i=0; i<scalelabels.length; i++) { - this.scales.push({ - text: scalelabels[i].textContent, - position: Number(scalelabels[i].getAttribute('position')) - }); - } - } - }; - - this.encode = function(doc) { - var node = doc.createElement("interface"); + for (var i = 0; i < scalelabels.length; i++) { + this.scales.push({ + text: scalelabels[i].textContent, + position: Number(scalelabels[i].getAttribute('position')) + }); + } + } + }; + + this.encode = function (doc) { + var node = doc.createElement("interface"); if (typeof name == "string") - node.setAttribute("name",this.name); + node.setAttribute("name", this.name); if (typeof this.title == "string") { var titleNode = doc.createElement("title"); titleNode.textContent = this.title; node.appendChild(titleNode); } - for (var option of this.options) - { + for (var option of this.options) { var child = doc.createElement("interfaceoption"); - child.setAttribute("type",option.type); - child.setAttribute("name",option.name); + child.setAttribute("type", option.type); + child.setAttribute("name", option.name); node.appendChild(child); } if (this.scales.length != 0) { var scales = doc.createElement("scales"); - for (var scale of this.scales) - { + for (var scale of this.scales) { var child = doc.createElement("scalelabel"); - child.setAttribute("position",scale.position); + child.setAttribute("position", scale.position); child.textContent = scale.text; scales.appendChild(child); } node.appendChild(scales); } return node; - }; - }; - - this.metricNode = function() { + }; + }; + + this.metricNode = function () { this.enabled = []; - this.decode = function(parent, xml) { + this.decode = function (parent, xml) { var children = xml.getElementsByTagName('metricenable'); - for (var i in children) { - if (isNaN(Number(i)) == true){break;} + for (var i in children) { + if (isNaN(Number(i)) == true) { + break; + } this.enabled.push(children[i].textContent); } } - this.encode = function(doc) { + this.encode = function (doc) { var node = doc.createElement('metric'); - for (var i in this.enabled) - { - if (isNaN(Number(i)) == true){break;} + for (var i in this.enabled) { + if (isNaN(Number(i)) == true) { + break; + } var child = doc.createElement('metricenable'); child.textContent = this.enabled[i]; node.appendChild(child); @@ -450,248 +504,318 @@ return node; } } - - this.page = function(specification) { - this.presentedId = undefined; - this.id = undefined; + + this.page = function (specification) { + this.presentedId = undefined; + this.id = undefined; this.title = undefined; - this.hostURL = undefined; - this.randomiseOrder = undefined; - this.loop = undefined; - this.outsideReference = null; - this.loudness = null; + this.hostURL = undefined; + this.randomiseOrder = undefined; + this.loop = undefined; + this.outsideReference = null; + this.loudness = null; this.label = null; - this.preTest = null; - this.postTest = null; - this.interfaces = []; + this.labelStart = ""; + this.preTest = null; + this.postTest = null; + this.interfaces = []; this.playOne = null; - this.commentBoxPrefix = "Comment on track"; - this.audioElements = []; - this.commentQuestions = []; - this.schema = specification.schema.getAllElementsByName("page")[0]; + this.commentBoxPrefix = "Comment on track"; + this.audioElements = []; + this.commentQuestions = []; + this.schema = specification.schema.getAllElementsByName("page")[0]; this.specification = specification; this.parent = null; - - this.decode = function(parent,xml) - { + + this.decode = function (parent, xml) { this.parent = parent; - var attributeMap = this.schema.getAllElementsByTagName('xs:attribute'); - for (var i=0; i<attributeMap.length; i++) - { - var attributeName = attributeMap[i].getAttribute('name') || attributeMap[i].getAttribute('ref'); - var projectAttr = xml.getAttribute(attributeName); - projectAttr = parent.processAttribute(projectAttr,attributeMap[i],parent.schema); - switch(typeof projectAttr) - { - case "number": - case "boolean": - eval('this.'+attributeName+' = '+projectAttr); - break; - case "string": - eval('this.'+attributeName+' = "'+projectAttr+'"'); - break; - } - } - + var attributeMap = this.schema.getAllElementsByTagName('xs:attribute'); + for (var i = 0; i < attributeMap.length; i++) { + var attributeName = attributeMap[i].getAttribute('name') || attributeMap[i].getAttribute('ref'); + var projectAttr = xml.getAttribute(attributeName); + projectAttr = parent.processAttribute(projectAttr, attributeMap[i], parent.schema); + switch (typeof projectAttr) { + case "number": + case "boolean": + eval('this.' + attributeName + ' = ' + projectAttr); + break; + case "string": + eval('this.' + attributeName + ' = "' + projectAttr + '"'); + break; + } + } + // Get the title var title = xml.getElementsByTagName('title'); if (title.length != 0 && title[0].parentElement == xml) { this.title = title[0].textContent; } - - // Get the Comment Box Prefix - var CBP = xml.getElementsByTagName('commentboxprefix'); - if (CBP.length != 0 && CBP[0].parentElement == xml) { - this.commentBoxPrefix = CBP[0].textContent; - } - - // Now decode the interfaces - var interfaceNode = xml.getElementsByTagName('interface'); - for (var i=0; i<interfaceNode.length; i++) - { - var node = new parent.interfaceNode(this.specification); - node.decode(this,interfaceNode[i],parent.schema.getAllElementsByName('interface')[1]); - this.interfaces.push(node); - } - - // Now process the survey node options - var survey = xml.getElementsByTagName('survey'); - var surveySchema = parent.schema.getAllElementsByName('survey')[0]; - for (var i=0; i<survey.length; i++){ - var location = survey[i].getAttribute('location'); - if (location == 'pre' || location == 'before') - { - if (this.preTest != null){this.errors.push("Already a pre/before test survey defined! Ignoring second!!");} - else { - this.preTest = new parent.surveyNode(this.specification); - this.preTest.decode(parent,survey[i],surveySchema); - } - } else if (location == 'post' || location == 'after') { - if (this.postTest != null){this.errors.push("Already a post/after test survey defined! Ignoring second!!");} - else { - this.postTest = new parent.surveyNode(this.specification); - this.postTest.decode(parent,survey[i],surveySchema); - } - } - } - - // Now process the audioelement tags - var audioElements = xml.getElementsByTagName('audioelement'); - for (var i=0; i<audioElements.length; i++) - { - var node = new this.audioElementNode(this.specification); - node.decode(this,audioElements[i]); - this.audioElements.push(node); - } - - // Now decode the commentquestions - var commentQuestions = xml.getElementsByTagName('commentquestion'); - for (var i=0; i<commentQuestions.length; i++) - { - var node = new this.commentQuestionNode(this.specification); - node.decode(parent,commentQuestions[i]); - this.commentQuestions.push(node); - } - }; - - this.encode = function(root) - { - var AHNode = root.createElement("page"); + + // Get the Comment Box Prefix + var CBP = xml.getElementsByTagName('commentboxprefix'); + if (CBP.length != 0 && CBP[0].parentElement == xml) { + this.commentBoxPrefix = CBP[0].textContent; + } + + // Now decode the interfaces + var interfaceNode = xml.getElementsByTagName('interface'); + for (var i = 0; i < interfaceNode.length; i++) { + var node = new parent.interfaceNode(this.specification); + node.decode(this, interfaceNode[i], parent.schema.getAllElementsByName('interface')[1]); + this.interfaces.push(node); + } + + // Now process the survey node options + var survey = xml.getElementsByTagName('survey'); + var surveySchema = parent.schema.getAllElementsByName('survey')[0]; + for (var i = 0; i < survey.length; i++) { + var location = survey[i].getAttribute('location'); + if (location == 'pre' || location == 'before') { + if (this.preTest != null) { + this.errors.push("Already a pre/before test survey defined! Ignoring second!!"); + } else { + this.preTest = new parent.surveyNode(this.specification); + this.preTest.decode(parent, survey[i], surveySchema); + } + } else if (location == 'post' || location == 'after') { + if (this.postTest != null) { + this.errors.push("Already a post/after test survey defined! Ignoring second!!"); + } else { + this.postTest = new parent.surveyNode(this.specification); + this.postTest.decode(parent, survey[i], surveySchema); + } + } + } + + // Now process the audioelement tags + var audioElements = xml.getElementsByTagName('audioelement'); + for (var i = 0; i < audioElements.length; i++) { + var node = new this.audioElementNode(this.specification); + node.decode(this, audioElements[i]); + this.audioElements.push(node); + } + + // Now decode the commentquestions + var cqNode = xml.getElementsByTagName('commentquestions'); + if (cqNode.length != 0) { + cqNode = cqNode[0]; + var commentQuestions = cqNode.children; + for (var i = 0; i < commentQuestions.length; i++) { + var node = new this.commentQuestionNode(this.specification); + node.decode(parent, commentQuestions[i]); + this.commentQuestions.push(node); + } + } + }; + + this.encode = function (root) { + var AHNode = root.createElement("page"); // First decode the attributes var attributes = this.schema.getAllElementsByTagName('xs:attribute'); - for (var i=0; i<attributes.length; i++) - { + for (var i = 0; i < attributes.length; i++) { var name = attributes[i].getAttribute("name"); if (name == undefined) { name = attributes[i].getAttribute("ref"); } - if(eval("this."+name+" != undefined") || attributes[i].getAttribute("use") == "required") - { - eval("AHNode.setAttribute('"+name+"',this."+name+")"); + if (eval("this." + name + " != undefined") || attributes[i].getAttribute("use") == "required") { + eval("AHNode.setAttribute('" + name + "',this." + name + ")"); } } - if(this.loudness != null) {AHNode.setAttribute("loudness",this.loudness);} + if (this.loudness != null) { + AHNode.setAttribute("loudness", this.loudness); + } // <commentboxprefix> var commentboxprefix = root.createElement("commentboxprefix"); commentboxprefix.textContent = this.commentBoxPrefix; AHNode.appendChild(commentboxprefix); - - for (var i=0; i<this.interfaces.length; i++) - { - AHNode.appendChild(this.interfaces[i].encode(root)); - } - - for (var i=0; i<this.audioElements.length; i++) { - AHNode.appendChild(this.audioElements[i].encode(root)); - } - // Create <CommentQuestion> - for (var i=0; i<this.commentQuestions.length; i++) - { - AHNode.appendChild(this.commentQuestions[i].encode(root)); - } - - AHNode.appendChild(this.preTest.encode(root)); + + for (var i = 0; i < this.interfaces.length; i++) { + AHNode.appendChild(this.interfaces[i].encode(root)); + } + + for (var i = 0; i < this.audioElements.length; i++) { + AHNode.appendChild(this.audioElements[i].encode(root)); + } + // Create <CommentQuestion> + for (var i = 0; i < this.commentQuestions.length; i++) { + AHNode.appendChild(this.commentQuestions[i].encode(root)); + } + + AHNode.appendChild(this.preTest.encode(root)); AHNode.appendChild(this.postTest.encode(root)); - return AHNode; - }; - - this.commentQuestionNode = function(specification) { - this.id = null; + return AHNode; + }; + + this.commentQuestionNode = function (specification) { + this.id = null; this.name = undefined; - this.type = undefined; - this.options = []; - this.statement = undefined; - this.schema = specification.schema.getAllElementsByName('commentquestion')[0]; - this.decode = function(parent,xml) - { - this.id = xml.id; + this.type = undefined; + this.statement = undefined; + this.schema = specification.schema.getAllElementsByName('commentquestion')[0]; + this.decode = function (parent, xml) { + this.id = xml.id; this.name = xml.getAttribute('name'); - this.type = xml.getAttribute('type'); - this.statement = xml.getElementsByTagName('statement')[0].textContent; - var optNodes = xml.getElementsByTagName('option'); - for (var i=0; i<optNodes.length; i++) - { - var optNode = optNodes[i]; - this.options.push({ - name: optNode.getAttribute('name'), - text: optNode.textContent - }); - } - }; - - this.encode = function(root) - { - var node = root.createElement("commentquestion"); + switch (xml.nodeName) { + case "commentradio": + this.type = "radio"; + this.options = []; + break; + case "commentcheckbox": + this.type = "checkbox"; + this.options = []; + break; + case "commentslider": + this.type = "slider"; + this.min = undefined; + this.max = undefined; + this.step = undefined; + break; + case "commentquestion": + default: + this.type = "question"; + break; + } + this.statement = xml.getElementsByTagName('statement')[0].textContent; + if (this.type == "radio" || this.type == "checkbox") { + var optNodes = xml.getElementsByTagName('option'); + for (var i = 0; i < optNodes.length; i++) { + var optNode = optNodes[i]; + this.options.push({ + name: optNode.getAttribute('name'), + text: optNode.textContent + }); + } + } + if (this.type == "slider") { + this.min = Number(xml.getAttribute("min")); + this.max = Number(xml.getAttribute("max")); + this.step = Number(xml.getAttribute("step")); + if (this.step == undefined) { + this.step = 1; + } + this.value = Number(xml.getAttribute("value")); + if (this.value == undefined) { + this.value = min; + } + this.leftText = xml.getElementsByTagName("minText"); + if (this.leftText && this.leftText.length > 0) { + this.leftText = this.leftText[0].textContent; + } else { + this.leftText = ""; + } + this.rightText = xml.getElementsByTagName("maxText"); + if (this.rightText && this.rightText.length > 0) { + this.rightText = this.rightText[0].textContent; + } else { + this.rightText = ""; + } + } + }; + + this.encode = function (root) { + var node; + switch (this.type) { + case "radio": + node = root.createElement("commentradio"); + break; + case "checkbox": + node = root.createElement("commentcheckbox"); + break; + case "slider": + node = root.createElement("commentslider"); + break; + case "question": + default: + node = root.createElement("commentquestion"); + break; + } node.id = this.id; - node.setAttribute("type",this.type); - if (this.name != undefined){node.setAttribute("name",this.name);} + node.setAttribute("type", this.type); + if (this.name != undefined) { + node.setAttribute("name", this.name); + } var statement = root.createElement("statement"); statement.textContent = this.statement; node.appendChild(statement); - for (var option of this.options) - { - var child = root.createElement("option"); - child.setAttribute("name",option.name); - child.textContent = option.text; - node.appendChild(child); + if (this.type == "radio" || this.type == "checkbox") { + for (var option of this.options) { + var child = root.createElement("option"); + child.setAttribute("name", option.name); + child.textContent = option.text; + node.appendChild(child); + } + } + if (this.type == "slider") { + node.setAttribute("min", this.min); + node.setAttribute("max", this.max); + if (this.step !== 1) { + node.setAttribute("step", this.step); + } + if (this.value !== this.min) { + node.setAttribute("value", this.value); + } + if (this.leftText.length > 0) { + var leftText = root.createElement("minText"); + leftText.textContent = this.leftText; + node.appendChild(leftText); + } + if (this.rightText.length > 0) { + var rightText = root.createElement("maxText"); + rightText.textContent = this.rightText; + node.appendChild(rightText); + } } return node; - }; - }; - - this.audioElementNode = function(specification) { - this.url = null; - this.id = null; + }; + }; + + this.audioElementNode = function (specification) { + this.url = null; + this.id = null; this.name = null; - this.parent = null; - this.type = null; - this.marker = null; - this.enforce = false; - this.gain = 0.0; + this.parent = null; + this.type = null; + this.marker = null; + this.enforce = false; + this.gain = 0.0; this.label = null; this.startTime = undefined; this.stopTime = undefined; - this.schema = specification.schema.getAllElementsByName('audioelement')[0];; - this.parent = null; - this.decode = function(parent,xml) - { - this.parent = parent; - var attributeMap = this.schema.getAllElementsByTagName('xs:attribute'); - for (var i=0; i<attributeMap.length; i++) - { - var attributeName = attributeMap[i].getAttribute('name') || attributeMap[i].getAttribute('ref'); - var projectAttr = xml.getAttribute(attributeName); - projectAttr = parent.parent.processAttribute(projectAttr,attributeMap[i],parent.parent.schema); - switch(typeof projectAttr) - { - case "number": - case "boolean": - eval('this.'+attributeName+' = '+projectAttr); - break; - case "string": - eval('this.'+attributeName+' = "'+projectAttr+'"'); - break; - } - } - - }; - this.encode = function(root) - { - var AENode = root.createElement("audioelement"); - var attributes = this.schema.getAllElementsByTagName('xs:attribute'); - for (var i=0; i<attributes.length; i++) - { + this.schema = specification.schema.getAllElementsByName('audioelement')[0];; + this.parent = null; + this.decode = function (parent, xml) { + this.parent = parent; + var attributeMap = this.schema.getAllElementsByTagName('xs:attribute'); + for (var i = 0; i < attributeMap.length; i++) { + var attributeName = attributeMap[i].getAttribute('name') || attributeMap[i].getAttribute('ref'); + var projectAttr = xml.getAttribute(attributeName); + projectAttr = parent.parent.processAttribute(projectAttr, attributeMap[i], parent.parent.schema); + switch (typeof projectAttr) { + case "number": + case "boolean": + eval('this.' + attributeName + ' = ' + projectAttr); + break; + case "string": + eval('this.' + attributeName + ' = "' + projectAttr + '"'); + break; + } + } + + }; + this.encode = function (root) { + var AENode = root.createElement("audioelement"); + var attributes = this.schema.getAllElementsByTagName('xs:attribute'); + for (var i = 0; i < attributes.length; i++) { var name = attributes[i].getAttribute("name"); if (name == undefined) { name = attributes[i].getAttribute("ref"); } - if(eval("this."+name+" != undefined") || attributes[i].getAttribute("use") == "required") - { - eval("AENode.setAttribute('"+name+"',this."+name+")"); + if (eval("this." + name + " != undefined") || attributes[i].getAttribute("use") == "required") { + eval("AENode.setAttribute('" + name + "',this." + name + ")"); } } - return AENode; - }; - }; - }; + return AENode; + }; + }; + }; }
--- a/python/generate_report.py Thu Nov 03 11:02:56 2016 +0000 +++ b/python/generate_report.py Thu Nov 03 11:04:26 2016 +0000 @@ -116,9 +116,10 @@ # generate images for later use if render_figures: - subprocess.call("python timeline_view_movement.py '"+folder_name+"'", shell=True) - subprocess.call("python score_parser.py '"+folder_name+"'", shell=True) - subprocess.call("python score_plot.py '"+folder_name+"ratings/'", shell=True) + script_path = os.path.dirname(os.path.realpath(__file__)) # where is generate_report.py? + subprocess.call("python " +script_path+"/timeline_view_movement.py '"+folder_name+"'", shell=True) + subprocess.call("python " +script_path+"/score_parser.py '"+folder_name+"'", shell=True) + subprocess.call("python " +script_path+"/score_plot.py '"+folder_name+"ratings/'", shell=True) # make array of text and array of dates body_array = []
--- a/python/timeline_view_movement.py Thu Nov 03 11:02:56 2016 +0000 +++ b/python/timeline_view_movement.py Thu Nov 03 11:04:26 2016 +0000 @@ -124,9 +124,10 @@ stop_times_global = [] listen_events = audioelement.findall("./metric/metricresult/[@name='elementListenTracker']/event") for event in listen_events: - # get testtime: start and stop - start_times_global.append(float(event.find('testtime').get('start')))#-time_offset) - stop_times_global.append(float(event.find('testtime').get('stop')))#-time_offset) + if event.find('testtime') is not None: + # get testtime: start and stop + start_times_global.append(float(event.find('testtime').get('start')))#-time_offset) + stop_times_global.append(float(event.find('testtime').get('stop')))#-time_offset) # display fragment name at start plt.text(0,initial_position+0.02,audio_id,color=colormap[increment%len(colormap)]) #,rotation=45 @@ -289,12 +290,35 @@ plt.ylabel('Rating') # default plt.ylim(0, 1) # rating between 0 and 1 - # TO DO: - # Y axis title and tick labels as specified in 'setup' for corresponding page + # Y axis title and tick labels as specified in 'setup' + # for corresponding page + page_setup = root.find("./waet/page[@id='"+page_name+"']") + # 'ref' of page is 'id' in page setup + # Different plots for different axes + interfaces = page_setup.findall("./interface") + interface_title = interfaces[0].find("./title") + scales = interfaces[0].findall("./scales") # get first interface by default + scalelabels = scales[0].findall("./scalelabel") # get first scale by default + + labelpos = [] # array of scalelabel positions + labelstr = [] # array of strings at labels + for scalelabel in scalelabels: + labelpos.append(float(scalelabel.get('position'))/100.0) + labelstr.append(scalelabel.text) + + # use interface name as Y axis label + if interface_title is not None: + plt.ylabel(interface_title.text) + else: + plt.ylabel('Rating') # default + + if len(labelpos): + plt.yticks(labelpos, labelstr) #plt.show() # uncomment to show plot; comment when just saving #exit() + # save as PDF plt.savefig(timeline_folder+subject_id+"-"+page_name+".pdf", bbox_inches='tight') plt.close()
--- a/test_create/attributes.json Thu Nov 03 11:02:56 2016 +0000 +++ b/test_create/attributes.json Thu Nov 03 11:04:26 2016 +0000 @@ -27,5 +27,9 @@ "postSilence": "Post Silence", "poolSize": "Pool Size", "alwaysInclude": "Always Include", - "crossFade": "Cross Fade" + "crossFade": "Cross Fade", + "check": "Check", + "value": "Value", + "jumpToOnPass": "Jump To ID On Pass", + "jumpToOnFail": "Jump To ID On Fail" }
--- a/test_create/test_core.js Thu Nov 03 11:02:56 2016 +0000 +++ b/test_create/test_core.js Thu Nov 03 11:04:26 2016 +0000 @@ -855,7 +855,62 @@ maximumEntry.value = this.option.max; this.dynamic.appendChild(maximum); break; + case "video": + case "youtube": + this.dynamic.appendChild(id); + + var mandatory = document.createElement("div"); + var mandatoryInput = document.createElement("input"); + var mandatoryText = document.createElement("span"); + mandatoryText.textContent = "Mandatory: "; + mandatory.appendChild(mandatoryText); + mandatory.appendChild(mandatoryInput); + mandatory.className = "survey-entry-attribute"; + mandatoryInput.type = "checkbox"; + if (this.option.mandatory) {mandatoryInput.checked = true;} else {mandatoryInput.checked = false;} + mandatoryInput.setAttribute("name","mandatory"); + mandatoryInput.addEventListener("change",this,false); + this.dynamic.appendChild(mandatory); + + var url = document.createElement("div"); + var urlInput = document.createElement("input"); + var urlText = document.createElement("span"); + urlText.textContent = "URL: "; + url.appendChild(urlText); + url.appendChild(urlInput); + url.className = "survey-entry-attribute"; + urlInput.type = "text"; + if (this.option.mandatory) {urlInput.value = this.option.mandatory;} else {urlInput.value = "";} + urlInput.setAttribute("name","url"); + urlInput.addEventListener("change",this,false); + this.dynamic.appendChild(url); + break; case "checkbox": + var minimum = document.createElement("div"); + var minimumEntry = document.createElement("input"); + var minimumText = document.createElement("span"); + minimumText.textContent = "Minimum: "; + minimum.appendChild(minimumText); + minimum.appendChild(minimumEntry); + minimum.className = "survey-entry-attribute"; + minimumEntry.type = "number"; + minimumEntry.setAttribute("name","min"); + minimumEntry.addEventListener("change",this,false); + minimumEntry.value = this.option.min; + this.dynamic.appendChild(minimum); + + var maximum = document.createElement("div"); + var maximumEntry = document.createElement("input"); + var maximumText = document.createElement("span"); + maximumText.textContent = "Maximum: "; + maximum.appendChild(maximumText); + maximum.appendChild(maximumEntry); + maximum.className = "survey-entry-attribute"; + maximumEntry.type = "number"; + maximumEntry.setAttribute("name","max"); + maximumEntry.addEventListener("change",this,false); + maximumEntry.value = this.option.max; + this.dynamic.appendChild(maximum); case "radio": this.dynamic.appendChild(id); var optionHolder = document.createElement("div"); @@ -938,7 +993,7 @@ this.dynamic.appendChild(optionHolder); } } - this.handleEvent = function() + this.handleEvent = function(event) { var name = event.currentTarget.getAttribute("name"); var nodeName = event.currentTarget.nodeName; @@ -1888,6 +1943,8 @@ var max = convert.convertAttributeToDOM(this.specification,specification.schema.getAllElementsByName("max")[0]); this.attributeDOM.appendChild(id.holder); this.attributes.push(id); + this.attributeDOM.appendChild(mandatory.holder); + this.attributes.push(mandatory); this.attributeDOM.appendChild(min.holder); this.attributes.push(min); this.attributeDOM.appendChild(max.holder); @@ -1896,8 +1953,14 @@ case "checkbox": this.titleDOM.textContent = "Checkbox"; var id = convert.convertAttributeToDOM(this.specification,specification.schema.getAllElementsByName("id")[0]); + var min = convert.convertAttributeToDOM(this.specification,specification.schema.getAllElementsByName("min")[0]); + var max = convert.convertAttributeToDOM(this.specification,specification.schema.getAllElementsByName("max")[0]); this.attributeDOM.appendChild(id.holder); this.attributes.push(id); + this.attributeDOM.appendChild(min.holder); + this.attributes.push(min); + this.attributeDOM.appendChild(max.holder); + this.attributes.push(max); break; case "radio": this.titleDOM.textContent = "Radio"; @@ -1905,10 +1968,122 @@ this.attributeDOM.appendChild(id.holder); this.attributes.push(id); break; + case "video": + this.titleDOM.textContent = "Video"; + var id = convert.convertAttributeToDOM(this.specification,specification.schema.getAllElementsByName("id")[0]); + this.attributeDOM.appendChild(id.holder); + this.attributes.push(id); + var mandatory = convert.convertAttributeToDOM(this.specification,specification.schema.getAllElementsByName("mandatory")[0]); + var url = convert.convertAttributeToDOM(this.specification,specification.schema.getAllElementsByName("url")[0]); + this.attributeDOM.appendChild(mandatory.holder); + this.attributes.push(mandatory); + this.attributeDOM.appendChild(url.holder); + this.attributes.push(url); + break; + case "youtube": + this.titleDOM.textContent = "YouTube"; + var id = convert.convertAttributeToDOM(this.specification,specification.schema.getAllElementsByName("id")[0]); + this.attributeDOM.appendChild(id.holder); + this.attributes.push(id); + var mandatory = convert.convertAttributeToDOM(this.specification,specification.schema.getAllElementsByName("mandatory")[0]); + var url = convert.convertAttributeToDOM(this.specification,specification.schema.getAllElementsByName("url")[0]); + this.attributeDOM.appendChild(mandatory.holder); + this.attributes.push(mandatory); + this.attributeDOM.appendChild(url.holder); + this.attributes.push(url); + break; } } this.build(); + var Conditional = function(parent, rootObject) { + this.type = "surveyEntryConditionalNode"; + this.rootDOM = document.createElement("div"); + this.titleDOM = document.createElement("span"); + this.attributeDOM = document.createElement("div"); + this.attributes = []; + this.childrenDOM = document.createElement("div"); + this.children = []; + this.buttonDOM = document.createElement("div"); + this.parent = parent; + this.specification = rootObject; + this.schema = specification.schema.getAllElementsByName("conditional")[0]; + + this.rootDOM.className = "node"; + this.rootDOM.style.minWidth = "50%"; + + var titleDiv = document.createElement('div'); + titleDiv.className = "node-title"; + this.titleDOM.className = "node-title"; + titleDiv.appendChild(this.titleDOM); + + this.attributeDOM.className = "node-attributes"; + this.childrenDOM.className = "node-children"; + this.buttonDOM.className = "node-buttons"; + + this.rootDOM.appendChild(titleDiv); + this.rootDOM.appendChild(this.attributeDOM); + this.rootDOM.appendChild(this.childrenDOM); + this.rootDOM.appendChild(this.buttonDOM); + + var attributeList = this.schema.getAllElementsByTagName("xs:attribute"); + + for (var i=0; i<attributeList.length; i++) { + var attributeName = attributeList[i].getAttribute("name"); + var attribute = convert.convertAttributeToDOM(this.specification,this.schema.getAllElementsByName(attributeName)[0]); + this.attributes.push(attribute); + this.attributeDOM.appendChild(attribute.holder); + } + + this.build = function() { + } + + this.deleteNode = { + root: document.createElement("button"), + parent: this, + handleEvent: function(event) { + this.parent.parent.childrenDOM.removeChild(this.parent.rootDOM); + this.parent.parent.addConditional.root.disabled = false; + var index = this.parent.parent.children.findIndex(function(element){ + if (this == element) {return true;} return false; + },this.parent); + if (index >= 0) { + this.parent.parent.children.splice(index,1); + } + index = this.parent.parent.specification.conditions.findIndex(function(element){ + if (this == element) {return true;} return false; + },this.parent.specification); + if (index >= 0) { + this.parent.parent.specification.conditions.splice(index); + } + } + } + this.deleteNode.root.textContent = "Delete"; + this.deleteNode.root.addEventListener("click",this.deleteNode); + + this.buttonDOM.appendChild(this.deleteNode.root); + } + + this.addConditional = { + root: document.createElement("button"), + parent: this, + handleEvent: function(event) { + var spec = { + check: null, + value: null, + jumpToOnPass: null, + jumpToOnFail: null + }; + this.parent.specification.conditions.push(spec); + var condition = new Conditional(this.parent,spec); + this.parent.children.push(condition); + this.parent.childrenDOM.appendChild(condition.rootDOM); + } + } + this.addConditional.root.addEventListener("click",this.addConditional); + this.addConditional.root.textContent = "Add Condition"; + this.buttonDOM.appendChild(this.addConditional.root); + this.editNode = { root: document.createElement("button"), parent: this, @@ -2017,6 +2192,12 @@ this.moveButtons.root_down.textContent = "Move Down"; this.buttonDOM.appendChild(this.moveButtons.root_up); this.buttonDOM.appendChild(this.moveButtons.root_down); + + for (var condition of this.specification.conditions) { + var newNode = new Conditional(this,condition); + this.children.push(newNode); + this.childrenDOM.appendChild(newNode.rootDOM); + } } this.addNode = { root: document.createElement("button"),
--- a/tests/examples/AB_example.xml Thu Nov 03 11:02:56 2016 +0000 +++ b/tests/examples/AB_example.xml Thu Nov 03 11:04:26 2016 +0000 @@ -1,97 +1,109 @@ <?xml version="1.0" encoding="utf-8"?> -<waet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="test-schema.xsd"> - <setup interface="AB" projectReturn="save.php" randomiseOrder='true' poolSize="2" loudness="-23" playOne="true"> - <survey location="before"> - <surveyentry type="question" id="sessionId" mandatory="true"> - <statement>Please enter your name.</statement> - </surveyentry> - <surveyentry type="checkbox" id="checkboxtest" mandatory="true"> - <statement>Please select with which activities you have any experience (example checkbox question)</statement> - <option name="musician">Playing a musical instrument</option> - <option name="soundengineer">Recording or mixing audio</option> - <option name="developer">Developing audio software</option> - <option name="hwdesigner">Designing or building audio hardware</option> - <option name="researcher">Research in the field of audio</option> - </surveyentry> - <surveyentry type="statement" id="test-intro"> - <statement>This is an example of an 'AB'-style test, with two pages, using the test stimuli in 'example_eval/'.</statement> - </surveyentry> - </survey> - <survey location="after"> - <surveyentry type="question" id="location" mandatory="true" boxsize="large"> - <statement>Please enter your location. (example mandatory text question)</statement> - </surveyentry> - <surveyentry type="number" id="age" min="0"> - <statement>Please enter your age (example non-mandatory number question)</statement> - </surveyentry> - <surveyentry type="radio" id="rating"> - <statement>Please rate this interface (example radio button question)</statement> - <option name="bad">Bad</option> - <option name="poor">Poor</option> - <option name="good">Good</option> - <option name="great">Great</option> - </surveyentry> - <surveyentry type="statement" id="test-thank-you"> - <statement>Thank you for taking this listening test. Please click 'submit' and your results will appear in the 'saves/' folder.</statement> - </surveyentry> - </survey> - <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"/> - <interfaceoption type="check" name="scalerange" min="25" max="75"/> - <interfaceoption type="show" name='playhead'/> - <interfaceoption type="show" name="page-count"/> - <interfaceoption type="show" name='volume'/> - <interfaceoption type="show" name='comments'/> - </interface> - </setup> - <page id='test-0' hostURL="media/example/" randomiseOrder='true' repeatCount='0' loop='true' loudness="-12"> - <commentboxprefix>Comment on fragment</commentboxprefix> - <interface> - <title>Depth</title> - </interface> - <audioelement url="0.wav" id="track-0"/> - <audioelement url="1.wav" id="track-1"/> - <survey location="before"> - <surveyentry type="statement" id="test-0-intro"> - <statement>A two way comparison using randomised element order, automatic loudness and synchronised looping.</statement> - </surveyentry> - </survey> - <survey location="after"> - <surveyentry type="question" id="genre-0" mandatory="true"> - <statement>Please enter the genre.</statement> - </surveyentry> - </survey> - </page> - <page id='test-1' hostURL="media/example/" randomiseOrder='true' repeatCount='0' loop='false' loudness="-12"> - <commentboxprefix>Comment on fragment</commentboxprefix> - <interface> - <title>Depth</title> - </interface> - <audioelement url="0.wav" id="track-2"/> - <audioelement url="1.wav" id="track-3"/> - <audioelement url="2.wav" id="track-4"/> - <audioelement url="3.wav" id="track-5"/> - <audioelement url="4.wav" id="track-6"/> - <audioelement url="5.wav" id="track-7"/> - <audioelement url="6.wav" id="track-8"/> - <survey location="before"> - <surveyentry type="statement" id="test-1-intro"> - <statement>A 7 way comparison using randomised element order and synchronised looping.</statement> - </surveyentry> - </survey> - <survey location="after"> - <surveyentry type="question" id="genre-1" mandatory="true"> - <statement>Please enter the genre.</statement> - </surveyentry> - </survey> - </page> -</waet> \ No newline at end of file + <waet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="test-schema.xsd"> + <setup interface="AB" projectReturn="save.php" randomiseOrder='true' poolSize="2" loudness="-23" playOne="true"> + <survey location="before"> + <surveyslider id="sliders" max="100" min="0"> + <statement>Sliders!!</statement> + <minText>Lefty</minText> + <maxText>Righty</maxText> + </surveyslider> + <surveyquestion id="sessionId" mandatory="true"> + <statement>Please enter your name.</statement> + </surveyquestion> + <surveycheckbox id="checkboxtest" mandatory="true"> + <statement>Please select with which activities you have any experience (example checkbox question)</statement> + <option name="musician">Playing a musical instrument</option> + <option name="soundengineer">Recording or mixing audio</option> + <option name="developer">Developing audio software</option> + <option name="hwdesigner">Designing or building audio hardware</option> + <option name="researcher">Research in the field of audio</option> + </surveycheckbox> + <surveystatement id="test-intro"> + <statement>This is an example of an 'AB'-style test, with two pages, using the test stimuli in 'example_eval/'.</statement> + </surveystatement> + </survey> + <survey location="after"> + <surveyentry type="question" id="location" mandatory="true" boxsize="large"> + <statement>Please enter your location. (example mandatory text question)</statement> + </surveyentry> + <surveyentry type="number" id="age" min="0"> + <statement>Please enter your age (example non-mandatory number question)</statement> + </surveyentry> + <surveyentry type="radio" id="rating"> + <statement>Please rate this interface (example radio button question)</statement> + <option name="bad">Bad</option> + <option name="poor">Poor</option> + <option name="good">Good</option> + <option name="great">Great</option> + </surveyentry> + <surveyentry type="statement" id="test-thank-you"> + <statement>Thank you for taking this listening test. Please click 'submit' and your results will appear in the 'saves/' folder.</statement> + </surveyentry> + </survey> + <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" /> + <interfaceoption type="check" name="scalerange" min="25" max="75" /> + <interfaceoption type="show" name='playhead' /> + <interfaceoption type="show" name="page-count" /> + <interfaceoption type="show" name='volume' /> + <interfaceoption type="show" name='comments' /> + </interface> + </setup> + <page id='test-0' hostURL="media/example/" randomiseOrder='true' repeatCount='0' loop='true' loudness="-12"> + <commentboxprefix>Comment on fragment</commentboxprefix> + <interface> + <title>Depth</title> + </interface> + <audioelement url="0.wav" id="track-0" /> + <audioelement url="1.wav" id="track-1" /> + <commentquestions> + <commentslider id="slider" min="0" max="100"> + <statement>Test</statement> + <minText>Left</minText> + <maxText>Right</maxText> + </commentslider> + </commentquestions> + <survey location="before"> + <surveyentry type="statement" id="test-0-intro"> + <statement>A two way comparison using randomised element order, automatic loudness and synchronised looping.</statement> + </surveyentry> + </survey> + <survey location="after"> + <surveyentry type="question" id="genre-0" mandatory="true"> + <statement>Please enter the genre.</statement> + </surveyentry> + </survey> + </page> + <page id='test-1' hostURL="media/example/" randomiseOrder='true' repeatCount='0' loop='false' loudness="-12"> + <commentboxprefix>Comment on fragment</commentboxprefix> + <interface> + <title>Depth</title> + </interface> + <audioelement url="0.wav" id="track-2" /> + <audioelement url="1.wav" id="track-3" /> + <audioelement url="2.wav" id="track-4" /> + <audioelement url="3.wav" id="track-5" /> + <audioelement url="4.wav" id="track-6" /> + <audioelement url="5.wav" id="track-7" /> + <audioelement url="6.wav" id="track-8" /> + <survey location="before"> + <surveyentry type="statement" id="test-1-intro"> + <statement>A 7 way comparison using randomised element order and synchronised looping.</statement> + </surveyentry> + </survey> + <survey location="after"> + <surveyentry type="question" id="genre-1" mandatory="true"> + <statement>Please enter the genre.</statement> + </surveyentry> + </survey> + </page> + </waet>
--- a/tests/examples/mushra_example.xml Thu Nov 03 11:02:56 2016 +0000 +++ b/tests/examples/mushra_example.xml Thu Nov 03 11:04:26 2016 +0000 @@ -101,24 +101,26 @@ <audioelement url="4.wav" gain="0.0" id="track-9"/> <audioelement url="5.wav" gain="0.0" id="track-10"/> <audioelement url="6.wav" gain="0.0" id="track-11" type="outside-reference"/> - <commentquestion id='mixingExperience' type="question"> - <statement>What is your general experience with numbers?</statement> - </commentquestion> - <commentquestion id="preference" type="radio"> - <statement>Please enter your overall preference</statement> - <option name="worst">Very Bad</option> - <option name="bad"></option> - <option name="OK">OK</option> - <option name="Good"></option> - <option name="Great">Great</option> - </commentquestion> - <commentquestion id="character" type="checkbox"> - <statement>Please describe the overall character</statement> - <option name="funky">Funky</option> - <option name="mellow">Mellow</option> - <option name="laidback">Laid back</option> - <option name="heavy">Heavy</option> - </commentquestion> + <commentquestions> + <commentquestion id='mixingExperience' type="question"> + <statement>What is your general experience with numbers?</statement> + </commentquestion> + <commentquestion id="preference" type="radio"> + <statement>Please enter your overall preference</statement> + <option name="worst">Very Bad</option> + <option name="bad"></option> + <option name="OK">OK</option> + <option name="Good"></option> + <option name="Great">Great</option> + </commentquestion> + <commentquestion id="character" type="checkbox"> + <statement>Please describe the overall character</statement> + <option name="funky">Funky</option> + <option name="mellow">Mellow</option> + <option name="laidback">Laid back</option> + <option name="heavy">Heavy</option> + </commentquestion> + </commentquestions> <survey location="before"> <surveyentry type="statement" id="test-1-intro"> <statement>Example of a 'MUSHRA' style interface with hidden anchor 'zero' (which needs to be below 20%), looping of the samples, randomisation of marker labels, mandatory moving of every sample, and a forced scale usage of at least 25%-75%.</statement>
--- a/tests/examples/project.xml Thu Nov 03 11:02:56 2016 +0000 +++ b/tests/examples/project.xml Thu Nov 03 11:04:26 2016 +0000 @@ -6,7 +6,7 @@ <statement>Please enter your name.</statement> <conditional check="equals" value="John" jumpToOnPass="test-intro" jumpToOnFail="checkboxtest"/> </surveyentry> - <surveyentry type="checkbox" id="checkboxtest" mandatory="true"> + <surveyentry type="checkbox" id="checkboxtest" mandatory="true" min="2" max="4"> <statement>Please select with which activities you have any experience (example checkbox question)</statement> <option name="musician">Playing a musical instrument</option> <option name="soundengineer">Recording or mixing audio</option> @@ -111,24 +111,26 @@ <audioelement url="4.wav" gain="0.0" id="track-9"/> <audioelement url="5.wav" gain="0.0" id="track-10"/> <audioelement url="6.wav" gain="0.0" id="track-11" type="outside-reference"/> - <commentquestion id='mixingExperience' type="question"> - <statement>What is your general experience with numbers?</statement> - </commentquestion> - <commentquestion id="preference" type="radio"> - <statement>Please enter your overall preference</statement> - <option name="worst">Very Bad</option> - <option name="bad"></option> - <option name="OK">OK</option> - <option name="Good"></option> - <option name="Great">Great</option> - </commentquestion> - <commentquestion id="character" type="checkbox"> - <statement>Please describe the overall character</statement> - <option name="funky">Funky</option> - <option name="mellow">Mellow</option> - <option name="laidback">Laid back</option> - <option name="heavy">Heavy</option> - </commentquestion> + <commentquestions> + <commentquestion id='mixingExperience'> + <statement>What is your general experience with numbers?</statement> + </commentquestion> + <commentradio id="preference"> + <statement>Please enter your overall preference</statement> + <option name="worst">Very Bad</option> + <option name="bad"></option> + <option name="OK">OK</option> + <option name="Good"></option> + <option name="Great">Great</option> + </commentradio> + <commentcheckbox id="character" type="checkbox"> + <statement>Please describe the overall character</statement> + <option name="funky">Funky</option> + <option name="mellow">Mellow</option> + <option name="laidback">Laid back</option> + <option name="heavy">Heavy</option> + </commentcheckbox> + </commentquestions> <survey location="before"> <surveyentry type="statement" id="test-1-intro"> <statement>Example of an 'APE' style interface with hidden anchor 'zero' (which needs to be below 20%), looping of the samples, randomisation of marker labels, mandatory moving of every sample, and a forced scale usage of at least 25%-75%.</statement>
--- a/xml/test-schema.xsd Thu Nov 03 11:02:56 2016 +0000 +++ b/xml/test-schema.xsd Thu Nov 03 11:04:26 2016 +0000 @@ -26,8 +26,8 @@ </xs:restriction> </xs:simpleType> </xs:attribute> - - <xs:attribute name="playOne" type="xs:boolean" default="false"/> + + <xs:attribute name="playOne" type="xs:boolean" default="false" /> <!-- define complex elements--> <xs:element name="waet"> @@ -75,7 +75,7 @@ <xs:element name="commentboxprefix" type="xs:string" minOccurs="0" maxOccurs="1" /> <xs:element ref="interface" minOccurs="1" maxOccurs="unbounded" /> <xs:element ref="audioelement" minOccurs="1" maxOccurs="unbounded" /> - <xs:element ref="commentquestion" minOccurs="0" maxOccurs="unbounded" /> + <xs:element ref="commentquestions" minOccurs="0" maxOccurs="1" /> <xs:element ref="survey" minOccurs="0" maxOccurs="2" /> </xs:sequence> <xs:attribute ref="id" use="required" /> @@ -96,6 +96,7 @@ </xs:restriction> </xs:simpleType> </xs:attribute> + <xs:attribute name="labelStart" use="optional" type="xs:string" default="" /> <xs:attribute ref="poolSize" /> <xs:attribute ref="alwaysInclude" /> <xs:attribute ref="preSilence" /> @@ -205,10 +206,21 @@ </xs:complexType> </xs:element> - <xs:element name="commentquestion"> + <xs:element name="commentquestions"> + <xs:complexType> + <xs:choice maxOccurs="unbounded"> + <xs:element name="commentquestion" maxOccurs="unbounded" /> + <xs:element name="commentradio" maxOccurs="unbounded" /> + <xs:element name="commentcheckbox" maxOccurs="unbounded" /> + <xs:element name="commentslider" maxOccurs="unbounded" /> + </xs:choice> + </xs:complexType> + </xs:element> + + <xs:element name="commentradio"> <xs:complexType> <xs:sequence> - <xs:element ref="statement" minOccurs="0" maxOccurs="1" /> + <xs:element ref="statement" minOccurs="1" maxOccurs="1" /> <xs:element name="option" minOccurs="0" maxOccurs="unbounded"> <xs:complexType> <xs:simpleContent> @@ -221,22 +233,202 @@ </xs:sequence> <xs:attribute ref="id" use="optional" /> <xs:attribute ref="name" use="optional" /> - <xs:attribute name="type" default="question"> + </xs:complexType> + </xs:element> + + <xs:element name="commentcheckbox"> + <xs:complexType> + <xs:sequence> + <xs:element ref="statement" minOccurs="1" maxOccurs="1" /> + <xs:element name="option" minOccurs="0" maxOccurs="unbounded"> + <xs:complexType> + <xs:simpleContent> + <xs:extension base="xs:string"> + <xs:attribute ref="name" /> + </xs:extension> + </xs:simpleContent> + </xs:complexType> + </xs:element> + </xs:sequence> + <xs:attribute ref="id" use="optional" /> + <xs:attribute ref="name" use="optional" /> + </xs:complexType> + </xs:element> + + <xs:element name="commentquestion"> + <xs:complexType> + <xs:sequence> + <xs:element ref="statement" minOccurs="1" maxOccurs="1" /> + </xs:sequence> + <xs:attribute ref="id" use="optional" /> + <xs:attribute ref="name" use="optional" /> + </xs:complexType> + </xs:element> + + <xs:element name="commentslider"> + <xs:complexType> + <xs:sequence> + <xs:element ref="statement" minOccurs="1" maxOccurs="1" /> + <xs:element name="minText" minOccurs="0" maxOccurs="1" type="xs:string" /> + <xs:element name="maxText" minOccurs="0" maxOccurs="1" type="xs:string" /> + </xs:sequence> + <xs:attribute ref="id" use="optional" /> + <xs:attribute ref="name" use="optional" /> + <xs:attribute name="min" type="xs:decimal" use="required" /> + <xs:attribute name="max" type="xs:decimal" use="required" /> + <xs:attribute name="step" type="xs:decimal" use="optional" default="1" /> + <xs:attribute name="value" type="xs:decimal" use="optional" /> + </xs:complexType> + </xs:element> + + <xs:element name="conditional"> + <xs:complexType> + <xs:attribute name="check" use="required"> <xs:simpleType> <xs:restriction base="xs:string"> - <xs:enumeration value="question" /> - <xs:enumeration value="radio" /> - <xs:enumeration value="checkbox" /> + <xs:enumeration value="equals" /> + <xs:enumeration value="lessThan" /> + <xs:enumeration value="greaterThan" /> + <xs:enumeration value="stringContains" /> + </xs:restriction> + </xs:simpleType> + </xs:attribute> + <xs:attribute name="value" type="xs:string" use="optional" /> + <xs:attribute name="jumpToOnPass" type="xs:string" use="optional" /> + <xs:attribute name="jumpToOnFail" type="xs:string" use="optional" /> + </xs:complexType> + </xs:element> + + <xs:element name="surveyquestion"> + <xs:complexType> + <xs:sequence> + <xs:element ref="statement" minOccurs="1" maxOccurs="1" /> + <xs:element ref="conditional" minOccurs="0" maxOccurs="unbounded" /> + </xs:sequence> + <xs:attribute ref="id" use="required" /> + <xs:attribute ref="name" /> + <xs:attribute ref="mandatory" /> + <xs:attribute name="boxsize" default="normal"> + <xs:simpleType> + <xs:restriction base="xs:string"> + <xs:enumeration value="normal" /> + <xs:enumeration value="large" /> + <xs:enumeration value="small" /> + <xs:enumeration value="huge" /> </xs:restriction> </xs:simpleType> </xs:attribute> </xs:complexType> </xs:element> + <xs:element name="surveyradio"> + <xs:complexType> + <xs:sequence> + <xs:element ref="statement" minOccurs="1" maxOccurs="1" /> + <xs:element name="option" minOccurs="0" maxOccurs="unbounded"> + <xs:complexType> + <xs:simpleContent> + <xs:extension base="xs:string"> + <xs:attribute ref="name" /> + </xs:extension> + </xs:simpleContent> + </xs:complexType> + </xs:element> + <xs:element ref="conditional" minOccurs="0" maxOccurs="unbounded" /> + </xs:sequence> + <xs:attribute ref="id" use="required" /> + <xs:attribute ref="name" /> + <xs:attribute ref="mandatory" /> + <xs:attribute name="min" type="xs:decimal" /> + <xs:attribute name="max" type="xs:decimal" /> + </xs:complexType> + </xs:element> + + <xs:element name="surveycheckbox"> + <xs:complexType> + <xs:sequence> + <xs:element ref="statement" minOccurs="1" maxOccurs="1" /> + <xs:element name="option" minOccurs="0" maxOccurs="unbounded"> + <xs:complexType> + <xs:simpleContent> + <xs:extension base="xs:string"> + <xs:attribute ref="name" /> + </xs:extension> + </xs:simpleContent> + </xs:complexType> + </xs:element> + <xs:element ref="conditional" minOccurs="0" maxOccurs="unbounded" /> + </xs:sequence> + <xs:attribute ref="id" use="required" /> + <xs:attribute ref="name" /> + <xs:attribute ref="mandatory" /> + <xs:attribute name="min" type="xs:decimal" /> + <xs:attribute name="max" type="xs:decimal" /> + </xs:complexType> + </xs:element> + + <xs:element name="surveystatement"> + <xs:complexType> + <xs:sequence> + <xs:element ref="statement" minOccurs="1" maxOccurs="1" /> + </xs:sequence> + <xs:attribute ref="id" use="required" /> + </xs:complexType> + </xs:element> + + <xs:element name="surveynumber"> + <xs:complexType> + <xs:sequence> + <xs:element ref="statement" minOccurs="1" maxOccurs="1" /> + <xs:element ref="conditional" minOccurs="0" maxOccurs="unbounded" /> + </xs:sequence> + <xs:attribute ref="id" use="required" /> + <xs:attribute ref="name" /> + <xs:attribute ref="mandatory" /> + <xs:attribute name="min" type="xs:decimal" /> + <xs:attribute name="max" type="xs:decimal" /> + </xs:complexType> + </xs:element> + + <xs:element name="surveyslider"> + <xs:complexType> + <xs:sequence> + <xs:element ref="statement" minOccurs="1" maxOccurs="1" /> + <xs:element name="minText" minOccurs="0" maxOccurs="1" type="xs:string" /> + <xs:element name="maxText" minOccurs="0" maxOccurs="1" type="xs:string" /> + <xs:element ref="conditional" minOccurs="0" maxOccurs="unbounded" /> + </xs:sequence> + <xs:attribute ref="id" use="required" /> + <xs:attribute ref="name" /> + <xs:attribute name="min" use="required" type="xs:decimal" /> + <xs:attribute name="max" use="required" type="xs:decimal" /> + </xs:complexType> + </xs:element> + + <xs:element name="surveyvideo"> + <xs:complexType> + <xs:sequence> + <xs:element ref="statement" minOccurs="1" maxOccurs="1" /> + </xs:sequence> + <xs:attribute ref="id" use="required" /> + <xs:attribute name="url" use="required" type="xs:string" /> + </xs:complexType> + </xs:element> + + <xs:element name="surveyyoutube"> + <xs:complexType> + <xs:sequence> + <xs:element ref="statement" minOccurs="1" maxOccurs="1" /> + </xs:sequence> + <xs:attribute ref="id" use="required" /> + <xs:attribute name="url" use="required" type="xs:string" /> + </xs:complexType> + </xs:element> + <xs:element name="survey"> <xs:complexType> - <xs:sequence> - <xs:element name="surveyentry" minOccurs="0" maxOccurs="unbounded"> + <xs:choice maxOccurs="unbounded"> + <xs:element name="surveyentry" maxOccurs="unbounded"> <xs:complexType> <xs:sequence> <xs:element ref="statement" minOccurs="1" maxOccurs="1" /> @@ -249,23 +441,6 @@ </xs:simpleContent> </xs:complexType> </xs:element> - <xs:element name="conditional" minOccurs="0" maxOccurs="unbounded"> - <xs:complexType> - <xs:attribute name="check" use="required"> - <xs:simpleType> - <xs:restriction base="xs:string"> - <xs:enumeration value="equals"/> - <xs:enumeration value="lessThan"/> - <xs:enumeration value="greaterThan"/> - <xs:enumeration value="stringContains"/> - </xs:restriction> - </xs:simpleType> - </xs:attribute> - <xs:attribute name="value" type="xs:string" use="optional"/> - <xs:attribute name="jumpToOnPass" type="xs:string" use="optional"/> - <xs:attribute name="jumpToOnFail" type="xs:string" use="optional"/> - </xs:complexType> - </xs:element> </xs:sequence> <xs:attribute ref="id" use="required" /> <xs:attribute ref="name" /> @@ -295,10 +470,18 @@ </xs:restriction> </xs:simpleType> </xs:attribute> - <xs:attribute name="url" type="xs:string" use="optional"/> + <xs:attribute name="url" type="xs:string" use="optional" default="" /> </xs:complexType> </xs:element> - </xs:sequence> + <xs:element name="surveyquestion" maxOccurs="unbounded" /> + <xs:element name="surveyradio" maxOccurs="unbounded" /> + <xs:element name="surveycheckbox" maxOccurs="unbounded" /> + <xs:element name="surveystatement" maxOccurs="unbounded" /> + <xs:element name="surveynumber" maxOccurs="unbounded" /> + <xs:element name="surveyslider" maxOccurs="unbounded" /> + <xs:element name="surveyvideo" maxOccurs="unbounded" /> + <xs:element name="surveyyoutube" maxOccurs="unbounded" /> + </xs:choice> <xs:attribute name="location"> <xs:simpleType> <xs:restriction base="xs:string">