123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606 |
- var fs = require('fs');
- var indent_string = "\t";
- var xml_header = '<?xml version="1.0"?>';
- var sort_args = null;
- var re_valid_tag_name = /^\w[\w\-\:]*$/;
- var XML = exports.XML = function XML(args) {
-
-
- if (!args) args = '';
- if (isa_hash(args)) {
- for (var key in args) this[key] = args[key];
- }
- else this.text = args || '';
-
-
- if (this.text instanceof Buffer) {
- this.text = this.text.toString();
- }
-
- if (!this.text.match(/^\s*</)) {
-
- var file = this.text;
- this.text = fs.readFileSync(file, { encoding: 'utf8' });
- if (!this.text) throw new Error("File not found: " + file);
- }
-
- this.tree = {};
- this.errors = [];
- this.piNodeList = [];
- this.dtdNodeList = [];
- this.documentNodeName = '';
-
- if (this.lowerCase) {
- this.attribsKey = this.attribsKey.toLowerCase();
- this.dataKey = this.dataKey.toLowerCase();
- }
-
- this.patTag.lastIndex = 0;
- if (this.text) this.parse();
- }
- XML.prototype.preserveAttributes = false;
- XML.prototype.lowerCase = false;
- XML.prototype.patTag = /([^<]*?)<([^>]+)>/g;
- XML.prototype.patSpecialTag = /^\s*([\!\?])/;
- XML.prototype.patPITag = /^\s*\?/;
- XML.prototype.patCommentTag = /^\s*\!--/;
- XML.prototype.patDTDTag = /^\s*\!DOCTYPE/;
- XML.prototype.patCDATATag = /^\s*\!\s*\[\s*CDATA/;
- XML.prototype.patStandardTag = /^\s*(\/?)([\w\-\:\.]+)\s*(.*)$/;
- XML.prototype.patSelfClosing = /\/\s*$/;
- XML.prototype.patAttrib = new RegExp("([\\w\\-\\:\\.]+)\\s*=\\s*([\\\"\\'])([^\\2]*?)\\2", "g");
- XML.prototype.patPINode = /^\s*\?\s*([\w\-\:]+)\s*(.*)$/;
- XML.prototype.patEndComment = /--$/;
- XML.prototype.patNextClose = /([^>]*?)>/g;
- XML.prototype.patExternalDTDNode = new RegExp("^\\s*\\!DOCTYPE\\s+([\\w\\-\\:]+)\\s+(SYSTEM|PUBLIC)\\s+\\\"([^\\\"]+)\\\"");
- XML.prototype.patInlineDTDNode = /^\s*\!DOCTYPE\s+([\w\-\:]+)\s+\[/;
- XML.prototype.patEndDTD = /\]$/;
- XML.prototype.patDTDNode = /^\s*\!DOCTYPE\s+([\w\-\:]+)\s+\[(.*)\]/;
- XML.prototype.patEndCDATA = /\]\]$/;
- XML.prototype.patCDATANode = /^\s*\!\s*\[\s*CDATA\s*\[([^]*)\]\]/;
- XML.prototype.attribsKey = '_Attribs';
- XML.prototype.dataKey = '_Data';
- XML.prototype.parse = function(branch, name) {
-
- if (!branch) branch = this.tree;
- if (!name) name = null;
- var foundClosing = false;
- var matches = null;
-
-
- while ( matches = this.patTag.exec(this.text) ) {
- var before = matches[1];
- var tag = matches[2];
-
-
- if (before.match(/\S/)) {
- if (typeof(branch[this.dataKey]) != 'undefined') branch[this.dataKey] += ' '; else branch[this.dataKey] = '';
- branch[this.dataKey] += trim(decode_entities(before));
- }
-
-
- if (tag.match(this.patSpecialTag)) {
-
- if (tag.match(this.patPITag)) tag = this.parsePINode(tag);
- else if (tag.match(this.patCommentTag)) tag = this.parseCommentNode(tag);
- else if (tag.match(this.patDTDTag)) tag = this.parseDTDNode(tag);
- else if (tag.match(this.patCDATATag)) {
- tag = this.parseCDATANode(tag);
- if (typeof(branch[this.dataKey]) != 'undefined') branch[this.dataKey] += ' '; else branch[this.dataKey] = '';
- branch[this.dataKey] += trim(decode_entities(tag));
- }
- else {
- this.throwParseError( "Malformed special tag", tag );
- break;
- }
-
- if (tag == null) break;
- continue;
- }
- else {
-
- var matches = tag.match(this.patStandardTag);
- if (!matches) {
- this.throwParseError( "Malformed tag", tag );
- break;
- }
-
- var closing = matches[1];
- var nodeName = this.lowerCase ? matches[2].toLowerCase() : matches[2];
- var attribsRaw = matches[3];
-
-
- if (closing) {
- if (nodeName == (name || '')) {
- foundClosing = 1;
- break;
- }
- else {
- this.throwParseError( "Mismatched closing tag (expected </" + name + ">)", tag );
- break;
- }
- }
- else {
-
-
- var selfClosing = !!attribsRaw.match(this.patSelfClosing);
- var leaf = {};
- var attribs = leaf;
-
-
-
- if (this.preserveAttributes) {
- leaf[this.attribsKey] = {};
- attribs = leaf[this.attribsKey];
- }
-
-
- this.patAttrib.lastIndex = 0;
- while ( matches = this.patAttrib.exec(attribsRaw) ) {
- var key = this.lowerCase ? matches[1].toLowerCase() : matches[1];
- attribs[ key ] = decode_entities( matches[3] );
- }
-
-
- if (this.preserveAttributes && !num_keys(attribs)) {
- delete leaf[this.attribsKey];
- }
-
-
- if (!selfClosing) {
- this.parse( leaf, nodeName );
- if (this.error()) break;
- }
-
-
- var num_leaf_keys = num_keys(leaf);
- if ((typeof(leaf[this.dataKey]) != 'undefined') && (num_leaf_keys == 1)) {
- leaf = leaf[this.dataKey];
- }
- else if (!num_leaf_keys) {
- leaf = '';
- }
-
-
- if (typeof(branch[nodeName]) != 'undefined') {
- if (isa_array(branch[nodeName])) {
- branch[nodeName].push( leaf );
- }
- else {
- var temp = branch[nodeName];
- branch[nodeName] = [ temp, leaf ];
- }
- }
- else {
- branch[nodeName] = leaf;
- }
-
- if (this.error() || (branch == this.tree)) break;
- }
- }
- }
-
-
- if (name && !foundClosing) {
- this.throwParseError( "Missing closing tag (expected </" + name + ">)", name );
- }
-
-
- if (branch == this.tree) {
- if (typeof(this.tree[this.dataKey]) != 'undefined') delete this.tree[this.dataKey];
-
- if (num_keys(this.tree) > 1) {
- this.throwParseError( 'Only one top-level node is allowed in document', first_key(this.tree) );
- return;
- }
- this.documentNodeName = first_key(this.tree);
- if (this.documentNodeName) {
- this.tree = this.tree[this.documentNodeName];
- }
- }
- };
- XML.prototype.throwParseError = function(key, tag) {
-
- var parsedSource = this.text.substring(0, this.patTag.lastIndex);
- var eolMatch = parsedSource.match(/\n/g);
- var lineNum = (eolMatch ? eolMatch.length : 0) + 1;
- lineNum -= tag.match(/\n/) ? tag.match(/\n/g).length : 0;
-
- this.errors.push({
- type: 'Parse',
- key: key,
- text: '<' + tag + '>',
- line: lineNum
- });
-
-
- throw new Error( this.getLastError() );
- };
- XML.prototype.error = function() {
-
- return this.errors.length;
- };
- XML.prototype.getError = function(error) {
-
- var text = '';
- if (!error) return '';
- text = (error.type || 'General') + ' Error';
- if (error.code) text += ' ' + error.code;
- text += ': ' + error.key;
-
- if (error.line) text += ' on line ' + error.line;
- if (error.text) text += ': ' + error.text;
- return text;
- };
- XML.prototype.getLastError = function() {
-
- if (!this.error()) return '';
- return this.getError( this.errors[this.errors.length - 1] );
- };
- XML.prototype.parsePINode = function(tag) {
-
- if (!tag.match(this.patPINode)) {
- this.throwParseError( "Malformed processor instruction", tag );
- return null;
- }
-
- this.piNodeList.push( tag );
- return tag;
- };
- XML.prototype.parseCommentNode = function(tag) {
-
- var matches = null;
- this.patNextClose.lastIndex = this.patTag.lastIndex;
-
- while (!tag.match(this.patEndComment)) {
- if (matches = this.patNextClose.exec(this.text)) {
- tag += '>' + matches[1];
- }
- else {
- this.throwParseError( "Unclosed comment tag", tag );
- return null;
- }
- }
-
- this.patTag.lastIndex = this.patNextClose.lastIndex;
- return tag;
- };
- XML.prototype.parseDTDNode = function(tag) {
-
- var matches = null;
-
- if (tag.match(this.patExternalDTDNode)) {
-
- this.dtdNodeList.push( tag );
- }
- else if (tag.match(this.patInlineDTDNode)) {
-
- this.patNextClose.lastIndex = this.patTag.lastIndex;
-
- while (!tag.match(this.patEndDTD)) {
- if (matches = this.patNextClose.exec(this.text)) {
- tag += '>' + matches[1];
- }
- else {
- this.throwParseError( "Unclosed DTD tag", tag );
- return null;
- }
- }
-
- this.patTag.lastIndex = this.patNextClose.lastIndex;
-
-
- if (tag.match(this.patDTDNode)) {
- this.dtdNodeList.push( tag );
- }
- else {
- this.throwParseError( "Malformed DTD tag", tag );
- return null;
- }
- }
- else {
- this.throwParseError( "Malformed DTD tag", tag );
- return null;
- }
-
- return tag;
- };
- XML.prototype.parseCDATANode = function(tag) {
-
- var matches = null;
- this.patNextClose.lastIndex = this.patTag.lastIndex;
-
- while (!tag.match(this.patEndCDATA)) {
- if (matches = this.patNextClose.exec(this.text)) {
- tag += '>' + matches[1];
- }
- else {
- this.throwParseError( "Unclosed CDATA tag", tag );
- return null;
- }
- }
-
- this.patTag.lastIndex = this.patNextClose.lastIndex;
-
- if (matches = tag.match(this.patCDATANode)) {
- return matches[1];
- }
- else {
- this.throwParseError( "Malformed CDATA tag", tag );
- return null;
- }
- };
- XML.prototype.getTree = function() {
-
- return this.tree;
- };
- XML.prototype.compose = function() {
-
- var raw = compose_xml( this.tree, this.documentNodeName );
- var body = raw.substring( raw.indexOf("\n") + 1, raw.length );
- var xml = '';
-
- if (this.piNodeList.length) {
- for (var idx = 0, len = this.piNodeList.length; idx < len; idx++) {
- xml += '<' + this.piNodeList[idx] + '>' + "\n";
- }
- }
- else {
- xml += xml_header + "\n";
- }
-
- if (this.dtdNodeList.length) {
- for (var idx = 0, len = this.dtdNodeList.length; idx < len; idx++) {
- xml += '<' + this.dtdNodeList[idx] + '>' + "\n";
- }
- }
-
- xml += body;
- return xml;
- };
- var parse_xml = exports.parse = function parse_xml(text, opts) {
-
- if (!opts) opts = {};
- opts.text = text;
- var parser = new XML(opts);
- return parser.error() ? parser.getLastError() : parser.getTree();
- };
- var trim = exports.trim = function trim(text) {
-
- if (text == null) return '';
-
- if (text && text.replace) {
- text = text.replace(/^\s+/, "");
- text = text.replace(/\s+$/, "");
- }
-
- return text;
- };
- var encode_entities = exports.encodeEntities = function encode_entities(text) {
-
- if (text == null) return '';
-
- if (text && text.replace) {
- text = text.replace(/\&/g, "&");
- text = text.replace(/</g, "<");
- text = text.replace(/>/g, ">");
- }
-
- return text;
- };
- var encode_attrib_entities = exports.encodeAttribEntities = function encode_attrib_entities(text) {
-
- if (text == null) return '';
-
- if (text && text.replace) {
- text = text.replace(/\&/g, "&");
- text = text.replace(/</g, "<");
- text = text.replace(/>/g, ">");
- text = text.replace(/\"/g, """);
- text = text.replace(/\'/g, "'");
- }
-
- return text;
- };
- var decode_entities = exports.decodeEntities = function decode_entities(text) {
-
- if (text == null) return '';
-
- if (text && text.replace && text.match(/\&/)) {
- text = text.replace(/\<\;/g, "<");
- text = text.replace(/\>\;/g, ">");
- text = text.replace(/\"\;/g, '"');
- text = text.replace(/\&apos\;/g, "'");
- text = text.replace(/\&\;/g, "&");
- }
-
- return text;
- };
- var compose_xml = exports.stringify = function compose_xml(node, name, indent) {
-
-
- var xml = "";
-
-
-
- if (!indent) {
- indent = 0;
- xml = xml_header + "\n";
-
- if (!name) {
-
- name = first_key(node);
- node = node[name];
- }
- }
-
-
- var indent_text = "";
- for (var k = 0; k < indent; k++) indent_text += indent_string;
- if ((typeof(node) == 'object') && (node != null)) {
-
- if (!node.length) {
-
- xml += indent_text + "<" + name;
- var num_keys = 0;
- var has_attribs = 0;
- for (var key in node) num_keys++;
- if (node["_Attribs"]) {
- has_attribs = 1;
- var sorted_keys = hash_keys_to_array(node["_Attribs"]).sort();
- for (var idx = 0, len = sorted_keys.length; idx < len; idx++) {
- var key = sorted_keys[idx];
- xml += " " + key + "=\"" + encode_attrib_entities(node["_Attribs"][key]) + "\"";
- }
- }
- if (num_keys > has_attribs) {
-
- xml += ">";
- if (node["_Data"]) {
-
- xml += encode_entities(node["_Data"]) + "</" + name + ">\n";
- }
- else {
- xml += "\n";
-
- var sorted_keys = hash_keys_to_array(node).sort();
- for (var idx = 0, len = sorted_keys.length; idx < len; idx++) {
- var key = sorted_keys[idx];
- if ((key != "_Attribs") && key.match(re_valid_tag_name)) {
-
- xml += compose_xml( node[key], key, indent + 1 );
- }
- }
- xml += indent_text + "</" + name + ">\n";
- }
- }
- else {
-
- xml += "/>\n";
- }
- }
- else {
-
- for (var idx = 0; idx < node.length; idx++) {
-
- xml += compose_xml( node[idx], name, indent );
- }
- }
- }
- else {
-
- xml += indent_text + "<" + name + ">" + encode_entities(node) + "</" + name + ">\n";
- }
- return xml;
- };
- var always_array = exports.alwaysArray = function always_array(obj, key) {
-
-
- if (key) {
- if ((typeof(obj[key]) != 'object') || (typeof(obj[key].length) == 'undefined')) {
- var temp = obj[key];
- delete obj[key];
- obj[key] = new Array();
- obj[key][0] = temp;
- }
- return null;
- }
- else {
- if ((typeof(obj) != 'object') || (typeof(obj.length) == 'undefined')) { return [ obj ]; }
- else return obj;
- }
- };
- var hash_keys_to_array = exports.hashKeysToArray = function hash_keys_to_array(hash) {
-
- var array = [];
- for (var key in hash) array.push(key);
- return array;
- };
- var isa_hash = exports.isaHash = function isa_hash(arg) {
-
- return( !!arg && (typeof(arg) == 'object') && (typeof(arg.length) == 'undefined') );
- };
- var isa_array = exports.isaArray = function isa_array(arg) {
-
- if (typeof(arg) == 'array') return true;
- return( !!arg && (typeof(arg) == 'object') && (typeof(arg.length) != 'undefined') );
- };
- var first_key = exports.firstKey = function first_key(hash) {
-
- for (var key in hash) return key;
- return null;
- };
- var num_keys = exports.numKeys = function num_keys(hash) {
-
- var count = 0;
- for (var a in hash) count++;
- return count;
- };
|