// Generated by CoffeeScript 1.6.3
(function() {
  var bom, builder, events, isEmpty, processName, processors, 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');

  builder = require('xmlbuilder');

  bom = require('./bom');

  processors = require('./processors');

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

  processName = function(processors, processedName) {
    var process, _i, _len;
    for (_i = 0, _len = processors.length; _i < _len; _i++) {
      process = processors[_i];
      processedName = process(processedName);
    }
    return processedName;
  };

  exports.processors = processors;

  exports.defaults = {
    "0.1": {
      explicitCharkey: false,
      trim: true,
      normalize: true,
      normalizeTags: false,
      attrkey: "@",
      charkey: "#",
      explicitArray: false,
      ignoreAttrs: false,
      mergeAttrs: false,
      explicitRoot: false,
      validator: null,
      xmlns: false,
      explicitChildren: false,
      childkey: '@@',
      charsAsChildren: false,
      async: false,
      strict: true,
      attrNameProcessors: null,
      tagNameProcessors: null
    },
    "0.2": {
      explicitCharkey: false,
      trim: false,
      normalize: false,
      normalizeTags: false,
      attrkey: "$",
      charkey: "_",
      explicitArray: true,
      ignoreAttrs: false,
      mergeAttrs: false,
      explicitRoot: true,
      validator: null,
      xmlns: false,
      explicitChildren: false,
      childkey: '$$',
      charsAsChildren: false,
      async: false,
      strict: true,
      attrNameProcessors: null,
      tagNameProcessors: null,
      rootName: 'root',
      xmldec: {
        'version': '1.0',
        'encoding': 'UTF-8',
        'standalone': true
      },
      doctype: null,
      renderOpts: {
        'pretty': true,
        'indent': '  ',
        'newline': '\n'
      }
    }
  };

  exports.ValidationError = (function(_super) {
    __extends(ValidationError, _super);

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

    return ValidationError;

  })(Error);

  exports.Builder = (function() {
    function Builder(opts) {
      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;
      }
    }

    Builder.prototype.buildObject = function(rootObj) {
      var attrkey, charkey, render, rootElement, rootName;
      attrkey = this.options.attrkey;
      charkey = this.options.charkey;
      if ((Object.keys(rootObj).length === 1) && (this.options.rootName === exports.defaults['0.2'].rootName)) {
        rootName = Object.keys(rootObj)[0];
        rootObj = rootObj[rootName];
      } else {
        rootName = this.options.rootName;
      }
      render = function(element, obj) {
        var attr, child, entry, index, key, value, _ref, _ref1;
        if (typeof obj !== 'object') {
          element.txt(obj);
        } else {
          for (key in obj) {
            if (!__hasProp.call(obj, key)) continue;
            child = obj[key];
            if (key === attrkey) {
              if (typeof child === "object") {
                for (attr in child) {
                  value = child[attr];
                  element = element.att(attr, value);
                }
              }
            } else if (key === charkey) {
              element = element.txt(child);
            } else if (typeof child === 'object' && ((child != null ? child.constructor : void 0) != null) && ((child != null ? (_ref = child.constructor) != null ? _ref.name : void 0 : void 0) != null) && (child != null ? (_ref1 = child.constructor) != null ? _ref1.name : void 0 : void 0) === 'Array') {
              for (index in child) {
                if (!__hasProp.call(child, index)) continue;
                entry = child[index];
                if (typeof entry === 'string') {
                  element = element.ele(key, entry).up();
                } else {
                  element = arguments.callee(element.ele(key), entry).up();
                }
              }
            } else if (typeof child === "object") {
              element = arguments.callee(element.ele(key), child).up();
            } else {
              element = element.ele(key, child.toString()).up();
            }
          }
        }
        return element;
      };
      rootElement = builder.create(rootName, this.options.xmldec, this.options.doctype);
      return render(rootElement, rootObj).end(this.options.renderOpts);
    };

    return Builder;

  })();

  exports.Parser = (function(_super) {
    __extends(Parser, _super);

    function Parser(opts) {
      this.parseString = __bind(this.parseString, this);
      this.reset = __bind(this.reset, this);
      this.assignOrPush = __bind(this.assignOrPush, this);
      var key, value, _ref;
      if (!(this instanceof exports.Parser)) {
        return new exports.Parser(opts);
      }
      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;
      }
      if (this.options.xmlns) {
        this.options.xmlnskey = this.options.attrkey + "ns";
      }
      if (this.options.normalizeTags) {
        if (!this.options.tagNameProcessors) {
          this.options.tagNameProcessors = [];
        }
        this.options.tagNameProcessors.unshift(processors.normalize);
      }
      this.reset();
    }

    Parser.prototype.assignOrPush = function(obj, key, newValue) {
      if (!(key in obj)) {
        if (!this.options.explicitArray) {
          return obj[key] = newValue;
        } else {
          return obj[key] = [newValue];
        }
      } else {
        if (!(obj[key] instanceof Array)) {
          obj[key] = [obj[key]];
        }
        return obj[key].push(newValue);
      }
    };

    Parser.prototype.reset = function() {
      var attrkey, charkey, err, ontext, stack,
        _this = this;
      this.removeAllListeners();
      this.saxParser = sax.parser(this.options.strict, {
        trim: false,
        normalize: false,
        xmlns: this.options.xmlns
      });
      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, newValue, obj, processedKey, _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] = {};
            }
            newValue = node.attributes[key];
            processedKey = _this.options.attrNameProcessors ? processName(_this.options.attrNameProcessors, key) : key;
            if (_this.options.mergeAttrs) {
              _this.assignOrPush(obj, processedKey, newValue);
            } else {
              obj[attrkey][processedKey] = newValue;
            }
          }
        }
        obj["#name"] = _this.options.tagNameProcessors ? processName(_this.options.tagNameProcessors, node.name) : node.name;
        if (_this.options.xmlns) {
          obj[_this.options.xmlnskey] = {
            uri: node.uri,
            local: node.local
          };
        }
        return stack.push(obj);
      };
      this.saxParser.onclosetag = function() {
        var cdata, emptyStr, node, nodeName, obj, old, s, xpath;
        obj = stack.pop();
        nodeName = obj["#name"];
        delete obj["#name"];
        cdata = obj.cdata;
        delete obj.cdata;
        s = stack[stack.length - 1];
        if (obj[charkey].match(/^\s*$/) && !cdata) {
          emptyStr = obj[charkey];
          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 (isEmpty(obj)) {
          obj = _this.options.emptyTag !== void 0 ? _this.options.emptyTag : emptyStr;
        }
        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("/");
          try {
            obj = _this.options.validator(xpath, s && s[nodeName], obj);
          } catch (_error) {
            err = _error;
            _this.emit("error", err);
          }
        }
        if (_this.options.explicitChildren && !_this.options.mergeAttrs && typeof obj === 'object') {
          node = {};
          if (_this.options.attrkey in obj) {
            node[_this.options.attrkey] = obj[_this.options.attrkey];
            delete obj[_this.options.attrkey];
          }
          if (!_this.options.charsAsChildren && _this.options.charkey in obj) {
            node[_this.options.charkey] = obj[_this.options.charkey];
            delete obj[_this.options.charkey];
          }
          if (Object.getOwnPropertyNames(obj).length > 0) {
            node[_this.options.childkey] = obj;
          }
          obj = node;
        }
        if (stack.length > 0) {
          return _this.assignOrPush(s, nodeName, obj);
        } else {
          if (_this.options.explicitRoot) {
            old = obj;
            obj = {};
            obj[nodeName] = old;
          }
          _this.resultObject = obj;
          return _this.emit("end", _this.resultObject);
        }
      };
      ontext = function(text) {
        var s;
        s = stack[stack.length - 1];
        if (s) {
          s[charkey] += text;
          return s;
        }
      };
      this.saxParser.ontext = ontext;
      return this.saxParser.oncdata = function(text) {
        var s;
        s = ontext(text);
        if (s) {
          return s.cdata = true;
        }
      };
    };

    Parser.prototype.parseString = function(str, cb) {
      if ((cb != null) && typeof cb === "function") {
        this.on("end", function(result) {
          this.reset();
          if (this.options.async) {
            return process.nextTick(function() {
              return cb(null, result);
            });
          } else {
            return cb(null, result);
          }
        });
        this.on("error", function(err) {
          this.reset();
          if (this.options.async) {
            return process.nextTick(function() {
              return cb(err);
            });
          } else {
            return cb(err);
          }
        });
      }
      if (str.toString().trim() === '') {
        this.emit("end", null);
        return true;
      }
      return this.saxParser.write(bom.stripBOM(str.toString())).close();
    };

    return Parser;

  })(events.EventEmitter);

  exports.parseString = function(str, a, b) {
    var cb, options, parser;
    if (b != null) {
      if (typeof b === 'function') {
        cb = b;
      }
      if (typeof a === 'object') {
        options = a;
      }
    } else {
      if (typeof a === 'function') {
        cb = a;
      }
      options = {};
    }
    parser = new exports.Parser(options);
    return parser.parseString(str, cb);
  };

}).call(this);
