Mercurial > hg > webaudioevaluationtool
changeset 2604:8188efa9415d
Merge branch 'vnext' into Dev_main
# Conflicts:
# css/core.css
# interfaces/ape.js
# js/specification.js
# tests/examples/APE_example.xml
# tests/examples/mushra_example.xml
author | Nicholas Jillings <nicholas.jillings@mail.bcu.ac.uk> |
---|---|
date | Mon, 14 Nov 2016 14:49:57 +0000 |
parents | 2577d983f291 (diff) 1c8aac0ee5e9 (current diff) |
children | ac9b08b961c0 |
files | css/core.css interfaces/ape.js js/core.js js/specification.js tests/examples/AB_example.xml tests/examples/APE_example.xml tests/examples/mushra_example.xml xml/test-schema.xsd |
diffstat | 7 files changed, 614 insertions(+), 110 deletions(-) [+] |
line wrap: on
line diff
--- a/css/core.css Mon Nov 14 14:45:39 2016 +0000 +++ b/css/core.css Mon Nov 14 14:49:57 2016 +0000 @@ -101,6 +101,12 @@ table.popup-option-list tr td { padding: 5px; } +div.survey-slider-text-holder { + display: flex; + flex-direction: row; + justify-content: space-between; + padding: 0px 15px; +} button#popup-proceed { bottom: 10px; right: 10px; @@ -245,3 +251,14 @@ padding: 0 5px; height: 290px; } + +/* Comment Boxes */ + +div.comment-slider-text-holder { + display: flex; + flex-direction: row; + justify-content: space-between; +} +div.comment-slider-text-holder span { + margin: 0px 5px; +}
--- a/interfaces/ape.js Mon Nov 14 14:45:39 2016 +0000 +++ b/interfaces/ape.js Mon Nov 14 14:49:57 2016 +0000 @@ -273,6 +273,10 @@ feedbackHolder.innerHTML = ""; sliderHolder.innerHTML = ""; + // Set labelType if default to number + if (audioHolderObject.label == "default" || audioHolderObject.label == "") { + audioHolderObject.label = "number"; + } // Set the page title if (typeof audioHolderObject.title == "string" && audioHolderObject.title.length > 0) { document.getElementById("test-title").textContent = audioHolderObject.title @@ -592,22 +596,8 @@ // Create a new slider object; this.parent = audioObject; this.trackSliderObjects = []; - this.label = null; + this.label = interfaceContext.getLabel(audioObject.specification.parent.label, index, audioObject.specification.parent.labelStart); this.playing = false; - switch (audioObject.specification.parent.label) { - case "letter": - this.label = String.fromCharCode(97 + index); - break; - case "capital": - this.label = String.fromCharCode(65 + index); - break; - case "none": - this.label = ""; - break; - default: - this.label = "" + (index + 1); - break; - } for (var i = 0; i < interfaceContext.interfaceSliders.length; i++) { var trackObj = interfaceContext.interfaceSliders[i].createSliderObject(audioObject, this.label); this.trackSliderObjects.push(trackObj);
--- a/js/core.js Mon Nov 14 14:45:39 2016 +0000 +++ b/js/core.js Mon Nov 14 14:49:57 2016 +0000 @@ -799,13 +799,13 @@ } else if (node.specification.type == 'number') { var input = document.createElement('input'); input.type = 'textarea'; - if (node.min != null) { + if (node.specification.min != null) { input.min = node.specification.min; } - if (node.max != null) { + if (node.specification.max != null) { input.max = node.specification.max; } - if (node.step != null) { + if (node.specification.step != null) { input.step = node.specification.step; } if (node.response != undefined) { @@ -823,6 +823,30 @@ iframe.className = "youtube"; iframe.src = node.specification.url; this.popupResponse.appendChild(iframe); + } else if (node.specification.type == "slider") { + var hold = document.createElement('div'); + var input = document.createElement('input'); + input.type = 'range'; + input.style.width = "90%"; + if (node.specification.min != null) { + input.min = node.specification.min; + } + if (node.specification.max != null) { + input.max = node.specification.max; + } + if (node.response != undefined) { + input.value = node.response; + } + hold.className = "survey-slider-text-holder"; + var minText = document.createElement('span'); + var maxText = document.createElement('span'); + minText.textContent = node.specification.leftText; + maxText.textContent = node.specification.rightText; + hold.appendChild(minText); + hold.appendChild(maxText); + this.popupResponse.appendChild(input); + this.popupResponse.appendChild(hold); + this.popupResponse.style.textAlign = "center"; } if (this.currentIndex + 1 == this.popupOptions.length) { if (this.node.location == "pre") { @@ -1107,6 +1131,49 @@ break; } } + } else if (node.specification.type == 'slider') { + var input = this.popupContent.getElementsByTagName('input')[0]; + node.response = input.value; + for (var condition of node.specification.conditions) { + var pass = false; + switch (condition.check) { + case "contains": + console.log("Survey Element of type 'number' cannot interpret contains conditions. IGNORING"); + break; + case "greaterThan": + if (node.response > Number(condition.value)) { + pass = true; + } + break; + case "lessThan": + if (node.response < Number(condition.value)) { + pass = true; + } + break; + case "equals": + if (node.response == condition.value) { + pass = true; + } + break; + } + var jumpID; + if (pass) { + jumpID = condition.jumpToOnPass; + } else { + jumpID = condition.jumpToOnFail; + } + if (jumpID != undefined) { + var index = this.popupOptions.findIndex(function (item, index, element) { + if (item.specification.id == jumpID) { + return true; + } else { + return false; + } + }, this); + this.currentIndex = index - 1; + break; + } + } } this.currentIndex++; if (this.currentIndex < this.popupOptions.length) { @@ -2629,6 +2696,63 @@ this.resize(); }; + this.sliderBox = function (commentQuestion) { + this.specification = commentQuestion; + this.holder = document.createElement("div"); + this.holder.className = 'comment-div'; + this.string = document.createElement("span"); + this.string.innerHTML = commentQuestion.statement; + this.slider = document.createElement("input"); + this.slider.type = "range"; + this.slider.min = commentQuestion.min; + this.slider.max = commentQuestion.max; + this.slider.step = commentQuestion.step; + this.slider.value = commentQuestion.value; + var br = document.createElement('br'); + + var textHolder = document.createElement("div"); + textHolder.className = "comment-slider-text-holder"; + + this.leftText = document.createElement("span"); + this.leftText.textContent = commentQuestion.leftText; + this.rightText = document.createElement("span"); + this.rightText.textContent = commentQuestion.rightText; + textHolder.appendChild(this.leftText); + textHolder.appendChild(this.rightText); + + this.holder.appendChild(this.string); + this.holder.appendChild(br); + this.holder.appendChild(this.slider); + this.holder.appendChild(textHolder); + + this.exportXMLDOM = function (storePoint) { + var root = storePoint.parent.document.createElement('comment'); + root.id = this.specification.id; + root.setAttribute('type', this.specification.type); + console.log("Question: " + this.string.textContent); + console.log("Response: " + this.slider.value); + var question = storePoint.parent.document.createElement('question'); + question.textContent = this.string.textContent; + var response = storePoint.parent.document.createElement('response'); + response.textContent = this.slider.value; + root.appendChild(question); + root.appendChild(response); + storePoint.XMLDOM.appendChild(root); + return root; + }; + this.resize = function () { + var boxwidth = (window.innerWidth - 100) / 2; + if (boxwidth >= 600) { + boxwidth = 600; + } else if (boxwidth < 400) { + boxwidth = 400; + } + this.holder.style.width = boxwidth + "px"; + this.slider.style.width = boxwidth - 24 + "px"; + }; + this.resize(); + }; + this.createCommentQuestion = function (element) { var node; if (element.type == 'question') { @@ -2637,6 +2761,8 @@ node = new this.radioBox(element); } else if (element.type == 'checkbox') { node = new this.checkboxBox(element); + } else if (element.type == 'slider') { + node = new this.sliderBox(element); } this.commentQuestions.push(node); return node; @@ -2912,8 +3038,8 @@ obj.input.value = obj.gain.gain.value; obj.input.setAttribute('orient', 'vertical'); obj.input.type = "range"; - obj.input.min = -6; - obj.input.max = 6; + obj.input.min = -12; + obj.input.max = 0; obj.input.step = 0.25; if (f0 != 1000) { obj.input.value = (Math.random() * 12) - 6; @@ -3128,6 +3254,70 @@ node.textContent = errorMessage; testState.currentStore.XMLDOM.appendChild(node); }; + + this.getLabel = function (labelType, index, labelStart) { + /* + Get the correct label based on type, index and offset + */ + + function calculateLabel(labelType, index, offset) { + if (labelType == "none") { + return ""; + } + switch (labelType) { + case "letter": + return String.fromCharCode((index + offset) % 26 + 97); + case "capital": + return String.fromCharCode((index + offset) % 26 + 66); + case "number": + return String(index + offset); + default: + return ""; + } + } + + if (typeof labelStart !== "string" || labelStart.length == 0) { + labelStart = String.fromCharCode(0); + } + + switch (labelType) { + case "letter": + labelStart = labelStart.charCodeAt(0); + if (labelStart < 97 || labelStart > 122) { + labelStart = 97; + } + labelStart -= 97; + break; + case "capital": + labelStart = labelStart.charCodeAt(0); + if (labelStart < 65 || labelStart > 90) { + labelStart = 65; + } + labelStart -= 65; + break; + case "number": + if (!isFinite(Number(labelStart))) { + labelStart = 1; + } + break; + case "none": + default: + labelStart = 0; + } + if (typeof index == "number") { + return calculateLabel(labelType, index, labelStart); + } else if (index.length && index.length > 0) { + var a = [], + l = index.length, + i; + for (i = 0; i < l; i++) { + a[i] = calculateLabel(labelType, index[i], labelStart); + } + return a; + } else { + throw ("Invalid arguments"); + } + } } function Storage() { @@ -3283,6 +3473,7 @@ switch (node.specification.type) { case "number": case "question": + case "slider": var child = this.parent.document.createElement('response'); child.textContent = node.response; surveyresult.appendChild(child);
--- a/js/specification.js Mon Nov 14 14:45:39 2016 +0000 +++ b/js/specification.js Mon Nov 14 14:49:57 2016 +0000 @@ -195,7 +195,7 @@ this.OptionNode = function (specification) { this.type = undefined; - this.schema = specification.schema.getAllElementsByName('surveyentry')[0]; + this.schema = undefined; this.id = undefined; this.name = undefined; this.mandatory = undefined; @@ -208,6 +208,7 @@ this.conditions = []; this.decode = function (parent, child) { + this.schema = specification.schema.getAllElementsByName(child.nodeName)[0]; var attributeMap = this.schema.getAllElementsByTagName('xs:attribute'); for (var i in attributeMap) { if (isNaN(Number(i)) == true) { @@ -226,6 +227,15 @@ break; } } + if (child.nodeName == 'surveyentry') { + console.log("NOTE - Use of <surveyelement> is now deprecated. Whilst these will still work, newer nodes and tighter error checking will not be enforced"); + console.log("Please use the newer, type specifc nodes"); + if (!this.type) { + throw ("Type not specified"); + } + } else { + this.type = child.nodeName.split('survey')[1]; + } this.statement = child.getElementsByTagName('statement')[0].textContent; if (this.type == "checkbox" || this.type == "radio") { var children = child.getElementsByTagName('option'); @@ -242,6 +252,17 @@ }); } } + } else if (this.type == "slider") { + this.leftText = ""; + this.rightText = ""; + var minText = child.getElementsByTagName("minText"); + var maxText = child.getElementsByTagName("maxText"); + if (minText.length > 0) { + this.leftText = minText[0].textContent; + } + if (maxText.length > 0) { + this.rightText = maxText[0].textContent; + } } var conditionElements = child.getElementsByTagName("conditional"); for (var i = 0; i < conditionElements.length; i++) { @@ -257,8 +278,7 @@ }; this.exportXML = function (doc) { - var node = doc.createElement('surveyentry'); - node.setAttribute('type', this.type); + var node = doc.createElement('survey' + this.type); var statement = doc.createElement('statement'); statement.textContent = this.statement; node.appendChild(statement); @@ -319,6 +339,19 @@ node.setAttribute("url", this.url); } break; + case "slider": + node.setAttribute("min", this.min); + node.setAttribute("max", this.max); + if (this.leftText) { + var minText = doc.createElement("minText"); + minText.textContent = this.leftText; + node.appendChild(minText); + } + if (this.rightText) { + var maxText = doc.createElement("maxText"); + maxText.textContent = this.rightText; + node.appendChild(maxText); + } default: break; } @@ -341,11 +374,12 @@ } else if (this.location == 'after') { this.location = 'post'; } - var children = xml.getAllElementsByTagName('surveyentry'); - for (var i = 0; i < children.length; i++) { + var child = xml.firstElementChild + while (child) { var node = new this.OptionNode(this.specification); - node.decode(parent, children[i]); + node.decode(parent, child); this.options.push(node); + child = child.nextElementSibling; } if (this.options.length == 0) { console.log("Empty survey node"); @@ -562,11 +596,15 @@ } // Now decode the commentquestions - var commentQuestions = xml.getElementsByTagName('commentquestion'); - for (var i = 0; i < commentQuestions.length; i++) { - var node = new this.commentQuestionNode(this.specification); - node.decode(parent, commentQuestions[i]); - this.commentQuestions.push(node); + var cqNode = xml.getElementsByTagName('commentquestions'); + if (cqNode.length != 0) { + cqNode = cqNode[0]; + var commentQuestions = cqNode.children; + for (var i = 0; i < commentQuestions.length; i++) { + var node = new this.commentQuestionNode(this.specification); + node.decode(parent, commentQuestions[i]); + this.commentQuestions.push(node); + } } }; @@ -612,26 +650,85 @@ this.id = null; this.name = undefined; this.type = undefined; - this.options = []; this.statement = undefined; this.schema = specification.schema.getAllElementsByName('commentquestion')[0]; this.decode = function (parent, xml) { this.id = xml.id; this.name = xml.getAttribute('name'); - this.type = xml.getAttribute('type'); + switch (xml.nodeName) { + case "commentradio": + this.type = "radio"; + this.options = []; + break; + case "commentcheckbox": + this.type = "checkbox"; + this.options = []; + break; + case "commentslider": + this.type = "slider"; + this.min = undefined; + this.max = undefined; + this.step = undefined; + break; + case "commentquestion": + default: + this.type = "question"; + break; + } this.statement = xml.getElementsByTagName('statement')[0].textContent; - var optNodes = xml.getElementsByTagName('option'); - for (var i = 0; i < optNodes.length; i++) { - var optNode = optNodes[i]; - this.options.push({ - name: optNode.getAttribute('name'), - text: optNode.textContent - }); + if (this.type == "radio" || this.type == "checkbox") { + var optNodes = xml.getElementsByTagName('option'); + for (var i = 0; i < optNodes.length; i++) { + var optNode = optNodes[i]; + this.options.push({ + name: optNode.getAttribute('name'), + text: optNode.textContent + }); + } + } + if (this.type == "slider") { + this.min = Number(xml.getAttribute("min")); + this.max = Number(xml.getAttribute("max")); + this.step = Number(xml.getAttribute("step")); + if (this.step == undefined) { + this.step = 1; + } + this.value = Number(xml.getAttribute("value")); + if (this.value == undefined) { + this.value = min; + } + this.leftText = xml.getElementsByTagName("minText"); + if (this.leftText && this.leftText.length > 0) { + this.leftText = this.leftText[0].textContent; + } else { + this.leftText = ""; + } + this.rightText = xml.getElementsByTagName("maxText"); + if (this.rightText && this.rightText.length > 0) { + this.rightText = this.rightText[0].textContent; + } else { + this.rightText = ""; + } } }; this.encode = function (root) { - var node = root.createElement("commentquestion"); + var node; + switch (this.type) { + case "radio": + node = root.createElement("commentradio"); + break; + case "checkbox": + node = root.createElement("commentcheckbox"); + break; + case "slider": + node = root.createElement("commentslider"); + break; + case "question": + default: + node = root.createElement("commentquestion"); + break; + } node.id = this.id; node.setAttribute("type", this.type); if (this.name != undefined) { @@ -640,11 +737,33 @@ var statement = root.createElement("statement"); statement.textContent = this.statement; node.appendChild(statement); - for (var option of this.options) { - var child = root.createElement("option"); - child.setAttribute("name", option.name); - child.textContent = option.text; - node.appendChild(child); + if (this.type == "radio" || this.type == "checkbox") { + for (var option of this.options) { + var child = root.createElement("option"); + child.setAttribute("name", option.name); + child.textContent = option.text; + node.appendChild(child); + } + } + if (this.type == "slider") { + node.setAttribute("min", this.min); + node.setAttribute("max", this.max); + if (this.step !== 1) { + node.setAttribute("step", this.step); + } + if (this.value !== this.min) { + node.setAttribute("value", this.value); + } + if (this.leftText.length > 0) { + var leftText = root.createElement("minText"); + leftText.textContent = this.leftText; + node.appendChild(leftText); + } + if (this.rightText.length > 0) { + var rightText = root.createElement("maxText"); + rightText.textContent = this.rightText; + node.appendChild(rightText); + } } return node; };
--- a/tests/examples/APE_example.xml Mon Nov 14 14:45:39 2016 +0000 +++ b/tests/examples/APE_example.xml Mon Nov 14 14:49:57 2016 +0000 @@ -6,7 +6,7 @@ <statement>Please enter your name.</statement> <conditional check="equals" value="John" jumpToOnPass="test-intro" jumpToOnFail="checkboxtest" /> </surveyentry> - <surveyentry type="checkbox" id="checkboxtest" mandatory="true"> + <surveyentry type="checkbox" id="checkboxtest" mandatory="true" min="2" max="4"> <statement>Please select with which activities you have any experience (example checkbox question)</statement> <option name="musician">Playing a musical instrument</option> <option name="soundengineer">Recording or mixing audio</option> @@ -111,24 +111,26 @@ <audioelement url="4.wav" gain="0.0" id="track-9" /> <audioelement url="5.wav" gain="0.0" id="track-10" /> <audioelement url="6.wav" gain="0.0" id="track-11" type="outside-reference" /> - <commentquestion id='mixingExperience' type="question"> - <statement>What is your general experience with numbers?</statement> - </commentquestion> - <commentquestion id="preference" type="radio"> - <statement>Please enter your overall preference</statement> - <option name="worst">Very Bad</option> - <option name="bad"></option> - <option name="OK">OK</option> - <option name="Good"></option> - <option name="Great">Great</option> - </commentquestion> - <commentquestion id="character" type="checkbox"> - <statement>Please describe the overall character</statement> - <option name="funky">Funky</option> - <option name="mellow">Mellow</option> - <option name="laidback">Laid back</option> - <option name="heavy">Heavy</option> - </commentquestion> + <commentquestions> + <commentquestion id='mixingExperience'> + <statement>What is your general experience with numbers?</statement> + </commentquestion> + <commentradio id="preference"> + <statement>Please enter your overall preference</statement> + <option name="worst">Very Bad</option> + <option name="bad"></option> + <option name="OK">OK</option> + <option name="Good"></option> + <option name="Great">Great</option> + </commentradio> + <commentcheckbox id="character" type="checkbox"> + <statement>Please describe the overall character</statement> + <option name="funky">Funky</option> + <option name="mellow">Mellow</option> + <option name="laidback">Laid back</option> + <option name="heavy">Heavy</option> + </commentcheckbox> + </commentquestions> <survey location="before"> <surveyentry type="statement" id="test-1-intro"> <statement>Example of an 'APE' style interface with hidden anchor 'zero' (which needs to be below 20%), looping of the samples, randomisation of marker labels, mandatory moving of every sample, and a forced scale usage of at least 25%-75%.</statement>
--- a/tests/examples/mushra_example.xml Mon Nov 14 14:45:39 2016 +0000 +++ b/tests/examples/mushra_example.xml Mon Nov 14 14:49:57 2016 +0000 @@ -101,24 +101,26 @@ <audioelement url="4.wav" gain="0.0" id="track-9" /> <audioelement url="5.wav" gain="0.0" id="track-10" /> <audioelement url="1.wav" gain="0.0" id="track-11" type="outside-reference" /> - <commentquestion id='mixingExperience' type="question"> - <statement>What is your general experience with numbers?</statement> - </commentquestion> - <commentquestion id="preference" type="radio"> - <statement>Please enter your overall preference</statement> - <option name="worst">Very Bad</option> - <option name="bad"></option> - <option name="OK">OK</option> - <option name="Good"></option> - <option name="Great">Great</option> - </commentquestion> - <commentquestion id="character" type="checkbox"> - <statement>Please describe the overall character</statement> - <option name="funky">Funky</option> - <option name="mellow">Mellow</option> - <option name="laidback">Laid back</option> - <option name="heavy">Heavy</option> - </commentquestion> + <commentquestions> + <commentquestion id='mixingExperience' type="question"> + <statement>What is your general experience with numbers?</statement> + </commentquestion> + <commentquestion id="preference" type="radio"> + <statement>Please enter your overall preference</statement> + <option name="worst">Very Bad</option> + <option name="bad"></option> + <option name="OK">OK</option> + <option name="Good"></option> + <option name="Great">Great</option> + </commentquestion> + <commentquestion id="character" type="checkbox"> + <statement>Please describe the overall character</statement> + <option name="funky">Funky</option> + <option name="mellow">Mellow</option> + <option name="laidback">Laid back</option> + <option name="heavy">Heavy</option> + </commentquestion> + </commentquestions> <survey location="before"> <surveyentry type="statement" id="test-1-intro"> <statement>Example of a 'MUSHRA' style interface with hidden anchor 'zero' (which needs to be below 20%), looping of the samples, randomisation of marker labels, mandatory moving of every sample, and a forced scale usage of at least 25%-75%.</statement>
--- a/xml/test-schema.xsd Mon Nov 14 14:45:39 2016 +0000 +++ b/xml/test-schema.xsd Mon Nov 14 14:49:57 2016 +0000 @@ -75,7 +75,7 @@ <xs:element name="commentboxprefix" type="xs:string" minOccurs="0" maxOccurs="1" /> <xs:element ref="interface" minOccurs="1" maxOccurs="unbounded" /> <xs:element ref="audioelement" minOccurs="1" maxOccurs="unbounded" /> - <xs:element ref="commentquestion" minOccurs="0" maxOccurs="unbounded" /> + <xs:element ref="commentquestions" minOccurs="0" maxOccurs="1" /> <xs:element ref="survey" minOccurs="0" maxOccurs="2" /> </xs:sequence> <xs:attribute ref="id" use="required" /> @@ -205,10 +205,21 @@ </xs:complexType> </xs:element> - <xs:element name="commentquestion"> + <xs:element name="commentquestions"> + <xs:complexType> + <xs:choice maxOccurs="unbounded"> + <xs:element name="commentquestion" maxOccurs="unbounded" /> + <xs:element name="commentradio" maxOccurs="unbounded" /> + <xs:element name="commentcheckbox" maxOccurs="unbounded" /> + <xs:element name="commentslider" maxOccurs="unbounded" /> + </xs:choice> + </xs:complexType> + </xs:element> + + <xs:element name="commentradio"> <xs:complexType> <xs:sequence> - <xs:element ref="statement" minOccurs="0" maxOccurs="1" /> + <xs:element ref="statement" minOccurs="1" maxOccurs="1" /> <xs:element name="option" minOccurs="0" maxOccurs="unbounded"> <xs:complexType> <xs:simpleContent> @@ -221,22 +232,202 @@ </xs:sequence> <xs:attribute ref="id" use="optional" /> <xs:attribute ref="name" use="optional" /> - <xs:attribute name="type" default="question"> + </xs:complexType> + </xs:element> + + <xs:element name="commentcheckbox"> + <xs:complexType> + <xs:sequence> + <xs:element ref="statement" minOccurs="1" maxOccurs="1" /> + <xs:element name="option" minOccurs="0" maxOccurs="unbounded"> + <xs:complexType> + <xs:simpleContent> + <xs:extension base="xs:string"> + <xs:attribute ref="name" /> + </xs:extension> + </xs:simpleContent> + </xs:complexType> + </xs:element> + </xs:sequence> + <xs:attribute ref="id" use="optional" /> + <xs:attribute ref="name" use="optional" /> + </xs:complexType> + </xs:element> + + <xs:element name="commentquestion"> + <xs:complexType> + <xs:sequence> + <xs:element ref="statement" minOccurs="1" maxOccurs="1" /> + </xs:sequence> + <xs:attribute ref="id" use="optional" /> + <xs:attribute ref="name" use="optional" /> + </xs:complexType> + </xs:element> + + <xs:element name="commentslider"> + <xs:complexType> + <xs:sequence> + <xs:element ref="statement" minOccurs="1" maxOccurs="1" /> + <xs:element name="minText" minOccurs="0" maxOccurs="1" type="xs:string" /> + <xs:element name="maxText" minOccurs="0" maxOccurs="1" type="xs:string" /> + </xs:sequence> + <xs:attribute ref="id" use="optional" /> + <xs:attribute ref="name" use="optional" /> + <xs:attribute name="min" type="xs:decimal" use="required" /> + <xs:attribute name="max" type="xs:decimal" use="required" /> + <xs:attribute name="step" type="xs:decimal" use="optional" default="1" /> + <xs:attribute name="value" type="xs:decimal" use="optional" /> + </xs:complexType> + </xs:element> + + <xs:element name="conditional"> + <xs:complexType> + <xs:attribute name="check" use="required"> <xs:simpleType> <xs:restriction base="xs:string"> - <xs:enumeration value="question" /> - <xs:enumeration value="radio" /> - <xs:enumeration value="checkbox" /> + <xs:enumeration value="equals" /> + <xs:enumeration value="lessThan" /> + <xs:enumeration value="greaterThan" /> + <xs:enumeration value="stringContains" /> + </xs:restriction> + </xs:simpleType> + </xs:attribute> + <xs:attribute name="value" type="xs:string" use="optional" /> + <xs:attribute name="jumpToOnPass" type="xs:string" use="optional" /> + <xs:attribute name="jumpToOnFail" type="xs:string" use="optional" /> + </xs:complexType> + </xs:element> + + <xs:element name="surveyquestion"> + <xs:complexType> + <xs:sequence> + <xs:element ref="statement" minOccurs="1" maxOccurs="1" /> + <xs:element ref="conditional" minOccurs="0" maxOccurs="unbounded" /> + </xs:sequence> + <xs:attribute ref="id" use="required" /> + <xs:attribute ref="name" /> + <xs:attribute ref="mandatory" /> + <xs:attribute name="boxsize" default="normal"> + <xs:simpleType> + <xs:restriction base="xs:string"> + <xs:enumeration value="normal" /> + <xs:enumeration value="large" /> + <xs:enumeration value="small" /> + <xs:enumeration value="huge" /> </xs:restriction> </xs:simpleType> </xs:attribute> </xs:complexType> </xs:element> + <xs:element name="surveyradio"> + <xs:complexType> + <xs:sequence> + <xs:element ref="statement" minOccurs="1" maxOccurs="1" /> + <xs:element name="option" minOccurs="0" maxOccurs="unbounded"> + <xs:complexType> + <xs:simpleContent> + <xs:extension base="xs:string"> + <xs:attribute ref="name" /> + </xs:extension> + </xs:simpleContent> + </xs:complexType> + </xs:element> + <xs:element ref="conditional" minOccurs="0" maxOccurs="unbounded" /> + </xs:sequence> + <xs:attribute ref="id" use="required" /> + <xs:attribute ref="name" /> + <xs:attribute ref="mandatory" /> + <xs:attribute name="min" type="xs:decimal" /> + <xs:attribute name="max" type="xs:decimal" /> + </xs:complexType> + </xs:element> + + <xs:element name="surveycheckbox"> + <xs:complexType> + <xs:sequence> + <xs:element ref="statement" minOccurs="1" maxOccurs="1" /> + <xs:element name="option" minOccurs="0" maxOccurs="unbounded"> + <xs:complexType> + <xs:simpleContent> + <xs:extension base="xs:string"> + <xs:attribute ref="name" /> + </xs:extension> + </xs:simpleContent> + </xs:complexType> + </xs:element> + <xs:element ref="conditional" minOccurs="0" maxOccurs="unbounded" /> + </xs:sequence> + <xs:attribute ref="id" use="required" /> + <xs:attribute ref="name" /> + <xs:attribute ref="mandatory" /> + <xs:attribute name="min" type="xs:decimal" /> + <xs:attribute name="max" type="xs:decimal" /> + </xs:complexType> + </xs:element> + + <xs:element name="surveystatement"> + <xs:complexType> + <xs:sequence> + <xs:element ref="statement" minOccurs="1" maxOccurs="1" /> + </xs:sequence> + <xs:attribute ref="id" use="required" /> + </xs:complexType> + </xs:element> + + <xs:element name="surveynumber"> + <xs:complexType> + <xs:sequence> + <xs:element ref="statement" minOccurs="1" maxOccurs="1" /> + <xs:element ref="conditional" minOccurs="0" maxOccurs="unbounded" /> + </xs:sequence> + <xs:attribute ref="id" use="required" /> + <xs:attribute ref="name" /> + <xs:attribute ref="mandatory" /> + <xs:attribute name="min" type="xs:decimal" /> + <xs:attribute name="max" type="xs:decimal" /> + </xs:complexType> + </xs:element> + + <xs:element name="surveyslider"> + <xs:complexType> + <xs:sequence> + <xs:element ref="statement" minOccurs="1" maxOccurs="1" /> + <xs:element name="minText" minOccurs="0" maxOccurs="1" type="xs:string" /> + <xs:element name="maxText" minOccurs="0" maxOccurs="1" type="xs:string" /> + <xs:element ref="conditional" minOccurs="0" maxOccurs="unbounded" /> + </xs:sequence> + <xs:attribute ref="id" use="required" /> + <xs:attribute ref="name" /> + <xs:attribute name="min" use="required" type="xs:decimal" /> + <xs:attribute name="max" use="required" type="xs:decimal" /> + </xs:complexType> + </xs:element> + + <xs:element name="surveyvideo"> + <xs:complexType> + <xs:sequence> + <xs:element ref="statement" minOccurs="1" maxOccurs="1" /> + </xs:sequence> + <xs:attribute ref="id" use="required" /> + <xs:attribute name="url" use="required" type="xs:string" /> + </xs:complexType> + </xs:element> + + <xs:element name="surveyyoutube"> + <xs:complexType> + <xs:sequence> + <xs:element ref="statement" minOccurs="1" maxOccurs="1" /> + </xs:sequence> + <xs:attribute ref="id" use="required" /> + <xs:attribute name="url" use="required" type="xs:string" /> + </xs:complexType> + </xs:element> + <xs:element name="survey"> <xs:complexType> - <xs:sequence> - <xs:element name="surveyentry" minOccurs="0" maxOccurs="unbounded"> + <xs:choice maxOccurs="unbounded"> + <xs:element name="surveyentry" maxOccurs="unbounded"> <xs:complexType> <xs:sequence> <xs:element ref="statement" minOccurs="1" maxOccurs="1" /> @@ -249,23 +440,7 @@ </xs:simpleContent> </xs:complexType> </xs:element> - <xs:element name="conditional" minOccurs="0" maxOccurs="unbounded"> - <xs:complexType> - <xs:attribute name="check" use="required"> - <xs:simpleType> - <xs:restriction base="xs:string"> - <xs:enumeration value="equals" /> - <xs:enumeration value="lessThan" /> - <xs:enumeration value="greaterThan" /> - <xs:enumeration value="stringContains" /> - </xs:restriction> - </xs:simpleType> - </xs:attribute> - <xs:attribute name="value" type="xs:string" use="optional" /> - <xs:attribute name="jumpToOnPass" type="xs:string" use="optional" /> - <xs:attribute name="jumpToOnFail" type="xs:string" use="optional" /> - </xs:complexType> - </xs:element> + <xs:element ref="conditional" minOccurs="0" maxOccurs="unbounded" /> </xs:sequence> <xs:attribute ref="id" use="required" /> <xs:attribute ref="name" /> @@ -298,7 +473,15 @@ <xs:attribute name="url" type="xs:string" use="optional" /> </xs:complexType> </xs:element> - </xs:sequence> + <xs:element name="surveyquestion" maxOccurs="unbounded" /> + <xs:element name="surveyradio" maxOccurs="unbounded" /> + <xs:element name="surveycheckbox" maxOccurs="unbounded" /> + <xs:element name="surveystatement" maxOccurs="unbounded" /> + <xs:element name="surveynumber" maxOccurs="unbounded" /> + <xs:element name="surveyslider" maxOccurs="unbounded" /> + <xs:element name="surveyvideo" maxOccurs="unbounded" /> + <xs:element name="surveyyoutube" maxOccurs="unbounded" /> + </xs:choice> <xs:attribute name="location"> <xs:simpleType> <xs:restriction base="xs:string">