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