// Generated by CoffeeScript 1.3.3
(function() {
  var events, isEmpty, sax,
    __hasProp = {}.hasOwnProperty,
    __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
    __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };

  sax = require('sax');

  events = require('events');

  isEmpty = function(thing) {
    return typeof thing === "object" && (thing != null) && Object.keys(thing).length === 0;
  };

  exports.defaults = {
    "0.1": {
      explicitCharkey: false,
      trim: true,
      normalize: true,
      attrkey: "@",
      charkey: "#",
      explicitArray: false,
      ignoreAttrs: false,
      mergeAttrs: false,
      explicitRoot: false,
      validator: null
    },
    "0.2": {
      explicitCharkey: false,
      trim: false,
      normalize: false,
      attrkey: "$",
      charkey: "_",
      explicitArray: true,
      ignoreAttrs: false,
      mergeAttrs: false,
      explicitRoot: true,
      validator: null
    }
  };

  exports.ValidationError = (function(_super) {

    __extends(ValidationError, _super);

    function ValidationError(message) {
      this.message = message;
    }

    return ValidationError;

  })(Error);

  exports.Parser = (function(_super) {

    __extends(Parser, _super);

    function Parser(opts) {
      this.parseString = __bind(this.parseString, this);

      this.reset = __bind(this.reset, this);

      var key, value, _ref;
      this.options = {};
      _ref = exports.defaults["0.2"];
      for (key in _ref) {
        if (!__hasProp.call(_ref, key)) continue;
        value = _ref[key];
        this.options[key] = value;
      }
      for (key in opts) {
        if (!__hasProp.call(opts, key)) continue;
        value = opts[key];
        this.options[key] = value;
      }
      this.reset();
    }

    Parser.prototype.reset = function() {
      var attrkey, charkey, err, stack,
        _this = this;
      this.removeAllListeners();
      this.saxParser = sax.parser(true, {
        trim: false,
        normalize: false
      });
      err = false;
      this.saxParser.onerror = function(error) {
        if (!err) {
          err = true;
          return _this.emit("error", error);
        }
      };
      this.EXPLICIT_CHARKEY = this.options.explicitCharkey;
      this.resultObject = null;
      stack = [];
      attrkey = this.options.attrkey;
      charkey = this.options.charkey;
      this.saxParser.onopentag = function(node) {
        var key, obj, _ref;
        obj = {};
        obj[charkey] = "";
        if (!_this.options.ignoreAttrs) {
          _ref = node.attributes;
          for (key in _ref) {
            if (!__hasProp.call(_ref, key)) continue;
            if (!(attrkey in obj) && !_this.options.mergeAttrs) {
              obj[attrkey] = {};
            }
            if (_this.options.mergeAttrs) {
              obj[key] = node.attributes[key];
            } else {
              obj[attrkey][key] = node.attributes[key];
            }
          }
        }
        obj["#name"] = node.name;
        return stack.push(obj);
      };
      this.saxParser.onclosetag = function() {
        var node, nodeName, obj, old, s, xpath;
        obj = stack.pop();
        nodeName = obj["#name"];
        delete obj["#name"];
        s = stack[stack.length - 1];
        if (obj[charkey].match(/^\s*$/)) {
          delete obj[charkey];
        } else {
          if (_this.options.trim) {
            obj[charkey] = obj[charkey].trim();
          }
          if (_this.options.normalize) {
            obj[charkey] = obj[charkey].replace(/\s{2,}/g, " ").trim();
          }
          if (Object.keys(obj).length === 1 && charkey in obj && !_this.EXPLICIT_CHARKEY) {
            obj = obj[charkey];
          }
        }
        if (_this.options.emptyTag !== void 0 && isEmpty(obj)) {
          obj = _this.options.emptyTag;
        }
        if (_this.options.validator != null) {
          xpath = "/" + ((function() {
            var _i, _len, _results;
            _results = [];
            for (_i = 0, _len = stack.length; _i < _len; _i++) {
              node = stack[_i];
              _results.push(node["#name"]);
            }
            return _results;
          })()).concat(nodeName).join("/");
          obj = _this.options.validator(xpath, s && s[nodeName], obj);
        }
        if (stack.length > 0) {
          if (!_this.options.explicitArray) {
            if (!(nodeName in s)) {
              return s[nodeName] = obj;
            } else if (s[nodeName] instanceof Array) {
              return s[nodeName].push(obj);
            } else {
              old = s[nodeName];
              s[nodeName] = [old];
              return s[nodeName].push(obj);
            }
          } else {
            if (!(s[nodeName] instanceof Array)) {
              s[nodeName] = [];
            }
            return s[nodeName].push(obj);
          }
        } else {
          if (_this.options.explicitRoot) {
            old = obj;
            obj = {};
            obj[nodeName] = old;
          }
          _this.resultObject = obj;
          return _this.emit("end", _this.resultObject);
        }
      };
      return this.saxParser.ontext = this.saxParser.oncdata = function(text) {
        var s;
        s = stack[stack.length - 1];
        if (s) {
          return s[charkey] += text;
        }
      };
    };

    Parser.prototype.parseString = function(str, cb) {
      if ((cb != null) && typeof cb === "function") {
        this.on("end", function(result) {
          this.reset();
          return cb(null, result);
        });
        this.on("error", function(err) {
          this.reset();
          return cb(err);
        });
      }
      if (str.toString().trim() === '') {
        this.emit("end", null);
        return true;
      }
      try {
        return this.saxParser.write(str.toString());
      } catch (ex) {
        if (ex instanceof exports.ValidationError) {
          return this.emit("error", ex.message);
        } else {
          throw ex;
        }
      }
    };

    return Parser;

  })(events.EventEmitter);

}).call(this);
