tildefriends/packages/cory/libxml/libxml.js

261 lines
5.5 KiB
JavaScript

"use strict";
//! { "category": "libraries" }
function xmlEncode(text) {
return text.replace(/([\&"'<>])/g, function(x, item) {
return {'&': '&amp;', '"': '&quot;', '<': '&lt;', '>': '&gt;', "'": '&apos;'}[item];
});
}
function xmlDecode(xml) {
return xml.replace(/(&quot;|&lt;|&gt;|&amp;|&apos;)/g, function(x, item) {
return {'&amp;': '&', '&quot;': '"', '&lt;': '<', '&gt;': '>', '&apos;': "'"}[item];
});
}
function XmlStreamParser() {
this.buffer = "";
this._parsed = [];
this.reset();
return this;
}
XmlStreamParser.kText = "text";
XmlStreamParser.kElement = "element";
XmlStreamParser.kEndElement = "endElement";
XmlStreamParser.kAttributeName = "attributeName";
XmlStreamParser.kAttributeValue = "attributeValue";
XmlStreamParser.prototype.reset = function() {
this._state = XmlStreamParser.kText;
this._attributes = {};
this._attributeName = "";
this._attributeValue = "";
this._attributeEquals = false;
this._attributeQuote = "";
this._slash = false;
this._value = "";
this._decl = false;
}
XmlStreamParser.prototype.parse = function(data) {
this._parsed = [];
for (var i = 0; i < data.length; i++) {
var c = data.charAt(i);
this.parseCharacter(c);
}
return this._parsed;
}
XmlStreamParser.prototype.flush = function() {
var node = {type: this._state};
if (this._value) {
node.value = xmlDecode(this._value);
}
if (this._attributes.length || this._state == XmlStreamParser.kElement) {
node.attributes = this._attributes;
}
if (this._state != XmlStreamParser.kText || this._value) {
this.emit(node);
}
this.reset();
}
XmlStreamParser.prototype.parseCharacter = function(c) {
switch (this._state) {
case XmlStreamParser.kText:
if (c == '<') {
this.flush();
this._state = XmlStreamParser.kElement;
} else {
this._value += c;
}
break;
case XmlStreamParser.kElement:
case XmlStreamParser.kEndElement:
switch (c) {
case '>':
this.finishElement();
break;
case '/':
if (!this._value) {
this._state = XmlStreamParser.kEndElement;
} else if (!this._slash) {
this._slash = true;
} else {
this._value += c;
}
break;
case '?':
if (!this._value) {
this._decl = true;
} else {
this._value += '?';
}
break;
case ' ':
case '\t':
case '\r':
case '\n':
this._state = XmlStreamParser.kAttributeName;
break;
default:
if (this._slash) {
this._slash = false;
this._value += '/';
}
this._value += c;
break;
}
break;
case XmlStreamParser.kAttributeName:
switch (c) {
case ' ':
case '\t':
case '\r':
case '\n':
if (this._attributeName) {
this._state = XmlStreamParser.kAttributeValue;
}
break;
case '/':
if (!this._slash) {
this._slash = true;
} else {
this._value += '/';
}
break;
case '=':
this._state = XmlStreamParser.kAttributeValue;
break;
case '>':
if (this._attributeName) {
this._attributes[this._attributeName] = null;
}
this._state = XmlStreamParser.kElement;
this.finishElement();
break;
default:
this._attributeName += c;
break;
}
break;
case XmlStreamParser.kAttributeValue:
switch (c) {
case ' ':
case '\t':
case '\r':
case '\n':
if (this._attributeValue) {
this._state = XmlStreamParser.kAttributeName;
}
break;
case '"':
case "'":
if (!this._attributeValue && !this._attributeQuote) {
this._attributeQuote = c;
} else if (this._attributeQuote == c) {
this._attributes[this._attributeName] = this._attributeValue;
this._attributeName = "";
this._attributeValue = "";
this._attributeQuote = "";
this._state = XmlStreamParser.kAttributeName;
} else {
this._attributeValue += c;
}
break;
case '>':
this.finishElement();
break;
default:
this._attributeValue += c;
break;
}
break;
}
}
XmlStreamParser.prototype.finishElement = function() {
if (this._decl) {
this.reset();
} else {
var value = this._value;
var slash = this._slash;
this.flush();
if (slash) {
this._state = XmlStreamParser.kEndElement;
this._value = value;
this.flush();
}
}
this._state = XmlStreamParser.kText;
}
XmlStreamParser.prototype.emit = function(node) {
this._parsed.push(node);
}
function XmlStanzaParser(depth) {
this._depth = depth || 0;
this._parsed = [];
this._stack = [];
this._stream = new XmlStreamParser();
return this;
}
XmlStanzaParser.prototype.reset = function() {
this._parsed = [];
this._stack = [];
this._stream.reset();
}
XmlStanzaParser.prototype.emit = function(stanza) {
this._parsed.push(stanza);
}
XmlStanzaParser.prototype.parse = function(data) {
this._parsed = [];
var nodes = this._stream.parse(data);
for (var i = 0; i < nodes.length; i++) {
this.parseNode(nodes[i]);
}
return this._parsed;
}
XmlStanzaParser.prototype.parseNode = function(node) {
switch (node.type) {
case XmlStreamParser.kElement:
this._stack.push({name: node.value, attributes: node.attributes, children: [], text: ""});
break;
case XmlStreamParser.kEndElement:
if (this._stack.length == 1 + this._depth) {
this.emit(this._stack.pop());
} else {
var last = this._stack.pop();
this._stack[this._stack.length - 1].children.push(last);
}
break;
case XmlStreamParser.kText:
if (this._stack && this._stack.length) {
this._stack[this._stack.length - 1].text += node.value;
}
break;
}
}
exports.StanzaParser = function(depth) {
let parser = new XmlStanzaParser(depth);
return {
parse: parser.parse.bind(parser),
reset: parser.reset.bind(parser),
};
}
exports.StreamParser = function() {
let parser = new XmlStreamParser();
return {
parse: parser.parse.bind(parser),
};
}