[Pkg-javascript-commits] [ltx] 46/80: Imported Upstream version 0.2.0
Jonas Smedegaard
dr at jones.dk
Sun Feb 28 10:50:12 UTC 2016
This is an automated email from the git hooks/post-receive script.
js pushed a commit to branch master
in repository ltx.
commit 8e0f7d8e318af106c9dfc68cfc89c5a07543af49
Author: Jonas Smedegaard <dr at jones.dk>
Date: Sun Mar 25 12:43:27 2012 +0200
Imported Upstream version 0.2.0
---
README.markdown | 46 +++++++++++++-
benchmark/.gitignore | 1 +
benchmark/benchmark.js | 144 +++++++++++++++++++++++++++++++++++++++++++
benchmark/build.js | 14 +++++
benchmark/index.html | 9 +++
lib/element.js | 4 +-
lib/index-browserify.js | 7 +++
lib/index.js | 16 ++++-
lib/parse.js | 61 +++++++++++--------
lib/sax_easysax.js | 45 ++++++++++++++
lib/sax_expat.js | 39 ++++++++++++
lib/sax_ltx.js | 159 ++++++++++++++++++++++++++++++++++++++++++++++++
lib/sax_node-xml.js | 63 +++++++++++++++++++
lib/sax_saxjs.js | 39 ++++++++++++
package.json | 12 +++-
test/parse-test.js | 117 ++++++++++++++++++++++++++++-------
16 files changed, 723 insertions(+), 53 deletions(-)
diff --git a/README.markdown b/README.markdown
index b4ac945..2886ec1 100644
--- a/README.markdown
+++ b/README.markdown
@@ -2,11 +2,45 @@
* *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("<document/>")
+
+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
-* `Element(name, attrs?)`: constructor
* `is(name, xmlns?)`: check
* `getName()`: name without ns prefix
* `getNS()`: element's xmlns, respects prefixes and searches upwards
@@ -31,6 +65,7 @@
## 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
@@ -39,7 +74,14 @@
## Building XML Elements
-This resembles strophejs a bit.
+ 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
diff --git a/benchmark/.gitignore b/benchmark/.gitignore
new file mode 100644
index 0000000..012a3cd
--- /dev/null
+++ b/benchmark/.gitignore
@@ -0,0 +1 @@
+index.js
diff --git a/benchmark/benchmark.js b/benchmark/benchmark.js
new file mode 100644
index 0000000..b725272
--- /dev/null
+++ b/benchmark/benchmark.js
@@ -0,0 +1,144 @@
+if (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 = "<div style='float: left; min-width: 25em'><h2>" + this.name + "</h2><dl>";
+ for(var k in this.timings) {
+ var t = this.timings[k].t / this.timings[k].i;
+ html += "<dt>" + k + "</dt><dd class='" + k + "'>" + t + " ms </dd>";
+ }
+ html += "</dl></div>\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 = [
+ "<message/>",
+ "<message foo='bar'/>",
+ "<message foo='bar'><foo/><bar>fnord<baz/>fnord</bar></message>"
+];
+messages[3] = "<message>";
+for(var i = 0; i < 10; i++) {
+ messages[3] += "fnord fnord fnord<foo bar='baz'>";
+}
+for(var i = 0; i < 10; i++) {
+ messages[3] += "</foo>fnordfnordfnordfnord<quux foo='bar'/>";
+}
+messages[3] += "</message>";
+
+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 = "<style>.parse0, .parse1, .parse2, .parse3 { color: red; } .serialize1, .serialize2, .serialize3, .serialize4 { color: blue; }</style>\n" +
+ "<h1>Iteration " + iteration + "<h1>\n";
+ tests.forEach(function(test) {
+ document.body.innerHTML += test.report() + "<br>";
+ });
+ requestAnimationFrame(runTests);
+ } else {
+ console.log("Iteration " + iteration);
+ tests.forEach(function(test) {
+ console.log(test.report());
+ });
+ process.nextTick(runTests);
+ }
+}
+
+setTimeout(function() {
+ runTests();
+}, 1000);
diff --git a/benchmark/build.js b/benchmark/build.js
new file mode 100644
index 0000000..8fd00cd
--- /dev/null
+++ b/benchmark/build.js
@@ -0,0 +1,14 @@
+var 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());
diff --git a/benchmark/index.html b/benchmark/index.html
new file mode 100644
index 0000000..44a50c8
--- /dev/null
+++ b/benchmark/index.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <script src="index.js"></script>
+ </head>
+ <body>
+ </body>
+</html>
+
diff --git a/lib/element.js b/lib/element.js
index 010e358..12b2367 100644
--- a/lib/element.js
+++ b/lib/element.js
@@ -280,14 +280,14 @@ function escapeXml(s) {
replace(/>/g, '>').
replace(/"/g, '"').
replace(/'/g, ''');
-};
+}
function escapeXmlText(s) {
return s.
replace(/\&/g, '&').
replace(/</g, '<').
replace(/>/g, '>');
-};
+}
exports.Element = Element;
exports.escapeXml = escapeXml;
diff --git a/lib/index-browserify.js b/lib/index-browserify.js
new file mode 100644
index 0000000..5c44ae7
--- /dev/null
+++ b/lib/index-browserify.js
@@ -0,0 +1,7 @@
+/* Cause browserify to bundle SAX parsers: */
+//require('./sax_easysax');
+//require('./sax_saxjs');
+require('./sax_ltx');
+
+/* SHIM */
+module.exports = require('./index');
\ No newline at end of file
diff --git a/lib/index.js b/lib/index.js
index a9614b5..68de945 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -1,8 +1,22 @@
var element = require('./element');
var parse = require('./parse');
+/**
+ * The only (relevant) data structure
+ */
exports.Element = element.Element;
+/**
+ * Helper
+ */
exports.escapeXml = element.escapeXml;
-exports.Parser = parse.Parser;
+/**
+ * DOM parser interface
+ */
exports.parse = parse.parse;
+exports.Parser = parse.Parser;
+/**
+ * SAX parser interface
+ */
+exports.availableSaxParsers = parse.availableSaxParsers;
+exports.bestSaxParser = parse.bestSaxParser;
diff --git a/lib/parse.js b/lib/parse.js
index 0265c8f..29cdfb1 100644
--- a/lib/parse.js
+++ b/lib/parse.js
@@ -1,17 +1,33 @@
var events = require('events');
-var util;
-try {
- util = require('util');
-} catch(e) {
- util = require('sys');
-}
-var expat = require('node-expat');
+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() {
+exports.Parser = function(saxParser) {
+ events.EventEmitter.call(this);
var that = this;
- this.parser = new expat.Parser('UTF-8');
+ 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) {
@@ -22,7 +38,7 @@ exports.Parser = function() {
el = el.cnode(child);
}
});
- this.parser.addListener('endElement', function(name, attrs) {
+ this.parser.addListener('endElement', function(name) {
if (!el) {
/* Err */
} else if (el && name == el.name) {
@@ -38,24 +54,21 @@ exports.Parser = function() {
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) {
- 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() { };
- }
+ this.parser.write(data);
};
-exports.Parser.prototype.end = function() {
- if (!this.parser.parse('', true))
- this.emit('error', new Error(this.parser.getError()));
- else {
+exports.Parser.prototype.end = function(data) {
+ this.parser.end(data);
+
+ if (!this.error) {
if (this.tree)
this.emit('tree', this.tree);
else
@@ -63,8 +76,8 @@ exports.Parser.prototype.end = function() {
}
};
-exports.parse = function(data) {
- var p = new exports.Parser();
+exports.parse = function(data, saxParser) {
+ var p = new exports.Parser(saxParser);
var result = null, error = null;
p.on('tree', function(tree) {
diff --git a/lib/sax_easysax.js b/lib/sax_easysax.js
new file mode 100644
index 0000000..a8b7f60
--- /dev/null
+++ b/lib/sax_easysax.js
@@ -0,0 +1,45 @@
+var 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);
+};
diff --git a/lib/sax_expat.js b/lib/sax_expat.js
new file mode 100644
index 0000000..9d05cb2
--- /dev/null
+++ b/lib/sax_expat.js
@@ -0,0 +1,39 @@
+var 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');
+};
diff --git a/lib/sax_ltx.js b/lib/sax_ltx.js
new file mode 100644
index 0000000..cb3b709
--- /dev/null
+++ b/lib/sax_ltx.js
@@ -0,0 +1,159 @@
+var 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, '\'');
+}
diff --git a/lib/sax_node-xml.js b/lib/sax_node-xml.js
new file mode 100644
index 0000000..16c64cc
--- /dev/null
+++ b/lib/sax_node-xml.js
@@ -0,0 +1,63 @@
+var 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, '\'');
+}
diff --git a/lib/sax_saxjs.js b/lib/sax_saxjs.js
new file mode 100644
index 0000000..1a27b6e
--- /dev/null
+++ b/lib/sax_saxjs.js
@@ -0,0 +1,39 @@
+var 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();
+};
diff --git a/package.json b/package.json
index bc5e9b0..59b7838 100644
--- a/package.json
+++ b/package.json
@@ -1,9 +1,11 @@
{ "name": "ltx"
-,"version": "0.1.3"
+,"version": "0.2.0"
,"main": "./lib/index"
+,"browserify": "./lib/index-browserify.js"
,"description": "<xml for=\"node.js\">"
,"author": "Stephan Maka"
,"dependencies": {"node-expat": ">=1.2.0"
+ ,"sax": ">=0.3.5"
}
,"repositories": [{"type": "git"
,"path": "http://github.com/astro/ltx.git"
@@ -17,6 +19,12 @@
,"contributors": ["Stephan Maka", "Will Fife", "Markus Kohlhase"]
,"licenses": [{"type": "MIT"}]
,"engine": "node"
-,"devDependencies": {"vows": ">=0.5.12"}
+,"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"}
}
diff --git a/test/parse-test.js b/test/parse-test.js
index 0f05708..a9c0b44 100644
--- a/test/parse-test.js
+++ b/test/parse-test.js
@@ -2,26 +2,99 @@ var vows = require('vows'),
assert = require('assert'),
ltx = require('./../lib/index');
-vows.describe('ltx').addBatch({
- 'parsing': {
- 'simple document': function() {
- var el = ltx.parse('<root/>');
- assert.equal(el.name, 'root');
- assert.equal(0, el.children.length);
+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('<root/>');
+ assert.equal(el.name, 'root');
+ assert.equal(0, el.children.length);
+ },
+ 'text with commas': function() {
+ var el = parse("<body>sa'sa'1'sasa</body>");
+ assert.equal("sa'sa'1'sasa", el.getText());
+ },
+ 'text with entities': function() {
+ var el = parse("<body><>&"'</body>");
+ assert.equal("<>&\"'", el.getText());
+ },
+ 'attribute with entities': function() {
+ var el = parse("<body title='<>&"''/>");
+ assert.equal("<>&\"'", el.attrs.title);
+ },
+ 'erroneous document raises error': function() {
+ assert.throws(function() {
+ parse('<root></toor>');
+ });
+ },
+ 'incomplete document raises error': function() {
+ assert.throws(function() {
+ parse('<root>');
+ });
+ },
+ 'namespace declaration': function() {
+ var el = parse("<root xmlns='https://github.com/astro/ltx'/>");
+ 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("<x:root xmlns:x='https://github.com/astro/ltx'/>");
+ 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('<root/>');
+ var el = parse(buf);
+ assert.equal(el.name, 'root');
+ assert.equal(0, el.children.length);
+ },
+ 'utf-8 text': function() {
+ var el = parse('<?xml version="1.0" encoding="utf-8"?><text>Möwe</text>');
+ assert.equal(el.name, 'text');
+ assert.equal(el.getText(), "Möwe");
+ },
+ 'iso8859-1 text': function() {
+ var el = parse('<?xml version="1.0" encoding="iso-8859-1"?><text>M\xF6we</text>');
+ assert.equal(el.name, 'text');
+ assert.equal(el.getText(), "Möwe");
+ }
},
- 'text with commas': function() {
- var el = ltx.parse("<body>sa'sa'1'sasa</body>");
- assert.equal("sa'sa'1'sasa", el.getText());
- },
- 'erroneous document raises error': function() {
- assert.throws(function() {
- ltx.parse('<root></toor>');
- });
- },
- 'incomplete document raises error': function() {
- assert.throws(function() {
- ltx.parse('<root>');
- });
- }
- }
-}).export(module);
+ '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("<?xml version='1.0'?><stream:stream xmlns='jabber:client'");
+ parser.write(" xmlns:stream='http://etherx.jabber.org/streams' id='5568");
+ assert.equal(events.length, 0);
+ parser.write("90365' from='jabber.ccc.de' version='1.0' xml:lang='en'><");
+ assert.equal(events.length, 1);
+ parser.write("stream:features><starttls xmlns='urn:ietf:params:xml:ns:x");
+ assert.equal(events.length, 2);
+ parser.write("mpp-tls'/><mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-");
+ assert.equal(events.length, 4);
+ parser.write("sasl'><mechanism>PLAIN</mechanism><mechanism>DIGEST-MD5</");
+ assert.ok(events.length >= 9);
+ parser.write("mechanism><mechanism>SCRAM-SHA-1</mechanism></mechanisms>");
+ assert.equal(events.length, 15);
+ parser.write("<register xmlns='http://jabber.org/features/iq-register'/");
+ assert.equal(events.length, 15);
+ parser.write("></stream:features>");
+ assert.equal(events.length, 18);
+ }
+ }
+ }).export(module);
+});
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/ltx.git
More information about the Pkg-javascript-commits
mailing list