text: import typing
  import warnings
  
  from .base import BaseElement, If
  from .lazy import Lazy, resolve_lazy
  
  
  class HTMLElement(BaseElement):
      """The base for all HTML tags."""
  
      tag: str = ""
  
      def __init__(
          self, *children, lazy_attributes: typing.Optional[Lazy] = None, **attributes
      ):
          assert self.tag != ""
          if any(attr == "class" for attr in attributes.keys()):
              warnings.warn(
                  'You should use "_class" instead of "class" when specifiying HTML classes in htmlgenerator ("class" is a python keyword).'
              )
          self.attributes: dict = attributes
          super().__init__(*children)
          self.lazy_attributes = lazy_attributes
  
      def render(self, context: dict) -> typing.Generator[str, None, None]:
          attr_str = flatattrs(
              {
                  **self.attributes,
                  **(resolve_lazy(self.lazy_attributes, context) or {}),
              },
              context,
          )
          # quirk to prevent tags having a single space if there are no attributes
          attr_str = (" " + attr_str) if attr_str else attr_str
          yield f"<{self.tag}{attr_str}>"
          yield from super().render_children(context)
          yield f"</{self.tag}>"
  
      # mostly for debugging purposes
      def __repr__(self) -> str:
          return (
              f"<{self.tag} "
              + " ".join(f"{k.lstrip('_')}=\"{v}\"" for k, v in self.attributes.items())
              + f"> ({self.__class__})"
          )
  
  
  class VoidElement(HTMLElement):
      """Wrapper for elements without a closing tag, cannot have children"""
  
      # does not accept children
      def __init__(self, **kwargs):
          super().__init__(**kwargs)
  
      def render(self, context) -> typing.Generator[str, None, None]:
          yield f"<{self.tag} {flatattrs(self.attributes, context)} />"
  
  
  class A(HTMLElement):
      tag = "a"
  
      def __init__(self, *args, newtab=False, **kwargs):
          if newtab:
              kwargs["target"] = "_blank"
              kwargs["rel"] = "noopener noreferrer"
          super().__init__(*args, **kwargs)
  
  
  class ABBR(HTMLElement):
      tag = "abbr"
  
  
  class ACRONYM(HTMLElement):
      tag = "acronym"
  
  
  class ADDRESS(HTMLElement):
      tag = "address"
  
  
  class APPLET(HTMLElement):
      tag = "applet"
  
  
  class AREA(VoidElement):
      tag = "area"
  
  
  class ARTICLE(HTMLElement):
      tag = "article"
  
  
  class ASIDE(HTMLElement):
      tag = "aside"
  
  
  class AUDIO(HTMLElement):
      tag = "audio"
  
  
  class B(HTMLElement):
      tag = "b"
  
  
  class BASE(VoidElement):
      tag = "base"
  
  
  class BASEFONT(HTMLElement):
      tag = "basefont"
  
  
  class BDI(HTMLElement):
      tag = "bdi"
  
  
  class BDO(HTMLElement):
      tag = "bdo"
  
  
  class BGSOUND(HTMLElement):
      tag = "bgsound"
  
  
  class BIG(HTMLElement):
      tag = "big"
  
  
  class BLINK(HTMLElement):
      tag = "blink"
  
  
  class BLOCKQUOTE(HTMLElement):
      tag = "blockquote"
  
  
  class BODY(HTMLElement):
      tag = "body"
  
  
  class BR(VoidElement):
      tag = "br"
  
  
  class BUTTON(HTMLElement):
      tag = "button"
  
  
  class CANVAS(HTMLElement):
      tag = "canvas"
  
  
  class CAPTION(HTMLElement):
      tag = "caption"
  
  
  class CENTER(HTMLElement):
      tag = "center"
  
  
  class CITE(HTMLElement):
      tag = "cite"
  
  
  class CODE(HTMLElement):
      tag = "code"
  
  
  class COL(VoidElement):
      tag = "col"
  
  
  class COLGROUP(HTMLElement):
      tag = "colgroup"
  
  
  class COMMAND(VoidElement):
      tag = "command"
  
  
  class CONTENT(HTMLElement):
      tag = "content"
  
  
  class DATA(HTMLElement):
      tag = "data"
  
  
  class DATALIST(HTMLElement):
      tag = "datalist"
  
  
  class DD(HTMLElement):
      tag = "dd"
  
  
  class DEL(HTMLElement):
      tag = "del"
  
  
  class DETAILS(HTMLElement):
      tag = "details"
  
  
  class DFN(HTMLElement):
      tag = "dfn"
  
  
  class DIALOG(HTMLElement):
      tag = "dialog"
  
  
  class DIR(HTMLElement):
      tag = "dir"
  
  
  class DIV(HTMLElement):
      tag = "div"
  
  
  class DL(HTMLElement):
      tag = "dl"
  
  
  class DT(HTMLElement):
      tag = "dt"
  
  
  class EDIASTREA(HTMLElement):
      tag = "ediastrea"
  
  
  class ELEMENT(HTMLElement):
      tag = "element"
  
  
  class EM(HTMLElement):
      tag = "em"
  
  
  class EMBED(VoidElement):
      tag = "embed"
  
  
  class FIELDSET(HTMLElement):
      tag = "fieldset"
  
  
  class FIGCAPTION(HTMLElement):
      tag = "figcaption"
  
  
  class FIGURE(HTMLElement):
      tag = "figure"
  
  
  class FONT(HTMLElement):
      tag = "font"
  
  
  class FOOTER(HTMLElement):
      tag = "footer"
  
  
  class FORM(HTMLElement):
      tag = "form"
  
  
  class FRAME(HTMLElement):
      tag = "frame"
  
  
  class FRAMESET(HTMLElement):
      tag = "frameset"
  
  
  class H1(HTMLElement):
      tag = "h1"
  
  
  class H2(HTMLElement):
      tag = "h2"
  
  
  class H3(HTMLElement):
      tag = "h3"
  
  
  class H4(HTMLElement):
      tag = "h4"
  
  
  class H5(HTMLElement):
      tag = "h5"
  
  
  class H6(HTMLElement):
      tag = "h6"
  
  
  class HEAD(HTMLElement):
      tag = "head"
  
      def __init__(self, *children):
          super().__init__(
              META(charset="utf-8"),
              META(name="viewport", content="width=device-width, initial-scale=1.0"),
              *children,
          )
  
  
  class HEADER(HTMLElement):
      tag = "header"
  
  
  class HGROUP(HTMLElement):
      tag = "hgroup"
  
  
  class HR(VoidElement):
      tag = "hr"
  
  
  class HTML(HTMLElement):
      tag = "html"
  
      def __init__(self, *args, doctype=False, **kwargs):
          super().__init__(*args, **kwargs)
          self.doctype = doctype
  
      def render(self, context: dict) -> typing.Generator[str, None, None]:
          if self.doctype:
              yield "<!DOCTYPE html>"
          yield from super().render(context)
  
  
  class I(HTMLElement):  # noqa
      tag = "i"
  
  
  class IFRAME(HTMLElement):
      tag = "iframe"
  
  
  class IMAGE(HTMLElement):
      tag = "image"
  
  
  class IMG(VoidElement):
      tag = "img"
  
  
  class INPUT(VoidElement):
      tag = "input"
  
  
  class INS(HTMLElement):
      tag = "ins"
  
  
  class ISINDEX(HTMLElement):
      tag = "isindex"
  
  
  class KBD(HTMLElement):
      tag = "kbd"
  
  
  class KEYGEN(VoidElement):
      tag = "keygen"
  
  
  class LABEL(HTMLElement):
      tag = "label"
  
  
  class LEGEND(HTMLElement):
      tag = "legend"
  
  
  class LI(HTMLElement):
      tag = "li"
  
  
  class LINK(VoidElement):
      tag = "link"
  
  
  class LISTING(HTMLElement):
      tag = "listing"
  
  
  class MAIN(HTMLElement):
      tag = "main"
  
  
  class MAP(HTMLElement):
      tag = "map"
  
  
  class MARK(HTMLElement):
      tag = "mark"
  
  
  class MARQUEE(HTMLElement):
      tag = "marquee"
  
  
  class MENU(HTMLElement):
      tag = "menu"
  
  
  class MENUITEM(HTMLElement):
      tag = "menuitem"
  
  
  class META(VoidElement):
      tag = "meta"
  
  
  class METER(HTMLElement):
      tag = "meter"
  
  
  class MULTICOL(HTMLElement):
      tag = "multicol"
  
  
  class NAV(HTMLElement):
      tag = "nav"
  
  
  class NEXTID(HTMLElement):
      tag = "nextid"
  
  
  class NOBR(HTMLElement):
      tag = "nobr"
  
  
  class NOEMBED(HTMLElement):
      tag = "noembed"
  
  
  class NOFRAMES(HTMLElement):
      tag = "noframes"
  
  
  class NOSCRIPT(HTMLElement):
      tag = "noscript"
  
  
  class OBJECT(HTMLElement):
      tag = "object"
  
  
  class OL(HTMLElement):
      tag = "ol"
  
  
  class OPTGROUP(HTMLElement):
      tag = "optgroup"
  
  
  class OPTION(HTMLElement):
      tag = "option"
  
  
  class OUTPUT(HTMLElement):
      tag = "output"
  
  
  class P(HTMLElement):
      tag = "p"
  
  
  class PARAM(VoidElement):
      tag = "param"
  
  
  class PICTURE(HTMLElement):
      tag = "picture"
  
  
  class PLAINTEXT(HTMLElement):
      tag = "plaintext"
  
  
  class PRE(HTMLElement):
      tag = "pre"
  
  
  class PROGRESS(HTMLElement):
      tag = "progress"
  
  
  class Q(HTMLElement):
      tag = "q"
  
  
  class RB(HTMLElement):
      tag = "rb"
  
  
  class RE(HTMLElement):
      tag = "re"
  
  
  class RP(HTMLElement):
      tag = "rp"
  
  
  class RT(HTMLElement):
      tag = "rt"
  
  
  class RTC(HTMLElement):
      tag = "rtc"
  
  
  class RUBY(HTMLElement):
      tag = "ruby"
  
  
  class S(HTMLElement):
      tag = "s"
  
  
  class SAMP(HTMLElement):
      tag = "samp"
  
  
  class SCRIPT(HTMLElement):
      tag = "script"
  
  
  class SECTION(HTMLElement):
      tag = "section"
  
  
  class SELECT(HTMLElement):
      tag = "select"
  
  
  class SHADOW(HTMLElement):
      tag = "shadow"
  
  
  class SLOT(HTMLElement):
      tag = "slot"
  
  
  class SMALL(HTMLElement):
      tag = "small"
  
  
  class SOURCE(VoidElement):
      tag = "source"
  
  
  class SPACER(HTMLElement):
      tag = "spacer"
  
  
  class SPAN(HTMLElement):
      tag = "span"
  
  
  class STRIKE(HTMLElement):
      tag = "strike"
  
  
  class STRONG(HTMLElement):
      tag = "strong"
  
  
  class STYLE(HTMLElement):
      tag = "style"
  
  
  class SUB(HTMLElement):
      tag = "sub"
  
  
  class SUMMARY(HTMLElement):
      tag = "summary"
  
  
  class SUP(HTMLElement):
      tag = "sup"
  
  
  class SVG(HTMLElement):
      tag = "svg"
  
  
  class TABLE(HTMLElement):
      tag = "table"
  
  
  class TBODY(HTMLElement):
      tag = "tbody"
  
  
  class TD(HTMLElement):
      tag = "td"
  
  
  class TEMPLATE(HTMLElement):
      tag = "template"
  
  
  class TEXTAREA(HTMLElement):
      tag = "textarea"
  
  
  class TFOOT(HTMLElement):
      tag = "tfoot"
  
  
  class TH(HTMLElement):
      tag = "th"
  
  
  class THEAD(HTMLElement):
      tag = "thead"
  
  
  class TIME(HTMLElement):
      tag = "time"
  
  
  class TITLE(HTMLElement):
      tag = "title"
  
  
  class TR(HTMLElement):
      tag = "tr"
  
  
  class TRACK(VoidElement):
      tag = "track"
  
  
  class TT(HTMLElement):
      tag = "tt"
  
  
  class U(HTMLElement):
      tag = "u"
  
  
  class UL(HTMLElement):
      tag = "ul"
  
  
  class VAR(HTMLElement):
      tag = "var"
  
  
  class VIDEO(HTMLElement):
      tag = "video"
  
  
  class WBR(VoidElement):
      tag = "wbr"
  
  
  class XMP(HTMLElement):
      tag = "xmp"
  
  
  def flatattrs(attributes: dict, context: dict) -> str:
      """Converts a dictionary to a string of HTML-attributes.
      Leading underscores are removed and other underscores are replaced with dashes."""
  
      attlist = []
      for key, value in attributes.items():
          value = resolve_lazy(value, context)
          if isinstance(value, If):
              rendered = list(value.render(context, stringify=False))
              if len(rendered) == 1 and isinstance(rendered[0], bool):
                  value = rendered[0]
              else:
                  rendered = list(value.render(context))
                  value = "".join(rendered) if rendered else None
          elif isinstance(value, BaseElement):
              rendered = list(value.render(context))
              value = "".join(rendered) if rendered else None
          if value is None:
              continue
  
          if key[0] == "_":
              key = key[1:]
          key = key.replace("_", "-")
          if isinstance(value, bool) and key != "value":
              if value is True:
                  attlist.append(f"{key}")
          else:
              attlist.append(f'{key}="{value}"')
      return " ".join(attlist)
  
  
  def append_attribute(attrs, key, value, separator=None):
      """
      In many cases we want a component to add e.g. something to the
      _class attribute of an HTML element but still allow the caller to
      pass his own _class attribute to the component. In these cases
      we can use ``append_attribute`` which will make sure that existing
      attributes will not simply be overwritten.
      Multiple items of attribute will in general require to be separated
      by a separator. If the ``separator`` is not set, a best guess will be
      done automatically.
  
      Usage:
  
          class MyDiv(hg.DIV):
              def __init__(self, *args, **kwargs):
                  hg.append_attribute(kwargs, "_class", "mydiv")
                  super().__init__(*args, **kwargs)
  
          a = MyDiv(_class="special-class")
  
          assert a.attributes["_class"].split(" ") == ["special-class", mydiv]
  
      """
  
      def guess_separator(key):
          default_separators = {
              "_class": " ",
              "style": ";",
          }
          if key in default_separators:
              return default_separators[key]
          # onclick and similar event handlers are javascript
          if key.startswith("on") and key.islower() and key.isalpha():
              return ";"
          return " "
  
      if key in attrs:
          attrs[key] = BaseElement(attrs[key], separator or guess_separator(key), value)
      else:
          attrs[key] = value
  