pax_global_header00006660000000000000000000000064117331631360014516gustar00rootroot0000000000000052 comment=f68025ee9134e7b4ccb440772f55146ea3470483 astro-ltx-f68025e/000077500000000000000000000000001173316313600140145ustar00rootroot00000000000000astro-ltx-f68025e/.travis.yml000066400000000000000000000000531173316313600161230ustar00rootroot00000000000000language: node_js node_js: - 0.4 - 0.6 astro-ltx-f68025e/LICENSE000066400000000000000000000020411173316313600150160ustar00rootroot00000000000000Copyright (c) 2010 Stephan Maka Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. astro-ltx-f68025e/README.markdown000066400000000000000000000056561173316313600165310ustar00rootroot00000000000000# Less-Than XML * *Element:* any XML Element * Text nodes are Strings * Runs on node.js and browserify ## Parsing ### DOM Parse a little document at once: el = ltx.parse("") Push parser: p = new ltx.Parser(); p.on('tree', function(tree) { proceed(null, tree); }); p.on('error', function(error) { proceed(error); }); ### SAX ltx implements multiple SAX backends: * *node-expat*: libexpat binding * *ltx*: fast native-JavaScript parser without error handling * *saxjs*: native-JavaScript parser If present, they are available through `ltx.availableSaxParsers`. Mostly, you'll want to do: parser = new ltx.bestSaxParser(); Refer to `lib/parse.js` for the interface. ## Element traversal * `is(name, xmlns?)`: check * `getName()`: name without ns prefix * `getNS()`: element's xmlns, respects prefixes and searches upwards * `findNS(prefix?)`: search for xmlns of a prefix upwards * `getChild(name, xmlns?)`: find first child * `getChildren(name, xmlns?)`: find all children * `getChildByAttr(attr, value, xmlns?)`: find first child by a specific attribute * `getChildrenByAttr(attr, value, xmlns?)`: find all children by a specific attribute * `getText()`: appends all text nodes recursively * `getChildText(name)`: a child's text contents * `root()`: uppermost parent in the tree * `up()`: parent or self ## Element attributes * `attrs` is an object of the Element's attributes * `name` contains optional prefix, colon, name * `parent` points to its parent, this should always be consistent with children * `children` is an Array of Strings and Elements ## Modifying XML Elements * `new Element(name, attrs?)`: constructor * `remove(child)`: remove child by reference * `remove(name, xmlns)`: remove child by tag name and xmlns * `attr(attrName, value?)`: modify or get an attribute's value * `text(value?)`: modify or get the inner text * `clone()`: clones an element that is detached from the document ## Building XML Elements el = new ltx.Element('root'). c('children'); el.c('child', { age: 5 }).t('Hello').up() .c('child', { age: 7 }).t('Hello').up() .c('child', { age: 99 }).t('Hello').up() console.log("Serialized document:", el.root().toString()); This resembles Strophejs a bit. strophejs' XML Builder is very convenient for producing XMPP stanzas. node-xmpp includes it in a much more primitive way: the `c()`, `cnode()` and `t()` methods can be called on any *Element* object, returning the child element. This can be confusing: in the end, you will hold the last-added child until you use `up()`, a getter for the parent. `Connection.send()` first invokes `tree()` to retrieve the uppermost parent, the XMPP stanza, before sending it out the wire. ## Destructive manipulation Please always make sure `parent` and `children` are consistent. Don't append children of other parents to your own element. We're not adoption-safe! ## TODO * More documentation * More tests (Using [Vows](http://vowsjs.org/)) astro-ltx-f68025e/benchmark/000077500000000000000000000000001173316313600157465ustar00rootroot00000000000000astro-ltx-f68025e/benchmark/.gitignore000066400000000000000000000000111173316313600177260ustar00rootroot00000000000000index.js astro-ltx-f68025e/benchmark/benchmark.js000066400000000000000000000070151173316313600202410ustar00rootroot00000000000000if (process.title === 'browser') { var ltx = require("ltx"); var strophe = require('Strophe.js'); var requestAnimationFrame = require('request-animation-frame').requestAnimationFrame; } else { var path = "../lib/index"; var ltx = require(path); } var util = require('util'); function now() { return new Date().getTime(); } function Test() { this.timings = {}; } Test.prototype = { record: function(name, fun) { var t1 = now(); var res = fun(); var t2 = now(); if (!this.timings.hasOwnProperty(name)) this.timings[name] = { i: 0, t: 0 }; this.timings[name].i++; this.timings[name].t += t2 - t1; }, report: function() { if (process.title === 'browser') { var s = []; var html = "

" + this.name + "

"; for(var k in this.timings) { var t = this.timings[k].t / this.timings[k].i; html += "
" + k + "
" + t + " ms
"; } html += "
\n"; return html; } else { var s = this.name + "\t"; for(k in this.timings) { var t = this.timings[k].t / this.timings[k].i; s += k + ": " + t + " ms\t"; } return s; } } }; function LtxTest(saxParser) { Test.call(this); this.saxParser = saxParser; this.name = "LTX/" + saxParser.name; } util.inherits(LtxTest, Test); LtxTest.prototype.parse = function(s) { return ltx.parse(s, this.saxParser); }; LtxTest.prototype.serialize = function(el) { return el.toString(); }; LtxTest.prototype.traverse = function(node) { while(node.children && node.children[0]) node = node.children[0]; }; function StropheTest() { Test.call(this); this.serialize = Strophe.serialize; } util.inherits(StropheTest, Test); StropheTest.prototype.name = "Strophe.js"; StropheTest.prototype.parse = function(s) { return Strophe.xmlHtmlNode(s).firstChild; }; StropheTest.prototype.traverse = function(node) { while(node.firstChild) node = node.firstChild; }; var tests = ltx.availableSaxParsers.map(function(saxParser) { return new LtxTest(saxParser); }); if (process.title === 'browser') tests.push(new StropheTest()); var messages = [ "", "", "fnordfnord" ]; messages[3] = ""; for(var i = 0; i < 10; i++) { messages[3] += "fnord fnord fnord"; } for(var i = 0; i < 10; i++) { messages[3] += "fnordfnordfnordfnord"; } messages[3] += ""; var iteration = 0; function runTests() { iteration++; tests.forEach(function(test) { for(var j = 0; j < messages.length; j++) { var parsed, serialized; test.record('parse' + j, function() { parsed = test.parse(messages[j]); }); test.record('serialize' + j, function() { serialized = test.serialize(parsed); }); test.record('traverse' + j, function() { test.traverse(parsed); }); } }); if (process.title === 'browser') { document.body.innerHTML = "\n" + "

Iteration " + iteration + "

\n"; tests.forEach(function(test) { document.body.innerHTML += test.report() + "
"; }); requestAnimationFrame(runTests); } else { console.log("Iteration " + iteration); tests.forEach(function(test) { console.log(test.report()); }); process.nextTick(runTests); } } setTimeout(function() { runTests(); }, 1000); astro-ltx-f68025e/benchmark/build.js000066400000000000000000000005541173316313600174070ustar00rootroot00000000000000var browserify = require('browserify'); var path = require('path'); var b = browserify({ debug: true }); var root = path.join(__dirname, ".."); b.require("./lib/index-browserify.js", { root: root, basedir: root }); b.alias("ltx", "./lib/index-browserify.js"); b.addEntry('benchmark.js'); var fs = require('fs'); fs.writeFileSync('index.js', b.bundle()); astro-ltx-f68025e/benchmark/index.html000066400000000000000000000001531173316313600177420ustar00rootroot00000000000000 astro-ltx-f68025e/index.js000066400000000000000000000000541173316313600154600ustar00rootroot00000000000000module.exports = require('./lib/index.js'); astro-ltx-f68025e/lib/000077500000000000000000000000001173316313600145625ustar00rootroot00000000000000astro-ltx-f68025e/lib/element.js000066400000000000000000000152071173316313600165560ustar00rootroot00000000000000/** * This cheap replica of DOM/Builder puts me to shame :-) * * Attributes are in the element.attrs object. Children is a list of * either other Elements or Strings for text content. **/ function Element(name, attrs) { this.name = name; this.parent = null; this.attrs = attrs || {}; this.children = []; } /*** Accessors ***/ /** * if (element.is('message', 'jabber:client')) ... **/ Element.prototype.is = function(name, xmlns) { return this.getName() == name && (!xmlns || this.getNS() == xmlns); }; /* without prefix */ Element.prototype.getName = function() { if (this.name.indexOf(":") >= 0) return this.name.substr(this.name.indexOf(":") + 1); else return this.name; }; /** * retrieves the namespace of the current element, upwards recursively **/ Element.prototype.getNS = function() { if (this.name.indexOf(":") >= 0) { var prefix = this.name.substr(0, this.name.indexOf(":")); return this.findNS(prefix); } else { return this.findNS(); } }; /** * find the namespace to the given prefix, upwards recursively **/ Element.prototype.findNS = function(prefix) { if (!prefix) { /* default namespace */ if (this.attrs.xmlns) return this.attrs.xmlns; else if (this.parent) return this.parent.findNS(); } else { /* prefixed namespace */ var attr = 'xmlns:' + prefix; if (this.attrs[attr]) return this.attrs[attr]; else if (this.parent) return this.parent.findNS(prefix); } }; /** * xmlns can be null **/ Element.prototype.getChild = function(name, xmlns) { return this.getChildren(name, xmlns)[0]; }; /** * xmlns can be null **/ Element.prototype.getChildren = function(name, xmlns) { var result = []; for(var i = 0; i < this.children.length; i++) { var child = this.children[i]; if (child.getName && child.getName() == name && (!xmlns || child.getNS() == xmlns)) result.push(child); } return result; }; /** * xmlns can be null **/ Element.prototype.getChildByAttr = function(attr, val, xmlns) { return this.getChildrenByAttr(attr, val, xmlns)[0]; }; /** * xmlns can be null **/ Element.prototype.getChildrenByAttr = function(attr, val, xmlns) { var result = []; for(var i = 0; i < this.children.length; i++) { var child = this.children[i]; if (child.attrs && child.attrs[attr] == val && (!xmlns || child.getNS() == xmlns)) result.push(child); } return result; }; Element.prototype.getText = function() { var text = ""; for(var i = 0; i < this.children.length; i++) { var child = this.children[i]; if (typeof child == 'string') text += child; } return text; }; Element.prototype.getChildText = function(name) { var text = null; for(var i = 0; i < this.children.length; i++) { var child = this.children[i]; if (!text && child.name == name) { text = child.getText(); } } return text; }; /*** Builder ***/ /** returns uppermost parent */ Element.prototype.root = function() { if (this.parent) return this.parent.root(); else return this; }; Element.prototype.tree = Element.prototype.root; /** just parent or itself */ Element.prototype.up = function() { if (this.parent) return this.parent; else return this; }; /** create child node and return it */ Element.prototype.c = function(name, attrs) { return this.cnode(new Element(name, attrs)); }; Element.prototype.cnode = function(child) { this.children.push(child); child.parent = this; return child; }; /** add text node and return element */ Element.prototype.t = function(text) { this.children.push(text); return this; }; /*** Manipulation ***/ /** * Either: * el.remove(childEl); * el.remove('author', 'urn:...'); */ Element.prototype.remove = function(el, xmlns) { var filter; if (typeof el === 'string') { /* 1st parameter is tag name */ filter = function(child) { return !(child.is && child.is(el, xmlns)); }; } else { /* 1st parameter is element */ filter = function(child) { return child !== el; }; } this.children = this.children.filter(filter); return this; }; /** * To use in case you want the same XML data for separate uses. * Please refrain from this practise unless you know what you are * doing. Building XML with ltx is easy! */ Element.prototype.clone = function() { var clone = new Element(this.name, {}); for(var k in this.attrs) { if (this.attrs.hasOwnProperty(k)) clone.attrs[k] = this.attrs[k]; } for(var i = 0; i < this.children.length; i++) { var child = this.children[i]; clone.cnode(child.clone ? child.clone() : child); } return clone; }; Element.prototype.text = function(val) { if(val && this.children.length == 1){ this.children[0] = val; return this; } return this.getText(); }; Element.prototype.attr = function(attr, val) { if(val){ if(!this.attrs){ this.attrs = {}; } this.attrs[attr] = val; return this; } return this.attrs[attr]; }; /*** Serialization ***/ Element.prototype.toString = function() { var s = ""; this.write(function(c) { s += c; }); return s; }; Element.prototype.write = function(writer) { writer("<"); writer(this.name); for(var k in this.attrs) { var v = this.attrs[k]; if (v || v === '' || v === 0) { writer(" "); writer(k); writer("=\""); if (typeof v != 'string') v = v.toString(); writer(escapeXml(v)); writer("\""); } } if (this.children.length == 0) { writer("/>"); } else { writer(">"); for(var i = 0; i < this.children.length; i++) { var child = this.children[i]; /* Skip null/undefined */ if (child || child === 0) { if (child.write) child.write(writer); else if (typeof child === 'string') writer(escapeXmlText(child)); else if (child.toString) writer(escapeXmlText(child.toString())); } } writer(""); } }; function escapeXml(s) { return s. replace(/\&/g, '&'). replace(//g, '>'). replace(/"/g, '"'). replace(/'/g, '''); } function escapeXmlText(s) { return s. replace(/\&/g, '&'). replace(//g, '>'); } exports.Element = Element; exports.escapeXml = escapeXml; astro-ltx-f68025e/lib/index-browserify.js000066400000000000000000000002521173316313600204170ustar00rootroot00000000000000/* Cause browserify to bundle SAX parsers: */ //require('./sax_easysax'); //require('./sax_saxjs'); require('./sax_ltx'); /* SHIM */ module.exports = require('./index');astro-ltx-f68025e/lib/index.js000066400000000000000000000006621173316313600162330ustar00rootroot00000000000000var element = require('./element'); var parse = require('./parse'); /** * The only (relevant) data structure */ exports.Element = element.Element; /** * Helper */ exports.escapeXml = element.escapeXml; /** * DOM parser interface */ exports.parse = parse.parse; exports.Parser = parse.Parser; /** * SAX parser interface */ exports.availableSaxParsers = parse.availableSaxParsers; exports.bestSaxParser = parse.bestSaxParser; astro-ltx-f68025e/lib/parse.js000066400000000000000000000044221173316313600162340ustar00rootroot00000000000000var events = require('events'); var util = require('util'); exports.availableSaxParsers = []; exports.bestSaxParser = null; ['./sax_expat.js', './sax_ltx.js', /*'./sax_easysax.js', './sax_node-xml.js',*/ './sax_saxjs.js'].forEach(function(modName) { var mod; try { mod = require(modName); } catch (e) { /* Silently missing libraries drop; for debug: console.error(e.stack || e); */ } if (mod) { exports.availableSaxParsers.push(mod); if (!exports.bestSaxParser) exports.bestSaxParser = mod; } }); var element = require('./element'); exports.Parser = function(saxParser) { events.EventEmitter.call(this); var that = this; var parserMod = saxParser || exports.bestSaxParser; if (!parserMod) throw new Error("No SAX parser available"); this.parser = new parserMod(); var el; this.parser.addListener('startElement', function(name, attrs) { var child = new element.Element(name, attrs); if (!el) { el = child; } else { el = el.cnode(child); } }); this.parser.addListener('endElement', function(name) { if (!el) { /* Err */ } else if (el && name == el.name) { if (el.parent) el = el.parent; else if (!that.tree) { that.tree = el; el = undefined; } } }); this.parser.addListener('text', function(str) { if (el) el.t(str); }); this.parser.addListener('error', function(e) { that.error = e; that.emit('error', e); }); }; util.inherits(exports.Parser, events.EventEmitter); exports.Parser.prototype.write = function(data) { this.parser.write(data); }; exports.Parser.prototype.end = function(data) { this.parser.end(data); if (!this.error) { if (this.tree) this.emit('tree', this.tree); else this.emit('error', new Error('Incomplete document')); } }; exports.parse = function(data, saxParser) { var p = new exports.Parser(saxParser); var result = null, error = null; p.on('tree', function(tree) { result = tree; }); p.on('error', function(e) { error = e; }); p.write(data); p.end(); if (error) throw error; else return result; }; astro-ltx-f68025e/lib/sax_easysax.js000066400000000000000000000023111173316313600174450ustar00rootroot00000000000000var util = require('util'); var events = require('events'); var Easysax = require('easysax'); /** * FIXME: This SAX parser cannot be pushed to! */ var SaxEasysax = module.exports = function SaxEasysax() { events.EventEmitter.call(this); this.parser = new Easysax(); var that = this; this.parser.on('startNode', function(name, attr, uq, str, tagend) { attr = attr(); for(var k in attr) if (attr.hasOwnProperty(k)) { attr[k] = uq(attr[k]); } that.emit('startElement', name, attr); }); this.parser.on('endNode', function(name, uq, str, tagstart) { that.emit('endElement', name); }); this.parser.on('textNode', function(str, uq) { that.emit('text', uq(str)); }); this.parser.on('cdata', function(str) { that.emit('text', str); }); this.parser.on('error', function(e) { that.emit('error', e); }); // TODO: other events, esp. entityDecl (billion laughs!) }; util.inherits(SaxEasysax, events.EventEmitter); SaxEasysax.prototype.write = function(data) { if (typeof data !== 'string') data = data.toString(); this.parser.parse(data); }; SaxEasysax.prototype.end = function(data) { if (data) this.write(data); }; astro-ltx-f68025e/lib/sax_expat.js000066400000000000000000000021721173316313600171160ustar00rootroot00000000000000var util = require('util'); var events = require('events'); var expat = require('node-expat'); var SaxExpat = module.exports = function SaxExpat() { events.EventEmitter.call(this); this.parser = new expat.Parser('UTF-8'); var that = this; this.parser.on('startElement', function(name, attrs) { that.emit('startElement', name, attrs); }); this.parser.on('endElement', function(name) { that.emit('endElement', name); }); this.parser.on('text', function(str) { that.emit('text', str); }); // TODO: other events, esp. entityDecl (billion laughs!) }; util.inherits(SaxExpat, events.EventEmitter); SaxExpat.prototype.write = function(data) { if (!this.parser.parse(data, false)) { this.emit('error', new Error(this.parser.getError())); // Premature error thrown, // disable all functionality: this.write = function() { }; this.end = function() { }; } }; SaxExpat.prototype.end = function(data) { if (!this.parser.parse('', true)) this.emit('error', new Error(this.parser.getError())); else this.emit('end'); }; astro-ltx-f68025e/lib/sax_ltx.js000066400000000000000000000071021173316313600166020ustar00rootroot00000000000000var util = require('util'); var events = require('events'); const STATE_TEXT = 0, STATE_IGNORE_TAG = 1, STATE_TAG_NAME = 2, STATE_TAG = 3, STATE_ATTR_NAME = 4, STATE_ATTR_EQ = 5, STATE_ATTR_QUOT = 6, STATE_ATTR_VALUE = 7; var RE_TAG_NAME = /^[^\s\/>]+$/, RE_ATTR_NAME = /^[^\s=]+$/; var SaxLtx = module.exports = function SaxLtx() { events.EventEmitter.call(this); var state = STATE_TEXT, remainder; var tagName, attrs, endTag, selfClosing, attrQuote; var recordStart = 0; this.write = function(data) { if (typeof data !== 'string') data = data.toString(); var pos = 0; /* Anything from previous write()? */ if (remainder) { data = remainder + data; pos += remainder.length; delete remainder; } function endRecording() { if (typeof recordStart === 'number') { var recorded = data.slice(recordStart, pos); recordStart = undefined; return recorded; } } for(; pos < data.length; pos++) { var c = data.charCodeAt(pos); //console.log("state", state, "c", c, data[pos]); switch(state) { case STATE_TEXT: if (c === 60 /* < */) { var text = endRecording(); if (text) this.emit('text', unescapeXml(text)); state = STATE_TAG_NAME; recordStart = pos + 1; attrs = {}; } break; case STATE_TAG_NAME: if (c === 47 /* / */ && recordStart === pos) { recordStart = pos + 1; endTag = true; } else if (c === 33 /* ! */ || c === 63 /* ? */) { recordStart = undefined; state = STATE_IGNORE_TAG; } else if (c <= 32 || c === 47 /* / */ || c === 62 /* > */) { tagName = endRecording(); pos--; state = STATE_TAG; } break; case STATE_IGNORE_TAG: if (c === 62 /* > */) { state = STATE_TEXT; } break; case STATE_TAG: if (c === 62 /* > */) { if (!endTag) { this.emit('startElement', tagName, attrs); if (selfClosing) this.emit('endElement', tagName); } else this.emit('endElement', tagName); tagName = undefined; attrs = undefined; endTag = undefined; selfClosing = undefined; state = STATE_TEXT; recordStart = pos + 1; } else if (c === 47 /* / */) { selfClosing = true; } else if (c > 32) { recordStart = pos; state = STATE_ATTR_NAME; } break; case STATE_ATTR_NAME: if (c <= 32 || c === 61 /* = */) { attrName = endRecording(); pos--; state = STATE_ATTR_EQ; } break; case STATE_ATTR_EQ: if (c === 61 /* = */) { state = STATE_ATTR_QUOT; } break; case STATE_ATTR_QUOT: if (c === 34 /* " */ || c === 39 /* ' */) { attrQuote = c; state = STATE_ATTR_VALUE; recordStart = pos + 1; } break; case STATE_ATTR_VALUE: if (c === attrQuote) { var value = unescapeXml(endRecording()); attrs[attrName] = value; attrName = undefined; state = STATE_TAG; } break; } } if (typeof recordStart === 'number' && recordStart <= data.length) { remainder = data.slice(recordStart); recordStart = 0; } }; /*var origEmit = this.emit; this.emit = function() { console.log('ltx', arguments); origEmit.apply(this, arguments); };*/ }; util.inherits(SaxLtx, events.EventEmitter); SaxLtx.prototype.end = function(data) { if (data) this.write(data); /* Uh, yeah */ this.write = function() { }; }; function unescapeXml(s) { return s. replace(/\&/g, '&'). replace(/\</g, '<'). replace(/\>/g, '>'). replace(/\"/g, '"'). replace(/\'/g, '\''); } astro-ltx-f68025e/lib/sax_node-xml.js000066400000000000000000000032551173316313600175230ustar00rootroot00000000000000var util = require('util'); var events = require('events'); var xml = require('node-xml'); /** * This cannot be used as long as node-xml starts parsing only after * setTimeout(f, 0); */ var SaxNodeXML = module.exports = function SaxNodeXML() { events.EventEmitter.call(this); var that = this; this.parser = new xml.SaxParser(function(handler) { handler.onStartElementNS(function(elem, attrs, prefix, uri, namespaces) { var i, attrsHash = {}; if (prefix) elem = prefix + ":" + elem; for(i = 0; i < attrs.length; i++) { var attr = attrs[i]; attrsHash[attr[0]] = unescapeXml(attr[1]); } for(i = 0; i < namespaces.length; i++) { var namespace = namespaces[i]; var k = !namespace[0] ? "xmlns" : "xmlns:" + namespace[0]; attrsHash[k] = unescapeXml(namespace[1]); } that.emit('startElement', elem, attrsHash); }); handler.onEndElementNS(function(elem, prefix, uri) { if (prefix) elem = prefix + ":" + elem; that.emit('endElement', elem); }); handler.onCharacters(function(str) { that.emit('text', str); }); handler.onCdata(function(str) { that.emit('text', str); }); handler.onError(function(e) { that.emit('error', e); }); // TODO: other events, esp. entityDecl (billion laughs!) }); }; util.inherits(SaxNodeXML, events.EventEmitter); SaxNodeXML.prototype.write = function(data) { this.parser.parseString(data); }; SaxNodeXML.prototype.end = function(data) { if (data) this.write(data); }; function unescapeXml(s) { return s. replace(/\&/g, '&'). replace(/\</g, '<'). replace(/\>/g, '>'). replace(/\"/g, '"'). replace(/\'/g, '\''); } astro-ltx-f68025e/lib/sax_saxjs.js000066400000000000000000000020011173316313600171140ustar00rootroot00000000000000var util = require('util'); var events = require('events'); var sax = require('sax'); var SaxSaxjs = module.exports = function SaxSaxjs() { events.EventEmitter.call(this); this.parser = sax.parser(true); var that = this; this.parser.onopentag = function(a) { that.emit('startElement', a.name, a.attributes); }; this.parser.onclosetag = function(name) { that.emit('endElement', name); }; this.parser.ontext = function(str) { that.emit('text', str); }; this.parser.onend = function() { that.emit('end'); }; this.parser.onerror = function(e) { that.emit('error', e); }; // TODO: other events, esp. entityDecl (billion laughs!) }; util.inherits(SaxSaxjs, events.EventEmitter); SaxSaxjs.prototype.write = function(data) { if (typeof data !== 'string') data = data.toString(); this.parser.write(data); }; SaxSaxjs.prototype.end = function(data) { if (data) this.parser.write(data); this.parser.close(); }; astro-ltx-f68025e/package.json000066400000000000000000000016521173316313600163060ustar00rootroot00000000000000{ "name": "ltx" ,"version": "0.2.0" ,"main": "./lib/index" ,"browserify": "./lib/index-browserify.js" ,"description": "" ,"author": "Stephan Maka" ,"dependencies": {"node-expat": ">=1.2.0" ,"sax": ">=0.3.5" } ,"repositories": [{"type": "git" ,"path": "http://github.com/astro/ltx.git" }] ,"homepage": "http://github.com/astro/ltx" ,"bugs": "http://github.com/astro/ltx/issues" ,"maintainers": [{"name": "Astro" ,"email": "astro@spaceboyz.net" ,"web": "http://spaceboyz.net/~astro/" }] ,"contributors": ["Stephan Maka", "Will Fife", "Markus Kohlhase"] ,"licenses": [{"type": "MIT"}] ,"engine": "node" ,"devDependencies": {"vows": ">=0.5.12" ,"easysax": ">=0.1.7" ,"node-xml": ">=1.0.1" ,"Strophe.js": "https://github.com/metajack/strophejs/tarball/master" ,"request-animation-frame": ">=0.1.0" ,"browserify": ">=1.10.4" } ,"scripts": {"test":"vows --spec"} } astro-ltx-f68025e/test/000077500000000000000000000000001173316313600147735ustar00rootroot00000000000000astro-ltx-f68025e/test/element-test.js000066400000000000000000000103141173316313600177360ustar00rootroot00000000000000var vows = require('vows'), assert = require('assert'), ltx = require('./../lib/index'); vows.describe('ltx').addBatch({ 'serialization': { 'serialize an element': function() { var e = new ltx.Element('e'); assert.equal(e.toString(), ''); }, 'serialize an element with attributes': function() { var e = new ltx.Element('e', { a1: 'foo' }); assert.equal(e.toString(), ''); }, 'serialize an element with attributes to entities': function() { var e = new ltx.Element('e', { a1: '"well"' }); assert.equal(e.toString(), ''); }, 'serialize an element with text': function() { var e = new ltx.Element('e').t('bar').root(); assert.equal(e.toString(), 'bar'); }, 'serialize an element with text to entities': function() { var e = new ltx.Element('e').t('1 < 2').root(); assert.equal(e.toString(), '1 < 2'); }, 'serialize an element with a number attribute': function() { var e = new ltx.Element('e', { a: 23 }); assert.equal(e.toString(), ''); }, 'serialize an element with number contents': function() { var e = new ltx.Element('e'); e.c('foo').t(23); e.c('bar').t(0); assert.equal(e.toString(), '230'); }, 'serialize with undefined attribute': function() { var e = new ltx.Element('e', { foo: undefined }); assert.equal(e.toString(), ''); }, 'serialize with null attribute': function() { var e = new ltx.Element('e', { foo: null }); assert.equal(e.toString(), ''); }, 'serialize with number attribute': function() { var e = new ltx.Element('e', { foo: 23, bar: 0 }); var s = e.toString(); assert.ok(s.match(/foo="23"/)); assert.ok(s.match(/bar="0"/)); }, 'serialize with undefined child': function() { var e = new ltx.Element('e'); e.children = [undefined]; assert.equal(e.toString(), ''); }, 'serialize with null child': function() { var e = new ltx.Element('e'); e.children = [null]; assert.equal(e.toString(), ''); } }, 'remove': { 'by element': function() { var el = new ltx.Element('e'). c('c').c('x').up().up(). c('c2').up(). c('c').up(); el.remove(el.getChild('c')); assert.equal(el.getChildren('c').length, 1); assert.equal(el.getChildren('c2').length, 1); }, 'by name': function() { var el = new ltx.Element('e'). c('c').up(). c('c2').up(). c('c').up(); el.remove('c'); assert.equal(el.getChildren('c').length, 0); assert.equal(el.getChildren('c2').length, 1); } }, 'clone': { 'clones': function() { var orig = new ltx.Element('msg', { type: 'get' }). c('content').t('foo').root(); var clone = orig.clone(); assert.equal(clone.name, orig.name); assert.equal(clone.attrs.type, orig.attrs.type); assert.equal(clone.attrs.to, orig.attrs.to); assert.equal(clone.children.length, orig.children.length); assert.equal(clone.getChildText('content'), orig.getChildText('content')); assert.equal(orig.getChild('content').up(), orig); assert.equal(clone.getChild('content').up(), clone); }, 'mod attr': function() { var orig = new ltx.Element('msg', { type: 'get' }); var clone = orig.clone(); clone.attrs.type += '-result'; assert.equal(orig.attrs.type, 'get'); assert.equal(clone.attrs.type, 'get-result'); }, 'rm attr': function() { var orig = new ltx.Element('msg', { from: 'me' }); var clone = orig.clone(); delete clone.attrs.from; clone.attrs.to = 'you'; assert.equal(orig.attrs.from, 'me'); assert.equal(orig.attrs.to, undefined); assert.equal(clone.attrs.from, undefined); assert.equal(clone.attrs.to, 'you'); }, 'mod child': function() { var orig = new ltx.Element('msg', { type: 'get' }). c('content').t('foo').root(); var clone = orig.clone(); clone.getChild('content'). t('bar'). name = 'description'; assert.equal(orig.children[0].name, 'content'); assert.equal(orig.getChildText('content'), 'foo'); assert.equal(clone.children[0].name, 'description'); assert.equal(clone.getChildText('description'), 'foobar'); } } }).export(module); astro-ltx-f68025e/test/parse-test.js000066400000000000000000000077211173316313600174270ustar00rootroot00000000000000var vows = require('vows'), assert = require('assert'), ltx = require('./../lib/index'); ltx.availableSaxParsers.forEach(function(saxParser) { var parse = function(s) { return ltx.parse(s, saxParser); }; vows.describe('ltx with ' + saxParser.name).addBatch({ 'DOM parsing': { 'simple document': function() { var el = parse(''); assert.equal(el.name, 'root'); assert.equal(0, el.children.length); }, 'text with commas': function() { var el = parse("sa'sa'1'sasa"); assert.equal("sa'sa'1'sasa", el.getText()); }, 'text with entities': function() { var el = parse("<>&"'"); assert.equal("<>&\"'", el.getText()); }, 'attribute with entities': function() { var el = parse(""); assert.equal("<>&\"'", el.attrs.title); }, 'erroneous document raises error': function() { assert.throws(function() { parse(''); }); }, 'incomplete document raises error': function() { assert.throws(function() { parse(''); }); }, 'namespace declaration': function() { var el = parse(""); assert.equal(el.name, 'root'); assert.equal(el.attrs.xmlns, 'https://github.com/astro/ltx'); assert.ok(el.is('root', 'https://github.com/astro/ltx')); }, 'namespace declaration with prefix': function() { var el = parse(""); assert.equal(el.name, 'x:root'); assert.equal(el.getName(), 'root'); assert.ok(el.is('root', 'https://github.com/astro/ltx')); }, 'buffer': function() { var buf = new Buffer(''); var el = parse(buf); assert.equal(el.name, 'root'); assert.equal(0, el.children.length); }, 'utf-8 text': function() { var el = parse('Möwe'); assert.equal(el.name, 'text'); assert.equal(el.getText(), "Möwe"); }, 'iso8859-1 text': function() { var el = parse('M\xF6we'); assert.equal(el.name, 'text'); assert.equal(el.getText(), "Möwe"); } }, 'SAX parsing': { 'XMPP stream': function() { var parser = new saxParser(); var events = []; parser.on('startElement', function(name) { events.push({ start: name }); }); parser.on('endElement', function(name) { events.push({ end: name }); }); parser.on('text', function(s) { events.push({ text: s }); }); parser.write("<"); assert.equal(events.length, 1); parser.write("stream:features>PLAINDIGEST-MD5= 9); parser.write("mechanism>SCRAM-SHA-1"); assert.equal(events.length, 15); parser.write(""); assert.equal(events.length, 18); } } }).export(module); });