Dactyloidae/mailnews/base/util/JXON.js

180 lines
6.1 KiB
JavaScript

/* -*- 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;
};
})();