GT2/GT2-Android/node_modules/xmldoc/lib/xmldoc.js

258 lines
7.0 KiB
JavaScript

(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.isValCdata = false;
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;
this.isValCdata=true;
}
};
XmlElement.prototype._error = function(err) {
throw err;
};
// 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 undefined;
};
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 undefined;
};
// 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";
var preserveWhitespace = options && options.preserveWhitespace;
for (var name in this.attr)
if (Object.prototype.hasOwnProperty.call(this.attr, name))
s += " " + name + '="' + escapeXML(this.attr[name]) + '"';
var finalVal = '';
if (this.isValCdata){
finalVal = '<![CDATA['+this.val+']]>';
} else if (preserveWhitespace) {
finalVal = escapeXML(this.val);
} else{
finalVal = escapeXML(this.val.trim());
}
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;
parser.onerror = parser_error;
}
// 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) }
function parser_error() { delegates[0]._error.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];
}
// escapes XML entities like "<", "&", etc.
function escapeXML(value){
return value.replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/&/g, '&amp;').replace(/'/g, '&apos;').replace(/"/g, '&quot;');
}
// Are we being used in a Node-like environment?
if (typeof module !== 'undefined' && module.exports)
module.exports.XmlDocument = XmlDocument;
else
this.XmlDocument = XmlDocument;
})();