Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

configurable sax parsers (expat & saxjs backends)

  • Loading branch information...
commit b6f9e1ba12a1bf53cea436fc22935447908cb7f8 1 parent bf56718
Astro authored March 15, 2012
2  lib/index.js
@@ -6,3 +6,5 @@ exports.escapeXml = element.escapeXml;
6 6
 
7 7
 exports.Parser = parse.Parser;
8 8
 exports.parse = parse.parse;
  9
+exports.availableSaxParsers = parse.availableSaxParsers;
  10
+exports.bestSaxParser = parse.bestSaxParser;
57  lib/parse.js
... ...
@@ -1,17 +1,31 @@
1 1
 var events = require('events');
2  
-var util;
3  
-try {
4  
-    util = require('util');
5  
-} catch(e) {
6  
-    util = require('sys');
7  
-}
8  
-var expat = require('node-expat');
  2
+var util = require('util');
  3
+
  4
+exports.availableSaxParsers = [];
  5
+exports.bestSaxParser = null;
  6
+['./sax_expat', './sax_saxjs'].forEach(function(modName) {
  7
+    var mod;
  8
+    try {
  9
+	mod = require(modName);
  10
+    } catch (e) {
  11
+	console.error(e);
  12
+    }
  13
+    if (mod) {
  14
+	exports.availableSaxParsers.push(mod);
  15
+	if (!exports.bestSaxParser)
  16
+	    exports.bestSaxParser = mod;
  17
+    }
  18
+});
9 19
 var element = require('./element');
10 20
 
11  
-exports.Parser = function() {
  21
+exports.Parser = function(saxParser) {
  22
+    events.EventEmitter.call(this);
12 23
     var that = this;
13 24
 
14  
-    this.parser = new expat.Parser('UTF-8');
  25
+    var parserMod = saxParser || exports.bestSaxParser;
  26
+    if (!parserMod)
  27
+	throw new Error("No SAX parser available");
  28
+    this.parser = new parserMod();
15 29
 
16 30
     var el;
17 31
     this.parser.addListener('startElement', function(name, attrs) {
@@ -38,24 +52,21 @@ exports.Parser = function() {
38 52
         if (el)
39 53
             el.t(str);
40 54
     });
  55
+    this.parser.addListener('error', function(e) {
  56
+	that.error = e;
  57
+	that.emit('error', e);
  58
+    });
41 59
 };
42 60
 util.inherits(exports.Parser, events.EventEmitter);
43 61
 
44 62
 exports.Parser.prototype.write = function(data) {
45  
-    if (!this.parser.parse(data, false)) {
46  
-        this.emit('error', new Error(this.parser.getError()));
47  
-
48  
-        // Premature error thrown,
49  
-        // disable all functionality:
50  
-        this.write = function() { };
51  
-        this.end = function() { };
52  
-    }
  63
+    this.parser.write(data);
53 64
 };
54 65
 
55  
-exports.Parser.prototype.end = function() {
56  
-    if (!this.parser.parse('', true))
57  
-        this.emit('error', new Error(this.parser.getError()));
58  
-    else {
  66
+exports.Parser.prototype.end = function(data) {
  67
+    this.parser.end(data);
  68
+
  69
+    if (!this.error) {
59 70
 	if (this.tree)
60 71
 	    this.emit('tree', this.tree);
61 72
 	else
@@ -63,8 +74,8 @@ exports.Parser.prototype.end = function() {
63 74
     }
64 75
 };
65 76
 
66  
-exports.parse = function(data) {
67  
-    var p = new exports.Parser();
  77
+exports.parse = function(data, saxParser) {
  78
+    var p = new exports.Parser(saxParser);
68 79
     var result = null, error = null;
69 80
 
70 81
     p.on('tree', function(tree) {
39  lib/sax_expat.js
... ...
@@ -0,0 +1,39 @@
  1
+var util = require('util');
  2
+var events = require('events');
  3
+var expat = require('node-expat');
  4
+
  5
+var SaxExpat = module.exports = function SaxExpat() {
  6
+    events.EventEmitter.call(this);
  7
+    this.parser = new expat.Parser('UTF-8');
  8
+
  9
+    var that = this;
  10
+    this.parser.on('startElement', function(name, attrs) {
  11
+        that.emit('startElement', name, attrs);
  12
+    });
  13
+    this.parser.on('endElement', function(name) {
  14
+        that.emit('endElement', name);
  15
+    });
  16
+    this.parser.on('text', function(str) {
  17
+        that.emit('text', str);
  18
+    });
  19
+    // TODO: other events, esp. entityDecl (billion laughs!)
  20
+};
  21
+util.inherits(SaxExpat, events.EventEmitter);
  22
+
  23
+SaxExpat.prototype.write = function(data) {
  24
+    if (!this.parser.parse(data, false)) {
  25
+        this.emit('error', new Error(this.parser.getError()));
  26
+
  27
+        // Premature error thrown,
  28
+        // disable all functionality:
  29
+        this.write = function() { };
  30
+        this.end = function() { };
  31
+    }
  32
+};
  33
+
  34
+SaxExpat.prototype.end = function(data) {
  35
+    if (!this.parser.parse('', true))
  36
+        this.emit('error', new Error(this.parser.getError()));
  37
+    else
  38
+        this.emit('end');
  39
+};
37  lib/sax_saxjs.js
... ...
@@ -0,0 +1,37 @@
  1
+var util = require('util');
  2
+var events = require('events');
  3
+var sax = require('sax');
  4
+
  5
+var SaxSaxjs = module.exports = function SaxSaxjs() {
  6
+    events.EventEmitter.call(this);
  7
+    this.parser = sax.parser(true);
  8
+
  9
+    var that = this;
  10
+    this.parser.onopentag = function(a) {
  11
+        that.emit('startElement', a.name, a.attributes);
  12
+    };
  13
+    this.parser.onclosetag = function(name) {
  14
+        that.emit('endElement', name);
  15
+    };
  16
+    this.parser.ontext = function(str) {
  17
+        that.emit('text', str);
  18
+    };
  19
+    this.parser.onend = function() {
  20
+        that.emit('end');
  21
+    };
  22
+    this.parser.onerror = function(e) {
  23
+        that.emit('error', e);
  24
+    };
  25
+    // TODO: other events, esp. entityDecl (billion laughs!)
  26
+};
  27
+util.inherits(SaxSaxjs, events.EventEmitter);
  28
+
  29
+SaxSaxjs.prototype.write = function(data) {
  30
+    this.parser.write(data);
  31
+};
  32
+
  33
+SaxSaxjs.prototype.end = function(data) {
  34
+    if (data)
  35
+        this.parser.write(data);
  36
+    this.parser.close();
  37
+};
61  test/parse-test.js
@@ -2,26 +2,43 @@ var vows = require('vows'),
2 2
 assert = require('assert'),
3 3
 ltx = require('./../lib/index');
4 4
 
5  
-vows.describe('ltx').addBatch({
6  
-    'parsing': {
7  
-        'simple document': function() {
8  
-            var el = ltx.parse('<root/>');
9  
-            assert.equal(el.name, 'root');
10  
-            assert.equal(0, el.children.length);
11  
-        },
12  
-	'text with commas': function() {
13  
-	    var el = ltx.parse("<body>sa'sa'1'sasa</body>");
14  
-	    assert.equal("sa'sa'1'sasa", el.getText());
15  
-	},
16  
-        'erroneous document raises error': function() {
17  
-            assert.throws(function() {
18  
-                ltx.parse('<root></toor>');
19  
-            });
20  
-        },
21  
-        'incomplete document raises error': function() {
22  
-            assert.throws(function() {
23  
-                ltx.parse('<root>');
24  
-            });
  5
+ltx.availableSaxParsers.forEach(function(saxParser) {
  6
+    var parse = function(s) {
  7
+        return ltx.parse(s, saxParser);
  8
+    };
  9
+    vows.describe('ltx with ' + saxParser.name).addBatch({
  10
+        'parsing': {
  11
+            'simple document': function() {
  12
+                var el = parse('<root/>');
  13
+                assert.equal(el.name, 'root');
  14
+                assert.equal(0, el.children.length);
  15
+            },
  16
+            'text with commas': function() {
  17
+                var el = parse("<body>sa'sa'1'sasa</body>");
  18
+                assert.equal("sa'sa'1'sasa", el.getText());
  19
+            },
  20
+            'erroneous document raises error': function() {
  21
+                assert.throws(function() {
  22
+                    parse('<root></toor>');
  23
+                });
  24
+            },
  25
+            'incomplete document raises error': function() {
  26
+                assert.throws(function() {
  27
+                    parse('<root>');
  28
+                });
  29
+            },
  30
+            'namespace declaration': function() {
  31
+                var el = parse("<root xmlns='https://github.com/astro/ltx'/>");
  32
+                assert.equal(el.name, 'root');
  33
+                assert.equal(el.attrs.xmlns, 'https://github.com/astro/ltx');
  34
+                assert.ok(el.is('root', 'https://github.com/astro/ltx'));
  35
+            },
  36
+            'namespace declaration with prefix': function() {
  37
+                var el = parse("<x:root xmlns:x='https://github.com/astro/ltx'/>");
  38
+                assert.equal(el.name, 'x:root');
  39
+                assert.equal(el.getName(), 'root');
  40
+                assert.ok(el.is('root', 'https://github.com/astro/ltx'));
  41
+            }
25 42
         }
26  
-    }
27  
-}).export(module);
  43
+    }).export(module);
  44
+});

0 notes on commit b6f9e1b

Please sign in to comment.
Something went wrong with that request. Please try again.