Mercurial > hg > webaudioevaluationtool
view js/specification.js @ 2376:c41caaa96633
Some fixes for #90. Also a failsafe loop if the server never responds with meaningul information from saves (for instance, running only on apache or basic http servers). More changes to pythonServer for python 3.5. Please check if still valid on 2.7
author | Nicholas Jillings <nicholas.jillings@mail.bcu.ac.uk> |
---|---|
date | Thu, 19 May 2016 10:44:19 +0100 |
parents | e4a2a39838db |
children | 737e3f18e32b |
line wrap: on
line source
function Specification() { // Handles the decoding of the project specification XML into a simple JavaScript Object. this.interface = null; this.projectReturn = null; this.returnURL = null; this.randomiseOrder = null; this.testPages = null; this.pages = []; this.metrics = null; this.interfaces = null; this.loudness = null; this.errors = []; this.schema = null; this.exitText = "Thank you."; this.processAttribute = function(attribute,schema,schemaRoot) { // attribute is the string returned from getAttribute on the XML // schema is the <xs:attribute> node if (schema.getAttribute('name') == undefined && schema.getAttribute('ref') != undefined) { schema = schemaRoot.getAllElementsByName(schema.getAttribute('ref'))[0]; } var defaultOpt = schema.getAttribute('default'); if (attribute == null) { attribute = defaultOpt; } var dataType = schema.getAttribute('type'); if (typeof dataType == "string") { dataType = dataType.substr(3);} else { var rest = schema.getAllElementsByTagName("xs:restriction").concat(schema.getAllElementsByTagName("xs:enumeration")); if (rest.length > 0) { dataType = rest[0].getAttribute("base"); if (typeof dataType == "string") { dataType = dataType.substr(3); } else { dataType = "string"; } } else { dataType = "string"; } } if (attribute == null) { return attribute; } switch(dataType) { case "boolean": if (attribute == 'true'){attribute = true;}else{attribute=false;} break; case "negativeInteger": case "positiveInteger": case "nonNegativeInteger": case "nonPositiveInteger": case "integer": case "decimal": case "short": attribute = Number(attribute); break; case "string": default: attribute = String(attribute); break; } return attribute; }; this.decode = function(projectXML) { this.errors = []; // projectXML - DOM Parsed document this.projectXML = projectXML.childNodes[0]; var setupNode = projectXML.getElementsByTagName('setup')[0]; var schemaSetup = this.schema.getAllElementsByName('setup')[0]; // First decode the attributes var attributes = schemaSetup.getAllElementsByTagName('xs:attribute'); for (var i in attributes) { if (isNaN(Number(i)) == true){break;} var attributeName = attributes[i].getAttribute('name') || attributes[i].getAttribute('ref'); var projectAttr = setupNode.getAttribute(attributeName); projectAttr = this.processAttribute(projectAttr,attributes[i],this.schema); switch(typeof projectAttr) { case "number": case "boolean": eval('this.'+attributeName+' = '+projectAttr); break; case "string": eval('this.'+attributeName+' = "'+projectAttr+'"'); break; } } var exitTextNode = setupNode.getElementsByTagName('exitText'); if (exitTextNode.length == 1) { this.exitText = exitTextNode[0].textContent; } this.metrics = new this.metricNode(); this.metrics.decode(this,setupNode.getElementsByTagName('metric')[0]); // Now process the survey node options var survey = setupNode.getElementsByTagName('survey'); for (var i in survey) { if (isNaN(Number(i)) == true){break;} var location = survey[i].getAttribute('location'); switch(location) { case 'pre': case 'before': if (this.preTest != null){console.log("Already a pre/before test survey defined! Ignoring second!!");} else { this.preTest = new this.surveyNode(this); this.preTest.decode(this,survey[i]); } break; case 'post': case 'after': if (this.postTest != null){console.log("Already a post/after test survey defined! Ignoring second!!");} else { this.postTest = new this.surveyNode(this); this.postTest.decode(this,survey[i]); } break; } } var interfaceNode = setupNode.getElementsByTagName('interface'); if (interfaceNode.length > 1) { this.errors.push("Only one <interface> node in the <setup> node allowed! Others except first ingnored!"); } this.interfaces = new this.interfaceNode(this); if (interfaceNode.length != 0) { interfaceNode = interfaceNode[0]; this.interfaces.decode(this,interfaceNode,this.schema.getAllElementsByName('interface')[1]); } // Page tags var pageTags = projectXML.getElementsByTagName('page'); var pageSchema = this.schema.getAllElementsByName('page')[0]; for (var i=0; i<pageTags.length; i++) { var node = new this.page(this); node.decode(this,pageTags[i],pageSchema); this.pages.push(node); } }; this.encode = function() { var RootDocument = document.implementation.createDocument(null,"waet"); var root = RootDocument.children[0]; root.setAttribute("xmlns:xsi","http://www.w3.org/2001/XMLSchema-instance"); root.setAttribute("xsi:noNamespaceSchemaLocation","test-schema.xsd"); // Build setup node var setup = RootDocument.createElement("setup"); var schemaSetup = this.schema.getAllElementsByName('setup')[0]; // First decode the attributes var attributes = schemaSetup.getAllElementsByTagName('xs:attribute'); for (var i=0; i<attributes.length; i++) { var name = attributes[i].getAttribute("name"); if (name == undefined) { name = attributes[i].getAttribute("ref"); } if(eval("this."+name+" != undefined") || attributes[i].getAttribute("use") == "required") { eval("setup.setAttribute('"+name+"',this."+name+")"); } } root.appendChild(setup); // Survey node if (this.exitText != null) { var exitTextNode = RootDocument.createElement('exitText'); exitTextNode.textContent = this.exitText; setup.appendChild(exitTextNode); } setup.appendChild(this.preTest.encode(RootDocument)); setup.appendChild(this.postTest.encode(RootDocument)); setup.appendChild(this.metrics.encode(RootDocument)); setup.appendChild(this.interfaces.encode(RootDocument)); for (var page of this.pages) { root.appendChild(page.encode(RootDocument)); } return RootDocument; }; this.surveyNode = function(specification) { this.location = null; this.options = []; this.parent = null; this.schema = specification.schema.getAllElementsByName('survey')[0]; this.specification = specification; this.OptionNode = function(specification) { this.type = undefined; this.schema = specification.schema.getAllElementsByName('surveyentry')[0]; this.id = undefined; this.name = undefined; this.mandatory = undefined; this.statement = undefined; this.boxsize = undefined; this.options = []; this.min = undefined; this.max = undefined; this.step = undefined; this.decode = function(parent,child) { var attributeMap = this.schema.getAllElementsByTagName('xs:attribute'); for (var i in attributeMap){ if(isNaN(Number(i)) == true){break;} var attributeName = attributeMap[i].getAttribute('name') || attributeMap[i].getAttribute('ref'); var projectAttr = child.getAttribute(attributeName); projectAttr = parent.processAttribute(projectAttr,attributeMap[i],parent.schema); switch(typeof projectAttr) { case "number": case "boolean": eval('this.'+attributeName+' = '+projectAttr); break; case "string": eval('this.'+attributeName+' = "'+projectAttr+'"'); break; } } this.statement = child.getElementsByTagName('statement')[0].textContent; if (this.type == "checkbox" || this.type == "radio") { var children = child.getElementsByTagName('option'); if (children.length == null) { console.log('Malformed' +child.nodeName+ 'entry'); this.statement = 'Malformed' +child.nodeName+ 'entry'; this.type = 'statement'; } else { this.options = []; for (var i in children) { if (isNaN(Number(i))==true){break;} this.options.push({ name: children[i].getAttribute('name'), text: children[i].textContent }); } } } }; this.exportXML = function(doc) { var node = doc.createElement('surveyentry'); node.setAttribute('type',this.type); var statement = doc.createElement('statement'); statement.textContent = this.statement; node.appendChild(statement); node.id = this.id; if (this.name != undefined) { node.setAttribute("name",this.name);} if (this.mandatory != undefined) { node.setAttribute("mandatory",this.mandatory);} node.id = this.id; if (this.name != undefined) {node.setAttribute("name",this.name);} switch(this.type) { case "checkbox": case "radio": for (var i=0; i<this.options.length; i++) { var option = this.options[i]; var optionNode = doc.createElement("option"); optionNode.setAttribute("name",option.name); optionNode.textContent = option.text; node.appendChild(optionNode); } case "number": if (this.min != undefined) {node.setAttribute("min", this.min);} if (this.max != undefined) {node.setAttribute("max", this.max);} case "question": if (this.boxsize != undefined) {node.setAttribute("boxsize",this.boxsize);} if (this.mandatory != undefined) {node.setAttribute("mandatory",this.mandatory);} default: break; } return node; }; }; this.decode = function(parent,xml) { this.parent = parent; this.location = xml.getAttribute('location'); if (this.location == 'before'){this.location = 'pre';} else if (this.location == 'after'){this.location = 'post';} var children = xml.getAllElementsByTagName('surveyentry'); for (var i=0; i<children.length; i++) { var node = new this.OptionNode(this.specification); node.decode(parent,children[i]); this.options.push(node); } if (this.options.length == 0) { console.log("Empty survey node"); console.log(this); } }; this.encode = function(doc) { var node = doc.createElement('survey'); node.setAttribute('location',this.location); for (var i=0; i<this.options.length; i++) { node.appendChild(this.options[i].exportXML(doc)); } return node; }; }; this.interfaceNode = function(specification) { this.title = null; this.name = null; this.options = []; this.scales = []; this.schema = specification.schema.getAllElementsByName('interface')[1]; this.decode = function(parent,xml) { this.name = xml.getAttribute('name'); var titleNode = xml.getElementsByTagName('title'); if (titleNode.length == 1) { this.title = titleNode[0].textContent; } var interfaceOptionNodes = xml.getElementsByTagName('interfaceoption'); // Extract interfaceoption node schema var interfaceOptionNodeSchema = this.schema.getAllElementsByName('interfaceoption')[0]; var attributeMap = interfaceOptionNodeSchema.getAllElementsByTagName('xs:attribute'); for (var i=0; i<interfaceOptionNodes.length; i++) { var ioNode = interfaceOptionNodes[i]; var option = {}; for (var j=0; j<attributeMap.length; j++) { var attributeName = attributeMap[j].getAttribute('name') || attributeMap[j].getAttribute('ref'); var projectAttr = ioNode.getAttribute(attributeName); if(parent.processAttribute) { parent.processAttribute(projectAttr, attributeMap[j], parent.schema) } else { parent.parent.processAttribute(projectAttr, attributeMap[j], parent.parent.schema) } switch(typeof projectAttr) { case "number": case "boolean": eval('option.'+attributeName+' = '+projectAttr); break; case "string": eval('option.'+attributeName+' = "'+projectAttr+'"'); break; } } this.options.push(option); } // Now the scales nodes var scaleParent = xml.getElementsByTagName('scales'); if (scaleParent.length == 1) { scaleParent = scaleParent[0]; var scalelabels = scaleParent.getAllElementsByTagName('scalelabel'); for (var i=0; i<scalelabels.length; i++) { this.scales.push({ text: scalelabels[i].textContent, position: Number(scalelabels[i].getAttribute('position')) }); } } }; this.encode = function(doc) { var node = doc.createElement("interface"); if (typeof name == "string") node.setAttribute("name",this.name); if (typeof this.title == "string") { var titleNode = doc.createElement("title"); titleNode.textContent = this.title; node.appendChild(titleNode); } for (var option of this.options) { var child = doc.createElement("interfaceoption"); child.setAttribute("type",option.type); child.setAttribute("name",option.name); node.appendChild(child); } if (this.scales.length != 0) { var scales = doc.createElement("scales"); for (var scale of this.scales) { var child = doc.createElement("scalelabel"); child.setAttribute("position",scale.position); child.textContent = scale.text; scales.appendChild(child); } node.appendChild(scales); } return node; }; }; this.metricNode = function() { this.enabled = []; this.decode = function(parent, xml) { var children = xml.getElementsByTagName('metricenable'); for (var i in children) { if (isNaN(Number(i)) == true){break;} this.enabled.push(children[i].textContent); } } this.encode = function(doc) { var node = doc.createElement('metric'); for (var i in this.enabled) { if (isNaN(Number(i)) == true){break;} var child = doc.createElement('metricenable'); child.textContent = this.enabled[i]; node.appendChild(child); } return node; } } this.page = function(specification) { this.presentedId = undefined; this.id = undefined; this.hostURL = undefined; this.randomiseOrder = undefined; this.loop = undefined; this.showElementComments = undefined; this.outsideReference = null; this.loudness = null; this.label = null; this.preTest = null; this.postTest = null; this.interfaces = []; this.commentBoxPrefix = "Comment on track"; this.audioElements = []; this.commentQuestions = []; this.schema = specification.schema.getAllElementsByName("page")[0]; this.specification = specification; this.parent = null; this.decode = function(parent,xml) { this.parent = parent; var attributeMap = this.schema.getAllElementsByTagName('xs:attribute'); for (var i=0; i<attributeMap.length; i++) { var attributeName = attributeMap[i].getAttribute('name') || attributeMap[i].getAttribute('ref'); var projectAttr = xml.getAttribute(attributeName); projectAttr = parent.processAttribute(projectAttr,attributeMap[i],parent.schema); switch(typeof projectAttr) { case "number": case "boolean": eval('this.'+attributeName+' = '+projectAttr); break; case "string": eval('this.'+attributeName+' = "'+projectAttr+'"'); break; } } // Get the Comment Box Prefix var CBP = xml.getElementsByTagName('commentboxprefix'); if (CBP.length != 0) { this.commentBoxPrefix = CBP[0].textContent; } // Now decode the interfaces var interfaceNode = xml.getElementsByTagName('interface'); for (var i=0; i<interfaceNode.length; i++) { var node = new parent.interfaceNode(this.specification); node.decode(this,interfaceNode[i],parent.schema.getAllElementsByName('interface')[1]); this.interfaces.push(node); } // Now process the survey node options var survey = xml.getElementsByTagName('survey'); var surveySchema = parent.schema.getAllElementsByName('survey')[0]; for (var i in survey) { if (isNaN(Number(i)) == true){break;} var location = survey[i].getAttribute('location'); if (location == 'pre' || location == 'before') { if (this.preTest != null){this.errors.push("Already a pre/before test survey defined! Ignoring second!!");} else { this.preTest = new parent.surveyNode(this.specification); this.preTest.decode(parent,survey[i],surveySchema); } } else if (location == 'post' || location == 'after') { if (this.postTest != null){this.errors.push("Already a post/after test survey defined! Ignoring second!!");} else { this.postTest = new parent.surveyNode(this.specification); this.postTest.decode(parent,survey[i],surveySchema); } } } // Now process the audioelement tags var audioElements = xml.getElementsByTagName('audioelement'); for (var i=0; i<audioElements.length; i++) { var node = new this.audioElementNode(this.specification); node.decode(this,audioElements[i]); this.audioElements.push(node); } // Now decode the commentquestions var commentQuestions = xml.getElementsByTagName('commentquestion'); for (var i=0; i<commentQuestions.length; i++) { var node = new this.commentQuestionNode(this.specification); node.decode(parent,commentQuestions[i]); this.commentQuestions.push(node); } }; this.encode = function(root) { var AHNode = root.createElement("page"); // First decode the attributes var attributes = this.schema.getAllElementsByTagName('xs:attribute'); for (var i=0; i<attributes.length; i++) { var name = attributes[i].getAttribute("name"); if (name == undefined) { name = attributes[i].getAttribute("ref"); } if(eval("this."+name+" != undefined") || attributes[i].getAttribute("use") == "required") { eval("AHNode.setAttribute('"+name+"',this."+name+")"); } } if(this.loudness != null) {AHNode.setAttribute("loudness",this.loudness);} // <commentboxprefix> var commentboxprefix = root.createElement("commentboxprefix"); commentboxprefix.textContent = this.commentBoxPrefix; AHNode.appendChild(commentboxprefix); for (var i=0; i<this.interfaces.length; i++) { AHNode.appendChild(this.interfaces[i].encode(root)); } for (var i=0; i<this.audioElements.length; i++) { AHNode.appendChild(this.audioElements[i].encode(root)); } // Create <CommentQuestion> for (var i=0; i<this.commentQuestions.length; i++) { AHNode.appendChild(this.commentQuestions[i].encode(root)); } AHNode.appendChild(this.preTest.encode(root)); AHNode.appendChild(this.postTest.encode(root)); return AHNode; }; this.commentQuestionNode = function(specification) { this.id = null; this.name = undefined; this.type = undefined; this.options = []; this.statement = undefined; this.schema = specification.schema.getAllElementsByName('commentquestion')[0]; this.decode = function(parent,xml) { this.id = xml.id; this.name = xml.getAttribute('name'); this.type = xml.getAttribute('type'); this.statement = xml.getElementsByTagName('statement')[0].textContent; var optNodes = xml.getElementsByTagName('option'); for (var i=0; i<optNodes.length; i++) { var optNode = optNodes[i]; this.options.push({ name: optNode.getAttribute('name'), text: optNode.textContent }); } }; this.encode = function(root) { var node = root.createElement("commentquestion"); node.id = this.id; node.setAttribute("type",this.type); if (this.name != undefined){node.setAttribute("name",this.name);} var statement = root.createElement("statement"); statement.textContent = this.statement; node.appendChild(statement); for (var option of this.options) { var child = root.createElement("option"); child.setAttribute("name",option.name); child.textContent = option.text; node.appendChild(child); } return node; }; }; this.audioElementNode = function(specification) { this.url = null; this.id = null; this.name = null; this.parent = null; this.type = null; this.marker = null; this.enforce = false; this.gain = 0.0; this.schema = specification.schema.getAllElementsByName('audioelement')[0];; this.parent = null; this.decode = function(parent,xml) { this.parent = parent; var attributeMap = this.schema.getAllElementsByTagName('xs:attribute'); for (var i=0; i<attributeMap.length; i++) { var attributeName = attributeMap[i].getAttribute('name') || attributeMap[i].getAttribute('ref'); var projectAttr = xml.getAttribute(attributeName); projectAttr = parent.parent.processAttribute(projectAttr,attributeMap[i],parent.parent.schema); switch(typeof projectAttr) { case "number": case "boolean": eval('this.'+attributeName+' = '+projectAttr); break; case "string": eval('this.'+attributeName+' = "'+projectAttr+'"'); break; } } }; this.encode = function(root) { var AENode = root.createElement("audioelement"); var attributes = this.schema.getAllElementsByTagName('xs:attribute'); for (var i=0; i<attributes.length; i++) { var name = attributes[i].getAttribute("name"); if (name == undefined) { name = attributes[i].getAttribute("ref"); } if(eval("this."+name+" != undefined") || attributes[i].getAttribute("use") == "required") { eval("AENode.setAttribute('"+name+"',this."+name+")"); } } return AENode; }; }; }; }