diff options
Diffstat (limited to 'mailnews/base/util/JXON.js')
-rw-r--r-- | mailnews/base/util/JXON.js | 180 |
1 files changed, 180 insertions, 0 deletions
diff --git a/mailnews/base/util/JXON.js b/mailnews/base/util/JXON.js new file mode 100644 index 000000000..509c9b5d8 --- /dev/null +++ b/mailnews/base/util/JXON.js @@ -0,0 +1,180 @@ +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// This is a modification of the JXON parsers found on the page +// <https://developer.mozilla.org/en-US/docs/JXON> + +var EXPORTED_SYMBOLS = ["JXON"]; + +var JXON = new (function() { + const sValueProp = "value"; /* you can customize these values */ + const sAttributesProp = "attr"; + const sAttrPref = "@"; + const sElementListPrefix = "$"; + const sConflictSuffix = "_"; // used when there's a name conflict with special JXON properties + const aCache = []; + const rIsNull = /^\s*$/; + const rIsBool = /^(?:true|false)$/i; + + function parseText(sValue) { + //if (rIsNull.test(sValue)) + // return null; + if (rIsBool.test(sValue)) + return sValue.toLowerCase() === "true"; + if (isFinite(sValue)) + return parseFloat(sValue); + if (isFinite(Date.parse(sValue))) + return new Date(sValue); + return sValue; + }; + + function EmptyTree() { + } + EmptyTree.prototype = { + toString : function () { + return "null"; + }, + valueOf : function () { + return null; + }, + }; + + function objectify(vValue) { + if (vValue === null) + return new EmptyTree(); + else if (vValue instanceof Object) + return vValue; + else + return new vValue.constructor(vValue); // What does this? copy? + }; + + function createObjTree(oParentNode, nVerb, bFreeze, bNesteAttr) { + const nLevelStart = aCache.length; + const bChildren = oParentNode.hasChildNodes(); + const bAttributes = oParentNode.attributes && + oParentNode.attributes.length; + const bHighVerb = Boolean(nVerb & 2); + + var sProp = 0; + var vContent = 0; + var nLength = 0; + var sCollectedTxt = ""; + var vResult = bHighVerb ? {} : /* put here the default value for empty nodes: */ true; + + if (bChildren) { + for (var oNode, nItem = 0; nItem < oParentNode.childNodes.length; nItem++) { + oNode = oParentNode.childNodes.item(nItem); + if (oNode.nodeType === 4) // CDATASection + sCollectedTxt += oNode.nodeValue; + else if (oNode.nodeType === 3) // Text + sCollectedTxt += oNode.nodeValue; + else if (oNode.nodeType === 1) // Element + aCache.push(oNode); + } + } + + const nLevelEnd = aCache.length; + const vBuiltVal = parseText(sCollectedTxt); + + if (!bHighVerb && (bChildren || bAttributes)) + vResult = nVerb === 0 ? objectify(vBuiltVal) : {}; + + for (var nElId = nLevelStart; nElId < nLevelEnd; nElId++) { + sProp = aCache[nElId].nodeName; + if (sProp == sValueProp || sProp == sAttributesProp) + sProp = sProp + sConflictSuffix; + vContent = createObjTree(aCache[nElId], nVerb, bFreeze, bNesteAttr); + if (!vResult.hasOwnProperty(sProp)) { + vResult[sProp] = vContent; + vResult[sElementListPrefix + sProp] = []; + } + vResult[sElementListPrefix + sProp].push(vContent); + nLength++; + } + + if (bAttributes) { + const nAttrLen = oParentNode.attributes.length; + const sAPrefix = bNesteAttr ? "" : sAttrPref; + const oAttrParent = bNesteAttr ? {} : vResult; + + for (var oAttrib, nAttrib = 0; nAttrib < nAttrLen; nLength++, nAttrib++) { + oAttrib = oParentNode.attributes.item(nAttrib); + oAttrParent[sAPrefix + oAttrib.name] = parseText(oAttrib.value); + } + + if (bNesteAttr) { + if (bFreeze) + Object.freeze(oAttrParent); + vResult[sAttributesProp] = oAttrParent; + nLength -= nAttrLen - 1; + } + } + + if (nVerb === 3 || (nVerb === 2 || nVerb === 1 && nLength > 0) && sCollectedTxt) + vResult[sValueProp] = vBuiltVal; + else if (!bHighVerb && nLength === 0 && sCollectedTxt) + vResult = vBuiltVal; + + if (bFreeze && (bHighVerb || nLength > 0)) + Object.freeze(vResult); + + aCache.length = nLevelStart; + + return vResult; + }; + + function loadObjTree(oXMLDoc, oParentEl, oParentObj) { + var vValue, oChild; + + if (oParentObj instanceof String || oParentObj instanceof Number || + oParentObj instanceof Boolean) + oParentEl.appendChild(oXMLDoc.createTextNode(oParentObj.toString())); /* verbosity level is 0 */ + else if (oParentObj.constructor === Date) + oParentEl.appendChild(oXMLDoc.createTextNode(oParentObj.toGMTString())); + + for (var sName in oParentObj) { + vValue = oParentObj[sName]; + if (isFinite(sName) || vValue instanceof Function) + continue; /* verbosity level is 0 */ + if (sName === sValueProp) { + if (vValue !== null && vValue !== true) { + oParentEl.appendChild(oXMLDoc.createTextNode( + vValue.constructor === Date ? vValue.toGMTString() : String(vValue))); + } + } else if (sName === sAttributesProp) { /* verbosity level is 3 */ + for (var sAttrib in vValue) + oParentEl.setAttribute(sAttrib, vValue[sAttrib]); + } else if (sName.charAt(0) === sAttrPref) { + oParentEl.setAttribute(sName.slice(1), vValue); + } else if (vValue.constructor === Array) { + for (var nItem = 0; nItem < vValue.length; nItem++) { + oChild = oXMLDoc.createElement(sName); + loadObjTree(oXMLDoc, oChild, vValue[nItem]); + oParentEl.appendChild(oChild); + } + } else { + oChild = oXMLDoc.createElement(sName); + if (vValue instanceof Object) + loadObjTree(oXMLDoc, oChild, vValue); + else if (vValue !== null && vValue !== true) + oChild.appendChild(oXMLDoc.createTextNode(vValue.toString())); + oParentEl.appendChild(oChild); + } + } + }; + + this.build = function(oXMLParent, nVerbosity /* optional */, bFreeze /* optional */, bNesteAttributes /* optional */) { + const _nVerb = arguments.length > 1 && + typeof nVerbosity === "number" ? nVerbosity & 3 : + /* put here the default verbosity level: */ 1; + return createObjTree(oXMLParent, _nVerb, bFreeze || false, + arguments.length > 3 ? bNesteAttributes : _nVerb === 3); + }; + + this.unbuild = function(oObjTree) { + const oNewDoc = document.implementation.createDocument("", "", null); + loadObjTree(oNewDoc, oNewDoc, oObjTree); + return oNewDoc; + }; +})(); |