UNPKG

alasql

Version:

AlaSQL.js - JavaScript SQL database library for relational and graph data manipulation with support of localStorage, IndexedDB, and Excel

236 lines (180 loc) 6.55 kB
(function () { var sax; if (typeof module !== 'undefined' && module.exports) { // We're being used in a Node-like environment sax = require('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<l; i++) if (iterator.call(context, this.children[i], i, this.children) === false) return; }; XmlElement.prototype.childNamed = function(name) { for (var i=0, l=this.children.length; i<l; i++) { var child = this.children[i]; if (child.name === name) return child; } return undefined; }; XmlElement.prototype.childrenNamed = function(name) { var matches = []; for (var i=0, l=this.children.length; i<l; i++) if (this.children[i].name === name) matches.push(this.children[i]); return matches; }; XmlElement.prototype.childWithAttribute = function(name,value) { for (var i=0, l=this.children.length; i<l; i++) { var child = this.children[i]; if ( (value && child.attr[name] === value) || (!value && child.attr[name]) ) return child; } return null; }; XmlElement.prototype.descendantWithPath = function(path) { var descendant = this; var components = path.split('.'); for (var i=0, l=components.length; i<l; i++) if (descendant) descendant = descendant.childNamed(components[i]); else return undefined; return descendant; }; XmlElement.prototype.valueWithPath = function(path) { var components = path.split('@'); var descendant = this.descendantWithPath(components[0]); if (descendant) return components.length > 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, "&lt;").replace(/>/g, "&gt;").replace(/&/g, '&amp;'); 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<l; i++) s += this.children[i].toStringWithIndent(childIndent, options) + linebreak; s += indent + "</" + this.name + ">"; } else if (finalVal.length) { s += ">" + finalVal + "</" + this.name +">"; } 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; })();