diff options
Diffstat (limited to 'content/includes/xmltools.js')
-rw-r--r-- | content/includes/xmltools.js | 155 |
1 files changed, 155 insertions, 0 deletions
diff --git a/content/includes/xmltools.js b/content/includes/xmltools.js new file mode 100644 index 0000000..68b69d0 --- /dev/null +++ b/content/includes/xmltools.js @@ -0,0 +1,155 @@ +/* + * This file is part of EAS-4-TbSync. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + + "use strict"; + +var xmltools = { + + isString : function (obj) { + return (Object.prototype.toString.call(obj) === '[object String]'); + }, + + checkString : function(d, fallback = "") { + if (this.isString(d)) return d; + else return fallback; + }, + + nodeAsArray : function (node) { + let a = []; + if (node) { + //return, if already an array + if (node instanceof Array) return node; + + //else push node into an array + a.push(node); + } + return a; + }, + + hasWbxmlDataField: function(wbxmlData, path) { + if (wbxmlData) { + let pathElements = path.split("."); + let data = wbxmlData; + for (let x = 0; x < pathElements.length; x++) { + if (data[pathElements[x]]) data = data[pathElements[x]]; + else return false; + } + return true; + } + return false; + }, + + getWbxmlDataField: function(wbxmlData,path) { + if (wbxmlData) { + let pathElements = path.split("."); + let data = wbxmlData; + let valid = true; + for (let x = 0; valid && x < pathElements.length; x++) { + if (data[pathElements[x]]) data = data[pathElements[x]]; + else valid = false; + } + if (valid) return data; + } + return false + }, + + //print content of xml data object (if debug output enabled) + printXmlData : function (data, printApplicationData) { + if (TbSync.prefs.getIntPref("log.userdatalevel") > 1 || (TbSync.prefs.getIntPref("log.userdatalevel") == 1 && printApplicationData)) { + let dump = JSON.stringify(data); + TbSync.dump("Extracted XML data", "\n" + dump); + } + }, + + getDataFromXMLString: function (str) { + let data = null; + let xml = ""; + if (str == "") return data; + + let oParser = (Services.vc.compare(Services.appinfo.platformVersion, "61.*") >= 0) ? new DOMParser() : Components.classes["@mozilla.org/xmlextras/domparser;1"].createInstance(Components.interfaces.nsIDOMParser); + try { + xml = oParser.parseFromString(str, "application/xml"); + } catch (e) { + //however, domparser does not throw an error, it returns an error document + //https://developer.mozilla.org/de/docs/Web/API/DOMParser + //just in case + throw eas.sync.finish("error", "malformed-xml"); + } + + //check if xml is error document + if (xml.documentElement.nodeName == "parsererror") { + TbSync.dump("BAD XML", "The above XML and WBXML could not be parsed correctly, something is wrong."); + throw eas.sync.finish("error", "malformed-xml"); + } + + try { + data = this.getDataFromXML(xml); + } catch (e) { + throw eas.sync.finish("error", "mailformed-data"); + } + + return data; + }, + + //create data object from XML node + getDataFromXML : function (nodes) { + + /* + * The passed nodes value could be an entire document in a single node (type 9) or a + * single element node (type 1) as returned by getElementById. It could however also + * be an array of nodes as returned by getElementsByTagName or a nodeList as returned + * by childNodes. In that case node.length is defined. + */ + + // create the return object + let obj = {}; + let nodeList = []; + let multiplicity = {}; + + if (nodes.length === undefined) nodeList.push(nodes); + else nodeList = nodes; + + // nodelist contains all childs, if two childs have the same name, we cannot add the chils as an object, but as an array of objects + for (let node of nodeList) { + if (node.nodeType == 1 || node.nodeType == 3) { + if (!multiplicity.hasOwnProperty(node.nodeName)) multiplicity[node.nodeName] = 0; + multiplicity[node.nodeName]++; + //if this nodeName has multiplicity > 1, prepare obj (but only once) + if (multiplicity[node.nodeName]==2) obj[node.nodeName] = []; + } + } + + // process nodes + for (let node of nodeList) { + switch (node.nodeType) { + case 9: + //document node, dive directly and process all children + if (node.hasChildNodes) obj = this.getDataFromXML(node.childNodes); + break; + case 1: + //element node + if (node.hasChildNodes) { + //if this is an element with only one text child, do not dive, but get text childs value + let o; + if (node.childNodes.length == 1 && node.childNodes.item(0).nodeType==3) { + //the passed xml is a save xml with all special chars in the user data encoded by encodeURIComponent + o = decodeURIComponent(node.childNodes.item(0).nodeValue); + } else { + o = this.getDataFromXML(node.childNodes); + } + //check, if we can add the object directly, or if we have to push it into an array + if (multiplicity[node.nodeName]>1) obj[node.nodeName].push(o) + else obj[node.nodeName] = o; + } + break; + } + } + return obj; + } + +}; |