stimeo-ui 0.18.0 → 0.18.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +31 -2
- package/dist/cable/index.d.ts +2 -1
- package/dist/controllers/command_palette_controller.js +1 -0
- package/dist/controllers/command_palette_controller.js.map +1 -1
- package/dist/controllers/dismissible_controller.js +2 -0
- package/dist/controllers/dismissible_controller.js.map +1 -1
- package/dist/controllers/editable_controller.js +1 -0
- package/dist/controllers/editable_controller.js.map +1 -1
- package/dist/controllers/form_field_controller.d.ts +2 -1
- package/dist/controllers/input_mask_controller.d.ts +2 -1
- package/dist/controllers/input_mask_controller.js +23 -0
- package/dist/controllers/input_mask_controller.js.map +1 -1
- package/dist/controllers/multi_select_controller.d.ts +2 -1
- package/dist/controllers/multi_select_controller.js +21 -1
- package/dist/controllers/multi_select_controller.js.map +1 -1
- package/dist/controllers/pointer_drag_controller.d.ts +1 -0
- package/dist/controllers/pointer_drag_controller.js +6 -1
- package/dist/controllers/pointer_drag_controller.js.map +1 -1
- package/dist/controllers/popover_controller.d.ts +6 -2
- package/dist/controllers/popover_controller.js +9 -0
- package/dist/controllers/popover_controller.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +63 -2
- package/dist/index.js.map +1 -1
- package/dist/inspector/cli.d.ts +10 -1
- package/dist/inspector/cli.js +5 -0
- package/dist/inspector/cli.js.map +1 -1
- package/dist/inspector/cli_bin.d.ts +1 -1
- package/dist/inspector/cli_bin.js +9 -0
- package/dist/inspector/cli_bin.js.map +1 -1
- package/dist/inspector/manifest.json +2 -2
- package/dist/positioning/index.d.ts +2 -1
- package/package.json +5 -5
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utils/composition_tracker.ts","../../src/utils/declared_value.ts","../../src/utils/detach_gate.ts","../../src/utils/focus_candidate.ts","../../src/utils/shared_registry.ts","../../src/utils/form_data_projection.ts","../../src/utils/half_width.ts","../../src/utils/listener_set.ts","../../src/utils/microtask_coalescer.ts","../../src/utils/morph_render_watcher.ts","../../src/utils/safe_timeout.ts","../../src/controllers/input_mask_controller.ts"],"names":["document"],"mappings":"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","file":"input_mask_controller.js","sourcesContent":["/** Minimal event shape carrying the platform's per-event composition signal. */\nexport interface CompositionSignal {\n readonly isComposing?: boolean;\n}\n\n/** Minimal `input` shape needed to tell a composition echo from a fresh edit. */\nexport interface ConfirmedInputSignal {\n readonly target: EventTarget | null;\n readonly inputType?: string;\n}\n\n/**\n * The `inputType` values an engine reports on an `input` that belongs to a\n * composition rather than to an edit the user just made.\n */\nconst COMPOSITION_INPUT_TYPES = new Set([\n \"insertCompositionText\",\n \"insertFromComposition\",\n \"deleteCompositionText\",\n \"deleteByComposition\",\n]);\n\n/** Whether a platform input kind belongs to composition rather than ordinary editing. */\nexport function isCompositionInputType(inputType: string | undefined): boolean {\n return inputType !== undefined && COMPOSITION_INPUT_TYPES.has(inputType);\n}\n\n/** The cancelable notice Turbo dispatches before a morph writes one attribute. */\nconst BEFORE_MORPH_ATTRIBUTE = \"turbo:before-morph-attribute\";\n\n/** Hooks that keep component-specific work outside {@link CompositionTracker}. */\nexport interface CompositionTrackerOptions {\n /** Runs after lifecycle state is set for a `compositionstart` event. */\n readonly onStart?: (event: Event) => void;\n /** Runs after lifecycle state is cleared for a `compositionend` event. */\n readonly onEnd?: (event: Event) => void;\n /**\n * Runs once lifecycle state is cleared for a composition that ended without\n * `compositionend`, with the `element` it ran in. Nothing was confirmed: the\n * element holds what a write left (`inPlace` false), or still holds the text the\n * composition left, which the engine stopped composing (`inPlace` true) — text\n * typed against whatever the page declared while it ran.\n *\n * A hook applies the work the composition held back — and only that — at once,\n * keeping the caret where it was. The event that showed the end is then handled as\n * it would be with no composition: the text the element holds is read by whatever\n * reads it next, as the user's edit if an edit reads it. A consumer whose contract\n * judges what an interrupted conversion leaves may also settle that text here, as\n * `stimeo--otp` returns a residual its `pattern` rejects to the prior character: it reports\n * that as a reconciliation, never as the user's edit, and keeps the selection.\n *\n * When the tracker sees the end in an event it hears — a key, an edit, a focus\n * change, the next `compositionstart` — the hook runs there, in the capture phase,\n * ahead of that event's own handlers and of the edit it announces, whoever\n * dispatched it. When a question ({@link CompositionTracker.isComposing},\n * {@link CompositionTracker.composingElement}) or a Turbo morph's notice for one\n * attribute shows the end, the hook runs in a microtask after them, never inside\n * its caller or a half-applied morph, unless an event the tracker hears comes\n * first. On `compositionstart` it runs after the new composition is set and before\n * `onStart`: {@link CompositionTracker.isComposing} is true again, so work held for\n * the old composition stays held for the new.\n */\n readonly onAbandon?: (element: EventTarget, inPlace: boolean) => void;\n}\n\n/** One composition in progress. */\ninterface Composition {\n /** The observed target the composition was heard on. */\n readonly target: EventTarget;\n /** The element composing: the observed field itself, or a field inside an observed ancestor. */\n readonly element: EventTarget;\n /** The value the composition last left in the element; `undefined` for an element with none. */\n text: unknown;\n}\n\n/**\n * The element's live value; `undefined` for an element without one, which a write never\n * changes.\n */\nconst textOf = (element: EventTarget): unknown => (element as { value?: unknown }).value;\n\n/** Whether `element` is the focused element of the document or shadow root it is in. */\nconst holdsFocus = (element: EventTarget): boolean =>\n element instanceof Node &&\n (element.getRootNode() as Partial<DocumentOrShadowRoot>).activeElement === element;\n\n/**\n * Owns IME composition listeners and transient state for one or more event targets.\n *\n * Some browsers omit `KeyboardEvent.isComposing` on the keydown that confirms a\n * conversion. Tracking `compositionstart` through `compositionend` supplies the\n * missing lifecycle signal without relying on deprecated numeric key codes.\n * Component policy stays with the consumer: filtering, validation, and submission\n * belong in the optional {@link CompositionTrackerOptions.onEnd} callback.\n *\n * The tracker also owns the other half of that lifecycle: **the `input` some\n * engines send after `compositionend` to echo the text just confirmed.** A\n * consumer that ran `onEnd` and then handled the echo would commit the same\n * confirmation twice, so {@link CompositionTracker.consumesConfirmedInput} tells\n * the echo apart and the consumer returns early on it.\n *\n * **What the echo is not, is the harder half.** Not every engine sends one —\n * Chromium ends a confirmation at `compositionend` with no `input` after it — so\n * a window that waits for the echo and only closes on the next key would swallow\n * the first edit that arrives without one: dictation, autofill, a drop,\n * `insertText`. Two things keep that from happening. The window closes on the\n * next anything — a key, a new composition, any `input`, losing the field — and\n * an `input` that reports an `inputType` outside {@link COMPOSITION_INPUT_TYPES}\n * is an edit, so it is never folded. Only an engine that reports no `inputType`\n * at all is left relying on the window alone.\n *\n * **A composition can also end without `compositionend`.** Chromium drops the\n * conversion silently when something other than the IME writes a different value\n * into its field — a Turbo morph, a form reset, a script — and when the field\n * moves or leaves the page, leaving the text it composed. The tracker reads the end\n * from what the engine and the field still say:\n *\n * - **The field holds other text than the composition left.** The IME's edits, and\n * the user's own between them, arrive as an `input` the engine dispatched\n * (`isTrusted`) or one that reports the composition, and the tracker takes the\n * field's text from those. A write by anything else changes the text with no such\n * `input` — at most a synthetic one — so the composition is over, ended with\n * {@link CompositionTrackerOptions.onAbandon}, before anyone acts on the field.\n * Writing the same text keeps the conversion, and the match.\n * - **The engine edits outside a composition.** A `beforeinput` reporting\n * `isComposing: false` with an `inputType` outside {@link COMPOSITION_INPUT_TYPES}\n * is an edit the engine no longer counts as composition — unless the `keydown`\n * right before it reported `isComposing: true`: a key the IME lets through, such\n * as Backspace, edits the field while the conversion goes on. This is what shows\n * a composition that ended with its text in place. Only one field composes at a\n * time, so such an edit on any observed target ends the composition held, with\n * `onAbandon`. A `keydown` can hold this back but never starts it: the keydown\n * that confirms a conversion may carry no `isComposing`, and dictation and\n * autofill edit with no key at all.\n * - **The field stays where it was heard.** A field the consumer observes itself is\n * the consumer's to release when it moves or leaves, which reads what it holds;\n * one that keeps focus through the move keeps composing\n * ({@link CompositionTracker.disconnect}). A field inside an observed ancestor\n * that leaves the ancestor, or loses focus within its document, ends its\n * composition quietly; one that stays the document's active element — the window\n * lost focus — keeps composing.\n * - **No other field composes**: a `compositionstart` ends any composition the\n * tracker still holds, with `onAbandon`, before the new one starts.\n *\n * The tracker checks this before it answers {@link CompositionTracker.isComposing}\n * or {@link CompositionTracker.composingElement}, and at every key, edit, blur,\n * `compositionstart` and morph it hears — keys, edits and blurs in the capture\n * phase, ahead of the consumer's own handlers. While\n * a composition is live, a Turbo morph leaves its field's value alone: Idiomorph\n * asks through `turbo:before-morph-attribute` before it writes `value`, as an\n * attribute or as the live text, and the tracker cancels that one notice for the\n * element composing, so the morph keeps the conversion, as it keeps focus. Every\n * other attribute, and every other element, still morphs.\n *\n * A consumer that observes several fields tells the conversion's own events from\n * edits in the other fields with {@link CompositionTracker.owns}, which holds even\n * while the tracker keeps a composition whose end no event has shown yet.\n *\n * @example\n * ```ts\n * #composition = new CompositionTracker({ onEnd: () => this.filter() });\n *\n * connect(): void {\n * this.#composition.observe(this.inputTarget);\n * }\n *\n * disconnect(): void {\n * this.#composition.disconnect();\n * }\n *\n * onInput(event: InputEvent): void {\n * if (this.#composition.consumesConfirmedInput(event)) return;\n * if (this.#composition.isComposing(event)) return;\n * }\n *\n * onKeydown(event: KeyboardEvent): void {\n * if (this.#composition.isComposing(event)) return;\n * }\n * ```\n */\nexport class CompositionTracker {\n readonly #observedTargets = new Set<EventTarget>();\n /** The composition in progress, if any: only one field composes at a time. */\n #composition: Composition | null = null;\n readonly #onStart: ((event: Event) => void) | undefined;\n readonly #onEnd: ((event: Event) => void) | undefined;\n readonly #onAbandon: ((element: EventTarget, inPlace: boolean) => void) | undefined;\n /** The field whose confirming `input` is still owed, while the window is open. */\n #confirmedTarget: EventTarget | null = null;\n /**\n * Whether the last `keydown` reported a composition, until the `beforeinput`\n * after it: the edit that key announces lands inside the composition.\n */\n #keyComposing = false;\n /** Whether the consumer is connected: from `observe()` until `disconnect()`. */\n #connected = false;\n /** A composition that ended without `compositionend` and whose `onAbandon` has not run yet. */\n #abandoned: { readonly element: EventTarget; readonly inPlace: boolean } | null = null;\n\n constructor(options: CompositionTrackerOptions = {}) {\n this.#onStart = options.onStart;\n this.#onEnd = options.onEnd;\n this.#onAbandon = options.onAbandon;\n }\n\n /**\n * Starts lifecycle tracking for `target`; repeated calls are idempotent. The\n * targets one tracker observes do not contain one another: a composition event\n * reaching two of them would be counted as two.\n */\n observe(target: EventTarget): void {\n this.#connected = true;\n if (this.#observedTargets.has(target)) return;\n target.addEventListener(\"compositionstart\", this.#handleStart);\n target.addEventListener(\"compositionend\", this.#handleEnd);\n target.addEventListener(\"keydown\", this.#handleKeydown, true);\n target.addEventListener(\"beforeinput\", this.#handleBeforeInput, true);\n target.addEventListener(\"input\", this.#handleInput, true);\n target.addEventListener(\"blur\", this.#handleBlur, true);\n target.addEventListener(BEFORE_MORPH_ATTRIBUTE, this.#handleBeforeMorphAttribute);\n this.#observedTargets.add(target);\n }\n\n /**\n * Stops tracking one target. A composition it held ends quietly, unless its element\n * still holds focus — see {@link CompositionTracker.disconnect}.\n */\n unobserve(target: EventTarget): void {\n if (!this.#observedTargets.delete(target)) return;\n this.#release(target);\n if (this.#composition?.target === target) this.#letGo();\n if (this.#confirmedTarget === target) this.#confirmedTarget = null;\n }\n\n /**\n * Releases every listener and clears state so reconnect starts cleanly.\n *\n * A composition whose element still holds focus is the exception. A move that\n * keeps focus — `moveBefore`, which Turbo's morph uses where the browser provides\n * it — can leave the conversion running, and Stimulus releases the moved target, or\n * the controller around it, and observes it again within the one mutation batch. So\n * the composition waits for its target to be observed again before the turn ends,\n * and goes on; otherwise it ends then — quietly once disconnected, and with\n * {@link CompositionTrackerOptions.onAbandon} for a consumer that only let that one\n * target go. A move without `moveBefore`, or a removal, takes focus away.\n */\n disconnect(): void {\n this.#connected = false;\n for (const target of this.#observedTargets) this.#release(target);\n this.#observedTargets.clear();\n this.#letGo();\n this.#confirmedTarget = null;\n this.#keyComposing = false;\n this.#abandoned = null;\n }\n\n /**\n * True when a composition is live or the current event reports one. A\n * composition that ended without `compositionend` ends here first.\n */\n isComposing(event?: CompositionSignal): boolean {\n this.#settle();\n return event?.isComposing === true || this.#composition !== null;\n }\n\n /**\n * The element a live composition is running in, or `null`. A composition that\n * ended without `compositionend` ends here first.\n */\n composingElement(): EventTarget | null {\n this.#settle();\n return this.#composition?.element ?? null;\n }\n\n /**\n * Whether `event` belongs to a composition: it reports one, or it is in the element a\n * live composition runs in. An event in another element — a key there, or a script's\n * write reported with a bare `input` — is an edit outside the composition, even while\n * the tracker holds one whose end it has not seen: only one element composes at a\n * time, and the IME edits nothing else. The composition held stays, since such an\n * event does not show its end. A consumer that observes several fields asks this of\n * each event; {@link CompositionTracker.isComposing} asks of the targets as a whole.\n * A composition that ended without `compositionend` ends here first.\n */\n owns(event: CompositionSignal & { readonly target: EventTarget | null }): boolean {\n const element = this.composingElement();\n return event.isComposing === true || (element !== null && element === event.target);\n }\n\n /**\n * Whether `event` is the `input` echoing the composition just confirmed.\n *\n * Asking closes the window either way, so one confirmation is folded at most\n * once and a consumer asks once per `input`.\n */\n consumesConfirmedInput(event: ConfirmedInputSignal): boolean {\n const confirmed = this.#confirmedTarget;\n this.#confirmedTarget = null;\n if (confirmed === null || confirmed !== event.target) return false;\n // An engine that reports no kind at all leaves `inputType` empty, and the\n // window is then the only signal there is.\n const inputType = event.inputType;\n return !inputType || isCompositionInputType(inputType);\n }\n\n /** Removes every listener {@link CompositionTracker.observe} added to `target`. */\n #release(target: EventTarget): void {\n target.removeEventListener(\"compositionstart\", this.#handleStart);\n target.removeEventListener(\"compositionend\", this.#handleEnd);\n target.removeEventListener(\"keydown\", this.#handleKeydown, true);\n target.removeEventListener(\"beforeinput\", this.#handleBeforeInput, true);\n target.removeEventListener(\"input\", this.#handleInput, true);\n target.removeEventListener(\"blur\", this.#handleBlur, true);\n target.removeEventListener(BEFORE_MORPH_ATTRIBUTE, this.#handleBeforeMorphAttribute);\n }\n\n /**\n * Lets go of the composition held once its target is no longer observed: it is over\n * unless its element kept focus and its target is observed again this turn.\n */\n #letGo(): void {\n const composition = this.#composition;\n if (composition === null) return;\n if (!holdsFocus(composition.element)) {\n this.#composition = null;\n return;\n }\n queueMicrotask(() => {\n if (this.#composition !== composition || this.#observedTargets.has(composition.target)) {\n return;\n }\n this.#composition = null;\n if (!this.#connected) return;\n this.#abandoned = { element: composition.element, inPlace: true };\n this.#runAbandon();\n });\n }\n\n /**\n * Ends the composition held if its field left the observed target, taking the\n * composition's text along, or holds text a write left.\n */\n #settle(): void {\n const composition = this.#composition;\n if (composition === null) return;\n const { target, element, text } = composition;\n if (target instanceof Node && element instanceof Node && !target.contains(element)) {\n this.#composition = null;\n } else if (textOf(element) !== text) {\n this.#abandon(false);\n }\n }\n\n /**\n * Ends the composition held, unconfirmed. Its `onAbandon` runs in a microtask,\n * unless an event the tracker hears runs it first.\n */\n #abandon(inPlace: boolean): void {\n this.#owe(inPlace);\n this.#composition = null;\n queueMicrotask(this.#runAbandon);\n }\n\n /** Records the `onAbandon` the composition held owes. */\n #owe(inPlace: boolean): void {\n const composition = this.#composition;\n if (composition !== null) this.#abandoned = { element: composition.element, inPlace };\n }\n\n /** Runs the `onAbandon` an ended composition owes, once. */\n readonly #runAbandon = (): void => {\n const abandoned = this.#abandoned;\n if (abandoned === null) return;\n this.#abandoned = null;\n this.#onAbandon?.(abandoned.element, abandoned.inPlace);\n };\n\n /** A composition starting while one is held ends the held one, unconfirmed. */\n readonly #handleStart = (event: Event): void => {\n this.#confirmedTarget = null;\n this.#settle();\n this.#owe(true);\n // A listener runs while the event is dispatched, so both targets are set.\n const target = event.currentTarget as EventTarget;\n const element = event.target as EventTarget;\n this.#composition = { target, element, text: textOf(element) };\n this.#runAbandon();\n this.#onStart?.(event);\n };\n\n readonly #handleEnd = (event: Event): void => {\n this.#runAbandon();\n if (this.#composition?.target === event.currentTarget) this.#composition = null;\n this.#confirmedTarget = event.target;\n this.#onEnd?.(event);\n };\n\n /**\n * A key on an observed field opens an edit of its own, so no echo is owed; it\n * also says whether the edit it announces lands inside a composition.\n */\n readonly #handleKeydown = (event: Event): void => {\n this.#settle();\n this.#runAbandon();\n this.#confirmedTarget = null;\n this.#keyComposing = (event as KeyboardEvent).isComposing === true;\n };\n\n /** An edit is about to land: a composition the engine no longer runs ends first. */\n readonly #handleBeforeInput = (event: Event): void => {\n const keyComposing = this.#keyComposing;\n this.#keyComposing = false;\n this.#settle();\n const { isComposing, inputType } = event as InputEvent;\n if (\n this.#composition !== null &&\n !keyComposing &&\n !isComposing &&\n inputType &&\n !isCompositionInputType(inputType)\n ) {\n this.#abandon(true);\n }\n this.#runAbandon();\n };\n\n /**\n * Takes the composing field's text from an `input` the engine dispatched or one\n * that reports the composition; any other `input` follows a write the\n * composition did not make.\n */\n readonly #handleInput = (event: Event): void => {\n const composition = this.#composition;\n const { isTrusted, isComposing, inputType } = event as InputEvent;\n if (\n composition !== null &&\n event.target === composition.element &&\n (isTrusted || isComposing || isCompositionInputType(inputType))\n ) {\n composition.text = textOf(composition.element);\n } else {\n this.#settle();\n }\n this.#runAbandon();\n };\n\n /**\n * Checks the composition as a field loses focus, ahead of the consumer's own blur\n * handlers. A field inside an observed ancestor that loses focus within its\n * document keeps its text and composes no more.\n */\n readonly #handleBlur = (event: Event): void => {\n this.#settle();\n this.#runAbandon();\n const composition = this.#composition;\n if (composition === null || composition.target === composition.element) return;\n const { element } = composition;\n if (!(element instanceof Node) || event.target !== element) return;\n const root = element.getRootNode() as Document | ShadowRoot;\n if (root.activeElement !== element) this.#composition = null;\n };\n\n /** Keeps a Turbo morph from writing the value of the element composing. */\n readonly #handleBeforeMorphAttribute = (event: Event): void => {\n this.#settle();\n const composition = this.#composition;\n if (composition?.target !== event.currentTarget || event.target !== composition.element) return;\n const detail: { readonly attributeName?: unknown } | null = (event as CustomEvent).detail;\n if (detail?.attributeName === \"value\") event.preventDefault();\n };\n}\n","/**\n * Readers for the String Values whose text can only be checked by handing it to\n * a parser: a CSS selector, a regular-expression source, a JSON object.\n *\n * Stimulus reads each of them as an ordinary string, so the controller connects\n * and the flaw surfaces later, inside the first handler that consumes the value\n * (a `SyntaxError` from `new RegExp`, a `DOMException` from the selector engine),\n * where it takes the whole handler down on every event. Parsing here, once, in\n * the `<name>ValueChanged` callback keeps the failure local to the declaration:\n * an unreadable value falls back to its default, the element stays alive, and\n * the hot path only ever receives a value that parsed.\n *\n * Which default a broken declaration falls back to is the caller's contract, so\n * every reader takes (or returns) the fallback rather than choosing one.\n */\n\n/**\n * The result of `parse(raw)`, or `fallback` when `parse` throws.\n *\n * Any exception counts as \"does not parse\": the parsers these Values feed\n * (`RegExp`, `JSON.parse`, the selector engine) all report a malformed input by\n * throwing, and none of them throws for another reason.\n */\nexport function parseDeclared<T>(raw: string, parse: (raw: string) => T, fallback: T): T {\n try {\n return parse(raw);\n } catch {\n return fallback;\n }\n}\n\n/** What {@link validSelector} needs of the element it probes with. */\nexport interface SelectorProbe {\n matches(selector: string): boolean;\n}\n\n/**\n * `raw` when it is a non-empty selector the DOM accepts, otherwise `fallback`.\n *\n * `element.matches` is the probe: the engine parses the selector and throws for\n * one it cannot read. Whether the selector actually matches `element` plays no\n * part, so a selector aimed at another element still passes.\n *\n * The parameter names the one method the probe uses rather than `Element`, so\n * this module carries no DOM type and the readers below stay importable from\n * the Node-side Inspector, which checks the same declarations statically.\n */\nexport function validSelector(element: SelectorProbe, raw: string, fallback: string): string {\n if (raw.length === 0) return fallback;\n return parseDeclared(\n raw,\n (selector) => {\n element.matches(selector);\n return selector;\n },\n fallback,\n );\n}\n\n/** How `compileRegExp` wraps a source before compiling it. */\nexport type RegExpAnchor = \"none\" | \"exact\";\n\n/**\n * `source` compiled as a `RegExp`, or `null` when it does not compile.\n *\n * `\"exact\"` wraps the source as `^(?:source)$`, so the whole input has to match\n * and an alternation inside the source cannot escape the anchors. The source is\n * compiled on its own first, because an unbalanced source can be made to parse\n * by the wrapper's own parentheses — `0)|(1` becomes `^(?:0)|(1)$` — which would\n * accept a declaration that is not a regular expression and leave half of it\n * outside the anchors. The default that replaces a broken source is the\n * caller's, so `null` is returned rather than a fallback pattern.\n */\nexport function compileRegExp(source: string, anchor: RegExpAnchor = \"none\"): RegExp | null {\n return parseDeclared(\n source,\n (text) => {\n const bare = new RegExp(text);\n return anchor === \"exact\" ? new RegExp(`^(?:${text})$`) : bare;\n },\n null,\n );\n}\n\n/**\n * The JSON object `raw` declares, or `null` when the text does not parse or\n * parses to something other than a plain object (`null`, an array, a scalar).\n * Values are returned as parsed; narrowing them is the caller's contract.\n */\nexport function parseJsonObject(raw: string): Record<string, unknown> | null {\n const parsed = parseDeclared<unknown>(raw, (text) => JSON.parse(text), null);\n if (parsed === null || typeof parsed !== \"object\" || Array.isArray(parsed)) return null;\n return parsed as Record<string, unknown>;\n}\n","/**\n * Shared \"in-page move vs real detach\" discriminator for `disconnect()`.\n *\n * A mid-session `disconnect()` whose element is still in the document is\n * ambiguous. It can be:\n *\n * - an **in-page move** — a consumer (sortable, a teleport) re-inserted the\n * element; Stimulus reconnects the SAME instance in the same mutation batch\n * and the controller's runtime state must survive;\n * - a **detach that keeps the element** — `data-controller` no longer lists the\n * identifier (a Turbo 8 morph), or the element moved outside the observed\n * root (a scoped `Application.start(root)`, a shadow root); no reconnect will\n * ever come and the controller must tear down or it leaks its sessions,\n * document listeners, and moved nodes.\n *\n * Neither available signal decides this alone. A synchronous `data-controller`\n * token check catches the morph case but misses an observed-root exit (the\n * token stays, the reconnect never comes). Deferring teardown one microtask and\n * letting `connect()` cancel it catches the root exit, but leans on Stimulus\n * reconnecting moved elements within the same mutation batch — internal\n * behavior, not a public contract. {@link DetachGate} composes both: the token\n * check is the synchronous fast path (no probe, no reliance on batching), the\n * microtask probe is the fallback for the genuinely ambiguous remainder.\n *\n * An element out of the document is a real detach, with one exception: a\n * `data-turbo-permanent` element, or one inside it, that Turbo carries to the\n * next page. Turbo takes it out of the old `<body>`, which Stimulus answers with\n * a `disconnect()` while it is out of the document, and puts it into the new one\n * a few microtasks later in the same task, where the same instance connects\n * again. Such a disconnect is probed until the next task instead.\n *\n * Policy stays with the consumer; this util owns the discrimination only:\n *\n * - **Probe** ({@link disconnected}) when the teardown must eventually happen —\n * live drag sessions, document listeners, teleported *content*. Call\n * {@link cancel} from `connect()` (a reconnect means in-page move) and from\n * the head of any direct teardown path (see below).\n * - **Keep** (bare {@link DetachGate.isDetached}) when undoing the ambiguous\n * case would fight the controller's own effect: a controller that moves its\n * OWN element (portal's no-`content` form) exits a scoped observed root as\n * its normal job — a probe-driven teardown would restore the element into\n * the root, reconnect, re-teleport, and disconnect again, forever. Such\n * controllers keep their state on an ambiguous disconnect and tear down only\n * on {@link DetachGate.isDetached}.\n *\n * Event convention for probe consumers: teardown into a dead (detached) tree is\n * silent — consumers restore from their own `connect()`; a detach that keeps the\n * tree alive must end an in-flight session with a `cancel` event, or composing\n * consumers strand their session bookkeeping.\n */\n\n/** The slice of a Stimulus controller the gate inspects. */\nexport interface DetachGateHost {\n readonly element: Element;\n readonly identifier: string;\n}\n\n/**\n * Per-controller-instance gate deciding whether a `disconnect()` is a real\n * detach. Hold one per controller (`#gate = new DetachGate()`) — Stimulus\n * reuses the instance across reconnects, so the pending probe state carries\n * over exactly as the protocol needs.\n *\n * @example\n * ```ts\n * #gate = new DetachGate();\n *\n * connect(): void {\n * this.#gate.cancel(); // a reconnect: the element moved in-page\n * // …\n * }\n *\n * disconnect(): void {\n * this.#gate.disconnected(this, () => this.#teardown());\n * }\n *\n * dismiss(): void {\n * this.#gate.cancel(); // a direct path: disarm a probe a disconnect queued\n * this.#teardown();\n * }\n *\n * #teardown(): void {\n * // … (`disconnected` has already disarmed the probe on its own path)\n * }\n * ```\n */\nexport class DetachGate {\n /** Set while a probe is queued, waiting for a reconnect to cancel it. */\n #pending = false;\n\n /**\n * True while a probe is queued — the last disconnect was ambiguous and no\n * reconnect has cancelled it yet. Read it from `connect()` to tell the\n * reconnect half of an in-page move from a first connect: a controller whose\n * initialisation restarts a measurement (a min-duration floor, an elapsed\n * counter) must skip it for the move, where nothing actually restarted.\n */\n get pending(): boolean {\n return this.#pending;\n }\n\n /**\n * True when the disconnect is definitely a real detach — the element left\n * the document, or `data-controller` no longer lists the identifier. False\n * means ambiguous (in-page move or observed-root exit), NOT \"alive\".\n */\n static isDetached(host: DetachGateHost): boolean {\n return !host.element.isConnected || !DetachGate.#listed(host);\n }\n\n /**\n * Call from `disconnect()`: runs `teardown` synchronously on a definite\n * detach (fast path), otherwise defers it one microtask — a reconnect\n * ({@link cancel} from `connect()`) keeps the state, no reconnect runs it.\n * One microtask is the whole probe window: Stimulus reconnects a moved\n * element within the same mutation batch, before the checkpoint drains. An\n * element Turbo carries to the next page is probed until the next task, by\n * which time Turbo has put it back.\n */\n disconnected(host: DetachGateHost, teardown: () => void): void {\n const carried = DetachGate.#carried(host);\n if (!carried && DetachGate.isDetached(host)) {\n // Also disarms a probe a previous ambiguous disconnect left queued\n // (defer, element removed, disconnect again) — exactly one teardown.\n this.#pending = false;\n teardown();\n return;\n }\n this.#pending = true;\n const probe = (): void => {\n if (!this.#pending) return;\n this.#pending = false;\n teardown();\n };\n if (carried) setTimeout(probe, 0);\n else queueMicrotask(probe);\n }\n\n /**\n * Whether `host` is out of the document inside a `data-turbo-permanent` element, its\n * identifier still listed: where Turbo holds an element it carries to the next page.\n */\n static #carried(host: DetachGateHost): boolean {\n const element = host.element;\n return (\n !element.isConnected &&\n element.closest(\"[data-turbo-permanent]\") !== null &&\n DetachGate.#listed(host)\n );\n }\n\n /** Whether the element's `data-controller` still lists the identifier. */\n static #listed(host: DetachGateHost): boolean {\n const tokens = (host.element.getAttribute(\"data-controller\") ?? \"\").split(/\\s+/);\n return tokens.includes(host.identifier);\n }\n\n /**\n * Disarms a pending probe. Call from `connect()` (the reconnect that proves\n * an in-page move) and from the head of any teardown path not routed through\n * {@link disconnected} (disabled-toggle, Escape), so an orphaned probe can\n * never run the teardown a second time.\n */\n cancel(): void {\n this.#pending = false;\n }\n}\n","/**\n * Whether a disabled `<fieldset>` ancestor actually reaches `control`.\n *\n * HTML exempts the contents of a fieldset's **first direct-child `<legend>`**, and\n * the exemption is per fieldset — a control legal in one legend can still be\n * disabled by a second, outer fieldset, so the walk continues upward.\n *\n * Exported on its own for callers that need the disabled-inheritance rule without\n * the rest of {@link canTakeFocus} — a control's availability check, or a `hidden`\n * walk bounded at a widget root, which is a different rule to compose with.\n */\nexport function inheritsFieldsetDisabled(control: HTMLElement): boolean {\n let fieldset: Element | null = control.closest(\"fieldset[disabled]\");\n while (fieldset) {\n const legend = Array.from(fieldset.children).find((child) => child.tagName === \"LEGEND\");\n if (!legend?.contains(control)) return true;\n fieldset = fieldset.parentElement?.closest(\"fieldset[disabled]\") ?? null;\n }\n return false;\n}\n\n/**\n * Whether the engine reports `element` assigned to no slot of the open shadow root its parent\n * hosts: such an element is not in the flat tree, so it and what it holds are rendered nowhere\n * and cannot take focus.\n */\nfunction unslotted(element: Element): boolean {\n return element.assignedSlot === null && element.parentElement?.shadowRoot != null;\n}\n\n/**\n * The parent of `element` in the flat tree: the slot it is assigned to, its parent element,\n * or the host of the shadow root it is a child of; `null` at the top of the document or of a\n * detached tree, and for an element the engine assigns to no slot of the open shadow root its\n * parent hosts, which is outside the flat tree. A closed shadow root does not report its slots,\n * so an element assigned to one climbs to the host.\n */\nexport function flatTreeParent(element: Element): Element | null {\n if (unslotted(element)) return null;\n const parent = element.assignedSlot ?? element.parentNode;\n if (parent instanceof ShadowRoot) return parent.host;\n return parent instanceof Element ? parent : null;\n}\n\n/** Whether `element` is `ancestor` or one of its descendants in the flat tree. */\nexport function flatTreeContains(ancestor: Element, element: Element | null): boolean {\n for (let node = element; node; node = flatTreeParent(node)) {\n if (node === ancestor) return true;\n }\n return false;\n}\n\n/** The nearest of `element` and its flat-tree ancestors that matches `selector`, or `null`. */\nexport function closestInFlatTree(element: Element, selector: string): Element | null {\n for (let node: Element | null = element; node; node = flatTreeParent(node)) {\n if (node.matches(selector)) return node;\n }\n return null;\n}\n\n/**\n * The children of `element` in the flat tree: its open shadow root's children, a slot's\n * assigned elements (its own children when nothing is assigned to it), or its children.\n */\nexport function flatTreeChildren(element: Element): Element[] {\n return ownedScope(element) ?? Array.from(element.children);\n}\n\n/**\n * Whether an element can actually take focus, checked **before** `focus()` runs.\n *\n * A controller that must move focus off a control it is about to disable or hide\n * picks a destination and calls `focus()` on it. If that destination cannot take\n * focus, the call **fails silently**: `hidden` and natively `disabled` elements\n * swallow it, so the caret stays in the subtree that is disappearing and lands on\n * `<body>` a frame later — the exact outcome the rescue exists to prevent, minus\n * any signal that it happened.\n *\n * Testing after the fact is the obvious alternative and is deliberately not the\n * rule. Reading `document.activeElement` back only works in a real browser, and\n * looping over candidates that way performs a real focus move per failure —\n * observable to assistive technology. Checking first costs nothing and catches\n * the cases that actually occur.\n *\n * **`aria-disabled` is not disqualifying.** It is the attribute an author uses\n * for a control that must stay *discoverable*, and the roving contract keeps\n * such items reachable. Only the conditions that make the platform refuse\n * focus are checked: `hidden` / `inert` on the element or a flat-tree ancestor\n * (the host of an open shadow root it sits in, the slot it is assigned to), the\n * element or an ancestor left out of the flat tree (one the engine assigns to no\n * slot of the open shadow root its parent hosts), `input[type=\"hidden\"]`, the\n * native `disabled` property, and `disabled` inherited from an ancestor\n * `fieldset`. CSS-only invisibility is handled by {@link isRenderedForFocus}\n * when a consumer needs sequential-focus semantics.\n *\n * Reading `:disabled` instead of walking the fieldset chain would be shorter, but\n * that pseudo-class is not evaluated consistently outside real browsers and this\n * has to be right headlessly too. happy-dom in particular focuses a `<button>`\n * inside a disabled fieldset where a real engine refuses, so the inheritance is\n * spelled out rather than delegated.\n *\n * What a consumer does when nothing survives is its own call: some fall back to\n * their landmark, while a widget whose caret already sits somewhere legitimate\n * refuses the move outright rather than relocating it.\n *\n * @example\n * ```ts\n * const target = candidates.find(canTakeFocus);\n * if (target) target.focus();\n * else {\n * this.#tabindex.lend(this.element); // nothing left: fall back to the landmark\n * this.element.focus();\n * }\n * ```\n *\n * @param element - the candidate destination\n */\nexport function canTakeFocus(element: HTMLElement): boolean {\n for (let node: Element | null = element; node; node = flatTreeParent(node)) {\n if (unslotted(node) || node.matches(\"[hidden], [inert]\")) return false;\n }\n if (element instanceof HTMLInputElement && element.type === \"hidden\") return false;\n if (!(\"disabled\" in element)) return true;\n if ((element as HTMLElement & { disabled: boolean }).disabled) return false;\n return !inheritsFieldsetDisabled(element);\n}\n\n/** Elements whose semantics or authored attributes can place them in sequential focus order. */\nexport const TAB_STOP_CANDIDATE_SELECTOR = [\n \"a[href]\",\n \"area[href]\",\n \"button\",\n \"input\",\n \"select\",\n \"textarea\",\n \"summary\",\n \"iframe\",\n \"audio[controls]\",\n \"video[controls]\",\n \"[tabindex]\",\n \"[contenteditable]\",\n].join(\",\");\n\n/** Optional browser visibility API used to exclude CSS-hidden candidates. */\ninterface VisibilityCheckable {\n checkVisibility?: (options?: { visibilityProperty?: boolean }) => boolean;\n}\n\n/** Whether CSS visibility allows an otherwise eligible element to participate in focus order. */\nexport function isRenderedForFocus(element: HTMLElement): boolean {\n const check = (element as HTMLElement & VisibilityCheckable).checkVisibility;\n return typeof check === \"function\" ? check.call(element, { visibilityProperty: true }) : true;\n}\n\n/** Parses an authored `tabindex`; invalid syntax has no explicit focus-order meaning. */\nfunction authoredTabindex(element: Element): number | null {\n const value = element.getAttribute(\"tabindex\");\n if (value === null || !/^[+-]?\\d+$/.test(value.trim())) return null;\n return Number(value);\n}\n\n/** Whether the element's native semantics place it in sequential focus order. */\nfunction hasNativeTabStop(element: HTMLElement): boolean {\n if (element instanceof HTMLAnchorElement || element instanceof HTMLAreaElement) {\n return element.hasAttribute(\"href\");\n }\n if (\n element instanceof HTMLButtonElement ||\n element instanceof HTMLSelectElement ||\n element instanceof HTMLTextAreaElement\n ) {\n return true;\n }\n if (element instanceof HTMLInputElement) return element.type !== \"hidden\";\n if (element instanceof HTMLIFrameElement) return true;\n if (element.tagName === \"AUDIO\" || element.tagName === \"VIDEO\") {\n return element.hasAttribute(\"controls\");\n }\n if (element instanceof HTMLElement && element.tagName === \"SUMMARY\") {\n const details = element.parentElement;\n return (\n details instanceof HTMLDetailsElement &&\n Array.from(details.children).find((child) => child.tagName === \"SUMMARY\") === element\n );\n }\n return false;\n}\n\n/** Whether an explicit `contenteditable` value creates an editable tab stop. */\nfunction hasEditableTabStop(element: HTMLElement): boolean {\n const value = element.getAttribute(\"contenteditable\")?.toLowerCase();\n return value === \"\" || value === \"true\" || value === \"plaintext-only\";\n}\n\n/**\n * Whether an element is a usable sequential Tab stop right now.\n *\n * Native semantics, authored `tabindex`, editable hosts, inherited disabled state,\n * HTML `hidden`/`inert`, and CSS visibility are evaluated together. `aria-disabled`\n * remains focusable because it communicates unavailability without removing the\n * control from discovery order.\n */\nexport function isTabStop(element: HTMLElement): boolean {\n if (!canTakeFocus(element) || !isRenderedForFocus(element)) return false;\n\n const tabindex = authoredTabindex(element);\n if (tabindex !== null) return tabindex >= 0;\n return hasNativeTabStop(element) || hasEditableTabStop(element);\n}\n\n/** Returns every usable sequential Tab stop below `root` in document order. */\nexport function tabStopsWithin(root: ParentNode): HTMLElement[] {\n return Array.from(root.querySelectorAll<HTMLElement>(TAB_STOP_CANDIDATE_SELECTOR)).filter(\n isTabStop,\n );\n}\n\n/** Returns the first usable sequential Tab stop below `root`, if one exists. */\nexport function firstTabStop(root: ParentNode): HTMLElement | null {\n for (const candidate of root.querySelectorAll<HTMLElement>(TAB_STOP_CANDIDATE_SELECTOR)) {\n if (isTabStop(candidate)) return candidate;\n }\n return null;\n}\n\n/** Whether `root` contains at least one usable sequential Tab stop. */\nexport function hasTabStop(root: ParentNode): boolean {\n return firstTabStop(root) !== null;\n}\n\n/** An element sequential focus navigation can stop on. */\nexport type TabStopElement = HTMLElement | SVGElement;\n\n/** Whether the element is a radio button. */\nfunction isRadio(element: Element | null): element is HTMLInputElement {\n return element instanceof HTMLInputElement && element.type === \"radio\";\n}\n\n/**\n * Whether two radios form one group: the same non-empty `name`, the same form owner and\n * the same tree. A radio without a name is a group of its own.\n */\nfunction sameRadioGroup(a: HTMLInputElement, b: HTMLInputElement): boolean {\n return (\n a.name !== \"\" && a.name === b.name && a.form === b.form && a.getRootNode() === b.getRootNode()\n );\n}\n\n/**\n * Whether the element is part of editable content: the nearest ancestor that sets a valid\n * `contenteditable` decides. `false` ends the editable region (a non-editable island\n * inside an editor); an invalid value inherits from further up.\n */\nfunction isInEditableContent(element: Element): boolean {\n for (let node = element.parentElement; node; node = node.parentElement) {\n if (node.getAttribute(\"contenteditable\")?.toLowerCase() === \"false\") return false;\n if (hasEditableTabStop(node)) return true;\n }\n return false;\n}\n\n/** The XLink namespace, where an SVG link may carry its `href`. */\nconst XLINK_NAMESPACE = \"http://www.w3.org/1999/xlink\";\n\n/** Whether the element is an SVG link: an SVG `a` with `href` or `xlink:href`. */\nfunction isSvgLink(element: Element): boolean {\n return (\n element instanceof SVGElement &&\n element.localName === \"a\" &&\n (element.hasAttribute(\"href\") || element.hasAttributeNS(XLINK_NAMESPACE, \"href\"))\n );\n}\n\n/**\n * Whether an image-map area has a rendered image: an `img` in the area's tree whose\n * `usemap` names the area's map (by `name`, or by `id` without one) and that is rendered.\n * The area has no box of its own for {@link isRenderedForFocus} to check.\n */\nfunction hasRenderedImage(area: HTMLAreaElement): boolean {\n const map = area.closest(\"map\");\n const key = map?.name || map?.id;\n if (!map || !key) return false;\n const root = map.getRootNode() as Document | ShadowRoot;\n return Array.from(root.querySelectorAll<HTMLElement>(\"img[usemap]\")).some(\n (image) => image.getAttribute(\"usemap\") === `#${key}` && isRenderedForFocus(image),\n );\n}\n\n/** Whether the element can take focus and has no negative `tabindex`. */\nfunction isFocusableInOrder(element: TabStopElement): boolean {\n const tabindex = authoredTabindex(element);\n return (tabindex === null || tabindex >= 0) && canTakeFocus(element as HTMLElement);\n}\n\n/**\n * The engine's verdict for the stops {@link isTabStop} does not judge the way sequential\n * navigation does, or `null` for every other element: an image-map area with an `href` or\n * its own `tabindex` stops while an image that uses its map is rendered; an object stops\n * while it holds a nested document, as an iframe does; an SVG link stops while it is\n * rendered. Each also needs to take focus and not be taken out of the order with a\n * negative `tabindex`.\n */\nfunction extraStop(element: TabStopElement): boolean | null {\n if (element instanceof HTMLAreaElement) {\n const focusable = element.hasAttribute(\"href\") || authoredTabindex(element) !== null;\n return focusable && isFocusableInOrder(element) && hasRenderedImage(element);\n }\n if (element instanceof HTMLObjectElement) {\n return (\n isFocusableInOrder(element) && isRenderedForFocus(element) && element.contentWindow !== null\n );\n }\n if (isSvgLink(element))\n return isFocusableInOrder(element) && isRenderedForFocus(element as HTMLElement);\n return null;\n}\n\n/**\n * Whether sequential navigation stops on the element: a usable Tab stop\n * ({@link isTabStop}), or one of the stops `extraStop` judges, except a link inside\n * editable content without a `tabindex` of its own, which the engine leaves to editing.\n * Other controls inside an editing host, and the host itself, stay stops.\n */\nfunction isSequentialStop(element: TabStopElement): boolean {\n const stop =\n extraStop(element) ??\n (element.matches(TAB_STOP_CANDIDATE_SELECTOR) && isTabStop(element as HTMLElement));\n if (!stop) return false;\n const link =\n element instanceof HTMLAnchorElement ||\n element instanceof HTMLAreaElement ||\n isSvgLink(element);\n return !(link && authoredTabindex(element) === null && isInEditableContent(element));\n}\n\n/**\n * The elements of the focus navigation scope `element` owns: its open shadow root's\n * children, or a slot's assigned elements (its fallback content when nothing is\n * assigned). `null` when it owns no scope. A closed shadow root cannot be read, so its\n * host owns none.\n */\nfunction ownedScope(element: Element): Element[] | null {\n if (element.shadowRoot) return Array.from(element.shadowRoot.children);\n if (element instanceof HTMLSlotElement) {\n const assigned = element.assignedElements();\n return assigned.length > 0 ? assigned : Array.from(element.children);\n }\n return null;\n}\n\n/** One member of a focus navigation scope: its tabindex and the stops it contributes. */\ninterface ScopeEntry {\n readonly tabindex: number;\n readonly stops: TabStopElement[];\n}\n\n/** What a walk of the order records besides the order itself. */\ninterface OrderWalk {\n /** The element a move starts from. */\n readonly from: Element | null;\n /** Every stop, in flat-tree order. */\n readonly tree: TabStopElement[];\n /**\n * How many entries of {@link OrderWalk.tree} come before `from`; `-1` until the walk\n * meets it.\n */\n fromAt: number;\n}\n\n/**\n * Orders one focus navigation scope the way HTML's sequential navigation does: members\n * with a positive `tabindex` first, ascending, ties in tree order, then the rest in tree\n * order. A member that owns a scope (a shadow host, a slot) is followed by that scope's\n * own order; one taken out of the order with a negative `tabindex` takes its scope with it.\n */\nfunction orderScope(elements: Element[], walk: OrderWalk): TabStopElement[] {\n const entries: ScopeEntry[] = [];\n const visit = (element: Element): void => {\n if (element === walk.from) walk.fromAt = walk.tree.length;\n const tabindex = authoredTabindex(element) ?? 0;\n const stop =\n (element instanceof HTMLElement || element instanceof SVGElement) && isSequentialStop(element)\n ? element\n : null;\n if (stop) walk.tree.push(stop);\n const owned = ownedScope(element);\n if (owned) {\n if (tabindex >= 0) {\n entries.push({ tabindex, stops: [...(stop ? [stop] : []), ...orderScope(owned, walk)] });\n }\n return;\n }\n if (stop) entries.push({ tabindex, stops: [stop] });\n for (const child of Array.from(element.children)) visit(child);\n };\n for (const element of elements) visit(element);\n const positive = entries.filter((entry) => entry.tabindex > 0);\n positive.sort((a, b) => a.tabindex - b.tabindex);\n const rest = entries.filter((entry) => entry.tabindex === 0);\n return [...positive, ...rest].flatMap((entry) => entry.stops);\n}\n\n/**\n * Whether a candidate passes the radio-group rule the engine applies: a checked radio\n * among the stops always does; an unchecked one only as the first radio of a group with\n * no checked stop, and not while focus is on a radio of that group. `tree` is the walk's\n * stops in flat-tree order, so a group is counted within the walked root only.\n */\nfunction passesRadioRule(\n element: TabStopElement,\n tree: TabStopElement[],\n focusedRadio: HTMLInputElement | null,\n): boolean {\n if (!isRadio(element) || element.checked) return true;\n const group = tree.filter(\n (stop): stop is HTMLInputElement =>\n stop === element || (isRadio(stop) && sameRadioGroup(stop, element)),\n );\n if (group.some((radio) => radio.checked)) return false;\n if (group[0] !== element) return false;\n return focusedRadio === null || !sameRadioGroup(focusedRadio, element);\n}\n\n/**\n * The index in `order` a move from the walk's `from` visits first. A start the walk did\n * not meet is outside: the move enters at the first stop, or the last going backward. A\n * start that is a stop moves one place along the order. Any other start (the root, an\n * element with a negative `tabindex`) moves to the nearest stop after it in tree order,\n * or before it going backward, and wraps when there is none.\n */\nfunction startIndex(\n order: TabStopElement[],\n walk: OrderWalk,\n backward: boolean,\n isStop: (element: TabStopElement) => boolean,\n): number {\n if (walk.fromAt < 0) return backward ? order.length - 1 : 0;\n const at = order.indexOf(walk.from as TabStopElement);\n if (at >= 0) return backward ? at - 1 : at + 1;\n const neighbour = backward\n ? walk.tree.slice(0, walk.fromAt).reverse().find(isStop)\n : walk.tree.slice(walk.fromAt).find(isStop);\n if (neighbour) return order.indexOf(neighbour);\n return backward ? order.length - 1 : 0;\n}\n\n/**\n * The stops below `root` in the order a sequential move from `from` visits them: the\n * next stop first (the previous one when `backward`), wrapping at the ends, with `from`\n * itself left out. Without a `from` — or with one outside `root` — the list starts at the\n * first stop, or at the last one when `backward`.\n *\n * The order is HTML's sequential navigation order within `root`: positive `tabindex`\n * first, ascending, then `tabindex=\"0\"` and native stops in tree order. Open shadow roots\n * are walked in flat-tree order, slots expanded, each scope ordered on its own; a closed\n * shadow root is not read. A radio group, counted within `root`, stops once: on its\n * checked radio when that radio can take focus, even with focus elsewhere in the group,\n * and otherwise on its first radio that can, unless focus is already in the group.\n * A link inside editable content without its own `tabindex` is not a stop. Every stop\n * also passes its applicable focusability and rendering checks.\n *\n * @param root - The element whose descendants are ordered; it is not a stop itself.\n * @param from - Where the move starts, usually {@link deepActiveElement}.\n * @param backward - Whether the move goes backward (`Shift+Tab`).\n */\nexport function sequentialTabStops(\n root: Element,\n from: Element | null = null,\n backward = false,\n): TabStopElement[] {\n const walk: OrderWalk = { from, tree: [], fromAt: from === root ? 0 : -1 };\n const order = orderScope(ownedScope(root) ?? Array.from(root.children), walk);\n const focusedRadio = walk.fromAt >= 0 && isRadio(from) ? from : null;\n const isStop = (element: TabStopElement): boolean =>\n passesRadioRule(element, walk.tree, focusedRadio);\n const start = startIndex(order, walk, backward, isStop);\n const visited = backward\n ? [...order.slice(0, start + 1).reverse(), ...order.slice(start + 1).reverse()]\n : [...order.slice(start), ...order.slice(0, start)];\n return visited.filter((element) => element !== from && isStop(element));\n}\n\n/**\n * The element that holds focus in `ownerDocument`, looking through open shadow roots:\n * `document.activeElement` stops at a shadow host. A closed shadow root cannot be read,\n * so focus inside one reads as its host.\n */\nexport function deepActiveElement(ownerDocument: Document): Element | null {\n let active = ownerDocument.activeElement;\n while (active?.shadowRoot?.activeElement) active = active.shadowRoot.activeElement;\n return active;\n}\n","/**\n * One registry per page for every copy of the library.\n *\n * Each `stimeo-ui/controllers/*` file, and each opt-in entry, is built on its own and carries\n * its own copy of every utility it uses, so a page that imports controllers one by one, or one\n * of them next to the barrel, evaluates a utility several times. A registry that coordinates\n * instances of different controllers — the stacks of leased values, the loans of a\n * `tabindex`, the Escape layers, the focus traps — must not be split by that: a dialog inside\n * a drawer, or a character counter and a form field on one input, coordinate whichever files\n * they came from. So such a registry lives once on `globalThis`, under a `Symbol.for` key the\n * copies agree on, and the first copy to ask for it creates it.\n *\n * The key names the registry and the version of its shape\n * (`stimeo-ui.<utility>.registry.v<n>`). Copies whose registries have the same shape share\n * one; a release that changes the shape changes the version, so a copy of another release\n * keeps a registry of its own instead of reading entries it does not understand — the two\n * then coordinate within each release only. An entry another copy put in a registry is read\n * through its fields alone, never through `#private` members, which belong to the class of\n * the copy that created it.\n */\n\n/**\n * The registry stored under `key`, created by `create` on first use.\n *\n * @param key - `stimeo-ui.<utility>.registry.v<n>`: the version is that of the shape `create`\n * returns.\n * @param create - Builds the empty registry.\n */\nexport function sharedRegistry<T extends object>(key: string, create: () => T): T {\n const symbol = Symbol.for(key);\n const scope = globalThis as unknown as Record<symbol, T | undefined>;\n const existing = scope[symbol];\n if (existing !== undefined) return existing;\n const registry = create();\n Object.defineProperty(globalThis, symbol, { value: registry });\n return registry;\n}\n","import { sharedRegistry } from \"./shared_registry\";\n\n/** Reserved transport names identify one produced entry without grouping equal public names. */\nconst PREFIX = \"__stimeo_raw_v1_\";\n\ninterface Projection {\n readonly field: HTMLInputElement;\n readonly document: Document;\n readonly state: DocumentProjection;\n readonly key: string;\n readonly name: string;\n readonly project: (value: FormDataEntryValue) => FormDataEntryValue;\n readonly restore: () => void;\n}\n\ninterface DocumentProjection {\n readonly fields: Map<string, Projection>;\n readonly handle: (event: Event) => void;\n readonly byField: WeakMap<HTMLInputElement, Projection>;\n}\n\nconst documents = sharedRegistry(\n \"stimeo-ui.form-data-projection.registry.v1\",\n () => new WeakMap<Document, DocumentProjection>(),\n);\n\n/** The public name encoded by a reserved transport name, or null for an ordinary name. */\nfunction decodedName(key: string): string | null {\n if (!key.startsWith(PREFIX)) return null;\n const separator = PREFIX.length + 32;\n if (key[separator] !== \":\") return null;\n try {\n const name: unknown = JSON.parse(key.slice(separator + 1));\n return typeof name === \"string\" ? name : null;\n } catch {\n return null;\n }\n}\n\n/** Reads a field's logical submission name without exposing its transport identity. */\nexport function projectedFieldName(name: string): string {\n return decodedName(name) ?? name;\n}\n\n/** Installs one capture listener for the document's reserved transport namespace. */\nfunction forDocument(document: Document): DocumentProjection {\n const existing = documents.get(document);\n if (existing) return existing;\n const state: DocumentProjection = {\n fields: new Map(),\n byField: new WeakMap(),\n handle: (event) => {\n const data = (event as FormDataEvent).formData;\n if (!data) return;\n const entries = [...data.entries()];\n let changed = false;\n const projected = entries.map(([key, value]): [string, FormDataEntryValue] => {\n const name = decodedName(key);\n if (name === null) return [key, value];\n changed = true;\n const field = state.fields.get(key);\n const live = field && field.field.name === key && field.field.form === event.target;\n return [name, live ? field.project(value) : \"\"];\n });\n if (!changed) return;\n for (const key of new Set(data.keys())) data.delete(key);\n for (const [name, value] of projected) data.append(name, value);\n },\n };\n documents.set(document, state);\n // @stimeoListenerLifetime document Decodes cached or released transport entries even when no\n // producer remains.\n document.addEventListener(\"formdata\", state.handle, { capture: true });\n return state;\n}\n\n/** Prepares a form's projection event path, including while the form is detached. */\nexport function prepareFormDataProjection(form: HTMLFormElement): void {\n const state = forDocument(form.ownerDocument);\n // @stimeoListenerLifetime element A form keeps transport decoding when detached from its\n // document.\n form.addEventListener(\"formdata\", state.handle, { capture: true });\n}\n\n/**\n * Gives a maintained hidden field a unique transport identity while preserving its\n * value. FormData exposes the public name in the same entry position; the producer\n * decides the submitted value without changing the DOM or releasing ownership.\n */\nexport class FormDataProjection {\n #held: Projection | null = null;\n\n /**\n * Claims this field's current name; a normal name written by the page starts a new claim.\n * A Turbo morph that writes the public name back returns the field to this claim at once.\n */\n hold(field: HTMLInputElement, project: Projection[\"project\"]): void {\n const held = this.#held;\n if (held?.field === field && held.key === field.name && held.document === field.ownerDocument) {\n if (field.form) prepareFormDataProjection(field.form);\n return;\n }\n this.release();\n const name = projectedFieldName(field.name);\n if (name === \"\" || (field.type === \"hidden\" && name.toLowerCase() === \"_charset_\")) return;\n const state = forDocument(field.ownerDocument);\n if (field.form) prepareFormDataProjection(field.form);\n const previous = state.byField.get(field);\n if (previous) {\n state.fields.delete(previous.key);\n field.removeEventListener(\"turbo:morph-element\", previous.restore);\n }\n const nonce = Array.from(crypto.getRandomValues(new Uint8Array(16)), (value) =>\n value.toString(16).padStart(2, \"0\"),\n ).join(\"\");\n const key = `${PREFIX}${nonce}:${JSON.stringify(name)}`;\n const projection: Projection = {\n field,\n key,\n name,\n project,\n document: field.ownerDocument,\n state,\n restore: () => {\n if (field.name === name) field.name = key;\n },\n };\n state.fields.set(key, projection);\n state.byField.set(field, projection);\n this.#held = projection;\n field.name = key;\n // @stimeoListenerLifetime element The current claim keeps its name through a morph; a\n // release or a new claim removes it.\n field.addEventListener(\"turbo:morph-element\", projection.restore);\n }\n\n /** Restores only the name this claim still holds; another writer's name stays untouched. */\n release(): void {\n const held = this.#held;\n this.#held = null;\n if (held === null) return;\n held.field.removeEventListener(\"turbo:morph-element\", held.restore);\n const state = held.state;\n if (state.byField.get(held.field) !== held) return;\n state.fields.delete(held.key);\n state.byField.delete(held.field);\n if (held.field.name === held.key) held.field.name = held.name;\n }\n}\n","/** The distance between a full-width ASCII code point and its half-width twin. */\nconst FULL_WIDTH_SHIFT = 0xfee0;\n\n/**\n * The half-width form of `char`, or `char` itself when it has none.\n *\n * An IME confirming in full-width mode produces U+FF01–U+FF5E (and U+3000 for\n * the space) where the page means ASCII — the digits of a phone number, the\n * letters of a code, the separators between them. Reading those as their ASCII\n * equivalents keeps confirmed text usable instead of discarding it. Characters\n * outside that block, including kana and astral code points, are returned\n * unchanged, so this never rewrites text a consumer meant to keep as typed.\n */\nexport function halfWidthChar(char: string): string {\n if (char >= \"!\" && char <= \"~\") {\n return String.fromCharCode(char.charCodeAt(0) - FULL_WIDTH_SHIFT);\n }\n return char === \" \" ? \" \" : char;\n}\n\n/** Every full-width ASCII character in `text` rewritten by {@link halfWidthChar}. */\nexport function toHalfWidth(text: string): string {\n let out = \"\";\n for (const char of text) out += halfWidthChar(char);\n return out;\n}\n","/** What {@link ListenerSet.add} passes on besides the signal it supplies itself. */\nexport type ListenerOptions = Omit<AddEventListenerOptions, \"signal\">;\n\n/**\n * The event map a target's own `addEventListener` types its handlers with: the\n * window's, a document's, an HTML or SVG element's, any other element's, or a\n * media query list's. Other targets name none.\n */\ntype EventMapOf<T extends EventTarget> = T extends Window\n ? WindowEventMap\n : T extends Document\n ? DocumentEventMap\n : T extends HTMLElement\n ? HTMLElementEventMap\n : T extends SVGElement\n ? SVGElementEventMap\n : T extends Element\n ? ElementEventMap\n : T extends MediaQueryList\n ? MediaQueryListEventMap\n : Record<never, never>;\n\n/**\n * Owns every listener a controller attaches for one connected lifetime and\n * releases them together, so an event name is spelled once and the release\n * cannot drift from the registration.\n *\n * Detaching by hand repeats three things at the release site — the type, the\n * handler reference and the capture flag — and any one of them drifting leaves\n * a listener alive on an element the controller no longer drives. Aborting the\n * signal a registration was created with releases it whatever it was made of,\n * so the release site names nothing.\n *\n * One set per lifetime, held as a field and never rebuilt.\n * {@link ListenerSet.dispose} is the only thing that closes a generation, and it opens\n * the next one in the same step, so the lifecycle hooks can call `add` and\n * `dispose` in any order any number of times, and no `add` ever meets an\n * already aborted signal — a DOM that registers a listener on such a signal\n * would keep it forever. Rebuilding the set instead would leak:\n * `addEventListener` matches an existing registration on target, type, callback\n * and capture alone, so re-adding the same handler while the first registration\n * is live is discarded, and that live registration keeps the signal it was\n * attached with — a set built by a second `connect()` can never release what it\n * appears to own.\n *\n * Scope is the registration only. Timers, observers, leases and the order they\n * are unwound in stay with the controller, which places `dispose()` in its\n * cleanup sequence. A receiver that changes at runtime (a\n * swapped target) is rebound by calling `dispose()` and then `add()` again from\n * the rebind, not by a second set.\n *\n * A lifetime shorter than the connection — while a Value holds, until a first\n * load, while a subscription is open — gets a set of its own, which the end of\n * that lifetime disposes. A controller therefore holds one set per lifetime: the\n * connection's, and one for each swapped receiver or shorter span.\n *\n * The set must own every registration of a tuple it holds: a listener the same\n * target already carries for the same type, callback and capture flag keeps the\n * right to remove it, and this set's `add` for that tuple is discarded. A\n * subscription shared between instances behind a participant count, listeners of\n * several elements that come and go one at a time, and those one gesture holds\n * keep their own pair of calls or a signal of their own.\n *\n * @example\n * ```ts\n * readonly #listeners = new ListenerSet();\n *\n * connect(): void {\n * this.#listeners.add(this.element, \"submit\", this.#onNativeSubmit, { capture: true });\n * this.#listeners.add(this.element, \"turbo:submit-start\", this.#onSubmitStart);\n * this.#listeners.add(this.element, \"turbo:submit-end\", this.#onSubmitEnd);\n * }\n *\n * disconnect(): void {\n * this.#listeners.dispose();\n * }\n * ```\n */\nexport class ListenerSet {\n /** The generation every `add` joins until the next `dispose()`. */\n #abort = new AbortController();\n\n /**\n * Attaches `handler` to the open generation, exactly as the caller spelled it.\n *\n * The set supplies the signal, so `options` carries everything else the DOM\n * accepts — `capture` included. A removal by hand would have to repeat that\n * flag; aborting the signal releases the listener without it.\n *\n * A `type` the target's own event map names types `handler` with that event,\n * as the target's `addEventListener` does, so a handler written for a\n * `KeyboardEvent` is passed as it is; any other `type` takes an\n * `EventListener`.\n */\n add<T extends EventTarget, K extends keyof EventMapOf<T> & string>(\n target: T,\n type: K,\n handler: (event: EventMapOf<T>[K]) => void,\n options?: ListenerOptions,\n ): void;\n add(target: EventTarget, type: string, handler: EventListener, options?: ListenerOptions): void;\n add(target: EventTarget, type: string, handler: EventListener, options?: ListenerOptions): void {\n target.addEventListener(type, handler, { ...options, signal: this.#abort.signal });\n }\n\n /**\n * Releases every listener of the open generation, synchronously, and opens the\n * next one. Idempotent, and safe before anything has been added.\n */\n dispose(): void {\n this.#abort.abort();\n this.#abort = new AbortController();\n }\n}\n","/**\n * Collapses many Stimulus lifecycle callbacks from one DOM mutation into a\n * single pass.\n *\n * Stimulus fires `<name>TargetConnected` / `Disconnected` once per element and\n * `<name>ValueChanged` once per changed attribute. Replacing a list of N options\n * or morphing several render Values therefore delivers N callbacks — but the\n * useful unit of work is \"reconcile against the resulting declarative input\",\n * once, after the batch has settled. Every controller with reconcilable targets\n * or render Values needs the same shape: a `queued` flag plus `queueMicrotask`.\n *\n * **A microtask is the right horizon, and the reason is specific.** Stimulus\n * drives these callbacks from a `MutationObserver`, whose own callback already\n * runs as a microtask with the whole batch in hand; scheduling one more lands\n * after the last sibling callback of that batch and still before paint or any\n * event handler. A timer would be later than it needs to be, and reconciling\n * synchronously would run once per element against a half-applied DOM.\n *\n * **The two guards are not the same guard.** Scheduling is refused before the\n * controller connects, and running is refused after it disconnects:\n *\n * - **Before `connect()`** — Stimulus delivers initial target and Value callbacks\n * ahead of `connect()`. Reconciling there would compute output against a\n * controller whose own state has not been initialised, and `connect()` is\n * about to do a full pass anyway.\n * - **After `disconnect()`** — Stimulus fires a callback for **every** target\n * during teardown, and a microtask queued just before it would otherwise run\n * against a detached tree. {@link MicrotaskCoalescer.cancel} exists for the\n * teardown path to drop the pending pass outright.\n *\n * Both guards are part of one contract here rather than something each consumer\n * has to remember separately.\n *\n * Scope is the scheduling only. *What* to reconcile — keep the surviving active\n * option, fall back to the next / previous / first visible one, rebuild derived\n * chips or hidden fields — stays in the controller, because no two consumers\n * answer it the same way.\n *\n * @example\n * ```ts\n * readonly #reconcile = new MicrotaskCoalescer(() => this.#reconcileOptions());\n *\n * connect() { this.#reconcile.activate(); }\n * disconnect() { this.#reconcile.cancel(); }\n *\n * optionTargetConnected() { this.#reconcile.schedule(); }\n * optionTargetDisconnected() { this.#reconcile.schedule(); }\n * ```\n */\nexport class MicrotaskCoalescer {\n readonly #run: () => void;\n #queued = false;\n #active = false;\n #generation = 0;\n\n /** @param run - the single reconciliation pass, invoked at most once per batch. */\n constructor(run: () => void) {\n this.#run = run;\n }\n\n /** Opens the window in which {@link schedule} is honoured; call from `connect()`. */\n activate(): void {\n this.#active = true;\n }\n\n /** Closes the window and drops any pending pass; call from `disconnect()`. */\n cancel(): void {\n this.#active = false;\n this.#queued = false;\n this.#generation += 1;\n }\n\n /** Whether a requested pass has yet to run. */\n get pending(): boolean {\n return this.#queued;\n }\n\n /** Requests one pass after the batch settles. Idempotent; inert outside the window. */\n schedule(): void {\n if (!this.#active || this.#queued) return;\n this.#queued = true;\n const generation = this.#generation;\n queueMicrotask(() => {\n // A cancelled callback must not consume a pass queued after reconnect.\n if (generation !== this.#generation || !this.#queued || !this.#active) return;\n this.#queued = false;\n this.#run();\n });\n }\n}\n","import { ListenerSet } from \"./listener_set\";\nimport { MicrotaskCoalescer } from \"./microtask_coalescer\";\n\n/**\n * Coalesces retained-element morphs and explicit lifecycle requests into one pass.\n *\n * Observe the controller's element after its initial state is settled. Morphs on\n * that element and bubbling morphs on its descendants share the same microtask\n * as {@link schedule}. The callback repairs derived output; the controller owns\n * any logical transition reporting. {@link disconnect} releases the listener and\n * cancels pending work, including work from an earlier connected lifetime.\n */\nexport class MorphRenderWatcher {\n readonly #listeners = new ListenerSet();\n readonly #pass: MicrotaskCoalescer;\n #element: Element | null = null;\n readonly #onMorph = (): void => this.#pass.schedule();\n\n /** @param run - the single pass that repairs the settled controller's output. */\n constructor(run: () => void) {\n this.#pass = new MicrotaskCoalescer(run);\n }\n\n /** Opens observation; observing the same element again preserves queued work. */\n observe(element: Element): void {\n if (this.#element === element) return;\n this.disconnect();\n this.#element = element;\n this.#pass.activate();\n this.#listeners.add(element, \"turbo:morph-element\", this.#onMorph);\n }\n\n /** Requests one pass; requests outside an observed lifetime are ignored. */\n schedule(): void {\n this.#pass.schedule();\n }\n\n /** Whether a pass requested by a morph or {@link schedule} has yet to run. */\n get pending(): boolean {\n return this.#pass.pending;\n }\n\n /** Releases observation and pending work synchronously; safe to repeat. */\n disconnect(): void {\n this.#listeners.dispose();\n this.#pass.cancel();\n this.#element = null;\n }\n}\n","/**\n * Self-cleaning timer registries shared by Stimeo controllers.\n *\n * Stimulus controllers frequently schedule `setTimeout` / `setInterval` work\n * (auto-dismiss, debouncing, polling). When the element leaves the DOM — a\n * Turbo Drive navigation, a Turbo Stream replacement, or any `disconnect()` —\n * orphaned timers keep firing against a detached controller, leaking memory and\n * mutating stale state. {@link SafeTimeout} and {@link SafeInterval} track every\n * timer they create so a single {@link TimerRegistry.clearAll | clearAll()} call\n * in `disconnect()` tears them all down.\n *\n * These are intentionally low-level primitives: they own *registration and\n * cleanup only*. One pending timer per key is `KeyedTimers`\n * (`keyed_timers.ts`), and a timer a person can hold open, with its remaining-time\n * accounting, is `PausableTimers` (`pausable_timers.ts`).\n */\n\nexport { MAX_TIMER_DELAY_MS } from \"./timer_bounds\";\n\n/**\n * Shared registry bookkeeping for the timeout/interval variants.\n *\n * Subclasses provide the scheduling primitive ({@link schedule}) and its matching\n * canceller ({@link cancel}); this base owns the set of live ids plus the\n * per-id and bulk teardown shared by both.\n */\nabstract class TimerRegistry {\n /** Live timer ids that have not yet been cleared (or, for timeouts, fired). */\n protected readonly ids = new Set<number>();\n\n /** Schedules the underlying platform timer and returns its id. */\n protected abstract schedule(callback: () => void, delay: number): number;\n\n /** Cancels the underlying platform timer for `id`. */\n protected abstract cancel(id: number): void;\n\n /**\n * Cancels a single tracked timer.\n *\n * No-ops if the id is unknown (already cleared, fired, or never owned by this\n * registry), so callers can clear defensively without guarding.\n */\n clear(id: number): void {\n if (this.ids.delete(id)) {\n this.cancel(id);\n }\n }\n\n /**\n * Cancels every tracked timer. Call this from a controller's `disconnect()`\n * to guarantee no timer outlives the element.\n */\n clearAll(): void {\n for (const id of this.ids) {\n this.cancel(id);\n }\n this.ids.clear();\n }\n\n /** Number of timers currently tracked (pending). */\n get size(): number {\n return this.ids.size;\n }\n}\n\n/**\n * `setTimeout` wrapper that auto-forgets each timer once it fires and supports\n * bulk teardown on disconnect.\n *\n * @example\n * ```ts\n * #timers = new SafeTimeout();\n *\n * connect() {\n * this.#timers.set(() => this.dismiss(), 5000);\n * }\n *\n * disconnect() {\n * this.#timers.clearAll();\n * }\n * ```\n */\nexport class SafeTimeout extends TimerRegistry {\n /**\n * Schedules `callback` after `delay` ms and returns the timer id.\n *\n * The id is removed from the registry automatically when the timeout fires,\n * so {@link TimerRegistry.size | size} reflects only still-pending timers.\n */\n set(callback: () => void, delay: number): number {\n const id = this.schedule(() => {\n this.ids.delete(id);\n callback();\n }, delay);\n this.ids.add(id);\n return id;\n }\n\n protected schedule(callback: () => void, delay: number): number {\n return window.setTimeout(callback, delay);\n }\n\n protected cancel(id: number): void {\n window.clearTimeout(id);\n }\n}\n\n/**\n * `setInterval` wrapper that tracks every interval for bulk teardown on\n * disconnect. Unlike {@link SafeTimeout}, intervals are retained until they are\n * explicitly cleared because they fire repeatedly.\n *\n * @example\n * ```ts\n * #intervals = new SafeInterval();\n *\n * connect() {\n * this.#intervals.set(() => this.tick(), 1000);\n * }\n *\n * disconnect() {\n * this.#intervals.clearAll();\n * }\n * ```\n */\nexport class SafeInterval extends TimerRegistry {\n /** Schedules a repeating `callback` every `delay` ms and returns the timer id. */\n set(callback: () => void, delay: number): number {\n const id = this.schedule(callback, delay);\n this.ids.add(id);\n return id;\n }\n\n protected schedule(callback: () => void, delay: number): number {\n return window.setInterval(callback, delay);\n }\n\n protected cancel(id: number): void {\n window.clearInterval(id);\n }\n}\n","import { Controller } from \"@hotwired/stimulus\";\nimport { CompositionTracker } from \"../utils/composition_tracker\";\nimport { compileRegExp, parseJsonObject } from \"../utils/declared_value\";\nimport { DetachGate } from \"../utils/detach_gate\";\nimport { inheritsFieldsetDisabled } from \"../utils/focus_candidate\";\nimport { FormDataProjection, prepareFormDataProjection } from \"../utils/form_data_projection\";\nimport { halfWidthChar } from \"../utils/half_width\";\nimport { MorphRenderWatcher } from \"../utils/morph_render_watcher\";\nimport { SafeTimeout } from \"../utils/safe_timeout\";\nimport { sharedRegistry } from \"../utils/shared_registry\";\n\n/**\n * Synchronously prepares a new detached form for managed raw-field submission.\n * Registration is idempotent and lasts for this form element in its document;\n * later fields and repeated FormData constructions need no further calls.\n * Connected forms, and forms where `stimeo--input-mask` has synchronized a managed field,\n * are automatic. Call this before constructing FormData for any other detached\n * form, including an existing form that received a managed field only by a DOM\n * move. This observes submission only, without committing edits or releasing\n * raw-field ownership. Discard the form to discard its registration.\n */\nexport function prepareInputMaskForm(form: HTMLFormElement): void {\n prepareFormDataProjection(form);\n}\n\n/** Default placeholder tokens → single-char regex sources (user tokens merge over these). */\nconst DEFAULT_TOKENS: Readonly<Record<string, string>> = {\n \"9\": \"\\\\d\",\n a: \"[A-Za-z]\",\n \"*\": \"[A-Za-z0-9]\",\n};\n\n/**\n * Suffix of the attribute marking the hidden raw-value sink; its value may name the\n * paired input's `id`.\n */\nconst UNMASK_ATTR = \"unmask\";\n\n/**\n * The mask that last wrote each sink's raw value: one map per page for every copy of the\n * library, so masks registered by another Stimulus application, or loaded through both the\n * barrel and the per-controller entry, read the same writer.\n */\nconst rawWriters = sharedRegistry(\n \"stimeo-ui.input-mask-raw-writer.registry.v1\",\n () => new WeakMap<HTMLInputElement, object>(),\n);\n\n/** Which neighbour a deleting keystroke aimed at, when the platform names one. */\ntype Deletion = \"backward\" | \"forward\";\n\n/** Who moved the value: the user editing the field, or the controller re-deciding it. */\ntype Cause = \"edit\" | \"reconcile\";\n\n/** The outcome of applying a fixed mask to a raw string. */\nexport interface MaskResult {\n /** The formatted (masked) string. */\n readonly masked: string;\n /** The significant characters only (literals/separators removed). */\n readonly unmasked: string;\n /** Whether every token slot in the pattern is filled. */\n readonly complete: boolean;\n /** Per-output-char flag: true where the char fills a token (not a literal). */\n readonly tokenFlags: readonly boolean[];\n}\n\n/**\n * The form of `char` this token takes: as typed when the token accepts it, else\n * its half-width form when *that* is what the token accepts, else `null` for a\n * rejected character. Trying the character as typed first keeps a token written\n * for full-width text (or for text an IME leaves as kana) authoritative.\n */\nfunction acceptedForm(regex: RegExp, char: string): string | null {\n if (regex.test(char)) return char;\n const half = halfWidthChar(char);\n return half !== char && regex.test(half) ? half : null;\n}\n\n/**\n * Applies a fixed mask `pattern` to `value` using `tokens` (placeholder → regex).\n * Non-matching input characters are rejected (skipped); literals are auto-inserted\n * and a typed literal that matches is consumed. A character an IME confirmed in\n * full-width form fills a token — or consumes a literal — when its half-width\n * form is the one the mask asked for, and the half-width form is what the output\n * carries. Pure and exported for direct testing.\n */\nexport function applyMask(\n value: string,\n pattern: string,\n tokens: ReadonlyMap<string, RegExp>,\n): MaskResult {\n let masked = \"\";\n let unmasked = \"\";\n const tokenFlags: boolean[] = [];\n let valueIndex = 0;\n let totalTokens = 0;\n\n for (const patternChar of pattern) {\n const regex = tokens.get(patternChar);\n if (regex) totalTokens += 1;\n if (valueIndex >= value.length) continue;\n\n if (regex) {\n // Skip rejected characters until one fills this token (or input runs out).\n let taken: string | null = null;\n while (valueIndex < value.length) {\n taken = acceptedForm(regex, value[valueIndex] ?? \"\");\n if (taken !== null) break;\n valueIndex += 1;\n }\n if (taken === null) continue;\n masked += taken;\n unmasked += taken;\n tokenFlags.push(true);\n valueIndex += 1;\n } else {\n masked += patternChar;\n tokenFlags.push(false);\n // The literal is already in the output, so the typed character only has to\n // be the one that renders it — in either width.\n if (halfWidthChar(value[valueIndex] ?? \"\") === halfWidthChar(patternChar)) valueIndex += 1;\n }\n }\n\n return {\n masked,\n unmasked,\n complete: totalTokens > 0 && unmasked.length === totalTokens,\n tokenFlags,\n };\n}\n\n/**\n * The token map a `tokens` declaration selects: the defaults with the declared\n * single-char regex sources compiled over them.\n *\n * A declaration that is not a JSON object of string sources declares no tokens,\n * leaving the defaults. A source that is not a valid regex is answered per key:\n * a key that has a default keeps that default, and a key that has none is left\n * out so the character stays a literal. Either way the rest of the mask, and the\n * field, keep working instead of going down with the broken source.\n */\nfunction compileTokens(declaration: string): Map<string, RegExp> {\n const map = new Map<string, RegExp>();\n for (const [key, source] of Object.entries(DEFAULT_TOKENS)) {\n const compiled = compileRegExp(source, \"exact\");\n if (compiled) map.set(key, compiled);\n }\n for (const [key, source] of Object.entries(parseTokens(declaration))) {\n const compiled = compileRegExp(source, \"exact\");\n // A broken source declares nothing: the default this key may already carry\n // is what it falls back to, and a key without one holds no token at all.\n if (compiled) map.set(key, compiled);\n }\n return map;\n}\n\n/** The `{ token: source }` pairs a declaration holds; anything else reads as none. */\nfunction parseTokens(declaration: string): Record<string, string> {\n const parsed = parseJsonObject(declaration);\n if (parsed === null) return {};\n\n const tokens: Record<string, string> = {};\n for (const [key, source] of Object.entries(parsed)) {\n if (typeof source === \"string\") tokens[key] = source;\n }\n return tokens;\n}\n\n/** The direction an `input` event deleted in, or `null` when it is not a deletion. */\nfunction deletionOf(event: Event | undefined): Deletion | null {\n const inputType = event instanceof InputEvent ? event.inputType : \"\";\n if (!inputType.startsWith(\"delete\")) return null;\n // Word, line, and content deletions all name their direction; the ones that\n // name none (a cut, a drag-out) took a selection, so read them as backward.\n return inputType.endsWith(\"Forward\") ? \"forward\" : \"backward\";\n}\n\n/** The offset of the `n`-th token char in a masked string, or `-1` when there is none. */\nfunction tokenCharOffset(tokenFlags: readonly boolean[], n: number): number {\n let seen = 0;\n for (let i = 0; i < tokenFlags.length; i += 1) {\n if (!tokenFlags[i]) continue;\n seen += 1;\n if (seen === n) return i;\n }\n return -1;\n}\n\n/**\n * Headless **input mask** — formats a field in place against a fixed pattern\n * (`9`=digit, `a`=letter, `*`=alphanumeric, others literal), preserving the caret,\n * rejecting invalid characters, and syncing the raw value to a hidden field. No\n * dedicated APG pattern; `stimeo--currency-input` owns money-specific formatting.\n *\n * Markup contract (identifier: `stimeo--input-mask`, on the `<input>`):\n * <input type=\"text\" inputmode=\"numeric\"\n * data-controller=\"stimeo--input-mask\"\n * data-stimeo--input-mask-pattern-value=\"999-9999\"\n * data-action=\"input->stimeo--input-mask#format\">\n * <input type=\"hidden\" name=\"zip\" data-stimeo--input-mask-unmask>\n *\n * `change` and `reconcile` dispatch `{ masked, unmasked, complete }`.\n *\n * @remarks\n * Behavior only and **idempotent** — a snapshot of the committed derived output\n * repairs the hooks and the maintained sink while the IME owns the field's text.\n * Page-wide registries coordinate the last writer and each sink's form transport\n * identity. `connect()` re-formats the existing value and is stable across Turbo\n * restore/morph. The controller sits on the `<input>` itself (a void element), so\n * the raw-value sink is not a Stimulus target; it is the\n * `[data-stimeo--input-mask-unmask]` field resolved nearest-first among the\n * associated form's own controls (an explicit pairing by the sink attribute's\n * value naming the input's `id` wins; otherwise the closest ancestor's value-less\n * sink) — when one form holds several masked inputs, wrap each input+sink pair in a\n * container or pair them by `id`.\n *\n * A maintained named sink carries a reserved internal DOM name. Native FormData and\n * submission expose its original name and entry position. A removed source contributes\n * an empty raw entry without committing a draft or changing the maintained DOM value.\n * Consumers find the sink by id or pairing attribute rather than its DOM name.\n *\n * Formatting never steals keys: it rejects disallowed characters silently and keeps\n * the caret on insert, Backspace, and range replacement (WCAG 2.2 3.3.2 / 1.3.5;\n * the expected format is the consumer's `aria-describedby`). The caret anchors on\n * the token slots the text before it fills, so a rejected character leaves it\n * exactly where it was. Backspace and Delete always progress: when the keystroke\n * only removed literals the mask re-inserts, the significant character it aimed at\n * goes instead. Events fired mid-IME-composition are ignored; the confirmed text is\n * formatted once on `compositionend`. A `pattern` / `tokens` / `unmaskToHidden`\n * change that arrives mid-composition leaves the uncommitted text alone and applies\n * with that same format: as the user's `change` when the composition committed\n * text, as `reconcile` when it ended on the text it began with.\n *\n * It reflects `data-mask-complete` / `data-mask-empty`, dispatches\n * `stimeo--input-mask:change` only when a user edit moves the committed value, and\n * `stimeo--input-mask:reconcile` when the controller itself decides that value —\n * a server-rendered or restored value normalized on connection, or a re-format\n * after `pattern` / `tokens` / `unmaskToHidden` changed. A malformed `tokens`\n * declaration falls back to the default tokens, and a single broken source falls\n * back to that key's default: it is parsed once in `tokensValueChanged`, and the\n * hot path only ever sees the validated map. A field the user cannot edit\n * (`readonly`, `disabled`, or inside a disabled `<fieldset>`) keeps the value the\n * page authored — the sink and the state hooks still follow it, but the field\n * itself is never rewritten. The composition listener is released on `disconnect()`.\n */\nexport class InputMaskController extends Controller<HTMLInputElement> {\n /** The attribute above, in the namespace this controller is registered under. */\n get #unmaskAttribute(): string {\n return `data-${this.identifier}-${UNMASK_ATTR}`;\n }\n\n static override values = {\n pattern: { type: String, default: \"\" },\n tokens: { type: String, default: \"\" },\n unmaskToHidden: { type: Boolean, default: true },\n };\n static actions = [\"format\"] as const;\n static events = [\"change\", \"reconcile\"] as const;\n\n declare patternValue: string;\n declare tokensValue: string;\n declare unmaskToHiddenValue: boolean;\n\n /** The value this controller last committed, and the baseline both events compare. */\n #lastValue: string | null = null;\n #started = false;\n\n /** The sink this instance last wrote a raw value to and still answers for. */\n #rawSink: HTMLInputElement | null = null;\n /**\n * The raw value written to `#rawSink`, so a different value written there since is left\n * alone.\n */\n #rawValue = \"\";\n #rawSourceForm: HTMLFormElement | null = null;\n readonly #submission = new FormDataProjection();\n\n /** Validated token map; the hot path never parses the `tokens` declaration. */\n #tokens = compileTokens(\"\");\n\n /** The field's text when the running composition began. */\n #textBeforeComposition = \"\";\n /**\n * The declarations the last format applied; `null` while no format stands — none\n * ran yet, or the running composition withdrew the sink it wrote. A composition owes\n * a format at its end exactly while this differs from what the page declares, so a\n * change taken back owes nothing and a pass that changed nothing cannot discard one.\n */\n #formatted: string | null = null;\n\n /** The derived output last published, kept apart from the IME's unfinished text. */\n #shown: { result: MaskResult | null; empty: boolean } = { result: null, empty: true };\n\n /**\n * Holds mid-composition input so the IME's uncommitted text is never rewritten,\n * and formats the confirmed text once when the composition ends. A composition\n * that ends on the text it began with committed nothing, so a re-format then is\n * the page's: the mask changed while it ran. One that ends without\n * `compositionend` committed nothing either: a re-format the page asked for\n * meanwhile is the page's and runs then; with none held, the field's text waits\n * for whatever formats it next.\n */\n readonly #composition = new CompositionTracker({\n onStart: () => {\n this.#textBeforeComposition = this.element.value;\n },\n onEnd: () => {\n const committedNothing = this.element.value === this.#textBeforeComposition;\n this.#reformat(committedNothing ? \"reconcile\" : \"edit\");\n },\n onAbandon: () => {\n if (this.#formatted !== this.#declared) this.#reformat(\"reconcile\");\n },\n });\n\n /** Re-parses the declaration and re-formats under the tokens it now selects. */\n tokensValueChanged(): void {\n this.#tokens = compileTokens(this.tokensValue);\n if (this.#started) this.#reformat(\"reconcile\");\n }\n\n patternValueChanged(): void {\n if (this.#started) this.#reformat(\"reconcile\");\n }\n\n /** Takes back the raw value it stops maintaining so a submit cannot carry it stale. */\n unmaskToHiddenValueChanged(): void {\n if (!this.#started) return;\n if (!this.unmaskToHiddenValue) this.#withdraw();\n this.#reformat(\"reconcile\");\n }\n\n /**\n * Takes back the raw value of a synchronization that stopped. A sink withdrawn\n * mid-composition is written again only by a format, so the composition's end owes\n * one even when synchronization comes back before it.\n */\n #withdraw(): void {\n if (this.#retractRaw() && this.#composition.isComposing()) this.#formatted = null;\n }\n\n readonly #morph = new MorphRenderWatcher(() => this.#reformat(\"reconcile\"));\n\n /** Tells an in-page move or a Turbo carry from the field leaving the page. */\n readonly #gate = new DetachGate();\n\n /** The second look for a sink that arrives after the field connected. */\n readonly #adoption = new SafeTimeout();\n\n override connect(): void {\n this.#gate.cancel();\n this.#morph.observe(this.element);\n this.#started = true;\n this.#composition.observe(this.element);\n if (!this.unmaskToHiddenValue) this.#withdraw();\n // A field that moved here with its conversion keeps the value it committed; the\n // conversion's end formats it, and a format the move asks for waits for that end.\n if (this.#composition.isComposing()) {\n this.#reformat(\"reconcile\");\n return;\n }\n // Seed the baseline from the DOM so a value that is already masked stays\n // silent, then re-format any server-rendered/restored value.\n this.#lastValue = this.element.value;\n this.#reformat(\"reconcile\");\n // A sink Turbo carries to the next page is put back later in the task that connected\n // the field; look for it once more on the next task.\n if (this.unmaskToHiddenValue && this.#rawSink === null) {\n this.#adoption.set(() => this.#adoptSink(), 0);\n }\n }\n\n override disconnect(): void {\n this.#morph.disconnect();\n this.#started = false;\n this.#composition.disconnect();\n this.#adoption.clearAll();\n // A move, a morph or a Turbo carry connects the field again; a field that does not\n // come back has left the page, including the page a Turbo visit leaves.\n this.#gate.disconnected(this, () => this.#retractRaw());\n }\n\n /** Formats the field on input, preserving the caret. Bound via `data-action`. */\n format(event?: Event): void {\n if (this.#composition.isComposing(event as InputEvent | undefined)) return;\n this.#reformat(\"edit\", deletionOf(event));\n }\n\n /**\n * Core reformat: mask the current value, restore the caret, sync the sink and\n * the state hooks, and report a moved value under the event `cause` selects.\n *\n * @stimeoRenderRoot\n */\n #reformat(cause: Cause, deletion: Deletion | null = null): void {\n // The IME owns the text until it commits; the composition's end re-formats it. A\n // format asked for meanwhile waits for that end, and the outputs show the last\n // format again.\n if (this.#composition.isComposing()) {\n this.#showAgain();\n return;\n }\n this.#formatted = this.#declared;\n const input = this.element;\n const raw = input.value;\n\n // No pattern → act as a pass-through. Without this branch `applyMask` would\n // produce an empty string and blank a misconfigured field's value; the hooks\n // still describe the field so a pattern removed at runtime cannot leave a\n // stale `data-mask-complete` behind, and a raw value written under the pattern\n // is taken back, as nothing maintains it any more.\n if (this.patternValue === \"\") {\n this.#retractRaw();\n this.#shown = { result: null, empty: raw.length === 0 };\n this.#flag(\"data-mask-complete\", false);\n this.#flag(\"data-mask-empty\", raw.length === 0);\n this.#lastValue = raw;\n return;\n }\n\n const caret = input.selectionStart ?? raw.length;\n let anchor = this.#significantBefore(raw.slice(0, caret));\n let result = applyMask(raw, this.patternValue, this.#tokens);\n\n // A deletion that only took literals the mask re-inserts would land back on\n // the value it started from, so that keystroke could never delete anything:\n // drop the significant character it aimed at instead.\n if (deletion !== null && result.masked === this.#lastValue) {\n const target = deletion === \"backward\" ? anchor : anchor + 1;\n const offset = tokenCharOffset(result.tokenFlags, target);\n if (offset >= 0) {\n anchor = target - 1;\n result = applyMask(\n result.masked.slice(0, offset) + result.masked.slice(offset + 1),\n this.patternValue,\n this.#tokens,\n );\n }\n }\n\n // The value of a field the user cannot edit belongs to the page: publish the\n // derived outputs against it, but never rewrite it and never report a move.\n if (input.readOnly || input.disabled || inheritsFieldsetDisabled(input)) {\n this.#publish(result, raw);\n this.#lastValue = raw;\n return;\n }\n\n if (input.value !== result.masked) {\n input.value = result.masked;\n this.#restoreCaret(result.tokenFlags, anchor);\n }\n this.#publish(result, result.masked);\n\n if (result.masked === this.#lastValue) return;\n this.#lastValue = result.masked;\n const detail = { masked: result.masked, unmasked: result.unmasked, complete: result.complete };\n if (cause === \"edit\") this.dispatch(\"change\", { detail });\n else this.dispatch(\"reconcile\", { detail });\n }\n\n /** The declarations a format applies. */\n get #declared(): string {\n return JSON.stringify([this.patternValue, this.tokensValue, this.unmaskToHiddenValue]);\n }\n\n /** Syncs the raw-value sink and the state hooks for the `shown` field text. */\n #publish(result: MaskResult, shown: string): void {\n this.#shown = { result, empty: shown.length === 0 };\n if (this.unmaskToHiddenValue) {\n const sink = this.#resolveSink();\n if (sink !== this.#rawSink) this.#retractRaw();\n if (sink) {\n sink.value = result.unmasked;\n rawWriters.set(sink, this);\n this.#rawSink = sink;\n this.#rawValue = result.unmasked;\n this.#rawSourceForm = this.element.form;\n this.#submission.hold(sink, (value) => this.#submittedValue(sink, value));\n }\n }\n this.#flag(\"data-mask-complete\", result.complete);\n this.#flag(\"data-mask-empty\", shown.length === 0);\n }\n\n /**\n * Restores the committed hooks and the same sink this instance still writes,\n * without reading the composing field or changing which sink it maintains.\n *\n * @stimeoRenderRoot\n */\n #showAgain(): void {\n const shown = this.#shown;\n const sink = this.#rawSink;\n if (\n shown.result !== null &&\n this.unmaskToHiddenValue &&\n sink !== null &&\n rawWriters.get(sink) === this &&\n this.#resolveSink() === sink\n ) {\n sink.value = shown.result.unmasked;\n }\n this.#flag(\"data-mask-complete\", shown.result?.complete ?? false);\n this.#flag(\"data-mask-empty\", shown.empty);\n }\n\n /**\n * Takes back the raw value this instance last wrote: empties that sink, wherever the\n * resolution now points, and stops answering for it. The sink keeps a value another\n * mask has written there since, and a different value the page has written there; an\n * instance that has written none leaves every sink as it is. Answers whether it gave\n * up a sink it still answered for.\n */\n #retractRaw(): boolean {\n const sink = this.#rawSink;\n this.#rawSink = null;\n this.#submission.release();\n if (sink === null || rawWriters.get(sink) !== this) return false;\n rawWriters.delete(sink);\n if (sink.value === this.#rawValue) sink.value = \"\";\n return true;\n }\n\n /** Projects only its own unchanged raw entry; a page or another writer keeps its value. */\n #submittedValue(sink: HTMLInputElement, value: FormDataEntryValue): FormDataEntryValue {\n if (rawWriters.get(sink) !== this || sink.value !== this.#rawValue || value !== this.#rawValue)\n return value;\n const source = this.element;\n const listed = (source.getAttribute(\"data-controller\") ?? \"\")\n .split(/\\s+/)\n .includes(this.identifier);\n const associated =\n source.form === null ? this.#rawSourceForm === null : source.form === sink.form;\n const eligible =\n source.isConnected &&\n this.application.element.contains(source) &&\n listed &&\n this.unmaskToHiddenValue &&\n associated;\n return eligible ? value : \"\";\n }\n\n /** Writes a sink that arrived after the field connected without one. */\n #adoptSink(): void {\n if (this.#resolveSink() !== null) this.#reformat(\"reconcile\");\n }\n\n /**\n * How many token slots the text before the caret fills. Masking that prefix is\n * what makes a rejected character — or a literal that also matches a token —\n * count exactly as the rendering counts it, so the caret cannot drift.\n */\n #significantBefore(prefix: string): number {\n return applyMask(prefix, this.patternValue, this.#tokens).unmasked.length;\n }\n\n /** Places the caret after the `n`-th token char (skipping following literals). */\n #restoreCaret(tokenFlags: readonly boolean[], n: number): void {\n let position: number;\n if (n <= 0) {\n // Sit after any leading literals, before the first token slot.\n let i = 0;\n while (i < tokenFlags.length && !tokenFlags[i]) i += 1;\n position = i;\n } else {\n const offset = tokenCharOffset(tokenFlags, n);\n if (offset < 0) {\n position = tokenFlags.length;\n } else {\n let j = offset + 1;\n while (j < tokenFlags.length && !tokenFlags[j]) j += 1;\n position = j;\n }\n }\n try {\n this.element.setSelectionRange(position, position);\n } catch {\n /* selection unsupported for this input type — value formatting still applies */\n }\n }\n\n /**\n * The hidden raw-value sink for this input, resolved so several masked inputs\n * can coexist in one form:\n *\n * 1. **Explicit pairing** — a sink whose attribute value names this input's\n * `id` (`data-stimeo--input-mask-unmask=\"zip\"`).\n * 2. **Nearest container** — otherwise, walking up from the input, the first\n * *value-less* sink in the closest ancestor. Wrapped input+sink pairs each\n * find their own sink, and a single form-level sink is found the same\n * way. A sink claimed by another input's id is never matched here.\n *\n * Both steps only ever consider the associated form's own controls, so a sink\n * belonging to a different form (or to none) is never written, while one the\n * `form` attribute associates from elsewhere in the document still resolves —\n * for either the input or the sink, neither of which needs to contain or be\n * contained by the form. A form-less input reads the document instead.\n */\n #resolveSink(): HTMLInputElement | null {\n const candidates = this.#sinkCandidates();\n const id = this.element.id;\n if (id.length > 0) {\n const paired = candidates.find((sink) => sink.getAttribute(this.#unmaskAttribute) === id);\n if (paired) return paired;\n }\n\n // A bare (value-less) attribute is unclaimed, so a sink naming another\n // input's id can never be taken by this fallback.\n const free = candidates.filter((sink) => sink.getAttribute(this.#unmaskAttribute) === \"\");\n for (let node = this.element.parentElement; node !== null; node = node.parentElement) {\n const ancestor = node;\n const sink = free.find((candidate) => ancestor.contains(candidate));\n if (sink) return sink;\n }\n return null;\n }\n\n /**\n * Every sink the input's form owns, in document order — one collection read per\n * resolution, so the cost tracks the form's own controls and not the depth of\n * the markup around it. `form.elements` lists controls the `form` attribute\n * associates from anywhere in the document, and lists nothing another form owns.\n */\n #sinkCandidates(): HTMLInputElement[] {\n const form = this.element.form;\n if (form === null) {\n return Array.from(\n document.querySelectorAll<HTMLInputElement>(`input[${this.#unmaskAttribute}]`),\n );\n }\n return Array.from(form.elements).filter(\n (element): element is HTMLInputElement =>\n element instanceof HTMLInputElement && element.hasAttribute(this.#unmaskAttribute),\n );\n }\n\n /** Sets a boolean `data-*` flag to `\"true\"` when `on`, else removes it. */\n #flag(name: string, on: boolean): void {\n if (on) this.element.setAttribute(name, \"true\");\n else this.element.removeAttribute(name);\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/utils/composition_tracker.ts","../../src/utils/declared_value.ts","../../src/utils/detach_gate.ts","../../src/utils/focus_candidate.ts","../../src/utils/shared_registry.ts","../../src/utils/form_data_projection.ts","../../src/utils/half_width.ts","../../src/utils/listener_set.ts","../../src/utils/microtask_coalescer.ts","../../src/utils/morph_render_watcher.ts","../../src/utils/safe_timeout.ts","../../src/controllers/input_mask_controller.ts"],"names":["document"],"mappings":"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","file":"input_mask_controller.js","sourcesContent":["/** Minimal event shape carrying the platform's per-event composition signal. */\nexport interface CompositionSignal {\n readonly isComposing?: boolean;\n}\n\n/** Minimal `input` shape needed to tell a composition echo from a fresh edit. */\nexport interface ConfirmedInputSignal {\n readonly target: EventTarget | null;\n readonly inputType?: string;\n}\n\n/**\n * The `inputType` values an engine reports on an `input` that belongs to a\n * composition rather than to an edit the user just made.\n */\nconst COMPOSITION_INPUT_TYPES = new Set([\n \"insertCompositionText\",\n \"insertFromComposition\",\n \"deleteCompositionText\",\n \"deleteByComposition\",\n]);\n\n/** Whether a platform input kind belongs to composition rather than ordinary editing. */\nexport function isCompositionInputType(inputType: string | undefined): boolean {\n return inputType !== undefined && COMPOSITION_INPUT_TYPES.has(inputType);\n}\n\n/** The cancelable notice Turbo dispatches before a morph writes one attribute. */\nconst BEFORE_MORPH_ATTRIBUTE = \"turbo:before-morph-attribute\";\n\n/** Hooks that keep component-specific work outside {@link CompositionTracker}. */\nexport interface CompositionTrackerOptions {\n /** Runs after lifecycle state is set for a `compositionstart` event. */\n readonly onStart?: (event: Event) => void;\n /** Runs after lifecycle state is cleared for a `compositionend` event. */\n readonly onEnd?: (event: Event) => void;\n /**\n * Runs once lifecycle state is cleared for a composition that ended without\n * `compositionend`, with the `element` it ran in. Nothing was confirmed: the\n * element holds what a write left (`inPlace` false), or still holds the text the\n * composition left, which the engine stopped composing (`inPlace` true) — text\n * typed against whatever the page declared while it ran.\n *\n * A hook applies the work the composition held back — and only that — at once,\n * keeping the caret where it was. The event that showed the end is then handled as\n * it would be with no composition: the text the element holds is read by whatever\n * reads it next, as the user's edit if an edit reads it. A consumer whose contract\n * judges what an interrupted conversion leaves may also settle that text here, as\n * `stimeo--otp` returns a residual its `pattern` rejects to the prior character: it reports\n * that as a reconciliation, never as the user's edit, and keeps the selection.\n *\n * When the tracker sees the end in an event it hears — a key, an edit, a focus\n * change, the next `compositionstart` — the hook runs there, in the capture phase,\n * ahead of that event's own handlers and of the edit it announces, whoever\n * dispatched it. When a question ({@link CompositionTracker.isComposing},\n * {@link CompositionTracker.composingElement}) or a Turbo morph's notice for one\n * attribute shows the end, the hook runs in a microtask after them, never inside\n * its caller or a half-applied morph, unless an event the tracker hears comes\n * first. On `compositionstart` it runs after the new composition is set and before\n * `onStart`: {@link CompositionTracker.isComposing} is true again, so work held for\n * the old composition stays held for the new.\n */\n readonly onAbandon?: (element: EventTarget, inPlace: boolean) => void;\n}\n\n/** One composition in progress. */\ninterface Composition {\n /** The observed target the composition was heard on. */\n readonly target: EventTarget;\n /** The element composing: the observed field itself, or a field inside an observed ancestor. */\n readonly element: EventTarget;\n /** The value the composition last left in the element; `undefined` for an element with none. */\n text: unknown;\n}\n\n/**\n * The element's live value; `undefined` for an element without one, which a write never\n * changes.\n */\nconst textOf = (element: EventTarget): unknown => (element as { value?: unknown }).value;\n\n/** Whether `element` is the focused element of the document or shadow root it is in. */\nconst holdsFocus = (element: EventTarget): boolean =>\n element instanceof Node &&\n (element.getRootNode() as Partial<DocumentOrShadowRoot>).activeElement === element;\n\n/**\n * Owns IME composition listeners and transient state for one or more event targets.\n *\n * Some browsers omit `KeyboardEvent.isComposing` on the keydown that confirms a\n * conversion. Tracking `compositionstart` through `compositionend` supplies the\n * missing lifecycle signal without relying on deprecated numeric key codes.\n * Component policy stays with the consumer: filtering, validation, and submission\n * belong in the optional {@link CompositionTrackerOptions.onEnd} callback.\n *\n * The tracker also owns the other half of that lifecycle: **the `input` some\n * engines send after `compositionend` to echo the text just confirmed.** A\n * consumer that ran `onEnd` and then handled the echo would commit the same\n * confirmation twice, so {@link CompositionTracker.consumesConfirmedInput} tells\n * the echo apart and the consumer returns early on it.\n *\n * **What the echo is not, is the harder half.** Not every engine sends one —\n * Chromium ends a confirmation at `compositionend` with no `input` after it — so\n * a window that waits for the echo and only closes on the next key would swallow\n * the first edit that arrives without one: dictation, autofill, a drop,\n * `insertText`. Two things keep that from happening. The window closes on the\n * next anything — a key, a new composition, any `input`, losing the field — and\n * an `input` that reports an `inputType` outside {@link COMPOSITION_INPUT_TYPES}\n * is an edit, so it is never folded. Only an engine that reports no `inputType`\n * at all is left relying on the window alone.\n *\n * **A composition can also end without `compositionend`.** Chromium drops the\n * conversion silently when something other than the IME writes a different value\n * into its field — a Turbo morph, a form reset, a script — and when the field\n * moves or leaves the page, leaving the text it composed. The tracker reads the end\n * from what the engine and the field still say:\n *\n * - **The field holds other text than the composition left.** The IME's edits, and\n * the user's own between them, arrive as an `input` the engine dispatched\n * (`isTrusted`) or one that reports the composition, and the tracker takes the\n * field's text from those. A write by anything else changes the text with no such\n * `input` — at most a synthetic one — so the composition is over, ended with\n * {@link CompositionTrackerOptions.onAbandon}, before anyone acts on the field.\n * Writing the same text keeps the conversion, and the match.\n * - **The engine edits outside a composition.** A `beforeinput` reporting\n * `isComposing: false` with an `inputType` outside {@link COMPOSITION_INPUT_TYPES}\n * is an edit the engine no longer counts as composition — unless the `keydown`\n * right before it reported `isComposing: true`: a key the IME lets through, such\n * as Backspace, edits the field while the conversion goes on. This is what shows\n * a composition that ended with its text in place. Only one field composes at a\n * time, so such an edit on any observed target ends the composition held, with\n * `onAbandon`. A `keydown` can hold this back but never starts it: the keydown\n * that confirms a conversion may carry no `isComposing`, and dictation and\n * autofill edit with no key at all.\n * - **The field stays where it was heard.** A field the consumer observes itself is\n * the consumer's to release when it moves or leaves, which reads what it holds;\n * one that keeps focus through the move keeps composing\n * ({@link CompositionTracker.disconnect}). A field inside an observed ancestor\n * that leaves the ancestor, or loses focus within its document, ends its\n * composition quietly; one that stays the document's active element — the window\n * lost focus — keeps composing.\n * - **No other field composes**: a `compositionstart` ends any composition the\n * tracker still holds, with `onAbandon`, before the new one starts.\n *\n * The tracker checks this before it answers {@link CompositionTracker.isComposing}\n * or {@link CompositionTracker.composingElement}, and at every key, edit, blur,\n * `compositionstart` and morph it hears — keys, edits and blurs in the capture\n * phase, ahead of the consumer's own handlers. While\n * a composition is live, a Turbo morph leaves its field's value alone: Idiomorph\n * asks through `turbo:before-morph-attribute` before it writes `value`, as an\n * attribute or as the live text, and the tracker cancels that one notice for the\n * element composing, so the morph keeps the conversion, as it keeps focus. Every\n * other attribute, and every other element, still morphs.\n *\n * A consumer that observes several fields tells the conversion's own events from\n * edits in the other fields with {@link CompositionTracker.owns}, which holds even\n * while the tracker keeps a composition whose end no event has shown yet.\n *\n * @example\n * ```ts\n * #composition = new CompositionTracker({ onEnd: () => this.filter() });\n *\n * connect(): void {\n * this.#composition.observe(this.inputTarget);\n * }\n *\n * disconnect(): void {\n * this.#composition.disconnect();\n * }\n *\n * onInput(event: InputEvent): void {\n * if (this.#composition.consumesConfirmedInput(event)) return;\n * if (this.#composition.isComposing(event)) return;\n * }\n *\n * onKeydown(event: KeyboardEvent): void {\n * if (this.#composition.isComposing(event)) return;\n * }\n * ```\n */\nexport class CompositionTracker {\n readonly #observedTargets = new Set<EventTarget>();\n /** The composition in progress, if any: only one field composes at a time. */\n #composition: Composition | null = null;\n readonly #onStart: ((event: Event) => void) | undefined;\n readonly #onEnd: ((event: Event) => void) | undefined;\n readonly #onAbandon: ((element: EventTarget, inPlace: boolean) => void) | undefined;\n /** The field whose confirming `input` is still owed, while the window is open. */\n #confirmedTarget: EventTarget | null = null;\n /**\n * Whether the last `keydown` reported a composition, until the `beforeinput`\n * after it: the edit that key announces lands inside the composition.\n */\n #keyComposing = false;\n /** Whether the consumer is connected: from `observe()` until `disconnect()`. */\n #connected = false;\n /** A composition that ended without `compositionend` and whose `onAbandon` has not run yet. */\n #abandoned: { readonly element: EventTarget; readonly inPlace: boolean } | null = null;\n\n constructor(options: CompositionTrackerOptions = {}) {\n this.#onStart = options.onStart;\n this.#onEnd = options.onEnd;\n this.#onAbandon = options.onAbandon;\n }\n\n /**\n * Starts lifecycle tracking for `target`; repeated calls are idempotent. The\n * targets one tracker observes do not contain one another: a composition event\n * reaching two of them would be counted as two.\n */\n observe(target: EventTarget): void {\n this.#connected = true;\n if (this.#observedTargets.has(target)) return;\n target.addEventListener(\"compositionstart\", this.#handleStart);\n target.addEventListener(\"compositionend\", this.#handleEnd);\n target.addEventListener(\"keydown\", this.#handleKeydown, true);\n target.addEventListener(\"beforeinput\", this.#handleBeforeInput, true);\n target.addEventListener(\"input\", this.#handleInput, true);\n target.addEventListener(\"blur\", this.#handleBlur, true);\n target.addEventListener(BEFORE_MORPH_ATTRIBUTE, this.#handleBeforeMorphAttribute);\n this.#observedTargets.add(target);\n }\n\n /**\n * Stops tracking one target. A composition it held ends quietly, unless its element\n * still holds focus — see {@link CompositionTracker.disconnect}.\n */\n unobserve(target: EventTarget): void {\n if (!this.#observedTargets.delete(target)) return;\n this.#release(target);\n if (this.#composition?.target === target) this.#letGo();\n if (this.#confirmedTarget === target) this.#confirmedTarget = null;\n }\n\n /**\n * Releases every listener and clears state so reconnect starts cleanly.\n *\n * A composition whose element still holds focus is the exception. A move that\n * keeps focus — `moveBefore`, which Turbo's morph uses where the browser provides\n * it — can leave the conversion running, and Stimulus releases the moved target, or\n * the controller around it, and observes it again within the one mutation batch. So\n * the composition waits for its target to be observed again before the turn ends,\n * and goes on; otherwise it ends then — quietly once disconnected, and with\n * {@link CompositionTrackerOptions.onAbandon} for a consumer that only let that one\n * target go. A move without `moveBefore`, or a removal, takes focus away.\n */\n disconnect(): void {\n this.#connected = false;\n for (const target of this.#observedTargets) this.#release(target);\n this.#observedTargets.clear();\n this.#letGo();\n this.#confirmedTarget = null;\n this.#keyComposing = false;\n this.#abandoned = null;\n }\n\n /**\n * True when a composition is live or the current event reports one. A\n * composition that ended without `compositionend` ends here first.\n */\n isComposing(event?: CompositionSignal): boolean {\n this.#settle();\n return event?.isComposing === true || this.#composition !== null;\n }\n\n /**\n * The element a live composition is running in, or `null`. A composition that\n * ended without `compositionend` ends here first.\n */\n composingElement(): EventTarget | null {\n this.#settle();\n return this.#composition?.element ?? null;\n }\n\n /**\n * Whether `event` belongs to a composition: it reports one, or it is in the element a\n * live composition runs in. An event in another element — a key there, or a script's\n * write reported with a bare `input` — is an edit outside the composition, even while\n * the tracker holds one whose end it has not seen: only one element composes at a\n * time, and the IME edits nothing else. The composition held stays, since such an\n * event does not show its end. A consumer that observes several fields asks this of\n * each event; {@link CompositionTracker.isComposing} asks of the targets as a whole.\n * A composition that ended without `compositionend` ends here first.\n */\n owns(event: CompositionSignal & { readonly target: EventTarget | null }): boolean {\n const element = this.composingElement();\n return event.isComposing === true || (element !== null && element === event.target);\n }\n\n /**\n * Whether `event` is the `input` echoing the composition just confirmed.\n *\n * Asking closes the window either way, so one confirmation is folded at most\n * once and a consumer asks once per `input`.\n */\n consumesConfirmedInput(event: ConfirmedInputSignal): boolean {\n const confirmed = this.#confirmedTarget;\n this.#confirmedTarget = null;\n if (confirmed === null || confirmed !== event.target) return false;\n // An engine that reports no kind at all leaves `inputType` empty, and the\n // window is then the only signal there is.\n const inputType = event.inputType;\n return !inputType || isCompositionInputType(inputType);\n }\n\n /** Removes every listener {@link CompositionTracker.observe} added to `target`. */\n #release(target: EventTarget): void {\n target.removeEventListener(\"compositionstart\", this.#handleStart);\n target.removeEventListener(\"compositionend\", this.#handleEnd);\n target.removeEventListener(\"keydown\", this.#handleKeydown, true);\n target.removeEventListener(\"beforeinput\", this.#handleBeforeInput, true);\n target.removeEventListener(\"input\", this.#handleInput, true);\n target.removeEventListener(\"blur\", this.#handleBlur, true);\n target.removeEventListener(BEFORE_MORPH_ATTRIBUTE, this.#handleBeforeMorphAttribute);\n }\n\n /**\n * Lets go of the composition held once its target is no longer observed: it is over\n * unless its element kept focus and its target is observed again this turn.\n */\n #letGo(): void {\n const composition = this.#composition;\n if (composition === null) return;\n if (!holdsFocus(composition.element)) {\n this.#composition = null;\n return;\n }\n queueMicrotask(() => {\n if (this.#composition !== composition || this.#observedTargets.has(composition.target)) {\n return;\n }\n this.#composition = null;\n if (!this.#connected) return;\n this.#abandoned = { element: composition.element, inPlace: true };\n this.#runAbandon();\n });\n }\n\n /**\n * Ends the composition held if its field left the observed target, taking the\n * composition's text along, or holds text a write left.\n */\n #settle(): void {\n const composition = this.#composition;\n if (composition === null) return;\n const { target, element, text } = composition;\n if (target instanceof Node && element instanceof Node && !target.contains(element)) {\n this.#composition = null;\n } else if (textOf(element) !== text) {\n this.#abandon(false);\n }\n }\n\n /**\n * Ends the composition held, unconfirmed. Its `onAbandon` runs in a microtask,\n * unless an event the tracker hears runs it first.\n */\n #abandon(inPlace: boolean): void {\n this.#owe(inPlace);\n this.#composition = null;\n queueMicrotask(this.#runAbandon);\n }\n\n /** Records the `onAbandon` the composition held owes. */\n #owe(inPlace: boolean): void {\n const composition = this.#composition;\n if (composition !== null) this.#abandoned = { element: composition.element, inPlace };\n }\n\n /** Runs the `onAbandon` an ended composition owes, once. */\n readonly #runAbandon = (): void => {\n const abandoned = this.#abandoned;\n if (abandoned === null) return;\n this.#abandoned = null;\n this.#onAbandon?.(abandoned.element, abandoned.inPlace);\n };\n\n /** A composition starting while one is held ends the held one, unconfirmed. */\n readonly #handleStart = (event: Event): void => {\n this.#confirmedTarget = null;\n this.#settle();\n this.#owe(true);\n // A listener runs while the event is dispatched, so both targets are set.\n const target = event.currentTarget as EventTarget;\n const element = event.target as EventTarget;\n this.#composition = { target, element, text: textOf(element) };\n this.#runAbandon();\n this.#onStart?.(event);\n };\n\n readonly #handleEnd = (event: Event): void => {\n this.#runAbandon();\n if (this.#composition?.target === event.currentTarget) this.#composition = null;\n this.#confirmedTarget = event.target;\n this.#onEnd?.(event);\n };\n\n /**\n * A key on an observed field opens an edit of its own, so no echo is owed; it\n * also says whether the edit it announces lands inside a composition.\n */\n readonly #handleKeydown = (event: Event): void => {\n this.#settle();\n this.#runAbandon();\n this.#confirmedTarget = null;\n this.#keyComposing = (event as KeyboardEvent).isComposing === true;\n };\n\n /** An edit is about to land: a composition the engine no longer runs ends first. */\n readonly #handleBeforeInput = (event: Event): void => {\n const keyComposing = this.#keyComposing;\n this.#keyComposing = false;\n this.#settle();\n const { isComposing, inputType } = event as InputEvent;\n if (\n this.#composition !== null &&\n !keyComposing &&\n !isComposing &&\n inputType &&\n !isCompositionInputType(inputType)\n ) {\n this.#abandon(true);\n }\n this.#runAbandon();\n };\n\n /**\n * Takes the composing field's text from an `input` the engine dispatched or one\n * that reports the composition; any other `input` follows a write the\n * composition did not make.\n */\n readonly #handleInput = (event: Event): void => {\n const composition = this.#composition;\n const { isTrusted, isComposing, inputType } = event as InputEvent;\n if (\n composition !== null &&\n event.target === composition.element &&\n (isTrusted || isComposing || isCompositionInputType(inputType))\n ) {\n composition.text = textOf(composition.element);\n } else {\n this.#settle();\n }\n this.#runAbandon();\n };\n\n /**\n * Checks the composition as a field loses focus, ahead of the consumer's own blur\n * handlers. A field inside an observed ancestor that loses focus within its\n * document keeps its text and composes no more.\n */\n readonly #handleBlur = (event: Event): void => {\n this.#settle();\n this.#runAbandon();\n const composition = this.#composition;\n if (composition === null || composition.target === composition.element) return;\n const { element } = composition;\n if (!(element instanceof Node) || event.target !== element) return;\n const root = element.getRootNode() as Document | ShadowRoot;\n if (root.activeElement !== element) this.#composition = null;\n };\n\n /** Keeps a Turbo morph from writing the value of the element composing. */\n readonly #handleBeforeMorphAttribute = (event: Event): void => {\n this.#settle();\n const composition = this.#composition;\n if (composition?.target !== event.currentTarget || event.target !== composition.element) return;\n const detail: { readonly attributeName?: unknown } | null = (event as CustomEvent).detail;\n if (detail?.attributeName === \"value\") event.preventDefault();\n };\n}\n","/**\n * Readers for the String Values whose text can only be checked by handing it to\n * a parser: a CSS selector, a regular-expression source, a JSON object.\n *\n * Stimulus reads each of them as an ordinary string, so the controller connects\n * and the flaw surfaces later, inside the first handler that consumes the value\n * (a `SyntaxError` from `new RegExp`, a `DOMException` from the selector engine),\n * where it takes the whole handler down on every event. Parsing here, once, in\n * the `<name>ValueChanged` callback keeps the failure local to the declaration:\n * an unreadable value falls back to its default, the element stays alive, and\n * the hot path only ever receives a value that parsed.\n *\n * Which default a broken declaration falls back to is the caller's contract, so\n * every reader takes (or returns) the fallback rather than choosing one.\n */\n\n/**\n * The result of `parse(raw)`, or `fallback` when `parse` throws.\n *\n * Any exception counts as \"does not parse\": the parsers these Values feed\n * (`RegExp`, `JSON.parse`, the selector engine) all report a malformed input by\n * throwing, and none of them throws for another reason.\n */\nexport function parseDeclared<T>(raw: string, parse: (raw: string) => T, fallback: T): T {\n try {\n return parse(raw);\n } catch {\n return fallback;\n }\n}\n\n/** What {@link validSelector} needs of the element it probes with. */\nexport interface SelectorProbe {\n matches(selector: string): boolean;\n}\n\n/**\n * `raw` when it is a non-empty selector the DOM accepts, otherwise `fallback`.\n *\n * `element.matches` is the probe: the engine parses the selector and throws for\n * one it cannot read. Whether the selector actually matches `element` plays no\n * part, so a selector aimed at another element still passes.\n *\n * The parameter names the one method the probe uses rather than `Element`, so\n * this module carries no DOM type and the readers below stay importable from\n * the Node-side Inspector, which checks the same declarations statically.\n */\nexport function validSelector(element: SelectorProbe, raw: string, fallback: string): string {\n if (raw.length === 0) return fallback;\n return parseDeclared(\n raw,\n (selector) => {\n element.matches(selector);\n return selector;\n },\n fallback,\n );\n}\n\n/** How `compileRegExp` wraps a source before compiling it. */\nexport type RegExpAnchor = \"none\" | \"exact\";\n\n/**\n * `source` compiled as a `RegExp`, or `null` when it does not compile.\n *\n * `\"exact\"` wraps the source as `^(?:source)$`, so the whole input has to match\n * and an alternation inside the source cannot escape the anchors. The source is\n * compiled on its own first, because an unbalanced source can be made to parse\n * by the wrapper's own parentheses — `0)|(1` becomes `^(?:0)|(1)$` — which would\n * accept a declaration that is not a regular expression and leave half of it\n * outside the anchors. The default that replaces a broken source is the\n * caller's, so `null` is returned rather than a fallback pattern.\n */\nexport function compileRegExp(source: string, anchor: RegExpAnchor = \"none\"): RegExp | null {\n return parseDeclared(\n source,\n (text) => {\n const bare = new RegExp(text);\n return anchor === \"exact\" ? new RegExp(`^(?:${text})$`) : bare;\n },\n null,\n );\n}\n\n/**\n * The JSON object `raw` declares, or `null` when the text does not parse or\n * parses to something other than a plain object (`null`, an array, a scalar).\n * Values are returned as parsed; narrowing them is the caller's contract.\n */\nexport function parseJsonObject(raw: string): Record<string, unknown> | null {\n const parsed = parseDeclared<unknown>(raw, (text) => JSON.parse(text), null);\n if (parsed === null || typeof parsed !== \"object\" || Array.isArray(parsed)) return null;\n return parsed as Record<string, unknown>;\n}\n","/**\n * Shared \"in-page move vs real detach\" discriminator for `disconnect()`.\n *\n * A mid-session `disconnect()` whose element is still in the document is\n * ambiguous. It can be:\n *\n * - an **in-page move** — a consumer (sortable, a teleport) re-inserted the\n * element; Stimulus reconnects the SAME instance in the same mutation batch\n * and the controller's runtime state must survive;\n * - a **detach that keeps the element** — `data-controller` no longer lists the\n * identifier (a Turbo 8 morph), or the element moved outside the observed\n * root (a scoped `Application.start(root)`, a shadow root); no reconnect will\n * ever come and the controller must tear down or it leaks its sessions,\n * document listeners, and moved nodes.\n *\n * Neither available signal decides this alone. A synchronous `data-controller`\n * token check catches the morph case but misses an observed-root exit (the\n * token stays, the reconnect never comes). Deferring teardown one microtask and\n * letting `connect()` cancel it catches the root exit, but leans on Stimulus\n * reconnecting moved elements within the same mutation batch — internal\n * behavior, not a public contract. {@link DetachGate} composes both: the token\n * check is the synchronous fast path (no probe, no reliance on batching), the\n * microtask probe is the fallback for the genuinely ambiguous remainder.\n *\n * An element out of the document is a real detach, with one exception: a\n * `data-turbo-permanent` element, or one inside it, that Turbo carries to the\n * next page. Turbo takes it out of the old `<body>`, which Stimulus answers with\n * a `disconnect()` while it is out of the document, and puts it into the new one\n * a few microtasks later in the same task, where the same instance connects\n * again. Such a disconnect is probed until the next task instead.\n *\n * Policy stays with the consumer; this util owns the discrimination only:\n *\n * - **Probe** ({@link disconnected}) when the teardown must eventually happen —\n * live drag sessions, document listeners, teleported *content*. Call\n * {@link cancel} from `connect()` (a reconnect means in-page move) and from\n * the head of any direct teardown path (see below).\n * - **Keep** (bare {@link DetachGate.isDetached}) when undoing the ambiguous\n * case would fight the controller's own effect: a controller that moves its\n * OWN element (portal's no-`content` form) exits a scoped observed root as\n * its normal job — a probe-driven teardown would restore the element into\n * the root, reconnect, re-teleport, and disconnect again, forever. Such\n * controllers keep their state on an ambiguous disconnect and tear down only\n * on {@link DetachGate.isDetached}.\n *\n * Event convention for probe consumers: teardown into a dead (detached) tree is\n * silent — consumers restore from their own `connect()`; a detach that keeps the\n * tree alive must end an in-flight session with a `cancel` event, or composing\n * consumers strand their session bookkeeping.\n */\n\n/** The slice of a Stimulus controller the gate inspects. */\nexport interface DetachGateHost {\n readonly element: Element;\n readonly identifier: string;\n}\n\n/**\n * Per-controller-instance gate deciding whether a `disconnect()` is a real\n * detach. Hold one per controller (`#gate = new DetachGate()`) — Stimulus\n * reuses the instance across reconnects, so the pending probe state carries\n * over exactly as the protocol needs.\n *\n * @example\n * ```ts\n * #gate = new DetachGate();\n *\n * connect(): void {\n * this.#gate.cancel(); // a reconnect: the element moved in-page\n * // …\n * }\n *\n * disconnect(): void {\n * this.#gate.disconnected(this, () => this.#teardown());\n * }\n *\n * dismiss(): void {\n * this.#gate.cancel(); // a direct path: disarm a probe a disconnect queued\n * this.#teardown();\n * }\n *\n * #teardown(): void {\n * // … (`disconnected` has already disarmed the probe on its own path)\n * }\n * ```\n */\nexport class DetachGate {\n /** Set while a probe is queued, waiting for a reconnect to cancel it. */\n #pending = false;\n\n /**\n * True while a probe is queued — the last disconnect was ambiguous and no\n * reconnect has cancelled it yet. Read it from `connect()` to tell the\n * reconnect half of an in-page move from a first connect: a controller whose\n * initialisation restarts a measurement (a min-duration floor, an elapsed\n * counter) must skip it for the move, where nothing actually restarted.\n */\n get pending(): boolean {\n return this.#pending;\n }\n\n /**\n * True when the disconnect is definitely a real detach — the element left\n * the document, or `data-controller` no longer lists the identifier. False\n * means ambiguous (in-page move or observed-root exit), NOT \"alive\".\n */\n static isDetached(host: DetachGateHost): boolean {\n return !host.element.isConnected || !DetachGate.#listed(host);\n }\n\n /**\n * Call from `disconnect()`: runs `teardown` synchronously on a definite\n * detach (fast path), otherwise defers it one microtask — a reconnect\n * ({@link cancel} from `connect()`) keeps the state, no reconnect runs it.\n * One microtask is the whole probe window: Stimulus reconnects a moved\n * element within the same mutation batch, before the checkpoint drains. An\n * element Turbo carries to the next page is probed until the next task, by\n * which time Turbo has put it back.\n */\n disconnected(host: DetachGateHost, teardown: () => void): void {\n const carried = DetachGate.#carried(host);\n if (!carried && DetachGate.isDetached(host)) {\n // Also disarms a probe a previous ambiguous disconnect left queued\n // (defer, element removed, disconnect again) — exactly one teardown.\n this.#pending = false;\n teardown();\n return;\n }\n this.#pending = true;\n const probe = (): void => {\n if (!this.#pending) return;\n this.#pending = false;\n teardown();\n };\n if (carried) setTimeout(probe, 0);\n else queueMicrotask(probe);\n }\n\n /**\n * Whether `host` is out of the document inside a `data-turbo-permanent` element, its\n * identifier still listed: where Turbo holds an element it carries to the next page.\n */\n static #carried(host: DetachGateHost): boolean {\n const element = host.element;\n return (\n !element.isConnected &&\n element.closest(\"[data-turbo-permanent]\") !== null &&\n DetachGate.#listed(host)\n );\n }\n\n /** Whether the element's `data-controller` still lists the identifier. */\n static #listed(host: DetachGateHost): boolean {\n const tokens = (host.element.getAttribute(\"data-controller\") ?? \"\").split(/\\s+/);\n return tokens.includes(host.identifier);\n }\n\n /**\n * Disarms a pending probe. Call from `connect()` (the reconnect that proves\n * an in-page move) and from the head of any teardown path not routed through\n * {@link disconnected} (disabled-toggle, Escape), so an orphaned probe can\n * never run the teardown a second time.\n */\n cancel(): void {\n this.#pending = false;\n }\n}\n","/**\n * Whether a disabled `<fieldset>` ancestor actually reaches `control`.\n *\n * HTML exempts the contents of a fieldset's **first direct-child `<legend>`**, and\n * the exemption is per fieldset — a control legal in one legend can still be\n * disabled by a second, outer fieldset, so the walk continues upward.\n *\n * Exported on its own for callers that need the disabled-inheritance rule without\n * the rest of {@link canTakeFocus} — a control's availability check, or a `hidden`\n * walk bounded at a widget root, which is a different rule to compose with.\n */\nexport function inheritsFieldsetDisabled(control: HTMLElement): boolean {\n let fieldset: Element | null = control.closest(\"fieldset[disabled]\");\n while (fieldset) {\n const legend = Array.from(fieldset.children).find((child) => child.tagName === \"LEGEND\");\n if (!legend?.contains(control)) return true;\n fieldset = fieldset.parentElement?.closest(\"fieldset[disabled]\") ?? null;\n }\n return false;\n}\n\n/**\n * Whether the engine reports `element` assigned to no slot of the open shadow root its parent\n * hosts: such an element is not in the flat tree, so it and what it holds are rendered nowhere\n * and cannot take focus.\n */\nfunction unslotted(element: Element): boolean {\n return element.assignedSlot === null && element.parentElement?.shadowRoot != null;\n}\n\n/**\n * The parent of `element` in the flat tree: the slot it is assigned to, its parent element,\n * or the host of the shadow root it is a child of; `null` at the top of the document or of a\n * detached tree, and for an element the engine assigns to no slot of the open shadow root its\n * parent hosts, which is outside the flat tree. A closed shadow root does not report its slots,\n * so an element assigned to one climbs to the host.\n */\nexport function flatTreeParent(element: Element): Element | null {\n if (unslotted(element)) return null;\n const parent = element.assignedSlot ?? element.parentNode;\n if (parent instanceof ShadowRoot) return parent.host;\n return parent instanceof Element ? parent : null;\n}\n\n/** Whether `element` is `ancestor` or one of its descendants in the flat tree. */\nexport function flatTreeContains(ancestor: Element, element: Element | null): boolean {\n for (let node = element; node; node = flatTreeParent(node)) {\n if (node === ancestor) return true;\n }\n return false;\n}\n\n/** The nearest of `element` and its flat-tree ancestors that matches `selector`, or `null`. */\nexport function closestInFlatTree(element: Element, selector: string): Element | null {\n for (let node: Element | null = element; node; node = flatTreeParent(node)) {\n if (node.matches(selector)) return node;\n }\n return null;\n}\n\n/**\n * The children of `element` in the flat tree: its open shadow root's children, a slot's\n * assigned elements (its own children when nothing is assigned to it), or its children.\n */\nexport function flatTreeChildren(element: Element): Element[] {\n return ownedScope(element) ?? Array.from(element.children);\n}\n\n/**\n * Whether an element can actually take focus, checked **before** `focus()` runs.\n *\n * A controller that must move focus off a control it is about to disable or hide\n * picks a destination and calls `focus()` on it. If that destination cannot take\n * focus, the call **fails silently**: `hidden` and natively `disabled` elements\n * swallow it, so the caret stays in the subtree that is disappearing and lands on\n * `<body>` a frame later — the exact outcome the rescue exists to prevent, minus\n * any signal that it happened.\n *\n * Testing after the fact is the obvious alternative and is deliberately not the\n * rule. Reading `document.activeElement` back only works in a real browser, and\n * looping over candidates that way performs a real focus move per failure —\n * observable to assistive technology. Checking first costs nothing and catches\n * the cases that actually occur.\n *\n * **`aria-disabled` is not disqualifying.** It is the attribute an author uses\n * for a control that must stay *discoverable*, and the roving contract keeps\n * such items reachable. Only the conditions that make the platform refuse\n * focus are checked: `hidden` / `inert` on the element or a flat-tree ancestor\n * (the host of an open shadow root it sits in, the slot it is assigned to), the\n * element or an ancestor left out of the flat tree (one the engine assigns to no\n * slot of the open shadow root its parent hosts), `input[type=\"hidden\"]`, the\n * native `disabled` property, and `disabled` inherited from an ancestor\n * `fieldset`. CSS-only invisibility is handled by {@link isRenderedForFocus}\n * when a consumer needs sequential-focus semantics.\n *\n * Reading `:disabled` instead of walking the fieldset chain would be shorter, but\n * that pseudo-class is not evaluated consistently outside real browsers and this\n * has to be right headlessly too. happy-dom in particular focuses a `<button>`\n * inside a disabled fieldset where a real engine refuses, so the inheritance is\n * spelled out rather than delegated.\n *\n * What a consumer does when nothing survives is its own call: some fall back to\n * their landmark, while a widget whose caret already sits somewhere legitimate\n * refuses the move outright rather than relocating it.\n *\n * @example\n * ```ts\n * const target = candidates.find(canTakeFocus);\n * if (target) target.focus();\n * else {\n * this.#tabindex.lend(this.element); // nothing left: fall back to the landmark\n * this.element.focus();\n * }\n * ```\n *\n * @param element - the candidate destination\n */\nexport function canTakeFocus(element: HTMLElement): boolean {\n for (let node: Element | null = element; node; node = flatTreeParent(node)) {\n if (unslotted(node) || node.matches(\"[hidden], [inert]\")) return false;\n }\n if (element instanceof HTMLInputElement && element.type === \"hidden\") return false;\n if (!(\"disabled\" in element)) return true;\n if ((element as HTMLElement & { disabled: boolean }).disabled) return false;\n return !inheritsFieldsetDisabled(element);\n}\n\n/** Elements whose semantics or authored attributes can place them in sequential focus order. */\nexport const TAB_STOP_CANDIDATE_SELECTOR = [\n \"a[href]\",\n \"area[href]\",\n \"button\",\n \"input\",\n \"select\",\n \"textarea\",\n \"summary\",\n \"iframe\",\n \"audio[controls]\",\n \"video[controls]\",\n \"[tabindex]\",\n \"[contenteditable]\",\n].join(\",\");\n\n/** Optional browser visibility API used to exclude CSS-hidden candidates. */\ninterface VisibilityCheckable {\n checkVisibility?: (options?: { visibilityProperty?: boolean }) => boolean;\n}\n\n/** Whether CSS visibility allows an otherwise eligible element to participate in focus order. */\nexport function isRenderedForFocus(element: HTMLElement): boolean {\n const check = (element as HTMLElement & VisibilityCheckable).checkVisibility;\n return typeof check === \"function\" ? check.call(element, { visibilityProperty: true }) : true;\n}\n\n/** Parses an authored `tabindex`; invalid syntax has no explicit focus-order meaning. */\nfunction authoredTabindex(element: Element): number | null {\n const value = element.getAttribute(\"tabindex\");\n if (value === null || !/^[+-]?\\d+$/.test(value.trim())) return null;\n return Number(value);\n}\n\n/** Whether the element's native semantics place it in sequential focus order. */\nfunction hasNativeTabStop(element: HTMLElement): boolean {\n if (element instanceof HTMLAnchorElement || element instanceof HTMLAreaElement) {\n return element.hasAttribute(\"href\");\n }\n if (\n element instanceof HTMLButtonElement ||\n element instanceof HTMLSelectElement ||\n element instanceof HTMLTextAreaElement\n ) {\n return true;\n }\n if (element instanceof HTMLInputElement) return element.type !== \"hidden\";\n if (element instanceof HTMLIFrameElement) return true;\n if (element.tagName === \"AUDIO\" || element.tagName === \"VIDEO\") {\n return element.hasAttribute(\"controls\");\n }\n if (element instanceof HTMLElement && element.tagName === \"SUMMARY\") {\n const details = element.parentElement;\n return (\n details instanceof HTMLDetailsElement &&\n Array.from(details.children).find((child) => child.tagName === \"SUMMARY\") === element\n );\n }\n return false;\n}\n\n/** Whether an explicit `contenteditable` value creates an editable tab stop. */\nfunction hasEditableTabStop(element: HTMLElement): boolean {\n const value = element.getAttribute(\"contenteditable\")?.toLowerCase();\n return value === \"\" || value === \"true\" || value === \"plaintext-only\";\n}\n\n/**\n * Whether an element is a usable sequential Tab stop right now.\n *\n * Native semantics, authored `tabindex`, editable hosts, inherited disabled state,\n * HTML `hidden`/`inert`, and CSS visibility are evaluated together. `aria-disabled`\n * remains focusable because it communicates unavailability without removing the\n * control from discovery order.\n */\nexport function isTabStop(element: HTMLElement): boolean {\n if (!canTakeFocus(element) || !isRenderedForFocus(element)) return false;\n\n const tabindex = authoredTabindex(element);\n if (tabindex !== null) return tabindex >= 0;\n return hasNativeTabStop(element) || hasEditableTabStop(element);\n}\n\n/** Returns every usable sequential Tab stop below `root` in document order. */\nexport function tabStopsWithin(root: ParentNode): HTMLElement[] {\n return Array.from(root.querySelectorAll<HTMLElement>(TAB_STOP_CANDIDATE_SELECTOR)).filter(\n isTabStop,\n );\n}\n\n/** Returns the first usable sequential Tab stop below `root`, if one exists. */\nexport function firstTabStop(root: ParentNode): HTMLElement | null {\n for (const candidate of root.querySelectorAll<HTMLElement>(TAB_STOP_CANDIDATE_SELECTOR)) {\n if (isTabStop(candidate)) return candidate;\n }\n return null;\n}\n\n/** Whether `root` contains at least one usable sequential Tab stop. */\nexport function hasTabStop(root: ParentNode): boolean {\n return firstTabStop(root) !== null;\n}\n\n/** An element sequential focus navigation can stop on. */\nexport type TabStopElement = HTMLElement | SVGElement;\n\n/** Whether the element is a radio button. */\nfunction isRadio(element: Element | null): element is HTMLInputElement {\n return element instanceof HTMLInputElement && element.type === \"radio\";\n}\n\n/**\n * Whether two radios form one group: the same non-empty `name`, the same form owner and\n * the same tree. A radio without a name is a group of its own.\n */\nfunction sameRadioGroup(a: HTMLInputElement, b: HTMLInputElement): boolean {\n return (\n a.name !== \"\" && a.name === b.name && a.form === b.form && a.getRootNode() === b.getRootNode()\n );\n}\n\n/**\n * Whether the element is part of editable content: the nearest ancestor that sets a valid\n * `contenteditable` decides. `false` ends the editable region (a non-editable island\n * inside an editor); an invalid value inherits from further up.\n */\nfunction isInEditableContent(element: Element): boolean {\n for (let node = element.parentElement; node; node = node.parentElement) {\n if (node.getAttribute(\"contenteditable\")?.toLowerCase() === \"false\") return false;\n if (hasEditableTabStop(node)) return true;\n }\n return false;\n}\n\n/** The XLink namespace, where an SVG link may carry its `href`. */\nconst XLINK_NAMESPACE = \"http://www.w3.org/1999/xlink\";\n\n/** Whether the element is an SVG link: an SVG `a` with `href` or `xlink:href`. */\nfunction isSvgLink(element: Element): boolean {\n return (\n element instanceof SVGElement &&\n element.localName === \"a\" &&\n (element.hasAttribute(\"href\") || element.hasAttributeNS(XLINK_NAMESPACE, \"href\"))\n );\n}\n\n/**\n * Whether an image-map area has a rendered image: an `img` in the area's tree whose\n * `usemap` names the area's map (by `name`, or by `id` without one) and that is rendered.\n * The area has no box of its own for {@link isRenderedForFocus} to check.\n */\nfunction hasRenderedImage(area: HTMLAreaElement): boolean {\n const map = area.closest(\"map\");\n const key = map?.name || map?.id;\n if (!map || !key) return false;\n const root = map.getRootNode() as Document | ShadowRoot;\n return Array.from(root.querySelectorAll<HTMLElement>(\"img[usemap]\")).some(\n (image) => image.getAttribute(\"usemap\") === `#${key}` && isRenderedForFocus(image),\n );\n}\n\n/** Whether the element can take focus and has no negative `tabindex`. */\nfunction isFocusableInOrder(element: TabStopElement): boolean {\n const tabindex = authoredTabindex(element);\n return (tabindex === null || tabindex >= 0) && canTakeFocus(element as HTMLElement);\n}\n\n/**\n * The engine's verdict for the stops {@link isTabStop} does not judge the way sequential\n * navigation does, or `null` for every other element: an image-map area with an `href` or\n * its own `tabindex` stops while an image that uses its map is rendered; an object stops\n * while it holds a nested document, as an iframe does; an SVG link stops while it is\n * rendered. Each also needs to take focus and not be taken out of the order with a\n * negative `tabindex`.\n */\nfunction extraStop(element: TabStopElement): boolean | null {\n if (element instanceof HTMLAreaElement) {\n const focusable = element.hasAttribute(\"href\") || authoredTabindex(element) !== null;\n return focusable && isFocusableInOrder(element) && hasRenderedImage(element);\n }\n if (element instanceof HTMLObjectElement) {\n return (\n isFocusableInOrder(element) && isRenderedForFocus(element) && element.contentWindow !== null\n );\n }\n if (isSvgLink(element))\n return isFocusableInOrder(element) && isRenderedForFocus(element as HTMLElement);\n return null;\n}\n\n/**\n * Whether sequential navigation stops on the element: a usable Tab stop\n * ({@link isTabStop}), or one of the stops `extraStop` judges, except a link inside\n * editable content without a `tabindex` of its own, which the engine leaves to editing.\n * Other controls inside an editing host, and the host itself, stay stops.\n */\nfunction isSequentialStop(element: TabStopElement): boolean {\n const stop =\n extraStop(element) ??\n (element.matches(TAB_STOP_CANDIDATE_SELECTOR) && isTabStop(element as HTMLElement));\n if (!stop) return false;\n const link =\n element instanceof HTMLAnchorElement ||\n element instanceof HTMLAreaElement ||\n isSvgLink(element);\n return !(link && authoredTabindex(element) === null && isInEditableContent(element));\n}\n\n/**\n * The elements of the focus navigation scope `element` owns: its open shadow root's\n * children, or a slot's assigned elements (its fallback content when nothing is\n * assigned). `null` when it owns no scope. A closed shadow root cannot be read, so its\n * host owns none.\n */\nfunction ownedScope(element: Element): Element[] | null {\n if (element.shadowRoot) return Array.from(element.shadowRoot.children);\n if (element instanceof HTMLSlotElement) {\n const assigned = element.assignedElements();\n return assigned.length > 0 ? assigned : Array.from(element.children);\n }\n return null;\n}\n\n/** One member of a focus navigation scope: its tabindex and the stops it contributes. */\ninterface ScopeEntry {\n readonly tabindex: number;\n readonly stops: TabStopElement[];\n}\n\n/** What a walk of the order records besides the order itself. */\ninterface OrderWalk {\n /** The element a move starts from. */\n readonly from: Element | null;\n /** Every stop, in flat-tree order. */\n readonly tree: TabStopElement[];\n /**\n * How many entries of {@link OrderWalk.tree} come before `from`; `-1` until the walk\n * meets it.\n */\n fromAt: number;\n}\n\n/**\n * Orders one focus navigation scope the way HTML's sequential navigation does: members\n * with a positive `tabindex` first, ascending, ties in tree order, then the rest in tree\n * order. A member that owns a scope (a shadow host, a slot) is followed by that scope's\n * own order; one taken out of the order with a negative `tabindex` takes its scope with it.\n */\nfunction orderScope(elements: Element[], walk: OrderWalk): TabStopElement[] {\n const entries: ScopeEntry[] = [];\n const visit = (element: Element): void => {\n if (element === walk.from) walk.fromAt = walk.tree.length;\n const tabindex = authoredTabindex(element) ?? 0;\n const stop =\n (element instanceof HTMLElement || element instanceof SVGElement) && isSequentialStop(element)\n ? element\n : null;\n if (stop) walk.tree.push(stop);\n const owned = ownedScope(element);\n if (owned) {\n if (tabindex >= 0) {\n entries.push({ tabindex, stops: [...(stop ? [stop] : []), ...orderScope(owned, walk)] });\n }\n return;\n }\n if (stop) entries.push({ tabindex, stops: [stop] });\n for (const child of Array.from(element.children)) visit(child);\n };\n for (const element of elements) visit(element);\n const positive = entries.filter((entry) => entry.tabindex > 0);\n positive.sort((a, b) => a.tabindex - b.tabindex);\n const rest = entries.filter((entry) => entry.tabindex === 0);\n return [...positive, ...rest].flatMap((entry) => entry.stops);\n}\n\n/**\n * Whether a candidate passes the radio-group rule the engine applies: a checked radio\n * among the stops always does; an unchecked one only as the first radio of a group with\n * no checked stop, and not while focus is on a radio of that group. `tree` is the walk's\n * stops in flat-tree order, so a group is counted within the walked root only.\n */\nfunction passesRadioRule(\n element: TabStopElement,\n tree: TabStopElement[],\n focusedRadio: HTMLInputElement | null,\n): boolean {\n if (!isRadio(element) || element.checked) return true;\n const group = tree.filter(\n (stop): stop is HTMLInputElement =>\n stop === element || (isRadio(stop) && sameRadioGroup(stop, element)),\n );\n if (group.some((radio) => radio.checked)) return false;\n if (group[0] !== element) return false;\n return focusedRadio === null || !sameRadioGroup(focusedRadio, element);\n}\n\n/**\n * The index in `order` a move from the walk's `from` visits first. A start the walk did\n * not meet is outside: the move enters at the first stop, or the last going backward. A\n * start that is a stop moves one place along the order. Any other start (the root, an\n * element with a negative `tabindex`) moves to the nearest stop after it in tree order,\n * or before it going backward, and wraps when there is none.\n */\nfunction startIndex(\n order: TabStopElement[],\n walk: OrderWalk,\n backward: boolean,\n isStop: (element: TabStopElement) => boolean,\n): number {\n if (walk.fromAt < 0) return backward ? order.length - 1 : 0;\n const at = order.indexOf(walk.from as TabStopElement);\n if (at >= 0) return backward ? at - 1 : at + 1;\n const neighbour = backward\n ? walk.tree.slice(0, walk.fromAt).reverse().find(isStop)\n : walk.tree.slice(walk.fromAt).find(isStop);\n if (neighbour) return order.indexOf(neighbour);\n return backward ? order.length - 1 : 0;\n}\n\n/**\n * The stops below `root` in the order a sequential move from `from` visits them: the\n * next stop first (the previous one when `backward`), wrapping at the ends, with `from`\n * itself left out. Without a `from` — or with one outside `root` — the list starts at the\n * first stop, or at the last one when `backward`.\n *\n * The order is HTML's sequential navigation order within `root`: positive `tabindex`\n * first, ascending, then `tabindex=\"0\"` and native stops in tree order. Open shadow roots\n * are walked in flat-tree order, slots expanded, each scope ordered on its own; a closed\n * shadow root is not read. A radio group, counted within `root`, stops once: on its\n * checked radio when that radio can take focus, even with focus elsewhere in the group,\n * and otherwise on its first radio that can, unless focus is already in the group.\n * A link inside editable content without its own `tabindex` is not a stop. Every stop\n * also passes its applicable focusability and rendering checks.\n *\n * @param root - The element whose descendants are ordered; it is not a stop itself.\n * @param from - Where the move starts, usually {@link deepActiveElement}.\n * @param backward - Whether the move goes backward (`Shift+Tab`).\n */\nexport function sequentialTabStops(\n root: Element,\n from: Element | null = null,\n backward = false,\n): TabStopElement[] {\n const walk: OrderWalk = { from, tree: [], fromAt: from === root ? 0 : -1 };\n const order = orderScope(ownedScope(root) ?? Array.from(root.children), walk);\n const focusedRadio = walk.fromAt >= 0 && isRadio(from) ? from : null;\n const isStop = (element: TabStopElement): boolean =>\n passesRadioRule(element, walk.tree, focusedRadio);\n const start = startIndex(order, walk, backward, isStop);\n const visited = backward\n ? [...order.slice(0, start + 1).reverse(), ...order.slice(start + 1).reverse()]\n : [...order.slice(start), ...order.slice(0, start)];\n return visited.filter((element) => element !== from && isStop(element));\n}\n\n/**\n * The element that holds focus in `ownerDocument`, looking through open shadow roots:\n * `document.activeElement` stops at a shadow host. A closed shadow root cannot be read,\n * so focus inside one reads as its host.\n */\nexport function deepActiveElement(ownerDocument: Document): Element | null {\n let active = ownerDocument.activeElement;\n while (active?.shadowRoot?.activeElement) active = active.shadowRoot.activeElement;\n return active;\n}\n","/**\n * One registry per page for every copy of the library.\n *\n * Each `stimeo-ui/controllers/*` file, and each opt-in entry, is built on its own and carries\n * its own copy of every utility it uses, so a page that imports controllers one by one, or one\n * of them next to the barrel, evaluates a utility several times. A registry that coordinates\n * instances of different controllers — the stacks of leased values, the loans of a\n * `tabindex`, the Escape layers, the focus traps — must not be split by that: a dialog inside\n * a drawer, or a character counter and a form field on one input, coordinate whichever files\n * they came from. So such a registry lives once on `globalThis`, under a `Symbol.for` key the\n * copies agree on, and the first copy to ask for it creates it.\n *\n * The key names the registry and the version of its shape\n * (`stimeo-ui.<utility>.registry.v<n>`). Copies whose registries have the same shape share\n * one; a release that changes the shape changes the version, so a copy of another release\n * keeps a registry of its own instead of reading entries it does not understand — the two\n * then coordinate within each release only. An entry another copy put in a registry is read\n * through its fields alone, never through `#private` members, which belong to the class of\n * the copy that created it.\n */\n\n/**\n * The registry stored under `key`, created by `create` on first use.\n *\n * @param key - `stimeo-ui.<utility>.registry.v<n>`: the version is that of the shape `create`\n * returns.\n * @param create - Builds the empty registry.\n */\nexport function sharedRegistry<T extends object>(key: string, create: () => T): T {\n const symbol = Symbol.for(key);\n const scope = globalThis as unknown as Record<symbol, T | undefined>;\n const existing = scope[symbol];\n if (existing !== undefined) return existing;\n const registry = create();\n Object.defineProperty(globalThis, symbol, { value: registry });\n return registry;\n}\n","import { sharedRegistry } from \"./shared_registry\";\n\n/** Reserved transport names identify one produced entry without grouping equal public names. */\nconst PREFIX = \"__stimeo_raw_v1_\";\n\ninterface Projection {\n readonly field: HTMLInputElement;\n readonly document: Document;\n readonly state: DocumentProjection;\n readonly key: string;\n readonly name: string;\n readonly project: (value: FormDataEntryValue) => FormDataEntryValue;\n readonly restore: () => void;\n}\n\ninterface DocumentProjection {\n readonly fields: Map<string, Projection>;\n readonly handle: (event: Event) => void;\n readonly byField: WeakMap<HTMLInputElement, Projection>;\n}\n\nconst documents = sharedRegistry(\n \"stimeo-ui.form-data-projection.registry.v1\",\n () => new WeakMap<Document, DocumentProjection>(),\n);\n\n/** The public name encoded by a reserved transport name, or null for an ordinary name. */\nfunction decodedName(key: string): string | null {\n if (!key.startsWith(PREFIX)) return null;\n const separator = PREFIX.length + 32;\n if (key[separator] !== \":\") return null;\n try {\n const name: unknown = JSON.parse(key.slice(separator + 1));\n return typeof name === \"string\" ? name : null;\n } catch {\n return null;\n }\n}\n\n/** Reads a field's logical submission name without exposing its transport identity. */\nexport function projectedFieldName(name: string): string {\n return decodedName(name) ?? name;\n}\n\n/** Installs one capture listener for the document's reserved transport namespace. */\nfunction forDocument(document: Document): DocumentProjection {\n const existing = documents.get(document);\n if (existing) return existing;\n const state: DocumentProjection = {\n fields: new Map(),\n byField: new WeakMap(),\n handle: (event) => {\n const data = (event as FormDataEvent).formData;\n if (!data) return;\n const entries = [...data.entries()];\n let changed = false;\n const projected = entries.map(([key, value]): [string, FormDataEntryValue] => {\n const name = decodedName(key);\n if (name === null) return [key, value];\n changed = true;\n const field = state.fields.get(key);\n const live = field && field.field.name === key && field.field.form === event.target;\n return [name, live ? field.project(value) : \"\"];\n });\n if (!changed) return;\n for (const key of new Set(data.keys())) data.delete(key);\n for (const [name, value] of projected) data.append(name, value);\n },\n };\n documents.set(document, state);\n // @stimeoListenerLifetime document Decodes cached or released transport entries even when no\n // producer remains.\n document.addEventListener(\"formdata\", state.handle, { capture: true });\n return state;\n}\n\n/** Prepares a form's projection event path, including while the form is detached. */\nexport function prepareFormDataProjection(form: HTMLFormElement): void {\n const state = forDocument(form.ownerDocument);\n // @stimeoListenerLifetime element A form keeps transport decoding when detached from its\n // document.\n form.addEventListener(\"formdata\", state.handle, { capture: true });\n}\n\n/**\n * Gives a maintained hidden field a unique transport identity while preserving its\n * value. FormData exposes the public name in the same entry position; the producer\n * decides the submitted value without changing the DOM or releasing ownership.\n */\nexport class FormDataProjection {\n #held: Projection | null = null;\n\n /**\n * Claims this field's current name; a normal name written by the page starts a new claim.\n * A Turbo morph that writes the public name back returns the field to this claim at once.\n */\n hold(field: HTMLInputElement, project: Projection[\"project\"]): void {\n const held = this.#held;\n if (held?.field === field && held.key === field.name && held.document === field.ownerDocument) {\n if (field.form) prepareFormDataProjection(field.form);\n return;\n }\n this.release();\n const name = projectedFieldName(field.name);\n if (name === \"\" || (field.type === \"hidden\" && name.toLowerCase() === \"_charset_\")) return;\n const state = forDocument(field.ownerDocument);\n if (field.form) prepareFormDataProjection(field.form);\n const previous = state.byField.get(field);\n if (previous) {\n state.fields.delete(previous.key);\n field.removeEventListener(\"turbo:morph-element\", previous.restore);\n }\n const nonce = Array.from(crypto.getRandomValues(new Uint8Array(16)), (value) =>\n value.toString(16).padStart(2, \"0\"),\n ).join(\"\");\n const key = `${PREFIX}${nonce}:${JSON.stringify(name)}`;\n const projection: Projection = {\n field,\n key,\n name,\n project,\n document: field.ownerDocument,\n state,\n restore: () => {\n if (field.name === name) field.name = key;\n },\n };\n state.fields.set(key, projection);\n state.byField.set(field, projection);\n this.#held = projection;\n field.name = key;\n // @stimeoListenerLifetime element The current claim keeps its name through a morph; a\n // release or a new claim removes it.\n field.addEventListener(\"turbo:morph-element\", projection.restore);\n }\n\n /** Restores only the name this claim still holds; another writer's name stays untouched. */\n release(): void {\n const held = this.#held;\n this.#held = null;\n if (held === null) return;\n held.field.removeEventListener(\"turbo:morph-element\", held.restore);\n const state = held.state;\n if (state.byField.get(held.field) !== held) return;\n state.fields.delete(held.key);\n state.byField.delete(held.field);\n if (held.field.name === held.key) held.field.name = held.name;\n }\n}\n","/** The distance between a full-width ASCII code point and its half-width twin. */\nconst FULL_WIDTH_SHIFT = 0xfee0;\n\n/**\n * The half-width form of `char`, or `char` itself when it has none.\n *\n * An IME confirming in full-width mode produces U+FF01–U+FF5E (and U+3000 for\n * the space) where the page means ASCII — the digits of a phone number, the\n * letters of a code, the separators between them. Reading those as their ASCII\n * equivalents keeps confirmed text usable instead of discarding it. Characters\n * outside that block, including kana and astral code points, are returned\n * unchanged, so this never rewrites text a consumer meant to keep as typed.\n */\nexport function halfWidthChar(char: string): string {\n if (char >= \"!\" && char <= \"~\") {\n return String.fromCharCode(char.charCodeAt(0) - FULL_WIDTH_SHIFT);\n }\n return char === \" \" ? \" \" : char;\n}\n\n/** Every full-width ASCII character in `text` rewritten by {@link halfWidthChar}. */\nexport function toHalfWidth(text: string): string {\n let out = \"\";\n for (const char of text) out += halfWidthChar(char);\n return out;\n}\n","/** What {@link ListenerSet.add} passes on besides the signal it supplies itself. */\nexport type ListenerOptions = Omit<AddEventListenerOptions, \"signal\">;\n\n/**\n * The event map a target's own `addEventListener` types its handlers with: the\n * window's, a document's, an HTML or SVG element's, any other element's, or a\n * media query list's. Other targets name none.\n */\ntype EventMapOf<T extends EventTarget> = T extends Window\n ? WindowEventMap\n : T extends Document\n ? DocumentEventMap\n : T extends HTMLElement\n ? HTMLElementEventMap\n : T extends SVGElement\n ? SVGElementEventMap\n : T extends Element\n ? ElementEventMap\n : T extends MediaQueryList\n ? MediaQueryListEventMap\n : Record<never, never>;\n\n/**\n * Owns every listener a controller attaches for one connected lifetime and\n * releases them together, so an event name is spelled once and the release\n * cannot drift from the registration.\n *\n * Detaching by hand repeats three things at the release site — the type, the\n * handler reference and the capture flag — and any one of them drifting leaves\n * a listener alive on an element the controller no longer drives. Aborting the\n * signal a registration was created with releases it whatever it was made of,\n * so the release site names nothing.\n *\n * One set per lifetime, held as a field and never rebuilt.\n * {@link ListenerSet.dispose} is the only thing that closes a generation, and it opens\n * the next one in the same step, so the lifecycle hooks can call `add` and\n * `dispose` in any order any number of times, and no `add` ever meets an\n * already aborted signal — a DOM that registers a listener on such a signal\n * would keep it forever. Rebuilding the set instead would leak:\n * `addEventListener` matches an existing registration on target, type, callback\n * and capture alone, so re-adding the same handler while the first registration\n * is live is discarded, and that live registration keeps the signal it was\n * attached with — a set built by a second `connect()` can never release what it\n * appears to own.\n *\n * Scope is the registration only. Timers, observers, leases and the order they\n * are unwound in stay with the controller, which places `dispose()` in its\n * cleanup sequence. A receiver that changes at runtime (a\n * swapped target) is rebound by calling `dispose()` and then `add()` again from\n * the rebind, not by a second set.\n *\n * A lifetime shorter than the connection — while a Value holds, until a first\n * load, while a subscription is open — gets a set of its own, which the end of\n * that lifetime disposes. A controller therefore holds one set per lifetime: the\n * connection's, and one for each swapped receiver or shorter span.\n *\n * The set must own every registration of a tuple it holds: a listener the same\n * target already carries for the same type, callback and capture flag keeps the\n * right to remove it, and this set's `add` for that tuple is discarded. A\n * subscription shared between instances behind a participant count, listeners of\n * several elements that come and go one at a time, and those one gesture holds\n * keep their own pair of calls or a signal of their own.\n *\n * @example\n * ```ts\n * readonly #listeners = new ListenerSet();\n *\n * connect(): void {\n * this.#listeners.add(this.element, \"submit\", this.#onNativeSubmit, { capture: true });\n * this.#listeners.add(this.element, \"turbo:submit-start\", this.#onSubmitStart);\n * this.#listeners.add(this.element, \"turbo:submit-end\", this.#onSubmitEnd);\n * }\n *\n * disconnect(): void {\n * this.#listeners.dispose();\n * }\n * ```\n */\nexport class ListenerSet {\n /** The generation every `add` joins until the next `dispose()`. */\n #abort = new AbortController();\n\n /**\n * Attaches `handler` to the open generation, exactly as the caller spelled it.\n *\n * The set supplies the signal, so `options` carries everything else the DOM\n * accepts — `capture` included. A removal by hand would have to repeat that\n * flag; aborting the signal releases the listener without it.\n *\n * A `type` the target's own event map names types `handler` with that event,\n * as the target's `addEventListener` does, so a handler written for a\n * `KeyboardEvent` is passed as it is; any other `type` takes an\n * `EventListener`.\n */\n add<T extends EventTarget, K extends keyof EventMapOf<T> & string>(\n target: T,\n type: K,\n handler: (event: EventMapOf<T>[K]) => void,\n options?: ListenerOptions,\n ): void;\n add(target: EventTarget, type: string, handler: EventListener, options?: ListenerOptions): void;\n add(target: EventTarget, type: string, handler: EventListener, options?: ListenerOptions): void {\n target.addEventListener(type, handler, { ...options, signal: this.#abort.signal });\n }\n\n /**\n * Releases every listener of the open generation, synchronously, and opens the\n * next one. Idempotent, and safe before anything has been added.\n */\n dispose(): void {\n this.#abort.abort();\n this.#abort = new AbortController();\n }\n}\n","/**\n * Collapses many Stimulus lifecycle callbacks from one DOM mutation into a\n * single pass.\n *\n * Stimulus fires `<name>TargetConnected` / `Disconnected` once per element and\n * `<name>ValueChanged` once per changed attribute. Replacing a list of N options\n * or morphing several render Values therefore delivers N callbacks — but the\n * useful unit of work is \"reconcile against the resulting declarative input\",\n * once, after the batch has settled. Every controller with reconcilable targets\n * or render Values needs the same shape: a `queued` flag plus `queueMicrotask`.\n *\n * **A microtask is the right horizon, and the reason is specific.** Stimulus\n * drives these callbacks from a `MutationObserver`, whose own callback already\n * runs as a microtask with the whole batch in hand; scheduling one more lands\n * after the last sibling callback of that batch and still before paint or any\n * event handler. A timer would be later than it needs to be, and reconciling\n * synchronously would run once per element against a half-applied DOM.\n *\n * **The two guards are not the same guard.** Scheduling is refused before the\n * controller connects, and running is refused after it disconnects:\n *\n * - **Before `connect()`** — Stimulus delivers initial target and Value callbacks\n * ahead of `connect()`. Reconciling there would compute output against a\n * controller whose own state has not been initialised, and `connect()` is\n * about to do a full pass anyway.\n * - **After `disconnect()`** — Stimulus fires a callback for **every** target\n * during teardown, and a microtask queued just before it would otherwise run\n * against a detached tree. {@link MicrotaskCoalescer.cancel} exists for the\n * teardown path to drop the pending pass outright.\n *\n * Both guards are part of one contract here rather than something each consumer\n * has to remember separately.\n *\n * Scope is the scheduling only. *What* to reconcile — keep the surviving active\n * option, fall back to the next / previous / first visible one, rebuild derived\n * chips or hidden fields — stays in the controller, because no two consumers\n * answer it the same way.\n *\n * @example\n * ```ts\n * readonly #reconcile = new MicrotaskCoalescer(() => this.#reconcileOptions());\n *\n * connect() { this.#reconcile.activate(); }\n * disconnect() { this.#reconcile.cancel(); }\n *\n * optionTargetConnected() { this.#reconcile.schedule(); }\n * optionTargetDisconnected() { this.#reconcile.schedule(); }\n * ```\n */\nexport class MicrotaskCoalescer {\n readonly #run: () => void;\n #queued = false;\n #active = false;\n #generation = 0;\n\n /** @param run - the single reconciliation pass, invoked at most once per batch. */\n constructor(run: () => void) {\n this.#run = run;\n }\n\n /** Opens the window in which {@link schedule} is honoured; call from `connect()`. */\n activate(): void {\n this.#active = true;\n }\n\n /** Closes the window and drops any pending pass; call from `disconnect()`. */\n cancel(): void {\n this.#active = false;\n this.#queued = false;\n this.#generation += 1;\n }\n\n /** Whether a requested pass has yet to run. */\n get pending(): boolean {\n return this.#queued;\n }\n\n /** Requests one pass after the batch settles. Idempotent; inert outside the window. */\n schedule(): void {\n if (!this.#active || this.#queued) return;\n this.#queued = true;\n const generation = this.#generation;\n queueMicrotask(() => {\n // A cancelled callback must not consume a pass queued after reconnect.\n if (generation !== this.#generation || !this.#queued || !this.#active) return;\n this.#queued = false;\n this.#run();\n });\n }\n}\n","import { ListenerSet } from \"./listener_set\";\nimport { MicrotaskCoalescer } from \"./microtask_coalescer\";\n\n/**\n * Coalesces retained-element morphs and explicit lifecycle requests into one pass.\n *\n * Observe the controller's element after its initial state is settled. Morphs on\n * that element and bubbling morphs on its descendants share the same microtask\n * as {@link schedule}. The callback repairs derived output; the controller owns\n * any logical transition reporting. {@link disconnect} releases the listener and\n * cancels pending work, including work from an earlier connected lifetime.\n */\nexport class MorphRenderWatcher {\n readonly #listeners = new ListenerSet();\n readonly #pass: MicrotaskCoalescer;\n #element: Element | null = null;\n readonly #onMorph = (): void => this.#pass.schedule();\n\n /** @param run - the single pass that repairs the settled controller's output. */\n constructor(run: () => void) {\n this.#pass = new MicrotaskCoalescer(run);\n }\n\n /** Opens observation; observing the same element again preserves queued work. */\n observe(element: Element): void {\n if (this.#element === element) return;\n this.disconnect();\n this.#element = element;\n this.#pass.activate();\n this.#listeners.add(element, \"turbo:morph-element\", this.#onMorph);\n }\n\n /** Requests one pass; requests outside an observed lifetime are ignored. */\n schedule(): void {\n this.#pass.schedule();\n }\n\n /** Whether a pass requested by a morph or {@link schedule} has yet to run. */\n get pending(): boolean {\n return this.#pass.pending;\n }\n\n /** Releases observation and pending work synchronously; safe to repeat. */\n disconnect(): void {\n this.#listeners.dispose();\n this.#pass.cancel();\n this.#element = null;\n }\n}\n","/**\n * Self-cleaning timer registries shared by Stimeo controllers.\n *\n * Stimulus controllers frequently schedule `setTimeout` / `setInterval` work\n * (auto-dismiss, debouncing, polling). When the element leaves the DOM — a\n * Turbo Drive navigation, a Turbo Stream replacement, or any `disconnect()` —\n * orphaned timers keep firing against a detached controller, leaking memory and\n * mutating stale state. {@link SafeTimeout} and {@link SafeInterval} track every\n * timer they create so a single {@link TimerRegistry.clearAll | clearAll()} call\n * in `disconnect()` tears them all down.\n *\n * These are intentionally low-level primitives: they own *registration and\n * cleanup only*. One pending timer per key is `KeyedTimers`\n * (`keyed_timers.ts`), and a timer a person can hold open, with its remaining-time\n * accounting, is `PausableTimers` (`pausable_timers.ts`).\n */\n\nexport { MAX_TIMER_DELAY_MS } from \"./timer_bounds\";\n\n/**\n * Shared registry bookkeeping for the timeout/interval variants.\n *\n * Subclasses provide the scheduling primitive ({@link schedule}) and its matching\n * canceller ({@link cancel}); this base owns the set of live ids plus the\n * per-id and bulk teardown shared by both.\n */\nabstract class TimerRegistry {\n /** Live timer ids that have not yet been cleared (or, for timeouts, fired). */\n protected readonly ids = new Set<number>();\n\n /** Schedules the underlying platform timer and returns its id. */\n protected abstract schedule(callback: () => void, delay: number): number;\n\n /** Cancels the underlying platform timer for `id`. */\n protected abstract cancel(id: number): void;\n\n /**\n * Cancels a single tracked timer.\n *\n * No-ops if the id is unknown (already cleared, fired, or never owned by this\n * registry), so callers can clear defensively without guarding.\n */\n clear(id: number): void {\n if (this.ids.delete(id)) {\n this.cancel(id);\n }\n }\n\n /**\n * Cancels every tracked timer. Call this from a controller's `disconnect()`\n * to guarantee no timer outlives the element.\n */\n clearAll(): void {\n for (const id of this.ids) {\n this.cancel(id);\n }\n this.ids.clear();\n }\n\n /** Number of timers currently tracked (pending). */\n get size(): number {\n return this.ids.size;\n }\n}\n\n/**\n * `setTimeout` wrapper that auto-forgets each timer once it fires and supports\n * bulk teardown on disconnect.\n *\n * @example\n * ```ts\n * #timers = new SafeTimeout();\n *\n * connect() {\n * this.#timers.set(() => this.dismiss(), 5000);\n * }\n *\n * disconnect() {\n * this.#timers.clearAll();\n * }\n * ```\n */\nexport class SafeTimeout extends TimerRegistry {\n /**\n * Schedules `callback` after `delay` ms and returns the timer id.\n *\n * The id is removed from the registry automatically when the timeout fires,\n * so {@link TimerRegistry.size | size} reflects only still-pending timers.\n */\n set(callback: () => void, delay: number): number {\n const id = this.schedule(() => {\n this.ids.delete(id);\n callback();\n }, delay);\n this.ids.add(id);\n return id;\n }\n\n protected schedule(callback: () => void, delay: number): number {\n return window.setTimeout(callback, delay);\n }\n\n protected cancel(id: number): void {\n window.clearTimeout(id);\n }\n}\n\n/**\n * `setInterval` wrapper that tracks every interval for bulk teardown on\n * disconnect. Unlike {@link SafeTimeout}, intervals are retained until they are\n * explicitly cleared because they fire repeatedly.\n *\n * @example\n * ```ts\n * #intervals = new SafeInterval();\n *\n * connect() {\n * this.#intervals.set(() => this.tick(), 1000);\n * }\n *\n * disconnect() {\n * this.#intervals.clearAll();\n * }\n * ```\n */\nexport class SafeInterval extends TimerRegistry {\n /** Schedules a repeating `callback` every `delay` ms and returns the timer id. */\n set(callback: () => void, delay: number): number {\n const id = this.schedule(callback, delay);\n this.ids.add(id);\n return id;\n }\n\n protected schedule(callback: () => void, delay: number): number {\n return window.setInterval(callback, delay);\n }\n\n protected cancel(id: number): void {\n window.clearInterval(id);\n }\n}\n","import { Controller } from \"@hotwired/stimulus\";\nimport { CompositionTracker } from \"../utils/composition_tracker\";\nimport { compileRegExp, parseJsonObject } from \"../utils/declared_value\";\nimport { DetachGate } from \"../utils/detach_gate\";\nimport { inheritsFieldsetDisabled } from \"../utils/focus_candidate\";\nimport { FormDataProjection, prepareFormDataProjection } from \"../utils/form_data_projection\";\nimport { halfWidthChar } from \"../utils/half_width\";\nimport { ListenerSet } from \"../utils/listener_set\";\nimport { MorphRenderWatcher } from \"../utils/morph_render_watcher\";\nimport { SafeTimeout } from \"../utils/safe_timeout\";\nimport { sharedRegistry } from \"../utils/shared_registry\";\n\n/**\n * Synchronously prepares a new detached form for managed raw-field submission.\n * Registration is idempotent and lasts for this form element in its document;\n * later fields and repeated FormData constructions need no further calls.\n * Connected forms, and forms where `stimeo--input-mask` has synchronized a managed field,\n * are automatic. Call this before constructing FormData for any other detached\n * form, including an existing form that received a managed field only by a DOM\n * move. This observes submission only, without committing edits or releasing\n * raw-field ownership. Discard the form to discard its registration.\n */\nexport function prepareInputMaskForm(form: HTMLFormElement): void {\n prepareFormDataProjection(form);\n}\n\n/** Default placeholder tokens → single-char regex sources (user tokens merge over these). */\nconst DEFAULT_TOKENS: Readonly<Record<string, string>> = {\n \"9\": \"\\\\d\",\n a: \"[A-Za-z]\",\n \"*\": \"[A-Za-z0-9]\",\n};\n\n/**\n * Suffix of the attribute marking the hidden raw-value sink; its value may name the\n * paired input's `id`.\n */\nconst UNMASK_ATTR = \"unmask\";\n\n/**\n * The mask that last wrote each sink's raw value: one map per page for every copy of the\n * library, so masks registered by another Stimulus application, or loaded through both the\n * barrel and the per-controller entry, read the same writer.\n */\nconst rawWriters = sharedRegistry(\n \"stimeo-ui.input-mask-raw-writer.registry.v1\",\n () => new WeakMap<HTMLInputElement, object>(),\n);\n\n/** Which neighbour a deleting keystroke aimed at, when the platform names one. */\ntype Deletion = \"backward\" | \"forward\";\n\n/** Who moved the value: the user editing the field, or the controller re-deciding it. */\ntype Cause = \"edit\" | \"reconcile\";\n\n/** The outcome of applying a fixed mask to a raw string. */\nexport interface MaskResult {\n /** The formatted (masked) string. */\n readonly masked: string;\n /** The significant characters only (literals/separators removed). */\n readonly unmasked: string;\n /** Whether every token slot in the pattern is filled. */\n readonly complete: boolean;\n /** Per-output-char flag: true where the char fills a token (not a literal). */\n readonly tokenFlags: readonly boolean[];\n}\n\n/**\n * The form of `char` this token takes: as typed when the token accepts it, else\n * its half-width form when *that* is what the token accepts, else `null` for a\n * rejected character. Trying the character as typed first keeps a token written\n * for full-width text (or for text an IME leaves as kana) authoritative.\n */\nfunction acceptedForm(regex: RegExp, char: string): string | null {\n if (regex.test(char)) return char;\n const half = halfWidthChar(char);\n return half !== char && regex.test(half) ? half : null;\n}\n\n/**\n * Applies a fixed mask `pattern` to `value` using `tokens` (placeholder → regex).\n * Non-matching input characters are rejected (skipped); literals are auto-inserted\n * and a typed literal that matches is consumed. A character an IME confirmed in\n * full-width form fills a token — or consumes a literal — when its half-width\n * form is the one the mask asked for, and the half-width form is what the output\n * carries. Pure and exported for direct testing.\n */\nexport function applyMask(\n value: string,\n pattern: string,\n tokens: ReadonlyMap<string, RegExp>,\n): MaskResult {\n let masked = \"\";\n let unmasked = \"\";\n const tokenFlags: boolean[] = [];\n let valueIndex = 0;\n let totalTokens = 0;\n\n for (const patternChar of pattern) {\n const regex = tokens.get(patternChar);\n if (regex) totalTokens += 1;\n if (valueIndex >= value.length) continue;\n\n if (regex) {\n // Skip rejected characters until one fills this token (or input runs out).\n let taken: string | null = null;\n while (valueIndex < value.length) {\n taken = acceptedForm(regex, value[valueIndex] ?? \"\");\n if (taken !== null) break;\n valueIndex += 1;\n }\n if (taken === null) continue;\n masked += taken;\n unmasked += taken;\n tokenFlags.push(true);\n valueIndex += 1;\n } else {\n masked += patternChar;\n tokenFlags.push(false);\n // The literal is already in the output, so the typed character only has to\n // be the one that renders it — in either width.\n if (halfWidthChar(value[valueIndex] ?? \"\") === halfWidthChar(patternChar)) valueIndex += 1;\n }\n }\n\n return {\n masked,\n unmasked,\n complete: totalTokens > 0 && unmasked.length === totalTokens,\n tokenFlags,\n };\n}\n\n/**\n * The token map a `tokens` declaration selects: the defaults with the declared\n * single-char regex sources compiled over them.\n *\n * A declaration that is not a JSON object leaves the defaults. Within an object,\n * non-string entries are ignored individually. An invalid regex is answered per key:\n * a key that has a default keeps that default, and a key that has none is left\n * out so the character stays a literal. Either way the rest of the mask, and the\n * field, keep working instead of going down with the broken source.\n */\nfunction compileTokens(declaration: string): Map<string, RegExp> {\n const map = new Map<string, RegExp>();\n for (const [key, source] of Object.entries(DEFAULT_TOKENS)) {\n const compiled = compileRegExp(source, \"exact\");\n if (compiled) map.set(key, compiled);\n }\n for (const [key, source] of Object.entries(parseTokens(declaration))) {\n const compiled = compileRegExp(source, \"exact\");\n // A broken source declares nothing: the default this key may already carry\n // is what it falls back to, and a key without one holds no token at all.\n if (compiled) map.set(key, compiled);\n }\n return map;\n}\n\n/** The `{ token: source }` pairs a declaration holds; anything else reads as none. */\nfunction parseTokens(declaration: string): Record<string, string> {\n const parsed = parseJsonObject(declaration);\n if (parsed === null) return {};\n\n const tokens: Record<string, string> = {};\n for (const [key, source] of Object.entries(parsed)) {\n if (typeof source === \"string\") tokens[key] = source;\n }\n return tokens;\n}\n\n/** The direction an `input` event deleted in, or `null` when it is not a deletion. */\nfunction deletionOf(event: Event | undefined): Deletion | null {\n const inputType = event instanceof InputEvent ? event.inputType : \"\";\n if (!inputType.startsWith(\"delete\")) return null;\n // Word, line, and content deletions all name their direction; the ones that\n // name none (a cut, a drag-out) took a selection, so read them as backward.\n return inputType.endsWith(\"Forward\") ? \"forward\" : \"backward\";\n}\n\n/** The offset of the `n`-th token char in a masked string, or `-1` when there is none. */\nfunction tokenCharOffset(tokenFlags: readonly boolean[], n: number): number {\n let seen = 0;\n for (let i = 0; i < tokenFlags.length; i += 1) {\n if (!tokenFlags[i]) continue;\n seen += 1;\n if (seen === n) return i;\n }\n return -1;\n}\n\n/**\n * Headless **input mask** — formats a field in place against a fixed pattern\n * (`9`=digit, `a`=letter, `*`=alphanumeric, others literal), preserving the caret,\n * rejecting invalid characters, and syncing the raw value to a hidden field. No\n * dedicated APG pattern; `stimeo--currency-input` owns money-specific formatting.\n *\n * Markup contract (identifier: `stimeo--input-mask`, on the `<input>`):\n * <input type=\"text\" inputmode=\"numeric\"\n * data-controller=\"stimeo--input-mask\"\n * data-stimeo--input-mask-pattern-value=\"999-9999\"\n * data-action=\"input->stimeo--input-mask#format\">\n * <input type=\"hidden\" name=\"zip\" data-stimeo--input-mask-unmask>\n *\n * `change` and `reconcile` dispatch `{ masked, unmasked, complete }`.\n *\n * @remarks\n * Behavior only and **idempotent** — a snapshot of the committed derived output\n * repairs the hooks and the maintained sink while the IME owns the field's text.\n * Page-wide registries coordinate the last writer and each sink's form transport\n * identity. `connect()` re-formats the existing value and is stable across Turbo\n * restore/morph. The controller sits on the `<input>` itself (a void element), so\n * the raw-value sink is not a Stimulus target; it is the\n * `[data-stimeo--input-mask-unmask]` field resolved nearest-first among the\n * associated form's own controls (an explicit pairing by the sink attribute's\n * value naming the input's `id` wins; otherwise the closest ancestor's value-less\n * sink) — when one form holds several masked inputs, wrap each input+sink pair in a\n * container or pair them by `id`.\n *\n * A maintained named sink carries a reserved internal DOM name. Native FormData and\n * submission expose its original name and entry position. A removed source contributes\n * an empty raw entry without committing a draft or changing the maintained DOM value.\n * Consumers find the sink by id or pairing attribute rather than its DOM name.\n *\n * Formatting never steals keys: it rejects disallowed characters silently and keeps\n * the caret on insert, Backspace, and range replacement (WCAG 2.2 3.3.2 / 1.3.5;\n * the expected format is the consumer's `aria-describedby`). The caret anchors on\n * the token slots the text before it fills, so a rejected character leaves it\n * exactly where it was. Backspace and Delete always progress: when the keystroke\n * only removed literals the mask re-inserts, the significant character it aimed at\n * goes instead. Events fired mid-IME-composition are ignored; the confirmed text is\n * formatted once on `compositionend`. A `pattern` / `tokens` / `unmaskToHidden`\n * change that arrives mid-composition leaves the uncommitted text alone and applies\n * with that same format: as the user's `change` when the composition committed\n * text, as `reconcile` when it ended on the text it began with.\n *\n * It reflects `data-mask-complete` / `data-mask-empty`, dispatches\n * `stimeo--input-mask:change` only when a user edit moves the committed value, and\n * `stimeo--input-mask:reconcile` when the controller itself decides that value —\n * a server-rendered or restored value normalized on connection, or a re-format\n * after `pattern` / `tokens` / `unmaskToHidden` changed. A malformed `tokens`\n * declaration falls back to the default tokens, and a single broken source falls\n * back to that key's default: it is parsed once in `tokensValueChanged`, and the\n * hot path only ever sees the validated map. A field the user cannot edit\n * (`readonly`, `disabled`, or inside a disabled `<fieldset>`) keeps the value the\n * page authored — the sink and the state hooks still follow it, but the field\n * itself is never rewritten. The composition listener is released on `disconnect()`.\n */\nexport class InputMaskController extends Controller<HTMLInputElement> {\n /** The attribute above, in the namespace this controller is registered under. */\n get #unmaskAttribute(): string {\n return `data-${this.identifier}-${UNMASK_ATTR}`;\n }\n\n static override values = {\n pattern: { type: String, default: \"\" },\n tokens: { type: String, default: \"\" },\n unmaskToHidden: { type: Boolean, default: true },\n };\n static actions = [\"format\"] as const;\n static events = [\"change\", \"reconcile\"] as const;\n\n declare patternValue: string;\n declare tokensValue: string;\n declare unmaskToHiddenValue: boolean;\n\n /** The value this controller last committed, and the baseline both events compare. */\n #lastValue: string | null = null;\n #started = false;\n\n /** The sink this instance last wrote a raw value to and still answers for. */\n #rawSink: HTMLInputElement | null = null;\n /**\n * The raw value written to `#rawSink`, so a different value written there since is left\n * alone.\n */\n #rawValue = \"\";\n #rawSourceForm: HTMLFormElement | null = null;\n readonly #submission = new FormDataProjection();\n\n /** Validated token map; the hot path never parses the `tokens` declaration. */\n #tokens = compileTokens(\"\");\n\n /** The field's text when the running composition began. */\n #textBeforeComposition = \"\";\n /**\n * The declarations the last format applied; `null` while no format stands — none\n * ran yet, or the running composition withdrew the sink it wrote. A composition owes\n * a format at its end exactly while this differs from what the page declares, so a\n * change taken back owes nothing and a pass that changed nothing cannot discard one.\n */\n #formatted: string | null = null;\n\n /** The derived output last published, kept apart from the IME's unfinished text. */\n #shown: { result: MaskResult | null; empty: boolean } = { result: null, empty: true };\n\n readonly #edits = new ListenerSet();\n\n /** Rejects a single invalid typed replacement before native editing deletes its selection. */\n readonly #onBeforeInput = (event: InputEvent): void => {\n const data = event.data;\n if (\n event.defaultPrevented ||\n !event.cancelable ||\n event.inputType !== \"insertText\" ||\n data === null ||\n Array.from(data).length !== 1 ||\n !this.#started ||\n this.#composition.isComposing(event)\n )\n return;\n const input = this.element;\n const start = input.selectionStart;\n const end = input.selectionEnd;\n if (\n start === null ||\n end === null ||\n start === end ||\n input.readOnly ||\n input.disabled ||\n inheritsFieldsetDisabled(input) ||\n !input.isConnected ||\n !this.application.element.contains(input) ||\n !(input.getAttribute(\"data-controller\") ?? \"\").split(/\\s+/).includes(this.identifier) ||\n this.patternValue === \"\" ||\n this.#formatted !== this.#declared ||\n input.value !== this.#lastValue\n )\n return;\n const prefix = applyMask(input.value.slice(0, start), this.patternValue, this.#tokens);\n for (const character of Array.from(this.patternValue).slice(prefix.tokenFlags.length)) {\n const token = this.#tokens.get(character);\n if (token) {\n if (acceptedForm(token, data) === null) event.preventDefault();\n return;\n }\n if (halfWidthChar(data) === halfWidthChar(character)) return;\n }\n event.preventDefault();\n };\n\n /**\n * Holds mid-composition input so the IME's uncommitted text is never rewritten,\n * and formats the confirmed text once when the composition ends. A composition\n * that ends on the text it began with committed nothing, so a re-format then is\n * the page's: the mask changed while it ran. One that ends without\n * `compositionend` committed nothing either: a re-format the page asked for\n * meanwhile is the page's and runs then; with none held, the field's text waits\n * for whatever formats it next.\n */\n readonly #composition = new CompositionTracker({\n onStart: () => {\n this.#textBeforeComposition = this.element.value;\n },\n onEnd: () => {\n const committedNothing = this.element.value === this.#textBeforeComposition;\n this.#reformat(committedNothing ? \"reconcile\" : \"edit\");\n },\n onAbandon: () => {\n if (this.#formatted !== this.#declared) this.#reformat(\"reconcile\");\n },\n });\n\n /** Re-parses the declaration and re-formats under the tokens it now selects. */\n tokensValueChanged(): void {\n this.#tokens = compileTokens(this.tokensValue);\n if (this.#started) this.#reformat(\"reconcile\");\n }\n\n patternValueChanged(): void {\n if (this.#started) this.#reformat(\"reconcile\");\n }\n\n /** Takes back the raw value it stops maintaining so a submit cannot carry it stale. */\n unmaskToHiddenValueChanged(): void {\n if (!this.#started) return;\n if (!this.unmaskToHiddenValue) this.#withdraw();\n this.#reformat(\"reconcile\");\n }\n\n /**\n * Takes back the raw value of a synchronization that stopped. A sink withdrawn\n * mid-composition is written again only by a format, so the composition's end owes\n * one even when synchronization comes back before it.\n */\n #withdraw(): void {\n if (this.#retractRaw() && this.#composition.isComposing()) this.#formatted = null;\n }\n\n readonly #morph = new MorphRenderWatcher(() => this.#reformat(\"reconcile\"));\n\n /** Tells an in-page move or a Turbo carry from the field leaving the page. */\n readonly #gate = new DetachGate();\n\n /** The second look for a sink that arrives after the field connected. */\n readonly #adoption = new SafeTimeout();\n\n override connect(): void {\n this.#gate.cancel();\n this.#morph.observe(this.element);\n this.#started = true;\n this.#composition.observe(this.element);\n this.#edits.add(this.element, \"beforeinput\", this.#onBeforeInput);\n if (!this.unmaskToHiddenValue) this.#withdraw();\n // A field that moved here with its conversion keeps the value it committed; the\n // conversion's end formats it, and a format the move asks for waits for that end.\n if (this.#composition.isComposing()) {\n this.#reformat(\"reconcile\");\n return;\n }\n // Seed the baseline from the DOM so a value that is already masked stays\n // silent, then re-format any server-rendered/restored value.\n this.#lastValue = this.element.value;\n this.#reformat(\"reconcile\");\n // A sink Turbo carries to the next page is put back later in the task that connected\n // the field; look for it once more on the next task.\n if (this.unmaskToHiddenValue && this.#rawSink === null) {\n this.#adoption.set(() => this.#adoptSink(), 0);\n }\n }\n\n override disconnect(): void {\n this.#morph.disconnect();\n this.#started = false;\n this.#edits.dispose();\n this.#composition.disconnect();\n this.#adoption.clearAll();\n // A move, a morph or a Turbo carry connects the field again; a field that does not\n // come back has left the page, including the page a Turbo visit leaves.\n this.#gate.disconnected(this, () => this.#retractRaw());\n }\n\n /** Formats the field on input, preserving the caret. Bound via `data-action`. */\n format(event?: Event): void {\n if (this.#composition.isComposing(event as InputEvent | undefined)) return;\n this.#reformat(\"edit\", deletionOf(event));\n }\n\n /**\n * Core reformat: mask the current value, restore the caret, sync the sink and\n * the state hooks, and report a moved value under the event `cause` selects.\n *\n * @stimeoRenderRoot\n */\n #reformat(cause: Cause, deletion: Deletion | null = null): void {\n // The IME owns the text until it commits; the composition's end re-formats it. A\n // format asked for meanwhile waits for that end, and the outputs show the last\n // format again.\n if (this.#composition.isComposing()) {\n this.#showAgain();\n return;\n }\n this.#formatted = this.#declared;\n const input = this.element;\n const raw = input.value;\n\n // No pattern → act as a pass-through. Without this branch `applyMask` would\n // produce an empty string and blank a misconfigured field's value; the hooks\n // still describe the field so a pattern removed at runtime cannot leave a\n // stale `data-mask-complete` behind, and a raw value written under the pattern\n // is taken back, as nothing maintains it any more.\n if (this.patternValue === \"\") {\n this.#retractRaw();\n this.#shown = { result: null, empty: raw.length === 0 };\n this.#flag(\"data-mask-complete\", false);\n this.#flag(\"data-mask-empty\", raw.length === 0);\n this.#lastValue = raw;\n return;\n }\n\n const caret = input.selectionStart ?? raw.length;\n let anchor = this.#significantBefore(raw.slice(0, caret));\n let result = applyMask(raw, this.patternValue, this.#tokens);\n\n // A deletion that only took literals the mask re-inserts would land back on\n // the value it started from, so that keystroke could never delete anything:\n // drop the significant character it aimed at instead.\n if (deletion !== null && result.masked === this.#lastValue) {\n const target = deletion === \"backward\" ? anchor : anchor + 1;\n const offset = tokenCharOffset(result.tokenFlags, target);\n if (offset >= 0) {\n anchor = target - 1;\n result = applyMask(\n result.masked.slice(0, offset) + result.masked.slice(offset + 1),\n this.patternValue,\n this.#tokens,\n );\n }\n }\n\n // The value of a field the user cannot edit belongs to the page: publish the\n // derived outputs against it, but never rewrite it and never report a move.\n if (input.readOnly || input.disabled || inheritsFieldsetDisabled(input)) {\n this.#publish(result, raw);\n this.#lastValue = raw;\n return;\n }\n\n if (input.value !== result.masked) {\n input.value = result.masked;\n this.#restoreCaret(result.tokenFlags, anchor);\n }\n this.#publish(result, result.masked);\n\n if (result.masked === this.#lastValue) return;\n this.#lastValue = result.masked;\n const detail = { masked: result.masked, unmasked: result.unmasked, complete: result.complete };\n if (cause === \"edit\") this.dispatch(\"change\", { detail });\n else this.dispatch(\"reconcile\", { detail });\n }\n\n /** The declarations a format applies. */\n get #declared(): string {\n return JSON.stringify([this.patternValue, this.tokensValue, this.unmaskToHiddenValue]);\n }\n\n /** Syncs the raw-value sink and the state hooks for the `shown` field text. */\n #publish(result: MaskResult, shown: string): void {\n this.#shown = { result, empty: shown.length === 0 };\n if (this.unmaskToHiddenValue) {\n const sink = this.#resolveSink();\n if (sink !== this.#rawSink) this.#retractRaw();\n if (sink) {\n sink.value = result.unmasked;\n rawWriters.set(sink, this);\n this.#rawSink = sink;\n this.#rawValue = result.unmasked;\n this.#rawSourceForm = this.element.form;\n this.#submission.hold(sink, (value) => this.#submittedValue(sink, value));\n }\n }\n this.#flag(\"data-mask-complete\", result.complete);\n this.#flag(\"data-mask-empty\", shown.length === 0);\n }\n\n /**\n * Restores the committed hooks and the same sink this instance still writes,\n * without reading the composing field or changing which sink it maintains.\n *\n * @stimeoRenderRoot\n */\n #showAgain(): void {\n const shown = this.#shown;\n const sink = this.#rawSink;\n if (\n shown.result !== null &&\n this.unmaskToHiddenValue &&\n sink !== null &&\n rawWriters.get(sink) === this &&\n this.#resolveSink() === sink\n ) {\n sink.value = shown.result.unmasked;\n }\n this.#flag(\"data-mask-complete\", shown.result?.complete ?? false);\n this.#flag(\"data-mask-empty\", shown.empty);\n }\n\n /**\n * Takes back the raw value this instance last wrote: empties that sink, wherever the\n * resolution now points, and stops answering for it. The sink keeps a value another\n * mask has written there since, and a different value the page has written there; an\n * instance that has written none leaves every sink as it is. Answers whether it gave\n * up a sink it still answered for.\n */\n #retractRaw(): boolean {\n const sink = this.#rawSink;\n this.#rawSink = null;\n this.#submission.release();\n if (sink === null || rawWriters.get(sink) !== this) return false;\n rawWriters.delete(sink);\n if (sink.value === this.#rawValue) sink.value = \"\";\n return true;\n }\n\n /** Projects only its own unchanged raw entry; a page or another writer keeps its value. */\n #submittedValue(sink: HTMLInputElement, value: FormDataEntryValue): FormDataEntryValue {\n if (rawWriters.get(sink) !== this || sink.value !== this.#rawValue || value !== this.#rawValue)\n return value;\n const source = this.element;\n const listed = (source.getAttribute(\"data-controller\") ?? \"\")\n .split(/\\s+/)\n .includes(this.identifier);\n const associated =\n source.form === null ? this.#rawSourceForm === null : source.form === sink.form;\n const eligible =\n source.isConnected &&\n this.application.element.contains(source) &&\n listed &&\n this.unmaskToHiddenValue &&\n associated;\n return eligible ? value : \"\";\n }\n\n /** Writes a sink that arrived after the field connected without one. */\n #adoptSink(): void {\n if (this.#resolveSink() !== null) this.#reformat(\"reconcile\");\n }\n\n /**\n * How many token slots the text before the caret fills. Masking that prefix is\n * what makes a rejected character — or a literal that also matches a token —\n * count exactly as the rendering counts it, so the caret cannot drift.\n */\n #significantBefore(prefix: string): number {\n return applyMask(prefix, this.patternValue, this.#tokens).unmasked.length;\n }\n\n /** Places the caret after the `n`-th token char (skipping following literals). */\n #restoreCaret(tokenFlags: readonly boolean[], n: number): void {\n let position: number;\n if (n <= 0) {\n // Sit after any leading literals, before the first token slot.\n let i = 0;\n while (i < tokenFlags.length && !tokenFlags[i]) i += 1;\n position = i;\n } else {\n const offset = tokenCharOffset(tokenFlags, n);\n if (offset < 0) {\n position = tokenFlags.length;\n } else {\n let j = offset + 1;\n while (j < tokenFlags.length && !tokenFlags[j]) j += 1;\n position = j;\n }\n }\n try {\n this.element.setSelectionRange(position, position);\n } catch {\n /* selection unsupported for this input type — value formatting still applies */\n }\n }\n\n /**\n * The hidden raw-value sink for this input, resolved so several masked inputs\n * can coexist in one form:\n *\n * 1. **Explicit pairing** — a sink whose attribute value names this input's\n * `id` (`data-stimeo--input-mask-unmask=\"zip\"`).\n * 2. **Nearest container** — otherwise, walking up from the input, the first\n * *value-less* sink in the closest ancestor. Wrapped input+sink pairs each\n * find their own sink, and a single form-level sink is found the same\n * way. A sink claimed by another input's id is never matched here.\n *\n * Both steps only ever consider the associated form's own controls, so a sink\n * belonging to a different form (or to none) is never written, while one the\n * `form` attribute associates from elsewhere in the document still resolves —\n * for either the input or the sink, neither of which needs to contain or be\n * contained by the form. A form-less input reads the document instead.\n */\n #resolveSink(): HTMLInputElement | null {\n const candidates = this.#sinkCandidates();\n const id = this.element.id;\n if (id.length > 0) {\n const paired = candidates.find((sink) => sink.getAttribute(this.#unmaskAttribute) === id);\n if (paired) return paired;\n }\n\n // A bare (value-less) attribute is unclaimed, so a sink naming another\n // input's id can never be taken by this fallback.\n const free = candidates.filter((sink) => sink.getAttribute(this.#unmaskAttribute) === \"\");\n for (let node = this.element.parentElement; node !== null; node = node.parentElement) {\n const ancestor = node;\n const sink = free.find((candidate) => ancestor.contains(candidate));\n if (sink) return sink;\n }\n return null;\n }\n\n /**\n * Every sink the input's form owns, in document order — one collection read per\n * resolution, so the cost tracks the form's own controls and not the depth of\n * the markup around it. `form.elements` lists controls the `form` attribute\n * associates from anywhere in the document, and lists nothing another form owns.\n */\n #sinkCandidates(): HTMLInputElement[] {\n const form = this.element.form;\n if (form === null) {\n return Array.from(\n document.querySelectorAll<HTMLInputElement>(`input[${this.#unmaskAttribute}]`),\n );\n }\n return Array.from(form.elements).filter(\n (element): element is HTMLInputElement =>\n element instanceof HTMLInputElement && element.hasAttribute(this.#unmaskAttribute),\n );\n }\n\n /** Sets a boolean `data-*` flag to `\"true\"` when `on`, else removes it. */\n #flag(name: string, on: boolean): void {\n if (on) this.element.setAttribute(name, \"true\");\n else this.element.removeAttribute(name);\n }\n}\n"]}
|