text: (function () {
  
      var sax;
      var global = {};
      if (typeof module !== 'undefined' && module.exports && !global.xmldocAssumeBrowser) {
        // We're being used in a Node-like environment
        sax = require('sax');
      }
      else {
        // assume it's attached to the Window object in a browser
        sax = this.sax;
      
        if (!sax) // no sax for you!
          throw new Error("Expected sax to be defined. Make sure you're including sax.js before this file.");
      }
      
      /*
      XmlElement is our basic building block. Everything is an XmlElement; even XmlDocument
      behaves like an XmlElement by inheriting its attributes and functions.
      */
      
      function XmlElement(tag) {
        // Capture the parser object off of the XmlDocument delegate
        var parser = delegates[delegates.length - 1].parser;
      
        this.name = tag.name;
        this.attr = tag.attributes;
        this.val = "";
        this.children = [];
        this.firstChild = null;
        this.lastChild = null;
      
        // Assign parse information
        this.line = parser.line;
        this.column = parser.column;
        this.position = parser.position;
        this.startTagPosition = parser.startTagPosition;
      }
      
      // Private methods
      
      XmlElement.prototype._addChild = function(child) {
        // add to our children array
        this.children.push(child);
      
        // update first/last pointers
        if (!this.firstChild) this.firstChild = child;
        this.lastChild = child;
      };
      
      // SaxParser handlers
      
      XmlElement.prototype._opentag = function(tag) {
      
        var child = new XmlElement(tag);
      
        this._addChild(child);
      
        delegates.unshift(child);
      };
      
      XmlElement.prototype._closetag = function() {
        delegates.shift();
      };
      
      XmlElement.prototype._text = function(text) {
        if (typeof this.children === 'undefined')
          return
      
        this.val += text;
      
        this._addChild(new XmlTextNode(text));
      };
      
      XmlElement.prototype._cdata = function(cdata) {
        this.val += cdata;
      
        this._addChild(new XmlCDataNode(cdata));
      };
      
      XmlElement.prototype._comment = function(comment) {
        if (typeof this.children === 'undefined')
          return
      
        this._addChild(new XmlCommentNode(comment));
      };
      
      XmlElement.prototype._error = function(err) {
        throw err;
      };
      
      // Useful functions
      
      XmlElement.prototype.eachChild = function(iterator, context) {
        for (var i=0, l=this.children.length; i<l; i++)
          if (this.children[i].type === "element")
            if (iterator.call(context, this.children[i], i, this.children) === false) return;
      };
      
      XmlElement.prototype.childNamed = function(name) {
        for (var i=0, l=this.children.length; i<l; i++) {
          var child = this.children[i];
          if (child.name === name) return child;
        }
        return undefined;
      };
      
      XmlElement.prototype.childrenNamed = function(name) {
        var matches = [];
      
        for (var i=0, l=this.children.length; i<l; i++)
          if (this.children[i].name === name)
            matches.push(this.children[i]);
      
        return matches;
      };
      
      XmlElement.prototype.childWithAttribute = function(name,value) {
        for (var i=0, l=this.children.length; i<l; i++) {
          var child = this.children[i];
          if (child.type === "element" && ((value && child.attr[name] === value) || (!value && child.attr[name])))
            return child;
        }
        return undefined;
      };
      
      XmlElement.prototype.descendantWithPath = function(path) {
        var descendant = this;
        var components = path.split('.');
      
        for (var i=0, l=components.length; i<l; i++)
          if (descendant && descendant.type === "element")
            descendant = descendant.childNamed(components[i]);
          else
            return undefined;
      
        return descendant;
      };
      
      XmlElement.prototype.valueWithPath = function(path) {
        var components = path.split('@');
        var descendant = this.descendantWithPath(components[0]);
        if (descendant)
          return components.length > 1 ? descendant.attr[components[1]] : descendant.val;
        else
          return undefined;
      };
      
      // String formatting (for debugging)
      
      XmlElement.prototype.toString = function(options) {
        return this.toStringWithIndent("", options);
      };
      
      XmlElement.prototype.toStringWithIndent = function(indent, options) {
        var s = indent + "<" + this.name;
        var linebreak = options && options.compressed ? "" : "\n";
        var preserveWhitespace = options && options.preserveWhitespace;
      
        for (var name in this.attr)
          if (Object.prototype.hasOwnProperty.call(this.attr, name))
              s += " " + name + '="' + escapeXML(this.attr[name]) + '"';
      
        if (this.children.length === 1 && this.children[0].type !== "element") {
          s += ">" + this.children[0].toString(options) + "</" + this.name + ">";
        }
        else if (this.children.length) {
          s += ">" + linebreak;
      
          var childIndent = indent + (options && options.compressed ? "" : "  ");
      
          for (var i=0, l=this.children.length; i<l; i++) {
            s += this.children[i].toStringWithIndent(childIndent, options) + linebreak;
          }
      
          s += indent + "</" + this.name + ">";
        }
        else if (options && options.html) {
          var whiteList = ["br", "img", "input", "link", "meta"];
          if (whiteList.indexOf(this.name) !== -1) s += "/>";
          else s += "></" + this.name + ">";
        }
        else {
          s += "/>";
        }
      
        return s;
      };
      
      // Alternative XML nodes
      
      function XmlTextNode (text) {
        this.text = text;
      }
      
      XmlTextNode.prototype.toString = function(options) {
        return formatText(escapeXML(this.text), options);
      };
      
      XmlTextNode.prototype.toStringWithIndent = function(indent, options) {
        return indent+this.toString(options);
      };
      
      function XmlCDataNode (cdata) {
        this.cdata = cdata;
      }
      
      XmlCDataNode.prototype.toString = function(options) {
        return "<![CDATA["+formatText(this.cdata, options)+"]]>";
      };
      
      XmlCDataNode.prototype.toStringWithIndent = function(indent, options) {
        return indent+this.toString(options);
      };
      
      function XmlCommentNode (comment) {
        this.comment = comment;
      }
      
      XmlCommentNode.prototype.toString = function(options) {
        return "<!--"+formatText(escapeXML(this.comment), options)+"-->";
      };
      
      XmlCommentNode.prototype.toStringWithIndent = function(indent, options) {
        return indent+this.toString(options);
      };
      
      // Node type tag
      
      XmlElement.prototype.type = "element";
      XmlTextNode.prototype.type = "text";
      XmlCDataNode.prototype.type = "cdata";
      XmlCommentNode.prototype.type = "comment";
      
      /*
      XmlDocument is the class we expose to the user; it uses the sax parser to create a hierarchy
      of XmlElements.
      */
      
      function XmlDocument(xml) {
        xml && (xml = xml.toString().trim());
      
        if (!xml)
          throw new Error("No XML to parse!");
      
        // Stores doctype (if defined)
        this.doctype = "";
      
        // Expose the parser to the other delegates while the parser is running
        this.parser = sax.parser(true); // strict
        addParserEvents(this.parser);
      
        // We'll use the file-scoped "delegates" var to remember what elements we're currently
        // parsing; they will push and pop off the stack as we get deeper into the XML hierarchy.
        // It's safe to use a global because JS is single-threaded.
        delegates = [this];
      
        this.parser.write(xml);
      
        // Remove the parser as it is no longer needed and should not be exposed to clients
        delete this.parser;
      }
      
      // make XmlDocument inherit XmlElement's methods
      extend(XmlDocument.prototype, XmlElement.prototype);
      
      XmlDocument.prototype._opentag = function(tag) {
        if (typeof this.children === 'undefined')
          // the first tag we encounter should be the root - we'll "become" the root XmlElement
          XmlElement.call(this,tag);
        else
          // all other tags will be the root element's children
          XmlElement.prototype._opentag.apply(this,arguments);
      };
      
      XmlDocument.prototype._doctype = function(doctype) {
        this.doctype += doctype;
      }
      
      // file-scoped global stack of delegates
      var delegates = null;
      
      /*
      Helper functions
      */
      
      function addParserEvents(parser) {
        parser.onopentag = parser_opentag;
        parser.onclosetag = parser_closetag;
        parser.ontext = parser_text;
        parser.oncdata = parser_cdata;
        parser.oncomment = parser_comment;
        parser.ondoctype = parser_doctype;
        parser.onerror = parser_error;
      }
      
      // create these closures and cache them by keeping them file-scoped
      function parser_opentag() { delegates[0] && delegates[0]._opentag.apply(delegates[0],arguments) }
      function parser_closetag() { delegates[0] && delegates[0]._closetag.apply(delegates[0],arguments) }
      function parser_text() { delegates[0] && delegates[0]._text.apply(delegates[0],arguments) }
      function parser_cdata() { delegates[0] && delegates[0]._cdata.apply(delegates[0],arguments) }
      function parser_comment() { delegates[0] && delegates[0]._comment.apply(delegates[0],arguments) }
      function parser_doctype() { delegates[0] && delegates[0]._doctype.apply(delegates[0],arguments) }
      function parser_error() { delegates[0] && delegates[0]._error.apply(delegates[0],arguments) }
      
      // a relatively standard extend method
      function extend(destination, source) {
        for (var prop in source)
          if (source.hasOwnProperty(prop))
            destination[prop] = source[prop];
      }
      
      // escapes XML entities like "<", "&", etc.
      function escapeXML(value){
        return value.replace(/&/g, '&amp;').replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/'/g, '&apos;').replace(/"/g, '&quot;');
      }
      
      // formats some text for debugging given a few options
      function formatText(text, options) {
        var finalText = text;
      
        if (options && options.trimmed && text.length > 25)
          finalText = finalText.substring(0,25).trim() + "…";
        if (!(options && options.preserveWhitespace))
          finalText = finalText.trim();
      
        return finalText;
      }
      
      // Are we being used in a Node-like environment?
      if (typeof module !== 'undefined' && module.exports && !global.xmldocAssumeBrowser)
          module.exports.XmlDocument = XmlDocument;
      else
          this.XmlDocument = XmlDocument;
      
      })();