text: var domWalk = require("dom-walk")
  var dispatchEvent = require("./event/dispatch-event.js")
  var addEventListener = require("./event/add-event-listener.js")
  var removeEventListener = require("./event/remove-event-listener.js")
  var serializeNode = require("./serialize.js")
  
  var htmlns = "http://www.w3.org/1999/xhtml"
  
  module.exports = DOMElement
  
  function DOMElement(tagName, owner, namespace) {
      if (!(this instanceof DOMElement)) {
          return new DOMElement(tagName)
      }
  
      var ns = namespace === undefined ? htmlns : (namespace || null)
  
      this.tagName = ns === htmlns ? String(tagName).toUpperCase() : tagName
      this.className = ""
      this.dataset = {}
      this.childNodes = []
      this.parentNode = null
      this.style = {}
      this.ownerDocument = owner || null
      this.namespaceURI = ns
      this._attributes = {}
  
      if (this.tagName === 'INPUT') {
        this.type = 'text'
      }
  }
  
  DOMElement.prototype.type = "DOMElement"
  DOMElement.prototype.nodeType = 1
  
  DOMElement.prototype.appendChild = function _Element_appendChild(child) {
      if (child.parentNode) {
          child.parentNode.removeChild(child)
      }
  
      this.childNodes.push(child)
      child.parentNode = this
  
      return child
  }
  
  DOMElement.prototype.replaceChild =
      function _Element_replaceChild(elem, needle) {
          // TODO: Throw NotFoundError if needle.parentNode !== this
  
          if (elem.parentNode) {
              elem.parentNode.removeChild(elem)
          }
  
          var index = this.childNodes.indexOf(needle)
  
          needle.parentNode = null
          this.childNodes[index] = elem
          elem.parentNode = this
  
          return needle
      }
  
  DOMElement.prototype.removeChild = function _Element_removeChild(elem) {
      // TODO: Throw NotFoundError if elem.parentNode !== this
  
      var index = this.childNodes.indexOf(elem)
      this.childNodes.splice(index, 1)
  
      elem.parentNode = null
      return elem
  }
  
  DOMElement.prototype.insertBefore =
      function _Element_insertBefore(elem, needle) {
          // TODO: Throw NotFoundError if referenceElement is a dom node
          // and parentNode !== this
  
          if (elem.parentNode) {
              elem.parentNode.removeChild(elem)
          }
  
          var index = needle === null || needle === undefined ?
              -1 :
              this.childNodes.indexOf(needle)
  
          if (index > -1) {
              this.childNodes.splice(index, 0, elem)
          } else {
              this.childNodes.push(elem)
          }
  
          elem.parentNode = this
          return elem
      }
  
  DOMElement.prototype.setAttributeNS =
      function _Element_setAttributeNS(namespace, name, value) {
          var colonPosition = name.indexOf(":")
          var localName = colonPosition > -1 ? name.substr(colonPosition + 1) : name
          var attributes = this._attributes[namespace] || (this._attributes[namespace] = {})
          attributes[localName] = value
      }
  
  DOMElement.prototype.getAttributeNS =
      function _Element_getAttributeNS(namespace, name) {
          var attributes = this._attributes[namespace];
          if (!(attributes && typeof attributes[name] === "string")) {
              return null
          }
  
          return attributes[name]
      }
  
  DOMElement.prototype.removeAttributeNS =
      function _Element_removeAttributeNS(namespace, name) {
          var attributes = this._attributes[namespace];
          if (attributes) {
              delete attributes[name]
          }
      }
  
  DOMElement.prototype.hasAttributeNS =
      function _Element_hasAttributeNS(namespace, name) {
          var attributes = this._attributes[namespace]
          return !!attributes && name in attributes;
      }
  
  DOMElement.prototype.setAttribute = function _Element_setAttribute(name, value) {
      return this.setAttributeNS(null, name, value)
  }
  
  DOMElement.prototype.getAttribute = function _Element_getAttribute(name) {
      return this.getAttributeNS(null, name)
  }
  
  DOMElement.prototype.removeAttribute = function _Element_removeAttribute(name) {
      return this.removeAttributeNS(null, name)
  }
  
  DOMElement.prototype.hasAttribute = function _Element_hasAttribute(name) {
      return this.hasAttributeNS(null, name)
  }
  
  DOMElement.prototype.removeEventListener = removeEventListener
  DOMElement.prototype.addEventListener = addEventListener
  DOMElement.prototype.dispatchEvent = dispatchEvent
  
  // Un-implemented
  DOMElement.prototype.focus = function _Element_focus() {
      return void 0
  }
  
  DOMElement.prototype.toString = function _Element_toString() {
      return serializeNode(this)
  }
  
  DOMElement.prototype.getElementsByClassName = function _Element_getElementsByClassName(classNames) {
      var classes = classNames.split(" ");
      var elems = []
  
      domWalk(this, function (node) {
          if (node.nodeType === 1) {
              var nodeClassName = node.className || ""
              var nodeClasses = nodeClassName.split(" ")
  
              if (classes.every(function (item) {
                  return nodeClasses.indexOf(item) !== -1
              })) {
                  elems.push(node)
              }
          }
      })
  
      return elems
  }
  
  DOMElement.prototype.getElementsByTagName = function _Element_getElementsByTagName(tagName) {
      tagName = tagName.toLowerCase()
      var elems = []
  
      domWalk(this.childNodes, function (node) {
          if (node.nodeType === 1 && (tagName === '*' || node.tagName.toLowerCase() === tagName)) {
              elems.push(node)
          }
      })
  
      return elems
  }
  