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 = \"
\"\
+ \ %}\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 tags.
+ if (this.decodeEntities && c === CharCodes.Amp) {
+ this.startEntity();
+ }
+ }
+ else if (this.fastForwardTo(CharCodes.Lt)) {
+ // Outside of tags, we can fast-forward.
+ this.sequenceIndex = 1;
+ }
+ }
+ else {
+ // If we see a `<`, set the sequence index to 1; useful for eg. `<`.
+ 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({
+ //
+ onprocessinginstruction(name, data) {
+ if (name.toLowerCase() === '!doctype')
+ document.doctype = data.slice(name.length).trim();
+ },
+
+ //
+ 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; },
+
+ //
+ 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