html_global_attributes = set([
    'accesskey',
    'autocapitalize',
    'autofocus',
    'contenteditable',
    'dir',
    'draggable',
    'enterkeyhint',
    'hidden',
    'inert',
    'inputmode',
    'is',
    'itemid',
    'itemprop',
    'itemref',
    'itemscope',
    'itemtype',
    'lang',
    'nonce',
    'popover',
    'spellcheck',
    'style',
    'tabindex',
    'title',
    'translate',
    'id',
    'class',
    'slot',
    'style',
    'role', # for aria
    ])

html_element_attributes = set([
    ('a',          ('href', 'target', 'download', 'ping', 'rel', 'hreflang', 'type', 'referrerpolicy',)),
    ('attr',       ()),
    ('address',    ()),
    ('area',       ('alt', 'coords', 'shape', 'href', 'target', 'download', 'ping', 'rel', 'referrerpolicy',)),
    ('article',    ()),
    ('aside',      ()),
    ('audio',      ('src', 'crossorigin', 'preload', 'autoplay', 'loop', 'muted', 'controls',)),
    ('b',          ()),
    ('base',       ('href', 'target',)),
    ('bdi',        ()),
    ('bdo',        ()),
    ('blockquote', ('cite',)),
    ('body',       ()),
    ('br',         ()),
    ('button',     ('disabled', 'form', 'formaction', 'formenctype', 'formmethod', 'formnovalidate', 'formtarget', 'name', 'popovertarget', 'popovertargetaction', 'type', 'value',)),
    ('canvas',     ('width', 'height',)),
    ('caption',    ()),
    ('cite',       ()),
    ('code',       ()),
    ('col',        ('span',)),
    ('colgroup',   ('span',)),
    ('data',       ('value',)),
    ('datalist',   ()),
    ('dd',         ()),
    ('del',        ('cite', 'datetime',)),
    ('details',    ('open',)),
    ('dfn',        ()),
    ('dialog',     ('open',)),
    ('div',        ()),
    ('dl',         ()),
    ('em',         ()),
    ('embed',      ('src', 'type', 'width', 'height',)),
    ('fieldset',   ('disabled', 'form', 'name',)),
    ('figcaption', ()),
    ('figure',     ()),
    ('footer',     ()),
    ('form',       ('accept-charset', 'action', 'autocomplete', 'enctype', 'method', 'name', 'novalidate', 'rel', 'target',)),
    ('h1',         ()),
    ('h2',         ()),
    ('h3',         ()),
    ('h4',         ()),
    ('h5',         ()),
    ('h6',         ()),
    ('head',       ()),
    ('header',     ()),
    ('hgroup',     ()),
    ('hr',         ()),
    ('html',       ('maifest',)),
    ('i',          ()),
    ('iframe',     ('src', 'srcdoc', 'name', 'sandbox', 'allow', 'allowfullscreen', 'width', 'height', 'referrerpolicy', 'loading',)),
    ('img',        ('alt', 'src', 'srcset', 'sizes', 'crossorigin', 'usemap', 'ismap', 'width', 'height', 'referrerpolicy', 'decoding', 'loading', 'fetchpriority',)),
    ('input',      ('accept', 'alt', 'autocomplete', 'checked', 'dirname', 'disabled', 'form', 'formaction', 'formenctype', 'formmethod', 'formnovalidate', 'formtarget', 'height', 'list', 'max', 'maxlength', 'min', 'minlength', 'multiple', 'name', 'pattern', 'placeholder', 'popovertarget', 'popovertargetaction', 'readonly', 'required', 'size', 'src', 'step', 'type', 'value', 'width',)),
    ('ins',        ('cite', 'datetime',)),
    ('kbd',        ()),
    ('label',      ('for',)),
    ('legend',     ()),
    ('li',         ('value',)),
    ('link',       ('href', 'crossorigin', 'rel', 'as', 'media', 'hreflang', 'type', 'sizes', 'imagesrcset', 'imagesizes', 'referrerpolicy', 'integrity', 'blocking', 'color', 'disabled', 'fetchpriority',)),
    ('main',       ()),
    ('map',        ('name',)),
    ('mark',       ()),
    ('math',       ()),
    ('menu',       ()),
    ('meta',       ('name', 'http-equiv', 'content', 'charset', 'media')),
    ('meter',      ('value', 'min', 'max', 'low', 'high', 'optimum',)),
    ('nav',        ()),
    ('noscript',   ()),
    ('object',     ('data', 'type', 'name', 'form', 'width', 'height',)),
    ('ol',         ('reversed', 'start', 'type')),
    ('optgroup',   ('disabled', 'label')),
    ('option',     ('disabled', 'label', 'selected', 'value')),
    ('output',     ('for', 'form', 'name')),
    ('p',          ()),
    ('picture',    ()),
    ('pre',        ()),
    ('progress',   ('value', 'max')),
    ('q',          ('cite',)),
    ('rp',         ()),
    ('rt',         ()),
    ('ruby',       ()),
    ('s',          ()),
    ('samp',       ()),
    ('script',     ('src', 'type', 'nomodule', 'async', 'defer', 'crossorigin', 'integrity', 'referrerpolicy', 'blocking', 'fetchpriority',)),
    ('search',     ()),
    ('section',    ()),
    ('select',     ('autocomplete', 'disabled', 'form', 'multiple', 'name', 'required', 'size')),
    ('slot',       ('name')),
    ('small',      ()),
    ('source',     ('type', 'media', 'src', 'srcset', 'sizes', 'width', 'height',)),
    ('span',       ()),
    ('strong',     ()),
    ('style',      ('media', 'blocking')),
    ('sub',        ()),
    ('summary',    ()),
    ('sup',        ()),
    ('svg',        ()),
    ('table',      ()),
    ('tbody',      ()),
    ('td',         ('colspan', 'rowspan', 'headers')),
    ('template',   ()),
    ('textarea',   ('autocomplete', 'cols', 'dirname', 'disabled', 'form', 'maxlength', 'minlength', 'name', 'placeholder', 'readonly', 'required', 'rows', 'wrap')),
    ('tfoot',      ()),
    ('th',         ('colspan', 'rowspan', 'headers', 'scope', 'abbr')),
    ('thead',      ()),
    ('time',       ('datetime')),
    ('title',      ()),
    ('tr',         ()),
    ('track',      ('default', 'kind', 'label', 'src', 'srclang',)),
    ('u',          ()),
    ('ul',         ()),
    ('var',        ()),
    ('video',      ('src', 'crossorigin', 'poster', 'preload', 'autoplay', 'playsnline', 'loop', 'muted', 'controls', 'width', 'height',)),
    ('wbr',        ()),
    ])

void_elements = set([
    'area',
    'base',
    'br',
    'col',
    'embed',
    'hr',
    'img',
    'input',
    'link',
    'meta',
    'source',
    'track',
    'wbr',
])

svg_elements = set([
    #'a',
    'animate',
    'animateMotion',
    'animateTransform',
    #'audio',
    #'canvas',
    'circle',
    'clipPath',
    'defs',
    'desc',
    'discard',
    'ellipse',
    'feBlend',
    'feColorMatrix',
    'feComponentTransfer',
    'feComposite',
    'feConvolveMatrix',
    'feDiffuseLighting',
    'feDisplacementMap',
    'feDistantLight',
    'feDropShadow',
    'feFlood',
    'feFuncA',
    'feFuncB',
    'feFuncG',
    'feFuncR',
    'feGaussianBlur',
    'feImage',
    'feMerge',
    'feMergeNode',
    'feMorphology',
    'feOffset',
    'fePointLight',
    'feSpecularLighting',
    'feSpotLight',
    'feTile',
    'feTurbulence',
    'filter',
    'foreignObject',
    'g',
    #'iframe',
    'image',
    'line',
    'linearGradient',
    'marker',
    'mask',
    'metadata',
    'mpath',
    'path',
    'pattern',
    'polygon',
    'polyline',
    'radialGradient',
    'rect',
    #'script',
    'set',
    'stop',
    #'style',
    'svg',
    'switch',
    'symbol',
    'text',
    'textPath',
    'title',
    'tspan',
    'unknown',
    'use',
])

html_elements = set(e for e, _ in html_element_attributes)

html_element_attribute_pairs = set((e, a) for e, al in html_element_attributes for a in al)

html_events = set([
    'DOMContentLoaded',
    'afterprint',
    'beforeprint',
    'beforematch',
    'beforetoggle',
    'beforeunload',
    'blur',
    'cancel',
    'change',
    'click',
    'close',
    'connect',
    'contextlost',
    'contextrestored',
    'currententrychange',
    'dispose',
    'error',
    'focus',
    'formdata',
    'hashchange',
    'input',
    'invalid',
    'languagechange',
    'load',
    'message',
    'messageerror',
    'navigate',
    'navigateerror',
    'navigatesuccess',
    'offline',
    'online',
    'open',
    'pagehide',
    'pageshow',
    'pointercancel',
    'popstate',
    'readystatechange',
    'rejectionhandled',
    'reset',
    'select',
    'storage',
    'submit',
    'toggle',
    'unhandledrejection',
    'unload',
    'visibilitychange',
    ])


def is_void_element(e):
    return e in void_elements

def is_valid_html_event(e):
    return e in html_events

def is_valid_html_element(e):
    return e in html_elements or '-' in e

def is_valid_html_attribute(e, a):
    return (a in html_global_attributes  or
            a[:5] in ('data-', 'aria-')  or
            '-' in e                     or
            e in svg_elements            or
            e in ('math', )              or
            (e, a) in html_element_attribute_pairs
           )

def normalize_html_attribute(e, a, v):
    if is_valid_html_attribute(e, a):
        return a
    return 'data-@' + a.replace('/', '@')
