Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

configurable sax parsers (expat & saxjs backends)

  • Loading branch information...
commit b6f9e1ba12a1bf53cea436fc22935447908cb7f8 1 parent bf56718
@astro astro authored
View
2  lib/index.js
@@ -6,3 +6,5 @@ exports.escapeXml = element.escapeXml;
exports.Parser = parse.Parser;
exports.parse = parse.parse;
+exports.availableSaxParsers = parse.availableSaxParsers;
+exports.bestSaxParser = parse.bestSaxParser;
View
57 lib/parse.js
@@ -1,17 +1,31 @@
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', './sax_saxjs'].forEach(function(modName) {
+ var mod;
+ try {
+ mod = require(modName);
+ } catch (e) {
+ console.error(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) {
@@ -38,24 +52,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 +74,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) {
View
39 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');
+};
View
37 lib/sax_saxjs.js
@@ -0,0 +1,37 @@
+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) {
+ this.parser.write(data);
+};
+
+SaxSaxjs.prototype.end = function(data) {
+ if (data)
+ this.parser.write(data);
+ this.parser.close();
+};
View
61 test/parse-test.js
@@ -2,26 +2,43 @@ 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);
- },
- '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>');
- });
+ltx.availableSaxParsers.forEach(function(saxParser) {
+ var parse = function(s) {
+ return ltx.parse(s, saxParser);
+ };
+ vows.describe('ltx with ' + saxParser.name).addBatch({
+ '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());
+ },
+ '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'));
+ }
}
- }
-}).export(module);
+ }).export(module);
+});
Please sign in to comment.
Something went wrong with that request. Please try again.