Mercurial > hg > webaudioevaluationtool
diff core.js @ 654:37f3359709bd
Merge
author | Nicholas Jillings <n.g.r.jillings@se14.qmul.ac.uk> |
---|---|
date | Thu, 31 Mar 2016 15:48:57 +0100 |
parents | ce3d4d6d01b8 |
children |
line wrap: on
line diff
--- a/core.js Thu Mar 10 17:07:46 2016 +0000 +++ b/core.js Thu Mar 31 15:48:57 2016 +0100 @@ -28,6 +28,16 @@ // Add a prototype to the bufferNode to hold the computed LUFS loudness AudioBuffer.prototype.lufs = undefined; +// Convert relative URLs into absolutes +function escapeHTML(s) { + return s.split('&').join('&').split('<').join('<').split('"').join('"'); +} +function qualifyURL(url) { + var el= document.createElement('div'); + el.innerHTML= '<a href="'+escapeHTML(url)+'">x</a>'; + return el.firstChild.href; +} + // 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 @@ -182,6 +192,16 @@ document.getElementsByTagName('body')[0].appendChild(errorNode[0]); return; } + if (responseDocument == undefined) { + var msg = document.createElement("h3"); + msg.textContent = "FATAL ERROR"; + var span = document.createElement("span"); + span.textContent = "The project XML was not decoded properly, try refreshing your browser and clearing caches. If the problem persists, contact the test creator."; + document.getElementsByTagName('body')[0].innerHTML = null; + document.getElementsByTagName('body')[0].appendChild(msg); + document.getElementsByTagName('body')[0].appendChild(span); + return; + } if (responseDocument.children[0].nodeName == "waet") { // document is a specification @@ -281,69 +301,82 @@ switch(specification.interface) { case "APE": - interfaceJS.setAttribute("src","interfaces/ape.js"); - - // APE comes with a css file - var css = document.createElement('link'); - css.rel = 'stylesheet'; - css.type = 'text/css'; - css.href = 'interfaces/ape.css'; - - document.getElementsByTagName("head")[0].appendChild(css); - break; - + interfaceJS.setAttribute("src","interfaces/ape.js"); + + // APE comes with a css file + var css = document.createElement('link'); + css.rel = 'stylesheet'; + css.type = 'text/css'; + css.href = 'interfaces/ape.css'; + + document.getElementsByTagName("head")[0].appendChild(css); + break; + case "MUSHRA": - interfaceJS.setAttribute("src","interfaces/mushra.js"); - - // MUSHRA comes with a css file - var css = document.createElement('link'); - css.rel = 'stylesheet'; - css.type = 'text/css'; - css.href = 'interfaces/mushra.css'; - - document.getElementsByTagName("head")[0].appendChild(css); - break; + interfaceJS.setAttribute("src","interfaces/mushra.js"); + + // MUSHRA comes with a css file + var css = document.createElement('link'); + css.rel = 'stylesheet'; + css.type = 'text/css'; + css.href = 'interfaces/mushra.css'; + + document.getElementsByTagName("head")[0].appendChild(css); + break; case "AB": - interfaceJS.setAttribute("src","interfaces/AB.js"); - - // AB comes with a css file - var css = document.createElement('link'); - css.rel = 'stylesheet'; - css.type = 'text/css'; - css.href = 'interfaces/AB.css'; - - document.getElementsByTagName("head")[0].appendChild(css); - break; + interfaceJS.setAttribute("src","interfaces/AB.js"); + + // AB comes with a css file + var css = document.createElement('link'); + css.rel = 'stylesheet'; + css.type = 'text/css'; + css.href = 'interfaces/AB.css'; + + document.getElementsByTagName("head")[0].appendChild(css); + break; + + case "ABX": + interfaceJS.setAttribute("src","interfaces/ABX.js"); + + // AB comes with a css file + var css = document.createElement('link'); + css.rel = 'stylesheet'; + css.type = 'text/css'; + css.href = 'interfaces/ABX.css'; + + document.getElementsByTagName("head")[0].appendChild(css); + break; + case "Bipolar": case "ACR": case "DCR": case "CCR": case "ABC": - // Above enumerate to horizontal sliders - interfaceJS.setAttribute("src","interfaces/horizontal-sliders.js"); - - // horizontal-sliders comes with a css file - var css = document.createElement('link'); - css.rel = 'stylesheet'; - css.type = 'text/css'; - css.href = 'interfaces/horizontal-sliders.css'; - - document.getElementsByTagName("head")[0].appendChild(css); - break; + // Above enumerate to horizontal sliders + interfaceJS.setAttribute("src","interfaces/horizontal-sliders.js"); + + // horizontal-sliders comes with a css file + var css = document.createElement('link'); + css.rel = 'stylesheet'; + css.type = 'text/css'; + css.href = 'interfaces/horizontal-sliders.css'; + + document.getElementsByTagName("head")[0].appendChild(css); + break; case "discrete": case "likert": - // Above enumerate to horizontal discrete radios - interfaceJS.setAttribute("src","interfaces/discrete.js"); - - // horizontal-sliders comes with a css file - var css = document.createElement('link'); - css.rel = 'stylesheet'; - css.type = 'text/css'; - css.href = 'interfaces/discrete.css'; - - document.getElementsByTagName("head")[0].appendChild(css); - break; + // Above enumerate to horizontal discrete radios + interfaceJS.setAttribute("src","interfaces/discrete.js"); + + // horizontal-sliders comes with a css file + var css = document.createElement('link'); + css.rel = 'stylesheet'; + css.type = 'text/css'; + css.href = 'interfaces/discrete.css'; + + document.getElementsByTagName("head")[0].appendChild(css); + break; } document.getElementsByTagName("head")[0].appendChild(interfaceJS); @@ -352,6 +385,8 @@ } function createProjectSave(destURL) { + // Clear the window.onbeforeunload + window.onbeforeunload = null; // Save the data from interface into XML and send to destURL // If destURL is null then download XML in client // Now time to render file locally @@ -391,7 +426,7 @@ if (response.getAttribute("state") == "OK") { var file = response.getElementsByTagName("file")[0]; console.log("Save: OK, written "+file.getAttribute("bytes")+"B"); - popup.popupContent.textContent = "Thank you. Your session has been saved."; + popup.popupContent.textContent = specification.exitText; } else { var message = response.getElementsByTagName("message"); console.log("Save: Error! "+message.textContent); @@ -517,6 +552,7 @@ var blank = document.getElementsByClassName('testHalt')[0]; blank.style.zIndex = 2; blank.style.visibility = 'visible'; + this.popupResponse.style.left="0%"; }; this.hidePopup = function(){ @@ -576,7 +612,7 @@ span.textContent = option.text; var hold = document.createElement('div'); hold.setAttribute('name','option'); - hold.style.padding = '4px'; + hold.className = "popup-option-checbox"; hold.appendChild(input); hold.appendChild(span); this.popupResponse.appendChild(hold); @@ -585,14 +621,13 @@ input.checked = "true"; } } - var w = $(span).width(); + var w = $(hold).width(); if (w > max_w) max_w = w; index++; } - max_w += 12; this.popupResponse.style.textAlign=""; - var leftP = ((max_w/500)/2)*100; + var leftP = 50-(((max_w/$('#popupContent').width())/2)*100); this.popupResponse.style.left=leftP+"%"; } else if (node.specification.type == 'radio') { if (node.response == undefined) { @@ -609,20 +644,19 @@ span.textContent = option.text; var hold = document.createElement('div'); hold.setAttribute('name','option'); - hold.style.padding = '4px'; + hold.className = "popup-option-checbox"; hold.appendChild(input); hold.appendChild(span); this.popupResponse.appendChild(hold); if (input.id == node.response.name) { input.checked = "true"; } - var w = $(span).width(); + var w = $(hold).width(); if (w > max_w) max_w = w; } - max_w += 12; this.popupResponse.style.textAlign=""; - var leftP = ((max_w/500)/2)*100; + var leftP = 50-(((max_w/$('#popupContent').width())/2)*100); this.popupResponse.style.left=leftP+"%"; } else if (node.specification.type == 'number') { var input = document.createElement('input'); @@ -676,6 +710,11 @@ this.proceedClicked = function() { // Each time the popup button is clicked! + if (testState.stateIndex == 0 && specification.calibration) { + interfaceContext.calibrationModuleObject.collect(); + advanceState(); + return; + } var node = this.popupOptions[this.currentIndex]; if (node.specification.type == 'question') { // Must extract the question data @@ -829,11 +868,8 @@ } for (var i=0; i<pageHolder.length; i++) { - pageHolder[i].presentedId = i; - } - for (var i=0; i<specification.pages.length; i++) - { if (specification.testPages <= i && specification.testPages != 0) {break;} + pageHolder[i].presentedId = i; this.stateMap.push(pageHolder[i]); storage.createTestPageStore(pageHolder[i]); for (var element of pageHolder[i].audioElements) { @@ -860,7 +896,8 @@ if(this.stateIndex != null) { console.log('NOTE - State already initialise'); } - this.stateIndex = -1; + this.stateIndex = -2; + console.log('Starting test...'); } else { console.log('FATAL - StateMap not correctly constructed. EMPTY_STATE_MAP'); } @@ -870,16 +907,27 @@ this.initialise(); } storage.update(); - if (this.stateIndex == -1) { - this.stateIndex++; - console.log('Starting test...'); + if (this.stateIndex == -2) { + this.stateIndex++; if (this.preTestSurvey != null) { popup.initState(this.preTestSurvey,storage.globalPreTest); } else { this.advanceState(); } - } else if (this.stateIndex == this.stateMap.length) + } else if (this.stateIndex == -1) { + this.stateIndex++; + if (specification.calibration) { + popup.showPopup(); + popup.popupTitle.textContent = "Calibration. Set the levels so all tones are of equal amplitude. Move your mouse over the sliders to hear the tones. The red slider is the reference tone"; + interfaceContext.calibrationModuleObject = new interfaceContext.calibrationModule(); + interfaceContext.calibrationModuleObject.build(popup.popupResponse); + popup.hidePreviousButton(); + } else { + this.advanceState(); + } + } + else if (this.stateIndex == this.stateMap.length) { // All test pages complete, post test console.log('Ending test ...'); @@ -895,6 +943,7 @@ } else { + popup.hidePopup(); if (this.currentStateMap == null) { this.currentStateMap = this.stateMap[this.stateIndex]; @@ -1742,667 +1791,6 @@ console.log(outputSequence.toString()); // print randomised array to console return holdArr; } - -function returnDateNode() -{ - // Create an XML Node for the Date and Time a test was conducted - // Structure is - // <datetime> - // <date year="##" month="##" day="##">DD/MM/YY</date> - // <time hour="##" minute="##" sec="##">HH:MM:SS</time> - // </datetime> - var dateTime = new Date(); - var year = document.createAttribute('year'); - var month = document.createAttribute('month'); - var day = document.createAttribute('day'); - var hour = document.createAttribute('hour'); - var minute = document.createAttribute('minute'); - var secs = document.createAttribute('secs'); - - year.nodeValue = dateTime.getFullYear(); - month.nodeValue = dateTime.getMonth()+1; - day.nodeValue = dateTime.getDate(); - hour.nodeValue = dateTime.getHours(); - minute.nodeValue = dateTime.getMinutes(); - secs.nodeValue = dateTime.getSeconds(); - - var hold = document.createElement("datetime"); - var date = document.createElement("date"); - date.textContent = year.nodeValue+'/'+month.nodeValue+'/'+day.nodeValue; - var time = document.createElement("time"); - time.textContent = hour.nodeValue+':'+minute.nodeValue+':'+secs.nodeValue; - - date.setAttributeNode(year); - date.setAttributeNode(month); - date.setAttributeNode(day); - time.setAttributeNode(hour); - time.setAttributeNode(minute); - time.setAttributeNode(secs); - - hold.appendChild(date); - hold.appendChild(time); - return hold; - -} - -function Specification() { - // Handles the decoding of the project specification XML into a simple JavaScript Object. - - this.interface = null; - this.projectReturn = "null"; - this.randomiseOrder = null; - this.testPages = null; - this.pages = []; - this.metrics = null; - this.interfaces = null; - this.loudness = null; - this.errors = []; - this.schema = null; - - this.processAttribute = function(attribute,schema) - { - // 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 = this.schema.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 {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'); - var projectAttr = setupNode.getAttribute(attributeName); - projectAttr = this.processAttribute(projectAttr,attributes[i]); - switch(typeof projectAttr) - { - case "number": - case "boolean": - eval('this.'+attributeName+' = '+projectAttr); - break; - case "string": - eval('this.'+attributeName+' = "'+projectAttr+'"'); - break; - } - - } - - 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'); - 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 this.surveyNode(); - this.preTest.decode(this,survey[i]); - } - } 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 this.surveyNode(); - this.postTest.decode(this,survey[i]); - } - } - } - - 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(); - 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(); - 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 - 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() { - this.location = null; - this.options = []; - this.schema = specification.schema.getAllElementsByName('survey')[0]; - - this.OptionNode = function() { - 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]); - 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); - if (this.type != "statement") { - node.id = this.id; - if (this.name != undefined) { node.setAttribute("name",this.name);} - if (this.mandatory != undefined) { node.setAttribute("mandatory",this.mandatory);} - switch(this.type) - { - case "question": - if (this.boxsize != undefined) {node.setAttribute("boxsize",this.boxsize);} - break; - case "number": - if (this.min != undefined) {node.setAttribute("min", this.min);} - if (this.max != undefined) {node.setAttribute("max", this.max);} - break; - 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); - } - break; - } - } - return node; - }; - }; - this.decode = function(parent,xml) { - this.location = xml.getAttribute('location'); - if (this.location == 'before'){this.location = 'pre';} - else if (this.location == 'after'){this.location = 'post';} - for (var i in xml.children) - { - if(isNaN(Number(i))==true){break;} - var node = new this.OptionNode(); - node.decode(parent,xml.children[i]); - this.options.push(node); - } - }; - 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() - { - 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); - projectAttr = parent.processAttribute(projectAttr,attributeMap[j]); - 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]; - for (var i=0; i<scaleParent.children.length; i++) { - var child = scaleParent.children[i]; - this.scales.push({ - text: child.textContent, - position: Number(child.getAttribute('position')) - }); - } - } - }; - - this.encode = function(doc) { - var node = doc.createElement("interface"); - if (typeof name == "string") - node.setAttribute("name",this.name); - 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() { - 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.decode = function(parent,xml) - { - 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]); - 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(); - 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.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.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(); - 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(); - 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() { - 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() { - 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 = specification.processAttribute(projectAttr,attributeMap[i]); - 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; - }; - }; - }; -} function Interface(specificationObject) { // This handles the bindings between the interface and the audioEngineContext; @@ -2459,6 +1847,31 @@ node.appendChild(screen); return node; }; + + this.returnDateNode = function() + { + // Create an XML Node for the Date and Time a test was conducted + // Structure is + // <datetime> + // <date year="##" month="##" day="##">DD/MM/YY</date> + // <time hour="##" minute="##" sec="##">HH:MM:SS</time> + // </datetime> + var dateTime = new Date(); + var hold = storage.document.createElement("datetime"); + var date = storage.document.createElement("date"); + var time = storage.document.createElement("time"); + date.setAttribute('year',dateTime.getFullYear()); + date.setAttribute('month',dateTime.getMonth()+1); + date.setAttribute('day',dateTime.getDate()); + time.setAttribute('hour',dateTime.getHours()); + time.setAttribute('minute',dateTime.getMinutes()); + time.setAttribute('secs',dateTime.getSeconds()); + + hold.appendChild(date); + hold.appendChild(time); + return hold; + + } this.commentBoxes = new function() { this.boxes = []; @@ -2828,6 +2241,74 @@ { this.commentQuestions = []; }; + + this.outsideReferenceDOM = function(audioObject,index,inject) + { + this.parent = audioObject; + this.outsideReferenceHolder = document.createElement('button'); + this.outsideReferenceHolder.id = 'outside-reference'; + this.outsideReferenceHolder.className = 'outside-reference'; + this.outsideReferenceHolder.setAttribute('track-id',index); + this.outsideReferenceHolder.textContent = "Play Reference"; + this.outsideReferenceHolder.disabled = true; + + this.outsideReferenceHolder.onclick = function(event) + { + audioEngineContext.play(event.currentTarget.getAttribute('track-id')); + }; + inject.appendChild(this.outsideReferenceHolder); + this.enable = function() + { + if (this.parent.state == 1) + { + this.outsideReferenceHolder.disabled = false; + } + }; + this.updateLoading = function(progress) + { + if (progress != 100) + { + progress = String(progress); + progress = progress.split('.')[0]; + this.outsideReferenceHolder.textContent = progress+'%'; + } else { + this.outsideReferenceHolder.textContent = "Play Reference"; + } + }; + this.startPlayback = function() + { + // Called when playback has begun + $('.track-slider').removeClass('track-slider-playing'); + $('.comment-div').removeClass('comment-box-playing'); + this.outsideReferenceHolder.style.backgroundColor = "#FDD"; + }; + this.stopPlayback = function() + { + // Called when playback has stopped. This gets called even if playback never started! + this.outsideReferenceHolder.style.backgroundColor = ""; + }; + this.exportXMLDOM = function(audioObject) + { + return null; + }; + this.getValue = function() + { + return 0; + }; + this.getPresentedId = function() + { + return 'Reference'; + }; + this.canMove = function() + { + return false; + }; + this.error = function() { + // audioObject has an error!! + this.outsideReferenceHolder.textContent = "Error"; + this.outsideReferenceHolder.style.backgroundColor = "#F00"; + } + } this.playhead = new function() { @@ -2975,6 +2456,95 @@ this.object.appendChild(this.slider); this.object.appendChild(this.valueText); } + + this.calibrationModuleObject = null; + this.calibrationModule = function() { + // This creates an on-page calibration module + this.storeDOM = storage.document.createElement("calibration"); + storage.root.appendChild(this.storeDOM); + // The calibration is a fixed state module + this.calibrationNodes = []; + this.holder = null; + this.build = function(inject) { + var f0 = 62.5; + this.holder = document.createElement("div"); + this.holder.className = "calibration-holder"; + this.calibrationNodes = []; + while(f0 < 20000) { + var obj = { + root: document.createElement("div"), + input: document.createElement("input"), + oscillator: audioContext.createOscillator(), + gain: audioContext.createGain(), + f: f0, + parent: this, + handleEvent: function(event) { + switch(event.type) { + case "mouseenter": + this.oscillator.start(0); + break; + case "mouseleave": + this.oscillator.stop(0); + this.oscillator = audioContext.createOscillator(); + this.oscillator.connect(this.gain); + this.oscillator.frequency.value = this.f; + break; + case "mousemove": + var value = Math.pow(10,this.input.value/20); + if (this.f == 1000) { + audioEngineContext.outputGain.gain.value = value; + interfaceContext.volume.slider.value = this.input.value; + } else { + this.gain.gain.value = value + } + break; + } + }, + disconnect: function() { + this.gain.disconnect(); + } + } + obj.root.className = "calibration-slider"; + obj.root.appendChild(obj.input); + obj.oscillator.connect(obj.gain); + obj.gain.connect(audioEngineContext.outputGain); + obj.gain.gain.value = Math.random()*2; + 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.step = 0.25; + if (f0 != 1000) { + obj.input.value = (Math.random()*12)-6; + } else { + obj.input.value = 0; + obj.root.style.backgroundColor="rgb(255,125,125)"; + } + obj.input.addEventListener("mousemove",obj); + obj.input.addEventListener("mouseenter",obj); + obj.input.addEventListener("mouseleave",obj); + obj.gain.gain.value = Math.pow(10,obj.input.value/20); + obj.oscillator.frequency.value = f0; + this.calibrationNodes.push(obj); + this.holder.appendChild(obj.root); + f0 *= 2; + } + inject.appendChild(this.holder); + } + this.collect = function() { + for (var obj of this.calibrationNodes) { + var node = storage.document.createElement("calibrationresult"); + node.setAttribute("frequency",obj.f); + node.setAttribute("range-min",obj.input.min); + node.setAttribute("range-max",obj.input.max); + node.setAttribute("gain-lin",obj.gain.gain.value); + this.storeDOM.appendChild(node); + } + } + } + + // Global Checkers // These functions will help enforce the checkers this.checkHiddenAnchor = function() @@ -3158,8 +2728,9 @@ this.root = this.document.childNodes[0]; var projectDocument = specification.projectXML; projectDocument.setAttribute('file-name',url); + projectDocument.setAttribute('url',qualifyURL(url)); this.root.appendChild(projectDocument); - this.root.appendChild(returnDateNode()); + this.root.appendChild(interfaceContext.returnDateNode()); this.root.appendChild(interfaceContext.returnNavigator()); } else { this.document = existingStore; @@ -3318,6 +2889,7 @@ if (element.name != undefined){aeNode.setAttribute('name',element.name)}; aeNode.setAttribute('type',element.type); aeNode.setAttribute('url', element.url); + aeNode.setAttribute('fqurl',qualifyURL(element.url)); aeNode.setAttribute('gain', element.gain); if (element.type == 'anchor' || element.type == 'reference') {