// Generated by CoffeeScript 2.4.1
(function() {
  var DocumentPosition, NodeType, XMLCData, XMLComment, XMLDeclaration, XMLDocType, XMLDummy, XMLElement, XMLNamedNodeMap, XMLNode, XMLNodeList, XMLProcessingInstruction, XMLRaw, XMLText, getValue, isEmpty, isFunction, isObject,
    hasProp = {}.hasOwnProperty,
    splice = [].splice;

  ({isObject, isFunction, isEmpty, getValue} = require('./Utility'));

  XMLElement = null;

  XMLCData = null;

  XMLComment = null;

  XMLDeclaration = null;

  XMLDocType = null;

  XMLRaw = null;

  XMLText = null;

  XMLProcessingInstruction = null;

  XMLDummy = null;

  NodeType = null;

  XMLNodeList = null;

  XMLNamedNodeMap = null;

  DocumentPosition = null;

  // Represents a generic XMl element
  module.exports = XMLNode = (function() {
    class XMLNode {
      // Initializes a new instance of `XMLNode`

      // `parent` the parent node
      constructor(parent1) {
        this.parent = parent1;
        if (this.parent) {
          this.options = this.parent.options;
          this.stringify = this.parent.stringify;
        }
        this.value = null;
        this.children = [];
        this.baseURI = null;
        // first execution, load dependencies that are otherwise
        // circular (so we can't load them at the top)
        if (!XMLElement) {
          XMLElement = require('./XMLElement');
          XMLCData = require('./XMLCData');
          XMLComment = require('./XMLComment');
          XMLDeclaration = require('./XMLDeclaration');
          XMLDocType = require('./XMLDocType');
          XMLRaw = require('./XMLRaw');
          XMLText = require('./XMLText');
          XMLProcessingInstruction = require('./XMLProcessingInstruction');
          XMLDummy = require('./XMLDummy');
          NodeType = require('./NodeType');
          XMLNodeList = require('./XMLNodeList');
          XMLNamedNodeMap = require('./XMLNamedNodeMap');
          DocumentPosition = require('./DocumentPosition');
        }
      }

      
      // Sets the parent node of this node and its children recursively

      // `parent` the parent node
      setParent(parent) {
        var child, j, len, ref1, results;
        this.parent = parent;
        if (parent) {
          this.options = parent.options;
          this.stringify = parent.stringify;
        }
        ref1 = this.children;
        results = [];
        for (j = 0, len = ref1.length; j < len; j++) {
          child = ref1[j];
          results.push(child.setParent(this));
        }
        return results;
      }

      // Creates a child element node

      // `name` node name or an object describing the XML tree
      // `attributes` an object containing name/value pairs of attributes
      // `text` element text
      element(name, attributes, text) {
        var childNode, item, j, k, key, lastChild, len, len1, val;
        lastChild = null;
        if (attributes === null && (text == null)) {
          [attributes, text] = [{}, null];
        }
        if (attributes == null) {
          attributes = {};
        }
        attributes = getValue(attributes);
        // swap argument order: text <-> attributes
        if (!isObject(attributes)) {
          [text, attributes] = [attributes, text];
        }
        if (name != null) {
          name = getValue(name);
        }
        // expand if array
        if (Array.isArray(name)) {
          for (j = 0, len = name.length; j < len; j++) {
            item = name[j];
            lastChild = this.element(item);
          }
        // evaluate if function
        } else if (isFunction(name)) {
          lastChild = this.element(name.apply());
        // expand if object
        } else if (isObject(name)) {
          for (key in name) {
            if (!hasProp.call(name, key)) continue;
            val = name[key];
            if (isFunction(val)) {
              // evaluate if function
              val = val.apply();
            }
            // assign attributes
            if (!this.options.ignoreDecorators && this.stringify.convertAttKey && key.indexOf(this.stringify.convertAttKey) === 0) {
              lastChild = this.attribute(key.substr(this.stringify.convertAttKey.length), val);
            // skip empty arrays
            } else if (!this.options.separateArrayItems && Array.isArray(val) && isEmpty(val)) {
              lastChild = this.dummy();
            // empty objects produce one node
            } else if (isObject(val) && isEmpty(val)) {
              lastChild = this.element(key);
            // skip null and undefined nodes
            } else if (!this.options.keepNullNodes && (val == null)) {
              lastChild = this.dummy();
            
            // expand list by creating child nodes
            } else if (!this.options.separateArrayItems && Array.isArray(val)) {
              for (k = 0, len1 = val.length; k < len1; k++) {
                item = val[k];
                childNode = {};
                childNode[key] = item;
                lastChild = this.element(childNode);
              }
            
            // expand child nodes under parent
            } else if (isObject(val)) {
              // if the key is #text expand child nodes under this node to support mixed content
              if (!this.options.ignoreDecorators && this.stringify.convertTextKey && key.indexOf(this.stringify.convertTextKey) === 0) {
                lastChild = this.element(val);
              } else {
                lastChild = this.element(key);
                lastChild.element(val);
              }
            } else {
              
              // text node
              lastChild = this.element(key, val);
            }
          }
        // skip null nodes
        } else if (!this.options.keepNullNodes && text === null) {
          lastChild = this.dummy();
        } else {
          // text node
          if (!this.options.ignoreDecorators && this.stringify.convertTextKey && name.indexOf(this.stringify.convertTextKey) === 0) {
            lastChild = this.text(text);
          // cdata node
          } else if (!this.options.ignoreDecorators && this.stringify.convertCDataKey && name.indexOf(this.stringify.convertCDataKey) === 0) {
            lastChild = this.cdata(text);
          // comment node
          } else if (!this.options.ignoreDecorators && this.stringify.convertCommentKey && name.indexOf(this.stringify.convertCommentKey) === 0) {
            lastChild = this.comment(text);
          // raw text node
          } else if (!this.options.ignoreDecorators && this.stringify.convertRawKey && name.indexOf(this.stringify.convertRawKey) === 0) {
            lastChild = this.raw(text);
          // processing instruction
          } else if (!this.options.ignoreDecorators && this.stringify.convertPIKey && name.indexOf(this.stringify.convertPIKey) === 0) {
            lastChild = this.instruction(name.substr(this.stringify.convertPIKey.length), text);
          } else {
            // element node
            lastChild = this.node(name, attributes, text);
          }
        }
        if (lastChild == null) {
          throw new Error("Could not create any elements with: " + name + ". " + this.debugInfo());
        }
        return lastChild;
      }

      // Creates a child element node before the current node

      // `name` node name or an object describing the XML tree
      // `attributes` an object containing name/value pairs of attributes
      // `text` element text
      insertBefore(name, attributes, text) {
        var child, i, newChild, refChild, removed;
        // DOM level 1
        // insertBefore(newChild, refChild) inserts the child node newChild before refChild
        if (name != null ? name.type : void 0) {
          newChild = name;
          refChild = attributes;
          newChild.setParent(this);
          if (refChild) {
            // temporarily remove children starting *with* refChild
            i = children.indexOf(refChild);
            removed = children.splice(i);
            
            // add the new child
            children.push(newChild);
            
            // add back removed children after new child
            Array.prototype.push.apply(children, removed);
          } else {
            children.push(newChild);
          }
          return newChild;
        } else {
          if (this.isRoot) {
            throw new Error("Cannot insert elements at root level. " + this.debugInfo(name));
          }
          
          // temporarily remove children starting *with* this
          i = this.parent.children.indexOf(this);
          removed = this.parent.children.splice(i);
          
          // add the new child
          child = this.parent.element(name, attributes, text);
          
          // add back removed children after new child
          Array.prototype.push.apply(this.parent.children, removed);
          return child;
        }
      }

      // Creates a child element node after the current node

      // `name` node name or an object describing the XML tree
      // `attributes` an object containing name/value pairs of attributes
      // `text` element text
      insertAfter(name, attributes, text) {
        var child, i, removed;
        if (this.isRoot) {
          throw new Error("Cannot insert elements at root level. " + this.debugInfo(name));
        }
        
        // temporarily remove children starting *after* this
        i = this.parent.children.indexOf(this);
        removed = this.parent.children.splice(i + 1);
        
        // add the new child
        child = this.parent.element(name, attributes, text);
        
        // add back removed children after new child
        Array.prototype.push.apply(this.parent.children, removed);
        return child;
      }

      // Deletes a child element node

      remove() {
        var i, ref1;
        if (this.isRoot) {
          throw new Error("Cannot remove the root element. " + this.debugInfo());
        }
        i = this.parent.children.indexOf(this);
        splice.apply(this.parent.children, [i, i - i + 1].concat(ref1 = [])), ref1;
        return this.parent;
      }

      // Creates a node

      // `name` name of the node
      // `attributes` an object containing name/value pairs of attributes
      // `text` element text
      node(name, attributes, text) {
        var child;
        if (name != null) {
          name = getValue(name);
        }
        attributes || (attributes = {});
        attributes = getValue(attributes);
        // swap argument order: text <-> attributes
        if (!isObject(attributes)) {
          [text, attributes] = [attributes, text];
        }
        child = new XMLElement(this, name, attributes);
        if (text != null) {
          child.text(text);
        }
        this.children.push(child);
        return child;
      }

      // Creates a text node

      // `value` element text
      text(value) {
        var child;
        if (isObject(value)) {
          this.element(value);
        }
        child = new XMLText(this, value);
        this.children.push(child);
        return this;
      }

      // Creates a CDATA node

      // `value` element text without CDATA delimiters
      cdata(value) {
        var child;
        child = new XMLCData(this, value);
        this.children.push(child);
        return this;
      }

      // Creates a comment node

      // `value` comment text
      comment(value) {
        var child;
        child = new XMLComment(this, value);
        this.children.push(child);
        return this;
      }

      // Creates a comment node before the current node

      // `value` comment text
      commentBefore(value) {
        var child, i, removed;
        // temporarily remove children starting *with* this
        i = this.parent.children.indexOf(this);
        removed = this.parent.children.splice(i);
        // add the new child
        child = this.parent.comment(value);
        // add back removed children after new child
        Array.prototype.push.apply(this.parent.children, removed);
        return this;
      }

      // Creates a comment node after the current node

      // `value` comment text
      commentAfter(value) {
        var child, i, removed;
        // temporarily remove children starting *after* this
        i = this.parent.children.indexOf(this);
        removed = this.parent.children.splice(i + 1);
        // add the new child
        child = this.parent.comment(value);
        // add back removed children after new child
        Array.prototype.push.apply(this.parent.children, removed);
        return this;
      }

      // Adds unescaped raw text

      // `value` text
      raw(value) {
        var child;
        child = new XMLRaw(this, value);
        this.children.push(child);
        return this;
      }

      // Adds a dummy node
      dummy() {
        var child;
        child = new XMLDummy(this);
        // Normally when a new node is created it is added to the child node collection.
        // However, dummy nodes are never added to the XML tree. They are created while
        // converting JS objects to XML nodes in order not to break the recursive function
        // chain. They can be thought of as invisible nodes. They can be traversed through
        // by using prev(), next(), up(), etc. functions but they do not exists in the tree.

        // @children.push child
        return child;
      }

      // Adds a processing instruction

      // `target` instruction target
      // `value` instruction value
      instruction(target, value) {
        var insTarget, insValue, instruction, j, len;
        if (target != null) {
          target = getValue(target);
        }
        if (value != null) {
          value = getValue(value);
        }
        if (Array.isArray(target)) { // expand if array
          for (j = 0, len = target.length; j < len; j++) {
            insTarget = target[j];
            this.instruction(insTarget);
          }
        } else if (isObject(target)) { // expand if object
          for (insTarget in target) {
            if (!hasProp.call(target, insTarget)) continue;
            insValue = target[insTarget];
            this.instruction(insTarget, insValue);
          }
        } else {
          if (isFunction(value)) {
            value = value.apply();
          }
          instruction = new XMLProcessingInstruction(this, target, value);
          this.children.push(instruction);
        }
        return this;
      }

      // Creates a processing instruction node before the current node

      // `target` instruction target
      // `value` instruction value
      instructionBefore(target, value) {
        var child, i, removed;
        // temporarily remove children starting *with* this
        i = this.parent.children.indexOf(this);
        removed = this.parent.children.splice(i);
        // add the new child
        child = this.parent.instruction(target, value);
        // add back removed children after new child
        Array.prototype.push.apply(this.parent.children, removed);
        return this;
      }

      // Creates a processing instruction node after the current node

      // `target` instruction target
      // `value` instruction value
      instructionAfter(target, value) {
        var child, i, removed;
        // temporarily remove children starting *after* this
        i = this.parent.children.indexOf(this);
        removed = this.parent.children.splice(i + 1);
        // add the new child
        child = this.parent.instruction(target, value);
        // add back removed children after new child
        Array.prototype.push.apply(this.parent.children, removed);
        return this;
      }

      // Creates the xml declaration

      // `version` A version number string, e.g. 1.0
      // `encoding` Encoding declaration, e.g. UTF-8
      // `standalone` standalone document declaration: true or false
      declaration(version, encoding, standalone) {
        var doc, xmldec;
        doc = this.document();
        xmldec = new XMLDeclaration(doc, version, encoding, standalone);
        // Replace XML declaration if exists, otherwise insert at top
        if (doc.children.length === 0) {
          doc.children.unshift(xmldec);
        } else if (doc.children[0].type === NodeType.Declaration) {
          doc.children[0] = xmldec;
        } else {
          doc.children.unshift(xmldec);
        }
        return doc.root() || doc;
      }

      // Creates the document type declaration

      // `pubID` the public identifier of the external subset
      // `sysID` the system identifier of the external subset
      dtd(pubID, sysID) {
        var child, doc, doctype, i, j, k, len, len1, ref1, ref2;
        doc = this.document();
        doctype = new XMLDocType(doc, pubID, sysID);
        ref1 = doc.children;
        // Replace DTD if exists
        for (i = j = 0, len = ref1.length; j < len; i = ++j) {
          child = ref1[i];
          if (child.type === NodeType.DocType) {
            doc.children[i] = doctype;
            return doctype;
          }
        }
        ref2 = doc.children;
        // insert before root node if the root node exists
        for (i = k = 0, len1 = ref2.length; k < len1; i = ++k) {
          child = ref2[i];
          if (child.isRoot) {
            doc.children.splice(i, 0, doctype);
            return doctype;
          }
        }
        // otherwise append to end
        doc.children.push(doctype);
        return doctype;
      }

      // Gets the parent node
      up() {
        if (this.isRoot) {
          throw new Error("The root node has no parent. Use doc() if you need to get the document object.");
        }
        return this.parent;
      }

      // Gets the root node
      root() {
        var node;
        node = this;
        while (node) {
          if (node.type === NodeType.Document) {
            return node.rootObject;
          } else if (node.isRoot) {
            return node;
          } else {
            node = node.parent;
          }
        }
      }

      // Gets the node representing the XML document
      document() {
        var node;
        node = this;
        while (node) {
          if (node.type === NodeType.Document) {
            return node;
          } else {
            node = node.parent;
          }
        }
      }

      // Ends the document and converts string
      end(options) {
        return this.document().end(options);
      }

      // Gets the previous node
      prev() {
        var i;
        i = this.parent.children.indexOf(this);
        if (i < 1) {
          throw new Error("Already at the first node. " + this.debugInfo());
        }
        return this.parent.children[i - 1];
      }

      // Gets the next node
      next() {
        var i;
        i = this.parent.children.indexOf(this);
        if (i === -1 || i === this.parent.children.length - 1) {
          throw new Error("Already at the last node. " + this.debugInfo());
        }
        return this.parent.children[i + 1];
      }

      // Imports cloned root from another XML document

      // `doc` the XML document to insert nodes from
      importDocument(doc) {
        var child, clonedRoot, j, len, ref1;
        clonedRoot = doc.root().clone();
        clonedRoot.parent = this;
        clonedRoot.isRoot = false;
        this.children.push(clonedRoot);
        // set properties if imported element becomes the root node
        if (this.type === NodeType.Document) {
          clonedRoot.isRoot = true;
          clonedRoot.documentObject = this;
          this.rootObject = clonedRoot;
          // set dtd name
          if (this.children) {
            ref1 = this.children;
            for (j = 0, len = ref1.length; j < len; j++) {
              child = ref1[j];
              if (child.type === NodeType.DocType) {
                child.name = clonedRoot.name;
                break;
              }
            }
          }
        }
        return this;
      }

      
      // Returns debug string for this node
      debugInfo(name) {
        var ref1, ref2;
        name = name || this.name;
        if ((name == null) && !((ref1 = this.parent) != null ? ref1.name : void 0)) {
          return "";
        } else if (name == null) {
          return "parent: <" + this.parent.name + ">";
        } else if (!((ref2 = this.parent) != null ? ref2.name : void 0)) {
          return "node: <" + name + ">";
        } else {
          return "node: <" + name + ">, parent: <" + this.parent.name + ">";
        }
      }

      // Aliases
      ele(name, attributes, text) {
        return this.element(name, attributes, text);
      }

      nod(name, attributes, text) {
        return this.node(name, attributes, text);
      }

      txt(value) {
        return this.text(value);
      }

      dat(value) {
        return this.cdata(value);
      }

      com(value) {
        return this.comment(value);
      }

      ins(target, value) {
        return this.instruction(target, value);
      }

      doc() {
        return this.document();
      }

      dec(version, encoding, standalone) {
        return this.declaration(version, encoding, standalone);
      }

      e(name, attributes, text) {
        return this.element(name, attributes, text);
      }

      n(name, attributes, text) {
        return this.node(name, attributes, text);
      }

      t(value) {
        return this.text(value);
      }

      d(value) {
        return this.cdata(value);
      }

      c(value) {
        return this.comment(value);
      }

      r(value) {
        return this.raw(value);
      }

      i(target, value) {
        return this.instruction(target, value);
      }

      u() {
        return this.up();
      }

      // can be deprecated in a future release
      importXMLBuilder(doc) {
        return this.importDocument(doc);
      }

      // Adds or modifies an attribute.

      // `name` attribute name
      // `value` attribute value
      attribute(name, value) {
        throw new Error("attribute() applies to element nodes only.");
      }

      att(name, value) {
        return this.attribute(name, value);
      }

      a(name, value) {
        return this.attribute(name, value);
      }

      // Removes an attribute

      // `name` attribute name
      removeAttribute(name) {
        throw new Error("attribute() applies to element nodes only.");
      }

      // DOM level 1 functions to be implemented later
      replaceChild(newChild, oldChild) {
        throw new Error("This DOM method is not implemented." + this.debugInfo());
      }

      removeChild(oldChild) {
        throw new Error("This DOM method is not implemented." + this.debugInfo());
      }

      appendChild(newChild) {
        throw new Error("This DOM method is not implemented." + this.debugInfo());
      }

      hasChildNodes() {
        return this.children.length !== 0;
      }

      cloneNode(deep) {
        throw new Error("This DOM method is not implemented." + this.debugInfo());
      }

      normalize() {
        throw new Error("This DOM method is not implemented." + this.debugInfo());
      }

      // DOM level 2
      isSupported(feature, version) {
        return true;
      }

      hasAttributes() {
        return this.attribs.length !== 0;
      }

      // DOM level 3 functions to be implemented later
      compareDocumentPosition(other) {
        var ref, res;
        ref = this;
        if (ref === other) {
          return 0;
        } else if (this.document() !== other.document()) {
          res = DocumentPosition.Disconnected | DocumentPosition.ImplementationSpecific;
          if (Math.random() < 0.5) {
            res |= DocumentPosition.Preceding;
          } else {
            res |= DocumentPosition.Following;
          }
          return res;
        } else if (ref.isAncestor(other)) {
          return DocumentPosition.Contains | DocumentPosition.Preceding;
        } else if (ref.isDescendant(other)) {
          return DocumentPosition.Contains | DocumentPosition.Following;
        } else if (ref.isPreceding(other)) {
          return DocumentPosition.Preceding;
        } else {
          return DocumentPosition.Following;
        }
      }

      isSameNode(other) {
        throw new Error("This DOM method is not implemented." + this.debugInfo());
      }

      lookupPrefix(namespaceURI) {
        throw new Error("This DOM method is not implemented." + this.debugInfo());
      }

      isDefaultNamespace(namespaceURI) {
        throw new Error("This DOM method is not implemented." + this.debugInfo());
      }

      lookupNamespaceURI(prefix) {
        throw new Error("This DOM method is not implemented." + this.debugInfo());
      }

      isEqualNode(node) {
        var i, j, ref1;
        if (node.nodeType !== this.nodeType) {
          return false;
        }
        if (node.children.length !== this.children.length) {
          return false;
        }
        for (i = j = 0, ref1 = this.children.length - 1; (0 <= ref1 ? j <= ref1 : j >= ref1); i = 0 <= ref1 ? ++j : --j) {
          if (!this.children[i].isEqualNode(node.children[i])) {
            return false;
          }
        }
        return true;
      }

      getFeature(feature, version) {
        throw new Error("This DOM method is not implemented." + this.debugInfo());
      }

      setUserData(key, data, handler) {
        throw new Error("This DOM method is not implemented." + this.debugInfo());
      }

      getUserData(key) {
        throw new Error("This DOM method is not implemented." + this.debugInfo());
      }

      // Returns true if other is an inclusive descendant of node,
      // and false otherwise.
      contains(other) {
        if (!other) {
          return false;
        }
        return other === this || this.isDescendant(other);
      }

      // An object A is called a descendant of an object B, if either A is 
      // a child of B or A is a child of an object C that is a descendant of B.
      isDescendant(node) {
        var child, isDescendantChild, j, len, ref1;
        ref1 = this.children;
        for (j = 0, len = ref1.length; j < len; j++) {
          child = ref1[j];
          if (node === child) {
            return true;
          }
          isDescendantChild = child.isDescendant(node);
          if (isDescendantChild) {
            return true;
          }
        }
        return false;
      }

      // An object A is called an ancestor of an object B if and only if
      // B is a descendant of A.
      isAncestor(node) {
        return node.isDescendant(this);
      }

      // An object A is preceding an object B if A and B are in the 
      // same tree and A comes before B in tree order.
      isPreceding(node) {
        var nodePos, thisPos;
        nodePos = this.treePosition(node);
        thisPos = this.treePosition(this);
        if (nodePos === -1 || thisPos === -1) {
          return false;
        } else {
          return nodePos < thisPos;
        }
      }

      // An object A is folllowing an object B if A and B are in the 
      // same tree and A comes after B in tree order.
      isFollowing(node) {
        var nodePos, thisPos;
        nodePos = this.treePosition(node);
        thisPos = this.treePosition(this);
        if (nodePos === -1 || thisPos === -1) {
          return false;
        } else {
          return nodePos > thisPos;
        }
      }

      // Returns the preorder position of the given node in the tree, or -1
      // if the node is not in the tree.
      treePosition(node) {
        var found, pos;
        pos = 0;
        found = false;
        this.foreachTreeNode(this.document(), function(childNode) {
          pos++;
          if (!found && childNode === node) {
            return found = true;
          }
        });
        if (found) {
          return pos;
        } else {
          return -1;
        }
      }

      
      // Depth-first preorder traversal through the XML tree
      foreachTreeNode(node, func) {
        var child, j, len, ref1, res;
        node || (node = this.document());
        ref1 = node.children;
        for (j = 0, len = ref1.length; j < len; j++) {
          child = ref1[j];
          if (res = func(child)) {
            return res;
          } else {
            res = this.foreachTreeNode(child, func);
            if (res) {
              return res;
            }
          }
        }
      }

    };

    // DOM level 1
    Object.defineProperty(XMLNode.prototype, 'nodeName', {
      get: function() {
        return this.name;
      }
    });

    Object.defineProperty(XMLNode.prototype, 'nodeType', {
      get: function() {
        return this.type;
      }
    });

    Object.defineProperty(XMLNode.prototype, 'nodeValue', {
      get: function() {
        return this.value;
      }
    });

    Object.defineProperty(XMLNode.prototype, 'parentNode', {
      get: function() {
        return this.parent;
      }
    });

    Object.defineProperty(XMLNode.prototype, 'childNodes', {
      get: function() {
        if (!this.childNodeList || !this.childNodeList.nodes) {
          this.childNodeList = new XMLNodeList(this.children);
        }
        return this.childNodeList;
      }
    });

    Object.defineProperty(XMLNode.prototype, 'firstChild', {
      get: function() {
        return this.children[0] || null;
      }
    });

    Object.defineProperty(XMLNode.prototype, 'lastChild', {
      get: function() {
        return this.children[this.children.length - 1] || null;
      }
    });

    Object.defineProperty(XMLNode.prototype, 'previousSibling', {
      get: function() {
        var i;
        i = this.parent.children.indexOf(this);
        return this.parent.children[i - 1] || null;
      }
    });

    Object.defineProperty(XMLNode.prototype, 'nextSibling', {
      get: function() {
        var i;
        i = this.parent.children.indexOf(this);
        return this.parent.children[i + 1] || null;
      }
    });

    Object.defineProperty(XMLNode.prototype, 'ownerDocument', {
      get: function() {
        return this.document() || null;
      }
    });

    // DOM level 3
    Object.defineProperty(XMLNode.prototype, 'textContent', {
      get: function() {
        var child, j, len, ref1, str;
        if (this.nodeType === NodeType.Element || this.nodeType === NodeType.DocumentFragment) {
          str = '';
          ref1 = this.children;
          for (j = 0, len = ref1.length; j < len; j++) {
            child = ref1[j];
            if (child.textContent) {
              str += child.textContent;
            }
          }
          return str;
        } else {
          return null;
        }
      },
      set: function(value) {
        throw new Error("This DOM method is not implemented." + this.debugInfo());
      }
    });

    return XMLNode;

  }).call(this);

}).call(this);
