forked from cory/tildefriends
Adding a number of work-in-progress packages. Some data structures built on top of the key-value store and an http client, among others.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@3310 ed5197a5-7fde-0310-b194-c3ffbd925b24
This commit is contained in:
254
packages/cory/libxml/libxml.js
Normal file
254
packages/cory/libxml/libxml.js
Normal file
@ -0,0 +1,254 @@
|
||||
"use strict";
|
||||
|
||||
//! { "category": "libraries" }
|
||||
|
||||
function xmlEncode(text) {
|
||||
return text.replace(/([\&"'<>])/g, function(x, item) {
|
||||
return {'&': '&', '"': '"', '<': '<', '>': '>', "'": '''}[item];
|
||||
});
|
||||
}
|
||||
function xmlDecode(xml) {
|
||||
return xml.replace(/("|<|>|&|')/g, function(x, item) {
|
||||
return {'&': '&', '"': '"', '<': '<', '>': '>', ''': "'"}[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),
|
||||
};
|
||||
}
|
Reference in New Issue
Block a user