sax
Advanced tools
Comparing version 0.2.3 to 0.2.4
185
lib/sax.js
@@ -23,3 +23,3 @@ // wrapper for non-node envs | ||
"procInstName", "procInstBody", "entity", "attribName", | ||
"attribValue", "cdata" | ||
"attribValue", "cdata", "script" | ||
] | ||
@@ -40,2 +40,4 @@ | ||
if (this.opt.xmlns) this.ns = {} // NS bindings stacks: prefix -> [uri, uri...] | ||
// mostly just for error reporting | ||
@@ -66,2 +68,7 @@ this.position = this.line = this.column = 0 | ||
case "script": | ||
emitNode(parser, "onscript", parser.script) | ||
parser.script = "" | ||
break | ||
default: | ||
@@ -111,2 +118,4 @@ error(parser, "Max buffer length exceeded: "+buffers[i]) | ||
, "closecdata" | ||
, "opennamespace" | ||
, "closenamespace" | ||
, "ready" | ||
@@ -222,4 +231,4 @@ ] | ||
, PROC_INST_BODY : S++ // <?hi there | ||
, PROC_INST_QUOTED : S++ // <?hi there | ||
, PROC_INST_ENDING : S++ // <?hi there ? | ||
, PROC_INST_QUOTED : S++ // <?hi "there | ||
, PROC_INST_ENDING : S++ // <?hi "there" ? | ||
, OPEN_TAG : S++ // <strong | ||
@@ -237,2 +246,4 @@ , OPEN_TAG_SLASH : S++ // <strong / | ||
, CLOSE_TAG_SAW_WHITE : S++ // </a > | ||
, SCRIPT : S++ // <script> ... | ||
, SCRIPT_ENDING : S++ // <script> ... < | ||
} | ||
@@ -242,3 +253,3 @@ | ||
{ "apos" : "'" | ||
, "quot" : '"' | ||
, "quot" : "\"" | ||
, "amp" : "&" | ||
@@ -253,6 +264,21 @@ , "gt" : ">" | ||
S = sax.STATE | ||
sax.EVENTS = [ // for discoverability. | ||
"text", "processinginstruction", "sgmldeclaration", | ||
"doctype", "comment", "attribute", "opentag", "closetag", | ||
"opencdata", "cdata", "closecdata", "error", "end", "ready" ] | ||
sax.EVENTS = // for discoverability. | ||
[ "text" | ||
, "processinginstruction" | ||
, "sgmldeclaration" | ||
, "doctype" | ||
, "comment" | ||
, "attribute" | ||
, "opentag" | ||
, "closetag" | ||
, "opencdata" | ||
, "cdata" | ||
, "closecdata" | ||
, "error" | ||
, "end" | ||
, "ready" | ||
, "script" | ||
, "opennamespace" | ||
, "closenamespace" | ||
] | ||
@@ -305,8 +331,92 @@ function emit (parser, event, data) { | ||
function qname (n, parser) { | ||
var i = n.indexOf(":") | ||
, q = i < 0 ? [ "", n ] : n.split(":") | ||
, p = q[0] | ||
, l = q[1] | ||
, u = parser.ns[p] && parser.ns[p][0] || "" | ||
if (!u && p && p != "xmlns") { | ||
strictFail(parser, "Unbound namespace prefix: " + JSON.stringify(p)) | ||
parser.ns[p] = parser.ns[p] || [] | ||
parser.ns[p].push(p) | ||
u = p | ||
} | ||
return { prefix: p, local: l, uri: u } | ||
} | ||
function newTag (parser) { | ||
if (!parser.strict) parser.tagName = parser.tagName[parser.tagCase]() | ||
parser.tag = { name : parser.tagName, attributes : {} } | ||
if (parser.opt.xmlns) parser.tag.bindings = [] | ||
parser.attribList = [] | ||
} | ||
function attrib (parser) { | ||
if (parser.opt.xmlns) { | ||
var n = parser.attribName | ||
, qn = qname(n, parser) | ||
if (n == "xmlns" || qn.prefix == "xmlns") { | ||
// namespace binding attribute; push the binding into scope | ||
// and annotate tag so binding can be popped on tag close | ||
var prefix = n == "xmlns" ? "" : qn.local | ||
parser.ns[prefix] = parser.ns[prefix] || [] | ||
parser.ns[prefix].unshift(parser.attribValue) | ||
parser.tag.bindings.push(prefix) | ||
} | ||
// defer onattribute events until all attributes have been seen | ||
// so any new bindings can take effect; preserve attribute order | ||
// so deferred events can be emitted in document order | ||
parser.attribList.unshift([parser.attribName, parser.attribValue]) | ||
} else { | ||
// in non-xmlns mode, we can emit the event right away | ||
parser.tag.attributes[parser.attribName] = parser.attribValue | ||
emitNode( parser | ||
, "onattribute" | ||
, { name: parser.attribName | ||
, value: parser.attribValue } ) | ||
} | ||
parser.attribName = parser.attribValue = "" | ||
} | ||
function openTag (parser, selfClosing) { | ||
if (parser.opt.xmlns) { | ||
// emit namespace binding events | ||
for (var i = 0; i < parser.tag.bindings.length; i ++) { | ||
var p = parser.tag.bindings[i] | ||
emitNode( parser | ||
, "onopennamespace" | ||
, { prefix: p , uri: parser.ns[p][0] } ) | ||
} | ||
// handle deferred onattribute events | ||
while (parser.attribList.length) { | ||
var nv = parser.attribList.pop() | ||
, n = nv[0] | ||
, v = nv[1] | ||
, q = qname(n, parser) | ||
, ns = q.prefix ? q.uri : "" | ||
, a = { name: n | ||
, value: v | ||
, prefix: q.prefix | ||
, local: q.local | ||
, uri: ns | ||
} | ||
parser.tag.attributes[n] = a | ||
emitNode(parser, "onattribute", a) | ||
} | ||
// add namespace info to tag | ||
var qn = qname(parser.tagName, parser) | ||
parser.tag.prefix = qn.prefix | ||
parser.tag.local = qn.local | ||
parser.tag.uri = qn.uri | ||
} | ||
// process the tag | ||
parser.sawRoot = true | ||
@@ -316,7 +426,13 @@ parser.tags.push(parser.tag) | ||
if (!selfClosing) { | ||
// special case for <script> in non-strict mode. | ||
if (!parser.strict && parser.tagName.toLowerCase() === "script") { | ||
parser.state = S.SCRIPT | ||
} else { | ||
parser.state = S.TEXT | ||
} | ||
parser.tag = null | ||
parser.tagName = "" | ||
parser.state = S.TEXT | ||
} | ||
parser.attribName = parser.attribValue = "" | ||
parser.attribList = [] | ||
} | ||
@@ -358,5 +474,14 @@ | ||
emitNode(parser, "onclosetag", parser.tagName) | ||
if (parser.opt.xmlns) { | ||
// remove namespace bindings introduced by tag | ||
while (parser.tag.bindings.length) { | ||
var p = parser.tag.bindings.pop() | ||
, n = parser.ns[p].shift() | ||
emitNode(parser, "onclosenamespace", { prefix: p, uri: n }) | ||
} | ||
} | ||
} | ||
if (t === 0) parser.closedRoot = true | ||
parser.tagName = parser.attribValue = parser.attribName = "" | ||
parser.attribList = [] | ||
parser.tag = null | ||
@@ -367,3 +492,5 @@ parser.state = S.TEXT | ||
function parseEntity (parser) { | ||
var entity = parser.entity.toLowerCase(), num, numStr = "" | ||
var entity = parser.entity.toLowerCase() | ||
, num | ||
, numStr = "" | ||
if (parser.ENTITIES[entity]) return parser.ENTITIES[entity] | ||
@@ -373,8 +500,11 @@ if (entity.charAt(0) === "#") { | ||
entity = entity.slice(2) | ||
num = parseInt(entity, 16), numStr = num.toString(16) | ||
num = parseInt(entity, 16) | ||
numStr = num.toString(16) | ||
} else { | ||
entity = entity.slice(1) | ||
num = parseInt(entity, 10), numStr = num.toString(10) | ||
num = parseInt(entity, 10) | ||
numStr = num.toString(10) | ||
} | ||
} | ||
entity = entity.replace(/^0+/, "") | ||
if (numStr.toLowerCase() !== entity) { | ||
@@ -437,2 +567,21 @@ strictFail(parser, "Invalid character entity") | ||
case S.SCRIPT: | ||
// only non-strict | ||
if (c === "<") { | ||
parser.state = S.SCRIPT_ENDING | ||
} else parser.script += c | ||
continue | ||
case S.SCRIPT_ENDING: | ||
if (c === "/") { | ||
emitNode(parser, "onscript", parser.script) | ||
parser.state = S.CLOSE_TAG | ||
parser.script = "" | ||
parser.tagName = "" | ||
} else { | ||
parser.script += "<" + c | ||
parser.state = S.SCRIPT | ||
} | ||
continue | ||
case S.OPEN_WAKA: | ||
@@ -446,5 +595,7 @@ // either a /, ?, !, or text is coming next. | ||
} else if (is(nameStart,c)) { | ||
parser.startTagPosition = parser.position - 1 | ||
parser.state = S.OPEN_TAG | ||
parser.tagName = c | ||
} else if (c === "/") { | ||
parser.startTagPosition = parser.position - 1 | ||
parser.state = S.CLOSE_TAG | ||
@@ -710,6 +861,3 @@ parser.tagName = "" | ||
} | ||
parser.tag.attributes[parser.attribName] = parser.attribValue | ||
emitNode(parser, "onattribute", { | ||
name:parser.attribName, value:parser.attribValue}) | ||
parser.attribName = parser.attribValue = "" | ||
attrib(parser) | ||
parser.q = "" | ||
@@ -725,6 +873,3 @@ parser.state = S.ATTRIB | ||
} | ||
parser.tag.attributes[parser.attribName] = parser.attribValue | ||
emitNode(parser, "onattribute", | ||
{ name: parser.attribName, value: parser.attribValue}) | ||
parser.attribName = parser.attribValue = "" | ||
attrib(parser) | ||
if (c === ">") openTag(parser) | ||
@@ -731,0 +876,0 @@ else parser.state = S.ATTRIB |
{ "name" : "sax" | ||
, "description": "An evented streaming XML parser in JavaScript" | ||
, "author" : "Isaac Z. Schlueter <i@izs.me>" | ||
, "version" : "0.2.3" | ||
, "version" : "0.2.4" | ||
, "main" : "lib/sax" | ||
@@ -5,0 +6,0 @@ , "license" : "MIT" |
@@ -115,2 +115,4 @@ # sax js | ||
`startTagPosition` - Indicates the position where the current tag starts. | ||
`closed` - Boolean indicating whether or not the parser can be written to. If it's | ||
@@ -117,0 +119,0 @@ `true`, then wait for the `ready` event to write again. |
var sys = require("sys"), | ||
assert = require("assert"), | ||
fs = require("fs"), | ||
path = require("path"), | ||
sax = require("../lib/sax"); | ||
var util = require("util") | ||
, assert = require("assert") | ||
, fs = require("fs") | ||
, path = require("path") | ||
, sax = require("../lib/sax") | ||
exports.sax = sax; | ||
exports.sax = sax | ||
@@ -14,39 +14,40 @@ // handy way to do simple unit tests | ||
exports.test = function test (options) { | ||
var xml = options.xml, | ||
parser = sax.parser(options.strict, options.opt), | ||
expect = options.expect, | ||
e = 0; | ||
var xml = options.xml | ||
, parser = sax.parser(options.strict, options.opt) | ||
, expect = options.expect | ||
, e = 0 | ||
sax.EVENTS.forEach(function (ev) { | ||
parser["on" + ev] = function (n) { | ||
if (e >= expect.length && (ev === "end" || ev === "ready")) return; | ||
if (e >= expect.length && (ev === "end" || ev === "ready")) return | ||
assert.ok( e < expect.length, | ||
"expectation #"+e+" "+sys.inspect(expect[e])+"\n"+ | ||
"Unexpected event: "+ev+" "+(n ? sys.inspect(n) : "")); | ||
var inspected = n instanceof Error ? "\n"+ n.message : sys.inspect(n) | ||
"expectation #"+e+" "+util.inspect(expect[e])+"\n"+ | ||
"Unexpected event: "+ev+" "+(n ? util.inspect(n) : "")) | ||
var inspected = n instanceof Error ? "\n"+ n.message : util.inspect(n) | ||
assert.equal(ev, expect[e][0], | ||
"expectation #"+e+"\n"+ | ||
"Didn't get expected event\n"+ | ||
"expect: "+expect[e][0] + " " +sys.inspect(expect[e][1])+"\n"+ | ||
"actual: "+ev+" "+inspected+"\n"); | ||
if (ev === "error") assert.equal(n.message, expect[e][1]); | ||
"expect: "+expect[e][0] + " " +util.inspect(expect[e][1])+"\n"+ | ||
"actual: "+ev+" "+inspected+"\n") | ||
if (ev === "error") assert.equal(n.message, expect[e][1]) | ||
else assert.deepEqual(n, expect[e][1], | ||
"expectation #"+e+"\n"+ | ||
"Didn't get expected argument\n"+ | ||
"expect: "+expect[e][0] + " " +sys.inspect(expect[e][1])+"\n"+ | ||
"actual: "+ev+" "+inspected+"\n"); | ||
e++; | ||
if (ev === "error") parser.resume(); | ||
}; | ||
}); | ||
if (xml) parser.write(xml).close(); | ||
return parser; | ||
"expect: "+expect[e][0] + " " +util.inspect(expect[e][1])+"\n"+ | ||
"actual: "+ev+" "+inspected+"\n") | ||
e++ | ||
if (ev === "error") parser.resume() | ||
} | ||
}) | ||
if (xml) parser.write(xml).close() | ||
return parser | ||
} | ||
if (module === require.main) { | ||
var running = true, | ||
failures = 0; | ||
var running = true | ||
, failures = 0 | ||
function fail (file, er) { | ||
sys.error("Failed: "+file); | ||
sys.error(er.stack || er.message); | ||
failures ++; | ||
util.error("Failed: "+file) | ||
util.error(er.stack || er.message) | ||
failures ++ | ||
} | ||
@@ -71,5 +72,5 @@ | ||
}) | ||
if (!failures) return console.log("#all pass"); | ||
else return console.error(failures + " failure" + (failures > 1 ? "s" : "")); | ||
}); | ||
if (!failures) return console.log("#all pass") | ||
else return console.error(failures + " failure" + (failures > 1 ? "s" : "")) | ||
}) | ||
} |
@@ -8,3 +8,5 @@ var sax = require("../lib/sax"), | ||
parser['on' + expectation[0]] = function() { | ||
assert.equal(parser.position, expectation[1]); | ||
for (var prop in expectation[1]) { | ||
assert.equal(parser[prop], expectation[1][prop]); | ||
} | ||
} | ||
@@ -18,12 +20,11 @@ }); | ||
testPosition(['<div>abcdefgh</div>'], | ||
[ ['opentag', 5] | ||
, ['text', 19] | ||
, ['closetag', 19] | ||
[ ['opentag', { position: 5, startTagPosition: 1 }] | ||
, ['text', { position: 19, startTagPosition: 14 }] | ||
, ['closetag', { position: 19, startTagPosition: 14 }] | ||
]); | ||
testPosition(['<div>abcde','fgh</div>'], | ||
[ ['opentag', 5] | ||
, ['text', 19] | ||
, ['closetag', 19] | ||
[ ['opentag', { position: 5, startTagPosition: 1 }] | ||
, ['text', { position: 19, startTagPosition: 14 }] | ||
, ['closetag', { position: 19, startTagPosition: 14 }] | ||
]); | ||
SPDX disjunction
LicenseSPDX disjunction for an artifact's license information
Found 1 instance in 1 package
SPDX disjunction
LicenseSPDX disjunction for an artifact's license information
Found 1 instance in 1 package
144295
33
1510
174
25