From d333e9fdb440441849bcb23638c47565c76b6cde Mon Sep 17 00:00:00 2001 From: sbosse Date: Mon, 21 Jul 2025 22:59:26 +0200 Subject: [PATCH] Mon 21 Jul 22:43:21 CEST 2025 --- js/dos/ext/xmldoc.js | 235 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 235 insertions(+) create mode 100644 js/dos/ext/xmldoc.js diff --git a/js/dos/ext/xmldoc.js b/js/dos/ext/xmldoc.js new file mode 100644 index 0000000..b7df854 --- /dev/null +++ b/js/dos/ext/xmldoc.js @@ -0,0 +1,235 @@ +(function () { + +var sax; + +if (typeof module !== 'undefined' && module.exports) { + // We're being used in a Node-like environment + sax = Require('dos/ext/sax'); +} +else { + // assume it's attached to the Window object in a browser + sax = this.sax; + + if (!sax) // no sax for you! + throw new Error("Expected sax to be defined. Make sure you're including sax.js before this file."); +} + +/* +XmlElement is our basic building block. Everything is an XmlElement; even XmlDocument +behaves like an XmlElement by inheriting its attributes and functions. +*/ + +function XmlElement(tag) { + // Capture the parser object off of the XmlDocument delegate + var parser = delegates[delegates.length - 1].parser; + + this.name = tag.name; + this.attr = tag.attributes || {}; + this.val = ""; + this.children = []; + this.firstChild = null; + this.lastChild = null; + + // Assign parse information + this.line = parser.line; + this.column = parser.column; + this.position = parser.position; + this.startTagPosition = parser.startTagPosition; +} + +// SaxParser handlers + +XmlElement.prototype._opentag = function(tag) { + + var child = new XmlElement(tag); + + // add to our children array + this.children.push(child); + + // update first/last pointers + if (!this.firstChild) this.firstChild = child; + this.lastChild = child; + + delegates.unshift(child); +}; + +XmlElement.prototype._closetag = function() { + delegates.shift(); +}; + +XmlElement.prototype._text = function(text) { + if (text) this.val += text; +}; + +XmlElement.prototype._cdata = function(cdata) { + if (cdata) this.val += cdata; +}; + +// Useful functions + +XmlElement.prototype.eachChild = function(iterator, context) { + for (var i=0, l=this.children.length; i 1 ? descendant.attr[components[1]] : descendant.val; + else + return null; +}; + +// String formatting (for debugging) + +XmlElement.prototype.toString = function(options) { + return this.toStringWithIndent("", options); +}; + +XmlElement.prototype.toStringWithIndent = function(indent, options) { + var s = indent + "<" + this.name; + var linebreak = options && options.compressed ? "" : "\n"; + + for (var name in this.attr) + if (Object.prototype.hasOwnProperty.call(this.attr, name)) + s += " " + name + '="' + this.attr[name] + '"'; + + var finalVal = this.val.trim().replace(//g, ">").replace(/&/g, '&'); + + if (options && options.trimmed && finalVal.length > 25) + finalVal = finalVal.substring(0,25).trim() + "…"; + + if (this.children.length) { + s += ">" + linebreak; + + var childIndent = indent + (options && options.compressed ? "" : " "); + + if (finalVal.length) + s += childIndent + finalVal + linebreak; + + for (var i=0, l=this.children.length; i"; + } + else if (finalVal.length) { + s += ">" + finalVal + ""; + } + else s += "/>"; + + return s; +}; + +/* +XmlDocument is the class we expose to the user; it uses the sax parser to create a hierarchy +of XmlElements. +*/ + +function XmlDocument(xml) { + xml && (xml = xml.toString().trim()); + + if (!xml) + throw new Error("No XML to parse!"); + + // Expose the parser to the other delegates while the parser is running + this.parser = sax.parser(true); // strict + addParserEvents(this.parser); + + // We'll use the file-scoped "delegates" var to remember what elements we're currently + // parsing; they will push and pop off the stack as we get deeper into the XML hierarchy. + // It's safe to use a global because JS is single-threaded. + delegates = [this]; + + this.parser.write(xml); + + // Remove the parser as it is no longer needed and should not be exposed to clients + delete this.parser; +} + +// make XmlDocument inherit XmlElement's methods +extend(XmlDocument.prototype, XmlElement.prototype); + +XmlDocument.prototype._opentag = function(tag) { + if (typeof this.children === 'undefined') + // the first tag we encounter should be the root - we'll "become" the root XmlElement + XmlElement.call(this,tag); + else + // all other tags will be the root element's children + XmlElement.prototype._opentag.apply(this,arguments); +}; + +// file-scoped global stack of delegates +var delegates = null; + +/* +Helper functions +*/ + +function addParserEvents(parser) { + parser.onopentag = parser_opentag; + parser.onclosetag = parser_closetag; + parser.ontext = parser_text; + parser.oncdata = parser_cdata; +} + +// create these closures and cache them by keeping them file-scoped +function parser_opentag() { delegates[0]._opentag.apply(delegates[0],arguments) } +function parser_closetag() { delegates[0]._closetag.apply(delegates[0],arguments) } +function parser_text() { delegates[0]._text.apply(delegates[0],arguments) } +function parser_cdata() { delegates[0]._cdata.apply(delegates[0],arguments) } + +// a relatively standard extend method +function extend(destination, source) { + for (var prop in source) + if (source.hasOwnProperty(prop)) + destination[prop] = source[prop]; +} + +// Are we being used in a Node-like environment? +if (typeof module !== 'undefined' && module.exports) + module.exports.XmlDocument = XmlDocument; +else + this.XmlDocument = XmlDocument; + +})();