Mercurial > hg > webaudioevaluationtool
changeset 2797:9e060c431ab3
Merge branch 'Dev_main' into vnext
author | Nicholas Jillings <n.g.r.jillings@se14.qmul.ac.uk> |
---|---|
date | Sun, 23 Apr 2017 11:32:45 +0100 |
parents | c0cb7c416d8e (current diff) 35037fb7a843 (diff) |
children | 9e2363f78912 |
files | |
diffstat | 15 files changed, 335 insertions(+), 104 deletions(-) [+] |
line wrap: on
line diff
--- a/css/core.css Sun Apr 23 10:17:33 2017 +0100 +++ b/css/core.css Sun Apr 23 11:32:45 2017 +0100 @@ -201,7 +201,7 @@ position: absolute; top: 20px; left: 50px; - width: 250px%; + width: 250px; padding: 5px; } div#master-volume-root { @@ -277,3 +277,11 @@ div.comment-box-playing { background-color: #FFDDDD; } +div#imageController { + align-content: center; + text-align: center; + height: 250px; +} +div#imageController img { + max-height: 250px; +}
--- a/interfaces/AB.css Sun Apr 23 10:17:33 2017 +0100 +++ b/interfaces/AB.css Sun Apr 23 11:32:45 2017 +0100 @@ -2,6 +2,10 @@ /* Set the background colour (note US English spelling) to grey*/ background-color: #fff } +div#feedbackHolder { + display: flex; + flex-direction: column; +} div.pageTitle { width: auto; height: 20px; @@ -34,13 +38,20 @@ position: inherit; margin: 0px 5px; } +div#box-holders { + width: 100%; + text-align: center; +} +div#playback-holder { + float: none; +} div.comparator-holder { width: 260px; height: 300px; border: black 1px solid; - float: left; padding-top: 5px; margin: 25px; + display: inline-block; } div.comparator-selector { width: 248px; @@ -49,6 +60,20 @@ position: relative; background-color: #FF0000; border-radius: 20px; + text-align: center; + display: block; + margin: auto; +} +div.comparator-image { + background-color: rgba(255,255,255,0); +} +img.comparator-image { + width: inherit; + height: inherit; + z-index: -1; + position: absolute; + display: inline; + right: 0px; } div.disabled { background-color: #AAA; @@ -72,8 +97,3 @@ float: left; margin: 0px 5px; } -div#master-volume-holder { - position: absolute; - top: 10px; - left: 120px; -}
--- a/interfaces/AB.js Sun Apr 23 10:17:33 2017 +0100 +++ b/interfaces/AB.js Sun Apr 23 11:32:45 2017 +0100 @@ -73,9 +73,7 @@ // 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"; @@ -126,10 +124,15 @@ document.getElementById("test-title").textContent = audioHolderObject.title; } - if (interfaceObj.title !== null) { + if (interfaceObj.title !== undefined) { document.getElementById("pageTitle").textContent = interfaceObj.title; } + if (interfaceObj.image !== undefined) { + feedbackHolder.insertBefore(interfaceContext.imageHolder.root, document.getElementById("box-holders")); + interfaceContext.imageHolder.setImage(interfaceObj.image); + } + var interfaceOptions = interfaceObj.options; // Clear the interfaceElements { @@ -210,6 +213,13 @@ this.box.id = 'comparator-' + text; this.selector = document.createElement('div'); this.selector.className = 'comparator-selector disabled'; + if (audioElement.specification.image) { + this.selector.className += " comparator-image"; + var image = document.createElement("img"); + image.src = audioElement.specification.image; + image.className = "comparator-image"; + this.selector.appendChild(image); + } var selectorText = document.createElement('span'); selectorText.textContent = text; this.selector.appendChild(selectorText); @@ -260,7 +270,7 @@ } else if (event.currentTarget === this.playback) { this.playbackClicked(); } - } + }; this.playback.addEventListener("click", this); this.selector.addEventListener("click", this); @@ -375,9 +385,6 @@ 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.width = boxW + 'px'; var outsideRef = document.getElementById('outside-reference'); if (outsideRef !== null) { @@ -395,35 +402,35 @@ switch (checks[i].name) { case 'fragmentPlayed': // Check if all fragments have been played - checkState = interfaceContext.checkAllPlayed(); + checkState = interfaceContext.checkAllPlayed(checks[i].errorMessage); if (checkState === false) { canContinue = false; } break; case 'fragmentFullPlayback': // Check all fragments have been played to their full length - checkState = interfaceContext.checkFragmentsFullyPlayed(); + checkState = interfaceContext.checkFragmentsFullyPlayed(checks[i].errorMessage); if (checkState === false) { canContinue = false; } break; case 'fragmentMoved': // Check all fragment sliders have been moved. - checkState = interfaceContext.checkAllMoved(); + checkState = interfaceContext.checkAllMoved(checks[i].errorMessage); if (checkState === false) { canContinue = false; } break; case 'fragmentComments': // Check all fragment sliders have been moved. - checkState = interfaceContext.checkAllCommented(); + checkState = interfaceContext.checkAllCommented(checks[i].errorMessage); if (checkState === false) { canContinue = false; } break; case 'scalerange': // Check the scale has been used effectively - checkState = interfaceContext.checkScaleRange(); + checkState = interfaceContext.checkScaleRange(checks[i].errorMessage); if (checkState === false) { canContinue = false; }
--- a/interfaces/ABX.css Sun Apr 23 10:17:33 2017 +0100 +++ b/interfaces/ABX.css Sun Apr 23 11:32:45 2017 +0100 @@ -20,13 +20,20 @@ height: 40px; font-size: 1.2em; } +div#box-holders { + width: 100%; + text-align: center; +} +div#playback-holder { + float: none; +} div.comparator-holder { width: 260px; height: 300px; border: black 1px solid; - float: left; padding-top: 5px; margin: 25px; + display: inline-block; } div.comparator-selector { width: 248px; @@ -35,6 +42,9 @@ position: relative; background-color: #FF0000; border-radius: 20px; + text-align: center; + display: block; + margin: auto; } div.disabled { background-color: #AAA; @@ -61,8 +71,3 @@ float: left; margin: 0px 5px; } -div#master-volume-holder { - position: absolute; - top: 10px; - left: 120px; -}
--- a/interfaces/ABX.js Sun Apr 23 10:17:33 2017 +0100 +++ b/interfaces/ABX.js Sun Apr 23 11:32:45 2017 +0100 @@ -129,6 +129,11 @@ document.getElementById("pageTitle").textContent = interfaceObj.title; } + if (interfaceObj.image !== undefined) { + feedbackHolder.insertBefore(interfaceContext.imageHolder.root, document.getElementById("box-holders")); + interfaceContext.imageHolder.setImage(interfaceObj.image); + } + interfaceContext.comparator = new comparator(page); var interfaceOptions = interfaceObj.options; @@ -417,16 +422,16 @@ switch (checks[i].name) { case 'fragmentPlayed': // Check if all fragments have been played - checkState = interfaceContext.checkAllPlayed(); + checkState = interfaceContext.checkAllPlayed(checks[i].errorMessage); break; case 'fragmentFullPlayback': // Check all fragments have been played to their full length - checkState = interfaceContext.checkFragmentsFullyPlayed(); + checkState = interfaceContext.checkFragmentsFullyPlayed(checks[i].errorMessage); break; case 'fragmentMoved': // Check all fragment sliders have been moved. - checkState = interfaceContext.checkAllMoved(); + checkState = interfaceContext.checkAllMoved(checks[i].errorMessage); break; case 'fragmentComments': // Check all fragment sliders have been moved.
--- a/interfaces/ape.css Sun Apr 23 10:17:33 2017 +0100 +++ b/interfaces/ape.css Sun Apr 23 11:32:45 2017 +0100 @@ -89,3 +89,11 @@ top: 10px; left: 120px; } +div.imageController { + align-content: center; + text-align: center; + height: 250px; +} +div.imageController img { + max-height: 250px; +}
--- a/interfaces/ape.js Sun Apr 23 10:17:33 2017 +0100 +++ b/interfaces/ape.js Sun Apr 23 11:32:45 2017 +0100 @@ -106,7 +106,7 @@ return { min: Math.min(a.min, v), max: Math.max(a.max, v) - } + }; }, { min: 100, max: 0 @@ -253,7 +253,7 @@ var interfaceObj = interfaceContext.getCombinedInterfaces(audioHolderObject); interfaceObj.forEach(function (interfaceObjectInstance) { // Create the div box to center align - interfaceContext.interfaceSliders.push(new interfaceSliderHolder(interfaceObjectInstance)); + interfaceContext.interfaceSliders.push(new interfaceSliderHolder(interfaceObjectInstance, audioHolderObject)); }); interfaceObj.forEach(function (interface) { interface.options.forEach(function (option) { @@ -447,7 +447,7 @@ //testWaitIndicator(); } -function interfaceSliderHolder(interfaceObject) { +function interfaceSliderHolder(interfaceObject, page) { this.sliders = []; this.metrics = []; this.id = document.getElementsByClassName("sliderCanvasDiv").length; @@ -456,6 +456,21 @@ this.sliderDOM = document.createElement('div'); this.sliderDOM.className = 'sliderCanvasDiv'; this.sliderDOM.id = 'sliderCanvasHolder-' + this.id; + this.imageHolder = (function () { + var imageController = {}; + imageController.root = document.createElement("div"); + imageController.root.className = "imageController"; + imageController.img = document.createElement("img"); + imageController.root.appendChild(imageController.img); + imageController.setImage = function (src) { + imageController.img.src = ""; + if (typeof src !== "string" || src.length === undefined) { + return; + } + imageController.img.src = src; + }; + return imageController; + })(); var pagetitle = document.createElement('div'); pagetitle.className = "pageTitle"; @@ -470,6 +485,12 @@ pagetitle.appendChild(titleSpan); this.sliderDOM.appendChild(pagetitle); + if (interfaceObject.image !== undefined || page.audioElements.some(function (a) { + return a.image !== undefined; + })) { + this.sliderDOM.appendChild(this.imageHolder.root); + this.imageHolder.setImage(interfaceObject.image); + } // Create the slider box to hold the slider elements this.canvas = document.createElement('div'); if (this.name !== undefined) @@ -555,6 +576,18 @@ scaleObj.style.left = Math.floor((pixelPosition - ($(scaleObj).width() / 2))) + 'px'; } }; + + this.playing = function (id) { + var node = audioEngineContext.audioObjects.find(function (a) { + return a.id == id; + }); + if (node === undefined) { + this.imageHolder.setImage(interfaceObject.image || ""); + return; + } + var imgurl = node.specification.image || interfaceObject.image || ""; + this.imageHolder.setImage(imgurl); + } } function sliderObject(audioObject, interfaceObjects, index) { @@ -598,6 +631,9 @@ $('.track-slider').addClass('track-slider-disabled'); $('.outside-reference').addClass('track-slider-disabled'); } + interfaceContext.interfaceSliders.forEach(function (ts) { + ts.playing(this.parent.id); + }, this); }; this.stopPlayback = function () { if (this.playing) { @@ -714,28 +750,28 @@ } for (var i = 0; i < checks.length; i++) { + var checkState = true; if (checks[i].type == 'check') { - var checkState = true; switch (checks[i].name) { case 'fragmentPlayed': // Check if all fragments have been played - checkState = interfaceContext.checkAllPlayed(); + checkState = interfaceContext.checkAllPlayed(checks[i].errorMessage); break; case 'fragmentFullPlayback': // Check all fragments have been played to their full length - checkState = interfaceContext.checkFragmentsFullyPlayed(); + checkState = interfaceContext.checkFragmentsFullyPlayed(checks[i].errorMessage); break; case 'fragmentMoved': // Check all fragment sliders have been moved. - checkState = interfaceContext.checkAllMoved(); + checkState = interfaceContext.checkAllMoved(checks[i].errorMessage); break; case 'fragmentComments': // Check all fragment sliders have been moved. - checkState = interfaceContext.checkAllCommented(); + checkState = interfaceContext.checkAllCommented(checks[i].errorMessage); break; case 'scalerange': // Check the scale is used to its full width outlined by the node - checkState = interfaceContext.checkScaleRange(); + checkState = interfaceContext.checkScaleRange(checks[i].errorMessage); break; default: console.log("WARNING - Check option " + checks[i].name + " is not supported on this interface");
--- a/interfaces/discrete.js Sun Apr 23 10:17:33 2017 +0100 +++ b/interfaces/discrete.js Sun Apr 23 11:32:45 2017 +0100 @@ -137,6 +137,13 @@ document.getElementById("pageTitle").textContent = interfaceObj.title; } + if (interfaceObj.image !== undefined || audioHolderObject.audioElements.some(function (elem) { + return elem.image !== undefined; + })) { + document.getElementById("testContent").insertBefore(interfaceContext.imageHolder.root, document.getElementById("slider")); + interfaceContext.imageHolder.setImage(interfaceObj.image); + } + // Delete outside reference document.getElementById("outside-reference-holder").innerHTML = ""; @@ -339,6 +346,9 @@ $('.track-slider-button').attr("disabled", "true"); } interfaceContext.commentBoxes.highlightById(audioObject.id); + if (audioObject.specification.image !== undefined) { + interfaceContext.imageHolder.setImage(audioObject.specification.image); + } }; this.stopPlayback = function () { // Called by audioObject when playback stops @@ -354,6 +364,11 @@ if (box) { box.highlight(false); } + if (audioObject.specification.parent.interfaces[0].image !== undefined) { + interfaceContext.imageHolder.setImage(audioObject.specification.parent.interfaces[0].image); + } else { + interfaceContext.imageHolder.setImage(""); + } } }; @@ -474,24 +489,24 @@ switch (checks[i].name) { case 'fragmentPlayed': // Check if all fragments have been played - checkState = interfaceContext.checkAllPlayed(); + checkState = interfaceContext.checkAllPlayed(checks[i].errorMessage); break; case 'fragmentFullPlayback': // Check all fragments have been played to their full length - checkState = interfaceContext.checkAllPlayed(); + checkState = interfaceContext.checkAllPlayed(checks[i].errorMessage); console.log('NOTE: fragmentFullPlayback not currently implemented, performing check fragmentPlayed instead'); break; case 'fragmentMoved': // Check all fragment sliders have been moved. - checkState = interfaceContext.checkAllMoved(); + checkState = interfaceContext.checkAllMoved(checks[i].errorMessage); break; case 'fragmentComments': // Check all fragment sliders have been moved. - checkState = interfaceContext.checkAllCommented(); + checkState = interfaceContext.checkAllCommented(checks[i].errorMessage); break; case 'scalerange': // Check the scale has been used effectively - checkState = interfaceContext.checkScaleRange(checks[i].min, checks[i].max); + checkState = interfaceContext.checkScaleRange(checks[i].errorMessage); break; default: console.log("WARNING - Check option " + checks[i].check + " is not supported on this interface");
--- a/interfaces/horizontal-sliders.js Sun Apr 23 10:17:33 2017 +0100 +++ b/interfaces/horizontal-sliders.js Sun Apr 23 11:32:45 2017 +0100 @@ -137,6 +137,13 @@ document.getElementById("pageTitle").textContent = interfaceObj.title; } + if (interfaceObj.image !== undefined || audioHolderObject.audioElements.some(function (elem) { + return elem.image !== undefined; + })) { + document.getElementById("testContent").insertBefore(interfaceContext.imageHolder.root, document.getElementById("slider")); + interfaceContext.imageHolder.setImage(interfaceObj.image); + } + // Delete outside reference document.getElementById("outside-reference-holder").innerHTML = ""; @@ -297,6 +304,9 @@ $(outsideReference).removeClass('track-slider-playing'); } interfaceContext.commentBoxes.highlightById(audioObject.id); + if (audioObject.specification.image !== undefined) { + interfaceContext.imageHolder.setImage(audioObject.specification.image); + } }; this.stopPlayback = function () { // Called when playback has stopped. This gets called even if playback never started! @@ -308,6 +318,11 @@ if (box) { box.highlight(false); } + if (audioObject.specification.parent.interfaces[0].image !== undefined) { + interfaceContext.imageHolder.setImage(audioObject.specification.parent.interfaces[0].image); + } else { + interfaceContext.imageHolder.setImage(""); + } }; this.getValue = function () { // Return the current value of the object. If there is no value, return 0 @@ -420,24 +435,24 @@ switch (checks[i].name) { case 'fragmentPlayed': // Check if all fragments have been played - checkState = interfaceContext.checkAllPlayed(); + checkState = interfaceContext.checkAllPlayed(checks[i].errorMessage); break; case 'fragmentFullPlayback': // Check all fragments have been played to their full length - checkState = interfaceContext.checkAllPlayed(); + checkState = interfaceContext.checkAllPlayed(checks[i].errorMessage); console.log('NOTE: fragmentFullPlayback not currently implemented, performing check fragmentPlayed instead'); break; case 'fragmentMoved': // Check all fragment sliders have been moved. - checkState = interfaceContext.checkAllMoved(); + checkState = interfaceContext.checkAllMoved(checks[i].errorMessage); break; case 'fragmentComments': // Check all fragment sliders have been moved. - checkState = interfaceContext.checkAllCommented(); + checkState = interfaceContext.checkAllCommented(checks[i].errorMessage); break; case 'scalerange': // Check the scale has been used effectively - checkState = interfaceContext.checkScaleRange(checks[i].min, checks[i].max); + checkState = interfaceContext.checkScaleRange(checks[i].errorMessage); break; default:
--- a/interfaces/mushra.js Sun Apr 23 10:17:33 2017 +0100 +++ b/interfaces/mushra.js Sun Apr 23 11:32:45 2017 +0100 @@ -137,6 +137,13 @@ document.getElementById("pageTitle").textContent = interfaceObj.title; } + if (interfaceObj.image !== undefined || audioHolderObject.audioElements.some(function (elem) { + return elem.image !== undefined; + })) { + document.getElementById("testContent").insertBefore(interfaceContext.imageHolder.root, document.getElementById("slider")); + interfaceContext.imageHolder.setImage(interfaceObj.image); + } + // Delete outside reference var outsideReferenceHolder = document.getElementById("outside-reference-holder"); outsideReferenceHolder.innerHTML = ""; @@ -338,6 +345,9 @@ }); } } + if (audioObject.specification.image !== undefined) { + interfaceContext.imageHolder.setImage(audioObject.specification.image); + } }; this.stopPlayback = function () { // Called when playback has stopped. This gets called even if playback never started! @@ -354,14 +364,23 @@ if (box) { box.highlight(false); } + if (audioObject.specification.parent.interfaces[0].image !== undefined) { + interfaceContext.imageHolder.setImage(audioObject.specification.parent.interfaces[0].image); + } else { + interfaceContext.imageHolder.setImage(""); + } }; this.getValue = function () { return this.slider.value; }; this.resize = function (event) { - this.holder.style.height = window.innerHeight - 200 + 'px'; - this.slider.style.height = window.innerHeight - 250 + 'px'; + var imgHeight = 0; + if (document.getElementById("imageController")) { + imgHeight = $(interfaceContext.imageHolder.root).height(); + } + this.holder.style.height = window.innerHeight - 200 - imgHeight + 'px'; + this.slider.style.height = window.innerHeight - 250 - imgHeight + 'px'; }; this.updateLoading = function (progress) { progress = String(progress); @@ -389,7 +408,11 @@ // Function called when the window has been resized. // MANDATORY FUNCTION - var outsideRef = document.getElementById('outside-reference'); + var outsideRef = document.getElementById('outside-reference'), + imageHeight = 0; + if (document.getElementById("imageController")) { + imageHeight = $(interfaceContext.imageHolder.root).height(); + } if (outsideRef !== null) { outsideRef.style.left = (window.innerWidth - 120) / 2 + 'px'; } @@ -398,7 +421,7 @@ var numObj = document.getElementsByClassName('track-slider').length; var totalWidth = (numObj - 1) * 150 + 100; var diff = (window.innerWidth - totalWidth) / 2; - document.getElementById('slider').style.height = window.innerHeight - 180 + 'px'; + document.getElementById('slider').style.height = window.innerHeight - 180 - imageHeight + 'px'; if (diff <= 0) { diff = 0; } @@ -409,7 +432,7 @@ } } document.getElementById('scale-holder').style.marginLeft = (diff - 100) + 'px'; - document.getElementById('scale-text-holder').style.height = window.innerHeight - 194 + 'px'; + document.getElementById('scale-text-holder').style.height = window.innerHeight - imageHeight - 194 + 'px'; // Cheers edge for making me delete a canvas every resize. var canvas = document.getElementById('scale-canvas'); var new_canvas = document.createElement("canvas"); @@ -417,7 +440,7 @@ canvas.parentElement.appendChild(new_canvas); canvas.parentElement.removeChild(canvas); new_canvas.width = totalWidth; - new_canvas.height = window.innerHeight - 194; + new_canvas.height = window.innerHeight - 194 - imageHeight; drawScale(); } @@ -476,24 +499,24 @@ switch (checks[i].name) { case 'fragmentPlayed': // Check if all fragments have been played - checkState = interfaceContext.checkAllPlayed(); + checkState = interfaceContext.checkAllPlayed(checks[i].errorMessage); break; case 'fragmentFullPlayback': // Check all fragments have been played to their full length - checkState = interfaceContext.checkAllPlayed(); + checkState = interfaceContext.checkAllPlayed(checks[i].errorMessage); console.log('NOTE: fragmentFullPlayback not currently implemented, performing check fragmentPlayed instead'); break; case 'fragmentMoved': // Check all fragment sliders have been moved. - checkState = interfaceContext.checkAllMoved(); + checkState = interfaceContext.checkAllMoved(checks[i].errorMessage); break; case 'fragmentComments': // Check all fragment sliders have been moved. - checkState = interfaceContext.checkAllCommented(); + checkState = interfaceContext.checkAllCommented(checks[i].errorMessage); break; case 'scalerange': // Check the scale has been used effectively - checkState = interfaceContext.checkScaleRange(checks[i].min, checks[i].max); + checkState = interfaceContext.checkScaleRange(checks[i].errorMessage); break; default: console.log("WARNING - Check option " + checks[i].check + " is not supported on this interface");
--- a/interfaces/timeline.js Sun Apr 23 10:17:33 2017 +0100 +++ b/interfaces/timeline.js Sun Apr 23 11:32:45 2017 +0100 @@ -489,14 +489,14 @@ switch (checks[i].name) { case 'fragmentPlayed': //Check if all fragments have been played - checkState = interfaceContext.checkAllPlayed(); + checkState = interfaceContext.checkAllPlayed(checks[i].errorMessage); break; case 'fragmentFullPlayback': //Check if all fragments have played to their full length - checkState = interfaceContext.checkFragmentsFullyPlayed(); + checkState = interfaceContext.checkFragmentsFullyPlayed(checks[i].errorMessage); break; case 'fragmentComments': - checkState = interfaceContext.checkAllCommented(); + checkState = interfaceContext.checkAllCommented(checks[i].errorMessage); break; default: console.log("WARNING - Check option " + checks[i].check + " is not supported on this interface");
--- a/js/core.js Sun Apr 23 10:17:33 2017 +0100 +++ b/js/core.js Sun Apr 23 11:32:45 2017 +0100 @@ -553,6 +553,7 @@ this.currentIndex = null; this.node = null; this.store = null; + var lastNodeStart; $(window).keypress(function (e) { if (e.keyCode == 13 && popup.popup.style.visibility == 'visible') { console.log(e); @@ -1048,6 +1049,7 @@ var node = this.popupOptions[this.currentIndex], converter = new showdown.Converter(), p = new DOMParser(); + lastNodeStart = new Date(); this.popupResponse.innerHTML = ""; this.popupTitle.innerHTML = ""; this.popupTitle.appendChild(p.parseFromString(converter.makeHtml(node.specification.statement), "text/html").getElementsByTagName("body")[0].firstElementChild); @@ -1110,7 +1112,13 @@ return; } var node = this.popupOptions[this.currentIndex], - pass = true; + pass = true, + timeDelta = (new Date() - lastNodeStart) / 1000.0; + if (timeDelta < node.specification.minWait) { + interfaceContext.lightbox.post("Error", "Not enough time has elapsed, please wait " + (node.specification.minWait - timeDelta).toFixed(0) + " seconds"); + return; + } + node.elapsedTime = timeDelta; if (node.specification.type == 'question') { // Must extract the question data pass = processQuestion.call(this, node); @@ -3001,6 +3009,22 @@ return volume; })(); + this.imageHolder = (function () { + var imageController = {}; + imageController.root = document.createElement("div"); + imageController.root.id = "imageController"; + imageController.img = document.createElement("img"); + imageController.root.appendChild(imageController.img); + imageController.setImage = function (src) { + imageController.img.src = ""; + if (typeof src !== "string" || src.length === undefined) { + return; + } + imageController.img.src = src; + }; + return imageController; + })(); + this.calibrationModuleObject = null; this.calibrationModule = function () { // This creates an on-page calibration module @@ -3092,7 +3116,7 @@ // Global Checkers // These functions will help enforce the checkers - this.checkHiddenAnchor = function () { + this.checkHiddenAnchor = function (message) { var anchors = audioEngineContext.audioObjects.filter(function (ao) { return ao.specification.type === "anchor"; }); @@ -3101,14 +3125,18 @@ }); if (state) { console.log('Anchor node not below marker value'); - interfaceContext.lightbox.post("Message", 'Please keep listening'); + if (message) { + interfaceContext.lightbox.post("Message", message); + } else { + interfaceContext.lightbox.post("Message", 'Please keep listening'); + } this.storeErrorNode('Anchor node not below marker value'); return false; } return true; }; - this.checkHiddenReference = function () { + this.checkHiddenReference = function (message) { var references = audioEngineContext.audioObjects.filter(function (ao) { return ao.specification.type === "reference"; }); @@ -3117,14 +3145,18 @@ }); if (state) { console.log('Reference node not below marker value'); - interfaceContext.lightbox.post("Message", 'Please keep listening'); + if (message) { + interfaceContext.lightbox.post("Message", message); + } else { + interfaceContext.lightbox.post("Message", 'Please keep listening'); + } this.storeErrorNode('Reference node not below marker value'); return false; } return true; }; - this.checkFragmentsFullyPlayed = function () { + this.checkFragmentsFullyPlayed = function (message) { // Checks the entire file has been played back // NOTE ! This will return true IF playback is Looped!!! if (audioEngineContext.loopPlayback) { @@ -3164,14 +3196,17 @@ } } str_start += ". Please keep listening"; - console.log("[ALERT]: " + str_start); - this.storeErrorNode("[ALERT]: " + str_start); + console.log(str_start); + this.storeErrorNode(str_start); + if (message) { + str_start = message; + } interfaceContext.lightbox.post("Error", str_start); return false; } return true; }; - this.checkAllMoved = function () { + this.checkAllMoved = function (message) { var str = "You have not moved "; var failed = []; audioEngineContext.audioObjects.forEach(function (ao) { @@ -3191,12 +3226,15 @@ str += 'and ' + failed[i]; } str += '.'; - interfaceContext.lightbox.post("Error", str); console.log(str); this.storeErrorNode(str); + if (message) { + str = message; + } + interfaceContext.lightbox.post("Error", str); return false; }; - this.checkAllPlayed = function () { + this.checkAllPlayed = function (message) { var str = "You have not played "; var failed = []; audioEngineContext.audioObjects.forEach(function (ao) { @@ -3216,12 +3254,15 @@ str += 'and ' + failed[i]; } str += '.'; - interfaceContext.lightbox.post("Error", str); console.log(str); this.storeErrorNode(str); + if (message) { + str = message; + } + interfaceContext.lightbox.post("Error", str); return false; }; - this.checkAllCommented = function () { + this.checkAllCommented = function (message) { var str = "You have not commented on all the fragments."; var cont = true, boxes = this.commentBoxes.boxes, @@ -3229,15 +3270,18 @@ i; for (i = 0; i < numBoxes; i++) { if (boxes[i].trackCommentBox.value === "") { - interfaceContext.lightbox.post("Error", str); console.log(str); this.storeErrorNode(str); + if (message) { + str = message; + } + interfaceContext.lightbox.post("Error", str); return false; } } return true; }; - this.checkScaleRange = function () { + this.checkScaleRange = function (message) { var page = testState.getCurrentTestPage(); var interfaceObject = page.interfaces; var state = true; @@ -3275,6 +3319,9 @@ if (state === false) { console.log(str); this.storeErrorNode(str); + if (message) { + str = message; + } interfaceContext.lightbox.post("Error", str); } return state; @@ -3505,7 +3552,7 @@ var clone = this.parent.root.cloneNode(true); hold.appendChild(clone); var saveURL = specification.returnURL + "php/save.php?key=" + this.key + "&saveFilenamePrefix="; - if (this.parent.filenamePrefix.length == 0) { + if (this.parent.filenamePrefix.length === 0) { saveURL += "save"; } else { saveURL += this.parent.filenamePrefix; @@ -3598,6 +3645,7 @@ } surveyresult = surveyresult.nextElementSibling; } + surveyresult.setAttribute("duration", node.elapsedTime); switch (node.specification.type) { case "number": case "question":
--- a/js/specification.js Sun Apr 23 10:17:33 2017 +0100 +++ b/js/specification.js Sun Apr 23 11:32:45 2017 +0100 @@ -223,6 +223,7 @@ this.options = []; this.min = undefined; this.max = undefined; + this.minWait = undefined; this.step = undefined; this.conditions = []; @@ -414,6 +415,7 @@ this.interfaceNode = function (specification) { this.title = undefined; this.name = undefined; + this.image = undefined; this.options = []; this.scales = []; this.schema = schemaRoot.getAllElementsByName('interface')[1]; @@ -440,9 +442,16 @@ option[attributeName] = projectAttr; } } + if (option.type == "check" && ioNode.firstElementChild) { + option.errorMessage = ioNode.firstElementChild.textContent; + } this.options.push(option); } - + // Get the image node + var imageNode = xml.getElementsByTagName("image"); + if (imageNode.length == 1) { + this.image = imageNode[0].getAttribute("src"); + } // Now the scales nodes var scaleParent = xml.getElementsByTagName('scales'); if (scaleParent.length == 1) { @@ -470,8 +479,18 @@ var child = doc.createElement("interfaceoption"); child.setAttribute("type", option.type); child.setAttribute("name", option.name); + if (option.type == "check" && option.errorMessage !== undefined) { + var errorMessage = doc.createElement("errormessage"); + errorMessage.textContent = option.errorMessage; + child.appendChild(errorMessage); + } node.appendChild(child); }); + if (typeof this.image == "string" && this.image.length !== 0) { + var imgNode = doc.createElement("image"); + imgNode.setAttribute("src", this.image); + node.appendChild(imgNode); + } if (this.scales.length !== 0) { var scales = doc.createElement("scales"); this.scales.forEach(function (scale) { @@ -789,6 +808,7 @@ this.startTime = undefined; this.stopTime = undefined; this.sampleRate = undefined; + this.image = undefined; this.alternatives = []; this.schema = schemaRoot.getAllElementsByName('audioelement')[0]; this.parent = undefined;
--- a/tests/examples/AB_example.xml Sun Apr 23 10:17:33 2017 +0100 +++ b/tests/examples/AB_example.xml Sun Apr 23 11:32:45 2017 +0100 @@ -1,39 +1,39 @@ <?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"> + <setup interface="AB" projectReturn="save.php" randomiseOrder="false" poolSize="2" loudness="-23" playOne="true"> <survey location="before"> - <surveyentry type="question" id="sessionId" mandatory="true"> + <surveyquestion id="sessionId" mandatory="true"> <statement>Please enter your name.</statement> - </surveyentry> - <surveyentry type="checkbox" id="checkboxtest" mandatory="true"> + </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> - </surveyentry> - <surveyentry type="statement" id="test-intro"> + </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/'. The 'playOne' configuration option means a fragment has to be finished playing before another fragment can be auditioned. </statement> - </surveyentry> + </surveystatement> </survey> <survey location="after"> - <surveyentry type="question" id="location" mandatory="true" boxsize="large"> + <surveyquestion id="location" mandatory="true" boxsize="large"> <statement>Please enter your location. (example mandatory text question)</statement> - </surveyentry> - <surveyentry type="number" id="age" min="0"> + </surveyquestion> + <surveynumber id="age" min="0"> <statement>Please enter your age (example non-mandatory number question)</statement> - </surveyentry> - <surveyentry type="radio" id="rating"> + </surveynumber> + <surveyradio 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"> + </surveyradio> + <surveystatement 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> + </surveystatement> </survey> <metric> <metricenable>testTimer</metricenable> @@ -46,7 +46,9 @@ </metric> <interface> <interfaceoption type="check" name="fragmentMoved" /> - <interfaceoption type="check" name="scalerange" min="25" max="75" /> + <interfaceoption type="check" name="scalerange" min="25" max="75"> + <errormessage>Test Error Message</errormessage> + </interfaceoption> <interfaceoption type="show" name='playhead' /> <interfaceoption type="show" name="page-count" /> <interfaceoption type="show" name='volume' /> @@ -56,19 +58,20 @@ <page id='test-0' hostURL="media/example/" randomiseOrder='true' repeatCount='0' loop='false' loudness="-12"> <commentboxprefix>Comment on fragment</commentboxprefix> <interface> - <title>Depth</title> + <title>Which sounds most like a drum?</title> + <image src="https://upload.wikimedia.org/wikipedia/commons/0/0a/Drumkit-icon.png" /> </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> + <surveystatement id="test-0-intro"> + <statement>A two way comparison using randomised element order, automatic loudness and synchronised looping. Also an embedded image</statement> + </surveystatement> </survey> <survey location="after"> - <surveyentry type="question" id="genre-0" mandatory="true"> + <surveyquestion id="genre-0" mandatory="true"> <statement>Please enter the genre.</statement> - </surveyentry> + </surveyquestion> </survey> </page> <page id='test-1' hostURL="media/example/" randomiseOrder='true' repeatCount='0' loop='false' loudness="-12"> @@ -84,14 +87,14 @@ <audioelement url="5.wav" id="track-7" /> <audioelement url="6.wav" id="track-8" /> <survey location="before"> - <surveyentry type="statement" id="test-1-intro"> + <surveystatement id="test-1-intro"> <statement>A 7 way comparison using randomised element order and synchronised looping.</statement> - </surveyentry> + </surveystatement> </survey> <survey location="after"> - <surveyentry type="question" id="genre-1" mandatory="true"> + <surveyquestion id="genre-1" mandatory="true"> <statement>Please enter the genre.</statement> - </surveyentry> + </surveyquestion> </survey> </page> </waet>
--- a/xml/test-schema.xsd Sun Apr 23 10:17:33 2017 +0100 +++ b/xml/test-schema.xsd Sun Apr 23 11:32:45 2017 +0100 @@ -29,6 +29,8 @@ <xs:attribute name="playOne" type="xs:boolean" default="false" /> + <xs:attribute name="minWait" type="xs:nonNegativeInteger" default="0" /> + <!-- define complex elements--> <xs:element name="waet"> <xs:complexType> @@ -120,8 +122,16 @@ <xs:complexType> <xs:sequence> <xs:element ref="title" minOccurs="0" maxOccurs="1" /> + <xs:element name="image" minOccurs="0" maxOccurs="1"> + <xs:complexType> + <xs:attribute name="src" type="xs:anyURI" use="required" /> + </xs:complexType> + </xs:element> <xs:element name="interfaceoption" minOccurs="0" maxOccurs="unbounded"> <xs:complexType> + <xs:sequence> + <xs:element name="errormessage" type="xs:string" minOccurs="0" maxOccurs="1" /> + </xs:sequence> <xs:attribute name="type" use="required"> <xs:simpleType> <xs:restriction base="xs:string"> @@ -227,6 +237,7 @@ </xs:restriction> </xs:simpleType> </xs:attribute> + <xs:attribute name="image" type="xs:anyURI" use="optional" /> </xs:complexType> </xs:element> @@ -332,6 +343,7 @@ <xs:attribute ref="id" use="required" /> <xs:attribute ref="name" /> <xs:attribute ref="mandatory" /> + <xs:attribute ref="minWait" /> <xs:attribute name="boxsize" default="normal"> <xs:simpleType> <xs:restriction base="xs:string"> @@ -365,6 +377,7 @@ <xs:attribute ref="mandatory" /> <xs:attribute name="min" type="xs:decimal" /> <xs:attribute name="max" type="xs:decimal" /> + <xs:attribute ref="minWait" /> </xs:complexType> </xs:element> @@ -386,6 +399,7 @@ <xs:attribute ref="id" use="required" /> <xs:attribute ref="name" /> <xs:attribute ref="mandatory" /> + <xs:attribute ref="minWait" /> <xs:attribute name="min" type="xs:decimal" /> <xs:attribute name="max" type="xs:decimal" /> </xs:complexType> @@ -409,6 +423,7 @@ <xs:attribute ref="id" use="required" /> <xs:attribute ref="name" /> <xs:attribute ref="mandatory" /> + <xs:attribute ref="minWait" /> <xs:attribute name="min" type="xs:decimal" /> <xs:attribute name="max" type="xs:decimal" /> </xs:complexType> @@ -424,6 +439,7 @@ </xs:sequence> <xs:attribute ref="id" use="required" /> <xs:attribute ref="name" /> + <xs:attribute ref="minWait" /> <xs:attribute name="min" use="required" type="xs:decimal" /> <xs:attribute name="max" use="required" type="xs:decimal" /> </xs:complexType> @@ -435,6 +451,7 @@ <xs:element ref="statement" minOccurs="1" maxOccurs="1" /> </xs:sequence> <xs:attribute ref="id" use="required" /> + <xs:attribute ref="minWait" /> <xs:attribute name="url" use="required" type="xs:string" /> </xs:complexType> </xs:element> @@ -445,6 +462,7 @@ <xs:element ref="statement" minOccurs="1" maxOccurs="1" /> </xs:sequence> <xs:attribute ref="id" use="required" /> + <xs:attribute ref="minWait" /> <xs:attribute name="url" use="required" type="xs:string" /> </xs:complexType> </xs:element>