Warning: session_start(): Session cannot be started after headers have already been sent in /home/tvrreohg/public_html/manga.php on line 13
from webencodings import ascii_lower
from .compiler import compile_selector_list, split_whitespace
class cached_property(object):
# Borrowed from Werkzeug
# https://github.com/mitsuhiko/werkzeug/blob/master/werkzeug/utils.py
def __init__(self, func, name=None, doc=None):
self.__name__ = name or func.__name__
self.__module__ = func.__module__
self.__doc__ = doc or func.__doc__
self.func = func
def __get__(self, obj, type=None, __missing=object()):
if obj is None:
return self
value = obj.__dict__.get(self.__name__, __missing)
if value is __missing:
value = self.func(obj)
obj.__dict__[self.__name__] = value
return value
class ElementWrapper(object):
"""
A wrapper for an ElementTree :class:`xml.etree.ElementTree.Element`
for Selector matching.
This class should not be instanciated directly.
:meth:`from_xml_root` or :meth:`from_html_root` should be used
for the root element of a document,
and other elements should be accessed (and wrappers generated)
using methods such as :meth:`iter_children` and :meth:`iter_subtree`.
:class:`ElementWrapper` objects compare equal
if their underlying :class:`xml.etree.ElementTree.Element` do.
"""
@classmethod
def from_xml_root(cls, root, content_language=None):
"""Wrap for selector matching the root of an XML or XHTML document.
:param root:
An ElementTree :class:`xml.etree.ElementTree.Element`
for the root element of a document.
If the given element is not the root,
selector matching will behave is if it were.
In other words, selectors will be not be `scoped`_
to the subtree rooted at that element.
:returns:
A new :class:`ElementWrapper`
.. _scoped: https://drafts.csswg.org/selectors-4/#scoping
"""
return cls._from_root(root, content_language, in_html_document=False)
@classmethod
def from_html_root(cls, root, content_language=None):
"""Same as :meth:`from_xml_root`, but for documents parsed with an HTML parser
like html5lib, which should be the case of documents with the
``text/html`` MIME type.
Compared to :meth:`from_xml_root`,
this makes element attribute names in Selectors case-insensitive.
"""
return cls._from_root(root, content_language, in_html_document=True)
@classmethod
def _from_root(cls, root, content_language, in_html_document=True):
if hasattr(root, 'getroot'):
root = root.getroot()
return cls(root, parent=None, index=0, previous=None,
in_html_document=in_html_document,
content_language=content_language)
def __init__(self, etree_element, parent, index, previous,
in_html_document, content_language=None):
#: The underlying ElementTree :class:`xml.etree.ElementTree.Element`
self.etree_element = etree_element
#: The parent :class:`ElementWrapper`,
#: or :obj:`None` for the root element.
self.parent = parent
#: The previous sibling :class:`ElementWrapper`,
#: or :obj:`None` for the root element.
self.previous = previous
if parent is not None:
#: The :attr:`parent`’s children
#: as a list of
#: ElementTree :class:`xml.etree.ElementTree.Element`\ s.
#: For the root (which has no parent)
self.etree_siblings = parent.etree_children
else:
self.etree_siblings = [etree_element]
#: The position within the :attr:`parent`’s children, counting from 0.
#: ``e.etree_siblings[e.index]`` is always ``e.etree_element``.
self.index = index
self.in_html_document = in_html_document
self.transport_content_language = content_language
def __eq__(self, other):
return (type(self) == type(other) and
self.etree_element == other.etree_element)
def __ne__(self, other):
return not (self == other)
def __hash__(self):
return hash((type(self), self.etree_element))
def __iter__(self):
for element in self.iter_children():
yield element
def iter_ancestors(self):
"""Return an iterator of existing :class:`ElementWrapper` objects
for this element’s ancestors,
in reversed tree order (from :attr:`parent` to the root)
The element itself is not included,
this is an empty sequence for the root element.
"""
element = self
while element.parent is not None:
element = element.parent
yield element
def iter_previous_siblings(self):
"""Return an iterator of existing :class:`ElementWrapper` objects
for this element’s previous siblings,
in reversed tree order.
The element itself is not included,
this is an empty sequence for a first child or the root element.
"""
element = self
while element.previous is not None:
element = element.previous
yield element
def iter_children(self):
"""Return an iterator of newly-created :class:`ElementWrapper` objects
for this element’s child elements,
in tree order.
"""
child = None
for i, etree_child in enumerate(self.etree_children):
child = type(self)(
etree_child,
parent=self,
index=i,
previous=child,
in_html_document=self.in_html_document,
)
yield child
def iter_subtree(self):
"""Return an iterator of newly-created :class:`ElementWrapper` objects
for the entire subtree rooted at this element,
in tree order.
Unlike in other methods, the element itself *is* included.
This loops over an entire document:
.. code-block:: python
for element in ElementWrapper.from_root(root_etree).iter_subtree():
...
"""
stack = [iter([self])]
while stack:
element = next(stack[-1], None)
if element is None:
stack.pop()
else:
yield element
stack.append(element.iter_children())
@staticmethod
def _compile(selectors):
return [
compiled_selector.test
for selector in selectors
for compiled_selector in (
[selector] if hasattr(selector, 'test')
else compile_selector_list(selector)
)
if compiled_selector.pseudo_element is None and
not compiled_selector.never_matches
]
def matches(self, *selectors):
"""Return wether this elememt matches any of the given selectors.
:param selectors:
Each given selector is either a :class:`compiler.CompiledSelector`,
or an argument to :func:`compile_selector_list`.
"""
return any(test(self) for test in self._compile(selectors))
def query_all(self, *selectors):
"""
Return elements, in tree order, that match any of the given selectors.
Selectors are `scoped`_ to the subtree rooted at this element.
.. _scoped: https://drafts.csswg.org/selectors-4/#scoping
:param selectors:
Each given selector is either a :class:`compiler.CompiledSelector`,
or an argument to :func:`compile_selector_list`.
:returns:
An iterator of newly-created :class:`ElementWrapper` objects.
"""
tests = self._compile(selectors)
if len(tests) == 1:
return filter(tests[0], self.iter_subtree())
elif selectors:
return (
element
for element in self.iter_subtree()
if any(test(element) for test in tests)
)
else:
return iter(())
def query(self, *selectors):
"""Return the first element (in tree order)
that matches any of the given selectors.
:param selectors:
Each given selector is either a :class:`compiler.CompiledSelector`,
or an argument to :func:`compile_selector_list`.
:returns:
A newly-created :class:`ElementWrapper` object,
or :obj:`None` if there is no match.
"""
return next(self.query_all(*selectors), None)
@cached_property
def etree_children(self):
"""This element’s children,
as a list of ElementTree :class:`xml.etree.ElementTree.Element`.
Other ElementTree nodes such as
:func:`comments ` and
:func:`processing instructions
`
are not included.
"""
return [c for c in self.etree_element if isinstance(c.tag, str)]
@cached_property
def local_name(self):
"""The local name of this element, as a string."""
namespace_url, local_name = _split_etree_tag(self.etree_element.tag)
self.__dict__[str('namespace_url')] = namespace_url
return local_name
@cached_property
def namespace_url(self):
"""The namespace URL of this element, as a string."""
namespace_url, local_name = _split_etree_tag(self.etree_element.tag)
self.__dict__[str('local_name')] = local_name
return namespace_url
@cached_property
def id(self):
"""The ID of this element, as a string."""
return self.etree_element.get('id')
@cached_property
def classes(self):
"""The classes of this element, as a :class:`set` of strings."""
return set(split_whitespace(self.etree_element.get('class', '')))
@cached_property
def lang(self):
"""The language of this element, as a string."""
# http://whatwg.org/C#language
xml_lang = self.etree_element.get(
'{http://www.w3.org/XML/1998/namespace}lang')
if xml_lang is not None:
return ascii_lower(xml_lang)
is_html = (
self.in_html_document or
self.namespace_url == 'http://www.w3.org/1999/xhtml')
if is_html:
lang = self.etree_element.get('lang')
if lang is not None:
return ascii_lower(lang)
if self.parent is not None:
return self.parent.lang
# Root elememnt
if is_html:
content_language = None
iterator = self.etree_element.iter(
'{http://www.w3.org/1999/xhtml}meta')
for meta in iterator:
http_equiv = meta.get('http-equiv', '')
if ascii_lower(http_equiv) == 'content-language':
content_language = _parse_content_language(
meta.get('content'))
if content_language is not None:
return ascii_lower(content_language)
# Empty string means unknown
return _parse_content_language(self.transport_content_language) or ''
@cached_property
def in_disabled_fieldset(self):
if self.parent is None:
return False
if (self.parent.etree_element.tag == (
'{http://www.w3.org/1999/xhtml}fieldset') and
self.parent.etree_element.get('disabled') is not None and (
self.etree_element.tag != (
'{http://www.w3.org/1999/xhtml}legend') or
any(s.etree_element.tag == (
'{http://www.w3.org/1999/xhtml}legend')
for s in self.iter_previous_siblings()))):
return True
return self.parent.in_disabled_fieldset
def _split_etree_tag(tag):
pos = tag.rfind('}')
if pos == -1:
return '', tag
else:
assert tag[0] == '{'
return tag[1:pos], tag[pos + 1:]
def _parse_content_language(value):
if value is not None and ',' not in value:
parts = split_whitespace(value)
if len(parts) == 1:
return parts[0]