class html_attribute:
    """Add an attribute to an HTML element.

        >>> from htmlgen import Element
        >>> class MyElement(Element):
        ...     url = html_attribute("data-url")
        >>> element = MyElement("div")
        >>> element.url
        >>> str(element)
        '<div></div>'
        >>> element.url = "http://www.example.com/"
        >>> str(element)
        '<div data-url="http://www.example.com/"></div>'

    If the optional default argument is given, the attribute will not be
    included if the value matches it.

        >>> class MyElement(Element):
        ...     value = html_attribute("data-value", default="hello")
        >>> element = MyElement("div")
        >>> element.value
        'hello'
        >>> str(element)
        '<div></div>'

    """

    def __init__(self, attribute_name, default=None):
        self._attribute_name = attribute_name
        self._default = default

    def __get__(self, obj, _=None):
        return obj.get_attribute(self._attribute_name, default=self._default)

    def __set__(self, obj, value):
        if value is None or value == self._default:
            obj.remove_attribute(self._attribute_name)
        else:
            obj.set_attribute(self._attribute_name, value) class boolean_html_attribute:
    """Add a boolean attribute to an HTML element.

        >>> from htmlgen import Element
        >>> class MyElement(Element):
        ...     doit = boolean_html_attribute("data-doit")
        >>> element = MyElement("div")
        >>> element.doit
        False
        >>> str(element)
        '<div></div>'
        >>> element.doit = True
        >>> str(element)
        '<div data-doit="data-doit"></div>'

    """

    def __init__(self, attribute_name):
        self._attribute_name = attribute_name

    def __get__(self, obj, _=None):
        return obj.get_attribute(self._attribute_name) == self._attribute_name

    def __set__(self, obj, value):
        if value:
            obj.set_attribute(self._attribute_name, self._attribute_name)
        else:
            obj.remove_attribute(self._attribute_name) class int_html_attribute:
    """Add an attribute to an HTML element that accepts only integers.

        >>> from htmlgen import Element
        >>> class MyElement(Element):
        ...     value = int_html_attribute("data-value")
        >>> element = MyElement("div")
        >>> element.value
        >>> str(element)
        '<div></div>'
        >>> element.value = 42
        >>> str(element)
        '<div data-value="42"></div>'

    If the optional default argument is given, the attribute will not be
    included if the value matches it.

        >>> class MyElement(Element):
        ...     value = int_html_attribute("data-value", default=0)
        >>> element = MyElement("div")
        >>> element.value
        0
        >>> str(element)
        '<div></div>'

    """

    def __init__(self, attribute_name, default=None):
        self._attribute_name = attribute_name
        self._default = default

    def __get__(self, obj, _=None):
        value = obj.get_attribute(self._attribute_name, default=self._default)
        if value is None:
            return None
        return int(value)

    def __set__(self, obj, value):
        if value is None or value == self._default:
            obj.remove_attribute(self._attribute_name)
        else:
            obj.set_attribute(self._attribute_name, str(value)) class float_html_attribute:
    """Add an attribute to an HTML element that accepts only numbers.

        >>> from htmlgen import Element
        >>> class MyElement(Element):
        ...     value = float_html_attribute("data-value")
        >>> element = MyElement("div")
        >>> element.value
        >>> str(element)
        '<div></div>'
        >>> element.value = 4.2
        >>> str(element)
        '<div data-value="4.2"></div>'

    If the optional default argument is given, the attribute will not be
    included if the value matches it.

        >>> class MyElement(Element):
        ...     value = float_html_attribute("data-value", default=0.4)
        >>> element = MyElement("div")
        >>> element.value
        0.4
        >>> str(element)
        '<div></div>'

    """

    def __init__(self, attribute_name, default=None):
        self._attribute_name = attribute_name
        self._default = default

    def __get__(self, obj, _=None):
        value = obj.get_attribute(self._attribute_name, default=self._default)
        if value is None:
            return None
        return float(value)

    def __set__(self, obj, value):
        if value is None or value == self._default:
            obj.remove_attribute(self._attribute_name)
        else:
            obj.set_attribute(self._attribute_name, str(value)) class time_html_attribute:
    """Add an attribute to an HTML element that accepts only time values.

    >>> from htmlgen import Element
    >>> class MyElement(Element):
    ...     time = time_html_attribute("data-time")
    >>> element = MyElement("div")
    >>> element.time
    >>> str(element)
    '<div></div>'
    >>> element.time = datetime.time(8, 15)
    >>> str(element)
    '<div data-time="08:15:00"></div>'

    If the optional default argument is given, the attribute will not be
    included if the value matches it.

    >>> class MyElement(Element):
    ...     value = time_html_attribute(
    ...         "data-value", default=datetime.time(8, 0))
    >>> element = MyElement("div")
    >>> element.value
    datetime.time(8, 0)
    >>> str(element)
    '<div></div>'

    """

    def __init__(self, attribute_name, default=None):
        self._attribute_name = attribute_name
        self._default = default

    def __get__(self, obj, _=None):
        value = obj.get_attribute(self._attribute_name)
        if value is None:
            return self._default
        return parse_rfc3339_partial_time(value)

    def __set__(self, obj, value):
        if value is None or value == self._default:
            obj.remove_attribute(self._attribute_name)
        else:
            obj.set_attribute(self._attribute_name, str(value)) class list_html_attribute:
    """Add an attribute to an HTML element that accepts a list of strings.

    >>> from htmlgen import Element
    >>> class MyElement(Element):
    ...     list = list_html_attribute("data-list")
    >>> element = MyElement("div")
    >>> element.list
    []
    >>> str(element)
    '<div></div>'
    >>> element.list = ["foo", "bar"]
    >>> str(element)
    '<div data-list="foo,bar"></div>'

    """

    def __init__(self, attribute_name):
        self._attribute_name = attribute_name

    def __get__(self, obj, _=None):
        value = obj.get_attribute(self._attribute_name)
        return value.split(",") if value else []

    def __set__(self, obj, value):
        if value:
            obj.set_attribute(self._attribute_name, ",".join(value))
        else:
            obj.remove_attribute(self._attribute_name) class enum_attribute:
    """Add an attribute to an HTML element that only allows limited values.

        >>> from enum import Enum
        >>> from htmlgen import Element
        >>> class MyEnum(Enum):
        ...     FOO = "foo"
        ...     BAR = "bar"
        >>> class MyElement(Element):
        ...     value = enum_attribute("value", MyEnum)
        >>> element = MyElement("div")
        >>> element.value
        >>> str(element)
        '<div></div>'
        >>> element.value = MyEnum.FOO
        >>> str(element)
        '<div value="foo"></div>'

    If the optional default argument is given, the attribute will not be
    included if the value matches it.

        >>> class MyElement(Element):
        ...     value = enum_attribute("value", MyEnum, default=MyEnum.FOO)
        >>> element = MyElement("div")
        >>> element.value
        MyEnum.FOO
        >>> str(element)
        '<div></div>'
    """

    def __init__(self, attribute_name, enum, default=None):
        if not issubclass(enum, Enum):
            raise TypeError("enum must be an Enum class")
        self._attribute_name = attribute_name
        self._enum = enum
        self._default = default

    def __get__(self, obj, _=None):
        value = obj.get_attribute(self._attribute_name, None)
        if value is None:
            return self._default
        return self._enum(value)

    def __set__(self, obj, value):
        if value is None:
            obj.remove_attribute(self._attribute_name)
        elif not isinstance(value, self._enum):
            raise TypeError("value must be an {}".format(self._enum))
        else:
            obj.set_attribute(self._attribute_name, value.value) class Division(Element):
    """An HTML division (<div>) element.

    <div> elements are block-level elements without semantic meaning. They are
    containers for styling or scripting.

        >>> div = Division("Initial text. ")
        >>> div.id = "my-block"
        >>> div.add_css_classes("important", "legal")
        >>> div.append("This is more text.")
    """

    def __init__(self, *content):
        super().__init__("div")
        self.extend(content) class Element(NonVoidElement):

    """Base generator for HTML elements with children.

    This includes most elements, like <body>, <div>, <span>, <strong>, etc.

        >>> link = Element("a")
        >>> link.set_attribute("href", "http://www.example.com/")
        >>> link.append("Test Link")
        >>> str(link)
        '<a href="http://www.example.com/">Test Link</a>'

    append() and extend() escape reserved HTML characters in string, but
    not in sub-generators:

        >>> span = Element("span")
        >>> span.add_css_classes("my-css")
        >>> span.set_style("color", "red")
        >>> span.append("Test & Test")
        >>> str(span)
        '<span class="my-css" style="color: red">Test &amp; Test</span>'

    append_raw() and extend_raw() do not do any escaping. Do not use these
    methods with strings from untrusted sources!

        >>> span = Element("span")
        >>> span.append_raw("Test<br/>Test")
        >>> str(span)
        '<span>Test<br/>Test</span>'

    Instead of appending children to elements, the generate_children()
    method can be overwritten. This is generally preferred for element
    sub-classes that are not meant to be extended by users, since it avoids
    constructing the HTML tree in memory.

        >>> class MyElement(Element):
        ...     def generate_children(self):
        ...         yield "Hello, "
        ...         child = Element("strong")
        ...         child.append("World!")
        ...         yield child
        >>> str(MyElement("div"))
        '<div>Hello, <strong>World!</strong></div>'

    """

    def __init__(self, element_name):
        super().__init__(element_name)
        self.children = HTMLChildGenerator()

    def __bool__(self):
        return True

    def __getattr__(self, item):
        return getattr(self.children, item)

    def __len__(self):
        """Return the number of children.

        A sub-generator is counted as one child.

        """
        return len(self.children)

    def __nonzero__(self):
        return True

    def generate_children(self):
        """Return an iterator over the children of this element.

        This method can be overridden by sub-classes.

        """
        return self.children class VoidElement(ElementBase):

    """Base generator for content-less HTML elements.

    These are elements like <br> or <link>.

        >>> link = VoidElement("link")
        >>> link.set_attribute("rel", "stylesheet")
        >>> str(link)
        '<link rel="stylesheet"/>'

    """

    def generate(self):
        yield self.render_start_tag() + "/>" def is_element(o, element_name):
    """Return whether a given object is a certain element generator."""
    return (
        hasattr(o, "__iter__")
        and hasattr(o, "element_name")
        and o.element_name == element_name
    ) def parse_rfc3339_partial_time(value):
    matches = re.match(r"^(\d\d):(\d\d):(\d\d)(\.(\d+))?$", value)
    if not matches:
        return None
    fractions = int(matches.group(5)) if matches.group(4) else 0
    return datetime.time(
        int(matches.group(1)),
        int(matches.group(2)),
        int(matches.group(3)),
        fractions,
    )
import datetime
import re
from enum import Enum
from htmlgen.attribute import (
    html_attribute,
    boolean_html_attribute,
    int_html_attribute,
    float_html_attribute,
    time_html_attribute,
    list_html_attribute, enum_attribute,
)
from htmlgen.block import Division
from htmlgen.element import Element, VoidElement, is_element
from htmlgen.timeutil import parse_rfc3339_partial_time
        The value argument determines the initial content of the text field.

        """
        super().__init__("text", name)
        self.value = value


class SearchInput(Input):
    """An HTML search (<input type="search">) element."""

    def __init__(self, name=""):
        """Create an HTML search element.

        The optional name argument sets this input element's name, used when
        submitting a form.

        """
        super().__init__("search", name)


class PasswordInput(Input):
    """An HTML password input (<input type="password">) element."""

    def __init__(self, name=""):
        """Create an HTML password input element.

        The optional name argument sets this input element's name, used when
        submitting a form.

        """
        super().__init__("password", name)


class NumberInput(Input):
    """An HTML number input (<input type="number">) element."""

    def __init__(self, name="", number=None):
        """Create an HTML number input element.

        The optional name argument sets this input element's name, used when
        submitting a form.

        If number is not None, it determines the initial content of the
        this input element.

        """
        super().__init__("number", name)
        if number is not None:
            self.number = number

    number = float_html_attribute("value")
    minimum = float_html_attribute("min")
    maximum = float_html_attribute("max")
    step = float_html_attribute("step")


class DateInput(Input):
    """An HTML date input (<input type="date">) element."""

    def __init__(self, name="", date=None):
        """Create an HTML date element.

        The optional name argument sets this input element's name, used when
        submitting a form.

        date is the initial date. If date is None, the field is initially
        empty.

        """
        super().__init__("date", name)
        self.date = date

    @property
    def date(self):
        """Return the element's value attribute, parsed as date.

        If value is empty or can not be parsed as a date, return None.

        """
        return self._parse_date(self.value)

    @date.setter
    def date(self, date):
        """Set the element's value to the given date. If date is None,
        clear the value."""
        self.value = date.strftime("%Y-%m-%d") if date else ""

    @staticmethod
    def _parse_date(v):
        match = re.match(r"(\d\d\d\d)-(\d\d)-(\d\d)", v)
        if not match:
            return None
        return datetime.date(
            int(match.group(1)), int(match.group(2)), int(match.group(3))
        )


class TimeInput(Input):
    """An HTML time input (<input type="time">) element."""

    def __init__(self, name="", time=None):
        """Create an HTML time element.

        The optional name argument sets this input element's name, used when
        submitting a form.

        time is the initial time. If time is None, the field is initially
        empty.

        """
        super().__init__("time", name)
        self.time = time

    time = time_html_attribute("value")

    @property
    def minimum(self):
        value = self.get_attribute("min")
        if value is None:
            return None

        return parse_rfc3339_partial_time(value)