# HG changeset patch # User www-data # Date 1499253749 -3600 # Node ID b2f52634c830421f774b6a91598233b0638bc60a # Parent e61531ce764f4a50ab76564b1da5508b63d53d67# Parent 22d1f6d9f0bf4831b7b3643683ec5354f7b9dff5 Merge branch 'master' of https://github.com/BrechtDeMan/WebAudioEvaluationTool diff -r e61531ce764f -r b2f52634c830 css/core.css --- a/css/core.css Tue Jun 27 21:22:15 2017 +0100 +++ b/css/core.css Wed Jul 05 12:22:29 2017 +0100 @@ -8,6 +8,12 @@ margin-bottom: 10px; font-size: 2em; } +div#footer { + position: fixed; + bottom: 0px; + width: 100%; + text-align: center; +} div.indicator-box { position: absolute; left: 150px; diff -r e61531ce764f -r b2f52634c830 index.html --- a/index.html Tue Jun 27 21:22:15 2017 +0100 +++ b/index.html Wed Jul 05 12:22:29 2017 +0100 @@ -24,7 +24,7 @@
-

Web Audio Evaluation Tool

+

Web Audio Evaluation Toolbox (v1.2.1)

Start menu

+ diff -r e61531ce764f -r b2f52634c830 interfaces/ABX.js --- a/interfaces/ABX.js Tue Jun 27 21:22:15 2017 +0100 +++ b/interfaces/ABX.js Wed Jul 05 12:22:29 2017 +0100 @@ -14,7 +14,7 @@ interfaceContext.insertPoint.innerHTML = ""; // Clear the current schema // Custom comparator Object - Interface.prototype.comparator = null; + interfaceContext.comparator = null; // The injection point into the HTML page interfaceContext.insertPoint = document.getElementById("topLevelBody"); @@ -375,7 +375,7 @@ this.boxHolders.appendChild(node.box); }, this); var elementId = Math.floor(Math.random() * 2); //Randomly pick A or B to be X - var element = new page.audioElementNode(specification); + var element = page.addAudioElement(); for (var atr in page.audioElements[elementId]) { element[atr] = page.audioElements[elementId][atr]; } diff -r e61531ce764f -r b2f52634c830 interfaces/horizontal-sliders.css --- a/interfaces/horizontal-sliders.css Tue Jun 27 21:22:15 2017 +0100 +++ b/interfaces/horizontal-sliders.css Wed Jul 05 12:22:29 2017 +0100 @@ -7,23 +7,19 @@ /* Set the background colour (note US English spelling) to grey*/ background-color: #ddd } - div.pageTitle { width: auto; height: 20px; margin: 10px 0px; } - div.pageTitle span { font-size: 1.5em; } - button { /* Specify any button structure or style */ min-width: 20px; background-color: #ddd } - div#slider-holder { height: inherit; position: absolute; @@ -31,29 +27,24 @@ z-index: 3; margin-top: 25px; } - div#scale-holder { height: inherit; position: absolute; left: 0px; z-index: 2; } - div#scale-text-holder { position: relative; float: left; } - div.scale-text { position: absolute; font-size: 1.2em; } - canvas#scale-canvas { position: relative; float: left; } - div.track-slider { float: left; height: 94px; @@ -64,53 +55,44 @@ margin-left: 94px; margin-bottom: 25px; } - div.track-slider-title { float: left; padding-top: 40px; width: 100px; } - button.track-slider-button { float: left; width: 100px; height: 94px; } - div#outside-reference-holder { display: flex; align-content: center; justify-content: center; margin-bottom: 5px; } - button.outside-reference { position: inherit; margin: 0px 5px; } - div.track-slider-playing { - background-color: #FFDDDD; + background-color: rgba(255, 201, 201, 0.5); } - input.track-slider-range { float: left; margin: 2px 10px; } - input[type=range] { height: 94px; padding: 0px; color: rgb(255, 144, 144); } - input[type=range]::-webkit-slider-runnable-track { cursor: pointer; background: #fff; border-radius: 4px; border: 1px solid #000; } - input[type=range]::-moz-range-track { height: 8px; cursor: pointer; @@ -118,26 +100,21 @@ border-radius: 4px; border: 1px solid #000; } - input.track-slider-not-moved[type=range]::-webkit-slider-runnable-track { background: #aaa; } - input.track-slider-not-moved[type=range]::-moz-range-track { background: #aaa; } - div#page-count { float: left; margin: 0px 5px; } - div#master-volume-holder { position: absolute; top: 10px; left: 120px; } - div.comment-box-playing { background-color: #FFDDDD; } diff -r e61531ce764f -r b2f52634c830 interfaces/horizontal-sliders.js --- a/interfaces/horizontal-sliders.js Tue Jun 27 21:22:15 2017 +0100 +++ b/interfaces/horizontal-sliders.js Wed Jul 05 12:22:29 2017 +0100 @@ -237,6 +237,7 @@ // An example node, you can make this however you want for each audioElement. // However, every audioObject (audioEngineContext.audioObject) MUST have an interface object with the following // You attach them by calling audioObject.bindInterface( ) + var playing = false; this.parent = audioObject; this.holder = document.createElement('div'); @@ -275,9 +276,9 @@ this.play.onclick = function (event) { var id = Number(event.currentTarget.value); //audioEngineContext.metric.sliderPlayed(id); - if (event.currentTarget.getAttribute("playstate") == "ready") { + if (!playing) { audioEngineContext.play(id); - } else if (event.currentTarget.getAttribute("playstate") == "playing") { + } else if (playing) { audioEngineContext.stop(); } }; @@ -296,7 +297,8 @@ }; this.startPlayback = function () { // Called when playback has begun - this.play.setAttribute("playstate", "playing"); + playing = true; + this.play.textContent = "Stop"; $(".track-slider").removeClass('track-slider-playing'); $(this.holder).addClass('track-slider-playing'); var outsideReference = document.getElementById('outside-reference'); @@ -310,7 +312,8 @@ }; this.stopPlayback = function () { // Called when playback has stopped. This gets called even if playback never started! - this.play.setAttribute("playstate", "ready"); + playing = false; + this.play.textContent = "Play"; $(this.holder).removeClass('track-slider-playing'); var box = interfaceContext.commentBoxes.boxes.find(function (a) { return a.id === audioObject.id; diff -r e61531ce764f -r b2f52634c830 interfaces/interfaces.json --- a/interfaces/interfaces.json Tue Jun 27 21:22:15 2017 +0100 +++ b/interfaces/interfaces.json Wed Jul 05 12:22:29 2017 +0100 @@ -30,6 +30,10 @@ "name": "timeline", "scripts": ["interfaces/timeline.js"], "css": ["interfaces/timeline.css"] + }, { + "name": "ordinal", + "scripts": ["interfaces/ordinal.js"], + "css": ["interfaces/ordinal.css"] } ] } diff -r e61531ce764f -r b2f52634c830 interfaces/mushra.css --- a/interfaces/mushra.css Tue Jun 27 21:22:15 2017 +0100 +++ b/interfaces/mushra.css Wed Jul 05 12:22:29 2017 +0100 @@ -7,52 +7,43 @@ /* Set the background colour (note US English spelling) to grey*/ background-color: #ddd } - div.pageTitle { width: auto; height: 20px; margin: 10px 0px; } - div.pageTitle span { font-size: 1.5em; } - button { /* Specify any button structure or style */ min-width: 20px; background-color: #ddd } - div#slider-holder { height: inherit; position: absolute; left: 0px; z-index: 3; } - div#scale-holder { height: inherit; position: absolute; left: 0px; z-index: 2; } - div#scale-text-holder { position: relative; width: 100px; float: left; } - div.scale-text { position: absolute; } - canvas#scale-canvas { position: relative; float: left; } - div.track-slider { float: left; width: 94px; @@ -62,27 +53,22 @@ padding: 2px; margin-left: 50px; } - div#outside-reference-holder { display: flex; align-content: center; justify-content: center; margin-bottom: 5px; } - button.outside-reference { position: inherit; margin: 0px 5px; } - div.track-slider-playing { - background-color: #FFDDDD; + background-color: rgba(255, 201, 201, 0.5); } - input.track-slider-range { margin: 2px 0px; } - input[type=range][orient=vertical] { writing-mode: bt-lr; /* IE */ @@ -92,7 +78,6 @@ padding: 0 5px; color: rgb(255, 144, 144); } - input[type=range]::-webkit-slider-runnable-track { width: 8px; cursor: pointer; @@ -100,7 +85,6 @@ border-radius: 4px; border: 1px solid #000; } - input[type=range]::-moz-range-track { width: 8px; cursor: pointer; @@ -108,79 +92,63 @@ border-radius: 4px; border: 1px solid #000; } - input[type=range]::-ms-track { cursor: pointer; background: #fff; border-radius: 4px; border: 1px solid #000; } - input.track-slider-not-moved[type=range]::-webkit-slider-runnable-track { background: #aaa; } - input.track-slider-not-moved[type=range]::-moz-range-track { background: #aaa; } - input[type=range]::-moz-range-thumb { margin-left: -7px; cursor: pointer; margin-top: -1px; box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d; } - input[type=range]::-webkit-slider-thumb { cursor: pointer; margin-top: -1px; margin-left: -4px; } - input[type=range]::-ms-thumb { cursor: pointer; margin-top: -1px; margin-left: -4px; } - input[type=range]::-ms-tooltip { visibility: hidden; } - input.track-slider-range-disabled {} - input.track-slider-range-disabled[type=range]::-webkit-slider-runnable-track { cursor: not-allowed; } - input.track-slider-range-disabled[type=range]::-moz-range-track { cursor: not-allowed; } - input.track-slider-range-disabled[type=range]::-ms-track { cursor: not-allowed; } - input.track-slider-range-disabled[type=range]::-moz-range-thumb { cursor: not-allowed; background-color: #888; } - input.track-slider-range-disabled[type=range]::-webkit-slider-thumb { cursor: not-allowed; background-color: #888; } - input.track-slider-range-disabled[type=range]::-ms-thumb { cursor: not-allowed; background-color: #888; } - div#page-count { float: left; margin: 0px 5px; } - div#master-volume-holder { position: absolute; top: 10px; diff -r e61531ce764f -r b2f52634c830 interfaces/mushra.js --- a/interfaces/mushra.js Tue Jun 27 21:22:15 2017 +0100 +++ b/interfaces/mushra.js Wed Jul 05 12:22:29 2017 +0100 @@ -50,7 +50,7 @@ var playback = document.createElement("button"); playback.innerHTML = 'Stop'; playback.id = 'playback-button'; - playback.style.float = 'left'; + playback.style.display = 'inline-block'; // 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 () { @@ -66,7 +66,7 @@ submit.innerHTML = 'Next'; submit.onclick = buttonSubmitClick; submit.id = 'submit-button'; - submit.style.float = 'left'; + submit.style.display = 'inline-block'; // Append the interface buttons into the interfaceButtons object. interfaceButtons.appendChild(playback); interfaceButtons.appendChild(submit); @@ -224,6 +224,7 @@ if (pagecountHolder === null) { pagecountHolder = document.createElement('div'); pagecountHolder.id = 'page-count'; + pagecountHolder.style.display = 'inline-block'; } pagecountHolder.innerHTML = 'Page ' + (testState.stateIndex + 1) + ' of ' + testState.stateMap.length + ''; var inject = document.getElementById('interface-buttons'); @@ -237,6 +238,33 @@ case "comments": interfaceContext.commentBoxes.showCommentBoxes(feedbackHolder, true); break; + case "fragmentSort": + var button = document.getElementById('sort'); + if (button === null) { + button = document.createElement("button"); + button.id = 'sort'; + button.textContent = "Sort"; + button.style.display = 'inline-block'; + var container = document.getElementById("interface-buttons"); + var neighbour = container.lastElementChild; + container.appendChild(button); + button.onclick = function () { + var sortIndex = interfaceContext.sortFragmentsByScore(); + var sliderBox = document.getElementById("slider-holder"); + var nodes = audioEngineContext.audioObjects.filter(function (ao) { + return ao.specification.type !== "outside-reference"; + }); + var i; + nodes.forEach(function (ao) { + sliderBox.removeChild(ao.interfaceDOM.holder); + }); + for (i = 0; i < nodes.length; i++) { + var j = sortIndex[i]; + sliderBox.appendChild(nodes[j].interfaceDOM.holder); + } + }; + } + break; } } }); @@ -374,13 +402,9 @@ return this.slider.value; }; - this.resize = function (event) { - 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.resize = function (event, height) { + this.holder.style.height = height - 20 + 'px'; + this.slider.style.height = height - 70 + 'px'; }; this.updateLoading = function (progress) { progress = String(progress); @@ -409,7 +433,9 @@ // MANDATORY FUNCTION var outsideRef = document.getElementById('outside-reference'), - imageHeight = 0; + imageHeight = 0, + minHeight = Math.max(Math.floor(window.screen.height * 0.33), 200), + maxHeight = Math.floor(window.screen.height * 0.5); if (document.getElementById("imageController")) { imageHeight = $(interfaceContext.imageHolder.root).height(); } @@ -421,18 +447,21 @@ 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 - imageHeight + 'px'; + var height = window.innerHeight - 180 - imageHeight; + height = Math.min(height, maxHeight); + height = Math.max(height, minHeight); + document.getElementById('slider').style.height = height + 'px'; if (diff <= 0) { diff = 0; } document.getElementById('slider-holder').style.marginLeft = diff + 'px'; for (var i in audioEngineContext.audioObjects) { if (audioEngineContext.audioObjects[i].specification.type != 'outside-reference') { - audioEngineContext.audioObjects[i].interfaceDOM.resize(event); + audioEngineContext.audioObjects[i].interfaceDOM.resize(event, height); } } document.getElementById('scale-holder').style.marginLeft = (diff - 100) + 'px'; - document.getElementById('scale-text-holder').style.height = window.innerHeight - imageHeight - 194 + 'px'; + document.getElementById('scale-text-holder').style.height = height - 14 + 'px'; // Cheers edge for making me delete a canvas every resize. var canvas = document.getElementById('scale-canvas'); var new_canvas = document.createElement("canvas"); @@ -440,7 +469,7 @@ canvas.parentElement.appendChild(new_canvas); canvas.parentElement.removeChild(canvas); new_canvas.width = totalWidth; - new_canvas.height = window.innerHeight - 194 - imageHeight; + new_canvas.height = height - 14; drawScale(); } diff -r e61531ce764f -r b2f52634c830 interfaces/ordinal.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/interfaces/ordinal.css Wed Jul 05 12:22:29 2017 +0100 @@ -0,0 +1,36 @@ +[draggable] { + -moz-user-select: none; + -khtml-user-select: none; + -webkit-user-select: none; + user-select: none; + /* Required to make elements draggable in old WebKit */ + -khtml-user-drag: element; + -webkit-user-drag: element; +} +.ordinal-element { + width: 250px; + height: 250px; + background: #bbffbb; + border: 2px #050 solid; + border-radius: 10px; + float: left; + margin: 10px 5px; + text-align: center; + cursor: move; +} +.disabled { + background-color: grey; +} +.playing { + background-color: #ffbbbb; + border: 2px #500 solid; +} +.dragging { + opacity: 0.4; +} +.over { + border-style: dashed; +} +.ordinal-element-label { + font-size: 2em; +} diff -r e61531ce764f -r b2f52634c830 interfaces/ordinal.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/interfaces/ordinal.js Wed Jul 05 12:22:29 2017 +0100 @@ -0,0 +1,487 @@ +/** + * WAET Blank Template + * Use this to start building your custom interface + */ +/*globals interfaceContext, window, document, specification, audioEngineContext, console, testState, $, storage */ +// Once this is loaded and parsed, begin execution +loadInterface(); + +function loadInterface() { + // Use this to do any one-time page / element construction. For instance, placing any stationary text objects, + // holding div's, or setting up any nodes which are present for the entire test sequence + + // 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"; + + 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 + } + }; + // Create Submit (save) button + var submit = document.createElement("button"); + submit.innerHTML = 'Next'; + submit.onclick = buttonSubmitClick; + submit.id = 'submit-button'; + submit.style.float = 'left'; + // Append the interface buttons into the interfaceButtons object. + interfaceButtons.appendChild(playback); + interfaceButtons.appendChild(submit); + + // Create outside reference holder + var outsideRef = document.createElement("div"); + outsideRef.id = "outside-reference-holder"; + + // Create a slider box + var slider = document.createElement("div"); + slider.id = "slider"; + slider.style.height = "300px"; + + // Global parent for the comment boxes on the page + var feedbackHolder = document.createElement('div'); + feedbackHolder.id = 'feedbackHolder'; + + testContent.style.zIndex = 1; + interfaceContext.insertPoint.innerHTML = ""; // Clear the current schema + + // Inject into HTML + testContent.appendChild(title); // Insert the title + testContent.appendChild(pagetitle); + testContent.appendChild(interfaceButtons); + testContent.appendChild(outsideRef); + testContent.appendChild(slider); + testContent.appendChild(feedbackHolder); + interfaceContext.insertPoint.appendChild(testContent); + + // Load the full interface + testState.initialise(); + testState.advanceState(); +} + +function loadTest(page) { + // Called each time a new test page is to be build. The page specification node is the only item passed in + var id = page.id; + + var feedbackHolder = document.getElementById('feedbackHolder'); + feedbackHolder.innerHTML = ""; + + var interfaceObj = interfaceContext.getCombinedInterfaces(page); + if (interfaceObj.length > 1) { + console.log("WARNING - This interface only supports one node per page. Using first interface node"); + } + interfaceObj = interfaceObj[0]; + + // Set the page title + if (typeof page.title == "string" && page.title.length > 0) { + document.getElementById("test-title").textContent = page.title; + } + + if (interfaceObj.title !== null) { + document.getElementById("pageTitle").textContent = interfaceObj.title; + } + + if (interfaceObj.image !== undefined) { + feedbackHolder.insertBefore(interfaceContext.imageHolder.root, document.getElementById("slider")); + interfaceContext.imageHolder.setImage(interfaceObj.image); + } + // Delete outside reference + document.getElementById("outside-reference-holder").innerHTML = ""; + + var sliderBox = document.getElementById('slider'); + sliderBox.innerHTML = ""; + + var commentBoxPrefix = "Comment on track"; + if (interfaceObj.commentBoxPrefix !== undefined) { + commentBoxPrefix = interfaceObj.commentBoxPrefix; + } + + $(page.commentQuestions).each(function (index, element) { + var node = interfaceContext.createCommentQuestion(element); + feedbackHolder.appendChild(node.holder); + }); + + var index = 0; + var labelType = page.label; + if (labelType == "default") { + labelType = "number"; + } + page.audioElements.forEach(function (element, pageIndex) { + var audioObject = audioEngineContext.newTrack(element); + if (element.type == 'outside-reference') { + // Construct outside reference; + var orNode = new interfaceContext.outsideReferenceDOM(audioObject, index, document.getElementById("outside-reference-holder")); + audioObject.bindInterface(orNode); + } else { + // Create a slider per track + var label = interfaceContext.getLabel(labelType, index, page.labelStart); + var sliderObj = new interfaceObject(audioObject, label); + + sliderBox.appendChild(sliderObj.root); + audioObject.bindInterface(sliderObj); + interfaceContext.commentBoxes.createCommentBox(audioObject); + index += 1; + } + }); + interfaceObj.options.forEach(function (option) { + if (option.type == "show") { + switch (option.name) { + case "playhead": + var playbackHolder = document.getElementById('playback-holder'); + if (playbackHolder === null) { + playbackHolder = document.createElement('div'); + playbackHolder.style.width = "100%"; + playbackHolder.align = 'center'; + playbackHolder.appendChild(interfaceContext.playhead.object); + feedbackHolder.appendChild(playbackHolder); + } + break; + case "page-count": + var pagecountHolder = document.getElementById('page-count'); + if (pagecountHolder === null) { + pagecountHolder = document.createElement('div'); + pagecountHolder.id = 'page-count'; + } + pagecountHolder.innerHTML = 'Page ' + (testState.stateIndex + 1) + ' of ' + testState.stateMap.length + ''; + var inject = document.getElementById('interface-buttons'); + inject.appendChild(pagecountHolder); + break; + case "volume": + if (document.getElementById('master-volume-holder') === null) { + feedbackHolder.appendChild(interfaceContext.volume.object); + } + break; + case "comments": + interfaceContext.commentBoxes.showCommentBoxes(feedbackHolder, true); + break; + } + } + }); + resizeWindow(); +} + +function interfaceObject(audioObject, label) { + var container = document.getElementById("slider"); + var playing = false; + var root = document.createElement("div"); + root.className = "ordinal-element"; + root.draggable = "true"; + var labelElement = document.createElement("span"); + labelElement.className = "ordinal-element-label"; + labelElement.textContent = label; + root.appendChild(labelElement); + root.classList.add("disabled"); + // An example node, you can make this however you want for each audioElement. + // However, every audioObject (audioEngineContext.audioObject) MUST have an interface object with the following + // You attach them by calling audioObject.bindInterface( ) + root.addEventListener("click", this, true); + root.addEventListener('dragstart', this, true); + root.addEventListener('dragenter', this, true); + root.addEventListener('dragover', this, true); + root.addEventListener('dragleave', this, true); + root.addEventListener('drop', this, true); + root.addEventListener('dragend', this, true); + this.handleEvent = function (event) { + if (event.type == "click") { + if (playing === false) { + audioEngineContext.play(audioObject.id); + } else { + audioEngineContext.stop(); + } + playing = !playing; + return; + } else if (event.type == "dragstart") { + return dragStart.call(this, event); + } else if (event.type == "dragenter") { + return dragEnter.call(this, event); + } else if (event.type == "dragleave") { + return dragLeave.call(this, event); + } else if (event.type == "dragover") { + return dragOver.call(this, event); + } else if (event.type == "drop") { + return drop.call(this, event); + } else if (event.type == "dragend") { + return dragEnd.call(this, event); + } + throw (event); + }; + + function dragStart(e) { + e.currentTarget.classList.add("dragging"); + + e.dataTransfer.effectAllowed = 'move'; + e.dataTransfer.setData('text/plain', audioObject.id); + } + + function dragEnter(e) { + // this / e.target is the current hover target. + root.classList.add('over'); + } + + function dragLeave(e) { + root.classList.remove('over'); // this / e.target is previous target element. + } + + function dragOver(e) { + if (e.preventDefault) { + e.preventDefault(); // Necessary. Allows us to drop. + } + + e.dataTransfer.dropEffect = 'move'; // See the section on the DataTransfer object. + + var srcid = Number(e.dataTransfer.getData("text/plain")); + var elements = container.childNodes; + var srcObject = audioEngineContext.audioObjects.find(function (ao) { + return ao.id === srcid; + }); + var src = srcObject.interfaceDOM.root; + if (src !== root) { + var srcpos = srcObject.interfaceDOM.getElementPosition(); + var mypos = this.getElementPosition(); + var neighbour; + if (srcpos <= mypos) { + neighbour = root.nextElementSibling; + } else { + neighbour = root; + } + if (neighbour) + container.insertBefore(src, neighbour); + else { + container.removeChild(src); + container.appendChild(src); + } + + } + + return false; + } + + function drop(e) { + // this / e.target is current target element. + + if (e.stopPropagation) { + e.stopPropagation(); // stops the browser from redirecting. + } + if (e.preventDefault) { + e.preventDefault(); // Necessary. Allows us to drop. + } + + audioEngineContext.audioObjects.forEach(function (ao) { + ao.interfaceDOM.processMovement(); + }); + + return false; + } + + function dragEnd(e) { + // this/e.target is the source node. + $(".ordinal-element").removeClass("dragging"); + $(".ordinal-element").removeClass("over"); + } + + this.getElementPosition = function () { + var elements = container.childNodes, + position = 0, + elem = elements[0]; + while (root !== elem) { + position++; + elem = elem.nextElementSibling; + } + return position; + }; + + this.processMovement = function () { + var time = audioEngineContext.timer.getTestTime(); + var pos = this.getElementPosition(); + var rank = pos / (audioEngineContext.audioObjects.length - 1); + audioObject.metric.moved(time, rank); + console.log('slider ' + audioObject.id + ' moved to ' + rank + ' (' + time + ')'); + }; + + this.enable = function () { + // This is used to tell the interface object that playback of this node is ready + root.classList.remove("disabled"); + labelElement.textContent = label; + }; + this.updateLoading = function (progress) { + // progress is a value from 0 to 100 indicating the current download state of media files + labelElement.textContent = String(progress); + }; + this.startPlayback = function () { + // Called when playback has begun + root.classList.add("playing"); + if (audioObject.commentDOM) { + audioObject.commentDOM.trackComment.classList.add("comment-box-playing"); + } + }; + this.stopPlayback = function () { + // Called when playback has stopped. This gets called even if playback never started! + root.classList.remove("playing"); + playing = false; + if (audioObject.commentDOM) { + audioObject.commentDOM.trackComment.classList.remove("comment-box-playing"); + } + }; + this.getValue = function () { + // Return the current value of the object. If there is no value, return 0 + var pos = this.getElementPosition(); + var rank = pos / (audioEngineContext.audioObjects.length - 1); + }; + this.getPresentedId = function () { + // Return the presented ID of the object. For instance, the APE has sliders starting from 0. Whilst AB has alphabetical scale + return label; + }; + this.canMove = function () { + // Return either true or false if the interface object can be moved. AB / Reference cannot, whilst sliders can and therefore have a continuous scale. + // These are checked primarily if the interface check option 'fragmentMoved' is enabled. + return true; + }; + this.exportXMLDOM = function (audioObject) { + // Called by the audioObject holding this element to export the interface node. + // If there is no value node (such as outside reference), return null + // If there are multiple value nodes (such as multiple scale / 2D scales), return an array of nodes with each value node having an 'interfaceName' attribute + // Use storage.document.createElement('value'); to generate the XML node. + var node = storage.document.createElement('value'); + node.textContent = this.getValue(); + return node; + + }; + this.error = function () { + // If there is an error with the audioObject, this will be called to indicate a failure + root.classList.remove("disabled"); + labelElement.textContent = "Error"; + }; + Object.defineProperties(this, { + "root": { + "get": function () { + return root; + }, + "set": function () {} + } + }); +} + +function resizeWindow(event) { + // Called on every window resize event, use this to scale your page properly + var w = $("#slider").width(); + var N = audioEngineContext.audioObjects.length; + w /= N; + w -= 14; + w = Math.floor(w); + $(".ordinal-element").width(w); +} + +function buttonSubmitClick() // TODO: Only when all songs have been played! +{ + var checks = testState.currentStateMap.interfaces[0].options, + canContinue = true; + + // Check that the anchor and reference objects are correctly placed + if (interfaceContext.checkHiddenAnchor() === false) { + return; + } + if (interfaceContext.checkHiddenReference() === false) { + return; + } + + for (var i = 0; i < checks.length; i++) { + var checkState = true; + if (checks[i].type == 'check') { + switch (checks[i].name) { + case 'fragmentPlayed': + // Check if all fragments have been played + checkState = interfaceContext.checkAllPlayed(checks[i].errorMessage); + break; + case 'fragmentFullPlayback': + // Check all fragments have been played to their full length + 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(checks[i].errorMessage); + break; + case 'fragmentComments': + // Check all fragment sliders have been moved. + checkState = interfaceContext.checkAllCommented(checks[i].errorMessage); + break; + case 'scalerange': + // Check the scale has been used effectively + checkState = interfaceContext.checkScaleRange(checks[i].errorMessage); + + break; + default: + console.log("WARNING - Check option " + checks[i].check + " is not supported on this interface"); + break; + } + } + if (checkState === false) { + canContinue = false; + break; + } + } + + if (canContinue) { + if (audioEngineContext.status == 1) { + var playback = document.getElementById('playback-button'); + playback.click(); + // This function is called when the submit button is clicked. Will check for any further tests to perform, or any post-test options + } else { + if (audioEngineContext.timer.testStarted === false) { + interfaceContext.lightbox.post("Warning", 'You have not started the test! Please press start 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 saves + // Get the current 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(); +} diff -r e61531ce764f -r b2f52634c830 interfaces/timeline.js --- a/interfaces/timeline.js Tue Jun 27 21:22:15 2017 +0100 +++ b/interfaces/timeline.js Wed Jul 05 12:22:29 2017 +0100 @@ -441,9 +441,15 @@ }; this.startPlayback = function () { // Called when playback has begun - canvasIntervalID = window.setInterval(this.canvas.drawTicker.bind(this.canvas), 100); + var animate = function () { + this.canvas.drawTicker.call(this.canvas); + if (this.playButton.DOM.textContent == "Stop") { + window.requestAnimationFrame(animate); + } + }.bind(this); this.playButton.DOM.textContent = "Stop"; interfaceContext.commentBoxes.highlightById(audioObject.id); + canvasIntervalID = window.requestAnimationFrame(animate); }; this.stopPlayback = function () { // Called when playback has stopped. This gets called even if playback never started! diff -r e61531ce764f -r b2f52634c830 js/core.js --- a/js/core.js Tue Jun 27 21:22:15 2017 +0100 +++ b/js/core.js Wed Jul 05 12:22:29 2017 +0100 @@ -3355,6 +3355,25 @@ }; + this.sortFragmentsByScore = function () { + var elements = audioEngineContext.audioObjects.filter(function (elem) { + return elem.specification.type !== "outside-reference"; + }); + var indexes = []; + var i = 0; + while (indexes.push(i++) < elements.length); + return indexes.sort(function (x, y) { + var a = elements[x].interfaceDOM.getValue(); + var b = elements[y].interfaceDOM.getValue(); + if (a > b) { + return 1; + } else if (a < b) { + return -1; + } + return 0; + }, elements[0].interfaceDOM.getValue()); + }; + this.storeErrorNode = function (errorMessage) { var time = audioEngineContext.timer.getTestTime(); var node = storage.document.createElement('error'); diff -r e61531ce764f -r b2f52634c830 js/specification.js --- a/js/specification.js Tue Jun 27 21:22:15 2017 +0100 +++ b/js/specification.js Wed Jul 05 12:22:29 2017 +0100 @@ -21,19 +21,28 @@ this.maxNumberPlays = undefined; // nodes - this.metrics = undefined; - this.preTest = undefined; - this.postTest = undefined; + this.metrics = new metricNode(); + this.preTest = new surveyNode(this); + this.postTest = new surveyNode(this); + this.preTest.location = "pre"; + this.postTest.location = "post"; this.pages = []; - this.interfaces = undefined; + this.interfaces = new interfaceNode(this); this.errors = []; this.exitText = "Thank you."; + // Creators + this.createNewPage = function () { + var newpage = new page(this); + this.pages.push(newpage); + return newpage; + }; + var processAttribute = function (attribute, schema) { // attribute is the string returned from getAttribute on the XML // schema is the node if (schema.getAttribute('name') === null && schema.getAttribute('ref') !== undefined) { - schema = schemaRoot.getAllElementsByName(schema.getAttribute('ref'))[0]; + schema = schemaRoot.querySelector("[name=" + schema.getAttribute('ref') + "]"); } var defaultOpt = schema.getAttribute('default'); if (attribute === null) { @@ -43,7 +52,7 @@ if (typeof dataType == "string") { dataType = dataType.substr(3); } else { - var rest = schema.getAllElementsByTagName("xs:restriction").concat(schema.getAllElementsByTagName("xs:enumeration")); + var rest = schema.querySelectorAll("restriction,enumeration"); if (rest.length > 0) { dataType = rest[0].getAttribute("base"); if (typeof dataType == "string") { @@ -110,9 +119,9 @@ // projectXML - DOM Parsed document this.projectXML = projectXML.childNodes[0]; var setupNode = projectXML.getElementsByTagName('setup')[0]; - var schemaSetup = schemaRoot.getAllElementsByName('setup')[0]; + var schemaSetup = schemaRoot.querySelector('[name=setup]'); // First decode the attributes - var attributes = schemaSetup.getAllElementsByTagName('xs:attribute'); + var attributes = schemaSetup.querySelectorAll('attribute'); var i; for (i = 0; i < attributes.length; i++) { var attributeName = attributes[i].getAttribute('name') || attributes[i].getAttribute('ref'); @@ -129,8 +138,6 @@ this.exitText = exitTextNode[0].textContent; } - this.metrics = new this.metricNode(); - this.metrics.decode(this, setupNode.getElementsByTagName('metric')[0]); // Now process the survey node options @@ -140,12 +147,10 @@ switch (location) { case 'pre': case 'before': - 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]); break; } @@ -155,17 +160,17 @@ if (interfaceNode.length > 1) { this.errors.push("Only one node in the 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]); + this.interfaces.decode(this, interfaceNode, this.schema.querySelectorAll('[name=interface]')[1]); } // Page tags var pageTags = projectXML.getElementsByTagName('page'); - var pageSchema = this.schema.getAllElementsByName('page')[0]; + var pageSchema = this.schema.querySelector('[name=page]'); for (i = 0; i < pageTags.length; i++) { - var node = new this.page(this); + var node = new page(this); node.decode(this, pageTags[i], pageSchema); this.pages.push(node); } @@ -178,9 +183,9 @@ root.setAttribute("xsi:noNamespaceSchemaLocation", "test-schema.xsd"); // Build setup node var setup = RootDocument.createElement("setup"); - var schemaSetup = schemaRoot.getAllElementsByName('setup')[0]; + var schemaSetup = schemaRoot.querySelector('[name=setup]'); // First decode the attributes - var attributes = schemaSetup.getAllElementsByTagName('xs:attribute'); + var attributes = schemaSetup.querySelectorAll('attribute'); for (var i = 0; i < attributes.length; i++) { var name = attributes[i].getAttribute("name"); if (name === undefined) { @@ -207,13 +212,18 @@ return RootDocument; }; - this.surveyNode = function (specification) { + function surveyNode(specification) { this.location = undefined; this.options = []; this.parent = undefined; - this.schema = schemaRoot.getAllElementsByName('survey')[0]; this.specification = specification; + this.addOption = function () { + var node = new this.OptionNode(this.specification); + this.options.push(node); + return node; + }; + this.OptionNode = function (specification) { this.type = undefined; this.schema = undefined; @@ -230,8 +240,8 @@ this.conditions = []; this.decode = function (parent, child) { - this.schema = schemaRoot.getAllElementsByName(child.nodeName)[0]; - var attributeMap = this.schema.getAllElementsByTagName('xs:attribute'); + this.schema = schemaRoot.querySelector("[name=" + child.nodeName + "]"); + var attributeMap = this.schema.querySelectorAll('attribute'); var i; for (i in attributeMap) { if (isNaN(Number(i)) === true) { @@ -385,6 +395,7 @@ }; }; this.decode = function (parent, xml) { + this.schema = schemaRoot.querySelector('[name=survey]'); this.parent = parent; this.location = xml.getAttribute('location'); if (this.location == 'before') { @@ -412,17 +423,18 @@ } return node; }; - }; + } - this.interfaceNode = function (specification) { + function interfaceNode(specification) { this.title = undefined; this.name = undefined; this.image = undefined; this.options = []; this.scales = []; - this.schema = schemaRoot.getAllElementsByName('interface')[1]; + this.schema = undefined; this.decode = function (parent, xml) { + this.schema = schemaRoot.querySelectorAll('[name=interface]')[1]; this.name = xml.getAttribute('name'); var titleNode = xml.getElementsByTagName('title'); if (titleNode.length == 1) { @@ -430,8 +442,8 @@ } var interfaceOptionNodes = xml.getElementsByTagName('interfaceoption'); // Extract interfaceoption node schema - var interfaceOptionNodeSchema = this.schema.getAllElementsByName('interfaceoption')[0]; - var attributeMap = interfaceOptionNodeSchema.getAllElementsByTagName('xs:attribute'); + var interfaceOptionNodeSchema = this.schema.querySelector('[name=interfaceoption]'); + var attributeMap = interfaceOptionNodeSchema.querySelectorAll('attribute'); var i, j; for (i = 0; i < interfaceOptionNodes.length; i++) { var ioNode = interfaceOptionNodes[i]; @@ -458,7 +470,7 @@ var scaleParent = xml.getElementsByTagName('scales'); if (scaleParent.length == 1) { scaleParent = scaleParent[0]; - var scalelabels = scaleParent.getAllElementsByTagName('scalelabel'); + var scalelabels = scaleParent.querySelectorAll('scalelabel'); for (i = 0; i < scalelabels.length; i++) { this.scales.push({ text: scalelabels[i].textContent, @@ -505,9 +517,9 @@ } return node; }; - }; + } - this.metricNode = function () { + function metricNode() { this.enabled = []; this.decode = function (parent, xml) { var children = xml.getElementsByTagName('metricenable'); @@ -530,9 +542,9 @@ } return node; }; - }; + } - this.page = function (specification) { + function page(specification) { this.presentedId = undefined; this.id = undefined; this.title = undefined; @@ -543,8 +555,10 @@ this.loudness = undefined; this.label = undefined; this.labelStart = undefined; - this.preTest = undefined; - this.postTest = undefined; + this.preTest = new surveyNode(specification); + this.postTest = new surveyNode(specification); + this.preTest.location = "pre"; + this.postTest.location = "post"; this.interfaces = []; this.playOne = undefined; this.restrictMovement = undefined; @@ -554,13 +568,29 @@ this.maxNumberPlays = undefined; this.audioElements = []; this.commentQuestions = []; - this.schema = schemaRoot.getAllElementsByName("page")[0]; + this.schema = schemaRoot.querySelector("[name=page]"); this.specification = specification; this.parent = undefined; + this.addInterface = function () { + var node = new interfaceNode(specification); + this.interfaces.push(node); + return node; + }; + this.addCommentQuestion = function () { + var node = new commentQuestionNode(specification); + this.commentQuestions.push(node); + return node; + }; + this.addAudioElement = function () { + var node = new audioElementNode(specification); + this.audioElements.push(node); + return node; + }; + this.decode = function (parent, xml) { this.parent = parent; - var attributeMap = this.schema.getAllElementsByTagName('xs:attribute'); + var attributeMap = this.schema.querySelectorAll('attribute'); var i, node; for (i = 0; i < attributeMap.length; i++) { var attributeName = attributeMap[i].getAttribute('name') || attributeMap[i].getAttribute('ref'); @@ -584,30 +614,28 @@ } // Now decode the interfaces - var interfaceNode = xml.getElementsByTagName('interface'); - for (i = 0; i < interfaceNode.length; i++) { - node = new parent.interfaceNode(this.specification); - node.decode(this, interfaceNode[i], parent.schema.getAllElementsByName('interface')[1]); + var interfaceNodes = xml.getElementsByTagName('interface'); + for (i = 0; i < interfaceNodes.length; i++) { + node = new interfaceNode(this.specification); + node.decode(this, interfaceNodes[i], parent.schema.querySelectorAll('[name=interface]')[1]); this.interfaces.push(node); } // Now process the survey node options var survey = xml.getElementsByTagName('survey'); - var surveySchema = parent.schema.getAllElementsByName('survey')[0]; + var surveySchema = parent.schema.querySelector('[name=survey]'); for (i = 0; i < survey.length; i++) { var location = survey[i].getAttribute('location'); if (location == 'pre' || location == 'before') { - if (this.preTest !== undefined) { + if (this.preTest.options.length !== 0) { 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 !== undefined) { + if (this.postTest.options.length !== 0) { 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); } } @@ -616,7 +644,7 @@ // Now process the audioelement tags var audioElements = xml.getElementsByTagName('audioelement'); for (i = 0; i < audioElements.length; i++) { - var audioNode = new this.audioElementNode(this.specification); + var audioNode = new audioElementNode(this.specification); audioNode.decode(this, audioElements[i]); this.audioElements.push(audioNode); } @@ -627,7 +655,7 @@ cqNode = cqNode[0]; var commentQuestion = cqNode.firstElementChild; while (commentQuestion) { - node = new this.commentQuestionNode(this.specification); + node = new commentQuestionNode(this.specification); node.decode(parent, commentQuestion); this.commentQuestions.push(node); commentQuestion = commentQuestion.nextElementSibling; @@ -638,7 +666,7 @@ this.encode = function (root) { var AHNode = root.createElement("page"); // First decode the attributes - var attributes = this.schema.getAllElementsByTagName('xs:attribute'); + var attributes = this.schema.querySelectorAll('attribute'); var i; for (i = 0; i < attributes.length; i++) { var name = attributes[i].getAttribute("name"); @@ -671,12 +699,12 @@ return AHNode; }; - this.commentQuestionNode = function (specification) { + function commentQuestionNode(specification) { this.id = undefined; this.name = undefined; this.type = undefined; this.statement = undefined; - this.schema = schemaRoot.getAllElementsByName('commentquestion')[0]; + this.schema = schemaRoot.querySelector('[name=commentquestion]'); this.decode = function (parent, xml) { this.id = xml.id; this.name = xml.getAttribute('name'); @@ -797,9 +825,9 @@ } return node; }; - }; + } - this.audioElementNode = function (specification) { + function audioElementNode(specification) { this.url = undefined; this.id = undefined; this.name = undefined; @@ -816,11 +844,11 @@ this.minNumberPlays = undefined; this.maxNumberPlays = undefined; this.alternatives = []; - this.schema = schemaRoot.getAllElementsByName('audioelement')[0]; + this.schema = schemaRoot.querySelector('[name=audioelement]'); this.parent = undefined; this.decode = function (parent, xml) { this.parent = parent; - var attributeMap = this.schema.getAllElementsByTagName('xs:attribute'); + var attributeMap = this.schema.querySelectorAll('attribute'); for (var i = 0; i < attributeMap.length; i++) { var attributeName = attributeMap[i].getAttribute('name') || attributeMap[i].getAttribute('ref'); var projectAttr = xml.getAttribute(attributeName); @@ -844,7 +872,7 @@ }; this.encode = function (root) { var AENode = root.createElement("audioelement"); - var attributes = this.schema.getAllElementsByTagName('xs:attribute'); + var attributes = this.schema.querySelectorAll('attribute'); for (var i = 0; i < attributes.length; i++) { var name = attributes[i].getAttribute("name"); if (name === null) { @@ -862,6 +890,6 @@ }); return AENode; }; - }; - }; + } + } } diff -r e61531ce764f -r b2f52634c830 test.html --- a/test.html Tue Jun 27 21:22:15 2017 +0100 +++ b/test.html Wed Jul 05 12:22:29 2017 +0100 @@ -37,6 +37,7 @@ + diff -r e61531ce764f -r b2f52634c830 test_create.html --- a/test_create.html Tue Jun 27 21:22:15 2017 +0100 +++ b/test_create.html Wed Jul 05 12:22:29 2017 +0100 @@ -1,31 +1,1086 @@ - - - - - - - - - - - - - - -
-
-
- - - + + + + + + + + + + + + + + + WAET 1.2.1 Test Creator + + + +
+
+

Web Audio Evaluation Tool - Test Creator

+
+ + +
+
+
+ +

Invalid Specification!

+
+
+

Your specification is invalid. Please fix the following issues!

+
    +
  • Errors
  • +
+
+
+ +
+
+

Setup

+
+
+ Interface: + +
+
+ Save URL: + +
+
+ Exit URL: + +
+
+ Randomise Page Order: + +
+
+ Page Pool Size: + +
+
+ Loudness Normalisation (LUFS): + +
+
+ Fixed Sampling Rate: + +
+
+ Pre-Test audio calibration: + +
+
+ Global Cross-fade time: + +
+
+ Global Fragment Pre-Silence: + +
+
+ Global Fragment Post-Silence: + +
+
+ Play audio one-at-a-time: + +
+
+ Minimum number of fragment plays + +
+
+ Maximum number of fragment plays + +
+
+
+

Test Completed Message

+ +
+
+

Session Metrics

+
+
+ Collect Total Test Time: + +
+
+ Collect Fragment Listen Time: + +
+
+ Collect Fragment Initial Position: + +
+
+ Collect Fragment Movements: + +
+
+ Collect Fragment Listened To Flag: + +
+
+ Collect Fragment Moved Flag: + +
+
+ Collect Fragment Listened Flag: + +
+
+
+
+

Pre Test Survey

+ +
+

Survey Entry

+ +
+
+ Survey Type: + +
+
+ Unique Survey Entry ID: + +
+
+ Entry Name: + +
+
+ Mandatory: + +
+
+ Minimum Wait Time (s): + +
+
+ Box Size: + +
+
+ Minimum Selected: + +
+
+ Maximum Selected: + +
+
+ Minimum Value: + +
+
+ Maximum Value: + +
+
+ Video URL: + +
+
+
+

Statement

+ +
+
+

Options

+
+ +
+
+
+
+ +
+
+ Name: + +
+
+ Displayed Text: + +
+
+
+
+
+

Conditionals

+ +
+
+
+ +
+
+ Check Type: + +
+
+ Value: + +
+
+ Jump To On Pass: + +
+
+ Jump To On Fail: + +
+
+
+
+
+
+
+

Post Test Survey

+ +
+

Survey Entry

+ +
+
+ Survey Type: + +
+
+ Unique Survey Entry ID: + +
+
+ Entry Name: + +
+
+ Mandatory: + +
+
+ Minimum Wait Time (s): + +
+
+ Box Size: + +
+
+ Minimum Selected: + +
+
+ Maximum Selected: + +
+
+ Minimum Value: + +
+
+ Maximum Value: + +
+
+ Video URL: + +
+
+
+

Statement

+ +
+
+

Options

+
+ +
+
+
+
+ +
+
+ Name: + +
+
+ Displayed Text: + +
+
+
+
+
+

Conditionals

+ +
+
+
+ +
+
+ Check Type: + +
+
+ Value: + +
+
+ Jump To On Pass: + +
+
+ Jump To On Fail: + +
+
+
+
+
+
+
+

Interface (Globals)

+
+
+
+ Check all fragments played: + +
+
+ Check all fragments fully played: + +
+
+ Check all fragments have been moved: + +
+
+ Check all fragments have comments: + +
+
+ Enforce a scale usage: + + Minimum: + + Maximum: + +
+
+ Show master volume control: + +
+
+ Show playhead: + +
+
+ Show Page Count: + +
+
+ Show Fragment Comments: + +
+
+
+
+
+
+ +
+
+

Page

+ +
+
+ Unique ID: + +
+
+ Fragment common-root URL: + +
+
+ Randomise Fragment Order: + +
+
+ Repeat Page N-times: + +
+
+ Loop audio: + +
+
+ Synchronous audio playback: + +
+
+ Loudness (page): + +
+
+ Label type: + +
+
+ Label Start: + +
+
+ Fragment pool size: + +
+
+ Always include page: + +
+
+ Fixed Page Position: + +
+
+ Fragment pre-silence: + +
+
+ Fragment post-silence: + +
+
+ Cannot interupt audio: + +
+
+ Only move playing audio: + +
+
+ Minimum number of fragment plays + +
+
+ Maximum number of fragment plays + +
+
+
+

Page Title

+ +
+
+

Comment box text prefix

+ +

Example: + {{page.commentboxprefix}} A +

+
+
+

Pre Page Survey

+ +
+

Survey Entry

+ +
+
+ Survey Type: + +
+
+ Unique Survey Entry ID: + +
+
+ Entry Name: + +
+
+ Mandatory: + +
+
+ Minimum Wait Time (s): + +
+
+ Box Size: + +
+
+ Minimum Selected: + +
+
+ Maximum Selected: + +
+
+ Minimum Value: + +
+
+ Maximum Value: + +
+
+ Video URL: + +
+
+
+

Statement

+ +
+
+

Options

+
+ +
+
+
+
+ +
+
+ Name: + +
+
+ Displayed Text: + +
+
+
+
+
+

Conditionals

+ +
+
+
+ +
+
+ Check Type: + +
+
+ Value: + +
+
+ Jump To On Pass: + +
+
+ Jump To On Fail: + +
+
+
+
+
+
+
+

Post Page Survey

+ +
+

Survey Entry

+ +
+
+ Survey Type: + +
+
+ Unique Survey Entry ID: + +
+
+ Entry Name: + +
+
+ Mandatory: + +
+
+ Minimum Wait Time (s): + +
+
+ Box Size: + +
+
+ Minimum Selected: + +
+
+ Maximum Selected: + +
+
+ Minimum Value: + +
+
+ Maximum Value: + +
+
+ Video URL: + +
+
+
+

Statement

+ +
+
+

Options

+
+ +
+
+
+
+ +
+
+ Name: + +
+
+ Displayed Text: + +
+
+
+
+
+

Conditionals

+ +
+
+
+ +
+
+ Check Type: + +
+
+ Value: + +
+
+ Jump To On Pass: + +
+
+ Jump To On Fail: + +
+
+
+
+
+
+ +
+

Interface

+ +
+
+
+ Check all fragments played: + +
+
+ Check all fragments fully played: + +
+
+ Check all fragments have been moved: + +
+
+ Check all fragments have comments: + +
+
+ Enforce a scale usage: + + Minimum: + + Maximum: + +
+
+ Show master volume control: + +
+
+ Show playhead: + +
+
+ Show Page Count: + +
+
+ Show Fragment Comments: + +
+
+
+
+

Axis Title

+ +
+
+ Axis name (in saves): + +
+
+
+
+

Axis Image

+ +
+
+

Axis Scales

+ + + +
+
+
+ +
+
+ Position: + +
+
+ Text: + +
+
+
+
+
+
+

Comment Questions

+ +
+ +
+
+ Unique ID: + +
+
+ Common Name: + +
+
+ Minimum: + +
+
+ Maximum: + +
+
+ Step size: + +
+
+ Initial Value: + +
+
+
+

Question:

+ +
+
+

Options

+
+
+
+ +
+
+ Name: + +
+
+ Display Text: + +
+
+
+
+
+
+ +
+

Audio Fragment

+ +
+
+ Unique ID: + +
+
+ URL: + + Full URL: {{page.hostURL}}{{fragment.url}} +
+
+ Fragment Gain (dB): + +
+
+ Fragment Label: + +
+
+ Fragment Common name: + +
+
+ Fragment Type: + +
+
+ Anchor must be below: + +
+
+ Reference must be above: + +
+
+ Loudness: + +
+
+ Always include fragment: + +
+
+ Fragment Pre-Silence: + +
+
+ Fragment Post-Silence: + +
+
+ Fragment playback start position (s): + +
+
+ Fragment playback stop position (s): + +
+
+ Fragment sampling rate: + +
+
+ Fragment Image (URL): + +
+
+ Minimum number of plays + +
+
+ Maximum number of plays + +
+
+
+
+
+
+ +
+
+ + + diff -r e61531ce764f -r b2f52634c830 test_create/attributes.json --- a/test_create/attributes.json Tue Jun 27 21:22:15 2017 +0100 +++ b/test_create/attributes.json Wed Jul 05 12:22:29 2017 +0100 @@ -1,35 +1,1 @@ -{ - "id": "ID", - "mandatory": "Mandatory", - "name": "Name", - "interface": "Interface Module", - "projectReturn": "Save Return URL", - "returnURL": "On complete redirect URL", - "randomiseOrder": "Randomise Order", - "testPages": "Test Pages", - "loudness": "Target Loudness (LUFS)", - "sampleRate": "Required Sample Rate", - "hostURL": "Element URL Prefix", - "repeatCount": "Repeat Count", - "loop": "Loop playback", - "synchronous": "Synchronous playback", - "type": "Type", - "min": "Minimum", - "max": "Maximum", - "position": "Position", - "url": "URL", - "gain": "Gain (dB)", - "marker": "Marker", - "boxsize": "Box Size", - "label": "Label", - "calibration": "Perform Calibration", - "preSilence": "Pre Silence", - "postSilence": "Post Silence", - "poolSize": "Pool Size", - "alwaysInclude": "Always Include", - "crossFade": "Cross Fade", - "check": "Check", - "value": "Value", - "jumpToOnPass": "Jump To ID On Pass", - "jumpToOnFail": "Jump To ID On Fail" -} + diff -r e61531ce764f -r b2f52634c830 test_create/custom.css --- a/test_create/custom.css Tue Jun 27 21:22:15 2017 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,20 +0,0 @@ -div#content > div.node { - background-color: rgb(200, 228, 151); -} -div#content > div#setup { - background-color: coral; -} -input:disabled+span { - text-decoration: line-through; -} -div.attribute { - float: none; -} -div.attribute input { - max-width: 100%; - width: 300px; -} -div.attribute input[type=radio], -div.attribute input[type=checkbox] { - width: 10px; -} diff -r e61531ce764f -r b2f52634c830 test_create/interfaces/specifications.json --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test_create/interfaces/specifications.json Wed Jul 05 12:22:29 2017 +0100 @@ -0,0 +1,573 @@ +{ + "interfaces": [ + { + "name": "Audio Perceptual Evaluation (APE)", + "interface": "APE", + "description": { + "en": "Audio Perceptual Evaluation. A multi-stimulus test where each audio fragment is shown on one continuous slider. Fragments are randomnly positioned along the slider. The user clicks a fragment to play and drags to move." + }, + "checks": [], + "show": [], + "elements": [] + }, { + "name": "MUSHRA", + "interface": "MUSHRA", + "description": { + "en": "Multi-stimulus with hidden reference and anchor. Each fragment is shown on its own vertical slider. One fragment must be labelled as a reference and another labelled as an anchor. One external reference must also be shown." + }, + "scales": ["ACR"], + "checks": [{ + "name": "fragmentMoved", + "support": "none" + }, { + "name": "fragmentPlayed", + "support": "none" + }, { + "name": "fragmentFullPlayback", + "support": "none" + }, { + "name": "fragmentComments", + "support": "none" + }, { + "name": "scalerange", + "support": "none" + }], + "show": [{ + "name": "volume", + "support": "none" + }, { + "name": "page-count", + "support": "none" + }, { + "name": "playhead", + "support": "none" + }, { + "name": "comments", + "support": "none" + }], + "elements": [{ + "anchor": { + "min": 1, + "max": "undefined" + }, + "reference": { + "min": 1, + "max": "undefined" + }, + "outsidereference": { + "min": 1, + "max": "undefined" + } + }] + }, { + "name": "Vertical Sliders", + "interface": "MUSHRA", + "description": { + "en": "Each element is given its own vertical slider with user defined scale markers." + } + }, { + "name": "Horizontal Sliders", + "interface": "horizontal", + "description": { + "en": "Each element is given its own horizontal slider with user defined scale markers." + } + }, { + "name": "Discrete", + "interface": "discrete", + "description": { + "en": "Each element is given a horizontal scale broken into a number of discrete choices. The number of choices is defined by the scale markers." + } + }, { + "name": "Rank", + "interface": "ordinal", + "description": { + "en": "Each stimulus is placed on a discrete scale equalling the number of fragments. The fragments are then ranked based on the question posed. Only one element can occupy a rank position" + } + }, { + "name": "Likert", + "interface": "discrete", + "description": { + "en": "Each stimulus is placed on a discrete scale. The scale is fixed to the Likert scale options of 'Strongly Disagree', 'Disagree', 'Neutral', 'Agree' and 'Strongly Agree'" + }, + "scales": ["Likert"], + "checks": [{ + "name": "fragmentPlayed", + "support": "none" + }, { + "name": "fragmentFullPlayback", + "support": "none" + }, { + "name": "fragmentComments", + "support": "none" + }], + "show": [{ + "name": "volume", + "support": "none" + }, { + "name": "page-count", + "support": "none" + }, { + "name": "playhead", + "support": "none" + }, { + "name": "comments", + "support": "none" + }] + }, { + "name": "ABC/HR", + "interface": "MUSHRA", + "description": { + "en": "Each stimulus is placed on a vertical slider. The scale is fixed with the labels 'Imperceptible' to 'Very Annoying'" + }, + "scales": ["ABC"], + "checks": [{ + "name": "fragmentMoved", + "support": "none" + }, { + "name": "fragmentPlayed", + "support": "none" + }, { + "name": "fragmentFullPlayback", + "support": "none" + }, { + "name": "fragmentComments", + "support": "none" + }], + "show": [{ + "name": "volume", + "support": "none" + }, { + "name": "page-count", + "support": "none" + }, { + "name": "playhead", + "support": "none" + }, { + "name": "comments", + "support": "none" + }] + }, { + "name": "Bipolar", + "interface": "horizontal", + "description": { + "en": "Each stimulus is placed on a horizontal slider and initialised to the value '0'. The scale operates from -50 to +5-. In the results this is normalised, like all other interfaces, from 0 (-50) to 1 (+50)" + }, + "scales": ["Bipolar"], + "checks": [{ + "name": "fragmentMoved", + "support": "none" + }, { + "name": "fragmentPlayed", + "support": "none" + }, { + "name": "fragmentFullPlayback", + "support": "none" + }, { + "name": "fragmentComments", + "support": "none" + }], + "show": [{ + "name": "volume", + "support": "none" + }, { + "name": "page-count", + "support": "none" + }, { + "name": "playhead", + "support": "none" + }, { + "name": "comments", + "support": "none" + }] + }, { + "name": "Absolute Category Rating", + "interface": "discrete", + "description": { + "en": "Each element is on a discrete scale of 'Bad', 'Poor', 'Fair', 'Good' and 'Excellent'. Each element must be given a rating." + }, + "scales": ["ACR"], + "checks": [{ + "name": "fragmentMoved", + "support": "mandatory" + }, { + "name": "fragmentPlayed", + "support": "none" + }, { + "name": "fragmentFullPlayback", + "support": "none" + }, { + "name": "fragmentComments", + "support": "none" + }], + "show": [{ + "name": "volume", + "support": "none" + }, { + "name": "page-count", + "support": "none" + }, { + "name": "playhead", + "support": "none" + }, { + "name": "comments", + "support": "none" + }] + }, { + "name": "Discrete Category Rating", + "interface": "discrete", + "description": { + "en": "" + }, + "scales": ["DCR"], + "checks": [{ + "name": "fragmentPlayed", + "support": "none" + }, { + "name": "fragmentFullPlayback", + "support": "none" + }, { + "name": "fragmentComments", + "support": "none" + }], + "show": [{ + "name": "volume", + "support": "none" + }, { + "name": "page-count", + "support": "none" + }, { + "name": "playhead", + "support": "none" + }, { + "name": "comments", + "support": "none" + }] + }, { + "name": "Hedonic Cat. Rating", + "interface": "MUSHRA", + "description": { + "en": "" + }, + "scales": ["Hedonic Category Rating Scale"], + "checks": [{ + "name": "fragmentMoved", + "support": "mandatory" + }, { + "name": "fragmentPlayed", + "support": "none" + }, { + "name": "fragmentFullPlayback", + "support": "none" + }, { + "name": "fragmentComments", + "support": "none" + }], + "show": [{ + "name": "volume", + "support": "none" + }, { + "name": "page-count", + "support": "none" + }, { + "name": "playhead", + "support": "none" + }, { + "name": "comments", + "support": "none" + }], + "elements": { + "outsidereference": { + "min": 1, + "max": 1 + } + } + }, { + "name": "ITUR5PCIS", + "interface": "MUSHRA", + "description": { + "en": "" + }, + "scales": ["ABC"], + "checks": [{ + "name": "fragmentMoved", + "support": "none" + }, { + "name": "fragmentPlayed", + "support": "none" + }, { + "name": "fragmentFullPlayback", + "support": "none" + }, { + "name": "fragmentComments", + "support": "none" + }], + "show": [{ + "name": "volume", + "support": "none" + }, { + "name": "page-count", + "support": "none" + }, { + "name": "playhead", + "support": "none" + }, { + "name": "comments", + "support": "none" + }], + "elements": { + "outsidereference": { + "min": 1, + "max": 1 + } + } + }, { + "name": "Pairwise", + "interface": "AB", + "description": { + "en": "A discrete interface where each page holds each fragment. The user must select one fragment. All other fragments are not selected" + }, + "hasScales": "false", + "elements": { + "number": { + "min": 2, + "max": "undefined" + } + } + }, { + "name": "AB", + "interface": "AB", + "description": { + "en": "Each page contains two audio fragments. The user must select one of the fragments to proceed. There can be an outside reference." + }, + "hasScales": "false", + "checks": [{ + "name": "fragmentPlayed", + "support": "mandatory" + }], + "elements": { + "number": { + "min": 2, + "max": 2 + }, + "outsidereference": { + "min": 0, + "max": 1 + } + } + }, { + "name": "ABX", + "interface": "ABX", + "description": { + "en": "Each page has two audio fragments presented as A and B. The test duplicates one of the fragments and presents it as X. The user must choose which, out of A or B, is closest to X." + }, + "hasScales": "false", + "checks": [{ + "name": "fragmentPlayed", + "support": "mandatory" + }], + "elements": { + "number": { + "min": 2, + "max": 2 + }, + "outsidereference": { + "min": 0, + "max": 1 + } + } + }, { + "name": "Timeline", + "interface": "timeline", + "description": { + "en": "Each fragment is displayed with a clickable waveform of itself. The user must click on the waveform at the location that a specific event occured. Users can then enter in information about this event. This test is unit-/value-less." + } + } + ], + "scales": [ + { + "name": "Likert", + "scales": [ + { + "text": "Strongly Disagree", + "position": 0 + }, + { + "text": "Disagree", + "position": 25 + }, + { + "text": "Neutral", + "position": 50 + }, + { + "text": "Agree", + "position": 75 + }, + { + "text": "Strongly Agree", + "position": 100 + } + ] + }, { + "name": "ABC", + "scales": [ + { + "text": "Very annoying", + "position": 0 + }, + { + "text": "Annoying", + "position": 25 + }, + { + "text": "Slightly annoying", + "position": 50 + }, + { + "text": "Perceptible but not annoying", + "position": 75 + }, + { + "text": "Imperceptible", + "position": 100 + } + ] + }, { + "name": "Bipolar", + "scales": [ + { + "text": "-50", + "position": 0 + }, + { + "text": "0", + "position": 50 + }, + { + "text": "50", + "position": 100 + } + ] + }, { + "name": "ACR", + "scales": [ + { + "text": "Bad", + "position": 0 + }, + { + "text": "Poor", + "position": 25 + }, + { + "text": "Fair", + "position": 50 + }, + { + "text": "Good", + "position": 75 + }, + { + "text": "Excellent", + "position": 100 + } + ] + }, { + "name": "DCR", + "scales": [ + { + "text": "(1) Very Annoying", + "position": 0 + }, + { + "text": "(2) Annoying", + "position": 25 + }, + { + "text": "(3) Slightly Annoying", + "position": 50 + }, + { + "text": "(4) Audible but not Annoying", + "position": 75 + }, + { + "text": "(5) Inaudible", + "position": 100 + } + ] + }, { + "name": "CCR", + "scales": [ + { + "text": "Much Worse", + "position": 12 + }, + { + "text": "Worse", + "position": 25 + }, + { + "text": "Slightly Worse", + "position": 38 + }, + { + "text": "About the same", + "position": 50 + }, + { + "text": "Slightly Better", + "position": 62 + }, + { + "text": "Better", + "position": 75 + }, + { + "text": "Much Better", + "position": 88 + } + ] + }, { + "name": "Hedonic Category Rating Scale", + "scales": [ + { + "text": "Dislike Extremeley", + "position": 10 + }, + { + "text": "Dislike Very Much", + "position": 20 + }, + { + "text": "Dislike Moderate", + "position": 30 + }, + { + "text": "Dislike Slightly", + "position": 40 + }, + { + "text": "Neither like nor dislike", + "position": 50 + }, + { + "text": "Like Slightly", + "position": 60 + }, + { + "text": "Like Moderate", + "position": 70 + }, + { + "text": "Like Very Much", + "position": 80 + }, + { + "text": "Like Extremely", + "position": 90 + } + ] + } + ] +} diff -r e61531ce764f -r b2f52634c830 test_create/style.css --- a/test_create/style.css Tue Jun 27 21:22:15 2017 +0100 +++ b/test_create/style.css Wed Jul 05 12:22:29 2017 +0100 @@ -1,131 +1,95 @@ -div#blanket { - z-index: 2; - background-color: rgba(0, 0, 0, 0.5); +#screenblank { + z-index: 1; width: 100%; height: 100%; position: fixed; + top: 0px; left: 0px; + background-color: rgba(0, 0, 0, 0.75); +} +#popupHolder { + text-align: center; + width: 100%; + height: 100%; + position: fixed; top: 0px; + z-index: 2; } -div#popupHolder { +.popup { + position: relative; z-index: 3; background-color: rgba(255, 255, 255, 1); width: 730px; height: 480px; - position: fixed; + display: inline-block; + text-align: left; border-radius: 10px; box-shadow: 0px 0px 50px #000; padding: 10px; + margin-top: 20px; } -div#popup-title-holder { +.popupTitle { width: 100%; height: 50px; font-size: 2em; + text-align: center; } -button.popup-button { - width: 60px; - height: 27px; - padding: 5px; +.popupButtons { position: absolute; - bottom: 10px; + bottom: 5px; + width: 90%; + margin-left: 30px; + display: block; + align-self: center; } -button#popup-proceed { - right: 10px; +#popupBack { + float: left; } -button#popup-back { - left: 10px; +#popupNext { + float: right; } -div.drag-area { - border: 3px black dashed; +#introdragdrop { + width: 100%; + height: 100px; + border: 2px dashed black; + font-size: 1.5em; + text-align: center; + padding-top: 30px; + color: grey; } -div.drag-over { - background-color: aquamarine; +.new-test { + cursor: pointer; } -div.drag-dropped { - background-color: aqua; +.new-test:hover { + font-style: italic; } -div.drag-error { - background-color: coral +.node { + padding: 10px 20px; + border: 2px solid black; + margin: 20px; + border-radius: 20px; + background-color: inherit; } -div#project-drop { - width: 99%; - height: 50px; - margin: 10px 0px; +.node > textarea { + width: 80%; } -div.popup-checkbox { - padding: 5px; +.node > h1, +h2, +h3, +h4, +h5 { + text-align: center; } -div.popup-checkbox input { - margin: 0px 5px; +.attribute { + display: inline-block; + margin: 0px 10px; + border-left: 1px solid grey; + border-right: 1px solid grey; + padding: 5px 5px; } -div.popup-option-entry { - padding: 5px 0px; - border-bottom: 1px solid; +#setupNode { + background-color: rgba(255, 10, 10, 0.25); } -div.disabled { - color: rgb(100, 100, 100); +.pageNode { + background-color: rgba(10, 255, 10, 0.25); } -div#page-holder > div.node { - background-color: rgb(200, 228, 151); -} -div#content > div#setup { - background-color: coral; -} -div.node { - float: left; - padding: 10px; - border: black 2px solid; - border-radius: 10px; - margin: 10px; - min-width: 92%; - background-color: rgba(255, 255, 255, 0.5); -} -div.node-title { - float: left; - width: 100%; - font-size: 2em; - margin: 5px 0px; -} -div.node-attributes { - min-width: 92%; - float: none; - padding: 10px; -} -div.attribute { - float: left; - margin-right: 10px; -} -div.node-children { - float: left; - min-width: 92%; -} -div.node-buttons { - float: left; - min-width: 92%; -} -div.attribute input { - max-width: 100%; - width: 300px; - margin-right: 10px; -} -div.attribute input[type=number] { - width: 80px; -} -div.attribute input[type=radio], -div.attribute input[type=checkbox] { - width: 10px; -} -input:disabled+label { - text-decoration: line-through; -} -div.survey-entry-attribute { - margin: 10px 0px; - border: 1px gray solid; - border-radius: 5px; - height: 40px; - line-height: 40px; - padding: 0px 10px; -} -div.survey-entry-attribute span { - margin-right: 10px; -} diff -r e61531ce764f -r b2f52634c830 test_create/test_core.js --- a/test_create/test_core.js Tue Jun 27 21:22:15 2017 +0100 +++ b/test_create/test_core.js Wed Jul 05 12:22:29 2017 +0100 @@ -1,2339 +1,453 @@ -var interfaceSpecs; -var xmlHttp; -var popupObject; -var popupStateNodes; -var specification; -var convert; -var attributeText; -var page_lang = "en"; +/* globals document, angular, window, Promise, XMLHttpRequest, Specification, XMLSerializer, Blob, DOMParser, FileReader, $*/ +function get(url) { + // Return a new promise. + return new Promise(function (resolve, reject) { + // Do the usual XHR stuff + var req = new XMLHttpRequest(); + req.open('GET', url); -// Firefox does not have an XMLDocument.prototype.getElementsByName -// and there is no searchAll style command, this custom function will -// search all children recusrively for the name. Used for XSD where all -// element nodes must have a name and therefore can pull the schema node -XMLDocument.prototype.getAllElementsByName = function (name) { - name = String(name); - var selected = this.documentElement.getAllElementsByName(name); - return selected; + req.onload = function () { + // This is called even on 404 etc + // so check the status + if (req.status == 200) { + // Resolve the promise with the response text + resolve(req.response); + } else { + // Otherwise reject with the status text + // which will hopefully be a meaningful error + reject(Error(req.statusText)); + } + }; + + // Handle network errors + req.onerror = function () { + reject(Error("Network Error")); + }; + + // Make the request + req.send(); + }); } -Element.prototype.getAllElementsByName = function (name) { - name = String(name); - var selected = []; - var node = this.firstElementChild; - while (node != null) { - if (node.getAttribute('name') == name) { - selected.push(node); - } - if (node.childElementCount > 0) { - selected = selected.concat(node.getAllElementsByName(name)); - } - node = node.nextElementSibling; - } - return selected; +var AngularInterface = angular.module("creator", []); + +var specification = new Specification(); + +window.onload = function () { + // Get the test interface specifications + $(function () { + $('[data-toggle="popover"]').popover(); + }); +}; + +function handleFiles(event) { + var s = angular.element(event.currentTarget).scope(); + s.handleFiles(event); + s.$apply(); } -XMLDocument.prototype.getAllElementsByTagName = function (name) { - name = String(name); - var selected = this.documentElement.getAllElementsByTagName(name); - return selected; -} +AngularInterface.controller("view", ['$scope', '$element', '$window', function ($s, $e, $w) { + $s.popupVisible = true; + $s.testSpecifications = {}; -Element.prototype.getAllElementsByTagName = function (name) { - name = String(name); - var selected = []; - var node = this.firstElementChild; - while (node != null) { - if (node.nodeName == name) { - selected.push(node); + (function () { + new Promise(function (resolve, reject) { + var xml = new XMLHttpRequest(); + xml.open("GET", "test_create/interfaces/specifications.json"); + xml.onload = function () { + if (xml.status === 200) { + resolve(xml.responseText); + return; + } + reject(xml.status); + }; + xml.onerror = function () { + reject(new Error("Network Error")); + }; + xml.send(); + }).then(JSON.parse).then(function (data) { + $s.testSpecifications = data; + $s.$apply(); + }); + })(); + + $s.showPopup = function () { + $s.popupVisible = true; + }; + $s.hidePopup = function () { + $s.popupVisible = false; + }; + $s.globalSchema = undefined; + get("xml/test-schema.xsd").then(function (text) { + specification.processSchema(text); + $s.globalSchema = specification.getSchema(); + }); + $s.specification = specification; + $s.selectedTestPrototype = undefined; + $s.setTestPrototype = function (obj) { + $s.selectedTestPrototype = obj; + $w.specification.interface = obj.interface; + } + + $s.addPage = function () { + $s.specification.createNewPage(); + }; + + $s.removePage = function (page) { + var index = $s.specification.pages.findIndex(function (a) { + return a == page; + }); + if (index === -1) { + throw ("Invalid Page"); } - if (node.childElementCount > 0) { - selected = selected.concat(node.getAllElementsByTagName(name)); + $s.specification.pages.splice(index, 1); + }; + + $s.exportXML = function () { + var s = new XMLSerializer(); + var doc = specification.encode(); + var xmlstr = s.serializeToString(doc); + var bb = new Blob([s.serializeToString(doc)], { + type: 'application/xml' + }); + var dnlk = window.URL.createObjectURL(bb); + var a = document.createElement("a"); + a.href = dnlk; + a.download = "test.xml"; + a.click(); + window.URL.revokeObjectURL(dnlk); + }; + $s.validated = false; + $s.showValidationMessages = false; + $s.validate = function () { + var s = new XMLSerializer(); + var Module = { + xml: s.serializeToString(specification.encode()), + schema: specification.getSchemaString(), + arguments: ["--noout", "--schema", 'test-schema.xsd', 'document.xml'] + }; + var xmllint = validateXML(Module); + console.log(xmllint); + if (xmllint != 'document.xml validates\n') { + $s.validated = false; + var list = $e[0].querySelector("#validation-error-list"); + while (list.firstChild) { + list.removeChild(list.firstChild); + } + var errors = xmllint.split('\n'); + errors = errors.slice(0, errors.length - 2); + errors.forEach(function (str) { + var li = document.createElement("li"); + li.textContent = str; + list.appendChild(li); + }); + } else { + $s.validated = true; } - node = node.nextElementSibling; + $s.showValidationMessages = true; } - return selected; -} + $s.hideValidationMessages = function () { + $s.showValidationMessages = false; + } +}]); -// Firefox does not have an XMLDocument.prototype.getElementsByName -if (typeof XMLDocument.prototype.getElementsByName != "function") { - XMLDocument.prototype.getElementsByName = function (name) { - name = String(name); - var node = this.documentElement.firstElementChild; - var selected = []; - while (node != null) { - if (node.getAttribute('name') == name) { - selected.push(node); +AngularInterface.controller("introduction", ['$scope', '$element', '$window', function ($s, $e, $w) { + $s.state = 0; + $s.next = function () { + $s.state++; + if ($s.state > 1 || $s.file) { + $s.hidePopup(); + } + }; + $s.back = function () { + $s.state--; + }; + $s.mouseover = function (name) { + var obj = $s.testSpecifications.interfaces.find(function (i) { + return i.name == name; + }); + if (obj) { + $s.description = obj.description.en; + } + }; + $s.initialise = function (name) { + var obj = $s.testSpecifications.interfaces.find(function (i) { + return i.name == name; + }); + if (obj === undefined) { + throw ("Cannot find specification"); + } + $s.setTestPrototype(obj); + }; + // Get the test interface specifications + $s.file = undefined; + $s.description = ""; + + $s.handleFiles = function ($event) { + $s.file = $event.currentTarget.files[0]; + var r = new FileReader(); + r.onload = function () { + var p = new DOMParser(); + specification.decode(p.parseFromString(r.result, "text/xml")); + $s.$apply(); + }; + r.readAsText($s.file); + }; +}]); + +AngularInterface.controller("setup", ['$scope', '$element', '$window', function ($s, $e, $w) { + function initialise() { + if ($s.globalSchema) { + $s.schema = $s.globalSchema.querySelector("[name=setup]"); + } + } + $s.schema = undefined; + $s.attributes = []; + + $s.$watch("globalSchema", initialise); + $s.$watch("specification.metrics.enabled.length", function () { + var metricsNode = document.getElementById("metricsNode"); + if (!$s.specification.metrics) { + return; + } + metricsNode.querySelectorAll("input").forEach(function (DOM) { + DOM.checked = false; + }); + $s.specification.metrics.enabled.forEach(function (metric) { + var DOM = metricsNode.querySelector("[value=" + metric + "]"); + if (DOM) { + DOM.checked = true; } - node = node.nextElementSibling; - } - return selected; - } -} + }); + }); -window.onload = function () { - specification = new Specification(); - convert = new SpecificationToHTML(); - xmlHttp = new XMLHttpRequest(); - xmlHttp.open("GET", "test_create/interface-specs.xml", true); - xmlHttp.onload = function () { - var parse = new DOMParser(); - interfaceSpecs = parse.parseFromString(xmlHttp.response, 'text/xml'); - buildPage(); - popupObject.postNode(popupStateNodes.state[0]) - } - xmlHttp.send(); - - var xsdGet = new XMLHttpRequest(); - xsdGet.open("GET", "xml/test-schema.xsd", true); - xsdGet.onload = function () { - var parse = new DOMParser(); - specification.schema = parse.parseFromString(xsdGet.response, 'text/xml');; - } - xsdGet.send(); - - var jsonAttribute = new XMLHttpRequest(); - jsonAttribute.open("GET", "test_create/attributes.json", true); - jsonAttribute.onload = function () { - attributeText = JSON.parse(jsonAttribute.response) - } - jsonAttribute.send(); -} - -function buildPage() { - popupObject = new function () { - this.object = document.getElementById("popupHolder"); - this.blanket = document.getElementById("blanket"); - - this.popupTitle = document.createElement("div"); - this.popupTitle.id = "popup-title-holder"; - this.popupTitle.align = "center"; - this.titleDOM = document.createElement("span"); - this.titleDOM.id = "popup-title"; - this.popupTitle.appendChild(this.titleDOM); - this.object.appendChild(this.popupTitle); - - this.popupContent = document.createElement("div"); - this.popupContent.id = "popup-content"; - this.object.appendChild(this.popupContent); - - this.proceedButton = document.createElement("button"); - this.proceedButton.id = "popup-proceed"; - this.proceedButton.className = "popup-button"; - this.proceedButton.textContent = "Next"; - this.proceedButton.onclick = function () { - popupObject.popupContent.innerHTML = null; - if (typeof popupObject.shownObject.continue == "function") { - popupObject.shownObject.continue(); - } else { - popupObject.hide(); + $s.enableMetric = function ($event) { + var metric = $event.currentTarget.value; + var index = specification.metrics.enabled.findIndex(function (a) { + return a == metric; + }); + if ($event.currentTarget.checked) { + if (index == -1) { + specification.metrics.enabled.push(metric); } - }; - this.object.appendChild(this.proceedButton); - - this.backButton = document.createElement("button"); - this.backButton.id = "popup-back"; - this.backButton.className = "popup-button"; - this.backButton.textContent = "Back"; - this.backButton.onclick = function () { - popupObject.popupContent.innerHTML = null; - popupObject.shownObject.back(); - }; - this.object.appendChild(this.backButton); - - this.shownObject; - - this.resize = function () { - var w = window.innerWidth; - var h = window.innerHeight; - this.object.style.left = Math.floor((w - 750) / 2) + 'px'; - this.object.style.top = Math.floor((h - 500) / 2) + 'px'; - } - - this.show = function () { - this.object.style.visibility = "visible"; - this.blanket.style.visibility = "visible"; - if (typeof this.shownObject.back == "function") { - this.backButton.style.visibility = "visible"; - } else { - this.backButton.style.visibility = "hidden"; + } else { + if (index >= 0) { + specification.metrics.enabled.splice(index, 1); } } - - this.hide = function () { - this.object.style.visibility = "hidden"; - this.blanket.style.visibility = "hidden"; - this.backButton.style.visibility = "hidden"; - } - - this.postNode = function (postObject) { - //Passed object must have the following: - // Title: text to show in the title - // Content: HTML DOM to show on the page - // On complete this HTML DOM is destroyed so make sure it is referenced elsewhere for processing - this.titleDOM.textContent = postObject.title; - this.popupContent.appendChild(postObject.content); - this.shownObject = postObject; - if (typeof this.shownObject.back == "function") { - this.backButton.style.visibility = "visible"; - } else { - this.backButton.style.visibility = "hidden"; - } - if (typeof this.shownObject.continue == "function") { - this.proceedButton.textContent = "Next"; - } else { - this.proceedButton.textContent = "Finish"; - } - this.show(); - } - - this.resize(); - this.hide(); }; - popupStateNodes = new function () { - // This defines the several popup states wanted - this.state = []; - this.state[0] = new function () { - this.title = "Welcome"; - this.content = document.createElement("div"); - this.content.id = "state-0"; - var span = document.createElement("span"); - span.textContent = "Welcome to the WAET test creator tool. This will allow you to create a new test from scratch to suit your testing needs. If you wish to update a test file, please drag and drop the XML document into the area below for processing, otherwise press 'Next' to start a new test. This tool generates files for the WAET 1.2.0 version." - this.content.appendChild(span); - this.dragArea = document.createElement("div"); - this.dragArea.className = "drag-area"; - this.dragArea.id = "project-drop"; - this.content.appendChild(this.dragArea); + $s.configure = function () {} - this.dragArea.addEventListener('dragover', function (e) { - e.stopPropagation(); - e.preventDefault(); - e.dataTransfer.dropEffect = 'copy'; - e.currentTarget.className = "drag-area drag-over"; + $s.$watch("selectedTestPrototype", $s.configure); +}]); + +AngularInterface.controller("survey", ['$scope', '$element', '$window', function ($s, $e, $w) { + $s.addSurveyEntry = function () { + $s.survey.addOption(); + }; + $s.removeSurveyEntry = function (entry) { + var index = $s.survey.options.findIndex(function (a) { + return a == entry; + }); + if (index === -1) { + throw ("Invalid Entry"); + } + $s.survey.options.splice(index, 1); + }; +}]); + +AngularInterface.controller("surveyOption", ['$scope', '$element', '$window', function ($s, $e, $w) { + + $s.removeOption = function (option) { + var index = $s.opt.options.findIndex(function (a) { + return a == option; + }); + if (index === -1) { + throw ("Invalid option"); + } + $s.opt.options.splice(index, 1); + }; + $s.addOption = function () { + $s.opt.options.push({ + name: "", + text: "" + }); + }; + + $s.addCondition = function () { + $s.opt.conditions.push({ + check: "equals", + value: "", + jumpToOnPass: undefined, + jumpToOnFail: undefined + }); + }; + + $s.removeCondition = function (condition) { + var index = $s.opt.conditions.findIndex(function (c) { + return c == condition; + }); + if (index === -1) { + throw ("Invalid Condition"); + } + $s.opt.conditions.splice(index, 1); + }; +}]); + +AngularInterface.controller("interfaceNode", ['$scope', '$element', '$window', function ($s, $e, $w) { + $s.$watch("interface.options.length", function () { + if (!$s.interface || !$s.interface.options) { + return; + } + var options = $e[0].querySelector(".interfaceOptions").querySelectorAll(".attribute"); + options.forEach(function (option) { + var name = option.getAttribute("name"); + var index = $s.interface.options.findIndex(function (io) { + return io.name == name; }); + option.querySelector("input").checked = (index >= 0); + if (name == "scalerange" && index >= 0) { + option.querySelector("[name=min]").value = $s.interface.options[index].min; + option.querySelector("[name=max]").value = $s.interface.options[index].max; + } + }); + }); + $s.enableInterfaceOption = function ($event) { + var name = $event.currentTarget.parentElement.getAttribute("name"); + var type = $event.currentTarget.parentElement.getAttribute("type"); + var index = $s.interface.options.findIndex(function (io) { + return io.name == name; + }); + if (index == -1 && $event.currentTarget.checked) { + var obj = $s.interface.options.push({ + name: name, + type: type + }); + if (name == "scalerange") { + obj.min = $event.currentTarget.parentElement.querySelector("[name=min]").value; + obj.max = $event.currentTarget.parentElement.querySelector("[name=max]").value; + } + } else if (index >= 0 && !$event.currentTarget.checked) { + $s.interface.options.splice(index, 1); + } + }; + $s.scales = []; + $s.removeScale = function (scale) { + var index = $s.interface.scales.findIndex(function (s) { + return s == scale; + }); + if (index >= 0) { + $s.interface.scales.splice(index, 1); + } + }; + $s.addScale = function () { + $s.interface.scales.push({ + position: undefined, + text: undefined + }); + }; + $s.clearScales = function () { + $s.interface.scales = []; + }; + $s.useScales = function (scale) { + $s.clearScales(); + scale.scales.forEach(function (s) { + $s.interface.scales.push(s); + }); + $s.selectedScale = scale.name; + }; + $s.selectedScale = undefined; - this.dragArea.addEventListener('dragexit', function (e) { - e.stopPropagation(); - e.preventDefault(); - e.dataTransfer.dropEffect = 'copy'; - e.currentTarget.className = "drag-area"; + $s.configure = function () { + if ($s.selectedTestPrototype === undefined) { + return; + } + if ($s.selectedTestPrototype.checks && $s.selectedTestPrototype.checks.length >= 1) { + $s.selectedTestPrototype.checks.forEach(function (entry) { + var dom = $e[0].querySelector("[name=\"" + entry.name + "\"] input"); + if (entry.support == "none") { + dom.checked = false; + dom.disabled = true; + } }); - - this.dragArea.addEventListener('drop', function (e) { - e.stopPropagation(); - e.preventDefault(); - e.currentTarget.className = "drag-area drag-dropped"; - var files = e.dataTransfer.files[0]; - var reader = new FileReader(); - reader.onload = function (decoded) { - var parse = new DOMParser(); - specification.decode(parse.parseFromString(decoded.target.result, 'text/xml')); - popupObject.hide(); - popupObject.popupContent.innerHTML = null; - convert.convert(document.getElementById('content')); + } + if ($s.selectedTestPrototype.show && $s.selectedTestPrototype.show.length >= 1) { + $s.selectedTestPrototype.show.forEach(function (entry) { + var dom = $e[0].querySelector("[name=\"" + entry.name + "\"] input"); + if (entry.support == "none") { + dom.checked = false; + dom.disabled = true; } - reader.readAsText(files); }); - - - this.continue = function () { - popupObject.postNode(popupStateNodes.state[1]); + } + if ($s.interface !== specification.interfaces) { + // Page specific interface nodes + if ($s.selectedTestPrototype.hasScales !== undefined && ($s.selectedTestPrototype.hasScales == "false" || $s.selectedTestPrototype.hasScales == false)) { + var elem = $e[0].querySelector("[name=\"scale-selection\"]") + elem.style.visibility = "hidden"; + elem.style.height = "0px"; + } + if ($s.selectedTestPrototype.scales && $s.selectedTestPrototype.show.length >= 1) { + $s.scales = []; + $s.selectedTestPrototype.scales.forEach(function (scalename) { + var obj = $s.testSpecifications.scales.find(function (a) { + return a.name == scalename; + }); + $s.scales.push(obj); + }); + if ($s.selectedTestPrototype.scales.includes($s.selectedScale) == false) { + $s.clearScales(); + } + if ($s.scales.length == 1) { + $s.clearScales(); + $s.useScales($s.scales[0]); + } + } else { + $s.scales = $s.testSpecifications.scales; } } - this.state[1] = new function () { - this.title = "Select your interface"; - this.content = document.createElement("div"); - this.content.id = "state-1"; - var spnH = document.createElement('div'); - var span = document.createElement("span"); - span.textContent = "Please select your interface from the list shown below. This will define the various options which are available. This can later be changed."; - spnH.appendChild(span); - this.content.appendChild(spnH); - this.select = document.createElement("select"); - this.content.appendChild(this.select); - this.description = document.createElement("p"); - this.content.appendChild(this.description); - this.testsXML = interfaceSpecs.getElementsByTagName('tests')[0].getElementsByTagName('test'); - for (var i = 0; i < this.testsXML.length; i++) { - var option = document.createElement('option'); - option.value = this.testsXML[i].getAttribute('name'); - option.textContent = this.testsXML[i].getAttribute('name'); - this.select.appendChild(option); - } - this.handleEvent = function (event) { - var testXML = interfaceSpecs.getElementsByTagName("tests")[0].getAllElementsByName(this.select.value)[0]; - var descriptors = testXML.getAllElementsByTagName("description"); - this.description.textContent = ""; - for (var i = 0; i < descriptors.length; i++) { - if (descriptors[i].getAttribute("lang") == page_lang) { - this.description.textContent = descriptors[i].textContent; - } - } - } - this.select.addEventListener("change", this); - this.handleEvent(); - this.continue = function () { - var testXML = interfaceSpecs.getElementsByTagName("tests")[0].getAllElementsByName(this.select.value)[0]; - specification.interface = testXML.getAttribute("interface"); - if (specification.interfaces == null) { - specification.interfaces = new specification.interfaceNode(specification); - } - if (specification.metrics == null) { - specification.metrics = new specification.metricNode(); - } - popupStateNodes.state[2].generate(); - popupObject.postNode(popupStateNodes.state[2]); - } - this.back = function () { - popupObject.postNode(popupStateNodes.state[0]); - } + }; + + $s.$watch("selectedTestPrototype", $s.configure); + $s.configure(); +}]); +AngularInterface.controller("page", ['$scope', '$element', '$window', function ($s, $e, $w) { + $s.addInterface = function () { + $s.page.addInterface(); + }; + $s.removeInterface = function (node) { + var index = $s.page.interfaces.findIndex(function (a) { + return a == node; + }); + if (index === -1) { + throw ("Invalid node"); } - this.state[2] = new function () { - this.title = "Test Checks & Restrictions"; - this.content = document.createElement("div"); - this.content.id = "state-1"; - var spnH = document.createElement('div'); - var span = document.createElement("span"); - span.textContent = "Select your test checks and restrictions. Greyed out items are fixed by the test/interface and cannot be changed"; - spnH.appendChild(span); - this.content.appendChild(spnH); - var holder = document.createElement("div"); - this.options = []; - this.testXML = null; - this.interfaceXML = null; - this.dynamicContent = document.createElement("div"); - this.content.appendChild(this.dynamicContent); - this.generate = function () { - this.options = []; - this.dynamicContent.innerHTML = null; - var interfaceName = popupStateNodes.state[1].select.value; - this.checkText = interfaceSpecs.getElementsByTagName("global")[0].getAllElementsByTagName("checks")[0]; - this.testXML = interfaceSpecs.getElementsByTagName("tests")[0].getAllElementsByName(interfaceName)[0]; - this.interfaceXML = interfaceSpecs.getAllElementsByTagName("interfaces")[0].getAllElementsByName(this.testXML.getAttribute("interface"))[0].getAllElementsByTagName("checks")[0]; - this.testXML = this.testXML.getAllElementsByTagName("checks"); - var interfaceXMLChildren = this.interfaceXML.getElementsByTagName('entry'); - for (var i = 0; i < interfaceXMLChildren.length; i++) { - var interfaceNode = interfaceXMLChildren[i]; - var checkName = interfaceNode.getAttribute('name'); - var testNode - if (this.testXML.length > 0) { - testNode = this.testXML[0].getAllElementsByName(checkName); - if (testNode.length != 0) { - testNode = testNode[0]; - } else { - testNode = undefined; - } - } else { - testNode = undefined; - } - var obj = { - root: document.createElement("div"), - text: document.createElement("label"), - input: document.createElement("input"), - parent: this, - name: checkName, - handleEvent: function (event) { - if (this.input.checked) { - // Add to specification.interfaces.option - var included = specification.interfaces.options.find(function (element, index, array) { - if (element.name == this.name) { - return true; - } else { - return false; - } - }, this); - if (included == null) { - specification.interfaces.options.push({ - type: "check", - name: this.name - }); - } - } else { - // Remove from specification.interfaces.option - var position = specification.interfaces.options.findIndex(function (element, index, array) { - if (element.name == this.name) { - return true; - } else { - return false; - } - }, this); - if (position >= 0) { - specification.interfaces.options.splice(position, 1); - } - } - } - } + $s.page.interfaces.splice(index, 1); + }; - obj.input.addEventListener("click", obj); - obj.root.className = "popup-checkbox"; - obj.input.type = "checkbox"; - obj.input.setAttribute('id', checkName); - obj.text.setAttribute("for", checkName); - obj.text.textContent = this.checkText.getAllElementsByName(checkName)[0].textContent; - obj.root.appendChild(obj.input); - obj.root.appendChild(obj.text); - if (testNode != undefined) { - if (testNode.getAttribute('default') == 'on') { - obj.input.checked = true; - } - if (testNode.getAttribute('support') == "none") { - obj.input.disabled = true; - obj.input.checked = false; - obj.root.className = "popup-checkbox disabled"; - } else if (interfaceNode.getAttribute('support') == "mandatory") { - obj.input.disabled = true; - obj.input.checked = true; - obj.root.className = "popup-checkbox disabled"; - } - } else { - if (interfaceNode.getAttribute('default') == 'on') { - obj.input.checked = true; - } - if (interfaceNode.getAttribute('support') == "none") { - obj.input.disabled = true; - obj.input.checked = false; - obj.root.className = "popup-checkbox disabled"; - } else if (interfaceNode.getAttribute('support') == "mandatory") { - obj.input.disabled = true; - obj.input.checked = true; - obj.root.className = "popup-checkbox disabled"; - } - } - var included = specification.interfaces.options.find(function (element, index, array) { - if (element.name == this.name) { - return true; - } else { - return false; - } - }, obj); - if (included != undefined) { - obj.input.checked = true; - } - obj.handleEvent(); - this.options.push(obj); - this.dynamicContent.appendChild(obj.root); - } - } - this.continue = function () { - popupStateNodes.state[3].generate(); - popupObject.postNode(popupStateNodes.state[3]); - } - this.back = function () { - popupObject.postNode(popupStateNodes.state[1]); - } + $s.addCommentQuestion = function () { + $s.page.addCommentQuestion(); + }; + $s.removeCommentQuestion = function (node) { + var index = $s.page.commentQuestions.findIndex(function (a) { + return a == node; + }); + if (index === -1) { + throw ("Invalid node"); } - this.state[3] = new function () { - this.title = "Test Metrics"; - this.content = document.createElement("div"); - this.content.id = "state-1"; - var spnH = document.createElement('div'); - var span = document.createElement("span"); - span.textContent = "Select which data points to include in the exported results XML. Some of this is required for certain post script analysis. See the documentation for further details"; - spnH.appendChild(span); - this.content.appendChild(spnH); - this.options = []; - this.checkText; - this.testXML; - this.interfaceXML; - this.dynamicContent = document.createElement("div"); - this.content.appendChild(this.dynamicContent); - this.generate = function () { - this.options = []; - this.dynamicContent.innerHTML = null; - var interfaceName = popupStateNodes.state[1].select.value; - this.checkText = interfaceSpecs.getElementsByTagName("global")[0].getAllElementsByTagName("metrics")[0]; - this.testXML = interfaceSpecs.getElementsByTagName("tests")[0].getAllElementsByName(interfaceName)[0]; - this.interfaceXML = interfaceSpecs.getAllElementsByTagName("interfaces")[0].getAllElementsByName(this.testXML.getAttribute("interface"))[0].getAllElementsByTagName("metrics")[0]; - this.testXML = this.testXML.getAllElementsByTagName("metrics"); - var interfaceXMLChildren = this.interfaceXML.getElementsByTagName('entry'); - for (var i = 0; i < interfaceXMLChildren.length; i++) { - var interfaceNode = interfaceXMLChildren[i]; - var checkName = interfaceNode.getAttribute('name'); - var testNode - if (this.testXML.length > 0) { - testNode = this.testXML[0].getAllElementsByName(checkName); - if (testNode.length != 0) { - testNode = testNode[0]; - } else { - testNode = undefined; - } - } else { - testNode = undefined; - } - var obj = { - root: document.createElement("div"), - text: document.createElement("label"), - input: document.createElement("input"), - parent: this, - name: checkName, - handleEvent: function (event) { - if (this.input.checked) { - // Add to specification.interfaces.option - var included = specification.metrics.enabled.find(function (element, index, array) { - if (element == this.name) { - return true; - } else { - return false; - } - }, this); - if (included == null) { - specification.metrics.enabled.push(this.name); - } - } else { - // Remove from specification.interfaces.option - var position = specification.metrics.enabled.findIndex(function (element, index, array) { - if (element == this.name) { - return true; - } else { - return false; - } - }, this); - if (position >= 0) { - specification.metrics.enabled.splice(position, 1); - } - } - } - } - - obj.input.addEventListener("click", obj); - obj.root.className = "popup-checkbox"; - obj.input.type = "checkbox"; - obj.input.setAttribute('id', checkName); - obj.text.setAttribute("for", checkName); - obj.text.textContent = this.checkText.getAllElementsByName(checkName)[0].textContent; - obj.root.appendChild(obj.input); - obj.root.appendChild(obj.text); - if (testNode != undefined) { - if (testNode.getAttribute('default') == 'on') { - obj.input.checked = true; - } - if (testNode.getAttribute('support') == "none") { - obj.input.disabled = true; - obj.input.checked = false; - obj.root.className = "popup-checkbox disabled"; - } else if (interfaceNode.getAttribute('support') == "mandatory") { - obj.input.disabled = true; - obj.input.checked = true; - obj.root.className = "popup-checkbox disabled"; - } - } else { - if (interfaceNode.getAttribute('default') == 'on') { - obj.input.checked = true; - } - if (interfaceNode.getAttribute('support') == "none") { - obj.input.disabled = true; - obj.input.checked = false; - obj.root.className = "popup-checkbox disabled"; - } else if (interfaceNode.getAttribute('support') == "mandatory") { - obj.input.disabled = true; - obj.input.checked = true; - obj.root.className = "popup-checkbox disabled"; - } - } - var included = specification.metrics.enabled.find(function (element, index, array) { - if (element == this.name) { - return true; - } else { - return false; - } - }, obj); - obj.handleEvent(); - if (included != undefined) { - obj.input.checked = true; - } - this.options.push(obj); - this.dynamicContent.appendChild(obj.root); - } - } - this.continue = function () { - popupStateNodes.state[4].generate(); - popupObject.postNode(popupStateNodes.state[4]); - } - this.back = function () { - popupObject.postNode(popupStateNodes.state[2]); - } + $s.page.commentQuestions.splice(index, 1); + }; + $s.addAudioElement = function () { + $s.page.addAudioElement(); + }; + $s.removeAudioElement = function (element) { + var index = $s.page.audioElements.findIndex(function (a) { + return a == element; + }); + if (index === -1) { + throw ("Invalid node"); } - this.state[4] = new function () { - this.title = "Test Visuals"; - this.content = document.createElement("div"); - this.content.id = "state-1"; - var spnH = document.createElement('div'); - var span = document.createElement("span"); - span.textContent = "You can display extra visual content with your interface for the test user to interact with. Select from the available options below. Greyed out options are unavailable for your selected interface"; - spnH.appendChild(span); - this.content.appendChild(spnH); - this.options = []; - this.checkText; - this.testXML; - this.interfaceXML; - this.dynamicContent = document.createElement("div"); - this.content.appendChild(this.dynamicContent); - this.generate = function () { - this.options = []; - this.dynamicContent.innerHTML = null; - var interfaceName = popupStateNodes.state[1].select.value; - this.checkText = interfaceSpecs.getElementsByTagName("global")[0].getAllElementsByTagName("show")[0]; - this.testXML = interfaceSpecs.getElementsByTagName("tests")[0].getAllElementsByName(interfaceName)[0]; - this.interfaceXML = interfaceSpecs.getAllElementsByTagName("interfaces")[0].getAllElementsByName(this.testXML.getAttribute("interface"))[0].getAllElementsByTagName("show")[0]; - this.testXML = this.testXML.getAllElementsByTagName("show"); - var interfaceXMLChildren = this.interfaceXML.getElementsByTagName('entry'); - for (var i = 0; i < interfaceXMLChildren.length; i++) { - var interfaceNode = interfaceXMLChildren[i]; - var checkName = interfaceNode.getAttribute('name'); - var testNode - if (this.testXML.length > 0) { - testNode = this.testXML[0].getAllElementsByName(checkName); - if (testNode.length != 0) { - testNode = testNode[0]; - } else { - testNode = undefined; - } - } else { - testNode = undefined; - } - var obj = { - root: document.createElement("div"), - text: document.createElement("label"), - input: document.createElement("input"), - parent: this, - name: checkName, - handleEvent: function (event) { - if (this.input.checked) { - // Add to specification.interfaces.option - var included = specification.interfaces.options.find(function (element, index, array) { - if (element.name == this.name) { - return true; - } else { - return false; - } - }, this); - if (included == null) { - specification.interfaces.options.push({ - type: "show", - name: this.name - }); - } - } else { - // Remove from specification.interfaces.option - var position = specification.interfaces.options.findIndex(function (element, index, array) { - if (element.name == this.name) { - return true; - } else { - return false; - } - }, this); - if (position >= 0) { - specification.interfaces.options.splice(position, 1); - } - } - } - } - - obj.input.addEventListener("click", obj); - obj.root.className = "popup-checkbox"; - obj.input.type = "checkbox"; - obj.input.setAttribute('id', checkName); - obj.text.setAttribute("for", checkName); - obj.text.textContent = this.checkText.getAllElementsByName(checkName)[0].textContent; - obj.root.appendChild(obj.input); - obj.root.appendChild(obj.text); - if (testNode != undefined) { - if (testNode.getAttribute('default') == 'on') { - obj.input.checked = true; - } - if (testNode.getAttribute('support') == "none") { - obj.input.disabled = true; - obj.input.checked = false; - obj.root.className = "popup-checkbox disabled"; - } else if (interfaceNode.getAttribute('support') == "mandatory") { - obj.input.disabled = true; - obj.input.checked = true; - obj.root.className = "popup-checkbox disabled"; - } - } else { - if (interfaceNode.getAttribute('default') == 'on') { - obj.input.checked = true; - } - if (interfaceNode.getAttribute('support') == "none") { - obj.input.disabled = true; - obj.input.checked = false; - obj.root.className = "popup-checkbox disabled"; - } else if (interfaceNode.getAttribute('support') == "mandatory") { - obj.input.disabled = true; - obj.input.checked = true; - obj.root.className = "popup-checkbox disabled"; - } - } - var included = specification.interfaces.options.find(function (element, index, array) { - if (element.name == this.name) { - return true; - } else { - return false; - } - }, obj); - if (included != undefined) { - obj.input.checked = true; - } - obj.handleEvent(); - this.options.push(obj); - this.dynamicContent.appendChild(obj.root); - } - } - this.continue = function () { - popupObject.hide(); - convert.convert(document.getElementById('content')); - } - this.back = function () { - popupObject.postNode(popupStateNodes.state[3]); - } - } - this.state[5] = new function () { - this.title = "Add/Edit Survey Element"; - this.content = document.createElement("div"); - this.content.id = "state-1"; - var spnH = document.createElement('div'); - var span = document.createElement("span"); - span.textContent = "You can configure your survey element here. Press 'Continue' to complete your changes."; - spnH.appendChild(span); - this.content.appendChild(spnH); - this.dynamic = document.createElement("div"); - this.option = null; - this.parent = null; - this.optionLists = []; - this.select = document.createElement("select"); - this.select.setAttribute("name", "type"); - this.select.addEventListener("change", this, false); - this.content.appendChild(this.select); - this.content.appendChild(this.dynamic); - this.generate = function (option, parent) { - this.option = option; - this.parent = parent; - if (this.select.childElementCount == 0) { - var optionList = specification.schema.getAllElementsByName("survey")[0].getAllElementsByName("type")[0].getAllElementsByTagName("xs:enumeration"); - for (var i = 0; i < optionList.length; i++) { - var selectOption = document.createElement("option"); - selectOption.value = optionList[i].getAttribute("value"); - selectOption.textContent = selectOption.value; - this.select.appendChild(selectOption); - } - } - if (this.option.type != undefined) { - this.select.value = this.option.type - } else { - this.select.value = "statement"; - this.option.type = "statement"; - } - - this.dynamic.innerHTML = null; - var statement = document.createElement("div"); - var statementText = document.createElement("span"); - var statementEntry = document.createElement("input"); - statement.appendChild(statementText); - statement.appendChild(statementEntry); - statement.className = "survey-entry-attribute"; - statementText.textContent = "Statement/Question"; - statementEntry.style.width = "500px"; - statementEntry.addEventListener("change", this, false); - statementEntry.setAttribute("name", "statement"); - statementEntry.value = this.option.statement; - this.dynamic.appendChild(statement); - - var id = document.createElement("div"); - var idText = document.createElement("span"); - var idEntry = document.createElement("input"); - id.appendChild(idText); - id.appendChild(idEntry); - id.className = "survey-entry-attribute"; - idText.textContent = "ID: "; - idEntry.addEventListener("change", this, false); - idEntry.setAttribute("name", "id"); - idEntry.value = this.option.id; - - this.dynamic.appendChild(id); - - switch (this.option.type) { - case "statement": - break; - case "question": - var boxsizeSelect = document.createElement("select"); - var optionList = specification.schema.getAllElementsByName("survey")[0].getAllElementsByName("boxsize")[0].getAllElementsByTagName("xs:enumeration"); - for (var i = 0; i < optionList.length; i++) { - var selectOption = document.createElement("option"); - selectOption.value = optionList[i].getAttribute("value"); - selectOption.textContent = selectOption.value; - boxsizeSelect.appendChild(selectOption); - } - if (this.option.boxsize != undefined) { - boxsizeSelect.value = this.option.boxsize; - } else { - boxsizeSelect.value = "normal"; - this.option.boxsize = "normal"; - } - boxsizeSelect.setAttribute("name", "boxsize"); - boxsizeSelect.addEventListener("change", this, false); - var boxsize = document.createElement("div"); - var boxsizeText = document.createElement("span"); - boxsizeText.textContent = "Entry Size: "; - boxsize.appendChild(boxsizeText); - boxsize.appendChild(boxsizeSelect); - boxsize.className = "survey-entry-attribute"; - this.dynamic.appendChild(boxsize); - - 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); - break; - case "number": - 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 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); - break; - case "checkbox": - case "radio": - this.dynamic.appendChild(id); - var optionHolder = document.createElement("div"); - optionHolder.className = 'node'; - optionHolder.id = 'popup-option-holder'; - var optionObject = function (parent, option) { - this.rootDOM = document.createElement("div"); - this.rootDOM.className = "popup-option-entry"; - this.inputName = document.createElement("input"); - this.inputName.setAttribute("name", "name"); - this.inputLabel = document.createElement("input"); - this.inputLabel.setAttribute("name", "text"); - this.specification = option; - this.parent = parent; - this.handleEvent = function () { - var target = event.currentTarget.getAttribute("name"); - eval("this.specification." + target + " = event.currentTarget.value"); - }; - - var nameText = document.createElement("span"); - nameText.textContent = "Name: "; - var labelText = document.createElement("span"); - labelText.textContent = "Label: "; - this.rootDOM.appendChild(nameText); - this.rootDOM.appendChild(this.inputName); - this.rootDOM.appendChild(labelText); - this.rootDOM.appendChild(this.inputLabel); - this.inputName.addEventListener("change", this, false); - this.inputLabel.addEventListener("change", this, false); - this.inputName.value = this.specification.name; - this.inputLabel.value = this.specification.text; - this.inputLabel.style.width = "350px"; - - this.deleteEntry = { - root: document.createElement("button"), - parent: this, - handleEvent: function () { - document.getElementById("popup-option-holder").removeChild(this.parent.rootDOM); - var index = this.parent.parent.option.options.findIndex(function (element, index, array) { - if (element == this.parent.specification) - return true; - else - return false; - }, this); - var optionList = this.parent.parent.option.options; - if (index == optionList.length - 1) { - optionList = optionList.slice(0, index); - } else { - optionList = optionList.slice(0, index).concat(optionList.slice(index + 1)); - } - this.parent.parent.option.options = optionList; - } - }; - this.deleteEntry.root.textContent = "Delete Option"; - this.deleteEntry.root.addEventListener("click", this.deleteEntry, false); - this.rootDOM.appendChild(this.deleteEntry.root); - } - this.addEntry = { - parent: this, - root: document.createElement("button"), - handleEvent: function () { - var node = { - name: "name", - text: "text" - }; - var optionsList = this.parent.option.options; - optionsList.push(node); - var obj = new optionObject(this.parent, optionsList[optionsList.length - 1]); - this.parent.optionLists.push(obj); - document.getElementById("popup-option-holder").appendChild(obj.rootDOM); - } - } - this.addEntry.root.textContent = "Add Option"; - this.addEntry.root.addEventListener("click", this.addEntry); - this.dynamic.appendChild(this.addEntry.root); - for (var i = 0; i < this.option.options.length; i++) { - var obj = new optionObject(this, this.option.options[i]); - this.optionLists.push(obj); - optionHolder.appendChild(obj.rootDOM); - } - this.dynamic.appendChild(optionHolder); - } - } - this.handleEvent = function (event) { - var name = event.currentTarget.getAttribute("name"); - var nodeName = event.currentTarget.nodeName; - if (name == "type" && nodeName == "SELECT") { - // If type has changed, we may need to rebuild the entire state node - if (event.currentTarget.value != this.option.name) { - this.option.type = event.currentTarget.value; - this.generate(this.option, this.parent); - } - return; - } - switch (event.currentTarget.getAttribute("type")) { - case "checkbox": - eval("this.option." + name + " = event.currentTarget.checked"); - break; - default: - eval("this.option." + name + " = event.currentTarget.value"); - break; - } - } - this.continue = function () { - if (this.parent.type == "surveyNode") { - var newNode = new this.parent.surveyEntryNode(this.parent, this.option); - this.parent.children.push(newNode); - this.parent.childrenDOM.appendChild(newNode.rootDOM); - } else if (this.parent.type == "surveyEntryNode") { - this.parent.build(); - } - popupObject.hide(); - } - } - this.state[6] = new function () { - this.title = "Edit Scale Markers"; - this.content = document.createElement("div"); - this.content.id = "state-6"; - var spnH = document.createElement('div'); - var span = document.createElement("span"); - span.textContent = "You can edit your scale markers here for the selected interface."; - spnH.appendChild(span); - this.scaleRoot; - this.parent; - this.markerNodes = []; - this.preset = { - input: document.createElement("select"), - parent: this, - handleEvent: function (event) { - this.parent.scaleRoot.scales = []; - var protoScale = interfaceSpecs.getAllElementsByTagName('scaledefinitions')[0].getAllElementsByName(event.currentTarget.value)[0]; - var protoMarkers = protoScale.getElementsByTagName("scalelabel"); - for (var i = 0; i < protoMarkers.length; i++) { - var marker = { - position: protoMarkers[i].getAttribute("position"), - text: protoMarkers[i].textContent - } - this.parent.scaleRoot.scales.push(marker); - } - this.parent.buildMarkerList(); - } - } - this.preset.input.addEventListener("change", this.preset); - this.content.appendChild(this.preset.input); - var optionHolder = document.createElement("div"); - optionHolder.className = 'node'; - optionHolder.id = 'popup-option-holder'; - this.content.appendChild(optionHolder); - this.addMarker = { - root: document.createElement("button"), - parent: this, - handleEvent: function () { - var marker = { - position: 0, - text: "text" - }; - this.parent.scaleRoot.scales.push(marker); - var markerNode = new this.parent.buildMarkerNode(this.parent, marker); - document.getElementById("popup-option-holder").appendChild(markerNode.root); - this.parent.markerNodes.push(markerNode); - } - }; - this.addMarker.root.textContent = "Add Marker"; - this.addMarker.root.addEventListener("click", this.addMarker); - this.generate = function (scaleRoot, parent) { - this.scaleRoot = scaleRoot; - this.parent = parent; - - // Generate Pre-Set dropdown - var protoScales = interfaceSpecs.getAllElementsByTagName('scaledefinitions')[0].getElementsByTagName("scale"); - this.preset.input.innerHTML = ""; - - for (var i = 0; i < protoScales.length; i++) { - var selectOption = document.createElement("option"); - var scaleName = protoScales[i].getAttribute("name"); - selectOption.setAttribute("name", scaleName); - selectOption.textContent = scaleName; - this.preset.input.appendChild(selectOption); - } - this.content.appendChild(this.addMarker.root); - - // Create Marker List - this.buildMarkerList(); - } - this.buildMarkerList = function () { - var markerInject = document.getElementById("popup-option-holder"); - markerInject.innerHTML = ""; - this.markerNodes = []; - for (var i = 0; i < this.scaleRoot.scales.length; i++) { - var markerNode = new this.buildMarkerNode(this, this.scaleRoot.scales[i]); - markerInject.appendChild(markerNode.root); - this.markerNodes.push(markerNode); - - } - } - - this.buildMarkerNode = function (parent, specification) { - this.root = document.createElement("div"); - this.root.className = "popup-option-entry"; - this.positionInput = document.createElement("input"); - this.positionInput.min = 0; - this.positionInput.max = 100; - this.positionInput.value = specification.position; - this.positionInput.setAttribute("name", "position"); - this.textInput = document.createElement("input"); - this.textInput.setAttribute("name", "text"); - this.textInput.style.width = "300px"; - this.textInput.value = specification.text; - this.specification = specification; - this.parent = parent; - this.handleEvent = function (event) { - switch (event.currentTarget.getAttribute("name")) { - case "position": - this.specification.position = Number(event.currentTarget.value); - break; - case "text": - this.specification.text = event.currentTarget.value; - break; - } - } - this.positionInput.addEventListener("change", this, false); - this.textInput.addEventListener("change", this, false); - - var posText = document.createElement("span"); - posText.textContent = "Position: "; - var textText = document.createElement("span"); - textText.textContent = "Text: "; - this.root.appendChild(posText); - this.root.appendChild(this.positionInput); - this.root.appendChild(textText); - this.root.appendChild(this.textInput); - - this.deleteMarker = { - root: document.createElement("button"), - parent: this, - handleEvent: function () { - var index = this.parent.parent.scaleRoot.scales.findIndex(function (element, index, array) { - if (element == this) { - return true; - } else { - return false; - } - }, this.parent.specification) - if (index >= 0) { - this.parent.parent.scaleRoot.scales.splice(index, 1); - } - document.getElementById("popup-option-holder").removeChild(this.parent.root); - } - } - this.deleteMarker.root.addEventListener("click", this.deleteMarker); - this.deleteMarker.root.textContent = "Delete Marker" - this.root.appendChild(this.deleteMarker.root); - } - } - } -} - -function SpecificationToHTML() { - // This takes the specification node and converts it to an on-page HTML object - // Each Specification Node is given its own JS object which listens to the XSD for instant verification - // Once generated, it directly binds into the specification object to update with changes - // Fixed DOM entries - this.injectDOM; - this.setupDOM; - this.pages = []; - - // Self-contained generators - this.createGeneralNodeDOM = function (name, id, parent) { - this.type = name; - var root = document.createElement('div'); - root.id = id; - root.className = "node"; - - var titleDiv = document.createElement('div'); - titleDiv.className = "node-title"; - var title = document.createElement('span'); - title.className = "node-title"; - title.textContent = name; - titleDiv.appendChild(title); - - var attributeDiv = document.createElement('div'); - attributeDiv.className = "node-attributes"; - - var childrenDiv = document.createElement('div'); - childrenDiv.className = "node-children"; - - var buttonsDiv = document.createElement('div'); - buttonsDiv.className = "node-buttons"; - - root.appendChild(titleDiv); - root.appendChild(attributeDiv); - root.appendChild(childrenDiv); - root.appendChild(buttonsDiv); - - var obj = { - rootDOM: root, - titleDOM: title, - attributeDOM: attributeDiv, - attributes: [], - childrenDOM: childrenDiv, - children: [], - buttonDOM: buttonsDiv, - parent: parent - } - return obj; - } - - this.convertAttributeToDOM = function (node, schema) { - // This takes an attribute schema node and returns an object with the input node and any bindings - if (schema.getAttribute('name') == undefined && schema.getAttribute('ref') != undefined) { - schema = specification.schema.getAllElementsByName(schema.getAttribute('ref'))[0]; - } - var obj = new function () { - this.input; - this.name; - this.owner; - this.holder; - - this.name = schema.getAttribute('name'); - this.default = schema.getAttribute('default'); - this.dataType = schema.getAttribute('type'); - if (this.dataType == undefined) { - if (schema.childElementCount > 0) { - if (schema.firstElementChild.nodeName == "xs:simpleType") { - this.dataType = schema.getAllElementsByTagName("xs:restriction")[0].getAttribute("base"); - } - } - } - if (typeof this.dataType == "string") { - this.dataType = this.dataType.substr(3); - } else { - this.dataType = "string"; - } - var minVar = undefined; - var maxVar = undefined; - switch (this.dataType) { - case "negativeInteger": - maxVar = -1; - break; - case "positiveInteger": - minVar = 1; - break; - case "nonNegativeInteger": - minVar = 0; - break; - case "nonPositiveInteger": - maxVar = 0; - break; - case "byte": - minVar = 0; - maxVar = 256; - break; - case "short": - minVar = 0; - maxVar = 65536; - break; - default: - break; - } - - this.enumeration = schema.getAllElementsByTagName("xs:enumeration"); - if (this.enumeration.length == 0) { - this.input = document.createElement('input'); - switch (this.dataType) { - case "boolean": - this.input.type = "checkbox"; - break; - case "negativeInteger": - case "positiveInteger": - case "nonNegativeInteger": - case "nonPositiveInteger": - case "integer": - case "short": - case "byte": - this.input.step = 1; - case "decimal": - this.input.type = "number"; - this.input.min = minVar; - this.input.max = maxVar; - break; - default: - break; - } - } else { - this.input = document.createElement("select"); - for (var i = 0; i < this.enumeration.length; i++) { - var option = document.createElement("option"); - var value = this.enumeration[i].getAttribute("value"); - option.setAttribute("value", value); - option.textContent = value; - this.input.appendChild(option); - } - } - var value; - eval("value = node." + this.name) - if (this.default != undefined && value == undefined) { - value = this.default; - } - if (this.input.type == "checkbox") { - if (value == "true" || value == "True") { - this.input.checked = false; - } else { - this.input.checked = false; - } - } else { - this.input.value = value; - } - this.handleEvent = function (event) { - var value; - if (this.input.nodeName == "INPUT") { - switch (this.input.type) { - case "checkbox": - value = event.currentTarget.checked; - break; - case "number": - if (event.currentTarget.value != "") { - value = Number(event.currentTarget.value); - } else { - value = undefined; - } - break; - default: - if (event.currentTarget.value != "") { - value = event.currentTarget.value; - } else { - value = undefined; - } - break; - } - } else if (this.input.nodeName == "SELECT") { - value = event.currentTarget.value; - } - eval("this.owner." + this.name + " = value"); - } - this.holder = document.createElement('div'); - this.holder.className = "attribute"; - this.holder.setAttribute('name', this.name); - var text = document.createElement('span'); - eval("text.textContent = attributeText." + this.name + "+': '"); - this.holder.appendChild(text); - this.holder.appendChild(this.input); - this.owner = node; - this.input.addEventListener("change", this, false); - } - if (obj.attribute != null) { - obj.input.value = obj.attribute; - } - return obj; - } - - this.convert = function (root) { - //Performs the actual conversion using the given root DOM as the root - this.injectDOM = root; - - // Build the export button - var exportButton = document.createElement("button"); - exportButton.textContent = "Export to XML"; - exportButton.onclick = function () { - var doc = specification.encode(); - var obj = {}; - obj.title = "Export"; - obj.content = document.createElement("div"); - obj.content.id = "finish"; - var span = document.createElement("span"); - span.textContent = "Your XML document is linked below. On most browsers, simply right click on the link and select 'Save As'. Or clicking on the link may download the file directly." - obj.content.appendChild(span); - span = document.createElement("p"); - span.textContent = "NOTE FOR SAFARI! You cannot right click on the below link and save it as a file, Safari does not like that at all. Instead click on it to open the XML, the Press Cmd+S to open the save dialogue. Make sure you have 'save as Page Source' selected on the bottom of the window. Currently Safari has no plans to support the HTML 'download' attribute which causes this problem"; - obj.content.appendChild(span); - var link = document.createElement("div"); - link.appendChild(doc.firstChild); - var file = [link.innerHTML]; - var bb = new Blob(file, { - type: 'application/xml' - }); - var dnlk = window.URL.createObjectURL(bb); - var a = document.createElement("a"); - a.hidden = ''; - a.href = dnlk; - a.download = "project-specification.xml"; - a.textContent = "Save File"; - obj.content.appendChild(a); - popupObject.show(); - popupObject.postNode(obj); - } - this.injectDOM.appendChild(exportButton); - - // First perform the setupNode; - var setupSchema = specification.schema.getAllElementsByName('setup')[0]; - this.setupDOM = new this.createGeneralNodeDOM('Global Configuration', 'setup', null); - this.injectDOM.appendChild(this.setupDOM.rootDOM); - var setupAttributes = setupSchema.getAllElementsByTagName('xs:attribute'); - for (var i = 0; i < setupAttributes.length; i++) { - var attributeName = setupAttributes[i].getAttribute('name'); - var attrObject = this.convertAttributeToDOM(specification, setupAttributes[i]); - this.setupDOM.attributeDOM.appendChild(attrObject.holder); - this.setupDOM.attributes.push(attrObject); - } - - // Build the exit Text node - var exitText = new this.createGeneralNodeDOM("Exit Text", "exit-test", this.setupDOM); - exitText.rootDOM.removeChild(exitText.attributeDOM); - this.setupDOM.children.push(exitText); - this.setupDOM.childrenDOM.appendChild(exitText.rootDOM); - var obj = { - rootDOM: document.createElement("div"), - labelDOM: document.createElement("label"), - inputDOM: document.createElement("textarea"), - parent: exitText, - specification: specification, - handleEvent: function (event) { - this.specification.exitText = this.inputDOM.value; - } - } - var exitWarning = document.createElement("div"); - obj.rootDOM.appendChild(exitWarning); - exitWarning.textContent = "Only visible when the above 'On complete redirect URL' field is empty."; - obj.rootDOM.appendChild(obj.labelDOM); - obj.rootDOM.appendChild(obj.inputDOM); - obj.labelDOM.textContent = "Text: "; - obj.inputDOM.value = obj.specification.exitText; - obj.inputDOM.addEventListener("change", obj); - exitText.children.push(obj); - exitText.childrenDOM.appendChild(obj.rootDOM); - - // Now we must build the interface Node - this.interfaceDOM = new this.interfaceNode(this, specification.interfaces); - this.interfaceDOM.build("Interface", "setup-interface", this.setupDOM.rootDOM); - - // Now build the Metrics selection node - var metric = this.createGeneralNodeDOM("Session Metrics", "setup-metric", this.setupDOM); - metric.rootDOM.removeChild(metric.attributeDOM); - this.setupDOM.children.push(metric); - this.setupDOM.childrenDOM.appendChild(metric.rootDOM); - var interfaceName = popupStateNodes.state[1].select.value; - var checkText = interfaceSpecs.getElementsByTagName("global")[0].getAllElementsByTagName("metrics")[0]; - var testXML = interfaceSpecs.getElementsByTagName("tests")[0].getAllElementsByName(interfaceName)[0]; - var interfaceXML = interfaceSpecs.getAllElementsByTagName("interfaces")[0].getAllElementsByName(testXML.getAttribute("interface"))[0].getAllElementsByTagName("metrics")[0]; - testXML = testXML.getAllElementsByTagName("metrics"); - var interfaceXMLChild = interfaceXML.firstElementChild; - while (interfaceXMLChild) { - var obj = { - input: document.createElement('input'), - root: document.createElement('div'), - text: document.createElement('span'), - specification: specification.metrics.enabled, - name: interfaceXMLChild.getAttribute("name"), - handleEvent: function () { - for (var i = 0; i < this.specification.length; i++) { - if (this.specification[i] == this.name) { - var options = this.specification; - if (this.input.checked == false) { - if (i == options.length) { - options = options.slice(0, i); - } else { - options = options.slice(0, i).concat(options.slice(i + 1)); - } - } else { - return; - } - this.specification = options; - break; - } - } - if (this.input.checked) { - this.specification.push(this.name); - } - } - }; - obj.root.className = "attribute"; - obj.input.type = "checkbox"; - obj.root.appendChild(obj.text); - obj.root.appendChild(obj.input); - obj.text.textContent = checkText.getAllElementsByName(interfaceXMLChild.getAttribute("name"))[0].textContent; - metric.children.push(obj); - metric.childrenDOM.appendChild(obj.root); - for (var j = 0; j < specification.metrics.enabled.length; j++) { - if (specification.metrics.enabled[j] == obj.name) { - obj.input.checked = true; - break; - } - } - interfaceXMLChild = interfaceXMLChild.nextElementSibling; - } - - // Now both before and after surveys - if (specification.preTest == undefined) { - specification.preTest = new specification.surveyNode(specification); - specification.preTest.location = "pre"; - } - if (specification.postTest == undefined) { - specification.postTest = new specification.surveyNode(specification); - specification.postTest.location = "post"; - } - var surveyBefore = new this.surveyNode(this, specification.preTest, "Pre"); - var surveyAfter = new this.surveyNode(this, specification.postTest, "Post"); - this.setupDOM.children.push(surveyBefore); - this.setupDOM.children.push(surveyAfter); - this.setupDOM.childrenDOM.appendChild(surveyBefore.rootDOM); - this.setupDOM.childrenDOM.appendChild(surveyAfter.rootDOM); - - // Add in the page creator button - this.addPage = { - root: document.createElement("button"), - parent: this, - handleEvent: function () { - var pageObj = new specification.page(specification); - specification.pages.push(pageObj); - var newPage = new this.parent.pageNode(this.parent, pageObj); - document.getElementById("page-holder").appendChild(newPage.rootDOM); - this.parent.pages.push(newPage); - } - } - this.addPage.root.textContent = "Add Page"; - this.addPage.root.id = "new-page-button"; - this.addPage.root.style.float = "left"; - this.addPage.root.addEventListener("click", this.addPage, false); - - var pageHolder = document.createElement("div"); - pageHolder.id = "page-holder"; - this.injectDOM.appendChild(pageHolder); - - // Build each page - for (var page of specification.pages) { - var newPage = new this.pageNode(this, page); - pageHolder.appendChild(newPage.rootDOM); - this.pages.push(newPage); - } - - this.injectDOM.appendChild(this.addPage.root); - } - - this.interfaceNode = function (parent, rootObject) { - this.type = "interfaceNode"; - this.rootDOM; - this.titleDOM; - this.attributeDOM; - this.attributes = []; - this.childrenDOM; - this.children = []; - this.buttonDOM; - this.parent = parent; - this.HTMLPoint; - this.specification = rootObject; - this.schema = specification.schema.getAllElementsByName("interface")[1]; - - this.createIOasAttr = function (name, specification, parent, type) { - this.root = document.createElement('div'); - this.input = document.createElement("input"); - this.name = name; - this.type = type; - this.parent = parent; - this.specification = specification; - this.handleEvent = function (event) { - for (var i = 0; i < this.specification.options.length; i++) { - if (this.specification.options[i].name == this.name) { - var options = this.specification.options; - if (this.input.checked == false) { - if (i == options.length) { - options = options.slice(0, i); - } else { - options = options.slice(0, i).concat(options.slice(i + 1)); - } - } else { - return; - } - this.specification.options = options; - break; - } - } - if (this.input.checked) { - var obj = { - name: this.name, - type: this.type - }; - this.specification.options.push(obj); - } - if (this.parent.HTMLPoint.id == "setup") { - // We've changed a global setting, must update all child 'interfaces' and disable them - for (pages of convert.pages) { - for (interface of pages.interfaces) { - if (this.type == "check") { - for (node of interface.children[0].attributes) { - if (node.name == this.name) { - if (this.input.checked) { - node.input.disabled = true; - node.input.checked = false; - } else { - node.input.disabled = false; - } - break; - } - } - } else if (this.type == "show") { - for (node of interface.children[1].attributes) { - if (node.name == this.name) { - if (this.input.checked) { - node.input.disabled = true; - } else { - node.input.disabled = false; - } - break; - } - } - } - } - } - } - }; - this.findIndex = function (element, index, array) { - if (element.name == this.name) - return true; - else - return false; - }; - this.findNode = function (element, index, array) { - if (element.name == this.name) - return true; - else - return false; - }; - this.input.type = "checkbox"; - this.input.setAttribute("name", name); - this.input.addEventListener("change", this, false); - this.root.appendChild(this.input); - this.root.className = "attribute"; - return this; - } - - this.build = function (name, id, parent) { - var obj = this.parent.createGeneralNodeDOM(name, id, parent); - - this.rootDOM = obj.rootDOM; - this.titleDOM = obj.titleDOM; - this.attributeDOM = obj.attributeDOM; - this.childrenDOM = obj.childrenDOM; - this.buttonDOM = obj.buttonsDOM; - this.HTMLPoint = parent; - this.rootDOM.removeChild(this.attributeDOM); - if (parent.id != "setup") { - // Put in the node: - this.titleNode = { - root: document.createElement("div"), - label: document.createElement("span"), - input: document.createElement("input"), - parent: this, - handleEvent: function (event) { - this.parent.specification.title = event.currentTarget.value; - } - } - this.titleNode.label.textContent = "Presented Axis Title:"; - this.titleNode.root.className = "node-children"; - this.titleNode.root.appendChild(this.titleNode.label); - this.titleNode.root.appendChild(this.titleNode.input); - this.titleNode.input.addEventListener("change", this.titleNode, false); - this.titleNode.input.value = this.specification.title; - this.children.push(this.titleNode); - this.childrenDOM.appendChild(this.titleNode.root); - // Set the interface-name attribute - this.axisName = { - root: document.createElement("div"), - label: document.createElement("span"), - input: document.createElement("input"), - parent: this, - handleEvent: function (event) { - this.parent.specification.name = event.currentTarget.value; - } - } - this.axisName.label.textContent = "Saved Axis Name (no spaces):"; - this.axisName.root.className = "node-children"; - this.axisName.root.appendChild(this.axisName.label); - this.axisName.root.appendChild(this.axisName.input); - this.axisName.input.addEventListener("change", this.axisName, false); - this.axisName.input.value = this.specification.name; - this.children.push(this.axisName); - this.childrenDOM.appendChild(this.axisName.root); - } - - // Put in the check / show options as individual children - var checks = this.parent.createGeneralNodeDOM("Checks", "setup-interface-checks", this); - - var interfaceName = popupStateNodes.state[1].select.value; - var checkText = interfaceSpecs.getElementsByTagName("global")[0].getAllElementsByTagName("checks")[0]; - var testXML = interfaceSpecs.getElementsByTagName("tests")[0].getAllElementsByName(interfaceName)[0]; - var interfaceXML = interfaceSpecs.getAllElementsByTagName("interfaces")[0].getAllElementsByName(testXML.getAttribute("interface"))[0].getAllElementsByTagName("checks")[0]; - testXML = testXML.getAllElementsByTagName("checks"); - var interfaceXMLChild = interfaceXML.firstElementChild; - while (interfaceXMLChild) { - var obj = new this.createIOasAttr(interfaceXMLChild.getAttribute("name"), this.specification, this, "check"); - for (var option of this.specification.options) { - if (option.name == obj.name) { - obj.input.checked = true; - break; - } - } - if (parent.id != "setup") { - var node = convert.interfaceDOM.children[0].attributes.find(obj.findNode, obj); - if (node != undefined) { - if (node.input.checked) { - obj.input.checked = false; - obj.input.disabled = true; - } - } - } - var text = document.createElement('span'); - text.textContent = checkText.getAllElementsByName(interfaceXMLChild.getAttribute("name"))[0].textContent; - obj.root.appendChild(text); - checks.attributeDOM.appendChild(obj.root); - checks.attributes.push(obj); - interfaceXMLChild = interfaceXMLChild.nextElementSibling; - } - this.children.push(checks); - this.childrenDOM.appendChild(checks.rootDOM); - - var show = this.parent.createGeneralNodeDOM("Show", "setup-interface-show", this); - interfaceName = popupStateNodes.state[1].select.value; - checkText = interfaceSpecs.getElementsByTagName("global")[0].getAllElementsByTagName("show")[0]; - testXML = interfaceSpecs.getElementsByTagName("tests")[0].getAllElementsByName(interfaceName)[0]; - interfaceXML = interfaceSpecs.getAllElementsByTagName("interfaces")[0].getAllElementsByName(testXML.getAttribute("interface"))[0].getAllElementsByTagName("show")[0]; - testXML = testXML.getAllElementsByTagName("show"); - interfaceXMLChild = interfaceXML.firstElementChild; - while (interfaceXMLChild) { - var obj = new this.createIOasAttr(interfaceXMLChild.getAttribute("name"), this.specification, this, "show"); - for (var option of this.specification.options) { - if (option.name == obj.name) { - obj.input.checked = true; - break; - } - } - if (parent.id != "setup") { - var node = convert.interfaceDOM.children[0].attributes.find(obj.findNode, obj); - if (node != undefined) { - if (node.input.checked) { - obj.input.checked = false; - obj.input.disabled = true; - } - } - } - var text = document.createElement('span'); - text.textContent = checkText.getAllElementsByName(interfaceXMLChild.getAttribute("name"))[0].textContent; - obj.root.appendChild(text); - show.attributeDOM.appendChild(obj.root); - show.attributes.push(obj); - interfaceXMLChild = interfaceXMLChild.nextElementSibling; - } - this.children.push(show); - this.childrenDOM.appendChild(show.rootDOM); - - if (parent.id == "setup") {} else { - var nameAttr = this.parent.convertAttributeToDOM(this, specification.schema.getAllElementsByName("name")[0]); - this.attributeDOM.appendChild(nameAttr.holder); - this.attributes.push(nameAttr); - var scales = new this.scalesNode(this, this.specification); - this.children.push(scales); - this.childrenDOM.appendChild(scales.rootDOM); - } - if (parent != undefined) { - parent.appendChild(this.rootDOM); - } - } - - this.scalesNode = function (parent, rootObject) { - this.type = "scalesNode"; - 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("page")[0]; - this.rootDOM.className = "node"; - - var titleDiv = document.createElement('div'); - titleDiv.className = "node-title"; - this.titleDOM.className = "node-title"; - this.titleDOM.textContent = "Interface Scales"; - 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); - - this.editButton = { - button: document.createElement("button"), - parent: this, - handleEvent: function (event) { - popupObject.show(); - popupObject.postNode(popupStateNodes.state[6]); - popupStateNodes.state[6].generate(this.parent.specification, this.parent); - } - }; - this.editButton.button.textContent = "Edit Scales/Markers"; - this.editButton.button.addEventListener("click", this.editButton, false); - this.buttonDOM.appendChild(this.editButton.button); - } - } - - this.surveyNode = function (parent, rootObject, location) { - this.type = "surveyNode"; - 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("survey")[1]; - this.rootDOM.className = "node"; - - var titleDiv = document.createElement('div'); - titleDiv.className = "node-title"; - this.titleDOM.className = "node-title"; - this.titleDOM.textContent = "Survey"; - titleDiv.appendChild(this.titleDOM); - - this.attributeDOM.className = "node-attributes"; - var locationAttr = document.createElement("span"); - this.attributeDOM.appendChild(locationAttr); - if (location == "Pre" || location == "pre") { - locationAttr.textContent = "Location: Before"; - } else { - locationAttr.textContent = "Location: After"; - } - 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); - - this.surveyEntryNode = function (parent, rootObject) { - this.type = "surveyEntryNode"; - 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("surveyentry")[1]; - - 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); - - this.build = function () { - this.attributeDOM.innerHTML = null; - this.childrenDOM.innerHTML = null; - var statementRoot = document.createElement("div"); - var statement = document.createElement("span"); - statement.textContent = "Statement / Question: " + this.specification.statement; - statementRoot.appendChild(statement); - this.children.push(statementRoot); - this.childrenDOM.appendChild(statementRoot); - switch (this.specification.type) { - case "statement": - this.titleDOM.textContent = "Statement"; - break; - case "question": - this.titleDOM.textContent = "Question"; - var id = convert.convertAttributeToDOM(this.specification, specification.schema.getAllElementsByName("id")[0]); - var mandatory = convert.convertAttributeToDOM(this.specification, specification.schema.getAllElementsByName("mandatory")[0]); - var boxsize = convert.convertAttributeToDOM(this.specification, specification.schema.getAllElementsByName("boxsize")[0]); - this.attributeDOM.appendChild(id.holder); - this.attributes.push(id); - this.attributeDOM.appendChild(mandatory.holder); - this.attributes.push(mandatory); - this.attributeDOM.appendChild(boxsize.holder); - this.attributes.push(boxsize); - break; - case "number": - this.titleDOM.textContent = "Number"; - var id = convert.convertAttributeToDOM(this.specification, specification.schema.getAllElementsByName("id")[0]); - var mandatory = convert.convertAttributeToDOM(this.specification, specification.schema.getAllElementsByName("mandatory")[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 "checkbox": - this.titleDOM.textContent = "Checkbox"; - var id = convert.convertAttributeToDOM(this.specification, specification.schema.getAllElementsByName("id")[0]); - this.attributeDOM.appendChild(id.holder); - this.attributes.push(id); - break; - case "radio": - this.titleDOM.textContent = "Radio"; - var id = convert.convertAttributeToDOM(this.specification, specification.schema.getAllElementsByName("id")[0]); - this.attributeDOM.appendChild(id.holder); - this.attributes.push(id); - break; - } - } - this.build(); - - this.editNode = { - root: document.createElement("button"), - parent: this, - handleEvent: function () { - popupObject.show(); - popupStateNodes.state[5].generate(this.parent.specification, this.parent); - popupObject.postNode(popupStateNodes.state[5]); - } - } - this.editNode.root.textContent = "Edit Entry"; - this.editNode.root.addEventListener("click", this.editNode, false); - this.buttonDOM.appendChild(this.editNode.root); - - this.deleteNode = { - root: document.createElement("button"), - parent: this, - handleEvent: function () { - var optionList = this.parent.parent.specification.options; - var childList = this.parent.parent.children; - for (var i = 0; i < this.parent.parent.specification.options.length; i++) { - var option = this.parent.parent.specification.options[i]; - if (option == this.parent.specification) { - this.parent.parent.childrenDOM.removeChild(this.parent.rootDOM); - if (i == this.parent.parent.specification.options.length - 1) { - optionList = optionList.slice(0, i); - childList = childList.slice(0, i); - } else { - optionList = optionList.slice(0, i).concat(optionList.slice(i + 1)); - childList = childList.slice(0, i).concat(childList.slice(i + 1)); - } - this.parent.parent.specification.options = optionList; - this.parent.parent.children = childList; - } - } - } - } - this.deleteNode.root.textContent = "Delete Entry"; - this.deleteNode.root.addEventListener("click", this.deleteNode, false); - this.buttonDOM.appendChild(this.deleteNode.root); - - this.moveToPosition = function (new_index) { - new_index = Math.min(new_index, this.parent.children.length); - var curr_index = this.parent.children.findIndex(function (elem) { - if (elem == this) { - return true; - } else { - return false; - } - }, this); - // Split at the current location to remove the node and shift all the children up - var tail = this.parent.children.splice(curr_index + 1); - this.parent.children.pop(); - this.parent.children = this.parent.children.concat(tail); - - //Split at the new location and insert the node - tail = this.parent.children.splice(new_index); - this.parent.children.push(this); - this.parent.children = this.parent.children.concat(tail); - - // Re-build the specification - this.parent.specification.options = []; - this.parent.childrenDOM.innerHTML = ""; - for (var obj of this.parent.children) { - this.parent.specification.options.push(obj.specification); - this.parent.childrenDOM.appendChild(obj.rootDOM); - } - this.parent.children.forEach(function (obj, index) { - obj.moveButtons.disable(index); - }); - } - - this.moveButtons = { - root_up: document.createElement("button"), - root_down: document.createElement("button"), - parent: this, - handleEvent: function (event) { - var index = this.parent.parent.children.indexOf(this.parent); - if (event.currentTarget.getAttribute("direction") == "up") { - index = Math.max(index - 1, 0); - } else if (event.currentTarget.getAttribute("direction") == "down") { - index = Math.min(index + 1, this.parent.parent.children.length - 1); - } - this.parent.moveToPosition(index); - this.disable(index); - }, - disable: function (index) { - if (index == 0) { - this.root_up.disabled = true; - } else { - this.root_up.disabled = false; - } - if (index == this.parent.parent.children.length - 1) { - this.root_down.disabled = true; - } else { - this.root_down.disabled = false; - } - } - } - this.moveButtons.root_up.setAttribute("direction", "up"); - this.moveButtons.root_down.setAttribute("direction", "down"); - this.moveButtons.root_up.addEventListener("click", this.moveButtons, false); - this.moveButtons.root_down.addEventListener("click", this.moveButtons, false); - this.moveButtons.root_up.textContent = "Move Up"; - this.moveButtons.root_down.textContent = "Move Down"; - this.buttonDOM.appendChild(this.moveButtons.root_up); - this.buttonDOM.appendChild(this.moveButtons.root_down); - } - this.addNode = { - root: document.createElement("button"), - parent: this, - handleEvent: function () { - var newNode = new this.parent.specification.OptionNode(this.parent.specification); - this.parent.specification.options.push(newNode); - popupObject.show(); - popupStateNodes.state[5].generate(newNode, this.parent); - popupObject.postNode(popupStateNodes.state[5]); - } - } - this.addNode.root.textContent = "Add Survey Entry"; - this.addNode.root.addEventListener("click", this.addNode, false); - this.buttonDOM.appendChild(this.addNode.root); - - for (var option of this.specification.options) { - var newNode = new this.surveyEntryNode(this, option); - this.children.push(newNode); - this.childrenDOM.appendChild(newNode.rootDOM); - } - - this.children.forEach(function (obj, index) { - obj.moveButtons.disable(index); - }); - } - - this.pageNode = function (parent, rootObject) { - this.type = "pageNode"; - 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("page")[0]; - this.rootDOM.className = "node"; - - var titleDiv = document.createElement('div'); - titleDiv.className = "node-title"; - this.titleDOM.className = "node-title"; - this.titleDOM.textContent = "Test Page"; - 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); - - // Do the comment prefix node - var cpn = this.parent.createGeneralNodeDOM("Comment Prefix", "" + this.specification.id + "-commentprefix", this.parent); - cpn.rootDOM.removeChild(cpn.attributeDOM); - var obj = { - root: document.createElement("div"), - input: document.createElement("input"), - parent: this, - handleEvent: function () { - this.parent.specification.commentBoxPrefix = event.currentTarget.value; - } - } - cpn.children.push(obj); - cpn.childrenDOM.appendChild(obj.root); - obj.root.appendChild(obj.input); - obj.input.addEventListener("change", obj, false); - obj.input.value = this.specification.commentBoxPrefix; - this.childrenDOM.appendChild(cpn.rootDOM); - this.children.push(cpn); - - // Now both before and after surveys - if (this.specification.preTest == undefined) { - this.specification.preTest = new specification.surveyNode(specification); - this.specification.preTest.location = "pre"; - } - if (this.specification.postTest == undefined) { - this.specification.postTest = new specification.surveyNode(specification); - this.specification.postTest.location = "post"; - } - var surveyBefore = new this.parent.surveyNode(this, this.specification.preTest, "Pre"); - var surveyAfter = new this.parent.surveyNode(this, this.specification.postTest, "Post"); - this.children.push(surveyBefore); - this.children.push(surveyAfter); - this.childrenDOM.appendChild(surveyBefore.rootDOM); - this.childrenDOM.appendChild(surveyAfter.rootDOM); - - // Build the attributes - var attributeList = this.schema.getAllElementsByTagName("xs:attribute"); - for (var i = 0; i < attributeList.length; i++) { - var attributeName = attributeList[i].getAttribute('name'); - var attrObject = this.parent.convertAttributeToDOM(rootObject, attributeList[i]); - this.attributeDOM.appendChild(attrObject.holder); - this.attributes.push(attrObject); - } - - this.interfaces = []; - - this.getAudioElements = function () { - var array = []; - for (var i = 0; i < this.children.length; i++) { - if (this.children[i].type == "audioElementNode") { - array[array.length] = this.children[i]; - } - } - return array; - } - - this.redrawChildren = function () { - this.childrenDOM.innerHTML = ""; - for (var child of this.children) { - this.childrenDOM.appendChild(child.rootDOM); - } - } - - this.audioElementNode = function (parent, rootObject) { - this.type = "audioElementNode"; - 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("audioelement")[0]; - this.rootDOM.className = "node"; - - var titleDiv = document.createElement('div'); - titleDiv.className = "node-title"; - this.titleDOM.className = "node-title"; - this.titleDOM.textContent = "Audio Element"; - 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); - - // Build the attributes - var attributeList = this.schema.getAllElementsByTagName("xs:attribute"); - for (var i = 0; i < attributeList.length; i++) { - var attributeName = attributeList[i].getAttribute('name'); - var attrObject = this.parent.parent.convertAttributeToDOM(rootObject, attributeList[i]); - this.attributeDOM.appendChild(attrObject.holder); - this.attributes.push(attrObject); - } - - this.deleteNode = { - root: document.createElement("button"), - parent: this, - handleEvent: function () { - var i = this.parent.parent.specification.audioElements.findIndex(this.findNode, this); - if (i >= 0) { - var aeList = this.parent.parent.specification.audioElements; - if (i < aeList.length - 1) { - aeList = aeList.slice(0, i).concat(aeList.slice(i + 1)); - } else { - aeList = aeList.slice(0, i); - } - } - i = this.parent.parent.children.findIndex(function (element, index, array) { - if (element == this.parent) - return true; - else - return false; - }, this); - if (i >= 0) { - var childList = this.parent.children; - if (i < aeList.length - 1) { - childList = childList.slice(0, i).concat(childList.slice(i + 1)); - } else { - childList = childList.slice(0, i); - } - this.parent.parent.childrenDOM.removeChild(this.parent.rootDOM); - } - }, - findNode: function (element, index, array) { - if (element == this.parent.specification) - return true; - else - return false; - } - } - this.deleteNode.root.textContent = "Delete Entry"; - this.deleteNode.root.addEventListener("click", this.deleteNode, false); - this.buttonDOM.appendChild(this.deleteNode.root); - - this.moveButtons = { - root_up: document.createElement("button"), - root_down: document.createElement("button"), - parent: this, - handleEvent: function (event) { - var index = this.parent.parent.getAudioElements().indexOf(this.parent); - if (event.currentTarget.getAttribute("direction") == "up") { - index = Math.max(index - 1, 0); - } else if (event.currentTarget.getAttribute("direction") == "down") { - index = Math.min(index + 1, this.parent.parent.getAudioElements().length - 1); - } - this.parent.moveToPosition(index); - this.disable(index); - }, - disable: function (index) { - if (index == 0) { - this.root_up.disabled = true; - } else { - this.root_up.disabled = false; - } - if (index == this.parent.parent.getAudioElements().length - 1) { - this.root_down.disabled = true; - } else { - this.root_down.disabled = false; - } - } - } - this.moveButtons.root_up.setAttribute("direction", "up"); - this.moveButtons.root_down.setAttribute("direction", "down"); - this.moveButtons.root_up.addEventListener("click", this.moveButtons, false); - this.moveButtons.root_down.addEventListener("click", this.moveButtons, false); - this.moveButtons.root_up.textContent = "Move Up"; - this.moveButtons.root_down.textContent = "Move Down"; - this.buttonDOM.appendChild(this.moveButtons.root_up); - this.buttonDOM.appendChild(this.moveButtons.root_down); - - this.moveToPosition = function (new_index) { - - // Get the zero-th Object - var zero_object = this.parent.getAudioElements()[0]; - var parent_children_root_index = this.parent.children.indexOf(zero_object); - // splice out the array for processing - var process_array = this.parent.children.splice(parent_children_root_index); - - - new_index = Math.min(new_index, process_array.length); - var curr_index = process_array.findIndex(function (elem) { - if (elem == this) { - return true; - } else { - return false; - } - }, this); - - // Split at the current location to remove the node and shift all the children up - var tail = process_array.splice(curr_index + 1); - process_array.pop(); - process_array = process_array.concat(tail); - - //Split at the new location and insert the node - tail = process_array.splice(new_index); - process_array.push(this); - process_array = process_array.concat(tail); - - // Re-attach to the parent.children - this.parent.children = this.parent.children.concat(process_array); - - // Re-build the specification - this.parent.specification.audioElements = []; - for (var obj of process_array) { - this.parent.specification.audioElements.push(obj.specification); - } - this.parent.redrawChildren(); - - process_array.forEach(function (obj, index) { - obj.moveButtons.disable(index); - }); - - } - } - - this.commentQuestionNode = function (parent, rootObject) { - this.type = "commentQuestionNode"; - 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("page")[0]; - this.rootDOM.className = "node audio-element"; - - var titleDiv = document.createElement('div'); - titleDiv.className = "node-title"; - this.titleDOM.className = "node-title"; - this.titleDOM.textContent = "Test Page"; - 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); - - } - - // Build the components - if (this.specification.interfaces.length == 0) { - this.specification.interfaces.push(new specification.interfaceNode(specification)); - } - for (var interfaceObj of this.specification.interfaces) { - var newInterface = new this.parent.interfaceNode(this.parent, interfaceObj); - newInterface.build("Interface", "" + this.specification.id + "-interface", this.childrenDOM); - this.children.push(newInterface); - this.interfaces.push(newInterface); - } - - for (var elements of this.specification.audioElements) { - var audioElementDOM = new this.audioElementNode(this, elements); - this.children.push(audioElementDOM); - this.childrenDOM.appendChild(audioElementDOM.rootDOM); - } - - this.getAudioElements().forEach(function (elem) { - elem.moveButtons.disable(); - }); - - this.addInterface = { - root: document.createElement("button"), - parent: this, - handleEvent: function () { - var InterfaceObj = new specification.interfaceNode(specification); - var newInterface = new this.parent.parent.interfaceNode(this.parent.parent, InterfaceObj); - newInterface.build("Interface", "" + this.parent.specification.id + "-interface", this.parent.childrenDOM); - this.parent.children.push(newInterface); - this.parent.specification.interfaces.push(InterfaceObj); - this.parent.interfaces.push(newInterface); - } - } - this.addInterface.root.textContent = "Add Interface"; - this.addInterface.root.addEventListener("click", this.addInterface, false); - this.buttonDOM.appendChild(this.addInterface.root); - - this.addAudioElement = { - root: document.createElement("button"), - parent: this, - handleEvent: function () { - var audioElementObject = new this.parent.specification.audioElementNode(specification); - var audioElementDOM = new this.parent.audioElementNode(this.parent, audioElementObject); - this.parent.specification.audioElements.push(audioElementObject); - this.parent.children.push(audioElementDOM); - this.parent.childrenDOM.appendChild(audioElementDOM.rootDOM); - } - } - this.addAudioElement.root.textContent = "Add Audio Element"; - this.addAudioElement.root.addEventListener("click", this.addAudioElement, false); - this.buttonDOM.appendChild(this.addAudioElement.root); - } -} + $s.page.audioElements.splice(index, 1); + }; +}]); diff -r e61531ce764f -r b2f52634c830 tests/examples/mushra_example.xml --- a/tests/examples/mushra_example.xml Tue Jun 27 21:22:15 2017 +0100 +++ b/tests/examples/mushra_example.xml Wed Jul 05 12:22:29 2017 +0100 @@ -48,6 +48,7 @@ <interface> <interfaceoption type="check" name="fragmentMoved" /> <interfaceoption type="check" name="scalerange" min="25" max="75" /> + <interfaceoption type="show" name="fragmentSort" /> <interfaceoption type="show" name='playhead' /> <interfaceoption type="show" name="page-count" /> <interfaceoption type="show" name="volume" /> diff -r e61531ce764f -r b2f52634c830 tests/examples/ordinal_example.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/examples/ordinal_example.xml Wed Jul 05 12:22:29 2017 +0100 @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8" ?> + <waet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="test-schema.xsd"> + <setup interface="ordinal" projectReturn="save.php" randomiseOrder="true"> + <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="fragmentComment" /> + <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="-23"> + <title>Ordinal Evaluation + + + + + + + + + +
+ diff -r e61531ce764f -r b2f52634c830 xml/test-schema.xsd --- a/xml/test-schema.xsd Tue Jun 27 21:22:15 2017 +0100 +++ b/xml/test-schema.xsd Wed Jul 05 12:22:29 2017 +0100 @@ -419,6 +419,7 @@ +