Skip to content
Fetching contributors…
Cannot retrieve contributors at this time
117 lines (101 sloc) 3.6 KB
sax = require 'sax'
events = require 'events'
# Underscore has a nice function for this, but we try to go without dependencies
isEmpty = (thing) ->
return typeof thing == 'object' && thing? && Object.keys(thing).length == 0
class exports.Parser extends events.EventEmitter
constructor: (opts) ->
# default options. for compatibility's sake set to some
# sub-optimal settings. might change in the future.
options =
explicitCharkey: false
trim: true
# normalize implicates trimming, just so you know
normalize: true
# set default attribute object key
attrkey: "@"
# set default char object key
charkey: "#"
# always put child nodes in an array
explicitArray: false
# overwrite them with the specified options, if any
options[key] = value for own key, value of opts
# make the SAX parser. tried trim and normalize, but they are not
# very helpful
@saxParser = sax.parser true, {
trim: false,
normalize: false
}
# emit one error event if the sax parser fails. this is mostly a hack, but
# the sax parser isn't state of the art either.
err = false
@saxParser.onerror = (error) =>
if ! err
err = true
@emit "error", error
# always use the '#' key, even if there are no subkeys
# setting this property by and is deprecated, yet still supported.
# better pass it as explicitCharkey option to the constructor
@EXPLICIT_CHARKEY = options.explicitCharkey
@resultObject = null
stack = []
# aliases, so we don't have to type so much
attrkey = options.attrkey
charkey = options.charkey
@saxParser.onopentag = (node) =>
obj = {}
obj[charkey] = ""
for own key of node.attributes
if attrkey not of obj
obj[attrkey] = {}
obj[attrkey][key] = node.attributes[key]
# need a place to store the node name
obj["#name"] = node.name
stack.push obj
@saxParser.onclosetag = =>
obj = stack.pop()
nodeName = obj["#name"]
delete obj["#name"]
s = stack[stack.length - 1]
# remove the '#' key altogether if it's blank
if obj[charkey].match(/^\s*$/)
delete obj[charkey]
else
obj[charkey] = obj[charkey].trim() if options.trim
obj[charkey] = obj[charkey].replace(/\s{2,}/g, " ").trim() if options.normalize
# also do away with '#' key altogether, if there's no subkeys
# unless EXPLICIT_CHARKEY is set
if Object.keys(obj).length == 1 and charkey of obj and not @EXPLICIT_CHARKEY
obj = obj[charkey]
if options.emptyTag != undefined && isEmpty obj
obj = options.emptyTag
# check whether we closed all the open tags
if stack.length > 0
if not options.explicitArray
if nodeName not of s
s[nodeName] = obj
else if s[nodeName] instanceof Array
s[nodeName].push obj
else
old = s[nodeName]
s[nodeName] = [old]
s[nodeName].push obj
else
if not (s[nodeName] instanceof Array)
s[nodeName] = []
s[nodeName].push obj
else
# if explicitRoot was specified, wrap stuff in the root tag name
if options.explicitRoot
# avoid circular references
old = obj
obj = {}
obj[nodeName] = old
@resultObject = obj
@emit "end", @resultObject
@saxParser.ontext = @saxParser.oncdata = (text) =>
s = stack[stack.length - 1]
if s
s[charkey] += text
parseString: (str) =>
@saxParser.write str.toString()
Jump to Line
Something went wrong with that request. Please try again.