class Element(Node, EventTarget, ParentNode, NonDocumentTypeChildNode,
              ChildNode, metaclass=ElementMeta):
    """Element base class."""

    nodeType = Node.ELEMENT_NODE
    nodeValue = None
    _parser_class = ElementParser  # type: Type[ElementParser]
    _element_buffer = WeakSet()  # type: WeakSet[Node]
    _elements_with_id = WeakValueDictionary()  # type: MutableMapping
    _should_escape_text = True
    _special_attr_string = ['id']
    _special_attr_boolean = []  # type: List[str]

    def __init__(self, tag: str='', parent: Node = None,
                 _registered: bool = True, **kwargs: Any) -> None:
        """Initialize.

        :arg str tag: HTML tag of this node.
        :arg Node parent: Parent node of this node.
        :arg bool _registered: Is registered to CustomElementRegistry.
        :arg kwargs: key-value pair of attributes.
        """
        super().__init__(parent=parent)
        self._registered = _registered
        self.tag = tag
        self._element_buffer.add(self)  # used to suport custom elements
        self.attributes = NamedNodeMap(self)
        self.classList = DOMTokenList(self)

        if 'class_' in kwargs:
            kwargs['class'] = kwargs.pop('class_')
        if 'is_' in kwargs:
            kwargs['is'] = kwargs.pop('is_')
        for k, v in kwargs.items():
            self.setAttribute(k, v)

    def _clone_node(self) -> 'Element':
        clone = type(self)(self.tag)
        for attr in self.attributes:
            clone.setAttribute(attr, self.getAttribute(attr))
        # TODO: should clone event listeners???
        return clone

    def _get_attrs_by_string(self) -> str:
        # attrs = ' '.join(attr.html for attr in self.attributes.values())
        attrs = self.attributes.toString()
        classes = self.getAttribute('class')
        if classes:
            attrs = ' '.join((attrs.strip(), 'class="{}"'.format(classes)))
        return attrs.strip()

    @property
    def start_tag(self) -> str:
        """Return HTML start tag."""
        tag = '<' + self.tag
        attrs = self._get_attrs_by_string()
        if attrs:
            tag = ' '.join((tag, attrs))
        return tag + '>'

    @property
    def end_tag(self) -> str:
        """Return HTML end tag."""
        return '</{}>'.format(self.tag)

    def _parse_html(self, html: str) -> DocumentFragment:
        parser = self._parser_class()
        parser.feed(html)
        return parser.root

    def _get_inner_html(self) -> str:
        return ''.join(child.html for child in self.childNodes)

    def _set_inner_html(self, html: str) -> None:
        self._empty()
        self._append_child(self._parse_html(html))

    @property
    def innerHTML(self) -> str:
        """Return HTML representation of child nodes."""
        return self._get_inner_html()

    @innerHTML.setter
    def innerHTML(self, html: str) -> None:
        """Remove all child nodes and set ``html`` as new contents.

        ``html`` is parsed to ``Element`` nodes and set as child nodes.
        """
        self._set_inner_html(html)

    @property
    def html(self) -> str:
        """Return HTML representation of this node."""
        return self.start_tag + self.innerHTML + self.end_tag

    def insertAdjacentHTML(self, position: str, html: str) -> None:
        """Parse ``html`` to DOM and insert to ``position``.

        ``position`` is a case-insensive string, and must be one of
        "beforeBegin", "afterBegin", "beforeEnd", or "afterEnd".
        """
        df = self._parse_html(html)
        pos = position.lower()
        if pos == 'beforebegin':
            self.before(df)
        elif pos == 'afterbegin':
            self.prepend(df)
        elif pos == 'beforeend':
            self.append(df)
        elif pos == 'afterend':
            self.after(df)
        else:
            raise ValueError(
                'The value provided ({}) is not one of "beforeBegin", '
                '"afterBegin", "beforeEnd", or "afterEnd".'.format(position)
            )

    @property
    def outerHTML(self) -> str:
        """Return html representation of this node.

        Equivalent to ``self.html``.
        """
        return self.html

    @property
    def nodeName(self) -> str:  # type: ignore
        """Return tag name (capital case)."""
        return self.tag.upper()

    @property
    def tagName(self) -> str:
        """Return tag name (capital case)."""
        return self.tag.upper()

    @property
    def localName(self) -> str:
        """Return tag name (lower case)."""
        return self.tag.lower()

    @property
    def className(self) -> str:
        """Get/Set class name as/by string."""
        return self.getAttribute('class') or ''  # type: ignore

    @className.setter
    def className(self, new_class: str) -> None:
        if not isinstance(new_class, str):
            raise TypeError('className must be str.')
        self.setAttribute('class', new_class)

    def getAttribute(self, attr: str) -> _AttrValueType:
        """Get attribute of this node as string format.

        If this node does not have ``attr``, return None.
        """
        if attr == 'class':
            if self.classList:
                return self.classList.toString()
            return None
        attr_node = self.getAttributeNode(attr)
        if attr_node is None:
            return None
        return attr_node.value

    def getAttributeNode(self, attr: str) -> Optional[Attr]:
        """Get attribute of this node as Attr format.

        If this node does not have ``attr``, return None.
        """
        return self.attributes.getNamedItem(attr)

    def hasAttribute(self, attr: str) -> bool:
        """Return True if this node has ``attr``."""
        if attr == 'class':
            return bool(self.classList)
        return attr in self.attributes

    def hasAttributes(self) -> bool:
        """Return True if this node has any attributes."""
        return bool(self.attributes) or bool(self.classList)

    def _set_attribute_class(self, value: _AttrValueType) -> None:
        if isinstance(value, str):
            self.classList = DOMTokenList(self, value)
        elif isinstance(value, Iterable):
            self.classList = DOMTokenList(self, *value)
        else:
            raise TypeError(
                'class attribute must be str, '
                'but got {}'.format(type(value))
            )

    def _change_id(self, value: _AttrValueType) -> None:
        if 'id' in self.attributes:
            # remove old reference to self
            self._elements_with_id.pop(self.id, None)
        # register this elements with new id
        if isinstance(value, (int, str)):
            self._elements_with_id[value] = self
        else:
            raise TypeError(
                'id attribute must be int or integer-string.'
            )

    def _set_attribute(self, attr: str, value: _AttrValueType) -> None:
        if attr == 'class':
            self._set_attribute_class(value)
        else:
            if attr == 'id':
                self._change_id(value)
            if not attr == 'draggable':
                attr_cls = Attr
            else:
                attr_cls = DraggableAttr
            new_attr_node = attr_cls(attr, value)
            self.setAttributeNode(new_attr_node)

    def setAttribute(self, attr: str, value: _AttrValueType) -> None:
        """Set ``attr`` and ``value`` in this node."""
        self._set_attribute(attr, value)

    def setAttributeNode(self, attr: Attr) -> None:
        """Set ``Attr`` node as this node's attribute."""
        self.attributes.setNamedItem(attr)

    def _remove_attribute(self, attr: str) -> None:
        if attr == 'class':
            self.classList = DOMTokenList(self)
        else:
            if attr == 'id':
                self._elements_with_id.pop(self.id, None)
            _attr = self.getAttributeNode(attr)
            if _attr:
                self.attributes.removeNamedItem(_attr)

    def removeAttribute(self, attr: str) -> None:
        """Remove ``attr`` from this node."""
        self._remove_attribute(attr)

    def removeAttributeNode(self, attr: Attr) -> Optional[Attr]:
        """Remove ``Attr`` node from this node."""
        return self.attributes.removeNamedItem(attr)

    def getElementsBy(self, cond: Callable[['Element'], bool]) -> NodeList:
        """Get elements under this node which matches condition."""
        return getElementsBy(self, cond)

    def getElementsByTagName(self, tag: str) -> NodeList:
        """Get elements with tag name under this node."""
        return getElementsByTagName(self, tag)

    def getElementsByClassName(self, class_name: str) -> NodeList:
        """Get elements with class name under this node."""
        return getElementsByClassName(self, class_name)

    def querySelector(self, selectors: str) -> Node:
        """Not Implemented."""
        return querySelector(self, selectors)

    def querySelectorAll(self, selectors: str) -> NodeList:
        """Not Implemented."""
        return querySelectorAll(self, selectors) class Node(AbstractNode):
    """Base Class for Node interface."""

    @property
    def connected(self) -> bool:
        """When this instance has any connection, return True."""
        return False

    def __init__(self, parent: AbstractNode = None) -> None:
        """Initialize node object with parent node.

        :param Node parent: parent node.
        """
        super().__init__()  # Need to call init in multiple inheritce
        self.__children = list()  # type: List[Node]
        self.__parent = None
        if parent:
            parent.appendChild(self)

    def __bool__(self) -> bool:
        """Return always True."""
        return True

    def __len__(self) -> int:
        """Return number of child nodes."""
        return self.length

    def __contains__(self, other: AbstractNode) -> bool:
        return other in self.__children

    # DOM Level 1
    @property
    def length(self) -> int:
        """Return number of child nodes."""
        return len(self.childNodes)

    @property
    def parentNode(self) -> Optional[AbstractNode]:
        """Return parent node.

        If this node does not have a parent, return ``None``.
        """
        return self.__parent

    @property
    def childNodes(self) -> 'NodeList':
        """Return child nodes of this node.

        Returned object is an instance of NodeList, which is a list like object
        but not support any modification. NodeList is a **live object**, which
        means that changes on this node is reflected to the object.
        """
        return NodeList(self.__children)

    @property
    def firstChild(self) -> Optional[AbstractNode]:
        """Return the first child node.

        If this node does not have any child, return ``None``.
        """
        if self.hasChildNodes():
            return self.childNodes[0]
        return None

    @property
    def lastChild(self) -> Optional[AbstractNode]:
        """Return the last child node.

        If this node does not have any child, return ``None``.
        """
        if self.hasChildNodes():
            return self.childNodes[-1]
        return None

    @property
    def previousSibling(self) -> Optional[AbstractNode]:
        """Return the previous sibling of this node.

        If there is no previous sibling, return ``None``.
        """
        parent = self.parentNode
        if parent is None:
            return None
        return parent.childNodes.item(parent.childNodes.index(self) - 1)

    @property
    def nextSibling(self) -> Optional[AbstractNode]:
        """Return the next sibling of this node.

        If there is no next sibling, return ``None``.
        """
        parent = self.parentNode
        if parent is None:
            return None
        return parent.childNodes.item(parent.childNodes.index(self) + 1)

    # DOM Level 2
    @property
    def ownerDocument(self) -> Optional[AbstractNode]:
        """Return the owner document of this node.

        Owner document is an ancestor document node of this node. If this node
        (or node tree including this node) is not appended to any document
        node, this property returns ``None``.

        :rtype: Document or None
        """
        if self.nodeType == Node.DOCUMENT_NODE:
            return self
        elif self.parentNode:
            return self.parentNode.ownerDocument
        return None

    # Methods
    def _append_document_fragment(self, node: AbstractNode) -> AbstractNode:
        for c in tuple(node.childNodes):
            self._append_child(c)
        return node

    def _append_element(self, node: AbstractNode) -> AbstractNode:
        if node.parentNode:
            node.parentNode.removeChild(node)
        self.__children.append(node)
        node.__parent = self
        return node

    def _append_child(self, node: AbstractNode) -> AbstractNode:
        if not isinstance(node, Node):
            raise TypeError(
                'appndChild() only accepts a Node instance, but get {}. '
                'If you want to add string or mupltiple nodes once, '
                'use append() method instead.'.format(type(node)))
        if node.nodeType == Node.DOCUMENT_FRAGMENT_NODE:
            return self._append_document_fragment(node)
        return self._append_element(node)

    def appendChild(self, node: AbstractNode) -> AbstractNode:
        """Append the node at the last of this child nodes."""
        return self._append_child(node)

    def index(self, node: AbstractNode) -> int:
        """Return index of the node.

        If the node is not a child of this node, raise ``ValueError``.
        """
        if node in self.childNodes:
            return self.childNodes.index(node)
        elif isinstance(node, Text):
            for i, n in enumerate(self.childNodes):
                # should consider multiple match?
                if isinstance(n, Text) and n.data == node:
                    return i
        raise ValueError('node is not in this node')

    def _insert_document_fragment_before(self, node: AbstractNode,
                                         ref_node: AbstractNode
                                         ) -> AbstractNode:
        for c in tuple(node.childNodes):
            self._insert_before(c, ref_node)
        return node

    def _insert_element_before(self, node: AbstractNode,
                               ref_node: AbstractNode) -> AbstractNode:
        if node.parentNode:
            node.parentNode.removeChild(node)
        self.__children.insert(self.index(ref_node), node)
        node.__parent = self
        return node

    def _insert_before(self, node: AbstractNode, ref_node:
                       AbstractNode) -> AbstractNode:
        if not isinstance(node, Node):
            raise TypeError(
                'insertBefore() only accepts a Node instance, but get {}.'
                'If you want to insert string or mupltiple nodes, '
                'use ref_node.before() instead.'.format(type(node)))
        if node.nodeType == Node.DOCUMENT_FRAGMENT_NODE:
            return self._insert_document_fragment_before(node, ref_node)
        return self._insert_element_before(node, ref_node)

    def insertBefore(self, node: AbstractNode,
                     ref_node: AbstractNode) -> AbstractNode:
        """Insert a node just before the reference node."""
        return self._insert_before(node, ref_node)

    def hasChildNodes(self) -> bool:
        """Return True if this node has child nodes, otherwise return False."""
        return bool(self.childNodes)

    def _remove_child(self, node: AbstractNode) -> AbstractNode:
        if node not in self.__children:
            raise ValueError('node to be removed is not a child of this node.')
        self.__children.remove(node)
        node.__parent = None
        return node

    def removeChild(self, node: AbstractNode) -> AbstractNode:
        """Remove a node from this node.

        If node is not a child of this node, raise ``ValueError``.
        """
        return self._remove_child(node)

    def _replace_child(self, new_child: AbstractNode,
                       old_child: AbstractNode) -> AbstractNode:
        self._insert_before(new_child, old_child)
        return self._remove_child(old_child)

    def replaceChild(self, new_child: AbstractNode,
                     old_child: AbstractNode) -> AbstractNode:
        """Replace an old child with new child."""
        return self._replace_child(new_child, old_child)

    def hasAttributes(self) -> bool:
        """Return True if this node has attributes."""
        return hasattr(self, 'attributes') and bool(self.attributes)

    def _clone_node(self) -> 'Node':
        clone = type(self)()
        return clone

    def _clone_node_deep(self) -> 'Node':
        clone = self._clone_node()
        for child in self.childNodes:
            clone.appendChild(child._clone_node_deep())
        return clone

    def cloneNode(self, deep: bool=False) -> AbstractNode:
        """Return new copy of this node.

        If optional argument ``deep`` is specified and is True, new node has
        clones of child nodes of this node (if presents).
        """
        if deep:
            return self._clone_node_deep()
        return self._clone_node()

    __copy__ = _clone_node  # alias

    def __deepcopy__(self, memo: Any) -> 'Node':
        return self.cloneNode(True)

    def _empty(self) -> None:
        for child in tuple(self.__children):
            self._remove_child(child)

    def empty(self) -> None:
        """[Not Standard] Remove all child nodes from this node.

        This is equivalent to ``node.textContent = ''``.
        """
        self._empty()

    def _get_text_content(self) -> str:
        return ''.join(child.textContent for child in self.childNodes)

    def _set_text_content(self, value: str) -> None:
        self._empty()
        if value:
            self._append_child(Text(value))

    @property
    def textContent(self) -> str:
        """Return text contents of this node and all chid nodes.

        When any value is set to this property, all child nodes are removed and
        new value is set as a text node.
        """
        return self._get_text_content()

    @textContent.setter
    def textContent(self, value: str) -> None:
        """Remove all child nodes and set new text."""
        self._set_text_content(value) class NodeList(Sequence[Node]):
    """Collection of Node objects."""

    def __init__(self, nodes: Sequence[Node]) -> None:
        """Initialize NodeList by iterable `nodes`."""
        self.__nodes = nodes

    def __getitem__(self, index: int) -> Node:  # type: ignore
        """Get `index`-th node."""
        return self.__nodes[index]

    def __len__(self) -> int:
        return len(self.__nodes)

    def __contains__(self, other: object) -> bool:
        return other in self.__nodes

    def __iter__(self) -> Iterator[AbstractNode]:
        for n in self.__nodes:
            yield n

    @property
    def length(self) -> int:
        """Return number of nodes in this list."""
        return len(self)

    def item(self, index: int) -> Optional[Node]:
        """Return item with the index.

        If the index is negative number or out of the list, return None.
        """
        if not isinstance(index, int):
            raise TypeError(
                'Indeces must be integer, not {}'.format(type(index)))
        return self.__nodes[index] if 0 <= index < self.length else None

    def index(self, node: Node) -> int:  # type: ignore
        """Get index of the node."""
        return self.__nodes.index(node) class ParentNode(AbstractNode):
    """Mixin class for Node classes which can have child nodes.

    This class is inherited by Document, DocumentFragment, and Element class.
    """

    @property
    def children(self) -> NodeList:
        """Return list of child nodes.

        Currently this is not a live object.
        """
        return NodeList([e for e in self.childNodes
                         if e.nodeType == Node.ELEMENT_NODE])

    @property
    def firstElementChild(self) -> Optional[AbstractNode]:
        """First Element child node.

        If this node has no element child, return None.
        """
        for child in self.childNodes:
            if child.nodeType == Node.ELEMENT_NODE:
                return child
        return None

    @property
    def lastElementChild(self) -> Optional[AbstractNode]:
        """Last Element child node.

        If this node has no element child, return None.
        """
        for child in reversed(self.childNodes):  # type: ignore
            if child.nodeType == Node.ELEMENT_NODE:
                return child
        return None

    def prepend(self, *nodes: Union[str, AbstractNode]) -> None:
        """Insert new nodes before first child node."""
        node = _to_node_list(nodes)
        if self.firstChild:
            self.insertBefore(node, self.firstChild)
        else:
            self.appendChild(node)

    def append(self, *nodes: Union[AbstractNode, str]) -> None:
        """Append new nodes after last child node."""
        node = _to_node_list(nodes)
        self.appendChild(node) class NonDocumentTypeChildNode(AbstractNode):
    """Mixin class for ``CharacterData`` and ``DocumentType`` class."""

    @property
    def previousElementSibling(self) -> Optional[AbstractNode]:
        """Previous Element Node.

        If this node has no previous element node, return None.
        """
        if self.parentNode is None:
            return None
        siblings = self.parentNode.childNodes
        for i in range(siblings.index(self), 0, -1):
            n = siblings[i-1]
            if n.nodeType == Node.ELEMENT_NODE:
                return n
        return None

    @property
    def nextElementSibling(self) -> Optional[AbstractNode]:
        """Next Element Node.

        If this node has no next element node, return None.
        """
        if self.parentNode is None:
            return None
        siblings = self.parentNode.childNodes
        for i in range(siblings.index(self) + 1, len(siblings)):
            n = siblings[i]
            if n.nodeType == Node.ELEMENT_NODE:
                return n
        return None class ChildNode(AbstractNode):
    """Mixin class for Node classes which can have parent node.

    This class is inherited by DocumentType, Element, and CharacterData
    (super class of Text, Comment, and RawHtml) classes.
    """

    def before(self, *nodes: Union[AbstractNode, str]) -> None:
        """Insert nodes before this node.

        If nodes contains ``str``, it will be converted to Text node.
        """
        if self.parentNode:
            node = _to_node_list(nodes)
            self.parentNode.insertBefore(node, self)  # type: ignore

    def after(self, *nodes: Union[AbstractNode, str]) -> None:
        """Append nodes after this node.

        If nodes contains ``str``, it will be converted to Text node.
        """
        if self.parentNode:
            node = _to_node_list(nodes)
            _next_node = self.nextSibling
            if _next_node is None:
                self.parentNode.appendChild(node)
            else:
                self.parentNode.insertBefore(node, _next_node)

    def replaceWith(self, *nodes: Union[AbstractNode, str]) -> None:
        """Replace this node with nodes.

        If nodes contains ``str``, it will be converted to Text node.
        """
        if self.parentNode:
            node = _to_node_list(nodes)
            self.parentNode.replaceChild(node, self)  # type: ignore

    def _remove(self) -> None:
        if self.parentNode:
            self.parentNode.removeChild(self)  # type: ignore

    def remove(self) -> None:
        """Remove this node from the parent node."""
        self._remove() class Text(CharacterData):
    """Node class to wrap text contents."""

    nodeType = Node.TEXT_NODE
    nodeName = '#text'

    @property
    def html(self) -> str:
        """Return html-escaped string representation of this node."""
        if self.parentNode and self.parentNode._should_escape_text:
            return html.escape(self.data)
        return self.data class DocumentType(Node, NonDocumentTypeChildNode):
    """DocumentType node class."""

    nodeType = Node.DOCUMENT_TYPE_NODE
    nodeValue = None
    textContent = None  # type: ignore
    _should_escape_text = True

    def _clone_node(self) -> 'CharacterData':
        clone = type(self)(self.name)
        return clone

    @property
    def nodeName(self) -> str:  # type: ignore
        """Return node name (=type)."""
        return self.name

    def __init__(self, type: str = 'html', parent: Node = None) -> None:
        """Initialize DocumentType node with `type` doctype."""
        super().__init__(parent=parent)
        self.__type = type

    @property
    def name(self) -> str:
        """Return node type."""
        return self.__type

    @name.setter
    def name(self, name: str) -> None:
        self.__type = name

    @property
    def html(self) -> str:
        """Return html representation."""
        return '<!DOCTYPE {}>'.format(self.name) class DocumentFragment(Node, ParentNode):
    """DocumentFragument node class."""

    nodeType = Node.DOCUMENT_FRAGMENT_NODE
    nodeName = '#document-fragment'
    parentNode = None
    previousSibling = None
    nextSibling = None
    _should_escape_text = True

    @property
    def html(self) -> str:
        """Return html representation."""
        return ''.join(child.html for child in self.childNodes) class RawHtml(Text):
    """Very similar to ``Text`` class, but contents are always not escaped.

    This node is [NOT DOM Standard].
    """

    @property
    def html(self) -> str:
        """Return html representation."""
        return self.data class Comment(CharacterData):
    """Comment node class."""

    nodeType = Node.COMMENT_NODE
    nodeName = '#comment'

    @property
    def html(self) -> str:
        """Return html representation."""
        return ''.join(('<!--', self.data, '-->')) class CharacterData(Node, ChildNode, NonDocumentTypeChildNode):
    """Abstract class for classes which wraps text data.

    This class is a super class of ``Text`` and ``Comment``.
    """

    # DOM Level 1
    firstChild = None
    lastChild = None
    specified = False

    def __init__(self, text: str='', parent: Node = None) -> None:  # noqa
        super().__init__(parent=parent)
        self.data = text

    def _clone_node(self) -> 'CharacterData':
        clone = type(self)(self.data)
        return clone

    @property
    def html(self) -> str:
        """Return html representation of this node."""
        return self.textContent

    def _get_text_content(self) -> str:
        return self.data

    def _set_text_content(self, value: str) -> None:
        self.data = value

    def __len__(self) -> int:
        return len(self.data)

    @property
    def length(self) -> int:
        """Return length of content."""
        return len(self)

    def _append_data(self, string: str) -> None:
        self.data += string

    def appendData(self, string: str) -> None:
        """Add ``string`` to end of this node."""
        self._append_data(string)

    def _insert_data(self, offset: int, string: str) -> None:
        self.data = ''.join((self.data[:offset], string, self.data[offset:]))

    def insertData(self, offset: int, string: str) -> None:
        """Insert ``string`` at offset on this node."""
        self._insert_data(offset, string)

    def _delete_data(self, offset: int, count: int) -> None:
        self.data = ''.join((self.data[:offset], self.data[offset+count:]))

    def deleteData(self, offset: int, count: int) -> None:
        """Delete data by offset to count letters."""
        self._delete_data(offset, count)

    def _replace_data(self, offset: int, count: int, string: str) -> None:
        self.data = ''.join((
            self.data[:offset], string, self.data[offset+count:]))

    def replaceData(self, offset: int, count: int, string: str) -> None:
        """Replace data from offset to count by string."""
        self._replace_data(offset, count, string)

    @property
    def childNodes(self) -> NodeList:
        """Return child nodes.

        This node can't have child, so return empty ``NodeList`` object.
        """
        return NodeList([])

    # Methods
    def appendChild(self, node: Node) -> Node:
        """Not supported."""
        raise NotImplementedError('This node does not support this method.')

    def insertBefore(self, node: Node, ref_node: Node) -> Node:
        """Not supported."""
        raise NotImplementedError('This node does not support this method.')

    def hasChildNodes(self) -> bool:
        """Return false."""
        return False

    def removeChild(self, node: Node) -> Node:
        """Not supported."""
        raise NotImplementedError('This node does not support this method.')

    def replaceChild(self, new_child: Node, old_child: Node) -> Node:
        """Not supported."""
        raise NotImplementedError('This node does not support this method.')

    def hasAttributes(self) -> bool:
        """Return false."""
        return False class TestCase(unittest.TestCase):
    """Base class for testing wdom modules.

    This class is a sub class of the ``unittest.TestCase``. After all test
    methods, reset wdom's global objects like document, application, and
    elements. If you use ``tearDown`` method, do not forget to call
    ``super().tearDown()``.
    """

    def setUp(self) -> None:
        """Reset WDOM states."""
        super().setUp()
        reset()

    def tearDown(self) -> None:
        """Reset WDOM states."""
        reset()
        super().tearDown()

    def assertIsTrue(self, bl: bool) -> None:
        """Check arg is exactly True, not truthy."""
        self.assertIs(bl, True)

    def assertIsFalse(self, bl: bool) -> None:
        """Check arg is exactly False, not falsy."""
        self.assertIs(bl, False)
from wdom.element import Element
from wdom.node import Node, NodeList
from wdom.node import ParentNode, NonDocumentTypeChildNode, ChildNode
from wdom.node import Text, DocumentType, DocumentFragment, RawHtml
from wdom.node import Comment, CharacterData
from .base import TestCase
        from copy import copy
        from copy import deepcopy
        self._test_deep_copy(clone)

    def test_owner_document(self):
        self.assertIsNone(self.node.ownerDocument)

    def test_text_content(self):
        self.assertEqual(self.node.textContent, '')
        self.node.textContent = 'a'
        self.assertEqual(self.node.textContent, 'a')
        self.node.textContent = 'b'
        self.assertEqual(self.node.textContent, 'b')

        self.node.appendChild(self.c1)
        self.c1.textContent = 'c1'
        self.assertEqual(self.node.textContent, 'bc1')
        self.node.textContent = 'd'
        self.assertEqual(self.node.textContent, 'd')
        self.assertIsNone(self.c1.parentNode)
        self.assertEqual(self.c1.textContent, 'c1')

    def test_index(self):
        self.node.appendChild(self.c1)
        self.node.appendChild(self.c2)
        self.node.appendChild(self.c3)
        self.assertEqual(self.node.index(self.c1), 0)
        self.assertEqual(self.node.index(self.c2), 1)
        self.assertEqual(self.node.index(self.c3), 2)
        self.assertEqual(self.node.childNodes.index(self.c1), 0)
        self.assertEqual(self.node.childNodes.index(self.c2), 1)
        self.assertEqual(self.node.childNodes.index(self.c3), 2)


class TestNodeList(TestCase):
    def setUp(self):
        super().setUp()
        self.nl = NodeList(list(range(3)))

    def test_length(self):
        self.assertEqual(self.nl.length, 3)
        self.assertEqual(len(self.nl), 3)

    def test_index_access(self):
        self.assertEqual(self.nl[1], 1)
        self.assertEqual(self.nl[-1], 2)
        self.assertEqual(self.nl[1:2], [1])
        with self.assertRaises(IndexError):
            self.nl[5]

    def test_item(self):
        self.assertEqual(self.nl.item(1), 1)
        self.assertEqual(self.nl.item(-1), None)
        self.assertEqual(self.nl.item(5), None)
        with self.assertRaises(TypeError):
            self.nl.item(slice(1, 2))

    def test_contains(self):
        self.assertTrue(1 in self.nl)
        self.assertFalse(5 in self.nl)

    def test_iteration(self):
        l1 = [0, 1, 2]
        for n in self.nl:
            self.assertEqual(n, l1.pop(0))
        l2 = [2, 1, 0]
        for n in reversed(self.nl):
            self.assertEqual(n, l2.pop(0))

    def test_index(self):
        self.assertEqual(self.nl.index(0), 0)
        self.assertEqual(self.nl.index(1), 1)
        self.assertEqual(self.nl.index(2), 2)


class P(Node, ParentNode):
    pass


class NDTC(Node, NonDocumentTypeChildNode):
    pass


class C(Node, ChildNode):
    pass


def is_equal_nodes(p, nodes2):
    nodes1 = p.childNodes
    assert len(nodes1) == len(nodes2)
    for n1, n2 in zip(nodes1, nodes2):
        if isinstance(n1, Text):
            if isinstance(n2, Text):
                assert n1.textContent == n2.textContent
            elif isinstance(n2, str):
                assert n1.textContent == n2
            else:
                raise AssertionError(n1, n2)
        elif isinstance(n1, Node):
            assert n1 is n2
        else:
            raise AssertionError(n1, n2)


class TestParentNode(TestCase):
    def setUp(self):
        self.p = P()
        self.c1 = C()
        self.c2 = C()
        self.c3 = C()
        self.c4 = C()

    def test_children(self):
        self.p.appendChild(Text('a'))
        self.p.appendChild(Comment('b'))
        self.assertEqual(self.p.children.length, 0)
        elm1 = Element('c1')
        self.p.appendChild(elm1)
        self.p.appendChild(Text('d'))
        self.assertEqual(self.p.children.length, 1)
        self.assertIs(self.p.firstElementChild, elm1)
        self.assertIs(self.p.lastElementChild, elm1)

        elm2 = Element('c2')