From 220c9b677430b2d37014b2d985e3cc22c87748d0 Mon Sep 17 00:00:00 2001 From: Home Assistant Version Control Date: Thu, 15 Oct 2026 17:21:46 +0000 Subject: [PATCH] 4 files --- ....3d-printer-cellphone.20261015_172140.yaml | 201 + .storage/ha_mcp_tools/linkedom-0.18.13.js | 12652 ++++++++++++++++ .storage/lovelace.3d_printer_cellphone | 14 + zigbee2mqtt/state.json | 40 +- 4 files changed, 12887 insertions(+), 20 deletions(-) create mode 100644 .ha_mcp/backups/dashboard.3d-printer-cellphone.20261015_172140.yaml create mode 100644 .storage/ha_mcp_tools/linkedom-0.18.13.js diff --git a/.ha_mcp/backups/dashboard.3d-printer-cellphone.20261015_172140.yaml b/.ha_mcp/backups/dashboard.3d-printer-cellphone.20261015_172140.yaml new file mode 100644 index 00000000..747ef89a --- /dev/null +++ b/.ha_mcp/backups/dashboard.3d-printer-cellphone.20261015_172140.yaml @@ -0,0 +1,201 @@ +# ha_mcp_backup +schema_version: 1 +domain: dashboard +entity_id: 3d-printer-cellphone +captured: '2026-10-15T17:21:40.300831+00:00' +tool: ha_config_set_dashboard +config: + views: + - sections: + - cards: + - heading: Filament Usage + heading_style: title + type: heading + - card: + columns: 4 + square: false + type: grid + card_param: cards + filter: + template: "{% set ns = namespace(cards=[]) %}\n{% for n in [1, 2, 3, 4]\ + \ %}\n {% set sp = namespace(ent='') %}\n {% for s in states.sensor\ + \ %}\n {% if sp.ent == '' and s.entity_id.startswith('sensor.spoolman_spool_')\ + \ %}\n {% set loc = (s.attributes.location | default('')) | lower\ + \ %}\n {% if loc == ('cfs lite ' ~ n) %}\n {% set sp.ent =\ + \ s.entity_id %}\n {% endif %}\n {% endif %}\n {% endfor %}\n\ + \ {% set v = namespace(html='', label='Slot ' ~ n, border='transparent')\ + \ %}\n {% if sp.ent != '' %}\n {% set hex = state_attr(sp.ent, 'filament_color_hex')\ + \ | default('44739e') %}\n {% set hex_clean = hex if hex.startswith('#')\ + \ else '#' ~ hex %}\n {% set remain = (state_attr(sp.ent, 'remaining_weight')\ + \ | default(states(sp.ent)) | float(0)) | round(1) %}\n {% set total\ + \ = state_attr(sp.ent, 'filament_weight') | float(1000) %}\n {% set\ + \ raw_pct = ((remain / total) * 100) | round(1) if total > 0 else 0 %}\n\ + \ {% set pct = 0 if raw_pct < 0 else (100 if raw_pct > 100 else raw_pct)\ + \ %}\n {% set v.border = '#d0d0d0' if hex_clean | lower in ['#ffffff',\ + \ '#fff'] else 'transparent' %}\n {% set v.html = \"
\" ~ remain ~ \"g
\"\ + \ %}\n {% set v.label = 'Slot ' ~ n %}\n {% else %}\n {% set v.html\ + \ = \"
\" %}\n {% set v.label\ + \ = \"Slot \" ~ n ~ \" \u2014 empty\" %}\n\ + \ {% endif %}\n {% set card = {\n \"type\": \"custom:button-card\"\ + ,\n \"entity\": sp.ent if sp.ent != '' else 'sensor.sparkx_i7_active_filament_slot',\n\ + \ \"show_icon\": false,\n \"show_state\": false,\n \"show_name\"\ + : false,\n \"custom_fields\": {\n \"tube\": \"
\"\ + \ ~ v.html ~ \"
\" ~ v.label ~ \"
\"\ + \n },\n \"styles\": {\n \"card\": [\n {\"background\"\ + : \"transparent\"},\n {\"box-shadow\": \"none\"},\n {\"\ + border\": \"none\"},\n {\"padding\": \"8px 2px\"},\n {\"\ + display\": \"flex\"},\n {\"flex-direction\": \"column\"},\n \ + \ {\"align-items\": \"center\"}\n ],\n \"custom_fields\"\ + : {\n \"tube\": [\n {\"width\": \"100%\"},\n \ + \ {\"display\": \"flex\"},\n {\"flex-direction\": \"column\"\ + },\n {\"align-items\": \"center\"}\n ]\n }\n },\n\ + \ \"extra_styles\": \"\n .tube-wrap {\n display: flex;\n\ + \ flex-direction: column;\n align-items: center;\n \ + \ width: 100%;\n }\n .tube-bg {\n position: relative;\n\ + \ width: 60px;\n height: 180px;\n background-color:\ + \ #dedede;\n border-radius: 20px;\n overflow: hidden;\n\ + \ border: 1px solid \" ~ v.border ~ \";\n display: flex;\n\ + \ flex-direction: column;\n justify-content: flex-end;\n\ + \ }\n .tube-empty {\n background: transparent;\n \ + \ border: 2px dashed var(--error-color);\n }\n .tube-fill\ + \ {\n width: 100%;\n border-radius: 0 0 19px 19px;\n \ + \ }\n .tube-text {\n position: absolute;\n top: 48%;\n\ + \ left: 50%;\n transform: translate(-50%, -50%);\n \ + \ font-size: 13px;\n font-weight: 800;\n color: #111111;\n\ + \ pointer-events: none;\n z-index: 2;\n white-space:\ + \ nowrap;\n }\n .slot-label {\n margin-top: 8px;\n \ + \ font-size: 12px;\n font-weight: 700;\n letter-spacing:\ + \ 0.3px;\n color: var(--secondary-text-color);\n }\n \ + \ .slot-warn {\n color: var(--error-color);\n }\n \",\n\ + \ \"tap_action\": {\n \"action\": \"more-info\" if sp.ent != ''\ + \ else \"none\"\n }\n } %}\n\n {% set ns.cards = ns.cards + [card]\ + \ %}\n{% endfor %}\n{{ ns.cards }}\n" + type: custom:auto-entities + - entity: sensor.sparkx_i7_cfs_box_1_humidity + grid_options: + columns: 6 + icon: mdi:water-percent + name: Humidity + type: tile + - entity: sensor.sparkx_i7_cfs_box_1_temperature + grid_options: + columns: 6 + icon: mdi:thermometer + name: Temperature + type: tile + - cards: + - entity: sensor.sparkx_i7_active_filament_slot + grid_options: + columns: full + icon: mdi:magnet + icon_color: '{% set slot = states(''sensor.sparkx_i7_active_filament_slot'') + %}{% set c = states(''sensor.sparkx_i7_cfs_'' ~ (slot | lower | replace('' + '', ''_'')) ~ ''_color'') | replace(''#'', '''') | lower %}{% if c | length + == 6 %}#{{ c }}{% else %}var(--primary-color){% endif %}' + multiline_secondary: true + name: Next Deduction Match + primary: '{{ states(''sensor.sparkx_i7_active_filament_slot'') }}' + secondary: "{% set slot = states('sensor.sparkx_i7_active_filament_slot')\ + \ %}\n{% set n = slot.split('_')[-1] %}\n{% set ce = 'sensor.sparkx_i7_cfs_'\ + \ ~ (slot | lower | replace(' ', '_')) ~ '_color' %}\n{% set color = states(ce)\ + \ | replace('#', '') | lower %}\n{% set ns = namespace(id='', rem=-1)\ + \ %}\n{% for e in integration_entities('spoolman') %}\n{% if e.endswith('_color_hex')\ + \ and (state_attr(e[:-10], 'location') | default('', true) | lower) ==\ + \ ('cfs lite ' ~ n) and ns.id == '' %}\n{% set ns.id = states(e[:-10]\ + \ ~ '_id') %}\n{% set ns.rem = states(e[:-10]) | float(-1) %}\n{% endif\ + \ %}\n{% endfor %}\n{% if slot in ['', 'unknown', 'unavailable'] %}No\ + \ slot latched\n{% elif ns.id == '' %}No spool assigned to CFS Lite {{\ + \ n }}\n{% elif ns.rem < 0 %}Color #{{ color | upper }} \u2192 spool {{\ + \ ns.id }} unreachable\n{% elif ns.rem == 0 %}Color #{{ color | upper\ + \ }} \u2192 spool {{ ns.id }} is EMPTY\n{% else %}Color #{{ color | upper\ + \ }} \u2192 spool {{ ns.id }} ({{ states('sensor.spoolman_spool_' ~ ns.id\ + \ ~ '_filament_name') }} \xB7 {{ ns.rem | round(1) }} g left)\n{% endif\ + \ %}\n" + type: custom:mushroom-template-card + grid_options: + columns: full + type: horizontal-stack + - heading: Last/Current Print Job + heading_style: title + icon: mdi:printer-3d + type: heading + - entity: sensor.sparkx_i7_used_material_length + grid_options: + columns: 6 + rows: 1 + icon: mdi:plus-minus + name: Used Material Total + type: tile + - entity: sensor.sparkx_moonraker_print_eta + features_position: bottom + name: Print ETA + type: tile + vertical: false + grid_options: + columns: 6 + rows: 1 + - entity: sensor.sparkx_i7_filament_weight_estimated + grid_options: + columns: 6 + rows: auto + icon: "{% set st = states('sensor.sparkx_i7_print_status') %} {% if st in\ + \ ['printing', 'paused'] %}\n mdi:printer-3d-nozzle\n{% else %}\n mdi:cash-multiple\n\ + {% endif %}" + icon_color: "{% set st = states('sensor.sparkx_i7_print_status') %} {% if\ + \ st in ['printing', 'paused'] %}\n blue\n{% else %}\n green\n{% endif\ + \ %}" + multiline_secondary: true + primary: "{% set st = states('sensor.sparkx_i7_print_status') %}\n{% set w\ + \ = states('sensor.sparkx_i7_filament_weight_estimated') %}\n{% if st in\ + \ ['printing', 'paused'] %}\n {% if is_number(w) %}{{ w | float | round(1)\ + \ }} g{% else %}weight unavailable{% endif %}\n{% else %}\n Last: {{ states('input_number.sparkx_i7_last_job_filament_weight')\ + \ | float(0) | round(1) }} g\n{% endif %}\n" + secondary: "{% set st = states('sensor.sparkx_i7_print_status') %}\n{% set\ + \ c = states('sensor.sparkx_i7_filament_cost') %}\n{% if st in ['printing',\ + \ 'paused'] %}\n {% if is_number(c) %}{{ c | float | round(2) }} $ (live){%\ + \ else %}cost unavailable{% endif %}\n{% else %}\n {{ states('input_number.sparkx_i7_last_job_cost')\ + \ | float(0) | round(2) }} $ (last job)\n{% endif %}\n" + type: custom:mushroom-template-card + - heading: Temperature + heading_style: title + icon: mdi:coolant-temperature + type: heading + - entity: sensor.sparkx_i7_nozzle_temperature + grid_options: + columns: 4 + max: 300 + min: 0 + name: Nozzle + severity: + green: 0 + red: 240 + yellow: 180 + type: gauge + unit: "\xB0C" + - entity: sensor.sparkx_i7_bed_temperature + grid_options: + columns: 4 + max: 100 + min: 0 + name: Bed + severity: + green: 0 + red: 100 + yellow: 60 + type: gauge + unit: "\xB0C" + - entity: sensor.sparkx_i7_real_time_flow + grid_options: + columns: 4 + max: 40 + min: 0 + name: Flow + severity: + green: 0 + yellow: 12 + red: 24 + type: gauge + type: grid + type: sections diff --git a/.storage/ha_mcp_tools/linkedom-0.18.13.js b/.storage/ha_mcp_tools/linkedom-0.18.13.js new file mode 100644 index 00000000..0e17d05a --- /dev/null +++ b/.storage/ha_mcp_tools/linkedom-0.18.13.js @@ -0,0 +1,12652 @@ +(function () { +// used in Attr to signal changes +const CHANGED = Symbol('changed'); + +// used in Element to setup once classList +const CLASS_LIST = Symbol('classList'); + +// used in Document to attach once customElements +const CUSTOM_ELEMENTS = Symbol('CustomElements'); + +// used in HTMLTemplateElement +const CONTENT = Symbol('content'); + +// used in Element for data attributes +const DATASET = Symbol('dataset'); + +// used in Document to attach the DocType +const DOCTYPE = Symbol('doctype'); + +// used in parser and Document to attach once a DOMParser +const DOM_PARSER = Symbol('DOMParser'); + +// used to reference an end node +const END = Symbol('end'); + +// used in Document to make the globalThis an event target +const EVENT_TARGET = Symbol('EventTarget'); + +// used to augment a created document defaultView +const GLOBALS = Symbol('globals'); + +// used in both Canvas and Document to provide images +const IMAGE = Symbol('image'); + +// used to define Document mime type +const MIME = Symbol('mime'); + +// used in Document to attach once MutationObserver +const MUTATION_OBSERVER = Symbol('MutationObserver'); + +// used to define next node reference +const NEXT = Symbol('next'); + +// used to define Attr owner elements +const OWNER_ELEMENT = Symbol('ownerElement'); + +// used to define previous node reference +const PREV = Symbol('prev'); + +// used to define various "private" properties +const PRIVATE = Symbol('private'); + +// used to define the CSSStyleSheet.sheet +const SHEET = Symbol('sheet'); + +// used to define start node reference +const START = Symbol('start'); + +// used to define special CSS style attribute +const STYLE = Symbol('style'); + +// used to upgrade Custom Elements +const UPGRADE = Symbol('upgrade'); + +// used to define generic values +const VALUE = Symbol('value'); + +// Adapted from https://github.com/mathiasbynens/he/blob/36afe179392226cf1b6ccdb16ebbb7a5a844d93a/src/he.js#L106-L134 +var _a; +const decodeMap = new Map([ + [0, 65533], + // C1 Unicode control character reference replacements + [128, 8364], + [130, 8218], + [131, 402], + [132, 8222], + [133, 8230], + [134, 8224], + [135, 8225], + [136, 710], + [137, 8240], + [138, 352], + [139, 8249], + [140, 338], + [142, 381], + [145, 8216], + [146, 8217], + [147, 8220], + [148, 8221], + [149, 8226], + [150, 8211], + [151, 8212], + [152, 732], + [153, 8482], + [154, 353], + [155, 8250], + [156, 339], + [158, 382], + [159, 376], +]); +/** + * Polyfill for `String.fromCodePoint`. It is used to create a string from a Unicode code point. + */ +const fromCodePoint = +// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition, n/no-unsupported-features/es-builtins +(_a = String.fromCodePoint) !== null && _a !== void 0 ? _a : ((codePoint) => { + let output = ""; + if (codePoint > 65535) { + codePoint -= 65536; + output += String.fromCharCode(((codePoint >>> 10) & 1023) | 55296); + codePoint = 56320 | (codePoint & 1023); + } + output += String.fromCharCode(codePoint); + return output; +}); +/** + * Replace the given code point with a replacement character if it is a + * surrogate or is outside the valid range. Otherwise return the code + * point unchanged. + */ +function replaceCodePoint(codePoint) { + var _a; + if ((codePoint >= 55296 && codePoint <= 57343) || + codePoint > 1114111) { + return 65533; + } + return (_a = decodeMap.get(codePoint)) !== null && _a !== void 0 ? _a : codePoint; +} + +/* + * Shared base64 decode helper for generated decode data. + * Assumes global atob is available. + */ +function decodeBase64(input) { + const binary = + // eslint-disable-next-line n/no-unsupported-features/node-builtins + typeof atob === "function" + ? // Browser (and Node >=16) + // eslint-disable-next-line n/no-unsupported-features/node-builtins + atob(input) + : // Older Node versions (<16) + // eslint-disable-next-line n/no-unsupported-features/node-builtins + typeof Buffer.from === "function" + ? // eslint-disable-next-line n/no-unsupported-features/node-builtins + Buffer.from(input, "base64").toString("binary") + : // eslint-disable-next-line unicorn/no-new-buffer, n/no-deprecated-api + new Buffer(input, "base64").toString("binary"); + const evenLength = binary.length & -2; // Round down to even length + const out = new Uint16Array(evenLength / 2); + for (let index = 0, outIndex = 0; index < evenLength; index += 2) { + const lo = binary.charCodeAt(index); + const hi = binary.charCodeAt(index + 1); + out[outIndex++] = lo | (hi << 8); + } + return out; +} + +// Generated using scripts/write-decode-map.ts +const htmlDecodeTree = /* #__PURE__ */ decodeBase64("QR08ALkAAgH6AYsDNQR2BO0EPgXZBQEGLAbdBxMISQrvCmQLfQurDKQNLw4fD4YPpA+6D/IPAAAAAAAAAAAAAAAAKhBMEY8TmxUWF2EYLBkxGuAa3RsJHDscWR8YIC8jSCSIJcMl6ie3Ku8rEC0CLjoupS7kLgAIRU1hYmNmZ2xtbm9wcnN0dVQAWgBeAGUAaQBzAHcAfgCBAIQAhwCSAJoAoACsALMAbABpAGcAO4DGAMZAUAA7gCYAJkBjAHUAdABlADuAwQDBQHIiZXZlAAJhAAFpeW0AcgByAGMAO4DCAMJAEGRyAADgNdgE3XIAYQB2AGUAO4DAAMBA8CFoYZFj4SFjcgBhZAAAoFMqAAFncIsAjgBvAG4ABGFmAADgNdg43fAlbHlGdW5jdGlvbgCgYSBpAG4AZwA7gMUAxUAAAWNzpACoAHIAAOA12Jzc6SFnbgCgVCJpAGwAZABlADuAwwDDQG0AbAA7gMQAxEAABGFjZWZvcnN1xQDYANoA7QDxAPYA+QD8AAABY3LJAM8AayNzbGFzaAAAoBYidgHTANUAAKDnKmUAZAAAoAYjeQARZIABY3J0AOAA5QDrAGEidXNlAACgNSLuI291bGxpcwCgLCFhAJJjcgAA4DXYBd1wAGYAAOA12Dnd5SF2ZdhiYwDyAOoAbSJwZXEAAKBOIgAHSE9hY2RlZmhpbG9yc3UXARoBHwE6AVIBVQFiAWQBZgGCAakB6QHtAfIBYwB5ACdkUABZADuAqQCpQIABY3B5ACUBKAE1AfUhdGUGYWmg0iJ0KGFsRGlmZmVyZW50aWFsRAAAoEUhbCJleXMAAKAtIQACYWVpb0EBRAFKAU0B8iFvbgxhZABpAGwAO4DHAMdAcgBjAAhhbiJpbnQAAKAwIm8AdAAKYQABZG5ZAV0BaSJsbGEAuGB0I2VyRG90ALdg8gA5AWkAp2NyImNsZQAAAkRNUFRwAXQBeQF9AW8AdAAAoJkiaSJudXMAAKCWIuwhdXMAoJUiaSJtZXMAAKCXIm8AAAFjc4cBlAFrKndpc2VDb250b3VySW50ZWdyYWwAAKAyImUjQ3VybHkAAAFEUZwBpAFvJXVibGVRdW90ZQAAoB0gdSJvdGUAAKAZIAACbG5wdbABtgHNAdgBbwBuAGWgNyIAoHQqgAFnaXQAvAHBAcUB8iJ1ZW50AKBhIm4AdAAAoC8i7yV1ckludGVncmFsAKAuIgABZnLRAdMBAKACIe8iZHVjdACgECJuLnRlckNsb2Nrd2lzZUNvbnRvdXJJbnRlZ3JhbAAAoDMi7yFzcwCgLypjAHIAAOA12J7ccABDoNMiYQBwAACgTSKABURKU1phY2VmaW9zAAsCEgIVAhgCGwIsAjQCOQI9AnMCfwNvoEUh9CJyYWhkAKARKWMAeQACZGMAeQAFZGMAeQAPZIABZ3JzACECJQIoAuchZXIAoCEgcgAAoKEhaAB2AACg5CoAAWF5MAIzAvIhb24OYRRkbAB0oAciYQCUY3IAAOA12AfdAAFhZkECawIAAWNtRQJnAvIjaXRpY2FsAAJBREdUUAJUAl8CYwJjInV0ZQC0YG8AdAFZAloC2WJiJGxlQWN1dGUA3WJyImF2ZQBgYGkibGRlANxi7yFuZACgxCJmJWVyZW50aWFsRAAAoEYhcAR9AgAAAAAAAIECjgIAABoDZgAA4DXYO91EoagAhQKJAm8AdAAAoNwgcSJ1YWwAAKBQIuIhbGUAA0NETFJVVpkCqAK1Au8C/wIRA28AbgB0AG8AdQByAEkAbgB0AGUAZwByAGEA7ADEAW8AdAKvAgAAAACwAqhgbiNBcnJvdwAAoNMhAAFlb7kC0AJmAHQAgAFBUlQAwQLGAs0CciJyb3cAAKDQIekkZ2h0QXJyb3cAoNQhZQDlACsCbgBnAAABTFLWAugC5SFmdAABQVLcAuECciJyb3cAAKD4J+kkZ2h0QXJyb3cAoPon6SRnaHRBcnJvdwCg+SdpImdodAAAAUFU9gL7AnIicm93AACg0iFlAGUAAKCoInAAQQIGAwAAAAALA3Iicm93AACg0SFvJHduQXJyb3cAAKDVIWUlcnRpY2FsQmFyAACgJSJuAAADQUJMUlRhJAM2AzoDWgNxA3oDciJyb3cAAKGTIUJVLAMwA2EAcgAAoBMpcCNBcnJvdwAAoPUhciJldmUAEWPlIWZ00gJDAwAASwMAAFIDaSVnaHRWZWN0b3IAAKBQKWUkZVZlY3RvcgAAoF4p5SJjdG9yQqC9IWEAcgAAoFYpaSJnaHQA1AFiAwAAaQNlJGVWZWN0b3IAAKBfKeUiY3RvckKgwSFhAHIAAKBXKWUAZQBBoKQiciJyb3cAAKCnIXIAcgBvAPcAtAIAAWN0gwOHA3IA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YwAAoN8i4SFycnCgtiEAoD0pgKIqImJjZG9zACsVMBU6FT4VQRVyImNhcAAAoEgqAAFhdTQVNxVwAACgRipwAACgSipvAHQAAKCNInIAAKBFKgDgKiIA/gACYWxydksVURVuFXMVcgByAG2gtyEAoDwpeQCAAWV2dwBYFWUVaRVxAHACXxUAAAAAYxVyAGUA4wAXFXUA4wAZFWUAZQAAoM4iZSJkZ2UAAKDPImUAbgA7gKQApEBlI2Fycm93AAABbHJ7FX8V5SFmdACgtiFpImdodAAAoLchZQDkAG0VAAFjaYsVkRVvAG4AaQBuAPQAkwFuAHQAAKAxImwiY3R5AACgLSOACUFIYWJjZGVmaGlqbG9yc3R1d3oAuBW7Fb8V1RXgFegV+RUKFhUWHxZUFlcWZRbFFtsW7xb7FgUXChdyAPIAtAJhAHIAAKBlKQACZ2xyc8YVyhXOFdAV5yFlcgCgICDlIXRoAKA4IfIA9QxoAHagECAAoKMiawHZFd4VYSJyb3cAAKAPKWEA4wBfAgABYXnkFecV8iFvbg9hNGQAoUYhYW/tFfQVAAFnciEC8RVyAACgyiF0InNlcQAAoHcqgAFnbG0A/xUCFgUWO4CwALBAdABhALRjcCJ0eXYAAKCxKQABaXIOFhIW8yFodACgfykA4DXYId1hAHIAAAFschsWHRYAoMMhAKDCIYACYWVnc3YAKBauAjYWOhY+Fm0AAKHEIm9zLhY0Fm4AZABzoMQi9SFpdACgZiZhIm1tYQDdY2kAbgAAoPIiAKH3AGlvQxZRFmQAZQAAgfcAO29KFksW90BuI3RpbWVzAACgxyJuAPgAUBZjAHkAUmRjAG8CXhYAAAAAYhZyAG4AAKAeI28AcAAAoA0jgAJscHR1dwBuFnEWdRaSFp4W7CFhciRgZgAA4DXYVd0AotkCZW1wc30WhBaJFo0WcQBkoFAibwB0AACgUSJpIm51cwAAoDgi7CF1cwCgFCLxInVhcmUAoKEiYgBsAGUAYgBhAHIAdwBlAGQAZwDlANcAbgCAAWFkaAClFqoWtBZyAHIAbwD3APUMbwB3AG4AYQByAHIAbwB3APMA8xVhI3Jwb29uAAABbHK8FsAWZQBmAPQAHBZpAGcAaAD0AB4WYgHJFs8WawBhAHIAbwD3AJILbwLUFgAAAADYFnIAbgAAoB8jbwBwAACgDCOAAWNvdADhFukW7BYAAXJ55RboFgDgNdi53FVkbAAAoPYp8iFvaxFhAAFkcvMW9xZvAHQAAKDxImkA5qC/JVsSAAFhaP8WAhdyAPIANQNhAPIA1wvhIm5nbGUAoKYpAAFjaQ4XEBd5AF9k5yJyYXJyAKD/JwAJRGFjZGVmZ2xtbm9wcXJzdHV4MRc4F0YXWxcyBF4XaRd5F40XrBe0F78X2RcVGCEYLRg1GEAYAAFEbzUXgRZvAPQA+BUAAWNzPBdCF3UAdABlADuA6QDpQPQhZXIAoG4qAAJhaW95TRdQF1YXWhfyIW9uG2FyAGOgViI7gOoA6kDsIW9uAKBVIk1kbwB0ABdhAAFEcmIXZhdvAHQAAKBSIgDgNdgi3XKhmipuF3QXYQB2AGUAO4DoAOhAZKCWKm8AdAAAoJgqgKGZKmlscwCAF4UXhxfuInRlcnMAoOcjAKATIWSglSpvAHQAAKCXKoABYXBzAJMXlheiF2MAcgATYXQAeQBzogUinxcAAAAAoRdlAHQAAKAFInAAMaADIDMBqRerFwCgBCAAoAUgAAFnc7AXsRdLYXAAAKACIAABZ3C4F7sXbwBuABlhZgAA4DXYVt2AAWFscwDFF8sXzxdyAHOg1SJsAACg4yl1AHMAAKBxKmkAAKG1A2x21RfYF28AbgC1Y/VjAAJjc3V24BfoF/0XEBgAAWlv5BdWF3IAYwAAoFYiaQLuFwAAAADwF+0ADQThIW50AAFnbPUX+Rd0AHIAAKCWKuUhc3MAoJUqgAFhZWkAAxgGGAoYbABzAD1gcwB0AACgXyJ2AESgYSJEAACgeCrwImFyc2wAoOUpAAFEYRkYHRhvAHQAAKBTInIAcgAAoHEpgAFjZGkAJxgqGO0XcgAAoC8hbwD0AIwCAAFhaDEYMhi3YzuA8ADwQAABbXI5GD0YbAA7gOsA60BvAACgrCCAAWNpcABGGEgYSxhsACFgcwD0ACwEAAFlb08YVxhjAHQAYQB0AGkAbwDuABoEbgBlAG4AdABpAGEAbADlADME4Ql1GAAAgRgAAIMYiBgAAAAAoRilGAAAqhgAALsYvhjRGAAA1xgnGWwAbABpAG4AZwBkAG8AdABzAGUA8QBlF3kARGRtImFsZQAAoEAmgAFpbHIAjRiRGJ0Y7CFpZwCgA/tpApcYAAAAAJoYZwAAoAD7aQBnAACgBPsA4DXYI93sIWlnAKAB++whaWcA4GYAagCAAWFsdACvGLIYthh0AACgbSZpAGcAAKAC+24AcwAAoLElbwBmAJJh8AHCGAAAxhhmAADgNdhX3QABYWvJGMwYbADsAGsEdqDUIgCg2SphI3J0aW50AACgDSoAAWFv2hgiGQABY3PeGB8ZsQPnGP0YBRkSGRUZAAAdGbID7xjyGPQY9xj5GAAA+xg7gL0AvUAAoFMhO4C8ALxAAKBVIQCgWSEAoFshswEBGQAAAxkAoFQhAKBWIbQCCxkOGQAAAAAQGTuAvgC+QACgVyEAoFwhNQAAoFghtgEZGQAAGxkAoFohAKBdITgAAKBeIWwAAKBEIHcAbgAAoCIjYwByAADgNdi73IAIRWFiY2RlZmdpamxub3JzdHYARhlKGVoZXhlmGWkZkhmWGZkZnRmgGa0ZxhnLGc8Z4BkjGmygZyIAoIwqgAFjbXAAUBlTGVgZ9SF0ZfVhbQBhAOSgswM6FgCghipyImV2ZQAfYQABaXliGWUZcgBjAB1hM2RvAHQAIWGAoWUibHFzAMYEcBl6GfGhZSLOBAAAdhlsAGEAbgD0AN8EgKF+KmNkbACBGYQZjBljAACgqSpvAHQAb6CAKmyggioAoIQqZeDbIgD+cwAAoJQqcgAA4DXYJN3noGsirATtIWVsAKA3IWMAeQBTZIChdyJFYWoApxmpGasZAKCSKgCgpSoAoKQqAAJFYWVztBm2Gb0ZwhkAoGkicABwoIoq8iFveACgiipxoIgq8aCIKrUZaQBtAACg5yJwAGYAAOA12FjdYQB2AOUAYwIAAWNp0xnWGXIAAKAKIW0AAKFzImVs3BneGQCgjioAoJAqAIM+ADtjZGxxco0E6xn0GfgZ/BkBGgABY2nvGfEZAKCnKnIAAKB6Km8AdAAAoNci0CFhcgCglSl1ImVzdAAAoHwqgAJhZGVscwAKGvQZFhrVBCAa8AEPGgAAFBpwAHIAbwD4AFkZcgAAoHgpcQAAAWxxxAQbGmwAZQBzAPMASRlpAO0A5AQAAWVuJxouGnIjdG5lcXEAAOBpIgD+xQAsGgAFQWFiY2Vma29zeUAaQxpmGmoabRqDGocalhrCGtMacgDyAMwCAAJpbG1yShpOGlAaVBpyAHMA8ABxD2YAvWBpAGwA9AASBQABZHJYGlsaYwB5AEpkAKGUIWN3YBpkGmkAcgAAoEgpAKCtIWEAcgAAoA8h6SFyYyVhgAFhbHIAcxp7Gn8a8iF0c3WgZSZpAHQAAKBlJuwhaXAAoCYg4yFvbgCguSJyAADgNdgl3XMAAAFld4wakRphInJvdwAAoCUpYSJyb3cAAKAmKYACYW1vcHIAnxqjGqcauhq+GnIAcgAAoP8h9CFodACgOyJrAAABbHKsGrMaZSRmdGFycm93AACgqSHpJGdodGFycm93AKCqIWYAAOA12Fnd4iFhcgCgFSCAAWNsdADIGswa0BpyAADgNdi93GEAcwDoAGka8iFvaydhAAFicNca2xr1IWxsAKBDIOghZW4AoBAg4Qr2GgAA/RoAAAgbExsaGwAAIRs7GwAAAAA+G2IbmRuVG6sbAACyG80b0htjAHUAdABlADuA7QDtQAChYyBpeQEbBhtyAGMAO4DuAO5AOGQAAWN4CxsNG3kANWRjAGwAO4ChAKFAAAFmcssCFhsA4DXYJt1yAGEAdgBlADuA7ADsQIChSCFpbm8AJxsyGzYbAAFpbisbLxtuAHQAAKAMKnQAAKAtIuYhaW4AoNwpdABhAACgKSHsIWlnM2GAAWFvcABDG1sbXhuAAWNndABJG0sbWRtyACthgAFlbHAAcQVRG1UbaQBuAOUAyAVhAHIA9AByBWgAMWFmAACgtyJlAGQAtWEAoggiY2ZvdGkbbRt1G3kb4SFyZQCgBSFpAG4AdKAeImkAZQAAoN0pZABvAPQAWxsAoisiY2VscIEbhRuPG5QbYQBsAACguiIAAWdyiRuNG2UAcgDzACMQ4wCCG2EicmhrAACgFyryIW9kAKA8KgACY2dwdJ8boRukG6gbeQBRZG8AbgAvYWYAAOA12FrdYQC5Y3UAZQBzAHQAO4C/AL9AAAFjabUbuRtyAADgNdi+3G4AAKIIIkVkc3bCG8QbyBvQAwCg+SJvAHQAAKD1Inag9CIAoPMiaaBiIOwhZGUpYesB1hsAANkbYwB5AFZkbAA7gO8A70AAA2NmbW9zdeYb7hvyG/Ub+hsFHAABaXnqG+0bcgBjADVhOWRyAADgNdgn3eEhdGg3YnAAZgAA4DXYW93jAf8bAAADHHIAAOA12L/c8iFjeVhk6yFjeVRkAARhY2ZnaGpvcxUcGhwiHCYcKhwtHDAcNRzwIXBhdqC6A/BjAAFleR4cIRzkIWls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ZmdobG1vcHJzdHV3CiwNLBEsHSwnLDEsQCxLLFIsYix6LIQsjyzLLOgs7Sz/LAotcgDyAAkDYQByAACgYykAAWNyFSwbLHUAdABlADuA+gD6QPIACQ1yAOMBIywAACUseQBeZHYAZQBtYQABaXkrLDAscgBjADuA+wD7QENkgAFhYmgANyw6LD0scgDyANEO7CFhY3FhYQDyAOAOAAFpckQsSCzzIWh0AKB+KQDgNdgy3XIAYQB2AGUAO4D5APlAYQFWLF8scgAAAWxyWixcLACgvyEAoL4hbABrAACggCUAAWN0Zix2LG8CbCwAAAAAcyxyAG4AZaAcI3IAAKAcI28AcAAAoA8jcgBpAACg+CUAAWFsfiyBLGMAcgBrYTuAqACoQAABZ3CILIssbwBuAHNhZgAA4DXYZt0AA2FkaGxzdZksniynLLgsuyzFLHIAcgBvAPcACQ1vAHcAbgBhAHIAcgBvAPcA2A5hI3Jwb29uAAABbHKvLLMsZQBmAPQAWyxpAGcAaAD0AF0sdQDzAKYOaQAAocUDaGzBLMIs0mNvAG4AxWPwI2Fycm93cwCgyCGAAWNpdADRLOEs5CxvAtcsAAAAAN4scgBuAGWgHSNyAACgHSNvAHAAAKAOI24AZwBvYXIAaQAAoPklYwByAADgNdjK3IABZGlyAPMs9yz6LG8AdAAAoPAi7CFkZWlhaQBmoLUlAKC0JQABYW0DLQYtcgDyAMosbAA7gPwA/EDhIm5nbGUAoKcpgAdBQkRhY2RlZmxub3Byc3oAJy0qLTAtNC2bLZ0toS2/LcMtxy3TLdgt3C3gLfwtcgDyABADYQByAHag6CoAoOkqYQBzAOgA/gIAAW5yOC08LechcnQAoJwpgANla25wcnN0AJkpSC1NLVQtXi1iLYItYQBwAHAA4QAaHG8AdABoAGkAbgDnAKEXgAFoaXIAoSmzJFotbwBwAPQAdCVooJUh7wD4JgABaXVmLWotZwBtAOEAuygAAWJwbi14LXMjZXRuZXEAceCKIgD+AODLKgD+cyNldG5lcQBx4IsiAP4A4MwqAP4AAWhyhi2KLWUAdADhABIraSNhbmdsZQAAAWxyki2WLeUhZnQAoLIiaSJnaHQAAKCzInkAMmThIXNoAKCiIoABZWxyAKcttC24LWKiKCKuLQAAAACyLWEAcgAAoLsicQAAoFoi7CFpcACg7iIAAWJ0vC1eD2EA8gBfD3IAAOA12DPddAByAOkAlS1zAHUAAAFicM0t0C0A4IIi0iAA4IMi0iBwAGYAAOA12GfdcgBvAPAAWQt0AHIA6QCaLQABY3XkLegtcgAA4DXYy9wAAWJw7C30LW4AAAFFZXUt8S0A4IoiAP5uAAABRWV/LfktAOCLIgD+6SJnemFnAKCaKYADY2Vmb3BycwANLhAuJS4pLiMuLi40LukhcmN1YQABZGkULiEuAAFiZxguHC5hAHIAAKBfKmUAcaAnIgCgWSLlIXJwAKAYIXIAAOA12DTdcABmAADgNdho3WWgQCJhAHQA6ABqD2MAcgAA4DXYzNzjCuQRUC4AAFQuAABYLmIuAAAAAGMubS5wLnQuAAAAAIguki4AAJouJxIqEnQAcgDpAB0ScgAA4DXYNd0AAUFhWy5eLnIA8gDnAnIA8gCTB75jAAFBYWYuaS5yAPIA4AJyAPIAjAdhAPAAeh5pAHMAAKD7IoABZHB0APgReS6DLgABZmx9LoAuAOA12GnddQDzAP8RaQBtAOUABBIAAUFhiy6OLnIA8gDuAnIA8gCaBwABY3GVLgoScgAA4DXYzdwAAXB0nS6hLmwAdQDzACUScgDpACASAARhY2VmaW9zdbEuvC7ELsguzC7PLtQu2S5jAAABdXm2LrsudABlADuA/QD9QE9kAAFpecAuwy5yAGMAd2FLZG4AO4ClAKVAcgAA4DXYNt1jAHkAV2RwAGYAAOA12GrdYwByAADgNdjO3AABY23dLt8ueQBOZGwAO4D/AP9AAAVhY2RlZmhpb3N38y73Lv8uAi8MLxAvEy8YLx0vIi9jInV0ZQB6YQABYXn7Lv4u8iFvbn5hN2RvAHQAfGEAAWV0Bi8KL3QAcgDmAB8QYQC2Y3IAAOA12DfdYwB5ADZk5yJyYXJyAKDdIXAAZgAA4DXYa91jAHIAAOA12M/cAAFqbiYvKC8AoA0gagAAoAwg"); + +// Generated using scripts/write-decode-map.ts +const xmlDecodeTree = /* #__PURE__ */ decodeBase64("AAJhZ2xxBwARABMAFQBtAg0AAAAAAA8AcAAmYG8AcwAnYHQAPmB0ADxg9SFvdCJg"); + +/** + * Bit flags & masks for the binary trie encoding used for entity decoding. + * + * Bit layout (16 bits total): + * 15..14 VALUE_LENGTH (+1 encoding; 0 => no value) + * 13 FLAG13. If valueLength>0: semicolon required flag (implicit ';'). + * If valueLength==0: compact run flag. + * 12..7 BRANCH_LENGTH Branch length (0 => single branch in 6..0 if jumpOffset==char) OR run length (when compact run) + * 6..0 JUMP_TABLE Jump offset (jump table) OR single-branch char code OR first run char + */ +var BinTrieFlags; +(function (BinTrieFlags) { + BinTrieFlags[BinTrieFlags["VALUE_LENGTH"] = 49152] = "VALUE_LENGTH"; + BinTrieFlags[BinTrieFlags["FLAG13"] = 8192] = "FLAG13"; + BinTrieFlags[BinTrieFlags["BRANCH_LENGTH"] = 8064] = "BRANCH_LENGTH"; + BinTrieFlags[BinTrieFlags["JUMP_TABLE"] = 127] = "JUMP_TABLE"; +})(BinTrieFlags || (BinTrieFlags = {})); + +var CharCodes$1; +(function (CharCodes) { + CharCodes[CharCodes["NUM"] = 35] = "NUM"; + CharCodes[CharCodes["SEMI"] = 59] = "SEMI"; + CharCodes[CharCodes["EQUALS"] = 61] = "EQUALS"; + CharCodes[CharCodes["ZERO"] = 48] = "ZERO"; + CharCodes[CharCodes["NINE"] = 57] = "NINE"; + CharCodes[CharCodes["LOWER_A"] = 97] = "LOWER_A"; + CharCodes[CharCodes["LOWER_F"] = 102] = "LOWER_F"; + CharCodes[CharCodes["LOWER_X"] = 120] = "LOWER_X"; + CharCodes[CharCodes["LOWER_Z"] = 122] = "LOWER_Z"; + CharCodes[CharCodes["UPPER_A"] = 65] = "UPPER_A"; + CharCodes[CharCodes["UPPER_F"] = 70] = "UPPER_F"; + CharCodes[CharCodes["UPPER_Z"] = 90] = "UPPER_Z"; +})(CharCodes$1 || (CharCodes$1 = {})); +/** Bit that needs to be set to convert an upper case ASCII character to lower case */ +const TO_LOWER_BIT = 32; +function isNumber(code) { + return code >= CharCodes$1.ZERO && code <= CharCodes$1.NINE; +} +function isHexadecimalCharacter(code) { + return ((code >= CharCodes$1.UPPER_A && code <= CharCodes$1.UPPER_F) || + (code >= CharCodes$1.LOWER_A && code <= CharCodes$1.LOWER_F)); +} +function isAsciiAlphaNumeric(code) { + return ((code >= CharCodes$1.UPPER_A && code <= CharCodes$1.UPPER_Z) || + (code >= CharCodes$1.LOWER_A && code <= CharCodes$1.LOWER_Z) || + isNumber(code)); +} +/** + * Checks if the given character is a valid end character for an entity in an attribute. + * + * Attribute values that aren't terminated properly aren't parsed, and shouldn't lead to a parser error. + * See the example in https://html.spec.whatwg.org/multipage/parsing.html#named-character-reference-state + */ +function isEntityInAttributeInvalidEnd(code) { + return code === CharCodes$1.EQUALS || isAsciiAlphaNumeric(code); +} +var EntityDecoderState; +(function (EntityDecoderState) { + EntityDecoderState[EntityDecoderState["EntityStart"] = 0] = "EntityStart"; + EntityDecoderState[EntityDecoderState["NumericStart"] = 1] = "NumericStart"; + EntityDecoderState[EntityDecoderState["NumericDecimal"] = 2] = "NumericDecimal"; + EntityDecoderState[EntityDecoderState["NumericHex"] = 3] = "NumericHex"; + EntityDecoderState[EntityDecoderState["NamedEntity"] = 4] = "NamedEntity"; +})(EntityDecoderState || (EntityDecoderState = {})); +var DecodingMode; +(function (DecodingMode) { + /** Entities in text nodes that can end with any character. */ + DecodingMode[DecodingMode["Legacy"] = 0] = "Legacy"; + /** Only allow entities terminated with a semicolon. */ + DecodingMode[DecodingMode["Strict"] = 1] = "Strict"; + /** Entities in attributes have limitations on ending characters. */ + DecodingMode[DecodingMode["Attribute"] = 2] = "Attribute"; +})(DecodingMode || (DecodingMode = {})); +/** + * Token decoder with support of writing partial entities. + */ +class EntityDecoder { + constructor( + /** The tree used to decode entities. */ + // biome-ignore lint/correctness/noUnusedPrivateClassMembers: False positive + decodeTree, + /** + * The function that is called when a codepoint is decoded. + * + * For multi-byte named entities, this will be called multiple times, + * with the second codepoint, and the same `consumed` value. + * + * @param codepoint The decoded codepoint. + * @param consumed The number of bytes consumed by the decoder. + */ + emitCodePoint, + /** An object that is used to produce errors. */ + errors) { + this.decodeTree = decodeTree; + this.emitCodePoint = emitCodePoint; + this.errors = errors; + /** The current state of the decoder. */ + this.state = EntityDecoderState.EntityStart; + /** Characters that were consumed while parsing an entity. */ + this.consumed = 1; + /** + * The result of the entity. + * + * Either the result index of a numeric entity, or the codepoint of a + * numeric entity. + */ + this.result = 0; + /** The current index in the decode tree. */ + this.treeIndex = 0; + /** The number of characters that were consumed in excess. */ + this.excess = 1; + /** The mode in which the decoder is operating. */ + this.decodeMode = DecodingMode.Strict; + /** The number of characters that have been consumed in the current run. */ + this.runConsumed = 0; + } + /** Resets the instance to make it reusable. */ + startEntity(decodeMode) { + this.decodeMode = decodeMode; + this.state = EntityDecoderState.EntityStart; + this.result = 0; + this.treeIndex = 0; + this.excess = 1; + this.consumed = 1; + this.runConsumed = 0; + } + /** + * Write an entity to the decoder. This can be called multiple times with partial entities. + * If the entity is incomplete, the decoder will return -1. + * + * Mirrors the implementation of `getDecoder`, but with the ability to stop decoding if the + * entity is incomplete, and resume when the next string is written. + * + * @param input The string containing the entity (or a continuation of the entity). + * @param offset The offset at which the entity begins. Should be 0 if this is not the first call. + * @returns The number of characters that were consumed, or -1 if the entity is incomplete. + */ + write(input, offset) { + switch (this.state) { + case EntityDecoderState.EntityStart: { + if (input.charCodeAt(offset) === CharCodes$1.NUM) { + this.state = EntityDecoderState.NumericStart; + this.consumed += 1; + return this.stateNumericStart(input, offset + 1); + } + this.state = EntityDecoderState.NamedEntity; + return this.stateNamedEntity(input, offset); + } + case EntityDecoderState.NumericStart: { + return this.stateNumericStart(input, offset); + } + case EntityDecoderState.NumericDecimal: { + return this.stateNumericDecimal(input, offset); + } + case EntityDecoderState.NumericHex: { + return this.stateNumericHex(input, offset); + } + case EntityDecoderState.NamedEntity: { + return this.stateNamedEntity(input, offset); + } + } + } + /** + * Switches between the numeric decimal and hexadecimal states. + * + * Equivalent to the `Numeric character reference state` in the HTML spec. + * + * @param input The string containing the entity (or a continuation of the entity). + * @param offset The current offset. + * @returns The number of characters that were consumed, or -1 if the entity is incomplete. + */ + stateNumericStart(input, offset) { + if (offset >= input.length) { + return -1; + } + if ((input.charCodeAt(offset) | TO_LOWER_BIT) === CharCodes$1.LOWER_X) { + this.state = EntityDecoderState.NumericHex; + this.consumed += 1; + return this.stateNumericHex(input, offset + 1); + } + this.state = EntityDecoderState.NumericDecimal; + return this.stateNumericDecimal(input, offset); + } + /** + * Parses a hexadecimal numeric entity. + * + * Equivalent to the `Hexademical character reference state` in the HTML spec. + * + * @param input The string containing the entity (or a continuation of the entity). + * @param offset The current offset. + * @returns The number of characters that were consumed, or -1 if the entity is incomplete. + */ + stateNumericHex(input, offset) { + while (offset < input.length) { + const char = input.charCodeAt(offset); + if (isNumber(char) || isHexadecimalCharacter(char)) { + // Convert hex digit to value (0-15); 'a'/'A' -> 10. + const digit = char <= CharCodes$1.NINE + ? char - CharCodes$1.ZERO + : (char | TO_LOWER_BIT) - CharCodes$1.LOWER_A + 10; + this.result = this.result * 16 + digit; + this.consumed++; + offset++; + } + else { + return this.emitNumericEntity(char, 3); + } + } + return -1; // Incomplete entity + } + /** + * Parses a decimal numeric entity. + * + * Equivalent to the `Decimal character reference state` in the HTML spec. + * + * @param input The string containing the entity (or a continuation of the entity). + * @param offset The current offset. + * @returns The number of characters that were consumed, or -1 if the entity is incomplete. + */ + stateNumericDecimal(input, offset) { + while (offset < input.length) { + const char = input.charCodeAt(offset); + if (isNumber(char)) { + this.result = this.result * 10 + (char - CharCodes$1.ZERO); + this.consumed++; + offset++; + } + else { + return this.emitNumericEntity(char, 2); + } + } + return -1; // Incomplete entity + } + /** + * Validate and emit a numeric entity. + * + * Implements the logic from the `Hexademical character reference start + * state` and `Numeric character reference end state` in the HTML spec. + * + * @param lastCp The last code point of the entity. Used to see if the + * entity was terminated with a semicolon. + * @param expectedLength The minimum number of characters that should be + * consumed. Used to validate that at least one digit + * was consumed. + * @returns The number of characters that were consumed. + */ + emitNumericEntity(lastCp, expectedLength) { + var _a; + // Ensure we consumed at least one digit. + if (this.consumed <= expectedLength) { + (_a = this.errors) === null || _a === void 0 ? void 0 : _a.absenceOfDigitsInNumericCharacterReference(this.consumed); + return 0; + } + // Figure out if this is a legit end of the entity + if (lastCp === CharCodes$1.SEMI) { + this.consumed += 1; + } + else if (this.decodeMode === DecodingMode.Strict) { + return 0; + } + this.emitCodePoint(replaceCodePoint(this.result), this.consumed); + if (this.errors) { + if (lastCp !== CharCodes$1.SEMI) { + this.errors.missingSemicolonAfterCharacterReference(); + } + this.errors.validateNumericCharacterReference(this.result); + } + return this.consumed; + } + /** + * Parses a named entity. + * + * Equivalent to the `Named character reference state` in the HTML spec. + * + * @param input The string containing the entity (or a continuation of the entity). + * @param offset The current offset. + * @returns The number of characters that were consumed, or -1 if the entity is incomplete. + */ + stateNamedEntity(input, offset) { + const { decodeTree } = this; + let current = decodeTree[this.treeIndex]; + // The length is the number of bytes of the value, including the current byte. + let valueLength = (current & BinTrieFlags.VALUE_LENGTH) >> 14; + while (offset < input.length) { + // Handle compact runs (possibly inline): valueLength == 0 and SEMI_REQUIRED bit set. + if (valueLength === 0 && (current & BinTrieFlags.FLAG13) !== 0) { + const runLength = (current & BinTrieFlags.BRANCH_LENGTH) >> 7; /* 2..63 */ + // If we are starting a run, check the first char. + if (this.runConsumed === 0) { + const firstChar = current & BinTrieFlags.JUMP_TABLE; + if (input.charCodeAt(offset) !== firstChar) { + return this.result === 0 + ? 0 + : this.emitNotTerminatedNamedEntity(); + } + offset++; + this.excess++; + this.runConsumed++; + } + // Check remaining characters in the run. + while (this.runConsumed < runLength) { + if (offset >= input.length) { + return -1; + } + const charIndexInPacked = this.runConsumed - 1; + const packedWord = decodeTree[this.treeIndex + 1 + (charIndexInPacked >> 1)]; + const expectedChar = charIndexInPacked % 2 === 0 + ? packedWord & 0xff + : (packedWord >> 8) & 0xff; + if (input.charCodeAt(offset) !== expectedChar) { + this.runConsumed = 0; + return this.result === 0 + ? 0 + : this.emitNotTerminatedNamedEntity(); + } + offset++; + this.excess++; + this.runConsumed++; + } + this.runConsumed = 0; + this.treeIndex += 1 + (runLength >> 1); + current = decodeTree[this.treeIndex]; + valueLength = (current & BinTrieFlags.VALUE_LENGTH) >> 14; + } + if (offset >= input.length) + break; + const char = input.charCodeAt(offset); + /* + * Implicit semicolon handling for nodes that require a semicolon but + * don't have an explicit ';' branch stored in the trie. If we have + * a value on the current node, it requires a semicolon, and the + * current input character is a semicolon, emit the entity using the + * current node (without descending further). + */ + if (char === CharCodes$1.SEMI && + valueLength !== 0 && + (current & BinTrieFlags.FLAG13) !== 0) { + return this.emitNamedEntityData(this.treeIndex, valueLength, this.consumed + this.excess); + } + this.treeIndex = determineBranch(decodeTree, current, this.treeIndex + Math.max(1, valueLength), char); + if (this.treeIndex < 0) { + return this.result === 0 || + // If we are parsing an attribute + (this.decodeMode === DecodingMode.Attribute && + // We shouldn't have consumed any characters after the entity, + (valueLength === 0 || + // And there should be no invalid characters. + isEntityInAttributeInvalidEnd(char))) + ? 0 + : this.emitNotTerminatedNamedEntity(); + } + current = decodeTree[this.treeIndex]; + valueLength = (current & BinTrieFlags.VALUE_LENGTH) >> 14; + // If the branch is a value, store it and continue + if (valueLength !== 0) { + // If the entity is terminated by a semicolon, we are done. + if (char === CharCodes$1.SEMI) { + return this.emitNamedEntityData(this.treeIndex, valueLength, this.consumed + this.excess); + } + // If we encounter a non-terminated (legacy) entity while parsing strictly, then ignore it. + if (this.decodeMode !== DecodingMode.Strict && + (current & BinTrieFlags.FLAG13) === 0) { + this.result = this.treeIndex; + this.consumed += this.excess; + this.excess = 0; + } + } + // Increment offset & excess for next iteration + offset++; + this.excess++; + } + return -1; + } + /** + * Emit a named entity that was not terminated with a semicolon. + * + * @returns The number of characters consumed. + */ + emitNotTerminatedNamedEntity() { + var _a; + const { result, decodeTree } = this; + const valueLength = (decodeTree[result] & BinTrieFlags.VALUE_LENGTH) >> 14; + this.emitNamedEntityData(result, valueLength, this.consumed); + (_a = this.errors) === null || _a === void 0 ? void 0 : _a.missingSemicolonAfterCharacterReference(); + return this.consumed; + } + /** + * Emit a named entity. + * + * @param result The index of the entity in the decode tree. + * @param valueLength The number of bytes in the entity. + * @param consumed The number of characters consumed. + * + * @returns The number of characters consumed. + */ + emitNamedEntityData(result, valueLength, consumed) { + const { decodeTree } = this; + this.emitCodePoint(valueLength === 1 + ? decodeTree[result] & + ~(BinTrieFlags.VALUE_LENGTH | BinTrieFlags.FLAG13) + : decodeTree[result + 1], consumed); + if (valueLength === 3) { + // For multi-byte values, we need to emit the second byte. + this.emitCodePoint(decodeTree[result + 2], consumed); + } + return consumed; + } + /** + * Signal to the parser that the end of the input was reached. + * + * Remaining data will be emitted and relevant errors will be produced. + * + * @returns The number of characters consumed. + */ + end() { + var _a; + switch (this.state) { + case EntityDecoderState.NamedEntity: { + // Emit a named entity if we have one. + return this.result !== 0 && + (this.decodeMode !== DecodingMode.Attribute || + this.result === this.treeIndex) + ? this.emitNotTerminatedNamedEntity() + : 0; + } + // Otherwise, emit a numeric entity if we have one. + case EntityDecoderState.NumericDecimal: { + return this.emitNumericEntity(0, 2); + } + case EntityDecoderState.NumericHex: { + return this.emitNumericEntity(0, 3); + } + case EntityDecoderState.NumericStart: { + (_a = this.errors) === null || _a === void 0 ? void 0 : _a.absenceOfDigitsInNumericCharacterReference(this.consumed); + return 0; + } + case EntityDecoderState.EntityStart: { + // Return 0 if we have no entity. + return 0; + } + } + } +} +/** + * Determines the branch of the current node that is taken given the current + * character. This function is used to traverse the trie. + * + * @param decodeTree The trie. + * @param current The current node. + * @param nodeIdx The index right after the current node and its value. + * @param char The current character. + * @returns The index of the next node, or -1 if no branch is taken. + */ +function determineBranch(decodeTree, current, nodeIndex, char) { + const branchCount = (current & BinTrieFlags.BRANCH_LENGTH) >> 7; + const jumpOffset = current & BinTrieFlags.JUMP_TABLE; + // Case 1: Single branch encoded in jump offset + if (branchCount === 0) { + return jumpOffset !== 0 && char === jumpOffset ? nodeIndex : -1; + } + // Case 2: Multiple branches encoded in jump table + if (jumpOffset) { + const value = char - jumpOffset; + return value < 0 || value >= branchCount + ? -1 + : decodeTree[nodeIndex + value] - 1; + } + // Case 3: Multiple branches encoded in packed dictionary (two keys per uint16) + const packedKeySlots = (branchCount + 1) >> 1; + /* + * Treat packed keys as a virtual sorted array of length `branchCount`. + * Key(i) = low byte for even i, high byte for odd i in slot i>>1. + */ + let lo = 0; + let hi = branchCount - 1; + while (lo <= hi) { + const mid = (lo + hi) >>> 1; + const slot = mid >> 1; + const packed = decodeTree[nodeIndex + slot]; + const midKey = (packed >> ((mid & 1) * 8)) & 0xff; + if (midKey < char) { + lo = mid + 1; + } + else if (midKey > char) { + hi = mid - 1; + } + else { + return decodeTree[nodeIndex + packedKeySlots + mid]; + } + } + return -1; +} + +var CharCodes; +(function (CharCodes) { + CharCodes[CharCodes["Tab"] = 9] = "Tab"; + CharCodes[CharCodes["NewLine"] = 10] = "NewLine"; + CharCodes[CharCodes["FormFeed"] = 12] = "FormFeed"; + CharCodes[CharCodes["CarriageReturn"] = 13] = "CarriageReturn"; + CharCodes[CharCodes["Space"] = 32] = "Space"; + CharCodes[CharCodes["ExclamationMark"] = 33] = "ExclamationMark"; + CharCodes[CharCodes["Number"] = 35] = "Number"; + CharCodes[CharCodes["Amp"] = 38] = "Amp"; + CharCodes[CharCodes["SingleQuote"] = 39] = "SingleQuote"; + CharCodes[CharCodes["DoubleQuote"] = 34] = "DoubleQuote"; + CharCodes[CharCodes["Dash"] = 45] = "Dash"; + CharCodes[CharCodes["Slash"] = 47] = "Slash"; + CharCodes[CharCodes["Zero"] = 48] = "Zero"; + CharCodes[CharCodes["Nine"] = 57] = "Nine"; + CharCodes[CharCodes["Semi"] = 59] = "Semi"; + CharCodes[CharCodes["Lt"] = 60] = "Lt"; + CharCodes[CharCodes["Eq"] = 61] = "Eq"; + CharCodes[CharCodes["Gt"] = 62] = "Gt"; + CharCodes[CharCodes["Questionmark"] = 63] = "Questionmark"; + CharCodes[CharCodes["UpperA"] = 65] = "UpperA"; + CharCodes[CharCodes["LowerA"] = 97] = "LowerA"; + CharCodes[CharCodes["UpperF"] = 70] = "UpperF"; + CharCodes[CharCodes["LowerF"] = 102] = "LowerF"; + CharCodes[CharCodes["UpperZ"] = 90] = "UpperZ"; + CharCodes[CharCodes["LowerZ"] = 122] = "LowerZ"; + CharCodes[CharCodes["LowerX"] = 120] = "LowerX"; + CharCodes[CharCodes["OpeningSquareBracket"] = 91] = "OpeningSquareBracket"; +})(CharCodes || (CharCodes = {})); +/** All the states the tokenizer can be in. */ +var State; +(function (State) { + State[State["Text"] = 1] = "Text"; + State[State["BeforeTagName"] = 2] = "BeforeTagName"; + State[State["InTagName"] = 3] = "InTagName"; + State[State["InSelfClosingTag"] = 4] = "InSelfClosingTag"; + State[State["BeforeClosingTagName"] = 5] = "BeforeClosingTagName"; + State[State["InClosingTagName"] = 6] = "InClosingTagName"; + State[State["AfterClosingTagName"] = 7] = "AfterClosingTagName"; + // Attributes + State[State["BeforeAttributeName"] = 8] = "BeforeAttributeName"; + State[State["InAttributeName"] = 9] = "InAttributeName"; + State[State["AfterAttributeName"] = 10] = "AfterAttributeName"; + State[State["BeforeAttributeValue"] = 11] = "BeforeAttributeValue"; + State[State["InAttributeValueDq"] = 12] = "InAttributeValueDq"; + State[State["InAttributeValueSq"] = 13] = "InAttributeValueSq"; + State[State["InAttributeValueNq"] = 14] = "InAttributeValueNq"; + // Declarations + State[State["BeforeDeclaration"] = 15] = "BeforeDeclaration"; + State[State["InDeclaration"] = 16] = "InDeclaration"; + // Processing instructions + State[State["InProcessingInstruction"] = 17] = "InProcessingInstruction"; + // Comments & CDATA + State[State["BeforeComment"] = 18] = "BeforeComment"; + State[State["CDATASequence"] = 19] = "CDATASequence"; + State[State["InSpecialComment"] = 20] = "InSpecialComment"; + State[State["InCommentLike"] = 21] = "InCommentLike"; + // Special tags + State[State["BeforeSpecialS"] = 22] = "BeforeSpecialS"; + State[State["BeforeSpecialT"] = 23] = "BeforeSpecialT"; + State[State["SpecialStartSequence"] = 24] = "SpecialStartSequence"; + State[State["InSpecialTag"] = 25] = "InSpecialTag"; + State[State["InEntity"] = 26] = "InEntity"; +})(State || (State = {})); +function isWhitespace$1(c) { + return (c === CharCodes.Space || + c === CharCodes.NewLine || + c === CharCodes.Tab || + c === CharCodes.FormFeed || + c === CharCodes.CarriageReturn); +} +function isEndOfTagSection(c) { + return c === CharCodes.Slash || c === CharCodes.Gt || isWhitespace$1(c); +} +function isASCIIAlpha(c) { + return ((c >= CharCodes.LowerA && c <= CharCodes.LowerZ) || + (c >= CharCodes.UpperA && c <= CharCodes.UpperZ)); +} +var QuoteType; +(function (QuoteType) { + QuoteType[QuoteType["NoValue"] = 0] = "NoValue"; + QuoteType[QuoteType["Unquoted"] = 1] = "Unquoted"; + QuoteType[QuoteType["Single"] = 2] = "Single"; + QuoteType[QuoteType["Double"] = 3] = "Double"; +})(QuoteType || (QuoteType = {})); +/** + * Sequences used to match longer strings. + * + * We don't have `Script`, `Style`, or `Title` here. Instead, we re-use the *End + * sequences with an increased offset. + */ +const Sequences = { + Cdata: new Uint8Array([0x43, 0x44, 0x41, 0x54, 0x41, 0x5b]), // CDATA[ + CdataEnd: new Uint8Array([0x5d, 0x5d, 0x3e]), // ]]> + CommentEnd: new Uint8Array([0x2d, 0x2d, 0x3e]), // `-->` + ScriptEnd: new Uint8Array([0x3c, 0x2f, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74]), // ` this.emitCodePoint(cp, consumed)); + } + reset() { + this.state = State.Text; + this.buffer = ""; + this.sectionStart = 0; + this.index = 0; + this.baseState = State.Text; + this.currentSequence = undefined; + this.running = true; + this.offset = 0; + } + write(chunk) { + this.offset += this.buffer.length; + this.buffer = chunk; + this.parse(); + } + end() { + if (this.running) + this.finish(); + } + pause() { + this.running = false; + } + resume() { + this.running = true; + if (this.index < this.buffer.length + this.offset) { + this.parse(); + } + } + stateText(c) { + if (c === CharCodes.Lt || + (!this.decodeEntities && this.fastForwardTo(CharCodes.Lt))) { + if (this.index > this.sectionStart) { + this.cbs.ontext(this.sectionStart, this.index); + } + this.state = State.BeforeTagName; + this.sectionStart = this.index; + } + else if (this.decodeEntities && c === CharCodes.Amp) { + this.startEntity(); + } + } + stateSpecialStartSequence(c) { + const isEnd = this.sequenceIndex === this.currentSequence.length; + const isMatch = isEnd + ? // If we are at the end of the sequence, make sure the tag name has ended + isEndOfTagSection(c) + : // Otherwise, do a case-insensitive comparison + (c | 0x20) === this.currentSequence[this.sequenceIndex]; + if (!isMatch) { + this.isSpecial = false; + } + else if (!isEnd) { + this.sequenceIndex++; + return; + } + this.sequenceIndex = 0; + this.state = State.InTagName; + this.stateInTagName(c); + } + /** Look for an end tag. For tags, also decode entities. */ + stateInSpecialTag(c) { + if (this.sequenceIndex === this.currentSequence.length) { + if (c === CharCodes.Gt || isWhitespace$1(c)) { + const endOfText = this.index - this.currentSequence.length; + if (this.sectionStart < endOfText) { + // Spoof the index so that reported locations match up. + const actualIndex = this.index; + this.index = endOfText; + this.cbs.ontext(this.sectionStart, endOfText); + this.index = actualIndex; + } + this.isSpecial = false; + this.sectionStart = endOfText + 2; // Skip over the `</` + this.stateInClosingTagName(c); + return; // We are done; skip the rest of the function. + } + this.sequenceIndex = 0; + } + if ((c | 0x20) === this.currentSequence[this.sequenceIndex]) { + this.sequenceIndex += 1; + } + else if (this.sequenceIndex === 0) { + if (this.currentSequence === Sequences.TitleEnd) { + // We have to parse entities in <title> tags. + if (this.decodeEntities && c === CharCodes.Amp) { + this.startEntity(); + } + } + else if (this.fastForwardTo(CharCodes.Lt)) { + // Outside of <title> tags, we can fast-forward. + this.sequenceIndex = 1; + } + } + else { + // If we see a `<`, set the sequence index to 1; useful for eg. `<</script>`. + this.sequenceIndex = Number(c === CharCodes.Lt); + } + } + stateCDATASequence(c) { + if (c === Sequences.Cdata[this.sequenceIndex]) { + if (++this.sequenceIndex === Sequences.Cdata.length) { + this.state = State.InCommentLike; + this.currentSequence = Sequences.CdataEnd; + this.sequenceIndex = 0; + this.sectionStart = this.index + 1; + } + } + else { + this.sequenceIndex = 0; + this.state = State.InDeclaration; + this.stateInDeclaration(c); // Reconsume the character + } + } + /** + * When we wait for one specific character, we can speed things up + * by skipping through the buffer until we find it. + * + * @returns Whether the character was found. + */ + fastForwardTo(c) { + while (++this.index < this.buffer.length + this.offset) { + if (this.buffer.charCodeAt(this.index - this.offset) === c) { + return true; + } + } + /* + * We increment the index at the end of the `parse` loop, + * so set it to `buffer.length - 1` here. + * + * TODO: Refactor `parse` to increment index before calling states. + */ + this.index = this.buffer.length + this.offset - 1; + return false; + } + /** + * Comments and CDATA end with `-->` and `]]>`. + * + * Their common qualities are: + * - Their end sequences have a distinct character they start with. + * - That character is then repeated, so we have to check multiple repeats. + * - All characters but the start character of the sequence can be skipped. + */ + stateInCommentLike(c) { + if (c === this.currentSequence[this.sequenceIndex]) { + if (++this.sequenceIndex === this.currentSequence.length) { + if (this.currentSequence === Sequences.CdataEnd) { + this.cbs.oncdata(this.sectionStart, this.index, 2); + } + else { + this.cbs.oncomment(this.sectionStart, this.index, 2); + } + this.sequenceIndex = 0; + this.sectionStart = this.index + 1; + this.state = State.Text; + } + } + else if (this.sequenceIndex === 0) { + // Fast-forward to the first character of the sequence + if (this.fastForwardTo(this.currentSequence[0])) { + this.sequenceIndex = 1; + } + } + else if (c !== this.currentSequence[this.sequenceIndex - 1]) { + // Allow long sequences, eg. --->, ]]]> + this.sequenceIndex = 0; + } + } + /** + * HTML only allows ASCII alpha characters (a-z and A-Z) at the beginning of a tag name. + * + * XML allows a lot more characters here (@see https://www.w3.org/TR/REC-xml/#NT-NameStartChar). + * We allow anything that wouldn't end the tag. + */ + isTagStartChar(c) { + return this.xmlMode ? !isEndOfTagSection(c) : isASCIIAlpha(c); + } + startSpecial(sequence, offset) { + this.isSpecial = true; + this.currentSequence = sequence; + this.sequenceIndex = offset; + this.state = State.SpecialStartSequence; + } + stateBeforeTagName(c) { + if (c === CharCodes.ExclamationMark) { + this.state = State.BeforeDeclaration; + this.sectionStart = this.index + 1; + } + else if (c === CharCodes.Questionmark) { + this.state = State.InProcessingInstruction; + this.sectionStart = this.index + 1; + } + else if (this.isTagStartChar(c)) { + const lower = c | 0x20; + this.sectionStart = this.index; + if (this.xmlMode) { + this.state = State.InTagName; + } + else if (lower === Sequences.ScriptEnd[2]) { + this.state = State.BeforeSpecialS; + } + else if (lower === Sequences.TitleEnd[2] || + lower === Sequences.XmpEnd[2]) { + this.state = State.BeforeSpecialT; + } + else { + this.state = State.InTagName; + } + } + else if (c === CharCodes.Slash) { + this.state = State.BeforeClosingTagName; + } + else { + this.state = State.Text; + this.stateText(c); + } + } + stateInTagName(c) { + if (isEndOfTagSection(c)) { + this.cbs.onopentagname(this.sectionStart, this.index); + this.sectionStart = -1; + this.state = State.BeforeAttributeName; + this.stateBeforeAttributeName(c); + } + } + stateBeforeClosingTagName(c) { + if (isWhitespace$1(c)) ; + else if (c === CharCodes.Gt) { + this.state = State.Text; + } + else { + this.state = this.isTagStartChar(c) + ? State.InClosingTagName + : State.InSpecialComment; + this.sectionStart = this.index; + } + } + stateInClosingTagName(c) { + if (c === CharCodes.Gt || isWhitespace$1(c)) { + this.cbs.onclosetag(this.sectionStart, this.index); + this.sectionStart = -1; + this.state = State.AfterClosingTagName; + this.stateAfterClosingTagName(c); + } + } + stateAfterClosingTagName(c) { + // Skip everything until ">" + if (c === CharCodes.Gt || this.fastForwardTo(CharCodes.Gt)) { + this.state = State.Text; + this.sectionStart = this.index + 1; + } + } + stateBeforeAttributeName(c) { + if (c === CharCodes.Gt) { + this.cbs.onopentagend(this.index); + if (this.isSpecial) { + this.state = State.InSpecialTag; + this.sequenceIndex = 0; + } + else { + this.state = State.Text; + } + this.sectionStart = this.index + 1; + } + else if (c === CharCodes.Slash) { + this.state = State.InSelfClosingTag; + } + else if (!isWhitespace$1(c)) { + this.state = State.InAttributeName; + this.sectionStart = this.index; + } + } + stateInSelfClosingTag(c) { + if (c === CharCodes.Gt) { + this.cbs.onselfclosingtag(this.index); + this.state = State.Text; + this.sectionStart = this.index + 1; + this.isSpecial = false; // Reset special state, in case of self-closing special tags + } + else if (!isWhitespace$1(c)) { + this.state = State.BeforeAttributeName; + this.stateBeforeAttributeName(c); + } + } + stateInAttributeName(c) { + if (c === CharCodes.Eq || isEndOfTagSection(c)) { + this.cbs.onattribname(this.sectionStart, this.index); + this.sectionStart = this.index; + this.state = State.AfterAttributeName; + this.stateAfterAttributeName(c); + } + } + stateAfterAttributeName(c) { + if (c === CharCodes.Eq) { + this.state = State.BeforeAttributeValue; + } + else if (c === CharCodes.Slash || c === CharCodes.Gt) { + this.cbs.onattribend(QuoteType.NoValue, this.sectionStart); + this.sectionStart = -1; + this.state = State.BeforeAttributeName; + this.stateBeforeAttributeName(c); + } + else if (!isWhitespace$1(c)) { + this.cbs.onattribend(QuoteType.NoValue, this.sectionStart); + this.state = State.InAttributeName; + this.sectionStart = this.index; + } + } + stateBeforeAttributeValue(c) { + if (c === CharCodes.DoubleQuote) { + this.state = State.InAttributeValueDq; + this.sectionStart = this.index + 1; + } + else if (c === CharCodes.SingleQuote) { + this.state = State.InAttributeValueSq; + this.sectionStart = this.index + 1; + } + else if (!isWhitespace$1(c)) { + this.sectionStart = this.index; + this.state = State.InAttributeValueNq; + this.stateInAttributeValueNoQuotes(c); // Reconsume token + } + } + handleInAttributeValue(c, quote) { + if (c === quote || + (!this.decodeEntities && this.fastForwardTo(quote))) { + this.cbs.onattribdata(this.sectionStart, this.index); + this.sectionStart = -1; + this.cbs.onattribend(quote === CharCodes.DoubleQuote + ? QuoteType.Double + : QuoteType.Single, this.index + 1); + this.state = State.BeforeAttributeName; + } + else if (this.decodeEntities && c === CharCodes.Amp) { + this.startEntity(); + } + } + stateInAttributeValueDoubleQuotes(c) { + this.handleInAttributeValue(c, CharCodes.DoubleQuote); + } + stateInAttributeValueSingleQuotes(c) { + this.handleInAttributeValue(c, CharCodes.SingleQuote); + } + stateInAttributeValueNoQuotes(c) { + if (isWhitespace$1(c) || c === CharCodes.Gt) { + this.cbs.onattribdata(this.sectionStart, this.index); + this.sectionStart = -1; + this.cbs.onattribend(QuoteType.Unquoted, this.index); + this.state = State.BeforeAttributeName; + this.stateBeforeAttributeName(c); + } + else if (this.decodeEntities && c === CharCodes.Amp) { + this.startEntity(); + } + } + stateBeforeDeclaration(c) { + if (c === CharCodes.OpeningSquareBracket) { + this.state = State.CDATASequence; + this.sequenceIndex = 0; + } + else { + this.state = + c === CharCodes.Dash + ? State.BeforeComment + : State.InDeclaration; + } + } + stateInDeclaration(c) { + if (c === CharCodes.Gt || this.fastForwardTo(CharCodes.Gt)) { + this.cbs.ondeclaration(this.sectionStart, this.index); + this.state = State.Text; + this.sectionStart = this.index + 1; + } + } + stateInProcessingInstruction(c) { + if (c === CharCodes.Gt || this.fastForwardTo(CharCodes.Gt)) { + this.cbs.onprocessinginstruction(this.sectionStart, this.index); + this.state = State.Text; + this.sectionStart = this.index + 1; + } + } + stateBeforeComment(c) { + if (c === CharCodes.Dash) { + this.state = State.InCommentLike; + this.currentSequence = Sequences.CommentEnd; + // Allow short comments (eg. <!-->) + this.sequenceIndex = 2; + this.sectionStart = this.index + 1; + } + else { + this.state = State.InDeclaration; + } + } + stateInSpecialComment(c) { + if (c === CharCodes.Gt || this.fastForwardTo(CharCodes.Gt)) { + this.cbs.oncomment(this.sectionStart, this.index, 0); + this.state = State.Text; + this.sectionStart = this.index + 1; + } + } + stateBeforeSpecialS(c) { + const lower = c | 0x20; + if (lower === Sequences.ScriptEnd[3]) { + this.startSpecial(Sequences.ScriptEnd, 4); + } + else if (lower === Sequences.StyleEnd[3]) { + this.startSpecial(Sequences.StyleEnd, 4); + } + else { + this.state = State.InTagName; + this.stateInTagName(c); // Consume the token again + } + } + stateBeforeSpecialT(c) { + const lower = c | 0x20; + switch (lower) { + case Sequences.TitleEnd[3]: { + this.startSpecial(Sequences.TitleEnd, 4); + break; + } + case Sequences.TextareaEnd[3]: { + this.startSpecial(Sequences.TextareaEnd, 4); + break; + } + case Sequences.XmpEnd[3]: { + this.startSpecial(Sequences.XmpEnd, 4); + break; + } + default: { + this.state = State.InTagName; + this.stateInTagName(c); // Consume the token again + } + } + } + startEntity() { + this.baseState = this.state; + this.state = State.InEntity; + this.entityStart = this.index; + this.entityDecoder.startEntity(this.xmlMode + ? DecodingMode.Strict + : this.baseState === State.Text || + this.baseState === State.InSpecialTag + ? DecodingMode.Legacy + : DecodingMode.Attribute); + } + stateInEntity() { + const indexInBuffer = this.index - this.offset; + const length = this.entityDecoder.write(this.buffer, indexInBuffer); + // If `length` is positive, we are done with the entity. + if (length >= 0) { + this.state = this.baseState; + if (length === 0) { + this.index -= 1; + } + } + else { + if (indexInBuffer < this.buffer.length && + this.buffer.charCodeAt(indexInBuffer) === CharCodes.Amp) { + this.state = this.baseState; + this.index -= 1; + return; + } + // Mark buffer as consumed. + this.index = this.offset + this.buffer.length - 1; + } + } + /** + * Remove data that has already been consumed from the buffer. + */ + cleanup() { + // If we are inside of text or attributes, emit what we already have. + if (this.running && this.sectionStart !== this.index) { + if (this.state === State.Text || + (this.state === State.InSpecialTag && this.sequenceIndex === 0)) { + this.cbs.ontext(this.sectionStart, this.index); + this.sectionStart = this.index; + } + else if (this.state === State.InAttributeValueDq || + this.state === State.InAttributeValueSq || + this.state === State.InAttributeValueNq) { + this.cbs.onattribdata(this.sectionStart, this.index); + this.sectionStart = this.index; + } + } + } + shouldContinue() { + return this.index < this.buffer.length + this.offset && this.running; + } + /** + * Iterates through the buffer, calling the function corresponding to the current state. + * + * States that are more likely to be hit are higher up, as a performance improvement. + */ + parse() { + while (this.shouldContinue()) { + const c = this.buffer.charCodeAt(this.index - this.offset); + switch (this.state) { + case State.Text: { + this.stateText(c); + break; + } + case State.SpecialStartSequence: { + this.stateSpecialStartSequence(c); + break; + } + case State.InSpecialTag: { + this.stateInSpecialTag(c); + break; + } + case State.CDATASequence: { + this.stateCDATASequence(c); + break; + } + case State.InAttributeValueDq: { + this.stateInAttributeValueDoubleQuotes(c); + break; + } + case State.InAttributeName: { + this.stateInAttributeName(c); + break; + } + case State.InCommentLike: { + this.stateInCommentLike(c); + break; + } + case State.InSpecialComment: { + this.stateInSpecialComment(c); + break; + } + case State.BeforeAttributeName: { + this.stateBeforeAttributeName(c); + break; + } + case State.InTagName: { + this.stateInTagName(c); + break; + } + case State.InClosingTagName: { + this.stateInClosingTagName(c); + break; + } + case State.BeforeTagName: { + this.stateBeforeTagName(c); + break; + } + case State.AfterAttributeName: { + this.stateAfterAttributeName(c); + break; + } + case State.InAttributeValueSq: { + this.stateInAttributeValueSingleQuotes(c); + break; + } + case State.BeforeAttributeValue: { + this.stateBeforeAttributeValue(c); + break; + } + case State.BeforeClosingTagName: { + this.stateBeforeClosingTagName(c); + break; + } + case State.AfterClosingTagName: { + this.stateAfterClosingTagName(c); + break; + } + case State.BeforeSpecialS: { + this.stateBeforeSpecialS(c); + break; + } + case State.BeforeSpecialT: { + this.stateBeforeSpecialT(c); + break; + } + case State.InAttributeValueNq: { + this.stateInAttributeValueNoQuotes(c); + break; + } + case State.InSelfClosingTag: { + this.stateInSelfClosingTag(c); + break; + } + case State.InDeclaration: { + this.stateInDeclaration(c); + break; + } + case State.BeforeDeclaration: { + this.stateBeforeDeclaration(c); + break; + } + case State.BeforeComment: { + this.stateBeforeComment(c); + break; + } + case State.InProcessingInstruction: { + this.stateInProcessingInstruction(c); + break; + } + case State.InEntity: { + this.stateInEntity(); + break; + } + } + this.index++; + } + this.cleanup(); + } + finish() { + if (this.state === State.InEntity) { + this.entityDecoder.end(); + this.state = this.baseState; + } + this.handleTrailingData(); + this.cbs.onend(); + } + /** Handle any trailing data. */ + handleTrailingData() { + const endIndex = this.buffer.length + this.offset; + // If there is no remaining data, we are done. + if (this.sectionStart >= endIndex) { + return; + } + if (this.state === State.InCommentLike) { + if (this.currentSequence === Sequences.CdataEnd) { + this.cbs.oncdata(this.sectionStart, endIndex, 0); + } + else { + this.cbs.oncomment(this.sectionStart, endIndex, 0); + } + } + else if (this.state === State.InTagName || + this.state === State.BeforeAttributeName || + this.state === State.BeforeAttributeValue || + this.state === State.AfterAttributeName || + this.state === State.InAttributeName || + this.state === State.InAttributeValueSq || + this.state === State.InAttributeValueDq || + this.state === State.InAttributeValueNq || + this.state === State.InClosingTagName) ; + else { + this.cbs.ontext(this.sectionStart, endIndex); + } + } + emitCodePoint(cp, consumed) { + if (this.baseState !== State.Text && + this.baseState !== State.InSpecialTag) { + if (this.sectionStart < this.entityStart) { + this.cbs.onattribdata(this.sectionStart, this.entityStart); + } + this.sectionStart = this.entityStart + consumed; + this.index = this.sectionStart - 1; + this.cbs.onattribentity(cp); + } + else { + if (this.sectionStart < this.entityStart) { + this.cbs.ontext(this.sectionStart, this.entityStart); + } + this.sectionStart = this.entityStart + consumed; + this.index = this.sectionStart - 1; + this.cbs.ontextentity(cp, this.sectionStart); + } + } +} + +const formTags = new Set([ + "input", + "option", + "optgroup", + "select", + "button", + "datalist", + "textarea", +]); +const pTag = new Set(["p"]); +const tableSectionTags = new Set(["thead", "tbody"]); +const ddtTags = new Set(["dd", "dt"]); +const rtpTags = new Set(["rt", "rp"]); +const openImpliesClose = new Map([ + ["tr", new Set(["tr", "th", "td"])], + ["th", new Set(["th"])], + ["td", new Set(["thead", "th", "td"])], + ["body", new Set(["head", "link", "script"])], + ["li", new Set(["li"])], + ["p", pTag], + ["h1", pTag], + ["h2", pTag], + ["h3", pTag], + ["h4", pTag], + ["h5", pTag], + ["h6", pTag], + ["select", formTags], + ["input", formTags], + ["output", formTags], + ["button", formTags], + ["datalist", formTags], + ["textarea", formTags], + ["option", new Set(["option"])], + ["optgroup", new Set(["optgroup", "option"])], + ["dd", ddtTags], + ["dt", ddtTags], + ["address", pTag], + ["article", pTag], + ["aside", pTag], + ["blockquote", pTag], + ["details", pTag], + ["div", pTag], + ["dl", pTag], + ["fieldset", pTag], + ["figcaption", pTag], + ["figure", pTag], + ["footer", pTag], + ["form", pTag], + ["header", pTag], + ["hr", pTag], + ["main", pTag], + ["nav", pTag], + ["ol", pTag], + ["pre", pTag], + ["section", pTag], + ["table", pTag], + ["ul", pTag], + ["rt", rtpTags], + ["rp", rtpTags], + ["tbody", tableSectionTags], + ["tfoot", tableSectionTags], +]); +const voidElements$1 = new Set([ + "area", + "base", + "basefont", + "br", + "col", + "command", + "embed", + "frame", + "hr", + "img", + "input", + "isindex", + "keygen", + "link", + "meta", + "param", + "source", + "track", + "wbr", +]); +const foreignContextElements = new Set(["math", "svg"]); +const htmlIntegrationElements = new Set([ + "mi", + "mo", + "mn", + "ms", + "mtext", + "annotation-xml", + "foreignobject", + "desc", + "title", +]); +const reNameEnd = /\s|\//; +let Parser$1 = class Parser { + constructor(cbs, options = {}) { + var _a, _b, _c, _d, _e, _f; + this.options = options; + /** The start index of the last event. */ + this.startIndex = 0; + /** The end index of the last event. */ + this.endIndex = 0; + /** + * Store the start index of the current open tag, + * so we can update the start index for attributes. + */ + this.openTagStart = 0; + this.tagname = ""; + this.attribname = ""; + this.attribvalue = ""; + this.attribs = null; + this.stack = []; + this.buffers = []; + this.bufferOffset = 0; + /** The index of the last written buffer. Used when resuming after a `pause()`. */ + this.writeIndex = 0; + /** Indicates whether the parser has finished running / `.end` has been called. */ + this.ended = false; + this.cbs = cbs !== null && cbs !== void 0 ? cbs : {}; + this.htmlMode = !this.options.xmlMode; + this.lowerCaseTagNames = (_a = options.lowerCaseTags) !== null && _a !== void 0 ? _a : this.htmlMode; + this.lowerCaseAttributeNames = + (_b = options.lowerCaseAttributeNames) !== null && _b !== void 0 ? _b : this.htmlMode; + this.recognizeSelfClosing = + (_c = options.recognizeSelfClosing) !== null && _c !== void 0 ? _c : !this.htmlMode; + this.tokenizer = new ((_d = options.Tokenizer) !== null && _d !== void 0 ? _d : Tokenizer)(this.options, this); + this.foreignContext = [!this.htmlMode]; + (_f = (_e = this.cbs).onparserinit) === null || _f === void 0 ? void 0 : _f.call(_e, this); + } + // Tokenizer event handlers + /** @internal */ + ontext(start, endIndex) { + var _a, _b; + const data = this.getSlice(start, endIndex); + this.endIndex = endIndex - 1; + (_b = (_a = this.cbs).ontext) === null || _b === void 0 ? void 0 : _b.call(_a, data); + this.startIndex = endIndex; + } + /** @internal */ + ontextentity(cp, endIndex) { + var _a, _b; + this.endIndex = endIndex - 1; + (_b = (_a = this.cbs).ontext) === null || _b === void 0 ? void 0 : _b.call(_a, fromCodePoint(cp)); + this.startIndex = endIndex; + } + /** + * Checks if the current tag is a void element. Override this if you want + * to specify your own additional void elements. + */ + isVoidElement(name) { + return this.htmlMode && voidElements$1.has(name); + } + /** @internal */ + onopentagname(start, endIndex) { + this.endIndex = endIndex; + let name = this.getSlice(start, endIndex); + if (this.lowerCaseTagNames) { + name = name.toLowerCase(); + } + this.emitOpenTag(name); + } + emitOpenTag(name) { + var _a, _b, _c, _d; + this.openTagStart = this.startIndex; + this.tagname = name; + const impliesClose = this.htmlMode && openImpliesClose.get(name); + if (impliesClose) { + while (this.stack.length > 0 && impliesClose.has(this.stack[0])) { + const element = this.stack.shift(); + (_b = (_a = this.cbs).onclosetag) === null || _b === void 0 ? void 0 : _b.call(_a, element, true); + } + } + if (!this.isVoidElement(name)) { + this.stack.unshift(name); + if (this.htmlMode) { + if (foreignContextElements.has(name)) { + this.foreignContext.unshift(true); + } + else if (htmlIntegrationElements.has(name)) { + this.foreignContext.unshift(false); + } + } + } + (_d = (_c = this.cbs).onopentagname) === null || _d === void 0 ? void 0 : _d.call(_c, name); + if (this.cbs.onopentag) + this.attribs = {}; + } + endOpenTag(isImplied) { + var _a, _b; + this.startIndex = this.openTagStart; + if (this.attribs) { + (_b = (_a = this.cbs).onopentag) === null || _b === void 0 ? void 0 : _b.call(_a, this.tagname, this.attribs, isImplied); + this.attribs = null; + } + if (this.cbs.onclosetag && this.isVoidElement(this.tagname)) { + this.cbs.onclosetag(this.tagname, true); + } + this.tagname = ""; + } + /** @internal */ + onopentagend(endIndex) { + this.endIndex = endIndex; + this.endOpenTag(false); + // Set `startIndex` for next node + this.startIndex = endIndex + 1; + } + /** @internal */ + onclosetag(start, endIndex) { + var _a, _b, _c, _d, _e, _f, _g, _h; + this.endIndex = endIndex; + let name = this.getSlice(start, endIndex); + if (this.lowerCaseTagNames) { + name = name.toLowerCase(); + } + if (this.htmlMode && + (foreignContextElements.has(name) || + htmlIntegrationElements.has(name))) { + this.foreignContext.shift(); + } + if (!this.isVoidElement(name)) { + const pos = this.stack.indexOf(name); + if (pos !== -1) { + for (let index = 0; index <= pos; index++) { + const element = this.stack.shift(); + // We know the stack has sufficient elements. + (_b = (_a = this.cbs).onclosetag) === null || _b === void 0 ? void 0 : _b.call(_a, element, index !== pos); + } + } + else if (this.htmlMode && name === "p") { + // Implicit open before close + this.emitOpenTag("p"); + this.closeCurrentTag(true); + } + } + else if (this.htmlMode && name === "br") { + // We can't use `emitOpenTag` for implicit open, as `br` would be implicitly closed. + (_d = (_c = this.cbs).onopentagname) === null || _d === void 0 ? void 0 : _d.call(_c, "br"); + (_f = (_e = this.cbs).onopentag) === null || _f === void 0 ? void 0 : _f.call(_e, "br", {}, true); + (_h = (_g = this.cbs).onclosetag) === null || _h === void 0 ? void 0 : _h.call(_g, "br", false); + } + // Set `startIndex` for next node + this.startIndex = endIndex + 1; + } + /** @internal */ + onselfclosingtag(endIndex) { + this.endIndex = endIndex; + if (this.recognizeSelfClosing || this.foreignContext[0]) { + this.closeCurrentTag(false); + // Set `startIndex` for next node + this.startIndex = endIndex + 1; + } + else { + // Ignore the fact that the tag is self-closing. + this.onopentagend(endIndex); + } + } + closeCurrentTag(isOpenImplied) { + var _a, _b; + const name = this.tagname; + this.endOpenTag(isOpenImplied); + // Self-closing tags will be on the top of the stack + if (this.stack[0] === name) { + // If the opening tag isn't implied, the closing tag has to be implied. + (_b = (_a = this.cbs).onclosetag) === null || _b === void 0 ? void 0 : _b.call(_a, name, !isOpenImplied); + this.stack.shift(); + } + } + /** @internal */ + onattribname(start, endIndex) { + this.startIndex = start; + const name = this.getSlice(start, endIndex); + this.attribname = this.lowerCaseAttributeNames + ? name.toLowerCase() + : name; + } + /** @internal */ + onattribdata(start, endIndex) { + this.attribvalue += this.getSlice(start, endIndex); + } + /** @internal */ + onattribentity(cp) { + this.attribvalue += fromCodePoint(cp); + } + /** @internal */ + onattribend(quote, endIndex) { + var _a, _b; + this.endIndex = endIndex; + (_b = (_a = this.cbs).onattribute) === null || _b === void 0 ? void 0 : _b.call(_a, this.attribname, this.attribvalue, quote === QuoteType.Double + ? '"' + : quote === QuoteType.Single + ? "'" + : quote === QuoteType.NoValue + ? undefined + : null); + if (this.attribs && + !Object.prototype.hasOwnProperty.call(this.attribs, this.attribname)) { + this.attribs[this.attribname] = this.attribvalue; + } + this.attribvalue = ""; + } + getInstructionName(value) { + const index = value.search(reNameEnd); + let name = index < 0 ? value : value.substr(0, index); + if (this.lowerCaseTagNames) { + name = name.toLowerCase(); + } + return name; + } + /** @internal */ + ondeclaration(start, endIndex) { + this.endIndex = endIndex; + const value = this.getSlice(start, endIndex); + if (this.cbs.onprocessinginstruction) { + const name = this.getInstructionName(value); + this.cbs.onprocessinginstruction(`!${name}`, `!${value}`); + } + // Set `startIndex` for next node + this.startIndex = endIndex + 1; + } + /** @internal */ + onprocessinginstruction(start, endIndex) { + this.endIndex = endIndex; + const value = this.getSlice(start, endIndex); + if (this.cbs.onprocessinginstruction) { + const name = this.getInstructionName(value); + this.cbs.onprocessinginstruction(`?${name}`, `?${value}`); + } + // Set `startIndex` for next node + this.startIndex = endIndex + 1; + } + /** @internal */ + oncomment(start, endIndex, offset) { + var _a, _b, _c, _d; + this.endIndex = endIndex; + (_b = (_a = this.cbs).oncomment) === null || _b === void 0 ? void 0 : _b.call(_a, this.getSlice(start, endIndex - offset)); + (_d = (_c = this.cbs).oncommentend) === null || _d === void 0 ? void 0 : _d.call(_c); + // Set `startIndex` for next node + this.startIndex = endIndex + 1; + } + /** @internal */ + oncdata(start, endIndex, offset) { + var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k; + this.endIndex = endIndex; + const value = this.getSlice(start, endIndex - offset); + if (!this.htmlMode || this.options.recognizeCDATA) { + (_b = (_a = this.cbs).oncdatastart) === null || _b === void 0 ? void 0 : _b.call(_a); + (_d = (_c = this.cbs).ontext) === null || _d === void 0 ? void 0 : _d.call(_c, value); + (_f = (_e = this.cbs).oncdataend) === null || _f === void 0 ? void 0 : _f.call(_e); + } + else { + (_h = (_g = this.cbs).oncomment) === null || _h === void 0 ? void 0 : _h.call(_g, `[CDATA[${value}]]`); + (_k = (_j = this.cbs).oncommentend) === null || _k === void 0 ? void 0 : _k.call(_j); + } + // Set `startIndex` for next node + this.startIndex = endIndex + 1; + } + /** @internal */ + onend() { + var _a, _b; + if (this.cbs.onclosetag) { + // Set the end index for all remaining tags + this.endIndex = this.startIndex; + for (let index = 0; index < this.stack.length; index++) { + this.cbs.onclosetag(this.stack[index], true); + } + } + (_b = (_a = this.cbs).onend) === null || _b === void 0 ? void 0 : _b.call(_a); + } + /** + * Resets the parser to a blank state, ready to parse a new HTML document + */ + reset() { + var _a, _b, _c, _d; + (_b = (_a = this.cbs).onreset) === null || _b === void 0 ? void 0 : _b.call(_a); + this.tokenizer.reset(); + this.tagname = ""; + this.attribname = ""; + this.attribs = null; + this.stack.length = 0; + this.startIndex = 0; + this.endIndex = 0; + (_d = (_c = this.cbs).onparserinit) === null || _d === void 0 ? void 0 : _d.call(_c, this); + this.buffers.length = 0; + this.foreignContext.length = 0; + this.foreignContext.unshift(!this.htmlMode); + this.bufferOffset = 0; + this.writeIndex = 0; + this.ended = false; + } + /** + * Resets the parser, then parses a complete document and + * pushes it to the handler. + * + * @param data Document to parse. + */ + parseComplete(data) { + this.reset(); + this.end(data); + } + getSlice(start, end) { + while (start - this.bufferOffset >= this.buffers[0].length) { + this.shiftBuffer(); + } + let slice = this.buffers[0].slice(start - this.bufferOffset, end - this.bufferOffset); + while (end - this.bufferOffset > this.buffers[0].length) { + this.shiftBuffer(); + slice += this.buffers[0].slice(0, end - this.bufferOffset); + } + return slice; + } + shiftBuffer() { + this.bufferOffset += this.buffers[0].length; + this.writeIndex--; + this.buffers.shift(); + } + /** + * Parses a chunk of data and calls the corresponding callbacks. + * + * @param chunk Chunk to parse. + */ + write(chunk) { + var _a, _b; + if (this.ended) { + (_b = (_a = this.cbs).onerror) === null || _b === void 0 ? void 0 : _b.call(_a, new Error(".write() after done!")); + return; + } + this.buffers.push(chunk); + if (this.tokenizer.running) { + this.tokenizer.write(chunk); + this.writeIndex++; + } + } + /** + * Parses the end of the buffer and clears the stack, calls onend. + * + * @param chunk Optional final chunk to parse. + */ + end(chunk) { + var _a, _b; + if (this.ended) { + (_b = (_a = this.cbs).onerror) === null || _b === void 0 ? void 0 : _b.call(_a, new Error(".end() after done!")); + return; + } + if (chunk) + this.write(chunk); + this.ended = true; + this.tokenizer.end(); + } + /** + * Pauses parsing. The parser won't emit events until `resume` is called. + */ + pause() { + this.tokenizer.pause(); + } + /** + * Resumes parsing after `pause` was called. + */ + resume() { + this.tokenizer.resume(); + while (this.tokenizer.running && + this.writeIndex < this.buffers.length) { + this.tokenizer.write(this.buffers[this.writeIndex++]); + } + if (this.ended) + this.tokenizer.end(); + } + /** + * Alias of `write`, for backwards compatibility. + * + * @param chunk Chunk to parse. + * @deprecated + */ + parseChunk(chunk) { + this.write(chunk); + } + /** + * Alias of `end`, for backwards compatibility. + * + * @param chunk Optional final chunk to parse. + * @deprecated + */ + done(chunk) { + this.end(chunk); + } +}; + +var HTMLParser2 = /*#__PURE__*/Object.freeze({ + __proto__: null, + Parser: Parser$1, + get QuoteType () { return QuoteType; }, + Tokenizer: Tokenizer +}); + +// Internal +const NODE_END = -1; + +// Node +const ELEMENT_NODE = 1; +const ATTRIBUTE_NODE = 2; +const TEXT_NODE = 3; +const CDATA_SECTION_NODE = 4; +const COMMENT_NODE = 8; +const DOCUMENT_NODE = 9; +const DOCUMENT_TYPE_NODE = 10; +const DOCUMENT_FRAGMENT_NODE = 11; + +// Elements +const BLOCK_ELEMENTS = new Set(['ARTICLE', 'ASIDE', 'BLOCKQUOTE', 'BODY', 'BR', 'BUTTON', 'CANVAS', 'CAPTION', 'COL', 'COLGROUP', 'DD', 'DIV', 'DL', 'DT', 'EMBED', 'FIELDSET', 'FIGCAPTION', 'FIGURE', 'FOOTER', 'FORM', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'LI', 'UL', 'OL', 'P']); + +// TreeWalker +const SHOW_ALL = -1; +const SHOW_ELEMENT = 1; +const SHOW_TEXT = 4; +const SHOW_CDATA_SECTION = 8; +const SHOW_COMMENT = 128; + +// Document position +const DOCUMENT_POSITION_DISCONNECTED = 0x01; +const DOCUMENT_POSITION_PRECEDING = 0x02; +const DOCUMENT_POSITION_FOLLOWING = 0x04; +const DOCUMENT_POSITION_CONTAINS = 0x08; +const DOCUMENT_POSITION_CONTAINED_BY = 0x10; +const DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC = 0x20; + +// SVG +const SVG_NAMESPACE = 'http://www.w3.org/2000/svg'; + +const { + assign, + create: create$1, + defineProperties, + entries, + keys, + setPrototypeOf +} = Object; + +const $String = String; + +const getEnd = node => node.nodeType === ELEMENT_NODE ? node[END] : node; + +const ignoreCase = ({ownerDocument}) => ownerDocument[MIME].ignoreCase; + +const knownAdjacent = (prev, next) => { + prev[NEXT] = next; + next[PREV] = prev; +}; + +const knownBoundaries = (prev, current, next) => { + knownAdjacent(prev, current); + knownAdjacent(getEnd(current), next); +}; + +const knownSegment = (prev, start, end, next) => { + knownAdjacent(prev, start); + knownAdjacent(getEnd(end), next); +}; + +const knownSiblings = (prev, current, next) => { + knownAdjacent(prev, current); + knownAdjacent(current, next); +}; + +const localCase = ({localName, ownerDocument}) => { + return ownerDocument[MIME].ignoreCase ? localName.toUpperCase() : localName; +}; + +const setAdjacent = (prev, next) => { + if (prev) + prev[NEXT] = next; + if (next) + next[PREV] = prev; +}; + +/** + * @param {import("../interface/document.js").Document} ownerDocument + * @param {string} html + * @return {import("../interface/document-fragment.js").DocumentFragment} + */ +const htmlToFragment = (ownerDocument, html) => { + const fragment = ownerDocument.createDocumentFragment(); + + const elem = ownerDocument.createElement(''); + elem.innerHTML = html; + const { firstChild, lastChild } = elem; + + if (firstChild) { + knownSegment(fragment, firstChild, lastChild, fragment[END]); + + let child = firstChild; + do { + child.parentNode = fragment; + } while (child !== lastChild && (child = getEnd(child)[NEXT])); + } + + return fragment; +}; + +const shadowRoots = new WeakMap; + +let reactive = false; + +const Classes = new WeakMap; + +const customElements = new WeakMap; + +const attributeChangedCallback$1 = (element, attributeName, oldValue, newValue) => { + if ( + reactive && + customElements.has(element) && + element.attributeChangedCallback && + element.constructor.observedAttributes.includes(attributeName) + ) { + element.attributeChangedCallback(attributeName, oldValue, newValue); + } +}; + +const createTrigger = (method, isConnected) => element => { + if (customElements.has(element)) { + const info = customElements.get(element); + if (info.connected !== isConnected && element.isConnected === isConnected) { + info.connected = isConnected; + if (method in element) + element[method](); + } + } +}; + +const triggerConnected = createTrigger('connectedCallback', true); +const connectedCallback = element => { + if (reactive) { + triggerConnected(element); + if (shadowRoots.has(element)) + element = shadowRoots.get(element).shadowRoot; + let {[NEXT]: next, [END]: end} = element; + while (next !== end) { + if (next.nodeType === ELEMENT_NODE) + triggerConnected(next); + next = next[NEXT]; + } + } +}; + +const triggerDisconnected = createTrigger('disconnectedCallback', false); +const disconnectedCallback = element => { + if (reactive) { + triggerDisconnected(element); + if (shadowRoots.has(element)) + element = shadowRoots.get(element).shadowRoot; + let {[NEXT]: next, [END]: end} = element; + while (next !== end) { + if (next.nodeType === ELEMENT_NODE) + triggerDisconnected(next); + next = next[NEXT]; + } + } +}; + +/** + * @implements globalThis.CustomElementRegistry + */ +class CustomElementRegistry { + + /** + * @param {Document} ownerDocument + */ + constructor(ownerDocument) { + /** + * @private + */ + this.ownerDocument = ownerDocument; + + /** + * @private + */ + this.registry = new Map; + + /** + * @private + */ + this.waiting = new Map; + + /** + * @private + */ + this.active = false; + } + + /** + * @param {string} localName the custom element definition name + * @param {Function} Class the custom element **Class** definition + * @param {object?} options the optional object with an `extends` property + */ + define(localName, Class, options = {}) { + const {ownerDocument, registry, waiting} = this; + + if (registry.has(localName)) + throw new Error('unable to redefine ' + localName); + + if (Classes.has(Class)) + throw new Error('unable to redefine the same class: ' + Class); + + this.active = (reactive = true); + + const {extends: extend} = options; + + Classes.set(Class, { + ownerDocument, + options: {is: extend ? localName : ''}, + localName: extend || localName + }); + + const check = extend ? + element => { + return element.localName === extend && + element.getAttribute('is') === localName; + } : + element => element.localName === localName; + registry.set(localName, {Class, check}); + if (waiting.has(localName)) { + for (const resolve of waiting.get(localName)) + resolve(Class); + waiting.delete(localName); + } + ownerDocument.querySelectorAll( + extend ? `${extend}[is="${localName}"]` : localName + ).forEach(this.upgrade, this); + } + + /** + * @param {Element} element + */ + upgrade(element) { + if (customElements.has(element)) + return; + const {ownerDocument, registry} = this; + const ce = element.getAttribute('is') || element.localName; + if (registry.has(ce)) { + const {Class, check} = registry.get(ce); + if (check(element)) { + const {attributes, isConnected} = element; + for (const attr of attributes) + element.removeAttributeNode(attr); + + const values = entries(element); + for (const [key] of values) + delete element[key]; + + setPrototypeOf(element, Class.prototype); + ownerDocument[UPGRADE] = {element, values}; + new Class(ownerDocument, ce); + + customElements.set(element, {connected: isConnected}); + + for (const attr of attributes) + element.setAttributeNode(attr); + + if (isConnected && element.connectedCallback) + element.connectedCallback(); + } + } + } + + /** + * @param {string} localName the custom element definition name + */ + whenDefined(localName) { + const {registry, waiting} = this; + return new Promise(resolve => { + if (registry.has(localName)) + resolve(registry.get(localName).Class); + else { + if (!waiting.has(localName)) + waiting.set(localName, []); + waiting.get(localName).push(resolve); + } + }); + } + + /** + * @param {string} localName the custom element definition name + * @returns {Function?} the custom element **Class**, if any + */ + get(localName) { + const info = this.registry.get(localName); + return info && info.Class; + } + + /** + * @param {Function} Class **Class** of custom element + * @returns {string?} found tag name or null + */ + getName(Class) { + if (Classes.has(Class)) { + const { localName } = Classes.get(Class); + return localName; + } + return null; + } +} + +const {Parser} = HTMLParser2; + +const append$2 = (self, node, active) => { + const end = self[END]; + node.parentNode = self; + knownBoundaries(end[PREV], node, end); + if (active && node.nodeType === ELEMENT_NODE) + connectedCallback(node); + return node; +}; + +const attribute = (element, end, attribute, value, active) => { + attribute[VALUE] = value; + attribute.ownerElement = element; + knownSiblings(end[PREV], attribute, end); + if (attribute.name === 'class') + element.className = value; + if (active) + attributeChangedCallback$1(element, attribute.name, null, value); +}; + +const parseFromString = (document, isHTML, markupLanguage) => { + const {active, registry} = document[CUSTOM_ELEMENTS]; + + let node = document; + let ownerSVGElement = null; + let parsingCData = false; + + const content = new Parser({ + // <!DOCTYPE ...> + onprocessinginstruction(name, data) { + if (name.toLowerCase() === '!doctype') + document.doctype = data.slice(name.length).trim(); + }, + + // <tagName> + onopentag(name, attributes) { + let create = true; + if (isHTML) { + if (ownerSVGElement) { + node = append$2(node, document.createElementNS(SVG_NAMESPACE, name), active); + node.ownerSVGElement = ownerSVGElement; + create = false; + } + else if (name === 'svg' || name === 'SVG') { + ownerSVGElement = document.createElementNS(SVG_NAMESPACE, name); + node = append$2(node, ownerSVGElement, active); + create = false; + } + else if (active) { + const ce = name.includes('-') ? name : (attributes.is || ''); + if (ce && registry.has(ce)) { + const {Class} = registry.get(ce); + node = append$2(node, new Class, active); + delete attributes.is; + create = false; + } + } + } + + if (create) + node = append$2(node, document.createElement(name), false); + + let end = node[END]; + for (const name of keys(attributes)) + attribute(node, end, document.createAttribute(name), attributes[name], active); + }, + + // #text, #comment + oncomment(data) { append$2(node, document.createComment(data), active); }, + ontext(text) { + if (parsingCData) { + append$2(node, document.createCDATASection(text), active); + } else { + append$2(node, document.createTextNode(text), active); + } + }, + + // #cdata + oncdatastart() { parsingCData = true; }, + oncdataend() { parsingCData = false; }, + + // </tagName> + onclosetag() { + if (isHTML && node === ownerSVGElement) + ownerSVGElement = null; + node = node.parentNode; + } + }, { + lowerCaseAttributeNames: false, + decodeEntities: true, + xmlMode: !isHTML + }); + + content.write(markupLanguage); + content.end(); + + return document; +}; + +const htmlClasses = new Map; + +const registerHTMLClass = (names, Class) => { + for (const name of [].concat(names)) { + htmlClasses.set(name, Class); + htmlClasses.set(name.toUpperCase(), Class); + } +}; + +const loopSegment = ({[NEXT]: next, [END]: end}, json) => { + while (next !== end) { + switch (next.nodeType) { + case ATTRIBUTE_NODE: + attrAsJSON(next, json); + break; + case TEXT_NODE: + case COMMENT_NODE: + case CDATA_SECTION_NODE: + characterDataAsJSON(next, json); + break; + case ELEMENT_NODE: + elementAsJSON(next, json); + next = getEnd(next); + break; + case DOCUMENT_TYPE_NODE: + documentTypeAsJSON(next, json); + break; + } + next = next[NEXT]; + } + const last = json.length - 1; + const value = json[last]; + if (typeof value === 'number' && value < 0) + json[last] += NODE_END; + else + json.push(NODE_END); +}; + +const attrAsJSON = (attr, json) => { + json.push(ATTRIBUTE_NODE, attr.name); + const value = attr[VALUE].trim(); + if (value) + json.push(value); +}; + +const characterDataAsJSON = (node, json) => { + const value = node[VALUE]; + if (value.trim()) + json.push(node.nodeType, value); +}; + +const nonElementAsJSON = (node, json) => { + json.push(node.nodeType); + loopSegment(node, json); +}; + +const documentTypeAsJSON = ({name, publicId, systemId}, json) => { + json.push(DOCUMENT_TYPE_NODE, name); + if (publicId) + json.push(publicId); + if (systemId) + json.push(systemId); +}; + +const elementAsJSON = (element, json) => { + json.push(ELEMENT_NODE, element.localName); + loopSegment(element, json); +}; + +const createRecord = + (type, target, element, addedNodes, removedNodes, attributeName, oldValue) => + ({ + type, + target, + addedNodes, + removedNodes, + attributeName, + oldValue, + previousSibling: element?.previousSibling || null, + nextSibling: element?.nextSibling || null, +}); + +const queueAttribute = ( + observer, target, attributeName, attributeFilter, attributeOldValue, oldValue +) => { + if ((!attributeFilter || attributeFilter.includes(attributeName))) { + const {callback, records, scheduled} = observer; + records.push(createRecord( + 'attributes', target, null, + [], [], + attributeName, attributeOldValue ? oldValue : void 0 + )); + if (!scheduled) { + observer.scheduled = true; + Promise.resolve().then(() => { + observer.scheduled = false; + callback(records.splice(0), observer); + }); + } + } +}; + +const attributeChangedCallback = (element, attributeName, oldValue) => { + const {ownerDocument} = element; + const {active, observers} = ownerDocument[MUTATION_OBSERVER]; + if (active) { + for (const observer of observers) { + for (const [ + target, + { + childList, + subtree, + attributes, + attributeFilter, + attributeOldValue + } + ] of observer.nodes) { + if (childList) { + if ( + (subtree && (target === ownerDocument || target.contains(element))) || + (!subtree && target.children.includes(element)) + ) { + queueAttribute( + observer, element, + attributeName, attributeFilter, attributeOldValue, oldValue + ); + break; + } + } + else if ( + attributes && + target === element + ) { + queueAttribute( + observer, element, + attributeName, attributeFilter, attributeOldValue, oldValue + ); + break; + } + } + } + } +}; + +const moCallback = (element, parentNode) => { + const {ownerDocument} = element; + const {active, observers} = ownerDocument[MUTATION_OBSERVER]; + if (active) { + for (const observer of observers) { + for (const [target, {subtree, childList, characterData}] of observer.nodes) { + if (childList) { + if ( + (parentNode && (target === parentNode || /* c8 ignore next */(subtree && target.contains(parentNode)))) || + (!parentNode && ((subtree && (target === ownerDocument || /* c8 ignore next */target.contains(element))) || + (!subtree && target[characterData ? 'childNodes' : 'children'].includes(element)))) + ) { + const {callback, records, scheduled} = observer; + records.push(createRecord( + 'childList', target, element, + parentNode ? [] : [element], parentNode ? [element] : [] + )); + if (!scheduled) { + observer.scheduled = true; + Promise.resolve().then(() => { + observer.scheduled = false; + callback(records.splice(0), observer); + }); + } + break; + } + } + } + } + } +}; + +class MutationObserverClass { + constructor(ownerDocument) { + const observers = new Set; + this.observers = observers; + this.active = false; + + /** + * @implements globalThis.MutationObserver + */ + this.class = class MutationObserver { + + constructor(callback) { + /** + * @private + */ + this.callback = callback; + + /** + * @private + */ + this.nodes = new Map; + + /** + * @private + */ + this.records = []; + + /** + * @private + */ + this.scheduled = false; + } + + disconnect() { + this.records.splice(0); + this.nodes.clear(); + observers.delete(this); + ownerDocument[MUTATION_OBSERVER].active = !!observers.size; + } + + /** + * @param {Element} target + * @param {MutationObserverInit} options + */ + observe(target, options = { + subtree: false, + childList: false, + attributes: false, + attributeFilter: null, + attributeOldValue: false, + characterData: false, + // TODO: not implemented yet + // characterDataOldValue: false + }) { + if (('attributeOldValue' in options) || ('attributeFilter' in options)) + options.attributes = true; + // if ('characterDataOldValue' in options) + // options.characterData = true; + options.childList = !!options.childList; + options.subtree = !!options.subtree; + this.nodes.set(target, options); + observers.add(this); + ownerDocument[MUTATION_OBSERVER].active = true; + } + + /** + * @returns {MutationRecord[]} + */ + takeRecords() { return this.records.splice(0); } + }; + } +} + +const emptyAttributes = new Set([ + 'allowfullscreen', + 'allowpaymentrequest', + 'async', + 'autofocus', + 'autoplay', + 'checked', + 'class', + 'contenteditable', + 'controls', + 'default', + 'defer', + 'disabled', + 'draggable', + 'formnovalidate', + 'hidden', + 'id', + 'ismap', + 'itemscope', + 'loop', + 'multiple', + 'muted', + 'nomodule', + 'novalidate', + 'open', + 'playsinline', + 'readonly', + 'required', + 'reversed', + 'selected', + 'style', + 'truespeed' +]); + +const setAttribute = (element, attribute) => { + const {[VALUE]: value, name} = attribute; + attribute.ownerElement = element; + knownSiblings(element, attribute, element[NEXT]); + if (name === 'class') + element.className = value; + attributeChangedCallback(element, name, null); + attributeChangedCallback$1(element, name, null, value); +}; + +const removeAttribute = (element, attribute) => { + const {[VALUE]: value, name} = attribute; + knownAdjacent(attribute[PREV], attribute[NEXT]); + attribute.ownerElement = attribute[PREV] = attribute[NEXT] = null; + if (name === 'class') + element[CLASS_LIST] = null; + attributeChangedCallback(element, name, value); + attributeChangedCallback$1(element, name, value, null); +}; + +const booleanAttribute = { + get(element, name) { + return element.hasAttribute(name); + }, + set(element, name, value) { + if (value) + element.setAttribute(name, ''); + else + element.removeAttribute(name); + } +}; + +const numericAttribute = { + get(element, name) { + return parseFloat(element.getAttribute(name) || 0); + }, + set(element, name, value) { + element.setAttribute(name, value); + } +}; + +const stringAttribute = { + get(element, name) { + return element.getAttribute(name) || ''; + }, + set(element, name, value) { + element.setAttribute(name, value); + } +}; + +/* oddly enough, this apparently is not a thing +const nullableAttribute = { + get(element, name) { + return element.getAttribute(name); + }, + set(element, name, value) { + if (value === null) + element.removeAttribute(name); + else + element.setAttribute(name, value); + } +}; +*/ + +// https://dom.spec.whatwg.org/#interface-eventtarget + +const wm = new WeakMap(); + +function dispatch(event, listener) { + if (typeof listener === 'function') + listener.call(event.target, event); + else + listener.handleEvent(event); + return event._stopImmediatePropagationFlag; +} + +function invokeListeners({currentTarget, target}) { + const map = wm.get(currentTarget); + if (map && map.has(this.type)) { + const listeners = map.get(this.type); + if (currentTarget === target) { + this.eventPhase = this.AT_TARGET; + } else { + this.eventPhase = this.BUBBLING_PHASE; + } + + this.currentTarget = currentTarget; + this.target = target; + for (const [listener, options] of listeners) { + if (options && options.once) + listeners.delete(listener); + if (dispatch(this, listener)) + break; + } + delete this.currentTarget; + delete this.target; + return this.cancelBubble; + } +} + + +/** + * @implements globalThis.EventTarget + */ +class DOMEventTarget { + + constructor() { + wm.set(this, new Map); + } + + /** + * @protected + */ + _getParent() { + return null; + } + + addEventListener(type, listener, options) { + const map = wm.get(this); + if (!map.has(type)) + map.set(type, new Map); + map.get(type).set(listener, options); + } + + removeEventListener(type, listener) { + const map = wm.get(this); + if (map.has(type)) { + const listeners = map.get(type); + if (listeners.delete(listener) && !listeners.size) + map.delete(type); + } + } + + dispatchEvent(event) { + let node = this; + event.eventPhase = event.CAPTURING_PHASE; + + // intentionally simplified, specs imply way more code: https://dom.spec.whatwg.org/#event-path + while (node) { + if (node.dispatchEvent) + event._path.push({currentTarget: node, target: this}); + node = event.bubbles && node._getParent && node._getParent(); + } + event._path.some(invokeListeners, event); + event._path = []; + event.eventPhase = event.NONE; + return !event.defaultPrevented; + } + +} + +// https://dom.spec.whatwg.org/#interface-nodelist + +/** + * @implements globalThis.NodeList + */ +class NodeList extends Array { + item(i) { return i < this.length ? this[i] : null; } +} + +// https://dom.spec.whatwg.org/#node + + +const getParentNodeCount = ({parentNode}) => { + let count = 0; + while (parentNode) { + count++; + parentNode = parentNode.parentNode; + } + return count; +}; + +/** + * @implements globalThis.Node + */ +let Node$1 = class Node extends DOMEventTarget { + + static get ELEMENT_NODE() { return ELEMENT_NODE; } + static get ATTRIBUTE_NODE() { return ATTRIBUTE_NODE; } + static get TEXT_NODE() { return TEXT_NODE; } + static get CDATA_SECTION_NODE() { return CDATA_SECTION_NODE; } + static get COMMENT_NODE() { return COMMENT_NODE; } + static get DOCUMENT_NODE() { return DOCUMENT_NODE; } + static get DOCUMENT_FRAGMENT_NODE() { return DOCUMENT_FRAGMENT_NODE; } + static get DOCUMENT_TYPE_NODE() { return DOCUMENT_TYPE_NODE; } + + constructor(ownerDocument, localName, nodeType) { + super(); + this.ownerDocument = ownerDocument; + this.localName = localName; + this.nodeType = nodeType; + this.parentNode = null; + this[NEXT] = null; + this[PREV] = null; + } + + get ELEMENT_NODE() { return ELEMENT_NODE; } + get ATTRIBUTE_NODE() { return ATTRIBUTE_NODE; } + get TEXT_NODE() { return TEXT_NODE; } + get CDATA_SECTION_NODE() { return CDATA_SECTION_NODE; } + get COMMENT_NODE() { return COMMENT_NODE; } + get DOCUMENT_NODE() { return DOCUMENT_NODE; } + get DOCUMENT_FRAGMENT_NODE() { return DOCUMENT_FRAGMENT_NODE; } + get DOCUMENT_TYPE_NODE() { return DOCUMENT_TYPE_NODE; } + + get baseURI() { + const ownerDocument = this.nodeType === DOCUMENT_NODE ? + this : this.ownerDocument; + if (ownerDocument) { + const base = ownerDocument.querySelector('base'); + if (base) + return base.getAttribute('href'); + + const {location} = ownerDocument.defaultView; + if (location) + return location.href; + } + + return null; + } + + /* c8 ignore start */ + // mixin: node + get isConnected() { return false; } + get nodeName() { return this.localName; } + get parentElement() { return null; } + get previousSibling() { return null; } + get previousElementSibling() { return null; } + get nextSibling() { return null; } + get nextElementSibling() { return null; } + get childNodes() { return new NodeList; } + get firstChild() { return null; } + get lastChild() { return null; } + + // default values + get nodeValue() { return null; } + set nodeValue(value) {} + get textContent() { return null; } + set textContent(value) {} + normalize() {} + cloneNode() { return null; } + contains() { return false; } + /** + * Inserts a node before a reference node as a child of this parent node. + * @param {Node} newNode The node to be inserted. + * @param {Node} referenceNode The node before which newNode is inserted. If this is null, then newNode is inserted at the end of node's child nodes. + * @returns The added child + */ + // eslint-disable-next-line no-unused-vars + insertBefore(newNode, referenceNode) { return newNode } + /** + * Adds a node to the end of the list of children of this node. + * @param {Node} child The node to append to the given parent node. + * @returns The appended child. + */ + appendChild(child) { return child } + /** + * Replaces a child node within this node + * @param {Node} newChild The new node to replace oldChild. + * @param {Node} oldChild The child to be replaced. + * @returns The replaced Node. This is the same node as oldChild. + */ + replaceChild(newChild, oldChild) { return oldChild } + /** + * Removes a child node from the DOM. + * @param {Node} child A Node that is the child node to be removed from the DOM. + * @returns The removed node. + */ + removeChild(child) { return child } + toString() { return ''; } + /* c8 ignore stop */ + + hasChildNodes() { return !!this.lastChild; } + isSameNode(node) { return this === node; } + + // TODO: attributes? + compareDocumentPosition(target) { + let result = 0; + if (this !== target) { + let self = getParentNodeCount(this); + let other = getParentNodeCount(target); + if (self < other) { + result += DOCUMENT_POSITION_FOLLOWING; + if (this.contains(target)) + result += DOCUMENT_POSITION_CONTAINED_BY; + } + else if (other < self) { + result += DOCUMENT_POSITION_PRECEDING; + if (target.contains(this)) + result += DOCUMENT_POSITION_CONTAINS; + } + else if (self && other) { + const {childNodes} = this.parentNode; + if (childNodes.indexOf(this) < childNodes.indexOf(target)) + result += DOCUMENT_POSITION_FOLLOWING; + else + result += DOCUMENT_POSITION_PRECEDING; + } + if (!self || !other) { + result += DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC; + result += DOCUMENT_POSITION_DISCONNECTED; + } + } + return result; + } + + isEqualNode(node) { + if (this === node) + return true; + if (this.nodeType === node.nodeType) { + switch (this.nodeType) { + case DOCUMENT_NODE: + case DOCUMENT_FRAGMENT_NODE: { + const aNodes = this.childNodes; + const bNodes = node.childNodes; + return aNodes.length === bNodes.length && aNodes.every((node, i) => node.isEqualNode(bNodes[i])); + } + } + return this.toString() === node.toString(); + } + return false; + } + + /** + * @protected + */ + _getParent() { + return this.parentNode; + } + + /** + * Calling it on an element inside a standard web page will return an HTMLDocument object representing the entire page (or <iframe>). + * Calling it on an element inside a shadow DOM will return the associated ShadowRoot. + * @return {ShadowRoot | HTMLDocument} + */ + getRootNode() { + let root = this; + while (root.parentNode) + root = root.parentNode; + return root; + } +}; + +const {replace} = ''; + +// escape +const ca = /[<>&\xA0]/g; + +const esca = { + '\xA0': ' ', + '&': '&', + '<': '<', + '>': '>' +}; + +const pe = m => esca[m]; + +/** + * Safely escape HTML entities such as `&`, `<`, `>` only. + * @param {string} es the input to safely escape + * @returns {string} the escaped input, and it **throws** an error if + * the input type is unexpected, except for boolean and numbers, + * converted as string. + */ +const escape = es => replace.call(es, ca, pe); + +const QUOTE = /"/g; + +/** + * @implements globalThis.Attr + */ +let Attr$1 = class Attr extends Node$1 { + constructor(ownerDocument, name, value = '') { + super(ownerDocument, name, ATTRIBUTE_NODE); + this.ownerElement = null; + this.name = $String(name); + this[VALUE] = $String(value); + this[CHANGED] = false; + } + + get value() { return this[VALUE]; } + set value(newValue) { + const {[VALUE]: oldValue, name, ownerElement} = this; + this[VALUE] = $String(newValue); + this[CHANGED] = true; + if (ownerElement) { + attributeChangedCallback(ownerElement, name, oldValue); + attributeChangedCallback$1(ownerElement, name, oldValue, this[VALUE]); + } + } + + cloneNode() { + const {ownerDocument, name, [VALUE]: value} = this; + return new Attr(ownerDocument, name, value); + } + + toString() { + const {name, [VALUE]: value} = this; + if (emptyAttributes.has(name) && !value) { + return ignoreCase(this) ? name : `${name}=""`; + } + const escapedValue = (ignoreCase(this) ? value : escape(value)).replace(QUOTE, '"'); + return `${name}="${escapedValue}"`; + } + + toJSON() { + const json = []; + attrAsJSON(this, json); + return json; + } +}; + +const isConnected = ({ownerDocument, parentNode}) => { + while (parentNode) { + if (parentNode === ownerDocument) + return true; + parentNode = parentNode.parentNode || parentNode.host; + } + return false; +}; + +const parentElement = ({parentNode}) => { + if (parentNode) { + switch (parentNode.nodeType) { + case DOCUMENT_NODE: + case DOCUMENT_FRAGMENT_NODE: + return null; + } + } + return parentNode; +}; + +const previousSibling = ({[PREV]: prev}) => { + switch (prev ? prev.nodeType : 0) { + case NODE_END: + return prev[START]; + case TEXT_NODE: + case COMMENT_NODE: + case CDATA_SECTION_NODE: + return prev; + } + return null; +}; + +const nextSibling = node => { + const next = getEnd(node)[NEXT]; + return next && (next.nodeType === NODE_END ? null : next); +}; + +// https://dom.spec.whatwg.org/#nondocumenttypechildnode +// CharacterData, Element + + +const nextElementSibling$1 = node => { + let next = nextSibling(node); + while (next && next.nodeType !== ELEMENT_NODE) + next = nextSibling(next); + return next; +}; + +const previousElementSibling = node => { + let prev = previousSibling(node); + while (prev && prev.nodeType !== ELEMENT_NODE) + prev = previousSibling(prev); + return prev; +}; + +// https://dom.spec.whatwg.org/#childnode +// CharacterData, DocumentType, Element + + +const asFragment = (ownerDocument, nodes) => { + const fragment = ownerDocument.createDocumentFragment(); + fragment.append(...nodes); + return fragment; +}; + +const before = (node, nodes) => { + const {ownerDocument, parentNode} = node; + if (parentNode) + parentNode.insertBefore( + asFragment(ownerDocument, nodes), + node + ); +}; + +const after = (node, nodes) => { + const {ownerDocument, parentNode} = node; + if (parentNode) + parentNode.insertBefore( + asFragment(ownerDocument, nodes), + getEnd(node)[NEXT] + ); +}; + +const replaceWith = (node, nodes) => { + const {ownerDocument, parentNode} = node; + if (parentNode) { + if (nodes.includes(node)) + replaceWith(node, [node = node.cloneNode()]); + parentNode.insertBefore( + asFragment(ownerDocument, nodes), + node + ); + node.remove(); + } +}; + +const remove = (prev, current, next) => { + const {parentNode, nodeType} = current; + if (prev || next) { + setAdjacent(prev, next); + current[PREV] = null; + getEnd(current)[NEXT] = null; + } + if (parentNode) { + current.parentNode = null; + moCallback(current, parentNode); + if (nodeType === ELEMENT_NODE) + disconnectedCallback(current); + } +}; + +// https://dom.spec.whatwg.org/#interface-characterdata + + +/** + * @implements globalThis.CharacterData + */ +let CharacterData$1 = class CharacterData extends Node$1 { + + constructor(ownerDocument, localName, nodeType, data) { + super(ownerDocument, localName, nodeType); + this[VALUE] = $String(data); + } + + // <Mixins> + get isConnected() { return isConnected(this); } + get parentElement() { return parentElement(this); } + get previousSibling() { return previousSibling(this); } + get nextSibling() { return nextSibling(this); } + + get previousElementSibling() { return previousElementSibling(this); } + get nextElementSibling() { return nextElementSibling$1(this); } + + before(...nodes) { before(this, nodes); } + after(...nodes) { after(this, nodes); } + replaceWith(...nodes) { replaceWith(this, nodes); } + remove() { remove(this[PREV], this, this[NEXT]); } + // </Mixins> + + // CharacterData only + /* c8 ignore start */ + get data() { return this[VALUE]; } + set data(value) { + this[VALUE] = $String(value); + moCallback(this, this.parentNode); + } + + get nodeValue() { return this.data; } + set nodeValue(value) { this.data = value; } + + get textContent() { return this.data; } + set textContent(value) { this.data = value; } + + get length() { return this.data.length; } + + substringData(offset, count) { + return this.data.substr(offset, count); + } + + appendData(data) { + this.data += data; + } + + insertData(offset, data) { + const {data: t} = this; + this.data = t.slice(0, offset) + data + t.slice(offset); + } + + deleteData(offset, count) { + const {data: t} = this; + this.data = t.slice(0, offset) + t.slice(offset + count); + } + + replaceData(offset, count, data) { + const {data: t} = this; + this.data = t.slice(0, offset) + data + t.slice(offset + count); + } + /* c8 ignore stop */ + + toJSON() { + const json = []; + characterDataAsJSON(this, json); + return json; + } +}; + +/** + * @implements globalThis.CDATASection + */ +let CDATASection$1 = class CDATASection extends CharacterData$1 { + constructor(ownerDocument, data = '') { + super(ownerDocument, '#cdatasection', CDATA_SECTION_NODE, data); + } + + cloneNode() { + const {ownerDocument, [VALUE]: data} = this; + return new CDATASection(ownerDocument, data); + } + + toString() { return `<![CDATA[${this[VALUE]}]]>`; } +}; + +/** + * @implements globalThis.Comment + */ +let Comment$2 = class Comment extends CharacterData$1 { + constructor(ownerDocument, data = '') { + super(ownerDocument, '#comment', COMMENT_NODE, data); + } + + cloneNode() { + const {ownerDocument, [VALUE]: data} = this; + return new Comment(ownerDocument, data); + } + + toString() { return `<!--${this[VALUE]}-->`; } +}; + +function trueFunc() { + return true; +} +function falseFunc() { + return false; +} + +/** Discriminants for selector token kinds. */ +var SelectorType; +(function (SelectorType) { + SelectorType["Attribute"] = "attribute"; + SelectorType["Pseudo"] = "pseudo"; + SelectorType["PseudoElement"] = "pseudo-element"; + SelectorType["Tag"] = "tag"; + SelectorType["Universal"] = "universal"; + // Traversals + SelectorType["Adjacent"] = "adjacent"; + SelectorType["Child"] = "child"; + SelectorType["Descendant"] = "descendant"; + SelectorType["Parent"] = "parent"; + SelectorType["Sibling"] = "sibling"; + SelectorType["ColumnCombinator"] = "column-combinator"; +})(SelectorType || (SelectorType = {})); +/** Operators available for attribute selectors. */ +var AttributeAction; +(function (AttributeAction) { + AttributeAction["Any"] = "any"; + AttributeAction["Element"] = "element"; + AttributeAction["End"] = "end"; + AttributeAction["Equals"] = "equals"; + AttributeAction["Exists"] = "exists"; + AttributeAction["Hyphen"] = "hyphen"; + AttributeAction["Not"] = "not"; + AttributeAction["Start"] = "start"; +})(AttributeAction || (AttributeAction = {})); + +const reName = /^[^#\\]?(?:\\(?:[\da-f]{1,6}\s?|.)|[\w\u00B0-\uFFFF-])+/; +const reEscape = /\\([\da-f]{1,6}\s?|(\s)|.)/gi; +var CharCode; +(function (CharCode) { + CharCode[CharCode["LeftParenthesis"] = 40] = "LeftParenthesis"; + CharCode[CharCode["RightParenthesis"] = 41] = "RightParenthesis"; + CharCode[CharCode["LeftSquareBracket"] = 91] = "LeftSquareBracket"; + CharCode[CharCode["RightSquareBracket"] = 93] = "RightSquareBracket"; + CharCode[CharCode["Comma"] = 44] = "Comma"; + CharCode[CharCode["Period"] = 46] = "Period"; + CharCode[CharCode["Colon"] = 58] = "Colon"; + CharCode[CharCode["SingleQuote"] = 39] = "SingleQuote"; + CharCode[CharCode["DoubleQuote"] = 34] = "DoubleQuote"; + CharCode[CharCode["Plus"] = 43] = "Plus"; + CharCode[CharCode["Tilde"] = 126] = "Tilde"; + CharCode[CharCode["QuestionMark"] = 63] = "QuestionMark"; + CharCode[CharCode["ExclamationMark"] = 33] = "ExclamationMark"; + CharCode[CharCode["Slash"] = 47] = "Slash"; + CharCode[CharCode["Equal"] = 61] = "Equal"; + CharCode[CharCode["Dollar"] = 36] = "Dollar"; + CharCode[CharCode["Pipe"] = 124] = "Pipe"; + CharCode[CharCode["Circumflex"] = 94] = "Circumflex"; + CharCode[CharCode["Asterisk"] = 42] = "Asterisk"; + CharCode[CharCode["GreaterThan"] = 62] = "GreaterThan"; + CharCode[CharCode["LessThan"] = 60] = "LessThan"; + CharCode[CharCode["Hash"] = 35] = "Hash"; + CharCode[CharCode["LowerI"] = 105] = "LowerI"; + CharCode[CharCode["LowerS"] = 115] = "LowerS"; + CharCode[CharCode["BackSlash"] = 92] = "BackSlash"; + // Whitespace + CharCode[CharCode["Space"] = 32] = "Space"; + CharCode[CharCode["Tab"] = 9] = "Tab"; + CharCode[CharCode["NewLine"] = 10] = "NewLine"; + CharCode[CharCode["FormFeed"] = 12] = "FormFeed"; + CharCode[CharCode["CarriageReturn"] = 13] = "CarriageReturn"; +})(CharCode || (CharCode = {})); +const actionTypes = new Map([ + [CharCode.Tilde, AttributeAction.Element], + [CharCode.Circumflex, AttributeAction.Start], + [CharCode.Dollar, AttributeAction.End], + [CharCode.Asterisk, AttributeAction.Any], + [CharCode.ExclamationMark, AttributeAction.Not], + [CharCode.Pipe, AttributeAction.Hyphen], +]); +// Pseudos, whose data property is parsed as well. +const unpackPseudos = new Set([ + "has", + "not", + "matches", + "is", + "where", + "host", + "host-context", +]); +/** + * Pseudo elements defined in CSS Level 1 and CSS Level 2 can be written with + * a single colon; eg. :before will turn into ::before. + * @see {@link https://www.w3.org/TR/2018/WD-selectors-4-20181121/#pseudo-element-syntax} + */ +const pseudosToPseudoElements = new Set([ + "before", + "after", + "first-line", + "first-letter", +]); +/** + * Checks whether a specific selector is a traversal. + * This is useful eg. in swapping the order of elements that + * are not traversals. + * @param selector Selector to check. + */ +function isTraversal$1(selector) { + switch (selector.type) { + case SelectorType.Adjacent: + case SelectorType.Child: + case SelectorType.Descendant: + case SelectorType.Parent: + case SelectorType.Sibling: + case SelectorType.ColumnCombinator: { + return true; + } + case SelectorType.Attribute: + case SelectorType.Pseudo: + case SelectorType.PseudoElement: + case SelectorType.Tag: + case SelectorType.Universal: { + return false; + } + } +} +const stripQuotesFromPseudos = new Set(["contains", "icontains"]); +// Unescape function taken from https://github.com/jquery/sizzle/blob/master/src/sizzle.js#L152 +function funescape(_, escaped, escapedWhitespace) { + const high = Number.parseInt(escaped, 16) - 0x1_00_00; + // NaN means non-codepoint + return Number.isNaN(high) || escapedWhitespace + ? escaped + : high < 0 + ? // BMP codepoint + String.fromCharCode(high + 0x1_00_00) + : // Supplemental Plane codepoint (surrogate pair) + String.fromCharCode((high >> 10) | 0xd8_00, (high & 0x3_ff) | 0xdc_00); +} +function unescapeCSS(cssString) { + return cssString.replace(reEscape, funescape); +} +function isQuote(c) { + return c === CharCode.SingleQuote || c === CharCode.DoubleQuote; +} +function isWhitespace(c) { + return (c === CharCode.Space || + c === CharCode.Tab || + c === CharCode.NewLine || + c === CharCode.FormFeed || + c === CharCode.CarriageReturn); +} +/** + * Parses `selector`. + * @param selector Selector to parse. + * @returns Returns a two-dimensional array. + * The first dimension represents selectors separated by commas (eg. `sub1, sub2`), + * the second contains the relevant tokens for that selector. + */ +function parse$3(selector) { + const subselects = []; + const endIndex = parseSelector(subselects, `${selector}`, 0); + if (endIndex < selector.length) { + throw new Error(`Unmatched selector: ${selector.slice(endIndex)}`); + } + return subselects; +} +function parseSelector(subselects, selector, selectorIndex) { + let tokens = []; + function getName(offset) { + const match = selector.slice(selectorIndex + offset).match(reName); + if (!match) { + throw new Error(`Expected name, found ${selector.slice(selectorIndex)}`); + } + const [name] = match; + selectorIndex += offset + name.length; + return unescapeCSS(name); + } + function stripWhitespace(offset) { + selectorIndex += offset; + while (selectorIndex < selector.length && + isWhitespace(selector.charCodeAt(selectorIndex))) { + selectorIndex++; + } + } + function readValueWithParenthesis() { + selectorIndex += 1; + const start = selectorIndex; + for (let counter = 1; selectorIndex < selector.length; selectorIndex++) { + switch (selector.charCodeAt(selectorIndex)) { + case CharCode.BackSlash: { + // Skip next character + selectorIndex += 1; + break; + } + case CharCode.LeftParenthesis: { + counter += 1; + break; + } + case CharCode.RightParenthesis: { + counter -= 1; + if (counter === 0) { + return unescapeCSS(selector.slice(start, selectorIndex++)); + } + break; + } + } + } + throw new Error("Parenthesis not matched"); + } + function ensureNotTraversal() { + if (tokens.length > 0 && isTraversal$1(tokens[tokens.length - 1])) { + throw new Error("Did not expect successive traversals."); + } + } + function addTraversal(type) { + if (tokens.length > 0 && + tokens[tokens.length - 1].type === SelectorType.Descendant) { + tokens[tokens.length - 1].type = type; + return; + } + ensureNotTraversal(); + tokens.push({ type }); + } + function addSpecialAttribute(name, action) { + tokens.push({ + type: SelectorType.Attribute, + name, + action, + value: getName(1), + namespace: null, + ignoreCase: "quirks", + }); + } + /** + * We have finished parsing the current part of the selector. + * + * Remove descendant tokens at the end if they exist, + * and return the last index, so that parsing can be + * picked up from here. + */ + function finalizeSubselector() { + if (tokens.length > 0 && + tokens[tokens.length - 1].type === SelectorType.Descendant) { + tokens.pop(); + } + if (tokens.length === 0) { + throw new Error("Empty sub-selector"); + } + subselects.push(tokens); + } + stripWhitespace(0); + if (selector.length === selectorIndex) { + return selectorIndex; + } + loop: while (selectorIndex < selector.length) { + const firstChar = selector.charCodeAt(selectorIndex); + switch (firstChar) { + // Whitespace + case CharCode.Space: + case CharCode.Tab: + case CharCode.NewLine: + case CharCode.FormFeed: + case CharCode.CarriageReturn: { + if (tokens.length === 0 || + tokens[0].type !== SelectorType.Descendant) { + ensureNotTraversal(); + tokens.push({ type: SelectorType.Descendant }); + } + stripWhitespace(1); + break; + } + // Traversals + case CharCode.GreaterThan: { + addTraversal(SelectorType.Child); + stripWhitespace(1); + break; + } + case CharCode.LessThan: { + addTraversal(SelectorType.Parent); + stripWhitespace(1); + break; + } + case CharCode.Tilde: { + addTraversal(SelectorType.Sibling); + stripWhitespace(1); + break; + } + case CharCode.Plus: { + addTraversal(SelectorType.Adjacent); + stripWhitespace(1); + break; + } + // Special attribute selectors: .class, #id + case CharCode.Period: { + addSpecialAttribute("class", AttributeAction.Element); + break; + } + case CharCode.Hash: { + addSpecialAttribute("id", AttributeAction.Equals); + break; + } + case CharCode.LeftSquareBracket: { + stripWhitespace(1); + // Determine attribute name and namespace + let name; + let namespace = null; + if (selector.charCodeAt(selectorIndex) === CharCode.Pipe) { + // Equivalent to no namespace + name = getName(1); + } + else if (selector.startsWith("*|", selectorIndex)) { + namespace = "*"; + name = getName(2); + } + else { + name = getName(0); + if (selector.charCodeAt(selectorIndex) === CharCode.Pipe && + selector.charCodeAt(selectorIndex + 1) !== + CharCode.Equal) { + namespace = name; + name = getName(1); + } + } + stripWhitespace(0); + // Determine comparison operation + let action = AttributeAction.Exists; + const possibleAction = actionTypes.get(selector.charCodeAt(selectorIndex)); + if (possibleAction) { + action = possibleAction; + if (selector.charCodeAt(selectorIndex + 1) !== + CharCode.Equal) { + throw new Error("Expected `=`"); + } + stripWhitespace(2); + } + else if (selector.charCodeAt(selectorIndex) === CharCode.Equal) { + action = AttributeAction.Equals; + stripWhitespace(1); + } + // Determine value + let value = ""; + let ignoreCase = null; + if (action !== "exists") { + if (isQuote(selector.charCodeAt(selectorIndex))) { + const quote = selector.charCodeAt(selectorIndex); + selectorIndex += 1; + const sectionStart = selectorIndex; + while (selectorIndex < selector.length && + selector.charCodeAt(selectorIndex) !== quote) { + selectorIndex += + // Skip next character if it is escaped + selector.charCodeAt(selectorIndex) === + CharCode.BackSlash + ? 2 + : 1; + } + if (selector.charCodeAt(selectorIndex) !== quote) { + throw new Error("Attribute value didn't end"); + } + value = unescapeCSS(selector.slice(sectionStart, selectorIndex)); + selectorIndex += 1; + } + else { + const valueStart = selectorIndex; + while (selectorIndex < selector.length && + !isWhitespace(selector.charCodeAt(selectorIndex)) && + selector.charCodeAt(selectorIndex) !== + CharCode.RightSquareBracket) { + selectorIndex += + // Skip next character if it is escaped + selector.charCodeAt(selectorIndex) === + CharCode.BackSlash + ? 2 + : 1; + } + value = unescapeCSS(selector.slice(valueStart, selectorIndex)); + } + stripWhitespace(0); + // See if we have a force ignore flag + switch (selector.charCodeAt(selectorIndex) | 0x20) { + // If the forceIgnore flag is set (either `i` or `s`), use that value + case CharCode.LowerI: { + ignoreCase = true; + stripWhitespace(1); + break; + } + case CharCode.LowerS: { + ignoreCase = false; + stripWhitespace(1); + break; + } + } + } + if (selector.charCodeAt(selectorIndex) !== + CharCode.RightSquareBracket) { + throw new Error("Attribute selector didn't terminate"); + } + selectorIndex += 1; + const attributeSelector = { + type: SelectorType.Attribute, + name, + action, + value, + namespace, + ignoreCase, + }; + tokens.push(attributeSelector); + break; + } + case CharCode.Colon: { + if (selector.charCodeAt(selectorIndex + 1) === CharCode.Colon) { + tokens.push({ + type: SelectorType.PseudoElement, + name: getName(2).toLowerCase(), + data: selector.charCodeAt(selectorIndex) === + CharCode.LeftParenthesis + ? readValueWithParenthesis() + : null, + }); + break; + } + const name = getName(1).toLowerCase(); + if (pseudosToPseudoElements.has(name)) { + tokens.push({ + type: SelectorType.PseudoElement, + name, + data: null, + }); + break; + } + let data = null; + if (selector.charCodeAt(selectorIndex) === + CharCode.LeftParenthesis) { + if (unpackPseudos.has(name)) { + if (isQuote(selector.charCodeAt(selectorIndex + 1))) { + throw new Error(`Pseudo-selector ${name} cannot be quoted`); + } + data = []; + selectorIndex = parseSelector(data, selector, selectorIndex + 1); + if (selector.charCodeAt(selectorIndex) !== + CharCode.RightParenthesis) { + throw new Error(`Missing closing parenthesis in :${name} (${selector})`); + } + selectorIndex += 1; + } + else { + data = readValueWithParenthesis(); + if (stripQuotesFromPseudos.has(name)) { + const quot = data.charCodeAt(0); + if (quot === data.charCodeAt(data.length - 1) && + isQuote(quot)) { + data = data.slice(1, -1); + } + } + data = unescapeCSS(data); + } + } + tokens.push({ type: SelectorType.Pseudo, name, data }); + break; + } + case CharCode.Comma: { + finalizeSubselector(); + tokens = []; + stripWhitespace(1); + break; + } + default: { + if (selector.startsWith("/*", selectorIndex)) { + const endIndex = selector.indexOf("*/", selectorIndex + 2); + if (endIndex === -1) { + throw new Error("Comment was not terminated"); + } + selectorIndex = endIndex + 2; + // Remove leading whitespace + if (tokens.length === 0) { + stripWhitespace(0); + } + break; + } + let namespace = null; + let name; + if (firstChar === CharCode.Asterisk) { + selectorIndex += 1; + name = "*"; + } + else if (firstChar === CharCode.Pipe) { + name = ""; + if (selector.charCodeAt(selectorIndex + 1) === CharCode.Pipe) { + addTraversal(SelectorType.ColumnCombinator); + stripWhitespace(2); + break; + } + } + else if (reName.test(selector.slice(selectorIndex))) { + name = getName(0); + } + else { + break loop; + } + if (selector.charCodeAt(selectorIndex) === CharCode.Pipe && + selector.charCodeAt(selectorIndex + 1) !== CharCode.Pipe) { + namespace = name; + if (selector.charCodeAt(selectorIndex + 1) === + CharCode.Asterisk) { + name = "*"; + selectorIndex += 2; + } + else { + name = getName(1); + } + } + tokens.push(name === "*" + ? { type: SelectorType.Universal, namespace } + : { type: SelectorType.Tag, name, namespace }); + } + } + } + finalizeSubselector(); + return selectorIndex; +} + +/** Types of elements found in htmlparser2's DOM */ +var ElementType; +(function (ElementType) { + /** Type for the root element of a document */ + ElementType["Root"] = "root"; + /** Type for Text */ + ElementType["Text"] = "text"; + /** Type for <? ... ?> */ + ElementType["Directive"] = "directive"; + /** Type for <!-- ... --> */ + ElementType["Comment"] = "comment"; + /** Type for <script> tags */ + ElementType["Script"] = "script"; + /** Type for <style> tags */ + ElementType["Style"] = "style"; + /** Type for Any tag */ + ElementType["Tag"] = "tag"; + /** Type for <![CDATA[ ... ]]> */ + ElementType["CDATA"] = "cdata"; + /** Type for <!doctype ...> */ + ElementType["Doctype"] = "doctype"; +})(ElementType || (ElementType = {})); +/** + * Tests whether an element is a tag or not. + * @param element Element to test + * @param element.type Node type discriminator to check. + */ +function isTag$2(element) { + return (element.type === ElementType.Tag || + element.type === ElementType.Script || + element.type === ElementType.Style); +} +// Exports for backwards compatibility +/** Type for the root element of a document */ +// eslint-disable-next-line prefer-destructuring +const Root = ElementType.Root; +/** Type for Text */ +// eslint-disable-next-line prefer-destructuring +const Text$2 = ElementType.Text; +/** Type for <? ... ?> */ +// eslint-disable-next-line prefer-destructuring +const Directive = ElementType.Directive; +/** Type for <!-- ... --> */ +// eslint-disable-next-line prefer-destructuring +const Comment$1 = ElementType.Comment; +/** Type for <script> tags */ +// eslint-disable-next-line prefer-destructuring +const Script = ElementType.Script; +/** Type for <style> tags */ +// eslint-disable-next-line prefer-destructuring +const Style = ElementType.Style; +/** Type for Any tag */ +// eslint-disable-next-line prefer-destructuring +const Tag = ElementType.Tag; +/** Type for <![CDATA[ ... ]]> */ +// eslint-disable-next-line prefer-destructuring +const CDATA = ElementType.CDATA; +/** Type for <!doctype ...> */ +// eslint-disable-next-line prefer-destructuring +const Doctype = ElementType.Doctype; + +/** + * Checks if `node` is an element node. + * @param node Node to check. + * @returns `true` if the node is an element node. + */ +function isTag$1(node) { + return isTag$2(node); +} +/** + * Checks if `node` is a CDATA node. + * @param node Node to check. + * @returns `true` if the node is a CDATA node. + */ +function isCDATA(node) { + return node.type === ElementType.CDATA; +} +/** + * Checks if `node` is a text node. + * @param node Node to check. + * @returns `true` if the node is a text node. + */ +function isText(node) { + return node.type === ElementType.Text; +} +/** + * Checks if `node` is a comment node. + * @param node Node to check. + * @returns `true` if the node is a comment node. + */ +function isComment(node) { + return node.type === ElementType.Comment; +} +/** + * Checks if `node` has children. + * @param node Node to check. + * @returns `true` if the node has children. + */ +function hasChildren(node) { + return Object.hasOwn(node, "children"); +} + +/** + * Search a node and its children for nodes passing a test function. If `node` is not an array, it will be wrapped in one. + * + * @category Querying + * @param test Function to test nodes on. + * @param node Node to search. Will be included in the result set if it matches. + * @param recurse Also consider child nodes. + * @param limit Maximum number of nodes to return. + * @returns All nodes passing `test`. + */ +function filter(test, node, recurse = true, limit = Number.POSITIVE_INFINITY) { + return find(test, Array.isArray(node) ? node : [node], recurse, limit); +} +/** + * Search an array of nodes and their children for nodes passing a test function. + * + * @category Querying + * @param test Function to test nodes on. + * @param nodes Array of nodes to search. + * @param recurse Also consider child nodes. + * @param limit Maximum number of nodes to return. + * @returns All nodes passing `test`. + */ +function find(test, nodes, recurse, limit) { + const result = []; + /** Stack of the arrays we are looking at. */ + const nodeStack = [Array.isArray(nodes) ? nodes : [nodes]]; + /** Stack of the indices within the arrays. */ + const indexStack = [0]; + for (;;) { + // First, check if the current array has any more elements to look at. + if (indexStack[0] >= nodeStack[0].length) { + // If we have no more arrays to look at, we are done. + if (indexStack.length === 1) { + return result; + } + // Otherwise, remove the current array from the stack. + nodeStack.shift(); + indexStack.shift(); + // Loop back to the start to continue with the next array. + continue; + } + const element = nodeStack[0][indexStack[0]++]; + if (test(element)) { + result.push(element); + if (--limit <= 0) + return result; + } + if (recurse && hasChildren(element) && element.children.length > 0) { + /* + * Add the children to the stack. We are depth-first, so this is + * the next array we look at. + */ + indexStack.unshift(0); + nodeStack.unshift(element.children); + } + } +} +/** + * Finds one element in a tree that passes a test. + * + * @category Querying + * @param test Function to test nodes on. + * @param nodes Node or array of nodes to search. + * @param recurse Also consider child nodes. + * @returns The first node that passes `test`. + */ +function findOne$2(test, nodes, recurse = true) { + const searchedNodes = Array.isArray(nodes) ? nodes : [nodes]; + for (const node of searchedNodes) { + if (isTag$1(node) && test(node)) { + return node; + } + if (recurse && hasChildren(node) && node.children.length > 0) { + const found = findOne$2(test, node.children, true); + if (found) + return found; + } + } + return null; +} +/** + * Checks if a tree of nodes contains at least one node passing a test. + * + * @category Querying + * @param test Function to test nodes on. + * @param nodes Array of nodes to search. + * @returns Whether a tree of nodes contains at least one node passing the test. + */ +function existsOne$1(test, nodes) { + return (Array.isArray(nodes) ? nodes : [nodes]).some((node) => (isTag$1(node) && test(node)) || + (hasChildren(node) && existsOne$1(test, node.children))); +} +/** + * Search an array of nodes and their children for elements passing a test function. + * + * Same as `find`, but limited to elements and with less options, leading to reduced complexity. + * + * @category Querying + * @param test Function to test nodes on. + * @param nodes Array of nodes to search. + * @returns All nodes passing `test`. + */ +function findAll$1(test, nodes) { + const result = []; + const nodeStack = [Array.isArray(nodes) ? nodes : [nodes]]; + const indexStack = [0]; + for (;;) { + if (indexStack[0] >= nodeStack[0].length) { + if (nodeStack.length === 1) { + return result; + } + // Otherwise, remove the current array from the stack. + nodeStack.shift(); + indexStack.shift(); + // Loop back to the start to continue with the next array. + continue; + } + const element = nodeStack[0][indexStack[0]++]; + if (isTag$1(element) && test(element)) + result.push(element); + if (hasChildren(element) && element.children.length > 0) { + indexStack.unshift(0); + nodeStack.unshift(element.children); + } + } +} + +/** + * A map of functions to check nodes against. + */ +const Checks = { + tag_name(name) { + if (typeof name === "function") { + return (element) => isTag$1(element) && name(element.name); + } + if (name === "*") { + return isTag$1; + } + return (element) => isTag$1(element) && element.name === name; + }, + tag_type(type) { + if (typeof type === "function") { + return (element) => type(element.type); + } + return (element) => element.type === type; + }, + tag_contains(data) { + if (typeof data === "function") { + return (element) => isText(element) && data(element.data); + } + return (element) => isText(element) && element.data === data; + }, +}; +/** + * Returns a function to check whether a node has an attribute with a particular + * value. + * + * @param attrib Attribute to check. + * @param value Attribute value to look for. + * @returns A function to check whether the a node has an attribute with a + * particular value. + */ +function getAttribCheck(attrib, value) { + if (typeof value === "function") { + return (element) => isTag$1(element) && value(element.attribs[attrib]); + } + return (element) => isTag$1(element) && element.attribs[attrib] === value; +} +/** + * Returns a function that returns `true` if either of the input functions + * returns `true` for a node. + * + * @param a First function to combine. + * @param b Second function to combine. + * @returns A function taking a node and returning `true` if either of the input + * functions returns `true` for the node. + */ +function combineFuncs(a, b) { + return (element) => a(element) || b(element); +} +/** + * Returns a function that executes all checks in `options` and returns `true` + * if any of them match a node. + * + * @param options An object describing nodes to look for. + * @returns A function that executes all checks in `options` and returns `true` + * if any of them match a node. + */ +function compileTest(options) { + const funcs = Object.keys(options).map((key) => { + const value = options[key]; + return Object.hasOwn(Checks, key) + ? Checks[key](value) + : getAttribCheck(key, value); + }); + return funcs.length === 0 ? null : funcs.reduce(combineFuncs); +} +/** + * Checks whether a node matches the description in `options`. + * + * @category Legacy Query Functions + * @param options An object describing nodes to look for. + * @param node The element to test. + * @returns Whether the element matches the description in `options`. + */ +function testElement(options, node) { + const test = compileTest(options); + return test ? test(node) : true; +} +/** + * Returns all nodes that match `options`. + * + * @category Legacy Query Functions + * @param options An object describing nodes to look for. + * @param nodes Nodes to search through. + * @param recurse Also consider child nodes. + * @param limit Maximum number of nodes to return. + * @returns All nodes that match `options`. + */ +function getElements(options, nodes, recurse, limit = Number.POSITIVE_INFINITY) { + const test = compileTest(options); + return test ? filter(test, nodes, recurse, limit) : []; +} +/** + * Returns the node with the supplied ID. + * + * @category Legacy Query Functions + * @param id The unique ID attribute value to look for. + * @param nodes Nodes to search through. + * @param recurse Also consider child nodes. + * @returns The node with the supplied ID. + */ +function getElementById(id, nodes, recurse = true) { + if (!Array.isArray(nodes)) + nodes = [nodes]; + return findOne$2(getAttribCheck("id", id), nodes, recurse); +} +/** + * Returns all nodes with the supplied `tagName`. + * + * @category Legacy Query Functions + * @param tagName Tag name to search for. + * @param nodes Nodes to search through. + * @param recurse Also consider child nodes. + * @param limit Maximum number of nodes to return. + * @returns All nodes with the supplied `tagName`. + */ +function getElementsByTagName(tagName, nodes, recurse = true, limit = Number.POSITIVE_INFINITY) { + return filter(Checks["tag_name"](tagName), nodes, recurse, limit); +} +/** + * Returns all nodes with the supplied `className`. + * + * @category Legacy Query Functions + * @param className Class name to search for. + * @param nodes Nodes to search through. + * @param recurse Also consider child nodes. + * @param limit Maximum number of nodes to return. + * @returns All nodes with the supplied `className`. + */ +function getElementsByClassName(className, nodes, recurse = true, limit = Number.POSITIVE_INFINITY) { + return filter(getAttribCheck("class", className), nodes, recurse, limit); +} +/** + * Returns all nodes with the supplied `type`. + * + * @category Legacy Query Functions + * @param type Element type to look for. + * @param nodes Nodes to search through. + * @param recurse Also consider child nodes. + * @param limit Maximum number of nodes to return. + * @returns All nodes with the supplied `type`. + */ +function getElementsByTagType(type, nodes, recurse = true, limit = Number.POSITIVE_INFINITY) { + return filter(Checks["tag_type"](type), nodes, recurse, limit); +} + +const xmlCodeMap = new Map([ + [34, """], + [38, "&"], + [39, "'"], + [60, "<"], + [62, ">"], +]); +// For compatibility with node < 4, we wrap `codePointAt` +/** + * Read a code point at a given index. + * @param input Input string to encode or decode. + * @param index Current read position in the input string. + */ +const getCodePoint = typeof String.prototype.codePointAt === "function" + ? (input, index) => input.codePointAt(index) + : // http://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae + (c, index) => (c.charCodeAt(index) & 0xfc_00) === 0xd8_00 + ? (c.charCodeAt(index) - 0xd8_00) * 0x4_00 + + c.charCodeAt(index + 1) - + 0xdc_00 + + 0x1_00_00 + : c.charCodeAt(index); +/** + * Bitset for ASCII characters that need to be escaped in XML. + */ +const XML_BITSET_VALUE = 0x50_00_00_c4; // 32..63 -> 34 ("),38 (&),39 ('),60 (<),62 (>) +/** + * Encodes all non-ASCII characters, as well as characters not valid in XML + * documents using XML entities. Uses a fast bitset scan instead of RegExp. + * + * If a character has no equivalent entity, a numeric hexadecimal reference + * (eg. `ü`) will be used. + * @param input Input string to encode or decode. + */ +function encodeXML(input) { + let out; + let last = 0; + const { length } = input; + for (let index = 0; index < length; index++) { + const char = input.charCodeAt(index); + // Check for ASCII chars that don't need escaping + if (char < 0x80 && + (((XML_BITSET_VALUE >>> char) & 1) === 0 || char >= 64 || char < 32)) { + continue; + } + if (out === undefined) + out = input.substring(0, index); + else if (last !== index) + out += input.substring(last, index); + if (char < 64) { + // Known replacement + out += xmlCodeMap.get(char); + last = index + 1; + continue; + } + // Non-ASCII: encode as numeric entity (handle surrogate pair) + const cp = getCodePoint(input, index); + out += `&#x${cp.toString(16)};`; + if (cp !== char) + index++; // Skip trailing surrogate + last = index + 1; + } + if (out === undefined) + return input; + if (last < length) + out += input.substr(last); + return out; +} +/** + * Creates a function that escapes all characters matched by the given regular + * expression using the given map of characters to escape to their entities. + * @param regex Regular expression to match characters to escape. + * @param map Map of characters to escape to their entities. + * @returns Function that escapes all characters matched by the given regular + * expression using the given map of characters to escape to their entities. + */ +function getEscaper(regex, map) { + return function escape(data) { + let match; + let lastIndex = 0; + let result = ""; + while ((match = regex.exec(data))) { + if (lastIndex !== match.index) { + result += data.substring(lastIndex, match.index); + } + // We know that this character will be in the map. + result += map.get(match[0].charCodeAt(0)); + // Every match will be of length 1 + lastIndex = match.index + 1; + } + return result + data.substring(lastIndex); + }; +} +/** + * Encodes all characters that have to be escaped in HTML attributes, + * following {@link https://html.spec.whatwg.org/multipage/parsing.html#escapingString}. + * @param data String to escape. + */ +const escapeAttribute = +/* #__PURE__ */ getEscaper(/["&\u00A0]/g, new Map([ + [34, """], + [38, "&"], + [160, " "], +])); +/** + * Encodes all characters that have to be escaped in HTML text, + * following {@link https://html.spec.whatwg.org/multipage/parsing.html#escapingString}. + * @param data String to escape. + */ +const escapeText = /* #__PURE__ */ getEscaper(/[&<>\u00A0]/g, new Map([ + [38, "&"], + [60, "<"], + [62, ">"], + [160, " "], +])); + +/** + * Mixed-case SVG and MathML element names recognized in foreign content. + */ +const elementNames = new Map([ + "altGlyph", + "altGlyphDef", + "altGlyphItem", + "animateColor", + "animateMotion", + "animateTransform", + "clipPath", + "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", + "foreignObject", + "glyphRef", + "linearGradient", + "radialGradient", + "textPath", +].map((value) => [value.toLowerCase(), value])); +/** + * Mixed-case SVG and MathML attribute names recognized in foreign content. + */ +const attributeNames = new Map([ + "definitionURL", + "attributeName", + "attributeType", + "baseFrequency", + "baseProfile", + "calcMode", + "clipPathUnits", + "diffuseConstant", + "edgeMode", + "filterUnits", + "glyphRef", + "gradientTransform", + "gradientUnits", + "kernelMatrix", + "kernelUnitLength", + "keyPoints", + "keySplines", + "keyTimes", + "lengthAdjust", + "limitingConeAngle", + "markerHeight", + "markerUnits", + "markerWidth", + "maskContentUnits", + "maskUnits", + "numOctaves", + "pathLength", + "patternContentUnits", + "patternTransform", + "patternUnits", + "pointsAtX", + "pointsAtY", + "pointsAtZ", + "preserveAlpha", + "preserveAspectRatio", + "primitiveUnits", + "refX", + "refY", + "repeatCount", + "repeatDur", + "requiredExtensions", + "requiredFeatures", + "specularConstant", + "specularExponent", + "spreadMethod", + "startOffset", + "stdDeviation", + "stitchTiles", + "surfaceScale", + "systemLanguage", + "tableValues", + "targetX", + "targetY", + "textLength", + "viewBox", + "viewTarget", + "xChannelSelector", + "yChannelSelector", + "zoomAndPan", +].map((value) => [value.toLowerCase(), value])); + +/* + * Module dependencies + */ +const unencodedElements = new Set([ + "style", + "script", + "xmp", + "iframe", + "noembed", + "noframes", + "plaintext", + "noscript", +]); +function replaceQuotes(value) { + return value.replace(/"/g, """); +} +/** + * Format attributes + * @param attributes Attribute map to serialize. + * @param options Options that control this operation. + */ +function formatAttributes(attributes, options) { + if (!attributes) + return; + const encode = (options.encodeEntities ?? options.decodeEntities) === false + ? replaceQuotes + : !!options.xmlMode || options.encodeEntities !== "utf8" + ? encodeXML + : escapeAttribute; + return Object.keys(attributes) + .map((key) => { + const value = attributes[key]; + const normalizedValue = value == null ? "" : String(value); + if (options.xmlMode === "foreign") { + /* Fix up mixed-case attribute names */ + key = attributeNames.get(key) ?? key; + } + if (!(options.emptyAttrs || options.xmlMode) && normalizedValue === "") { + return key; + } + return `${key}="${encode(normalizedValue)}"`; + }) + .join(" "); +} +/** + * Self-enclosing tags + */ +const singleTag = new Set([ + "area", + "base", + "basefont", + "br", + "col", + "command", + "embed", + "frame", + "hr", + "img", + "input", + "isindex", + "keygen", + "link", + "meta", + "param", + "source", + "track", + "wbr", +]); +/** + * Renders a DOM node or an array of DOM nodes to a string. + * + * Can be thought of as the equivalent of the `outerHTML` of the passed node(s). + * @param node Node to be rendered. + * @param options Changes serialization behavior + */ +function render(node, options = {}) { + const nodes = "length" in node ? node : [node]; + let output = ""; + let index = 0; + while (index < nodes.length) { + output += renderNode(nodes[index], options); + index++; + } + return output; +} +function renderNode(node, options) { + switch (node.type) { + case Root: { + return render(node.children, options); + } + // @ts-expect-error We don't use `Doctype` yet + case Doctype: + case Directive: { + return renderDirective(node); + } + case Comment$1: { + return renderComment(node); + } + case CDATA: { + return renderCdata(node); + } + case Script: + case Style: + case Tag: { + return renderTag(node, options); + } + case Text$2: { + return renderText(node, options); + } + } +} +const foreignModeIntegrationPoints = new Set([ + "mi", + "mo", + "mn", + "ms", + "mtext", + "annotation-xml", + "foreignObject", + "desc", + "title", +]); +const foreignElements = new Set(["svg", "math"]); +function renderTag(element, options) { + // Handle SVG / MathML in HTML + if (options.xmlMode === "foreign") { + /* Fix up mixed-case element names */ + element.name = elementNames.get(element.name) ?? element.name; + /* Exit foreign mode at integration points */ + if (element.parent && + foreignModeIntegrationPoints.has(element.parent.name)) { + options = { ...options, xmlMode: false }; + } + } + if (!options.xmlMode && foreignElements.has(element.name)) { + options = { ...options, xmlMode: "foreign" }; + } + let tag = `<${element.name}`; + const attribs = formatAttributes(element.attribs, options); + if (attribs) { + tag += ` ${attribs}`; + } + if (element.children.length === 0 && + (options.xmlMode + ? // In XML mode or foreign mode, and user hasn't explicitly turned off self-closing tags + options.selfClosingTags !== false + : // User explicitly asked for self-closing tags, even in HTML mode + options.selfClosingTags && singleTag.has(element.name))) { + if (!options.xmlMode) + tag += " "; + tag += "/>"; + } + else { + tag += ">"; + if (element.children.length > 0) { + tag += render(element.children, options); + } + if (!!options.xmlMode || !singleTag.has(element.name)) { + tag += `</${element.name}>`; + } + } + return tag; +} +function renderDirective(element) { + return `<${element.data}>`; +} +function renderText(element, options) { + let data = element.data || ""; + // If entities weren't decoded, no need to encode them back + if ((options.encodeEntities ?? options.decodeEntities) !== false && + !(!options.xmlMode && + element.parent && + unencodedElements.has(element.parent.name))) { + data = + !!options.xmlMode || options.encodeEntities !== "utf8" + ? encodeXML(data) + : escapeText(data); + } + return data; +} +function renderCdata(element) { + return `<![CDATA[${element.children[0].data}]]>`; +} +function renderComment(element) { + return `<!--${element.data}-->`; +} + +/** + * @category Stringify + * @deprecated Use the `dom-serializer` module directly. + * @param node Node to get the outer HTML of. + * @param options Options for serialization. + * @returns `node`'s outer HTML. + */ +function getOuterHTML(node, options) { + return render(node, options); +} +/** + * @category Stringify + * @deprecated Use the `dom-serializer` module directly. + * @param node Node to get the inner HTML of. + * @param options Options for serialization. + * @returns `node`'s inner HTML. + */ +function getInnerHTML(node, options) { + return hasChildren(node) + ? node.children.map((node) => getOuterHTML(node, options)).join("") + : ""; +} +/** + * Get a node's inner text. Same as `textContent`, but inserts newlines for `<br>` tags. Ignores comments. + * + * @category Stringify + * @deprecated Use `textContent` instead. + * @param node Node to get the inner text of. + * @returns `node`'s inner text. + */ +function getText$1(node) { + if (Array.isArray(node)) + return node.map(getText$1).join(""); + if (isTag$1(node)) + return node.name === "br" ? "\n" : getText$1(node.children); + if (isCDATA(node)) + return getText$1(node.children); + if (isText(node)) + return node.data; + return ""; +} +/** + * Get a node's text content. Ignores comments. + * + * @category Stringify + * @param node Node to get the text content of. + * @returns `node`'s text content. + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent} + */ +function textContent(node) { + if (Array.isArray(node)) + return node.map(textContent).join(""); + if (hasChildren(node) && !isComment(node)) { + return textContent(node.children); + } + if (isText(node)) + return node.data; + return ""; +} +/** + * Get a node's inner text, ignoring `<script>` and `<style>` tags. Ignores comments. + * + * @category Stringify + * @param node Node to get the inner text of. + * @returns `node`'s inner text. + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Node/innerText} + */ +function innerText(node) { + if (Array.isArray(node)) + return node.map(innerText).join(""); + if (hasChildren(node) && (node.type === ElementType.Tag || isCDATA(node))) { + return innerText(node.children); + } + if (isText(node)) + return node.data; + return ""; +} + +/** + * Get the feed object from the root of a DOM tree. + * + * @category Feeds + * @param document The DOM to extract the feed from. + * @returns The feed. + */ +function getFeed(document) { + const feedRoot = getOneElement(isValidFeed, document); + return feedRoot + ? feedRoot.name === "feed" + ? getAtomFeed(feedRoot) + : getRssFeed(feedRoot) + : null; +} +/** + * Parse an Atom feed. + * + * @param feedRoot The root of the feed. + * @returns The parsed feed. + */ +function getAtomFeed(feedRoot) { + const childs = feedRoot.children; + const feed = { + type: "atom", + items: getElementsByTagName("entry", childs).map((item) => { + const { children } = item; + const entry = { media: getMediaElements(children) }; + addConditionally(entry, "id", "id", children); + addConditionally(entry, "title", "title", children); + const href = getOneElement("link", children)?.attribs["href"]; + if (href) { + entry.link = href; + } + const description = fetch("summary", children) || fetch("content", children); + if (description) { + entry.description = description; + } + const pubDate = fetch("updated", children); + if (pubDate) { + entry.pubDate = new Date(pubDate); + } + return entry; + }), + }; + addConditionally(feed, "id", "id", childs); + addConditionally(feed, "title", "title", childs); + const href = getOneElement("link", childs)?.attribs["href"]; + if (href) { + feed.link = href; + } + addConditionally(feed, "description", "subtitle", childs); + const updated = fetch("updated", childs); + if (updated) { + feed.updated = new Date(updated); + } + addConditionally(feed, "author", "email", childs, true); + return feed; +} +/** + * Parse a RSS feed. + * + * @param feedRoot The root of the feed. + * @returns The parsed feed. + */ +function getRssFeed(feedRoot) { + const childs = getOneElement("channel", feedRoot.children)?.children ?? []; + const feed = { + type: feedRoot.name.substr(0, 3), + id: "", + items: getElementsByTagName("item", feedRoot.children).map((item) => { + const { children } = item; + const entry = { media: getMediaElements(children) }; + addConditionally(entry, "id", "guid", children); + addConditionally(entry, "title", "title", children); + addConditionally(entry, "link", "link", children); + addConditionally(entry, "description", "description", children); + const pubDate = fetch("pubDate", children) || fetch("dc:date", children); + if (pubDate) + entry.pubDate = new Date(pubDate); + return entry; + }), + }; + addConditionally(feed, "title", "title", childs); + addConditionally(feed, "link", "link", childs); + addConditionally(feed, "description", "description", childs); + const updated = fetch("lastBuildDate", childs); + if (updated) { + feed.updated = new Date(updated); + } + addConditionally(feed, "author", "managingEditor", childs, true); + return feed; +} +const MEDIA_KEYS_STRING = ["url", "type", "lang"]; +const MEDIA_KEYS_INT = [ + "fileSize", + "bitrate", + "framerate", + "samplingrate", + "channels", + "duration", + "height", + "width", +]; +/** + * Get all media elements of a feed item. + * + * @param where Nodes to search in. + * @returns Media elements. + */ +function getMediaElements(where) { + return getElementsByTagName("media:content", where).map((element) => { + const { attribs } = element; + const media = { + medium: attribs["medium"], + isDefault: !!attribs["isDefault"], + }; + for (const attrib of MEDIA_KEYS_STRING) { + if (attribs[attrib]) { + media[attrib] = attribs[attrib]; + } + } + for (const attrib of MEDIA_KEYS_INT) { + if (attribs[attrib]) { + media[attrib] = Number.parseInt(attribs[attrib], 10); + } + } + if (attribs["expression"]) { + media.expression = attribs["expression"]; + } + return media; + }); +} +/** + * Get one element by tag name. + * + * @param tagName Tag name to look for + * @param node Node to search in + * @returns The element or null + */ +function getOneElement(tagName, node) { + return getElementsByTagName(tagName, node, true, 1)[0]; +} +/** + * Get the text content of an element with a certain tag name. + * + * @param tagName Tag name to look for. + * @param where Node to search in. + * @param recurse Whether to recurse into child nodes. + * @returns The text content of the element. + */ +function fetch(tagName, where, recurse = false) { + return textContent(getElementsByTagName(tagName, where, recurse, 1)).trim(); +} +/** + * Adds a property to an object if it has a value. + * + * @param object Object to be extended. + * @param property Property name. + * @param tagName Tag name that contains the conditionally added property. + * @param where Element to search for the property. + * @param recurse Whether to recurse into child nodes. + */ +function addConditionally(object, property, tagName, where, recurse = false) { + const value = fetch(tagName, where, recurse); + if (value) + object[property] = value; +} +/** + * Checks if an element is a feed root node. + * + * @param value The name of the element to check. + * @returns Whether an element is a feed root node. + */ +function isValidFeed(value) { + return value === "rss" || value === "feed" || value === "rdf:RDF"; +} + +/** + * Given an array of nodes, remove any member that is contained by another + * member. + * + * @category Helpers + * @param nodes Nodes to filter. + * @returns Remaining nodes that aren't contained by other nodes. + */ +function removeSubsets$1(nodes) { + let index = nodes.length; + /* + * Check if each node (or one of its ancestors) is already contained in the + * array. + */ + while (--index >= 0) { + const node = nodes[index]; + /* + * Remove the node if it is not unique. + * We are going through the array from the end, so we only + * have to check nodes that preceed the node under consideration in the array. + */ + if (index > 0 && nodes.lastIndexOf(node, index - 1) >= 0) { + nodes.splice(index, 1); + continue; + } + for (let ancestor = node.parent; ancestor; ancestor = ancestor.parent) { + if (nodes.includes(ancestor)) { + nodes.splice(index, 1); + break; + } + } + } + return nodes; +} +/** + * @category Helpers + * @see {@link http://dom.spec.whatwg.org/#dom-node-comparedocumentposition} + */ +var DocumentPosition; +(function (DocumentPosition) { + DocumentPosition[DocumentPosition["DISCONNECTED"] = 1] = "DISCONNECTED"; + DocumentPosition[DocumentPosition["PRECEDING"] = 2] = "PRECEDING"; + DocumentPosition[DocumentPosition["FOLLOWING"] = 4] = "FOLLOWING"; + DocumentPosition[DocumentPosition["CONTAINS"] = 8] = "CONTAINS"; + DocumentPosition[DocumentPosition["CONTAINED_BY"] = 16] = "CONTAINED_BY"; +})(DocumentPosition || (DocumentPosition = {})); +/** + * Compare the position of one node against another node in any other document, + * returning a bitmask with the values from {@link DocumentPosition}. + * + * Document order: + * > There is an ordering, document order, defined on all the nodes in the + * > document corresponding to the order in which the first character of the + * > XML representation of each node occurs in the XML representation of the + * > document after expansion of general entities. Thus, the document element + * > node will be the first node. Element nodes occur before their children. + * > Thus, document order orders element nodes in order of the occurrence of + * > their start-tag in the XML (after expansion of entities). The attribute + * > nodes of an element occur after the element and before its children. The + * > relative order of attribute nodes is implementation-dependent. + * + * Source: + * http://www.w3.org/TR/DOM-Level-3-Core/glossary.html#dt-document-order + * + * @category Helpers + * @param nodeA The first node to use in the comparison + * @param nodeB The second node to use in the comparison + * @returns A bitmask describing the input nodes' relative position. + * + * See http://dom.spec.whatwg.org/#dom-node-comparedocumentposition for + * a description of these values. + */ +function compareDocumentPosition(nodeA, nodeB) { + const aParents = []; + const bParents = []; + if (nodeA === nodeB) { + return 0; + } + let current = hasChildren(nodeA) ? nodeA : nodeA.parent; + while (current) { + aParents.unshift(current); + current = current.parent; + } + current = hasChildren(nodeB) ? nodeB : nodeB.parent; + while (current) { + bParents.unshift(current); + current = current.parent; + } + const maxIndex = Math.min(aParents.length, bParents.length); + let index = 0; + while (index < maxIndex && aParents[index] === bParents[index]) { + index++; + } + if (index === 0) { + return DocumentPosition.DISCONNECTED; + } + const sharedParent = aParents[index - 1]; + const siblings = sharedParent.children; + const aSibling = aParents[index]; + const bSibling = bParents[index]; + if (siblings.indexOf(aSibling) > siblings.indexOf(bSibling)) { + if (sharedParent === nodeB) { + return DocumentPosition.FOLLOWING | DocumentPosition.CONTAINED_BY; + } + return DocumentPosition.FOLLOWING; + } + if (sharedParent === nodeA) { + return DocumentPosition.PRECEDING | DocumentPosition.CONTAINS; + } + return DocumentPosition.PRECEDING; +} +/** + * Sort an array of nodes based on their relative position in the document, + * removing any duplicate nodes. If the array contains nodes that do not belong + * to the same document, sort order is unspecified. + * + * @category Helpers + * @param nodes Array of DOM nodes. + * @returns Collection of unique nodes, sorted in document order. + */ +function uniqueSort(nodes) { + nodes = nodes.filter((node, index, array) => !array.includes(node, index + 1)); + nodes.sort((a, b) => { + const relative = compareDocumentPosition(a, b); + if (relative & DocumentPosition.PRECEDING) { + return -1; + } + if (relative & DocumentPosition.FOLLOWING) { + return 1; + } + return 0; + }); + return nodes; +} + +/** + * Remove an element from the dom + * + * @category Manipulation + * @param element The element to be removed. + */ +function removeElement(element) { + if (element.prev) + element.prev.next = element.next; + if (element.next) + element.next.prev = element.prev; + if (element.parent) { + const childs = element.parent.children; + const childsIndex = childs.lastIndexOf(element); + if (childsIndex !== -1) { + childs.splice(childsIndex, 1); + } + } + element.next = null; + element.prev = null; + element.parent = null; +} +/** + * Replace an element in the dom + * + * @category Manipulation + * @param element The element to be replaced. + * @param replacement The element to be added + */ +function replaceElement(element, replacement) { + replacement.prev = element.prev; + if (replacement.prev) { + replacement.prev.next = replacement; + } + replacement.next = element.next; + if (replacement.next) { + replacement.next.prev = replacement; + } + replacement.parent = element.parent; + if (replacement.parent) { + const { children } = replacement.parent; + const elementIndex = children.lastIndexOf(element); + if (elementIndex === -1) { + return; + } + children[elementIndex] = replacement; + element.parent = null; + } +} +/** + * Append a child to an element. + * + * @category Manipulation + * @param parent The element to append to. + * @param child The element to be added as a child. + */ +function appendChild(parent, child) { + removeElement(child); + child.next = null; + child.parent = parent; + if (parent.children.push(child) > 1) { + const sibling = parent.children[parent.children.length - 2]; + sibling.next = child; + child.prev = sibling; + } + else { + child.prev = null; + } +} +/** + * Append an element after another. + * + * @category Manipulation + * @param element The element to append after. + * @param next The element be added. + */ +function append$1(element, next) { + removeElement(next); + const { parent } = element; + const currentNext = element.next; + next.next = currentNext; + next.prev = element; + element.next = next; + next.parent = parent; + if (currentNext) { + currentNext.prev = next; + if (parent) { + const childs = parent.children; + childs.splice(childs.lastIndexOf(currentNext), 0, next); + } + } + else if (parent) { + parent.children.push(next); + } +} +/** + * Prepend a child to an element. + * + * @category Manipulation + * @param parent The element to prepend before. + * @param child The element to be added as a child. + */ +function prependChild(parent, child) { + removeElement(child); + child.parent = parent; + child.prev = null; + if (parent.children.unshift(child) === 1) { + child.next = null; + } + else { + const sibling = parent.children[1]; + sibling.prev = child; + child.next = sibling; + } +} +/** + * Prepend an element before another. + * + * @category Manipulation + * @param element The element to prepend before. + * @param previous The element to be added. + */ +function prepend(element, previous) { + removeElement(previous); + const { parent } = element; + if (parent) { + const childs = parent.children; + childs.splice(childs.indexOf(element), 0, previous); + } + if (element.prev) { + element.prev.next = previous; + } + previous.parent = parent; + previous.prev = element.prev; + previous.next = element; + element.prev = previous; +} + +/** + * Get a node's children. + * + * @category Traversal + * @param element Node to get the children of. + * @returns `element`'s children, or an empty array. + */ +function getChildren$1(element) { + return hasChildren(element) ? element.children : []; +} +function getParent$1(element) { + return element.parent || null; +} +/** + * Gets an elements siblings, including the element itself. + * + * Attempts to get the children through the element's parent first. If we don't + * have a parent (the element is a root node), we walk the element's `prev` & + * `next` to get all remaining nodes. + * + * @category Traversal + * @param element Element to get the siblings of. + * @returns `element`'s siblings, including `element`. + */ +function getSiblings$1(element) { + const parent = getParent$1(element); + if (parent != null) + return getChildren$1(parent); + const siblings = [element]; + let { prev, next } = element; + while (prev != null) { + siblings.unshift(prev); + ({ prev } = prev); + } + while (next != null) { + siblings.push(next); + ({ next } = next); + } + return siblings; +} +/** + * Gets an attribute from an element. + * + * @category Traversal + * @param element Element to check. + * @param name Attribute name to retrieve. + * @returns The element's attribute value, or `undefined`. + */ +function getAttributeValue$1(element, name) { + const { attribs } = element; + return attribs?.[name]; +} +/** + * Checks whether an element has an attribute. + * + * @category Traversal + * @param element Element to check. + * @param name Attribute name to look for. + * @returns Returns whether `element` has the attribute `name`. + */ +function hasAttrib$1(element, name) { + const { attribs } = element; + return (attribs != null && Object.hasOwn(attribs, name) && attribs[name] != null); +} +/** + * Get the tag name of an element. + * + * @category Traversal + * @param element The element to get the name for. + * @returns The tag name of `element`. + */ +function getName$1(element) { + return element.name; +} +/** + * Returns the next element sibling of a node. + * + * @category Traversal + * @param element The element to get the next sibling of. + * @returns `element`'s next sibling that is a tag, or `null` if there is no next + * sibling. + */ +function nextElementSibling(element) { + let { next } = element; + while (next !== null && !isTag$1(next)) + ({ next } = next); + return next; +} +/** + * Returns the previous element sibling of a node. + * + * @category Traversal + * @param element The element to get the previous sibling of. + * @returns `element`'s previous sibling that is a tag, or `null` if there is no + * previous sibling. + */ +// eslint-disable-next-line unicorn/prevent-abbreviations -- Keep public API name for backwards compatibility. +function prevElementSibling(element) { + let { prev } = element; + while (prev !== null && !isTag$1(prev)) + ({ prev } = prev); + return prev; +} + +var DomUtils = /*#__PURE__*/Object.freeze({ + __proto__: null, + get DocumentPosition () { return DocumentPosition; }, + append: append$1, + appendChild: appendChild, + compareDocumentPosition: compareDocumentPosition, + existsOne: existsOne$1, + filter: filter, + find: find, + findAll: findAll$1, + findOne: findOne$2, + getAttributeValue: getAttributeValue$1, + getChildren: getChildren$1, + getElementById: getElementById, + getElements: getElements, + getElementsByClassName: getElementsByClassName, + getElementsByTagName: getElementsByTagName, + getElementsByTagType: getElementsByTagType, + getFeed: getFeed, + getInnerHTML: getInnerHTML, + getName: getName$1, + getOuterHTML: getOuterHTML, + getParent: getParent$1, + getSiblings: getSiblings$1, + getText: getText$1, + hasAttrib: hasAttrib$1, + innerText: innerText, + nextElementSibling: nextElementSibling, + prepend: prepend, + prependChild: prependChild, + prevElementSibling: prevElementSibling, + removeElement: removeElement, + removeSubsets: removeSubsets$1, + replaceElement: replaceElement, + testElement: testElement, + textContent: textContent, + uniqueSort: uniqueSort +}); + +/** + * All reserved characters in a regex, used for escaping. + * + * Taken from XRegExp, (c) 2007-2020 Steven Levithan under the MIT license + * https://github.com/slevithan/xregexp/blob/95eeebeb8fac8754d54eafe2b4743661ac1cf028/src/xregexp.js#L794 + */ +const reChars = /[-[\]{}()*+?.,\\^$|#\s]/g; +const whitespaceRe = /\s/; +function escapeRegex(value) { + return value.replace(reChars, "\\$&"); +} +/** + * Attributes that are case-insensitive in HTML. + * @see https://html.spec.whatwg.org/multipage/semantics-other.html#case-sensitivity-of-selectors + */ +const caseInsensitiveAttributes = new Set([ + "accept", + "accept-charset", + "align", + "alink", + "axis", + "bgcolor", + "charset", + "checked", + "clear", + "codetype", + "color", + "compact", + "declare", + "defer", + "dir", + "direction", + "disabled", + "enctype", + "face", + "frame", + "hreflang", + "http-equiv", + "lang", + "language", + "link", + "media", + "method", + "multiple", + "nohref", + "noresize", + "noshade", + "nowrap", + "readonly", + "rel", + "rev", + "rules", + "scope", + "scrolling", + "selected", + "shape", + "target", + "text", + "type", + "valign", + "valuetype", + "vlink", +]); +function shouldIgnoreCase(selector, options) { + return typeof selector.ignoreCase === "boolean" + ? selector.ignoreCase + : selector.ignoreCase === "quirks" + ? !!options.quirksMode + : !options.xmlMode && caseInsensitiveAttributes.has(selector.name); +} +/** + * Attribute selectors + */ +const attributeRules = { + equals(next, data, options) { + const { adapter } = options; + const { name } = data; + let { value } = data; + if (shouldIgnoreCase(data, options)) { + value = value.toLowerCase(); + return (element) => { + const attribute = adapter.getAttributeValue(element, name); + return (attribute != null && + attribute.length === value.length && + attribute.toLowerCase() === value && + next(element)); + }; + } + return (element) => adapter.getAttributeValue(element, name) === value && next(element); + }, + hyphen(next, data, options) { + const { adapter } = options; + const { name } = data; + let { value } = data; + const { length } = value; + if (shouldIgnoreCase(data, options)) { + value = value.toLowerCase(); + return function hyphenIC(element) { + const attribute = adapter.getAttributeValue(element, name); + return (attribute != null && + (attribute.length === length || + attribute.charAt(length) === "-") && + attribute.substr(0, length).toLowerCase() === value && + next(element)); + }; + } + return function hyphen(element) { + const attribute = adapter.getAttributeValue(element, name); + return (attribute != null && + (attribute.length === length || + attribute.charAt(length) === "-") && + attribute.substr(0, length) === value && + next(element)); + }; + }, + element(next, data, options) { + const { adapter } = options; + const { name, value } = data; + if (whitespaceRe.test(value)) { + return falseFunc; + } + const regex = new RegExp(`(?:^|\\s)${escapeRegex(value)}(?:$|\\s)`, shouldIgnoreCase(data, options) ? "i" : ""); + return function element(node) { + const attribute = adapter.getAttributeValue(node, name); + return (attribute != null && + attribute.length >= value.length && + regex.test(attribute) && + next(node)); + }; + }, + exists(next, { name }, { adapter }) { + return (element) => adapter.hasAttrib(element, name) && next(element); + }, + start(next, data, options) { + const { adapter } = options; + const { name } = data; + let { value } = data; + const { length } = value; + if (length === 0) { + return falseFunc; + } + if (shouldIgnoreCase(data, options)) { + value = value.toLowerCase(); + return (element) => { + const attribute = adapter.getAttributeValue(element, name); + return (attribute != null && + attribute.length >= length && + attribute.substr(0, length).toLowerCase() === value && + next(element)); + }; + } + return (element) => !!adapter.getAttributeValue(element, name)?.startsWith(value) && + next(element); + }, + end(next, data, options) { + const { adapter } = options; + const { name } = data; + let { value } = data; + const length = -value.length; + if (length === 0) { + return falseFunc; + } + if (shouldIgnoreCase(data, options)) { + value = value.toLowerCase(); + return (element) => adapter + .getAttributeValue(element, name) + ?.substr(length) + .toLowerCase() === value && next(element); + } + return (element) => !!adapter.getAttributeValue(element, name)?.endsWith(value) && + next(element); + }, + any(next, data, options) { + const { adapter } = options; + const { name, value } = data; + if (value === "") { + return falseFunc; + } + if (shouldIgnoreCase(data, options)) { + const regex = new RegExp(escapeRegex(value), "i"); + return function anyIC(element) { + const attribute = adapter.getAttributeValue(element, name); + return (attribute != null && + attribute.length >= value.length && + regex.test(attribute) && + next(element)); + }; + } + return (element) => !!adapter.getAttributeValue(element, name)?.includes(value) && + next(element); + }, + not(next, data, options) { + const { adapter } = options; + const { name } = data; + let { value } = data; + if (value === "") { + return (element) => !!adapter.getAttributeValue(element, name) && next(element); + } + if (shouldIgnoreCase(data, options)) { + value = value.toLowerCase(); + return (element) => { + const attribute = adapter.getAttributeValue(element, name); + return ((attribute == null || + attribute.length !== value.length || + attribute.toLowerCase() !== value) && + next(element)); + }; + } + return (element) => adapter.getAttributeValue(element, name) !== value && next(element); + }, +}; + +/** + * Find all elements matching the query. If not in XML mode, the query will ignore + * the contents of `<template>` elements. + * @param query - Function that returns true if the element matches the query. + * @param nodes - Nodes to query. If a node is an element, its children will be queried. + * @param options - Options for querying the document. + * @returns All matching elements. + */ +/** + * Find the first element matching the query. If not in XML mode, the query will ignore + * the contents of `<template>` elements. + * @param query - Function that returns true if the element matches the query. + * @param nodes - Nodes to query. If a node is an element, its children will be queried. + * @param options - Options for querying the document. + * @returns The first matching element, or null if there was no match. + */ +function findOne$1(query, nodes, options) { + const { adapter, xmlMode = false } = options; + /** Stack of the arrays we are looking at. */ + const nodeStack = [nodes]; + /** Stack of the indices within the arrays. */ + const indexStack = [0]; + for (;;) { + // First, check if the current array has any more elements to look at. + if (indexStack[0] >= nodeStack[0].length) { + // If we have no more arrays to look at, we are done. + if (nodeStack.length === 1) { + return null; + } + nodeStack.shift(); + indexStack.shift(); + // Loop back to the start to continue with the next array. + continue; + } + const element = nodeStack[0][indexStack[0]++]; + if (!adapter.isTag(element)) { + continue; + } + if (query(element)) { + return element; + } + if (xmlMode || adapter.getName(element) !== "template") { + /* + * Add the children to the stack. We are depth-first, so this is + * the next array we look at. + */ + const children = adapter.getChildren(element); + if (children.length > 0) { + nodeStack.unshift(children); + indexStack.unshift(0); + } + } + } +} +/** + * Get all element siblings after the provided node. + * @param element Element candidate being tested. + * @param adapter Adapter implementation used for DOM operations. + */ +function getNextSiblings(element, adapter) { + const siblings = adapter.getSiblings(element); + if (siblings.length <= 1) { + return []; + } + const elementIndex = siblings.indexOf(element); + if (elementIndex === -1 || elementIndex === siblings.length - 1) { + return []; + } + return siblings.slice(elementIndex + 1).filter(adapter.isTag); +} +/** + * Get the parent element of a node. + * @param node Node to inspect. + * @param adapter Adapter implementation used for DOM operations. + */ +function getElementParent(node, adapter) { + const parent = adapter.getParent(node); + return parent != null && adapter.isTag(parent) ? parent : null; +} + +/** + * Only text controls can be made read-only, since for other controls (such + * as checkboxes and buttons) there is no useful distinction between being + * read-only and being disabled. + * @see {@link https://html.spec.whatwg.org/multipage/input.html#attr-input-readonly} + */ +const textControl = "input:is([type=text i],[type=search i],[type=url i],[type=tel i],[type=email i],[type=password i],[type=date i],[type=month i],[type=week i],[type=time i],[type=datetime-local i],[type=number i])"; +/** + * Aliases are pseudos that are expressed as selectors. + */ +const aliases = { + // Links + "any-link": ":is(a, area, link)[href]", + link: ":any-link:not(:visited)", + // Forms + // https://html.spec.whatwg.org/multipage/scripting.html#disabled-elements + disabled: `:is( + :is(button, input, select, textarea, optgroup, option)[disabled], + optgroup[disabled] > option, + fieldset[disabled]:not(fieldset[disabled] legend:first-of-type *) + )`, + enabled: ":is(button, input, select, textarea, optgroup, option, fieldset):not(:disabled)", + checked: ":is(:is(input[type=radio], input[type=checkbox])[checked], :selected)", + required: ":is(input, select, textarea)[required]", + optional: ":is(input, select, textarea):not([required])", + "read-only": `[readonly]:is(textarea, ${textControl})`, + "read-write": `:not([readonly]):is(textarea, ${textControl})`, + // JQuery extensions + /** + * `:selected` matches option elements that have the `selected` attribute, + * or are the first option element in a select element that does not have + * the `multiple` attribute and does not have any option elements with the + * `selected` attribute. + * @see https://html.spec.whatwg.org/multipage/form-elements.html#concept-option-selectedness + */ + selected: "option:is([selected], select:not([multiple]):not(:has(> option[selected])) > :first-of-type)", + checkbox: "[type=checkbox]", + file: "[type=file]", + password: "[type=password]", + radio: "[type=radio]", + reset: "[type=reset]", + image: "[type=image]", + submit: "[type=submit]", + parent: ":not(:empty)", + header: ":is(h1, h2, h3, h4, h5, h6)", + button: ":is(button, input[type=button])", + input: ":is(input, textarea, select, button)", + text: "input:is(:not([type!='']), [type=text])", +}; + +/** + * Returns a function that checks if an elements index matches the given rule + * highly optimized to return the fastest solution. + * @param parsed A tuple [a, b], as returned by `parse`. + * @returns A highly optimized function that returns whether an index matches the nth-check. + * @example + * + * ```js + * const check = nthCheck.compile([2, 3]); + * + * check(0); // `false` + * check(1); // `false` + * check(2); // `true` + * check(3); // `false` + * check(4); // `true` + * check(5); // `false` + * check(6); // `true` + * ``` + */ +function compile$1(parsed) { + const a = parsed[0]; + // Subtract 1 from `b`, to convert from one- to zero-indexed. + const b = parsed[1] - 1; + /* + * When `b <= 0`, `a * n` won't be lead to any matches for `a < 0`. + * Besides, the specification states that no elements are + * matched when `a` and `b` are 0. + * + * `b < 0` here as we subtracted 1 from `b` above. + */ + if (b < 0 && a <= 0) + return falseFunc; + // When `a` is in the range -1..1, it matches any element (so only `b` is checked). + if (a === -1) + return (index) => index <= b; + if (a === 0) + return (index) => index === b; + // When `b <= 0` and `a === 1`, they match any element. + if (a === 1) + return b < 0 ? trueFunc : (index) => index >= b; + /* + * Otherwise, modulo can be used to check if there is a match. + * + * Modulo doesn't care about the sign, so let's use `a`s absolute value. + */ + const absA = Math.abs(a); + // Get `b mod a`, + a if this is negative. + const bModulo = ((b % absA) + absA) % absA; + return a > 1 + ? (index) => index >= b && index % absA === bModulo + : (index) => index <= b && index % absA === bModulo; +} + +// Following http://www.w3.org/TR/css3-selectors/#nth-child-pseudo +// Whitespace as per https://www.w3.org/TR/selectors-3/#lex is " \t\r\n\f" +const whitespace = new Set([9, 10, 12, 13, 32]); +const ZERO = "0".charCodeAt(0); +const NINE = "9".charCodeAt(0); +/** + * Parses an expression. + * @param formula CSS nth-formula to parse. + * @throws {Error} An `Error` if parsing fails. + * @returns An array containing the integer step size and the integer offset of the nth rule. + * @example nthCheck.parse("2n+3"); // returns [2, 3] + */ +function parse$2(formula) { + formula = formula.trim().toLowerCase(); + switch (formula) { + case "even": { + return [2, 0]; + } + case "odd": { + return [2, 1]; + } + } + // Parse [ ['-'|'+']? INTEGER? {N} [ S* ['-'|'+'] S* INTEGER ]? + let index = 0; + let a = 0; + let sign = readSign(); + let number = readNumber(); + if (index < formula.length && formula.charAt(index) === "n") { + index++; + a = sign * (number ?? 1); + skipWhitespace(); + if (index < formula.length) { + sign = readSign(); + skipWhitespace(); + number = readNumber(); + } + else { + sign = number = 0; + } + } + // Throw if there is anything else + if (number === null || index < formula.length) { + throw new Error(`n-th rule couldn't be parsed ('${formula}')`); + } + return [a, sign * number]; + function readSign() { + switch (formula.charAt(index)) { + case "-": { + index++; + return -1; + } + case "+": { + index++; + break; + } + } + return 1; + } + function readNumber() { + const start = index; + let value = 0; + while (index < formula.length && + formula.charCodeAt(index) >= ZERO && + formula.charCodeAt(index) <= NINE) { + value = value * 10 + (formula.charCodeAt(index) - ZERO); + index++; + } + // Return `null` if we didn't read anything. + return index === start ? null : value; + } + function skipWhitespace() { + while (index < formula.length && + whitespace.has(formula.charCodeAt(index))) { + index++; + } + } +} + +/** + * Parses and compiles a formula to a highly optimized function. + * Combination of {@link parse} and {@link compile}. + * + * If the formula doesn't match any elements, + * it returns [`boolbase`](https://github.com/fb55/boolbase)'s `falseFunc`. + * Otherwise, a function accepting an _index_ is returned, which returns + * whether or not the passed _index_ matches the formula. + * + * Note: The nth-rule starts counting at `1`, the returned function at `0`. + * @param formula The formula to compile. + * @example + * const check = nthCheck("2n+3"); + * + * check(0); // `false` + * check(1); // `false` + * check(2); // `true` + * check(3); // `false` + * check(4); // `true` + * check(5); // `false` + * check(6); // `true` + */ +function nthCheck(formula) { + return compile$1(parse$2(formula)); +} + +/** + * Some selectors such as `:contains` and (non-relative) `:has` will only be + * able to match elements if their parents match the selector (as they contain + * a subset of the elements that the parent contains). + * + * This function wraps the given `matches` function in a function that caches + * the results of the parent elements, so that the `matches` function only + * needs to be called once for each subtree. + * @param next Matcher to run after this matcher succeeds. + * @param options Configuration object for cache behavior. + * @param options.adapter Adapter implementation used for DOM access. + * @param options.cacheResults Whether results should be memoized by input root. + * @param matches Compiled matcher function to wrap with caching. + */ +function cacheParentResults(next, { adapter, cacheResults }, matches) { + if (cacheResults === false || typeof WeakMap === "undefined") { + return (element) => next(element) && matches(element); + } + // Use a cache to avoid re-checking children of an element. + // @ts-expect-error `Node` is not extending object + const resultCache = new WeakMap(); + function addResultToCache(element) { + const result = matches(element); + resultCache.set(element, result); + return result; + } + return function cachedMatcher(element) { + if (!next(element)) { + return false; + } + if (resultCache.has(element)) { + return resultCache.get(element) ?? false; + } + // Check all of the element's parents. + let node = element; + do { + const parent = getElementParent(node, adapter); + if (parent === null) { + return addResultToCache(element); + } + node = parent; + } while (!resultCache.has(node)); + return resultCache.get(node) ? addResultToCache(element) : false; + }; +} + +/** + * Create a copy of options, omitting `context` and `rootFunc`. + * + * This is used when compiling nested selectors (e.g. inside `:is`, `:not`, + * `:nth-child(… of S)`) so that the parent compilation state doesn't leak. + */ +function copyOptions(options) { + // Omit context and rootFunc so parent compilation state doesn't leak. + const { context: _, rootFunc: __, ...copied } = options; + return copied; +} + +/** + * RFC 4647 extended filtering with pre-split subtags. + * @param tag - Lowercased subtags of the element's language value. + * @param range - Lowercased subtags of the language range to match against. + */ +function extendedFilter(tag, range) { + if (range[0] !== "*" && range[0] !== tag[0]) + return false; + let tagIndex = 1; + for (let rangeIndex = 1; rangeIndex < range.length; rangeIndex++) { + if (range[rangeIndex] === "*") + continue; + // Skip non-singleton tag subtags until we find a match. + while (tagIndex < tag.length && tag[tagIndex] !== range[rangeIndex]) { + if (tag[tagIndex++].length <= 1) + return false; + } + if (tagIndex >= tag.length) + return false; + tagIndex++; + } + return true; +} +/** @see {@link https://www.w3.org/TR/selectors-4/#the-nth-child-pseudo} */ +const nthOfRegex = /^(.+?)\s+of\s+(.+)$/is; +function compileNth(reverse, ofType) { + return function nth(next, rule, options, context, compileToken) { + const { adapter, equals } = options; + const ofMatch = ofType ? null : rule.match(nthOfRegex); + const nthCheck$1 = nthCheck(ofMatch ? ofMatch[1].trim() : rule); + if (nthCheck$1 === falseFunc) + return falseFunc; + const ofSelector = ofMatch && compileToken + ? compileToken(parse$3(ofMatch[2].trim()), copyOptions(options), context) + : undefined; + if (ofSelector === falseFunc) + return falseFunc; + if (nthCheck$1 === trueFunc && !ofSelector) { + return (element) => getElementParent(element, adapter) !== null && next(element); + } + const shouldCount = ofSelector + ? (_element, sibling) => ofSelector(sibling) + : ofType + ? (element, sibling) => adapter.getName(sibling) === adapter.getName(element) + : trueFunc; + if (reverse) { + return function nthLast(element) { + if (ofSelector && !ofSelector(element)) + return false; + const siblings = adapter.getSiblings(element); + let pos = 0; + for (let index = siblings.length - 1; index >= 0; index--) { + const sibling = siblings[index]; + if (equals(element, sibling)) + break; + if (adapter.isTag(sibling) && shouldCount(element, sibling)) + pos++; + } + return nthCheck$1(pos) && next(element); + }; + } + return function nth(element) { + if (ofSelector && !ofSelector(element)) + return false; + const siblings = adapter.getSiblings(element); + let pos = 0; + for (const sibling of siblings) { + if (equals(element, sibling)) + break; + if (adapter.isTag(sibling) && shouldCount(element, sibling)) + pos++; + } + return nthCheck$1(pos) && next(element); + }; + }; +} +/** + * Pre-compiled pseudo filters. + */ +const filters = { + contains(next, text, options) { + const { getText } = options.adapter; + return cacheParentResults(next, options, (element) => getText(element).includes(text)); + }, + icontains(next, text, options) { + const itext = text.toLowerCase(); + const { getText } = options.adapter; + return cacheParentResults(next, options, (element) => getText(element).toLowerCase().includes(itext)); + }, + // Location specific methods + "nth-child": compileNth(false, false), + "nth-last-child": compileNth(true, false), + "nth-of-type": compileNth(false, true), + "nth-last-of-type": compileNth(true, true), + // TODO determine the actual root element + root(next, _rule, { adapter }) { + return (element) => getElementParent(element, adapter) === null && next(element); + }, + scope(next, rule, options, context) { + const { equals } = options; + if (!context || context.length === 0) { + // Equivalent to :root + return filters["root"](next, rule, options); + } + if (context.length === 1) { + // NOTE: can't be unpacked, as :has uses this for side-effects + return (element) => equals(context[0], element) && next(element); + } + return (element) => context.includes(element) && next(element); + }, + lang(next, code, { adapter }) { + const ranges = code + .split(",") + .map((r) => r.trim()) + .filter((r) => r.length > 0) + .map((r) => r + .replace(/^['"]|['"]$/g, "") + .toLowerCase() + .split("-")); + return function lang(element) { + let node = element; + while (node != null) { + const value = adapter.getAttributeValue(node, "xml:lang") ?? + adapter.getAttributeValue(node, "lang"); + if (value != null) { + if (!value) { + return ranges.some((r) => r[0] === "") && next(element); + } + const tag = value.toLowerCase().split("-"); + return (ranges.some((r) => extendedFilter(tag, r)) && + next(element)); + } + const parent = adapter.getParent(node); + node = + parent != null && adapter.isTag(parent) + ? parent + : null; + } + return ranges.some((r) => r[0] === "") && next(element); + }; + }, + hover: dynamicStatePseudo("isHovered"), + visited: dynamicStatePseudo("isVisited"), + active: dynamicStatePseudo("isActive"), +}; +/** + * Dynamic state pseudos. These depend on optional Adapter methods. + * @param name The name of the adapter method to call. + * @returns Pseudo for the `filters` object. + */ +function dynamicStatePseudo(name) { + return function dynamicPseudo(next, _rule, { adapter }) { + const filterFunction = adapter[name]; + if (typeof filterFunction !== "function") { + return falseFunc; + } + return function active(element) { + return filterFunction(element) && next(element); + }; + }; +} + +/** + * CSS limits the characters considered as whitespace to space, tab & line + * feed. We add carriage returns as htmlparser2 doesn't normalize them to + * line feeds. + * @see {@link https://www.w3.org/TR/css-text-3/#white-space} + */ +const isDocumentWhiteSpace = /^[ \t\r\n]*$/; +// While filters are precompiled, pseudos get called when they are needed +/** Runtime pseudo selector implementations. */ +const pseudos = { + empty(element, { adapter }) { + const children = adapter.getChildren(element); + return ( + // First, make sure the tag does not have any element children. + children.every((element) => !adapter.isTag(element)) && + // Then, check that the text content is only whitespace. + children.every((element) => + // FIXME: `getText` call is potentially expensive. + isDocumentWhiteSpace.test(adapter.getText(element)))); + }, + "first-child"(element, { adapter, equals }) { + if (adapter.prevElementSibling) { + return adapter.prevElementSibling(element) == null; + } + const firstChild = adapter + .getSiblings(element) + .find((sibling) => adapter.isTag(sibling)); + return firstChild != null && equals(element, firstChild); + }, + "last-child"(element, { adapter, equals }) { + const siblings = adapter.getSiblings(element); + for (let index = siblings.length - 1; index >= 0; index--) { + if (equals(element, siblings[index])) { + return true; + } + if (adapter.isTag(siblings[index])) { + break; + } + } + return false; + }, + "first-of-type"(element, { adapter, equals }) { + const siblings = adapter.getSiblings(element); + const elementName = adapter.getName(element); + for (const currentSibling of siblings) { + if (equals(element, currentSibling)) { + return true; + } + if (adapter.isTag(currentSibling) && + adapter.getName(currentSibling) === elementName) { + break; + } + } + return false; + }, + "last-of-type"(element, { adapter, equals }) { + const siblings = adapter.getSiblings(element); + const elementName = adapter.getName(element); + for (let index = siblings.length - 1; index >= 0; index--) { + const currentSibling = siblings[index]; + if (equals(element, currentSibling)) { + return true; + } + if (adapter.isTag(currentSibling) && + adapter.getName(currentSibling) === elementName) { + break; + } + } + return false; + }, + "only-of-type"(element, { adapter, equals }) { + const elementName = adapter.getName(element); + return adapter + .getSiblings(element) + .every((sibling) => equals(element, sibling) || + !adapter.isTag(sibling) || + adapter.getName(sibling) !== elementName); + }, + "only-child"(element, { adapter, equals }) { + return adapter + .getSiblings(element) + .every((sibling) => equals(element, sibling) || !adapter.isTag(sibling)); + }, +}; +/** + * Validate pseudo selector argument arity. + * @param pseudoClassCondition Pseudo-function implementation to wrap. + * @param name Name of the pseudo selector. + * @param subselect Subselector passed to the pseudo-function. + * @param argumentIndex Index of the argument parser to apply. + */ +function verifyPseudoArguments(pseudoClassCondition, name, subselect, argumentIndex) { + if (subselect === null) { + if (pseudoClassCondition.length > argumentIndex) { + throw new Error(`Pseudo-class :${name} requires an argument`); + } + } + else if (pseudoClassCondition.length === argumentIndex) { + throw new Error(`Pseudo-class :${name} doesn't have any arguments`); + } +} + +/** + * Check whether a selector token performs traversal. + * @param token Selector token(s) to compile. + */ +function isTraversal(token) { + return token.type === "_flexibleDescendant" || isTraversal$1(token); +} +/** + * Sort the parts of the passed selector, as there is potential for + * optimization (some types of selectors are faster than others). + * @param array Selector to sort + */ +function sortRules(array) { + const ratings = array.map(getQuality); + for (let index = 1; index < array.length; index++) { + const procNew = ratings[index]; + if (procNew < 0) { + continue; + } + // Use insertion sort to move the token to the correct position. + for (let currentIndex = index; currentIndex > 0 && procNew < ratings[currentIndex - 1]; currentIndex--) { + const token = array[currentIndex]; + array[currentIndex] = array[currentIndex - 1]; + array[currentIndex - 1] = token; + ratings[currentIndex] = ratings[currentIndex - 1]; + ratings[currentIndex - 1] = procNew; + } + } +} +function getAttributeQuality(token) { + switch (token.action) { + case AttributeAction.Exists: { + return 10; + } + case AttributeAction.Equals: { + // Prefer ID selectors (eg. #ID) + return token.name === "id" ? 9 : 8; + } + case AttributeAction.Not: { + return 7; + } + case AttributeAction.Start: { + return 6; + } + case AttributeAction.End: { + return 6; + } + case AttributeAction.Any: { + return 5; + } + case AttributeAction.Hyphen: { + return 4; + } + case AttributeAction.Element: { + return 3; + } + } +} +/** + * Determine the quality of the passed token. The higher the number, the + * faster the token is to execute. + * @param token Token to get the quality of. + * @returns The token's quality. + */ +function getQuality(token) { + switch (token.type) { + case SelectorType.Universal: { + return 50; + } + case SelectorType.Tag: { + return 30; + } + case SelectorType.Attribute: { + return Math.floor(getAttributeQuality(token) / + // `ignoreCase` adds some overhead, half the result if applicable. + (token.ignoreCase ? 2 : 1)); + } + case SelectorType.Pseudo: { + return token.data + ? token.name === "has" || + token.name === "contains" || + token.name === "icontains" + ? // Expensive in any case — run as late as possible. + 0 + : Array.isArray(token.data) + ? // Eg. `:is`, `:not` + Math.max( + // If we have traversals, try to avoid executing this selector + 0, Math.min(...token.data.map((d) => Math.min(...d.map(getQuality))))) + : 2 + : 3; + } + default: { + return -1; + } + } +} +/** + * Check whether a token or nested token includes `:scope`. + * @param t Selector token under inspection. + */ +function includesScopePseudo(t) { + return (t.type === SelectorType.Pseudo && + (t.name === "scope" || + (Array.isArray(t.data) && + t.data.some((data) => data.some(includesScopePseudo))))); +} + +/** Used as a placeholder for :has. Will be replaced with the actual element. */ +const PLACEHOLDER_ELEMENT = {}; +/** + * Check if the selector has any properties that rely on the current element. + * If not, we can cache the result of the selector. + * + * We can't cache selectors that start with a traversal (e.g. `>`, `+`, `~`), + * or include a `:scope`. + * @param selector - The selector to check. + * @returns Whether the selector has any properties that rely on the current element. + */ +function hasDependsOnCurrentElement(selector) { + return selector.some((sel) => sel.length > 0 && + (isTraversal(sel[0]) || sel.some(includesScopePseudo))); +} +const is$1 = (next, token, options, context, compileToken) => { + const compiledToken = compileToken(token, copyOptions(options), context); + return compiledToken === trueFunc + ? next + : compiledToken === falseFunc + ? falseFunc + : (element) => compiledToken(element) && next(element); +}; +/* + * :not, :has, :is, :matches and :where have to compile selectors + * doing this in src/pseudos.ts would lead to circular dependencies, + * so we add them here + */ +/** Pseudo selectors that compile nested selectors. */ +const subselects = { + is: is$1, + /** + * `:matches` and `:where` are aliases for `:is`. + */ + matches: is$1, + where: is$1, + not(next, token, options, context, compileToken) { + const compiledToken = compileToken(token, copyOptions(options), context); + return compiledToken === falseFunc + ? next + : compiledToken === trueFunc + ? falseFunc + : (element) => !compiledToken(element) && next(element); + }, + has(next, subselect, options, _context, compileToken) { + const { adapter } = options; + const copiedOptions = copyOptions(options); + copiedOptions.relativeSelector = true; + const context = subselect.some((s) => s.some(isTraversal)) + ? // Used as a placeholder. Will be replaced with the actual element. + [PLACEHOLDER_ELEMENT] + : undefined; + const skipCache = hasDependsOnCurrentElement(subselect); + const compiled = compileToken(subselect, copiedOptions, context); + if (compiled === falseFunc) { + return falseFunc; + } + // If `compiled` is `trueFunc`, we can skip this. + if (context && compiled !== trueFunc) { + return skipCache + ? (element) => { + if (!next(element)) { + return false; + } + context[0] = element; + const childs = adapter.getChildren(element); + return (findOne$1(compiled, compiled.shouldTestNextSiblings + ? [ + ...childs, + ...getNextSiblings(element, adapter), + ] + : childs, options) !== null); + } + : cacheParentResults(next, options, (element) => { + context[0] = element; + return (findOne$1(compiled, adapter.getChildren(element), options) !== null); + }); + } + const hasOne = (element) => findOne$1(compiled, adapter.getChildren(element), options) !== null; + return skipCache + ? (element) => next(element) && hasOne(element) + : cacheParentResults(next, options, hasOne); + }, +}; + +/* + * Pseudo selectors + * + * Pseudo selectors are available in three forms: + * + * 1. Filters are called when the selector is compiled and return a function + * that has to return either false, or the results of `next()`. + * 2. Pseudos are called on execution. They have to return a boolean. + * 3. Subselects work like filters, but have an embedded selector that will be run separately. + * + * Filters are great if you want to do some pre-processing, or change the call order + * of `next()` and your code. + * Pseudos should be used to implement simple checks. + */ +/** + * Compile a pseudo selector into an executable query function. + * @param next Matcher to run after this matcher succeeds. + * @param selector Selector used to match elements. + * @param options Options that control this operation. + * @param context Context nodes used to scope selector matching. + * @param compileToken Function used to compile nested selector tokens. + */ +function compilePseudoSelector(next, selector, options, context, compileToken) { + const { name, data } = selector; + if (Array.isArray(data)) { + if (!(name in subselects)) { + throw new Error(`Unknown pseudo-class :${name}(${data})`); + } + return subselects[name](next, data, options, context, compileToken); + } + const userPseudo = options.pseudos?.[name]; + const stringPseudo = typeof userPseudo === "string" ? userPseudo : aliases[name]; + if (typeof stringPseudo === "string") { + if (data != null) { + throw new Error(`Pseudo ${name} doesn't have any arguments`); + } + // The alias has to be parsed here, to make sure options are respected. + const alias = parse$3(stringPseudo); + return subselects["is"](next, alias, options, context, compileToken); + } + if (typeof userPseudo === "function") { + verifyPseudoArguments(userPseudo, name, data, 1); + return (element) => userPseudo(element, data) && next(element); + } + if (name in filters) { + return filters[name](next, data, options, context, compileToken); + } + if (name in pseudos) { + const pseudo = pseudos[name]; + verifyPseudoArguments(pseudo, name, data, 2); + return (element) => pseudo(element, options, data) && next(element); + } + throw new Error(`Unknown pseudo-class :${name}`); +} + +/* + * All available rules + */ +/** + * Compile a single selector token. + * @param next Matcher to run after this matcher succeeds. + * @param selector Selector used to match elements. + * @param options Options that control this operation. + * @param context Context nodes used to scope selector matching. + * @param compileToken Function used to compile nested selector tokens. + * @param hasExpensiveSubselector Whether the selector contains expensive subselectors. + */ +function compileGeneralSelector(next, selector, options, context, compileToken, hasExpensiveSubselector) { + const { adapter, equals, cacheResults } = options; + switch (selector.type) { + case SelectorType.PseudoElement: { + throw new Error("Pseudo-elements are not supported by css-select"); + } + case SelectorType.ColumnCombinator: { + throw new Error("Column combinators are not yet supported by css-select"); + } + case SelectorType.Attribute: { + if (selector.namespace != null) { + throw new Error("Namespaced attributes are not yet supported by css-select"); + } + if (!options.xmlMode || options.lowerCaseAttributeNames) { + selector.name = selector.name.toLowerCase(); + } + return attributeRules[selector.action](next, selector, options); + } + case SelectorType.Pseudo: { + return compilePseudoSelector(next, selector, options, context, compileToken); + } + // Tags + case SelectorType.Tag: { + if (selector.namespace != null) { + throw new Error("Namespaced tag names are not yet supported by css-select"); + } + let { name } = selector; + if (!options.xmlMode || options.lowerCaseTags) { + name = name.toLowerCase(); + } + return function tag(element) { + return adapter.getName(element) === name && next(element); + }; + } + // Traversal + case SelectorType.Descendant: { + if (!hasExpensiveSubselector || + cacheResults === false || + typeof WeakMap === "undefined") { + return function descendant(element) { + let current = element; + while ((current = getElementParent(current, adapter))) { + if (next(current)) { + return true; + } + } + return false; + }; + } + const resultCache = new WeakMap(); + return function cachedDescendant(element) { + let current = element; + let result; + while ((current = getElementParent(current, adapter))) { + const cached = resultCache.get(current); + if (cached === undefined) { + result ??= { matches: false }; + result.matches = next(current); + resultCache.set(current, result); + if (result.matches) { + return true; + } + } + else { + if (result) { + result.matches = cached.matches; + } + return cached.matches; + } + } + return false; + }; + } + case "_flexibleDescendant": { + // Include element itself, only used while querying an array + return function flexibleDescendant(element) { + let current = element; + do { + if (next(current)) { + return true; + } + current = getElementParent(current, adapter); + } while (current); + return false; + }; + } + case SelectorType.Parent: { + return function parent(element) { + return adapter + .getChildren(element) + .some((element) => adapter.isTag(element) && next(element)); + }; + } + case SelectorType.Child: { + return function child(element) { + const parent = getElementParent(element, adapter); + return parent !== null && next(parent); + }; + } + case SelectorType.Sibling: { + return function sibling(element) { + const siblings = adapter.getSiblings(element); + for (const currentSibling of siblings) { + if (equals(element, currentSibling)) { + break; + } + if (adapter.isTag(currentSibling) && next(currentSibling)) { + return true; + } + } + return false; + }; + } + case SelectorType.Adjacent: { + if (adapter.prevElementSibling) { + return function adjacent(element) { + // biome-ignore lint/style/noNonNullAssertion: checked by if statement + const previous = adapter.prevElementSibling(element); + return previous != null && next(previous); + }; + } + return function adjacent(element) { + const siblings = adapter.getSiblings(element); + let lastElement; + for (const currentSibling of siblings) { + if (equals(element, currentSibling)) { + break; + } + if (adapter.isTag(currentSibling)) { + lastElement = currentSibling; + } + } + return !!lastElement && next(lastElement); + }; + } + case SelectorType.Universal: { + if (selector.namespace != null && selector.namespace !== "*") { + throw new Error("Namespaced universal selectors are not yet supported by css-select"); + } + return next; + } + } +} + +const DESCENDANT_TOKEN = { type: SelectorType.Descendant }; +const FLEXIBLE_DESCENDANT_TOKEN = { + type: "_flexibleDescendant", +}; +const SCOPE_TOKEN = { + type: SelectorType.Pseudo, + name: "scope", + data: null, +}; +/* + * CSS 4 Spec (Draft): 3.4.1. Absolutizing a Relative Selector + * http://www.w3.org/TR/selectors4/#absolutizing + */ +function absolutize(token, { adapter }, context) { + // TODO Use better check if the context is a document + const hasContext = !!context?.every((element) => element === PLACEHOLDER_ELEMENT || + (adapter.isTag(element) && + getElementParent(element, adapter) !== null)); + for (const t of token) { + if (t.length > 0 && + isTraversal(t[0]) && + t[0].type !== SelectorType.Descendant) ; + else if (hasContext && !t.some(includesScopePseudo)) { + t.unshift(DESCENDANT_TOKEN); + } + else { + continue; + } + t.unshift(SCOPE_TOKEN); + } +} +/** + * Compile a parsed selector token into an executable query function. + * @param token Selector token(s) to compile. + * @param options Options that control this operation. + * @param compilationContext Compilation context for relative selector handling. + */ +function compileToken(token, options, compilationContext) { + for (const rules of token) { + sortRules(rules); + } + const { context = compilationContext, rootFunc: rootFunction = trueFunc, } = options; + const isArrayContext = Array.isArray(context); + const finalContext = context && (Array.isArray(context) ? context : [context]); + // Check if the selector is relative + if (options.relativeSelector !== false) { + absolutize(token, options, finalContext); + } + else if (token.some((t) => t.length > 0 && isTraversal(t[0]))) { + throw new Error("Relative selectors are not allowed when the `relativeSelector` option is disabled"); + } + let shouldTestNextSiblings = false; + let query = falseFunc; + combineLoop: for (const rules of token) { + if (rules.length >= 2) { + const [first, second] = rules; + if (first.type !== SelectorType.Pseudo || first.name !== "scope") ; + else if (isArrayContext && + second.type === SelectorType.Descendant) { + rules[1] = FLEXIBLE_DESCENDANT_TOKEN; + } + else if (second.type === SelectorType.Adjacent || + second.type === SelectorType.Sibling) { + shouldTestNextSiblings = true; + } + } + let next = rootFunction; + let hasExpensiveSubselector = false; + for (const rule of rules) { + next = compileGeneralSelector(next, rule, options, finalContext, compileToken, hasExpensiveSubselector); + const quality = getQuality(rule); + if (quality === 0) { + hasExpensiveSubselector = true; + } + // If the sub-selector won't match any elements, skip it. + if (next === falseFunc) { + continue combineLoop; + } + } + // If we have a function that always returns true, we can stop here. + if (next === rootFunction) { + return rootFunction; + } + query = query === falseFunc ? next : or(query, next); + } + query.shouldTestNextSiblings = shouldTestNextSiblings; + return query; +} +function or(a, b) { + return (element) => a(element) || b(element); +} + +const defaultEquals = (a, b) => a === b; +const defaultOptions = { + adapter: { ...DomUtils, isTag: isTag$1 }, + equals: defaultEquals, +}; +function convertOptionFormats(options) { + /* + * We force one format of options to the other one. + */ + // @ts-expect-error Default options may have incompatible `Node` / `ElementNode`. + const finalOptions = options ?? defaultOptions; + // @ts-expect-error Same as above. + finalOptions.adapter ??= defaultOptions.adapter; + // @ts-expect-error `equals` does not exist on `Options` + finalOptions.equals ??= finalOptions.adapter?.equals ?? defaultEquals; + return finalOptions; +} +/** + * Compiles a selector to an executable function. + * + * The returned function checks if each passed node is an element. Use + * `_compileUnsafe` to skip this check. + * @param selector Selector to compile. + * @param options Compilation options. + * @param context Optional context for the selector. + */ +function compile(selector, options, context) { + const convertedOptions = convertOptionFormats(options); + const next = _compileUnsafe(selector, convertedOptions, context); + return next === falseFunc + ? falseFunc + : (element) => convertedOptions.adapter.isTag(element) && next(element); +} +/** + * Like `compile`, but does not add a check if elements are tags. + * @param selector Selector used to match elements. + * @param options Options that control this operation. + * @param context Context nodes used to scope selector matching. + */ +function _compileUnsafe(selector, options, context) { + return compileToken(typeof selector === "string" ? parse$3(selector) : selector, convertOptionFormats(options), context); +} +/** + * Tests whether or not an element is matched by query. + * @template Node The generic Node type for the DOM adapter being used. + * @template ElementNode The Node type for elements for the DOM adapter being used. + * @param element The element to test if it matches the query. + * @param query can be either a CSS selector string or a compiled query function. + * @param [options] options for querying the document. + * @see compile for supported selector queries. + * @returns Whether the element matches the query. + */ +function is(element, query, options) { + return (typeof query === "function" ? query : compile(query, options))(element); +} + +const {isArray} = Array; + +/* c8 ignore start */ +const isTag = ({nodeType}) => nodeType === ELEMENT_NODE; + +const existsOne = (test, elements) => elements.some( + element => isTag(element) && ( + test(element) || + existsOne(test, getChildren(element)) + ) +); + +const getAttributeValue = (element, name) => name === 'class' ? + element.classList.value : element.getAttribute(name); + +const getChildren = ({childNodes}) => childNodes; + +const getName = (element) => { + const {localName} = element; + return ignoreCase(element) ? localName.toLowerCase() : localName; +}; + +const getParent = ({parentNode}) => parentNode; + +const getSiblings = element => { + const {parentNode} = element; + return parentNode ? getChildren(parentNode) : element; +}; + +const getText = node => { + if (isArray(node)) + return node.map(getText).join(''); + if (isTag(node)) + return getText(getChildren(node)); + if (node.nodeType === TEXT_NODE) + return node.data; + return ''; +}; + +const hasAttrib = (element, name) => element.hasAttribute(name); + +const removeSubsets = nodes => { + let {length} = nodes; + while (length--) { + const node = nodes[length]; + if (length && -1 < nodes.lastIndexOf(node, length - 1)) { + nodes.splice(length, 1); + continue; + } + for (let {parentNode} = node; parentNode; parentNode = parentNode.parentNode) { + if (nodes.includes(parentNode)) { + nodes.splice(length, 1); + break; + } + } + } + return nodes; +}; + +const findAll = (test, nodes) => { + const matches = []; + for (const node of nodes) { + if (isTag(node)) { + if (test(node)) + matches.push(node); + matches.push(...findAll(test, getChildren(node))); + } + } + return matches; +}; + +const findOne = (test, nodes) => { + for (let node of nodes) + if (test(node) || (node = findOne(test, getChildren(node)))) + return node; + return null; +}; +/* c8 ignore stop */ + +const adapter = { + isTag, + existsOne, + getAttributeValue, + getChildren, + getName, + getParent, + getSiblings, + getText, + hasAttrib, + removeSubsets, + findAll, + findOne +}; + +const prepareMatch = (element, selectors) => compile( + selectors, + { + context: selectors.includes(':scope') ? element : void 0, + xmlMode: !ignoreCase(element), + adapter + } +); + +const matches = (element, selectors) => is( + element, + selectors, + { + strict: true, + context: selectors.includes(':scope') ? element : void 0, + xmlMode: !ignoreCase(element), + adapter + } +); + +/** + * @implements globalThis.Text + */ +let Text$1 = class Text extends CharacterData$1 { + constructor(ownerDocument, data = '') { + super(ownerDocument, '#text', TEXT_NODE, data); + } + + get wholeText() { + const text = []; + let {previousSibling, nextSibling} = this; + while (previousSibling) { + if (previousSibling.nodeType === TEXT_NODE) + text.unshift(previousSibling[VALUE]); + else + break; + previousSibling = previousSibling.previousSibling; + } + text.push(this[VALUE]); + while (nextSibling) { + if (nextSibling.nodeType === TEXT_NODE) + text.push(nextSibling[VALUE]); + else + break; + nextSibling = nextSibling.nextSibling; + } + return text.join(''); + } + + cloneNode() { + const {ownerDocument, [VALUE]: data} = this; + return new Text(ownerDocument, data); + } + + toString() { return escape(this[VALUE]); } +}; + +// https://dom.spec.whatwg.org/#interface-parentnode +// Document, DocumentFragment, Element + + +const isNode = node => node instanceof Node$1; + +const insert = (parentNode, child, nodes) => { + const {ownerDocument} = parentNode; + for (const node of nodes) + parentNode.insertBefore( + isNode(node) ? node : new Text$1(ownerDocument, node), + child + ); +}; + +/** @typedef { import('../interface/element.js').Element & { + [typeof NEXT]: NodeStruct, + [typeof PREV]: NodeStruct, + [typeof START]: NodeStruct, + nodeType: typeof ATTRIBUTE_NODE | typeof DOCUMENT_FRAGMENT_NODE | typeof ELEMENT_NODE | typeof TEXT_NODE | typeof NODE_END | typeof COMMENT_NODE | typeof CDATA_SECTION_NODE, + ownerDocument: Document, + parentNode: ParentNode, +}} NodeStruct */ + +class ParentNode extends Node$1 { + constructor(ownerDocument, localName, nodeType) { + super(ownerDocument, localName, nodeType); + this[PRIVATE] = null; + /** @type {NodeStruct} */ + this[NEXT] = this[END] = { + [NEXT]: null, + [PREV]: this, + [START]: this, + nodeType: NODE_END, + ownerDocument: this.ownerDocument, + parentNode: null + }; + } + + get childNodes() { + const childNodes = new NodeList; + let {firstChild} = this; + while (firstChild) { + childNodes.push(firstChild); + firstChild = nextSibling(firstChild); + } + return childNodes; + } + + get children() { + const children = new NodeList; + let {firstElementChild} = this; + while (firstElementChild) { + children.push(firstElementChild); + firstElementChild = nextElementSibling$1(firstElementChild); + } + return children; + } + + /** + * @returns {NodeStruct | null} + */ + get firstChild() { + let {[NEXT]: next, [END]: end} = this; + while (next.nodeType === ATTRIBUTE_NODE) + next = next[NEXT]; + return next === end ? null : next; + } + + /** + * @returns {NodeStruct | null} + */ + get firstElementChild() { + let {firstChild} = this; + while (firstChild) { + if (firstChild.nodeType === ELEMENT_NODE) + return firstChild; + firstChild = nextSibling(firstChild); + } + return null; + } + + get lastChild() { + const prev = this[END][PREV]; + switch (prev.nodeType) { + case NODE_END: + return prev[START]; + case ATTRIBUTE_NODE: + return null; + } + return prev === this ? null : prev; + } + + get lastElementChild() { + let {lastChild} = this; + while (lastChild) { + if (lastChild.nodeType === ELEMENT_NODE) + return lastChild; + lastChild = previousSibling(lastChild); + } + return null; + } + + get childElementCount() { + return this.children.length; + } + + prepend(...nodes) { + insert(this, this.firstChild, nodes); + } + + append(...nodes) { + insert(this, this[END], nodes); + } + + replaceChildren(...nodes) { + let {[NEXT]: next, [END]: end} = this; + while (next !== end && next.nodeType === ATTRIBUTE_NODE) + next = next[NEXT]; + while (next !== end) { + const after = getEnd(next)[NEXT]; + next.remove(); + next = after; + } + if (nodes.length) + insert(this, end, nodes); + } + + getElementsByClassName(className) { + const elements = new NodeList; + let {[NEXT]: next, [END]: end} = this; + while (next !== end) { + if ( + next.nodeType === ELEMENT_NODE && + next.hasAttribute('class') && + next.classList.has(className) + ) + elements.push(next); + next = next[NEXT]; + } + return elements; + } + + getElementsByTagName(tagName) { + const elements = new NodeList; + let {[NEXT]: next, [END]: end} = this; + while (next !== end) { + if (next.nodeType === ELEMENT_NODE && ( + next.localName === tagName || + localCase(next) === tagName + )) + elements.push(next); + next = next[NEXT]; + } + return elements; + } + + querySelector(selectors) { + const matches = prepareMatch(this, selectors); + let {[NEXT]: next, [END]: end} = this; + while (next !== end) { + if (next.nodeType === ELEMENT_NODE && matches(next)) + return next; + next = next.nodeType === ELEMENT_NODE && next.localName === 'template' ? next[END] : next[NEXT]; + } + return null; + } + + querySelectorAll(selectors) { + const matches = prepareMatch(this, selectors); + const elements = new NodeList; + let {[NEXT]: next, [END]: end} = this; + while (next !== end) { + if (next.nodeType === ELEMENT_NODE && matches(next)) + elements.push(next); + next = next.nodeType === ELEMENT_NODE && next.localName === 'template' ? next[END] : next[NEXT]; + } + return elements; + } + + appendChild(node) { + return this.insertBefore(node, this[END]); + } + + contains(node) { + let parentNode = node; + while (parentNode && parentNode !== this) + parentNode = parentNode.parentNode; + return parentNode === this; + } + + insertBefore(node, before = null) { + if (node === before) + return node; + if (node === this) + throw new Error('unable to append a node to itself'); + const next = before || this[END]; + switch (node.nodeType) { + case ELEMENT_NODE: + node.remove(); + node.parentNode = this; + knownBoundaries(next[PREV], node, next); + moCallback(node, null); + connectedCallback(node); + break; + case DOCUMENT_FRAGMENT_NODE: { + let {[PRIVATE]: parentNode, firstChild, lastChild} = node; + if (firstChild) { + knownSegment(next[PREV], firstChild, lastChild, next); + knownAdjacent(node, node[END]); + if (parentNode) + parentNode.replaceChildren(); + do { + firstChild.parentNode = this; + moCallback(firstChild, null); + if (firstChild.nodeType === ELEMENT_NODE) + connectedCallback(firstChild); + } while ( + firstChild !== lastChild && + (firstChild = nextSibling(firstChild)) + ); + } + break; + } + case TEXT_NODE: + case COMMENT_NODE: + case CDATA_SECTION_NODE: + node.remove(); + /* eslint no-fallthrough:0 */ + // this covers DOCUMENT_TYPE_NODE too + default: + node.parentNode = this; + knownSiblings(next[PREV], node, next); + moCallback(node, null); + break; + } + return node; + } + + normalize() { + let {[NEXT]: next, [END]: end} = this; + while (next !== end) { + const {[NEXT]: $next, [PREV]: $prev, nodeType} = next; + if (nodeType === TEXT_NODE) { + if (!next[VALUE]) + next.remove(); + else if ($prev && $prev.nodeType === TEXT_NODE) { + $prev.textContent += next.textContent; + next.remove(); + } + } + next = $next; + } + } + + removeChild(node) { + if (node.parentNode !== this) + throw new Error('node is not a child'); + node.remove(); + return node; + } + + replaceChild(node, replaced) { + const next = getEnd(replaced)[NEXT]; + replaced.remove(); + this.insertBefore(node, next); + return replaced; + } +} + +// https://dom.spec.whatwg.org/#interface-nonelementparentnode +// Document, DocumentFragment + + +class NonElementParentNode extends ParentNode { + getElementById(id) { + let {[NEXT]: next, [END]: end} = this; + while (next !== end) { + if (next.nodeType === ELEMENT_NODE && next.id === id) + return next; + next = next[NEXT]; + } + return null; + } + + cloneNode(deep) { + const {ownerDocument, constructor} = this; + const nonEPN = new constructor(ownerDocument); + if (deep) { + const {[END]: end} = nonEPN; + for (const node of this.childNodes) + nonEPN.insertBefore(node.cloneNode(deep), end); + } + return nonEPN; + } + + toString() { + const {childNodes, localName} = this; + return `<${localName}>${childNodes.join('')}</${localName}>`; + } + + toJSON() { + const json = []; + nonElementAsJSON(this, json); + return json; + } +} + +/** + * @implements globalThis.DocumentFragment + */ +let DocumentFragment$1 = class DocumentFragment extends NonElementParentNode { + constructor(ownerDocument) { + super(ownerDocument, '#document-fragment', DOCUMENT_FRAGMENT_NODE); + } +}; + +/** + * @implements globalThis.DocumentType + */ +let DocumentType$1 = class DocumentType extends Node$1 { + constructor(ownerDocument, name, publicId = '', systemId = '') { + super(ownerDocument, '#document-type', DOCUMENT_TYPE_NODE); + this.name = name; + this.publicId = publicId; + this.systemId = systemId; + } + + cloneNode() { + const {ownerDocument, name, publicId, systemId} = this; + return new DocumentType(ownerDocument, name, publicId, systemId); + } + + toString() { + const {name, publicId, systemId} = this; + const hasPublic = 0 < publicId.length; + const str = [name]; + if (hasPublic) + str.push('PUBLIC', `"${publicId}"`); + if (systemId.length) { + if (!hasPublic) + str.push('SYSTEM'); + str.push(`"${systemId}"`); + } + return `<!DOCTYPE ${str.join(' ')}>`; + } + + toJSON() { + const json = []; + documentTypeAsJSON(this, json); + return json; + } +}; + +/** + * @param {Node} node + * @returns {String} + */ +const getInnerHtml = node => node.childNodes.join(''); + +/** + * @param {Node} node + * @param {String} html + */ +const setInnerHtml = (node, html) => { + const {ownerDocument} = node; + const {constructor} = ownerDocument; + const document = new constructor; + document[CUSTOM_ELEMENTS] = ownerDocument[CUSTOM_ELEMENTS]; + const {childNodes} = parseFromString(document, ignoreCase(node), html); + + node.replaceChildren(...childNodes.map(setOwnerDocument, ownerDocument)); +}; + +function setOwnerDocument(node) { + node.ownerDocument = this; + switch (node.nodeType) { + case ELEMENT_NODE: + case DOCUMENT_FRAGMENT_NODE: + node.childNodes.forEach(setOwnerDocument, this); + break; + } + return node; +} + +var uhyphen = camel => camel.replace(/(([A-Z0-9])([A-Z0-9][a-z]))|(([a-z0-9]+)([A-Z]))/g, '$2$5-$3$6') + .toLowerCase(); + +const refs$1 = new WeakMap; + +const key = name => `data-${uhyphen(name)}`; +const prop = name => name.slice(5).replace(/-([a-z])/g, (_, $1) => $1.toUpperCase()); + +const handler$2 = { + get(dataset, name) { + if (name in dataset) + return refs$1.get(dataset).getAttribute(key(name)); + }, + + set(dataset, name, value) { + dataset[name] = value; + refs$1.get(dataset).setAttribute(key(name), value); + return true; + }, + + deleteProperty(dataset, name) { + if (name in dataset) + refs$1.get(dataset).removeAttribute(key(name)); + return delete dataset[name]; + } +}; + +/** + * @implements globalThis.DOMStringMap + */ +class DOMStringMap { + /** + * @param {Element} ref + */ + constructor(ref) { + for (const {name, value} of ref.attributes) { + if (/^data-/.test(name)) + this[prop(name)] = value; + } + refs$1.set(this, ref); + return new Proxy(this, handler$2); + } +} + +setPrototypeOf(DOMStringMap.prototype, null); + +const {add} = Set.prototype; +const addTokens = (self, tokens) => { + for (const token of tokens) { + if (token) + add.call(self, token); + } +}; + +const update = ({[OWNER_ELEMENT]: ownerElement, value}) => { + const attribute = ownerElement.getAttributeNode('class'); + if (attribute) + attribute.value = value; + else + setAttribute( + ownerElement, + new Attr$1(ownerElement.ownerDocument, 'class', value) + ); +}; + +/** + * @implements globalThis.DOMTokenList + */ +class DOMTokenList extends Set { + + constructor(ownerElement) { + super(); + this[OWNER_ELEMENT] = ownerElement; + const attribute = ownerElement.getAttributeNode('class'); + if (attribute) + addTokens(this, attribute.value.split(/\s+/)); + } + + get length() { return this.size; } + + get value() { return [...this].join(' '); } + + /** + * @param {...string} tokens + */ + add(...tokens) { + addTokens(this, tokens); + update(this); + } + + /** + * @param {string} token + */ + contains(token) { return this.has(token); } + + /** + * @param {...string} tokens + */ + remove(...tokens) { + for (const token of tokens) + this.delete(token); + update(this); + } + + /** + * @param {string} token + * @param {boolean?} force + */ + toggle(token, force) { + if (this.has(token)) { + if (force) + return true; + this.delete(token); + update(this); + } + else if (force || arguments.length === 1) { + super.add(token); + update(this); + return true; + } + return false; + } + + /** + * @param {string} token + * @param {string} newToken + */ + replace(token, newToken) { + if (this.has(token)) { + this.delete(token); + super.add(newToken); + update(this); + return true; + } + return false; + } + + /** + * @param {string} token + */ + supports() { return true; } +} + +const refs = new WeakMap; + +const getKeys = style => [...style.keys()].filter(key => key !== PRIVATE); + +const updateKeys = style => { + const attr = refs.get(style).getAttributeNode('style'); + if (!attr || attr[CHANGED] || style.get(PRIVATE) !== attr) { + style.clear(); + if (attr) { + style.set(PRIVATE, attr); + for (const rule of attr[VALUE].split(/\s*;\s*/)) { + let [key, ...rest] = rule.split(':'); + if (rest.length > 0) { + key = key.trim(); + const value = rest.join(':').trim(); + if (key && value) + style.set(key, value); + } + } + } + } + return attr; +}; + +const handler$1 = { + get(style, name) { + if (name in prototype) + return style[name]; + updateKeys(style); + if (name === 'length') + return getKeys(style).length; + if (/^\d+$/.test(name)) + return getKeys(style)[name]; + return style.get(uhyphen(name)) ?? ''; + }, + + set(style, name, value) { + if (name === 'cssText') + style[name] = value; + else { + let attr = updateKeys(style); + if (value == null) + style.delete(uhyphen(name)); + else + style.set(uhyphen(name), value); + if (!attr) { + const element = refs.get(style); + attr = element.ownerDocument.createAttribute('style'); + element.setAttributeNode(attr); + style.set(PRIVATE, attr); + } + attr[CHANGED] = false; + attr[VALUE] = style.toString(); + } + return true; + } +}; + +/** + * @implements globalThis.CSSStyleDeclaration + */ +let CSSStyleDeclaration$1 = class CSSStyleDeclaration extends Map { + constructor(element) { + super(); + refs.set(this, element); + /* c8 ignore start */ + return new Proxy(this, handler$1); + /* c8 ignore stop */ + } + + get cssText() { + return this.toString(); + } + + set cssText(value) { + refs.get(this).setAttribute('style', value); + } + + getPropertyValue(name) { + const self = this[PRIVATE]; + return handler$1.get(self, name); + } + + setProperty(name, value) { + const self = this[PRIVATE]; + handler$1.set(self, name, value); + } + + removeProperty(name) { + const self = this[PRIVATE]; + handler$1.set(self, name, null); + } + + [Symbol.iterator]() { + const self = this[PRIVATE]; + updateKeys(self); + const keys = getKeys(self); + const {length} = keys; + let i = 0; + return { + next() { + const done = i === length; + return {done, value: done ? null : keys[i++]}; + } + }; + } + + get[PRIVATE]() { return this; } + + toString() { + const self = this[PRIVATE]; + updateKeys(self); + const cssText = []; + self.forEach(push, cssText); + return cssText.join(';'); + } +}; + +const {prototype} = CSSStyleDeclaration$1; + +function push(value, key) { + if (key !== PRIVATE) + this.push(`${key}:${value}`); +} + +// https://dom.spec.whatwg.org/#interface-event + +/* c8 ignore start */ + +// Node 15 has Event but 14 and 12 don't +const BUBBLING_PHASE = 3; +const AT_TARGET = 2; +const CAPTURING_PHASE = 1; +const NONE = 0; + +function getCurrentTarget(ev) { + return ev.currentTarget; +} + +/** + * @implements globalThis.Event + */ +class GlobalEvent { + static get BUBBLING_PHASE() { return BUBBLING_PHASE; } + static get AT_TARGET() { return AT_TARGET; } + static get CAPTURING_PHASE() { return CAPTURING_PHASE; } + static get NONE() { return NONE; } + + constructor(type, eventInitDict = {}) { + this.type = type; + this.bubbles = !!eventInitDict.bubbles; + this.cancelBubble = false; + this._stopImmediatePropagationFlag = false; + this.cancelable = !!eventInitDict.cancelable; + this.eventPhase = this.NONE; + this.timeStamp = Date.now(); + this.defaultPrevented = false; + this.originalTarget = null; + this.returnValue = null; + this.srcElement = null; + this.target = null; + this._path = []; + } + + get BUBBLING_PHASE() { return BUBBLING_PHASE; } + get AT_TARGET() { return AT_TARGET; } + get CAPTURING_PHASE() { return CAPTURING_PHASE; } + get NONE() { return NONE; } + + preventDefault() { this.defaultPrevented = true; } + + // simplified implementation, should be https://dom.spec.whatwg.org/#dom-event-composedpath + composedPath() { + return this._path.map(getCurrentTarget); + } + + stopPropagation() { + this.cancelBubble = true; + } + + stopImmediatePropagation() { + this.stopPropagation(); + this._stopImmediatePropagationFlag = true; + } + } + +/* c8 ignore stop */ + +/** + * @implements globalThis.NamedNodeMap + */ +class NamedNodeMap extends Array { + constructor(ownerElement) { + super(); + this.ownerElement = ownerElement; + } + + getNamedItem(name) { + return this.ownerElement.getAttributeNode(name); + } + + setNamedItem(attr) { + this.ownerElement.setAttributeNode(attr); + this.unshift(attr); + } + + removeNamedItem(name) { + const item = this.getNamedItem(name); + this.ownerElement.removeAttribute(name); + this.splice(this.indexOf(item), 1); + } + + item(index) { + return index < this.length ? this[index] : null; + } + + /* c8 ignore start */ + getNamedItemNS(_, name) { + return this.getNamedItem(name); + } + + setNamedItemNS(_, attr) { + return this.setNamedItem(attr); + } + + removeNamedItemNS(_, name) { + return this.removeNamedItem(name); + } + /* c8 ignore stop */ +} + +/** + * @implements globalThis.ShadowRoot + */ +let ShadowRoot$1 = class ShadowRoot extends NonElementParentNode { + constructor(host) { + super(host.ownerDocument, '#shadow-root', DOCUMENT_FRAGMENT_NODE); + this.host = host; + } + + get innerHTML() { + return getInnerHtml(this); + } + set innerHTML(html) { + setInnerHtml(this, html); + } +}; + +// https://dom.spec.whatwg.org/#interface-element + + +// <utils> +const attributesHandler = { + get(target, key) { + return key in target ? target[key] : target.find(({name}) => name === key); + } +}; + +const create = (ownerDocument, element, localName) => { + if ('ownerSVGElement' in element) { + const svg = ownerDocument.createElementNS(SVG_NAMESPACE, localName); + svg.ownerSVGElement = element.ownerSVGElement; + return svg; + } + return ownerDocument.createElement(localName); +}; + +const isVoid = ({localName, ownerDocument}) => { + return ownerDocument[MIME].voidElements.test(localName); +}; + +// </utils> + +/** + * @implements globalThis.Element + */ +let Element$1 = class Element extends ParentNode { + constructor(ownerDocument, localName) { + super(ownerDocument, localName, ELEMENT_NODE); + this[CLASS_LIST] = null; + this[DATASET] = null; + this[STYLE] = null; + } + + // <Mixins> + get isConnected() { return isConnected(this); } + get parentElement() { return parentElement(this); } + get previousSibling() { return previousSibling(this); } + get nextSibling() { return nextSibling(this); } + get namespaceURI() { + return 'http://www.w3.org/1999/xhtml'; + } + + get previousElementSibling() { return previousElementSibling(this); } + get nextElementSibling() { return nextElementSibling$1(this); } + + before(...nodes) { before(this, nodes); } + after(...nodes) { after(this, nodes); } + replaceWith(...nodes) { replaceWith(this, nodes); } + remove() { remove(this[PREV], this, this[END][NEXT]); } + // </Mixins> + + // <specialGetters> + get id() { return stringAttribute.get(this, 'id'); } + set id(value) { stringAttribute.set(this, 'id', value); } + + get className() { return this.classList.value; } + set className(value) { + const {classList} = this; + classList.clear(); + classList.add(...($String(value).split(/\s+/))); + } + + get nodeName() { return localCase(this); } + get tagName() { return localCase(this); } + + get classList() { + return this[CLASS_LIST] || ( + this[CLASS_LIST] = new DOMTokenList(this) + ); + } + + get dataset() { + return this[DATASET] || ( + this[DATASET] = new DOMStringMap(this) + ); + } + + getBoundingClientRect() { + return { + x: 0, + y: 0, + bottom: 0, + height: 0, + left: 0, + right: 0, + top: 0, + width: 0 + }; + } + + get nonce() { return stringAttribute.get(this, 'nonce'); } + set nonce(value) { stringAttribute.set(this, 'nonce', value); } + + get style() { + return this[STYLE] || ( + this[STYLE] = new CSSStyleDeclaration$1(this) + ); + } + + get tabIndex() { return numericAttribute.get(this, 'tabindex') || -1; } + set tabIndex(value) { numericAttribute.set(this, 'tabindex', value); } + + get slot() { return stringAttribute.get(this, 'slot'); } + set slot(value) { stringAttribute.set(this, 'slot', value); } + // </specialGetters> + + + // <contentRelated> + get innerText() { + const text = []; + let {[NEXT]: next, [END]: end} = this; + while (next !== end) { + if (next.nodeType === TEXT_NODE) { + text.push(next.textContent.replace(/\s+/g, ' ')); + } else if( + text.length && next[NEXT] != end && + BLOCK_ELEMENTS.has(next.tagName) + ) { + text.push('\n'); + } + next = next[NEXT]; + } + return text.join(''); + } + + /** + * @returns {String} + */ + get textContent() { + const text = []; + let {[NEXT]: next, [END]: end} = this; + while (next !== end) { + const nodeType = next.nodeType; + if (nodeType === TEXT_NODE || nodeType === CDATA_SECTION_NODE) + text.push(next.textContent); + next = next[NEXT]; + } + return text.join(''); + } + + set textContent(text) { + this.replaceChildren(); + if (text != null && text !== '') + this.appendChild(new Text$1(this.ownerDocument, text)); + } + + get innerHTML() { + return getInnerHtml(this); + } + set innerHTML(html) { + setInnerHtml(this, html); + } + + get outerHTML() { return this.toString(); } + set outerHTML(html) { + const template = this.ownerDocument.createElement(''); + template.innerHTML = html; + this.replaceWith(...template.childNodes); + } + // </contentRelated> + + // <attributes> + get attributes() { + const attributes = new NamedNodeMap(this); + let next = this[NEXT]; + while (next.nodeType === ATTRIBUTE_NODE) { + attributes.push(next); + next = next[NEXT]; + } + return new Proxy(attributes, attributesHandler); + } + + focus() { this.dispatchEvent(new GlobalEvent('focus')); } + + getAttribute(name) { + if (name === 'class') + return this.className; + const attribute = this.getAttributeNode(name); + return attribute && (ignoreCase(this) ? attribute.value : escape(attribute.value)); + } + + getAttributeNode(name) { + let next = this[NEXT]; + while (next.nodeType === ATTRIBUTE_NODE) { + if (next.name === name) + return next; + next = next[NEXT]; + } + return null; + } + + getAttributeNames() { + const attributes = new NodeList; + let next = this[NEXT]; + while (next.nodeType === ATTRIBUTE_NODE) { + attributes.push(next.name); + next = next[NEXT]; + } + return attributes; + } + + hasAttribute(name) { return !!this.getAttributeNode(name); } + hasAttributes() { return this[NEXT].nodeType === ATTRIBUTE_NODE; } + + removeAttribute(name) { + if (name === 'class' && this[CLASS_LIST]) + this[CLASS_LIST].clear(); + let next = this[NEXT]; + while (next.nodeType === ATTRIBUTE_NODE) { + if (next.name === name) { + removeAttribute(this, next); + return; + } + next = next[NEXT]; + } + } + + removeAttributeNode(attribute) { + let next = this[NEXT]; + while (next.nodeType === ATTRIBUTE_NODE) { + if (next === attribute) { + removeAttribute(this, next); + return; + } + next = next[NEXT]; + } + } + + setAttribute(name, value) { + if (name === 'class') + this.className = value; + else { + const attribute = this.getAttributeNode(name); + if (attribute) + attribute.value = value; + else + setAttribute(this, new Attr$1(this.ownerDocument, name, value)); + } + } + + setAttributeNode(attribute) { + const {name} = attribute; + const previously = this.getAttributeNode(name); + if (previously !== attribute) { + if (previously) + this.removeAttributeNode(previously); + const {ownerElement} = attribute; + if (ownerElement) + ownerElement.removeAttributeNode(attribute); + setAttribute(this, attribute); + } + return previously; + } + + toggleAttribute(name, force) { + if (this.hasAttribute(name)) { + if (!force) { + this.removeAttribute(name); + return false; + } + return true; + } + else if (force || arguments.length === 1) { + this.setAttribute(name, ''); + return true; + } + return false; + } + // </attributes> + + // <ShadowDOM> + get shadowRoot() { + if (shadowRoots.has(this)) { + const {mode, shadowRoot} = shadowRoots.get(this); + if (mode === 'open') + return shadowRoot; + } + return null; + } + + attachShadow(init) { + if (shadowRoots.has(this)) + throw new Error('operation not supported'); + // TODO: shadowRoot should be likely a specialized class that extends DocumentFragment + // but until DSD is out, I am not sure I should spend time on this. + const shadowRoot = new ShadowRoot$1(this); + shadowRoots.set(this, { + mode: init.mode, + shadowRoot + }); + return shadowRoot; + } + // </ShadowDOM> + + // <selectors> + matches(selectors) { return matches(this, selectors); } + closest(selectors) { + let parentElement = this; + const matches = prepareMatch(parentElement, selectors); + while (parentElement && !matches(parentElement)) + parentElement = parentElement.parentElement; + return parentElement; + } + // </selectors> + + // <insertAdjacent> + insertAdjacentElement(position, element) { + const {parentElement} = this; + switch (position) { + case 'beforebegin': + if (parentElement) { + parentElement.insertBefore(element, this); + break; + } + return null; + case 'afterbegin': + this.insertBefore(element, this.firstChild); + break; + case 'beforeend': + this.insertBefore(element, null); + break; + case 'afterend': + if (parentElement) { + parentElement.insertBefore(element, this.nextSibling); + break; + } + return null; + } + return element; + } + + insertAdjacentHTML(position, html) { + this.insertAdjacentElement(position, htmlToFragment(this.ownerDocument, html)); + } + + insertAdjacentText(position, text) { + const node = this.ownerDocument.createTextNode(text); + this.insertAdjacentElement(position, node); + } + // </insertAdjacent> + + cloneNode(deep = false) { + const {ownerDocument, localName} = this; + const addNext = next => { + next.parentNode = parentNode; + knownAdjacent($next, next); + $next = next; + }; + const clone = create(ownerDocument, this, localName); + let parentNode = clone, $next = clone; + let {[NEXT]: next, [END]: prev} = this; + while (next !== prev && (deep || next.nodeType === ATTRIBUTE_NODE)) { + switch (next.nodeType) { + case NODE_END: + knownAdjacent($next, parentNode[END]); + $next = parentNode[END]; + parentNode = parentNode.parentNode; + break; + case ELEMENT_NODE: { + const node = create(ownerDocument, next, next.localName); + addNext(node); + parentNode = node; + break; + } + case ATTRIBUTE_NODE: { + const attr = next.cloneNode(deep); + attr.ownerElement = parentNode; + addNext(attr); + break; + } + case TEXT_NODE: + case COMMENT_NODE: + case CDATA_SECTION_NODE: + addNext(next.cloneNode(deep)); + break; + } + next = next[NEXT]; + } + knownAdjacent($next, clone[END]); + return clone; + } + + // <custom> + toString() { + const out = []; + const {[END]: end} = this; + let next = {[NEXT]: this}; + let isOpened = false; + do { + next = next[NEXT]; + switch (next.nodeType) { + case ATTRIBUTE_NODE: { + const attr = ' ' + next; + switch (attr) { + case ' id': + case ' class': + case ' style': + break; + default: + out.push(attr); + } + break; + } + case NODE_END: { + const start = next[START]; + if (isOpened) { + if ('ownerSVGElement' in start) + out.push(' />'); + else if (isVoid(start)) + out.push(ignoreCase(start) ? '>' : ' />'); + else + out.push(`></${start.localName}>`); + isOpened = false; + } + else + out.push(`</${start.localName}>`); + break; + } + case ELEMENT_NODE: + if (isOpened) + out.push('>'); + if (next.toString !== this.toString) { + out.push(next.toString()); + next = next[END]; + isOpened = false; + } + else { + out.push(`<${next.localName}`); + isOpened = true; + } + break; + case TEXT_NODE: + case COMMENT_NODE: + case CDATA_SECTION_NODE: + out.push((isOpened ? '>' : '') + next); + isOpened = false; + break; + } + } while (next !== end); + return out.join(''); + } + + toJSON() { + const json = []; + elementAsJSON(this, json); + return json; + } + // </custom> + + + /* c8 ignore start */ + getAttributeNS(_, name) { return this.getAttribute(name); } + getElementsByTagNameNS(_, name) { return this.getElementsByTagName(name); } + hasAttributeNS(_, name) { return this.hasAttribute(name); } + removeAttributeNS(_, name) { this.removeAttribute(name); } + setAttributeNS(_, name, value) { this.setAttribute(name, value); } + setAttributeNodeNS(attr) { return this.setAttributeNode(attr); } + /* c8 ignore stop */ +}; + +const classNames = new WeakMap; + +const handler = { + get(target, name) { + return target[name]; + }, + set(target, name, value) { + target[name] = value; + return true; + } +}; + +/** + * @implements globalThis.SVGElement + */ +let SVGElement$1 = class SVGElement extends Element$1 { + constructor(ownerDocument, localName, ownerSVGElement = null) { + super(ownerDocument, localName); + this.ownerSVGElement = ownerSVGElement; + } + + get className() { + if (!classNames.has(this)) + classNames.set(this, new Proxy({baseVal: '', animVal: ''}, handler)); + return classNames.get(this); + } + + /* c8 ignore start */ + set className(value) { + const {classList} = this; + classList.clear(); + classList.add(...($String(value).split(/\s+/))); + } + /* c8 ignore stop */ + + get namespaceURI() { + return 'http://www.w3.org/2000/svg'; + } + + getAttribute(name) { + return name === 'class' ? + [...this.classList].join(' ') : + super.getAttribute(name); + } + + setAttribute(name, value) { + if (name === 'class') + this.className = value; + else if (name === 'style') { + const {className} = this; + className.baseVal = className.animVal = value; + } + super.setAttribute(name, value); + } +}; + +/* c8 ignore start */ +const illegalConstructor = () => { + throw new TypeError('Illegal constructor'); +}; + +function Attr() { illegalConstructor(); } +setPrototypeOf(Attr, Attr$1); +Attr.prototype = Attr$1.prototype; + +function CDATASection() { illegalConstructor(); } +setPrototypeOf(CDATASection, CDATASection$1); +CDATASection.prototype = CDATASection$1.prototype; + +function CharacterData() { illegalConstructor(); } +setPrototypeOf(CharacterData, CharacterData$1); +CharacterData.prototype = CharacterData$1.prototype; + +function Comment() { illegalConstructor(); } +setPrototypeOf(Comment, Comment$2); +Comment.prototype = Comment$2.prototype; + +function DocumentFragment() { illegalConstructor(); } +setPrototypeOf(DocumentFragment, DocumentFragment$1); +DocumentFragment.prototype = DocumentFragment$1.prototype; + +function DocumentType() { illegalConstructor(); } +setPrototypeOf(DocumentType, DocumentType$1); +DocumentType.prototype = DocumentType$1.prototype; + +function Element() { illegalConstructor(); } +setPrototypeOf(Element, Element$1); +Element.prototype = Element$1.prototype; + +function Node() { illegalConstructor(); } +setPrototypeOf(Node, Node$1); +Node.prototype = Node$1.prototype; + +function ShadowRoot() { illegalConstructor(); } +setPrototypeOf(ShadowRoot, ShadowRoot$1); +ShadowRoot.prototype = ShadowRoot$1.prototype; + +function Text() { illegalConstructor(); } +setPrototypeOf(Text, Text$1); +Text.prototype = Text$1.prototype; + +function SVGElement() { illegalConstructor(); } +setPrototypeOf(SVGElement, SVGElement$1); +SVGElement.prototype = SVGElement$1.prototype; +/* c8 ignore stop */ + +const Facades = { + Attr, + CDATASection, + CharacterData, + Comment, + DocumentFragment, + DocumentType, + Element, + Node, + ShadowRoot, + Text, + SVGElement +}; + +const Level0 = new WeakMap; +const level0 = { + get(element, name) { + return Level0.has(element) && Level0.get(element)[name] || null; + }, + set(element, name, value) { + if (!Level0.has(element)) + Level0.set(element, {}); + const handlers = Level0.get(element); + const type = name.slice(2); + if (handlers[name]) + element.removeEventListener(type, handlers[name], false); + if ((handlers[name] = value)) + element.addEventListener(type, value, false); + } +}; + +/** + * @implements globalThis.HTMLElement + */ +class HTMLElement extends Element$1 { + + static get observedAttributes() { return []; } + + constructor(ownerDocument = null, localName = '') { + super(ownerDocument, localName); + + const ownerLess = !ownerDocument; + let options; + + if (ownerLess) { + const {constructor: Class} = this; + if (!Classes.has(Class)) + throw new Error('unable to initialize this Custom Element'); + ({ownerDocument, localName, options} = Classes.get(Class)); + } + + if (ownerDocument[UPGRADE]) { + const {element, values} = ownerDocument[UPGRADE]; + ownerDocument[UPGRADE] = null; + for (const [key, value] of values) + element[key] = value; + return element; + } + + if (ownerLess) { + this.ownerDocument = this[END].ownerDocument = ownerDocument; + this.localName = localName; + customElements.set(this, {connected: false}); + if (options.is) + this.setAttribute('is', options.is); + } + } + + /* c8 ignore start */ + + /* TODO: what about these? + offsetHeight + offsetLeft + offsetParent + offsetTop + offsetWidth + */ + + blur() { this.dispatchEvent(new GlobalEvent('blur')); } + click() { + const clickEvent = new GlobalEvent('click', {bubbles: true, cancelable: true}); + clickEvent.button = 0; + + this.dispatchEvent(clickEvent); + } + + // Boolean getters + get accessKeyLabel() { + const {accessKey} = this; + return accessKey && `Alt+Shift+${accessKey}`; + } + get isContentEditable() { return this.hasAttribute('contenteditable'); } + + // Boolean Accessors + get contentEditable() { return booleanAttribute.get(this, 'contenteditable'); } + set contentEditable(value) { booleanAttribute.set(this, 'contenteditable', value); } + get draggable() { return booleanAttribute.get(this, 'draggable'); } + set draggable(value) { booleanAttribute.set(this, 'draggable', value); } + get hidden() { return booleanAttribute.get(this, 'hidden'); } + set hidden(value) { booleanAttribute.set(this, 'hidden', value); } + get spellcheck() { return booleanAttribute.get(this, 'spellcheck'); } + set spellcheck(value) { booleanAttribute.set(this, 'spellcheck', value); } + + // String Accessors + get accessKey() { return stringAttribute.get(this, 'accesskey'); } + set accessKey(value) { stringAttribute.set(this, 'accesskey', value); } + get dir() { return stringAttribute.get(this, 'dir'); } + set dir(value) { stringAttribute.set(this, 'dir', value); } + get lang() { return stringAttribute.get(this, 'lang'); } + set lang(value) { stringAttribute.set(this, 'lang', value); } + get title() { return stringAttribute.get(this, 'title'); } + set title(value) { stringAttribute.set(this, 'title', value); } + + // DOM Level 0 + get onabort() { return level0.get(this, 'onabort'); } + set onabort(value) { level0.set(this, 'onabort', value); } + + get onblur() { return level0.get(this, 'onblur'); } + set onblur(value) { level0.set(this, 'onblur', value); } + + get oncancel() { return level0.get(this, 'oncancel'); } + set oncancel(value) { level0.set(this, 'oncancel', value); } + + get oncanplay() { return level0.get(this, 'oncanplay'); } + set oncanplay(value) { level0.set(this, 'oncanplay', value); } + + get oncanplaythrough() { return level0.get(this, 'oncanplaythrough'); } + set oncanplaythrough(value) { level0.set(this, 'oncanplaythrough', value); } + + get onchange() { return level0.get(this, 'onchange'); } + set onchange(value) { level0.set(this, 'onchange', value); } + + get onclick() { return level0.get(this, 'onclick'); } + set onclick(value) { level0.set(this, 'onclick', value); } + + get onclose() { return level0.get(this, 'onclose'); } + set onclose(value) { level0.set(this, 'onclose', value); } + + get oncontextmenu() { return level0.get(this, 'oncontextmenu'); } + set oncontextmenu(value) { level0.set(this, 'oncontextmenu', value); } + + get oncuechange() { return level0.get(this, 'oncuechange'); } + set oncuechange(value) { level0.set(this, 'oncuechange', value); } + + get ondblclick() { return level0.get(this, 'ondblclick'); } + set ondblclick(value) { level0.set(this, 'ondblclick', value); } + + get ondrag() { return level0.get(this, 'ondrag'); } + set ondrag(value) { level0.set(this, 'ondrag', value); } + + get ondragend() { return level0.get(this, 'ondragend'); } + set ondragend(value) { level0.set(this, 'ondragend', value); } + + get ondragenter() { return level0.get(this, 'ondragenter'); } + set ondragenter(value) { level0.set(this, 'ondragenter', value); } + + get ondragleave() { return level0.get(this, 'ondragleave'); } + set ondragleave(value) { level0.set(this, 'ondragleave', value); } + + get ondragover() { return level0.get(this, 'ondragover'); } + set ondragover(value) { level0.set(this, 'ondragover', value); } + + get ondragstart() { return level0.get(this, 'ondragstart'); } + set ondragstart(value) { level0.set(this, 'ondragstart', value); } + + get ondrop() { return level0.get(this, 'ondrop'); } + set ondrop(value) { level0.set(this, 'ondrop', value); } + + get ondurationchange() { return level0.get(this, 'ondurationchange'); } + set ondurationchange(value) { level0.set(this, 'ondurationchange', value); } + + get onemptied() { return level0.get(this, 'onemptied'); } + set onemptied(value) { level0.set(this, 'onemptied', value); } + + get onended() { return level0.get(this, 'onended'); } + set onended(value) { level0.set(this, 'onended', value); } + + get onerror() { return level0.get(this, 'onerror'); } + set onerror(value) { level0.set(this, 'onerror', value); } + + get onfocus() { return level0.get(this, 'onfocus'); } + set onfocus(value) { level0.set(this, 'onfocus', value); } + + get oninput() { return level0.get(this, 'oninput'); } + set oninput(value) { level0.set(this, 'oninput', value); } + + get oninvalid() { return level0.get(this, 'oninvalid'); } + set oninvalid(value) { level0.set(this, 'oninvalid', value); } + + get onkeydown() { return level0.get(this, 'onkeydown'); } + set onkeydown(value) { level0.set(this, 'onkeydown', value); } + + get onkeypress() { return level0.get(this, 'onkeypress'); } + set onkeypress(value) { level0.set(this, 'onkeypress', value); } + + get onkeyup() { return level0.get(this, 'onkeyup'); } + set onkeyup(value) { level0.set(this, 'onkeyup', value); } + + get onload() { return level0.get(this, 'onload'); } + set onload(value) { level0.set(this, 'onload', value); } + + get onloadeddata() { return level0.get(this, 'onloadeddata'); } + set onloadeddata(value) { level0.set(this, 'onloadeddata', value); } + + get onloadedmetadata() { return level0.get(this, 'onloadedmetadata'); } + set onloadedmetadata(value) { level0.set(this, 'onloadedmetadata', value); } + + get onloadstart() { return level0.get(this, 'onloadstart'); } + set onloadstart(value) { level0.set(this, 'onloadstart', value); } + + get onmousedown() { return level0.get(this, 'onmousedown'); } + set onmousedown(value) { level0.set(this, 'onmousedown', value); } + + get onmouseenter() { return level0.get(this, 'onmouseenter'); } + set onmouseenter(value) { level0.set(this, 'onmouseenter', value); } + + get onmouseleave() { return level0.get(this, 'onmouseleave'); } + set onmouseleave(value) { level0.set(this, 'onmouseleave', value); } + + get onmousemove() { return level0.get(this, 'onmousemove'); } + set onmousemove(value) { level0.set(this, 'onmousemove', value); } + + get onmouseout() { return level0.get(this, 'onmouseout'); } + set onmouseout(value) { level0.set(this, 'onmouseout', value); } + + get onmouseover() { return level0.get(this, 'onmouseover'); } + set onmouseover(value) { level0.set(this, 'onmouseover', value); } + + get onmouseup() { return level0.get(this, 'onmouseup'); } + set onmouseup(value) { level0.set(this, 'onmouseup', value); } + + get onmousewheel() { return level0.get(this, 'onmousewheel'); } + set onmousewheel(value) { level0.set(this, 'onmousewheel', value); } + + get onpause() { return level0.get(this, 'onpause'); } + set onpause(value) { level0.set(this, 'onpause', value); } + + get onplay() { return level0.get(this, 'onplay'); } + set onplay(value) { level0.set(this, 'onplay', value); } + + get onplaying() { return level0.get(this, 'onplaying'); } + set onplaying(value) { level0.set(this, 'onplaying', value); } + + get onprogress() { return level0.get(this, 'onprogress'); } + set onprogress(value) { level0.set(this, 'onprogress', value); } + + get onratechange() { return level0.get(this, 'onratechange'); } + set onratechange(value) { level0.set(this, 'onratechange', value); } + + get onreset() { return level0.get(this, 'onreset'); } + set onreset(value) { level0.set(this, 'onreset', value); } + + get onresize() { return level0.get(this, 'onresize'); } + set onresize(value) { level0.set(this, 'onresize', value); } + + get onscroll() { return level0.get(this, 'onscroll'); } + set onscroll(value) { level0.set(this, 'onscroll', value); } + + get onseeked() { return level0.get(this, 'onseeked'); } + set onseeked(value) { level0.set(this, 'onseeked', value); } + + get onseeking() { return level0.get(this, 'onseeking'); } + set onseeking(value) { level0.set(this, 'onseeking', value); } + + get onselect() { return level0.get(this, 'onselect'); } + set onselect(value) { level0.set(this, 'onselect', value); } + + get onshow() { return level0.get(this, 'onshow'); } + set onshow(value) { level0.set(this, 'onshow', value); } + + get onstalled() { return level0.get(this, 'onstalled'); } + set onstalled(value) { level0.set(this, 'onstalled', value); } + + get onsubmit() { return level0.get(this, 'onsubmit'); } + set onsubmit(value) { level0.set(this, 'onsubmit', value); } + + get onsuspend() { return level0.get(this, 'onsuspend'); } + set onsuspend(value) { level0.set(this, 'onsuspend', value); } + + get ontimeupdate() { return level0.get(this, 'ontimeupdate'); } + set ontimeupdate(value) { level0.set(this, 'ontimeupdate', value); } + + get ontoggle() { return level0.get(this, 'ontoggle'); } + set ontoggle(value) { level0.set(this, 'ontoggle', value); } + + get onvolumechange() { return level0.get(this, 'onvolumechange'); } + set onvolumechange(value) { level0.set(this, 'onvolumechange', value); } + + get onwaiting() { return level0.get(this, 'onwaiting'); } + set onwaiting(value) { level0.set(this, 'onwaiting', value); } + + get onauxclick() { return level0.get(this, 'onauxclick'); } + set onauxclick(value) { level0.set(this, 'onauxclick', value); } + + get ongotpointercapture() { return level0.get(this, 'ongotpointercapture'); } + set ongotpointercapture(value) { level0.set(this, 'ongotpointercapture', value); } + + get onlostpointercapture() { return level0.get(this, 'onlostpointercapture'); } + set onlostpointercapture(value) { level0.set(this, 'onlostpointercapture', value); } + + get onpointercancel() { return level0.get(this, 'onpointercancel'); } + set onpointercancel(value) { level0.set(this, 'onpointercancel', value); } + + get onpointerdown() { return level0.get(this, 'onpointerdown'); } + set onpointerdown(value) { level0.set(this, 'onpointerdown', value); } + + get onpointerenter() { return level0.get(this, 'onpointerenter'); } + set onpointerenter(value) { level0.set(this, 'onpointerenter', value); } + + get onpointerleave() { return level0.get(this, 'onpointerleave'); } + set onpointerleave(value) { level0.set(this, 'onpointerleave', value); } + + get onpointermove() { return level0.get(this, 'onpointermove'); } + set onpointermove(value) { level0.set(this, 'onpointermove', value); } + + get onpointerout() { return level0.get(this, 'onpointerout'); } + set onpointerout(value) { level0.set(this, 'onpointerout', value); } + + get onpointerover() { return level0.get(this, 'onpointerover'); } + set onpointerover(value) { level0.set(this, 'onpointerover', value); } + + get onpointerup() { return level0.get(this, 'onpointerup'); } + set onpointerup(value) { level0.set(this, 'onpointerup', value); } + /* c8 ignore stop */ + +} + +const tagName$h = 'template'; + +/** + * @implements globalThis.HTMLTemplateElement + */ +class HTMLTemplateElement extends HTMLElement { + constructor(ownerDocument) { + super(ownerDocument, tagName$h); + const content = this.ownerDocument.createDocumentFragment(); + (this[CONTENT] = content)[PRIVATE] = this; + } + + get content() { + if (this.hasChildNodes() && !this[CONTENT].hasChildNodes()) { + for (const node of this.childNodes) + this[CONTENT].appendChild(node.cloneNode(true)); + } + return this[CONTENT]; + } +} + +registerHTMLClass(tagName$h, HTMLTemplateElement); + +/** + * @implements globalThis.HTMLHtmlElement + */ +class HTMLHtmlElement extends HTMLElement { + constructor(ownerDocument, localName = 'html') { + super(ownerDocument, localName); + } +} + +const {toString} = HTMLElement.prototype; + +class TextElement extends HTMLElement { + + get innerHTML() { return this.textContent; } + set innerHTML(html) { this.textContent = html; } + + toString() { + const outerHTML = toString.call(this.cloneNode()); + return outerHTML.replace('><', () => `>${this.textContent}<`); + } +} + +const tagName$g = 'script'; + +/** + * @implements globalThis.HTMLScriptElement + */ +class HTMLScriptElement extends TextElement { + constructor(ownerDocument, localName = tagName$g) { + super(ownerDocument, localName); + } + + get type() { + return stringAttribute.get(this, 'type'); + } + set type(value) { + stringAttribute.set(this, 'type', value); + } + + get src() { + return stringAttribute.get(this, 'src'); + } + set src(value) { + stringAttribute.set(this, 'src', value); + } + + get defer() { + return booleanAttribute.get(this, 'defer'); + } + + set defer(value) { + booleanAttribute.set(this, 'defer', value); + } + + get crossOrigin() { + return stringAttribute.get(this, 'crossorigin'); + } + set crossOrigin(value) { + stringAttribute.set(this, 'crossorigin', value); + } + + get nomodule() { + return booleanAttribute.get(this, 'nomodule'); + } + set nomodule(value) { + booleanAttribute.set(this, 'nomodule', value); + } + + get referrerPolicy() { + return stringAttribute.get(this, 'referrerpolicy'); + } + set referrerPolicy(value) { + stringAttribute.set(this, 'referrerpolicy', value); + } + + get nonce() { + return stringAttribute.get(this, 'nonce'); + } + set nonce(value) { + stringAttribute.set(this, 'nonce', value); + } + + get async() { + return booleanAttribute.get(this, 'async'); + } + set async(value) { + booleanAttribute.set(this, 'async', value); + } + + get text() { return this.textContent; } + set text(content) { this.textContent = content; } +} + +registerHTMLClass(tagName$g, HTMLScriptElement); + +/** + * @implements globalThis.HTMLFrameElement + */ +class HTMLFrameElement extends HTMLElement { + constructor(ownerDocument, localName = 'frame') { + super(ownerDocument, localName); + } +} + +const tagName$f = 'iframe'; + +/** + * @implements globalThis.HTMLIFrameElement + */ +class HTMLIFrameElement extends HTMLElement { + constructor(ownerDocument, localName = tagName$f) { + super(ownerDocument, localName); + } + + /* c8 ignore start */ + get src() { return stringAttribute.get(this, 'src'); } + set src(value) { stringAttribute.set(this, 'src', value); } + + get srcdoc() { return stringAttribute.get(this, "srcdoc"); } + set srcdoc(value) { stringAttribute.set(this, "srcdoc", value); } + + get name() { return stringAttribute.get(this, "name"); } + set name(value) { stringAttribute.set(this, "name", value); } + + get allow() { return stringAttribute.get(this, "allow"); } + set allow(value) { stringAttribute.set(this, "allow", value); } + + get allowFullscreen() { return booleanAttribute.get(this, "allowfullscreen"); } + set allowFullscreen(value) { booleanAttribute.set(this, "allowfullscreen", value); } + + get referrerPolicy() { return stringAttribute.get(this, "referrerpolicy"); } + set referrerPolicy(value) { stringAttribute.set(this, "referrerpolicy", value); } + + get loading() { return stringAttribute.get(this, "loading"); } + set loading(value) { stringAttribute.set(this, "loading", value); } + /* c8 ignore stop */ +} + +registerHTMLClass(tagName$f, HTMLIFrameElement); + +/** + * @implements globalThis.HTMLObjectElement + */ +class HTMLObjectElement extends HTMLElement { + constructor(ownerDocument, localName = 'object') { + super(ownerDocument, localName); + } +} + +/** + * @implements globalThis.HTMLHeadElement + */ +class HTMLHeadElement extends HTMLElement { + constructor(ownerDocument, localName = 'head') { + super(ownerDocument, localName); + } +} + +/** + * @implements globalThis.HTMLBodyElement + */ +class HTMLBodyElement extends HTMLElement { + constructor(ownerDocument, localName = 'body') { + super(ownerDocument, localName); + } +} + +var lib = {}; + +var CSSStyleDeclaration = {}; + +var parse$1 = {}; + +var CSSStyleSheet = {}; + +var StyleSheet = {}; + +var hasRequiredStyleSheet; + +function requireStyleSheet () { + if (hasRequiredStyleSheet) return StyleSheet; + hasRequiredStyleSheet = 1; + //.CommonJS + var CSSOM = {}; + ///CommonJS + + + /** + * @constructor + * @see http://dev.w3.org/csswg/cssom/#the-stylesheet-interface + */ + CSSOM.StyleSheet = function StyleSheet() { + this.parentStyleSheet = null; + }; + + + //.CommonJS + StyleSheet.StyleSheet = CSSOM.StyleSheet; + ///CommonJS + return StyleSheet; +} + +var CSSStyleRule = {}; + +var CSSRule = {}; + +var hasRequiredCSSRule; + +function requireCSSRule () { + if (hasRequiredCSSRule) return CSSRule; + hasRequiredCSSRule = 1; + //.CommonJS + var CSSOM = {}; + ///CommonJS + + + /** + * @constructor + * @see http://dev.w3.org/csswg/cssom/#the-cssrule-interface + * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSRule + */ + CSSOM.CSSRule = function CSSRule() { + this.parentRule = null; + this.parentStyleSheet = null; + }; + + CSSOM.CSSRule.UNKNOWN_RULE = 0; // obsolete + CSSOM.CSSRule.STYLE_RULE = 1; + CSSOM.CSSRule.CHARSET_RULE = 2; // obsolete + CSSOM.CSSRule.IMPORT_RULE = 3; + CSSOM.CSSRule.MEDIA_RULE = 4; + CSSOM.CSSRule.FONT_FACE_RULE = 5; + CSSOM.CSSRule.PAGE_RULE = 6; + CSSOM.CSSRule.KEYFRAMES_RULE = 7; + CSSOM.CSSRule.KEYFRAME_RULE = 8; + CSSOM.CSSRule.MARGIN_RULE = 9; + CSSOM.CSSRule.NAMESPACE_RULE = 10; + CSSOM.CSSRule.COUNTER_STYLE_RULE = 11; + CSSOM.CSSRule.SUPPORTS_RULE = 12; + CSSOM.CSSRule.DOCUMENT_RULE = 13; + CSSOM.CSSRule.FONT_FEATURE_VALUES_RULE = 14; + CSSOM.CSSRule.VIEWPORT_RULE = 15; + CSSOM.CSSRule.REGION_STYLE_RULE = 16; + + + CSSOM.CSSRule.prototype = { + constructor: CSSOM.CSSRule + //FIXME + }; + + + //.CommonJS + CSSRule.CSSRule = CSSOM.CSSRule; + ///CommonJS + return CSSRule; +} + +var hasRequiredCSSStyleRule; + +function requireCSSStyleRule () { + if (hasRequiredCSSStyleRule) return CSSStyleRule; + hasRequiredCSSStyleRule = 1; + //.CommonJS + var CSSOM = { + CSSStyleDeclaration: requireCSSStyleDeclaration().CSSStyleDeclaration, + CSSRule: requireCSSRule().CSSRule + }; + ///CommonJS + + + /** + * @constructor + * @see http://dev.w3.org/csswg/cssom/#cssstylerule + * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleRule + */ + CSSOM.CSSStyleRule = function CSSStyleRule() { + CSSOM.CSSRule.call(this); + this.selectorText = ""; + this.style = new CSSOM.CSSStyleDeclaration(); + this.style.parentRule = this; + }; + + CSSOM.CSSStyleRule.prototype = new CSSOM.CSSRule(); + CSSOM.CSSStyleRule.prototype.constructor = CSSOM.CSSStyleRule; + CSSOM.CSSStyleRule.prototype.type = 1; + + Object.defineProperty(CSSOM.CSSStyleRule.prototype, "cssText", { + get: function() { + var text; + if (this.selectorText) { + text = this.selectorText + " {" + this.style.cssText + "}"; + } else { + text = ""; + } + return text; + }, + set: function(cssText) { + var rule = CSSOM.CSSStyleRule.parse(cssText); + this.style = rule.style; + this.selectorText = rule.selectorText; + } + }); + + + /** + * NON-STANDARD + * lightweight version of parse.js. + * @param {string} ruleText + * @return CSSStyleRule + */ + CSSOM.CSSStyleRule.parse = function(ruleText) { + var i = 0; + var state = "selector"; + var index; + var j = i; + var buffer = ""; + + var SIGNIFICANT_WHITESPACE = { + "selector": true, + "value": true + }; + + var styleRule = new CSSOM.CSSStyleRule(); + var name, priority=""; + + for (var character; (character = ruleText.charAt(i)); i++) { + + switch (character) { + + case " ": + case "\t": + case "\r": + case "\n": + case "\f": + if (SIGNIFICANT_WHITESPACE[state]) { + // Squash 2 or more white-spaces in the row into 1 + switch (ruleText.charAt(i - 1)) { + case " ": + case "\t": + case "\r": + case "\n": + case "\f": + break; + default: + buffer += " "; + break; + } + } + break; + + // String + case '"': + j = i + 1; + index = ruleText.indexOf('"', j) + 1; + if (!index) { + throw '" is missing'; + } + buffer += ruleText.slice(i, index); + i = index - 1; + break; + + case "'": + j = i + 1; + index = ruleText.indexOf("'", j) + 1; + if (!index) { + throw "' is missing"; + } + buffer += ruleText.slice(i, index); + i = index - 1; + break; + + // Comment + case "/": + if (ruleText.charAt(i + 1) === "*") { + i += 2; + index = ruleText.indexOf("*/", i); + if (index === -1) { + throw new SyntaxError("Missing */"); + } else { + i = index + 1; + } + } else { + buffer += character; + } + break; + + case "{": + if (state === "selector") { + styleRule.selectorText = buffer.trim(); + buffer = ""; + state = "name"; + } + break; + + case ":": + if (state === "name") { + name = buffer.trim(); + buffer = ""; + state = "value"; + } else { + buffer += character; + } + break; + + case "!": + if (state === "value" && ruleText.indexOf("!important", i) === i) { + priority = "important"; + i += "important".length; + } else { + buffer += character; + } + break; + + case ";": + if (state === "value") { + styleRule.style.setProperty(name, buffer.trim(), priority); + priority = ""; + buffer = ""; + state = "name"; + } else { + buffer += character; + } + break; + + case "}": + if (state === "value") { + styleRule.style.setProperty(name, buffer.trim(), priority); + priority = ""; + buffer = ""; + } else if (state === "name") { + break; + } else { + buffer += character; + } + state = "selector"; + break; + + default: + buffer += character; + break; + + } + } + + return styleRule; + + }; + + + //.CommonJS + CSSStyleRule.CSSStyleRule = CSSOM.CSSStyleRule; + ///CommonJS + return CSSStyleRule; +} + +var hasRequiredCSSStyleSheet; + +function requireCSSStyleSheet () { + if (hasRequiredCSSStyleSheet) return CSSStyleSheet; + hasRequiredCSSStyleSheet = 1; + //.CommonJS + var CSSOM = { + StyleSheet: requireStyleSheet().StyleSheet, + CSSStyleRule: requireCSSStyleRule().CSSStyleRule + }; + ///CommonJS + + + /** + * @constructor + * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleSheet + */ + CSSOM.CSSStyleSheet = function CSSStyleSheet() { + CSSOM.StyleSheet.call(this); + this.cssRules = []; + }; + + + CSSOM.CSSStyleSheet.prototype = new CSSOM.StyleSheet(); + CSSOM.CSSStyleSheet.prototype.constructor = CSSOM.CSSStyleSheet; + + + /** + * Used to insert a new rule into the style sheet. The new rule now becomes part of the cascade. + * + * sheet = new Sheet("body {margin: 0}") + * sheet.toString() + * -> "body{margin:0;}" + * sheet.insertRule("img {border: none}", 0) + * -> 0 + * sheet.toString() + * -> "img{border:none;}body{margin:0;}" + * + * @param {string} rule + * @param {number} index + * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleSheet-insertRule + * @return {number} The index within the style sheet's rule collection of the newly inserted rule. + */ + CSSOM.CSSStyleSheet.prototype.insertRule = function(rule, index) { + if (index < 0 || index > this.cssRules.length) { + throw new RangeError("INDEX_SIZE_ERR"); + } + var cssRule = CSSOM.parse(rule).cssRules[0]; + cssRule.parentStyleSheet = this; + this.cssRules.splice(index, 0, cssRule); + return index; + }; + + + /** + * Used to delete a rule from the style sheet. + * + * sheet = new Sheet("img{border:none} body{margin:0}") + * sheet.toString() + * -> "img{border:none;}body{margin:0;}" + * sheet.deleteRule(0) + * sheet.toString() + * -> "body{margin:0;}" + * + * @param {number} index within the style sheet's rule list of the rule to remove. + * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleSheet-deleteRule + */ + CSSOM.CSSStyleSheet.prototype.deleteRule = function(index) { + if (index < 0 || index >= this.cssRules.length) { + throw new RangeError("INDEX_SIZE_ERR"); + } + this.cssRules.splice(index, 1); + }; + + + /** + * NON-STANDARD + * @return {string} serialize stylesheet + */ + CSSOM.CSSStyleSheet.prototype.toString = function() { + var result = ""; + var rules = this.cssRules; + for (var i=0; i<rules.length; i++) { + result += rules[i].cssText + "\n"; + } + return result; + }; + + + //.CommonJS + CSSStyleSheet.CSSStyleSheet = CSSOM.CSSStyleSheet; + CSSOM.parse = requireParse().parse; // Cannot be included sooner due to the mutual dependency between parse.js and CSSStyleSheet.js + ///CommonJS + return CSSStyleSheet; +} + +var CSSImportRule = {}; + +var MediaList = {}; + +var hasRequiredMediaList; + +function requireMediaList () { + if (hasRequiredMediaList) return MediaList; + hasRequiredMediaList = 1; + //.CommonJS + var CSSOM = {}; + ///CommonJS + + + /** + * @constructor + * @see http://dev.w3.org/csswg/cssom/#the-medialist-interface + */ + CSSOM.MediaList = function MediaList(){ + this.length = 0; + }; + + CSSOM.MediaList.prototype = { + + constructor: CSSOM.MediaList, + + /** + * @return {string} + */ + get mediaText() { + return Array.prototype.join.call(this, ", "); + }, + + /** + * @param {string} value + */ + set mediaText(value) { + var values = value.split(","); + var length = this.length = values.length; + for (var i=0; i<length; i++) { + this[i] = values[i].trim(); + } + }, + + /** + * @param {string} medium + */ + appendMedium: function(medium) { + if (Array.prototype.indexOf.call(this, medium) === -1) { + this[this.length] = medium; + this.length++; + } + }, + + /** + * @param {string} medium + */ + deleteMedium: function(medium) { + var index = Array.prototype.indexOf.call(this, medium); + if (index !== -1) { + Array.prototype.splice.call(this, index, 1); + } + } + + }; + + + //.CommonJS + MediaList.MediaList = CSSOM.MediaList; + ///CommonJS + return MediaList; +} + +var hasRequiredCSSImportRule; + +function requireCSSImportRule () { + if (hasRequiredCSSImportRule) return CSSImportRule; + hasRequiredCSSImportRule = 1; + //.CommonJS + var CSSOM = { + CSSRule: requireCSSRule().CSSRule, + CSSStyleSheet: requireCSSStyleSheet().CSSStyleSheet, + MediaList: requireMediaList().MediaList + }; + ///CommonJS + + + /** + * @constructor + * @see http://dev.w3.org/csswg/cssom/#cssimportrule + * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSImportRule + */ + CSSOM.CSSImportRule = function CSSImportRule() { + CSSOM.CSSRule.call(this); + this.href = ""; + this.media = new CSSOM.MediaList(); + this.styleSheet = new CSSOM.CSSStyleSheet(); + }; + + CSSOM.CSSImportRule.prototype = new CSSOM.CSSRule(); + CSSOM.CSSImportRule.prototype.constructor = CSSOM.CSSImportRule; + CSSOM.CSSImportRule.prototype.type = 3; + + Object.defineProperty(CSSOM.CSSImportRule.prototype, "cssText", { + get: function() { + var mediaText = this.media.mediaText; + return "@import url(" + this.href + ")" + (mediaText ? " " + mediaText : "") + ";"; + }, + set: function(cssText) { + var i = 0; + + /** + * @import url(partial.css) screen, handheld; + * || | + * after-import media + * | + * url + */ + var state = ''; + + var buffer = ''; + var index; + for (var character; (character = cssText.charAt(i)); i++) { + + switch (character) { + case ' ': + case '\t': + case '\r': + case '\n': + case '\f': + if (state === 'after-import') { + state = 'url'; + } else { + buffer += character; + } + break; + + case '@': + if (!state && cssText.indexOf('@import', i) === i) { + state = 'after-import'; + i += 'import'.length; + buffer = ''; + } + break; + + case 'u': + if (state === 'url' && cssText.indexOf('url(', i) === i) { + index = cssText.indexOf(')', i + 1); + if (index === -1) { + throw i + ': ")" not found'; + } + i += 'url('.length; + var url = cssText.slice(i, index); + if (url[0] === url[url.length - 1]) { + if (url[0] === '"' || url[0] === "'") { + url = url.slice(1, -1); + } + } + this.href = url; + i = index; + state = 'media'; + } + break; + + case '"': + if (state === 'url') { + index = cssText.indexOf('"', i + 1); + if (!index) { + throw i + ": '\"' not found"; + } + this.href = cssText.slice(i + 1, index); + i = index; + state = 'media'; + } + break; + + case "'": + if (state === 'url') { + index = cssText.indexOf("'", i + 1); + if (!index) { + throw i + ': "\'" not found'; + } + this.href = cssText.slice(i + 1, index); + i = index; + state = 'media'; + } + break; + + case ';': + if (state === 'media') { + if (buffer) { + this.media.mediaText = buffer.trim(); + } + } + break; + + default: + if (state === 'media') { + buffer += character; + } + break; + } + } + } + }); + + + //.CommonJS + CSSImportRule.CSSImportRule = CSSOM.CSSImportRule; + ///CommonJS + return CSSImportRule; +} + +var CSSGroupingRule = {}; + +var hasRequiredCSSGroupingRule; + +function requireCSSGroupingRule () { + if (hasRequiredCSSGroupingRule) return CSSGroupingRule; + hasRequiredCSSGroupingRule = 1; + //.CommonJS + var CSSOM = { + CSSRule: requireCSSRule().CSSRule + }; + ///CommonJS + + + /** + * @constructor + * @see https://drafts.csswg.org/cssom/#the-cssgroupingrule-interface + */ + CSSOM.CSSGroupingRule = function CSSGroupingRule() { + CSSOM.CSSRule.call(this); + this.cssRules = []; + }; + + CSSOM.CSSGroupingRule.prototype = new CSSOM.CSSRule(); + CSSOM.CSSGroupingRule.prototype.constructor = CSSOM.CSSGroupingRule; + + + /** + * Used to insert a new CSS rule to a list of CSS rules. + * + * @example + * cssGroupingRule.cssText + * -> "body{margin:0;}" + * cssGroupingRule.insertRule("img{border:none;}", 1) + * -> 1 + * cssGroupingRule.cssText + * -> "body{margin:0;}img{border:none;}" + * + * @param {string} rule + * @param {number} [index] + * @see https://www.w3.org/TR/cssom-1/#dom-cssgroupingrule-insertrule + * @return {number} The index within the grouping rule's collection of the newly inserted rule. + */ + CSSOM.CSSGroupingRule.prototype.insertRule = function insertRule(rule, index) { + if (index < 0 || index > this.cssRules.length) { + throw new RangeError("INDEX_SIZE_ERR"); + } + var cssRule = CSSOM.parse(rule).cssRules[0]; + cssRule.parentRule = this; + this.cssRules.splice(index, 0, cssRule); + return index; + }; + + /** + * Used to delete a rule from the grouping rule. + * + * cssGroupingRule.cssText + * -> "img{border:none;}body{margin:0;}" + * cssGroupingRule.deleteRule(0) + * cssGroupingRule.cssText + * -> "body{margin:0;}" + * + * @param {number} index within the grouping rule's rule list of the rule to remove. + * @see https://www.w3.org/TR/cssom-1/#dom-cssgroupingrule-deleterule + */ + CSSOM.CSSGroupingRule.prototype.deleteRule = function deleteRule(index) { + if (index < 0 || index >= this.cssRules.length) { + throw new RangeError("INDEX_SIZE_ERR"); + } + this.cssRules.splice(index, 1)[0].parentRule = null; + }; + + //.CommonJS + CSSGroupingRule.CSSGroupingRule = CSSOM.CSSGroupingRule; + ///CommonJS + return CSSGroupingRule; +} + +var CSSMediaRule = {}; + +var CSSConditionRule = {}; + +var hasRequiredCSSConditionRule; + +function requireCSSConditionRule () { + if (hasRequiredCSSConditionRule) return CSSConditionRule; + hasRequiredCSSConditionRule = 1; + //.CommonJS + var CSSOM = { + CSSRule: requireCSSRule().CSSRule, + CSSGroupingRule: requireCSSGroupingRule().CSSGroupingRule + }; + ///CommonJS + + + /** + * @constructor + * @see https://www.w3.org/TR/css-conditional-3/#the-cssconditionrule-interface + */ + CSSOM.CSSConditionRule = function CSSConditionRule() { + CSSOM.CSSGroupingRule.call(this); + this.cssRules = []; + }; + + CSSOM.CSSConditionRule.prototype = new CSSOM.CSSGroupingRule(); + CSSOM.CSSConditionRule.prototype.constructor = CSSOM.CSSConditionRule; + CSSOM.CSSConditionRule.prototype.conditionText = ''; + CSSOM.CSSConditionRule.prototype.cssText = ''; + + //.CommonJS + CSSConditionRule.CSSConditionRule = CSSOM.CSSConditionRule; + ///CommonJS + return CSSConditionRule; +} + +var hasRequiredCSSMediaRule; + +function requireCSSMediaRule () { + if (hasRequiredCSSMediaRule) return CSSMediaRule; + hasRequiredCSSMediaRule = 1; + //.CommonJS + var CSSOM = { + CSSRule: requireCSSRule().CSSRule, + CSSGroupingRule: requireCSSGroupingRule().CSSGroupingRule, + CSSConditionRule: requireCSSConditionRule().CSSConditionRule, + MediaList: requireMediaList().MediaList + }; + ///CommonJS + + + /** + * @constructor + * @see http://dev.w3.org/csswg/cssom/#cssmediarule + * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSMediaRule + */ + CSSOM.CSSMediaRule = function CSSMediaRule() { + CSSOM.CSSConditionRule.call(this); + this.media = new CSSOM.MediaList(); + }; + + CSSOM.CSSMediaRule.prototype = new CSSOM.CSSConditionRule(); + CSSOM.CSSMediaRule.prototype.constructor = CSSOM.CSSMediaRule; + CSSOM.CSSMediaRule.prototype.type = 4; + + // https://opensource.apple.com/source/WebCore/WebCore-7611.1.21.161.3/css/CSSMediaRule.cpp + Object.defineProperties(CSSOM.CSSMediaRule.prototype, { + "conditionText": { + get: function() { + return this.media.mediaText; + }, + set: function(value) { + this.media.mediaText = value; + }, + configurable: true, + enumerable: true + }, + "cssText": { + get: function() { + var cssTexts = []; + for (var i=0, length=this.cssRules.length; i < length; i++) { + cssTexts.push(this.cssRules[i].cssText); + } + return "@media " + this.media.mediaText + " {" + cssTexts.join("") + "}"; + }, + configurable: true, + enumerable: true + } + }); + + + //.CommonJS + CSSMediaRule.CSSMediaRule = CSSOM.CSSMediaRule; + ///CommonJS + return CSSMediaRule; +} + +var CSSSupportsRule = {}; + +var hasRequiredCSSSupportsRule; + +function requireCSSSupportsRule () { + if (hasRequiredCSSSupportsRule) return CSSSupportsRule; + hasRequiredCSSSupportsRule = 1; + //.CommonJS + var CSSOM = { + CSSRule: requireCSSRule().CSSRule, + CSSGroupingRule: requireCSSGroupingRule().CSSGroupingRule, + CSSConditionRule: requireCSSConditionRule().CSSConditionRule + }; + ///CommonJS + + + /** + * @constructor + * @see https://drafts.csswg.org/css-conditional-3/#the-csssupportsrule-interface + */ + CSSOM.CSSSupportsRule = function CSSSupportsRule() { + CSSOM.CSSConditionRule.call(this); + }; + + CSSOM.CSSSupportsRule.prototype = new CSSOM.CSSConditionRule(); + CSSOM.CSSSupportsRule.prototype.constructor = CSSOM.CSSSupportsRule; + CSSOM.CSSSupportsRule.prototype.type = 12; + + Object.defineProperty(CSSOM.CSSSupportsRule.prototype, "cssText", { + get: function() { + var cssTexts = []; + + for (var i = 0, length = this.cssRules.length; i < length; i++) { + cssTexts.push(this.cssRules[i].cssText); + } + + return "@supports " + this.conditionText + " {" + cssTexts.join("") + "}"; + } + }); + + //.CommonJS + CSSSupportsRule.CSSSupportsRule = CSSOM.CSSSupportsRule; + ///CommonJS + return CSSSupportsRule; +} + +var CSSFontFaceRule = {}; + +var hasRequiredCSSFontFaceRule; + +function requireCSSFontFaceRule () { + if (hasRequiredCSSFontFaceRule) return CSSFontFaceRule; + hasRequiredCSSFontFaceRule = 1; + //.CommonJS + var CSSOM = { + CSSStyleDeclaration: requireCSSStyleDeclaration().CSSStyleDeclaration, + CSSRule: requireCSSRule().CSSRule + }; + ///CommonJS + + + /** + * @constructor + * @see http://dev.w3.org/csswg/cssom/#css-font-face-rule + */ + CSSOM.CSSFontFaceRule = function CSSFontFaceRule() { + CSSOM.CSSRule.call(this); + this.style = new CSSOM.CSSStyleDeclaration(); + this.style.parentRule = this; + }; + + CSSOM.CSSFontFaceRule.prototype = new CSSOM.CSSRule(); + CSSOM.CSSFontFaceRule.prototype.constructor = CSSOM.CSSFontFaceRule; + CSSOM.CSSFontFaceRule.prototype.type = 5; + //FIXME + //CSSOM.CSSFontFaceRule.prototype.insertRule = CSSStyleSheet.prototype.insertRule; + //CSSOM.CSSFontFaceRule.prototype.deleteRule = CSSStyleSheet.prototype.deleteRule; + + // http://www.opensource.apple.com/source/WebCore/WebCore-955.66.1/css/WebKitCSSFontFaceRule.cpp + Object.defineProperty(CSSOM.CSSFontFaceRule.prototype, "cssText", { + get: function() { + return "@font-face {" + this.style.cssText + "}"; + } + }); + + + //.CommonJS + CSSFontFaceRule.CSSFontFaceRule = CSSOM.CSSFontFaceRule; + ///CommonJS + return CSSFontFaceRule; +} + +var CSSHostRule = {}; + +var hasRequiredCSSHostRule; + +function requireCSSHostRule () { + if (hasRequiredCSSHostRule) return CSSHostRule; + hasRequiredCSSHostRule = 1; + //.CommonJS + var CSSOM = { + CSSRule: requireCSSRule().CSSRule + }; + ///CommonJS + + + /** + * @constructor + * @see http://www.w3.org/TR/shadow-dom/#host-at-rule + */ + CSSOM.CSSHostRule = function CSSHostRule() { + CSSOM.CSSRule.call(this); + this.cssRules = []; + }; + + CSSOM.CSSHostRule.prototype = new CSSOM.CSSRule(); + CSSOM.CSSHostRule.prototype.constructor = CSSOM.CSSHostRule; + CSSOM.CSSHostRule.prototype.type = 1001; + //FIXME + //CSSOM.CSSHostRule.prototype.insertRule = CSSStyleSheet.prototype.insertRule; + //CSSOM.CSSHostRule.prototype.deleteRule = CSSStyleSheet.prototype.deleteRule; + + Object.defineProperty(CSSOM.CSSHostRule.prototype, "cssText", { + get: function() { + var cssTexts = []; + for (var i=0, length=this.cssRules.length; i < length; i++) { + cssTexts.push(this.cssRules[i].cssText); + } + return "@host {" + cssTexts.join("") + "}"; + } + }); + + + //.CommonJS + CSSHostRule.CSSHostRule = CSSOM.CSSHostRule; + ///CommonJS + return CSSHostRule; +} + +var CSSKeyframeRule = {}; + +var hasRequiredCSSKeyframeRule; + +function requireCSSKeyframeRule () { + if (hasRequiredCSSKeyframeRule) return CSSKeyframeRule; + hasRequiredCSSKeyframeRule = 1; + //.CommonJS + var CSSOM = { + CSSRule: requireCSSRule().CSSRule, + CSSStyleDeclaration: requireCSSStyleDeclaration().CSSStyleDeclaration + }; + ///CommonJS + + + /** + * @constructor + * @see http://www.w3.org/TR/css3-animations/#DOM-CSSKeyframeRule + */ + CSSOM.CSSKeyframeRule = function CSSKeyframeRule() { + CSSOM.CSSRule.call(this); + this.keyText = ''; + this.style = new CSSOM.CSSStyleDeclaration(); + this.style.parentRule = this; + }; + + CSSOM.CSSKeyframeRule.prototype = new CSSOM.CSSRule(); + CSSOM.CSSKeyframeRule.prototype.constructor = CSSOM.CSSKeyframeRule; + CSSOM.CSSKeyframeRule.prototype.type = 8; + //FIXME + //CSSOM.CSSKeyframeRule.prototype.insertRule = CSSStyleSheet.prototype.insertRule; + //CSSOM.CSSKeyframeRule.prototype.deleteRule = CSSStyleSheet.prototype.deleteRule; + + // http://www.opensource.apple.com/source/WebCore/WebCore-955.66.1/css/WebKitCSSKeyframeRule.cpp + Object.defineProperty(CSSOM.CSSKeyframeRule.prototype, "cssText", { + get: function() { + return this.keyText + " {" + this.style.cssText + "} "; + } + }); + + + //.CommonJS + CSSKeyframeRule.CSSKeyframeRule = CSSOM.CSSKeyframeRule; + ///CommonJS + return CSSKeyframeRule; +} + +var CSSKeyframesRule = {}; + +var hasRequiredCSSKeyframesRule; + +function requireCSSKeyframesRule () { + if (hasRequiredCSSKeyframesRule) return CSSKeyframesRule; + hasRequiredCSSKeyframesRule = 1; + //.CommonJS + var CSSOM = { + CSSRule: requireCSSRule().CSSRule + }; + ///CommonJS + + + /** + * @constructor + * @see http://www.w3.org/TR/css3-animations/#DOM-CSSKeyframesRule + */ + CSSOM.CSSKeyframesRule = function CSSKeyframesRule() { + CSSOM.CSSRule.call(this); + this.name = ''; + this.cssRules = []; + }; + + CSSOM.CSSKeyframesRule.prototype = new CSSOM.CSSRule(); + CSSOM.CSSKeyframesRule.prototype.constructor = CSSOM.CSSKeyframesRule; + CSSOM.CSSKeyframesRule.prototype.type = 7; + //FIXME + //CSSOM.CSSKeyframesRule.prototype.insertRule = CSSStyleSheet.prototype.insertRule; + //CSSOM.CSSKeyframesRule.prototype.deleteRule = CSSStyleSheet.prototype.deleteRule; + + // http://www.opensource.apple.com/source/WebCore/WebCore-955.66.1/css/WebKitCSSKeyframesRule.cpp + Object.defineProperty(CSSOM.CSSKeyframesRule.prototype, "cssText", { + get: function() { + var cssTexts = []; + for (var i=0, length=this.cssRules.length; i < length; i++) { + cssTexts.push(" " + this.cssRules[i].cssText); + } + return "@" + (this._vendorPrefix || '') + "keyframes " + this.name + " { \n" + cssTexts.join("\n") + "\n}"; + } + }); + + + //.CommonJS + CSSKeyframesRule.CSSKeyframesRule = CSSOM.CSSKeyframesRule; + ///CommonJS + return CSSKeyframesRule; +} + +var CSSValueExpression = {}; + +var CSSValue = {}; + +var hasRequiredCSSValue; + +function requireCSSValue () { + if (hasRequiredCSSValue) return CSSValue; + hasRequiredCSSValue = 1; + //.CommonJS + var CSSOM = {}; + ///CommonJS + + + /** + * @constructor + * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSValue + * + * TODO: add if needed + */ + CSSOM.CSSValue = function CSSValue() { + }; + + CSSOM.CSSValue.prototype = { + constructor: CSSOM.CSSValue, + + // @see: http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSValue + set cssText(text) { + var name = this._getConstructorName(); + + throw new Error('DOMException: property "cssText" of "' + name + '" is readonly and can not be replaced with "' + text + '"!'); + }, + + get cssText() { + var name = this._getConstructorName(); + + throw new Error('getter "cssText" of "' + name + '" is not implemented!'); + }, + + _getConstructorName: function() { + var s = this.constructor.toString(), + c = s.match(/function\s([^\(]+)/), + name = c[1]; + + return name; + } + }; + + + //.CommonJS + CSSValue.CSSValue = CSSOM.CSSValue; + ///CommonJS + return CSSValue; +} + +var hasRequiredCSSValueExpression; + +function requireCSSValueExpression () { + if (hasRequiredCSSValueExpression) return CSSValueExpression; + hasRequiredCSSValueExpression = 1; + //.CommonJS + var CSSOM = { + CSSValue: requireCSSValue().CSSValue + }; + ///CommonJS + + + /** + * @constructor + * @see http://msdn.microsoft.com/en-us/library/ms537634(v=vs.85).aspx + * + */ + CSSOM.CSSValueExpression = function CSSValueExpression(token, idx) { + this._token = token; + this._idx = idx; + }; + + CSSOM.CSSValueExpression.prototype = new CSSOM.CSSValue(); + CSSOM.CSSValueExpression.prototype.constructor = CSSOM.CSSValueExpression; + + /** + * parse css expression() value + * + * @return {Object} + * - error: + * or + * - idx: + * - expression: + * + * Example: + * + * .selector { + * zoom: expression(documentElement.clientWidth > 1000 ? '1000px' : 'auto'); + * } + */ + CSSOM.CSSValueExpression.prototype.parse = function() { + var token = this._token, + idx = this._idx; + + var character = '', + expression = '', + error = '', + info, + paren = []; + + + for (; ; ++idx) { + character = token.charAt(idx); + + // end of token + if (character === '') { + error = 'css expression error: unfinished expression!'; + break; + } + + switch(character) { + case '(': + paren.push(character); + expression += character; + break; + + case ')': + paren.pop(character); + expression += character; + break; + + case '/': + if ((info = this._parseJSComment(token, idx))) { // comment? + if (info.error) { + error = 'css expression error: unfinished comment in expression!'; + } else { + idx = info.idx; + // ignore the comment + } + } else if ((info = this._parseJSRexExp(token, idx))) { // regexp + idx = info.idx; + expression += info.text; + } else { // other + expression += character; + } + break; + + case "'": + case '"': + info = this._parseJSString(token, idx, character); + if (info) { // string + idx = info.idx; + expression += info.text; + } else { + expression += character; + } + break; + + default: + expression += character; + break; + } + + if (error) { + break; + } + + // end of expression + if (paren.length === 0) { + break; + } + } + + var ret; + if (error) { + ret = { + error: error + }; + } else { + ret = { + idx: idx, + expression: expression + }; + } + + return ret; + }; + + + /** + * + * @return {Object|false} + * - idx: + * - text: + * or + * - error: + * or + * false + * + */ + CSSOM.CSSValueExpression.prototype._parseJSComment = function(token, idx) { + var nextChar = token.charAt(idx + 1), + text; + + if (nextChar === '/' || nextChar === '*') { + var startIdx = idx, + endIdx, + commentEndChar; + + if (nextChar === '/') { // line comment + commentEndChar = '\n'; + } else if (nextChar === '*') { // block comment + commentEndChar = '*/'; + } + + endIdx = token.indexOf(commentEndChar, startIdx + 1 + 1); + if (endIdx !== -1) { + endIdx = endIdx + commentEndChar.length - 1; + text = token.substring(idx, endIdx + 1); + return { + idx: endIdx, + text: text + }; + } else { + var error = 'css expression error: unfinished comment in expression!'; + return { + error: error + }; + } + } else { + return false; + } + }; + + + /** + * + * @return {Object|false} + * - idx: + * - text: + * or + * false + * + */ + CSSOM.CSSValueExpression.prototype._parseJSString = function(token, idx, sep) { + var endIdx = this._findMatchedIdx(token, idx, sep), + text; + + if (endIdx === -1) { + return false; + } else { + text = token.substring(idx, endIdx + sep.length); + + return { + idx: endIdx, + text: text + }; + } + }; + + + /** + * parse regexp in css expression + * + * @return {Object|false} + * - idx: + * - regExp: + * or + * false + */ + + /* + + all legal RegExp + + /a/ + (/a/) + [/a/] + [12, /a/] + + !/a/ + + +/a/ + -/a/ + * /a/ + / /a/ + %/a/ + + ===/a/ + !==/a/ + ==/a/ + !=/a/ + >/a/ + >=/a/ + </a/ + <=/a/ + + &/a/ + |/a/ + ^/a/ + ~/a/ + <</a/ + >>/a/ + >>>/a/ + + &&/a/ + ||/a/ + ?/a/ + =/a/ + ,/a/ + + delete /a/ + in /a/ + instanceof /a/ + new /a/ + typeof /a/ + void /a/ + + */ + CSSOM.CSSValueExpression.prototype._parseJSRexExp = function(token, idx) { + var before = token.substring(0, idx).replace(/\s+$/, ""), + legalRegx = [ + /^$/, + /\($/, + /\[$/, + /\!$/, + /\+$/, + /\-$/, + /\*$/, + /\/\s+/, + /\%$/, + /\=$/, + /\>$/, + /<$/, + /\&$/, + /\|$/, + /\^$/, + /\~$/, + /\?$/, + /\,$/, + /delete$/, + /in$/, + /instanceof$/, + /new$/, + /typeof$/, + /void$/ + ]; + + var isLegal = legalRegx.some(function(reg) { + return reg.test(before); + }); + + if (!isLegal) { + return false; + } else { + var sep = '/'; + + // same logic as string + return this._parseJSString(token, idx, sep); + } + }; + + + /** + * + * find next sep(same line) index in `token` + * + * @return {Number} + * + */ + CSSOM.CSSValueExpression.prototype._findMatchedIdx = function(token, idx, sep) { + var startIdx = idx, + endIdx; + + var NOT_FOUND = -1; + + while(true) { + endIdx = token.indexOf(sep, startIdx + 1); + + if (endIdx === -1) { // not found + endIdx = NOT_FOUND; + break; + } else { + var text = token.substring(idx + 1, endIdx), + matched = text.match(/\\+$/); + if (!matched || matched[0] % 2 === 0) { // not escaped + break; + } else { + startIdx = endIdx; + } + } + } + + // boundary must be in the same line(js sting or regexp) + var nextNewLineIdx = token.indexOf('\n', idx + 1); + if (nextNewLineIdx < endIdx) { + endIdx = NOT_FOUND; + } + + + return endIdx; + }; + + + + + //.CommonJS + CSSValueExpression.CSSValueExpression = CSSOM.CSSValueExpression; + ///CommonJS + return CSSValueExpression; +} + +var CSSDocumentRule = {}; + +var MatcherList = {}; + +var hasRequiredMatcherList; + +function requireMatcherList () { + if (hasRequiredMatcherList) return MatcherList; + hasRequiredMatcherList = 1; + //.CommonJS + var CSSOM = {}; + ///CommonJS + + + /** + * @constructor + * @see https://developer.mozilla.org/en/CSS/@-moz-document + */ + CSSOM.MatcherList = function MatcherList(){ + this.length = 0; + }; + + CSSOM.MatcherList.prototype = { + + constructor: CSSOM.MatcherList, + + /** + * @return {string} + */ + get matcherText() { + return Array.prototype.join.call(this, ", "); + }, + + /** + * @param {string} value + */ + set matcherText(value) { + // just a temporary solution, actually it may be wrong by just split the value with ',', because a url can include ','. + var values = value.split(","); + var length = this.length = values.length; + for (var i=0; i<length; i++) { + this[i] = values[i].trim(); + } + }, + + /** + * @param {string} matcher + */ + appendMatcher: function(matcher) { + if (Array.prototype.indexOf.call(this, matcher) === -1) { + this[this.length] = matcher; + this.length++; + } + }, + + /** + * @param {string} matcher + */ + deleteMatcher: function(matcher) { + var index = Array.prototype.indexOf.call(this, matcher); + if (index !== -1) { + Array.prototype.splice.call(this, index, 1); + } + } + + }; + + + //.CommonJS + MatcherList.MatcherList = CSSOM.MatcherList; + ///CommonJS + return MatcherList; +} + +var hasRequiredCSSDocumentRule; + +function requireCSSDocumentRule () { + if (hasRequiredCSSDocumentRule) return CSSDocumentRule; + hasRequiredCSSDocumentRule = 1; + //.CommonJS + var CSSOM = { + CSSRule: requireCSSRule().CSSRule, + MatcherList: requireMatcherList().MatcherList + }; + ///CommonJS + + + /** + * @constructor + * @see https://developer.mozilla.org/en/CSS/@-moz-document + */ + CSSOM.CSSDocumentRule = function CSSDocumentRule() { + CSSOM.CSSRule.call(this); + this.matcher = new CSSOM.MatcherList(); + this.cssRules = []; + }; + + CSSOM.CSSDocumentRule.prototype = new CSSOM.CSSRule(); + CSSOM.CSSDocumentRule.prototype.constructor = CSSOM.CSSDocumentRule; + CSSOM.CSSDocumentRule.prototype.type = 10; + //FIXME + //CSSOM.CSSDocumentRule.prototype.insertRule = CSSStyleSheet.prototype.insertRule; + //CSSOM.CSSDocumentRule.prototype.deleteRule = CSSStyleSheet.prototype.deleteRule; + + Object.defineProperty(CSSOM.CSSDocumentRule.prototype, "cssText", { + get: function() { + var cssTexts = []; + for (var i=0, length=this.cssRules.length; i < length; i++) { + cssTexts.push(this.cssRules[i].cssText); + } + return "@-moz-document " + this.matcher.matcherText + " {" + cssTexts.join("") + "}"; + } + }); + + + //.CommonJS + CSSDocumentRule.CSSDocumentRule = CSSOM.CSSDocumentRule; + ///CommonJS + return CSSDocumentRule; +} + +var hasRequiredParse; + +function requireParse () { + if (hasRequiredParse) return parse$1; + hasRequiredParse = 1; + //.CommonJS + var CSSOM = {}; + ///CommonJS + + + /** + * @param {string} token + */ + CSSOM.parse = function parse(token) { + + var i = 0; + + /** + "before-selector" or + "selector" or + "atRule" or + "atBlock" or + "conditionBlock" or + "before-name" or + "name" or + "before-value" or + "value" + */ + var state = "before-selector"; + + var index; + var buffer = ""; + var valueParenthesisDepth = 0; + + var SIGNIFICANT_WHITESPACE = { + "selector": true, + "value": true, + "value-parenthesis": true, + "atRule": true, + "importRule-begin": true, + "importRule": true, + "atBlock": true, + "conditionBlock": true, + 'documentRule-begin': true + }; + + var styleSheet = new CSSOM.CSSStyleSheet(); + + // @type CSSStyleSheet|CSSMediaRule|CSSSupportsRule|CSSFontFaceRule|CSSKeyframesRule|CSSDocumentRule + var currentScope = styleSheet; + + // @type CSSMediaRule|CSSSupportsRule|CSSKeyframesRule|CSSDocumentRule + var parentRule; + + var ancestorRules = []; + var hasAncestors = false; + var prevScope; + + var name, priority="", styleRule, mediaRule, supportsRule, importRule, fontFaceRule, keyframesRule, documentRule, hostRule; + + var atKeyframesRegExp = /@(-(?:\w+-)+)?keyframes/g; + + var parseError = function(message) { + var lines = token.substring(0, i).split('\n'); + var lineCount = lines.length; + var charCount = lines.pop().length + 1; + var error = new Error(message + ' (line ' + lineCount + ', char ' + charCount + ')'); + error.line = lineCount; + /* jshint sub : true */ + error['char'] = charCount; + error.styleSheet = styleSheet; + throw error; + }; + + for (var character; (character = token.charAt(i)); i++) { + + switch (character) { + + case " ": + case "\t": + case "\r": + case "\n": + case "\f": + if (SIGNIFICANT_WHITESPACE[state]) { + buffer += character; + } + break; + + // String + case '"': + index = i + 1; + do { + index = token.indexOf('"', index) + 1; + if (!index) { + parseError('Unmatched "'); + } + } while (token[index - 2] === '\\'); + buffer += token.slice(i, index); + i = index - 1; + switch (state) { + case 'before-value': + state = 'value'; + break; + case 'importRule-begin': + state = 'importRule'; + break; + } + break; + + case "'": + index = i + 1; + do { + index = token.indexOf("'", index) + 1; + if (!index) { + parseError("Unmatched '"); + } + } while (token[index - 2] === '\\'); + buffer += token.slice(i, index); + i = index - 1; + switch (state) { + case 'before-value': + state = 'value'; + break; + case 'importRule-begin': + state = 'importRule'; + break; + } + break; + + // Comment + case "/": + if (token.charAt(i + 1) === "*") { + i += 2; + index = token.indexOf("*/", i); + if (index === -1) { + parseError("Missing */"); + } else { + i = index + 1; + } + } else { + buffer += character; + } + if (state === "importRule-begin") { + buffer += " "; + state = "importRule"; + } + break; + + // At-rule + case "@": + if (token.indexOf("@-moz-document", i) === i) { + state = "documentRule-begin"; + documentRule = new CSSOM.CSSDocumentRule(); + documentRule.__starts = i; + i += "-moz-document".length; + buffer = ""; + break; + } else if (token.indexOf("@media", i) === i) { + state = "atBlock"; + mediaRule = new CSSOM.CSSMediaRule(); + mediaRule.__starts = i; + i += "media".length; + buffer = ""; + break; + } else if (token.indexOf("@supports", i) === i) { + state = "conditionBlock"; + supportsRule = new CSSOM.CSSSupportsRule(); + supportsRule.__starts = i; + i += "supports".length; + buffer = ""; + break; + } else if (token.indexOf("@host", i) === i) { + state = "hostRule-begin"; + i += "host".length; + hostRule = new CSSOM.CSSHostRule(); + hostRule.__starts = i; + buffer = ""; + break; + } else if (token.indexOf("@import", i) === i) { + state = "importRule-begin"; + i += "import".length; + buffer += "@import"; + break; + } else if (token.indexOf("@font-face", i) === i) { + state = "fontFaceRule-begin"; + i += "font-face".length; + fontFaceRule = new CSSOM.CSSFontFaceRule(); + fontFaceRule.__starts = i; + buffer = ""; + break; + } else { + atKeyframesRegExp.lastIndex = i; + var matchKeyframes = atKeyframesRegExp.exec(token); + if (matchKeyframes && matchKeyframes.index === i) { + state = "keyframesRule-begin"; + keyframesRule = new CSSOM.CSSKeyframesRule(); + keyframesRule.__starts = i; + keyframesRule._vendorPrefix = matchKeyframes[1]; // Will come out as undefined if no prefix was found + i += matchKeyframes[0].length - 1; + buffer = ""; + break; + } else if (state === "selector") { + state = "atRule"; + } + } + buffer += character; + break; + + case "{": + if (state === "selector" || state === "atRule") { + styleRule.selectorText = buffer.trim(); + styleRule.style.__starts = i; + buffer = ""; + state = "before-name"; + } else if (state === "atBlock") { + mediaRule.media.mediaText = buffer.trim(); + + if (parentRule) { + ancestorRules.push(parentRule); + } + + currentScope = parentRule = mediaRule; + mediaRule.parentStyleSheet = styleSheet; + buffer = ""; + state = "before-selector"; + } else if (state === "conditionBlock") { + supportsRule.conditionText = buffer.trim(); + + if (parentRule) { + ancestorRules.push(parentRule); + } + + currentScope = parentRule = supportsRule; + supportsRule.parentStyleSheet = styleSheet; + buffer = ""; + state = "before-selector"; + } else if (state === "hostRule-begin") { + if (parentRule) { + ancestorRules.push(parentRule); + } + + currentScope = parentRule = hostRule; + hostRule.parentStyleSheet = styleSheet; + buffer = ""; + state = "before-selector"; + } else if (state === "fontFaceRule-begin") { + if (parentRule) { + fontFaceRule.parentRule = parentRule; + } + fontFaceRule.parentStyleSheet = styleSheet; + styleRule = fontFaceRule; + buffer = ""; + state = "before-name"; + } else if (state === "keyframesRule-begin") { + keyframesRule.name = buffer.trim(); + if (parentRule) { + ancestorRules.push(parentRule); + keyframesRule.parentRule = parentRule; + } + keyframesRule.parentStyleSheet = styleSheet; + currentScope = parentRule = keyframesRule; + buffer = ""; + state = "keyframeRule-begin"; + } else if (state === "keyframeRule-begin") { + styleRule = new CSSOM.CSSKeyframeRule(); + styleRule.keyText = buffer.trim(); + styleRule.__starts = i; + buffer = ""; + state = "before-name"; + } else if (state === "documentRule-begin") { + // FIXME: what if this '{' is in the url text of the match function? + documentRule.matcher.matcherText = buffer.trim(); + if (parentRule) { + ancestorRules.push(parentRule); + documentRule.parentRule = parentRule; + } + currentScope = parentRule = documentRule; + documentRule.parentStyleSheet = styleSheet; + buffer = ""; + state = "before-selector"; + } + break; + + case ":": + if (state === "name") { + name = buffer.trim(); + buffer = ""; + state = "before-value"; + } else { + buffer += character; + } + break; + + case "(": + if (state === 'value') { + // ie css expression mode + if (buffer.trim() === 'expression') { + var info = (new CSSOM.CSSValueExpression(token, i)).parse(); + + if (info.error) { + parseError(info.error); + } else { + buffer += info.expression; + i = info.idx; + } + } else { + state = 'value-parenthesis'; + //always ensure this is reset to 1 on transition + //from value to value-parenthesis + valueParenthesisDepth = 1; + buffer += character; + } + } else if (state === 'value-parenthesis') { + valueParenthesisDepth++; + buffer += character; + } else { + buffer += character; + } + break; + + case ")": + if (state === 'value-parenthesis') { + valueParenthesisDepth--; + if (valueParenthesisDepth === 0) state = 'value'; + } + buffer += character; + break; + + case "!": + if (state === "value" && token.indexOf("!important", i) === i) { + priority = "important"; + i += "important".length; + } else { + buffer += character; + } + break; + + case ";": + switch (state) { + case "value": + styleRule.style.setProperty(name, buffer.trim(), priority); + priority = ""; + buffer = ""; + state = "before-name"; + break; + case "atRule": + buffer = ""; + state = "before-selector"; + break; + case "importRule": + importRule = new CSSOM.CSSImportRule(); + importRule.parentStyleSheet = importRule.styleSheet.parentStyleSheet = styleSheet; + importRule.cssText = buffer + character; + styleSheet.cssRules.push(importRule); + buffer = ""; + state = "before-selector"; + break; + default: + buffer += character; + break; + } + break; + + case "}": + switch (state) { + case "value": + styleRule.style.setProperty(name, buffer.trim(), priority); + priority = ""; + /* falls through */ + case "before-name": + case "name": + styleRule.__ends = i + 1; + if (parentRule) { + styleRule.parentRule = parentRule; + } + styleRule.parentStyleSheet = styleSheet; + currentScope.cssRules.push(styleRule); + buffer = ""; + if (currentScope.constructor === CSSOM.CSSKeyframesRule) { + state = "keyframeRule-begin"; + } else { + state = "before-selector"; + } + break; + case "keyframeRule-begin": + case "before-selector": + case "selector": + // End of media/supports/document rule. + if (!parentRule) { + parseError("Unexpected }"); + } + + // Handle rules nested in @media or @supports + hasAncestors = ancestorRules.length > 0; + + while (ancestorRules.length > 0) { + parentRule = ancestorRules.pop(); + + if ( + parentRule.constructor.name === "CSSMediaRule" + || parentRule.constructor.name === "CSSSupportsRule" + ) { + prevScope = currentScope; + currentScope = parentRule; + currentScope.cssRules.push(prevScope); + break; + } + + if (ancestorRules.length === 0) { + hasAncestors = false; + } + } + + if (!hasAncestors) { + currentScope.__ends = i + 1; + styleSheet.cssRules.push(currentScope); + currentScope = styleSheet; + parentRule = null; + } + + buffer = ""; + state = "before-selector"; + break; + } + break; + + default: + switch (state) { + case "before-selector": + state = "selector"; + styleRule = new CSSOM.CSSStyleRule(); + styleRule.__starts = i; + break; + case "before-name": + state = "name"; + break; + case "before-value": + state = "value"; + break; + case "importRule-begin": + state = "importRule"; + break; + } + buffer += character; + break; + } + } + + return styleSheet; + }; + + + //.CommonJS + parse$1.parse = CSSOM.parse; + // The following modules cannot be included sooner due to the mutual dependency with parse.js + CSSOM.CSSStyleSheet = requireCSSStyleSheet().CSSStyleSheet; + CSSOM.CSSStyleRule = requireCSSStyleRule().CSSStyleRule; + CSSOM.CSSImportRule = requireCSSImportRule().CSSImportRule; + CSSOM.CSSGroupingRule = requireCSSGroupingRule().CSSGroupingRule; + CSSOM.CSSMediaRule = requireCSSMediaRule().CSSMediaRule; + CSSOM.CSSConditionRule = requireCSSConditionRule().CSSConditionRule; + CSSOM.CSSSupportsRule = requireCSSSupportsRule().CSSSupportsRule; + CSSOM.CSSFontFaceRule = requireCSSFontFaceRule().CSSFontFaceRule; + CSSOM.CSSHostRule = requireCSSHostRule().CSSHostRule; + CSSOM.CSSStyleDeclaration = requireCSSStyleDeclaration().CSSStyleDeclaration; + CSSOM.CSSKeyframeRule = requireCSSKeyframeRule().CSSKeyframeRule; + CSSOM.CSSKeyframesRule = requireCSSKeyframesRule().CSSKeyframesRule; + CSSOM.CSSValueExpression = requireCSSValueExpression().CSSValueExpression; + CSSOM.CSSDocumentRule = requireCSSDocumentRule().CSSDocumentRule; + ///CommonJS + return parse$1; +} + +var hasRequiredCSSStyleDeclaration; + +function requireCSSStyleDeclaration () { + if (hasRequiredCSSStyleDeclaration) return CSSStyleDeclaration; + hasRequiredCSSStyleDeclaration = 1; + //.CommonJS + var CSSOM = {}; + ///CommonJS + + + /** + * @constructor + * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration + */ + CSSOM.CSSStyleDeclaration = function CSSStyleDeclaration(){ + this.length = 0; + this.parentRule = null; + + // NON-STANDARD + this._importants = {}; + }; + + + CSSOM.CSSStyleDeclaration.prototype = { + + constructor: CSSOM.CSSStyleDeclaration, + + /** + * + * @param {string} name + * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration-getPropertyValue + * @return {string} the value of the property if it has been explicitly set for this declaration block. + * Returns the empty string if the property has not been set. + */ + getPropertyValue: function(name) { + return this[name] || ""; + }, + + /** + * + * @param {string} name + * @param {string} value + * @param {string} [priority=null] "important" or null + * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration-setProperty + */ + setProperty: function(name, value, priority) { + if (this[name]) { + // Property already exist. Overwrite it. + var index = Array.prototype.indexOf.call(this, name); + if (index < 0) { + this[this.length] = name; + this.length++; + } + } else { + // New property. + this[this.length] = name; + this.length++; + } + this[name] = value + ""; + this._importants[name] = priority; + }, + + /** + * + * @param {string} name + * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration-removeProperty + * @return {string} the value of the property if it has been explicitly set for this declaration block. + * Returns the empty string if the property has not been set or the property name does not correspond to a known CSS property. + */ + removeProperty: function(name) { + if (!(name in this)) { + return ""; + } + var index = Array.prototype.indexOf.call(this, name); + if (index < 0) { + return ""; + } + var prevValue = this[name]; + this[name] = ""; + + // That's what WebKit and Opera do + Array.prototype.splice.call(this, index, 1); + + // That's what Firefox does + //this[index] = "" + + return prevValue; + }, + + getPropertyCSSValue: function() { + //FIXME + }, + + /** + * + * @param {String} name + */ + getPropertyPriority: function(name) { + return this._importants[name] || ""; + }, + + + /** + * element.style.overflow = "auto" + * element.style.getPropertyShorthand("overflow-x") + * -> "overflow" + */ + getPropertyShorthand: function() { + //FIXME + }, + + isPropertyImplicit: function() { + //FIXME + }, + + // Doesn't work in IE < 9 + get cssText(){ + var properties = []; + for (var i=0, length=this.length; i < length; ++i) { + var name = this[i]; + var value = this.getPropertyValue(name); + var priority = this.getPropertyPriority(name); + if (priority) { + priority = " !" + priority; + } + properties[i] = name + ": " + value + priority + ";"; + } + return properties.join(" "); + }, + + set cssText(text){ + var i, name; + for (i = this.length; i--;) { + name = this[i]; + this[name] = ""; + } + Array.prototype.splice.call(this, 0, this.length); + this._importants = {}; + + var dummyRule = CSSOM.parse('#bogus{' + text + '}').cssRules[0].style; + var length = dummyRule.length; + for (i = 0; i < length; ++i) { + name = dummyRule[i]; + this.setProperty(dummyRule[i], dummyRule.getPropertyValue(name), dummyRule.getPropertyPriority(name)); + } + } + }; + + + //.CommonJS + CSSStyleDeclaration.CSSStyleDeclaration = CSSOM.CSSStyleDeclaration; + CSSOM.parse = requireParse().parse; // Cannot be included sooner due to the mutual dependency between parse.js and CSSStyleDeclaration.js + ///CommonJS + return CSSStyleDeclaration; +} + +var clone = {}; + +var hasRequiredClone; + +function requireClone () { + if (hasRequiredClone) return clone; + hasRequiredClone = 1; + //.CommonJS + var CSSOM = { + CSSStyleSheet: requireCSSStyleSheet().CSSStyleSheet, + CSSRule: requireCSSRule().CSSRule, + CSSStyleRule: requireCSSStyleRule().CSSStyleRule, + CSSGroupingRule: requireCSSGroupingRule().CSSGroupingRule, + CSSConditionRule: requireCSSConditionRule().CSSConditionRule, + CSSMediaRule: requireCSSMediaRule().CSSMediaRule, + CSSSupportsRule: requireCSSSupportsRule().CSSSupportsRule, + CSSStyleDeclaration: requireCSSStyleDeclaration().CSSStyleDeclaration, + CSSKeyframeRule: requireCSSKeyframeRule().CSSKeyframeRule, + CSSKeyframesRule: requireCSSKeyframesRule().CSSKeyframesRule + }; + ///CommonJS + + + /** + * Produces a deep copy of stylesheet — the instance variables of stylesheet are copied recursively. + * @param {CSSStyleSheet|CSSOM.CSSStyleSheet} stylesheet + * @nosideeffects + * @return {CSSOM.CSSStyleSheet} + */ + CSSOM.clone = function clone(stylesheet) { + + var cloned = new CSSOM.CSSStyleSheet(); + + var rules = stylesheet.cssRules; + if (!rules) { + return cloned; + } + + for (var i = 0, rulesLength = rules.length; i < rulesLength; i++) { + var rule = rules[i]; + var ruleClone = cloned.cssRules[i] = new rule.constructor(); + + var style = rule.style; + if (style) { + var styleClone = ruleClone.style = new CSSOM.CSSStyleDeclaration(); + for (var j = 0, styleLength = style.length; j < styleLength; j++) { + var name = styleClone[j] = style[j]; + styleClone[name] = style[name]; + styleClone._importants[name] = style.getPropertyPriority(name); + } + styleClone.length = style.length; + } + + if (rule.hasOwnProperty('keyText')) { + ruleClone.keyText = rule.keyText; + } + + if (rule.hasOwnProperty('selectorText')) { + ruleClone.selectorText = rule.selectorText; + } + + if (rule.hasOwnProperty('mediaText')) { + ruleClone.mediaText = rule.mediaText; + } + + if (rule.hasOwnProperty('conditionText')) { + ruleClone.conditionText = rule.conditionText; + } + + if (rule.hasOwnProperty('cssRules')) { + ruleClone.cssRules = clone(rule).cssRules; + } + } + + return cloned; + + }; + + //.CommonJS + clone.clone = CSSOM.clone; + ///CommonJS + return clone; +} + +var hasRequiredLib; + +function requireLib () { + if (hasRequiredLib) return lib; + hasRequiredLib = 1; + + lib.CSSStyleDeclaration = requireCSSStyleDeclaration().CSSStyleDeclaration; + lib.CSSRule = requireCSSRule().CSSRule; + lib.CSSGroupingRule = requireCSSGroupingRule().CSSGroupingRule; + lib.CSSConditionRule = requireCSSConditionRule().CSSConditionRule; + lib.CSSStyleRule = requireCSSStyleRule().CSSStyleRule; + lib.MediaList = requireMediaList().MediaList; + lib.CSSMediaRule = requireCSSMediaRule().CSSMediaRule; + lib.CSSSupportsRule = requireCSSSupportsRule().CSSSupportsRule; + lib.CSSImportRule = requireCSSImportRule().CSSImportRule; + lib.CSSFontFaceRule = requireCSSFontFaceRule().CSSFontFaceRule; + lib.CSSHostRule = requireCSSHostRule().CSSHostRule; + lib.StyleSheet = requireStyleSheet().StyleSheet; + lib.CSSStyleSheet = requireCSSStyleSheet().CSSStyleSheet; + lib.CSSKeyframesRule = requireCSSKeyframesRule().CSSKeyframesRule; + lib.CSSKeyframeRule = requireCSSKeyframeRule().CSSKeyframeRule; + lib.MatcherList = requireMatcherList().MatcherList; + lib.CSSDocumentRule = requireCSSDocumentRule().CSSDocumentRule; + lib.CSSValue = requireCSSValue().CSSValue; + lib.CSSValueExpression = requireCSSValueExpression().CSSValueExpression; + lib.parse = requireParse().parse; + lib.clone = requireClone().clone; + return lib; +} + +var libExports = requireLib(); + +const tagName$e = 'style'; + +/** + * @implements globalThis.HTMLStyleElement + */ +class HTMLStyleElement extends TextElement { + constructor(ownerDocument, localName = tagName$e) { + super(ownerDocument, localName); + this[SHEET] = null; + } + + get sheet() { + const sheet = this[SHEET]; + if (sheet !== null) { + return sheet; + } + return this[SHEET] = libExports.parse(this.textContent); + } + + get innerHTML() { + return super.innerHTML || ''; + } + set innerHTML(value) { + super.textContent = value; + this[SHEET] = null; + } + get innerText() { + return super.innerText || ''; + } + set innerText(value) { + super.textContent = value; + this[SHEET] = null; + } + get textContent() { + return super.textContent || ''; + } + set textContent(value) { + super.textContent = value; + this[SHEET] = null; + } +} + +registerHTMLClass(tagName$e, HTMLStyleElement); + +/** + * @implements globalThis.HTMLTimeElement + */ +class HTMLTimeElement extends HTMLElement { + constructor(ownerDocument, localName = 'time') { + super(ownerDocument, localName); + } + + /** + * @type {string} + */ + get dateTime() { return stringAttribute.get(this, 'datetime'); } + set dateTime(value) { stringAttribute.set(this, 'datetime', value); } +} + +registerHTMLClass('time', HTMLTimeElement); + +/** + * @implements globalThis.HTMLFieldSetElement + */ +class HTMLFieldSetElement extends HTMLElement { + constructor(ownerDocument, localName = 'fieldset') { + super(ownerDocument, localName); + } +} + +/** + * @implements globalThis.HTMLEmbedElement + */ +class HTMLEmbedElement extends HTMLElement { + constructor(ownerDocument, localName = 'embed') { + super(ownerDocument, localName); + } +} + +/** + * @implements globalThis.HTMLHRElement + */ +class HTMLHRElement extends HTMLElement { + constructor(ownerDocument, localName = 'hr') { + super(ownerDocument, localName); + } +} + +/** + * @implements globalThis.HTMLProgressElement + */ +class HTMLProgressElement extends HTMLElement { + constructor(ownerDocument, localName = 'progress') { + super(ownerDocument, localName); + } +} + +/** + * @implements globalThis.HTMLParagraphElement + */ +class HTMLParagraphElement extends HTMLElement { + constructor(ownerDocument, localName = 'p') { + super(ownerDocument, localName); + } +} + +/** + * @implements globalThis.HTMLTableElement + */ +class HTMLTableElement extends HTMLElement { + constructor(ownerDocument, localName = 'table') { + super(ownerDocument, localName); + } +} + +/** + * @implements globalThis.HTMLFrameSetElement + */ +class HTMLFrameSetElement extends HTMLElement { + constructor(ownerDocument, localName = 'frameset') { + super(ownerDocument, localName); + } +} + +/** + * @implements globalThis.HTMLLIElement + */ +class HTMLLIElement extends HTMLElement { + constructor(ownerDocument, localName = 'li') { + super(ownerDocument, localName); + } +} + +/** + * @implements globalThis.HTMLBaseElement + */ +class HTMLBaseElement extends HTMLElement { + constructor(ownerDocument, localName = 'base') { + super(ownerDocument, localName); + } +} + +/** + * @implements globalThis.HTMLDataListElement + */ +class HTMLDataListElement extends HTMLElement { + constructor(ownerDocument, localName = 'datalist') { + super(ownerDocument, localName); + } +} + +const tagName$d = 'input'; + +/** + * @implements globalThis.HTMLInputElement + */ +class HTMLInputElement extends HTMLElement { + constructor(ownerDocument, localName = tagName$d) { + super(ownerDocument, localName); + } + + /* c8 ignore start */ + get autofocus() { return booleanAttribute.get(this, 'autofocus') || -1; } + set autofocus(value) { booleanAttribute.set(this, 'autofocus', value); } + + get disabled() { return booleanAttribute.get(this, 'disabled'); } + set disabled(value) { booleanAttribute.set(this, 'disabled', value); } + + get name() { return this.getAttribute('name'); } + set name(value) { this.setAttribute('name', value); } + + get placeholder() { return this.getAttribute('placeholder'); } + set placeholder(value) { this.setAttribute('placeholder', value); } + + get type() { return this.getAttribute('type'); } + set type(value) { this.setAttribute('type', value); } + + get value() { return stringAttribute.get(this, 'value'); } + set value(value) { stringAttribute.set(this, 'value', value); } + /* c8 ignore stop */ +} + +registerHTMLClass(tagName$d, HTMLInputElement); + +/** + * @implements globalThis.HTMLParamElement + */ +class HTMLParamElement extends HTMLElement { + constructor(ownerDocument, localName = 'param') { + super(ownerDocument, localName); + } +} + +/** + * @implements globalThis.HTMLMediaElement + */ +class HTMLMediaElement extends HTMLElement { + constructor(ownerDocument, localName = 'media') { + super(ownerDocument, localName); + } +} + +/** + * @implements globalThis.HTMLAudioElement + */ +class HTMLAudioElement extends HTMLElement { + constructor(ownerDocument, localName = 'audio') { + super(ownerDocument, localName); + } +} + +const tagName$c = 'h1'; + +/** + * @implements globalThis.HTMLHeadingElement + */ +class HTMLHeadingElement extends HTMLElement { + constructor(ownerDocument, localName = tagName$c) { + super(ownerDocument, localName); + } +} + +registerHTMLClass([tagName$c, 'h2', 'h3', 'h4', 'h5', 'h6'], HTMLHeadingElement); + +/** + * @implements globalThis.HTMLDirectoryElement + */ +class HTMLDirectoryElement extends HTMLElement { + constructor(ownerDocument, localName = 'dir') { + super(ownerDocument, localName); + } +} + +/** + * @implements globalThis.HTMLQuoteElement + */ +class HTMLQuoteElement extends HTMLElement { + constructor(ownerDocument, localName = 'quote') { + super(ownerDocument, localName); + } +} + +class Canvas { + constructor(width, height) { + this.width = width; + this.height = height; + } + getContext() { return null; } + toDataURL() { return ''; } + } + var Canvas$1 = {createCanvas: (width, height) => new Canvas(width, height)}; + +const {createCanvas} = Canvas$1; + +const tagName$b = 'canvas'; + +/** + * @implements globalThis.HTMLCanvasElement + */ +class HTMLCanvasElement extends HTMLElement { + constructor(ownerDocument, localName = tagName$b) { + super(ownerDocument, localName); + this[IMAGE] = createCanvas(300, 150); + } + + get width() { + return this[IMAGE].width; + } + + set width(value) { + numericAttribute.set(this, 'width', value); + this[IMAGE].width = value; + } + + get height() { + return this[IMAGE].height; + } + + set height(value) { + numericAttribute.set(this, 'height', value); + this[IMAGE].height = value; + } + + getContext(type) { + return this[IMAGE].getContext(type); + } + + toDataURL(...args) { + return this[IMAGE].toDataURL(...args); + } +} + +registerHTMLClass(tagName$b, HTMLCanvasElement); + +/** + * @implements globalThis.HTMLLegendElement + */ +class HTMLLegendElement extends HTMLElement { + constructor(ownerDocument, localName = 'legend') { + super(ownerDocument, localName); + } +} + +const tagName$a = 'option'; + +/** + * @implements globalThis.HTMLOptionElement + */ +class HTMLOptionElement extends HTMLElement { + constructor(ownerDocument, localName = tagName$a) { + super(ownerDocument, localName); + } + + /* c8 ignore start */ + get value() { return stringAttribute.get(this, 'value'); } + set value(value) { stringAttribute.set(this, 'value', value); } + /* c8 ignore stop */ + + get selected() { return booleanAttribute.get(this, 'selected'); } + set selected(value) { + const option = this.parentElement?.querySelector('option[selected]'); + if (option && option !== this) + option.selected = false; + booleanAttribute.set(this, 'selected', value); + } +} + +registerHTMLClass(tagName$a, HTMLOptionElement); + +/** + * @implements globalThis.HTMLSpanElement + */ +class HTMLSpanElement extends HTMLElement { + constructor(ownerDocument, localName = 'span') { + super(ownerDocument, localName); + } +} + +/** + * @implements globalThis.HTMLMeterElement + */ +class HTMLMeterElement extends HTMLElement { + constructor(ownerDocument, localName = 'meter') { + super(ownerDocument, localName); + } +} + +/** + * @implements globalThis.HTMLVideoElement + */ +class HTMLVideoElement extends HTMLElement { + constructor(ownerDocument, localName = 'video') { + super(ownerDocument, localName); + } +} + +/** + * @implements globalThis.HTMLTableCellElement + */ +class HTMLTableCellElement extends HTMLElement { + constructor(ownerDocument, localName = 'td') { + super(ownerDocument, localName); + } +} + +const tagName$9 = 'title'; + +/** + * @implements globalThis.HTMLTitleElement + */ +class HTMLTitleElement extends TextElement { + constructor(ownerDocument, localName = tagName$9) { + super(ownerDocument, localName); + } +} + +registerHTMLClass(tagName$9, HTMLTitleElement); + +/** + * @implements globalThis.HTMLOutputElement + */ +class HTMLOutputElement extends HTMLElement { + constructor(ownerDocument, localName = 'output') { + super(ownerDocument, localName); + } +} + +/** + * @implements globalThis.HTMLTableRowElement + */ +class HTMLTableRowElement extends HTMLElement { + constructor(ownerDocument, localName = 'tr') { + super(ownerDocument, localName); + } +} + +/** + * @implements globalThis.HTMLDataElement + */ +class HTMLDataElement extends HTMLElement { + constructor(ownerDocument, localName = 'data') { + super(ownerDocument, localName); + } +} + +/** + * @implements globalThis.HTMLMenuElement + */ +class HTMLMenuElement extends HTMLElement { + constructor(ownerDocument, localName = 'menu') { + super(ownerDocument, localName); + } +} + +const tagName$8 = 'select'; + +/** + * @implements globalThis.HTMLSelectElement + */ +class HTMLSelectElement extends HTMLElement { + constructor(ownerDocument, localName = tagName$8) { + super(ownerDocument, localName); + } + + get options() { + let children = new NodeList; + let {firstElementChild} = this; + while (firstElementChild) { + if (firstElementChild.tagName === 'OPTGROUP') + children.push(...firstElementChild.children); + else + children.push(firstElementChild); + firstElementChild = firstElementChild.nextElementSibling; + } + return children; + } + + /* c8 ignore start */ + get disabled() { return booleanAttribute.get(this, 'disabled'); } + set disabled(value) { booleanAttribute.set(this, 'disabled', value); } + + get name() { return this.getAttribute('name'); } + set name(value) { this.setAttribute('name', value); } + /* c8 ignore stop */ + + get value() { return this.querySelector('option[selected]')?.value; } +} + +registerHTMLClass(tagName$8, HTMLSelectElement); + +/** + * @implements globalThis.HTMLBRElement + */ +class HTMLBRElement extends HTMLElement { + constructor(ownerDocument, localName = 'br') { + super(ownerDocument, localName); + } +} + +const tagName$7 = 'button'; + +/** + * @implements globalThis.HTMLButtonElement + */ +class HTMLButtonElement extends HTMLElement { + constructor(ownerDocument, localName = tagName$7) { + super(ownerDocument, localName); + } + + /* c8 ignore start */ + get disabled() { return booleanAttribute.get(this, 'disabled'); } + set disabled(value) { booleanAttribute.set(this, 'disabled', value); } + + get name() { return this.getAttribute('name'); } + set name(value) { this.setAttribute('name', value); } + + get type() { return this.getAttribute('type'); } + set type(value) { this.setAttribute('type', value); } + /* c8 ignore stop */ +} + +registerHTMLClass(tagName$7, HTMLButtonElement); + +/** + * @implements globalThis.HTMLMapElement + */ +class HTMLMapElement extends HTMLElement { + constructor(ownerDocument, localName = 'map') { + super(ownerDocument, localName); + } +} + +/** + * @implements globalThis.HTMLOptGroupElement + */ +class HTMLOptGroupElement extends HTMLElement { + constructor(ownerDocument, localName = 'optgroup') { + super(ownerDocument, localName); + } +} + +/** + * @implements globalThis.HTMLDListElement + */ +class HTMLDListElement extends HTMLElement { + constructor(ownerDocument, localName = 'dl') { + super(ownerDocument, localName); + } +} + +const tagName$6 = 'textarea'; + +/** + * @implements globalThis.HTMLTextAreaElement + */ +class HTMLTextAreaElement extends TextElement { + constructor(ownerDocument, localName = tagName$6) { + super(ownerDocument, localName); + } + + /* c8 ignore start */ + get disabled() { return booleanAttribute.get(this, 'disabled'); } + set disabled(value) { booleanAttribute.set(this, 'disabled', value); } + + get name() { return this.getAttribute('name'); } + set name(value) { this.setAttribute('name', value); } + + get placeholder() { return this.getAttribute('placeholder'); } + set placeholder(value) { this.setAttribute('placeholder', value); } + + get type() { return this.getAttribute('type'); } + set type(value) { this.setAttribute('type', value); } + + get value() { return this.textContent; } + set value(content) { this.textContent = content; } + /* c8 ignore stop */ +} + +registerHTMLClass(tagName$6, HTMLTextAreaElement); + +/** + * @implements globalThis.HTMLFontElement + */ +class HTMLFontElement extends HTMLElement { + constructor(ownerDocument, localName = 'font') { + super(ownerDocument, localName); + } +} + +/** + * @implements globalThis.HTMLDivElement + */ +class HTMLDivElement extends HTMLElement { + constructor(ownerDocument, localName = 'div') { + super(ownerDocument, localName); + } +} + +const tagName$5 = 'link'; + +/** + * @implements globalThis.HTMLLinkElement + */ +class HTMLLinkElement extends HTMLElement { + constructor(ownerDocument, localName = tagName$5) { + super(ownerDocument, localName); + } + + /* c8 ignore start */ // copy paste from img.src, already covered + get disabled() { return booleanAttribute.get(this, 'disabled'); } + set disabled(value) { booleanAttribute.set(this, 'disabled', value); } + + get href() { return stringAttribute.get(this, 'href').trim(); } + set href(value) { stringAttribute.set(this, 'href', value); } + + get hreflang() { return stringAttribute.get(this, 'hreflang'); } + set hreflang(value) { stringAttribute.set(this, 'hreflang', value); } + + get media() { return stringAttribute.get(this, 'media'); } + set media(value) { stringAttribute.set(this, 'media', value); } + + get rel() { return stringAttribute.get(this, 'rel'); } + set rel(value) { stringAttribute.set(this, 'rel', value); } + + get type() { return stringAttribute.get(this, 'type'); } + set type(value) { stringAttribute.set(this, 'type', value); } + /* c8 ignore stop */ + +} + +registerHTMLClass(tagName$5, HTMLLinkElement); + +const tagName$4 = 'slot'; + +/** + * @implements globalThis.HTMLSlotElement + */ +class HTMLSlotElement extends HTMLElement { + constructor(ownerDocument, localName = tagName$4) { + super(ownerDocument, localName); + } + + /* c8 ignore start */ + get name() { return this.getAttribute('name'); } + set name(value) { this.setAttribute('name', value); } + + assign() {} + + assignedNodes(options) { + const isNamedSlot = !!this.name; + const hostChildNodes = this.getRootNode().host?.childNodes ?? []; + let slottables; + + if (isNamedSlot) { + slottables = [...hostChildNodes].filter(node => node.slot === this.name); + } else { + slottables = [...hostChildNodes].filter(node => !node.slot); + } + + if (options?.flatten) { + const result = []; + + // Element and Text nodes are slottables. A slot can be a slottable. + for (let slottable of slottables) { + if (slottable.localName === 'slot') { + result.push(...slottable.assignedNodes({ flatten: true })); + } else { + result.push(slottable); + } + } + + slottables = result; + } + + // If no assigned nodes are found, it returns the slot's fallback content. + return slottables.length ? slottables : [...this.childNodes]; + } + + assignedElements(options) { + const slottables = this.assignedNodes(options).filter(n => n.nodeType === 1); + + // If no assigned elements are found, it returns the slot's fallback content. + return slottables.length ? slottables : [...this.children]; + } + /* c8 ignore stop */ +} + +registerHTMLClass(tagName$4, HTMLSlotElement); + +/** + * @implements globalThis.HTMLFormElement + */ +class HTMLFormElement extends HTMLElement { + constructor(ownerDocument, localName = 'form') { + super(ownerDocument, localName); + } +} + +const tagName$3 = 'img'; + +/** + * @implements globalThis.HTMLImageElement + */ +class HTMLImageElement extends HTMLElement { + constructor(ownerDocument, localName = tagName$3) { + super(ownerDocument, localName); + } + + /* c8 ignore start */ + get alt() { return stringAttribute.get(this, 'alt'); } + set alt(value) { stringAttribute.set(this, 'alt', value); } + + get sizes() { return stringAttribute.get(this, 'sizes'); } + set sizes(value) { stringAttribute.set(this, 'sizes', value); } + + get src() { return stringAttribute.get(this, 'src'); } + set src(value) { stringAttribute.set(this, 'src', value); } + + get srcset() { return stringAttribute.get(this, 'srcset'); } + set srcset(value) { stringAttribute.set(this, 'srcset', value); } + + get title() { return stringAttribute.get(this, 'title'); } + set title(value) { stringAttribute.set(this, 'title', value); } + + get width() { return numericAttribute.get(this, 'width'); } + set width(value) { numericAttribute.set(this, 'width', value); } + + get height() { return numericAttribute.get(this, 'height'); } + set height(value) { numericAttribute.set(this, 'height', value); } + /* c8 ignore stop */ +} + +registerHTMLClass(tagName$3, HTMLImageElement); + +/** + * @implements globalThis.HTMLPreElement + */ +class HTMLPreElement extends HTMLElement { + constructor(ownerDocument, localName = 'pre') { + super(ownerDocument, localName); + } +} + +/** + * @implements globalThis.HTMLUListElement + */ +class HTMLUListElement extends HTMLElement { + constructor(ownerDocument, localName = 'ul') { + super(ownerDocument, localName); + } +} + +const tagName$2 = 'meta'; +/** + * @implements globalThis.HTMLMetaElement + */ +class HTMLMetaElement extends HTMLElement { + constructor(ownerDocument, localName =tagName$2) { + super(ownerDocument, localName); + } + + /* c8 ignore start */ + get name() { return stringAttribute.get(this, 'name'); } + set name(value) { stringAttribute.set(this, 'name', value); } + + get httpEquiv() { return stringAttribute.get(this, 'http-equiv'); } + set httpEquiv(value) { stringAttribute.set(this, 'http-equiv', value); } + + get content() { return stringAttribute.get(this, 'content'); } + set content(value) { stringAttribute.set(this, 'content', value); } + + get charset() { return stringAttribute.get(this, 'charset'); } + set charset(value) { stringAttribute.set(this, 'charset', value); } + + get media() { return stringAttribute.get(this, 'media'); } + set media(value) { stringAttribute.set(this, 'media', value); } + /* c8 ignore stop */ + +} + +registerHTMLClass(tagName$2, HTMLMetaElement); + +/** + * @implements globalThis.HTMLPictureElement + */ +class HTMLPictureElement extends HTMLElement { + constructor(ownerDocument, localName = 'picture') { + super(ownerDocument, localName); + } +} + +/** + * @implements globalThis.HTMLAreaElement + */ +class HTMLAreaElement extends HTMLElement { + constructor(ownerDocument, localName = 'area') { + super(ownerDocument, localName); + } +} + +/** + * @implements globalThis.HTMLOListElement + */ +class HTMLOListElement extends HTMLElement { + constructor(ownerDocument, localName = 'ol') { + super(ownerDocument, localName); + } +} + +/** + * @implements globalThis.HTMLTableCaptionElement + */ +class HTMLTableCaptionElement extends HTMLElement { + constructor(ownerDocument, localName = 'caption') { + super(ownerDocument, localName); + } +} + +const tagName$1 = 'a'; + +/** + * @implements globalThis.HTMLAnchorElement + */ +class HTMLAnchorElement extends HTMLElement { + constructor(ownerDocument, localName = tagName$1) { + super(ownerDocument, localName); + } + + /* c8 ignore start */ // copy paste from img.src, already covered + get href() { return encodeURI(decodeURI(stringAttribute.get(this, 'href'))).trim(); } + set href(value) { stringAttribute.set(this, 'href', decodeURI(value)); } + + get download() { return encodeURI(decodeURI(stringAttribute.get(this, 'download'))); } + set download(value) { stringAttribute.set(this, 'download', decodeURI(value)); } + + get target() { return stringAttribute.get(this, 'target'); } + set target(value) { stringAttribute.set(this, 'target', value); } + + get type() { return stringAttribute.get(this, 'type'); } + set type(value) { stringAttribute.set(this, 'type', value); } + + get rel() { return stringAttribute.get(this, 'rel'); } + set rel(value) { stringAttribute.set(this, 'rel', value); } + /* c8 ignore stop */ + +} + +registerHTMLClass(tagName$1, HTMLAnchorElement); + +/** + * @implements globalThis.HTMLLabelElement + */ +class HTMLLabelElement extends HTMLElement { + constructor(ownerDocument, localName = 'label') { + super(ownerDocument, localName); + } +} + +/** + * @implements globalThis.HTMLUnknownElement + */ +class HTMLUnknownElement extends HTMLElement { + constructor(ownerDocument, localName = 'unknown') { + super(ownerDocument, localName); + } +} + +/** + * @implements globalThis.HTMLModElement + */ +class HTMLModElement extends HTMLElement { + constructor(ownerDocument, localName = 'mod') { + super(ownerDocument, localName); + } +} + +/** + * @implements globalThis.HTMLDetailsElement + */ +class HTMLDetailsElement extends HTMLElement { + constructor(ownerDocument, localName = 'details') { + super(ownerDocument, localName); + } +} + +const tagName = 'source'; + +/** + * @implements globalThis.HTMLSourceElement + */ +class HTMLSourceElement extends HTMLElement { + constructor(ownerDocument, localName = tagName) { + super(ownerDocument, localName); + } + + /* c8 ignore start */ + get src() { return stringAttribute.get(this, 'src'); } + set src(value) { stringAttribute.set(this, 'src', value); } + + get srcset() { return stringAttribute.get(this, 'srcset'); } + set srcset(value) { stringAttribute.set(this, 'srcset', value); } + + get sizes() { return stringAttribute.get(this, 'sizes'); } + set sizes(value) { stringAttribute.set(this, 'sizes', value); } + + get type() { return stringAttribute.get(this, 'type'); } + set type(value) { stringAttribute.set(this, 'type', value); } + /* c8 ignore stop */ +} + +registerHTMLClass(tagName, HTMLSourceElement); + +/** + * @implements globalThis.HTMLTrackElement + */ +class HTMLTrackElement extends HTMLElement { + constructor(ownerDocument, localName = 'track') { + super(ownerDocument, localName); + } +} + +/** + * @implements globalThis.HTMLMarqueeElement + */ +class HTMLMarqueeElement extends HTMLElement { + constructor(ownerDocument, localName = 'marquee') { + super(ownerDocument, localName); + } +} + +const HTMLClasses = { + HTMLElement, + HTMLTemplateElement, + HTMLHtmlElement, + HTMLScriptElement, + HTMLFrameElement, + HTMLIFrameElement, + HTMLObjectElement, + HTMLHeadElement, + HTMLBodyElement, + HTMLStyleElement, + HTMLTimeElement, + HTMLFieldSetElement, + HTMLEmbedElement, + HTMLHRElement, + HTMLProgressElement, + HTMLParagraphElement, + HTMLTableElement, + HTMLFrameSetElement, + HTMLLIElement, + HTMLBaseElement, + HTMLDataListElement, + HTMLInputElement, + HTMLParamElement, + HTMLMediaElement, + HTMLAudioElement, + HTMLHeadingElement, + HTMLDirectoryElement, + HTMLQuoteElement, + HTMLCanvasElement, + HTMLLegendElement, + HTMLOptionElement, + HTMLSpanElement, + HTMLMeterElement, + HTMLVideoElement, + HTMLTableCellElement, + HTMLTitleElement, + HTMLOutputElement, + HTMLTableRowElement, + HTMLDataElement, + HTMLMenuElement, + HTMLSelectElement, + HTMLBRElement, + HTMLButtonElement, + HTMLMapElement, + HTMLOptGroupElement, + HTMLDListElement, + HTMLTextAreaElement, + HTMLFontElement, + HTMLDivElement, + HTMLLinkElement, + HTMLSlotElement, + HTMLFormElement, + HTMLImageElement, + HTMLPreElement, + HTMLUListElement, + HTMLMetaElement, + HTMLPictureElement, + HTMLAreaElement, + HTMLOListElement, + HTMLTableCaptionElement, + HTMLAnchorElement, + HTMLLabelElement, + HTMLUnknownElement, + HTMLModElement, + HTMLDetailsElement, + HTMLSourceElement, + HTMLTrackElement, + HTMLMarqueeElement +}; + +// TODO: ensure all these are text only +// /^(?:plaintext|script|style|textarea|title|xmp)$/i + +const voidElements = {test: () => true}; +const Mime = { + 'text/html': { + docType: '<!DOCTYPE html>', + ignoreCase: true, + voidElements: /^(?:area|base|br|col|embed|hr|img|input|keygen|link|menuitem|meta|param|source|track|wbr)$/i + }, + 'image/svg+xml': { + docType: '<?xml version="1.0" encoding="utf-8"?>', + ignoreCase: false, + voidElements + }, + 'text/xml': { + docType: '<?xml version="1.0" encoding="utf-8"?>', + ignoreCase: false, + voidElements + }, + 'application/xml': { + docType: '<?xml version="1.0" encoding="utf-8"?>', + ignoreCase: false, + voidElements + }, + 'application/xhtml+xml': { + docType: '<?xml version="1.0" encoding="utf-8"?>', + ignoreCase: false, + voidElements + } +}; + +// https://dom.spec.whatwg.org/#interface-customevent + + +/** + * @implements globalThis.CustomEvent + */ +class CustomEvent extends GlobalEvent { + constructor(type, eventInitDict = {}) { + super(type, eventInitDict); + this.detail = eventInitDict.detail; + } +} + +/* c8 ignore stop */ + +// https://dom.spec.whatwg.org/#interface-customevent + + +/** + * @implements globalThis.InputEvent + */ +class InputEvent extends GlobalEvent { + constructor(type, inputEventInit = {}) { + super(type, inputEventInit); + this.inputType = inputEventInit.inputType; + this.data = inputEventInit.data; + this.dataTransfer = inputEventInit.dataTransfer; + this.isComposing = inputEventInit.isComposing || false; + this.ranges = inputEventInit.ranges; + } +} +/* c8 ignore stop */ + +const ImageClass = ownerDocument => +/** + * @implements globalThis.Image + */ +class Image extends HTMLImageElement { + constructor(width, height) { + super(ownerDocument); + switch (arguments.length) { + case 1: + this.height = width; + this.width = width; + break; + case 2: + this.height = height; + this.width = width; + break; + } + } +}; + +// https://dom.spec.whatwg.org/#concept-live-range + + +const deleteContents = ({[START]: start, [END]: end}, fragment = null) => { + setAdjacent(start[PREV], end[NEXT]); + do { + const after = getEnd(start); + const next = after === end ? after : after[NEXT]; + if (fragment) + fragment.insertBefore(start, fragment[END]); + else + start.remove(); + start = next; + } while (start !== end); +}; + +/** + * @implements globalThis.Range + */ +class Range { + constructor() { + this[START] = null; + this[END] = null; + this.commonAncestorContainer = null; + } + + /* TODO: this is more complicated than it looks + setStart(node, offset) { + this[START] = node.childNodes[offset]; + } + + setEnd(node, offset) { + this[END] = getEnd(node.childNodes[offset]); + } + //*/ + + insertNode(newNode) { + this[END].parentNode.insertBefore(newNode, this[START]); + } + + selectNode(node) { + this[START] = node; + this[END] = getEnd(node); + } + + // TODO: SVG elements should then create contextual fragments + // that return SVG nodes + selectNodeContents(node) { + this.selectNode(node); + this.commonAncestorContainer = node; + } + + surroundContents(parentNode) { + parentNode.replaceChildren(this.extractContents()); + } + + setStartBefore(node) { + this[START] = node; + } + + setStartAfter(node) { + this[START] = node.nextSibling; + } + + setEndBefore(node) { + this[END] = getEnd(node.previousSibling); + } + + setEndAfter(node) { + this[END] = getEnd(node); + } + + cloneContents() { + let {[START]: start, [END]: end} = this; + const fragment = start.ownerDocument.createDocumentFragment(); + while (start !== end) { + fragment.insertBefore(start.cloneNode(true), fragment[END]); + start = getEnd(start); + if (start !== end) + start = start[NEXT]; + } + return fragment; + } + + deleteContents() { + deleteContents(this); + } + + extractContents() { + const fragment = this[START].ownerDocument.createDocumentFragment(); + deleteContents(this, fragment); + return fragment; + } + + createContextualFragment(html) { + const { commonAncestorContainer: doc } = this; + const isSVG = 'ownerSVGElement' in doc; + const document = isSVG ? doc.ownerDocument : doc; + let content = htmlToFragment(document, html); + if (isSVG) { + const childNodes = [...content.childNodes]; + content = document.createDocumentFragment(); + Object.setPrototypeOf(content, SVGElement$1.prototype); + content.ownerSVGElement = document; + for (const child of childNodes) { + Object.setPrototypeOf(child, SVGElement$1.prototype); + child.ownerSVGElement = document; + content.appendChild(child); + } + } + else + this.selectNode(content); + return content; + } + + cloneRange() { + const range = new Range; + range[START] = this[START]; + range[END] = this[END]; + return range; + } +} + +const isOK = ({nodeType}, mask) => { + switch (nodeType) { + case ELEMENT_NODE: + return mask & SHOW_ELEMENT; + case TEXT_NODE: + return mask & SHOW_TEXT; + case COMMENT_NODE: + return mask & SHOW_COMMENT; + case CDATA_SECTION_NODE: + return mask & SHOW_CDATA_SECTION; + } + return 0; +}; + +/** + * @implements globalThis.TreeWalker + */ +class TreeWalker { + constructor(root, whatToShow = SHOW_ALL) { + this.root = root; + this.currentNode = root; + this.whatToShow = whatToShow; + let {[NEXT]: next, [END]: end} = root; + if (root.nodeType === DOCUMENT_NODE) { + const {documentElement} = root; + next = documentElement; + end = documentElement[END]; + } + const nodes = []; + while (next && next !== end) { + if (isOK(next, whatToShow)) + nodes.push(next); + next = next[NEXT]; + } + this[PRIVATE] = {i: 0, nodes}; + } + + nextNode() { + const $ = this[PRIVATE]; + this.currentNode = $.i < $.nodes.length ? $.nodes[$.i++] : null; + return this.currentNode; + } +} + +const query = (method, ownerDocument, selectors) => { + let {[NEXT]: next, [END]: end} = ownerDocument; + return method.call({ownerDocument, [NEXT]: next, [END]: end}, selectors); +}; + +const globalExports = assign( + {}, + Facades, + HTMLClasses, + { + CustomEvent, + Event: GlobalEvent, + EventTarget: DOMEventTarget, + InputEvent, + NamedNodeMap, + NodeList + } +); + +const window = new WeakMap; + +/** + * @implements globalThis.Document + */ +let Document$1 = class Document extends NonElementParentNode { + constructor(type) { + super(null, '#document', DOCUMENT_NODE); + this[CUSTOM_ELEMENTS] = {active: false, registry: null}; + this[MUTATION_OBSERVER] = {active: false, class: null}; + this[MIME] = Mime[type]; + /** @type {DocumentType} */ + this[DOCTYPE] = null; + this[DOM_PARSER] = null; + this[GLOBALS] = null; + this[IMAGE] = null; + this[UPGRADE] = null; + } + + /** + * @type {globalThis.Document['defaultView']} + */ + get defaultView() { + if (!window.has(this)) + window.set(this, new Proxy(globalThis, { + set: (target, name, value) => { + switch (name) { + case 'addEventListener': + case 'removeEventListener': + case 'dispatchEvent': + this[EVENT_TARGET][name] = value; + break; + default: + target[name] = value; + break; + } + return true; + }, + get: (globalThis, name) => { + switch (name) { + case 'addEventListener': + case 'removeEventListener': + case 'dispatchEvent': + if (!this[EVENT_TARGET]) { + const et = this[EVENT_TARGET] = new DOMEventTarget; + et.dispatchEvent = et.dispatchEvent.bind(et); + et.addEventListener = et.addEventListener.bind(et); + et.removeEventListener = et.removeEventListener.bind(et); + } + return this[EVENT_TARGET][name]; + case 'document': + return this; + /* c8 ignore start */ + case 'navigator': + return { + userAgent: 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.150 Safari/537.36' + }; + /* c8 ignore stop */ + case 'window': + return window.get(this); + case 'customElements': + if (!this[CUSTOM_ELEMENTS].registry) + this[CUSTOM_ELEMENTS] = new CustomElementRegistry(this); + return this[CUSTOM_ELEMENTS]; + case 'performance': + return globalThis.performance; + case 'DOMParser': + return this[DOM_PARSER]; + case 'Image': + if (!this[IMAGE]) + this[IMAGE] = ImageClass(this); + return this[IMAGE]; + case 'MutationObserver': + if (!this[MUTATION_OBSERVER].class) + this[MUTATION_OBSERVER] = new MutationObserverClass(this); + return this[MUTATION_OBSERVER].class; + } + return (this[GLOBALS] && this[GLOBALS][name]) || + globalExports[name] || + globalThis[name]; + } + })); + return window.get(this); + } + + get doctype() { + const docType = this[DOCTYPE]; + if (docType) + return docType; + const {firstChild} = this; + if (firstChild && firstChild.nodeType === DOCUMENT_TYPE_NODE) + return (this[DOCTYPE] = firstChild); + return null; + } + + set doctype(value) { + if (/^([a-z:]+)(\s+system|\s+public(\s+"([^"]+)")?)?(\s+"([^"]+)")?/i.test(value)) { + const {$1: name, $4: publicId, $6: systemId} = RegExp; + this[DOCTYPE] = new DocumentType$1(this, name, publicId, systemId); + knownSiblings(this, this[DOCTYPE], this[NEXT]); + } + } + + get documentElement() { + return this.firstElementChild; + } + + get isConnected() { return true; } + + /** + * @protected + */ + _getParent() { + return this[EVENT_TARGET]; + } + + createAttribute(name) { return new Attr$1(this, name); } + createCDATASection(data) { return new CDATASection$1(this, data); } + createComment(textContent) { return new Comment$2(this, textContent); } + createDocumentFragment() { return new DocumentFragment$1(this); } + createDocumentType(name, publicId, systemId) { return new DocumentType$1(this, name, publicId, systemId); } + createElement(localName) { return new Element$1(this, localName); } + createRange() { + const range = new Range; + range.commonAncestorContainer = this; + return range; + } + createTextNode(textContent) { return new Text$1(this, textContent); } + createTreeWalker(root, whatToShow = -1) { return new TreeWalker(root, whatToShow); } + createNodeIterator(root, whatToShow = -1) { return this.createTreeWalker(root, whatToShow); } + + createEvent(name) { + const event = create$1(name === 'Event' ? new GlobalEvent('') : new CustomEvent('')); + event.initEvent = event.initCustomEvent = ( + type, + canBubble = false, + cancelable = false, + detail + ) => { + event.bubbles = !!canBubble; + + defineProperties(event, { + type: {value: type}, + canBubble: {value: canBubble}, + cancelable: {value: cancelable}, + detail: {value: detail} + }); + }; + return event; + } + + cloneNode(deep = false) { + const { + constructor, + [CUSTOM_ELEMENTS]: customElements, + [DOCTYPE]: doctype + } = this; + const document = new constructor(); + document[CUSTOM_ELEMENTS] = customElements; + if (deep) { + const end = document[END]; + const {childNodes} = this; + for (let {length} = childNodes, i = 0; i < length; i++) + document.insertBefore(childNodes[i].cloneNode(true), end); + if (doctype) + document[DOCTYPE] = childNodes[0]; + } + return document; + } + + importNode(externalNode) { + // important: keep the signature length as *one* + // or it would behave like old IE or Edge with polyfills + const deep = 1 < arguments.length && !!arguments[1]; + const node = externalNode.cloneNode(deep); + const {[CUSTOM_ELEMENTS]: customElements} = this; + const {active} = customElements; + const upgrade = element => { + const {ownerDocument, nodeType} = element; + element.ownerDocument = this; + if (active && ownerDocument !== this && nodeType === ELEMENT_NODE) + customElements.upgrade(element); + }; + upgrade(node); + if (deep) { + switch (node.nodeType) { + case ELEMENT_NODE: + case DOCUMENT_FRAGMENT_NODE: { + let {[NEXT]: next, [END]: end} = node; + while (next !== end) { + if (next.nodeType === ELEMENT_NODE) + upgrade(next); + next = next[NEXT]; + } + break; + } + } + } + return node; + } + + toString() { return this.childNodes.join(''); } + + querySelector(selectors) { + return query(super.querySelector, this, selectors); + } + + querySelectorAll(selectors) { + return query(super.querySelectorAll, this, selectors); + } + + /* c8 ignore start */ + getElementsByTagNameNS(_, name) { + return this.getElementsByTagName(name); + } + createAttributeNS(_, name) { + return this.createAttribute(name); + } + createElementNS(nsp, localName, options) { + return nsp === SVG_NAMESPACE ? + new SVGElement$1(this, localName, null) : + this.createElement(localName, options); + } + /* c8 ignore stop */ +}; + +setPrototypeOf( + globalExports.Document = function Document() { + illegalConstructor(); + }, + Document$1 +).prototype = Document$1.prototype; + +const createHTMLElement$1 = (ownerDocument, builtin, localName, options) => { + if (!builtin && htmlClasses.has(localName)) { + const Class = htmlClasses.get(localName); + return new Class(ownerDocument, localName); + } + const {[CUSTOM_ELEMENTS]: {active, registry}} = ownerDocument; + if (active) { + const ce = builtin ? options.is : localName; + if (registry.has(ce)) { + const {Class} = registry.get(ce); + const element = new Class(ownerDocument, localName); + customElements.set(element, {connected: false}); + return element; + } + } + return new HTMLElement(ownerDocument, localName); +}; + +/** + * @implements globalThis.HTMLDocument + */ +class HTMLDocument extends Document$1 { + constructor() { super('text/html'); } + + get all() { + const nodeList = new NodeList; + let {[NEXT]: next, [END]: end} = this; + while (next !== end) { + switch (next.nodeType) { + case ELEMENT_NODE: + nodeList.push(next); + break; + } + next = next[NEXT]; + } + return nodeList; + } + + /** + * @type HTMLHeadElement + */ + get head() { + const {documentElement} = this; + let {firstElementChild} = documentElement; + if (!firstElementChild || firstElementChild.tagName !== 'HEAD') { + firstElementChild = this.createElement('head'); + documentElement.prepend(firstElementChild); + } + return firstElementChild; + } + + /** + * @type HTMLBodyElement + */ + get body() { + const {head} = this; + let {nextElementSibling} = head; + if (!nextElementSibling || nextElementSibling.tagName !== 'BODY') { + nextElementSibling = this.createElement('body'); + head.after(nextElementSibling); + } + return nextElementSibling; + } + + /** + * @type HTMLTitleElement + */ + get title() { + const {head} = this; + return head.getElementsByTagName('title').at(0)?.textContent || ''; + } + + set title(textContent) { + const {head} = this; + let title = head.getElementsByTagName('title').at(0); + if (title) + title.textContent = textContent; + else { + head.insertBefore( + this.createElement('title'), + head.firstChild + ).textContent = textContent; + } + } + + createElement(localName, options) { + const builtin = !!(options && options.is); + const element = createHTMLElement$1(this, builtin, localName, options); + if (builtin) + element.setAttribute('is', options.is); + return element; + } +} + +/** + * @implements globalThis.Document + */ +class SVGDocument extends Document$1 { + constructor() { super('image/svg+xml'); } + toString() { + return this[MIME].docType + super.toString(); + } +} + +/** + * @implements globalThis.XMLDocument + */ +class XMLDocument extends Document$1 { + constructor() { super('text/xml'); } + toString() { + return this[MIME].docType + super.toString(); + } +} + +/** + * @implements globalThis.DOMParser + */ +class DOMParser { + + /** @typedef {{ "text/html": HTMLDocument, "image/svg+xml": SVGDocument, "text/xml": XMLDocument }} MimeToDoc */ + /** + * @template {keyof MimeToDoc} MIME + * @param {string} markupLanguage + * @param {MIME} mimeType + * @returns {MimeToDoc[MIME]} + */ + parseFromString(markupLanguage, mimeType, globals = null) { + let isHTML = false, document; + if (mimeType === 'text/html') { + isHTML = true; + document = new HTMLDocument; + } + else if (mimeType === 'image/svg+xml') + document = new SVGDocument; + else + document = new XMLDocument; + document[DOM_PARSER] = DOMParser; + if (globals) + document[GLOBALS] = globals; + if (isHTML && markupLanguage === '...') + markupLanguage = '<!doctype html><html><head></head><body></body></html>'; + return markupLanguage ? + parseFromString(document, isHTML, markupLanguage) : + document; + } +} + +const {parse} = JSON; + +const append = (parentNode, node, end) => { + node.parentNode = parentNode; + knownSiblings(end[PREV], node, end); +}; + +const createHTMLElement = (ownerDocument, localName) => { + if (htmlClasses.has(localName)) { + const Class = htmlClasses.get(localName); + return new Class(ownerDocument, localName); + } + return new HTMLElement(ownerDocument, localName); +}; + +/** + * @typedef {number|string} jsdonValue - either a node type or its content + */ + +/** + * Given a stringified, or arrayfied DOM element, returns an HTMLDocument + * that represent the content of such string, or array. + * @param {string|jsdonValue[]} value + * @returns {HTMLDocument} + */ +const parseJSON = value => { + const array = typeof value === 'string' ? parse(value) : value; + const {length} = array; + const document = new HTMLDocument; + let parentNode = document, end = parentNode[END], svg = false, i = 0; + while (i < length) { + let nodeType = array[i++]; + switch (nodeType) { + case ELEMENT_NODE: { + const localName = array[i++]; + const isSVG = svg || localName === 'svg' || localName === 'SVG'; + const element = isSVG ? + new SVGElement$1(document, localName, parentNode.ownerSVGElement || null) : + createHTMLElement(document, localName); + knownBoundaries(end[PREV], element, end); + element.parentNode = parentNode; + parentNode = element; + end = parentNode[END]; + svg = isSVG; + break; + } + case ATTRIBUTE_NODE: { + const name = array[i++]; + const value = typeof array[i] === 'string' ? array[i++] : ''; + const attr = new Attr$1(document, name, value); + attr.ownerElement = parentNode; + knownSiblings(end[PREV], attr, end); + break; + } + case TEXT_NODE: + append(parentNode, new Text$1(document, array[i++]), end); + break; + case COMMENT_NODE: + append(parentNode, new Comment$2(document, array[i++]), end); + break; + case CDATA_SECTION_NODE: + append(parentNode, new CDATASection$1(document, array[i++]), end); + break; + case DOCUMENT_TYPE_NODE: { + const args = [document]; + while (typeof array[i] === 'string') + args.push(array[i++]); + if (args.length === 3 && /\.dtd$/i.test(args[2])) + args.splice(2, 0, ''); + append(parentNode, new DocumentType$1(...args), end); + break; + } + case DOCUMENT_FRAGMENT_NODE: + parentNode = document.createDocumentFragment(); + end = parentNode[END]; + /* eslint no-fallthrough:0 */ + case DOCUMENT_NODE: + break; + default: + do { + nodeType -= NODE_END; + if (svg && !parentNode.ownerSVGElement) + svg = false; + parentNode = parentNode.parentNode || parentNode; + } while (nodeType < 0); + end = parentNode[END]; + break; + } + } + switch (i && array[0]) { + case ELEMENT_NODE: + return document.firstElementChild; + case DOCUMENT_FRAGMENT_NODE: + return parentNode; + } + return document; +}; + +/** + * + * @param {Document|Element} node the Document or Element to serialize + * @returns {jsdonValue[]} the linear jsdon serialized array + */ +const toJSON = node => node.toJSON(); + +class NodeFilter { + static get SHOW_ALL() { return SHOW_ALL; } + static get SHOW_ELEMENT() { return SHOW_ELEMENT; } + static get SHOW_COMMENT() { return SHOW_COMMENT; } + static get SHOW_CDATA_SECTION() { return SHOW_CDATA_SECTION; } + static get SHOW_TEXT() { return SHOW_TEXT; } +} + +const parseHTML = (html, globals = null) => (new DOMParser).parseFromString( + html, 'text/html', globals +).defaultView; + +function Document() { + illegalConstructor(); +} + +setPrototypeOf(Document, Document$1).prototype = Document$1.prototype; + +globalThis.__linkedom = {Attr: Attr, CDATASection: CDATASection, CharacterData: CharacterData, Comment: Comment, CustomEvent: CustomEvent, DOMParser: DOMParser, Document: Document, DocumentFragment: DocumentFragment, DocumentType: DocumentType, Element: Element, Event: GlobalEvent, EventTarget: DOMEventTarget, Facades: Facades, HTMLAnchorElement: HTMLAnchorElement, HTMLAreaElement: HTMLAreaElement, HTMLAudioElement: HTMLAudioElement, HTMLBRElement: HTMLBRElement, HTMLBaseElement: HTMLBaseElement, HTMLBodyElement: HTMLBodyElement, HTMLButtonElement: HTMLButtonElement, HTMLCanvasElement: HTMLCanvasElement, HTMLClasses: HTMLClasses, HTMLDListElement: HTMLDListElement, HTMLDataElement: HTMLDataElement, HTMLDataListElement: HTMLDataListElement, HTMLDetailsElement: HTMLDetailsElement, HTMLDirectoryElement: HTMLDirectoryElement, HTMLDivElement: HTMLDivElement, HTMLElement: HTMLElement, HTMLEmbedElement: HTMLEmbedElement, HTMLFieldSetElement: HTMLFieldSetElement, HTMLFontElement: HTMLFontElement, HTMLFormElement: HTMLFormElement, HTMLFrameElement: HTMLFrameElement, HTMLFrameSetElement: HTMLFrameSetElement, HTMLHRElement: HTMLHRElement, HTMLHeadElement: HTMLHeadElement, HTMLHeadingElement: HTMLHeadingElement, HTMLHtmlElement: HTMLHtmlElement, HTMLIFrameElement: HTMLIFrameElement, HTMLImageElement: HTMLImageElement, HTMLInputElement: HTMLInputElement, HTMLLIElement: HTMLLIElement, HTMLLabelElement: HTMLLabelElement, HTMLLegendElement: HTMLLegendElement, HTMLLinkElement: HTMLLinkElement, HTMLMapElement: HTMLMapElement, HTMLMarqueeElement: HTMLMarqueeElement, HTMLMediaElement: HTMLMediaElement, HTMLMenuElement: HTMLMenuElement, HTMLMetaElement: HTMLMetaElement, HTMLMeterElement: HTMLMeterElement, HTMLModElement: HTMLModElement, HTMLOListElement: HTMLOListElement, HTMLObjectElement: HTMLObjectElement, HTMLOptGroupElement: HTMLOptGroupElement, HTMLOptionElement: HTMLOptionElement, HTMLOutputElement: HTMLOutputElement, HTMLParagraphElement: HTMLParagraphElement, HTMLParamElement: HTMLParamElement, HTMLPictureElement: HTMLPictureElement, HTMLPreElement: HTMLPreElement, HTMLProgressElement: HTMLProgressElement, HTMLQuoteElement: HTMLQuoteElement, HTMLScriptElement: HTMLScriptElement, HTMLSelectElement: HTMLSelectElement, HTMLSlotElement: HTMLSlotElement, HTMLSourceElement: HTMLSourceElement, HTMLSpanElement: HTMLSpanElement, HTMLStyleElement: HTMLStyleElement, HTMLTableCaptionElement: HTMLTableCaptionElement, HTMLTableCellElement: HTMLTableCellElement, HTMLTableElement: HTMLTableElement, HTMLTableRowElement: HTMLTableRowElement, HTMLTemplateElement: HTMLTemplateElement, HTMLTextAreaElement: HTMLTextAreaElement, HTMLTimeElement: HTMLTimeElement, HTMLTitleElement: HTMLTitleElement, HTMLTrackElement: HTMLTrackElement, HTMLUListElement: HTMLUListElement, HTMLUnknownElement: HTMLUnknownElement, HTMLVideoElement: HTMLVideoElement, InputEvent: InputEvent, Node: Node, NodeFilter: NodeFilter, NodeList: NodeList, SVGElement: SVGElement, ShadowRoot: ShadowRoot, Text: Text, illegalConstructor: illegalConstructor, parseHTML: parseHTML, parseJSON: parseJSON, toJSON: toJSON}; +})(); \ No newline at end of file diff --git a/.storage/lovelace.3d_printer_cellphone b/.storage/lovelace.3d_printer_cellphone index ddb4d2e1..e7e58744 100644 --- a/.storage/lovelace.3d_printer_cellphone +++ b/.storage/lovelace.3d_printer_cellphone @@ -105,6 +105,20 @@ "secondary": "{% set st = states('sensor.sparkx_i7_print_status') %}\n{% set c = states('sensor.sparkx_i7_filament_cost') %}\n{% if st in ['printing', 'paused'] %}\n {% if is_number(c) %}{{ c | float | round(2) }} $ (live){% else %}cost unavailable{% endif %}\n{% else %}\n {{ states('input_number.sparkx_i7_last_job_cost') | float(0) | round(2) }} $ (last job)\n{% endif %}\n", "type": "custom:mushroom-template-card" }, + { + "type": "custom:mushroom-template-card", + "entity": "sensor.sparkx_i7_expected_material_length", + "name": "Filament Sufficiency", + "icon": "mdi:scale-balance", + "icon_color": "{% set gcm = states('input_number.sparkx_i7_filament_g_cm') | float(0.0298) %}\n{% set lencm = states('sensor.sparkx_i7_expected_material_length') | float(0) %}\n{% set wgt = states('sensor.sparkx_i7_expected_material_weight') | float(0) %}\n{% set job_g = ((wgt if wgt > 0 else lencm * gcm) | round(1)) %}\n{% set used_g = ((states('sensor.sparkx_i7_used_material_length') | float(0)) * gcm) | round(1) %}\n{% set rem_g = ([job_g - used_g, 0] | max) | round(1) %}\n{% set gc = namespace(list=[]) %}\n{% for p in (state_attr('sensor.sparkx_i7_expected_material_length','color') | default('', true) | string).split(';') %}\n{% set q = p | replace('#','') | lower | trim %}\n{% if q not in ['','none','unknown','unavailable'] %}{% set gc.list = gc.list + [q] %}{% endif %}\n{% endfor %}\n{% set single = (gc.list | length) == 1 %}\n{% set aslot = (states('sensor.sparkx_i7_active_filament_slot') | default('', true) | string).split('_')[-1] %}\n{% set aslot = aslot if aslot in ['1','2','3','4'] else '' %}\n{% set s = namespace(short=false, ok=false, matched=0, active=false) %}\n{% for n in ['1','2','3','4'] %}\n{% set sp = namespace(id='', rem=-1) %}\n{% for e in integration_entities('spoolman') if e.endswith('_color_hex') %}\n{% if sp.id == '' and (state_attr(e[:-10],'location') | default('', true) | lower) == ('cfs lite ' ~ n) %}\n{% set sp.id = e[:-10] %}\n{% set sp.rem = states(e[:-10]) | float(-1) %}\n{% endif %}\n{% endfor %}\n{% if sp.id != '' and sp.rem >= 0 %}\n{% set s.matched = s.matched + 1 %}\n{% if single %}\n{% if n == aslot %}\n{% set s.active = true %}\n{% if sp.rem < job_g %}{% set s.short = true %}{% else %}{% set s.ok = true %}{% endif %}\n{% endif %}\n{% else %}\n{% if sp.rem < job_g %}{% set s.short = true %}{% else %}{% set s.ok = true %}{% endif %}\n{% endif %}\n{% endif %}\n{% endfor %}\n{% set v = namespace(state='unknown', label='check unavailable') %}\n{% if job_g <= 0 %}\n{% set v.state = 'none' %}\n{% set v.label = 'no slicer estimate yet' %}\n{% elif s.short %}\n{% set v.state = 'short' %}\n{% set v.label = 'NOT ENOUGH' %}\n{% elif s.ok %}\n{% set v.state = 'ok' %}\n{% set v.label = 'enough' %}\n{% elif single and aslot == '' %}\n{% set v.state = 'unknown' %}\n{% set v.label = 'no active CFS slot' %}\n{% elif single and not s.active %}\n{% set v.state = 'unknown' %}\n{% set v.label = 'active slot has no spool' %}\n{% elif s.matched == 0 %}\n{% set v.state = 'unknown' %}\n{% set v.label = 'no spool loaded' %}\n{% endif %}\n{% if v.state == 'ok' %}green{% elif v.state == 'short' %}red{% elif v.state == 'none' %}grey{% else %}orange{% endif %}", + "primary": "{% set gcm = states('input_number.sparkx_i7_filament_g_cm') | float(0.0298) %}\n{% set lencm = states('sensor.sparkx_i7_expected_material_length') | float(0) %}\n{% set wgt = states('sensor.sparkx_i7_expected_material_weight') | float(0) %}\n{% set job_g = ((wgt if wgt > 0 else lencm * gcm) | round(1)) %}\n{% set used_g = ((states('sensor.sparkx_i7_used_material_length') | float(0)) * gcm) | round(1) %}\n{% set rem_g = ([job_g - used_g, 0] | max) | round(1) %}\n{% set gc = namespace(list=[]) %}\n{% for p in (state_attr('sensor.sparkx_i7_expected_material_length','color') | default('', true) | string).split(';') %}\n{% set q = p | replace('#','') | lower | trim %}\n{% if q not in ['','none','unknown','unavailable'] %}{% set gc.list = gc.list + [q] %}{% endif %}\n{% endfor %}\n{% set single = (gc.list | length) == 1 %}\n{% set aslot = (states('sensor.sparkx_i7_active_filament_slot') | default('', true) | string).split('_')[-1] %}\n{% set aslot = aslot if aslot in ['1','2','3','4'] else '' %}\n{% set s = namespace(short=false, ok=false, matched=0, active=false) %}\n{% for n in ['1','2','3','4'] %}\n{% set sp = namespace(id='', rem=-1) %}\n{% for e in integration_entities('spoolman') if e.endswith('_color_hex') %}\n{% if sp.id == '' and (state_attr(e[:-10],'location') | default('', true) | lower) == ('cfs lite ' ~ n) %}\n{% set sp.id = e[:-10] %}\n{% set sp.rem = states(e[:-10]) | float(-1) %}\n{% endif %}\n{% endfor %}\n{% if sp.id != '' and sp.rem >= 0 %}\n{% set s.matched = s.matched + 1 %}\n{% if single %}\n{% if n == aslot %}\n{% set s.active = true %}\n{% if sp.rem < job_g %}{% set s.short = true %}{% else %}{% set s.ok = true %}{% endif %}\n{% endif %}\n{% else %}\n{% if sp.rem < job_g %}{% set s.short = true %}{% else %}{% set s.ok = true %}{% endif %}\n{% endif %}\n{% endif %}\n{% endfor %}\n{% set v = namespace(state='unknown', label='check unavailable') %}\n{% if job_g <= 0 %}\n{% set v.state = 'none' %}\n{% set v.label = 'no slicer estimate yet' %}\n{% elif s.short %}\n{% set v.state = 'short' %}\n{% set v.label = 'NOT ENOUGH' %}\n{% elif s.ok %}\n{% set v.state = 'ok' %}\n{% set v.label = 'enough' %}\n{% elif single and aslot == '' %}\n{% set v.state = 'unknown' %}\n{% set v.label = 'no active CFS slot' %}\n{% elif single and not s.active %}\n{% set v.state = 'unknown' %}\n{% set v.label = 'active slot has no spool' %}\n{% elif s.matched == 0 %}\n{% set v.state = 'unknown' %}\n{% set v.label = 'no spool loaded' %}\n{% endif %}\n{{ job_g }} g needed · {{ used_g }} g used", + "secondary": "{% set gcm = states('input_number.sparkx_i7_filament_g_cm') | float(0.0298) %}\n{% set lencm = states('sensor.sparkx_i7_expected_material_length') | float(0) %}\n{% set wgt = states('sensor.sparkx_i7_expected_material_weight') | float(0) %}\n{% set job_g = ((wgt if wgt > 0 else lencm * gcm) | round(1)) %}\n{% set used_g = ((states('sensor.sparkx_i7_used_material_length') | float(0)) * gcm) | round(1) %}\n{% set rem_g = ([job_g - used_g, 0] | max) | round(1) %}\n{% set gc = namespace(list=[]) %}\n{% for p in (state_attr('sensor.sparkx_i7_expected_material_length','color') | default('', true) | string).split(';') %}\n{% set q = p | replace('#','') | lower | trim %}\n{% if q not in ['','none','unknown','unavailable'] %}{% set gc.list = gc.list + [q] %}{% endif %}\n{% endfor %}\n{% set single = (gc.list | length) == 1 %}\n{% set aslot = (states('sensor.sparkx_i7_active_filament_slot') | default('', true) | string).split('_')[-1] %}\n{% set aslot = aslot if aslot in ['1','2','3','4'] else '' %}\n{% set s = namespace(short=false, ok=false, matched=0, active=false) %}\n{% for n in ['1','2','3','4'] %}\n{% set sp = namespace(id='', rem=-1) %}\n{% for e in integration_entities('spoolman') if e.endswith('_color_hex') %}\n{% if sp.id == '' and (state_attr(e[:-10],'location') | default('', true) | lower) == ('cfs lite ' ~ n) %}\n{% set sp.id = e[:-10] %}\n{% set sp.rem = states(e[:-10]) | float(-1) %}\n{% endif %}\n{% endfor %}\n{% if sp.id != '' and sp.rem >= 0 %}\n{% set s.matched = s.matched + 1 %}\n{% if single %}\n{% if n == aslot %}\n{% set s.active = true %}\n{% if sp.rem < job_g %}{% set s.short = true %}{% else %}{% set s.ok = true %}{% endif %}\n{% endif %}\n{% else %}\n{% if sp.rem < job_g %}{% set s.short = true %}{% else %}{% set s.ok = true %}{% endif %}\n{% endif %}\n{% endif %}\n{% endfor %}\n{% set v = namespace(state='unknown', label='check unavailable') %}\n{% if job_g <= 0 %}\n{% set v.state = 'none' %}\n{% set v.label = 'no slicer estimate yet' %}\n{% elif s.short %}\n{% set v.state = 'short' %}\n{% set v.label = 'NOT ENOUGH' %}\n{% elif s.ok %}\n{% set v.state = 'ok' %}\n{% set v.label = 'enough' %}\n{% elif single and aslot == '' %}\n{% set v.state = 'unknown' %}\n{% set v.label = 'no active CFS slot' %}\n{% elif single and not s.active %}\n{% set v.state = 'unknown' %}\n{% set v.label = 'active slot has no spool' %}\n{% elif s.matched == 0 %}\n{% set v.state = 'unknown' %}\n{% set v.label = 'no spool loaded' %}\n{% endif %}\n{{ rem_g }} g left to print · {{ v.label }}", + "multiline_secondary": true, + "grid_options": { + "columns": 6, + "rows": "auto" + } + }, { "heading": "Temperature", "heading_style": "title", diff --git a/zigbee2mqtt/state.json b/zigbee2mqtt/state.json index 2bf6cd12..94d40963 100644 --- a/zigbee2mqtt/state.json +++ b/zigbee2mqtt/state.json @@ -15,14 +15,14 @@ "latest_source": "https://raw.githubusercontent.com/Koenkk/zigbee-OTA/master/images/ThirdReality/SmartPlug_Zigbee_PROD_OTA_V101_1.01.01.ota", "latest_release_notes": null }, - "power": 0.4, + "power": 0.5, "linkquality": 149, "current": 0.03, "power_on_behavior": "on" }, "0xffffb40e0607af27": { "state": "ON", - "voltage": 119.7, + "voltage": 119.6, "ac_frequency": 60, "led_brightness": 100, "countdown_to_turn_off": 0, @@ -30,7 +30,7 @@ "power": 1.1, "current": 0.09, "energy": 57.13, - "power_factor": 0.11, + "power_factor": 0.1, "update": { "state": "idle", "installed_version": 268513381, @@ -45,10 +45,10 @@ "state": "ON", "led_brightness": 100, "countdown_to_turn_off": 0, - "voltage": 119.1, + "voltage": 119.7, "countdown_to_turn_on": 0, "energy": 109.7, - "power_factor": 0.99, + "power_factor": 0.2, "ac_frequency": 60, "update": { "state": "idle", @@ -58,8 +58,8 @@ "latest_release_notes": null }, "linkquality": 145, - "power": 198.5, - "current": 1.69, + "power": 0.2, + "current": 0.01, "power_on_behavior": "on" }, "0xb40e060fffe031e3": { @@ -74,11 +74,11 @@ "led_brightness": 100, "countdown_to_turn_off": 0, "countdown_to_turn_on": 0, - "voltage": 118.9, + "voltage": 117.9, "state": "ON", "ac_frequency": 60, - "energy": 222.81, - "power": 97.2, + "energy": 222.82, + "power": 97, "current": 0.87, "power_factor": 0.94, "update": { @@ -95,13 +95,13 @@ "led_brightness": 100, "countdown_to_turn_off": 0, "countdown_to_turn_on": 0, - "voltage": 119.8, - "energy": 104.33, + "voltage": 119.7, + "energy": 104.34, "state": "ON", - "power": 44.2, - "current": 0.49, + "power": 44.7, + "current": 0.47, "ac_frequency": 60, - "power_factor": 0.77, + "power_factor": 0.74, "update": { "state": "idle", "installed_version": 268513381, @@ -114,12 +114,12 @@ }, "0xffffb40e060895b3": { "state": "ON", - "voltage": 120, + "voltage": 119.8, "ac_frequency": 60, "energy": 15.31, "current": 0.01, "power": 0.1, - "power_factor": 0.11, + "power_factor": 0.22, "linkquality": 116, "update": { "state": "idle", @@ -136,14 +136,14 @@ "0xffffb40e0608864e": { "led_brightness": 100, "countdown_to_turn_off": 0, - "voltage": 120.5, + "voltage": 119.6, "energy": 37.98, "countdown_to_turn_on": 0, "state": "ON", "current": 0.04, "ac_frequency": 60, "power": 0.4, - "power_factor": 0.14, + "power_factor": 0.2, "update": { "state": "idle", "installed_version": 268513381, @@ -171,7 +171,7 @@ }, "0xa4c1380d0679ffff": { "battery": 100, - "temperature": 11.3, + "temperature": 11.4, "temperature_units": "celsius", "temperature_calibration": 0, "update": {