diff --git a/docs/clipboard-semantics.md b/docs/clipboard-semantics.md
index 8a5bfbf05..d709acd09 100644
--- a/docs/clipboard-semantics.md
+++ b/docs/clipboard-semantics.md
@@ -30,6 +30,8 @@ Already ~true because markers are real text nodes; this plan closes the exceptio
- **An opaque construct copies out on ONE line:** the walk's ordinary rule puts a `\n` at every non-inline element boundary it crosses, which would spread a single construct over several lines — a sidebar's nested `\p`, and every row and cell of a table, are real block-level nodes. Pasting that text replays each `\n` as a paragraph split, and Tier 2 re-tokenizes strictly one paragraph at a time, so the `\esb`/`\esbe` and `\tr`/`\th`/`\tc` assembly the fragment tokenizer already implements never saw the whole construct in one pass. `$startsBlockLine` (`whitespaceDisplay.plugin.utils.ts`) therefore suppresses the line break for a block boundary with an opaque ancestor ABOVE it — the construct's own outermost node still starts its own line, so a table stays separated from the prose around it. USFM markers self-delimit, so the one-line form is valid USFM of the same document; it is simply not the line-per-marker layout a USFM writer emits, which is the deliberate trade for a paste that survives. Pinned by the corpus sweep's now-unskipped `"sidebar (esb)"` and `"table with header and cells"` fixtures and by `unknownClipboardFidelity.test.tsx`. A `\periph` peripheral division needs the rule just as much, for the same reason and with nothing periph-specific about it: the division marker carries no closing bytes at all, so what ends its attribute run and what opens its content are the SAME thing — the next marker — and both spellings tokenize identically (pinned in `usfmFragmentToUsj.test.ts`). It is the paste's line splitting, not the byte layout, that decides whether one re-tokenization pass ever sees the division together with the blocks inside it; measured both ways and pinned in `unknownClipboardFidelity.test.tsx`. Tables needed one more thing on the paste side: `usfm.sty` classifies `\th1`/`\tc1`/… as CHARACTER styles, so with a real project stylesheet a cell marker reached the fragment tokenizer as a char token and its table-cell assembly — which only ran for paragraph tokens — never saw it, turning every cell into a loose `char` span inside an empty row. ParatextData derives a cell from the marker NAME, so the assembly now recognizes a cell marker on either token kind, guarded on an open `\tr` row. Pinned sheet-backed (`createMarkerLookup(defaultStyleInfo)`, where the cell markers really are Character) in `usfmFragmentToUsj.test.ts`'s `"cell markers under a real stylesheet"` describe, including the align-infix names (`\thc3` → center, `\thr5` → end) and both guards — a cell-named span with no open row stays an ordinary char span, and a nested `\+tc1` is never a cell.
+- **A copy limit is fitted to what the copy writes:** when the host sets `EditorOptions.copyLimit` (its TSDoc holds the contract), one CRITICAL handler in `CopyLimitPlugin` measures each copy or cut the way the view writes it: Standard-view USFM (`$selectionToUsfmText`), Markers-view USFM (`$selectionToUsfmViaStandardView`, which rebuilds the document as Standard view once per editor state), or elsewhere the selected text plus every note caller's preview text. A copy or cut that fits is a normal one, except that Standard view writes no `application/x-lexical-editor` flavor while a limit is set: a caller's `previewText` carries its whole note. One over the limit has its selection shortened (`$limitSelectionLength`) until the measure fits. The start is kept; the end never splits a grapheme cluster, even across text nodes; and atomic text (token-mode, glyph and chapter-line text), a note or an opaque construct (a table, a figure, a sidebar) that the selection runs into is kept whole or left out, unless the selection starts strictly inside it. What stays selected is what is copied, and a read-only editor moves the page selection to match. When nothing fits, the copy or cut is blocked, writing and removing nothing, as at a limit of 0; a node selection that does not fit is blocked the same way. `$writeCopyPayload` keeps only a backstop: a payload that still overshoots is written as the start of its plain text, with a development warning, and removes nothing. Pinned in `Editor.test.tsx` (`options.copyLimit`), `CopyLimitPlugin.test.tsx` and `copyLimit.utils.test.tsx`.
+
### S2. Paste (Standard view, internal) — same-namespace `application/x-lexical-editor` payload reconstructs the exact node tree (existing)
**Fixed gap (2026-08-13, extended to every kind 2026-09-10), lexical-JSON flavor only:** the "exact node tree" promise did not hold for a selection containing an `UnknownNode` (figure, sidebar, periph, ref, optbreak). `@lexical/clipboard`'s JSON generator (`$appendNodesToJSON`, the function behind the `application/x-lexical-editor` flavor) computes node exclusion from `currentNode.excludeFromCopy('html')` — the literal string `'html'`, hardcoded for EVERY copy-out destination `$appendNodesToJSON` itself handles, not only actual HTML generation (`'clone'` is never passed by any Lexical-shipped code path in the installed version). This change touches ONLY that flavor, and doubly so: Standard view's `text/html` is not a DOM export at all (S1 — it renders the copy walker's own USFM), and even where Lexical's exporter does run, `$appendNodesToHTML` (`@lexical/html`) computes the same `excludeFromCopy('html')` value but returns early on `UnknownNode.exportDOM()`'s unconditional `{element: null}` BEFORE ever consulting it.
@@ -105,13 +107,17 @@ Under S3 this is semantically identical to a normal external paste in Standard v
- **Copy-out is prose** (no marker text) — existing, gets a gate test protecting Standard-view handlers from leaking there. Their `text/html` is Lexical's own `$generateHtmlFromNodes` export, unchanged by this work, so it drops every `UnknownNode` and `ImmutableNoteCallerNode` subtree — the one-set-of-bytes guarantee (S1) is Standard view's alone.
+- **With a copy limit, an over-limit copy from these views is plain text of what they show:** when the host sets `EditorOptions.copyLimit`, a copy or cut here that fits, counting every note caller's preview text (which Lexical's `text/html` and `application/x-lexical-editor` flavors carry), is Lexical's normal copy. One over the limit, like a Markers-view range that cannot be mapped, is fitted to and written as the plain text the view shows (`$selectionShownText`): a collapsed note's text is left out and does not count. A limited cut removes the range only when that text carries all of it, and never when the range holds a verse or chapter marker, a note or another construct. Pinned in `CopyLimitPlugin.test.tsx` and `Editor.test.tsx`.
+
+Unformatted view is not a hidden-marker view, but it has no view-specific clipboard claim either, so the same rules apply to its copies and cuts.
+
- **The paste gate** (swallow anything containing `\` **or `/`**) is over-broad — it eats URLs, dates, "and/or" (live-confirmed 2026-08-07). **Pre-existing upstream behavior** (`CommandMenuPlugin` on `origin/main`, predates the standard-view branches) — per TJ, NOT fixed here; recorded in the semantics doc's deferred list. Structural apply-markers-on-paste in formatted views likewise out of scope.
### S6. Cut = copy + `removeText()` (existing)
Including the empty case: a cut with nothing selected writes nothing and removes nothing, by the same rule and the same two enforcement layers as copy (see S1's "Copying nothing leaves the clipboard untouched").
-A structure-protected document's cut of a selection `StructureKeyboardPlugin` refuses to replace (a range spanning a paragraph boundary, or containing a verse marker) is declined to that plugin, which refuses it whole — nothing is copied and nothing removed — the same one-owner rule the paste claim follows (S3). Pinned in `whitespaceDisplay.plugin.utils.test.tsx` (`structure protection`).
+A structure-protected document's cut of a selection `StructureKeyboardPlugin` refuses to replace (a range spanning a paragraph boundary, or containing a verse marker) is declined to that plugin, which refuses it whole — nothing is copied and nothing removed — the same one-owner rule the paste claim follows (S3). Pinned in `whitespaceDisplay.plugin.utils.test.tsx` (`structure protection`). With a copy limit, the selection is fitted before that plugin judges it, so a cut it would refuse whole can go ahead once shortened to within one paragraph (pinned in `Editor.test.tsx`).
The WI-2 filed quirk — a selection-delete ending exactly at a just-settled char-span boundary absorbing one adjacent character — gets a targeted regression pin. **Outcome (2026-08-07):** does NOT reproduce at the Lexical selection level. Pinned as regression armor for both `CUT_COMMAND` and a plain `removeText()`, each given a selection whose focus is an element point mirroring DOM `range.setEndAfter(spanElement)` — the exact boundary shape the live repro's programmatic DOM selection used. Both variants produced byte-exact, correctly-scoped results, narrowing the live repro's root cause to the DOM Range → Lexical selection-resolution layer (`applyDOMRange`/`$internalResolveSelectionPoints`), which is not exercised by a Lexical-level `RangeSelection` and so remains covered only by the E2E selection steps.
diff --git a/libs/shared-react/src/plugins/usj/OpaqueBlockGuardPlugin.tsx b/libs/shared-react/src/plugins/usj/OpaqueBlockGuardPlugin.tsx
index 62174cd5b..54c5b6c52 100644
--- a/libs/shared-react/src/plugins/usj/OpaqueBlockGuardPlugin.tsx
+++ b/libs/shared-react/src/plugins/usj/OpaqueBlockGuardPlugin.tsx
@@ -177,12 +177,18 @@ export function isEditingKey(event: KeyboardEvent): boolean {
* lands, this predicate is where it comes back out.
*/
export function $opaqueBlockAncestor(node: LexicalNode): LexicalNode | undefined {
- return (
- $findMatchingParent(
- node,
- (current) => $isUnknownNode(current) || $isImmutableTableNode(current),
- ) ?? undefined
- );
+ return $findMatchingParent(node, $isOpaqueBlockNode) ?? undefined;
+}
+
+/**
+ * Read-only: safe inside `editor.getEditorState().read()`, an `editor.update()`, or a command
+ * handler.
+ *
+ * Whether `node` itself is an opaque construct — the test {@link $opaqueBlockAncestor} applies at
+ * each ancestor, for a caller that already walks the ancestors itself.
+ */
+export function $isOpaqueBlockNode(node: LexicalNode | null | undefined): boolean {
+ return $isUnknownNode(node) || $isImmutableTableNode(node);
}
/**
diff --git a/packages/platform/CHANGELOG.md b/packages/platform/CHANGELOG.md
index 48a9b43b3..850aaf0c1 100644
--- a/packages/platform/CHANGELOG.md
+++ b/packages/platform/CHANGELOG.md
@@ -26,6 +26,16 @@ refused. The public surface grew substantially; nothing was removed.
- `defaultStyleInfo` and the `StyleInfo` / `MarkerStyleInfo` / `StyleType` types.
- `EditorOptions.styleInfo`, `EditorOptions.markerSettleDelayMs`,
`ViewOptions.showParaMarkerPrefixes`, and the caller/separator fields on `UsjNodeOptions`.
+- `EditorOptions.copyLimit`: the most text one copy or cut may take, in UTF-16 code units
+ (fractions round down, negative or `NaN` is `0`, `undefined` is no limit). A copy that fits is a
+ normal copy. One over the limit is shortened silently, keeping its start and never splitting a
+ character, marker, note or construct, so it may take less than the limit or nothing; Standard
+ and Markers views fit the USFM they copy, and other views write plain text of what they show. A
+ limited cut removes only what it copied, except that in Standard view a cut ending inside a
+ character style also removes the style's closing marker, as any Standard-view cut does. The
+ Select All shortcut is blocked, except in other text fields, and `0` blocks copy and cut.
+- Dependency on `unicode-segmenter` (`^0.17.3`), which keeps a shortened copy on grapheme cluster
+ boundaries whatever the runtime's own segmenter does. It is imported, not bundled.
- **Ctrl+Space removes character formatting from the selection.** On macOS this is ⌃Space rather than
⌘Space, which is Spotlight. It can collide with the macOS input-source switcher and with some IME
on/off toggles; the handler declines while a composition is active.
diff --git a/packages/platform/dist/index.d.ts b/packages/platform/dist/index.d.ts
index f73b7303d..23238867f 100644
--- a/packages/platform/dist/index.d.ts
+++ b/packages/platform/dist/index.d.ts
@@ -291,6 +291,23 @@ export declare interface EditorOptions {
* Falls back to the bundled default stylesheet data when absent.
*/
styleInfo?: StyleInfo;
+ /**
+ * Most text one copy or cut may take, in UTF-16 code units (`String.length`). Fractions round
+ * down; negative or `NaN` means `0`; `undefined` means no limit.
+ *
+ * While set:
+ * - A copy or cut that fits the limit is a normal copy or cut, except that Standard view leaves
+ * out the editor's own `application/x-lexical-editor` clipboard format.
+ * - One over the limit is shortened silently. It keeps the selection's start and never splits a
+ * character, marker, note or construct (a table, figure or sidebar), so it may take less than
+ * the limit, or nothing. Standard view and the Markers view fit the USFM they copy; other views
+ * write plain text of what they show.
+ * - A limited cut removes only what it copied, except that in Standard view a cut ending inside a
+ * character style also removes the style's closing marker, as any Standard-view cut does.
+ * - The Select All shortcut is blocked, except in other text fields.
+ * - `0` blocks copy and cut.
+ */
+ copyLimit?: number;
/**
* EXPERIMENTAL: Delay in milliseconds before pending marker edits settle in place while the
* user is idle (Paratext 9's debounced-reformat cadence), in editable marker modes. Defaults
diff --git a/packages/platform/dist/index.js b/packages/platform/dist/index.js
index aac3a371a..a423b5353 100644
--- a/packages/platform/dist/index.js
+++ b/packages/platform/dist/index.js
@@ -1,34 +1,35 @@
-import { jsx as C, jsxs as Te, Fragment as gn } from "react/jsx-runtime";
-import { forwardRef as En, useState as de, useRef as Z, useCallback as he, useEffect as z, useMemo as ze, memo as ey, createContext as gf, useContext as mf, Children as ty, isValidElement as ry, cloneElement as ny, useImperativeHandle as gc, useLayoutEffect as cs } from "react";
-import { assertSafeKey as He, isValidBookCode as iy, MARKER_OBJECT_PROPS as sy, USJ_VERSION as br, USJ_TYPE as kr, isUsjTextContentLocation as oy, indexesFromUsjJsonPath as yf, isUsjAttributeKeyLocation as ay, isUsjAttributeMarkerLocation as cy, isUsjClosingAttributeMarkerLocation as ly, isUsjMarkerLocation as uy, isUsjClosingMarkerLocation as dy, isUsjPropertyValueLocation as fy, getUsjDocumentLocationTypeName as py, usjJsonPathFromIndexes as on, EMPTY_USJ as bf } from "@eten-tech-foundation/scripture-utilities";
-import { $applyNodeReplacement as je, $parseSerializedNode as ao, DecoratorNode as ls, ElementNode as Zt, isHTMLElement as An, createState as co, $getState as ne, $setState as bt, $isRangeSelection as N, $isElementNode as F, $isTextNode as v, $getSelection as w, $isNodeSelection as mc, ParagraphNode as yc, TextNode as Ke, $createTextNode as pe, $getCommonAncestor as hy, $isLineBreakNode as us, NODE_STATE_KEY as ds, $getEditor as Zn, $hasUpdateTag as gy, $getNodeByKey as se, $getRoot as Fe, $createRangeSelection as bc, $createPoint as su, $getCharacterOffsets as kc, KEY_DOWN_COMMAND as vr, COMMAND_PRIORITY_HIGH as Ne, HISTORY_MERGE_TAG as kf, CLICK_COMMAND as lo, COMMAND_PRIORITY_EDITOR as mn, isDOMNode as Tf, $getNearestNodeFromDOMNode as li, CONTROLLED_TEXT_INSERTION_COMMAND as Tc, PASTE_COMMAND as yr, COMMAND_PRIORITY_CRITICAL as Wt, CUT_COMMAND as yn, DROP_COMMAND as xc, DELETE_CHARACTER_COMMAND as my, DELETE_WORD_COMMAND as yy, DELETE_LINE_COMMAND as by, $isDecoratorNode as uo, COPY_COMMAND as fo, COMMAND_PRIORITY_LOW as Tt, COMMAND_PRIORITY_NORMAL as Jn, SELECTION_CHANGE_COMMAND as ur, getDOMSelection as ky, isSelectionWithinEditor as Ty, $createRangeSelectionFromDom as xy, $setSelection as ei, isDOMTextNode as _y, BLUR_COMMAND as _c, $addUpdateTag as jr, SKIP_DOM_SELECTION_TAG as Cy, CLEAR_HISTORY_COMMAND as Sy, $getPreviousSelection as vy, $isRootOrShadowRoot as My, CAN_UNDO_COMMAND as Ey, CAN_REDO_COMMAND as Ay, DRAGSTART_COMMAND as Py, $createNodeSelection as xf, getDOMSelectionFromTarget as Ny, $onUpdate as Oy, KEY_ENTER_COMMAND as _f, LineBreakNode as Cf, $copyNode as wy, FOCUS_COMMAND as qy, $isRootNode as Ry, KEY_ESCAPE_COMMAND as Sf, INSERT_PARAGRAPH_COMMAND as Rs, createCommand as vf, HISTORIC_TAG as Cc, createEditor as $y, UNDO_COMMAND as Mf, REDO_COMMAND as Ef, CLEAR_EDITOR_COMMAND as Iy } from "lexical";
-import { addClassNamesToElement as Un, removeClassNamesFromElement as Ho, $findMatchingParent as nt, $dfsIterator as Af, $dfs as ui, mergeRegister as Ve, registerNestedElementResolver as Pf, $unwrapNode as _a, IS_APPLE as $s } from "@lexical/utils";
-import { useLexicalNodeSelection as Ly } from "@lexical/react/useLexicalNodeSelection";
-import { deepEqual as wt } from "fast-equals";
-import qi from "quill-delta";
-import { useLexicalComposerContext as ce } from "@lexical/react/LexicalComposerContext";
-import { $getLexicalContent as Dy, copyToClipboard as Uy } from "@lexical/clipboard";
-import { TreeView as Fy } from "@lexical/react/LexicalTreeView";
-import * as zy from "react-dom";
-import { createPortal as hn } from "react-dom";
-import { LexicalComposer as Nf } from "@lexical/react/LexicalComposer";
-import { ContentEditable as Of } from "@lexical/react/LexicalContentEditable";
-import { EditorRefPlugin as wf } from "@lexical/react/LexicalEditorRefPlugin";
-import { LexicalErrorBoundary as qf } from "@lexical/react/LexicalErrorBoundary";
-import { HistoryPlugin as Rf } from "@lexical/react/LexicalHistoryPlugin";
-import { RichTextPlugin as Ky } from "@lexical/react/LexicalRichTextPlugin";
-import { $setBlocksType as jy, createDOMRange as By, createRectsFromDOMRange as Vy } from "@lexical/selection";
-import { autoUpdate as Wy, computePosition as Hy, shift as Gy, flip as Jy } from "@floating-ui/dom";
-import { $generateNodesFromDOM as Yy } from "@lexical/html";
-import { AutoFocusPlugin as Xy } from "@lexical/react/LexicalAutoFocusPlugin";
-import { ClearEditorPlugin as Qy } from "@lexical/react/LexicalClearEditorPlugin";
-import { useCollaborationContext as $f, LexicalCollaboration as Zy } from "@lexical/react/LexicalCollaborationContext";
-import { OnChangePlugin as eb } from "@lexical/react/LexicalOnChangePlugin";
-import { PlainTextPlugin as tb } from "@lexical/react/LexicalPlainTextPlugin";
-import { $rootTextContent as rb, $isRootTextContentEmpty as nb } from "@lexical/text";
-import { TOGGLE_CONNECT_COMMAND as ib } from "@lexical/yjs";
-import { Array as ou, Map as au, YArrayEvent as sb } from "yjs";
-const Go = (e) => je(ao(e)), ob = {
+import { jsx as S, jsxs as Te, Fragment as yn } from "react/jsx-runtime";
+import { forwardRef as An, useState as de, useRef as Z, useCallback as me, useEffect as F, useMemo as Ke, memo as yy, createContext as Pf, useContext as Nf, Children as by, isValidElement as ky, cloneElement as Ty, useImperativeHandle as Sc, useLayoutEffect as ps } from "react";
+import { assertSafeKey as Je, isValidBookCode as xy, MARKER_OBJECT_PROPS as _y, USJ_VERSION as _r, USJ_TYPE as Cr, isUsjTextContentLocation as Cy, indexesFromUsjJsonPath as Of, isUsjAttributeKeyLocation as vy, isUsjAttributeMarkerLocation as Sy, isUsjClosingAttributeMarkerLocation as My, isUsjMarkerLocation as Ey, isUsjClosingMarkerLocation as Ay, isUsjPropertyValueLocation as Py, getUsjDocumentLocationTypeName as Ny, usjJsonPathFromIndexes as cn, EMPTY_USJ as wf } from "@eten-tech-foundation/scripture-utilities";
+import { $applyNodeReplacement as Ve, $parseSerializedNode as po, DecoratorNode as hs, ElementNode as Qt, isHTMLElement as Pn, createState as ho, $getState as ne, $setState as kt, $isRangeSelection as N, $isElementNode as L, $isTextNode as M, $getSelection as w, $isNodeSelection as go, ParagraphNode as Mc, TextNode as je, $createTextNode as ge, $getCommonAncestor as Oy, $isLineBreakNode as Nn, NODE_STATE_KEY as gs, $getEditor as ri, $hasUpdateTag as wy, $getNodeByKey as se, $getRoot as ze, $createRangeSelection as Ec, $createPoint as Ma, $getCharacterOffsets as Ac, KEY_DOWN_COMMAND as Nr, COMMAND_PRIORITY_HIGH as Oe, HISTORY_MERGE_TAG as qf, CLICK_COMMAND as mo, COMMAND_PRIORITY_EDITOR as bn, isDOMNode as Rf, $getNearestNodeFromDOMNode as hi, CONTROLLED_TEXT_INSERTION_COMMAND as Pc, PASTE_COMMAND as xr, COMMAND_PRIORITY_CRITICAL as xt, CUT_COMMAND as vr, DROP_COMMAND as Nc, DELETE_CHARACTER_COMMAND as qy, DELETE_WORD_COMMAND as Ry, DELETE_LINE_COMMAND as $y, $isDecoratorNode as On, COPY_COMMAND as ni, COMMAND_PRIORITY_LOW as _t, COMMAND_PRIORITY_NORMAL as Vr, SELECTION_CHANGE_COMMAND as ur, getDOMSelection as Ly, isSelectionWithinEditor as $f, $createRangeSelectionFromDom as Iy, $setSelection as ii, isDOMTextNode as Dy, BLUR_COMMAND as Oc, $addUpdateTag as Wr, SKIP_DOM_SELECTION_TAG as Uy, CLEAR_HISTORY_COMMAND as Fy, $getPreviousSelection as zy, $isRootOrShadowRoot as Ky, CAN_UNDO_COMMAND as jy, CAN_REDO_COMMAND as By, DRAGSTART_COMMAND as Vy, $createNodeSelection as Lf, getDOMSelectionFromTarget as Wy, $onUpdate as If, KEY_ENTER_COMMAND as Df, LineBreakNode as Uf, $copyNode as Hy, FOCUS_COMMAND as Gy, INSERT_PARAGRAPH_COMMAND as Us, createEditor as Jy, SELECT_ALL_COMMAND as Yy, isExactShortcutMatch as Xy, getDOMTextNode as Qy, $isRootNode as Zy, KEY_ESCAPE_COMMAND as Ff, createCommand as zf, HISTORIC_TAG as wc, UNDO_COMMAND as Kf, REDO_COMMAND as jf, CLEAR_EDITOR_COMMAND as eb } from "lexical";
+import { addClassNamesToElement as jn, removeClassNamesFromElement as Yo, $findMatchingParent as Xe, $dfsIterator as Bf, $dfs as wn, mergeRegister as Be, registerNestedElementResolver as Vf, $unwrapNode as Ea, IS_APPLE as si } from "@lexical/utils";
+import { useLexicalNodeSelection as tb } from "@lexical/react/useLexicalNodeSelection";
+import { deepEqual as qt } from "fast-equals";
+import Di from "quill-delta";
+import { useLexicalComposerContext as ae } from "@lexical/react/LexicalComposerContext";
+import { graphemeSegments as rb } from "unicode-segmenter/grapheme";
+import { copyToClipboard as nb, $getLexicalContent as ib } from "@lexical/clipboard";
+import { TreeView as sb } from "@lexical/react/LexicalTreeView";
+import * as ob from "react-dom";
+import { createPortal as mn } from "react-dom";
+import { LexicalComposer as Wf } from "@lexical/react/LexicalComposer";
+import { ContentEditable as Hf } from "@lexical/react/LexicalContentEditable";
+import { EditorRefPlugin as Gf } from "@lexical/react/LexicalEditorRefPlugin";
+import { LexicalErrorBoundary as Jf } from "@lexical/react/LexicalErrorBoundary";
+import { HistoryPlugin as Yf } from "@lexical/react/LexicalHistoryPlugin";
+import { RichTextPlugin as ab } from "@lexical/react/LexicalRichTextPlugin";
+import { $setBlocksType as cb, createDOMRange as lb, createRectsFromDOMRange as ub } from "@lexical/selection";
+import { autoUpdate as db, computePosition as fb, shift as pb, flip as hb } from "@floating-ui/dom";
+import { $generateNodesFromDOM as gb } from "@lexical/html";
+import { AutoFocusPlugin as mb } from "@lexical/react/LexicalAutoFocusPlugin";
+import { ClearEditorPlugin as yb } from "@lexical/react/LexicalClearEditorPlugin";
+import { useCollaborationContext as Xf, LexicalCollaboration as bb } from "@lexical/react/LexicalCollaborationContext";
+import { OnChangePlugin as kb } from "@lexical/react/LexicalOnChangePlugin";
+import { PlainTextPlugin as Tb } from "@lexical/react/LexicalPlainTextPlugin";
+import { $rootTextContent as xb, $isRootTextContentEmpty as _b } from "@lexical/text";
+import { TOGGLE_CONNECT_COMMAND as Cb } from "@lexical/yjs";
+import { Array as bu, Map as ku, YArrayEvent as vb } from "yjs";
+const Xo = (e) => Ve(po(e)), Sb = {
c: ["number"],
ef: ["caller"],
efe: ["caller"],
@@ -39,16 +40,16 @@ const Go = (e) => je(ao(e)), ob = {
v: ["number"],
x: ["caller"]
};
-function If(e) {
- return ob[e];
-}
-const I = " ", Is = "", Dt = I, Sc = `${I}|`, cr = "p", Bi = "+", Lf = "-", Ls = "chapter", Ca = "verse", cu = "invalid", ab = "text-spacing", cb = "formatted-font", lb = "marker-", Df = "external-usj-mutation", Uf = "selection-change", Br = "cursor-change", Sa = "annotation-change", Vi = "delta-change", Ff = "marker-settle", ub = [
- Df,
- Uf,
- Br,
- Sa,
- Vi
-], bn = "zmsc-s", Yn = "zmsc-e", db = [bn, Yn], fb = [
+function Qf(e) {
+ return Sb[e];
+}
+const R = " ", Fs = "", Ut = R, qc = `${R}|`, cr = "p", Ji = "+", Zf = "-", zs = "chapter", Aa = "verse", Tu = "invalid", Mb = "text-spacing", Eb = "formatted-font", Ab = "marker-", ep = "external-usj-mutation", tp = "selection-change", Hr = "cursor-change", Pa = "annotation-change", Yi = "delta-change", rp = "marker-settle", Pb = [
+ ep,
+ tp,
+ Hr,
+ Pa,
+ Yi
+], kn = "zmsc-s", Zn = "zmsc-e", Nb = [kn, Zn], Ob = [
"ts-s",
"ts-e",
"t-s",
@@ -67,16 +68,16 @@ const I = " ", Is = "", Dt = I, Sc = `${I}|`, cr = "p", Bi = "+", Lf = "-",
"qt-s",
"qt-e",
// custom markers used for annotations
- bn,
- Yn
-], zf = 1, vc = [
+ kn,
+ Zn
+], np = 1, Rc = [
"type",
"marker",
"sid",
"eid",
"content"
-], pb = vc.filter((e) => e !== "sid" && e !== "eid");
-class Jt extends ls {
+], wb = Rc.filter((e) => e !== "sid" && e !== "eid");
+class Gt extends hs {
__marker;
__sid;
__eid;
@@ -95,13 +96,13 @@ class Jt extends ls {
}
static clone(t) {
const { __marker: r, __sid: n, __eid: i, __unknownAttributes: s, __attributeOrder: o, __key: a } = t;
- return new Jt(r, n, i, s, a, o);
+ return new Gt(r, n, i, s, a, o);
}
static importJSON(t) {
- return jf().updateFromJSON(t);
+ return sp().updateFromJSON(t);
}
static isValidMarker(t, r) {
- return t !== void 0 && (fb.includes(t) || t.startsWith("z") || (r?.includes(t) ?? !1));
+ return t !== void 0 && (Ob.includes(t) || t.startsWith("z") || (r?.includes(t) ?? !1));
}
updateFromJSON(t) {
return super.updateFromJSON(t).setMarker(t.marker).setSid(t.sid).setEid(t.eid).setUnknownAttributes(t.unknownAttributes).setAttributeOrder(t.attributeOrder);
@@ -185,7 +186,7 @@ class Jt extends ls {
eid: this.getEid(),
unknownAttributes: this.getUnknownAttributes(),
attributeOrder: this.getAttributeOrder(),
- version: zf
+ version: np
};
}
// Mutation
@@ -193,18 +194,18 @@ class Jt extends ls {
return !1;
}
}
-function Kf(e) {
- return db.includes(e);
+function ip(e) {
+ return Nb.includes(e);
}
-function jf(e, t, r, n, i) {
- return je(new Jt(e, t, r, n, void 0, i));
+function sp(e, t, r, n, i) {
+ return Ve(new Gt(e, t, r, n, void 0, i));
}
-function Be(e) {
- return e instanceof Jt;
+function He(e) {
+ return e instanceof Gt;
}
-const Mc = "f", hb = [
+const $c = "f", qb = [
// Footnote
- Mc,
+ $c,
"fe",
"ef",
"efe",
@@ -212,45 +213,45 @@ const Mc = "f", hb = [
"x",
"ex"
];
-function Ri(e) {
+function Ui(e) {
return e.startsWith("f") || e.startsWith("ef") ? "footnote" : "crossref";
}
-const gb = [
+const Rb = [
"type",
"marker",
"caller",
"category",
"content"
-], Bf = 1;
-class Ee extends Zt {
+], op = 1;
+class Ae extends Qt {
__marker;
__caller;
__isCollapsed;
__category;
__unknownAttributes;
- constructor(t = Mc, r, n = !0, i, s, o) {
- super(o), this.__marker = t, this.__caller = r ?? (Ri(t) === "crossref" ? Lf : Bi), this.__isCollapsed = n, this.__category = i, this.__unknownAttributes = s;
+ constructor(t = $c, r, n = !0, i, s, o) {
+ super(o), this.__marker = t, this.__caller = r ?? (Ui(t) === "crossref" ? Zf : Ji), this.__isCollapsed = n, this.__category = i, this.__unknownAttributes = s;
}
static getType() {
return "note";
}
static clone(t) {
const { __marker: r, __caller: n, __isCollapsed: i, __category: s, __unknownAttributes: o, __key: a } = t;
- return new Ee(r, n, i, s, o, a);
+ return new Ae(r, n, i, s, o, a);
}
static importDOM() {
return {
- span: (t) => yb(t) ? {
- conversion: mb,
+ span: (t) => Lb(t) ? {
+ conversion: $b,
priority: 1
} : null
};
}
static importJSON(t) {
- return Ec().updateFromJSON(t);
+ return Lc().updateFromJSON(t);
}
static isValidMarker(t, r) {
- return t !== void 0 && (hb.includes(t) || (r?.includes(t) ?? !1));
+ return t !== void 0 && (qb.includes(t) || (r?.includes(t) ?? !1));
}
updateFromJSON(t) {
return super.updateFromJSON(t).setMarker(t.marker).setCaller(t.caller).setIsCollapsed(t.isCollapsed).setCategory(t.category).setUnknownAttributes(t.unknownAttributes);
@@ -304,14 +305,14 @@ class Ee extends Zt {
}
createDOM() {
const t = document.createElement("span");
- return t.setAttribute("data-marker", this.__marker), t.classList.add(this.__type, `usfm_${this.__marker}`, this.__isCollapsed ? "collapsed" : "expanded"), t.setAttribute("data-caller", this.__caller), t.setAttribute("data-note-kind", Ri(this.__marker)), t;
+ return t.setAttribute("data-marker", this.__marker), t.classList.add(this.__type, `usfm_${this.__marker}`, this.__isCollapsed ? "collapsed" : "expanded"), t.setAttribute("data-caller", this.__caller), t.setAttribute("data-note-kind", Ui(this.__marker)), t;
}
updateDOM(t, r) {
- return t.__isCollapsed !== this.__isCollapsed ? !0 : (t.__marker !== this.__marker && (r.setAttribute("data-marker", this.__marker), r.classList.remove(`usfm_${t.__marker}`), r.classList.add(`usfm_${this.__marker}`), r.setAttribute("data-note-kind", Ri(this.__marker))), t.__caller !== this.__caller && r.setAttribute("data-caller", this.__caller), !1);
+ return t.__isCollapsed !== this.__isCollapsed ? !0 : (t.__marker !== this.__marker && (r.setAttribute("data-marker", this.__marker), r.classList.remove(`usfm_${t.__marker}`), r.classList.add(`usfm_${this.__marker}`), r.setAttribute("data-note-kind", Ui(this.__marker))), t.__caller !== this.__caller && r.setAttribute("data-caller", this.__caller), !1);
}
exportDOM(t) {
const { element: r } = super.exportDOM(t);
- return r && An(r) && (r.setAttribute("data-marker", this.getMarker()), r.classList.add(this.getType(), `usfm_${this.getMarker()}`, this.getIsCollapsed() ? "collapsed" : "expanded"), r.setAttribute("data-caller", this.getCaller()), r.setAttribute("data-note-kind", Ri(this.getMarker()))), { element: r };
+ return r && Pn(r) && (r.setAttribute("data-marker", this.getMarker()), r.classList.add(this.getType(), `usfm_${this.getMarker()}`, this.getIsCollapsed() ? "collapsed" : "expanded"), r.setAttribute("data-caller", this.getCaller()), r.setAttribute("data-note-kind", Ui(this.getMarker()))), { element: r };
}
exportJSON() {
return {
@@ -322,7 +323,7 @@ class Ee extends Zt {
isCollapsed: this.getIsCollapsed(),
category: this.getCategory(),
unknownAttributes: this.getUnknownAttributes(),
- version: Bf
+ version: op
};
}
// Mutation
@@ -333,34 +334,34 @@ class Ee extends Zt {
return !0;
}
}
-function mb(e) {
+function $b(e) {
const t = e.getAttribute("data-marker") ?? "f", r = e.getAttribute("data-caller") ?? "", n = e.classList.contains("collapsed");
- return { node: Ec(t, r, n) };
+ return { node: Lc(t, r, n) };
}
-function Ec(e, t, r, n, i) {
- return je(new Ee(e, t, r, n, i));
+function Lc(e, t, r, n, i) {
+ return Ve(new Ae(e, t, r, n, i));
}
-function yb(e) {
+function Lb(e) {
if (!e)
return !1;
const t = e.getAttribute("data-marker") ?? "";
- return Ee.isValidMarker(t) && e.classList.contains(Ee.getType());
+ return Ae.isValidMarker(t) && e.classList.contains(Ae.getType());
}
-function j(e) {
- return e instanceof Ee;
+function z(e) {
+ return e instanceof Ae;
}
var T;
(function(e) {
e.FileIdentification = "FileIdentification", e.Headers = "Headers", e.Remarks = "Remarks", e.Introduction = "Introduction", e.DivisionMarks = "DivisionMarks", e.Paragraphs = "Paragraphs", e.Poetry = "Poetry", e.TitlesHeadings = "TitlesHeadings", e.Tables = "Tables", e.CenterTables = "CenterTables", e.RightTables = "RightTables", e.Lists = "Lists", e.Footnotes = "Footnotes", e.CrossReferences = "CrossReferences", e.SpecialText = "SpecialText", e.CharacterStyling = "CharacterStyling", e.Breaks = "Breaks", e.SpecialFeatures = "SpecialFeatures", e.PeripheralReferences = "PeripheralReferences", e.PeripheralMaterials = "PeripheralMaterials", e.Uncategorized = "Uncategorized";
})(T || (T = {}));
-var b;
+var k;
(function(e) {
e.Paragraph = "Paragraph", e.Character = "Character", e.Note = "Note", e.Milestone = "Milestone", e.Unknown = "Unknown";
-})(b || (b = {}));
-const va = {
+})(k || (k = {}));
+const Na = {
id: {
category: T.FileIdentification,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "File identification information (BOOKID, FILENAME, EDITOR, MODIFICATION DATE)",
hasEndMarker: !1,
children: {
@@ -410,14 +411,14 @@ const va = {
},
usfm: {
category: T.FileIdentification,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "File markup version information",
hasEndMarker: !1,
children: void 0
},
ide: {
category: T.FileIdentification,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "File encoding information",
hasEndMarker: !1,
children: {
@@ -426,7 +427,7 @@ const va = {
},
h: {
category: T.Headers,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Running header text for a book (basic)",
hasEndMarker: !1,
children: {
@@ -435,7 +436,7 @@ const va = {
},
h1: {
category: T.Headers,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Running header text",
hasEndMarker: !1,
children: {
@@ -444,7 +445,7 @@ const va = {
},
h2: {
category: T.Headers,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Running header text, left side of page",
hasEndMarker: !1,
children: {
@@ -453,7 +454,7 @@ const va = {
},
h3: {
category: T.Headers,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Running header text, right side of page",
hasEndMarker: !1,
children: {
@@ -462,70 +463,70 @@ const va = {
},
toc1: {
category: T.Headers,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Long table of contents text",
hasEndMarker: !1,
children: void 0
},
toc2: {
category: T.Headers,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Short table of contents text",
hasEndMarker: !1,
children: void 0
},
toc3: {
category: T.Headers,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Book Abbreviation",
hasEndMarker: !1,
children: void 0
},
toca1: {
category: T.Headers,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Alternative language long table of contents text",
hasEndMarker: !1,
children: void 0
},
toca2: {
category: T.Headers,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Alternative language short table of contents text",
hasEndMarker: !1,
children: void 0
},
toca3: {
category: T.Headers,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Alternative language book Abbreviation",
hasEndMarker: !1,
children: void 0
},
rem: {
category: T.Remarks,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Comments and remarks",
hasEndMarker: !1,
children: void 0
},
sts: {
category: T.Remarks,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Status of this file",
hasEndMarker: !1,
children: void 0
},
restore: {
category: T.Remarks,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Project restore information",
hasEndMarker: !1,
children: void 0
},
imt: {
category: T.Introduction,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Introduction major title, level 1 (if single level) (basic)",
hasEndMarker: !1,
children: {
@@ -535,7 +536,7 @@ const va = {
},
imt1: {
category: T.Introduction,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Introduction major title, level 1 (if multiple levels)",
hasEndMarker: !1,
children: {
@@ -545,7 +546,7 @@ const va = {
},
imt2: {
category: T.Introduction,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Introduction major title, level 2",
hasEndMarker: !1,
children: {
@@ -555,7 +556,7 @@ const va = {
},
imt3: {
category: T.Introduction,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Introduction major title, level 3",
hasEndMarker: !1,
children: {
@@ -565,7 +566,7 @@ const va = {
},
imt4: {
category: T.Introduction,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Introduction major title, level 4 (usually within parenthesis)",
hasEndMarker: !1,
children: {
@@ -575,7 +576,7 @@ const va = {
},
imte: {
category: T.Introduction,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Introduction major title at introduction end, level 1 (if single level)",
hasEndMarker: !1,
children: {
@@ -585,7 +586,7 @@ const va = {
},
imte1: {
category: T.Introduction,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Introduction major title at introduction end, level 1 (if multiple levels)",
hasEndMarker: !1,
children: {
@@ -595,7 +596,7 @@ const va = {
},
imte2: {
category: T.Introduction,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Introduction major title at introduction end, level 2",
hasEndMarker: !1,
children: {
@@ -605,7 +606,7 @@ const va = {
},
is: {
category: T.Introduction,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Introduction section heading, level 1 (if single level) (basic)",
hasEndMarker: !1,
children: {
@@ -616,7 +617,7 @@ const va = {
},
is1: {
category: T.Introduction,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Introduction section heading, level 1 (if multiple levels)",
hasEndMarker: !1,
children: {
@@ -626,7 +627,7 @@ const va = {
},
is2: {
category: T.Introduction,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Introduction section heading, level 2",
hasEndMarker: !1,
children: {
@@ -636,7 +637,7 @@ const va = {
},
iot: {
category: T.Introduction,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Introduction outline title (basic)",
hasEndMarker: !1,
children: {
@@ -646,7 +647,7 @@ const va = {
},
io: {
category: T.Introduction,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Introduction outline text, level 1 (if single level)",
hasEndMarker: !1,
children: {
@@ -672,7 +673,7 @@ const va = {
},
io1: {
category: T.Introduction,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Introduction outline text, level 1 (if multiple levels) (basic)",
hasEndMarker: !1,
children: {
@@ -698,7 +699,7 @@ const va = {
},
io2: {
category: T.Introduction,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Introduction outline text, level 2",
hasEndMarker: !1,
children: {
@@ -724,7 +725,7 @@ const va = {
},
io3: {
category: T.Introduction,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Introduction outline text, level 3",
hasEndMarker: !1,
children: {
@@ -750,7 +751,7 @@ const va = {
},
io4: {
category: T.Introduction,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Introduction outline text, level 4",
hasEndMarker: !1,
children: {
@@ -776,14 +777,14 @@ const va = {
},
ior: {
category: T.Introduction,
- type: b.Character,
+ type: k.Character,
description: "Introduction references range for outline entry; for marking references separately",
hasEndMarker: !0,
children: void 0
},
ip: {
category: T.Introduction,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Introduction prose paragraph (basic)",
hasEndMarker: !1,
children: {
@@ -810,7 +811,7 @@ const va = {
},
im: {
category: T.Introduction,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Introduction prose paragraph, with no first line indent (may occur after poetry)",
hasEndMarker: !1,
children: {
@@ -836,7 +837,7 @@ const va = {
},
ipi: {
category: T.Introduction,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Introduction prose paragraph, indented, with first line indent",
hasEndMarker: !1,
children: {
@@ -862,7 +863,7 @@ const va = {
},
imi: {
category: T.Introduction,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Introduction prose paragraph text, indented, with no first line indent",
hasEndMarker: !1,
children: {
@@ -888,7 +889,7 @@ const va = {
},
ili: {
category: T.Introduction,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "A list entry, level 1 (if single level)",
hasEndMarker: !1,
children: {
@@ -914,7 +915,7 @@ const va = {
},
ili1: {
category: T.Introduction,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "A list entry, level 1 (if multiple levels)",
hasEndMarker: !1,
children: {
@@ -940,7 +941,7 @@ const va = {
},
ili2: {
category: T.Introduction,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "A list entry, level 2",
hasEndMarker: !1,
children: {
@@ -966,7 +967,7 @@ const va = {
},
ipq: {
category: T.Introduction,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Introduction prose paragraph, quote from the body text",
hasEndMarker: !1,
children: {
@@ -992,7 +993,7 @@ const va = {
},
imq: {
category: T.Introduction,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Introduction prose paragraph, quote from the body text, with no first line indent",
hasEndMarker: !1,
children: {
@@ -1018,7 +1019,7 @@ const va = {
},
ipr: {
category: T.Introduction,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Introduction prose paragraph, right aligned",
hasEndMarker: !1,
children: {
@@ -1044,7 +1045,7 @@ const va = {
},
ib: {
category: T.Introduction,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Introduction blank line",
hasEndMarker: !1,
children: {
@@ -1053,7 +1054,7 @@ const va = {
},
iq: {
category: T.Introduction,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Introduction poetry text, level 1 (if single level)",
hasEndMarker: !1,
children: {
@@ -1079,7 +1080,7 @@ const va = {
},
iq1: {
category: T.Introduction,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Introduction poetry text, level 1 (if multiple levels)",
hasEndMarker: !1,
children: {
@@ -1105,7 +1106,7 @@ const va = {
},
iq2: {
category: T.Introduction,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Introduction poetry text, level 2",
hasEndMarker: !1,
children: {
@@ -1131,7 +1132,7 @@ const va = {
},
iq3: {
category: T.Introduction,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Introduction poetry text, level 3",
hasEndMarker: !1,
children: {
@@ -1157,7 +1158,7 @@ const va = {
},
iex: {
category: T.Introduction,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Introduction explanatory or bridge text (e.g. explanation of missing book in Short Old Testament)",
hasEndMarker: !1,
children: {
@@ -1167,21 +1168,21 @@ const va = {
},
iqt: {
category: T.Introduction,
- type: b.Character,
+ type: k.Character,
description: "For quoted scripture text appearing in the introduction",
hasEndMarker: !0,
children: void 0
},
ie: {
category: T.Introduction,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Introduction ending marker",
hasEndMarker: !1,
children: void 0
},
c: {
category: T.DivisionMarks,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Chapter number (necessary for normal Paratext operation)",
hasEndMarker: !1,
children: {
@@ -1216,14 +1217,14 @@ const va = {
},
ca: {
category: T.DivisionMarks,
- type: b.Character,
+ type: k.Character,
description: "Second (alternate) chapter number (for coding dual versification; useful for places where different traditions of chapter breaks need to be supported in the same translation)",
hasEndMarker: !0,
children: void 0
},
cp: {
category: T.DivisionMarks,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Published chapter number (chapter string that should appear in the published text)",
hasEndMarker: !1,
children: {
@@ -1232,14 +1233,14 @@ const va = {
},
cl: {
category: T.DivisionMarks,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Chapter label used for translations that add a word such as 'Chapter' before chapter numbers (e.g. Psalms). The subsequent text is the chapter label.",
hasEndMarker: !1,
children: void 0
},
cd: {
category: T.DivisionMarks,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Chapter Description (Publishing option D, e.g. in Russian Bibles)",
hasEndMarker: !1,
children: {
@@ -1265,28 +1266,28 @@ const va = {
},
v: {
category: T.DivisionMarks,
- type: b.Character,
+ type: k.Character,
description: "A verse number (Necessary for normal paratext operation) (basic)",
hasEndMarker: !1,
children: void 0
},
va: {
category: T.DivisionMarks,
- type: b.Character,
+ type: k.Character,
description: "Second (alternate) verse number (for coding dual numeration in Psalms; see also NRSV Exo 22.1-4)",
hasEndMarker: !0,
children: void 0
},
vp: {
category: T.DivisionMarks,
- type: b.Character,
+ type: k.Character,
description: "Published verse marker (verse string that should appear in the published text)",
hasEndMarker: !0,
children: void 0
},
p: {
category: T.Paragraphs,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Paragraph text, with first line indent (basic)",
hasEndMarker: !1,
children: {
@@ -1316,7 +1317,7 @@ const va = {
},
m: {
category: T.Paragraphs,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Paragraph text, with no first line indent (may occur after poetry) (basic)",
hasEndMarker: !1,
children: {
@@ -1346,7 +1347,7 @@ const va = {
},
po: {
category: T.Paragraphs,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Letter opening",
hasEndMarker: !1,
children: {
@@ -1375,7 +1376,7 @@ const va = {
},
pr: {
category: T.Paragraphs,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Text refrain (paragraph text, right aligned)",
hasEndMarker: !1,
children: {
@@ -1405,7 +1406,7 @@ const va = {
},
cls: {
category: T.Paragraphs,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Letter Closing",
hasEndMarker: !1,
children: {
@@ -1414,7 +1415,7 @@ const va = {
},
pmo: {
category: T.Paragraphs,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Embedded text opening",
hasEndMarker: !1,
children: {
@@ -1442,7 +1443,7 @@ const va = {
},
pm: {
category: T.Paragraphs,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Embedded text paragraph",
hasEndMarker: !1,
children: {
@@ -1470,7 +1471,7 @@ const va = {
},
pmc: {
category: T.Paragraphs,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Embedded text closing",
hasEndMarker: !1,
children: {
@@ -1498,7 +1499,7 @@ const va = {
},
pmr: {
category: T.Paragraphs,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Embedded text refrain (e.g. Then all the people shall say, 'Amen!')",
hasEndMarker: !1,
children: {
@@ -1526,7 +1527,7 @@ const va = {
},
pi: {
category: T.Paragraphs,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Paragraph text, level 1 indent (if single level), with first line indent; often used for discourse (basic)",
hasEndMarker: !1,
children: {
@@ -1555,7 +1556,7 @@ const va = {
},
pi1: {
category: T.Paragraphs,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Paragraph text, level 1 indent (if multiple levels), with first line indent; often used for discourse",
hasEndMarker: !1,
children: {
@@ -1584,7 +1585,7 @@ const va = {
},
pi2: {
category: T.Paragraphs,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Paragraph text, level 2 indent, with first line indent; often used for discourse",
hasEndMarker: !1,
children: {
@@ -1613,7 +1614,7 @@ const va = {
},
pi3: {
category: T.Paragraphs,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Paragraph text, level 3 indent, with first line indent; often used for discourse",
hasEndMarker: !1,
children: {
@@ -1642,7 +1643,7 @@ const va = {
},
pc: {
category: T.Paragraphs,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Paragraph text, centered (for Inscription)",
hasEndMarker: !1,
children: {
@@ -1671,7 +1672,7 @@ const va = {
},
mi: {
category: T.Paragraphs,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Paragraph text, indented, with no first line indent; often used for discourse",
hasEndMarker: !1,
children: {
@@ -1700,7 +1701,7 @@ const va = {
},
nb: {
category: T.Paragraphs,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Paragraph text, with no break from previous paragraph text (at chapter boundary) (basic)",
hasEndMarker: !1,
children: {
@@ -1729,7 +1730,7 @@ const va = {
},
q: {
category: T.Poetry,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Poetry text, level 1 indent (if single level)",
hasEndMarker: !1,
children: {
@@ -1758,7 +1759,7 @@ const va = {
},
q1: {
category: T.Poetry,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Poetry text, level 1 indent (if multiple levels) (basic)",
hasEndMarker: !1,
children: {
@@ -1787,7 +1788,7 @@ const va = {
},
q2: {
category: T.Poetry,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Poetry text, level 2 indent (basic)",
hasEndMarker: !1,
children: {
@@ -1816,7 +1817,7 @@ const va = {
},
q3: {
category: T.Poetry,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Poetry text, level 3 indent",
hasEndMarker: !1,
children: {
@@ -1845,7 +1846,7 @@ const va = {
},
q4: {
category: T.Poetry,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Poetry text, level 4 indent",
hasEndMarker: !1,
children: {
@@ -1874,7 +1875,7 @@ const va = {
},
qc: {
category: T.Poetry,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Poetry text, centered",
hasEndMarker: !1,
children: {
@@ -1903,7 +1904,7 @@ const va = {
},
qr: {
category: T.Poetry,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Poetry text, Right Aligned",
hasEndMarker: !1,
children: {
@@ -1932,7 +1933,7 @@ const va = {
},
qs: {
category: T.Poetry,
- type: b.Character,
+ type: k.Character,
description: "Poetry text, Selah",
hasEndMarker: !0,
children: {
@@ -1942,21 +1943,21 @@ const va = {
},
qa: {
category: T.Poetry,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Poetry text, Acrostic marker/heading",
hasEndMarker: !1,
children: void 0
},
qac: {
category: T.Poetry,
- type: b.Character,
+ type: k.Character,
description: "Poetry text, Acrostic markup of the first character of a line of acrostic poetry",
hasEndMarker: !0,
children: void 0
},
qm: {
category: T.Poetry,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Poetry text, embedded, level 1 indent (if single level)",
hasEndMarker: !1,
children: {
@@ -1984,7 +1985,7 @@ const va = {
},
qm1: {
category: T.Poetry,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Poetry text, embedded, level 1 indent (if multiple levels)",
hasEndMarker: !1,
children: {
@@ -2012,7 +2013,7 @@ const va = {
},
qm2: {
category: T.Poetry,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Poetry text, embedded, level 2 indent",
hasEndMarker: !1,
children: {
@@ -2040,7 +2041,7 @@ const va = {
},
qm3: {
category: T.Poetry,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Poetry text, embedded, level 3 indent",
hasEndMarker: !1,
children: {
@@ -2068,7 +2069,7 @@ const va = {
},
qd: {
category: T.Poetry,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "A Hebrew musical performance annotation, similar in content to Hebrew descriptive title.",
hasEndMarker: !1,
children: {
@@ -2096,14 +2097,14 @@ const va = {
},
b: {
category: T.Poetry,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Poetry text stanza break (e.g. stanza break) (basic)",
hasEndMarker: !1,
children: void 0
},
mt: {
category: T.TitlesHeadings,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "The main title of the book (if single level)",
hasEndMarker: !1,
children: {
@@ -2113,7 +2114,7 @@ const va = {
},
mt1: {
category: T.TitlesHeadings,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "The main title of the book (if multiple levels) (basic)",
hasEndMarker: !1,
children: {
@@ -2123,7 +2124,7 @@ const va = {
},
mt2: {
category: T.TitlesHeadings,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "A secondary title usually occurring before the main title (basic)",
hasEndMarker: !1,
children: {
@@ -2133,7 +2134,7 @@ const va = {
},
mt3: {
category: T.TitlesHeadings,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "A secondary title occurring after the main title",
hasEndMarker: !1,
children: {
@@ -2143,21 +2144,21 @@ const va = {
},
mt4: {
category: T.TitlesHeadings,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "A small secondary title sometimes occurring within parentheses",
hasEndMarker: !1,
children: void 0
},
mte: {
category: T.TitlesHeadings,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "The main title of the book repeated at the end of the book, level 1 (if single level)",
hasEndMarker: !1,
children: void 0
},
mte1: {
category: T.TitlesHeadings,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "The main title of the book repeated at the end of the book, level 1 (if multiple levels)",
hasEndMarker: !1,
children: {
@@ -2166,14 +2167,14 @@ const va = {
},
mte2: {
category: T.TitlesHeadings,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "A secondary title occurring before or after the 'ending' main title",
hasEndMarker: !1,
children: void 0
},
ms: {
category: T.TitlesHeadings,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "A major section division heading, level 1 (if single level) (basic)",
hasEndMarker: !1,
children: {
@@ -2200,7 +2201,7 @@ const va = {
},
ms1: {
category: T.TitlesHeadings,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "A major section division heading, level 1 (if multiple levels)",
hasEndMarker: !1,
children: {
@@ -2227,7 +2228,7 @@ const va = {
},
ms2: {
category: T.TitlesHeadings,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "A major section division heading, level 2",
hasEndMarker: !1,
children: {
@@ -2254,7 +2255,7 @@ const va = {
},
ms3: {
category: T.TitlesHeadings,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "A major section division heading, level 3",
hasEndMarker: !1,
children: {
@@ -2264,14 +2265,14 @@ const va = {
},
mr: {
category: T.TitlesHeadings,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "A major section division references range heading (basic)",
hasEndMarker: !1,
children: void 0
},
s: {
category: T.TitlesHeadings,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "A section heading, level 1 (if single level) (basic)",
hasEndMarker: !1,
children: {
@@ -2298,7 +2299,7 @@ const va = {
},
s1: {
category: T.TitlesHeadings,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "A section heading, level 1 (if multiple levels)",
hasEndMarker: !1,
children: {
@@ -2325,7 +2326,7 @@ const va = {
},
s2: {
category: T.TitlesHeadings,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "A section heading, level 2 (e.g. Proverbs 22-24)",
hasEndMarker: !1,
children: {
@@ -2352,7 +2353,7 @@ const va = {
},
s3: {
category: T.TitlesHeadings,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "A section heading, level 3 (e.g. Genesis 'The First Day')",
hasEndMarker: !1,
children: {
@@ -2379,7 +2380,7 @@ const va = {
},
s4: {
category: T.TitlesHeadings,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "A section heading, level 4",
hasEndMarker: !1,
children: {
@@ -2405,21 +2406,21 @@ const va = {
},
sr: {
category: T.TitlesHeadings,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "A section division references range heading",
hasEndMarker: !1,
children: void 0
},
r: {
category: T.TitlesHeadings,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Parallel reference(s) (basic)",
hasEndMarker: !1,
children: void 0
},
sp: {
category: T.TitlesHeadings,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "A heading, to identify the speaker (e.g. Job)",
hasEndMarker: !1,
children: {
@@ -2446,7 +2447,7 @@ const va = {
},
d: {
category: T.TitlesHeadings,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "A Hebrew text heading, to provide description (e.g. Psalms)",
hasEndMarker: !1,
children: {
@@ -2472,42 +2473,42 @@ const va = {
},
sd: {
category: T.TitlesHeadings,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Vertical space used to divide the text into sections, level 1 (if single level)",
hasEndMarker: !1,
children: void 0
},
sd1: {
category: T.TitlesHeadings,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Vertical space used to divide the text into sections, level 1 (if multiple levels)",
hasEndMarker: !1,
children: void 0
},
sd2: {
category: T.TitlesHeadings,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Vertical space used to divide the text into sections, level 2",
hasEndMarker: !1,
children: void 0
},
sd3: {
category: T.TitlesHeadings,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Vertical space used to divide the text into sections, level 3",
hasEndMarker: !1,
children: void 0
},
sd4: {
category: T.TitlesHeadings,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Vertical space used to divide the text into sections, level 4",
hasEndMarker: !1,
children: void 0
},
lh: {
category: T.Lists,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "List header (introductory remark)",
hasEndMarker: !1,
children: {
@@ -2534,7 +2535,7 @@ const va = {
},
li: {
category: T.Lists,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "A list entry, level 1 (if single level)",
hasEndMarker: !1,
children: {
@@ -2562,7 +2563,7 @@ const va = {
},
li1: {
category: T.Lists,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "A list entry, level 1 (if multiple levels)",
hasEndMarker: !1,
children: {
@@ -2590,7 +2591,7 @@ const va = {
},
li2: {
category: T.Lists,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "A list entry, level 2",
hasEndMarker: !1,
children: {
@@ -2618,7 +2619,7 @@ const va = {
},
li3: {
category: T.Lists,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "A list entry, level 3",
hasEndMarker: !1,
children: {
@@ -2646,7 +2647,7 @@ const va = {
},
li4: {
category: T.Lists,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "A list entry, level 4",
hasEndMarker: !1,
children: {
@@ -2674,7 +2675,7 @@ const va = {
},
lf: {
category: T.Lists,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "List footer (concluding remark)",
hasEndMarker: !1,
children: {
@@ -2701,7 +2702,7 @@ const va = {
},
lim: {
category: T.Lists,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "An embedded list entry, level 1 (if single level)",
hasEndMarker: !1,
children: {
@@ -2729,7 +2730,7 @@ const va = {
},
lim1: {
category: T.Lists,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "An embedded list entry, level 1 (if multiple levels)",
hasEndMarker: !1,
children: {
@@ -2757,7 +2758,7 @@ const va = {
},
lim2: {
category: T.Lists,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "An embedded list entry, level 2",
hasEndMarker: !1,
children: {
@@ -2785,7 +2786,7 @@ const va = {
},
lim3: {
category: T.Lists,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "An embedded list item, level 3",
hasEndMarker: !1,
children: {
@@ -2813,7 +2814,7 @@ const va = {
},
lim4: {
category: T.Lists,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "An embedded list entry, level 4",
hasEndMarker: !1,
children: {
@@ -2841,63 +2842,63 @@ const va = {
},
litl: {
category: T.Lists,
- type: b.Character,
+ type: k.Character,
description: "List entry total text",
hasEndMarker: !0,
children: void 0
},
lik: {
category: T.Lists,
- type: b.Character,
+ type: k.Character,
description: "Structured list entry key text",
hasEndMarker: !0,
children: void 0
},
liv: {
category: T.Lists,
- type: b.Character,
+ type: k.Character,
description: "Structured list entry value 1 content (if single value)",
hasEndMarker: !0,
children: void 0
},
liv1: {
category: T.Lists,
- type: b.Character,
+ type: k.Character,
description: "Structured list entry value 1 content (if multiple values)",
hasEndMarker: !0,
children: void 0
},
liv2: {
category: T.Lists,
- type: b.Character,
+ type: k.Character,
description: "Structured list entry value 2 content",
hasEndMarker: !0,
children: void 0
},
liv3: {
category: T.Lists,
- type: b.Character,
+ type: k.Character,
description: "Structured list entry value 3 content",
hasEndMarker: !0,
children: void 0
},
liv4: {
category: T.Lists,
- type: b.Character,
+ type: k.Character,
description: "Structured list entry value 4 content",
hasEndMarker: !0,
children: void 0
},
liv5: {
category: T.Lists,
- type: b.Character,
+ type: k.Character,
description: "Structured list entry value 5 content",
hasEndMarker: !0,
children: void 0
},
f: {
category: T.Footnotes,
- type: b.Note,
+ type: k.Note,
description: "A Footnote text item (basic)",
hasEndMarker: !0,
children: {
@@ -2924,7 +2925,7 @@ const va = {
},
fe: {
category: T.Footnotes,
- type: b.Note,
+ type: k.Note,
description: "An Endnote text item",
hasEndMarker: !0,
children: {
@@ -2951,84 +2952,84 @@ const va = {
},
fr: {
category: T.Footnotes,
- type: b.Character,
+ type: k.Character,
description: "The origin reference for the footnote (basic)",
hasEndMarker: !0,
children: void 0
},
ft: {
category: T.Footnotes,
- type: b.Character,
+ type: k.Character,
description: "Footnote text, Protocanon (basic)",
hasEndMarker: !0,
children: void 0
},
fk: {
category: T.Footnotes,
- type: b.Character,
+ type: k.Character,
description: "A footnote keyword (basic)",
hasEndMarker: !0,
children: void 0
},
fq: {
category: T.Footnotes,
- type: b.Character,
+ type: k.Character,
description: "A footnote scripture quote or alternate rendering (basic)",
hasEndMarker: !0,
children: void 0
},
fqa: {
category: T.Footnotes,
- type: b.Character,
+ type: k.Character,
description: "A footnote alternate rendering for a portion of scripture text",
hasEndMarker: !0,
children: void 0
},
fl: {
category: T.Footnotes,
- type: b.Character,
+ type: k.Character,
description: "A footnote label text item, for marking or 'labelling' the type or alternate translation being provided in the note.",
hasEndMarker: !0,
children: void 0
},
fw: {
category: T.Footnotes,
- type: b.Character,
+ type: k.Character,
description: "A footnote witness list, for distinguishing a list of sigla representing witnesses in critical editions.",
hasEndMarker: !0,
children: void 0
},
fp: {
category: T.Footnotes,
- type: b.Character,
+ type: k.Character,
description: "A Footnote additional paragraph marker",
hasEndMarker: !0,
children: void 0
},
fv: {
category: T.Footnotes,
- type: b.Character,
+ type: k.Character,
description: "A verse number within the footnote text",
hasEndMarker: !0,
children: void 0
},
fdc: {
category: T.Footnotes,
- type: b.Character,
+ type: k.Character,
description: "Footnote text, applies to Deuterocanon only",
hasEndMarker: !0,
children: void 0
},
fm: {
category: T.Footnotes,
- type: b.Character,
+ type: k.Character,
description: "An additional footnote marker location for a previous footnote",
hasEndMarker: !0,
children: void 0
},
x: {
category: T.CrossReferences,
- type: b.Note,
+ type: k.Note,
description: "A list of cross references (basic)",
hasEndMarker: !0,
children: {
@@ -3038,236 +3039,236 @@ const va = {
},
xo: {
category: T.CrossReferences,
- type: b.Character,
+ type: k.Character,
description: "The cross reference origin reference (basic)",
hasEndMarker: !0,
children: void 0
},
xop: {
category: T.CrossReferences,
- type: b.Character,
+ type: k.Character,
description: "Published cross reference origin reference (origin reference that should appear in the published text)",
hasEndMarker: !0,
children: void 0
},
xt: {
category: T.CrossReferences,
- type: b.Character,
+ type: k.Character,
description: "The cross reference target reference(s), protocanon only (basic)",
hasEndMarker: !0,
children: void 0
},
xta: {
category: T.CrossReferences,
- type: b.Character,
+ type: k.Character,
description: "Cross reference target references added text",
hasEndMarker: !0,
children: void 0
},
xk: {
category: T.CrossReferences,
- type: b.Character,
+ type: k.Character,
description: "A cross reference keyword",
hasEndMarker: !0,
children: void 0
},
xq: {
category: T.CrossReferences,
- type: b.Character,
+ type: k.Character,
description: "A cross-reference quotation from the scripture text",
hasEndMarker: !0,
children: void 0
},
xot: {
category: T.CrossReferences,
- type: b.Character,
+ type: k.Character,
description: "Cross-reference target reference(s), Old Testament only",
hasEndMarker: !0,
children: void 0
},
xnt: {
category: T.CrossReferences,
- type: b.Character,
+ type: k.Character,
description: "Cross-reference target reference(s), New Testament only",
hasEndMarker: !0,
children: void 0
},
xdc: {
category: T.CrossReferences,
- type: b.Character,
+ type: k.Character,
description: "Cross-reference target reference(s), Deuterocanon only",
hasEndMarker: !0,
children: void 0
},
rq: {
category: T.CrossReferences,
- type: b.Character,
+ type: k.Character,
description: "A cross-reference indicating the source text for the preceding quotation.",
hasEndMarker: !0,
children: void 0
},
qt: {
category: T.SpecialText,
- type: b.Character,
+ type: k.Character,
description: "For Old Testament quoted text appearing in the New Testament (basic)",
hasEndMarker: !0,
children: void 0
},
nd: {
category: T.SpecialText,
- type: b.Character,
+ type: k.Character,
description: "For name of deity (basic)",
hasEndMarker: !0,
children: void 0
},
tl: {
category: T.SpecialText,
- type: b.Character,
+ type: k.Character,
description: "For transliterated words",
hasEndMarker: !0,
children: void 0
},
dc: {
category: T.SpecialText,
- type: b.Character,
+ type: k.Character,
description: "Deuterocanonical/LXX additions or insertions in the Protocanonical text",
hasEndMarker: !0,
children: void 0
},
bk: {
category: T.SpecialText,
- type: b.Character,
+ type: k.Character,
description: "For the quoted name of a book",
hasEndMarker: !0,
children: void 0
},
sig: {
category: T.SpecialText,
- type: b.Character,
+ type: k.Character,
description: "For the signature of the author of an Epistle",
hasEndMarker: !0,
children: void 0
},
pn: {
category: T.SpecialText,
- type: b.Character,
+ type: k.Character,
description: "For a proper name",
hasEndMarker: !0,
children: void 0
},
png: {
category: T.SpecialText,
- type: b.Character,
+ type: k.Character,
description: "For a geographic proper name",
hasEndMarker: !0,
children: void 0
},
addpn: {
category: T.SpecialText,
- type: b.Character,
+ type: k.Character,
description: "For chinese words to be dot underline & underline",
hasEndMarker: !0,
children: void 0
},
wj: {
category: T.SpecialText,
- type: b.Character,
+ type: k.Character,
description: "For marking the words of Jesus",
hasEndMarker: !0,
children: void 0
},
k: {
category: T.SpecialText,
- type: b.Character,
+ type: k.Character,
description: "For a keyword",
hasEndMarker: !0,
children: void 0
},
sls: {
category: T.SpecialText,
- type: b.Character,
+ type: k.Character,
description: "To represent where the original text is in a secondary language or from an alternate text source",
hasEndMarker: !0,
children: void 0
},
ord: {
category: T.SpecialText,
- type: b.Character,
+ type: k.Character,
description: "For the text portion of an ordinal number",
hasEndMarker: !0,
children: void 0
},
add: {
category: T.SpecialText,
- type: b.Character,
+ type: k.Character,
description: "For a translational addition to the text",
hasEndMarker: !0,
children: void 0
},
lit: {
category: T.SpecialText,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "For a comment or note inserted for liturgical use",
hasEndMarker: !1,
children: void 0
},
no: {
category: T.CharacterStyling,
- type: b.Character,
+ type: k.Character,
description: "A character style, use normal text",
hasEndMarker: !0,
children: void 0
},
it: {
category: T.CharacterStyling,
- type: b.Character,
+ type: k.Character,
description: "A character style, use italic text",
hasEndMarker: !0,
children: void 0
},
bd: {
category: T.CharacterStyling,
- type: b.Character,
+ type: k.Character,
description: "A character style, use bold text",
hasEndMarker: !0,
children: void 0
},
bdit: {
category: T.CharacterStyling,
- type: b.Character,
+ type: k.Character,
description: "A character style, use bold + italic text",
hasEndMarker: !0,
children: void 0
},
em: {
category: T.CharacterStyling,
- type: b.Character,
+ type: k.Character,
description: "A character style, use emphasized text style",
hasEndMarker: !0,
children: void 0
},
sc: {
category: T.CharacterStyling,
- type: b.Character,
+ type: k.Character,
description: "A character style, for small capitalization text",
hasEndMarker: !0,
children: void 0
},
sup: {
category: T.CharacterStyling,
- type: b.Character,
+ type: k.Character,
description: "A character style, for superscript text. Typically for use in critical edition footnotes.",
hasEndMarker: !0,
children: void 0
},
pb: {
category: T.Breaks,
- type: b.Paragraph,
+ type: k.Paragraph,
description: "Page Break used for new reader portions and children's bibles where content is controlled by the page",
hasEndMarker: !1,
children: void 0
}
-}, an = {
+}, ln = {
DivisionMarks: { add: ["v", "c"], remove: [] },
Paragraphs: { add: ["p"], remove: [] },
Poetry: { add: ["q", "q1", "q2", "q3", "q4", "b"], remove: [] },
@@ -3294,21 +3295,21 @@ const va = {
],
remove: []
}
-}, lu = {
- p: { children: an },
- q: { children: an },
- q1: { children: an },
- q2: { children: an },
- q3: { children: an },
- q4: { children: an },
- b: { children: an },
+}, xu = {
+ p: { children: ln },
+ q: { children: ln },
+ q1: { children: ln },
+ q2: { children: ln },
+ q3: { children: ln },
+ q4: { children: ln },
+ b: { children: ln },
qm: {
children: {
Paragraphs: { add: ["p"], remove: [] }
}
},
c: {
- type: b.Paragraph,
+ type: k.Paragraph,
children: null
},
v: {
@@ -3320,19 +3321,19 @@ const va = {
// generated file, which would be silently lost on regeneration.
w: {
category: T.SpecialFeatures,
- type: b.Character,
+ type: k.Character,
description: "A wordlist/glossary/dictionary entry marker for study/analysis purposes",
hasEndMarker: !0
},
rb: {
category: T.SpecialFeatures,
- type: b.Character,
+ type: k.Character,
description: "A ruby glossing marker for study/analysis purposes",
hasEndMarker: !0
},
jmp: {
category: T.SpecialFeatures,
- type: b.Character,
+ type: k.Character,
description: "A hyperlink marker for study/analysis purposes",
hasEndMarker: !0
},
@@ -3342,13 +3343,13 @@ const va = {
// into its own paragraph with the closer stranded as unmatched.
fig: {
category: T.SpecialFeatures,
- type: b.Character,
+ type: k.Character,
description: "Illustration [Columns to span, height, filename, caption text]",
hasEndMarker: !0
}
};
function lr(e) {
- const t = Object.hasOwn(va, e) ? va[e] : void 0, r = Object.hasOwn(lu, e) ? lu[e] : void 0;
+ const t = Object.hasOwn(Na, e) ? Na[e] : void 0, r = Object.hasOwn(xu, e) ? xu[e] : void 0;
if (!t)
return r?.category !== void 0 && r.type !== void 0 && r.description !== void 0 && r.hasEndMarker !== void 0 ? { ...r } : void 0;
if (!r)
@@ -3373,16 +3374,16 @@ function lr(e) {
children: n
};
}
-const Vf = "v", Wf = "c", cn = "fig", uu = "tr", Ma = "esb", Hf = "esbe", du = "periph", fu = "alt", pu = /^t[hc]([rc]?)(\d+)(?:-(\d+))?$/, bb = {
+const ap = "v", cp = "c", un = "fig", _u = "tr", Oa = "esb", lp = "esbe", Cu = "periph", vu = "alt", Su = /^t[hc]([rc]?)(\d+)(?:-(\d+))?$/, Ib = {
"": "start",
c: "center",
r: "end"
};
-function hu(e) {
+function Mu(e) {
const [, , t, r] = e;
return r ? /^[1-5]$/.test(t) && /^[2-5]$/.test(r) && Number(r) > Number(t) : /^(?:[1-9]|1[0-2])$/.test(t);
}
-function gu(e) {
+function Eu(e) {
const t = e.charCodeAt(0);
return t >= 9 && t <= 13 ? !0 : t === 32 || // SPACE
t === 133 || // NEXT LINE
@@ -3395,24 +3396,24 @@ function gu(e) {
t === 8287 || // MEDIUM MATHEMATICAL SPACE
t === 8203;
}
-const kb = /[\u200D\u2003\u2002\u0020\u00A0\u202F\u2009\u200A\u3000\u200B\u200C\u2060\u200E\u200F]/;
-function Tb(e) {
+const Db = /[\u200D\u2003\u2002\u0020\u00A0\u202F\u2009\u200A\u3000\u200B\u200C\u2060\u200E\u200F]/;
+function Ub(e) {
let t = "", r = !1, n = "\0", i = -1;
for (let s = 0; s < e.length; s += 1) {
const o = e[s];
- o.charCodeAt(0) < 32 ? (r || (i = t.length, t += " "), r = !0) : !r && o === Is && s + 1 < e.length && gu(e[s + 1]) || (gu(o) ? (r || (i = t.length, t += o), r = !0) : kb.test(o) && o === n || (t += o, r = !1, i = -1)), (o === `
+ o.charCodeAt(0) < 32 ? (r || (i = t.length, t += " "), r = !0) : !r && o === Fs && s + 1 < e.length && Eu(e[s + 1]) || (Eu(o) ? (r || (i = t.length, t += o), r = !0) : Db.test(o) && o === n || (t += o, r = !1, i = -1)), (o === `
` || o === "\r") && i >= 0 && (t = `${t.slice(0, i)}
${t.slice(i + 1)}`), n = o;
}
return t;
}
-function xb(e) {
+function Fb(e) {
if (e.length === 0)
return !0;
const t = e[0];
return t === "*" ? !1 : !!(t === "\\" || t === "|" || /[\s\u200B]/.test(t));
}
-function _b(e, t) {
+function zb(e, t) {
let r = t;
for (; r < e.length; ) {
const n = e[r];
@@ -3428,11 +3429,11 @@ function _b(e, t) {
}
return { name: e.slice(t, r), next: r };
}
-const Cb = /^(?:qt[1-5]?|ts)-[se]$/;
-function Ac(e) {
- return Cb.test(e) || Kf(e);
+const Kb = /^(?:qt[1-5]?|ts)-[se]$/;
+function Ic(e) {
+ return Kb.test(e) || ip(e);
}
-function Jo(e, t) {
+function Qo(e, t) {
let r = t;
for (; r < e.length && /[\s\u00A0\u200B]/.test(e[r]); )
r++;
@@ -3444,7 +3445,7 @@ function Jo(e, t) {
r++;
return { word: i, next: r };
}
-function Sb(e, t, r) {
+function jb(e, t, r) {
const n = [];
let i = 0, s;
const o = (c) => {
@@ -3453,18 +3454,18 @@ function Sb(e, t, r) {
const l = n[n.length - 1];
l?.kind === "text" ? l.text += c : n.push({ kind: "text", text: c });
}, a = (c) => {
- c.split("//").forEach((u, d) => {
- d > 0 && n.push({ kind: "optbreak" }), o(u);
+ c.split("//").forEach((d, u) => {
+ u > 0 && n.push({ kind: "optbreak" }), o(d);
});
};
for (; i < e.length; ) {
if (e[i] !== "\\") {
- const g = e.indexOf("\\", i), y = g === -1 ? e.length : g;
- a(Tb(e.slice(i, y))), i = y;
+ const h = e.indexOf("\\", i), m = h === -1 ? e.length : h;
+ a(Ub(e.slice(i, m))), i = m;
continue;
}
- const c = i, { name: l, next: u } = _b(e, i + 1);
- if (i = u, l === "") {
+ const c = i, { name: l, next: d } = zb(e, i + 1);
+ if (i = d, l === "") {
o(e.slice(c, i));
continue;
}
@@ -3476,64 +3477,64 @@ function Sb(e, t, r) {
l.slice(0, -1) === s && (s = void 0), n.push({ kind: "end", marker: l.slice(0, -1) });
continue;
}
- const d = () => {
+ const u = () => {
for (; i < e.length && /[\s\u00A0\u200B]/.test(e[i]); )
i++;
};
- if (l === Vf) {
- const { word: g, next: y } = Jo(e, i);
- i = y, n.push({ kind: "verse", number: g });
+ if (l === ap) {
+ const { word: h, next: m } = Qo(e, i);
+ i = m, n.push({ kind: "verse", number: h });
continue;
}
- if (l === Wf) {
- const { word: g, next: y } = Jo(e, i);
- i = y, s = void 0, n.push({ kind: "chapter", number: g });
+ if (l === cp) {
+ const { word: h, next: m } = Qo(e, i);
+ i = m, s = void 0, n.push({ kind: "chapter", number: h });
continue;
}
- const f = l.startsWith("+"), p = f ? l.slice(1) : l, m = t(p)?.type;
- if (m === b.Note || m === void 0 && Ee.isValidMarker(l)) {
- const { word: g, next: y } = Jo(e, i);
- i = y, s = l, n.push({ kind: "note", marker: l, caller: g || "+" });
+ const f = l.startsWith("+"), p = f ? l.slice(1) : l, y = t(p)?.type;
+ if (y === k.Note || y === void 0 && Ae.isValidMarker(l)) {
+ const { word: h, next: m } = Qo(e, i);
+ i = m, s = l, n.push({ kind: "note", marker: l, caller: h || "+" });
continue;
}
- if (m === b.Milestone || m === void 0 && Ac(l)) {
- const g = wb(e, c, l, i);
- if (g)
- n.push(g.token), g.ejectedText && o(g.ejectedText), i = g.next;
+ if (y === k.Milestone || y === void 0 && Ic(l)) {
+ const h = Xb(e, c, l, i);
+ if (h)
+ n.push(h.token), h.ejectedText && o(h.ejectedText), i = h.next;
else {
- const y = e.indexOf("\\", i), k = y === -1 ? e.length : y;
- o(e.slice(c, k)), i = k;
+ const m = e.indexOf("\\", i), b = m === -1 ? e.length : m;
+ o(e.slice(c, b)), i = b;
}
continue;
}
- m === b.Paragraph ? (d(), n.push({ kind: "para", marker: l })) : m === b.Character ? (d(), n.push({ kind: "charOpen", marker: p, isNested: f })) : Ds(p) ? (d(), Ds(p)?.shape === "para" ? n.push({ kind: "para", marker: l }) : n.push({ kind: "charOpen", marker: p, isNested: f })) : (d(), !(r || s !== void 0) || l === Ma || l === Hf ? n.push({ kind: "para", marker: l }) : n.push({ kind: "charOpen", marker: p, isNested: f }));
+ y === k.Paragraph ? (u(), n.push({ kind: "para", marker: l })) : y === k.Character ? (u(), n.push({ kind: "charOpen", marker: p, isNested: f })) : Ks(p) ? (u(), Ks(p)?.shape === "para" ? n.push({ kind: "para", marker: l }) : n.push({ kind: "charOpen", marker: p, isNested: f })) : (u(), !(r || s !== void 0) || l === Oa || l === lp ? n.push({ kind: "para", marker: l }) : n.push({ kind: "charOpen", marker: p, isNested: f }));
}
return n;
}
-const mu = {
+const Au = {
ca: { attrName: "altnumber", targetTypes: ["chapter"], shape: "char" },
cp: { attrName: "pubnumber", targetTypes: ["chapter"], shape: "para" },
va: { attrName: "altnumber", targetTypes: ["verse"], shape: "char" },
vp: { attrName: "pubnumber", targetTypes: ["verse"], shape: "char" },
cat: { attrName: "category", targetTypes: ["note", "sidebar"], shape: "char" }
};
-function Ds(e) {
- return Object.hasOwn(mu, e) ? mu[e] : void 0;
+function Ks(e) {
+ return Object.hasOwn(Au, e) ? Au[e] : void 0;
}
-function vb(e) {
- return Ds(e) !== void 0;
+function Bb(e) {
+ return Ks(e) !== void 0;
}
-const Mb = /([-\w]+)\s*=\s*"(.*?)"/g, Eb = /[\s\u200B]*[\n\r][\s\u200B]*/g, Gf = {
+const Vb = /([-\w]+)\s*=\s*"(.*?)"/g, Wb = /[\s\u200B]*[\n\r][\s\u200B]*/g, up = {
w: "lemma",
rb: "gloss",
xt: "link-href",
jmp: "link-href"
};
-function po(e) {
- return Gf[e];
+function yo(e) {
+ return up[e];
}
-const Ab = /* @__PURE__ */ new Set(["type", "marker", "content"]);
-function Pb(e, t) {
+const Hb = /* @__PURE__ */ new Set(["type", "marker", "content"]);
+function Gb(e, t) {
let r = 0;
for (const n of t) {
const i = n.index;
@@ -3543,26 +3544,26 @@ function Pb(e, t) {
}
return e.slice(r).trim() === "";
}
-function Wi(e, t, r = Gf[t]) {
- const n = e.replace(Eb, " "), i = /* @__PURE__ */ Object.create(null), s = [...n.matchAll(Mb)];
+function Xi(e, t, r = up[t]) {
+ const n = e.replace(Wb, " "), i = /* @__PURE__ */ Object.create(null), s = [...n.matchAll(Vb)];
if (s.length > 0) {
- if (!Pb(n, s) || s.some((o) => o[2] === ""))
+ if (!Gb(n, s) || s.some((o) => o[2] === ""))
return;
for (const [, o, a] of s)
- Ab.has(o) || (i[o] = a);
+ Hb.has(o) || (i[o] = a);
return Object.keys(i).length > 0 ? i : void 0;
}
if (n.trim() && r)
return { [r]: n };
}
-function ho(e) {
+function bo(e) {
return e.endsWith("-e") ? "eid" : e.startsWith("qt") ? "who" : "sid";
}
-function Nb(e) {
- const t = Mr(e)[0], r = typeof t == "object" && "content" in t ? t.content : void 0;
+function Jb(e) {
+ const t = Or(e)[0], r = typeof t == "object" && "content" in t ? t.content : void 0;
return r ? r.some((n, i) => typeof n != "object" || n.type !== "ms" || typeof r[i + 1] != "string" ? !1 : r.slice(i + 2).some((s) => typeof s != "string" && s.type === "unmatched" && s.marker === "*")) : !1;
}
-function Ob(e, t, r) {
+function Yb(e, t, r) {
let n = t;
for (; n < e.length && /[\s\u00A0\u200B]/.test(e[n]); )
n++;
@@ -3571,22 +3572,22 @@ function Ob(e, t, r) {
const i = e.indexOf("\\", n);
if (i === -1 || e.slice(i, i + 2) !== "\\*")
return;
- const s = Wi(e.slice(n + 1, i), r, ho(r));
+ const s = Xi(e.slice(n + 1, i), r, bo(r));
if (s)
return { attributes: s, next: i + 2 };
}
-function wb(e, t, r, n) {
+function Xb(e, t, r, n) {
const i = e.indexOf("\\", n);
if (i === -1 || e.slice(i, i + 2) !== "\\*")
return;
const s = e.slice(n, i), o = s.indexOf("|");
let a;
- if (o >= 0 && (a = Wi(s.slice(o + 1), r, ho(r)), !a && s.slice(o + 1).trim() !== "")) {
- const u = s.slice(0, o);
+ if (o >= 0 && (a = Xi(s.slice(o + 1), r, bo(r)), !a && s.slice(o + 1).trim() !== "")) {
+ const d = s.slice(0, o);
return {
token: { kind: "milestone", marker: r },
next: n + o,
- ejectedText: u.trim() !== "" ? u.replace(/^[ \u00A0]/, "") : void 0
+ ejectedText: d.trim() !== "" ? d.replace(/^[ \u00A0]/, "") : void 0
};
}
const c = o >= 0 ? s.slice(0, o) : s;
@@ -3596,7 +3597,7 @@ function wb(e, t, r, n) {
next: i,
ejectedText: c.replace(/^[ \u00A0]/, "")
};
- const l = Ob(e, i + 2, r);
+ const l = Yb(e, i + 2, r);
return l ? {
token: {
kind: "milestone",
@@ -3608,299 +3609,299 @@ function wb(e, t, r, n) {
}
function or(e) {
return e.replaceAll(`
-`, " ").replaceAll("~", I);
+`, " ").replaceAll("~", R);
}
-function Ir(e) {
+function Dr(e) {
return e.content || (e.content = []), e.content;
}
-function Mr(e, t) {
+function Or(e, t) {
const r = [], n = t?.isNoteContext ?? !1;
let i, s;
const o = [];
- let a = 0, c, l, u, d;
- const f = () => u ? Ir(u) : d ? Ir(d) : r;
+ let a = 0, c, l, d, u;
+ const f = () => d ? Dr(d) : u ? Dr(u) : r;
let p = !1;
- const m = () => {
+ const y = () => {
if (s)
- return o.length > a ? Ir(o[o.length - 1].object) : Ir(s);
+ return o.length > a ? Dr(o[o.length - 1].object) : Dr(s);
if (o.length > 0)
- return Ir(o[o.length - 1].object);
+ return Dr(o[o.length - 1].object);
if (!i) {
if (p && !n)
return f();
i = { type: "para", marker: cr, content: [] }, f().push(i);
}
- return Ir(i);
- }, g = (te) => {
- const E = m();
- typeof te == "string" && typeof E[E.length - 1] == "string" ? E[E.length - 1] = E[E.length - 1] + te : E.push(te);
- }, y = (te) => {
- for (let E = te; E < o.length; E += 1) {
- const Y = o[E].object;
- Y.closed = "false";
- }
- }, k = () => {
- y(0), o.length = 0;
- }, _ = (te) => {
- s && (o.length > a && (y(a), o.length = a), a = 0, te || (s.closed = "false"), s = void 0);
- }, S = () => {
+ return Dr(i);
+ }, h = (re) => {
+ const P = y();
+ typeof re == "string" && typeof P[P.length - 1] == "string" ? P[P.length - 1] = P[P.length - 1] + re : P.push(re);
+ }, m = (re) => {
+ for (let P = re; P < o.length; P += 1) {
+ const G = o[P].object;
+ G.closed = "false";
+ }
+ }, b = () => {
+ m(0), o.length = 0;
+ }, x = (re) => {
+ s && (o.length > a && (m(a), o.length = a), a = 0, re || (s.closed = "false"), s = void 0);
+ }, _ = () => {
c = void 0, l = void 0;
- }, P = (te, E, Y) => {
- k();
- const [, le, W, xe] = Y, ht = {
+ }, A = (re, P, G) => {
+ b();
+ const [, ue, Me, Q] = G, ve = {
type: "table:cell",
- marker: xe ? E.slice(0, E.indexOf("-")) : E,
- align: bb[le],
+ marker: Q ? P.slice(0, P.indexOf("-")) : P,
+ align: Ib[ue],
content: []
};
- xe && (ht.colspan = String(Number(xe) + 1 - Number(W))), Ir(te).push(ht), i = ht;
- }, A = (te) => {
- u && (te || (u.closed = "false"), u = void 0);
- }, B = () => {
- d = void 0;
+ Q && (ve.colspan = String(Number(Q) + 1 - Number(Me))), Dr(re).push(ve), i = ve;
+ }, E = (re) => {
+ d && (re || (d.closed = "false"), d = void 0);
+ }, D = () => {
+ u = void 0;
};
- let M, q = "", $;
- const J = () => {
- q && g(or(q)), q = "";
- }, Q = (te = !1) => {
- M?.type === "sidebar" ? q = "" : te && q.endsWith(`
-`) && (q = q.slice(0, -1)), M = void 0, J();
- }, Me = () => {
- if (!$)
+ let V, j = "", v;
+ const K = () => {
+ j && h(or(j)), j = "";
+ }, W = (re = !1) => {
+ V?.type === "sidebar" ? j = "" : re && j.endsWith(`
+`) && (j = j.slice(0, -1)), V = void 0, K();
+ }, fe = () => {
+ if (!v)
return;
- const te = { type: "char", marker: $.marker, content: [] };
- $.value && (te.content = [or($.value)]), m().push(te), o.push({ object: te }), $ = void 0;
- }, re = (te, E) => {
- p = !1, S(), k(), _(!1), i = { type: "para", marker: te, content: [] }, E && (i.content = [or(E)]), f().push(i);
- }, qe = () => {
- $ && (re($.marker, $.value), $ = void 0);
+ const re = { type: "char", marker: v.marker, content: [] };
+ v.value && (re.content = [or(v.value)]), y().push(re), o.push({ object: re }), v = void 0;
+ }, X = (re, P) => {
+ p = !1, _(), b(), x(!1), i = { type: "para", marker: re, content: [] }, P && (i.content = [or(P)]), f().push(i);
+ }, Re = () => {
+ v && (X(v.marker, v.value), v = void 0);
};
let be;
- const tr = (te) => {
+ const er = (re) => {
if (!be)
return;
- let { value: E } = be;
- be = void 0, te && E.endsWith(`
-`) && (E = E.slice(0, -1));
- const Y = E.indexOf("|"), le = Y >= 0 ? Wi(E.slice(Y + 1), du) : void 0, W = Y >= 0 ? E.slice(0, Y) : E, xe = Y >= 0 && (!le || !!W && !!le[fu]), ht = xe ? void 0 : le, zt = xe ? E : W, gt = {
+ let { value: P } = be;
+ be = void 0, re && P.endsWith(`
+`) && (P = P.slice(0, -1));
+ const G = P.indexOf("|"), ue = G >= 0 ? Xi(P.slice(G + 1), Cu) : void 0, Me = G >= 0 ? P.slice(0, G) : P, Q = G >= 0 && (!ue || !!Me && !!ue[vu]), ve = Q ? void 0 : ue, mr = Q ? P : Me, wt = {
type: "periph",
- ...zt ? { [fu]: or(zt) } : {},
- ...ht
+ ...mr ? { [vu]: or(mr) } : {},
+ ...ve
};
- gt.content = [], f().push(gt), d = gt, i = void 0;
+ wt.content = [], f().push(wt), u = wt, i = void 0;
};
- let Re;
- const en = () => {
- if (Re) {
- if (Re.shape === "para")
- re(cn, Re.value);
+ let $e;
+ const nn = () => {
+ if ($e) {
+ if ($e.shape === "para")
+ X(un, $e.value);
else {
- const te = { type: "char", marker: cn, content: [] };
- Re.value && (te.content = [or(Re.value)]), m().push(te), o.push({ object: te });
+ const re = { type: "char", marker: un, content: [] };
+ $e.value && (re.content = [or($e.value)]), y().push(re), o.push({ object: re });
}
- Re = void 0;
- }
- }, gr = Sb(e, t?.getMarker ?? lr, n);
- for (let te = 0; te < gr.length; te++) {
- const E = gr[te];
- if ($) {
- if (E.kind === "text") {
- $.value += E.text;
+ $e = void 0;
+ }
+ }, gr = jb(e, t?.getMarker ?? lr, n);
+ for (let re = 0; re < gr.length; re++) {
+ const P = gr[re];
+ if (v) {
+ if (P.kind === "text") {
+ v.value += P.text;
continue;
}
- if ($.shape === "char" && E.kind === "end" && E.marker.replace(/^\+/, "") === $.marker) {
- if ($.value.trim() === "") {
- m().push({ type: "char", marker: $.marker, content: [] }), $ = void 0, Q();
+ if (v.shape === "char" && P.kind === "end" && P.marker.replace(/^\+/, "") === v.marker) {
+ if (v.value.trim() === "") {
+ y().push({ type: "char", marker: v.marker, content: [] }), v = void 0, W();
continue;
}
- Object.assign($.target, {
- [$.attrName]: or($.value.trim())
+ Object.assign(v.target, {
+ [v.attrName]: or(v.value.trim())
});
- const Y = $.marker;
- if ($ = void 0, Y === "ca") {
- const le = gr[te + 1];
- le?.kind === "text" && /^[\s\u200B]*$/.test(le.text) && te++;
+ const G = v.marker;
+ if (v = void 0, G === "ca") {
+ const ue = gr[re + 1];
+ ue?.kind === "text" && /^[\s\u200B]*$/.test(ue.text) && re++;
}
continue;
}
- if ($.shape === "para" && (E.kind === "para" || E.kind === "chapter")) {
- const Y = $.value.replace(/[\s\u200B]+$/, "");
- Y === "" ? (re($.marker), $ = void 0) : (Object.assign($.target, { [$.attrName]: or(Y) }), $ = void 0);
+ if (v.shape === "para" && (P.kind === "para" || P.kind === "chapter")) {
+ const G = v.value.replace(/[\s\u200B]+$/, "");
+ G === "" ? (X(v.marker), v = void 0) : (Object.assign(v.target, { [v.attrName]: or(G) }), v = void 0);
} else {
- M = void 0, (E.kind === "para" || E.kind === "chapter") && $.value.endsWith(`
-`) && ($.value = $.value.slice(0, -1)), $.shape === "para" ? qe() : Me(), te--;
+ V = void 0, (P.kind === "para" || P.kind === "chapter") && v.value.endsWith(`
+`) && (v.value = v.value.slice(0, -1)), v.shape === "para" ? Re() : fe(), re--;
continue;
}
}
if (be) {
- if (E.kind === "text" || E.kind === "optbreak") {
- be.value += E.kind === "text" ? E.text : "//";
+ if (P.kind === "text" || P.kind === "optbreak") {
+ be.value += P.kind === "text" ? P.text : "//";
continue;
}
- tr(E.kind === "para" || E.kind === "chapter"), te--;
+ er(P.kind === "para" || P.kind === "chapter"), re--;
continue;
}
- if (Re) {
- if (E.kind === "text" || E.kind === "optbreak") {
- Re.value += E.kind === "text" ? E.text : "//";
+ if ($e) {
+ if (P.kind === "text" || P.kind === "optbreak") {
+ $e.value += P.kind === "text" ? P.text : "//";
continue;
}
- if (E.kind === "end" && E.marker.replace(/^\+/, "") === cn) {
- const Y = Re.value.indexOf("|"), le = Y >= 0 ? Wi(Re.value.slice(Y + 1), cn) : void 0;
- if (le) {
- const W = {};
- for (const [zt, gt] of Object.entries(le))
- W[zt === "src" ? "file" : zt] = gt;
- const xe = {
+ if (P.kind === "end" && P.marker.replace(/^\+/, "") === un) {
+ const G = $e.value.indexOf("|"), ue = G >= 0 ? Xi($e.value.slice(G + 1), un) : void 0;
+ if (ue) {
+ const Me = {};
+ for (const [mr, wt] of Object.entries(ue))
+ Me[mr === "src" ? "file" : mr] = wt;
+ const Q = {
type: "figure",
- marker: cn,
- ...W
- }, ht = Re.value.slice(0, Y);
- ht && (xe.content = [or(ht)]), g(xe), Re = void 0;
+ marker: un,
+ ...Me
+ }, ve = $e.value.slice(0, G);
+ ve && (Q.content = [or(ve)]), h(Q), $e = void 0;
continue;
}
}
- en(), te--;
+ nn(), re--;
continue;
}
- if (M)
- if (E.kind === "text") {
- if (E.text.includes(`
-`) && /^[\s\u200B]*$/.test(E.text)) {
- q += E.text;
+ if (V)
+ if (P.kind === "text") {
+ if (P.text.includes(`
+`) && /^[\s\u200B]*$/.test(P.text)) {
+ j += P.text;
continue;
}
- Q();
- } else if (E.kind === "charOpen" || E.kind === "para") {
- const Y = E.kind === "para" || !E.isNested ? Ds(E.marker) : void 0;
- if (Y && Y.targetTypes.includes(M.type)) {
- q = "", $ = {
- target: M,
- attrName: Y.attrName,
- marker: E.marker,
- shape: Y.shape,
+ W();
+ } else if (P.kind === "charOpen" || P.kind === "para") {
+ const G = P.kind === "para" || !P.isNested ? Ks(P.marker) : void 0;
+ if (G && G.targetTypes.includes(V.type)) {
+ j = "", v = {
+ target: V,
+ attrName: G.attrName,
+ marker: P.marker,
+ shape: G.shape,
value: ""
};
continue;
}
- Q(E.kind === "para");
+ W(P.kind === "para");
} else
- Q(E.kind === "chapter");
- if (!s && !n && (E.kind === "charOpen" && !E.isNested && E.marker === cn || E.kind === "para" && E.marker === cn)) {
- k(), Re = { shape: E.kind === "charOpen" ? "char" : "para", value: "" };
+ W(P.kind === "chapter");
+ if (!s && !n && (P.kind === "charOpen" && !P.isNested && P.marker === un || P.kind === "para" && P.marker === un)) {
+ b(), $e = { shape: P.kind === "charOpen" ? "char" : "para", value: "" };
continue;
}
- switch (E.kind) {
+ switch (P.kind) {
case "text": {
- let Y = E.text;
- if (!s && Y.endsWith(`
+ let G = P.text;
+ if (!s && G.endsWith(`
`)) {
- const le = gr[te + 1];
- (le === void 0 || le.kind === "para" || le.kind === "chapter") && (Y = Y.slice(0, -1));
+ const ue = gr[re + 1];
+ (ue === void 0 || ue.kind === "para" || ue.kind === "chapter") && (G = G.slice(0, -1));
}
- Y && g(or(Y));
+ G && h(or(G));
break;
}
case "para": {
- const Y = !s && !n;
- if (Y && E.marker === uu) {
- k(), c || (c = { type: "table", content: [] }, f().push(c)), l = { type: "table:row", marker: uu, content: [] }, Ir(c).push(l), i = l, p = !1;
+ const G = !s && !n;
+ if (G && P.marker === _u) {
+ b(), c || (c = { type: "table", content: [] }, f().push(c)), l = { type: "table:row", marker: _u, content: [] }, Dr(c).push(l), i = l, p = !1;
break;
}
- if (Y && l) {
- const le = pu.exec(E.marker);
- if (le && hu(le)) {
- P(l, E.marker, le);
+ if (G && l) {
+ const ue = Su.exec(P.marker);
+ if (ue && Mu(ue)) {
+ A(l, P.marker, ue);
break;
}
}
- if (S(), !n && E.marker === Ma) {
- k(), _(!1), A(!1);
- const le = {
+ if (_(), !n && P.marker === Oa) {
+ b(), x(!1), E(!1);
+ const ue = {
type: "sidebar",
- marker: Ma,
+ marker: Oa,
content: []
};
- f().push(le), u = le, i = void 0, M = u, p = !1;
+ f().push(ue), d = ue, i = void 0, V = d, p = !1;
break;
}
- if (E.marker === Hf && u) {
- k(), _(!1), A(!0), i = void 0;
+ if (P.marker === lp && d) {
+ b(), x(!1), E(!0), i = void 0;
break;
}
- if (!n && E.marker === du) {
- k(), _(!1), A(!1), B(), be = { value: "" }, i = void 0, p = !1;
+ if (!n && P.marker === Cu) {
+ b(), x(!1), E(!1), D(), be = { value: "" }, i = void 0, p = !1;
break;
}
- re(E.marker);
+ X(P.marker);
break;
}
case "verse": {
- _(!1);
- const Y = { type: "verse", marker: Vf, number: E.number };
- g(Y), M = Y;
+ x(!1);
+ const G = { type: "verse", marker: ap, number: P.number };
+ h(G), V = G;
break;
}
case "chapter": {
- k(), _(!1), S(), A(!1), B(), i = void 0;
- const Y = {
+ b(), x(!1), _(), E(!1), D(), i = void 0;
+ const G = {
type: "chapter",
- marker: Wf,
- number: E.number
+ marker: cp,
+ number: P.number
};
- r.push(Y), M = Y, p = !0;
+ r.push(G), V = G, p = !0;
break;
}
case "note": {
- _(!1);
- const Y = m();
- s = { type: "note", marker: E.marker, caller: E.caller, content: [] }, a = o.length, Y.push(s), M = s;
+ x(!1);
+ const G = y();
+ s = { type: "note", marker: P.marker, caller: P.caller, content: [] }, a = o.length, G.push(s), V = s;
break;
}
case "charOpen": {
- if (!s && !n && l && !E.isNested) {
- const W = pu.exec(E.marker);
- if (W && hu(W)) {
- P(l, E.marker, W);
+ if (!s && !n && l && !P.isNested) {
+ const Me = Su.exec(P.marker);
+ if (Me && Mu(Me)) {
+ A(l, P.marker, Me);
break;
}
}
- if (!E.isNested) {
- const W = s ? a : 0;
- y(W), o.length = W;
+ if (!P.isNested) {
+ const Me = s ? a : 0;
+ m(Me), o.length = Me;
}
- const Y = m(), le = { type: "char", marker: E.marker, content: [] };
- Y.push(le), o.push({ object: le });
+ const G = y(), ue = { type: "char", marker: P.marker, content: [] };
+ G.push(ue), o.push({ object: ue });
break;
}
case "end": {
- const Y = E.marker.replace(/^\+/, ""), le = s ? a : 0, W = o.findLastIndex((xe, ht) => ht >= le && xe.object.marker === Y);
- W >= 0 ? (qb(o[W].object), y(W + 1), o.length = W) : s && s.marker === Y ? _(!0) : (y(le), o.length = le, g({ type: "unmatched", marker: `${E.marker}*` }));
+ const G = P.marker.replace(/^\+/, ""), ue = s ? a : 0, Me = o.findLastIndex((Q, ve) => ve >= ue && Q.object.marker === G);
+ Me >= 0 ? (Qb(o[Me].object), m(Me + 1), o.length = Me) : s && s.marker === G ? x(!0) : (m(ue), o.length = ue, h({ type: "unmatched", marker: `${P.marker}*` }));
break;
}
case "milestone":
- g({ type: "ms", marker: E.marker, ...E.attributes });
+ h({ type: "ms", marker: P.marker, ...P.attributes });
break;
case "optbreak":
- g({ type: "optbreak" });
+ h({ type: "optbreak" });
break;
}
}
- if (be && tr(!0), Re && en(), $)
- if ($.shape === "para") {
- const te = $.value.replace(/[\s\u200B]+$/, "");
- te === "" ? re($.marker) : Object.assign($.target, { [$.attrName]: or(te) }), $ = void 0;
+ if (be && er(!0), $e && nn(), v)
+ if (v.shape === "para") {
+ const re = v.value.replace(/[\s\u200B]+$/, "");
+ re === "" ? X(v.marker) : Object.assign(v.target, { [v.attrName]: or(re) }), v = void 0;
} else
- $.value.endsWith(`
-`) && ($.value = $.value.slice(0, -1)), Me();
- k(), _(!1), A(!1);
- const vt = (te) => {
- for (const E of te)
- typeof E != "string" && E.content && (vt(E.content), E.content.length === 0 && delete E.content);
+ v.value.endsWith(`
+`) && (v.value = v.value.slice(0, -1)), fe();
+ b(), x(!1), E(!1);
+ const Mt = (re) => {
+ for (const P of re)
+ typeof P != "string" && P.content && (Mt(P.content), P.content.length === 0 && delete P.content);
};
- return vt(r), r;
+ return Mt(r), r;
}
-function qb(e) {
+function Qb(e) {
const t = e.content;
if (!t || t.length === 0)
return;
@@ -3914,22 +3915,22 @@ function qb(e) {
const n = t.findIndex((c, l) => l >= r && typeof c == "string" && c.includes("|"));
if (n < 0)
return;
- const i = t.slice(n).map((c) => typeof c == "string" ? c : "//").join(""), s = i.indexOf("|"), o = Wi(i.slice(s + 1), e.marker ?? "");
+ const i = t.slice(n).map((c) => typeof c == "string" ? c : "//").join(""), s = i.indexOf("|"), o = Xi(i.slice(s + 1), e.marker ?? "");
if (!o)
return;
const a = i.slice(0, s);
t.length = n, a && t.push(a), Object.assign(e, o);
}
-const kn = co("cid", {
+const Tn = ho("cid", {
parse: (e) => typeof e == "string" ? e : void 0
-}), Vr = co("segment", {
+}), Gr = ho("segment", {
parse: (e) => typeof e == "string" ? e : void 0
-}), oe = co("textType", {
+}), oe = ho("textType", {
parse: (e) => typeof e == "string" ? e : void 0
-}), fr = "marker-trailing-space", Jf = 1, Rb = "marker", Pc = co("isGutterMarker", {
+}), fr = "marker-trailing-space", dp = 1, Zb = "marker", Dc = ho("isGutterMarker", {
parse: (e) => e === !0
});
-class Er extends ls {
+class wr extends hs {
__textType;
__text;
constructor(t = "", r = "", n) {
@@ -3940,18 +3941,18 @@ class Er extends ls {
}
static clone(t) {
const { __textType: r, __text: n, __key: i } = t;
- return new Er(r, n, i);
+ return new wr(r, n, i);
}
static importDOM() {
return {
- span: (t) => Db(t) ? {
- conversion: $b,
+ span: (t) => nk(t) ? {
+ conversion: ek,
priority: 1
} : null
};
}
static importJSON(t) {
- return Tr().updateFromJSON(t);
+ return Sr().updateFromJSON(t);
}
updateFromJSON(t) {
return super.updateFromJSON(t).setTextType(t.textType).setTextContent(t.text);
@@ -3983,7 +3984,7 @@ class Er extends ls {
}
exportDOM(t) {
const { element: r } = super.exportDOM(t);
- return r && An(r) && r.setAttribute("data-text-type", this.getTextType()), { element: r };
+ return r && Pn(r) && r.setAttribute("data-text-type", this.getTextType()), { element: r };
}
/**
* No decorator payload: the glyph bytes are rendered by {@link createDOM} instead.
@@ -4019,7 +4020,7 @@ class Er extends ls {
type: this.getType(),
textType: this.getTextType(),
text: this.getTextContent(),
- version: Jf
+ version: dp
};
}
// Mutation
@@ -4027,30 +4028,30 @@ class Er extends ls {
return !1;
}
}
-function $b(e) {
+function ek(e) {
const t = e.getAttribute("data-text-type") ?? "", r = e.textContent ?? "";
- return { node: Tr(t, r) };
+ return { node: Sr(t, r) };
}
-function Tr(e, t) {
- return je(new Er(e, t));
+function Sr(e, t) {
+ return Ve(new wr(e, t));
}
-function Ib(e) {
- return bt(Tr(Rb, e), Pc, !0);
+function tk(e) {
+ return kt(Sr(Zb, e), Dc, !0);
}
-function Lb(e) {
- return Yt(e) && ne(e, Pc);
+function rk(e) {
+ return Jt(e) && ne(e, Dc);
}
-function Db(e) {
+function nk(e) {
return e?.tagName === "span";
}
-function Yt(e) {
- return e instanceof Er;
+function Jt(e) {
+ return e instanceof wr;
}
-function Yf(e) {
- return e?.type === Er.getType();
+function fp(e) {
+ return e?.type === wr.getType();
}
-const Kr = "internal-comment", Ub = [Kr], Xf = Object.freeze({}), Ea = Object.freeze({}), Aa = Object.freeze({}), Pa = Object.freeze({}), Na = Object.freeze({}), Fb = 1, Fn = /* @__PURE__ */ new Map(), vi = /* @__PURE__ */ new Map(), zn = /* @__PURE__ */ new Map(), Kn = /* @__PURE__ */ new Map();
-class Qe extends Zt {
+const Br = "internal-comment", ik = [Br], pp = Object.freeze({}), wa = Object.freeze({}), qa = Object.freeze({}), Ra = Object.freeze({}), $a = Object.freeze({}), sk = 1, Bn = /* @__PURE__ */ new Map(), Ni = /* @__PURE__ */ new Map(), Vn = /* @__PURE__ */ new Map(), Wn = /* @__PURE__ */ new Map();
+class tt extends Qt {
__typedIDs;
__typedOnClicks;
__typedOnRemoves;
@@ -4060,39 +4061,39 @@ class Qe extends Zt {
__domOnMouseEnterListener;
__domOnMouseLeaveListener;
__suppressOnRemoveCallbacks;
- constructor(t = Xf, r, n, i, s, o) {
- super(o), this.__typedIDs = Ss(t), this.__typedOnClicks = Yo(r), this.__typedOnRemoves = Xo(n), this.__typedOnMouseEnters = Qo(i), this.__typedOnMouseLeaves = Zo(s), this.pruneTypedOnClicks(), this.pruneTypedOnRemoves(), this.pruneTypedOnMouseEnters(), this.pruneTypedOnMouseLeaves(), this.syncTypedOnClicksToRegistry(), this.syncTypedOnRemovesToRegistry(), this.syncTypedOnMouseEntersToRegistry(), this.syncTypedOnMouseLeavesToRegistry();
+ constructor(t = pp, r, n, i, s, o) {
+ super(o), this.__typedIDs = Ps(t), this.__typedOnClicks = Zo(r), this.__typedOnRemoves = ea(n), this.__typedOnMouseEnters = ta(i), this.__typedOnMouseLeaves = ra(s), this.pruneTypedOnClicks(), this.pruneTypedOnRemoves(), this.pruneTypedOnMouseEnters(), this.pruneTypedOnMouseLeaves(), this.syncTypedOnClicksToRegistry(), this.syncTypedOnRemovesToRegistry(), this.syncTypedOnMouseEntersToRegistry(), this.syncTypedOnMouseLeavesToRegistry();
}
static getType() {
return "typed-mark";
}
static clone(t) {
- const r = Ss(t.__typedIDs), n = Yo(t.__typedOnClicks), i = Xo(t.__typedOnRemoves), s = Qo(t.__typedOnMouseEnters), o = Zo(t.__typedOnMouseLeaves);
- return new Qe(r, n, i, s, o, t.__key);
+ const r = Ps(t.__typedIDs), n = Zo(t.__typedOnClicks), i = ea(t.__typedOnRemoves), s = ta(t.__typedOnMouseEnters), o = ra(t.__typedOnMouseLeaves);
+ return new tt(r, n, i, s, o, t.__key);
}
static isReservedType(t) {
- return Ub.includes(t);
+ return ik.includes(t);
}
static importDOM() {
return null;
}
static importJSON(t) {
- return Hi().updateFromJSON(t);
+ return Qi().updateFromJSON(t);
}
exportJSON() {
return {
...super.exportJSON(),
type: this.getType(),
typedIDs: this.getTypedIDs(),
- version: Fb
+ version: sk
};
}
createDOM(t, r) {
const n = document.createElement("mark");
for (const [a, c] of Object.entries(this.__typedIDs)) {
- Un(n, ln(t.theme.typedMark, a)), c.length > 1 && Un(n, ln(t.theme.typedMarkOverlap, a));
+ jn(n, dn(t.theme.typedMark, a)), c.length > 1 && jn(n, dn(t.theme.typedMarkOverlap, a));
for (const l of c)
- Un(n, ln("annotationId", l));
+ jn(n, dn("annotationId", l));
}
const i = this.getOrCreateDOMClickListener(r);
n.addEventListener("click", i);
@@ -4107,13 +4108,13 @@ class Qe extends Zt {
...Object.keys(this.__typedIDs ?? {})
]);
for (const s of i) {
- const o = t.__typedIDs[s] ?? [], a = this.__typedIDs[s] ?? [], c = o.length, l = a.length, u = ln(n.theme.typedMark, s), d = ln(n.theme.typedMarkOverlap, s);
- c !== l && (c === 0 ? l === 1 && Un(r, u) : l === 0 && Ho(r, u), c === 1 ? l === 2 && Un(r, d) : l === 1 && Ho(r, d));
+ const o = t.__typedIDs[s] ?? [], a = this.__typedIDs[s] ?? [], c = o.length, l = a.length, d = dn(n.theme.typedMark, s), u = dn(n.theme.typedMarkOverlap, s);
+ c !== l && (c === 0 ? l === 1 && jn(r, d) : l === 0 && Yo(r, d), c === 1 ? l === 2 && jn(r, u) : l === 1 && Yo(r, u));
const f = new Set(o), p = new Set(a);
- for (const m of o)
- p.has(m) || Ho(r, ln("annotationId", m));
- for (const m of a)
- f.has(m) || Un(r, ln("annotationId", m));
+ for (const y of o)
+ p.has(y) || Yo(r, dn("annotationId", y));
+ for (const y of a)
+ f.has(y) || jn(r, dn("annotationId", y));
}
return !1;
}
@@ -4131,51 +4132,51 @@ class Qe extends Zt {
}
getTypedIDs() {
const t = this.getLatest();
- return Ce(t) ? t.__typedIDs : {};
+ return _e(t) ? t.__typedIDs : {};
}
setTypedIDs(t) {
- const r = this.getWritable(), n = Ss(r.__typedIDs);
- r.__typedIDs = Ss(t), r.dispatchRemovedIDs(n, r.__typedIDs, "removed"), r.pruneTypedOnClicks(), r.pruneTypedOnRemoves(), r.pruneTypedOnMouseEnters(), r.pruneTypedOnMouseLeaves(), r.syncTypedOnClicksToRegistry(), r.syncTypedOnRemovesToRegistry(), r.syncTypedOnMouseEntersToRegistry(), r.syncTypedOnMouseLeavesToRegistry();
+ const r = this.getWritable(), n = Ps(r.__typedIDs);
+ r.__typedIDs = Ps(t), r.dispatchRemovedIDs(n, r.__typedIDs, "removed"), r.pruneTypedOnClicks(), r.pruneTypedOnRemoves(), r.pruneTypedOnMouseEnters(), r.pruneTypedOnMouseLeaves(), r.syncTypedOnClicksToRegistry(), r.syncTypedOnRemovesToRegistry(), r.syncTypedOnMouseEntersToRegistry(), r.syncTypedOnMouseLeavesToRegistry();
const i = r.mergeWithAdjacentTypedMarks();
- return i.hasNoIDsForEveryType() && i.getParent() !== null && Us(i), i;
+ return i.hasNoIDsForEveryType() && i.getParent() !== null && js(i), i;
}
setTypedOnClicks(t) {
const r = this.getWritable();
- return r.__typedOnClicks = Yo(t), r.pruneTypedOnClicks(), r.syncTypedOnClicksToRegistry(), r;
+ return r.__typedOnClicks = Zo(t), r.pruneTypedOnClicks(), r.syncTypedOnClicksToRegistry(), r;
}
getTypedOnClicks() {
const t = this.getLatest();
- return Ce(t) ? Fn.get(t.getKey()) ?? {} : {};
+ return _e(t) ? Bn.get(t.getKey()) ?? {} : {};
}
setTypedOnRemoves(t) {
const r = this.getWritable();
- return r.__typedOnRemoves = Xo(t), r.pruneTypedOnRemoves(), r.syncTypedOnRemovesToRegistry(), r;
+ return r.__typedOnRemoves = ea(t), r.pruneTypedOnRemoves(), r.syncTypedOnRemovesToRegistry(), r;
}
getTypedOnRemoves() {
const t = this.getLatest();
- return Ce(t) ? vi.get(t.getKey()) ?? {} : {};
+ return _e(t) ? Ni.get(t.getKey()) ?? {} : {};
}
setTypedOnMouseEnters(t) {
const r = this.getWritable();
- return r.__typedOnMouseEnters = Qo(t), r.pruneTypedOnMouseEnters(), r.syncTypedOnMouseEntersToRegistry(), r;
+ return r.__typedOnMouseEnters = ta(t), r.pruneTypedOnMouseEnters(), r.syncTypedOnMouseEntersToRegistry(), r;
}
getTypedOnMouseEnters() {
const t = this.getLatest();
- return Ce(t) ? zn.get(t.getKey()) ?? {} : {};
+ return _e(t) ? Vn.get(t.getKey()) ?? {} : {};
}
setTypedOnMouseLeaves(t) {
const r = this.getWritable();
- return r.__typedOnMouseLeaves = Zo(t), r.pruneTypedOnMouseLeaves(), r.syncTypedOnMouseLeavesToRegistry(), r;
+ return r.__typedOnMouseLeaves = ra(t), r.pruneTypedOnMouseLeaves(), r.syncTypedOnMouseLeavesToRegistry(), r;
}
getTypedOnMouseLeaves() {
const t = this.getLatest();
- return Ce(t) ? Kn.get(t.getKey()) ?? {} : {};
+ return _e(t) ? Wn.get(t.getKey()) ?? {} : {};
}
addID(t, r, n, i, s, o) {
const a = this.getWritable();
- if (!Ce(a))
+ if (!_e(a))
return;
- He(t), He(r);
+ Je(t), Je(r);
let c = a.__typedIDs[t];
c || (c = [], a.__typedIDs[t] = c);
for (const l of c)
@@ -4187,7 +4188,7 @@ class Qe extends Zt {
}
deleteID(t, r) {
const n = this.getWritable();
- if (!Ce(n))
+ if (!_e(n))
return;
const i = n.__typedIDs[t];
if (!i || i.length === 0)
@@ -4199,13 +4200,13 @@ class Qe extends Zt {
}
n.removeOnClickFor(t, r), n.removeOnRemoveFor(t, r), n.removeOnMouseEnterFor(t, r), n.removeOnMouseLeaveFor(t, r), n.pruneTypedOnClicks(), n.pruneTypedOnRemoves(), n.pruneTypedOnMouseEnters(), n.pruneTypedOnMouseLeaves();
const s = n.mergeWithAdjacentTypedMarks();
- s.hasNoIDsForEveryType() && s.getParent() !== null && Us(s);
+ s.hasNoIDsForEveryType() && s.getParent() !== null && js(s);
}
hasNoIDsForEveryType() {
return Object.values(this.getTypedIDs()).every((t) => t === void 0 || t.length === 0);
}
insertNewAfter(t, r = !0) {
- const n = Hi(this.__typedIDs, this.getTypedOnClicks());
+ const n = Qi(this.__typedIDs, this.getTypedOnClicks());
return this.insertAfter(n, r), n;
}
canInsertTextBefore() {
@@ -4231,7 +4232,7 @@ class Qe extends Zt {
}
remove(t) {
const r = this.getWritable(), n = this.getTypedIDs();
- r.__suppressOnRemoveCallbacks ? r.__suppressOnRemoveCallbacks = void 0 : r.dispatchOnRemoveForTypedIDs(n, "destroyed"), Fn.delete(r.getKey()), vi.delete(r.getKey()), zn.delete(r.getKey()), Kn.delete(r.getKey()), r.__typedOnClicks = void 0, r.__typedOnRemoves = void 0, r.__typedOnMouseEnters = void 0, r.__typedOnMouseLeaves = void 0, super.remove.call(r, t);
+ r.__suppressOnRemoveCallbacks ? r.__suppressOnRemoveCallbacks = void 0 : r.dispatchOnRemoveForTypedIDs(n, "destroyed"), Bn.delete(r.getKey()), Ni.delete(r.getKey()), Vn.delete(r.getKey()), Wn.delete(r.getKey()), r.__typedOnClicks = void 0, r.__typedOnRemoves = void 0, r.__typedOnMouseEnters = void 0, r.__typedOnMouseLeaves = void 0, super.remove.call(r, t);
}
getOrCreateDOMClickListener(t) {
return this.__domOnClickListener || (this.__domOnClickListener = (r) => {
@@ -4239,7 +4240,7 @@ class Qe extends Zt {
}), this.__domOnClickListener;
}
handleDOMClick(t, r) {
- const n = Fn.get(this.getKey());
+ const n = Bn.get(this.getKey());
if (!n)
return;
const i = [];
@@ -4258,7 +4259,7 @@ class Qe extends Zt {
}), this.__domOnMouseEnterListener;
}
handleDOMMouseEnter(t, r) {
- const n = zn.get(this.getKey());
+ const n = Vn.get(this.getKey());
if (!n)
return;
const i = [];
@@ -4277,7 +4278,7 @@ class Qe extends Zt {
}), this.__domOnMouseLeaveListener;
}
handleDOMMouseLeave(t, r) {
- const n = Kn.get(this.getKey());
+ const n = Wn.get(this.getKey());
if (!n)
return;
const i = [];
@@ -4291,21 +4292,21 @@ class Qe extends Zt {
o(t, a, c, s);
}
ensureOnClickMapMutable() {
- if (this.__typedOnClicks === void 0 || this.__typedOnClicks === Ea) {
- const t = Fn.get(this.getKey());
+ if (this.__typedOnClicks === void 0 || this.__typedOnClicks === wa) {
+ const t = Bn.get(this.getKey());
this.__typedOnClicks = t ?? {};
}
return this.__typedOnClicks;
}
syncTypedOnClicksToRegistry() {
if (!this.__typedOnClicks || Object.keys(this.__typedOnClicks).length === 0) {
- Fn.delete(this.getKey()), this.__typedOnClicks && Object.keys(this.__typedOnClicks).length === 0 && (this.__typedOnClicks = void 0);
+ Bn.delete(this.getKey()), this.__typedOnClicks && Object.keys(this.__typedOnClicks).length === 0 && (this.__typedOnClicks = void 0);
return;
}
- Fn.set(this.getKey(), this.__typedOnClicks);
+ Bn.set(this.getKey(), this.__typedOnClicks);
}
setOnClickFor(t, r, n) {
- He(t), He(r);
+ Je(t), Je(r);
const i = this.ensureOnClickMapMutable(), s = i[t] ?? (i[t] = {});
s[r] = n, this.syncTypedOnClicksToRegistry();
}
@@ -4315,20 +4316,20 @@ class Qe extends Zt {
const n = this.__typedOnClicks[t];
if (!n)
return;
- const i = Lr(n, r);
+ const i = Ur(n, r);
if (Object.keys(i).length > 0)
this.__typedOnClicks = {
...this.__typedOnClicks,
[t]: i
};
else {
- const o = Lr(this.__typedOnClicks, t);
+ const o = Ur(this.__typedOnClicks, t);
this.__typedOnClicks = Object.keys(o).length > 0 ? o : void 0;
}
this.syncTypedOnClicksToRegistry();
}
pruneTypedOnClicks() {
- if (!this.__typedOnClicks || this.__typedOnClicks === Ea) {
+ if (!this.__typedOnClicks || this.__typedOnClicks === wa) {
this.__typedOnClicks = void 0, this.syncTypedOnClicksToRegistry();
return;
}
@@ -4345,21 +4346,21 @@ class Qe extends Zt {
this.__typedOnClicks = Object.keys(t).length > 0 ? t : void 0, this.syncTypedOnClicksToRegistry();
}
ensureOnRemoveMapMutable() {
- if (this.__typedOnRemoves === void 0 || this.__typedOnRemoves === Aa) {
- const t = vi.get(this.getKey());
+ if (this.__typedOnRemoves === void 0 || this.__typedOnRemoves === qa) {
+ const t = Ni.get(this.getKey());
this.__typedOnRemoves = t ?? {};
}
return this.__typedOnRemoves;
}
syncTypedOnRemovesToRegistry() {
if (!this.__typedOnRemoves || Object.keys(this.__typedOnRemoves).length === 0) {
- vi.delete(this.getKey()), this.__typedOnRemoves && Object.keys(this.__typedOnRemoves).length === 0 && (this.__typedOnRemoves = void 0);
+ Ni.delete(this.getKey()), this.__typedOnRemoves && Object.keys(this.__typedOnRemoves).length === 0 && (this.__typedOnRemoves = void 0);
return;
}
- vi.set(this.getKey(), this.__typedOnRemoves);
+ Ni.set(this.getKey(), this.__typedOnRemoves);
}
setOnRemoveFor(t, r, n) {
- He(t), He(r);
+ Je(t), Je(r);
const i = this.ensureOnRemoveMapMutable(), s = i[t] ?? (i[t] = {});
s[r] = n, this.syncTypedOnRemovesToRegistry();
}
@@ -4369,20 +4370,20 @@ class Qe extends Zt {
const n = this.__typedOnRemoves[t];
if (!n)
return;
- const i = Lr(n, r);
+ const i = Ur(n, r);
if (Object.keys(i).length > 0)
this.__typedOnRemoves = {
...this.__typedOnRemoves,
[t]: i
};
else {
- const o = Lr(this.__typedOnRemoves, t);
+ const o = Ur(this.__typedOnRemoves, t);
this.__typedOnRemoves = Object.keys(o).length > 0 ? o : void 0;
}
this.syncTypedOnRemovesToRegistry();
}
pruneTypedOnRemoves() {
- if (!this.__typedOnRemoves || this.__typedOnRemoves === Aa) {
+ if (!this.__typedOnRemoves || this.__typedOnRemoves === qa) {
this.__typedOnRemoves = void 0, this.syncTypedOnRemovesToRegistry();
return;
}
@@ -4399,21 +4400,21 @@ class Qe extends Zt {
this.__typedOnRemoves = Object.keys(t).length > 0 ? t : void 0, this.syncTypedOnRemovesToRegistry();
}
ensureOnMouseEnterMapMutable() {
- if (this.__typedOnMouseEnters === void 0 || this.__typedOnMouseEnters === Pa) {
- const t = zn.get(this.getKey());
+ if (this.__typedOnMouseEnters === void 0 || this.__typedOnMouseEnters === Ra) {
+ const t = Vn.get(this.getKey());
this.__typedOnMouseEnters = t ?? {};
}
return this.__typedOnMouseEnters;
}
syncTypedOnMouseEntersToRegistry() {
if (!this.__typedOnMouseEnters || Object.keys(this.__typedOnMouseEnters).length === 0) {
- zn.delete(this.getKey()), this.__typedOnMouseEnters && Object.keys(this.__typedOnMouseEnters).length === 0 && (this.__typedOnMouseEnters = void 0);
+ Vn.delete(this.getKey()), this.__typedOnMouseEnters && Object.keys(this.__typedOnMouseEnters).length === 0 && (this.__typedOnMouseEnters = void 0);
return;
}
- zn.set(this.getKey(), this.__typedOnMouseEnters);
+ Vn.set(this.getKey(), this.__typedOnMouseEnters);
}
setOnMouseEnterFor(t, r, n) {
- He(t), He(r);
+ Je(t), Je(r);
const i = this.ensureOnMouseEnterMapMutable(), s = i[t] ?? (i[t] = {});
s[r] = n, this.syncTypedOnMouseEntersToRegistry();
}
@@ -4423,20 +4424,20 @@ class Qe extends Zt {
const n = this.__typedOnMouseEnters[t];
if (!n)
return;
- const i = Lr(n, r);
+ const i = Ur(n, r);
if (Object.keys(i).length > 0)
this.__typedOnMouseEnters = {
...this.__typedOnMouseEnters,
[t]: i
};
else {
- const o = Lr(this.__typedOnMouseEnters, t);
+ const o = Ur(this.__typedOnMouseEnters, t);
this.__typedOnMouseEnters = Object.keys(o).length > 0 ? o : void 0;
}
this.syncTypedOnMouseEntersToRegistry();
}
pruneTypedOnMouseEnters() {
- if (!this.__typedOnMouseEnters || this.__typedOnMouseEnters === Pa) {
+ if (!this.__typedOnMouseEnters || this.__typedOnMouseEnters === Ra) {
this.__typedOnMouseEnters = void 0, this.syncTypedOnMouseEntersToRegistry();
return;
}
@@ -4453,21 +4454,21 @@ class Qe extends Zt {
this.__typedOnMouseEnters = Object.keys(t).length > 0 ? t : void 0, this.syncTypedOnMouseEntersToRegistry();
}
ensureOnMouseLeaveMapMutable() {
- if (this.__typedOnMouseLeaves === void 0 || this.__typedOnMouseLeaves === Na) {
- const t = Kn.get(this.getKey());
+ if (this.__typedOnMouseLeaves === void 0 || this.__typedOnMouseLeaves === $a) {
+ const t = Wn.get(this.getKey());
this.__typedOnMouseLeaves = t ?? {};
}
return this.__typedOnMouseLeaves;
}
syncTypedOnMouseLeavesToRegistry() {
if (!this.__typedOnMouseLeaves || Object.keys(this.__typedOnMouseLeaves).length === 0) {
- Kn.delete(this.getKey()), this.__typedOnMouseLeaves && Object.keys(this.__typedOnMouseLeaves).length === 0 && (this.__typedOnMouseLeaves = void 0);
+ Wn.delete(this.getKey()), this.__typedOnMouseLeaves && Object.keys(this.__typedOnMouseLeaves).length === 0 && (this.__typedOnMouseLeaves = void 0);
return;
}
- Kn.set(this.getKey(), this.__typedOnMouseLeaves);
+ Wn.set(this.getKey(), this.__typedOnMouseLeaves);
}
setOnMouseLeaveFor(t, r, n) {
- He(t), He(r);
+ Je(t), Je(r);
const i = this.ensureOnMouseLeaveMapMutable(), s = i[t] ?? (i[t] = {});
s[r] = n, this.syncTypedOnMouseLeavesToRegistry();
}
@@ -4477,20 +4478,20 @@ class Qe extends Zt {
const n = this.__typedOnMouseLeaves[t];
if (!n)
return;
- const i = Lr(n, r);
+ const i = Ur(n, r);
if (Object.keys(i).length > 0)
this.__typedOnMouseLeaves = {
...this.__typedOnMouseLeaves,
[t]: i
};
else {
- const o = Lr(this.__typedOnMouseLeaves, t);
+ const o = Ur(this.__typedOnMouseLeaves, t);
this.__typedOnMouseLeaves = Object.keys(o).length > 0 ? o : void 0;
}
this.syncTypedOnMouseLeavesToRegistry();
}
pruneTypedOnMouseLeaves() {
- if (!this.__typedOnMouseLeaves || this.__typedOnMouseLeaves === Na) {
+ if (!this.__typedOnMouseLeaves || this.__typedOnMouseLeaves === $a) {
this.__typedOnMouseLeaves = void 0, this.syncTypedOnMouseLeavesToRegistry();
return;
}
@@ -4511,7 +4512,7 @@ class Qe extends Zt {
s && (s(t, r, n, this.getTextContent()), this.removeOnRemoveFor(t, r));
}
dispatchRemovedIDs(t, r, n) {
- const i = zb(t, r);
+ const i = ok(t, r);
if (i.length !== 0)
for (const [s, o] of i)
this.invokeOnRemove(s, o, n);
@@ -4526,10 +4527,10 @@ class Qe extends Zt {
if (this.hasNoIDsForEveryType())
return this;
let t = this.getPreviousSibling();
- for (; Ce(t) && bu(t.getTypedIDs(), this.getTypedIDs()); )
+ for (; _e(t) && Nu(t.getTypedIDs(), this.getTypedIDs()); )
this.mergeWithPreviousTypedMark(t), t = this.getPreviousSibling();
let r = this.getNextSibling();
- for (; Ce(r) && bu(this.getTypedIDs(), r.getTypedIDs()); )
+ for (; _e(r) && Nu(this.getTypedIDs(), r.getTypedIDs()); )
this.mergeWithNextTypedMark(r), r = this.getNextSibling();
return this;
}
@@ -4546,107 +4547,107 @@ class Qe extends Zt {
mergeOnClicksFrom(t) {
if (!t || Object.keys(t).length === 0)
return;
- const r = Kb(this.getTypedOnClicks(), t);
+ const r = ak(this.getTypedOnClicks(), t);
Object.keys(r).length !== 0 && this.setTypedOnClicks(r);
}
mergeOnRemovesFrom(t) {
if (!t || Object.keys(t).length === 0)
return;
- const r = jb(this.getTypedOnRemoves(), t);
+ const r = ck(this.getTypedOnRemoves(), t);
Object.keys(r).length !== 0 && this.setTypedOnRemoves(r);
}
mergeOnMouseEntersFrom(t) {
if (!t || Object.keys(t).length === 0)
return;
- const r = Bb(this.getTypedOnMouseEnters(), t);
+ const r = lk(this.getTypedOnMouseEnters(), t);
Object.keys(r).length !== 0 && this.setTypedOnMouseEnters(r);
}
mergeOnMouseLeavesFrom(t) {
if (!t || Object.keys(t).length === 0)
return;
- const r = Vb(this.getTypedOnMouseLeaves(), t);
+ const r = uk(this.getTypedOnMouseLeaves(), t);
Object.keys(r).length !== 0 && this.setTypedOnMouseLeaves(r);
}
}
-function Ss(e = Xf) {
+function Ps(e = pp) {
const t = {};
for (const [r, n] of Object.entries(e)) {
- if (He(r), !Array.isArray(n)) {
+ if (Je(r), !Array.isArray(n)) {
t[r] = [];
continue;
}
const i = [];
for (const s of n)
- He(s), i.push(s);
+ Je(s), i.push(s);
t[r] = i;
}
return t;
}
-function Yo(e) {
- if (!e || e === Ea)
+function Zo(e) {
+ if (!e || e === wa)
return;
const t = {};
for (const [r, n] of Object.entries(e)) {
- He(r);
+ Je(r);
const i = {};
for (const [s, o] of Object.entries(n))
- He(s), i[s] = o;
+ Je(s), i[s] = o;
Object.keys(i).length > 0 && (t[r] = i);
}
return Object.keys(t).length > 0 ? t : void 0;
}
-function Xo(e) {
- if (!e || e === Aa)
+function ea(e) {
+ if (!e || e === qa)
return;
const t = {};
for (const [r, n] of Object.entries(e)) {
- He(r);
+ Je(r);
const i = {};
for (const [s, o] of Object.entries(n))
- He(s), i[s] = o;
+ Je(s), i[s] = o;
Object.keys(i).length > 0 && (t[r] = i);
}
return Object.keys(t).length > 0 ? t : void 0;
}
-function Qo(e) {
- if (!e || e === Pa)
+function ta(e) {
+ if (!e || e === Ra)
return;
const t = {};
for (const [r, n] of Object.entries(e)) {
- He(r);
+ Je(r);
const i = {};
for (const [s, o] of Object.entries(n))
- He(s), i[s] = o;
+ Je(s), i[s] = o;
Object.keys(i).length > 0 && (t[r] = i);
}
return Object.keys(t).length > 0 ? t : void 0;
}
-function Zo(e) {
- if (!e || e === Na)
+function ra(e) {
+ if (!e || e === $a)
return;
const t = {};
for (const [r, n] of Object.entries(e)) {
- He(r);
+ Je(r);
const i = {};
for (const [s, o] of Object.entries(n))
- He(s), i[s] = o;
+ Je(s), i[s] = o;
Object.keys(i).length > 0 && (t[r] = i);
}
return Object.keys(t).length > 0 ? t : void 0;
}
-function Lr(e, t) {
+function Ur(e, t) {
const r = {};
for (const [n, i] of Object.entries(e))
n !== t && (r[n] = i);
return r;
}
-function yu(e) {
+function Pu(e) {
const t = {};
for (const [r, n] of Object.entries(e))
!n || n.length === 0 || (t[r] = [...n].sort());
return t;
}
-function zb(e, t) {
+function ok(e, t) {
const r = [];
for (const [n, i] of Object.entries(e)) {
const s = new Set(t[n] ?? []);
@@ -4655,8 +4656,8 @@ function zb(e, t) {
}
return r;
}
-function bu(e, t) {
- const r = yu(e), n = yu(t), i = Object.keys(r).sort(), s = Object.keys(n).sort();
+function Nu(e, t) {
+ const r = Pu(e), n = Pu(t), i = Object.keys(r).sort(), s = Object.keys(n).sort();
if (i.length !== s.length)
return !1;
for (let o = 0; o < i.length; o++) {
@@ -4666,127 +4667,127 @@ function bu(e, t) {
const c = r[a], l = n[a];
if (!c || !l || c.length !== l.length)
return !1;
- for (let u = 0; u < c.length; u++)
- if (c[u] !== l[u])
+ for (let d = 0; d < c.length; d++)
+ if (c[d] !== l[d])
return !1;
}
return !0;
}
-function Kb(e, t) {
+function ak(e, t) {
const r = {}, n = /* @__PURE__ */ new Set([...Object.keys(e), ...Object.keys(t)]);
for (const i of n) {
const s = e[i] ?? {}, o = t[i] ?? {}, a = /* @__PURE__ */ new Set([...Object.keys(s), ...Object.keys(o)]), c = {};
for (const l of a) {
- const u = s[l] ?? o[l];
- u && (c[l] = u);
+ const d = s[l] ?? o[l];
+ d && (c[l] = d);
}
Object.keys(c).length > 0 && (r[i] = c);
}
return r;
}
-function jb(e, t) {
+function ck(e, t) {
const r = {}, n = /* @__PURE__ */ new Set([...Object.keys(e), ...Object.keys(t)]);
for (const i of n) {
const s = e[i] ?? {}, o = t[i] ?? {}, a = /* @__PURE__ */ new Set([...Object.keys(s), ...Object.keys(o)]), c = {};
for (const l of a) {
- const u = s[l] ?? o[l];
- u && (c[l] = u);
+ const d = s[l] ?? o[l];
+ d && (c[l] = d);
}
Object.keys(c).length > 0 && (r[i] = c);
}
return r;
}
-function Bb(e, t) {
+function lk(e, t) {
const r = {}, n = /* @__PURE__ */ new Set([...Object.keys(e), ...Object.keys(t)]);
for (const i of n) {
const s = e[i] ?? {}, o = t[i] ?? {}, a = /* @__PURE__ */ new Set([...Object.keys(s), ...Object.keys(o)]), c = {};
for (const l of a) {
- const u = s[l] ?? o[l];
- u && (c[l] = u);
+ const d = s[l] ?? o[l];
+ d && (c[l] = d);
}
Object.keys(c).length > 0 && (r[i] = c);
}
return r;
}
-function Vb(e, t) {
+function uk(e, t) {
const r = {}, n = /* @__PURE__ */ new Set([...Object.keys(e), ...Object.keys(t)]);
for (const i of n) {
const s = e[i] ?? {}, o = t[i] ?? {}, a = /* @__PURE__ */ new Set([...Object.keys(s), ...Object.keys(o)]), c = {};
for (const l of a) {
- const u = s[l] ?? o[l];
- u && (c[l] = u);
+ const d = s[l] ?? o[l];
+ d && (c[l] = d);
}
Object.keys(c).length > 0 && (r[i] = c);
}
return r;
}
-function ln(e, t) {
+function dn(e, t) {
return `${e}-${t}`;
}
-function ku(e) {
+function Ou(e) {
return `external-${e}`;
}
-function Hi(e, t, r, n, i) {
- return je(new Qe(e, t, r, n, i));
+function Qi(e, t, r, n, i) {
+ return Ve(new tt(e, t, r, n, i));
}
-function Ce(e) {
- return e instanceof Qe;
+function _e(e) {
+ return e instanceof tt;
}
-function Qf(e) {
- return e?.type === Qe.getType();
+function hp(e) {
+ return e?.type === tt.getType();
}
-function Us(e) {
+function js(e) {
const t = e.getChildren();
let r = null;
for (const n of t)
r === null ? e.insertBefore(n) : r.insertAfter(n), r = n;
e.remove();
}
-function Zf(e, t, r, n, i, s, o) {
- const a = e.getNodes(), c = e.anchor.offset, l = e.focus.offset, u = a.length, d = e.isBackward(), f = d ? l : c, p = d ? c : l;
- let m, g;
- for (let y = 0; y < u; y++) {
- const k = a[y];
- if (F(g) && g.isParentOf(k))
+function gp(e, t, r, n, i, s, o) {
+ const a = e.getNodes(), c = e.anchor.offset, l = e.focus.offset, d = a.length, u = e.isBackward(), f = u ? l : c, p = u ? c : l;
+ let y, h;
+ for (let m = 0; m < d; m++) {
+ const b = a[m];
+ if (L(h) && h.isParentOf(b))
continue;
- const _ = y === 0, S = y === u - 1;
- let P = null;
- if (v(k)) {
- const A = k.getTextContentSize(), B = _ ? f : 0, M = S ? p : A;
- if (B === 0 && M === 0)
+ const x = m === 0, _ = m === d - 1;
+ let A = null;
+ if (M(b)) {
+ const E = b.getTextContentSize(), D = x ? f : 0, V = _ ? p : E;
+ if (D === 0 && V === 0)
continue;
- const q = k.splitText(B, M);
- P = q.length > 1 && (q.length === 3 || _ && !S || M === A) ? q[1] : q[0];
+ const j = b.splitText(D, V);
+ A = j.length > 1 && (j.length === 3 || x && !_ || V === E) ? j[1] : j[0];
} else {
- if (Ce(k))
+ if (_e(b))
continue;
- F(k) && k.isInline() && (P = k);
+ L(b) && b.isInline() && (A = b);
}
- if (P !== null) {
- if (P && P.is(m))
+ if (A !== null) {
+ if (A && A.is(y))
continue;
- const A = P.getParent();
- (A == null || !A.is(m)) && (g = void 0), m = A, g === void 0 && (g = Hi(), g.addID(t, r, n, i, s, o), P.insertBefore(g)), g.append(P);
+ const E = A.getParent();
+ (E == null || !E.is(y)) && (h = void 0), y = E, h === void 0 && (h = Qi(), h.addID(t, r, n, i, s, o), A.insertBefore(h)), h.append(A);
} else
- m = void 0, g = void 0;
+ y = void 0, h = void 0;
}
- t === Kr && F(g) && (d ? g.selectStart() : g.selectEnd());
+ t === Br && L(h) && (u ? h.selectStart() : h.selectEnd());
}
-function Wb(e, t, r) {
+function dk(e, t, r) {
let n = e;
for (; n !== null; ) {
- if (Ce(n))
+ if (_e(n))
return n.getTypedIDs()[t];
- if (v(n) && r === n.getTextContentSize()) {
+ if (M(n) && r === n.getTextContentSize()) {
const i = n.getNextSibling();
- if (Ce(i))
+ if (_e(i))
return i.getTypedIDs()[t];
}
n = n.getParent();
}
}
-const Hb = ["type", "marker", "content"], Oa = "unknown", ep = 1, Gb = /* @__PURE__ */ new Set(["optbreak", "ref"]);
-class Pn extends Zt {
+const fk = ["type", "marker", "content"], La = "unknown", mp = 1, pk = /* @__PURE__ */ new Set(["optbreak", "ref"]);
+class qn extends Qt {
__tag;
__marker;
__unknownAttributes;
@@ -4798,18 +4799,18 @@ class Pn extends Zt {
}
static clone(t) {
const { __tag: r, __marker: n, __unknownAttributes: i, __key: s } = t;
- return new Pn(r, n, i, s);
+ return new qn(r, n, i, s);
}
static importDOM() {
return {
- [Oa]: (t) => Yb(t) ? {
- conversion: Jb,
+ [La]: (t) => gk(t) ? {
+ conversion: hk,
priority: 1
} : null
};
}
static importJSON(t) {
- return Nc().updateFromJSON(t);
+ return Uc().updateFromJSON(t);
}
updateFromJSON(t) {
return super.updateFromJSON(t).setTag(t.tag).setMarker(t.marker).setUnknownAttributes(t.unknownAttributes);
@@ -4830,7 +4831,7 @@ class Pn extends Zt {
* callers must not add or strip spaces next to them.
*/
isInlineTag() {
- return Gb.has(this.getTag());
+ return pk.has(this.getTag());
}
setMarker(t) {
if (this.__marker === t)
@@ -4849,7 +4850,7 @@ class Pn extends Zt {
return this.getLatest().__unknownAttributes;
}
createDOM() {
- const t = document.createElement(Oa);
+ const t = document.createElement(La);
return t.setAttribute("data-tag", this.getTag()), t.setAttribute("data-marker", this.getMarker() ?? ""), t.classList.add(this.isInlineTag() ? "unknown-inline" : "unknown-block"), t.contentEditable = "false", t;
}
updateDOM(t, r) {
@@ -4870,7 +4871,7 @@ class Pn extends Zt {
tag: this.getTag(),
marker: this.getMarker(),
unknownAttributes: this.getUnknownAttributes(),
- version: ep
+ version: mp
};
}
// Mutation
@@ -4963,7 +4964,7 @@ class Pn extends Zt {
const r = t ?? w();
if (!r)
return !1;
- if (mc(r) && super.isSelected(r))
+ if (go(r) && super.isSelected(r))
return !0;
if (r.isCollapsed())
return !1;
@@ -4971,24 +4972,24 @@ class Pn extends Zt {
return this.getChildren().some((i) => n.some((s) => s.is(i)));
}
}
-function Jb(e) {
+function hk(e) {
const t = e.getAttribute("data-tag") ?? "", r = e.getAttribute("data-marker") ?? "";
- return { node: Nc(t, r) };
+ return { node: Uc(t, r) };
}
-function Nc(e, t, r) {
- return je(new Pn(e, t, r));
+function Uc(e, t, r) {
+ return Ve(new qn(e, t, r));
}
-function Yb(e) {
- return e?.tagName.toLowerCase() === Oa;
+function gk(e) {
+ return e?.tagName.toLowerCase() === La;
}
-function De(e) {
- return e instanceof Pn;
+function Ue(e) {
+ return e instanceof qn;
}
-const tp = 1, Xb = "attribute-run";
-function ea(e) {
+const yp = 1, mk = "attribute-run";
+function na(e) {
return e === "va" || e === "vp" || e === "ca" || e === "cp" ? `usfm_${e}` : void 0;
}
-class Ar extends Zt {
+class qr extends Qt {
__runKind;
constructor(t, r) {
super(r), this.__runKind = t;
@@ -4998,10 +4999,10 @@ class Ar extends Zt {
}
static clone(t) {
const { __runKind: r, __key: n } = t;
- return new Ar(r, n);
+ return new qr(r, n);
}
static importJSON(t) {
- return rp(t.runKind).updateFromJSON(t);
+ return bp(t.runKind).updateFromJSON(t);
}
// No HTML shape ever round-trips: `exportDOM` below contributes no wrapper element of its own
// (a DocumentFragment leaves no markup behind), so there is nothing for a paste to hand back
@@ -5026,15 +5027,15 @@ class Ar extends Zt {
}
createDOM() {
const t = document.createElement("span");
- t.classList.add(Xb);
- const r = ea(this.__runKind);
+ t.classList.add(mk);
+ const r = na(this.__runKind);
return r !== void 0 && t.classList.add(r), t;
}
updateDOM(t, r) {
if (t.__runKind !== this.__runKind) {
- const n = ea(t.__runKind);
+ const n = na(t.__runKind);
n !== void 0 && r.classList.remove(n);
- const i = ea(this.__runKind);
+ const i = na(this.__runKind);
i !== void 0 && r.classList.add(i);
}
return !1;
@@ -5047,7 +5048,7 @@ class Ar extends Zt {
...super.exportJSON(),
type: this.getType(),
runKind: this.getRunKind(),
- version: tp
+ version: yp
};
}
// Mutation
@@ -5058,20 +5059,20 @@ class Ar extends Zt {
return !0;
}
}
-function rp(e) {
- return je(new Ar(e));
+function bp(e) {
+ return Ve(new qr(e));
}
-function Le(e) {
- return e instanceof Ar;
+function De(e) {
+ return e instanceof qr;
}
-const Gi = "id", np = 1, Qb = [
+const Zi = "id", kp = 1, yk = [
"type",
"marker",
"code",
"content"
];
-class Ut extends Zt {
- __marker = Gi;
+class Ft extends Qt {
+ __marker = Zi;
__code;
__unknownAttributes;
constructor(t = "", r, n) {
@@ -5082,14 +5083,14 @@ class Ut extends Zt {
}
static clone(t) {
const { __code: r, __unknownAttributes: n, __key: i } = t;
- return new Ut(r, n, i);
+ return new Ft(r, n, i);
}
static importJSON(t) {
const { code: r } = t;
- return ip(r).updateFromJSON(t);
+ return Tp(r).updateFromJSON(t);
}
static isValidBookCode(t) {
- return iy(t);
+ return xy(t);
}
updateFromJSON(t) {
return super.updateFromJSON(t).setCode(t.code).setUnknownAttributes(t.unknownAttributes);
@@ -5131,20 +5132,20 @@ class Ut extends Zt {
marker: this.getMarker(),
code: this.getCode(),
unknownAttributes: this.getUnknownAttributes(),
- version: np
+ version: kp
};
}
}
-function ip(e, t) {
- return je(new Ut(e, t));
+function Tp(e, t) {
+ return Ve(new Ft(e, t));
}
function pt(e) {
- return e instanceof Ut;
+ return e instanceof Ft;
}
-function sp(e) {
- return e?.type === Ut.getType();
+function xp(e) {
+ return e?.type === Ft.getType();
}
-const Fs = "c", op = 1, Zb = [
+const Bs = "c", _p = 1, bk = [
"type",
"marker",
"number",
@@ -5153,7 +5154,7 @@ const Fs = "c", op = 1, Zb = [
"pubnumber",
"content"
];
-class Nt extends Zt {
+class Ot extends Qt {
__marker;
__number;
__sid;
@@ -5161,17 +5162,17 @@ class Nt extends Zt {
__pubnumber;
__unknownAttributes;
constructor(t = "", r, n, i, s, o) {
- super(o), this.__marker = Fs, this.__number = t, this.__sid = r, this.__altnumber = n, this.__pubnumber = i, this.__unknownAttributes = s;
+ super(o), this.__marker = Bs, this.__number = t, this.__sid = r, this.__altnumber = n, this.__pubnumber = i, this.__unknownAttributes = s;
}
static getType() {
return "chapter";
}
static clone(t) {
const { __number: r, __sid: n, __altnumber: i, __pubnumber: s, __unknownAttributes: o, __key: a } = t;
- return new Nt(r, n, i, s, o, a);
+ return new Ot(r, n, i, s, o, a);
}
static importJSON(t) {
- return ap().updateFromJSON(t);
+ return Cp().updateFromJSON(t);
}
updateFromJSON(t) {
return super.updateFromJSON(t).setMarker(t.marker).setNumber(t.number).setSid(t.sid).setAltnumber(t.altnumber).setPubnumber(t.pubnumber).setUnknownAttributes(t.unknownAttributes);
@@ -5230,7 +5231,7 @@ class Nt extends Zt {
}
createDOM() {
const t = document.createElement("p");
- return t.setAttribute("data-marker", this.__marker), t.classList.add(Ls, `usfm_${this.__marker}`), t.setAttribute("data-number", this.__number), t;
+ return t.setAttribute("data-marker", this.__marker), t.classList.add(zs, `usfm_${this.__marker}`), t.setAttribute("data-number", this.__number), t;
}
updateDOM(t, r) {
return t.__number !== this.__number && r.setAttribute("data-number", this.__number), !1;
@@ -5245,20 +5246,20 @@ class Nt extends Zt {
altnumber: this.getAltnumber(),
pubnumber: this.getPubnumber(),
unknownAttributes: this.getUnknownAttributes(),
- version: op
+ version: _p
};
}
}
-function ap(e, t, r, n, i) {
- return je(new Nt(e, t, r, n, i));
+function Cp(e, t, r, n, i) {
+ return Ve(new Ot(e, t, r, n, i));
}
-function Pe(e) {
- return e instanceof Nt;
+function Ee(e) {
+ return e instanceof Ot;
}
-function ek(e) {
- return e?.type === Nt.getType();
+function kk(e) {
+ return e?.type === Ot.getType();
}
-const cp = [
+const vp = [
"fr",
"fq",
"fqa",
@@ -5271,7 +5272,7 @@ const cp = [
"fm",
"fdc"
// Deprecated marker.
-], lp = [
+], Sp = [
"xo",
"xop",
"xk",
@@ -5282,7 +5283,7 @@ const cp = [
"xnt",
"xdc"
// Deprecated marker.
-], tk = [
+], Tk = [
// Chapter & Verse
"ca",
"cp",
@@ -5337,10 +5338,10 @@ const cp = [
"liv3",
"liv4",
"liv5",
- ...cp,
- ...lp
-], up = 1, rk = ["type", "marker", "content"];
-class me extends Zt {
+ ...vp,
+ ...Sp
+], Mp = 1, xk = ["type", "marker", "content"];
+class ye extends Qt {
__marker;
__unknownAttributes;
constructor(t = "", r, n) {
@@ -5351,16 +5352,16 @@ class me extends Zt {
}
static clone(t) {
const { __marker: r, __unknownAttributes: n, __key: i } = t;
- return new me(r, n, i);
+ return new ye(r, n, i);
}
static isValidMarker(t, r) {
- return t !== void 0 && (tk.includes(t) || (r?.includes(t) ?? !1));
+ return t !== void 0 && (Tk.includes(t) || (r?.includes(t) ?? !1));
}
static isValidFootnoteMarker(t) {
- return t !== void 0 && cp.includes(t);
+ return t !== void 0 && vp.includes(t);
}
static isValidCrossReferenceMarker(t) {
- return t !== void 0 && lp.includes(t);
+ return t !== void 0 && Sp.includes(t);
}
/**
* Whether a character marker belongs to the note-content families - footnote or cross-reference.
@@ -5374,18 +5375,18 @@ class me extends Zt {
* @returns `true` if the marker is a footnote or cross-reference marker, `false` otherwise.
*/
static isNoteContentMarker(t) {
- return me.isValidFootnoteMarker(t) || me.isValidCrossReferenceMarker(t);
+ return ye.isValidFootnoteMarker(t) || ye.isValidCrossReferenceMarker(t);
}
static importDOM() {
return {
- span: (t) => ik(t) ? {
- conversion: nk,
+ span: (t) => Ck(t) ? {
+ conversion: _k,
priority: 1
} : null
};
}
static importJSON(t) {
- return xr().updateFromJSON(t);
+ return Mr().updateFromJSON(t);
}
updateFromJSON(t) {
return super.updateFromJSON(t).setMarker(t.marker).setUnknownAttributes(t.unknownAttributes);
@@ -5408,14 +5409,14 @@ class me extends Zt {
}
createDOM(t) {
const r = document.createElement("span");
- return Tu(r, this.__marker, t), r.classList.add(this.__type), r;
+ return wu(r, this.__marker, t), r.classList.add(this.__type), r;
}
updateDOM(t, r, n) {
- return t.__marker !== this.__marker && (r.classList.remove(`usfm_${t.__marker}`), Tu(r, this.__marker, n)), !1;
+ return t.__marker !== this.__marker && (r.classList.remove(`usfm_${t.__marker}`), wu(r, this.__marker, n)), !1;
}
exportDOM(t) {
const { element: r } = super.exportDOM(t);
- return r && An(r) && (r.setAttribute("data-marker", this.getMarker()), r.classList.add(this.getType(), `usfm_${this.getMarker()}`)), { element: r };
+ return r && Pn(r) && (r.setAttribute("data-marker", this.getMarker()), r.classList.add(this.getType(), `usfm_${this.getMarker()}`)), { element: r };
}
exportJSON() {
return {
@@ -5423,12 +5424,12 @@ class me extends Zt {
type: this.getType(),
marker: this.getMarker(),
unknownAttributes: this.getUnknownAttributes(),
- version: up
+ version: Mp
};
}
// Mutation
insertNewAfter(t, r) {
- const n = this.getUnknownAttributes()?.closed === "false", i = xr(this.getMarker(), n ? { closed: "false" } : void 0);
+ const n = this.getUnknownAttributes()?.closed === "false", i = Mr(this.getMarker(), n ? { closed: "false" } : void 0);
return i.setDirection(this.getDirection()), i.setFormat(this.getFormatType()), i.setStyle(this.getTextStyle()), this.insertAfter(i, r), i;
}
canBeEmpty() {
@@ -5438,30 +5439,30 @@ class me extends Zt {
return !0;
}
}
-function Tu(e, t, r) {
+function wu(e, t, r) {
e.setAttribute("data-marker", t), r.theme?.showCharMarkerTitles !== !1 ? e.setAttribute("title", t) : e.removeAttribute("title"), e.classList.add(`usfm_${t}`);
}
-function nk(e) {
+function _k(e) {
const t = e.getAttribute("data-marker") ?? "f";
- return { node: xr(t) };
+ return { node: Mr(t) };
}
-function xr(e, t) {
- return je(new me(e, t));
+function Mr(e, t) {
+ return Ve(new ye(e, t));
}
-function ik(e) {
+function Ck(e) {
if (!e)
return !1;
const t = e.getAttribute("data-marker") ?? "";
- return me.isValidMarker(t) && e.classList.contains(me.getType());
+ return ye.isValidMarker(t) && e.classList.contains(ye.getType());
}
-function D(e) {
- return e instanceof me;
+function I(e) {
+ return e instanceof ye;
}
-function sk(e) {
- return e?.type === me.getType();
+function vk(e) {
+ return e?.type === ye.getType();
}
-const dp = 1, ok = "c", fp = "span";
-class pr extends ls {
+const Ep = 1, Sk = "c", Ap = "span";
+class pr extends hs {
__marker;
__number;
__showMarker;
@@ -5470,7 +5471,7 @@ class pr extends ls {
__pubnumber;
__unknownAttributes;
constructor(t = "", r = !1, n, i, s, o, a) {
- super(a), this.__marker = ok, this.__number = t, this.__showMarker = r, this.__sid = n, this.__altnumber = i, this.__pubnumber = s, this.__unknownAttributes = o;
+ super(a), this.__marker = Sk, this.__number = t, this.__showMarker = r, this.__sid = n, this.__altnumber = i, this.__pubnumber = s, this.__unknownAttributes = o;
}
static getType() {
return "immutable-chapter";
@@ -5481,14 +5482,14 @@ class pr extends ls {
}
static importDOM() {
return {
- span: (t) => pp(t) ? {
- conversion: ak,
+ span: (t) => Pp(t) ? {
+ conversion: Mk,
priority: 1
} : null
};
}
static importJSON(t) {
- return Oc().updateFromJSON(t);
+ return Fc().updateFromJSON(t);
}
updateFromJSON(t) {
return super.updateFromJSON(t).setMarker(t.marker).setNumber(t.number).setShowMarker(t.showMarker).setSid(t.sid).setAltnumber(t.altnumber).setPubnumber(t.pubnumber).setUnknownAttributes(t.unknownAttributes);
@@ -5555,15 +5556,15 @@ class pr extends ls {
return this.getLatest().__unknownAttributes;
}
createDOM() {
- const t = document.createElement(fp);
- return t.setAttribute("data-marker", this.__marker), t.classList.add(Ls, `usfm_${this.__marker}`), this.__showMarker && t.classList.add("marker"), t.setAttribute("data-number", this.__number), t;
+ const t = document.createElement(Ap);
+ return t.setAttribute("data-marker", this.__marker), t.classList.add(zs, `usfm_${this.__marker}`), this.__showMarker && t.classList.add("marker"), t.setAttribute("data-number", this.__number), t;
}
updateDOM() {
return !1;
}
exportDOM(t) {
const { element: r } = super.exportDOM(t);
- return r && An(r) && (r.setAttribute("data-marker", this.getMarker()), r.classList.add(Ls, `usfm_${this.getMarker()}`), r.setAttribute("data-number", this.getNumber())), { element: r };
+ return r && Pn(r) && (r.setAttribute("data-marker", this.getMarker()), r.classList.add(zs, `usfm_${this.getMarker()}`), r.setAttribute("data-number", this.getNumber())), { element: r };
}
/**
* VISIBLE bytes with STABLE IDENTITY, which is only safe because of where this node lives.
@@ -5583,7 +5584,7 @@ class pr extends ls {
* these bytes from `createDOM` instead, the way `ImmutableTypedTextNode` does.
*/
decorate() {
- return this.getShowMarker() ? Lt(this.getMarker(), this.getNumber()) : this.getNumber();
+ return this.getShowMarker() ? Dt(this.getMarker(), this.getNumber()) : this.getNumber();
}
exportJSON() {
return {
@@ -5595,7 +5596,7 @@ class pr extends ls {
altnumber: this.getAltnumber(),
pubnumber: this.getPubnumber(),
unknownAttributes: this.getUnknownAttributes(),
- version: dp
+ version: Ep
};
}
// Mutation
@@ -5606,32 +5607,32 @@ class pr extends ls {
return !1;
}
}
-function ak(e) {
+function Mk(e) {
const t = e.getAttribute("data-number") ?? "0";
- return { node: Oc(t) };
+ return { node: Fc(t) };
}
-function Oc(e, t, r, n, i, s) {
- return je(new pr(e, t, r, n, i, s));
+function Fc(e, t, r, n, i, s) {
+ return Ve(new pr(e, t, r, n, i, s));
}
-function pp(e) {
- return e ? e.classList.contains(Ls) && e.tagName.toLowerCase() === fp : !1;
+function Pp(e) {
+ return e ? e.classList.contains(zs) && e.tagName.toLowerCase() === Ap : !1;
}
-function fs(e) {
+function ms(e) {
return e instanceof pr;
}
-function ck(e) {
+function Ek(e) {
return e?.type === pr.getType();
}
-const hp = 1;
-class Wr extends yc {
+const Np = 1;
+class Jr extends Mc {
static getType() {
return "implied-para";
}
static clone(t) {
- return new Wr(t.__key);
+ return new Jr(t.__key);
}
static importJSON(t) {
- return Ht().updateFromJSON(t);
+ return Wt().updateFromJSON(t);
}
getMarker() {
return cr;
@@ -5640,25 +5641,25 @@ class Wr extends yc {
return {
...super.exportJSON(),
type: this.getType(),
- version: hp
+ version: Np
};
}
// Mutation
insertNewAfter(t, r) {
- const n = Ht();
+ const n = Wt();
return n.setTextFormat(t.format), n.setTextStyle(t.style), n.setDirection(this.getDirection()), n.setFormat(this.getFormatType()), n.setStyle(this.getTextStyle()), this.insertAfter(n, r), n;
}
}
-function Ht() {
- return je(new Wr());
+function Wt() {
+ return Ve(new Jr());
}
function dr(e) {
- return e instanceof Wr;
+ return e instanceof Jr;
}
-function go(e) {
- return e?.type === Wr.getType();
+function ko(e) {
+ return e?.type === Jr.getType();
}
-const lk = [
+const Ak = [
// Identification
"ide",
"sts",
@@ -5791,8 +5792,8 @@ const lk = [
"lim4",
// Breaks - see https://docs.usfm.bible/usfm/3.1/char/breaks/pb.html
"pb"
-], gp = 1, uk = ["type", "marker", "content"];
-class Ze extends yc {
+], Op = 1, Pk = ["type", "marker", "content"];
+class rt extends Mc {
__marker;
__unknownAttributes;
constructor(t = cr, r, n) {
@@ -5803,21 +5804,21 @@ class Ze extends yc {
}
static clone(t) {
const { __marker: r, __unknownAttributes: n, __key: i } = t;
- return new Ze(r, n, i);
+ return new rt(r, n, i);
}
static isValidMarker(t, r) {
- return t !== void 0 && (lk.includes(t) || (r?.includes(t) ?? !1));
+ return t !== void 0 && (Ak.includes(t) || (r?.includes(t) ?? !1));
}
static importDOM() {
return {
p: () => ({
- conversion: dk,
+ conversion: Nk,
priority: 1
})
};
}
static importJSON(t) {
- return Ji().updateFromJSON(t);
+ return es().updateFromJSON(t);
}
updateFromJSON(t) {
return super.updateFromJSON(t).setMarker(t.marker).setUnknownAttributes(t.unknownAttributes);
@@ -5848,7 +5849,7 @@ class Ze extends yc {
}
exportDOM(t) {
const { element: r } = super.exportDOM(t);
- return r && An(r) && (r.setAttribute("data-marker", this.getMarker()), r.classList.add(this.getType(), `usfm_${this.getMarker()}`)), { element: r };
+ return r && Pn(r) && (r.setAttribute("data-marker", this.getMarker()), r.classList.add(this.getType(), `usfm_${this.getMarker()}`)), { element: r };
}
exportJSON() {
return {
@@ -5856,17 +5857,17 @@ class Ze extends yc {
type: this.getType(),
marker: this.getMarker(),
unknownAttributes: this.getUnknownAttributes(),
- version: gp
+ version: Op
};
}
// Mutation
insertNewAfter(t, r) {
- const n = Ji(this.getMarker());
+ const n = es(this.getMarker());
return n.setTextFormat(t.format), n.setTextStyle(t.style), n.setDirection(this.getDirection()), n.setFormat(this.getFormatType()), n.setStyle(this.getTextStyle()), this.insertAfter(n, r), n;
}
}
-function dk(e) {
- const t = e.getAttribute("data-marker") ?? void 0, r = Ji(t);
+function Nk(e) {
+ const t = e.getAttribute("data-marker") ?? void 0, r = es(t);
if (e.style) {
r.setFormat(e.style.textAlign);
const n = parseInt(e.style.textIndent, 10) / 20;
@@ -5874,16 +5875,16 @@ function dk(e) {
}
return { node: r };
}
-function Ji(e, t) {
- return je(new Ze(e, t));
+function es(e, t) {
+ return Ve(new rt(e, t));
}
-function ae(e) {
- return e instanceof Ze;
+function ce(e) {
+ return e instanceof rt;
}
-function wc(e) {
- return e?.type === Ze.getType();
+function zc(e) {
+ return e?.type === rt.getType();
}
-const zs = "v", mp = 1, fk = [
+const Vs = "v", wp = 1, Ok = [
"type",
"marker",
"number",
@@ -5892,7 +5893,7 @@ const zs = "v", mp = 1, fk = [
"pubnumber",
"content"
];
-class dt extends Ke {
+class dt extends je {
__marker;
__number;
__sid;
@@ -5900,7 +5901,7 @@ class dt extends Ke {
__pubnumber;
__unknownAttributes;
constructor(t = "", r, n, i, s, o, a) {
- super(r ?? t, a), this.__marker = zs, this.__number = t, this.__sid = n, this.__altnumber = i, this.__pubnumber = s, this.__unknownAttributes = o;
+ super(r ?? t, a), this.__marker = Vs, this.__number = t, this.__sid = n, this.__altnumber = i, this.__pubnumber = s, this.__unknownAttributes = o;
}
static getType() {
return "verse";
@@ -5910,7 +5911,7 @@ class dt extends Ke {
return new dt(r, n, i, s, o, a, c);
}
static importJSON(t) {
- return yp().updateFromJSON(t);
+ return qp().updateFromJSON(t);
}
updateFromJSON(t) {
return super.updateFromJSON(t).setMarker(t.marker).setNumber(t.number).setSid(t.sid).setAltnumber(t.altnumber).setPubnumber(t.pubnumber).setUnknownAttributes(t.unknownAttributes);
@@ -5969,7 +5970,7 @@ class dt extends Ke {
}
createDOM(t) {
const r = super.createDOM(t);
- return r.setAttribute("data-marker", this.__marker), r.classList.add(Ca, `usfm_${this.__marker}`), r.setAttribute("data-number", this.__number), r;
+ return r.setAttribute("data-marker", this.__marker), r.classList.add(Aa, `usfm_${this.__marker}`), r.setAttribute("data-number", this.__number), r;
}
updateDOM(t, r, n) {
const i = super.updateDOM(t, r, n);
@@ -5985,54 +5986,54 @@ class dt extends Ke {
altnumber: this.getAltnumber(),
pubnumber: this.getPubnumber(),
unknownAttributes: this.getUnknownAttributes(),
- version: mp
+ version: wp
};
}
}
-function yp(e, t, r, n, i, s) {
- return je(new dt(e, t, r, n, i, s));
+function qp(e, t, r, n, i, s) {
+ return Ve(new dt(e, t, r, n, i, s));
}
-function Oe(e) {
+function we(e) {
return e instanceof dt;
}
-function bp(e) {
+function Rp(e) {
return e?.type === dt.getType();
}
-const pk = "", ti = pk;
-var xu;
+const wk = "", oi = wk;
+var qu;
(function(e) {
e.LEFT = "left", e.RIGHT = "right";
-})(xu || (xu = {}));
-var _u;
+})(qu || (qu = {}));
+var Ru;
(function(e) {
e[e.BEFORE = 0] = "BEFORE", e[e.AFTER = 1] = "AFTER";
-})(_u || (_u = {}));
-function hk() {
- return pe(ti);
+})(Ru || (Ru = {}));
+function qk() {
+ return ge(oi);
}
-function gk(e) {
+function Rk(e) {
const t = e.getTextContent();
- e.setTextContent(t.replaceAll(ti, ""));
+ e.setTextContent(t.replaceAll(oi, ""));
}
-function ps(e) {
- return e.length > 0 && e.includes(ti) && e.replaceAll(ti, "") === "";
+function ys(e) {
+ return e.length > 0 && e.includes(oi) && e.replaceAll(oi, "") === "";
}
-function qc(e) {
- return v(e) && ps(e.getTextContent());
+function Kc(e) {
+ return M(e) && ys(e.getTextContent());
}
-function kp(e) {
- return ek(e) || ck(e);
+function $p(e) {
+ return kk(e) || Ek(e);
}
function Ge(e) {
- return Pe(e) || fs(e);
+ return Ee(e) || ms(e);
}
-function Tp(e, t) {
+function Lp(e, t) {
return e.find((r) => Ge(r) && r.getNumber() === t.toString());
}
-function mk(e, t = !1) {
+function $k(e, t = !1) {
return e.find((r, n) => (!t || n > 0) && Ge(r));
}
-function Cu(e) {
+function $u(e) {
let t = e;
for (; t && t.getParent() !== null; ) {
const r = t.getPreviousSibling();
@@ -6041,7 +6042,7 @@ function Cu(e) {
t = t.getParent();
}
}
-function xp(e) {
+function Ip(e) {
if (!e)
return;
if (Ge(e))
@@ -6052,13 +6053,13 @@ function xp(e) {
if (t && Ge(t))
return t;
}
-function Xt(e) {
- return nt(e, j) ?? void 0;
+function Yt(e) {
+ return Xe(e, z) ?? void 0;
}
-function yk(e) {
- return pt(e) || Pe(e) || D(e) || fs(e) || dr(e) || Be(e) || ae(e) || j(e) || Oe(e) || De(e);
+function Lk(e) {
+ return pt(e) || Ee(e) || I(e) || ms(e) || dr(e) || He(e) || ce(e) || z(e) || we(e) || Ue(e);
}
-function _p(e) {
+function Dp(e) {
if (e.anchor.type === "element") {
const r = e.anchor.getNode(), n = e.anchor.offset;
if (n < r.getChildrenSize())
@@ -6067,23 +6068,23 @@ function _p(e) {
const t = e.anchor.getNode();
return t.getNextSibling() ?? t.getParent()?.getNextSibling() ?? null;
}
-function bk(e) {
+function Ik(e) {
const t = e.anchor.offset;
if (e.anchor.type === "element" && t > 0)
return e.anchor.getNode().getChildAtIndex(t - 1);
const r = e.anchor.getNode();
return r.getPreviousSibling() ?? r.getParent()?.getPreviousSibling() ?? null;
}
-function Et(e) {
- return Se(e) || pt(e);
+function At(e) {
+ return Ce(e) || pt(e);
}
-function Se(e) {
- return ae(e) || dr(e);
+function Ce(e) {
+ return ce(e) || dr(e);
}
-function kk(e) {
- return wc(e) || go(e);
+function Dk(e) {
+ return zc(e) || ko(e);
}
-function Ks(e, t) {
+function Ws(e, t) {
let r = e.getParent();
for (; r; ) {
if (r.getKey() === t)
@@ -6092,141 +6093,141 @@ function Ks(e, t) {
}
return !1;
}
-function Tn(e, t) {
- const r = ne(t, kn), n = !!(e.cid && r), i = !e.cid && !r;
+function xn(e, t) {
+ const r = ne(t, Tn), n = !!(e.cid && r), i = !e.cid && !r;
return e.style === t.getMarker() && (i || n && e.cid === r);
}
-function Tk(e, t) {
- const r = F(e) ? e : e.getParent(), n = F(t) ? t : t.getParent(), i = r && n ? hy(r, n) : void 0;
+function Uk(e, t) {
+ const r = L(e) ? e : e.getParent(), n = L(t) ? t : t.getParent(), i = r && n ? Oy(r, n) : void 0;
return i ? i.commonAncestor : void 0;
}
-function xk(e) {
+function Fk(e) {
const t = e.getStartEndPoints();
if (!t)
return;
const [r, n] = t, i = e.isBackward() ? r : n;
e.focus.set(i.key, i.offset, i.type), e.anchor.set(i.key, i.offset, i.type);
}
-function ri(e) {
- return e?.type === Ke.getType();
+function ai(e) {
+ return e?.type === je.getType();
}
-function _k(e, t) {
+function zk(e, t) {
if (!t)
return;
const r = e.findIndex((n) => n === t);
r && (e.length = r);
}
-function Ck(e, t) {
+function Kk(e, t) {
if (!t)
return e;
const r = t.getIndexWithinParent();
return e.splice(r + 1, e.length - r - 1);
}
-function we(e, t = !1) {
+function qe(e, t = !1) {
return `\\${t ? "+" : ""}${e}`;
}
-function rt(e, t = !1) {
+function st(e, t = !1) {
return `\\${t ? "+" : ""}${e}*`;
}
-function Cp(e, t, r) {
- const n = we(e);
+function Up(e, t, r) {
+ const n = qe(e);
if (t?.startsWith(n)) {
const i = t.slice(n.length).replace(/^[\s ]+/, ""), s = /^([^ \u00A0\\]+)/.exec(i);
s && (r = s[1]);
}
return r;
}
-function Lt(e, t) {
- let r = we(e);
- return t && (r += `${I}${t}`), r += " ", r;
+function Dt(e, t) {
+ let r = qe(e);
+ return t && (r += `${R}${t}`), r += " ", r;
}
-function Sk(e) {
- const t = e[ds];
+function jk(e) {
+ const t = e[gs];
if (t && typeof t == "object" && "textType" in t) {
const r = t.textType;
if (typeof r == "string")
return r;
}
}
-function Sp(e) {
- return Dc(e) || Yf(e) && e.textType === "marker" || ri(e) && Sk(e) === "attribute" ? "" : ri(e) && e.text !== I ? e.text : sk(e) ? e.children.map((t) => Sp(t)).join("") : "";
+function Fp(e) {
+ return Hc(e) || fp(e) && e.textType === "marker" || ai(e) && jk(e) === "attribute" ? "" : ai(e) && e.text !== R ? e.text : vk(e) ? e.children.map((t) => Fp(t)).join("") : "";
}
-function vk(e) {
- return e.map((r) => Sp(r)).filter((r) => r.length > 0).join(" ").trim();
+function Bk(e) {
+ return e.map((r) => Fp(r)).filter((r) => r.length > 0).join(" ").trim();
}
-function At(e) {
- return " " + e + I;
+function Pt(e) {
+ return " " + e + R;
}
-function Rc(e) {
+function jc(e) {
const t = [];
for (const r of e) {
- if (!D(r))
+ if (!I(r))
continue;
- const n = vp(r);
- n !== Dt && n.length > 0 && t.push(n);
+ const n = zp(r);
+ n !== Ut && n.length > 0 && t.push(n);
}
return t.join(" ").trim();
}
-function vp(e) {
- return O(e) || Pr(e) || v(e) && ne(e, oe) === "attribute" ? "" : v(e) ? e.getTextContent() : F(e) ? e.getChildren().map((t) => vp(t)).join("") : "";
+function zp(e) {
+ return O(e) || Rr(e) || M(e) && ne(e, oe) === "attribute" ? "" : M(e) ? e.getTextContent() : L(e) ? e.getChildren().map((t) => zp(t)).join("") : "";
}
-function Pr(e) {
- return Yt(e) && e.getTextType() === "marker";
+function Rr(e) {
+ return Jt(e) && e.getTextType() === "marker";
}
-function Ft(e) {
- return O(e) || Pr(e);
+function zt(e) {
+ return O(e) || Rr(e);
}
-function Su(e, t) {
- Mk(e, t), e.setMarker(t);
+function Lu(e, t) {
+ Vk(e, t), e.setMarker(t);
}
-function Mk(e, t) {
- const r = e.getMarker(), n = we(r), i = we(r, !0), s = rt(r), o = rt(r, !0), a = me.isNoteContentMarker(t);
+function Vk(e, t) {
+ const r = e.getMarker(), n = qe(r), i = qe(r, !0), s = st(r), o = st(r, !0), a = ye.isNoteContentMarker(t);
e.getChildren().forEach((c) => {
- if (!Ft(c))
+ if (!zt(c))
return;
- const l = c.getTextContent(), u = l === n || l === i, d = !u && (l === s || l === o);
- if (!(!u && !d)) {
- if (d && a) {
+ const l = c.getTextContent(), d = l === n || l === i, u = !d && (l === s || l === o);
+ if (!(!d && !u)) {
+ if (u && a) {
c.remove();
return;
}
if (O(c))
c.setMarker(t);
- else if (Pr(c)) {
- const f = l.startsWith(we("", !0));
- c.setTextContent(u ? we(t, f) : rt(t, f));
+ else if (Rr(c)) {
+ const f = l.startsWith(qe("", !0));
+ c.setTextContent(d ? qe(t, f) : st(t, f));
}
}
});
}
-function Ue(e, t = sy) {
+function Fe(e, t = _y) {
const r = { ...e };
return t.forEach((n) => {
Reflect.deleteProperty(r, n);
}), Object.keys(r).length === 0 ? void 0 : r;
}
-function Ae(e) {
+function Pe(e) {
return Object.fromEntries(Object.entries(e).filter(([, t]) => t !== void 0));
}
-function Mp(e) {
+function Kp(e) {
const t = e instanceof Error ? e.message : String(e);
return t.includes("$caretFromPoint") && (t.includes("does not inherit from ElementNode") || t.includes("does not inherit from TextNode"));
}
-function $c(e) {
+function Bc(e) {
if (!N(e))
- return vu(e);
+ return Iu(e);
const t = e.anchor.getNode();
- if (t && (e.anchor.type === "element" && !F(t) || e.anchor.type === "text" && !v(t)))
+ if (t && (e.anchor.type === "element" && !L(t) || e.anchor.type === "text" && !M(t)))
return t ?? void 0;
try {
- return vu(e) ?? t ?? void 0;
+ return Iu(e) ?? t ?? void 0;
} catch (n) {
- if (Mp(n))
+ if (Kp(n))
return t ?? void 0;
throw n;
}
}
-function Ek(e, t) {
+function Wk(e, t) {
if (!t)
return (e + 1).toString();
const r = t.split("-");
@@ -6238,7 +6239,7 @@ function Ek(e, t) {
const i = String.fromCharCode(n[2].charCodeAt(0) + 1);
return `${n[1]}${i}`;
}
-function Ic(e, t) {
+function Vc(e, t) {
if (!t)
return !1;
const r = t.split("-").map((n) => parseInt(n));
@@ -6246,49 +6247,49 @@ function Ic(e, t) {
throw new Error("isVerseInRange: invalid range");
return r.length === 1 ? e === r[0] : r.length === 2 && isNaN(r[1]) ? e >= r[0] : (r.length === 2 && isNaN(r[0]) || e >= r[0]) && e <= r[1];
}
-function Ep(e) {
+function jp(e) {
return !!e && e.includes("-");
}
-function Ap(e) {
+function Bp(e) {
const t = e.split("-"), r = parseInt(t[0], 10), n = t.length > 1 ? parseInt(t[t.length - 1], 10) : r;
return { start: r, end: n };
}
-function vu(e) {
+function Iu(e) {
if (!e)
return;
const t = e.getNodes();
if (t.length > 0)
return e.isBackward() ? t[t.length - 1] : t[0];
}
-function Lc(e) {
+function Wc(e) {
if (!e)
return !1;
- if (us(e) || O(e) || Pr(e) || Le(e) || Yt(e) && e.getTextType() === "attribute")
+ if (Nn(e) || O(e) || Rr(e) || De(e) || Jt(e) && e.getTextType() === "attribute")
return !0;
- if (v(e)) {
+ if (M(e)) {
const t = ne(e, oe);
if (t === fr || t === "attribute")
return !0;
const r = e.getTextContent();
- if (r === "" || r === I || ps(r))
+ if (r === "" || r === R || ys(r))
return !0;
}
return !1;
}
-function mo() {
- const e = pe(I);
- return bt(e, oe, fr), e.setMode("token"), e;
+function To() {
+ const e = ge(R);
+ return kt(e, oe, fr), e.setMode("token"), e;
}
-function Ak(e) {
+function Hk(e) {
const t = e.getTextContent();
- t.startsWith(I) || e.setTextContent(I + t);
+ t.startsWith(R) || e.setTextContent(R + t);
}
-function Nn(e) {
- return v(e) && ne(e, oe) === fr;
+function Rn(e) {
+ return M(e) && ne(e, oe) === fr;
}
-function Pp(e) {
+function Vp(e) {
const t = e.getFirstChild();
- if (!Ft(t) || t === null || Nn(t.getNextSibling()))
+ if (!zt(t) || t === null || Rn(t.getNextSibling()))
return !1;
const r = w();
if (!N(r) || !r.isCollapsed())
@@ -6299,18 +6300,18 @@ function Pp(e) {
const i = t.getNextSibling();
return i !== null && n.is(i) && r.anchor.offset === 0;
}
-function di(e) {
+function gi(e) {
const t = [];
let r;
const n = () => {
r && (t.push({ type: "text", segments: r.segments, length: r.length }), r = void 0);
}, i = (s) => {
- if (!Lc(s)) {
- if (Ce(s)) {
+ if (!Wc(s)) {
+ if (_e(s)) {
s.getChildren().forEach(i);
return;
}
- if (v(s) && s.getType() === Ke.getType()) {
+ if (M(s) && s.getType() === je.getType()) {
r ??= { segments: [], length: 0 }, r.segments.push({ node: s, start: r.length }), r.length += s.getTextContentSize();
return;
}
@@ -6319,20 +6320,20 @@ function di(e) {
};
return e.getChildren().forEach(i), n(), t;
}
-function yo(e) {
+function xo(e) {
let t = e.getParent();
- for (; t && Ce(t); )
+ for (; t && _e(t); )
t = t.getParent();
return t;
}
-function Pk(e, t) {
- return di(e).findIndex((r) => r.type === "element" ? r.node.is(t) : r.segments.some((n) => n.node.is(t)));
+function Gk(e, t) {
+ return gi(e).findIndex((r) => r.type === "element" ? r.node.is(t) : r.segments.some((n) => n.node.is(t)));
}
-function Nk(e, t) {
- const r = yo(e);
+function Jk(e, t) {
+ const r = xo(e);
if (!r)
return;
- const n = di(r);
+ const n = gi(r);
for (let i = 0; i < n.length; i++) {
const s = n[i];
if (s.type !== "text")
@@ -6342,7 +6343,7 @@ function Nk(e, t) {
return { parent: r, index: i, offset: o.start + t };
}
}
-function Ok(e, t) {
+function Yk(e, t) {
if (t < 0 || t > e.length)
return;
for (const n of e.segments) {
@@ -6354,35 +6355,35 @@ function Ok(e, t) {
if (r)
return [r.node, t - r.start];
}
-function Np(e, t) {
- const r = di(e), n = e.getChildAtIndex(t);
+function Wp(e, t) {
+ const r = gi(e), n = e.getChildAtIndex(t);
if (!n)
return { type: "index", index: r.length };
- if (Lc(n))
- return Np(e, t + 1);
+ if (Wc(n))
+ return Wp(e, t + 1);
for (let i = 0; i < r.length; i++) {
const s = r[i];
if (s.type === "element") {
- if (s.node.is(n) || Ks(s.node, n.getKey()))
+ if (s.node.is(n) || Ws(s.node, n.getKey()))
return { type: "index", index: i };
continue;
}
for (const o of s.segments)
- if (o.node.is(n) || Ks(o.node, n.getKey()))
+ if (o.node.is(n) || Ws(o.node, n.getKey()))
return o.start === 0 ? { type: "index", index: i } : { type: "text", index: i, offset: o.start };
}
return { type: "index", index: r.length };
}
-function wk(e, t) {
+function Xk(e, t) {
if (t <= 0)
return 0;
- const r = di(e);
+ const r = gi(e);
if (r.length === 0 || t > r.length)
return e.getChildrenSize();
- const n = r[t - 1], i = n.type === "element" ? n.node : n.segments[n.segments.length - 1]?.node, s = i ? qk(e, i) : void 0;
+ const n = r[t - 1], i = n.type === "element" ? n.node : n.segments[n.segments.length - 1]?.node, s = i ? Qk(e, i) : void 0;
return s ? s.getIndexWithinParent() + 1 : e.getChildrenSize();
}
-function qk(e, t) {
+function Qk(e, t) {
let r = t;
for (; r; ) {
const n = r.getParent();
@@ -6391,8 +6392,8 @@ function qk(e, t) {
r = n;
}
}
-const Rk = 1;
-class hr extends Ke {
+const Zk = 1;
+class hr extends je {
__marker;
__markerSyntax;
__nested;
@@ -6401,7 +6402,7 @@ class hr extends Ke {
// slotting a new field ahead of it would silently reinterpret an existing 3-argument call's
// `NodeKey` as this flag.
constructor(t = "", r = "opening", n, i = !1) {
- super(fn(t, r, i), n), this.__marker = t, this.__markerSyntax = r, this.__nested = i;
+ super(hn(t, r, i), n), this.__marker = t, this.__markerSyntax = r, this.__nested = i;
}
static getType() {
return "marker";
@@ -6410,7 +6411,7 @@ class hr extends Ke {
return new hr(t.__marker, t.__markerSyntax, t.__key, t.__nested);
}
static importJSON(t) {
- return ot().updateFromJSON(t);
+ return ct().updateFromJSON(t);
}
updateFromJSON(t) {
const { marker: r, markerSyntax: n = "opening", nested: i = !1 } = t, o = super.updateFromJSON({
@@ -6418,7 +6419,7 @@ class hr extends Ke {
// An EMPTY serialized text is the "build canonical bytes" sentinel — the adaptor's
// createMarker serializes glyphs with `text: ""` and relies on the import deriving them.
// Any non-empty text is the glyph's actual displayed bytes and is kept verbatim.
- text: t.text || fn(r, n, i)
+ text: t.text || hn(r, n, i)
}).getWritable();
return o.__marker = r, o.__markerSyntax = n, o.__nested = i, o;
}
@@ -6426,7 +6427,7 @@ class hr extends Ke {
if (this.__marker === t)
return this;
const r = this.getWritable();
- return r.__marker = t, r.__text = fn(t, r.__markerSyntax, r.__nested), r;
+ return r.__marker = t, r.__text = hn(t, r.__markerSyntax, r.__nested), r;
}
getMarker() {
return this.getLatest().__marker;
@@ -6435,7 +6436,7 @@ class hr extends Ke {
if (this.__markerSyntax === t)
return this;
const r = this.getWritable();
- return r.__markerSyntax = t, r.__text = fn(r.__marker, t, r.__nested), r;
+ return r.__markerSyntax = t, r.__text = hn(r.__marker, t, r.__nested), r;
}
getMarkerSyntax() {
return this.getLatest().__markerSyntax;
@@ -6444,7 +6445,7 @@ class hr extends Ke {
if (this.__nested === t)
return this;
const r = this.getWritable();
- return r.__nested = t, r.__text = fn(r.__marker, r.__markerSyntax, t), r;
+ return r.__nested = t, r.__text = hn(r.__marker, r.__markerSyntax, t), r;
}
getNested() {
return this.getLatest().__nested;
@@ -6467,34 +6468,34 @@ class hr extends Ke {
// Only serialize the flag for genuinely nested glyphs; absence means non-nested, so
// existing states (and the overwhelmingly common non-nested markers) stay unchanged.
...this.getNested() ? { nested: !0 } : {},
- version: Rk
+ version: Zk
};
}
}
-function ot(e, t, r) {
- return je(new hr(e, t, void 0, r));
+function ct(e, t, r) {
+ return Ve(new hr(e, t, void 0, r));
}
function O(e) {
return e instanceof hr;
}
-function Dc(e) {
+function Hc(e) {
return e?.type === hr.getType();
}
-function Xr(e) {
- return e.getTextContent() === fn(e.getMarker(), e.getMarkerSyntax(), e.getNested());
+function en(e) {
+ return e.getTextContent() === hn(e.getMarker(), e.getMarkerSyntax(), e.getNested());
}
-function $k(e) {
- e.setTextContent(fn(e.getMarker(), e.getMarkerSyntax(), e.getNested()));
+function eT(e) {
+ e.setTextContent(hn(e.getMarker(), e.getMarkerSyntax(), e.getNested()));
}
-function fn(e, t, r = !1) {
- return t === "closing" ? rt(e, r) : t === "selfClosing" ? rt("") : we(e, r);
+function hn(e, t, r = !1) {
+ return t === "closing" ? st(e, r) : t === "selfClosing" ? st("") : qe(e, r);
}
-const Ik = /* @__PURE__ */ new Set(["closed"]);
+const tT = /* @__PURE__ */ new Set(["closed"]);
function ar(e, t) {
- const r = Object.entries(e).filter(([n, i]) => i !== void 0 && !Ik.has(n));
+ const r = Object.entries(e).filter(([n, i]) => i !== void 0 && !tT.has(n));
return r.length === 0 ? "" : r.length === 1 && r[0][0] === t && r[0][1] !== "" ? `|${r[0][1]}` : `|${r.map(([n, i]) => `${n}="${i}"`).join(" ")}`;
}
-function Op(e, t) {
+function Hp(e, t) {
if (!t || t.length === 0)
return e;
const r = {};
@@ -6504,16 +6505,16 @@ function Op(e, t) {
Object.hasOwn(r, n) || (r[n] = i);
}), r;
}
-function wp(e) {
- const t = Object.keys(e).filter((n) => !pb.includes(n)), r = [
+function Gp(e) {
+ const t = Object.keys(e).filter((n) => !wb.includes(n)), r = [
...t.filter((n) => n === "sid"),
...t.filter((n) => n === "eid"),
...t.filter((n) => n !== "sid" && n !== "eid")
];
return t.every((n, i) => n === r[i]) ? void 0 : t;
}
-function qp(e, t, r, n) {
- return Op(
+function Jp(e, t, r, n) {
+ return Hp(
// Presence, not truthiness: an authored `sid=""` is a byte the document holds, and folding it
// out here deletes it from the displayed run — which a settle then re-derives node state from,
// so the empty value would be gone from the file. Matches `orderedAttributes`' own `in` test
@@ -6526,35 +6527,35 @@ function qp(e, t, r, n) {
n
);
}
-function Di(e) {
+function ji(e) {
return e.getChildren().find((t) => O(t) && t.getMarkerSyntax() === "closing" && t.getMarker() === e.getMarker());
}
-function Lk(e) {
+function rT(e) {
const t = e.getUnknownAttributes();
- return !t || !Object.keys(t).some((n) => n !== "closed") ? !1 : Di(e) === void 0 && Rp(e) === void 0;
+ return !t || !Object.keys(t).some((n) => n !== "closed") ? !1 : ji(e) === void 0 && Yp(e) === void 0;
}
-function Rp(e) {
- return e.getChildren().find((t) => v(t) && ne(t, oe) === "attribute");
+function Yp(e) {
+ return e.getChildren().find((t) => M(t) && ne(t, oe) === "attribute");
}
-function Yi(e, t) {
- return hs(e.getNextSibling(), t);
+function ts(e, t) {
+ return bs(e.getNextSibling(), t);
}
-const Dk = /^[ \u00A0]+$/;
-function Uc(e) {
- if (Xr(e))
+const nT = /^[ \u00A0]+$/;
+function Gc(e) {
+ if (en(e))
return !0;
if (e.getMarkerSyntax() !== "opening")
return !1;
- const t = we(e.getMarker(), e.getNested()), r = e.getTextContent();
- return r.startsWith(t) && Dk.test(r.slice(t.length));
+ const t = qe(e.getMarker(), e.getNested()), r = e.getTextContent();
+ return r.startsWith(t) && nT.test(r.slice(t.length));
}
-function hs(e, t) {
+function bs(e, t) {
let r, n, i, s;
- return Le(e) && e.getRunKind() === t && (s = e, e = e.getFirstChild()), O(e) && e.getMarkerSyntax() === "opening" && e.getMarker() === t && // Typed-spacing licensed ({@link $isCanonicalRunOpenerGlyph}): a trailing space typed on the
+ return De(e) && e.getRunKind() === t && (s = e, e = e.getFirstChild()), O(e) && e.getMarkerSyntax() === "opening" && e.getMarker() === t && // Typed-spacing licensed ({@link $isCanonicalRunOpenerGlyph}): a trailing space typed on the
// opener is at rest, not byte damage.
- Uc(e) && (r = e, e = e.getNextSibling()), v(e) && ne(e, oe) === "attribute" && (n = e, e = e.getNextSibling()), O(e) && e.getMarkerSyntax() === "closing" && e.getMarker() === t && Xr(e) && (i = e), { opener: r, value: n, closer: i, wrapper: s };
+ Gc(e) && (r = e, e = e.getNextSibling()), M(e) && ne(e, oe) === "attribute" && (n = e, e = e.getNextSibling()), O(e) && e.getMarkerSyntax() === "closing" && e.getMarker() === t && en(e) && (i = e), { opener: r, value: n, closer: i, wrapper: s };
}
-function Xi(e) {
+function rs(e) {
const t = e.getChildren();
let r = 0;
for (; r < t.length; ) {
@@ -6564,87 +6565,87 @@ function Xi(e) {
r++;
}
const n = t[r];
- if (v(n) && n.getTextContent() === At(e.getCaller()))
+ if (M(n) && n.getTextContent() === Pt(e.getCaller()))
return n;
}
-function $p(e) {
- const t = Xi(e);
- return t ? hs(t.getNextSibling(), "cat") : {};
+function Xp(e) {
+ const t = rs(e);
+ return t ? bs(t.getNextSibling(), "cat") : {};
}
-function bo(e) {
+function _o(e) {
const t = e.getFirstChild();
- if (!(!v(t) || O(t)) && ne(t, oe) !== "attribute")
+ if (!(!M(t) || O(t)) && ne(t, oe) !== "attribute")
return t;
}
-function Ip(e) {
- const t = bo(e);
- return t ? hs(t.getNextSibling(), "ca") : {};
+function Qp(e) {
+ const t = _o(e);
+ return t ? bs(t.getNextSibling(), "ca") : {};
}
-function Lp(e) {
- const t = bo(e);
+function Zp(e) {
+ const t = _o(e);
if (!t)
return;
- const r = hs(t.getNextSibling(), "ca");
+ const r = bs(t.getNextSibling(), "ca");
return r.wrapper ?? r.closer ?? t;
}
-function Dp(e) {
- const t = Lp(e);
- return t ? hs(t.getNextSibling(), "cp") : {};
+function eh(e) {
+ const t = Zp(e);
+ return t ? bs(t.getNextSibling(), "cp") : {};
}
-function Up(e) {
+function th(e) {
const t = e.getParent();
- if (!D(t))
+ if (!I(t))
return;
const r = t.getMarker();
if (!(r !== "va" && r !== "vp"))
for (let n = t.getPreviousSibling(); n; n = n.getPreviousSibling()) {
- if (Oe(n))
+ if (we(n))
return n;
- if (!(O(n) && (n.getMarker() === "va" || n.getMarker() === "vp") || v(n) && ne(n, oe) === "attribute" || D(n) && (n.getMarker() === "va" || n.getMarker() === "vp") || Le(n)))
+ if (!(O(n) && (n.getMarker() === "va" || n.getMarker() === "vp") || M(n) && ne(n, oe) === "attribute" || I(n) && (n.getMarker() === "va" || n.getMarker() === "vp") || De(n)))
return;
}
}
-function ko(e) {
+function Co(e) {
let t, r, n, i, s = e.getNextSibling();
- return Le(s) && s.getRunKind() === "milestone" && (i = s, s = s.getFirstChild()), O(s) && s.getMarkerSyntax() === "opening" && s.getMarker() === e.getMarker() && // Typed-spacing licensed ({@link $isCanonicalRunOpenerGlyph}): a trailing space typed on the
+ return De(s) && s.getRunKind() === "milestone" && (i = s, s = s.getFirstChild()), O(s) && s.getMarkerSyntax() === "opening" && s.getMarker() === e.getMarker() && // Typed-spacing licensed ({@link $isCanonicalRunOpenerGlyph}): a trailing space typed on the
// opener is at rest, not byte damage.
- Uc(s) && (t = s, s = s.getNextSibling()), v(s) && ne(s, oe) === "attribute" && (r = s, s = s.getNextSibling()), O(s) && s.getMarkerSyntax() === "selfClosing" && Xr(s) && (n = s), { opening: t, attribute: r, closing: n, wrapper: i };
+ Gc(s) && (t = s, s = s.getNextSibling()), M(s) && ne(s, oe) === "attribute" && (r = s, s = s.getNextSibling()), O(s) && s.getMarkerSyntax() === "selfClosing" && en(s) && (n = s), { opening: t, attribute: r, closing: n, wrapper: i };
}
-function Fc(e) {
- return D(yo(e));
+function Jc(e) {
+ return I(xo(e));
}
-function wa(e, t) {
+function Ia(e, t) {
if (e.getMarkerSyntax() === "selfClosing")
return;
const r = e.getMarker();
- return r === t.getMarker() ? Fc(t) : t.getChildren().some((i) => D(i) && i.getMarker() === r) ? !0 : void 0;
+ return r === t.getMarker() ? Jc(t) : t.getChildren().some((i) => I(i) && i.getMarker() === r) ? !0 : void 0;
}
-function Uk(e) {
+function iT(e) {
e.isAttached() && e.getChildren().forEach((t) => {
if (!O(t))
return;
- const r = wa(t, e);
+ const r = Ia(t, e);
r !== void 0 && t.setNested(r);
});
}
-function To(e) {
- return v(e) && e.getType() === Ke.getType() && ne(e, oe) !== "attribute";
+function vo(e) {
+ return M(e) && e.getType() === je.getType() && ne(e, oe) !== "attribute";
}
-function zc(e, t) {
- if (e.getMarkerSyntax() !== "opening" || wa(e, t) === void 0)
+function Yc(e, t) {
+ if (e.getMarkerSyntax() !== "opening" || Ia(e, t) === void 0)
return;
const r = e.getNextSibling();
if (r !== null)
- return O(r) ? wa(r, t) === !0 ? "spacer" : void 0 : To(r) ? r.getTextContent().startsWith(I) ? void 0 : "prefix" : "spacer";
+ return O(r) ? Ia(r, t) === !0 ? "spacer" : void 0 : vo(r) ? r.getTextContent().startsWith(R) ? void 0 : "prefix" : "spacer";
}
-function Fk(e) {
+function sT(e) {
if (e.isAttached()) {
for (const t of e.getChildren())
- if (O(t) && zc(t, e) !== void 0)
+ if (O(t) && Yc(t, e) !== void 0)
return t.getNextSibling()?.getTextContent() ?? "";
}
}
-function Fp(e, t) {
+function rh(e, t) {
const r = w();
if (!N(r) || !r.isCollapsed())
return !1;
@@ -6654,33 +6655,33 @@ function Fp(e, t) {
const i = e.getNextSibling();
return i !== null && n.is(i) && r.anchor.offset === 0;
}
-function zp(e) {
+function nh(e) {
e.isAttached() && e.getChildren().forEach((t) => {
if (!O(t))
return;
- const r = zc(t, e);
- if (r !== void 0 && !Fp(t, e))
+ const r = Yc(t, e);
+ if (r !== void 0 && !rh(t, e))
if (r === "prefix") {
const n = t.getNextSibling();
- v(n) && n.setTextContent(I + n.getTextContent());
+ M(n) && n.setTextContent(R + n.getTextContent());
} else
- t.insertAfter(pe(I));
+ t.insertAfter(ge(R));
});
}
-function Kp(e) {
- return e.isAttached() ? e.getChildren().some((t) => O(t) && zc(t, e) !== void 0 && Fp(t, e)) : !1;
+function ih(e) {
+ return e.isAttached() ? e.getChildren().some((t) => O(t) && Yc(t, e) !== void 0 && rh(t, e)) : !1;
}
-const zk = "file", Kk = "src", jk = "colspan", Bk = "category", Vk = "alt", Wk = "closed", Hk = "false";
-function Gk(e) {
- return e[Wk] !== Hk;
+const oT = "file", aT = "src", cT = "colspan", lT = "category", uT = "alt", dT = "closed", fT = "false";
+function pT(e) {
+ return e[dT] !== fT;
}
-function Jk(e) {
+function hT(e) {
return Object.fromEntries(Object.entries(e).map(([t, r]) => [
- t === zk ? Kk : t,
+ t === oT ? aT : t,
r
]));
}
-function jp(e, t) {
+function sh(e, t) {
if (e === void 0)
return;
const r = Number(t);
@@ -6695,8 +6696,8 @@ function jp(e, t) {
}
return n === e.length ? e : `${e}-${Number(e.slice(n)) + r - 1}`;
}
-function Bp(e, t, r) {
- const n = r ?? {}, i = Gk(n);
+function oh(e, t, r) {
+ const n = r ?? {}, i = pT(n);
switch (e) {
case "optbreak":
return { opening: "//", attributes: "", closingAttributes: "", closing: "" };
@@ -6707,7 +6708,7 @@ function Bp(e, t, r) {
return { opening: `\\${t} `, attributes: "", closingAttributes: "", closing: "" };
case "table:cell":
return {
- opening: `\\${jp(t, n[jk])} `,
+ opening: `\\${sh(t, n[cT])} `,
attributes: "",
closingAttributes: "",
closing: ""
@@ -6716,11 +6717,11 @@ function Bp(e, t, r) {
return {
opening: `\\${t} `,
attributes: "",
- closingAttributes: ar(Jk(n), void 0),
+ closingAttributes: ar(hT(n), void 0),
closing: i ? `\\${t}*` : ""
};
case "sidebar": {
- const { [Bk]: s, ...o } = n;
+ const { [lT]: s, ...o } = n;
return {
opening: "\\esb",
attributes: (s === void 0 ? "" : ` \\cat ${s}\\cat*`) + ar(o, void 0),
@@ -6729,7 +6730,7 @@ function Bp(e, t, r) {
};
}
case "periph": {
- const { [Vk]: s, ...o } = n;
+ const { [uT]: s, ...o } = n;
return {
opening: `\\periph ${s ?? ""}`,
attributes: ar(o, void 0),
@@ -6746,21 +6747,21 @@ function Bp(e, t, r) {
};
}
}
-const xt = { wantsRun: !1, valueText: void 0 }, Nr = {};
-function ta(e, t) {
+const Ct = { wantsRun: !1, valueText: void 0 }, $r = {};
+function ia(e, t) {
if (t === "va")
return e;
- const r = Yi(e, "va");
+ const r = ts(e, "va");
return r.wrapper ?? r.closer ?? e;
}
-function Kc(e) {
+function Xc(e) {
const t = w();
if (!N(t) || !t.isCollapsed())
return !1;
const r = t.anchor.getNode();
if (r.is(e) && t.anchor.offset === e.getTextContentSize())
return !0;
- if (F(e)) {
+ if (L(e)) {
const i = e.getLastDescendant();
if (i !== null && r.is(i) && t.anchor.offset === i.getTextContentSize())
return !0;
@@ -6768,7 +6769,7 @@ function Kc(e) {
const n = e.getNextSibling();
return n !== null && r.is(n) && t.anchor.offset === 0;
}
-function xo(e) {
+function So(e) {
const { opener: t, closer: r } = e;
if (!t)
return !1;
@@ -6778,15 +6779,15 @@ function xo(e) {
const i = n.anchor.getNode(), s = i.is(t) && n.anchor.offset === t.getTextContentSize();
return r ? s || i.is(r) : s;
}
-function Yk(e) {
- return Le(e) ? e.getRunKind() === "va" || e.getRunKind() === "vp" : O(e) ? e.getMarker() === "va" || e.getMarker() === "vp" : v(e) && ne(e, oe) === "attribute";
+function gT(e) {
+ return De(e) ? e.getRunKind() === "va" || e.getRunKind() === "vp" : O(e) ? e.getMarker() === "va" || e.getMarker() === "vp" : M(e) && ne(e, oe) === "attribute";
}
-function Xk(e) {
+function mT(e) {
if (O(e)) {
const n = e.getMarker();
return n === "va" || n === "vp" ? n : void 0;
}
- if (!v(e) || ne(e, oe) !== "attribute")
+ if (!M(e) || ne(e, oe) !== "attribute")
return;
const t = e.getPreviousSibling();
if (!O(t))
@@ -6794,32 +6795,32 @@ function Xk(e) {
const r = t.getMarker();
return r === "va" || r === "vp" ? r : void 0;
}
-function ra(e) {
+function sa(e) {
for (let t = e.getPreviousSibling(); t; t = t.getPreviousSibling()) {
- if (Oe(t))
+ if (we(t))
return t;
- if (!Yk(t))
+ if (!gT(t))
return;
}
}
-function Mu(e) {
+function Du(e) {
return {
kind: e,
- ownerPredicate: (t) => Oe(t),
+ ownerPredicate: (t) => we(t),
ownerOf: (t) => {
- if (Le(t))
- return t.getRunKind() === e ? ra(t) : void 0;
+ if (De(t))
+ return t.getRunKind() === e ? sa(t) : void 0;
const r = t.getParent();
- return Le(r) ? r.getRunKind() === e ? ra(r) : void 0 : Xk(t) === e ? ra(t) : void 0;
+ return De(r) ? r.getRunKind() === e ? sa(r) : void 0 : mT(t) === e ? sa(t) : void 0;
},
expectedPieces: (t) => {
- if (!Oe(t))
- return xt;
+ if (!we(t))
+ return Ct;
const r = e === "va" ? t.getAltnumber() : t.getPubnumber();
- return r === void 0 ? xt : { wantsRun: !0, valueText: I + r };
+ return r === void 0 ? Ct : { wantsRun: !0, valueText: R + r };
},
- scanPieces: (t) => Oe(t) ? Yi(ta(t, e), e) : Nr,
- graceSite: (t, r) => Oe(t) ? !r.opener && !r.closer ? Kc(ta(t, e)) : xo(r) : !1,
+ scanPieces: (t) => we(t) ? ts(ia(t, e), e) : $r,
+ graceSite: (t, r) => we(t) ? !r.opener && !r.closer ? Xc(ia(t, e)) : So(r) : !1,
settleScope: "owner",
deletionPolicy: "retokenize",
byteFormat: {
@@ -6828,44 +6829,44 @@ function Mu(e) {
glyphs: "with-value",
glyphMarker: () => e,
closerSyntax: "closing",
- insertRunAfter: (t) => Oe(t) ? ta(t, e) : void 0
+ insertRunAfter: (t) => we(t) ? ia(t, e) : void 0
}
};
}
-const Qk = {
+const yT = {
kind: "separator",
// The NBSP a char span shows after its opening glyph. Its "deletion" is a TEXT mutation (an NBSP
// prefix edit), not node destruction, so it has no owner walk and no destruction pend — its
// caret-grace path is what settles it, exactly as before joining the registry.
- ownerPredicate: (e) => D(e),
+ ownerPredicate: (e) => I(e),
ownerOf: () => {
},
- expectedPieces: () => xt,
- scanPieces: () => Nr,
- graceSite: (e) => D(e) && Kp(e),
+ expectedPieces: () => Ct,
+ scanPieces: () => $r,
+ graceSite: (e) => I(e) && ih(e),
settleScope: "owner",
deletionPolicy: "retokenize",
byteFormat: { writer: "kind-owned", glyphs: "none" }
-}, Zk = {
+}, bT = {
kind: "char",
- ownerPredicate: (e) => D(e),
+ ownerPredicate: (e) => I(e),
ownerOf: (e) => {
- if (!v(e) || ne(e, oe) !== "attribute")
+ if (!M(e) || ne(e, oe) !== "attribute")
return;
const t = e.getParent();
- return D(t) ? t : void 0;
+ return I(t) ? t : void 0;
},
expectedPieces: (e) => {
- if (!D(e) || Di(e) === void 0)
- return xt;
- const t = ar(e.getUnknownAttributes() ?? {}, po(e.getMarker()));
- return t === "" ? xt : { wantsRun: !0, valueText: t };
+ if (!I(e) || ji(e) === void 0)
+ return Ct;
+ const t = ar(e.getUnknownAttributes() ?? {}, yo(e.getMarker()));
+ return t === "" ? Ct : { wantsRun: !0, valueText: t };
},
- scanPieces: (e) => D(e) ? { value: Rp(e) } : Nr,
+ scanPieces: (e) => I(e) ? { value: Yp(e) } : $r,
graceSite: (e, t) => {
- if (!D(e) || t.value)
+ if (!I(e) || t.value)
return !1;
- const r = Di(e);
+ const r = ji(e);
if (!r)
return !1;
const n = w();
@@ -6882,54 +6883,54 @@ const Qk = {
byteFormat: {
writer: "owner-children",
glyphs: "none",
- insertRunBefore: (e) => D(e) ? Di(e) : void 0
+ insertRunBefore: (e) => I(e) ? ji(e) : void 0
}
};
-function Vp(e) {
+function ah(e) {
if (O(e))
return e.getMarker() === "cat";
- if (!v(e) || ne(e, oe) !== "attribute")
+ if (!M(e) || ne(e, oe) !== "attribute")
return !1;
const t = e.getPreviousSibling();
return O(t) && t.getMarker() === "cat";
}
-function eT(e) {
+function kT(e) {
const t = e.getParent();
- if (!j(t))
+ if (!z(t))
return;
- const r = Xi(t);
+ const r = rs(t);
if (r)
for (let n = e.getPreviousSibling(); n; n = n.getPreviousSibling()) {
if (n.is(r))
return t;
- if (!Vp(n))
+ if (!ah(n))
return;
}
}
-const tT = {
+const TT = {
kind: "cat",
- ownerPredicate: (e) => j(e),
+ ownerPredicate: (e) => z(e),
ownerOf: (e) => {
- if (Le(e))
- return e.getRunKind() === "cat" && j(e.getParent()) ? e.getParent() ?? void 0 : void 0;
+ if (De(e))
+ return e.getRunKind() === "cat" && z(e.getParent()) ? e.getParent() ?? void 0 : void 0;
const t = e.getParent();
- return Le(t) ? t.getRunKind() === "cat" && j(t.getParent()) ? t.getParent() ?? void 0 : void 0 : Vp(e) ? eT(e) : void 0;
+ return De(t) ? t.getRunKind() === "cat" && z(t.getParent()) ? t.getParent() ?? void 0 : void 0 : ah(e) ? kT(e) : void 0;
},
expectedPieces: (e) => {
- if (!j(e) || e.getIsCollapsed() !== !1)
- return xt;
+ if (!z(e) || e.getIsCollapsed() !== !1)
+ return Ct;
const t = e.getCategory();
- return t === void 0 ? xt : { wantsRun: !0, valueText: I + t };
+ return t === void 0 ? Ct : { wantsRun: !0, valueText: R + t };
},
- scanPieces: (e) => j(e) ? $p(e) : Nr,
+ scanPieces: (e) => z(e) ? Xp(e) : $r,
graceSite: (e, t) => {
- if (!j(e))
+ if (!z(e))
return !1;
if (!t.opener && !t.closer) {
- const r = Xi(e);
- return r !== void 0 && Kc(r);
+ const r = rs(e);
+ return r !== void 0 && Xc(r);
}
- return xo(t);
+ return So(t);
},
settleScope: "owner",
deletionPolicy: "retokenize",
@@ -6939,18 +6940,18 @@ const tT = {
glyphs: "with-value",
glyphMarker: () => "cat",
closerSyntax: "closing",
- insertRunAfter: (e) => j(e) ? Xi(e) : void 0
+ insertRunAfter: (e) => z(e) ? rs(e) : void 0
}
};
-function rT(e) {
- return Le(e) ? e.getRunKind() === "ca" || e.getRunKind() === "cp" : O(e) ? e.getMarker() === "ca" || e.getMarker() === "cp" : v(e) && ne(e, oe) === "attribute";
+function xT(e) {
+ return De(e) ? e.getRunKind() === "ca" || e.getRunKind() === "cp" : O(e) ? e.getMarker() === "ca" || e.getMarker() === "cp" : M(e) && ne(e, oe) === "attribute";
}
-function nT(e) {
+function _T(e) {
if (O(e)) {
const n = e.getMarker();
return n === "ca" || n === "cp" ? n : void 0;
}
- if (!v(e) || ne(e, oe) !== "attribute")
+ if (!M(e) || ne(e, oe) !== "attribute")
return;
const t = e.getPreviousSibling();
if (!O(t))
@@ -6958,45 +6959,45 @@ function nT(e) {
const r = t.getMarker();
return r === "ca" || r === "cp" ? r : void 0;
}
-function iT(e) {
+function CT(e) {
const t = e.getParent();
- if (!Pe(t))
+ if (!Ee(t))
return;
- const r = bo(t);
+ const r = _o(t);
if (r)
for (let n = e.getPreviousSibling(); n; n = n.getPreviousSibling()) {
if (n.is(r))
return t;
- if (!rT(n))
+ if (!xT(n))
return;
}
}
-function Eu(e) {
- const t = (r) => Pe(r) ? e === "ca" ? bo(r) : Lp(r) : void 0;
+function Uu(e) {
+ const t = (r) => Ee(r) ? e === "ca" ? _o(r) : Zp(r) : void 0;
return {
kind: e,
- ownerPredicate: (r) => Pe(r),
+ ownerPredicate: (r) => Ee(r),
ownerOf: (r) => {
- if (Le(r))
- return r.getRunKind() === e && Pe(r.getParent()) ? r.getParent() ?? void 0 : void 0;
+ if (De(r))
+ return r.getRunKind() === e && Ee(r.getParent()) ? r.getParent() ?? void 0 : void 0;
const n = r.getParent();
- return Le(n) ? n.getRunKind() === e && Pe(n.getParent()) ? n.getParent() ?? void 0 : void 0 : nT(r) === e ? iT(r) : void 0;
+ return De(n) ? n.getRunKind() === e && Ee(n.getParent()) ? n.getParent() ?? void 0 : void 0 : _T(r) === e ? CT(r) : void 0;
},
expectedPieces: (r) => {
- if (!Pe(r))
- return xt;
+ if (!Ee(r))
+ return Ct;
const n = e === "ca" ? r.getAltnumber() : r.getPubnumber();
- return n === void 0 ? xt : { wantsRun: !0, valueText: I + n };
+ return n === void 0 ? Ct : { wantsRun: !0, valueText: R + n };
},
- scanPieces: (r) => Pe(r) ? e === "ca" ? Ip(r) : Dp(r) : Nr,
+ scanPieces: (r) => Ee(r) ? e === "ca" ? Qp(r) : eh(r) : $r,
graceSite: (r, n) => {
- if (!Pe(r))
+ if (!Ee(r))
return !1;
if (!n.opener && !n.closer) {
const i = t(r);
- return i !== void 0 && Kc(i);
+ return i !== void 0 && Xc(i);
}
- return xo(n);
+ return So(n);
},
settleScope: "owner",
deletionPolicy: "retokenize",
@@ -7010,47 +7011,47 @@ function Eu(e) {
}
};
}
-function Wp(e) {
+function ch(e) {
if (O(e)) {
const t = e.getMarkerSyntax();
return t === "selfClosing" || t === "opening";
}
- return v(e) && ne(e, oe) === "attribute";
+ return M(e) && ne(e, oe) === "attribute";
}
-function sT(e) {
+function vT(e) {
for (let t = e.getPreviousSibling(); t; t = t.getPreviousSibling()) {
- if (Be(t)) {
+ if (He(t)) {
const r = O(e) && e.getMarkerSyntax() === "opening" ? e : void 0;
return !r || r.getMarker() === t.getMarker() ? t : void 0;
}
- if (!Wp(t))
+ if (!ch(t))
return;
}
}
-const oT = {
+const ST = {
kind: "milestone",
- ownerPredicate: (e) => Be(e),
+ ownerPredicate: (e) => He(e),
ownerOf: (e) => {
- const t = Le(e) ? e.getRunKind() === "milestone" ? e : void 0 : Le(e.getParent()) ? e.getParent() : Wp(e) ? e : void 0;
- if (!t || Le(t) && t.getRunKind() !== "milestone")
+ const t = De(e) ? e.getRunKind() === "milestone" ? e : void 0 : De(e.getParent()) ? e.getParent() : ch(e) ? e : void 0;
+ if (!t || De(t) && t.getRunKind() !== "milestone")
return;
const r = t.getPreviousSibling();
- return Le(t) ? Be(r) ? r : void 0 : sT(t);
+ return De(t) ? He(r) ? r : void 0 : vT(t);
},
expectedPieces: (e) => {
- if (!Be(e))
- return xt;
- const t = qp(e.getSid(), e.getEid(), e.getUnknownAttributes(), e.getAttributeOrder()), r = ar(t, ho(e.getMarker()));
- return { wantsRun: !0, valueText: r === "" ? void 0 : I + r };
+ if (!He(e))
+ return Ct;
+ const t = Jp(e.getSid(), e.getEid(), e.getUnknownAttributes(), e.getAttributeOrder()), r = ar(t, bo(e.getMarker()));
+ return { wantsRun: !0, valueText: r === "" ? void 0 : R + r };
},
scanPieces: (e) => {
- if (!Be(e))
- return Nr;
- const { opening: t, attribute: r, closing: n, wrapper: i } = ko(e);
+ if (!He(e))
+ return $r;
+ const { opening: t, attribute: r, closing: n, wrapper: i } = Co(e);
return { opener: t, value: r, closer: n, wrapper: i };
},
graceSite: (e, t) => {
- if (!Be(e))
+ if (!He(e))
return !1;
if (!t.opener && !t.closer) {
const r = w();
@@ -7062,7 +7063,7 @@ const oT = {
const s = e.getNextSibling();
return s !== null && n.is(s) && r.anchor.offset === 0;
}
- return xo(t);
+ return So(t);
},
settleScope: "owner",
deletionPolicy: "remove-owner",
@@ -7070,17 +7071,17 @@ const oT = {
writer: "wrapper",
runKind: "milestone",
glyphs: "unconditional",
- glyphMarker: (e) => Be(e) ? e.getMarker() : "",
+ glyphMarker: (e) => He(e) ? e.getMarker() : "",
closerSyntax: "selfClosing",
insertRunAfter: (e) => e
}
-}, aT = Bp("optbreak", void 0, void 0).opening, cT = {
+}, MT = oh("optbreak", void 0, void 0).opening, ET = {
kind: "optbreak",
- ownerPredicate: (e) => De(e) && e.getTag() === "optbreak",
+ ownerPredicate: (e) => Ue(e) && e.getTag() === "optbreak",
ownerOf: (e) => {
const t = e.getParent();
- if (!(!De(t) || t.getTag() !== "optbreak"))
- return v(e) || Yt(e) ? t : void 0;
+ if (!(!Ue(t) || t.getTag() !== "optbreak"))
+ return M(e) || Jt(e) ? t : void 0;
},
// `valueText` is the RENDERED BYTES the kind owes — so `$runDiverges`'s value-byte comparison
// classifies the scanned token by what it actually spells: a canonical `//` is at rest, a
@@ -7089,13 +7090,13 @@ const oT = {
// CANONICAL optbreak read as diverged while a GUTTED one read as at rest. Nothing ever WRITES
// from this (the `"read-only"` writer returns before any sync write), so the value is purely
// classificatory.
- expectedPieces: () => ({ wantsRun: !0, valueText: aT }),
- scanPieces: (e) => De(e) ? { value: e.getFirstChild() ?? void 0 } : Nr,
+ expectedPieces: () => ({ wantsRun: !0, valueText: MT }),
+ scanPieces: (e) => Ue(e) ? { value: e.getFirstChild() ?? void 0 } : $r,
graceSite: () => !1,
settleScope: "owner",
deletionPolicy: "remove-owner",
byteFormat: { writer: "read-only", glyphs: "none" }
-}, lT = {
+}, AT = {
kind: "opaqueUnknown",
// Scope is every UnknownNode kind EXCEPT optbreak — `ownerPredicate` excludes it explicitly, so
// `optbreakDescriptor` above is the sole owner of that kind. A non-optbreak UnknownNode is a
@@ -7105,83 +7106,83 @@ const oT = {
// `remove-owner` shape — i.e. isn't entirely absent — falls through unhandled by either
// descriptor instead; harmlessly inert, since `$settleScopeForNode` refuses every `UnknownNode`
// outright, so the caller's `$requestTier2ForNode` fallback always bails on it too.)
- ownerPredicate: (e) => De(e) && e.getTag() !== "optbreak",
+ ownerPredicate: (e) => Ue(e) && e.getTag() !== "optbreak",
ownerOf: () => {
},
- expectedPieces: () => xt,
- scanPieces: () => Nr,
+ expectedPieces: () => Ct,
+ scanPieces: () => $r,
graceSite: () => !1,
settleScope: "owner",
deletionPolicy: "none",
byteFormat: { writer: "read-only", glyphs: "none" }
-}, uT = {
+}, PT = {
kind: "nestedGlyph",
// The `+` on a nested span's glyphs. Purely tree-derived and rewritten in place by its own sync;
// there is no state a user edit can leave half-finished, so it owes no pend or deletion duty.
- ownerPredicate: (e) => D(e),
+ ownerPredicate: (e) => I(e),
ownerOf: () => {
},
- expectedPieces: () => xt,
- scanPieces: () => Nr,
+ expectedPieces: () => Ct,
+ scanPieces: () => $r,
graceSite: () => !1,
settleScope: "none",
deletionPolicy: "none",
byteFormat: { writer: "kind-owned", glyphs: "none" }
-}, Qi = [
- Qk,
- Zk,
- Mu("va"),
- Mu("vp"),
- tT,
- Eu("ca"),
- Eu("cp"),
- oT,
- cT,
- lT,
- uT
-], dT = new Map(Qi.map((e) => [e.kind, e]));
-function xn(e) {
- const t = dT.get(e);
+}, ns = [
+ yT,
+ bT,
+ Du("va"),
+ Du("vp"),
+ TT,
+ Uu("ca"),
+ Uu("cp"),
+ ST,
+ ET,
+ AT,
+ PT
+], NT = new Map(ns.map((e) => [e.kind, e]));
+function _n(e) {
+ const t = NT.get(e);
if (!t)
throw new Error(`No display-run descriptor registered for kind "${e}"`);
return t;
}
-function _n(e) {
- for (const t of Qi) {
+function Cn(e) {
+ for (const t of ns) {
const r = t.ownerOf(e);
if (r)
return { owner: r, kind: t.kind };
}
}
-function Hp(e) {
- return _n(e) !== void 0;
+function lh(e) {
+ return Cn(e) !== void 0;
}
-const js = "unmatched", Gp = 2;
-function Ui(e) {
+const Hs = "unmatched", uh = 2;
+function Bi(e) {
return `\\${e}`;
}
-class Or extends Ke {
+class Lr extends je {
__marker;
constructor(t = "", r) {
- super(Ui(t), r), this.__marker = t, this.__mode = 1;
+ super(Bi(t), r), this.__marker = t, this.__mode = 1;
}
static getType() {
return "unmatched";
}
static clone(t) {
const { __marker: r, __key: n } = t;
- return new Or(r, n);
+ return new Lr(r, n);
}
static importDOM() {
return {
- [js]: (t) => pT(t) ? {
- conversion: fT,
+ [Hs]: (t) => wT(t) ? {
+ conversion: OT,
priority: 1
} : null
};
}
static importJSON(t) {
- return jc().updateFromJSON(t);
+ return Qc().updateFromJSON(t);
}
updateFromJSON(t) {
const r = t.marker ?? "", i = super.updateFromJSON({
@@ -7190,7 +7191,7 @@ class Or extends Ke {
format: t.format ?? 0,
mode: t.mode ?? "token",
style: t.style ?? "",
- text: t.text ?? Ui(r)
+ text: t.text ?? Bi(r)
}).getWritable();
return i.__marker = r, i;
}
@@ -7198,29 +7199,29 @@ class Or extends Ke {
if (this.__marker === t)
return this;
const r = this.getWritable();
- return r.__marker = t, r.__text = Ui(t), r;
+ return r.__marker = t, r.__text = Bi(t), r;
}
getMarker() {
return this.getLatest().__marker;
}
createDOM(t) {
const r = super.createDOM(t);
- return r.setAttribute("data-marker", this.__marker), r.classList.add(cu), r.title = Au(this.__marker), r;
+ return r.setAttribute("data-marker", this.__marker), r.classList.add(Tu), r.title = Fu(this.__marker), r;
}
updateDOM(t, r, n) {
const i = super.updateDOM(t, r, n);
- return t.__marker !== this.__marker && (r.setAttribute("data-marker", this.__marker), r.title = Au(this.__marker)), i;
+ return t.__marker !== this.__marker && (r.setAttribute("data-marker", this.__marker), r.title = Fu(this.__marker)), i;
}
exportDOM() {
- const t = document.createElement(js);
- return t.setAttribute("data-marker", this.getMarker()), t.classList.add(cu), t.textContent = this.getTextContent(), { element: t };
+ const t = document.createElement(Hs);
+ return t.setAttribute("data-marker", this.getMarker()), t.classList.add(Tu), t.textContent = this.getTextContent(), { element: t };
}
exportJSON() {
return {
...super.exportJSON(),
type: this.getType(),
marker: this.getMarker(),
- version: Gp
+ version: uh
};
}
canInsertTextBefore() {
@@ -7230,39 +7231,39 @@ class Or extends Ke {
return !1;
}
}
-function Jp(e) {
- return e.getTextContent() === Ui(e.getMarker());
+function dh(e) {
+ return e.getTextContent() === Bi(e.getMarker());
}
-function Au(e) {
+function Fu(e) {
return e.endsWith("*") ? "This closing marker has no matching opening marker!" : "This opening marker has no matching closing marker!";
}
-function fT(e) {
+function OT(e) {
const t = e.getAttribute("data-marker") ?? "";
- return { node: jc(t) };
+ return { node: Qc(t) };
}
-function jc(e) {
- return je(new Or(e));
+function Qc(e) {
+ return Ve(new Lr(e));
}
-function pT(e) {
- return e?.tagName.toLowerCase() === js;
+function wT(e) {
+ return e?.tagName.toLowerCase() === Hs;
}
-function Qr(e) {
- return e instanceof Or;
+function tn(e) {
+ return e instanceof Lr;
}
-const Yp = "table", qa = "immutable-table", Xp = 1, hT = ["type", "marker", "content"];
-class On extends Zt {
+const fh = "table", Da = "immutable-table", ph = 1, qT = ["type", "marker", "content"];
+class $n extends Qt {
__unknownAttributes;
constructor(t, r) {
super(r), this.__unknownAttributes = t;
}
static getType() {
- return qa;
+ return Da;
}
static clone(t) {
- return new On(t.__unknownAttributes, t.__key);
+ return new $n(t.__unknownAttributes, t.__key);
}
static importJSON(t) {
- return gT().updateFromJSON(t);
+ return RT().updateFromJSON(t);
}
updateFromJSON(t) {
return super.updateFromJSON(t).setUnknownAttributes(t.unknownAttributes);
@@ -7285,9 +7286,9 @@ class On extends Zt {
const t = this.getUnknownAttributes();
return {
...super.exportJSON(),
- type: qa,
+ type: Da,
...t !== void 0 && { unknownAttributes: t },
- version: Xp
+ version: ph
};
}
// Shadow root: isolate selection so content doesn't merge across the table boundary.
@@ -7295,33 +7296,33 @@ class On extends Zt {
return !0;
}
}
-function gT(e) {
- return je(new On(e));
+function RT(e) {
+ return Ve(new $n(e));
}
-function Qp(e) {
- return e instanceof On;
+function hh(e) {
+ return e instanceof $n;
}
-function mT(e) {
- return e?.type === qa;
+function $T(e) {
+ return e?.type === Da;
}
-const Zp = "table:row", Pu = "immutable-table-row", eh = 1, Ra = "tr", yT = ["type", "marker", "content"];
-class fi extends Zt {
+const gh = "table:row", zu = "immutable-table-row", mh = 1, Ua = "tr", LT = ["type", "marker", "content"];
+class mi extends Qt {
__marker;
__unknownAttributes;
- constructor(t = Ra, r, n) {
+ constructor(t = Ua, r, n) {
super(n), this.__marker = t, this.__unknownAttributes = r;
}
static getType() {
- return Pu;
+ return zu;
}
static clone(t) {
- return new fi(t.__marker, t.__unknownAttributes, t.__key);
+ return new mi(t.__marker, t.__unknownAttributes, t.__key);
}
static importJSON(t) {
- return bT().updateFromJSON(t);
+ return IT().updateFromJSON(t);
}
updateFromJSON(t) {
- return super.updateFromJSON(t).setMarker(t.marker ?? Ra).setUnknownAttributes(t.unknownAttributes);
+ return super.updateFromJSON(t).setMarker(t.marker ?? Ua).setUnknownAttributes(t.unknownAttributes);
}
setMarker(t) {
if (this.__marker === t)
@@ -7350,46 +7351,46 @@ class fi extends Zt {
const t = this.getUnknownAttributes();
return {
...super.exportJSON(),
- type: Pu,
+ type: zu,
marker: this.getMarker(),
...t !== void 0 && { unknownAttributes: t },
- version: eh
+ version: mh
};
}
}
-function bT(e, t) {
- return je(new fi(e, t));
+function IT(e, t) {
+ return Ve(new mi(e, t));
}
-const th = "table:cell", Nu = "immutable-table-cell", rh = 1, $a = "tc1", kT = [
+const yh = "table:cell", Ku = "immutable-table-cell", bh = 1, Fa = "tc1", DT = [
"type",
"marker",
"align",
"colspan",
"content"
];
-function TT(e) {
+function UT(e) {
return e === "start" || e === "center" || e === "end" ? e : void 0;
}
-class pi extends Zt {
+class yi extends Qt {
__marker;
__align;
__colspan;
__unknownAttributes;
- constructor(t = $a, r, n, i, s) {
+ constructor(t = Fa, r, n, i, s) {
super(s), this.__marker = t, this.__align = r, this.__colspan = n, this.__unknownAttributes = i;
}
static getType() {
- return Nu;
+ return Ku;
}
static clone(t) {
const { __marker: r, __align: n, __colspan: i, __unknownAttributes: s, __key: o } = t;
- return new pi(r, n, i, s, o);
+ return new yi(r, n, i, s, o);
}
static importJSON(t) {
- return xT().updateFromJSON(t);
+ return FT().updateFromJSON(t);
}
updateFromJSON(t) {
- return super.updateFromJSON(t).setMarker(t.marker ?? $a).setAlign(t.align).setColspan(t.colspan).setUnknownAttributes(t.unknownAttributes);
+ return super.updateFromJSON(t).setMarker(t.marker ?? Fa).setAlign(t.align).setColspan(t.colspan).setUnknownAttributes(t.unknownAttributes);
}
setMarker(t) {
if (this.__marker === t)
@@ -7428,7 +7429,7 @@ class pi extends Zt {
createDOM() {
const t = this.__marker.startsWith("th"), r = document.createElement(t ? "th" : "td");
r.setAttribute("data-marker", this.__marker), r.classList.add("table-cell", `usfm_${this.__marker}`);
- const n = TT(this.__align);
+ const n = UT(this.__align);
return n && (r.style.textAlign = n), this.__colspan && r.setAttribute("colspan", this.__colspan), r;
}
updateDOM(t) {
@@ -7438,324 +7439,324 @@ class pi extends Zt {
const t = this.getAlign(), r = this.getColspan(), n = this.getUnknownAttributes();
return {
...super.exportJSON(),
- type: Nu,
+ type: Ku,
marker: this.getMarker(),
...t !== void 0 && { align: t },
...r !== void 0 && { colspan: r },
...n !== void 0 && { unknownAttributes: n },
- version: rh
+ version: bh
};
}
}
-function xT(e, t, r, n) {
- return je(new pi(e, t, r, n));
+function FT(e, t, r, n) {
+ return Ve(new yi(e, t, r, n));
}
-function _o(e, t) {
+function Mo(e, t) {
const r = e.getChildAtIndex(t);
- return v(r) ? r : void 0;
+ return M(r) ? r : void 0;
}
-function Qt(e, t) {
- const r = _o(e, t);
+function Xt(e, t) {
+ const r = Mo(e, t);
r ? r.select(0, 0) : e.select(t, t);
}
-function Zi(e) {
+function is(e) {
return e.getUnknownAttributes()?.closed !== "false";
}
-function _T(e) {
+function zT(e) {
return e.getChildren().some((t) => O(t) && t.getMarkerSyntax() === "closing");
}
-function CT(e) {
- return Zi(e) ? void 0 : { closed: "false" };
+function KT(e) {
+ return is(e) ? void 0 : { closed: "false" };
}
-function ST(e, t, r, n) {
- const i = t.getMarker(), s = Fc(t), o = _T(t);
+function jT(e, t, r, n) {
+ const i = t.getMarker(), s = Jc(t), o = zT(t);
if (n) {
- e.append(ot(i, "opening", s));
+ e.append(ct(i, "opening", s));
const [a] = r;
- To(a) && !a.getTextContent().startsWith(I) && a.setTextContent(I + a.getTextContent());
+ vo(a) && !a.getTextContent().startsWith(R) && a.setTextContent(R + a.getTextContent());
}
- e.append(...r), o && e.append(ot(i, "closing", s));
+ e.append(...r), o && e.append(ct(i, "closing", s));
}
-function Cn(e) {
- return nt(e, D) ?? void 0;
+function vn(e) {
+ return Xe(e, I) ?? void 0;
}
-function Bc(e) {
+function Zc(e) {
let t = e.getParent();
- for (; D(t); )
+ for (; I(t); )
t = t.getParent();
return t;
}
-function Ia(e) {
- const t = nh(e);
- return e.getChildren().every((r) => O(r) || t && ne(r, oe) === "attribute" || v(r) && r.getTextContent().replaceAll(I, "") === "");
+function za(e) {
+ const t = kh(e);
+ return e.getChildren().every((r) => O(r) || t && ne(r, oe) === "attribute" || M(r) && r.getTextContent().replaceAll(R, "") === "");
}
-function nh(e) {
- return Zi(e);
+function kh(e) {
+ return is(e);
}
-function vT(e, t) {
- const r = e.getUnknownAttributes(), n = r ? ar(r, po(e.getMarker())) : "";
- n !== "" && t.insertAfter(pe(n)), e.remove();
+function BT(e, t) {
+ const r = e.getUnknownAttributes(), n = r ? ar(r, yo(e.getMarker())) : "";
+ n !== "" && t.insertAfter(ge(n)), e.remove();
}
-function MT(e, t) {
- if (Zi(e))
+function VT(e, t) {
+ if (is(e))
return;
const r = e.getUnknownAttributes();
if (r) {
const n = { ...r };
delete n.closed, e.setUnknownAttributes(Object.keys(n).length > 0 ? n : void 0);
}
- t && e.append(ot(e.getMarker(), "closing", Fc(e)));
+ t && e.append(ct(e.getMarker(), "closing", Jc(e)));
}
-function ET(e, t) {
- return D(e) && !Zi(e) && !Zi(t);
+function WT(e, t) {
+ return I(e) && !is(e) && !is(t);
}
-function AT(e, t, r) {
- Ia(e) && e.getChildren().forEach((i) => {
+function HT(e, t, r) {
+ za(e) && e.getChildren().forEach((i) => {
O(i) || i.remove();
});
const [n] = t;
- r && To(n) && !n.getTextContent().startsWith(I) && n.setTextContent(I + n.getTextContent()), e.append(...t);
+ r && vo(n) && !n.getTextContent().startsWith(R) && n.setTextContent(R + n.getTextContent()), e.append(...t);
}
-function PT(e, t, r) {
- const { renderGlyphs: n, closeImplicitSpans: i = !1 } = r, s = nh(t), o = [];
+function GT(e, t, r) {
+ const { renderGlyphs: n, closeImplicitSpans: i = !1 } = r, s = kh(t), o = [];
for (let l = e.getNextSibling(); l; ) {
- const u = l.getNextSibling(), d = O(l) && l.getMarkerSyntax() === "closing", f = s && ne(l, oe) === "attribute";
- !d && !f && o.push(l), l = u;
+ const d = l.getNextSibling(), u = O(l) && l.getMarkerSyntax() === "closing", f = s && ne(l, oe) === "attribute";
+ !u && !f && o.push(l), l = d;
}
- const a = ET(e, t);
+ const a = WT(e, t);
t.insertAfter(e);
let c = e;
if (o.length > 0)
if (a)
- AT(e, o, n);
+ HT(e, o, n);
else {
- const l = xr(t.getMarker(), CT(t));
- ST(l, t, o, n), e.insertAfter(l), Ia(l) ? l.remove() : c = l;
+ const l = Mr(t.getMarker(), KT(t));
+ jT(l, t, o, n), e.insertAfter(l), za(l) ? l.remove() : c = l;
}
- i && !a && MT(t, n), Ia(t) && vT(t, c);
+ i && !a && VT(t, n), za(t) && BT(t, c);
}
-function ni(e, t) {
+function ci(e, t) {
let r = e.getParent();
- for (; D(r); )
- PT(e, r, t), r = e.getParent();
+ for (; I(r); )
+ GT(e, r, t), r = e.getParent();
}
-function Vc(e) {
- if (v(e) && !O(e)) {
- const t = e.getTextContent().startsWith(I) ? 1 : 0;
+function el(e) {
+ if (M(e) && !O(e)) {
+ const t = e.getTextContent().startsWith(R) ? 1 : 0;
e.select(t, t);
return;
}
- if (F(e)) {
+ if (L(e)) {
const t = e.getChildren().find((r) => !O(r));
if (t) {
- Vc(t);
+ el(t);
return;
}
e.selectEnd();
}
}
-const Xn = /* @__PURE__ */ new WeakMap();
-function NT(e, t) {
- return Xn.set(e, t), () => {
- Xn.get(e) === t && Xn.delete(e);
+const ei = /* @__PURE__ */ new WeakMap();
+function JT(e, t) {
+ return ei.set(e, t), () => {
+ ei.get(e) === t && ei.delete(e);
};
}
-function Ou(e) {
- return Xn.get(e);
+function ju(e) {
+ return ei.get(e);
}
-function OT(e) {
- return Xn.get(Zn())?.has(e.getKey()) ?? !1;
+function YT(e) {
+ return ei.get(ri())?.has(e.getKey()) ?? !1;
}
-function wT(e) {
- Xn.get(Zn())?.add(e.getKey());
+function XT(e) {
+ ei.get(ri())?.add(e.getKey());
}
-function qT(e) {
+function QT(e) {
return !!(e.opener || e.value || e.closer || e.wrapper);
}
-function La(e) {
+function Ka(e) {
return !!(e.opener || e.value || e.closer);
}
-function wu(e) {
+function Bu(e) {
return /^\s/.test(e);
}
-function Wc(e, t) {
+function tl(e, t) {
if (e === t)
return !1;
- if (e === void 0 || t === void 0 || !wu(t) || !wu(e))
+ if (e === void 0 || t === void 0 || !Bu(t) || !Bu(e))
return !0;
const r = t.trim();
return r === "" || e.trim() !== r;
}
-function Co(e, t, r) {
- return r.wantsRun ? Wc(t.value?.getTextContent(), r.valueText) || e.byteFormat.glyphs !== "none" && (!t.opener || e.byteFormat.closerSyntax !== "none" && !t.closer) ? !0 : e.byteFormat.writer === "wrapper" && t.wrapper === void 0 : qT(t);
+function Eo(e, t, r) {
+ return r.wantsRun ? tl(t.value?.getTextContent(), r.valueText) || e.byteFormat.glyphs !== "none" && (!t.opener || e.byteFormat.closerSyntax !== "none" && !t.closer) ? !0 : e.byteFormat.writer === "wrapper" && t.wrapper === void 0 : QT(t);
}
-function RT(e, t) {
+function ZT(e, t) {
if (e.byteFormat.writer !== "wrapper")
return !1;
const r = e.expectedPieces(t);
if (!r.wantsRun)
return !1;
const n = e.scanPieces(t);
- return Wc(n.value?.getTextContent(), r.valueText) || e.byteFormat.glyphs !== "none" && (!n.opener || e.byteFormat.closerSyntax !== "none" && !n.closer) ? !1 : n.wrapper === void 0;
+ return tl(n.value?.getTextContent(), r.valueText) || e.byteFormat.glyphs !== "none" && (!n.opener || e.byteFormat.closerSyntax !== "none" && !n.closer) ? !1 : n.wrapper === void 0;
}
-function ih(e, t) {
- return !La(e.scanPieces(t));
+function Th(e, t) {
+ return !Ka(e.scanPieces(t));
}
-function gs(e, t) {
+function ks(e, t) {
if (!t.isAttached())
return !1;
if (e.byteFormat.writer === "kind-owned")
return e.graceSite(t, {});
const r = e.expectedPieces(t), n = e.scanPieces(t);
- if (!Co(e, n, r))
+ if (!Eo(e, n, r))
return !1;
const i = w();
if (!N(i) || !i.isCollapsed())
return !1;
const s = i.anchor.getNode(), { wrapper: o, value: a } = n;
- return o && (s.is(o) || Ks(s, o.getKey())) ? !0 : a ? s.is(a) : e.graceSite(t, n);
+ return o && (s.is(o) || Ws(s, o.getKey())) ? !0 : a ? s.is(a) : e.graceSite(t, n);
}
-function $T(e, t, r, n) {
- return !r.wantsRun || La(n) || gy(Vi) ? !1 : Zn().getEditorState().read(() => {
+function ex(e, t, r, n) {
+ return !r.wantsRun || Ka(n) || wy(Yi) ? !1 : ri().getEditorState().read(() => {
const i = se(t.getKey());
- return !i || !e.ownerPredicate(i) ? !1 : La(e.scanPieces(i));
+ return !i || !e.ownerPredicate(i) ? !1 : Ka(e.scanPieces(i));
});
}
-function IT(e) {
+function tx(e) {
e.opener?.remove(), e.value?.remove(), e.closer?.remove();
}
-function qu(e) {
- const t = pe(e);
- return bt(t, oe, "attribute"), t;
+function Vu(e) {
+ const t = ge(e);
+ return kt(t, oe, "attribute"), t;
}
-function LT(e, t, r) {
+function rx(e, t, r) {
if (r.wrapper)
return r.wrapper;
const { runKind: n, insertRunAfter: i } = e.byteFormat, s = i?.(t);
if (!n || !s)
return;
- const o = rp(n);
+ const o = bp(n);
return s.insertAfter(o), r.opener && o.append(r.opener), r.value && o.append(r.value), r.closer && o.append(r.closer), o;
}
-function DT(e, t, r, n) {
+function nx(e, t, r, n) {
const { writer: i, glyphs: s, glyphMarker: o, closerSyntax: a, insertRunBefore: c } = e.byteFormat;
if (i === "owner-children") {
const f = c?.(t);
if (!f || n.valueText === void 0)
return;
- v(r.value) ? r.value.setTextContent(n.valueText) : f.insertBefore(qu(n.valueText));
+ M(r.value) ? r.value.setTextContent(n.valueText) : f.insertBefore(Vu(n.valueText));
return;
}
- const l = LT(e, t, r);
+ const l = rx(e, t, r);
if (!l || s === "none" || !o || !a)
return;
- const u = r.opener ?? (() => {
- const f = ot(o(t), "opening"), p = l.getFirstChild();
+ const d = r.opener ?? (() => {
+ const f = ct(o(t), "opening"), p = l.getFirstChild();
return p ? p.insertBefore(f) : l.append(f), f;
})();
- let d = r.value;
- n.valueText === void 0 ? (d?.remove(), d = void 0) : v(d) ? Wc(d.getTextContent(), n.valueText) && d.setTextContent(n.valueText) : (d = qu(n.valueText), u.insertAfter(d)), a !== "none" && !r.closer && (d ?? u).insertAfter(ot(a === "selfClosing" ? "" : o(t), a));
+ let u = r.value;
+ n.valueText === void 0 ? (u?.remove(), u = void 0) : M(u) ? tl(u.getTextContent(), n.valueText) && u.setTextContent(n.valueText) : (u = Vu(n.valueText), d.insertAfter(u)), a !== "none" && !r.closer && (u ?? d).insertAfter(ct(a === "selfClosing" ? "" : o(t), a));
}
-function es(e, t) {
+function ss(e, t) {
const { writer: r } = e.byteFormat;
if (r === "kind-owned" || r === "read-only" || !t.isAttached())
return;
const n = e.expectedPieces(t), i = e.scanPieces(t);
- if (Co(e, i, n) && !OT(t)) {
- if ($T(e, t, n, i)) {
- wT(t);
+ if (Eo(e, i, n) && !YT(t)) {
+ if (ex(e, t, n, i)) {
+ XT(t);
return;
}
- if (!gs(e, t)) {
+ if (!ks(e, t)) {
if (!n.wantsRun) {
- IT(i);
+ tx(i);
return;
}
- DT(e, t, i, n);
+ nx(e, t, i, n);
}
}
}
-function UT(e, t, r) {
- es(e, t), t.isAttached() && gs(e, t) && r.add(t.getKey());
+function ix(e, t, r) {
+ ss(e, t), t.isAttached() && ks(e, t) && r.add(t.getKey());
}
-function sh(e) {
- if (!v(e))
+function rl(e) {
+ if (!M(e))
return !1;
- if (O(e) || Oe(e) || Qr(e))
+ if (O(e) || we(e) || tn(e))
return !0;
const t = ne(e, oe);
return t === "attribute" || t === fr;
}
-function Hc(e, t) {
- return O(e) ? !(t === e.getTextContentSize() && e.getMarkerSyntax() !== "opening" && Xr(e) && D(e.getParent())) : !1;
+function nl(e, t) {
+ return O(e) ? !(t === e.getTextContentSize() && e.getMarkerSyntax() !== "opening" && en(e) && I(e.getParent())) : !1;
}
-function FT() {
+function sx() {
const e = w();
- return N(e) ? Hc(e.focus.getNode(), e.focus.offset) : !1;
+ return N(e) ? nl(e.focus.getNode(), e.focus.offset) : !1;
}
-function oh(e) {
+function xh(e) {
if (e.type !== "text")
return;
const t = e.getNode();
- return v(t) && sh(t) ? t : void 0;
+ return M(t) && rl(t) ? t : void 0;
}
-function zT(e) {
- const t = oh(e);
+function ox(e) {
+ const t = xh(e);
if (t)
return e.offset > 0 && e.offset < t.getTextContentSize() ? t : void 0;
}
-function KT(e) {
- const t = oh(e);
+function ax(e) {
+ const t = xh(e);
if (t)
return e.offset === 0 || e.offset === t.getTextContentSize() ? t : void 0;
}
-function Ru(e) {
+function Wu(e) {
return { key: e.key, offset: e.offset, type: e.type };
}
-function $u(e, t) {
+function Hu(e, t) {
e.set(t.key, t.offset, t.type);
}
-function jT(e, t) {
- let r = KT(e);
+function cx(e, t) {
+ let r = ax(e);
for (; r; ) {
const n = t === "next" ? r.getNextSibling() : r.getPreviousSibling();
- if (!v(n))
+ if (!M(n))
return;
- if (!sh(n))
+ if (!rl(n))
return { node: n, offset: t === "next" ? 0 : n.getTextContentSize() };
r = n;
}
}
-function Iu(e, t) {
- const r = jT(e, t);
+function Gu(e, t) {
+ const r = cx(e, t);
return r ? (e.set(r.node.getKey(), r.offset, "text"), !0) : !1;
}
-function ah(e) {
+function _h(e) {
if (e.isCollapsed()) {
- const a = zT(e.anchor);
+ const a = ox(e.anchor);
if (!a)
return !1;
const c = a.getTextContentSize();
return e.anchor.set(a.getKey(), c, "text"), e.focus.set(a.getKey(), c, "text"), !0;
}
- const t = e.isBackward(), r = t ? e.focus : e.anchor, n = t ? e.anchor : e.focus, i = [Ru(r), Ru(n)], s = Iu(r, "next"), o = Iu(n, "previous");
- return !s && !o ? !1 : e.isCollapsed() || e.isBackward() !== t ? ($u(r, i[0]), $u(n, i[1]), !1) : !0;
+ const t = e.isBackward(), r = t ? e.focus : e.anchor, n = t ? e.anchor : e.focus, i = [Wu(r), Wu(n)], s = Gu(r, "next"), o = Gu(n, "previous");
+ return !s && !o ? !1 : e.isCollapsed() || e.isBackward() !== t ? (Hu(r, i[0]), Hu(n, i[1]), !1) : !0;
}
-const Bs = "verse-block", ch = 1, BT = "verse-block";
-class hi extends Zt {
+const Gs = "verse-block", Ch = 1, lx = "verse-block";
+class bi extends Qt {
/** The verse marker verbatim. Authoritative: the range is derived from it, never stored. */
__number;
constructor(t = "", r) {
super(r), this.__number = t;
}
static getType() {
- return Bs;
+ return Gs;
}
static clone(t) {
- return new hi(t.__number, t.__key);
+ return new bi(t.__number, t.__key);
}
static importJSON(t) {
- return VT().updateFromJSON(t);
+ return ux().updateFromJSON(t);
}
updateFromJSON(t) {
return super.updateFromJSON(t).setNumber(t.number);
@@ -7771,14 +7772,14 @@ class hi extends Zt {
}
/** The first and last verse numbers this block covers. A bridge covers more than one. */
getRange() {
- return Ap(this.getNumber());
+ return Bp(this.getNumber());
}
createDOM() {
const t = document.createElement("div");
- return t.classList.add(BT), Lu(t, this.__number), t;
+ return t.classList.add(lx), Ju(t, this.__number), t;
}
updateDOM(t, r) {
- return t.__number !== this.__number && Lu(r, this.__number), !1;
+ return t.__number !== this.__number && Ju(r, this.__number), !1;
}
// No `exportDOM`/`importDOM`: Lexical's default export already emits `createDOM`'s element, so
// the HTML flavor of a copied passage carries these wrappers, which an ordinary paste unwraps
@@ -7800,48 +7801,48 @@ class hi extends Zt {
exportJSON() {
return {
...super.exportJSON(),
- type: Bs,
+ type: Gs,
number: this.getNumber(),
- version: ch
+ version: Ch
};
}
canBeEmpty() {
return !1;
}
}
-function Lu(e, t) {
- const { start: r, end: n } = Ap(t), i = !isNaN(r) && !isNaN(n) && r <= n;
- e.setAttribute("data-verse-number", t), Du(e, "data-verse-start", i ? r : NaN), Du(e, "data-verse-end", i ? n : NaN);
+function Ju(e, t) {
+ const { start: r, end: n } = Bp(t), i = !isNaN(r) && !isNaN(n) && r <= n;
+ e.setAttribute("data-verse-number", t), Yu(e, "data-verse-start", i ? r : NaN), Yu(e, "data-verse-end", i ? n : NaN);
}
-function Du(e, t, r) {
+function Yu(e, t, r) {
isNaN(r) ? e.removeAttribute(t) : e.setAttribute(t, r.toString());
}
-function VT(e) {
- return je(new hi(e));
+function ux(e) {
+ return Ve(new bi(e));
}
-function ts(e) {
- return e instanceof hi;
+function os(e) {
+ return e instanceof bi;
}
-function WT(e) {
- return e?.type === Bs;
+function dx(e) {
+ return e?.type === Gs;
}
-const HT = [
- Ut,
+const fx = [
+ Ft,
pr,
- Nt,
+ Ot,
dt,
- me,
- Ee,
- Jt,
+ ye,
+ Ae,
+ Gt,
hr,
- Pn,
- Er,
- Or,
- Ze,
- Wr,
- On,
- fi,
- pi,
+ qn,
+ wr,
+ Lr,
+ rt,
+ Jr,
+ $n,
+ mi,
+ yi,
// The forward adaptor (usj-editor.adaptor.ts, platform) serializes editable-mode verse/milestone
// display runs as AttributeRunNode wrappers, and this package's own self-healing sync
// (displayRunSync.utils.ts's shared $syncDisplayRun driver, parameterized by each kind's own
@@ -7849,13 +7850,13 @@ const HT = [
// every USJ-shaped editor needs the class registered, not only shared-react's (a non-react host,
// e.g. packages/scribe's NoteEditor, builds its editor straight from usjBaseNodes with no
// react-specific node list).
- Ar,
+ qr,
{
- replace: yc,
- with: () => Ht(),
- withKlass: Wr
+ replace: Mc,
+ with: () => Wt(),
+ withKlass: Jr
}
-], Vs = {
+], Js = {
markers: {
id: {
marker: "id",
@@ -14895,13 +14896,13 @@ const HT = [
fontSize: 12
}
}
-}, GT = {
- paragraph: b.Paragraph,
- character: b.Character,
- note: b.Note,
- milestone: b.Milestone
+}, px = {
+ paragraph: k.Paragraph,
+ character: k.Character,
+ note: k.Note,
+ milestone: k.Milestone
};
-function JT(e) {
+function hx(e) {
if (!e)
return lr;
const t = /* @__PURE__ */ new Map();
@@ -14913,7 +14914,7 @@ function JT(e) {
// markers the generated data lacks (`w`, `rb`, `jmp`), and reading the table directly
// demoted exactly those to Uncategorized whenever a project StyleInfo was active.
category: lr(r)?.category ?? T.Uncategorized,
- type: GT[n.styleType] ?? b.Unknown,
+ type: px[n.styleType] ?? k.Unknown,
description: n.description ?? "",
hasEndMarker: !!n.endMarker,
children: lr(r)?.children
@@ -14921,16 +14922,16 @@ function JT(e) {
return t.set(r, i), i;
};
}
-function Uu(e, t, r) {
+function Xu(e, t, r) {
const n = {
- type: kr,
- version: br,
+ type: Cr,
+ version: _r,
content: e
}, i = t.serializeEditorState(n, r);
- return go(i.root.children[0]) ? i.root.children[0].children[0] : i.root.children[0];
+ return ko(i.root.children[0]) ? i.root.children[0].children[0] : i.root.children[0];
}
-const lh = "v", uh = 1, YT = "verse-selected";
-class Ct extends ls {
+const vh = "v", Sh = 1, gx = "verse-selected";
+class St extends hs {
__marker;
__number;
__showMarker;
@@ -14939,25 +14940,25 @@ class Ct extends ls {
__pubnumber;
__unknownAttributes;
constructor(t = "", r = !1, n, i, s, o, a) {
- super(a), this.__marker = lh, this.__number = t, this.__showMarker = r, this.__sid = n, this.__altnumber = i, this.__pubnumber = s, this.__unknownAttributes = o;
+ super(a), this.__marker = vh, this.__number = t, this.__showMarker = r, this.__sid = n, this.__altnumber = i, this.__pubnumber = s, this.__unknownAttributes = o;
}
static getType() {
return "immutable-verse";
}
static clone(t) {
const { __number: r, __showMarker: n, __sid: i, __altnumber: s, __pubnumber: o, __unknownAttributes: a, __key: c } = t;
- return new Ct(r, n, i, s, o, a, c);
+ return new St(r, n, i, s, o, a, c);
}
static importDOM() {
return {
- span: (t) => ZT(t) ? {
- conversion: QT,
+ span: (t) => bx(t) ? {
+ conversion: yx,
priority: 1
} : null
};
}
static importJSON(t) {
- return Gc().updateFromJSON(t);
+ return il().updateFromJSON(t);
}
updateFromJSON(t) {
return super.updateFromJSON(t).setMarker(t.marker).setNumber(t.number).setShowMarker(t.showMarker).setSid(t.sid).setAltnumber(t.altnumber).setPubnumber(t.pubnumber).setUnknownAttributes(t.unknownAttributes);
@@ -15025,21 +15026,21 @@ class Ct extends ls {
}
createDOM() {
const t = document.createElement("span");
- return t.setAttribute("data-marker", this.__marker), t.classList.add(Ca, `usfm_${this.__marker}`), this.__showMarker && t.classList.add("marker"), t.setAttribute("data-number", this.__number), t;
+ return t.setAttribute("data-marker", this.__marker), t.classList.add(Aa, `usfm_${this.__marker}`), this.__showMarker && t.classList.add("marker"), t.setAttribute("data-number", this.__number), t;
}
updateDOM() {
return !1;
}
exportDOM(t) {
const { element: r } = super.exportDOM(t);
- return r && An(r) && (r.setAttribute("data-marker", this.getMarker()), r.classList.add(Ca, `usfm_${this.getMarker()}`), r.setAttribute("data-number", this.getNumber())), { element: r };
+ return r && Pn(r) && (r.setAttribute("data-marker", this.getMarker()), r.classList.add(Aa, `usfm_${this.getMarker()}`), r.setAttribute("data-number", this.getNumber())), { element: r };
}
decorate() {
- const t = this.getShowMarker() ? Lt(this.getMarker(), this.getNumber()) : (
+ const t = this.getShowMarker() ? Dt(this.getMarker(), this.getNumber()) : (
// ZWSP added so double click word selection works without including this number.
- Is + this.getNumber() + Is
+ Fs + this.getNumber() + Fs
);
- return C(XT, { nodeKey: this.getKey(), text: t });
+ return S(mx, { nodeKey: this.getKey(), text: t });
}
exportJSON() {
return {
@@ -15051,14 +15052,14 @@ class Ct extends ls {
altnumber: this.getAltnumber(),
pubnumber: this.getPubnumber(),
unknownAttributes: this.getUnknownAttributes(),
- version: uh
+ version: Sh
};
}
isSelected(t) {
try {
return super.isSelected(t);
} catch (r) {
- if (Mp(r))
+ if (Kp(r))
return !1;
throw r;
}
@@ -15068,172 +15069,172 @@ class Ct extends ls {
return !1;
}
}
-function XT({ nodeKey: e, text: t }) {
- const [r] = Ly(e);
- return C("span", { className: r ? YT : void 0, children: t });
+function mx({ nodeKey: e, text: t }) {
+ const [r] = tb(e);
+ return S("span", { className: r ? gx : void 0, children: t });
}
-function QT(e) {
+function yx(e) {
const t = e.getAttribute("data-number") ?? "0";
- return { node: Gc(t) };
+ return { node: il(t) };
}
-function Gc(e, t, r, n, i, s) {
- return je(new Ct(e, t, r, n, i, s));
+function il(e, t, r, n, i, s) {
+ return Ve(new St(e, t, r, n, i, s));
}
-function ZT(e) {
- return (e?.getAttribute("data-marker") ?? void 0) === lh;
+function bx(e) {
+ return (e?.getAttribute("data-marker") ?? void 0) === vh;
}
-function wn(e) {
- return e instanceof Ct;
+function Ln(e) {
+ return e instanceof St;
}
-function ex(e) {
- return e?.type === Ct.getType();
+function kx(e) {
+ return e?.type === St.getType();
}
-function ge(e) {
- return Oe(e) || wn(e);
+function he(e) {
+ return we(e) || Ln(e);
}
-function dh(e) {
- return bp(e) || ex(e);
+function Mh(e) {
+ return Rp(e) || kx(e);
}
-function tx(e) {
- return rx(e).find((t) => ae(t));
+function Tx(e) {
+ return xx(e).find((t) => ce(t));
}
-function rx(e) {
- return e.some(ts) ? e.flatMap((t) => ts(t) ? t.getChildren() : t) : e;
+function xx(e) {
+ return e.some(os) ? e.flatMap((t) => os(t) ? t.getChildren() : t) : e;
}
-function So(e) {
- return F(e) ? ts(e) ? e.getChildren().flatMap(So) : e.getChildren() : [];
+function Ao(e) {
+ return L(e) ? os(e) ? e.getChildren().flatMap(Ao) : e.getChildren() : [];
}
-function nx(e, t) {
- return So(e).find((i) => ge(i) && Ic(t, i.getNumber()));
+function _x(e, t) {
+ return Ao(e).find((i) => he(i) && Vc(t, i.getNumber()));
}
-function ix(e, t) {
- return t === 0 ? tx(e) : e.map((r) => nx(r, t)).filter((r) => r)[0];
+function Cx(e, t) {
+ return t === 0 ? Tx(e) : e.map((r) => _x(r, t)).filter((r) => r)[0];
}
-function Ws(e) {
- return So(e).find((r) => ge(r));
+function Ys(e) {
+ return Ao(e).find((r) => he(r));
}
-function fh(e, t) {
- if (!F(e) || t <= 0)
+function Eh(e, t) {
+ if (!L(e) || t <= 0)
return;
const r = e.getChildren();
for (let n = t - 1; n >= 0; n--) {
const i = r[n];
- if (ge(i))
+ if (he(i))
return i;
}
}
-function sx(e) {
+function vx(e) {
const t = e.getParent();
- if (t && F(t)) {
+ if (t && L(t)) {
const n = t.getChildren();
for (let i = e.getIndexWithinParent() + 1; i < n.length; i++) {
const s = n[i];
- if (ge(s))
+ if (he(s))
return s;
}
}
let r = t?.getNextSibling();
for (; r && !Ge(r); ) {
- const n = Ws(r);
+ const n = Ys(r);
if (n)
return n;
r = r.getNextSibling();
}
}
-function Da(e) {
- return So(e).findLast((t) => ge(t));
+function ja(e) {
+ return Ao(e).findLast((t) => he(t));
}
-function ox(e) {
- if (!Oe(e))
+function Sx(e) {
+ if (!we(e))
return 0;
const t = e.getNumber();
return e.getTextContent().startsWith(t) ? t.length : 0;
}
-function ax(e, t, r) {
+function Mx(e, t, r) {
if (!r)
return !1;
const n = t.getParent();
- if (r === n && F(r)) {
+ if (r === n && L(r)) {
const i = t.getIndexWithinParent();
return e.anchor.offset <= i;
}
return r.getNextSibling() === t;
}
-function cx(e, t) {
+function Ex(e, t) {
const r = t.anchor.getNode();
if (r !== e)
- return ax(t, e, r);
- if (v(e)) {
- const n = ox(e);
+ return Mx(t, e, r);
+ if (M(e)) {
+ const n = Sx(e);
return t.anchor.offset < n;
}
return !0;
}
-function Fu(e) {
+function Qu(e) {
const t = e.getNumber(), r = Number.parseInt(t ?? "0", 10);
return {
verseNum: r,
verse: t != null && r.toString() !== t ? t : void 0
};
}
-function lx(e, t) {
+function Ax(e, t) {
if (!e)
return { verseNum: 0 };
if (!N(t))
- return Fu(e);
+ return Qu(e);
const r = Number.parseInt(e.getNumber() ?? "0", 10), n = r <= 1 ? 0 : r - 1;
- return cx(e, t) ? { verseNum: n } : Fu(e);
+ return Ex(e, t) ? { verseNum: n } : Qu(e);
}
-function ux(e) {
- return yk(e) || wn(e);
+function Px(e) {
+ return Lk(e) || Ln(e);
}
-function Jc(e) {
- if (v(e)) {
+function sl(e) {
+ if (M(e)) {
const t = e.getTextContent();
- !t.endsWith(" ") && !t.endsWith(I) && e.setTextContent(`${t} `);
+ !t.endsWith(" ") && !t.endsWith(R) && e.setTextContent(`${t} `);
}
}
-function ph(e) {
- if (v(e)) {
+function Ah(e) {
+ if (M(e)) {
const t = e.getTextContent();
t.startsWith(" ") && e.setTextContent(t.trimStart());
}
}
-function Ua(e, t) {
+function Ba(e, t) {
return e.getEditorState().read(() => !se(t));
}
-function dx(e) {
+function Nx(e) {
if (!e.isCollapsed())
return !1;
- const t = e.anchor.getNode(), r = Yc(t, e);
+ const t = e.anchor.getNode(), r = ol(t, e);
let n;
if (r) {
const i = r.getParent();
- if (i && F(i) && F(t) && t === i && e.anchor.offset < r.getIndexWithinParent() && (n = r), !n && i && F(i)) {
+ if (i && L(i) && L(t) && t === i && e.anchor.offset < r.getIndexWithinParent() && (n = r), !n && i && L(i)) {
const s = i.getChildren(), o = r.getIndexWithinParent();
for (let a = o + 1; a < s.length; a++) {
const c = s[a];
- if (ge(c)) {
+ if (he(c)) {
n = c;
break;
}
}
}
if (!n && i) {
- let s = zu(i);
+ let s = Zu(i);
for (; s && !Ge(s); ) {
- const o = Ws(s);
+ const o = Ys(s);
if (o) {
n = o;
break;
}
- s = zu(s);
+ s = Zu(s);
}
}
} else {
let s = t.getTopLevelElement() ?? t;
for (; s; ) {
- const o = Ws(s);
+ const o = Ys(s);
if (o) {
n = o;
break;
@@ -15244,28 +15245,28 @@ function dx(e) {
}
return n ? (n.selectNext(0, 0), !0) : !1;
}
-function fx(e) {
+function Ox(e) {
if (!e.isCollapsed())
return !1;
- const t = e.anchor.getNode(), r = Yc(t, e);
+ const t = e.anchor.getNode(), r = ol(t, e);
let n;
if (r) {
const i = r.getParent(), s = t.getTopLevelElement();
- if (i && s && s !== i.getTopLevelElement() && (n = r), !n && i && F(i) && (n = fh(i, r.getIndexWithinParent())), !n && i) {
- let o = Ku(i);
+ if (i && s && s !== i.getTopLevelElement() && (n = r), !n && i && L(i) && (n = Eh(i, r.getIndexWithinParent())), !n && i) {
+ let o = ed(i);
for (; o && !Ge(o); ) {
- const a = Da(o);
+ const a = ja(o);
if (a) {
n = a;
break;
}
- o = Ku(o);
+ o = ed(o);
}
}
} else {
let s = t.getTopLevelElement()?.getPreviousSibling() ?? null;
for (; s && !Ge(s); ) {
- const o = Da(s);
+ const o = ja(s);
if (o) {
n = o;
break;
@@ -15275,69 +15276,69 @@ function fx(e) {
}
return n ? (n.selectNext(0, 0), !0) : !1;
}
-function zu(e) {
+function Zu(e) {
const t = e.getNextSibling();
if (t)
return t;
const r = e.getTopLevelElement();
return r && r !== e ? r.getNextSibling() : null;
}
-function Ku(e) {
+function ed(e) {
const t = e.getPreviousSibling();
if (t)
return t;
const r = e.getTopLevelElement();
return r && r !== e ? r.getPreviousSibling() : null;
}
-function Yc(e, t) {
- if (F(e) && N(t) && t.anchor.key === e.getKey()) {
+function ol(e, t) {
+ if (L(e) && N(t) && t.anchor.key === e.getKey()) {
const n = e.getChildAtIndex(t.anchor.offset);
- if (n && ge(n))
+ if (n && he(n))
return n;
- const i = fh(e, t.anchor.offset);
+ const i = Eh(e, t.anchor.offset);
if (i)
return i;
- const s = Ws(e);
+ const s = Ys(e);
if (s)
return s;
}
- return Xc(e);
+ return al(e);
}
-function Xc(e) {
+function al(e) {
if (!e || Ge(e))
return;
- if (ge(e))
+ if (he(e))
return e;
- let t = Cu(e);
+ let t = $u(e);
for (; t; ) {
if (Ge(t))
return;
- if (ge(t))
+ if (he(t))
return t;
- const r = Da(t);
+ const r = ja(t);
if (r)
return r;
- t = Cu(t);
+ t = $u(t);
}
}
-const px = ["style"], hx = ["style", "code"], Hs = ["style", "cid"], gx = [
+const wx = ["style"], qx = ["style", "code"], Xs = ["style", "cid"], Rx = [
"style",
"number",
"sid",
"altnumber",
"pubnumber"
-], mx = [
+], $x = [
"style",
"number",
"sid",
"altnumber",
"pubnumber"
-], yx = [
+], Lx = [
"style",
"sid",
"eid",
"attributeOrder"
-], bx = ["style", "caller", "category", "contents"], kx = ["tag", "marker", "contents"], Tx = [
+], Ix = ["style", "caller", "category", "contents"], Dx = ["tag", "marker", "contents"], Ux = [
"chapter",
"immutable-chapter",
"verse",
@@ -15346,89 +15347,89 @@ const px = ["style"], hx = ["style", "code"], Hs = ["style", "cid"], gx = [
"note",
"unknown",
"unmatched"
-], rs = `
+], as = `
`;
-function xx(e, t) {
+function Fx(e, t) {
const r = se(e);
- if (!Pt(r))
+ if (!Nt(r))
return;
- const n = hh(r, "apply");
+ const n = Ph(r, "apply");
return n === void 0 ? void 0 : [{ retain: n }, ...t, { delete: 1 }];
}
-function hh(e, t = "delta-doc") {
+function Ph(e, t = "delta-doc") {
if (!e)
return;
- const r = Af();
+ const r = Bf();
let n = 0;
const i = [], s = [], o = e.getKey();
let a;
for (const c of r) {
const l = c.node;
- for (let d = i.length - 1; d >= 0; d--)
- if (ii(i[d], c)) {
- const f = i[d];
- if (i.splice(d, 1), n += 1, a && f.getKey() === a.getKey())
+ for (let u = i.length - 1; u >= 0; u--)
+ if (li(i[u], c)) {
+ const f = i[u];
+ if (i.splice(u, 1), n += 1, a && f.getKey() === a.getKey())
return n - 1;
}
- for (let d = s.length - 1; d >= 0; d--)
- ii(s[d].node, c) && s.splice(d, 1);
- const u = s[s.length - 1];
- if (u) {
+ for (let u = s.length - 1; u >= 0; u--)
+ li(s[u].node, c) && s.splice(u, 1);
+ const d = s[s.length - 1];
+ if (d) {
if (l.getKey() === o)
- return u.position;
+ return d.position;
continue;
}
if (l.getKey() === o) {
- if (_r(l) || Pt(l))
+ if (Er(l) || Nt(l))
return n;
- Et(l) && (a = l);
+ At(l) && (a = l);
}
- if (Et(l) && (i.includes(l) || i.push(l)), gh(l, t)) {
+ if (At(l) && (i.includes(l) || i.push(l)), Nh(l, t)) {
if (l.getKey() === o)
return n;
s.push({ node: l, position: n }), n += 1;
continue;
}
- n += Qc(l, t);
+ n += cl(l, t);
}
if (a)
return n;
}
-function ju(e, t, r = "delta-doc") {
- if (e.length < 2 || !Sx(e[0]) || !Cx(e[1]))
+function td(e, t, r = "delta-doc") {
+ if (e.length < 2 || !jx(e[0]) || !Kx(e[1]))
return;
const n = e[0].retain;
- return t.read(() => _x(n, r)?.getKey());
+ return t.read(() => zx(n, r)?.getKey());
}
-function _x(e, t = "delta-doc") {
- const r = Af();
+function zx(e, t = "delta-doc") {
+ const r = Bf();
let n = 0;
const i = [], s = [];
for (const o of r) {
const a = o.node;
- for (let u = i.length - 1; u >= 0; u--)
- if (ii(i[u], o)) {
- const d = i[u];
- if (i.splice(u, 1), n === e)
- return d;
+ for (let d = i.length - 1; d >= 0; d--)
+ if (li(i[d], o)) {
+ const u = i[d];
+ if (i.splice(d, 1), n === e)
+ return u;
n += 1;
}
- for (let u = s.length - 1; u >= 0; u--)
- ii(s[u].node, o) && s.splice(u, 1);
+ for (let d = s.length - 1; d >= 0; d--)
+ li(s[d].node, o) && s.splice(d, 1);
const c = s[s.length - 1];
if (c) {
if (c.position === e)
return c.node;
continue;
}
- if (Et(a) && (i.includes(a) || i.push(a)), gh(a, t)) {
+ if (At(a) && (i.includes(a) || i.push(a)), Nh(a, t)) {
if (n === e)
return a;
s.push({ node: a, position: n }), n += 1;
continue;
}
- const l = Qc(a, t);
- if (_r(a) && l > 0 && e >= n && e < n + l || Pt(a) && n === e)
+ const l = cl(a, t);
+ if (Er(a) && l > 0 && e >= n && e < n + l || Nt(a) && n === e)
return a;
n += l;
}
@@ -15438,56 +15439,56 @@ function _x(e, t = "delta-doc") {
n += 1;
}
}
-function ii(e, t) {
- return e ? t ? !Ks(t.node, e.getKey()) : !0 : !1;
+function li(e, t) {
+ return e ? t ? !Ws(t.node, e.getKey()) : !0 : !1;
}
-function _r(e) {
- return v(e) && !Pt(e);
+function Er(e) {
+ return M(e) && !Nt(e);
}
-function Pt(e) {
- return Ge(e) || ge(e) || Be(e) || j(e) || De(e) || Qr(e);
+function Nt(e) {
+ return Ge(e) || he(e) || He(e) || z(e) || Ue(e) || tn(e);
}
-function Fr(e, t) {
+function Kr(e, t) {
return t?.insert != null && typeof t.insert == "object" && e in t.insert;
}
-function Cx(e) {
+function Kx(e) {
if (e.insert == null || typeof e.insert != "object")
return !1;
const t = Object.keys(e.insert)[0];
- return e.insert != null && typeof e.insert == "object" && t in e.insert && Tx.includes(t);
+ return e.insert != null && typeof e.insert == "object" && t in e.insert && Ux.includes(t);
}
-function Sx(e) {
+function jx(e) {
return e.retain != null && typeof e.retain == "number";
}
-function gh(e, t) {
- return j(e) || De(e) ? !0 : t === "apply" && F(e) && Pt(e);
+function Nh(e, t) {
+ return z(e) || Ue(e) ? !0 : t === "apply" && L(e) && Nt(e);
}
-function mh(e) {
+function Oh(e) {
const t = e.getParent();
- return Ft(e) && ae(t) && t.getFirstChild() === e;
+ return zt(e) && ce(t) && t.getFirstChild() === e;
}
-function Fa(e) {
+function Va(e) {
const t = e.getParent();
- return t !== null && nt(t, Le) !== null;
+ return t !== null && Xe(t, De) !== null;
}
-function vx(e) {
+function Bx(e) {
const t = e.getParent();
- return D(t) && e.getTextContent() === Dt && t.getChildrenSize() === 1;
+ return I(t) && e.getTextContent() === Ut && t.getChildrenSize() === 1;
}
-function Mx(e) {
+function Vx(e) {
const t = e.getParent();
- if (!j(t))
+ if (!z(t))
return !1;
const r = e.getPreviousSibling();
- return O(r) && r === t.getFirstChild() && e.getTextContent() === At(t.getCaller());
+ return O(r) && r === t.getFirstChild() && e.getTextContent() === Pt(t.getCaller());
}
-function Ex(e) {
- return !Hp(e) && Qc(e, "delta-doc") === e.getTextContentSize();
+function Wx(e) {
+ return !lh(e) && cl(e, "delta-doc") === e.getTextContentSize();
}
-function Qc(e, t) {
- if (Pt(e))
+function cl(e, t) {
+ if (Nt(e))
return 1;
- if (v(e)) {
+ if (M(e)) {
const r = e.getTextContent();
return t === "delta-doc" && // A bare cursor host (EmptyVerseCaretGuardPlugin) is a transient, collab-invisible node:
// its insertion is never emitted, so it contributes nothing to DOC-DELTA positions or the
@@ -15497,25 +15498,25 @@ function Qc(e, t) {
// and adds raw `getTextContentSize()`), so excluding it here left a replace-embed retain
// one short whenever a host rested before the target — a footnote-popover save then
// deleted the unit BEFORE the note instead of the note itself.
- (qc(e) || mh(e) || ne(e, oe) === "marker-trailing-space" || // An attribute value keyed by its own state, not by ancestry: a CHAR span's run is a direct
+ (Kc(e) || Oh(e) || ne(e, oe) === "marker-trailing-space" || // An attribute value keyed by its own state, not by ancestry: a CHAR span's run is a direct
// TextNode child of the span, never wrapped (`displayRunRegistry.ts`'s char descriptor
// writes "owner-children"), so `$hasAttributeRunAncestor` cannot see it. That shape is at
// rest on every `\w …|strong="…"\w*`, and the ops stream already omits those bytes
// (`isNodeAttributeText` in editor-delta.adaptor.ts), so counting them here would put this
// side out of step with the op stream on ordinary Scripture.
- ne(e, oe) === "attribute" || Fa(e) || // The remaining ops-stream exclusions, so this side and $handleTextNodes count the same
+ ne(e, oe) === "attribute" || Va(e) || // The remaining ops-stream exclusions, so this side and $handleTextNodes count the same
// bytes (docs/standard-view-invariants.md §II — extend the shared list, never fork it):
// the legacy NBSP-`|` byte-prefixed attribute text the ops stream still honors for
// pre-state-tag peers and persisted deltas, the empty-char placeholder, and the
// editable-mode note caller in caller position.
- r.startsWith(Sc) || vx(e) || Mx(e)) ? 0 : e.getTextContentSize();
+ r.startsWith(qc) || Bx(e) || Vx(e)) ? 0 : e.getTextContentSize();
}
return 0;
}
-function za(e, t) {
- const r = { insert: e.__text }, n = ne(e, Vr);
+function Wa(e, t) {
+ const r = { insert: e.__text }, n = ne(e, Gr);
if (n && (r.attributes = { segment: n }), t && t.length > 0) {
- const i = yh(t);
+ const i = wh(t);
i && (r.attributes = {
...r.attributes,
char: i
@@ -15523,178 +15524,178 @@ function za(e, t) {
}
return r;
}
-function Bu(e) {
- const t = new qi();
+function rd(e) {
+ const t = new Di();
return e.isEmpty() || e.read(() => {
- const r = Fe();
+ const r = ze();
if (!r || r.isEmpty())
return;
const n = r.getChildren();
if (n.length === 1 && dr(n[0]) && (!n[0].getChildren() || n[0].getChildrenSize() === 0))
return;
- const i = Ax();
+ const i = Hx();
for (const s of i)
t.push(s);
}), t;
}
-function Zc(e, t) {
- const r = [], n = ui(e, t), i = [], s = [], o = [], a = /* @__PURE__ */ new Set();
+function ll(e, t) {
+ const r = [], n = wn(e, t), i = [], s = [], o = [], a = /* @__PURE__ */ new Set();
for (let c = 0; c < n.length; c++) {
const l = n[c].node;
- r.push(...Vu(l, c, n, i, s, o, a));
+ r.push(...nd(l, c, n, i, s, o, a));
}
for (const c of i)
- r.push(...Vu(c, n.length, n, i, s, o, a));
+ r.push(...nd(c, n.length, n, i, s, o, a));
return r;
}
-function Ax() {
- return Zc();
+function Hx() {
+ return ll();
}
-function Vu(e, t, r, n, i, s, o) {
+function nd(e, t, r, n, i, s, o) {
if (!e)
return [];
const a = [], c = r[t + 1];
- return Px(e, a, n), Nx(e, a, i, s, o), Ox(e, t, r, i, o, s, a), Ge(e) && a.push($x(e)), ge(e) && a.push(Lx(e)), Be(e) && a.push(Dx(e)), Qr(e) && a.push(Ux(e)), qx(e, a, s), wx(e, a, s), jx(c, s), a;
+ return Gx(e, a, n), Jx(e, a, i, s, o), Yx(e, t, r, i, o, s, a), Ge(e) && a.push(e_(e)), he(e) && a.push(r_(e)), He(e) && a.push(n_(e)), tn(e) && a.push(i_(e)), Qx(e, a, s), Xx(e, a, s), c_(c, s), a;
}
-function Px(e, t, r) {
+function Gx(e, t, r) {
if (!e.isInline()) {
const n = r.pop();
- pt(n) ? t.push(Rx(n)) : ae(n) ? t.push(Ix(n)) : dr(n) && t.push({ insert: rs });
+ pt(n) ? t.push(Zx(n)) : ce(n) ? t.push(t_(n)) : dr(n) && t.push({ insert: as });
}
- Et(e) && (r.includes(e) || r.push(e));
+ At(e) && (r.includes(e) || r.push(e));
}
-function Nx(e, t, r, n, i) {
- if (!v(e) || Oe(e) || Qr(e))
+function Jx(e, t, r, n, i) {
+ if (!M(e) || we(e) || tn(e))
return;
const s = e.getParent();
- if (j(s) && s.getFirstChild() === e)
+ if (z(s) && s.getFirstChild() === e)
return;
- const o = Xt(e) !== void 0;
- if (O(e) && (o || mh(e) || Fa(e) || Hp(e)) || ne(e, oe) === "marker-trailing-space")
+ const o = Yt(e) !== void 0;
+ if (O(e) && (o || Oh(e) || Va(e) || lh(e)) || ne(e, oe) === "marker-trailing-space")
return;
let a = e.getTextContent();
- if (ps(a))
+ if (ys(a))
return;
const c = e.getPreviousSibling();
- if (j(s) && O(c) && c === s.getFirstChild() && a === At(s.getCaller()))
+ if (z(s) && O(c) && c === s.getFirstChild() && a === Pt(s.getCaller()))
return;
- const l = D(s) ? s : void 0, u = l?.getFirstChild();
- o && l && O(u) && c === u && a.startsWith(I) && (a = a.slice(1));
- const d = a.startsWith(Sc) || ne(e, oe) === "attribute" || Fa(e), f = !!l && a === Dt && l.getChildrenSize() === 1, p = vo(e, n), m = p ? r.filter((k) => p.children.includes(k)) : r, g = za(e, m);
- if (g.insert = a, p) {
- if (!a || a === I || d)
+ const l = I(s) ? s : void 0, d = l?.getFirstChild();
+ o && l && O(d) && c === d && a.startsWith(R) && (a = a.slice(1));
+ const u = a.startsWith(qc) || ne(e, oe) === "attribute" || Va(e), f = !!l && a === Ut && l.getChildrenSize() === 1, p = Po(e, n), y = p ? r.filter((b) => p.children.includes(b)) : r, h = Wa(e, y);
+ if (h.insert = a, p) {
+ if (!a || a === R || u)
return;
- p.contentsOps?.push(g);
+ p.contentsOps?.push(h);
} else
- f || d || t.push(g);
- const y = a !== "" && !f && !(d && l);
- if (r.length > 0 && y)
- for (const k of r)
- i.add(k);
-}
-function Ox(e, t, r, n, i, s, o) {
- D(e) && !n.includes(e) && n.push(e);
+ f || u || t.push(h);
+ const m = a !== "" && !f && !(u && l);
+ if (r.length > 0 && m)
+ for (const b of r)
+ i.add(b);
+}
+function Yx(e, t, r, n, i, s, o) {
+ I(e) && !n.includes(e) && n.push(e);
const a = r[t + 1];
for (const c of n.toReversed())
- if (ii(c, a)) {
+ if (li(c, a)) {
if (n.pop(), !i.has(c)) {
- const l = zx(c), u = vo(c, s);
- u ? u.contentsOps?.push(l) : o.push(l);
+ const l = o_(c), d = Po(c, s);
+ d ? d.contentsOps?.push(l) : o.push(l);
}
i.delete(c);
}
}
-function wx(e, t, r) {
- if (!j(e))
+function Xx(e, t, r) {
+ if (!z(e))
return;
- const n = Fx(e), i = vo(e, r), s = {
+ const n = s_(e), i = Po(e, r), s = {
node: e,
- children: ui(e).map((o) => o.node),
+ children: wn(e).map((o) => o.node),
contentsOps: n.insert.note?.contents?.ops
};
r.push(s), i?.contentsOps ? i.contentsOps.push(n) : t.push(n);
}
-function qx(e, t, r) {
- if (!De(e))
+function Qx(e, t, r) {
+ if (!Ue(e))
return;
- const n = Kx(e), i = vo(e, r), s = {
+ const n = a_(e), i = Po(e, r), s = {
node: e,
- children: ui(e).map((o) => o.node),
+ children: wn(e).map((o) => o.node),
contentsOps: n.insert.unknown?.contents?.ops
};
r.push(s), i?.contentsOps ? i.contentsOps.push(n) : t.push(n);
}
-function Zr(e, t) {
+function rn(e, t) {
const r = t.getUnknownAttributes();
r && Object.assign(e, r);
}
-function Rx(e) {
- const t = { style: Gi, code: e.__code };
- return Zr(t, e), { insert: rs, attributes: { book: t } };
+function Zx(e) {
+ const t = { style: Zi, code: e.__code };
+ return rn(t, e), { insert: as, attributes: { book: t } };
}
-function $x(e) {
- const t = { style: Fs, number: e.__number };
- return e.__sid && (t.sid = e.__sid), e.__altnumber && (t.altnumber = e.__altnumber), e.__pubnumber && (t.pubnumber = e.__pubnumber), Zr(t, e), { insert: { chapter: t } };
+function e_(e) {
+ const t = { style: Bs, number: e.__number };
+ return e.__sid && (t.sid = e.__sid), e.__altnumber && (t.altnumber = e.__altnumber), e.__pubnumber && (t.pubnumber = e.__pubnumber), rn(t, e), { insert: { chapter: t } };
}
-function Ix(e) {
+function t_(e) {
const t = { style: e.__marker };
- return Zr(t, e), { insert: rs, attributes: { para: t } };
+ return rn(t, e), { insert: as, attributes: { para: t } };
}
-function Lx(e) {
- const t = { style: zs, number: e.__number };
- return e.__sid && (t.sid = e.__sid), e.__altnumber && (t.altnumber = e.__altnumber), e.__pubnumber && (t.pubnumber = e.__pubnumber), Zr(t, e), { insert: { verse: t } };
+function r_(e) {
+ const t = { style: Vs, number: e.__number };
+ return e.__sid && (t.sid = e.__sid), e.__altnumber && (t.altnumber = e.__altnumber), e.__pubnumber && (t.pubnumber = e.__pubnumber), rn(t, e), { insert: { verse: t } };
}
-function Dx(e) {
+function n_(e) {
const t = { style: e.__marker };
- return e.__sid && (t.sid = e.__sid), e.__eid && (t.eid = e.__eid), e.__attributeOrder && (t.attributeOrder = e.__attributeOrder), Zr(t, e), { insert: { milestone: t } };
+ return e.__sid && (t.sid = e.__sid), e.__eid && (t.eid = e.__eid), e.__attributeOrder && (t.attributeOrder = e.__attributeOrder), rn(t, e), { insert: { milestone: t } };
}
-function Ux(e) {
+function i_(e) {
return { insert: { unmatched: { marker: e.__marker } } };
}
-function Fx(e) {
+function s_(e) {
const t = {
style: e.__marker,
caller: e.__caller
};
- e.__category && (t.category = e.__category), Zr(t, e), e.getChildrenSize() > 1 && (t.contents = { ops: [] });
- const r = { insert: { note: t } }, n = ne(e, Vr);
+ e.__category && (t.category = e.__category), rn(t, e), e.getChildrenSize() > 1 && (t.contents = { ops: [] });
+ const r = { insert: { note: t } }, n = ne(e, Gr);
return n && (r.attributes = { segment: n }), r;
}
-function zx(e) {
- const t = { insert: "" }, r = yh([e]);
+function o_(e) {
+ const t = { insert: "" }, r = wh([e]);
return r && (t.attributes = { char: r }), t;
}
-function Kx(e) {
+function a_(e) {
const t = { tag: e.getTag() }, r = e.getMarker();
- return r && (t.marker = r), Zr(t, e), e.getChildrenSize() > 0 && (t.contents = { ops: [] }), { insert: { unknown: t } };
+ return r && (t.marker = r), rn(t, e), e.getChildrenSize() > 0 && (t.contents = { ops: [] }), { insert: { unknown: t } };
}
-function vo(e, t) {
+function Po(e, t) {
for (let r = t.length - 1; r >= 0; r--) {
const n = t[r];
if (n.children.includes(e))
return n;
}
}
-function jx(e, t) {
+function c_(e, t) {
for (let r = t.length - 1; r >= 0; r--)
- ii(t[r].node, e) && t.splice(r, 1);
+ li(t[r].node, e) && t.splice(r, 1);
}
-function yh(e) {
+function wh(e) {
if (e.length === 0)
return;
- const t = e.map(Bx);
+ const t = e.map(l_);
return t.length === 1 ? t[0] : t;
}
-function Bx(e) {
- const t = { style: e.__marker }, r = ne(e, kn);
- return r && (t.cid = r), Zr(t, e), t;
+function l_(e) {
+ const t = { style: e.__marker }, r = ne(e, Tn);
+ return r && (t.cid = r), rn(t, e), t;
}
-const bh = 1;
-class Gt extends ls {
+const qh = 1;
+class Ht extends hs {
__caller;
__previewText;
__onClick;
- constructor(t = Bi, r = "", n, i) {
+ constructor(t = Ji, r = "", n, i) {
super(i), this.__caller = t, this.__previewText = r, this.__onClick = n ?? (() => {
});
}
@@ -15703,18 +15704,18 @@ class Gt extends ls {
}
static clone(t) {
const { __caller: r, __previewText: n, __onClick: i, __key: s } = t;
- return new Gt(r, n, i, s);
+ return new Ht(r, n, i, s);
}
static importDOM() {
return {
- span: (t) => Wx(t) ? {
- conversion: Vx,
+ span: (t) => d_(t) ? {
+ conversion: u_,
priority: 1
} : null
};
}
static importJSON(t) {
- return el().updateFromJSON(t);
+ return ul().updateFromJSON(t);
}
updateFromJSON(t) {
return super.updateFromJSON(t).setCaller(t.caller).setPreviewText(t.previewText).setOnClick(t.onClick);
@@ -15755,17 +15756,17 @@ class Gt extends ls {
}
exportDOM(t) {
const { element: r } = super.exportDOM(t);
- return r && An(r) && (r.classList.add(this.getType()), r.setAttribute("data-caller", this.getCaller()), r.setAttribute("data-preview-text", this.getPreviewText())), { element: r };
+ return r && Pn(r) && (r.classList.add(this.getType()), r.setAttribute("data-caller", this.getCaller()), r.setAttribute("data-preview-text", this.getPreviewText())), { element: r };
}
decorate(t) {
const r = this.getParent();
if (!r)
return null;
- const n = r.getKey(), i = r.getIsCollapsed(), s = this.__key, o = (c) => this.__onClick?.(c, n, i, () => Hx(t, n), (l) => Gx(t, n, s, l), () => Jx(t, n), () => Yx(t, n)), a = `${this.__caller}_${this.__previewText}}`.replace(/\s+/g, "").substring(0, 25);
- return C("button", { onClick: o, title: this.__previewText, "data-caller-id": a, children: this.__caller === Bi && i ? (
+ const n = r.getKey(), i = r.getIsCollapsed(), s = this.__key, o = (c) => this.__onClick?.(c, n, i, () => f_(t, n), (l) => p_(t, n, s, l), () => h_(t, n), () => g_(t, n)), a = `${this.__caller}_${this.__previewText}}`.replace(/\s+/g, "").substring(0, 25);
+ return S("button", { onClick: o, title: this.__previewText, "data-caller-id": a, children: this.__caller === Ji && i ? (
// Caller is generated by CSS (footnote or cross-reference sequence, per note marker)
""
- ) : this.__caller === Lf && i ? (
+ ) : this.__caller === Zf && i ? (
// PT9: the hidden caller displays as `*` when collapsed
"*"
) : this.__caller });
@@ -15776,7 +15777,7 @@ class Gt extends ls {
caller: this.getCaller(),
previewText: this.getPreviewText(),
onClick: this.getOnClick(),
- version: bh
+ version: qh
};
}
// Mutation
@@ -15784,59 +15785,59 @@ class Gt extends ls {
return !1;
}
}
-function Vx(e) {
+function u_(e) {
const t = e.getAttribute("data-caller") ?? "", r = e.getAttribute("data-preview-text") ?? "";
- return { node: el(t, r) };
+ return { node: ul(t, r) };
}
-function el(e, t, r) {
- return je(new Gt(e, t, r));
+function ul(e, t, r) {
+ return Ve(new Ht(e, t, r));
}
-function Wx(e) {
- return e ? e.classList.contains(Gt.getType()) : !1;
+function d_(e) {
+ return e ? e.classList.contains(Ht.getType()) : !1;
}
-function St(e) {
- return e instanceof Gt;
+function ht(e) {
+ return e instanceof Ht;
}
-function Hx(e, t) {
+function f_(e, t) {
return e.getEditorState().read(() => {
const r = se(t);
- if (!j(r))
+ if (!z(r))
throw new Error(`getNoteCaller: Note node not found: ${t}`);
return r.getCaller();
});
}
-function Gx(e, t, r, n) {
+function p_(e, t, r, n) {
e.update(() => {
const i = se(t);
- if (!j(i))
+ if (!z(i))
throw new Error(`setNoteCaller: Note node not found: ${t}`);
i.setCaller(n);
const s = se(r);
- if (!St(s))
+ if (!ht(s))
throw new Error(`setNoteCaller: Caller node not found: ${r}`);
s.setCaller(n);
});
}
-function Jx(e, t) {
+function h_(e, t) {
return e.getEditorState().read(() => {
const r = se(t);
- if (!j(r))
+ if (!z(r))
throw new Error(`getNoteOps: Note node not found: ${t}`);
- return Zc(r);
+ return ll(r);
});
}
-function Yx(e, t) {
+function g_(e, t) {
return e.getEditorState().read(() => {
let r = 0;
- for (const { node: n } of ui())
- if (j(n)) {
+ for (const { node: n } of wn())
+ if (z(n)) {
if (n.getKey() === t)
return r;
r += 1;
}
});
}
-const Xx = [
+const m_ = [
"a",
"b",
"c",
@@ -15863,151 +15864,151 @@ const Xx = [
"x",
"y",
"z"
-], Qx = ["†"];
-function Mo(e) {
- if (kh())
+], y_ = ["†"];
+function No(e) {
+ if (Rh())
return;
const { start: t } = e;
let { end: r } = e;
r ??= t;
- let [n, i] = Wu(t), [s, o] = Wu(r);
+ let [n, i] = id(t), [s, o] = id(r);
if (!n || !s || i === void 0 || o === void 0)
return;
- [n, i] = Hu(n, i), [s, o] = Hu(s, o);
- const a = bc();
- return a.anchor = su(n.getKey(), i, Gu(n)), a.focus = su(s.getKey(), o, Gu(s)), a;
+ [n, i] = sd(n, i), [s, o] = sd(s, o);
+ const a = Ec();
+ return a.anchor = Ma(n.getKey(), i, od(n)), a.focus = Ma(s.getKey(), o, od(s)), a;
}
-function tl() {
- if (kh())
+function dl() {
+ if (Rh())
return;
const e = w();
if (!e || !N(e))
return;
- const t = e.isBackward() ? e.focus.getNode() : e.anchor.getNode(), r = e.isBackward() ? e.focus.offset : e.anchor.offset, n = Gs(t, r);
+ const t = e.isBackward() ? e.focus.getNode() : e.anchor.getNode(), r = e.isBackward() ? e.focus.offset : e.anchor.offset, n = Qs(t, r);
if (e.isCollapsed())
return { start: n };
- const i = e.isBackward() ? e.anchor.getNode() : e.focus.getNode(), s = e.isBackward() ? e.anchor.offset : e.focus.offset, o = Gs(i, s);
+ const i = e.isBackward() ? e.anchor.getNode() : e.focus.getNode(), s = e.isBackward() ? e.anchor.offset : e.focus.offset, o = Qs(i, s);
return { start: n, end: o };
}
-function Wu(e) {
- if (oy(e)) {
- const t = yf(e.jsonPath);
- let r = Fe();
+function id(e) {
+ if (Cy(e)) {
+ const t = Of(e.jsonPath);
+ let r = ze();
for (let n = 0; n < t.length; n++) {
- if (!r || !F(r))
+ if (!r || !L(r))
return [void 0, void 0];
- const i = di(r)[t[n]];
+ const i = gi(r)[t[n]];
if (!i)
return [void 0, void 0];
if (i.type === "text")
- return n !== t.length - 1 ? [void 0, void 0] : Ok(i, e.offset) ?? [void 0, void 0];
+ return n !== t.length - 1 ? [void 0, void 0] : Yk(i, e.offset) ?? [void 0, void 0];
r = i.node;
}
- return r && F(r) ? [r, wk(r, e.offset)] : [void 0, void 0];
+ return r && L(r) ? [r, Xk(r, e.offset)] : [void 0, void 0];
}
- if (ay(e) || cy(e)) {
- const t = Mi(e.jsonPath);
+ if (vy(e) || Sy(e)) {
+ const t = Oi(e.jsonPath);
if (!t)
return [void 0, void 0];
- if (F(t)) {
+ if (L(t)) {
const n = t.getLastChild();
- if (n && v(n))
+ if (n && M(n))
return [n, n.getTextContent().length];
}
const r = t.getNextSibling();
- return r && F(r) ? [r, 0] : [void 0, void 0];
+ return r && L(r) ? [r, 0] : [void 0, void 0];
}
- if (ly(e)) {
- const t = Mi(e.jsonPath);
+ if (My(e)) {
+ const t = Oi(e.jsonPath);
if (!t)
return [void 0, void 0];
- if (F(t)) {
+ if (L(t)) {
const n = t.getLastChild();
- if (n && v(n))
+ if (n && M(n))
return [n, n.getTextContent().length];
}
const r = t.getNextSibling();
- return r && F(r) ? [r, 0] : [void 0, void 0];
+ return r && L(r) ? [r, 0] : [void 0, void 0];
}
- if (uy(e)) {
- const t = Mi(e.jsonPath);
- if (!t || !F(t))
+ if (Ey(e)) {
+ const t = Oi(e.jsonPath);
+ if (!t || !L(t))
return [void 0, void 0];
- const r = na(t, "opening");
+ const r = oa(t, "opening");
if (r)
return [r, 0];
const n = t.getFirstChild();
- return n && v(n) ? [n, 0] : [void 0, void 0];
+ return n && M(n) ? [n, 0] : [void 0, void 0];
}
- if (dy(e)) {
- const t = Mi(e.jsonPath);
- if (!t || !F(t))
+ if (Ay(e)) {
+ const t = Oi(e.jsonPath);
+ if (!t || !L(t))
return [void 0, void 0];
- const r = na(t, "closing");
+ const r = oa(t, "closing");
if (r) {
const i = r.getTextContent(), s = Math.min(e.closingMarkerOffset, i.length);
return [r, s];
}
const n = t.getLastChild();
- return n && v(n) ? [n, n.getTextContent().length] : [void 0, void 0];
+ return n && M(n) ? [n, n.getTextContent().length] : [void 0, void 0];
}
- if (fy(e)) {
- const t = e.jsonPath.match(/\.(\w+)$|^\$\.(\w+)$|\['([^']+)'\]$/), r = t?.[1] ?? t?.[2] ?? t?.[3], n = Mi(e.jsonPath);
- if (!n || !F(n))
+ if (Py(e)) {
+ const t = e.jsonPath.match(/\.(\w+)$|^\$\.(\w+)$|\['([^']+)'\]$/), r = t?.[1] ?? t?.[2] ?? t?.[3], n = Oi(e.jsonPath);
+ if (!n || !L(n))
return [void 0, void 0];
if (r === "marker") {
- const s = na(n, "opening");
+ const s = oa(n, "opening");
if (s) {
const o = e.propertyOffset + 1, a = s.getTextContent();
return [s, Math.min(o, a.length)];
}
}
const i = n.getFirstChild();
- return i && v(i) ? [i, 0] : [void 0, void 0];
+ return i && M(i) ? [i, 0] : [void 0, void 0];
}
- throw new Error(`Unsupported UsjDocumentLocation type: ${py(e)}. All UsjDocumentLocation subtypes should be supported: UsjMarkerLocation, UsjClosingMarkerLocation, UsjTextContentLocation, UsjPropertyValueLocation, UsjAttributeKeyLocation, UsjAttributeMarkerLocation, andUsjClosingAttributeMarkerLocation. Received: ${JSON.stringify(e)}`);
+ throw new Error(`Unsupported UsjDocumentLocation type: ${Ny(e)}. All UsjDocumentLocation subtypes should be supported: UsjMarkerLocation, UsjClosingMarkerLocation, UsjTextContentLocation, UsjPropertyValueLocation, UsjAttributeKeyLocation, UsjAttributeMarkerLocation, andUsjClosingAttributeMarkerLocation. Received: ${JSON.stringify(e)}`);
}
-function Hu(e, t) {
- if (!Pr(e))
+function sd(e, t) {
+ if (!Rr(e))
return [e, t];
const r = e.getTextContent().length;
if (t < 0 || t >= r)
return [e, t];
const n = e.getParent();
- if (!n || !F(n))
+ if (!n || !L(n))
return [e, t];
const i = e.getIndexWithinParent();
return i < 0 ? [e, t] : [n, i];
}
-function Gu(e) {
- return F(e) ? "element" : "text";
+function od(e) {
+ return L(e) ? "element" : "text";
}
-function na(e, t) {
+function oa(e, t) {
const r = e.getChildren();
for (const n of r) {
if (O(n) && n.getMarkerSyntax() === t || t === "closing" && O(n) && n.getMarkerSyntax() === "selfClosing")
return n;
- if (Pr(n)) {
+ if (Rr(n)) {
const s = n.getTextContent().endsWith("*");
if (t === "opening" && !s || t === "closing" && s)
return n;
}
}
}
-function Mi(e) {
- const t = new RegExp(/^(\$(?:\.content\[\d+\])*)(?:\.|$|\[)/).exec(e), r = t ? t[1] : e, n = yf(r);
- let i = Fe();
+function Oi(e) {
+ const t = new RegExp(/^(\$(?:\.content\[\d+\])*)(?:\.|$|\[)/).exec(e), r = t ? t[1] : e, n = Of(r);
+ let i = ze();
for (const s of n) {
- if (!i || !F(i))
+ if (!i || !L(i))
return;
- const o = di(i)[s];
+ const o = gi(i)[s];
i = o?.type === "element" ? o.node : void 0;
}
return i;
}
-function Gs(e, t) {
+function Qs(e, t) {
if (O(e)) {
- const r = e.getMarkerSyntax(), n = Zx(e), i = n ? on(un(n)) : on(un(e));
+ const r = e.getMarkerSyntax(), n = b_(e), i = n ? cn(fn(n)) : cn(fn(e));
if (r === "closing" || r === "selfClosing")
return {
jsonPath: i,
@@ -16023,102 +16024,102 @@ function Gs(e, t) {
propertyOffset: o
};
}
- if (Ce(e)) {
+ if (_e(e)) {
const r = e.getChildrenSize(), n = e.getChildAtIndex(Math.min(t, r - 1));
- if (v(n)) {
+ if (M(n)) {
const s = t >= r ? n.getTextContentSize() : 0;
- return Gs(n, s);
+ return Qs(n, s);
}
- const i = yo(e);
+ const i = xo(e);
if (i?.is(e.getParent())) {
const s = e.getIndexWithinParent(), o = t >= r ? s + 1 : s;
- return Gs(i, o);
+ return Qs(i, o);
}
}
- if (F(e)) {
+ if (L(e)) {
const r = e.getChildAtIndex(t);
- if (Pr(r)) {
- const i = r.getTextContent().endsWith("*"), s = on(un(e));
+ if (Rr(r)) {
+ const i = r.getTextContent().endsWith("*"), s = cn(fn(e));
return i ? { jsonPath: s, closingMarkerOffset: 0 } : { jsonPath: s };
}
- const n = Np(e, t);
+ const n = Wp(e, t);
return n.type === "text" ? {
- jsonPath: on([...un(e), n.index]),
+ jsonPath: cn([...fn(e), n.index]),
offset: n.offset
} : {
- jsonPath: on(un(e)),
+ jsonPath: cn(fn(e)),
offset: n.index
};
}
- if (v(e)) {
- const r = Nk(e, t);
+ if (M(e)) {
+ const r = Jk(e, t);
if (r)
return {
- jsonPath: on([
- ...un(r.parent),
+ jsonPath: cn([
+ ...fn(r.parent),
r.index
]),
offset: r.offset
};
}
- return { jsonPath: on(un(e)), offset: t };
+ return { jsonPath: cn(fn(e)), offset: t };
}
-function Zx(e) {
+function b_(e) {
const t = e.getParent();
- if (!t || !F(t))
+ if (!t || !L(t))
return;
- const r = e_(e);
- return r && !Et(r) && !v(r) && !Ce(r) ? r : t;
+ const r = k_(e);
+ return r && !At(r) && !M(r) && !_e(r) ? r : t;
}
-function e_(e) {
+function k_(e) {
let t = e.getPreviousSibling();
for (; t; ) {
- if (!Lc(t))
+ if (!Wc(t))
return t;
t = t.getPreviousSibling();
}
}
-function un(e) {
+function fn(e) {
const t = [];
let r = e;
for (; r; ) {
- const n = yo(r);
+ const n = xo(r);
if (!n)
break;
- const i = Pk(n, r);
+ const i = Gk(n, r);
i >= 0 && t.unshift(i), r = n;
}
return t;
}
-function kh() {
- for (let e = Fe().getFirstChild(); e; e = e.getNextSibling())
- if (ts(e))
+function Rh() {
+ for (let e = ze().getFirstChild(); e; e = e.getNextSibling())
+ if (os(e))
return !0;
return !1;
}
-function Th(e, t, r, n, i, s, o) {
- if (!Ee.isValidMarker(e))
+function $h(e, t, r, n, i, s, o) {
+ if (!Ae.isValidMarker(e))
throw new Error(`$insertNote: Invalid note marker '${e}'`);
- const a = r ? Mo(r) : w();
+ const a = r ? No(r) : w();
if (!N(a))
return;
- const c = n_(a, e, n, i, s, o);
+ const c = __(a, e, n, i, s, o);
if (c === void 0)
return;
- const l = t ?? (Ri(e) === "crossref" ? s.defaultCrossRefCaller ?? "-" : s.defaultFootnoteCaller ?? "+"), u = xh(e, l, c, i, s, void 0, void 0);
- return r_(u, a, i), u;
+ const l = t ?? (Ui(e) === "crossref" ? s.defaultCrossRefCaller ?? "-" : s.defaultFootnoteCaller ?? "+"), d = Lh(e, l, c, i, s, void 0, void 0);
+ return x_(d, a, i), d;
}
-function rl(e) {
+function fl(e) {
return e !== "expanded";
}
-function t_(e) {
+function T_(e) {
if (!e.isCollapsed())
return;
const { anchor: t } = e;
if (t.type !== "text")
return;
const r = t.getNode();
- if (!v(r) || !D(r.getParent()))
+ if (!M(r) || !I(r.getParent()))
return;
if (O(r))
return t.offset === 0 && r.getMarkerSyntax() === "closing" ? r : void 0;
@@ -16127,40 +16128,40 @@ function t_(e) {
const n = r.getNextSibling();
return O(n) && n.getMarkerSyntax() === "closing" ? n : void 0;
}
-function r_(e, t, r) {
- const n = rl(r?.noteMode);
- e.setIsCollapsed(n), t.isCollapsed() || xk(t), ah(t);
- const i = t_(t);
- i ? (i.insertBefore(e), e.selectNext(0, 0)) : t.insertNodes([e]), n || e.getChildren().reverse().find(D)?.selectEnd();
+function x_(e, t, r) {
+ const n = fl(r?.noteMode);
+ e.setIsCollapsed(n), t.isCollapsed() || Fk(t), _h(t);
+ const i = T_(t);
+ i ? (i.insertBefore(e), e.selectNext(0, 0)) : t.insertNodes([e]), n || e.getChildren().reverse().find(I)?.selectEnd();
}
-function jn(e, t, r) {
- const n = xr(e);
+function Hn(e, t, r) {
+ const n = Mr(e);
n.setUnknownAttributes({ closed: "false" });
const i = r?.markerMode === "editable";
- i ? n.append(ot(e)) : r?.markerMode === "visible" && n.append(Tr("marker", we(e)));
- const s = t === "" ? Dt : i ? I + t : t;
- return n.append(pe(s)), n;
+ i ? n.append(ct(e)) : r?.markerMode === "visible" && n.append(Sr("marker", qe(e)));
+ const s = t === "" ? Ut : i ? R + t : t;
+ return n.append(ge(s)), n;
}
-function n_(e, t, r, n, i, s) {
- const o = [], { chapterNum: a, verseNum: c, verse: l } = r ?? {}, u = i.chapterVerseSeparator ?? ":", d = i.verseRangeSeparator ?? "-", f = a !== void 0 && c !== void 0 ? `${a}${u}${(l ?? `${c}`).replace(/-/g, () => d)} ` : void 0;
+function __(e, t, r, n, i, s) {
+ const o = [], { chapterNum: a, verseNum: c, verse: l } = r ?? {}, d = i.chapterVerseSeparator ?? ":", u = i.verseRangeSeparator ?? "-", f = a !== void 0 && c !== void 0 ? `${a}${d}${(l ?? `${c}`).replace(/-/g, () => u)} ` : void 0;
switch (t) {
case "f":
case "fe":
case "ef":
case "efe":
- if (f !== void 0 && o.push(jn("fr", f, n)), !e.isCollapsed()) {
- const p = Yu(e);
- p.length > 0 && o.push(jn("fq", p, n));
+ if (f !== void 0 && o.push(Hn("fr", f, n)), !e.isCollapsed()) {
+ const p = cd(e);
+ p.length > 0 && o.push(Hn("fq", p, n));
}
- o.push(jn("ft", "", n));
+ o.push(Hn("ft", "", n));
break;
case "x":
case "ex":
- if (f !== void 0 && o.push(jn("xo", f, n)), !e.isCollapsed()) {
- const p = Yu(e);
- p.length > 0 && o.push(jn("xq", p, n));
+ if (f !== void 0 && o.push(Hn("xo", f, n)), !e.isCollapsed()) {
+ const p = cd(e);
+ p.length > 0 && o.push(Hn("xq", p, n));
}
- o.push(jn("xt", "", n));
+ o.push(Hn("xt", "", n));
break;
default:
s?.warn(`$createNoteChildren: Unsupported note marker '${t}'`);
@@ -16168,45 +16169,45 @@ function n_(e, t, r, n, i, s) {
}
return o;
}
-function xh(e, t, r, n, i, s, o) {
- const a = o === "false", c = a ? !1 : rl(n?.noteMode), l = Ec(e, t, c);
- s && bt(l, Vr, () => s);
- const u = n?.isNoteShellEditable === !1;
- let d, f;
- n?.markerMode === "editable" ? (d = ot(e), u && d.setMode("token"), a || (f = ot(e, "closing"))) : n?.markerMode === "visible" && (d = Tr("marker", we(e) + " "), a || (f = Tr("marker", rt(e))));
+function Lh(e, t, r, n, i, s, o) {
+ const a = o === "false", c = a ? !1 : fl(n?.noteMode), l = Lc(e, t, c);
+ s && kt(l, Gr, () => s);
+ const d = n?.isNoteShellEditable === !1;
+ let u, f;
+ n?.markerMode === "editable" ? (u = ct(e), d && u.setMode("token"), a || (f = ct(e, "closing"))) : n?.markerMode === "visible" && (u = Sr("marker", qe(e) + " "), a || (f = Sr("marker", st(e))));
let p;
- if (d && l.append(d), n?.markerMode === "editable" && !c)
- t === "" ? l.append(...r) : (p = pe(At(l.__caller)), u && p.setMode("token"), l.append(p, ...r));
+ if (u && l.append(u), n?.markerMode === "editable" && !c)
+ t === "" ? l.append(...r) : (p = ge(Pt(l.__caller)), d && p.setMode("token"), l.append(p, ...r));
else {
- const m = () => mo(), g = r.flatMap(s_(m));
+ const y = () => To(), h = r.flatMap(v_(y));
if (t === "")
- l.append(...g);
+ l.append(...h);
else {
- const y = Rc(r);
- let k = () => {
+ const m = jc(r);
+ let b = () => {
};
- i?.noteCallerOnClick && (k = i.noteCallerOnClick), p = el(l.__caller, y, k), l.append(p, m(), ...g);
+ i?.noteCallerOnClick && (b = i.noteCallerOnClick), p = ul(l.__caller, m, b), l.append(p, y(), ...h);
}
}
return f && l.append(f), l;
}
-function Ju(e) {
+function ad(e) {
if (typeof e == "string") {
const i = se(e);
- return j(i) ? i : void 0;
+ return z(i) ? i : void 0;
}
- const t = ui();
+ const t = wn();
if (t.length <= 0)
return;
- const n = t.filter((i) => j(i.node))[e]?.node;
- if (j(n))
+ const n = t.filter((i) => z(i.node))[e]?.node;
+ if (z(n))
return n;
}
-function i_(e, t) {
+function C_(e, t) {
const r = t?.noteMode === "collapsed";
if (e.setIsCollapsed(r), r) {
const n = e.getPreviousSibling();
- if (wn(n) || !n) {
+ if (Ln(n) || !n) {
const i = e.getParent();
if (i) {
const s = e.getIndexWithinParent();
@@ -16215,46 +16216,46 @@ function i_(e, t) {
} else
n.selectEnd();
} else
- e.getChildren().reverse().find(D)?.selectEnd();
+ e.getChildren().reverse().find(I)?.selectEnd();
}
-function s_(e) {
- return (t) => Yt(t) ? [t] : [t, e()];
+function v_(e) {
+ return (t) => Jt(t) ? [t] : [t, e()];
}
-function o_(e) {
+function S_(e) {
const t = e.getParent();
- return t !== null && nt(t, j) !== null;
+ return t !== null && Xe(t, z) !== null;
}
-function Yu(e) {
+function cd(e) {
if (!N(e))
return "";
const t = e.getNodes();
if (t.length === 0)
return "";
- const r = t[0], n = t[t.length - 1], i = e.anchor.isBefore(e.focus), [s, o] = kc(e);
+ const r = t[0], n = t[t.length - 1], i = e.anchor.isBefore(e.focus), [s, o] = Ac(e);
let a = "";
for (const c of t)
- if (!(j(c) || St(c) || o_(c)) && !O(c) && !Qr(c) && ne(c, oe) !== "attribute") {
- if (ge(c)) {
+ if (!(z(c) || ht(c) || S_(c)) && !O(c) && !tn(c) && ne(c, oe) !== "attribute") {
+ if (he(c)) {
a += `\\+fv ${c.getNumber()}\\+fv*`;
continue;
}
- if (v(c)) {
+ if (M(c)) {
let l = c.getTextContent();
c === r && c === n ? l = s < o ? l.slice(s, o) : l.slice(o, s) : c === r ? l = i ? l.slice(s) : l.slice(o) : c === n && (l = i ? l.slice(0, o) : l.slice(0, s)), a += l;
}
}
return a.replace(/[ \t\r\n\f\v]+/g, " ").trim();
}
-const nl = [
- Gt,
- Ct,
- ...HT
-], a_ = [
- hi,
- ...nl
-], c_ = En((e, t) => {
+const pl = [
+ Ht,
+ St,
+ ...fx
+], M_ = [
+ bi,
+ ...pl
+], E_ = An((e, t) => {
const { coords: r, children: n, style: i, ...s } = e, o = r !== void 0;
- return C("div", { ref: t, className: "floating-box", "aria-hidden": !o, style: {
+ return S("div", { ref: t, className: "floating-box", "aria-hidden": !o, style: {
...i,
position: "absolute",
zIndex: 1e3,
@@ -16264,28 +16265,28 @@ const nl = [
opacity: o ? 1 : 0
}, ...s, children: n });
});
-function l_() {
- const [e, t] = de(void 0), [r, n] = de(), i = Z(null), s = he((a, c) => {
+function A_() {
+ const [e, t] = de(void 0), [r, n] = de(), i = Z(null), s = me((a, c) => {
i.current && i.current();
const l = a.commonAncestorContainer.nodeType === a.commonAncestorContainer.TEXT_NODE ? a : a.commonAncestorContainer;
- i.current = Wy(l, c, () => {
- Hy(l, c, {
+ i.current = db(l, c, () => {
+ fb(l, c, {
placement: "bottom-start",
- middleware: [Gy(), Jy()]
- }).then((u) => {
- n(u.placement), t((d) => d?.x === u.x && d?.y === u.y ? d : { x: u.x, y: u.y });
+ middleware: [pb(), hb()]
+ }).then((d) => {
+ n(d.placement), t((u) => u?.x === d.x && u?.y === d.y ? u : { x: d.x, y: d.y });
}).catch(() => {
t(void 0);
});
});
- }, []), o = he(() => {
+ }, []), o = me(() => {
i.current && (t(void 0), i.current(), i.current = null);
}, []);
- return z(() => o, [o]), { coords: e, placement: r, updatePosition: s, cleanup: o };
+ return F(() => o, [o]), { coords: e, placement: r, updatePosition: s, cleanup: o };
}
-function u_({ isOpen: e, floatingBoxRef: t }) {
- const { coords: r, updatePosition: n, cleanup: i, placement: s } = l_();
- return z(() => {
+function P_({ isOpen: e, floatingBoxRef: t }) {
+ const { coords: r, updatePosition: n, cleanup: i, placement: s } = A_();
+ return F(() => {
if (!e || !t.current) {
i();
return;
@@ -16298,44 +16299,44 @@ function u_({ isOpen: e, floatingBoxRef: t }) {
return n(o, t.current), i;
}, [i, e, t, n]), { coords: r, placement: s };
}
-const d_ = ey(c_);
-function _h({ isOpen: e = !1, children: t }) {
- const r = Z(null), { coords: n, placement: i } = u_({ isOpen: e, floatingBoxRef: r }), s = ze(() => n ? typeof t == "function" ? t : () => t : () => null, [t, n]);
- return hn(
- C(d_, { ref: r, coords: n, style: n ? void 0 : { display: "none" }, children: s({ isOpen: e, placement: i }) }),
+const N_ = yy(E_);
+function Ih({ isOpen: e = !1, children: t }) {
+ const r = Z(null), { coords: n, placement: i } = P_({ isOpen: e, floatingBoxRef: r }), s = Ke(() => n ? typeof t == "function" ? t : () => t : () => null, [t, n]);
+ return mn(
+ S(N_, { ref: r, coords: n, style: n ? void 0 : { display: "none" }, children: s({ isOpen: e, placement: i }) }),
// Read at render rather than at module scope: this module sits in the import graph of the
// package's utility entry points, so touching `document` on load throws for any consumer that
// imports one of them outside a DOM environment (a Node-environment unit test, SSR).
document.body
);
}
-const Ch = gf(void 0);
-function il() {
- const e = mf(Ch);
+const Dh = Pf(void 0);
+function hl() {
+ const e = Nf(Dh);
if (!e)
throw new Error("useMenuContext must be used within a MenuProvider");
return e;
}
-function f_(e, t) {
- const [r, n] = de(0), [i, s] = de(-1), o = ze(() => e ?? [], [e]), a = {
+function O_(e, t) {
+ const [r, n] = de(0), [i, s] = de(-1), o = Ke(() => e ?? [], [e]), a = {
menuItems: o,
activeIndex: r,
selectedIndex: i,
onSelectOption: t ?? (() => {
})
- }, c = he(() => {
- n((d) => {
+ }, c = me(() => {
+ n((u) => {
const f = o.length;
- return f ? (d - 1 + f) % f : 0;
+ return f ? (u - 1 + f) % f : 0;
});
- }, [o.length]), l = he(() => {
- n((d) => {
+ }, [o.length]), l = me(() => {
+ n((u) => {
const f = o.length;
- return f ? (d + 1) % f : 0;
+ return f ? (u + 1) % f : 0;
});
- }, [o.length]), u = he(() => {
- const d = o.length;
- if (r >= 0 && r < d) {
+ }, [o.length]), d = me(() => {
+ const u = o.length;
+ if (r >= 0 && r < u) {
const f = o[r];
t?.(f), s(r);
}
@@ -16344,91 +16345,91 @@ function f_(e, t) {
state: a,
moveUp: c,
moveDown: l,
- select: u,
+ select: d,
setActiveIndex: n,
setSelectedIndex: s
};
}
-function p_({ children: e, menuItems: t, onSelectOption: r, ...n }) {
- const i = f_(t, r);
- return C(Ch.Provider, { value: i, children: C("div", { ...n, children: e }) });
+function w_({ children: e, menuItems: t, onSelectOption: r, ...n }) {
+ const i = O_(t, r);
+ return S(Dh.Provider, { value: i, children: S("div", { ...n, children: e }) });
}
-const Sh = En(({ index: e, children: t, onMouseEnter: r, onClick: n, ...i }, s) => {
- const { state: { activeIndex: o }, setActiveIndex: a, setSelectedIndex: c, select: l } = il(), u = he((f) => {
+const Uh = An(({ index: e, children: t, onMouseEnter: r, onClick: n, ...i }, s) => {
+ const { state: { activeIndex: o }, setActiveIndex: a, setSelectedIndex: c, select: l } = hl(), d = me((f) => {
l(), c(-1), n?.(f);
- }, [n, l, c]), d = he((f) => {
+ }, [n, l, c]), u = me((f) => {
a(e), r?.(f);
}, [e, a, r]);
- return C("button", { ref: s, role: "menuitem", ...i, onClick: u, onMouseEnter: d, "aria-selected": e !== void 0 && o === e ? "true" : void 0, tabIndex: -1, children: t });
+ return S("button", { ref: s, role: "menuitem", ...i, onClick: d, onMouseEnter: u, "aria-selected": e !== void 0 && o === e ? "true" : void 0, tabIndex: -1, children: t });
});
-function h_({ children: e, autoIndex: t = !0, ...r }) {
- const n = Z(null), { state: { activeIndex: i, menuItems: s } } = il(), o = ze(() => s ? typeof e == "function" ? e : () => e : () => null, [e, s]), a = ze(() => {
+function q_({ children: e, autoIndex: t = !0, ...r }) {
+ const n = Z(null), { state: { activeIndex: i, menuItems: s } } = hl(), o = Ke(() => s ? typeof e == "function" ? e : () => e : () => null, [e, s]), a = Ke(() => {
const c = o(s);
- return t ? ty.map(c, (l, u) => ry(l) && l.type === Sh && l.props.index === void 0 ? ny(l, { index: u }) : l) : c;
+ return t ? by.map(c, (l, d) => ky(l) && l.type === Uh && l.props.index === void 0 ? Ty(l, { index: d }) : l) : c;
}, [o, t, s]);
- return z(() => {
+ return F(() => {
if (n.current) {
const c = n.current, l = c.children[i];
if (l) {
- const u = c.getBoundingClientRect(), d = l.getBoundingClientRect();
- d.bottom > u.bottom ? c.scrollTop += d.bottom - u.bottom : d.top < u.top && (c.scrollTop -= u.top - d.top);
+ const d = c.getBoundingClientRect(), u = l.getBoundingClientRect();
+ u.bottom > d.bottom ? c.scrollTop += u.bottom - d.bottom : u.top < d.top && (c.scrollTop -= d.top - u.top);
}
}
- }, [i]), C("div", { ref: n, role: "menu", ...r, children: a });
+ }, [i]), S("div", { ref: n, role: "menu", ...r, children: a });
}
-const g_ = (e, t, r) => Ns(e, r).toLowerCase().includes(t.toLowerCase()), Xu = (e) => Object.keys(e).find((t) => typeof e[t] == "string") || "", Ns = (e, t) => {
+const R_ = (e, t, r) => $s(e, r).toLowerCase().includes(t.toLowerCase()), ld = (e) => Object.keys(e).find((t) => typeof e[t] == "string") || "", $s = (e, t) => {
const r = e[t];
return typeof r == "string" ? r : String(r);
};
-function m_(e) {
+function $_(e) {
const { query: t, items: r, filterBy: n, filter: i, sortBy: s, sortingOptions: o } = e, { caseSensitive: a = !1, priorityOrder: c = ["exact", "startsWith", "contains"] } = o || {}, l = a ? t : t.toLowerCase();
- let u, d;
- i ? (d = i, u = r.length > 0 ? Xu(r[0]) : "") : (u = n || (r.length > 0 ? Xu(r[0]) : ""), d = (m, g) => g_(m, g, u));
- const f = s || u, p = /* @__PURE__ */ new Map();
- return r.filter((m) => {
+ let d, u;
+ i ? (u = i, d = r.length > 0 ? ld(r[0]) : "") : (d = n || (r.length > 0 ? ld(r[0]) : ""), u = (y, h) => R_(y, h, d));
+ const f = s || d, p = /* @__PURE__ */ new Map();
+ return r.filter((y) => {
try {
- return d(m, t);
- } catch (g) {
- return console.warn("Error filtering item:", m, g), !1;
- }
- }).sort((m, g) => {
- const y = (S) => (p.has(S) || p.set(S, Ns(S, f).toLowerCase()), p.get(S) ?? ""), k = a ? Ns(m, f) : y(m), _ = a ? Ns(g, f) : y(g);
- for (const S of c)
- switch (S) {
+ return u(y, t);
+ } catch (h) {
+ return console.warn("Error filtering item:", y, h), !1;
+ }
+ }).sort((y, h) => {
+ const m = (_) => (p.has(_) || p.set(_, $s(_, f).toLowerCase()), p.get(_) ?? ""), b = a ? $s(y, f) : m(y), x = a ? $s(h, f) : m(h);
+ for (const _ of c)
+ switch (_) {
case "exact":
- if (k === l && _ !== l)
+ if (b === l && x !== l)
return -1;
- if (_ === l && k !== l)
+ if (x === l && b !== l)
return 1;
break;
case "startsWith":
- if (k.startsWith(l) && !_.startsWith(l))
+ if (b.startsWith(l) && !x.startsWith(l))
return -1;
- if (_.startsWith(l) && !k.startsWith(l))
+ if (x.startsWith(l) && !b.startsWith(l))
return 1;
break;
case "contains": {
- const P = k.indexOf(l), A = _.indexOf(l);
- if (P !== -1 && A === -1)
+ const A = b.indexOf(l), E = x.indexOf(l);
+ if (A !== -1 && E === -1)
return -1;
- if (A !== -1 && P === -1)
+ if (E !== -1 && A === -1)
return 1;
- if (P !== -1 && A !== -1)
- return P - A;
+ if (A !== -1 && E !== -1)
+ return A - E;
break;
}
}
- return k.localeCompare(_);
+ return b.localeCompare(x);
});
}
-const ia = {
- Root: p_,
- Options: h_,
- Option: Sh
+const aa = {
+ Root: w_,
+ Options: q_,
+ Option: Uh
};
-function y_(e) {
+function L_(e) {
const { query: t, items: r, filterBy: n, filter: i, sortBy: s, sortingOptions: o } = e;
- return ze(() => m_({
+ return Ke(() => $_({
query: t,
items: r,
filterBy: n,
@@ -16437,17 +16438,17 @@ function y_(e) {
sortingOptions: o
}), [t, r, n, i, s, o]);
}
-function b_() {
- const { moveUp: e, moveDown: t, select: r } = il();
- return ze(() => ({
+function I_() {
+ const { moveUp: e, moveDown: t, select: r } = hl();
+ return Ke(() => ({
moveUp: e,
moveDown: t,
select: r
}), [e, t, r]);
}
-const k_ = () => {
- const e = b_(), [t] = ce();
- z(() => {
+const D_ = () => {
+ const e = I_(), [t] = ae();
+ F(() => {
const r = (n) => {
const s = {
ArrowDown: () => e?.moveDown(),
@@ -16457,43 +16458,43 @@ const k_ = () => {
}[n.key];
return s ? (s(), n.preventDefault(), n.stopPropagation(), !0) : !1;
};
- return t.registerCommand(vr, r, Ne);
+ return t.registerCommand(Nr, r, Oe);
}, [t, e]);
};
-function T_() {
- return k_(), null;
+function U_() {
+ return D_(), null;
}
-const x_ = ["Shift", "Control", "Alt", "Meta"];
-function vh(e) {
- const { options: t, onSelectOption: r, onClose: n, inverse: i, query: s, menuOpenKey: o, onFilterChange: a, passthroughKeys: c } = e, [l] = ce(), u = s !== void 0, [d, f] = de(""), p = u ? s ?? "" : d, m = y_({ query: p, items: t, filterBy: "name" }), g = (y) => {
- n?.(), r ? r(y) : y.action(l);
+const F_ = ["Shift", "Control", "Alt", "Meta"];
+function Fh(e) {
+ const { options: t, onSelectOption: r, onClose: n, inverse: i, query: s, menuOpenKey: o, onFilterChange: a, passthroughKeys: c } = e, [l] = ae(), d = s !== void 0, [u, f] = de(""), p = d ? s ?? "" : u, y = L_({ query: p, items: t, filterBy: "name" }), h = (m) => {
+ n?.(), r ? r(m) : m.action(l);
};
- return z(() => {
- a?.(p, m);
- }, [a, p, m]), z(() => l.registerCommand(vr, (y) => {
- if (u || c?.includes(y.key) || x_.includes(y.key))
+ return F(() => {
+ a?.(p, y);
+ }, [a, p, y]), F(() => l.registerCommand(Nr, (m) => {
+ if (d || c?.includes(m.key) || F_.includes(m.key))
return !1;
- if ((y.ctrlKey || y.metaKey || y.altKey) && !y.getModifierState("AltGraph"))
+ if ((m.ctrlKey || m.metaKey || m.altKey) && !m.getModifierState("AltGraph"))
return n?.(), !1;
- const _ = {
+ const x = {
Escape: () => n?.(),
Backspace: () => {
- p.length === 0 ? n?.() : f((S) => S.slice(0, -1));
+ p.length === 0 ? n?.() : f((_) => _.slice(0, -1));
}
- }[y.key];
- return _ ? (y.stopPropagation(), y.preventDefault(), _(), !0) : y.key.length === 1 ? (y.stopPropagation(), y.preventDefault(), y.key !== o && f((S) => S + y.key), !0) : !1;
- }, Ne), [l, u, p, o, n, c]), Te(ia.Root, { className: `autocomplete-menu-container ${i ? "inverse" : ""}`, menuItems: m, onSelectOption: (y) => g(y), children: [!u && C("input", { value: p, type: "text", disabled: !0 }), C(T_, {}), C(ia.Options, { className: "autocomplete-menu-options", autoIndex: !1, children: (y) => y.map((_, S) => Te(ia.Option, { index: S, children: [C("span", { className: "label", children: _.label ?? _.name }), C("span", { className: "description", children: _.description })] }, _.name)) })] });
+ }[m.key];
+ return x ? (m.stopPropagation(), m.preventDefault(), x(), !0) : m.key.length === 1 ? (m.stopPropagation(), m.preventDefault(), m.key !== o && f((_) => _ + m.key), !0) : !1;
+ }, Oe), [l, d, p, o, n, c]), Te(aa.Root, { className: `autocomplete-menu-container ${i ? "inverse" : ""}`, menuItems: y, onSelectOption: (m) => h(m), children: [!d && S("input", { value: p, type: "text", disabled: !0 }), S(U_, {}), S(aa.Options, { className: "autocomplete-menu-options", autoIndex: !1, children: (m) => m.map((x, _) => Te(aa.Option, { index: _, children: [S("span", { className: "label", children: x.label ?? x.name }), S("span", { className: "description", children: x.description })] }, x.name)) })] });
}
-function __({ trigger: e, items: t }) {
- const [r] = ce(), [n, i] = de(!1), s = he((o) => {
+function z_({ trigger: e, items: t }) {
+ const [r] = ae(), [n, i] = de(!1), s = me((o) => {
o.key === "Escape" && n ? (i(!1), r.focus()) : o.key === e && !n && (o.preventDefault(), i(!0));
}, [r, e, n]);
- return z(() => r.registerRootListener((o) => {
+ return F(() => r.registerRootListener((o) => {
if (o)
return o.addEventListener("keydown", s), () => {
o.removeEventListener("keydown", s);
};
- }), [r, s]), z(() => r.registerUpdateListener(({ prevEditorState: o, editorState: a }) => {
+ }), [r, s]), F(() => r.registerUpdateListener(({ prevEditorState: o, editorState: a }) => {
const c = o.read(() => {
const l = w();
if (N(l))
@@ -16503,10 +16504,10 @@ function __({ trigger: e, items: t }) {
const l = w();
!N(l) || c?.is(l) || i(!1);
});
- }), [r]), t && C(_h, { isOpen: n, children: ({ placement: o }) => C(vh, { options: t, onClose: () => i(!1), inverse: o === "top-start", menuOpenKey: e }) });
+ }), [r]), t && S(Ih, { isOpen: n, children: ({ placement: o }) => S(Fh, { options: t, onClose: () => i(!1), inverse: o === "top-start", menuOpenKey: e }) });
}
-function C_({ scriptureReference: e, contextMarker: t, getMarkerAction: r }) {
- return { markersMenuItems: ze(() => {
+function K_({ scriptureReference: e, contextMarker: t, getMarkerAction: r }) {
+ return { markersMenuItems: Ke(() => {
if (!t || !e)
return;
const i = lr(t);
@@ -16524,132 +16525,132 @@ function C_({ scriptureReference: e, contextMarker: t, getMarkerAction: r }) {
}));
}, [t, r, e]) };
}
-function Fi(e, t) {
+function Vi(e, t) {
return `${e}:${t}`;
}
-function S_(e, t) {
- z(() => {
- if (!e.hasNodes([Qe]))
+function j_(e, t) {
+ F(() => {
+ if (!e.hasNodes([tt]))
throw new Error("AnnotationPlugin: TypedMarkNode not registered on editor!");
const r = /* @__PURE__ */ new Map();
- return Ve(Pf(e, Qe, (n) => Hi(n.getTypedIDs(), n.getTypedOnClicks(), n.getTypedOnRemoves(), n.getTypedOnMouseEnters(), n.getTypedOnMouseLeaves()), (n, i) => {
+ return Be(Vf(e, tt, (n) => Qi(n.getTypedIDs(), n.getTypedOnClicks(), n.getTypedOnRemoves(), n.getTypedOnMouseEnters(), n.getTypedOnMouseLeaves()), (n, i) => {
const s = n.getTypedOnClicks(), o = n.getTypedOnRemoves(), a = n.getTypedOnMouseEnters(), c = n.getTypedOnMouseLeaves();
- for (const [l, u] of Object.entries(n.getTypedIDs()))
- u.forEach((d) => {
- const f = s[l]?.[d], p = o[l]?.[d], m = a[l]?.[d], g = c[l]?.[d];
- i.addID(l, d, f, p, m, g);
+ for (const [l, d] of Object.entries(n.getTypedIDs()))
+ d.forEach((u) => {
+ const f = s[l]?.[u], p = o[l]?.[u], y = a[l]?.[u], h = c[l]?.[u];
+ i.addID(l, u, f, p, y, h);
});
n.getWritable().__suppressOnRemoveCallbacks = !0;
- }), e.registerMutationListener(Qe, (n) => {
+ }), e.registerMutationListener(tt, (n) => {
e.getEditorState().read(() => {
for (const [i, s] of n) {
const o = se(i);
let a = {};
- s === "destroyed" ? a = r.get(i) ?? {} : Ce(o) && (a = o.getTypedIDs());
+ s === "destroyed" ? a = r.get(i) ?? {} : _e(o) && (a = o.getTypedIDs());
for (const [c, l] of Object.entries(a))
- if (!Qe.isReservedType(c))
- for (const u of l) {
- let d = t.get(Fi(c, u));
- a[c] = l, r.set(i, a), s === "destroyed" ? d !== void 0 && (d.delete(i), d.size === 0 && t.delete(Fi(c, u))) : (d === void 0 && (d = /* @__PURE__ */ new Set(), t.set(Fi(c, u), d)), d.has(i) || d.add(i));
+ if (!tt.isReservedType(c))
+ for (const d of l) {
+ let u = t.get(Vi(c, d));
+ a[c] = l, r.set(i, a), s === "destroyed" ? u !== void 0 && (u.delete(i), u.size === 0 && t.delete(Vi(c, d))) : (u === void 0 && (u = /* @__PURE__ */ new Set(), t.set(Vi(c, d), u)), u.has(i) || u.add(i));
}
}
});
}, { skipInitialization: !0 }));
}, [e, t]);
}
-const v_ = En(function({ logger: t }, r) {
- const [n] = ce(), i = ze(() => /* @__PURE__ */ new Map(), []);
- S_(n, i);
+const B_ = An(function({ logger: t }, r) {
+ const [n] = ae(), i = Ke(() => /* @__PURE__ */ new Map(), []);
+ j_(n, i);
const s = (o, a, c) => {
- const l = Array.from(c ?? i.get(Fi(o, a)) ?? []);
+ const l = Array.from(c ?? i.get(Vi(o, a)) ?? []);
if (l.length !== 0)
- for (const u of l) {
- const d = se(u);
- Ce(d) && (d.deleteID(o, a), d.hasNoIDsForEveryType() && Us(d));
+ for (const d of l) {
+ const u = se(d);
+ _e(u) && (u.deleteID(o, a), u.hasNoIDsForEveryType() && js(u));
}
};
- return gc(r, () => ({
- setAnnotation(o, a, c, l, u, d, f) {
- if (Qe.isReservedType(a))
+ return Sc(r, () => ({
+ setAnnotation(o, a, c, l, d, u, f) {
+ if (tt.isReservedType(a))
throw new Error(`setAnnotation: Can't directly set this reserved annotation type '${a}'. Use the appropriate plugin instead.`);
n.update(() => {
- const p = Mo(o);
+ const p = No(o);
if (p === void 0) {
t?.error("Failed to find start or end node of the annotation.");
return;
}
- s(a, c), Zf(p, a, c, l, u, d, f);
- }, { tag: Sa });
+ s(a, c), gp(p, a, c, l, d, u, f);
+ }, { tag: Pa });
},
removeAnnotation(o, a) {
- if (Qe.isReservedType(o))
+ if (tt.isReservedType(o))
throw new Error(`removeAnnotation: Can't directly remove this reserved annotation type '${o}'. Use the appropriate plugin instead.`);
- const c = i.get(Fi(o, a));
+ const c = i.get(Vi(o, a));
c === void 0 || c.size === 0 || n.update(() => {
s(o, a, c);
- }, { tag: Sa });
+ }, { tag: Pa });
}
})), null;
-}), M_ = [];
-function E_({ ignoreHistoryMergeTagChange: e = !0, ignoreSelectionChange: t = !1, ignoreTags: r = M_, onChange: n }) {
- const [i] = ce();
- return cs(() => {
+}), V_ = [];
+function W_({ ignoreHistoryMergeTagChange: e = !0, ignoreSelectionChange: t = !1, ignoreTags: r = V_, onChange: n }) {
+ const [i] = ae();
+ return ps(() => {
if (n)
return i.registerUpdateListener((s) => {
- const { editorState: o, dirtyElements: a, dirtyLeaves: c, prevEditorState: l, tags: u } = s;
+ const { editorState: o, dirtyElements: a, dirtyLeaves: c, prevEditorState: l, tags: d } = s;
if (t && a.size === 0 && c.size === 0 || // A `MARKER_SETTLE_TAG` commit carries the merge tag only to stay out of the undo
// stack — its bytes really did change, so it must reach `onChange` like any edit.
// Without this exemption the cached USJ and the emitted delta both keep showing the
// pre-settle bytes, and the host saves a document the editor is no longer displaying.
- e && u.has(kf) && !u.has(Ff) || r.some((f) => u.has(f)) || l.isEmpty())
+ e && d.has(qf) && !d.has(rp) || r.some((f) => d.has(f)) || l.isEmpty())
return;
- const d = A_(i, s);
- d.length !== 0 && n(o, i, u, d);
+ const u = H_(i, s);
+ u.length !== 0 && n(o, i, d, u);
});
}, [i, e, t, r, n]), null;
}
-function A_(e, { dirtyLeaves: t, prevEditorState: r }) {
- let n = new qi();
+function H_(e, { dirtyLeaves: t, prevEditorState: r }) {
+ let n = new Di();
return e.getEditorState().read(() => {
- const i = t.values().next().value ?? "", s = se(i), o = s !== null && Xt(s) !== void 0;
- if (t.size === 1 && v(s) && !o && Ex(s)) {
- const a = hh(s);
+ const i = t.values().next().value ?? "", s = se(i), o = s !== null && Yt(s) !== void 0;
+ if (t.size === 1 && M(s) && !o && Wx(s)) {
+ const a = Ph(s);
if (a !== void 0) {
const c = r.read(() => {
- const d = se(i);
- return new qi([v(d) ? za(d) : { insert: "" }]);
- }), l = new qi([za(s)]), u = new qi(a > 0 ? [{ retain: a }] : []);
- n = n.concat(u).concat(c.diff(l));
+ const u = se(i);
+ return new Di([M(u) ? Wa(u) : { insert: "" }]);
+ }), l = new Di([Wa(s)]), d = new Di(a > 0 ? [{ retain: a }] : []);
+ n = n.concat(d).concat(c.diff(l));
}
} else {
- const a = Bu(r), c = Bu(e.getEditorState());
+ const a = rd(r), c = rd(e.getEditorState());
n = a.diff(c);
}
}), n.ops;
}
-const sl = "formatted", Mh = "unformatted", Eh = "paragraph-structure", ol = "standard", Ah = "block-verse", P_ = {
- [sl]: "Formatted",
- [Mh]: "Unformatted",
- [Eh]: "Paragraph Structure",
- [ol]: "Standard",
- [Ah]: "Block Verse"
+const gl = "formatted", zh = "unformatted", Kh = "paragraph-structure", ml = "standard", jh = "block-verse", G_ = {
+ [gl]: "Formatted",
+ [zh]: "Unformatted",
+ [Kh]: "Paragraph Structure",
+ [ml]: "Standard",
+ [jh]: "Block Verse"
};
-function gi(e) {
+function ki(e) {
return e?.showParaMarkerPrefixes !== !1;
}
-let al, cl;
-function N_(e) {
- const t = ll(e);
+let yl, bl;
+function J_(e) {
+ const t = kl(e);
if (!t)
throw new Error(`Invalid view mode: ${e}`);
- al = e, cl = t;
+ yl = e, bl = t;
}
-N_(sl);
-const MP = () => al, Eo = () => cl;
-function ll(e) {
+J_(gl);
+const oN = () => yl, Oo = () => bl;
+function kl(e) {
let t;
- switch (e ?? al) {
- case sl:
+ switch (e ?? yl) {
+ case gl:
t = {
markerMode: "hidden",
noteMode: "collapsed",
@@ -16657,7 +16658,7 @@ function ll(e) {
isFormattedFont: !0
};
break;
- case Mh:
+ case zh:
t = {
markerMode: "editable",
noteMode: "expanded",
@@ -16665,7 +16666,7 @@ function ll(e) {
isFormattedFont: !1
};
break;
- case Eh:
+ case Kh:
t = {
markerMode: "hidden",
noteMode: "collapsed",
@@ -16675,7 +16676,7 @@ function ll(e) {
hasActiveTextFocusBox: !0
};
break;
- case ol:
+ case ml:
t = {
markerMode: "editable",
noteMode: "collapsed",
@@ -16683,7 +16684,7 @@ function ll(e) {
isFormattedFont: !0
};
break;
- case Ah:
+ case jh:
t = {
markerMode: "hidden",
noteMode: "collapsed",
@@ -16695,151 +16696,151 @@ function ll(e) {
}
return t;
}
-function EP(e) {
+function aN(e) {
if (!e)
return;
- const t = Qu(e);
- return Object.keys(P_).find((r) => wt(Qu(ll(r)), t));
+ const t = ud(e);
+ return Object.keys(G_).find((r) => qt(ud(kl(r)), t));
}
-const O_ = {
+const Y_ = {
showCharMarkerTitles: !0,
hasGutterParaMarkers: !1,
hasActiveTextFocusBox: !1,
verseLayout: "inline"
};
-function Qu(e) {
+function ud(e) {
if (!e)
return e;
const t = Object.fromEntries(Object.entries(e).filter(([, r]) => r !== void 0));
- return { ...O_, ...t };
+ return { ...Y_, ...t };
}
-function Ao(e) {
+function Ts(e) {
if (!e)
return !1;
const { markerMode: t, hasSpacing: r, isFormattedFont: n, hasGutterParaMarkers: i, hasActiveTextFocusBox: s } = e;
return t === "editable" && r && n && !i && !s;
}
-function w_(e) {
+function X_(e) {
if (e)
- return ns(e) ? Ct : e.markerMode === "editable" ? dt : Ct;
+ return cs(e) ? St : e.markerMode === "editable" ? dt : St;
}
-function ns(e) {
+function cs(e) {
return e?.verseLayout === "block";
}
-function q_(e) {
- const t = [], r = e ?? cl;
- return r && (t.push(`${lb}${r.markerMode}`), r.hasSpacing && t.push(ab), r.isFormattedFont && t.push(cb)), t;
+function Q_(e) {
+ const t = [], r = e ?? bl;
+ return r && (t.push(`${Ab}${r.markerMode}`), r.hasSpacing && t.push(Mb), r.isFormattedFont && t.push(Eb)), t;
}
-function R_(e, t, r, n) {
+function Z_(e, t, r, n) {
let i = 0;
e.forEach((s) => {
if ("retain" in s)
- i += $_(s, i, t, n);
+ i += eC(s, i, t, n);
else if ("delete" in s) {
if (typeof s.delete != "number" || s.delete <= 0) {
n?.error(`Invalid delete operation: ${JSON.stringify(s)}`);
return;
}
- n?.debug(`Delete: ${s.delete}`), L_(i, s.delete, n);
- } else "insert" in s ? typeof s.insert == "string" ? (n?.debug(`Insert: '${s.insert}'`), i += D_(i, s.insert, s.attributes, t, n)) : typeof s.insert == "object" && s.insert !== null ? (n?.debug(`Insert embed: ${JSON.stringify(s.insert)}`), F_(i, s, t, r, n) ? i += 1 : n?.error(`Failed to process insert embed operation: ${JSON.stringify(s.insert)} at index ${i}. Document may be inconsistent.`)) : n?.error(`Insert of unknown type: ${JSON.stringify(s.insert)}`) : n?.error(`Unknown operation: ${JSON.stringify(s)}`);
+ n?.debug(`Delete: ${s.delete}`), rC(i, s.delete, n);
+ } else "insert" in s ? typeof s.insert == "string" ? (n?.debug(`Insert: '${s.insert}'`), i += nC(i, s.insert, s.attributes, t, n)) : typeof s.insert == "object" && s.insert !== null ? (n?.debug(`Insert embed: ${JSON.stringify(s.insert)}`), sC(i, s, t, r, n) ? i += 1 : n?.error(`Failed to process insert embed operation: ${JSON.stringify(s.insert)} at index ${i}. Document may be inconsistent.`)) : n?.error(`Insert of unknown type: ${JSON.stringify(s.insert)}`) : n?.error(`Unknown operation: ${JSON.stringify(s)}`);
});
}
-function $_(e, t, r, n) {
- return typeof e.retain != "number" || e.retain < 0 ? (n?.error(`Invalid retain operation: ${JSON.stringify(e)}`), 0) : (n?.debug(`Retain: ${e.retain}`), e.attributes && (n?.debug(`Retain attributes: ${JSON.stringify(e.attributes)}`), I_(t, e.retain, e.attributes, r, n)), e.retain);
+function eC(e, t, r, n) {
+ return typeof e.retain != "number" || e.retain < 0 ? (n?.error(`Invalid retain operation: ${JSON.stringify(e)}`), 0) : (n?.debug(`Retain: ${e.retain}`), e.attributes && (n?.debug(`Retain attributes: ${JSON.stringify(e.attributes)}`), tC(t, e.retain, e.attributes, r, n)), e.retain);
}
-function I_(e, t, r, n, i) {
+function tC(e, t, r, n, i) {
i?.debug(`Applying attributes for range [${e}, ${e + t - 1}] with attributes: ${JSON.stringify(r)}`);
let s = t, o = 0, a = -1;
- const c = Fe();
- function l(u) {
+ const c = ze();
+ function l(d) {
if (s <= 0)
return !0;
- if (_r(u)) {
- const d = u.getTextContentSize();
- if (e < o + d && o < e + t) {
- const f = Math.max(0, e - o), p = d - f, m = Math.min(s, p);
- if (m > 0) {
- let g = u;
- const y = f > 0, k = m < d - f;
- if (y && k) {
- const [, _] = u.splitText(f);
- [g] = _.splitText(m);
- } else y ? [, g] = u.splitText(f) : k && ([g] = u.splitText(m));
- if (Hr(r)) {
- const _ = g.getParent();
- if (D(_)) {
- const S = r.char;
- let P;
- Array.isArray(S) ? a >= 0 && a <= S.length - 1 && (P = S[a]) : a === 0 && (P = S);
- const A = P ? Tn(P, _) : !1;
- if (A && Array.isArray(S) && S.length > 1) {
- const B = pe("");
- g.replace(B);
- const M = typeof r.segment == "string" ? r.segment : void 0, q = mi(S.slice(1), n, g, M);
- let $ = B;
- for (const J of q)
- $.insertAfter(J), $ = J;
- B.remove(), qt(r, g);
- } else if (A)
- qt(r, g);
+ if (Er(d)) {
+ const u = d.getTextContentSize();
+ if (e < o + u && o < e + t) {
+ const f = Math.max(0, e - o), p = u - f, y = Math.min(s, p);
+ if (y > 0) {
+ let h = d;
+ const m = f > 0, b = y < u - f;
+ if (m && b) {
+ const [, x] = d.splitText(f);
+ [h] = x.splitText(y);
+ } else m ? [, h] = d.splitText(f) : b && ([h] = d.splitText(y));
+ if (Yr(r)) {
+ const x = h.getParent();
+ if (I(x)) {
+ const _ = r.char;
+ let A;
+ Array.isArray(_) ? a >= 0 && a <= _.length - 1 && (A = _[a]) : a === 0 && (A = _);
+ const E = A ? xn(A, x) : !1;
+ if (E && Array.isArray(_) && _.length > 1) {
+ const D = ge("");
+ h.replace(D);
+ const V = typeof r.segment == "string" ? r.segment : void 0, j = Ti(_.slice(1), n, h, V);
+ let v = D;
+ for (const K of j)
+ v.insertAfter(K), v = K;
+ D.remove(), Rt(r, h);
+ } else if (E)
+ Rt(r, h);
else {
- g.remove();
- const B = Zu(g, r, n, i);
- if (B && B.length > 0) {
- let M = _;
- for (const q of B)
- M.insertAfter(q), M = q;
+ h.remove();
+ const D = dd(h, r, n, i);
+ if (D && D.length > 0) {
+ let V = x;
+ for (const j of D)
+ V.insertAfter(j), V = j;
}
}
} else {
- const S = pe("");
- g.replace(S);
- const P = Zu(g, r, n, i);
- if (P && P.length > 0) {
- let A = S;
- for (const B of P)
- A.insertAfter(B), A = B;
- S.remove();
+ const _ = ge("");
+ h.replace(_);
+ const A = dd(h, r, n, i);
+ if (A && A.length > 0) {
+ let E = _;
+ for (const D of A)
+ E.insertAfter(D), E = D;
+ _.remove();
} else
- S.replace(g);
+ _.replace(h);
}
} else
- qt(r, g);
- s -= m;
+ Rt(r, h);
+ s -= y;
}
}
- o += d;
- } else if (Pt(u))
- e <= o && o < e + t && s > 0 && (ed(u, r), s -= 1), o += 1;
- else if (D(u)) {
+ o += u;
+ } else if (Nt(d))
+ e <= o && o < e + t && s > 0 && (fd(d, r), s -= 1), o += 1;
+ else if (I(d)) {
a += 1;
- let d = !1;
+ let u = !1;
if (e <= o && o < e + t && s > 0)
- if (Hr(r)) {
+ if (Yr(r)) {
const f = r.char;
let p;
if (Array.isArray(f) ? a >= 0 && a <= f.length - 1 && (p = f[a]) : a === 0 && (p = f), p) {
- Ka(u, p.style), typeof p.cid == "string" && bt(u, kn, () => p.cid);
- const m = Ue(p, Hs);
- m && Object.keys(m).length > 0 ? u.setUnknownAttributes({
- ...u.getUnknownAttributes() ?? {},
- ...m
- }) : u.setUnknownAttributes(void 0);
+ Ha(d, p.style), typeof p.cid == "string" && kt(d, Tn, () => p.cid);
+ const y = Fe(p, Xs);
+ y && Object.keys(y).length > 0 ? d.setUnknownAttributes({
+ ...d.getUnknownAttributes() ?? {},
+ ...y
+ }) : d.setUnknownAttributes(void 0);
}
- } else (r.char === !1 || r.char === null || J_(r.char)) && (d = !0);
+ } else (r.char === !1 || r.char === null || hC(r.char)) && (u = !0);
if (s > 0) {
- const f = u.getChildren();
+ const f = d.getChildren();
for (const p of f) {
if (s <= 0)
break;
if (l(p) && s <= 0)
- return d && _a(u), !0;
+ return u && Ea(d), !0;
}
}
- d && _a(u), a -= 1;
- } else if (Et(u)) {
- const d = u.getChildren();
- for (const p of d) {
+ u && Ea(d), a -= 1;
+ } else if (At(d)) {
+ const u = d.getChildren();
+ for (const p of u) {
if (s <= 0)
break;
if (l(p) && s <= 0)
@@ -16847,18 +16848,18 @@ function I_(e, t, r, n, i) {
}
const f = 1;
if (e <= o && o < e + s && s > 0) {
- if (!dr(u))
- ed(u, r);
- else if (ul(r)) {
- const p = Oh(r.para, n);
- p && u.replace(p, !0);
+ if (!dr(d))
+ fd(d, r);
+ else if (Tl(r)) {
+ const p = Wh(r.para, n);
+ p && d.replace(p, !0);
}
s -= f;
}
o += f;
- } else if (F(u)) {
- const d = u.getChildren();
- for (const f of d) {
+ } else if (L(d)) {
+ const u = d.getChildren();
+ for (const f of u) {
if (s <= 0)
break;
if (l(f) && s <= 0)
@@ -16869,124 +16870,124 @@ function I_(e, t, r, n, i) {
}
l(c), s > 0 && i?.warn(`$applyAttributes: Not all characters in the retain operation (length ${t}) could be processed. Remaining: ${s}. targetIndex: ${e}, final currentIndex: ${o}`);
}
-function Zu(e, t, r, n) {
- const i = typeof t.segment == "string" ? t.segment : void 0, s = mi(t.char, r, e, i), o = s.find(D);
+function dd(e, t, r, n) {
+ const i = typeof t.segment == "string" ? t.segment : void 0, s = Ti(t.char, r, e, i), o = s.find(I);
if (!o) {
- n?.error(`Failed to create CharNode for text transformation. Style: ${Array.isArray(t.char) ? t.char[0].style : t.char?.style}. Falling back to standard text attributes.`), qt(t, e);
+ n?.error(`Failed to create CharNode for text transformation. Style: ${Array.isArray(t.char) ? t.char[0].style : t.char?.style}. Falling back to standard text attributes.`), Rt(t, e);
return;
}
const a = {};
- $h.forEach((u) => {
- e.hasFormat(u) && (a[u] = "true");
+ Yh.forEach((d) => {
+ e.hasFormat(d) && (a[d] = "true");
});
const c = {};
- Object.entries(t).forEach(([u, d]) => {
- u === "segment" || u === "char" || (typeof d == "string" ? c[u] = d : d === !0 ? c[u] = "true" : d === !1 && (c[u] = "false"));
+ Object.entries(t).forEach(([d, u]) => {
+ d === "segment" || d === "char" || (typeof u == "string" ? c[d] = u : u === !0 ? c[d] = "true" : u === !1 && (c[d] = "false"));
});
const l = {
...o.getUnknownAttributes() ?? {},
...a,
...c
};
- return Object.keys(l).length > 0 && o.setUnknownAttributes(l), qt(t, e), s;
+ return Object.keys(l).length > 0 && o.setUnknownAttributes(l), Rt(t, e), s;
}
-function Ph(e, t) {
+function Bh(e, t) {
e.setMarker(t);
const r = e.getFirstChild();
- O(r) ? (r.setMarker(t), r.setTextContent(we(t))) : Yt(r) && r.getTextType() === "marker" && r.setTextContent(we(t) + I);
+ O(r) ? (r.setMarker(t), r.setTextContent(qe(t))) : Jt(r) && r.getTextType() === "marker" && r.setTextContent(qe(t) + R);
}
-function Ka(e, t) {
+function Ha(e, t) {
const r = e.getMarker();
if (e.setMarker(t), t === r)
return;
e.getChildren().forEach((o) => {
O(o) && o.getMarker() === r && o.setMarker(t);
});
- const n = D(e.getParent()), i = e.getFirstChild();
- Yt(i) && i.getTextType() === "marker" && i.getTextContent() === we(r, n) && i.setTextContent(we(t, n));
+ const n = I(e.getParent()), i = e.getFirstChild();
+ Jt(i) && i.getTextType() === "marker" && i.getTextContent() === qe(r, n) && i.setTextContent(qe(t, n));
const s = e.getLastChild();
- Yt(s) && s.getTextType() === "marker" && s.getTextContent() === rt(r, n) && s.setTextContent(rt(t, n));
+ Jt(s) && s.getTextType() === "marker" && s.getTextContent() === st(r, n) && s.setTextContent(st(t, n));
}
-function ed(e, t) {
+function fd(e, t) {
for (const r of Object.keys(t)) {
const n = t[r];
- if (r === "char" && D(e) && Hr(t)) {
- const i = ja(n);
- if (Ka(e, i.style), typeof i.cid == "string") {
+ if (r === "char" && I(e) && Yr(t)) {
+ const i = Ga(n);
+ if (Ha(e, i.style), typeof i.cid == "string") {
const o = i.cid;
- bt(e, kn, () => o);
+ kt(e, Tn, () => o);
}
- const s = Ue(i, Hs);
+ const s = Fe(i, Xs);
s && Object.keys(s).length > 0 && e.setUnknownAttributes({
...e.getUnknownAttributes() ?? {},
...s
});
continue;
}
- typeof n == "string" && (Ge(e) || ge(e) || Be(e) || j(e) || De(e) ? e.setUnknownAttributes({
+ typeof n == "string" && (Ge(e) || he(e) || He(e) || z(e) || Ue(e) ? e.setUnknownAttributes({
...e.getUnknownAttributes() ?? {},
[r]: n
- }) : (pt(e) || ae(e) || D(e)) && (r === "style" && ae(e) ? Ph(e, n) : r === "style" && D(e) ? Ka(e, n) : r === "code" && pt(e) ? e.setCode(n) : e.setUnknownAttributes({
+ }) : (pt(e) || ce(e) || I(e)) && (r === "style" && ce(e) ? Bh(e, n) : r === "style" && I(e) ? Ha(e, n) : r === "code" && pt(e) ? e.setCode(n) : e.setUnknownAttributes({
...e.getUnknownAttributes() ?? {},
[r]: n
- })), r === "segment" && bt(e, Vr, () => n));
+ })), r === "segment" && kt(e, Gr, () => n));
}
}
-function L_(e, t, r) {
+function rC(e, t, r) {
if (t <= 0)
return;
- const n = Fe();
+ const n = ze();
let i = 0, s = t;
function o(a) {
if (s <= 0)
return !0;
- if (_r(a)) {
+ if (Er(a)) {
let c = a.getTextContentSize();
if (e < i + c && i < e + s) {
- const l = Math.max(0, e - i), u = c - l, d = Math.min(s, u);
- d > 0 && (a.spliceText(l, d, ""), a.getTextContentSize() === 0 && a.remove(), r?.debug(`Deleted ${d} length from TextNode (key: ${a.getKey()}) at nodeOffset ${l}. Original targetIndex: ${e}, current currentIndex: ${i}.`), s -= d, c -= d);
+ const l = Math.max(0, e - i), d = c - l, u = Math.min(s, d);
+ u > 0 && (a.spliceText(l, u, ""), a.getTextContentSize() === 0 && a.remove(), r?.debug(`Deleted ${u} length from TextNode (key: ${a.getKey()}) at nodeOffset ${l}. Original targetIndex: ${e}, current currentIndex: ${i}.`), s -= u, c -= u);
}
i += c;
- } else if (Pt(a))
+ } else if (Nt(a))
e <= i && i < e + s ? (a.remove(), r?.debug(`Deleted embed node (key: ${a.getKey()}) at currentIndex: ${i}. Original targetIndex: ${e}, remainingToDelete: ${s}.`), s -= 1) : i += 1;
- else if (Et(a)) {
+ else if (At(a)) {
const c = a.getChildren().slice(), l = a.getChildren();
- for (const u of l) {
+ for (const d of l) {
if (s <= 0)
break;
- if (o(u) && s <= 0)
+ if (o(d) && s <= 0)
return !0;
}
- if (e <= i && i < e + s && Et(a)) {
+ if (e <= i && i < e + s && At(a)) {
s -= 1;
- const u = a.getChildren().length;
- if (c.length > 0 && u === 0)
- (a.getParent()?.getChildren() ?? []).length > 1 ? (a.remove(), r?.debug(`Removed entire ParaNode that had all its content deleted at currentIndex: ${i}. Original targetIndex: ${e}, remainingToDelete: ${s}.`)) : (a.replace(Ht(), !0), r?.debug(`Replaced last ParaNode with ImpliedParaNode at currentIndex: ${i}. Original targetIndex: ${e}, remainingToDelete: ${s}.`));
+ const d = a.getChildren().length;
+ if (c.length > 0 && d === 0)
+ (a.getParent()?.getChildren() ?? []).length > 1 ? (a.remove(), r?.debug(`Removed entire ParaNode that had all its content deleted at currentIndex: ${i}. Original targetIndex: ${e}, remainingToDelete: ${s}.`)) : (a.replace(Wt(), !0), r?.debug(`Replaced last ParaNode with ImpliedParaNode at currentIndex: ${i}. Original targetIndex: ${e}, remainingToDelete: ${s}.`));
else if (s > 0) {
const p = a.getNextSibling();
- if (p && Se(p)) {
- let m = i + 1;
- const g = p.getChildren();
- for (const k of g) {
+ if (p && Ce(p)) {
+ let y = i + 1;
+ const h = p.getChildren();
+ for (const b of h) {
if (s <= 0)
break;
- const _ = i;
- if (i = m, o(k)) {
- i = _;
+ const x = i;
+ if (i = y, o(b)) {
+ i = x;
break;
}
- _r(k) ? m += k.getTextContentSize() : Pt(k) && (m += 1), i = _;
+ Er(b) ? y += b.getTextContentSize() : Nt(b) && (y += 1), i = x;
}
- const y = p.getChildren();
- for (const k of y)
- k.remove(), a.append(k);
+ const m = p.getChildren();
+ for (const b of m)
+ b.remove(), a.append(b);
p.remove(), r?.debug(`Merged next paragraph into current one after deleting symbolic close at currentIndex: ${i}. Original targetIndex: ${e}, remainingToDelete: ${s}.`);
} else
- a.replace(Ht(), !0);
- } else ae(a) ? a.replace(Ht(), !0) : a.remove();
+ a.replace(Wt(), !0);
+ } else ce(a) ? a.replace(Wt(), !0) : a.remove();
}
i += 1;
- } else if (F(a)) {
+ } else if (L(a)) {
const c = a.getChildren();
for (const l of c) {
if (s <= 0)
@@ -16999,146 +17000,146 @@ function L_(e, t, r) {
}
o(n), s > 0 && r?.warn(`Delete operation could not remove all requested characters. Remaining to delete: ${s}. Original targetIndex: ${e}, OT length: ${t}. Final currentIndex: ${i}`);
}
-function D_(e, t, r, n, i) {
- if (t === rs)
- return td(e, r, n, i);
- if (t.endsWith(rs) && !ul(r)) {
+function nC(e, t, r, n, i) {
+ if (t === as)
+ return pd(e, r, n, i);
+ if (t.endsWith(as) && !Tl(r)) {
const s = t.slice(0, -1);
let o = 0;
if (s.length > 0) {
- if (Hr(r))
+ if (Yr(r))
throw new Error("Text + LF should not have char attributes");
- o += Js(e, s, r, i);
+ o += Zs(e, s, r, i);
}
- return o += td(e + o, r, n, i), o;
- } else return Hr(r) ? U_(e, t, r, n, i) : Js(e, t, r, i);
+ return o += pd(e + o, r, n, i), o;
+ } else return Yr(r) ? iC(e, t, r, n, i) : Zs(e, t, r, i);
}
-function U_(e, t, r, n, i) {
+function iC(e, t, r, n, i) {
i?.debug(`Attempting to insert CharNode with text "${t}" and attributes ${JSON.stringify(r.char)} at index ${e}`);
- const s = pe(t === "" ? Dt : t);
- qt(r, s);
+ const s = ge(t === "" ? Ut : t);
+ Rt(r, s);
let o;
{
- let y = function(k) {
- if (_r(k)) {
- const _ = k.getTextContentSize();
- if (e >= g && e < g + _) {
- const S = k.getParent();
- return D(S) && (o = S), !0;
+ let m = function(b) {
+ if (Er(b)) {
+ const x = b.getTextContentSize();
+ if (e >= h && e < h + x) {
+ const _ = b.getParent();
+ return I(_) && (o = _), !0;
}
- g += _;
- } else if (Pt(k))
- g += 1;
- else if (D(k)) {
- const _ = k.getChildren();
- for (const S of _)
- if (y(S))
+ h += x;
+ } else if (Nt(b))
+ h += 1;
+ else if (I(b)) {
+ const x = b.getChildren();
+ for (const _ of x)
+ if (m(_))
return !0;
- } else if (F(k)) {
- const _ = k.getChildren();
- for (const S of _)
- if (y(S))
+ } else if (L(b)) {
+ const x = b.getChildren();
+ for (const _ of x)
+ if (m(_))
return !0;
- Et(k) && (g += 1);
+ At(b) && (h += 1);
}
return !1;
};
- const m = Fe();
- let g = 0;
- y(m);
+ const y = ze();
+ let h = 0;
+ m(y);
}
let a = r.char;
if (Array.isArray(a)) {
if (o) {
- const m = a[0];
- m && Tn(m, o) ? (a = a.slice(1), a.length === 1 && (a = a[0])) : o = void 0;
+ const y = a[0];
+ y && xn(y, o) ? (a = a.slice(1), a.length === 1 && (a = a[0])) : o = void 0;
}
- } else o && (Tn(a, o) || (o = void 0));
- const c = typeof r.segment == "string" ? r.segment : void 0, u = mi(a, n, s, c, o ? [o] : void 0);
- if (u.length === 0)
+ } else o && (xn(a, o) || (o = void 0));
+ const c = typeof r.segment == "string" ? r.segment : void 0, d = Ti(a, n, s, c, o ? [o] : void 0);
+ if (d.length === 0)
return t.length;
- const d = u.find(D);
- if (!d)
- return i?.error(`CharNode style is missing for text "${t}". Attributes: ${JSON.stringify(r.char)}. Falling back to rich text insertion.`), Js(e, t, void 0, i);
+ const u = d.find(I);
+ if (!u)
+ return i?.error(`CharNode style is missing for text "${t}". Attributes: ${JSON.stringify(r.char)}. Falling back to rich text insertion.`), Zs(e, t, void 0, i);
const f = {};
- for (const [m, g] of Object.entries(r))
- m !== "char" && m !== "segment" && typeof g == "string" && (f[m] = g);
- Object.keys(f).length > 0 && d.setUnknownAttributes(f);
+ for (const [y, h] of Object.entries(r))
+ y !== "char" && y !== "segment" && typeof h == "string" && (f[y] = h);
+ Object.keys(f).length > 0 && u.setUnknownAttributes(f);
let p = !0;
- for (const m of u)
- if (!Nh(e, m, i)) {
+ for (const y of d)
+ if (!Vh(e, y, i)) {
p = !1;
break;
}
- return p ? t.length : (i?.error(`Failed to insert CharNode with text "${t}" at index ${e}. Falling back to rich text.`), Js(e, t, void 0, i));
+ return p ? t.length : (i?.error(`Failed to insert CharNode with text "${t}" at index ${e}. Falling back to rich text.`), Zs(e, t, void 0, i));
}
-function Js(e, t, r, n) {
+function Zs(e, t, r, n) {
if (t.length <= 0)
return n?.debug("Attempted to insert empty string. No action taken."), 0;
- const i = Fe();
+ const i = ze();
let s = 0, o = !1;
function a(c) {
if (o)
return !0;
- if (_r(c)) {
+ if (Er(c)) {
const l = c.getTextContentSize();
if (e >= s && e <= s + l) {
- const u = e - s, d = pe(t);
- if (qt(r, d), u === 0)
- c.insertBefore(d);
- else if (u === l) {
+ const d = e - s, u = ge(t);
+ if (Rt(r, u), d === 0)
+ c.insertBefore(u);
+ else if (d === l) {
const f = c.getParent();
- D(f) && !Hr(r) ? f.insertAfter(d) : c.insertAfter(d);
+ I(f) && !Yr(r) ? f.insertAfter(u) : c.insertAfter(u);
} else {
- const [, f] = c.splitText(u);
- f.insertBefore(d);
+ const [, f] = c.splitText(d);
+ f.insertBefore(u);
}
- return n?.debug(`Inserted text "${t}" in/around TextNode (key: ${c.getKey()}) at nodeOffset ${u}. Original targetIndex: ${e}, currentIndex at node start: ${s}.`), o = !0, !0;
+ return n?.debug(`Inserted text "${t}" in/around TextNode (key: ${c.getKey()}) at nodeOffset ${d}. Original targetIndex: ${e}, currentIndex at node start: ${s}.`), o = !0, !0;
}
s += l;
- } else if (Pt(c))
+ } else if (Nt(c))
s += 1;
- else if (D(c)) {
+ else if (I(c)) {
if (!o && e === s) {
- const d = pe(t);
- qt(r, d);
+ const u = ge(t);
+ Rt(r, u);
const f = c.getFirstChild();
- return f ? f.insertBefore(d) : c.append(d), n?.debug(`Inserted text "${t}" at beginning of CharNode ${c.getType()} (key: ${c.getKey()}).`), o = !0, !0;
+ return f ? f.insertBefore(u) : c.append(u), n?.debug(`Inserted text "${t}" at beginning of CharNode ${c.getType()} (key: ${c.getKey()}).`), o = !0, !0;
}
- const u = c.getChildren();
- for (const d of u) {
- if (a(d))
+ const d = c.getChildren();
+ for (const u of d) {
+ if (a(u))
return !0;
if (o)
break;
}
if (!o && e === s) {
- const d = pe(t);
- return qt(r, d), c.append(d), n?.debug(`Appended text "${t}" to end of CharNode ${c.getType()} (key: ${c.getKey()}).`), o = !0, !0;
+ const u = ge(t);
+ return Rt(r, u), c.append(u), n?.debug(`Appended text "${t}" to end of CharNode ${c.getType()} (key: ${c.getKey()}).`), o = !0, !0;
}
- } else if (Et(c)) {
+ } else if (At(c)) {
if (!o && e === s) {
- const d = pe(t);
- qt(r, d);
+ const u = ge(t);
+ Rt(r, u);
const f = c.getFirstChild();
- return f ? f.insertBefore(d) : c.append(d), n?.debug(`Inserted text "${t}" at beginning of container ${c.getType()} (key: ${c.getKey()}).`), o = !0, !0;
+ return f ? f.insertBefore(u) : c.append(u), n?.debug(`Inserted text "${t}" at beginning of container ${c.getType()} (key: ${c.getKey()}).`), o = !0, !0;
}
- const u = c.getChildren();
- for (const d of u) {
- if (a(d))
+ const d = c.getChildren();
+ for (const u of d) {
+ if (a(u))
return !0;
if (o)
break;
}
if (!o && e === s) {
- const d = pe(t);
- return qt(r, d), c.append(d), n?.debug(`Appended text "${t}" to end of container ${c.getType()} (key: ${c.getKey()}).`), o = !0, !0;
+ const u = ge(t);
+ return Rt(r, u), c.append(u), n?.debug(`Appended text "${t}" to end of container ${c.getType()} (key: ${c.getKey()}).`), o = !0, !0;
}
s += 1;
- } else if (F(c)) {
+ } else if (L(c)) {
const l = c.getChildren();
- for (const u of l) {
- if (a(u))
+ for (const d of l) {
+ if (a(d))
return !0;
if (o)
break;
@@ -17148,125 +17149,125 @@ function Js(e, t, r, n) {
}
if (a(i), !o && e === s) {
n?.debug(`Insertion point matches end of document (targetIndex: ${e}, final currentIndex: ${s}). Appending text to new ParaNode.`);
- const c = pe(t);
- qt(r, c);
- const l = Ht().append(c);
+ const c = ge(t);
+ Rt(r, c);
+ const l = Wt().append(c);
i.append(l), o = !0;
}
return o ? t.length : (n?.warn(`$insertRichText: Could not find insertion point for text "${t}" at targetIndex ${e}. Final currentIndex: ${s}. Text not inserted.`), 0);
}
-function Nh(e, t, r) {
- const n = Fe();
+function Vh(e, t, r) {
+ const n = ze();
let i = 0, s = !1;
function o(a) {
if (s)
return !0;
if (a === n && e === 0 && !n.getFirstChild())
- return t.isInline() ? (r?.debug(`$insertNodeAtCharacterOffset: Inserting inline node ${t.getType()} into empty root, wrapped in ImpliedParaNode. targetIndex: ${e}`), n.append(Ht().append(t))) : (r?.debug(`$insertNodeAtCharacterOffset: Inserting block node ${t.getType()} directly into empty root. targetIndex: ${e}`), n.append(t)), s = !0, !0;
- if (!F(a))
+ return t.isInline() ? (r?.debug(`$insertNodeAtCharacterOffset: Inserting inline node ${t.getType()} into empty root, wrapped in ImpliedParaNode. targetIndex: ${e}`), n.append(Wt().append(t))) : (r?.debug(`$insertNodeAtCharacterOffset: Inserting block node ${t.getType()} directly into empty root. targetIndex: ${e}`), n.append(t)), s = !0, !0;
+ if (!L(a))
return !1;
const c = a.getChildren();
for (const l of c) {
if (e === i && !s) {
if (a === n && t.isInline())
- if (Se(l)) {
+ if (Ce(l)) {
r?.debug(`$insertNodeAtCharacterOffset: Inserting inline node ${t.getType()} into existing ${l.getType()} at beginning. targetIndex: ${e}`);
- const u = l.getFirstChild();
- u ? u.insertBefore(t) : l.append(t);
+ const d = l.getFirstChild();
+ d ? d.insertBefore(t) : l.append(t);
} else
- r?.debug(`$insertNodeAtCharacterOffset: Inserting inline node ${t.getType()} into root before ${l.getType()}, wrapping in ImpliedParaNode. targetIndex: ${e}`), l.insertBefore(Ht().append(t));
+ r?.debug(`$insertNodeAtCharacterOffset: Inserting inline node ${t.getType()} into root before ${l.getType()}, wrapping in ImpliedParaNode. targetIndex: ${e}`), l.insertBefore(Wt().append(t));
else
l.insertBefore(t), r?.debug(`$insertNodeAtCharacterOffset: Inserted node ${t.getType()} (key: ${t.getKey()}) before child ${l.getType()} (key: ${l.getKey()}) in ${a.getType()} (key: ${a.getKey()}). targetIndex: ${e}, currentIndex: ${i}`);
return s = !0, !0;
}
- if (_r(l)) {
- const u = l.getTextContentSize();
- if (!s && e > i && e < i + u) {
- const d = e - i, [f] = l.splitText(d);
- return f.insertAfter(t), r?.debug(`$insertNodeAtCharacterOffset: Inserted node ${t.getType()} (key: ${t.getKey()}) by splitting TextNode (key: ${l.getKey()}) at offset ${d}. targetIndex: ${e}, currentIndex at node start: ${i}`), s = !0, !0;
+ if (Er(l)) {
+ const d = l.getTextContentSize();
+ if (!s && e > i && e < i + d) {
+ const u = e - i, [f] = l.splitText(u);
+ return f.insertAfter(t), r?.debug(`$insertNodeAtCharacterOffset: Inserted node ${t.getType()} (key: ${t.getKey()}) by splitting TextNode (key: ${l.getKey()}) at offset ${u}. targetIndex: ${e}, currentIndex at node start: ${i}`), s = !0, !0;
}
- i += u;
- } else if (Pt(l))
+ i += d;
+ } else if (Nt(l))
i += 1;
- else if (D(l)) {
+ else if (I(l)) {
if (o(l))
return !0;
- } else if (Et(l)) {
- const u = l;
- if (o(u))
+ } else if (At(l)) {
+ const d = l;
+ if (o(d))
return !0;
- const d = i;
- if (dr(u) && Et(t) && // Target is at the ImpliedPara's implicit newline
- e === d && !s)
- return r?.debug(`$insertNodeAtCharacterOffset: Replacing ImpliedParaNode (key: ${u.getKey()}) with block node '${t.getType()}' (key: ${t.getKey()}) at OT index ${e}.`), l.replace(t, !0), i = d + 1, s = !0, !0;
+ const u = i;
+ if (dr(d) && At(t) && // Target is at the ImpliedPara's implicit newline
+ e === u && !s)
+ return r?.debug(`$insertNodeAtCharacterOffset: Replacing ImpliedParaNode (key: ${d.getKey()}) with block node '${t.getType()}' (key: ${t.getKey()}) at OT index ${e}.`), l.replace(t, !0), i = u + 1, s = !0, !0;
i += 1;
- } else if (F(l) && o(l))
+ } else if (L(l) && o(l))
return !0;
if (s)
return !0;
}
- return F(a) && !s && (e === i || a === n && e > i) ? a === n ? (t.isInline() ? (r?.debug(`$insertNodeAtCharacterOffset: Appending inline node ${t.getType()} to root. Wrapping in new ImpliedParaNode. targetIndex: ${e}, current document OT length: ${i}.`), n.append(Ht().append(t))) : (r?.debug(`$insertNodeAtCharacterOffset: Appending block node ${t.getType()} to root. targetIndex: ${e}, current document OT length: ${i}.`), n.append(t)), s = !0, !0) : (
+ return L(a) && !s && (e === i || a === n && e > i) ? a === n ? (t.isInline() ? (r?.debug(`$insertNodeAtCharacterOffset: Appending inline node ${t.getType()} to root. Wrapping in new ImpliedParaNode. targetIndex: ${e}, current document OT length: ${i}.`), n.append(Wt().append(t))) : (r?.debug(`$insertNodeAtCharacterOffset: Appending block node ${t.getType()} to root. targetIndex: ${e}, current document OT length: ${i}.`), n.append(t)), s = !0, !0) : (
// Appending to an existing container (ParaNode, ImpliedParaNode)
// currentNode here is the container itself. currentIndex is at the point of currentNode's
// closing marker. targetIndex === currentIndex means we are inserting at the conceptual end
// of this container.
- Se(a) ? dr(a) && ae(t) && e === i ? (r?.debug(`$insertNodeAtCharacterOffset: Replacing ImpliedParaNode container (key: ${a.getKey()}) with ParaNode ${t.getType()} (key: ${t.getKey()}) via append logic. targetIndex: ${e}`), a.replace(t, !0), s = !0, !0) : t.isInline() || !Se(t) ? (r?.debug(`$insertNodeAtCharacterOffset: Appending node ${t.getType()} to existing container ${a.getType()} (key: ${a.getKey()}). targetIndex: ${e}, container end OT index: ${i}.`), a.append(t), s = !0, !0) : (r?.debug(`$insertNodeAtCharacterOffset: Inserting block node ${t.getType()} after container ${a.getType()} (key: ${a.getKey()}). targetIndex: ${e}, container end OT index: ${i}.`), a.insertAfter(t), s = !0, !0) : (D(a) ? (r?.debug(`$insertNodeAtCharacterOffset: Inserting node ${t.getType()} after CharNode (key: ${a.getKey()}). targetIndex: ${e}, element end OT index: ${i}.`), a.insertAfter(t)) : t.isInline() || !Se(t) ? (r?.debug(`$insertNodeAtCharacterOffset: Appending node ${t.getType()} to generic element ${a.getType()} (key: ${a.getKey()}). targetIndex: ${e}, element end OT index: ${i}.`), a.append(t)) : (r?.debug(`$insertNodeAtCharacterOffset: Inserting block node ${t.getType()} after generic element ${a.getType()} (key: ${a.getKey()}). targetIndex: ${e}, element end OT index: ${i}.`), a.insertAfter(t)), s = !0, !0)
+ Ce(a) ? dr(a) && ce(t) && e === i ? (r?.debug(`$insertNodeAtCharacterOffset: Replacing ImpliedParaNode container (key: ${a.getKey()}) with ParaNode ${t.getType()} (key: ${t.getKey()}) via append logic. targetIndex: ${e}`), a.replace(t, !0), s = !0, !0) : t.isInline() || !Ce(t) ? (r?.debug(`$insertNodeAtCharacterOffset: Appending node ${t.getType()} to existing container ${a.getType()} (key: ${a.getKey()}). targetIndex: ${e}, container end OT index: ${i}.`), a.append(t), s = !0, !0) : (r?.debug(`$insertNodeAtCharacterOffset: Inserting block node ${t.getType()} after container ${a.getType()} (key: ${a.getKey()}). targetIndex: ${e}, container end OT index: ${i}.`), a.insertAfter(t), s = !0, !0) : (I(a) ? (r?.debug(`$insertNodeAtCharacterOffset: Inserting node ${t.getType()} after CharNode (key: ${a.getKey()}). targetIndex: ${e}, element end OT index: ${i}.`), a.insertAfter(t)) : t.isInline() || !Ce(t) ? (r?.debug(`$insertNodeAtCharacterOffset: Appending node ${t.getType()} to generic element ${a.getType()} (key: ${a.getKey()}). targetIndex: ${e}, element end OT index: ${i}.`), a.append(t)) : (r?.debug(`$insertNodeAtCharacterOffset: Inserting block node ${t.getType()} after generic element ${a.getType()} (key: ${a.getKey()}). targetIndex: ${e}, element end OT index: ${i}.`), a.insertAfter(t)), s = !0, !0)
) : s;
}
return o(n), s || r?.warn(`$insertNodeAtCharacterOffset: Could not find insertion point for node ${t.getType()} (key: ${t.getKey()}) at targetIndex ${e}. Final currentIndex: ${i}. Node not inserted.`), s;
}
-function F_(e, t, r, n, i) {
+function sC(e, t, r, n, i) {
let s;
- return Fr("chapter", t) ? s = K_(t.insert.chapter, r) : Fr("verse", t) ? s = j_(t.insert.verse, r) : Fr("ms", t) ? s = B_(t.insert.ms) : Fr("note", t) ? s = wh(t, r, n, i) : Fr("unknown", t) ? s = qh(t, r, n, i) : Fr("unmatched", t) && (s = W_(t.insert.unmatched, r)), s ? Nh(e, s, i) : (i?.error(`$insertEmbedAtCurrentIndex: Cannot create LexicalNode for embed object: ${JSON.stringify(t.insert)}`), !1);
+ return Kr("chapter", t) ? s = aC(t.insert.chapter, r) : Kr("verse", t) ? s = cC(t.insert.verse, r) : Kr("ms", t) ? s = lC(t.insert.ms) : Kr("note", t) ? s = Hh(t, r, n, i) : Kr("unknown", t) ? s = Gh(t, r, n, i) : Kr("unmatched", t) && (s = dC(t.insert.unmatched, r)), s ? Vh(e, s, i) : (i?.error(`$insertEmbedAtCurrentIndex: Cannot create LexicalNode for embed object: ${JSON.stringify(t.insert)}`), !1);
}
-function td(e, t, r, n) {
+function pd(e, t, r, n) {
let i;
- ul(t) ? i = Oh(t.para, r) : G_(t) && (i = z_(t.book)), i ??= Ht();
- const s = i, o = ae(s), a = dr(s);
+ Tl(t) ? i = Wh(t.para, r) : pC(t) && (i = oC(t.book)), i ??= Wt();
+ const s = i, o = ce(s), a = dr(s);
let c = 0, l = !1;
- function u(d) {
+ function d(u) {
if (l)
return !0;
- if (_r(d)) {
- const f = d.getTextContentSize();
+ if (Er(u)) {
+ const f = u.getTextContentSize();
if (e >= c && e <= c + f) {
- const p = d.getParent();
- if (ae(p) && (o || a)) {
+ const p = u.getParent();
+ if (ce(p) && (o || a)) {
n?.debug(`Splitting ParaNode (marker: ${p.getMarker()}) with LF attributes at targetIndex ${e}`);
- const m = e - c, [g] = m > 0 ? d.splitText(m) : [void 0];
- let y, k = g?.getPreviousSibling();
- for (; k; ) {
- const _ = k;
- k = k.getPreviousSibling(), y ? y.insertBefore(_) : s.append(_), y = _;
+ const y = e - c, [h] = y > 0 ? u.splitText(y) : [void 0];
+ let m, b = h?.getPreviousSibling();
+ for (; b; ) {
+ const x = b;
+ b = b.getPreviousSibling(), m ? m.insertBefore(x) : s.append(x), m = x;
}
- return g && s.append(g), p.insertBefore(s), l = !0, !0;
+ return h && s.append(h), p.insertBefore(s), l = !0, !0;
}
}
c += f;
- } else if (Pt(d))
+ } else if (Nt(u))
c += 1;
- else if (Et(d)) {
- const f = d.getChildren();
+ else if (At(u)) {
+ const f = u.getChildren();
for (const p of f) {
- if (u(p))
+ if (d(p))
return !0;
if (l)
break;
}
if (e === c) {
- if (dr(d) && s)
- return n?.debug(`Replacing ImpliedParaNode (key: ${d.getKey()}) with ParaNode at targetIndex ${e}`), d.replace(s, !0), l = !0, !0;
- if (ae(d) && s) {
- const p = d;
+ if (dr(u) && s)
+ return n?.debug(`Replacing ImpliedParaNode (key: ${u.getKey()}) with ParaNode at targetIndex ${e}`), u.replace(s, !0), l = !0, !0;
+ if (ce(u) && s) {
+ const p = u;
return n?.debug(`Creating new block node with LF attributes after existing ParaNode (marker: ${p.getMarker()}) at targetIndex ${e}`), p.insertAfter(s), l = !0, !0;
}
}
- if (c += 1, e === c && ae(d) && s)
- return n?.debug(`Creating new block node after existing ParaNode (marker: ${d.getMarker()}) at targetIndex ${e}`), d.insertAfter(s), l = !0, !0;
- } else if (F(d)) {
- const f = d.getChildren();
+ if (c += 1, e === c && ce(u) && s)
+ return n?.debug(`Creating new block node after existing ParaNode (marker: ${u.getMarker()}) at targetIndex ${e}`), u.insertAfter(s), l = !0, !0;
+ } else if (L(u)) {
+ const f = u.getChildren();
for (const p of f) {
- if (u(p))
+ if (d(p))
return !0;
if (l)
break;
@@ -17274,75 +17275,75 @@ function td(e, t, r, n) {
}
return l;
}
- return u(Fe()), l || n?.warn(`Could not find location to handle newline with para attributes at targetIndex ${e}. Final currentIndex: ${c}.`), 1;
+ return d(ze()), l || n?.warn(`Could not find location to handle newline with para attributes at targetIndex ${e}. Final currentIndex: ${c}.`), 1;
}
-function z_(e) {
+function oC(e) {
const { style: t, code: r } = e;
- if (!t || t !== Gi || !r || !Ut.isValidBookCode(r))
+ if (!t || t !== Zi || !r || !Ft.isValidBookCode(r))
return;
- const n = Ue(e, hx);
- return ip(r, n);
+ const n = Fe(e, qx);
+ return Tp(r, n);
}
-function Oh(e, t) {
+function Wh(e, t) {
const { style: r } = e;
if (!r)
return;
- const n = Ue(e, px), i = Ji(r, n);
- if (!gi(t))
+ const n = Fe(e, wx), i = es(r, n);
+ if (!ki(t))
return i;
if (t.markerMode === "editable")
- i.append(ot(r), mo());
+ i.append(ct(r), To());
else if (t.markerMode === "visible" || t.hasGutterParaMarkers) {
- const s = we(r) + I;
- i.append(t.hasGutterParaMarkers ? Ib(s) : Tr("marker", s));
+ const s = qe(r) + R;
+ i.append(t.hasGutterParaMarkers ? tk(s) : Sr("marker", s));
}
return i;
}
-function K_(e, t) {
+function aC(e, t) {
if (!e)
return;
const { number: r, sid: n, altnumber: i, pubnumber: s } = e;
if (!r)
return;
- const o = Ue(e, gx);
+ const o = Fe(e, Rx);
let a;
if (t.markerMode === "editable")
- a = ap(r, n, i, s, o);
+ a = Cp(r, n, i, s, o);
else {
const c = t.markerMode === "visible";
- a = Oc(r, c, n, i, s, o);
+ a = Fc(r, c, n, i, s, o);
}
return a;
}
-function j_(e, t) {
+function cC(e, t) {
if (!e)
return;
const { style: r, number: n, sid: i, altnumber: s, pubnumber: o } = e;
if (!n)
return;
- const a = Ue(e, mx);
+ const a = Fe(e, $x);
let c;
if (t.markerMode === "editable") {
if (!r)
return;
- const l = Lt(r, n);
- c = yp(n, l, i, s, o, a);
+ const l = Dt(r, n);
+ c = qp(n, l, i, s, o, a);
} else {
const l = t.markerMode === "visible";
- c = Gc(n, l, i, s, o, a);
+ c = il(n, l, i, s, o, a);
}
return c;
}
-function B_(e) {
+function lC(e) {
if (!e)
return;
const { style: t, sid: r, eid: n, attributeOrder: i } = e;
if (!t)
return;
- const s = Ue(e, yx);
- return jf(t, r, n, s, i);
+ const s = Fe(e, Lx);
+ return sp(t, r, n, s, i);
}
-function wh(e, t, r, n) {
+function Hh(e, t, r, n) {
const i = e.insert;
if (!i.note)
return;
@@ -17350,50 +17351,50 @@ function wh(e, t, r, n) {
if (!s || o == null)
return;
o === "" && n?.warn("Note has empty caller. Only use for note editing.");
- const l = Ue(i.note, bx), u = typeof l?.closed == "string" ? l.closed : void 0, d = e.attributes?.segment;
+ const l = Fe(i.note, Ix), d = typeof l?.closed == "string" ? l.closed : void 0, u = e.attributes?.segment;
let f;
- d && typeof d == "string" && (f = d);
+ u && typeof u == "string" && (f = u);
const p = [];
- for (const g of c?.ops ?? [])
- if (typeof g.insert == "string")
- if (Hr(g.attributes)) {
- const y = mi(g.attributes.char, t, pe(g.insert), void 0, Rh(g.attributes.char, p), !1, t.markerMode === "editable");
- p.push(...y);
+ for (const h of c?.ops ?? [])
+ if (typeof h.insert == "string")
+ if (Yr(h.attributes)) {
+ const m = Ti(h.attributes.char, t, ge(h.insert), void 0, Jh(h.attributes.char, p), !1, t.markerMode === "editable");
+ p.push(...m);
} else
- p.push(pe(g.insert));
- return xh(s, o, p, t, r, f, u).setCategory(a).setUnknownAttributes(l);
+ p.push(ge(h.insert));
+ return Lh(s, o, p, t, r, f, d).setCategory(a).setUnknownAttributes(l);
}
-function qh(e, t, r, n) {
+function Gh(e, t, r, n) {
const i = e.insert.unknown;
if (!i)
return;
const { tag: s, marker: o, contents: a } = i;
if (!s)
return;
- const c = Ue(i, kx), l = Nc(s, o, c), u = a?.ops ?? [];
- u.length > 0 && V_(u, t, r, n).forEach((p) => l.append(p));
- const d = e.attributes?.segment;
- return typeof d == "string" && bt(l, Vr, () => d), l;
+ const c = Fe(i, Dx), l = Uc(s, o, c), d = a?.ops ?? [];
+ d.length > 0 && uC(d, t, r, n).forEach((p) => l.append(p));
+ const u = e.attributes?.segment;
+ return typeof u == "string" && kt(l, Gr, () => u), l;
}
-function V_(e, t, r, n) {
+function uC(e, t, r, n) {
const i = [];
for (const s of e) {
if (typeof s.insert == "string") {
- if (Hr(s.attributes)) {
- const o = pe(s.insert), a = mi(s.attributes.char, t, o, void 0, Rh(s.attributes.char, i));
+ if (Yr(s.attributes)) {
+ const o = ge(s.insert), a = Ti(s.attributes.char, t, o, void 0, Jh(s.attributes.char, i));
i.push(...a);
} else
- i.push(pe(s.insert));
+ i.push(ge(s.insert));
continue;
}
if (!(!s.insert || typeof s.insert != "object")) {
- if (Fr("unknown", s)) {
- const o = qh(s, t, r, n);
+ if (Kr("unknown", s)) {
+ const o = Gh(s, t, r, n);
o && i.push(o);
continue;
}
- if (Fr("note", s)) {
- const o = wh(s, t, r, n);
+ if (Kr("note", s)) {
+ const o = Hh(s, t, r, n);
o && i.push(o);
continue;
}
@@ -17402,62 +17403,62 @@ function V_(e, t, r, n) {
}
return i;
}
-function W_(e, t) {
+function dC(e, t) {
if (!e)
return;
const { marker: r } = e;
if (!r)
return;
- const n = jc(r);
+ const n = Qc(r);
return t.markerMode === "editable" && n.setMode("normal"), n;
}
-function Rh(e, t) {
+function Jh(e, t) {
if (!(!Array.isArray(e) && e.style === "fp" && !e.cid))
return t;
}
-function ja(e) {
+function Ga(e) {
return e.style.startsWith("+") ? { ...e, style: e.style.slice(1) } : e;
}
-function mi(e, t, r, n, i, s = !1, o = !1) {
- v(r) && r.getTextContentSize() === 0 && r.setTextContent(Dt);
+function Ti(e, t, r, n, i, s = !1, o = !1) {
+ M(r) && r.getTextContentSize() === 0 && r.setTextContent(Ut);
const a = () => {
- o && v(r) && r.getTextContent() !== Dt && r.setTextContent(I + r.getTextContent());
+ o && M(r) && r.getTextContent() !== Ut && r.setTextContent(R + r.getTextContent());
};
if (Array.isArray(e)) {
if (e.length === 0)
throw new Error("Empty charAttr array");
- const c = e.map(ja), l = c[0], u = i?.[i.length - 1];
- if (D(u) && Tn(l, u))
- return c.length > 1 ? mi(c.slice(1), t, r, void 0, void 0, !0, o).forEach((p) => u.append(p)) : r && u.append(r), [];
+ const c = e.map(Ga), l = c[0], d = i?.[i.length - 1];
+ if (I(d) && xn(l, d))
+ return c.length > 1 ? Ti(c.slice(1), t, r, void 0, void 0, !0, o).forEach((p) => d.append(p)) : r && d.append(r), [];
a();
- const d = c.reduceRight((f, p, m) => {
- const g = xr(p.style, Ue(p, Hs));
- if (typeof p.cid == "string" && bt(g, kn, () => p.cid), n && m === c.length - 1 && bt(g, Vr, () => n), f)
- if (D(f)) {
- const y = f.getMarker(), k = [];
- oa(y, k, t, !0), k.forEach((S) => g.append(S)), g.append(f);
- const _ = [];
- sa(f, _, t, !0), _.forEach((S) => g.append(S));
+ const u = c.reduceRight((f, p, y) => {
+ const h = Mr(p.style, Fe(p, Xs));
+ if (typeof p.cid == "string" && kt(h, Tn, () => p.cid), n && y === c.length - 1 && kt(h, Gr, () => n), f)
+ if (I(f)) {
+ const m = f.getMarker(), b = [];
+ la(m, b, t, !0), b.forEach((_) => h.append(_)), h.append(f);
+ const x = [];
+ ca(f, x, t, !0), x.forEach((_) => h.append(_));
} else
- g.append(f);
- return g;
+ h.append(f);
+ return h;
}, r);
- return oa(l.style, d, t, s), sa(d, d, t, s), [d];
+ return la(l.style, u, t, s), ca(u, u, t, s), [u];
} else {
- const c = ja(e), l = i?.[i.length - 1];
- if (D(l) && Tn(c, l))
+ const c = Ga(e), l = i?.[i.length - 1];
+ if (I(l) && xn(c, l))
return r && l.append(r), [];
a();
- const u = xr(c.style, Ue(c, Hs));
- return typeof c.cid == "string" && bt(u, kn, () => c.cid), n && bt(u, Vr, () => n), r && u.append(r), oa(c.style, u, t, s), sa(u, u, t, s), [u];
+ const d = Mr(c.style, Fe(c, Xs));
+ return typeof c.cid == "string" && kt(d, Tn, () => c.cid), n && kt(d, Gr, () => n), r && d.append(r), la(c.style, d, t, s), ca(d, d, t, s), [d];
}
}
-function sa(e, t, r, n = !1) {
- e.getUnknownAttributes()?.closed !== "false" && H_(e.getMarker(), t, r, !1, n);
+function ca(e, t, r, n = !1) {
+ e.getUnknownAttributes()?.closed !== "false" && fC(e.getMarker(), t, r, !1, n);
}
-function oa(e, t, r, n = !1) {
+function la(e, t, r, n = !1) {
let i;
- if (r?.markerMode === "editable" ? i = ot(e, "opening", n) : r?.markerMode === "visible" && (i = Tr("marker", we(e, n))), i)
+ if (r?.markerMode === "editable" ? i = ct(e, "opening", n) : r?.markerMode === "visible" && (i = Sr("marker", qe(e, n))), i)
if (Array.isArray(t))
t.push(i);
else {
@@ -17465,37 +17466,37 @@ function oa(e, t, r, n = !1) {
s ? s.insertBefore(i) : t.append(i);
}
}
-function H_(e, t, r, n = !1, i = !1) {
+function fC(e, t, r, n = !1, i = !1) {
let s;
- r?.markerMode === "editable" ? n ? s = ot("", "selfClosing") : s = ot(e, "closing", i) : r?.markerMode === "visible" && (s = Tr("marker", n ? rt("") : rt(e, i))), s && (Array.isArray(t) ? t.push(s) : t.append(s));
+ r?.markerMode === "editable" ? n ? s = ct("", "selfClosing") : s = ct(e, "closing", i) : r?.markerMode === "visible" && (s = Sr("marker", n ? st("") : st(e, i))), s && (Array.isArray(t) ? t.push(s) : t.append(s));
}
-function G_(e) {
+function pC(e) {
return !!e && !!e.book && typeof e.book == "object" && e.book !== null && "style" in e.book && typeof e.book.style == "string" && "code" in e.book && typeof e.book.code == "string";
}
-function ul(e) {
+function Tl(e) {
return !!e && !!e.para && typeof e.para == "object" && e.para !== null && "style" in e.para && typeof e.para.style == "string";
}
-function Hr(e) {
+function Yr(e) {
return !!e && !!e.char && typeof e.char == "object" && e.char !== null && (!Array.isArray(e.char) && "style" in e.char && typeof e.char.style == "string" || Array.isArray(e.char) && e.char.length > 0 && "style" in e.char[0] && typeof e.char[0].style == "string");
}
-function J_(e) {
+function hC(e) {
return typeof e == "object" && e !== null && !Array.isArray(e) && Object.keys(e).length === 0;
}
-function qt(e, t) {
+function Rt(e, t) {
if (e)
for (const r of Object.keys(e)) {
if (r === "segment" && typeof e[r] == "string") {
const n = e[r];
- bt(t, Vr, () => n);
+ kt(t, Gr, () => n);
continue;
}
- if (Y_(r)) {
+ if (gC(r)) {
const n = !!e[r], i = r, s = t.hasFormat(i);
(n && !s || !n && s) && t.toggleFormat(i);
}
}
}
-const $h = [
+const Yh = [
"bold",
"underline",
"strikethrough",
@@ -17508,100 +17509,103 @@ const $h = [
"uppercase",
"capitalize"
];
-function Y_(e) {
- return $h.includes(e);
+function gC(e) {
+ return Yh.includes(e);
}
-function X_() {
- const [e] = ce();
- return z(() => e.registerCommand(lo, (t) => (Q_(t), !1), mn), [e]), null;
+function mC() {
+ const [e] = ae();
+ return F(() => e.registerCommand(mo, (t) => (yC(t), !1), bn), [e]), null;
}
-function Q_(e) {
- if (Z_(e.target))
+function yC(e) {
+ if (bC(e.target))
return;
const t = w();
- N(t) && eC(t);
+ N(t) && kC(t);
}
-function yi(e) {
+function xi(e) {
let t = e.getFirstChild(), r = 0;
for (; t !== null; )
- if (Ft(t))
- r++, t = t.getNextSibling(), v(t) && t.getTextContent() === I && (r++, t = t.getNextSibling());
- else if (ge(t))
+ if (zt(t))
+ r++, t = t.getNextSibling(), M(t) && t.getTextContent() === R && (r++, t = t.getNextSibling());
+ else if (he(t))
r++, t = t.getNextSibling();
else
break;
- return r === 0 ? !1 : (Qt(e, r), !0);
+ return r === 0 ? !1 : (Xt(e, r), !0);
}
-function Z_(e) {
- if (!Tf(e))
+function bC(e) {
+ if (!Rf(e))
return !1;
- const t = li(e);
- if (!Lb(t))
+ const t = hi(e);
+ if (!rk(t))
return !1;
const r = t.getParent();
- return r ? Se(r) ? yi(r) : (Qt(r, t.getIndexWithinParent() + 1), !0) : !1;
+ return r ? Ce(r) ? xi(r) : (Xt(r, t.getIndexWithinParent() + 1), !0) : !1;
}
-function eC(e) {
+function kC(e) {
if (!e.isCollapsed())
return !1;
const { anchor: t } = e;
if (t.type !== "element" || t.offset !== 0)
return !1;
const r = se(t.key);
- if (!Se(r))
+ if (!Ce(r))
return !1;
const n = r.getFirstChild();
- return !Pr(n) && !wn(n) ? !1 : yi(r);
-}
-function tC() {
- const [e] = ce();
- return z(() => {
- const t = (r) => r instanceof KeyboardEvent && !rC(r) || !Po() ? !1 : (r instanceof Event && r.preventDefault(), !0);
- return Ve(
- e.registerCommand(vr, t, Ne),
- e.registerCommand(Tc, t, Ne),
+ return !Rr(n) && !Ln(n) ? !1 : xi(r);
+}
+function TC() {
+ const [e] = ae();
+ return F(() => {
+ const t = (r) => r instanceof KeyboardEvent && !xC(r) || !wo() ? !1 : (r instanceof Event && r.preventDefault(), !0);
+ return Be(
+ e.registerCommand(Nr, t, Oe),
+ e.registerCommand(Pc, t, Oe),
// CUT and PASTE run at CRITICAL because their standard-view handlers — the ones that
// actually copy and then remove — are themselves registered at HIGH, where the winner is
// decided by registration order rather than by intent. A refusal has to outrank the actor it
// refuses, not tie with it. The engine's own CRITICAL cut arm, which records what a cut would
// cover, TIES with this refusal, so it consults `$selectionReachesIntoOpaqueBlock` itself
// rather than relying on order: an arm this refusal leaves behind would outlive the gesture.
- e.registerCommand(yr, t, Wt),
- e.registerCommand(yn, t, Wt),
+ e.registerCommand(xr, t, xt),
+ e.registerCommand(vr, t, xt),
// DROP is judged by the drop TARGET, not the live selection: Lexical dispatches
// DROP_COMMAND straight from the DOM handler with no selection update, so at drop time
// `$getSelection()` still holds whatever was selected when the drag STARTED. Testing that
// inverted both promises above — dragging a caption OUT of a figure was refused (source
// inside), while dragging outside text INTO a caption was allowed (source outside).
- e.registerCommand(xc, (r) => {
+ e.registerCommand(Nc, (r) => {
if (!(r instanceof Event) || !(r.target instanceof Node))
return !1;
- const n = li(r.target);
- return !n || !Gr(n) ? !1 : (r.preventDefault(), !0);
- }, Ne),
- e.registerCommand(my, t, Ne),
- e.registerCommand(yy, t, Ne),
- e.registerCommand(by, t, Ne)
+ const n = hi(r.target);
+ return !n || !Xr(n) ? !1 : (r.preventDefault(), !0);
+ }, Oe),
+ e.registerCommand(qy, t, Oe),
+ e.registerCommand(Ry, t, Oe),
+ e.registerCommand($y, t, Oe)
);
}, [e]), null;
}
-function rC(e) {
+function xC(e) {
return e.isComposing || e.keyCode === 229 ? !0 : !(typeof e.getModifierState == "function" && e.getModifierState("AltGraph")) && (e.ctrlKey || e.metaKey || e.altKey) ? !1 : e.key.length === 1 || e.key === "Backspace" || e.key === "Delete" || e.key === "Enter";
}
-function Gr(e) {
- return nt(e, (t) => De(t) || Qp(t)) ?? void 0;
+function Xr(e) {
+ return Xe(e, xl) ?? void 0;
+}
+function xl(e) {
+ return Ue(e) || hh(e);
}
-function Po() {
+function wo() {
const e = w();
- return N(e) ? Gr(e.anchor.getNode()) !== void 0 || Gr(e.focus.getNode()) !== void 0 : !1;
+ return N(e) ? Xr(e.anchor.getNode()) !== void 0 || Xr(e.focus.getNode()) !== void 0 : !1;
}
-function nC(e, t, r) {
+function _C(e, t, r) {
if (e.height === 0)
return !1;
const n = e.height / 4;
return t.some((i) => r === "down" ? i.top >= e.bottom - n : i.bottom <= e.top + n);
}
-function iC(e, t) {
+function CC(e, t) {
if (typeof window > "u")
return !1;
const r = window.getSelection();
@@ -17618,36 +17622,36 @@ function iC(e, t) {
o.collapse(!0);
const a = Array.from(n.querySelectorAll('[data-marker="v"]'));
let c, l;
- for (const d of a) {
+ for (const u of a) {
const f = document.createRange();
- if (f.selectNode(d), o.compareBoundaryPoints(Range.START_TO_START, f) > 0)
- c = d;
+ if (f.selectNode(u), o.compareBoundaryPoints(Range.START_TO_START, f) > 0)
+ c = u;
else {
- l = d;
+ l = u;
break;
}
}
if (!c)
return !1;
- const u = document.createRange();
- return u.setStartAfter(c), l ? u.setEndBefore(l) : u.setEnd(n, n.childNodes.length), nC(s, Array.from(u.getClientRects()), t);
+ const d = document.createRange();
+ return d.setStartAfter(c), l ? d.setEndBefore(l) : d.setEnd(n, n.childNodes.length), _C(s, Array.from(d.getClientRects()), t);
} catch {
return !1;
}
}
-function sC(e, t, r, n) {
- if (!kC(t) || iC(e, r))
+function vC(e, t, r, n) {
+ if (!DC(t) || CC(e, r))
return !1;
- const i = r === "up" ? fx(t) : dx(t);
+ const i = r === "up" ? Ox(t) : Nx(t);
return i && n.preventDefault(), i;
}
-function oC({ viewOptions: e }) {
- const [t] = ce();
- return aC(t, e), null;
+function SC({ viewOptions: e }) {
+ const [t] = ae();
+ return MC(t, e), null;
}
-function aC(e, t) {
- z(() => {
- if (!e.hasNodes([pr, Ct, Ee]))
+function MC(e, t) {
+ F(() => {
+ if (!e.hasNodes([pr, St, Ae]))
throw new Error("ArrowNavigationPlugin: ImmutableChapterNode, ImmutableVerseNode or NoteNode not registered on editor!");
const r = (n) => {
const i = w();
@@ -17655,70 +17659,70 @@ function aC(e, t) {
return !1;
const s = t?.markerMode === "editable", o = e.getRootElement();
if (s && o && (n.key === "ArrowLeft" || n.key === "ArrowRight") && n.shiftKey && !n.altKey && !n.ctrlKey && !n.metaKey) {
- const u = rd(o), d = hC(i, nd(u, n.key) ? "next" : "previous");
- return d && n.preventDefault(), d;
+ const d = hd(o), u = qC(i, gd(d, n.key) ? "next" : "previous");
+ return u && n.preventDefault(), u;
}
if (!i.isCollapsed())
return !1;
if (n.key === "ArrowUp" || n.key === "ArrowDown") {
if (n.shiftKey || n.altKey || n.ctrlKey || n.metaKey)
return !1;
- const u = n.key === "ArrowUp" ? "up" : "down";
- return sC(e, i, u, n);
+ const d = n.key === "ArrowUp" ? "up" : "down";
+ return vC(e, i, d, n);
}
if (n.key !== "ArrowLeft" && n.key !== "ArrowRight" || !o)
return !1;
- const a = rd(o), c = n.shiftKey || n.altKey || n.ctrlKey || n.metaKey;
+ const a = hd(o), c = n.shiftKey || n.altKey || n.ctrlKey || n.metaKey;
let l = !1;
- return nd(a, n.key) ? l = !c && od(i, "next") || !c && lC(i) || yC(i) || !c && s && sd(i, "next") : cC(a, n.key) && (l = !c && od(i, "previous") || !c && uC(i) || bC(i, t) || !c && s && sd(i, "previous")), l && n.preventDefault(), l;
+ return gd(a, n.key) ? l = !c && bd(i, "next") || !c && AC(i) || LC(i) || !c && s && yd(i, "next") : EC(a, n.key) && (l = !c && bd(i, "previous") || !c && PC(i) || IC(i, t) || !c && s && yd(i, "previous")), l && n.preventDefault(), l;
};
- return e.registerCommand(vr, r, Ne);
+ return e.registerCommand(Nr, r, Oe);
}, [e, t]);
}
-function rd(e) {
+function hd(e) {
return e.dir || "ltr";
}
-function nd(e, t) {
+function gd(e, t) {
return e === "ltr" && t === "ArrowRight" || e === "rtl" && t === "ArrowLeft";
}
-function cC(e, t) {
+function EC(e, t) {
return e === "ltr" && t === "ArrowLeft" || e === "rtl" && t === "ArrowRight";
}
-function Ba(e) {
- if (!D(e) || e.getMarker() !== "fp")
+function Ja(e) {
+ if (!I(e) || e.getMarker() !== "fp")
return;
- const t = Xt(e);
+ const t = Yt(e);
if (!(!t || t.getIsCollapsed()))
return e;
}
-function lC(e) {
- const t = Ba(_p(e));
+function AC(e) {
+ const t = Ja(Dp(e));
if (!t)
return !1;
const r = e.anchor;
- return r.type === "text" && r.offset !== r.getNode().getTextContentSize() ? !1 : (Qt(t, 0), !0);
+ return r.type === "text" && r.offset !== r.getNode().getTextContentSize() ? !1 : (Xt(t, 0), !0);
}
-function uC(e) {
+function PC(e) {
const t = e.anchor, r = t.getNode();
if (t.type === "text") {
- const n = Ba(r.getParent());
- return !n || !r.is(n.getFirstChild()) ? !1 : t.offset === 1 ? (r.select(0, 0), !0) : t.offset !== 0 ? !1 : id(n);
+ const n = Ja(r.getParent());
+ return !n || !r.is(n.getFirstChild()) ? !1 : t.offset === 1 ? (r.select(0, 0), !0) : t.offset !== 0 ? !1 : md(n);
}
if (t.offset === 0) {
- const n = Ba(r);
- return n ? id(n) : !1;
+ const n = Ja(r);
+ return n ? md(n) : !1;
}
return !1;
}
-function id(e) {
+function md(e) {
const t = e.getPreviousSibling();
if (!t)
return !1;
- if (v(t))
+ if (M(t))
return t.select(), !0;
- if (F(t)) {
+ if (L(t)) {
const i = t.getLastDescendant();
- return v(i) ? i.select() : t.selectEnd(), !0;
+ return M(i) ? i.select() : t.selectEnd(), !0;
}
const r = e.getParent();
if (!r)
@@ -17726,39 +17730,39 @@ function id(e) {
const n = e.getIndexWithinParent();
return r.select(n, n), !0;
}
-const Ys = typeof Intl.Segmenter > "u" ? void 0 : new Intl.Segmenter(void 0, { granularity: "grapheme" });
-function dC(e) {
- if (Ys)
- for (const { segment: r } of Ys.segment(e))
+const eo = typeof Intl.Segmenter > "u" ? void 0 : new Intl.Segmenter(void 0, { granularity: "grapheme" });
+function NC(e) {
+ if (eo)
+ for (const { segment: r } of eo.segment(e))
return r.length;
const t = e.codePointAt(0);
return t === void 0 ? 0 : String.fromCodePoint(t).length;
}
-function fC(e) {
- if (Ys) {
+function OC(e) {
+ if (eo) {
let n = 0;
- for (const { index: i } of Ys.segment(e))
+ for (const { index: i } of eo.segment(e))
n = i;
return n;
}
const t = e.codePointAt(Math.max(0, e.length - 2)), r = t !== void 0 && t > 65535;
return Math.max(0, e.length - (r ? 2 : 1));
}
-function Ih(e) {
+function Xh(e) {
for (let t = e; t; t = t.getParent())
- if (F(t) && !t.isInline())
+ if (L(t) && !t.isInline())
return t;
}
-function Lh(e) {
- return !!e && O(e) && Gr(e) !== void 0;
+function Qh(e) {
+ return !!e && O(e) && Xr(e) !== void 0;
}
-function si(e) {
- return v(e) && !e.isToken() && !Lh(e) && e.getTextContentSize() > 0;
+function ui(e) {
+ return M(e) && !e.isToken() && !Qh(e) && e.getTextContentSize() > 0;
}
-function Dh(e) {
- return us(e) ? !0 : j(e) ? e.getIsCollapsed() === !0 : v(e) ? (e.isToken() || Lh(e)) && e.getTextContentSize() > 0 : uo(e) ? !Be(e) : !1;
+function Zh(e) {
+ return Nn(e) ? !0 : z(e) ? e.getIsCollapsed() === !0 : M(e) ? (e.isToken() || Qh(e)) && e.getTextContentSize() > 0 : On(e) ? !He(e) : !1;
}
-function oi(e, t, r) {
+function di(e, t, r) {
for (let n = e; n && !n.is(r); ) {
const i = t === "next" ? n.getNextSibling() : n.getPreviousSibling();
if (i)
@@ -17766,99 +17770,99 @@ function oi(e, t, r) {
n = n.getParent();
}
}
-function No(e, t, r) {
+function qo(e, t, r) {
for (let n = e; n; ) {
- if (Dh(n))
+ if (Zh(n))
return n;
- if (F(n)) {
- n = (t === "next" ? n.getFirstChild() : n.getLastChild()) ?? oi(n, t, r);
+ if (L(n)) {
+ n = (t === "next" ? n.getFirstChild() : n.getLastChild()) ?? di(n, t, r);
continue;
}
- if (si(n))
+ if (ui(n))
return n;
- n = oi(n, t, r);
+ n = di(n, t, r);
}
}
-function dl(e, t, r, n, i) {
- return r === "element" && F(e) ? e.getChildAtIndex(n === "next" ? t : t - 1) ?? oi(e, n, i) : r === "text" && Dh(e) && (n === "next" ? t < e.getTextContentSize() : t > 0) ? e : oi(e, n, i);
+function _l(e, t, r, n, i) {
+ return r === "element" && L(e) ? e.getChildAtIndex(n === "next" ? t : t - 1) ?? di(e, n, i) : r === "text" && Zh(e) && (n === "next" ? t < e.getTextContentSize() : t > 0) ? e : di(e, n, i);
}
-function aa(e, t) {
+function ua(e, t) {
if (e.kind === "text" && e.offset > 0)
return e;
- const r = dl(e.node, e.offset, e.kind, "previous", t), n = No(r, "previous", t);
+ const r = _l(e.node, e.offset, e.kind, "previous", t), n = qo(r, "previous", t);
if (!n)
return e;
- if (si(n))
+ if (ui(n))
return { kind: "text", node: n, offset: n.getTextContentSize() };
const i = n.getParent();
return i ? { kind: "element", node: i, offset: n.getIndexWithinParent() + 1 } : e;
}
-function pC(e, t) {
- const r = e.getNode(), n = Ih(r);
+function wC(e, t) {
+ const r = e.getNode(), n = Xh(r);
if (!n)
return;
- if (e.type === "text" && si(r)) {
+ if (e.type === "text" && ui(r)) {
if (t === "next" && e.offset < r.getTextContentSize() || t === "previous" && e.offset > 1)
return;
if (t === "previous" && e.offset === 1)
- return aa({ kind: "text", node: r, offset: 0 }, n);
+ return ua({ kind: "text", node: r, offset: 0 }, n);
}
- const i = dl(r, e.offset, e.type, t, n), s = No(i, t, n);
+ const i = _l(r, e.offset, e.type, t, n), s = qo(i, t, n);
if (!s)
return;
- if (si(s)) {
- const c = s.getTextContent(), l = t === "next" ? dC(c) : fC(c);
- return aa({ kind: "text", node: s, offset: l }, n);
+ if (ui(s)) {
+ const c = s.getTextContent(), l = t === "next" ? NC(c) : OC(c);
+ return ua({ kind: "text", node: s, offset: l }, n);
}
const o = s.getParent();
if (!o)
return;
const a = s.getIndexWithinParent();
- return aa({ kind: "element", node: o, offset: t === "next" ? a + 1 : a }, n);
+ return ua({ kind: "element", node: o, offset: t === "next" ? a + 1 : a }, n);
}
-function Uh(e, t, r) {
- const n = r === "collapse" ? e.anchor : e.focus, i = pC(n, t);
+function eg(e, t, r) {
+ const n = r === "collapse" ? e.anchor : e.focus, i = wC(n, t);
return !i || i.node.is(n.getNode()) && i.offset === n.offset && i.kind === n.type ? !1 : r === "collapse" ? (i.node.select(i.offset, i.offset), !0) : (e.focus.set(i.node.getKey(), i.offset, i.kind), !0);
}
-function sd(e, t) {
- return Uh(e, t, "collapse");
+function yd(e, t) {
+ return eg(e, t, "collapse");
}
-function hC(e, t) {
- return Uh(e, t, "extend");
+function qC(e, t) {
+ return eg(e, t, "extend");
}
-function gC(e, t, r) {
+function RC(e, t, r) {
const n = e.getNode();
- if (e.type === "text" && si(n) && (t === "next" ? e.offset < n.getTextContentSize() : e.offset > 0))
+ if (e.type === "text" && ui(n) && (t === "next" ? e.offset < n.getTextContentSize() : e.offset > 0))
return !1;
- const i = dl(n, e.offset, e.type, t, r);
- return No(i, t, r) === void 0;
+ const i = _l(n, e.offset, e.type, t, r);
+ return qo(i, t, r) === void 0;
}
-function mC(e, t) {
- const r = Fe();
+function $C(e, t) {
+ const r = ze();
for (let n = e; n; ) {
- const i = oi(n, t, r), s = i && No(i, t, r);
+ const i = di(n, t, r), s = i && qo(i, t, r);
if (!s)
return;
- if (n = Gr(s), !n)
+ if (n = Xr(s), !n)
return s;
}
}
-function od(e, t) {
+function bd(e, t) {
const r = e.anchor, n = r.getNode();
- if (Gr(n))
+ if (Xr(n))
return !1;
- const i = Ih(n);
- if (!i || !gC(r, t, i))
+ const i = Xh(n);
+ if (!i || !RC(r, t, i))
return !1;
- const s = oi(i, t, Fe()), o = s && Gr(s);
+ const s = di(i, t, ze()), o = s && Xr(s);
if (!o)
return !1;
- const a = mC(o, t);
+ const a = $C(o, t);
if (!a)
return !0;
- if (si(a)) {
- const u = t === "next" ? 0 : a.getTextContentSize();
- return a.select(u, u), !0;
+ if (ui(a)) {
+ const d = t === "next" ? 0 : a.getTextContentSize();
+ return a.select(d, d), !0;
}
const c = a.getParent();
if (!c)
@@ -17866,17 +17870,17 @@ function od(e, t) {
const l = a.getIndexWithinParent() + (t === "next" ? 0 : 1);
return c.select(l, l), !0;
}
-function ad(e) {
+function kd(e) {
const t = e.getParent();
if (!t)
return;
const r = e.getIndexWithinParent() + 1;
t.select(r, r);
}
-function yC(e) {
- const t = e.anchor.getNode(), r = _p(e);
- if (j(r) && !O(r.getFirstChild())) {
- if (Se(t)) {
+function LC(e) {
+ const t = e.anchor.getNode(), r = Dp(e);
+ if (z(r) && !O(r.getFirstChild())) {
+ if (Ce(t)) {
if (e.anchor.offset === t.getChildrenSize())
return !1;
} else if (!(e.anchor.offset === t.getTextContentSize()))
@@ -17884,33 +17888,33 @@ function yC(e) {
if (r.getIsCollapsed()) {
if (r.is(r.getParent()?.getLastChild())) {
const i = r.getParent()?.getNextSibling();
- return i && !(Se(i) && yi(i)) && i.selectStart(), !0;
+ return i && !(Ce(i) && xi(i)) && i.selectStart(), !0;
}
- } else return Yt(r.getFirstChild()) ? r.select(2, 2) : r.select(1, 1), !0;
+ } else return Jt(r.getFirstChild()) ? r.select(2, 2) : r.select(1, 1), !0;
}
- if (Se(t) && j(r) && r.getIsCollapsed()) {
+ if (Ce(t) && z(r) && r.getIsCollapsed()) {
const i = r.getNextSibling();
- return i ? i.selectStart() : ad(r), !0;
+ return i ? i.selectStart() : kd(r), !0;
}
const n = r?.getParent();
- if (Yt(r) && j(n) && r.is(n?.getLastChild())) {
+ if (Jt(r) && z(n) && r.is(n?.getLastChild())) {
const i = n.getNextSibling();
- return i ? i.selectStart() : n.getIsCollapsed() ? ad(n) : n.selectEnd(), !0;
+ return i ? i.selectStart() : n.getIsCollapsed() ? kd(n) : n.selectEnd(), !0;
}
return !1;
}
-function bC(e, t) {
- const r = bk(e);
- if (fs(r) && !r.getPreviousSibling())
+function IC(e, t) {
+ const r = Ik(e);
+ if (ms(r) && !r.getPreviousSibling())
return !0;
if (!(e.anchor.offset === 0))
return !1;
const i = e.anchor.getNode();
if (pt(i.getParent()))
return !0;
- if (j(r) && r.getIsCollapsed()) {
+ if (z(r) && r.getIsCollapsed()) {
const o = r.getPreviousSibling();
- if (!wn(o))
+ if (!Ln(o))
return !1;
const a = r.getParent();
if (!a)
@@ -17918,12 +17922,12 @@ function bC(e, t) {
const c = r.getIndexWithinParent();
return a.select(c, c), !0;
}
- if (Se(r) && t?.noteMode === "collapsed") {
+ if (Ce(r) && t?.noteMode === "collapsed") {
const o = r.getLastChild();
if (!o)
return !1;
- const a = nt(o, (c) => j(c));
- if (j(a) && a.getIsCollapsed()) {
+ const a = Xe(o, (c) => z(c));
+ if (z(a) && a.getIsCollapsed()) {
const c = a.getParent();
if (!c)
return !1;
@@ -17931,10 +17935,10 @@ function bC(e, t) {
return c.select(l, l), !0;
}
}
- const s = Xt(i);
+ const s = Yt(i);
if (!s || s.getIsCollapsed())
return !1;
- if (St(r)) {
+ if (ht(r)) {
const o = s.getParent();
if (!o)
return !1;
@@ -17943,103 +17947,103 @@ function bC(e, t) {
}
return !1;
}
-function kC(e) {
+function DC(e) {
if (e.anchor.type === "element")
return !0;
if (e.anchor.offset !== 0)
return !1;
const t = e.anchor.getNode().getPreviousSibling();
- return ge(t) && uo(t);
+ return he(t) && On(t);
}
-function TC() {
- const [e] = ce();
- return xC(e), null;
+function UC() {
+ const [e] = ae();
+ return FC(e), null;
}
-function xC(e) {
- z(() => {
- if (!e.hasNodes([me]))
+function FC(e) {
+ F(() => {
+ if (!e.hasNodes([ye]))
throw new Error("CharNodePlugin: CharNode not registered on editor!");
- return Ve(
- e.registerNodeTransform(me, SC),
+ return Be(
+ e.registerNodeTransform(ye, jC),
// Self-healing nested glyphs: whenever a char span is dirtied (created, moved, merged,
// unwrapped), re-derive its glyphs' `+` from tree position — see nestedGlyphs.utils.ts
// (`shared`) for the full representation rules this enforces.
- e.registerNodeTransform(me, Uk),
+ e.registerNodeTransform(ye, iT),
// Self-healing display separators: every opening char glyph is followed by its NBSP
// separator (text prefix or standalone spacer) — see markerSeparators.utils.ts (`shared`).
- e.registerNodeTransform(me, zp),
+ e.registerNodeTransform(ye, nh),
// Self-healing attribute display run: re-derive the `|…` run from unknownAttributes
// whenever a span is dirtied — heals remote collab updates (delta-apply only calls
// setUnknownAttributes) and structure surgery. $syncDisplayRun (displayRunSync.utils.ts,
// `shared`), driven here with the char descriptor from displayRunRegistry.ts (`shared`).
- e.registerNodeTransform(me, (t) => es(xn("char"), t)),
- e.registerNodeTransform(Ke, vC)
+ e.registerNodeTransform(ye, (t) => ss(_n("char"), t)),
+ e.registerNodeTransform(je, BC)
);
}, [e]);
}
-function ca(e) {
+function da(e) {
return e.getChildren().some(O);
}
-function _C(e, t) {
+function zC(e, t) {
const r = t.getFirstChild();
if (!O(r) || r.getMarkerSyntax() !== "opening")
return !1;
const n = r.getNextSibling();
- if (To(n)) {
+ if (vo(n)) {
const i = n.getTextContent();
- i.startsWith(I) && (i === I ? n.remove() : n.setTextContent(i.slice(I.length)));
+ i.startsWith(R) && (i === R ? n.remove() : n.setTextContent(i.slice(R.length)));
}
return t.splice(1, 0, e.getChildren()), e.remove(), !0;
}
-function CC(e, t) {
+function KC(e, t) {
const r = t.getLastChild(), n = e.getChildren();
O(r) && r.getMarkerSyntax() === "closing" ? n.forEach((i) => r.insertBefore(i)) : t.append(...n), e.remove();
}
-function SC(e) {
- if (!D(e))
+function jC(e) {
+ if (!I(e))
return;
if (e.isEmpty()) {
e.remove();
return;
}
- if (ca(e))
+ if (da(e))
return;
const t = e.getMarker();
if (t === "fp")
return;
- const r = ne(e, kn), n = e.getUnknownAttributes(), i = e.getNextSibling();
- if (D(i) && Tn({ style: t, cid: r }, i) && wt(n, i.getUnknownAttributes()))
- if (ca(i)) {
- if (_C(e, i))
+ const r = ne(e, Tn), n = e.getUnknownAttributes(), i = e.getNextSibling();
+ if (I(i) && xn({ style: t, cid: r }, i) && qt(n, i.getUnknownAttributes()))
+ if (da(i)) {
+ if (zC(e, i))
return;
} else
e.append(...i.getChildren()), i.remove();
const s = e.getPreviousSibling();
- D(s) && Tn({ style: t, cid: r }, s) && wt(n, s.getUnknownAttributes()) && (ca(s) ? CC(e, s) : (s.append(...e.getChildren()), e.remove()));
+ I(s) && xn({ style: t, cid: r }, s) && qt(n, s.getUnknownAttributes()) && (da(s) ? KC(e, s) : (s.append(...e.getChildren()), e.remove()));
}
-function vC(e) {
+function BC(e) {
const t = e.getParent();
- if (!D(t) || t.getChildrenSize() !== 1)
+ if (!I(t) || t.getChildrenSize() !== 1)
return;
const r = e.getTextContent();
- r.length > 1 && r.startsWith(Dt) && (e.setTextContent(r.slice(1)), e.selectEnd());
+ r.length > 1 && r.startsWith(Ut) && (e.setTextContent(r.slice(1)), e.selectEnd());
}
-function Fh(e) {
+function tg(e) {
return e.replaceAll(" ", " ");
}
-function zh() {
+function rg() {
const e = w();
return !!e && !e.isCollapsed();
}
-function Kh(e) {
- const t = () => !zh();
- return Ve(e.registerCommand(fo, t, Tt), e.registerCommand(yn, t, Tt));
-}
-const fl = (e) => {
- e.dispatchCommand(fo, null);
-}, pl = (e) => {
- e.dispatchCommand(yn, null);
-}, hl = (e) => {
+function ng(e) {
+ const t = () => !rg();
+ return Be(e.registerCommand(ni, t, _t), e.registerCommand(vr, t, _t));
+}
+const Cl = (e) => {
+ e.dispatchCommand(ni, null);
+}, vl = (e) => {
+ e.dispatchCommand(vr, null);
+}, Sl = (e) => {
navigator.clipboard.read().then(async (t) => {
if ((await navigator.permissions.query({
// @ts-expect-error These types are incorrect.
@@ -18051,14 +18055,14 @@ const fl = (e) => {
const n = new DataTransfer(), i = t[0];
for (const o of i.types) {
const a = await (await i.getType(o)).text();
- n.setData(o, Fh(a));
+ n.setData(o, tg(a));
}
const s = new ClipboardEvent("paste", {
clipboardData: n
});
- e.dispatchCommand(yr, s);
+ e.dispatchCommand(xr, s);
});
-}, gl = (e) => {
+}, Ml = (e) => {
navigator.clipboard.read().then(async () => {
if ((await navigator.permissions.query({
// @ts-expect-error These types are incorrect.
@@ -18068,130 +18072,130 @@ const fl = (e) => {
return;
}
const r = new DataTransfer(), n = await navigator.clipboard.readText();
- r.setData("text/plain", Fh(n));
+ r.setData("text/plain", tg(n));
const i = new ClipboardEvent("paste", {
clipboardData: r
});
- e.dispatchCommand(yr, i);
+ e.dispatchCommand(xr, i);
});
};
-function MC() {
- const [e] = ce();
- return z(() => {
+function VC() {
+ const [e] = ae();
+ return F(() => {
const t = (r) => {
const { key: n, shiftKey: i, metaKey: s, ctrlKey: o, altKey: a } = r;
- !($s ? s : o) || a || (!i && n.toLowerCase() === "c" ? (r.preventDefault(), fl(e)) : !i && n.toLowerCase() === "x" ? (r.preventDefault(), pl(e)) : n.toLowerCase() === "v" && (r.preventDefault(), i ? gl(e) : hl(e)));
+ !(si ? s : o) || a || (!i && n.toLowerCase() === "c" ? (r.preventDefault(), Cl(e)) : !i && n.toLowerCase() === "x" ? (r.preventDefault(), vl(e)) : n.toLowerCase() === "v" && (r.preventDefault(), i ? Ml(e) : Sl(e)));
};
- return Ve(
+ return Be(
// Every copy/cut this plugin's shortcuts synthesize — and every one the context menu or an
// editor ref synthesizes against the same editor — passes through this guard.
- Kh(e),
+ ng(e),
e.registerRootListener((r, n) => {
n !== null && n.removeEventListener("keydown", t), r !== null && r.addEventListener("keydown", t);
})
);
}, [e]), null;
}
-function EC({ logger: e }) {
- const [t] = ce();
- return z(() => Ve(
+function WC({ logger: e }) {
+ const [t] = ae();
+ return F(() => Be(
// When the backslash or forward slash key is typed.
- t.registerCommand(vr, (r) => r.key !== "\\" && r.key !== "/" ? !1 : (r.preventDefault(), !0), Jn),
+ t.registerCommand(Nr, (r) => r.key !== "\\" && r.key !== "/" ? !1 : (r.preventDefault(), !0), Vr),
// When the backslash or forward slash character is pasted into the editor.
- t.registerCommand(yr, (r) => {
+ t.registerCommand(xr, (r) => {
const n = r.clipboardData?.getData("text/plain");
return !n || !n.includes("\\") && !n.includes("/") ? !1 : (e?.info("CommandMenuPlugin: paste containing backslash or forward slash ignored."), r.preventDefault(), !0);
- }, Jn),
+ }, Vr),
// When the backslash or forward slash character is dragged into the editor.
- t.registerCommand(xc, (r) => {
+ t.registerCommand(Nc, (r) => {
const n = r.dataTransfer?.getData("text/plain");
return !n || !n.includes("\\") && !n.includes("/") ? !1 : (e?.info("CommandMenuPlugin: drag containing backslash or forward slash ignored."), r.preventDefault(), !0);
- }, Jn)
+ }, Vr)
), [t, e]), null;
}
-function AC({ index: e, isSelected: t, onClick: r, onMouseEnter: n, option: i }) {
+function HC({ index: e, isSelected: t, onClick: r, onMouseEnter: n, option: i }) {
let s = "item";
- return t && (s += " selected"), i.isDisabled && (s += " disabled"), C("li", { tabIndex: -1, className: s, role: "option", "aria-selected": t, "aria-disabled": i.isDisabled, id: "typeahead-item-" + e, onMouseEnter: n, onClick: i.isDisabled ? void 0 : r, children: C("span", { className: "text", children: i.title }) });
+ return t && (s += " selected"), i.isDisabled && (s += " disabled"), S("li", { tabIndex: -1, className: s, role: "option", "aria-selected": t, "aria-disabled": i.isDisabled, id: "typeahead-item-" + e, onMouseEnter: n, onClick: i.isDisabled ? void 0 : r, children: S("span", { className: "text", children: i.title }) });
}
-function PC({ options: e, selectedItemIndex: t, onOptionClick: r, onOptionMouseEnter: n }) {
- return C("div", { className: "typeahead-popover", children: C("ul", { children: e.map((i, s) => C(AC, { index: s, isSelected: t === s, onClick: () => r(i, s), onMouseEnter: () => n(s), option: i }, i.key)) }) });
+function GC({ options: e, selectedItemIndex: t, onOptionClick: r, onOptionMouseEnter: n }) {
+ return S("div", { className: "typeahead-popover", children: S("ul", { children: e.map((i, s) => S(HC, { index: s, isSelected: t === s, onClick: () => r(i, s), onMouseEnter: () => n(s), option: i }, i.key)) }) });
}
-let NC = 0;
-class Ei {
+let JC = 0;
+class wi {
key;
title;
onSelect;
isDisabled;
constructor(t, r) {
- this.key = `context-menu-option-${NC++}`, this.title = t, this.onSelect = r.onSelect.bind(this), this.isDisabled = r.isDisabled || !1;
+ this.key = `context-menu-option-${JC++}`, this.title = t, this.onSelect = r.onSelect.bind(this), this.isDisabled = r.isDisabled || !1;
}
}
-function OC({ options: e } = {}) {
- const [t] = ce(), [r, n] = de(() => !t.isEditable()), [i, s] = de({
+function YC({ options: e } = {}) {
+ const [t] = ae(), [r, n] = de(() => !t.isEditable()), [i, s] = de({
isOpen: !1,
x: 0,
y: 0
- }), [o, a] = de(void 0), c = ze(() => {
- const d = [
+ }), [o, a] = de(void 0), c = Ke(() => {
+ const u = [
// Cut/Copy with nothing selected leave the clipboard alone rather than writing a placeholder
// over it — `registerEmptyCopyGuard` (mounted below) claims the command, so no selection
// check is needed here. They are not disabled in that case, because this option list is
// built once per editor rather than per menu opening, so its `isDisabled` flags cannot track
// the live selection.
- new Ei("Cut", {
+ new wi("Cut", {
onSelect: () => {
- pl(t);
+ vl(t);
},
isDisabled: r
}),
- new Ei("Copy", {
+ new wi("Copy", {
onSelect: () => {
- fl(t);
+ Cl(t);
}
}),
- new Ei("Paste", {
+ new wi("Paste", {
onSelect: () => {
- hl(t);
+ Sl(t);
},
isDisabled: r
}),
- new Ei("Paste as Plain Text", {
+ new wi("Paste as Plain Text", {
onSelect: () => {
- gl(t);
+ Ml(t);
},
isDisabled: r
})
- ], f = (e ?? []).map((p) => new Ei(p.title, { onSelect: p.onSelect, isDisabled: p.isDisabled }));
- return [...d, ...f];
- }, [t, r, e]), l = he(() => {
- s((d) => ({ ...d, isOpen: !1 })), a(void 0);
+ ], f = (e ?? []).map((p) => new wi(p.title, { onSelect: p.onSelect, isDisabled: p.isDisabled }));
+ return [...u, ...f];
+ }, [t, r, e]), l = me(() => {
+ s((u) => ({ ...u, isOpen: !1 })), a(void 0);
}, []);
- z(() => Kh(t), [t]), z(() => {
- const d = (f) => {
+ F(() => ng(t), [t]), F(() => {
+ const u = (f) => {
const p = f.target;
- t.getRootElement() === p || pp(p) || (f.preventDefault(), s({ isOpen: !0, x: f.clientX, y: f.clientY }), a(void 0));
+ t.getRootElement() === p || Pp(p) || (f.preventDefault(), s({ isOpen: !0, x: f.clientX, y: f.clientY }), a(void 0));
};
return t.registerRootListener((f, p) => {
- p?.removeEventListener("contextmenu", d), f && f.addEventListener("contextmenu", d);
+ p?.removeEventListener("contextmenu", u), f && f.addEventListener("contextmenu", u);
});
- }, [t]), z(() => {
+ }, [t]), F(() => {
if (!i.isOpen)
return;
- const d = () => {
+ const u = () => {
l();
};
- return globalThis.addEventListener("scroll", d, !0), () => globalThis.removeEventListener("scroll", d, !0);
- }, [i.isOpen, l]), z(() => {
+ return globalThis.addEventListener("scroll", u, !0), () => globalThis.removeEventListener("scroll", u, !0);
+ }, [i.isOpen, l]), F(() => {
if (!i.isOpen)
return;
- const d = () => {
+ const u = () => {
l();
};
- return document.addEventListener("pointerdown", d), () => document.removeEventListener("pointerdown", d);
- }, [i.isOpen, l]), z(() => {
+ return document.addEventListener("pointerdown", u), () => document.removeEventListener("pointerdown", u);
+ }, [i.isOpen, l]), F(() => {
if (!i.isOpen)
return;
- const d = (f) => {
+ const u = (f) => {
if (f.key === "Escape")
l();
else if (f.key === "ArrowDown")
@@ -18206,18 +18210,18 @@ function OC({ options: e } = {}) {
}), l());
}
};
- return document.addEventListener("keydown", d, !0), () => document.removeEventListener("keydown", d, !0);
- }, [i.isOpen, l, c, o, t]), z(() => t.registerEditableListener((d) => {
- n(!d);
+ return document.addEventListener("keydown", u, !0), () => document.removeEventListener("keydown", u, !0);
+ }, [i.isOpen, l, c, o, t]), F(() => t.registerEditableListener((u) => {
+ n(!u);
}), [t]);
- const u = Z(null);
- return cs(() => {
- const d = u.current;
- if (!d)
+ const d = Z(null);
+ return ps(() => {
+ const u = d.current;
+ if (!u)
return;
- const { width: f, height: p } = d.getBoundingClientRect(), m = Math.max(0, Math.min(i.x, globalThis.innerWidth - f)), g = Math.max(0, Math.min(i.y, globalThis.innerHeight - p));
- d.style.left = `${m}px`, d.style.top = `${g}px`, d.style.visibility = "visible";
- }, [i.isOpen, i.x, i.y]), i.isOpen ? zy.createPortal(C("div", { ref: u, className: "typeahead-popover auto-embed-menu", style: {
+ const { width: f, height: p } = u.getBoundingClientRect(), y = Math.max(0, Math.min(i.x, globalThis.innerWidth - f)), h = Math.max(0, Math.min(i.y, globalThis.innerHeight - p));
+ u.style.left = `${y}px`, u.style.top = `${h}px`, u.style.visibility = "visible";
+ }, [i.isOpen, i.x, i.y]), i.isOpen ? ob.createPortal(S("div", { ref: d, className: "typeahead-popover auto-embed-menu", style: {
left: i.x,
position: "fixed",
top: i.y,
@@ -18225,29 +18229,29 @@ function OC({ options: e } = {}) {
visibility: "hidden",
width: 200,
zIndex: 9999
- }, onPointerDown: (d) => d.stopPropagation(), children: C(PC, { options: c, selectedItemIndex: o, onOptionClick: (d) => {
- d.isDisabled || (t.update(() => {
- d.onSelect();
+ }, onPointerDown: (u) => u.stopPropagation(), children: S(GC, { options: c, selectedItemIndex: o, onOptionClick: (u) => {
+ u.isDisabled || (t.update(() => {
+ u.onSelect();
}), l());
- }, onOptionMouseEnter: (d) => {
- a(d);
+ }, onOptionMouseEnter: (u) => {
+ a(u);
} }) }), document.body) : null;
}
-function wC(e, t) {
+function XC(e, t) {
return e.startContainer === t.node && e.startOffset === t.offset;
}
-function qC(e) {
- if (!_y(e.node))
+function QC(e) {
+ if (!Dy(e.node))
return "before";
const t = e.node.nodeValue?.length ?? 0;
return e.offset * 2 < t ? "before" : "after";
}
-function RC(e) {
- return St(e);
+function ZC(e) {
+ return ht(e);
}
-function la(e, t, r) {
- const n = li(t.node);
- if (!uo(n) || RC(n))
+function fa(e, t, r) {
+ const n = hi(t.node);
+ if (!On(n) || ZC(n))
return;
const i = e.getElementByKey(n.getKey());
if (!i || i === t.node || !i.contains(t.node))
@@ -18259,36 +18263,36 @@ function la(e, t, r) {
if (!(o < 0))
return { node: s, offset: r === "before" ? o : o + 1 };
}
-function $C(e, t) {
+function ev(e, t) {
if (w())
return !1;
- const r = e.getRootElement(), n = ky(r?.ownerDocument.defaultView ?? null);
+ const r = e.getRootElement(), n = Ly(r?.ownerDocument.defaultView ?? null);
if (!n || n.rangeCount === 0)
return !1;
const { anchorNode: i, anchorOffset: s, focusNode: o, focusOffset: a } = n;
- if (!i || !o || !Ty(e, i, o))
+ if (!i || !o || !$f(e, i, o))
return !1;
const c = { node: i, offset: s }, l = { node: o, offset: a };
- let u, d;
+ let d, u;
if (n.isCollapsed)
- u = la(e, c, qC(c)), d = u;
+ d = fa(e, c, QC(c)), u = d;
else {
- const y = wC(n.getRangeAt(0), c);
- u = la(e, c, y ? "before" : "after"), d = la(e, l, y ? "after" : "before");
+ const m = XC(n.getRangeAt(0), c);
+ d = fa(e, c, m ? "before" : "after"), u = fa(e, l, m ? "after" : "before");
}
- if (!u && !d)
+ if (!d && !u)
return !1;
- const f = u ?? c, p = d ?? l, m = {
+ const f = d ?? c, p = u ?? l, y = {
anchorNode: f.node,
anchorOffset: f.offset,
focusNode: p.node,
focusOffset: p.offset
- }, g = xy(m, e);
- return g ? (ei(g), g.dirty = !t, t) : !1;
+ }, h = Iy(y, e);
+ return h ? (ii(h), h.dirty = !t, t) : !1;
}
-function IC() {
- const [e] = ce(), t = Z(!1), r = Z(!1);
- return z(() => {
+function tv() {
+ const [e] = ae(), t = Z(!1), r = Z(!1);
+ return F(() => {
const n = (s) => {
"button" in s && s.button !== 0 || (t.current = !0);
}, i = () => {
@@ -18303,80 +18307,80 @@ function IC() {
const c = s?.ownerDocument;
c?.addEventListener("pointerdown", n, !0), c?.addEventListener("pointerup", i, !0), c?.addEventListener("pointercancel", i, !0);
});
- }, [e]), z(() => e.registerCommand(ur, () => ($C(e, t.current) && (r.current = !0), !1), Wt), [e]), null;
+ }, [e]), F(() => e.registerCommand(ur, () => (ev(e, t.current) && (r.current = !0), !1), xt), [e]), null;
}
-function LC() {
- const [e] = ce();
- return z(() => e.registerCommand(vr, (t) => {
+function rv() {
+ const [e] = ae();
+ return F(() => e.registerCommand(Nr, (t) => {
const { key: r, shiftKey: n, metaKey: i, ctrlKey: s, altKey: o } = t;
- if (!($s ? i : s) || o)
+ if (!(si ? i : s) || o)
return !1;
const a = r.toLowerCase();
return !(a === "z" && !n) && !(a === "y" || a === "z" && n) ? !1 : (t.preventDefault(), !0);
- }, Wt), [e]), null;
+ }, xt), [e]), null;
}
-function DC({ isEditable: e }) {
- const [t] = ce();
- return cs(() => {
+function nv({ isEditable: e }) {
+ const [t] = ae();
+ return ps(() => {
t.setEditable(e);
}, [t, e]), null;
}
-function cd(e) {
- return !!e && qc(se(e));
+function Td(e) {
+ return !!e && Kc(se(e));
}
-function jh(e) {
- const [t] = ce(), r = Z(void 0), n = he((i) => {
- const s = w(), o = N(s) && s.isCollapsed() ? s.anchor.key : void 0, a = r.current, c = cd(a);
+function ig(e) {
+ const [t] = ae(), r = Z(void 0), n = me((i) => {
+ const s = w(), o = N(s) && s.isCollapsed() ? s.anchor.key : void 0, a = r.current, c = Td(a);
a && !c && (r.current = void 0);
let l;
if (i) {
- const u = i.getParentOrThrow(), d = i.getIndexWithinParent() + 1, f = _o(u, d);
+ const d = i.getParentOrThrow(), u = i.getIndexWithinParent() + 1, f = Mo(d, u);
if (f)
r.current = f.getKey(), l = f.getKey();
else {
- const p = hk();
+ const p = qk();
i.insertAfter(p), r.current = p.getKey(), l = p.getKey();
}
- Qt(u, d);
+ Xt(d, u);
}
if (a && c && a !== o && a !== l) {
- const u = se(a);
- v(u) && u.remove(), r.current === a && (r.current = void 0);
+ const d = se(a);
+ M(d) && d.remove(), r.current === a && (r.current = void 0);
}
}, []);
- return z(() => {
+ return F(() => {
const i = () => {
- const a = e(), c = w(), l = N(c) && c.isCollapsed() ? c.anchor.key : void 0, u = r.current;
- (a || u && u !== l) && (jr(Br), n(a));
+ const a = e(), c = w(), l = N(c) && c.isCollapsed() ? c.anchor.key : void 0, d = r.current;
+ (a || d && d !== l) && (Wr(Hr), n(a));
}, s = (a) => {
if (a.getKey() !== r.current)
return;
const c = a.getTextContent();
- if (ps(c) || !c.includes(ti))
+ if (ys(c) || !c.includes(oi))
return;
- const l = w(), u = N(l) && l.isCollapsed() && l.anchor.key === a.getKey() ? l.anchor.offset : void 0;
- if (gk(a), r.current = void 0, u !== void 0) {
- const d = c.slice(0, u).split(ti).length - 1, f = Math.max(0, u - d);
+ const l = w(), d = N(l) && l.isCollapsed() && l.anchor.key === a.getKey() ? l.anchor.offset : void 0;
+ if (Rk(a), r.current = void 0, d !== void 0) {
+ const u = c.slice(0, d).split(oi).length - 1, f = Math.max(0, d - u);
a.select(f, f);
}
- }, o = Ve(t.registerCommand(ur, () => (i(), !1), mn), t.registerCommand(_c, () => {
+ }, o = Be(t.registerCommand(ur, () => (i(), !1), bn), t.registerCommand(Oc, () => {
const a = r.current;
if (!a)
return !1;
let c = !1;
return t.getEditorState().read(() => {
- c = cd(a);
+ c = Td(a);
}), c && t.update(() => {
const l = se(a);
- v(l) && l.remove();
- }, { tag: Br }), r.current = void 0, !1;
- }, mn), t.registerNodeTransform(Ke, s));
+ M(l) && l.remove();
+ }, { tag: Hr }), r.current = void 0, !1;
+ }, bn), t.registerNodeTransform(je, s));
return () => {
o(), r.current = void 0;
};
}, [t, e, n]), n;
}
-function UC() {
+function iv() {
const e = w();
if (!N(e) || !e.isCollapsed())
return;
@@ -18384,23 +18388,23 @@ function UC() {
if (t.type !== "element")
return;
const r = t.getNode();
- if (!F(r))
+ if (!L(r))
return;
const n = r.getChildren(), i = n[t.offset - 1];
- if (!ge(i) || _o(r, t.offset))
+ if (!he(i) || Mo(r, t.offset))
return;
const s = n[t.offset];
- if (s === void 0 || ge(s))
+ if (s === void 0 || he(s))
return i;
}
-function FC() {
- return jh(UC), null;
+function sv() {
+ return ig(iv), null;
}
-function zC({ scripture: e, scriptureRef: t, nodeOptions: r, editorAdaptor: n, viewOptions: i, logger: s }) {
- const [o] = ce();
- return z(() => {
+function ov({ scripture: e, scriptureRef: t, nodeOptions: r, editorAdaptor: n, viewOptions: i, logger: s }) {
+ const [o] = ae();
+ return F(() => {
n.initialize?.(r, s);
- }, [n, s, r]), z(() => {
+ }, [n, s, r]), F(() => {
const a = t?.current ?? e;
n.reset?.();
const c = n.serializeEditorState(a, i);
@@ -18411,48 +18415,48 @@ function zC({ scripture: e, scriptureRef: t, nodeOptions: r, editorAdaptor: n, v
try {
const l = o.parseEditorState(c);
queueMicrotask(() => {
- const u = o.getRootElement(), d = u?.ownerDocument.activeElement, f = u != null && d != null && (u === d || u.contains(d));
+ const d = o.getRootElement(), u = d?.ownerDocument.activeElement, f = d != null && u != null && (d === u || d.contains(u));
o.update(() => {
- f || jr(Cy), o.setEditorState(l), o.dispatchCommand(Sy, void 0);
- }, { tag: Df });
+ f || Wr(Uy), o.setEditorState(l), o.dispatchCommand(Fy, void 0);
+ }, { tag: ep });
});
} catch {
s?.error("LoadStatePlugin: error parsing or setting editor state.");
}
}, [o, n, s, e, t, i]), null;
}
-function KC({ expandedNoteKeyRef: e, nodeOptions: t, viewOptions: r, logger: n }) {
- const [i] = ce();
- return jC(t, n), BC(i, e, r, n), null;
+function av({ expandedNoteKeyRef: e, nodeOptions: t, viewOptions: r, logger: n }) {
+ const [i] = ae();
+ return cv(t, n), lv(i, e, r, n), null;
}
-function jC(e, t) {
+function cv(e, t) {
const r = Z(void 0), n = Z(void 0), i = e.noteCallers, s = e.crossRefCallers;
- z(() => {
+ F(() => {
let o = i;
- (!o || o.length <= 0) && (o = Xx), r.current !== o && (r.current = o, ld("note-callers", o, t));
- }, [t, i]), z(() => {
+ (!o || o.length <= 0) && (o = m_), r.current !== o && (r.current = o, xd("note-callers", o, t));
+ }, [t, i]), F(() => {
let o = s;
- (!o || o.length <= 0) && (o = Qx), n.current !== o && (n.current = o, ld("cross-ref-callers", o, t));
+ (!o || o.length <= 0) && (o = y_), n.current !== o && (n.current = o, xd("cross-ref-callers", o, t));
}, [t, s]);
}
-function BC(e, t, r, n) {
- z(() => {
- if (!e.hasNodes([me, Ee, Gt]))
+function lv(e, t, r, n) {
+ F(() => {
+ if (!e.hasNodes([ye, Ae, Ht]))
throw new Error("NoteNodePlugin: CharNode, NoteNode or ImmutableNoteCallerNode not registered on editor!");
- const i = (s) => e.update(() => XC(s));
- return Ve(
+ const i = (s) => e.update(() => mv(s));
+ return Be(
// Remove NoteNode if it doesn't contain a caller node and ensure typed text goes before it.
- e.registerNodeTransform(Ee, (s) => VC(s, r)),
+ e.registerNodeTransform(Ae, (s) => uv(s, r)),
// Update NoteNodeCaller preview text when NoteNode children text is changed.
- e.registerNodeTransform(me, WC),
- e.registerNodeTransform(Ke, HC),
+ e.registerNodeTransform(ye, dv),
+ e.registerNodeTransform(je, fv),
// Ensure NBSP after caller.
- e.registerNodeTransform(Gt, GC),
+ e.registerNodeTransform(Ht, pv),
// Re-generate all note callers when a note is removed.
- e.registerMutationListener(Gt, (s, { prevEditorState: o }) => JC(s, o)),
+ e.registerMutationListener(Ht, (s, { prevEditorState: o }) => hv(s, o)),
// Handle the cursor moving next to a NoteNode. NoteNode arrow key navigation when note is
// after a verse node is handled in the ArrowNavigationPlugin.
- e.registerCommand(ur, () => YC(e, t, r, n), Tt),
+ e.registerCommand(ur, () => gv(e, t, r, n), _t),
// Handle double-click of a word immediately following a NoteNode (no space between).
e.registerRootListener((s, o) => {
o !== null && o.removeEventListener("dblclick", i), s !== null && s.addEventListener("dblclick", i);
@@ -18460,52 +18464,52 @@ function BC(e, t, r, n) {
);
}, [e, t, n, r]);
}
-function VC(e, t) {
+function uv(e, t) {
const r = e.getChildren();
- if (!r.some((i) => St(i)) && t?.markerMode !== "editable" && e.getCaller() !== "" && e.remove(), r.length > 0) {
+ if (!r.some((i) => ht(i)) && t?.markerMode !== "editable" && e.getCaller() !== "" && e.remove(), r.length > 0) {
const i = r[0];
- v(i) && !O(i) && i.getTextContent() !== At(e.getCaller()) && e.insertBefore(i);
+ M(i) && !O(i) && i.getTextContent() !== Pt(e.getCaller()) && e.insertBefore(i);
}
}
-function WC(e) {
- const t = e.getParentOrThrow(), r = t.getChildren(), n = r.find((o) => St(o));
- if (!D(e) || !j(t) || !n)
+function dv(e) {
+ const t = e.getParentOrThrow(), r = t.getChildren(), n = r.find((o) => ht(o));
+ if (!I(e) || !z(t) || !n)
return;
- const i = Rc(r);
+ const i = jc(r);
n.getPreviewText() !== i && n.setPreviewText(i);
const s = e.getNextSibling();
- v(s) ? s.getTextContent() !== I && s.setTextContent(I) : e.insertAfter(pe(I));
+ M(s) ? s.getTextContent() !== R && s.setTextContent(R) : e.insertAfter(ge(R));
}
-function HC(e) {
- const t = Xt(e), r = t?.getChildren(), n = r?.find((o) => St(o));
- if (!v(e) || !j(t) || !n || !r)
+function fv(e) {
+ const t = Yt(e), r = t?.getChildren(), n = r?.find((o) => ht(o));
+ if (!M(e) || !z(t) || !n || !r)
return;
const i = e.getParent();
- if (!O(e) && j(i) && e.getTextContent() !== I && (e.setTextContent(I), e.selectEnd()), D(i) && i.getChildrenSize() === 1) {
+ if (!O(e) && z(i) && e.getTextContent() !== R && (e.setTextContent(R), e.selectEnd()), I(i) && i.getChildrenSize() === 1) {
const o = e.getTextContent();
- o.length > 1 && o.startsWith(Dt) && (e.setTextContent(o.slice(1)), e.selectEnd());
+ o.length > 1 && o.startsWith(Ut) && (e.setTextContent(o.slice(1)), e.selectEnd());
}
- const s = Rc(r);
+ const s = jc(r);
n.getPreviewText() !== s && n.setPreviewText(s);
}
-function GC(e) {
- if (!St(e))
+function pv(e) {
+ if (!ht(e))
return;
const t = e.getNextSibling();
- !v(t) || O(t) ? e.insertAfter(pe(I)) : t.getTextContent() !== I && t.setTextContent(I);
+ !M(t) || O(t) ? e.insertAfter(ge(R)) : t.getTextContent() !== R && t.setTextContent(R);
}
-function JC(e, t) {
+function hv(e, t) {
for (const [r, n] of e) {
if (n !== "destroyed")
continue;
const i = t.read(() => {
const o = se(r), a = o?.getParent();
- return St(o) && j(a) && a.getCaller() === Bi;
+ return ht(o) && z(a) && a.getCaller() === Ji;
}), s = document.querySelector(".editor-input");
!i || !s || (s.classList.add("reset-counters"), s.offsetHeight, s.classList.remove("reset-counters"));
}
}
-function YC(e, t, r, n) {
+function gv(e, t, r, n) {
if (r?.noteMode !== "expandInline")
return !1;
const i = w();
@@ -18513,48 +18517,48 @@ function YC(e, t, r, n) {
return !1;
const s = i.anchor, o = s.getNode();
if (t.current) {
- const a = nt(o, (c) => j(c));
+ const a = Xe(o, (c) => z(c));
if (a)
t.current !== a.getKey() && (t.current = a.getKey());
else {
const c = se(t.current);
- c && !c.getIsCollapsed() && (n?.debug("Cursor moved away from NoteNode, collapsing it"), Ai(e, t.current, n)), t.current = void 0;
+ c && !c.getIsCollapsed() && (n?.debug("Cursor moved away from NoteNode, collapsing it"), qi(e, t.current, n)), t.current = void 0;
}
}
if (s.offset === 0) {
const a = o.getPreviousSibling();
- if (j(a)) {
+ if (z(a)) {
n?.debug("Cursor is just after a NoteNode");
const c = a.getKey();
- a.getIsCollapsed() ? t.current = c : t.current = void 0, Ai(e, c, n);
+ a.getIsCollapsed() ? t.current = c : t.current = void 0, qi(e, c, n);
}
}
if (s.offset === o.getTextContentSize()) {
const a = o.getNextSibling();
- if (j(a)) {
+ if (z(a)) {
n?.debug("Cursor is just before a NoteNode");
const c = a.getKey();
- a.getIsCollapsed() ? t.current = c : t.current = void 0, Ai(e, c, n);
+ a.getIsCollapsed() ? t.current = c : t.current = void 0, qi(e, c, n);
} else if (!a) {
- const c = nt(o, (l) => j(l));
- if (c && c.getIsCollapsed() && Se(c.getParent()) && c.is(c.getParent()?.getLastChild())) {
+ const c = Xe(o, (l) => z(l));
+ if (c && c.getIsCollapsed() && Ce(c.getParent()) && c.is(c.getParent()?.getLastChild())) {
n?.debug("Cursor is at end of note at end of para");
const l = c.getKey();
- t.current = l, Ai(e, l, n);
+ t.current = l, qi(e, l, n);
}
}
}
- if (Se(o)) {
+ if (Ce(o)) {
const a = o.getChildAtIndex(s.offset), c = a?.getPreviousSibling();
- if (wn(c) && j(a)) {
+ if (Ln(c) && z(a)) {
n?.debug("Cursor is between verse and NoteNode");
const l = a.getKey();
- a.getIsCollapsed() ? t.current = l : t.current = void 0, Ai(e, l, n);
+ a.getIsCollapsed() ? t.current = l : t.current = void 0, qi(e, l, n);
}
}
return !1;
}
-function Ai(e, t, r) {
+function qi(e, t, r) {
const n = se(t);
try {
n?.toggleIsCollapsed();
@@ -18569,23 +18573,23 @@ function Ai(e, t, r) {
throw i;
}
}
-function XC(e) {
+function mv(e) {
const t = w();
if (!N(t))
return;
const r = t.anchor, n = t.focus, i = r.getNode(), s = n.getNode();
- if (j(i) && v(s)) {
+ if (z(i) && M(s)) {
e.preventDefault();
- const o = bc();
- o.anchor.set(s.getKey(), 0, "text"), o.focus.set(s.getKey(), n.offset, "text"), ei(o);
+ const o = Ec();
+ o.anchor.set(s.getKey(), 0, "text"), o.focus.set(s.getKey(), n.offset, "text"), ii(o);
}
}
-function ld(e, t, r) {
+function xd(e, t, r) {
for (const n of document.styleSheets)
try {
const i = n.cssRules || n.rules;
for (const s of i)
- if (QC(s, e)) {
+ if (yv(s, e)) {
const o = t.map((a) => `"${a}"`).join(" ");
s.symbols = o;
return;
@@ -18595,13 +18599,13 @@ function ld(e, t, r) {
}
r?.warn(`Editor: counter style "${e}" not found.`);
}
-function QC(e, t) {
+function yv(e, t) {
return (
// This check could be simpler but as is also works for test mocks.
typeof e == "object" && e !== null && "name" in e && e.name === t && "symbols" in e && typeof e.symbols == "string"
);
}
-function Oo(e) {
+function Ro(e) {
if (e.getIsCollapsed() !== !1)
return [];
const t = [];
@@ -18610,80 +18614,80 @@ function Oo(e) {
break;
t.push(n);
}
- const r = Xi(e);
- return r && t.push(r), t.length > 0 && t.every((n) => v(n) && n.getMode() === "token") ? t : [];
+ const r = rs(e);
+ return r && t.push(r), t.length > 0 && t.every((n) => M(n) && n.getMode() === "token") ? t : [];
}
-function ZC(e) {
+function bv(e) {
const t = e.getParent();
- if (j(t))
- return Oo(t).some((r) => r.is(e)) ? t : void 0;
+ if (z(t))
+ return Ro(t).some((r) => r.is(e)) ? t : void 0;
}
-function Xs(e) {
- const t = Oo(e), r = t[t.length - 1];
+function to(e) {
+ const t = Ro(e), r = t[t.length - 1];
return r ? r.getIndexWithinParent() + 1 : 0;
}
-function eS(e, t) {
+function kv(e, t) {
for (let r = t; r; r = r.getParent())
if (e.is(r.getParent()))
return r;
}
-function tS(e) {
- const t = vy();
+function Tv(e) {
+ const t = zy();
if (!N(t))
return !1;
const { anchor: r } = t, n = r.getNode();
if (e.is(n))
- return r.offset >= Xs(e);
- const i = eS(e, n);
- return i !== void 0 && i.getIndexWithinParent() >= Xs(e);
+ return r.offset >= to(e);
+ const i = kv(e, n);
+ return i !== void 0 && i.getIndexWithinParent() >= to(e);
}
-function Va(e) {
+function Ya(e) {
if (e.type !== "text")
return;
- const t = e.getNode(), r = ZC(t);
+ const t = e.getNode(), r = bv(t);
if (r)
- return rS(r, t, e.offset) ? void 0 : r;
+ return xv(r, t, e.offset) ? void 0 : r;
}
-function rS(e, t, r) {
- const n = Oo(e), i = n[n.length - 1];
+function xv(e, t, r) {
+ const n = Ro(e), i = n[n.length - 1];
return i !== void 0 && i.is(t) && r === i.getTextContentSize();
}
-function nS(e) {
- const t = Oo(e), r = t[t.length - 1];
- v(r) ? r.select(r.getTextContentSize(), r.getTextContentSize()) : Qt(e, Xs(e));
+function _v(e) {
+ const t = Ro(e), r = t[t.length - 1];
+ M(r) ? r.select(r.getTextContentSize(), r.getTextContentSize()) : Xt(e, to(e));
}
-function iS(e = !1) {
+function Cv(e = !1) {
const t = w();
if (!N(t))
return !1;
if (!t.isCollapsed())
- return sS(t.anchor, t.focus);
- const r = Va(t.anchor);
+ return vv(t.anchor, t.focus);
+ const r = Ya(t.anchor);
if (!r)
return !1;
- if (!e && tS(r)) {
+ if (!e && Tv(r)) {
const n = r.getParent();
if (!n)
return !1;
- Qt(n, r.getIndexWithinParent());
+ Xt(n, r.getIndexWithinParent());
} else
- nS(r);
+ _v(r);
return !0;
}
-function sS(e, t) {
- const r = Va(e), n = Va(t);
+function vv(e, t) {
+ const r = Ya(e), n = Ya(t);
if (!r && !n)
return !1;
const i = e.isBefore(t);
- return r && ud(e, r, i), n && ud(t, n, !i), !0;
+ return r && _d(e, r, i), n && _d(t, n, !i), !0;
}
-function ud(e, t, r) {
+function _d(e, t, r) {
const n = t.getParent();
- r && n ? e.set(n.getKey(), t.getIndexWithinParent(), "element") : e.set(t.getKey(), Xs(t), "element");
+ r && n ? e.set(n.getKey(), t.getIndexWithinParent(), "element") : e.set(t.getKey(), to(t), "element");
}
-function oS() {
- const [e] = ce(), t = Z(!1);
- return z(() => {
+function Sv() {
+ const [e] = ae(), t = Z(!1);
+ return F(() => {
const r = () => {
t.current = !0;
}, n = () => {
@@ -18695,73 +18699,73 @@ function oS() {
const a = i?.ownerDocument;
a?.addEventListener("pointerdown", r, !0), a?.addEventListener("pointerup", n, !0), a?.addEventListener("pointercancel", n, !0);
});
- }, [e]), z(() => e.registerCommand(ur, () => (iS(t.current) && jr(Br), !1), mn), [e]), null;
+ }, [e]), F(() => e.registerCommand(ur, () => (Cv(t.current) && Wr(Hr), !1), bn), [e]), null;
}
-function aS({ onChange: e }) {
- const [t] = ce();
- return z(() => t.registerCommand(ur, () => {
- const r = tl();
+function Mv({ onChange: e }) {
+ const [t] = ae();
+ return F(() => t.registerCommand(ur, () => {
+ const r = dl();
return e?.(r), !1;
- }, Tt), [t, e]), null;
+ }, _t), [t, e]), null;
}
-function cS() {
- const [e] = ce();
- return lS(e), null;
+function Ev() {
+ const [e] = ae();
+ return Av(e), null;
}
-function lS(e) {
- z(() => {
- if (!e.hasNodes([Ze]))
+function Av(e) {
+ F(() => {
+ if (!e.hasNodes([rt]))
throw new Error("ParaNodePlugin: ParaNode not registered on editor!");
- return e.registerNodeTransform(Ze, (t) => uS(t, e));
+ return e.registerNodeTransform(rt, (t) => Pv(t, e));
}, [e]);
}
-function uS(e, t) {
- Ua(t, e.getKey()) && ph(e.getFirstChild()), !(!ae(e) || e.getMarker() !== "b" || e.isEmpty() || !t.getEditorState().read(() => {
+function Pv(e, t) {
+ Ba(t, e.getKey()) && Ah(e.getFirstChild()), !(!ce(e) || e.getMarker() !== "b" || e.isEmpty() || !t.getEditorState().read(() => {
const i = se(e.getKey());
- return ae(i) && (i?.isEmpty() ?? !1);
+ return ce(i) && (i?.isEmpty() ?? !1);
})) && e.clear();
}
-function Bh({ onStateChange: e }) {
- const [t] = ce(), [r, n] = de(t), i = Z(!1), s = Z(!1), o = Z(void 0), a = Z(void 0), c = he(() => {
+function sg({ onStateChange: e }) {
+ const [t] = ae(), [r, n] = de(t), i = Z(!1), s = Z(!1), o = Z(void 0), a = Z(void 0), c = me(() => {
const l = w();
- let u;
+ let d;
if (N(l)) {
- const d = l.anchor.getNode(), f = l.focus.getNode();
- let p = d.getKey() === "root" ? d : nt(d, (k) => {
- const _ = k.getParent();
- return _ !== null && My(_);
+ const u = l.anchor.getNode(), f = l.focus.getNode();
+ let p = u.getKey() === "root" ? u : Xe(u, (b) => {
+ const x = b.getParent();
+ return x !== null && Ky(x);
});
- p === null && (p = d.getTopLevelElementOrThrow()), ts(p) && (p = nt(d, ae) ?? p);
- const m = p.getKey(), g = r.getElementByKey(m), y = Tk(d, f);
- if (y && ux(y) && (u = y.getMarker()), g !== null && (ae(p) || pt(p) || fs(p))) {
- o.current = p.getMarker(), a.current = u, e?.({
+ p === null && (p = u.getTopLevelElementOrThrow()), os(p) && (p = Xe(u, ce) ?? p);
+ const y = p.getKey(), h = r.getElementByKey(y), m = Uk(u, f);
+ if (m && Px(m) && (d = m.getMarker()), h !== null && (ce(p) || pt(p) || ms(p))) {
+ o.current = p.getMarker(), a.current = d, e?.({
canUndo: i.current,
canRedo: s.current,
blockMarker: o.current,
- contextMarker: u
+ contextMarker: d
});
return;
}
}
- a.current = u;
+ a.current = d;
}, [r, e]);
- return z(() => t.registerCommand(ur, (l, u) => (c(), n(u), !1), Wt), [t, c]), z(() => Ve(r.registerUpdateListener(({ editorState: l }) => {
+ return F(() => t.registerCommand(ur, (l, d) => (c(), n(d), !1), xt), [t, c]), F(() => Be(r.registerUpdateListener(({ editorState: l }) => {
l.read(() => {
c();
});
- }), r.registerCommand(Ey, (l) => (i.current = l, e?.({
+ }), r.registerCommand(jy, (l) => (i.current = l, e?.({
canUndo: i.current,
canRedo: s.current,
blockMarker: o.current,
contextMarker: a.current
- }), !1), Wt), r.registerCommand(Ay, (l) => (s.current = l, e?.({
+ }), !1), xt), r.registerCommand(By, (l) => (s.current = l, e?.({
canUndo: i.current,
canRedo: s.current,
blockMarker: o.current,
contextMarker: a.current
- }), !1), Wt)), [c, r, e]), null;
+ }), !1), xt)), [c, r, e]), null;
}
-function Vh(e) {
+function og(e) {
if (e.key === "Enter" && !e.shiftKey)
return "insertParagraph";
if (e.key === "Backspace")
@@ -18771,26 +18775,26 @@ function Vh(e) {
if (e.key.length === 1 && !e.ctrlKey && !e.metaKey)
return "insertText";
}
-function Jr(e) {
- return e ? Se(e) ? e : nt(e, (r) => Se(r)) ?? void 0 : void 0;
+function Qr(e) {
+ return e ? Ce(e) ? e : Xe(e, (r) => Ce(r)) ?? void 0 : void 0;
}
-function Wh(e) {
+function ag(e) {
if (!N(e))
return !1;
const t = /* @__PURE__ */ new Set();
for (const r of e.getNodes()) {
- const n = Jr(r);
+ const n = Qr(r);
n && t.add(n.getKey());
}
return t.size > 1;
}
-function ml(e) {
- return N(e) && e.isCollapsed() && e.anchor.type === "element" || !N(e) && !mc(e) ? !1 : e.getNodes().some((t) => ge(t));
+function El(e) {
+ return N(e) && e.isCollapsed() && e.anchor.type === "element" || !N(e) && !go(e) ? !1 : e.getNodes().some((t) => he(t));
}
-function Hh(e) {
+function cg(e) {
if (!N(e) || !e.isCollapsed())
return !1;
- const { anchor: t } = e, r = t.getNode(), n = Jr(r);
+ const { anchor: t } = e, r = t.getNode(), n = Qr(r);
if (!n || t.offset !== 0)
return !1;
let i = r;
@@ -18801,13 +18805,13 @@ function Hh(e) {
}
return !0;
}
-function Gh(e) {
+function lg(e) {
if (!N(e) || !e.isCollapsed())
return !1;
- const { anchor: t } = e, r = t.getNode(), n = Jr(r);
+ const { anchor: t } = e, r = t.getNode(), n = Qr(r);
if (!n)
return !1;
- if (F(r)) {
+ if (L(r)) {
if (t.offset !== r.getChildrenSize())
return !1;
} else if (t.offset !== r.getTextContentSize())
@@ -18820,42 +18824,42 @@ function Gh(e) {
}
return !0;
}
-function dd(e, t) {
- return !!Wa(e, t);
+function Cd(e, t) {
+ return !!Xa(e, t);
}
-function Wa(e, t) {
+function Xa(e, t) {
if (!N(e) || !e.isCollapsed())
return;
const { anchor: r } = e, n = r.getNode();
- if (r.type === "element" && F(n)) {
+ if (r.type === "element" && L(n)) {
const s = n.getChildren(), o = t === "backward" ? r.offset - 1 : r.offset;
if (o < 0)
return;
const a = s[o];
- return ge(a) ? a : void 0;
+ return he(a) ? a : void 0;
}
if (t === "backward") {
if (r.offset !== 0)
return;
const s = n.getPreviousSibling();
- return ge(s) ? s : void 0;
+ return he(s) ? s : void 0;
}
if (r.offset !== n.getTextContentSize())
return;
const i = n.getNextSibling();
- return ge(i) ? i : void 0;
+ return he(i) ? i : void 0;
}
-function Qs(e, t) {
+function ro(e, t) {
if (!N(e))
return !1;
- const r = Jr(e.anchor.getNode());
+ const r = Qr(e.anchor.getNode());
return r ? !!(t === "backward" ? r.getPreviousSibling() : r.getNextSibling()) : !1;
}
-function Qn(e) {
- return ml(e) || Wh(e);
+function ti(e) {
+ return El(e) || ag(e);
}
-function Jh(e, t) {
- if (ml(e) || Wh(e))
+function ug(e, t) {
+ if (El(e) || ag(e))
return !0;
if (!N(e) || !e.isCollapsed())
return !1;
@@ -18863,44 +18867,44 @@ function Jh(e, t) {
case "insertParagraph":
return !0;
case "deleteBackward":
- return Hh(e) && Qs(e, "backward") || dd(e, "backward");
+ return cg(e) && ro(e, "backward") || Cd(e, "backward");
case "deleteForward":
- return Gh(e) && Qs(e, "forward") || dd(e, "forward");
+ return lg(e) && ro(e, "forward") || Cd(e, "forward");
case "insertText":
return !1;
}
}
-function dS(e, t) {
+function Nv(e, t) {
if (!(!N(e) || !e.isCollapsed())) {
if (t === "deleteBackward") {
- const r = Wa(e, "backward");
+ const r = Xa(e, "backward");
if (r)
return { kind: "verse", node: r };
- if (Hh(e) && Qs(e, "backward")) {
- const n = Jr(e.anchor.getNode());
- if (Se(n))
+ if (cg(e) && ro(e, "backward")) {
+ const n = Qr(e.anchor.getNode());
+ if (Ce(n))
return { kind: "para", node: n };
}
return;
}
if (t === "deleteForward") {
- const r = Wa(e, "forward");
+ const r = Xa(e, "forward");
if (r)
return { kind: "verse", node: r };
- if (Gh(e) && Qs(e, "forward")) {
- const i = Jr(e.anchor.getNode())?.getNextSibling();
- if (Se(i))
+ if (lg(e) && ro(e, "forward")) {
+ const i = Qr(e.anchor.getNode())?.getNextSibling();
+ if (Ce(i))
return { kind: "para", node: i };
}
return;
}
}
}
-function fd(e, t) {
+function vd(e, t) {
if (!e)
return !1;
if (t.kind === "verse")
- return mc(e) && e.has(t.key);
+ return go(e) && e.has(t.key);
if (t.kind === "selection") {
if (!N(e) || e.isCollapsed() || !t.anchor || !t.focus)
return !1;
@@ -18909,49 +18913,49 @@ function fd(e, t) {
}
if (!N(e) || e.isCollapsed())
return !1;
- const r = Jr(e.anchor.getNode()), n = Jr(e.focus.getNode());
+ const r = Qr(e.anchor.getNode()), n = Qr(e.focus.getNode());
return !!r && r.getKey() === t.key && !!n && n.getKey() === t.key;
}
-function Yh(e) {
- if (v(e)) {
+function dg(e) {
+ if (M(e)) {
const t = e.getTextContentSize();
e.select(t, t);
- } else F(e) ? e.selectEnd() : e.selectNext(0, 0);
+ } else L(e) ? e.selectEnd() : e.selectNext(0, 0);
}
-function fS(e) {
+function Ov(e) {
const t = e.getPreviousSibling();
- if (!Se(t))
+ if (!Ce(t))
return;
const r = t.getLastChild(), n = e.getChildren();
- t.append(...n), e.remove(), r ? Yh(r) : yi(t) || t.selectStart();
+ t.append(...n), e.remove(), r ? dg(r) : xi(t) || t.selectStart();
}
-function Xh(e) {
- return ge(e) || Ge(e) ? [] : Se(e) ? e.getChildren().flatMap(Xh) : [e];
+function fg(e) {
+ return he(e) || Ge(e) ? [] : Ce(e) ? e.getChildren().flatMap(fg) : [e];
}
-function pS(e) {
+function wv(e) {
const t = [];
for (const r of e) {
- const n = Xh(r);
- n.length !== 0 && (Se(r) && t.length > 0 && t.push(pe(" ")), t.push(...n));
+ const n = fg(r);
+ n.length !== 0 && (Ce(r) && t.length > 0 && t.push(ge(" ")), t.push(...n));
}
return t;
}
-function pd(e, t) {
+function Sd(e, t) {
(t == null || t > e.length) && (t = e.length);
for (var r = 0, n = Array(t); r < t; r++) n[r] = e[r];
return n;
}
-function hS(e) {
+function qv(e) {
if (Array.isArray(e)) return e;
}
-function gS(e, t) {
+function Rv(e, t) {
var r = e == null ? null : typeof Symbol < "u" && e[Symbol.iterator] || e["@@iterator"];
if (r != null) {
var n, i, s, o, a = [], c = !0, l = !1;
try {
if (s = (r = r.call(e)).next, t !== 0) for (; !(c = (n = s.call(r)).done) && (a.push(n.value), a.length !== t); c = !0) ;
- } catch (u) {
- l = !0, i = u;
+ } catch (d) {
+ l = !0, i = d;
} finally {
try {
if (!c && r.return != null && (o = r.return(), Object(o) !== o)) return;
@@ -18962,142 +18966,142 @@ function gS(e, t) {
return a;
}
}
-function mS() {
+function $v() {
throw new TypeError(`Invalid attempt to destructure non-iterable instance.
In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`);
}
-function yS(e, t) {
- return hS(e) || gS(e, t) || bS(e, t) || mS();
+function Lv(e, t) {
+ return qv(e) || Rv(e, t) || Iv(e, t) || $v();
}
-function bS(e, t) {
+function Iv(e, t) {
if (e) {
- if (typeof e == "string") return pd(e, t);
+ if (typeof e == "string") return Sd(e, t);
var r = {}.toString.call(e).slice(8, -1);
- return r === "Object" && e.constructor && (r = e.constructor.name), r === "Map" || r === "Set" ? Array.from(e) : r === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r) ? pd(e, t) : void 0;
+ return r === "Object" && e.constructor && (r = e.constructor.name), r === "Map" || r === "Set" ? Array.from(e) : r === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r) ? Sd(e, t) : void 0;
}
}
-const Qh = Object.entries, hd = Object.setPrototypeOf, kS = Object.isFrozen, TS = Object.getPrototypeOf, xS = Object.getOwnPropertyDescriptor;
-let et = Object.freeze, it = Object.seal, Hn = Object.create, Zh = typeof Reflect < "u" && Reflect, Ha = Zh.apply, Ga = Zh.construct;
-et || (et = function(t) {
+const pg = Object.entries, Md = Object.setPrototypeOf, Dv = Object.isFrozen, Uv = Object.getPrototypeOf, Fv = Object.getOwnPropertyDescriptor;
+let nt = Object.freeze, ot = Object.seal, Xn = Object.create, hg = typeof Reflect < "u" && Reflect, Qa = hg.apply, Za = hg.construct;
+nt || (nt = function(t) {
return t;
});
-it || (it = function(t) {
+ot || (ot = function(t) {
return t;
});
-Ha || (Ha = function(t, r) {
+Qa || (Qa = function(t, r) {
for (var n = arguments.length, i = new Array(n > 2 ? n - 2 : 0), s = 2; s < n; s++)
i[s - 2] = arguments[s];
return t.apply(r, i);
});
-Ga || (Ga = function(t) {
+Za || (Za = function(t) {
for (var r = arguments.length, n = new Array(r > 1 ? r - 1 : 0), i = 1; i < r; i++)
n[i - 1] = arguments[i];
return new t(...n);
});
-const Bn = Je(Array.prototype.forEach), _S = Je(Array.prototype.lastIndexOf), gd = Je(Array.prototype.pop), Vn = Je(Array.prototype.push), CS = Je(Array.prototype.splice), zr = Array.isArray, $i = Je(String.prototype.toLowerCase), ua = Je(String.prototype.toString), md = Je(String.prototype.match), Pi = Je(String.prototype.replace), yd = Je(String.prototype.indexOf), SS = Je(String.prototype.trim), vS = Je(Number.prototype.toString), MS = Je(Boolean.prototype.toString), bd = typeof BigInt > "u" ? null : Je(BigInt.prototype.toString), kd = typeof Symbol > "u" ? null : Je(Symbol.prototype.toString), Xe = Je(Object.prototype.hasOwnProperty), Ni = Je(Object.prototype.toString), Ye = Je(RegExp.prototype.test), dn = ES(TypeError);
-function Je(e) {
+const Gn = Ye(Array.prototype.forEach), zv = Ye(Array.prototype.lastIndexOf), Ed = Ye(Array.prototype.pop), Jn = Ye(Array.prototype.push), Kv = Ye(Array.prototype.splice), jr = Array.isArray, Fi = Ye(String.prototype.toLowerCase), pa = Ye(String.prototype.toString), Ad = Ye(String.prototype.match), Ri = Ye(String.prototype.replace), Pd = Ye(String.prototype.indexOf), jv = Ye(String.prototype.trim), Bv = Ye(Number.prototype.toString), Vv = Ye(Boolean.prototype.toString), Nd = typeof BigInt > "u" ? null : Ye(BigInt.prototype.toString), Od = typeof Symbol > "u" ? null : Ye(Symbol.prototype.toString), et = Ye(Object.prototype.hasOwnProperty), $i = Ye(Object.prototype.toString), Ze = Ye(RegExp.prototype.test), pn = Wv(TypeError);
+function Ye(e) {
return function(t) {
t instanceof RegExp && (t.lastIndex = 0);
for (var r = arguments.length, n = new Array(r > 1 ? r - 1 : 0), i = 1; i < r; i++)
n[i - 1] = arguments[i];
- return Ha(e, t, n);
+ return Qa(e, t, n);
};
}
-function ES(e) {
+function Wv(e) {
return function() {
for (var t = arguments.length, r = new Array(t), n = 0; n < t; n++)
r[n] = arguments[n];
- return Ga(e, r);
+ return Za(e, r);
};
}
-function fe(e, t) {
- let r = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : $i;
- if (hd && hd(e, null), !zr(t))
+function pe(e, t) {
+ let r = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : Fi;
+ if (Md && Md(e, null), !jr(t))
return e;
let n = t.length;
for (; n--; ) {
let i = t[n];
if (typeof i == "string") {
const s = r(i);
- s !== i && (kS(t) || (t[n] = s), i = s);
+ s !== i && (Dv(t) || (t[n] = s), i = s);
}
e[i] = !0;
}
return e;
}
-function AS(e) {
+function Hv(e) {
for (let t = 0; t < e.length; t++)
- Xe(e, t) || (e[t] = null);
+ et(e, t) || (e[t] = null);
return e;
}
-function st(e) {
- const t = Hn(null);
- for (const n of Qh(e)) {
- var r = yS(n, 2);
+function at(e) {
+ const t = Xn(null);
+ for (const n of pg(e)) {
+ var r = Lv(n, 2);
const i = r[0], s = r[1];
- Xe(e, i) && (zr(s) ? t[i] = AS(s) : s && typeof s == "object" && s.constructor === Object ? t[i] = st(s) : t[i] = s);
+ et(e, i) && (jr(s) ? t[i] = Hv(s) : s && typeof s == "object" && s.constructor === Object ? t[i] = at(s) : t[i] = s);
}
return t;
}
-function PS(e) {
+function Gv(e) {
switch (typeof e) {
case "string":
return e;
case "number":
- return vS(e);
+ return Bv(e);
case "boolean":
- return MS(e);
+ return Vv(e);
case "bigint":
- return bd ? bd(e) : "0";
+ return Nd ? Nd(e) : "0";
case "symbol":
- return kd ? kd(e) : "Symbol()";
+ return Od ? Od(e) : "Symbol()";
case "undefined":
- return Ni(e);
+ return $i(e);
case "function":
case "object": {
if (e === null)
- return Ni(e);
+ return $i(e);
const t = e, r = jt(t, "toString");
if (typeof r == "function") {
const n = r(t);
- return typeof n == "string" ? n : Ni(n);
+ return typeof n == "string" ? n : $i(n);
}
- return Ni(e);
+ return $i(e);
}
default:
- return Ni(e);
+ return $i(e);
}
}
function jt(e, t) {
for (; e !== null; ) {
- const n = xS(e, t);
+ const n = Fv(e, t);
if (n) {
if (n.get)
- return Je(n.get);
+ return Ye(n.get);
if (typeof n.value == "function")
- return Je(n.value);
+ return Ye(n.value);
}
- e = TS(e);
+ e = Uv(e);
}
function r() {
return null;
}
return r;
}
-function NS(e) {
+function Jv(e) {
try {
- return Ye(e, ""), !0;
+ return Ze(e, ""), !0;
} catch {
return !1;
}
}
-const Td = et(["a", "abbr", "acronym", "address", "area", "article", "aside", "audio", "b", "bdi", "bdo", "big", "blink", "blockquote", "body", "br", "button", "canvas", "caption", "center", "cite", "code", "col", "colgroup", "content", "data", "datalist", "dd", "decorator", "del", "details", "dfn", "dialog", "dir", "div", "dl", "dt", "element", "em", "fieldset", "figcaption", "figure", "font", "footer", "form", "h1", "h2", "h3", "h4", "h5", "h6", "head", "header", "hgroup", "hr", "html", "i", "img", "input", "ins", "kbd", "label", "legend", "li", "main", "map", "mark", "marquee", "menu", "menuitem", "meter", "nav", "nobr", "ol", "optgroup", "option", "output", "p", "picture", "pre", "progress", "q", "rp", "rt", "ruby", "s", "samp", "search", "section", "select", "shadow", "slot", "small", "source", "spacer", "span", "strike", "strong", "style", "sub", "summary", "sup", "table", "tbody", "td", "template", "textarea", "tfoot", "th", "thead", "time", "tr", "track", "tt", "u", "ul", "var", "video", "wbr"]), da = et(["svg", "a", "altglyph", "altglyphdef", "altglyphitem", "animatecolor", "animatemotion", "animatetransform", "circle", "clippath", "defs", "desc", "ellipse", "enterkeyhint", "exportparts", "filter", "font", "g", "glyph", "glyphref", "hkern", "image", "inputmode", "line", "lineargradient", "marker", "mask", "metadata", "mpath", "part", "path", "pattern", "polygon", "polyline", "radialgradient", "rect", "stop", "style", "switch", "symbol", "text", "textpath", "title", "tref", "tspan", "view", "vkern"]), fa = et(["feBlend", "feColorMatrix", "feComponentTransfer", "feComposite", "feConvolveMatrix", "feDiffuseLighting", "feDisplacementMap", "feDistantLight", "feDropShadow", "feFlood", "feFuncA", "feFuncB", "feFuncG", "feFuncR", "feGaussianBlur", "feImage", "feMerge", "feMergeNode", "feMorphology", "feOffset", "fePointLight", "feSpecularLighting", "feSpotLight", "feTile", "feTurbulence"]), OS = et(["animate", "color-profile", "cursor", "discard", "font-face", "font-face-format", "font-face-name", "font-face-src", "font-face-uri", "foreignobject", "hatch", "hatchpath", "mesh", "meshgradient", "meshpatch", "meshrow", "missing-glyph", "script", "set", "solidcolor", "unknown", "use"]), pa = et(["math", "menclose", "merror", "mfenced", "mfrac", "mglyph", "mi", "mlabeledtr", "mmultiscripts", "mn", "mo", "mover", "mpadded", "mphantom", "mroot", "mrow", "ms", "mspace", "msqrt", "mstyle", "msub", "msup", "msubsup", "mtable", "mtd", "mtext", "mtr", "munder", "munderover", "mprescripts"]), wS = et(["maction", "maligngroup", "malignmark", "mlongdiv", "mscarries", "mscarry", "msgroup", "mstack", "msline", "msrow", "semantics", "annotation", "annotation-xml", "mprescripts", "none"]), xd = et(["#text"]), _d = et(["accept", "action", "align", "alt", "autocapitalize", "autocomplete", "autopictureinpicture", "autoplay", "background", "bgcolor", "border", "capture", "cellpadding", "cellspacing", "checked", "cite", "class", "clear", "color", "cols", "colspan", "command", "commandfor", "controls", "controlslist", "coords", "crossorigin", "datetime", "decoding", "default", "dir", "disabled", "disablepictureinpicture", "disableremoteplayback", "download", "draggable", "enctype", "enterkeyhint", "exportparts", "face", "for", "headers", "height", "hidden", "high", "href", "hreflang", "id", "inert", "inputmode", "integrity", "ismap", "kind", "label", "lang", "list", "loading", "loop", "low", "max", "maxlength", "media", "method", "min", "minlength", "multiple", "muted", "name", "nonce", "noshade", "novalidate", "nowrap", "open", "optimum", "part", "pattern", "placeholder", "playsinline", "popover", "popovertarget", "popovertargetaction", "poster", "preload", "pubdate", "radiogroup", "readonly", "rel", "required", "rev", "reversed", "role", "rows", "rowspan", "spellcheck", "scope", "selected", "shape", "size", "sizes", "slot", "span", "srclang", "start", "src", "srcset", "step", "style", "summary", "tabindex", "title", "translate", "type", "usemap", "valign", "value", "width", "wrap", "xmlns"]), ha = et(["accent-height", "accumulate", "additive", "alignment-baseline", "amplitude", "ascent", "attributename", "attributetype", "azimuth", "basefrequency", "baseline-shift", "begin", "bias", "by", "class", "clip", "clippathunits", "clip-path", "clip-rule", "color", "color-interpolation", "color-interpolation-filters", "color-profile", "color-rendering", "cx", "cy", "d", "dx", "dy", "diffuseconstant", "direction", "display", "divisor", "dominant-baseline", "dur", "edgemode", "elevation", "end", "exponent", "fill", "fill-opacity", "fill-rule", "filter", "filterunits", "flood-color", "flood-opacity", "font-family", "font-size", "font-size-adjust", "font-stretch", "font-style", "font-variant", "font-weight", "fx", "fy", "g1", "g2", "glyph-name", "glyphref", "gradientunits", "gradienttransform", "height", "href", "id", "image-rendering", "in", "in2", "intercept", "k", "k1", "k2", "k3", "k4", "kerning", "keypoints", "keysplines", "keytimes", "lang", "lengthadjust", "letter-spacing", "kernelmatrix", "kernelunitlength", "lighting-color", "local", "marker-end", "marker-mid", "marker-start", "markerheight", "markerunits", "markerwidth", "maskcontentunits", "maskunits", "max", "mask", "mask-type", "media", "method", "mode", "min", "name", "numoctaves", "offset", "operator", "opacity", "order", "orient", "orientation", "origin", "overflow", "paint-order", "path", "pathlength", "patterncontentunits", "patterntransform", "patternunits", "points", "preservealpha", "preserveaspectratio", "primitiveunits", "r", "rx", "ry", "radius", "refx", "refy", "repeatcount", "repeatdur", "restart", "result", "rotate", "scale", "seed", "shape-rendering", "slope", "specularconstant", "specularexponent", "spreadmethod", "startoffset", "stddeviation", "stitchtiles", "stop-color", "stop-opacity", "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin", "stroke-miterlimit", "stroke-opacity", "stroke", "stroke-width", "style", "surfacescale", "systemlanguage", "tabindex", "tablevalues", "targetx", "targety", "transform", "transform-origin", "text-anchor", "text-decoration", "text-orientation", "text-rendering", "textlength", "type", "u1", "u2", "unicode", "values", "viewbox", "visibility", "version", "vert-adv-y", "vert-origin-x", "vert-origin-y", "width", "word-spacing", "wrap", "writing-mode", "xchannelselector", "ychannelselector", "x", "x1", "x2", "xmlns", "y", "y1", "y2", "z", "zoomandpan"]), Cd = et(["accent", "accentunder", "align", "bevelled", "close", "columnalign", "columnlines", "columnspacing", "columnspan", "denomalign", "depth", "dir", "display", "displaystyle", "encoding", "fence", "frame", "height", "href", "id", "largeop", "length", "linethickness", "lquote", "lspace", "mathbackground", "mathcolor", "mathsize", "mathvariant", "maxsize", "minsize", "movablelimits", "notation", "numalign", "open", "rowalign", "rowlines", "rowspacing", "rowspan", "rspace", "rquote", "scriptlevel", "scriptminsize", "scriptsizemultiplier", "selection", "separator", "separators", "stretchy", "subscriptshift", "supscriptshift", "symmetric", "voffset", "width", "xmlns"]), vs = et(["xlink:href", "xml:id", "xlink:title", "xml:space", "xmlns:xlink"]), qS = it(/{{[\w\W]*|^[\w\W]*}}/g), RS = it(/<%[\w\W]*|^[\w\W]*%>/g), $S = it(/\${[\w\W]*/g), IS = it(/^data-[\-\w.\u00B7-\uFFFF]+$/), LS = it(/^aria-[\-\w]+$/), Sd = it(
+const wd = nt(["a", "abbr", "acronym", "address", "area", "article", "aside", "audio", "b", "bdi", "bdo", "big", "blink", "blockquote", "body", "br", "button", "canvas", "caption", "center", "cite", "code", "col", "colgroup", "content", "data", "datalist", "dd", "decorator", "del", "details", "dfn", "dialog", "dir", "div", "dl", "dt", "element", "em", "fieldset", "figcaption", "figure", "font", "footer", "form", "h1", "h2", "h3", "h4", "h5", "h6", "head", "header", "hgroup", "hr", "html", "i", "img", "input", "ins", "kbd", "label", "legend", "li", "main", "map", "mark", "marquee", "menu", "menuitem", "meter", "nav", "nobr", "ol", "optgroup", "option", "output", "p", "picture", "pre", "progress", "q", "rp", "rt", "ruby", "s", "samp", "search", "section", "select", "shadow", "slot", "small", "source", "spacer", "span", "strike", "strong", "style", "sub", "summary", "sup", "table", "tbody", "td", "template", "textarea", "tfoot", "th", "thead", "time", "tr", "track", "tt", "u", "ul", "var", "video", "wbr"]), ha = nt(["svg", "a", "altglyph", "altglyphdef", "altglyphitem", "animatecolor", "animatemotion", "animatetransform", "circle", "clippath", "defs", "desc", "ellipse", "enterkeyhint", "exportparts", "filter", "font", "g", "glyph", "glyphref", "hkern", "image", "inputmode", "line", "lineargradient", "marker", "mask", "metadata", "mpath", "part", "path", "pattern", "polygon", "polyline", "radialgradient", "rect", "stop", "style", "switch", "symbol", "text", "textpath", "title", "tref", "tspan", "view", "vkern"]), ga = nt(["feBlend", "feColorMatrix", "feComponentTransfer", "feComposite", "feConvolveMatrix", "feDiffuseLighting", "feDisplacementMap", "feDistantLight", "feDropShadow", "feFlood", "feFuncA", "feFuncB", "feFuncG", "feFuncR", "feGaussianBlur", "feImage", "feMerge", "feMergeNode", "feMorphology", "feOffset", "fePointLight", "feSpecularLighting", "feSpotLight", "feTile", "feTurbulence"]), Yv = nt(["animate", "color-profile", "cursor", "discard", "font-face", "font-face-format", "font-face-name", "font-face-src", "font-face-uri", "foreignobject", "hatch", "hatchpath", "mesh", "meshgradient", "meshpatch", "meshrow", "missing-glyph", "script", "set", "solidcolor", "unknown", "use"]), ma = nt(["math", "menclose", "merror", "mfenced", "mfrac", "mglyph", "mi", "mlabeledtr", "mmultiscripts", "mn", "mo", "mover", "mpadded", "mphantom", "mroot", "mrow", "ms", "mspace", "msqrt", "mstyle", "msub", "msup", "msubsup", "mtable", "mtd", "mtext", "mtr", "munder", "munderover", "mprescripts"]), Xv = nt(["maction", "maligngroup", "malignmark", "mlongdiv", "mscarries", "mscarry", "msgroup", "mstack", "msline", "msrow", "semantics", "annotation", "annotation-xml", "mprescripts", "none"]), qd = nt(["#text"]), Rd = nt(["accept", "action", "align", "alt", "autocapitalize", "autocomplete", "autopictureinpicture", "autoplay", "background", "bgcolor", "border", "capture", "cellpadding", "cellspacing", "checked", "cite", "class", "clear", "color", "cols", "colspan", "command", "commandfor", "controls", "controlslist", "coords", "crossorigin", "datetime", "decoding", "default", "dir", "disabled", "disablepictureinpicture", "disableremoteplayback", "download", "draggable", "enctype", "enterkeyhint", "exportparts", "face", "for", "headers", "height", "hidden", "high", "href", "hreflang", "id", "inert", "inputmode", "integrity", "ismap", "kind", "label", "lang", "list", "loading", "loop", "low", "max", "maxlength", "media", "method", "min", "minlength", "multiple", "muted", "name", "nonce", "noshade", "novalidate", "nowrap", "open", "optimum", "part", "pattern", "placeholder", "playsinline", "popover", "popovertarget", "popovertargetaction", "poster", "preload", "pubdate", "radiogroup", "readonly", "rel", "required", "rev", "reversed", "role", "rows", "rowspan", "spellcheck", "scope", "selected", "shape", "size", "sizes", "slot", "span", "srclang", "start", "src", "srcset", "step", "style", "summary", "tabindex", "title", "translate", "type", "usemap", "valign", "value", "width", "wrap", "xmlns"]), ya = nt(["accent-height", "accumulate", "additive", "alignment-baseline", "amplitude", "ascent", "attributename", "attributetype", "azimuth", "basefrequency", "baseline-shift", "begin", "bias", "by", "class", "clip", "clippathunits", "clip-path", "clip-rule", "color", "color-interpolation", "color-interpolation-filters", "color-profile", "color-rendering", "cx", "cy", "d", "dx", "dy", "diffuseconstant", "direction", "display", "divisor", "dominant-baseline", "dur", "edgemode", "elevation", "end", "exponent", "fill", "fill-opacity", "fill-rule", "filter", "filterunits", "flood-color", "flood-opacity", "font-family", "font-size", "font-size-adjust", "font-stretch", "font-style", "font-variant", "font-weight", "fx", "fy", "g1", "g2", "glyph-name", "glyphref", "gradientunits", "gradienttransform", "height", "href", "id", "image-rendering", "in", "in2", "intercept", "k", "k1", "k2", "k3", "k4", "kerning", "keypoints", "keysplines", "keytimes", "lang", "lengthadjust", "letter-spacing", "kernelmatrix", "kernelunitlength", "lighting-color", "local", "marker-end", "marker-mid", "marker-start", "markerheight", "markerunits", "markerwidth", "maskcontentunits", "maskunits", "max", "mask", "mask-type", "media", "method", "mode", "min", "name", "numoctaves", "offset", "operator", "opacity", "order", "orient", "orientation", "origin", "overflow", "paint-order", "path", "pathlength", "patterncontentunits", "patterntransform", "patternunits", "points", "preservealpha", "preserveaspectratio", "primitiveunits", "r", "rx", "ry", "radius", "refx", "refy", "repeatcount", "repeatdur", "restart", "result", "rotate", "scale", "seed", "shape-rendering", "slope", "specularconstant", "specularexponent", "spreadmethod", "startoffset", "stddeviation", "stitchtiles", "stop-color", "stop-opacity", "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin", "stroke-miterlimit", "stroke-opacity", "stroke", "stroke-width", "style", "surfacescale", "systemlanguage", "tabindex", "tablevalues", "targetx", "targety", "transform", "transform-origin", "text-anchor", "text-decoration", "text-orientation", "text-rendering", "textlength", "type", "u1", "u2", "unicode", "values", "viewbox", "visibility", "version", "vert-adv-y", "vert-origin-x", "vert-origin-y", "width", "word-spacing", "wrap", "writing-mode", "xchannelselector", "ychannelselector", "x", "x1", "x2", "xmlns", "y", "y1", "y2", "z", "zoomandpan"]), $d = nt(["accent", "accentunder", "align", "bevelled", "close", "columnalign", "columnlines", "columnspacing", "columnspan", "denomalign", "depth", "dir", "display", "displaystyle", "encoding", "fence", "frame", "height", "href", "id", "largeop", "length", "linethickness", "lquote", "lspace", "mathbackground", "mathcolor", "mathsize", "mathvariant", "maxsize", "minsize", "movablelimits", "notation", "numalign", "open", "rowalign", "rowlines", "rowspacing", "rowspan", "rspace", "rquote", "scriptlevel", "scriptminsize", "scriptsizemultiplier", "selection", "separator", "separators", "stretchy", "subscriptshift", "supscriptshift", "symmetric", "voffset", "width", "xmlns"]), Ns = nt(["xlink:href", "xml:id", "xlink:title", "xml:space", "xmlns:xlink"]), Qv = ot(/{{[\w\W]*|^[\w\W]*}}/g), Zv = ot(/<%[\w\W]*|^[\w\W]*%>/g), eS = ot(/\${[\w\W]*/g), tS = ot(/^data-[\-\w.\u00B7-\uFFFF]+$/), rS = ot(/^aria-[\-\w]+$/), Ld = ot(
/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|matrix):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i
// eslint-disable-line no-useless-escape
-), DS = it(/^(?:\w+script|data):/i), US = it(
+), nS = ot(/^(?:\w+script|data):/i), iS = ot(
/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g
// eslint-disable-line no-control-regex
-), FS = it(/^html$/i), zS = it(/^[a-z][.\w]*(-[.\w]+)+$/i), vd = it(/<[/\w!]/g), Md = it(/<[/\w]/g), KS = it(/<\/no(script|embed|frames)/i), jS = it(/\/>/i), Mt = {
+), sS = ot(/^html$/i), oS = ot(/^[a-z][.\w]*(-[.\w]+)+$/i), Id = ot(/<[/\w!]/g), Dd = ot(/<[/\w]/g), aS = ot(/<\/no(script|embed|frames)/i), cS = ot(/\/>/i), Et = {
element: 1,
attribute: 2,
text: 3,
@@ -19113,9 +19117,9 @@ const Td = et(["a", "abbr", "acronym", "address", "area", "article", "aside", "a
documentFragment: 11,
notation: 12
// Deprecated
-}, BS = function() {
+}, lS = function() {
return typeof window > "u" ? null : window;
-}, VS = function(t, r) {
+}, uS = function(t, r) {
if (typeof t != "object" || typeof t.createPolicy != "function")
return null;
let n = null;
@@ -19134,7 +19138,7 @@ const Td = et(["a", "abbr", "acronym", "address", "area", "article", "aside", "a
} catch {
return console.warn("TrustedTypes policy " + s + " could not be created."), null;
}
-}, Ed = function() {
+}, Ud = function() {
return {
afterSanitizeAttributes: [],
afterSanitizeElements: [],
@@ -19146,53 +19150,53 @@ const Td = et(["a", "abbr", "acronym", "address", "area", "article", "aside", "a
uponSanitizeElement: [],
uponSanitizeShadowNode: []
};
-}, Dr = function(t, r, n, i) {
- return Xe(t, r) && zr(t[r]) ? fe(i.base ? st(i.base) : {}, t[r], i.transform) : n;
+}, Fr = function(t, r, n, i) {
+ return et(t, r) && jr(t[r]) ? pe(i.base ? at(i.base) : {}, t[r], i.transform) : n;
};
-function eg() {
- let e = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : BS();
- const t = (U) => eg(U);
- if (t.version = "3.4.13", t.removed = [], !e || !e.document || e.document.nodeType !== Mt.document || !e.Element)
+function gg() {
+ let e = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : lS();
+ const t = (U) => gg(U);
+ if (t.version = "3.4.13", t.removed = [], !e || !e.document || e.document.nodeType !== Et.document || !e.Element)
return t.isSupported = !1, t;
let r = e.document;
const n = r, i = n.currentScript;
e.DocumentFragment;
const s = e.HTMLTemplateElement, o = e.Node, a = e.Element, c = e.NodeFilter, l = e.NamedNodeMap;
l === void 0 && (e.NamedNodeMap || e.MozNamedAttrMap), e.HTMLFormElement;
- const u = e.DOMParser, d = e.trustedTypes, f = a.prototype, p = jt(f, "cloneNode"), m = jt(f, "remove"), g = jt(f, "nextSibling"), y = jt(f, "childNodes"), k = jt(f, "parentNode"), _ = jt(f, "shadowRoot"), S = jt(f, "attributes"), P = o && o.prototype ? jt(o.prototype, "nodeType") : null, A = o && o.prototype ? jt(o.prototype, "nodeName") : null, B = o && o.prototype ? jt(o.prototype, "ownerDocument") : null;
+ const d = e.DOMParser, u = e.trustedTypes, f = a.prototype, p = jt(f, "cloneNode"), y = jt(f, "remove"), h = jt(f, "nextSibling"), m = jt(f, "childNodes"), b = jt(f, "parentNode"), x = jt(f, "shadowRoot"), _ = jt(f, "attributes"), A = o && o.prototype ? jt(o.prototype, "nodeType") : null, E = o && o.prototype ? jt(o.prototype, "nodeName") : null, D = o && o.prototype ? jt(o.prototype, "ownerDocument") : null;
if (typeof s == "function") {
const U = r.createElement("template");
U.content && U.content.ownerDocument && (r = U.content.ownerDocument);
}
- let M, q = "", $, J = !1, Q = 0;
- const Me = function() {
- if (Q > 0)
- throw dn('A configured TRUSTED_TYPES_POLICY callback (createHTML or createScriptURL) must not call DOMPurify.sanitize, as that causes infinite recursion. Do not pass a policy whose callbacks wrap DOMPurify as TRUSTED_TYPES_POLICY; see the "DOMPurify and Trusted Types" section of the README.');
- }, re = function(h) {
- Me(), Q++;
+ let V, j = "", v, K = !1, W = 0;
+ const fe = function() {
+ if (W > 0)
+ throw pn('A configured TRUSTED_TYPES_POLICY callback (createHTML or createScriptURL) must not call DOMPurify.sanitize, as that causes infinite recursion. Do not pass a policy whose callbacks wrap DOMPurify as TRUSTED_TYPES_POLICY; see the "DOMPurify and Trusted Types" section of the README.');
+ }, X = function(g) {
+ fe(), W++;
try {
- return M.createHTML(h);
+ return V.createHTML(g);
} finally {
- Q--;
+ W--;
}
- }, qe = function(h) {
- Me(), Q++;
+ }, Re = function(g) {
+ fe(), W++;
try {
- return M.createScriptURL(h);
+ return V.createScriptURL(g);
} finally {
- Q--;
+ W--;
}
}, be = function() {
- return J || ($ = VS(d, i), J = !0), $;
- }, tr = r, Re = tr.implementation, en = tr.createNodeIterator, gr = tr.createDocumentFragment, vt = tr.getElementsByTagName, te = n.importNode;
- let E = Ed();
- t.isSupported = typeof Qh == "function" && typeof k == "function" && Re && Re.createHTMLDocument !== void 0;
- const Y = qS, le = RS, W = $S, xe = IS, ht = LS, zt = DS, gt = US, We = zS;
- let ct = Sd, ue = null;
- const Rn = fe({}, [...Td, ...da, ...fa, ...pa, ...xd]);
- let ye = null;
- const Ti = fe({}, [..._d, ...ha, ...Cd, ...vs]);
- let ve = Object.seal(Hn(null, {
+ return K || (v = uS(u, i), K = !0), v;
+ }, er = r, $e = er.implementation, nn = er.createNodeIterator, gr = er.createDocumentFragment, Mt = er.getElementsByTagName, re = n.importNode;
+ let P = Ud();
+ t.isSupported = typeof pg == "function" && typeof b == "function" && $e && $e.createHTMLDocument !== void 0;
+ const G = Qv, ue = Zv, Me = eS, Q = tS, ve = rS, mr = nS, wt = iS, sn = oS;
+ let We = Ld, le = null;
+ const gt = pe({}, [...wd, ...ha, ...ga, ...ma, ...qd]);
+ let xe = null;
+ const yr = pe({}, [...Rd, ...ya, ...$d, ...Ns]);
+ let Ne = Object.seal(Xn(null, {
tagNameCheck: {
writable: !0,
configurable: !1,
@@ -19211,8 +19215,8 @@ function eg() {
enumerable: !0,
value: !1
}
- })), wr = null, qr = null;
- const rr = Object.seal(Hn(null, {
+ })), br = null, vi = null;
+ const mt = Object.seal(Xn(null, {
tagCheck: {
writable: !0,
configurable: !1,
@@ -19226,10 +19230,10 @@ function eg() {
value: null
}
}));
- let Rr = !0, tn = !0, xi = !1, ms = !0, Kt = !1, R = !0, K = !1, G = !1, X = null, _e = null, lt = !1, Ot = !1, rn = !1, nn = !1, zl = !0, Kl = !1;
- const jl = "user-content-";
- let Io = !0, ys = !1, $n = {}, nr = null;
- const Lo = fe({}, [
+ let xs = !0, kr = !0, Dn = !1, Si = !0, tr = !1, Kt = !0, q = !1, B = !1, J = null, ee = null, Se = !1, Qe = !1, rr = !1, on = !1, Mi = !0, tu = !1;
+ const ru = "user-content-";
+ let Uo = !0, _s = !1, Un = {}, nr = null;
+ const Fo = pe({}, [
"annotation-xml",
"audio",
"colgroup",
@@ -19266,244 +19270,244 @@ function eg() {
"video",
"xmp"
]);
- let Bl = null;
- const Vl = fe({}, ["audio", "video", "img", "source", "image", "track"]);
- let Do = null;
- const Wl = fe({}, ["alt", "class", "for", "id", "label", "name", "pattern", "placeholder", "role", "summary", "title", "value", "style", "xmlns"]), bs = "http://www.w3.org/1998/Math/MathML", ks = "http://www.w3.org/2000/svg", ir = "http://www.w3.org/1999/xhtml";
- let In = ir, Uo = !1, Fo = null;
- const Dm = fe({}, [bs, ks, ir], ua), Hl = et(["mi", "mo", "mn", "ms", "mtext"]);
- let zo = fe({}, Hl);
- const Gl = et(["annotation-xml"]);
- let Ko = fe({}, Gl);
- const Um = fe({}, ["title", "style", "font", "a", "script"]);
- let _i = null;
- const Fm = ["application/xhtml+xml", "text/html"], zm = "text/html";
- let $e = null, Ln = null;
- const Km = r.createElement("form"), Jl = function(h) {
- return h instanceof RegExp || h instanceof Function;
- }, jo = function() {
- let h = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {};
- if (Ln && Ln === h)
+ let nu = null;
+ const iu = pe({}, ["audio", "video", "img", "source", "image", "track"]);
+ let zo = null;
+ const su = pe({}, ["alt", "class", "for", "id", "label", "name", "pattern", "placeholder", "role", "summary", "title", "value", "style", "xmlns"]), Cs = "http://www.w3.org/1998/Math/MathML", vs = "http://www.w3.org/2000/svg", ir = "http://www.w3.org/1999/xhtml";
+ let Fn = ir, Ko = !1, jo = null;
+ const ty = pe({}, [Cs, vs, ir], pa), ou = nt(["mi", "mo", "mn", "ms", "mtext"]);
+ let Bo = pe({}, ou);
+ const au = nt(["annotation-xml"]);
+ let Vo = pe({}, au);
+ const ry = pe({}, ["title", "style", "font", "a", "script"]);
+ let Ei = null;
+ const ny = ["application/xhtml+xml", "text/html"], iy = "text/html";
+ let Le = null, zn = null;
+ const sy = r.createElement("form"), cu = function(g) {
+ return g instanceof RegExp || g instanceof Function;
+ }, Wo = function() {
+ let g = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {};
+ if (zn && zn === g)
return;
- (!h || typeof h != "object") && (h = {}), h = st(h), _i = // eslint-disable-next-line unicorn/prefer-includes
- Fm.indexOf(h.PARSER_MEDIA_TYPE) === -1 ? zm : h.PARSER_MEDIA_TYPE, $e = _i === "application/xhtml+xml" ? ua : $i, ue = Dr(h, "ALLOWED_TAGS", Rn, {
- transform: $e
- }), ye = Dr(h, "ALLOWED_ATTR", Ti, {
- transform: $e
- }), Fo = Dr(h, "ALLOWED_NAMESPACES", Dm, {
- transform: ua
- }), Do = Dr(h, "ADD_URI_SAFE_ATTR", Wl, {
- transform: $e,
- base: Wl
- }), Bl = Dr(h, "ADD_DATA_URI_TAGS", Vl, {
- transform: $e,
- base: Vl
- }), nr = Dr(h, "FORBID_CONTENTS", Lo, {
- transform: $e
- }), wr = Dr(h, "FORBID_TAGS", st({}), {
- transform: $e
- }), qr = Dr(h, "FORBID_ATTR", st({}), {
- transform: $e
- }), $n = Xe(h, "USE_PROFILES") ? h.USE_PROFILES && typeof h.USE_PROFILES == "object" ? st(h.USE_PROFILES) : h.USE_PROFILES : !1, Rr = h.ALLOW_ARIA_ATTR !== !1, tn = h.ALLOW_DATA_ATTR !== !1, xi = h.ALLOW_UNKNOWN_PROTOCOLS || !1, ms = h.ALLOW_SELF_CLOSE_IN_ATTR !== !1, Kt = h.SAFE_FOR_TEMPLATES || !1, R = h.SAFE_FOR_XML !== !1, K = h.WHOLE_DOCUMENT || !1, Ot = h.RETURN_DOM || !1, rn = h.RETURN_DOM_FRAGMENT || !1, nn = h.RETURN_TRUSTED_TYPE || !1, lt = h.FORCE_BODY || !1, zl = h.SANITIZE_DOM !== !1, Kl = h.SANITIZE_NAMED_PROPS || !1, Io = h.KEEP_CONTENT !== !1, ys = h.IN_PLACE || !1, ct = NS(h.ALLOWED_URI_REGEXP) ? h.ALLOWED_URI_REGEXP : Sd, In = typeof h.NAMESPACE == "string" ? h.NAMESPACE : ir, zo = Xe(h, "MATHML_TEXT_INTEGRATION_POINTS") && h.MATHML_TEXT_INTEGRATION_POINTS && typeof h.MATHML_TEXT_INTEGRATION_POINTS == "object" ? st(h.MATHML_TEXT_INTEGRATION_POINTS) : fe({}, Hl), Ko = Xe(h, "HTML_INTEGRATION_POINTS") && h.HTML_INTEGRATION_POINTS && typeof h.HTML_INTEGRATION_POINTS == "object" ? st(h.HTML_INTEGRATION_POINTS) : fe({}, Gl);
- const x = Xe(h, "CUSTOM_ELEMENT_HANDLING") && h.CUSTOM_ELEMENT_HANDLING && typeof h.CUSTOM_ELEMENT_HANDLING == "object" ? st(h.CUSTOM_ELEMENT_HANDLING) : Hn(null);
- if (ve = Hn(null), Xe(x, "tagNameCheck") && Jl(x.tagNameCheck) && (ve.tagNameCheck = x.tagNameCheck), Xe(x, "attributeNameCheck") && Jl(x.attributeNameCheck) && (ve.attributeNameCheck = x.attributeNameCheck), Xe(x, "allowCustomizedBuiltInElements") && typeof x.allowCustomizedBuiltInElements == "boolean" && (ve.allowCustomizedBuiltInElements = x.allowCustomizedBuiltInElements), it(ve), Kt && (tn = !1), rn && (Ot = !0), $n && (ue = fe({}, xd), ye = Hn(null), $n.html === !0 && (fe(ue, Td), fe(ye, _d)), $n.svg === !0 && (fe(ue, da), fe(ye, ha), fe(ye, vs)), $n.svgFilters === !0 && (fe(ue, fa), fe(ye, ha), fe(ye, vs)), $n.mathMl === !0 && (fe(ue, pa), fe(ye, Cd), fe(ye, vs))), rr.tagCheck = null, rr.attributeCheck = null, Xe(h, "ADD_TAGS") && (typeof h.ADD_TAGS == "function" ? rr.tagCheck = h.ADD_TAGS : zr(h.ADD_TAGS) && (ue === Rn && (ue = st(ue)), fe(ue, h.ADD_TAGS, $e))), Xe(h, "ADD_ATTR") && (typeof h.ADD_ATTR == "function" ? rr.attributeCheck = h.ADD_ATTR : zr(h.ADD_ATTR) && (ye === Ti && (ye = st(ye)), fe(ye, h.ADD_ATTR, $e))), Xe(h, "ADD_URI_SAFE_ATTR") && zr(h.ADD_URI_SAFE_ATTR) && fe(Do, h.ADD_URI_SAFE_ATTR, $e), Xe(h, "FORBID_CONTENTS") && zr(h.FORBID_CONTENTS) && (nr === Lo && (nr = st(nr)), fe(nr, h.FORBID_CONTENTS, $e)), Xe(h, "ADD_FORBID_CONTENTS") && zr(h.ADD_FORBID_CONTENTS) && (nr === Lo && (nr = st(nr)), fe(nr, h.ADD_FORBID_CONTENTS, $e)), Io && (ue["#text"] = !0), K && fe(ue, ["html", "head", "body"]), ue.table && (fe(ue, ["tbody"]), delete wr.tbody), h.TRUSTED_TYPES_POLICY) {
- if (typeof h.TRUSTED_TYPES_POLICY.createHTML != "function")
- throw dn('TRUSTED_TYPES_POLICY configuration option must provide a "createHTML" hook.');
- if (typeof h.TRUSTED_TYPES_POLICY.createScriptURL != "function")
- throw dn('TRUSTED_TYPES_POLICY configuration option must provide a "createScriptURL" hook.');
- const L = M;
- M = h.TRUSTED_TYPES_POLICY;
+ (!g || typeof g != "object") && (g = {}), g = at(g), Ei = // eslint-disable-next-line unicorn/prefer-includes
+ ny.indexOf(g.PARSER_MEDIA_TYPE) === -1 ? iy : g.PARSER_MEDIA_TYPE, Le = Ei === "application/xhtml+xml" ? pa : Fi, le = Fr(g, "ALLOWED_TAGS", gt, {
+ transform: Le
+ }), xe = Fr(g, "ALLOWED_ATTR", yr, {
+ transform: Le
+ }), jo = Fr(g, "ALLOWED_NAMESPACES", ty, {
+ transform: pa
+ }), zo = Fr(g, "ADD_URI_SAFE_ATTR", su, {
+ transform: Le,
+ base: su
+ }), nu = Fr(g, "ADD_DATA_URI_TAGS", iu, {
+ transform: Le,
+ base: iu
+ }), nr = Fr(g, "FORBID_CONTENTS", Fo, {
+ transform: Le
+ }), br = Fr(g, "FORBID_TAGS", at({}), {
+ transform: Le
+ }), vi = Fr(g, "FORBID_ATTR", at({}), {
+ transform: Le
+ }), Un = et(g, "USE_PROFILES") ? g.USE_PROFILES && typeof g.USE_PROFILES == "object" ? at(g.USE_PROFILES) : g.USE_PROFILES : !1, xs = g.ALLOW_ARIA_ATTR !== !1, kr = g.ALLOW_DATA_ATTR !== !1, Dn = g.ALLOW_UNKNOWN_PROTOCOLS || !1, Si = g.ALLOW_SELF_CLOSE_IN_ATTR !== !1, tr = g.SAFE_FOR_TEMPLATES || !1, Kt = g.SAFE_FOR_XML !== !1, q = g.WHOLE_DOCUMENT || !1, Qe = g.RETURN_DOM || !1, rr = g.RETURN_DOM_FRAGMENT || !1, on = g.RETURN_TRUSTED_TYPE || !1, Se = g.FORCE_BODY || !1, Mi = g.SANITIZE_DOM !== !1, tu = g.SANITIZE_NAMED_PROPS || !1, Uo = g.KEEP_CONTENT !== !1, _s = g.IN_PLACE || !1, We = Jv(g.ALLOWED_URI_REGEXP) ? g.ALLOWED_URI_REGEXP : Ld, Fn = typeof g.NAMESPACE == "string" ? g.NAMESPACE : ir, Bo = et(g, "MATHML_TEXT_INTEGRATION_POINTS") && g.MATHML_TEXT_INTEGRATION_POINTS && typeof g.MATHML_TEXT_INTEGRATION_POINTS == "object" ? at(g.MATHML_TEXT_INTEGRATION_POINTS) : pe({}, ou), Vo = et(g, "HTML_INTEGRATION_POINTS") && g.HTML_INTEGRATION_POINTS && typeof g.HTML_INTEGRATION_POINTS == "object" ? at(g.HTML_INTEGRATION_POINTS) : pe({}, au);
+ const C = et(g, "CUSTOM_ELEMENT_HANDLING") && g.CUSTOM_ELEMENT_HANDLING && typeof g.CUSTOM_ELEMENT_HANDLING == "object" ? at(g.CUSTOM_ELEMENT_HANDLING) : Xn(null);
+ if (Ne = Xn(null), et(C, "tagNameCheck") && cu(C.tagNameCheck) && (Ne.tagNameCheck = C.tagNameCheck), et(C, "attributeNameCheck") && cu(C.attributeNameCheck) && (Ne.attributeNameCheck = C.attributeNameCheck), et(C, "allowCustomizedBuiltInElements") && typeof C.allowCustomizedBuiltInElements == "boolean" && (Ne.allowCustomizedBuiltInElements = C.allowCustomizedBuiltInElements), ot(Ne), tr && (kr = !1), rr && (Qe = !0), Un && (le = pe({}, qd), xe = Xn(null), Un.html === !0 && (pe(le, wd), pe(xe, Rd)), Un.svg === !0 && (pe(le, ha), pe(xe, ya), pe(xe, Ns)), Un.svgFilters === !0 && (pe(le, ga), pe(xe, ya), pe(xe, Ns)), Un.mathMl === !0 && (pe(le, ma), pe(xe, $d), pe(xe, Ns))), mt.tagCheck = null, mt.attributeCheck = null, et(g, "ADD_TAGS") && (typeof g.ADD_TAGS == "function" ? mt.tagCheck = g.ADD_TAGS : jr(g.ADD_TAGS) && (le === gt && (le = at(le)), pe(le, g.ADD_TAGS, Le))), et(g, "ADD_ATTR") && (typeof g.ADD_ATTR == "function" ? mt.attributeCheck = g.ADD_ATTR : jr(g.ADD_ATTR) && (xe === yr && (xe = at(xe)), pe(xe, g.ADD_ATTR, Le))), et(g, "ADD_URI_SAFE_ATTR") && jr(g.ADD_URI_SAFE_ATTR) && pe(zo, g.ADD_URI_SAFE_ATTR, Le), et(g, "FORBID_CONTENTS") && jr(g.FORBID_CONTENTS) && (nr === Fo && (nr = at(nr)), pe(nr, g.FORBID_CONTENTS, Le)), et(g, "ADD_FORBID_CONTENTS") && jr(g.ADD_FORBID_CONTENTS) && (nr === Fo && (nr = at(nr)), pe(nr, g.ADD_FORBID_CONTENTS, Le)), Uo && (le["#text"] = !0), q && pe(le, ["html", "head", "body"]), le.table && (pe(le, ["tbody"]), delete br.tbody), g.TRUSTED_TYPES_POLICY) {
+ if (typeof g.TRUSTED_TYPES_POLICY.createHTML != "function")
+ throw pn('TRUSTED_TYPES_POLICY configuration option must provide a "createHTML" hook.');
+ if (typeof g.TRUSTED_TYPES_POLICY.createScriptURL != "function")
+ throw pn('TRUSTED_TYPES_POLICY configuration option must provide a "createScriptURL" hook.');
+ const $ = V;
+ V = g.TRUSTED_TYPES_POLICY;
try {
- q = re("");
- } catch (V) {
- throw M = L, V;
+ j = X("");
+ } catch (H) {
+ throw V = $, H;
}
- } else h.TRUSTED_TYPES_POLICY === null ? (M = void 0, q = "") : (M === void 0 && (M = be()), M && typeof q == "string" && (q = re("")));
- et && et(h), Ln = h;
- }, Yl = fe({}, [...da, ...fa, ...OS]), Xl = fe({}, [...pa, ...wS]), jm = function(h, x, L) {
- return x.namespaceURI === ir ? h === "svg" : x.namespaceURI === bs ? h === "svg" && (L === "annotation-xml" || zo[L]) : !!Yl[h];
- }, Bm = function(h, x, L) {
- return x.namespaceURI === ir ? h === "math" : x.namespaceURI === ks ? h === "math" && Ko[L] : !!Xl[h];
- }, Vm = function(h, x, L) {
- return x.namespaceURI === ks && !Ko[L] || x.namespaceURI === bs && !zo[L] ? !1 : !Xl[h] && (Um[h] || !Yl[h]);
- }, Wm = function(h) {
- let x = k(h);
- (!x || !x.tagName) && (x = {
- namespaceURI: In,
+ } else g.TRUSTED_TYPES_POLICY === null ? (V = void 0, j = "") : (V === void 0 && (V = be()), V && typeof j == "string" && (j = X("")));
+ nt && nt(g), zn = g;
+ }, lu = pe({}, [...ha, ...ga, ...Yv]), uu = pe({}, [...ma, ...Xv]), oy = function(g, C, $) {
+ return C.namespaceURI === ir ? g === "svg" : C.namespaceURI === Cs ? g === "svg" && ($ === "annotation-xml" || Bo[$]) : !!lu[g];
+ }, ay = function(g, C, $) {
+ return C.namespaceURI === ir ? g === "math" : C.namespaceURI === vs ? g === "math" && Vo[$] : !!uu[g];
+ }, cy = function(g, C, $) {
+ return C.namespaceURI === vs && !Vo[$] || C.namespaceURI === Cs && !Bo[$] ? !1 : !uu[g] && (ry[g] || !lu[g]);
+ }, ly = function(g) {
+ let C = b(g);
+ (!C || !C.tagName) && (C = {
+ namespaceURI: Fn,
tagName: "template"
});
- const L = $i(h.tagName), V = $i(x.tagName);
- return Fo[h.namespaceURI] ? h.namespaceURI === ks ? jm(L, x, V) : h.namespaceURI === bs ? Bm(L, x, V) : h.namespaceURI === ir ? Vm(L, x, V) : !!(_i === "application/xhtml+xml" && Fo[h.namespaceURI]) : !1;
- }, $r = function(h) {
- Vn(t.removed, {
- element: h
+ const $ = Fi(g.tagName), H = Fi(C.tagName);
+ return jo[g.namespaceURI] ? g.namespaceURI === vs ? oy($, C, H) : g.namespaceURI === Cs ? ay($, C, H) : g.namespaceURI === ir ? cy($, C, H) : !!(Ei === "application/xhtml+xml" && jo[g.namespaceURI]) : !1;
+ }, Ir = function(g) {
+ Jn(t.removed, {
+ element: g
});
try {
- k(h).removeChild(h);
+ b(g).removeChild(g);
} catch {
- if (m(h), !k(h))
- throw dn("a node selected for removal could not be detached from its tree and cannot be safely returned; refusing to sanitize in place");
- }
- }, Ts = function(h) {
- Ci(h);
- const x = y(h);
- if (x) {
- const V = [];
- Bn(x, (H) => {
- Vn(V, H);
- }), Bn(V, (H) => {
+ if (y(g), !b(g))
+ throw pn("a node selected for removal could not be detached from its tree and cannot be safely returned; refusing to sanitize in place");
+ }
+ }, Ss = function(g) {
+ Ai(g);
+ const C = m(g);
+ if (C) {
+ const H = [];
+ Gn(C, (Y) => {
+ Jn(H, Y);
+ }), Gn(H, (Y) => {
try {
- m(H);
+ y(Y);
} catch {
}
});
}
- const L = S(h);
- if (L)
- for (let V = L.length - 1; V >= 0; --V) {
- const H = L[V], ie = H && H.name;
+ const $ = _(g);
+ if ($)
+ for (let H = $.length - 1; H >= 0; --H) {
+ const Y = $[H], ie = Y && Y.name;
if (typeof ie == "string")
try {
- h.removeAttribute(ie);
+ g.removeAttribute(ie);
} catch {
}
}
- }, sn = function(h, x) {
+ }, an = function(g, C) {
try {
- Vn(t.removed, {
- attribute: x.getAttributeNode(h),
- from: x
+ Jn(t.removed, {
+ attribute: C.getAttributeNode(g),
+ from: C
});
} catch {
- Vn(t.removed, {
+ Jn(t.removed, {
attribute: null,
- from: x
+ from: C
});
}
- if (x.removeAttribute(h), h === "is")
- if (Ot || rn)
+ if (C.removeAttribute(g), g === "is")
+ if (Qe || rr)
try {
- $r(x);
+ Ir(C);
} catch {
}
else
try {
- x.setAttribute(h, "");
+ C.setAttribute(g, "");
} catch {
}
- }, Hm = function(h) {
- const x = S(h);
- if (x)
- for (let L = x.length - 1; L >= 0; --L) {
- const V = x[L], H = V && V.name;
- if (!(typeof H != "string" || ye[$e(H)]))
+ }, uy = function(g) {
+ const C = _(g);
+ if (C)
+ for (let $ = C.length - 1; $ >= 0; --$) {
+ const H = C[$], Y = H && H.name;
+ if (!(typeof Y != "string" || xe[Le(Y)]))
try {
- h.removeAttribute(H);
+ g.removeAttribute(Y);
} catch {
}
}
- }, Ci = function(h) {
- const x = [h];
- for (; x.length > 0; ) {
- const L = x.pop();
- (P ? P(L) : L.nodeType) === Mt.element && Hm(L);
- const H = y(L);
- if (H)
- for (let ie = H.length - 1; ie >= 0; --ie)
- x.push(H[ie]);
- }
- }, Gm = function(h) {
- if (!R)
+ }, Ai = function(g) {
+ const C = [g];
+ for (; C.length > 0; ) {
+ const $ = C.pop();
+ (A ? A($) : $.nodeType) === Et.element && uy($);
+ const Y = m($);
+ if (Y)
+ for (let ie = Y.length - 1; ie >= 0; --ie)
+ C.push(Y[ie]);
+ }
+ }, dy = function(g) {
+ if (!Kt)
return;
- const x = [h];
- for (; x.length > 0; ) {
- const L = x.pop(), V = P ? P(L) : L.nodeType;
- if (V === Mt.processingInstruction || V === Mt.comment && Ye(Md, L.data)) {
+ const C = [g];
+ for (; C.length > 0; ) {
+ const $ = C.pop(), H = A ? A($) : $.nodeType;
+ if (H === Et.processingInstruction || H === Et.comment && Ze(Dd, $.data)) {
try {
- m(L);
+ y($);
} catch {
}
continue;
}
- if (V === Mt.element) {
- const ie = L, ke = $e(A ? A(L) : L.nodeName);
+ if (H === Et.element) {
+ const ie = $, ke = Le(E ? E($) : $.nodeName);
try {
ie.hasAttribute && ie.hasAttribute("patchsrc") && ie.removeAttribute("patchsrc"), ie.hasAttribute && ie.hasAttribute("for") && ke !== "label" && ke !== "output" && ie.removeAttribute("for");
} catch {
}
}
- const H = y(L);
- if (H)
- for (let ie = H.length - 1; ie >= 0; --ie)
- x.push(H[ie]);
- }
- }, Ql = function(h) {
- let x = null, L = null;
- if (lt)
- h = "" + h;
+ const Y = m($);
+ if (Y)
+ for (let ie = Y.length - 1; ie >= 0; --ie)
+ C.push(Y[ie]);
+ }
+ }, du = function(g) {
+ let C = null, $ = null;
+ if (Se)
+ g = "" + g;
else {
- const ie = md(h, /^[\r\n\t ]+/);
- L = ie && ie[0];
+ const ie = Ad(g, /^[\r\n\t ]+/);
+ $ = ie && ie[0];
}
- _i === "application/xhtml+xml" && In === ir && (h = '
' + h + "");
- const V = M ? re(h) : h;
- if (In === ir)
+ Ei === "application/xhtml+xml" && Fn === ir && (g = '' + g + "");
+ const H = V ? X(g) : g;
+ if (Fn === ir)
try {
- x = new u().parseFromString(V, _i);
+ C = new d().parseFromString(H, Ei);
} catch {
}
- if (!x || !x.documentElement) {
- x = Re.createDocument(In, "template", null);
+ if (!C || !C.documentElement) {
+ C = $e.createDocument(Fn, "template", null);
try {
- x.documentElement.innerHTML = Uo ? q : V;
+ C.documentElement.innerHTML = Ko ? j : H;
} catch {
}
}
- const H = x.body || x.documentElement;
- return h && L && H.insertBefore(r.createTextNode(L), H.childNodes[0] || null), In === ir ? vt.call(x, K ? "html" : "body")[0] : K ? x.documentElement : H;
- }, Zl = function(h) {
- const x = B ? B(h) : h.ownerDocument;
- return en.call(
- x || h,
- h,
+ const Y = C.body || C.documentElement;
+ return g && $ && Y.insertBefore(r.createTextNode($), Y.childNodes[0] || null), Fn === ir ? Mt.call(C, q ? "html" : "body")[0] : q ? C.documentElement : Y;
+ }, fu = function(g) {
+ const C = D ? D(g) : g.ownerDocument;
+ return nn.call(
+ C || g,
+ g,
// eslint-disable-next-line no-bitwise
c.SHOW_ELEMENT | c.SHOW_COMMENT | c.SHOW_TEXT | c.SHOW_PROCESSING_INSTRUCTION | c.SHOW_CDATA_SECTION,
null
);
- }, xs = function(h) {
- return h = Pi(h, Y, " "), h = Pi(h, le, " "), h = Pi(h, W, " "), h;
- }, Bo = function(h) {
- var x;
- h.normalize();
- const L = B ? B(h) : h.ownerDocument, V = en.call(
- L || h,
- h,
+ }, Ms = function(g) {
+ return g = Ri(g, G, " "), g = Ri(g, ue, " "), g = Ri(g, Me, " "), g;
+ }, Ho = function(g) {
+ var C;
+ g.normalize();
+ const $ = D ? D(g) : g.ownerDocument, H = nn.call(
+ $ || g,
+ g,
// eslint-disable-next-line no-bitwise
c.SHOW_TEXT | c.SHOW_COMMENT | c.SHOW_CDATA_SECTION | c.SHOW_PROCESSING_INSTRUCTION,
null
);
- let H = V.nextNode();
- for (; H; )
- H.data = xs(H.data), H = V.nextNode();
- const ie = (x = h.querySelectorAll) === null || x === void 0 ? void 0 : x.call(h, "template");
- ie && Bn(ie, (ke) => {
- Dn(ke.content) && Bo(ke.content);
+ let Y = H.nextNode();
+ for (; Y; )
+ Y.data = Ms(Y.data), Y = H.nextNode();
+ const ie = (C = g.querySelectorAll) === null || C === void 0 ? void 0 : C.call(g, "template");
+ ie && Gn(ie, (ke) => {
+ Kn(ke.content) && Ho(ke.content);
});
- }, _s = function(h) {
- const x = A ? A(h) : null;
- return typeof x != "string" || $e(x) !== "form" ? !1 : typeof h.nodeName != "string" || typeof h.textContent != "string" || typeof h.removeChild != "function" || // Realm-safe NamedNodeMap detection: equality against the cached
+ }, Es = function(g) {
+ const C = E ? E(g) : null;
+ return typeof C != "string" || Le(C) !== "form" ? !1 : typeof g.nodeName != "string" || typeof g.textContent != "string" || typeof g.removeChild != "function" || // Realm-safe NamedNodeMap detection: equality against the cached
// prototype getter. Clobbered .attributes (e.g. )
// makes the direct read diverge from the cached read; a clean form
// (same-realm OR foreign-realm) has both reads pointing at the same
// canonical NamedNodeMap.
- h.attributes !== S(h) || typeof h.removeAttribute != "function" || typeof h.setAttribute != "function" || typeof h.namespaceURI != "string" || typeof h.insertBefore != "function" || typeof h.hasChildNodes != "function" || // NodeType clobbering probe. Cached Node.prototype.nodeType getter
+ g.attributes !== _(g) || typeof g.removeAttribute != "function" || typeof g.setAttribute != "function" || typeof g.namespaceURI != "string" || typeof g.insertBefore != "function" || typeof g.hasChildNodes != "function" || // NodeType clobbering probe. Cached Node.prototype.nodeType getter
// returns the integer 1 for any Element regardless of realm; direct
// read on a clobbered form (e.g. ) returns
// the named child element. Cheap addition — nodeType is read from
// an internal slot, no serialization cost — and removes a residual
// clobbering surface used by several mXSS / PI / comment branches
// in _sanitizeElements that compare currentNode.nodeType directly.
- h.nodeType !== P(h) || // HTMLFormElement has [LegacyOverrideBuiltIns]: a descendant named
+ g.nodeType !== A(g) || // HTMLFormElement has [LegacyOverrideBuiltIns]: a descendant named
// "childNodes" shadows the prototype getter. Direct reads of
// form.childNodes from a clobbered form return the named child
// instead of the real NodeList, so any walk that reads it directly
@@ -19514,85 +19518,85 @@ function eg() {
// select, etc.) regardless of whether the named child happens to
// carry a numeric .length, which a typeof-based probe would miss
// (e.g. HTMLSelectElement.length is a defined unsigned-long).
- h.childNodes !== y(h);
- }, Dn = function(h) {
- if (!P || typeof h != "object" || h === null)
+ g.childNodes !== m(g);
+ }, Kn = function(g) {
+ if (!A || typeof g != "object" || g === null)
return !1;
try {
- return P(h) === Mt.documentFragment;
+ return A(g) === Et.documentFragment;
} catch {
return !1;
}
- }, Si = function(h) {
- if (!P || typeof h != "object" || h === null)
+ }, Pi = function(g) {
+ if (!A || typeof g != "object" || g === null)
return !1;
try {
- return typeof P(h) == "number";
+ return typeof A(g) == "number";
} catch {
return !1;
}
};
- function sr(U, h, x) {
- U.length !== 0 && Bn(U, (L) => {
- L.call(t, h, x, Ln);
+ function sr(U, g, C) {
+ U.length !== 0 && Gn(U, ($) => {
+ $.call(t, g, C, zn);
});
}
- const Jm = function(h, x) {
- return !!(R && h.hasChildNodes() && !Si(h.firstElementChild) && Ye(vd, h.textContent) && Ye(vd, h.innerHTML) || R && h.namespaceURI === ir && x === "style" && Si(h.firstElementChild) || h.nodeType === Mt.processingInstruction || R && h.nodeType === Mt.comment && Ye(Md, h.data));
- }, Ym = function(h, x, L) {
- if (!wr[x] && nu(x) && (ve.tagNameCheck instanceof RegExp && Ye(ve.tagNameCheck, x) || ve.tagNameCheck instanceof Function && ve.tagNameCheck(x)))
+ const fy = function(g, C) {
+ return !!(Kt && g.hasChildNodes() && !Pi(g.firstElementChild) && Ze(Id, g.textContent) && Ze(Id, g.innerHTML) || Kt && g.namespaceURI === ir && C === "style" && Pi(g.firstElementChild) || g.nodeType === Et.processingInstruction || Kt && g.nodeType === Et.comment && Ze(Dd, g.data));
+ }, py = function(g, C, $) {
+ if (!br[C] && mu(C) && (Ne.tagNameCheck instanceof RegExp && Ze(Ne.tagNameCheck, C) || Ne.tagNameCheck instanceof Function && Ne.tagNameCheck(C)))
return !1;
- if (Io && !nr[x]) {
- const V = k(h), H = y(h);
- if (H && V) {
- const ie = H.length;
+ if (Uo && !nr[C]) {
+ const H = b(g), Y = m(g);
+ if (Y && H) {
+ const ie = Y.length;
for (let ke = ie - 1; ke >= 0; --ke) {
- const Ie = h === L ? p(H[ke], !0) : H[ke];
- V.insertBefore(Ie, g(h));
+ const Ie = g === $ ? p(Y[ke], !0) : Y[ke];
+ H.insertBefore(Ie, h(g));
}
}
}
- return $r(h), !0;
- }, eu = function(h, x, L, V) {
- return h.length === 0 ? x : x === L || x === V ? st(x) : x;
- }, tu = function(h, x) {
- if (sr(E.beforeSanitizeElements, h, null), h !== x && k(h) === null)
- return ys && Ci(h), !0;
- if (_s(h))
- return $r(h), !0;
- const L = $e(A ? A(h) : h.nodeName);
- if (ue = eu(E.uponSanitizeElement, ue, Rn, X), sr(E.uponSanitizeElement, h, {
- tagName: L,
- allowedTags: ue
- }), h !== x && k(h) === null)
- return ys && Ci(h), !0;
- if (Jm(h, L))
- return $r(h), !0;
- if (wr[L] || !(rr.tagCheck instanceof Function && rr.tagCheck(L)) && !ue[L]) {
- const H = Ym(h, L, x);
- return H === !1 && sr(E.afterSanitizeElements, h, null), H;
- }
- if ((P ? P(h) : h.nodeType) === Mt.element && !Wm(h) || (L === "noscript" || L === "noembed" || L === "noframes") && Ye(KS, h.innerHTML))
- return $r(h), !0;
- if (Kt && h.nodeType === Mt.text) {
- const H = xs(h.textContent);
- h.textContent !== H && (Vn(t.removed, {
- element: h.cloneNode()
- }), h.textContent = H);
- }
- return sr(E.afterSanitizeElements, h, null), !1;
- }, ru = function(h, x, L) {
- if (qr[x] || R && x === "patchsrc" || R && x === "for" && h !== "label" && h !== "output" || zl && (x === "id" || x === "name") && (L in r || L in Km))
+ return Ir(g), !0;
+ }, pu = function(g, C, $, H) {
+ return g.length === 0 ? C : C === $ || C === H ? at(C) : C;
+ }, hu = function(g, C) {
+ if (sr(P.beforeSanitizeElements, g, null), g !== C && b(g) === null)
+ return _s && Ai(g), !0;
+ if (Es(g))
+ return Ir(g), !0;
+ const $ = Le(E ? E(g) : g.nodeName);
+ if (le = pu(P.uponSanitizeElement, le, gt, J), sr(P.uponSanitizeElement, g, {
+ tagName: $,
+ allowedTags: le
+ }), g !== C && b(g) === null)
+ return _s && Ai(g), !0;
+ if (fy(g, $))
+ return Ir(g), !0;
+ if (br[$] || !(mt.tagCheck instanceof Function && mt.tagCheck($)) && !le[$]) {
+ const Y = py(g, $, C);
+ return Y === !1 && sr(P.afterSanitizeElements, g, null), Y;
+ }
+ if ((A ? A(g) : g.nodeType) === Et.element && !ly(g) || ($ === "noscript" || $ === "noembed" || $ === "noframes") && Ze(aS, g.innerHTML))
+ return Ir(g), !0;
+ if (tr && g.nodeType === Et.text) {
+ const Y = Ms(g.textContent);
+ g.textContent !== Y && (Jn(t.removed, {
+ element: g.cloneNode()
+ }), g.textContent = Y);
+ }
+ return sr(P.afterSanitizeElements, g, null), !1;
+ }, gu = function(g, C, $) {
+ if (vi[C] || Kt && C === "patchsrc" || Kt && C === "for" && g !== "label" && g !== "output" || Mi && (C === "id" || C === "name") && ($ in r || $ in sy))
return !1;
- const V = ye[x] || rr.attributeCheck instanceof Function && rr.attributeCheck(x, h);
- if (!(tn && Ye(xe, x))) {
- if (!(Rr && Ye(ht, x))) {
- if (V) {
- if (!Do[x]) {
- if (!Ye(ct, Pi(L, gt, ""))) {
- if (!((x === "src" || x === "xlink:href" || x === "href") && h !== "script" && yd(L, "data:") === 0 && Bl[h])) {
- if (!(xi && !Ye(zt, Pi(L, gt, "")))) {
- if (L)
+ const H = xe[C] || mt.attributeCheck instanceof Function && mt.attributeCheck(C, g);
+ if (!(kr && Ze(Q, C))) {
+ if (!(xs && Ze(ve, C))) {
+ if (H) {
+ if (!zo[C]) {
+ if (!Ze(We, Ri($, wt, ""))) {
+ if (!((C === "src" || C === "xlink:href" || C === "href") && g !== "script" && Pd($, "data:") === 0 && nu[g])) {
+ if (!(Dn && !Ze(mr, Ri($, wt, "")))) {
+ if ($)
return !1;
}
}
@@ -19602,113 +19606,113 @@ function eg() {
// First condition does a very basic check if a) it's basically a valid custom element tagname AND
// b) if the tagName passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
// and c) if the attribute name passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.attributeNameCheck
- !(nu(h) && (ve.tagNameCheck instanceof RegExp && Ye(ve.tagNameCheck, h) || ve.tagNameCheck instanceof Function && ve.tagNameCheck(h)) && (ve.attributeNameCheck instanceof RegExp && Ye(ve.attributeNameCheck, x) || ve.attributeNameCheck instanceof Function && ve.attributeNameCheck(x, h)) || // Alternative, second condition checks if it's an `is`-attribute, AND
+ !(mu(g) && (Ne.tagNameCheck instanceof RegExp && Ze(Ne.tagNameCheck, g) || Ne.tagNameCheck instanceof Function && Ne.tagNameCheck(g)) && (Ne.attributeNameCheck instanceof RegExp && Ze(Ne.attributeNameCheck, C) || Ne.attributeNameCheck instanceof Function && Ne.attributeNameCheck(C, g)) || // Alternative, second condition checks if it's an `is`-attribute, AND
// the value passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
- x === "is" && ve.allowCustomizedBuiltInElements && (ve.tagNameCheck instanceof RegExp && Ye(ve.tagNameCheck, L) || ve.tagNameCheck instanceof Function && ve.tagNameCheck(L)))
+ C === "is" && Ne.allowCustomizedBuiltInElements && (Ne.tagNameCheck instanceof RegExp && Ze(Ne.tagNameCheck, $) || Ne.tagNameCheck instanceof Function && Ne.tagNameCheck($)))
) return !1;
}
}
return !0;
- }, Xm = fe({}, ["annotation-xml", "color-profile", "font-face", "font-face-format", "font-face-name", "font-face-src", "font-face-uri", "missing-glyph"]), nu = function(h) {
- return !Xm[$i(h)] && Ye(We, h);
- }, Qm = function(h, x, L, V) {
- if (M && typeof d == "object" && typeof d.getAttributeType == "function" && !L)
- switch (d.getAttributeType(h, x)) {
+ }, hy = pe({}, ["annotation-xml", "color-profile", "font-face", "font-face-format", "font-face-name", "font-face-src", "font-face-uri", "missing-glyph"]), mu = function(g) {
+ return !hy[Fi(g)] && Ze(sn, g);
+ }, gy = function(g, C, $, H) {
+ if (V && typeof u == "object" && typeof u.getAttributeType == "function" && !$)
+ switch (u.getAttributeType(g, C)) {
case "TrustedHTML":
- return re(V);
+ return X(H);
case "TrustedScriptURL":
- return qe(V);
+ return Re(H);
}
- return V;
- }, Zm = function(h, x, L, V) {
+ return H;
+ }, my = function(g, C, $, H) {
try {
- L ? h.setAttributeNS(L, x, V) : h.setAttribute(x, V), _s(h) ? $r(h) : gd(t.removed);
+ $ ? g.setAttributeNS($, C, H) : g.setAttribute(C, H), Es(g) ? Ir(g) : Ed(t.removed);
} catch {
- sn(x, h);
+ an(C, g);
}
- }, iu = function(h) {
- sr(E.beforeSanitizeAttributes, h, null);
- const x = h.attributes;
- if (!x || _s(h))
+ }, yu = function(g) {
+ sr(P.beforeSanitizeAttributes, g, null);
+ const C = g.attributes;
+ if (!C || Es(g))
return;
- ye = eu(E.uponSanitizeAttribute, ye, Ti, _e);
- const L = {
+ xe = pu(P.uponSanitizeAttribute, xe, yr, ee);
+ const $ = {
attrName: "",
attrValue: "",
keepAttr: !0,
- allowedAttributes: ye,
+ allowedAttributes: xe,
forceKeepAttr: void 0
};
- let V = x.length;
- const H = $e(h.nodeName);
- for (; V--; ) {
- const ie = x[V], ke = ie.name, Ie = ie.namespaceURI, mt = ie.value, yt = $e(ke), Wo = mt;
- let ut = ke === "value" ? Wo : SS(Wo);
- if (L.attrName = yt, L.attrValue = ut, L.keepAttr = !0, L.forceKeepAttr = void 0, sr(E.uponSanitizeAttribute, h, L), ut = L.attrValue, Kl && (yt === "id" || yt === "name") && yd(ut, jl) !== 0 && (sn(ke, h), ut = jl + ut), R && Ye(/((--!?|])>)|<\/(style|script|title|xmp|textarea|noscript|iframe|noembed|noframes)/i, ut)) {
- sn(ke, h);
+ let H = C.length;
+ const Y = Le(g.nodeName);
+ for (; H--; ) {
+ const ie = C[H], ke = ie.name, Ie = ie.namespaceURI, yt = ie.value, bt = Le(ke), Jo = yt;
+ let ut = ke === "value" ? Jo : jv(Jo);
+ if ($.attrName = bt, $.attrValue = ut, $.keepAttr = !0, $.forceKeepAttr = void 0, sr(P.uponSanitizeAttribute, g, $), ut = $.attrValue, tu && (bt === "id" || bt === "name") && Pd(ut, ru) !== 0 && (an(ke, g), ut = ru + ut), Kt && Ze(/((--!?|])>)|<\/(style|script|title|xmp|textarea|noscript|iframe|noembed|noframes)/i, ut)) {
+ an(ke, g);
continue;
}
- if (yt === "attributename" && md(ut, "href")) {
- sn(ke, h);
+ if (bt === "attributename" && Ad(ut, "href")) {
+ an(ke, g);
continue;
}
- if (!L.forceKeepAttr) {
- if (!L.keepAttr) {
- sn(ke, h);
+ if (!$.forceKeepAttr) {
+ if (!$.keepAttr) {
+ an(ke, g);
continue;
}
- if (!ms && Ye(jS, ut)) {
- sn(ke, h);
+ if (!Si && Ze(cS, ut)) {
+ an(ke, g);
continue;
}
- if (Kt && (ut = xs(ut)), !ru(H, yt, ut)) {
- sn(ke, h);
+ if (tr && (ut = Ms(ut)), !gu(Y, bt, ut)) {
+ an(ke, g);
continue;
}
- ut = Qm(H, yt, Ie, ut), ut !== Wo && Zm(h, ke, Ie, ut);
+ ut = gy(Y, bt, Ie, ut), ut !== Jo && my(g, ke, Ie, ut);
}
}
- sr(E.afterSanitizeAttributes, h, null);
- }, Cs = function(h) {
- let x = null;
- const L = Zl(h);
- for (sr(E.beforeSanitizeShadowDOM, h, null); x = L.nextNode(); )
- if (sr(E.uponSanitizeShadowNode, x, null), tu(x, h), iu(x), Dn(x.content) && Cs(x.content), (P ? P(x) : x.nodeType) === Mt.element) {
- const H = _(x);
- Dn(H) && (Vo(H), Cs(H));
+ sr(P.afterSanitizeAttributes, g, null);
+ }, As = function(g) {
+ let C = null;
+ const $ = fu(g);
+ for (sr(P.beforeSanitizeShadowDOM, g, null); C = $.nextNode(); )
+ if (sr(P.uponSanitizeShadowNode, C, null), hu(C, g), yu(C), Kn(C.content) && As(C.content), (A ? A(C) : C.nodeType) === Et.element) {
+ const Y = x(C);
+ Kn(Y) && (Go(Y), As(Y));
}
- sr(E.afterSanitizeShadowDOM, h, null);
- }, Vo = function(h) {
- const x = [{
- node: h,
+ sr(P.afterSanitizeShadowDOM, g, null);
+ }, Go = function(g) {
+ const C = [{
+ node: g,
shadow: null
}];
- for (; x.length > 0; ) {
- const L = x.pop();
- if (L.shadow) {
- Cs(L.shadow);
+ for (; C.length > 0; ) {
+ const $ = C.pop();
+ if ($.shadow) {
+ As($.shadow);
continue;
}
- const V = L.node, ie = (P ? P(V) : V.nodeType) === Mt.element, ke = y(V);
+ const H = $.node, ie = (A ? A(H) : H.nodeType) === Et.element, ke = m(H);
if (ke)
for (let Ie = ke.length - 1; Ie >= 0; --Ie)
- x.push({
+ C.push({
node: ke[Ie],
shadow: null
});
if (ie) {
- const Ie = A ? A(V) : null;
- if (typeof Ie == "string" && $e(Ie) === "template") {
- const mt = V.content;
- Dn(mt) && x.push({
- node: mt,
+ const Ie = E ? E(H) : null;
+ if (typeof Ie == "string" && Le(Ie) === "template") {
+ const yt = H.content;
+ Kn(yt) && C.push({
+ node: yt,
shadow: null
});
}
}
if (ie) {
- const Ie = _(V);
- Dn(Ie) && x.push({
+ const Ie = x(H);
+ Kn(Ie) && C.push({
node: null,
shadow: Ie
}, {
@@ -19719,168 +19723,168 @@ function eg() {
}
};
return t.sanitize = function(U) {
- let h = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {}, x = null, L = null, V = null, H = null;
- if (Uo = !U, Uo && (U = ""), typeof U != "string" && !Si(U) && (U = PS(U), typeof U != "string"))
- throw dn("dirty is not a string, aborting");
+ let g = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {}, C = null, $ = null, H = null, Y = null;
+ if (Ko = !U, Ko && (U = ""), typeof U != "string" && !Pi(U) && (U = Gv(U), typeof U != "string"))
+ throw pn("dirty is not a string, aborting");
if (!t.isSupported)
return U;
- G ? (ue = X, ye = _e) : jo(h), (E.uponSanitizeElement.length > 0 || E.uponSanitizeAttribute.length > 0) && (ue = st(ue)), E.uponSanitizeAttribute.length > 0 && (ye = st(ye)), t.removed = [];
- const ie = ys && typeof U != "string" && Si(U);
+ B ? (le = J, xe = ee) : Wo(g), (P.uponSanitizeElement.length > 0 || P.uponSanitizeAttribute.length > 0) && (le = at(le)), P.uponSanitizeAttribute.length > 0 && (xe = at(xe)), t.removed = [];
+ const ie = _s && typeof U != "string" && Pi(U);
if (ie) {
- Gm(U);
- const mt = A ? A(U) : U.nodeName;
- if (typeof mt == "string") {
- const yt = $e(mt);
- if (!ue[yt] || wr[yt])
- throw Ts(U), dn("root node is forbidden and cannot be sanitized in-place");
+ dy(U);
+ const yt = E ? E(U) : U.nodeName;
+ if (typeof yt == "string") {
+ const bt = Le(yt);
+ if (!le[bt] || br[bt])
+ throw Ss(U), pn("root node is forbidden and cannot be sanitized in-place");
}
- if (_s(U))
- throw Ts(U), dn("root node is clobbered and cannot be sanitized in-place");
+ if (Es(U))
+ throw Ss(U), pn("root node is clobbered and cannot be sanitized in-place");
try {
- Vo(U);
- } catch (yt) {
- throw Ts(U), yt;
+ Go(U);
+ } catch (bt) {
+ throw Ss(U), bt;
}
- } else if (Si(U))
- x = Ql(""), L = x.ownerDocument.importNode(U, !0), L.nodeType === Mt.element && L.nodeName === "BODY" || L.nodeName === "HTML" ? x = L : x.appendChild(L), Vo(L);
+ } else if (Pi(U))
+ C = du(""), $ = C.ownerDocument.importNode(U, !0), $.nodeType === Et.element && $.nodeName === "BODY" || $.nodeName === "HTML" ? C = $ : C.appendChild($), Go($);
else {
- if (!Ot && !Kt && !K && // eslint-disable-next-line unicorn/prefer-includes
+ if (!Qe && !tr && !q && // eslint-disable-next-line unicorn/prefer-includes
U.indexOf("<") === -1)
- return M && nn ? re(U) : U;
- if (x = Ql(U), !x)
- return Ot ? null : nn ? q : "";
+ return V && on ? X(U) : U;
+ if (C = du(U), !C)
+ return Qe ? null : on ? j : "";
}
- x && lt && $r(x.firstChild);
- const ke = ie ? U : x;
+ C && Se && Ir(C.firstChild);
+ const ke = ie ? U : C;
try {
- const mt = Zl(ke);
- for (; V = mt.nextNode(); )
- tu(V, ke), iu(V), Dn(V.content) && Cs(V.content);
- } catch (mt) {
- throw ie && (Ts(U), Bn(t.removed, (yt) => {
- yt.element && Ci(yt.element);
- })), mt;
+ const yt = fu(ke);
+ for (; H = yt.nextNode(); )
+ hu(H, ke), yu(H), Kn(H.content) && As(H.content);
+ } catch (yt) {
+ throw ie && (Ss(U), Gn(t.removed, (bt) => {
+ bt.element && Ai(bt.element);
+ })), yt;
}
if (ie)
- return Bn(t.removed, (mt) => {
- mt.element && Ci(mt.element);
- }), Kt && Bo(U), U;
- if (Ot) {
- if (Kt && Bo(x), rn)
- for (H = gr.call(x.ownerDocument); x.firstChild; )
- H.appendChild(x.firstChild);
+ return Gn(t.removed, (yt) => {
+ yt.element && Ai(yt.element);
+ }), tr && Ho(U), U;
+ if (Qe) {
+ if (tr && Ho(C), rr)
+ for (Y = gr.call(C.ownerDocument); C.firstChild; )
+ Y.appendChild(C.firstChild);
else
- H = x;
- return (ye.shadowroot || ye.shadowrootmode) && (H = te.call(n, H, !0)), H;
+ Y = C;
+ return (xe.shadowroot || xe.shadowrootmode) && (Y = re.call(n, Y, !0)), Y;
}
- let Ie = K ? x.outerHTML : x.innerHTML;
- return K && ue["!doctype"] && x.ownerDocument && x.ownerDocument.doctype && x.ownerDocument.doctype.name && Ye(FS, x.ownerDocument.doctype.name) && (Ie = "
-` + Ie), Kt && (Ie = xs(Ie)), M && nn ? re(Ie) : Ie;
+ let Ie = q ? C.outerHTML : C.innerHTML;
+ return q && le["!doctype"] && C.ownerDocument && C.ownerDocument.doctype && C.ownerDocument.doctype.name && Ze(sS, C.ownerDocument.doctype.name) && (Ie = "
+` + Ie), tr && (Ie = Ms(Ie)), V && on ? X(Ie) : Ie;
}, t.setConfig = function() {
let U = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {};
- jo(U), G = !0, X = ue, _e = ye;
+ Wo(U), B = !0, J = le, ee = xe;
}, t.clearConfig = function() {
- Ln = null, G = !1, X = null, _e = null, M = $, q = "";
- }, t.isValidAttribute = function(U, h, x) {
- Ln || jo({});
- const L = $e(U), V = $e(h);
- return ru(L, V, x);
- }, t.addHook = function(U, h) {
- typeof h == "function" && Xe(E, U) && Vn(E[U], h);
- }, t.removeHook = function(U, h) {
- if (Xe(E, U)) {
- if (h !== void 0) {
- const x = _S(E[U], h);
- return x === -1 ? void 0 : CS(E[U], x, 1)[0];
+ zn = null, B = !1, J = null, ee = null, V = v, j = "";
+ }, t.isValidAttribute = function(U, g, C) {
+ zn || Wo({});
+ const $ = Le(U), H = Le(g);
+ return gu($, H, C);
+ }, t.addHook = function(U, g) {
+ typeof g == "function" && et(P, U) && Jn(P[U], g);
+ }, t.removeHook = function(U, g) {
+ if (et(P, U)) {
+ if (g !== void 0) {
+ const C = zv(P[U], g);
+ return C === -1 ? void 0 : Kv(P[U], C, 1)[0];
}
- return gd(E[U]);
+ return Ed(P[U]);
}
}, t.removeHooks = function(U) {
- Xe(E, U) && (E[U] = []);
+ et(P, U) && (P[U] = []);
}, t.removeAllHooks = function() {
- E = Ed();
+ P = Ud();
}, t;
}
-var WS = eg();
-function HS({ structureProtectionMode: e = "off" }) {
- const [t] = ce(), r = Z(void 0), [n, i] = de(void 0), s = he((o) => {
+var dS = gg();
+function fS({ structureProtectionMode: e = "off" }) {
+ const [t] = ae(), r = Z(void 0), [n, i] = de(void 0), s = me((o) => {
r.current = o, i(o);
}, []);
- return z(() => {
+ return F(() => {
if (e === "off")
return;
const o = (p) => {
- const m = Vh(p);
- if (!m)
+ const y = og(p);
+ if (!y)
return !1;
- const g = w();
- return e === "protected" ? g && Jh(g, m) ? (p.preventDefault(), !0) : !1 : m !== "deleteBackward" && m !== "deleteForward" ? !1 : a(m, p);
- }, a = (p, m) => {
- const g = w(), y = r.current;
- if (y && g && fd(g, y)) {
- if (s(void 0), m.preventDefault(), p !== y.intent)
+ const h = w();
+ return e === "protected" ? h && ug(h, y) ? (p.preventDefault(), !0) : !1 : y !== "deleteBackward" && y !== "deleteForward" ? !1 : a(y, p);
+ }, a = (p, y) => {
+ const h = w(), m = r.current;
+ if (m && h && vd(h, m)) {
+ if (s(void 0), y.preventDefault(), p !== m.intent)
return !0;
- const _ = se(y.key) ?? void 0;
- if (y.kind === "verse") {
- if (_) {
- const S = _.getParent(), P = _.getPreviousSibling(), A = _.getNextSibling();
- _.remove(), P ? Yh(P) : A && v(A) ? A.select(0, 0) : S?.selectStart();
+ const x = se(m.key) ?? void 0;
+ if (m.kind === "verse") {
+ if (x) {
+ const _ = x.getParent(), A = x.getPreviousSibling(), E = x.getNextSibling();
+ x.remove(), A ? dg(A) : E && M(E) ? E.select(0, 0) : _?.selectStart();
}
- } else y.kind === "selection" ? N(g) && g.removeText() : Se(_) && fS(_);
+ } else m.kind === "selection" ? N(h) && h.removeText() : Ce(x) && Ov(x);
return !0;
}
- if (!g)
+ if (!h)
return !1;
- const k = dS(g, p);
- if (k) {
- if (k.kind === "verse") {
- const _ = xf();
- _.add(k.node.getKey()), ei(_);
+ const b = Nv(h, p);
+ if (b) {
+ if (b.kind === "verse") {
+ const x = Lf();
+ x.add(b.node.getKey()), ii(x);
} else {
- const _ = bc();
- _.anchor.set(k.node.getKey(), 0, "element"), _.focus.set(k.node.getKey(), k.node.getChildrenSize(), "element"), ei(_);
+ const x = Ec();
+ x.anchor.set(b.node.getKey(), 0, "element"), x.focus.set(b.node.getKey(), b.node.getChildrenSize(), "element"), ii(x);
}
- return s({ key: k.node.getKey(), kind: k.kind, intent: p }), m.preventDefault(), !0;
+ return s({ key: b.node.getKey(), kind: b.kind, intent: p }), y.preventDefault(), !0;
}
- if (N(g) && !g.isCollapsed() && ml(g)) {
- const _ = g.getNodes().filter(ge).map((A) => A.getKey()), { anchor: S, focus: P } = g;
+ if (N(h) && !h.isCollapsed() && El(h)) {
+ const x = h.getNodes().filter(he).map((E) => E.getKey()), { anchor: _, focus: A } = h;
return s({
kind: "selection",
intent: p,
- key: _[0],
- anchor: { key: S.key, offset: S.offset, type: S.type },
- focus: { key: P.key, offset: P.offset, type: P.type }
- }), m.preventDefault(), !0;
+ key: x[0],
+ anchor: { key: _.key, offset: _.offset, type: _.type },
+ focus: { key: A.key, offset: A.offset, type: A.type }
+ }), y.preventDefault(), !0;
}
return !1;
}, c = (p) => {
if (e !== "protected")
return !1;
- const m = w();
- return !m || !Qn(m) ? !1 : (p instanceof Event && p.preventDefault(), !0);
- }, l = (p, m) => {
+ const y = w();
+ return !y || !ti(y) ? !1 : (p instanceof Event && p.preventDefault(), !0);
+ }, l = (p, y) => {
if (!p)
return !1;
- const g = WS.sanitize(p), y = new DOMParser().parseFromString(g, "text/html"), k = pS(Yy(t, y)), _ = w();
- return N(_) && _.insertNodes(k), m.preventDefault(), !0;
- }, u = (p) => {
+ const h = dS.sanitize(p), m = new DOMParser().parseFromString(h, "text/html"), b = wv(gb(t, m)), x = w();
+ return N(x) && x.insertNodes(b), y.preventDefault(), !0;
+ }, d = (p) => {
if (e !== "protected")
return !1;
- const m = w();
- return m && Qn(m) ? (p.preventDefault(), !0) : l(p.clipboardData?.getData("text/html"), p);
- }, d = (p) => {
+ const y = w();
+ return y && ti(y) ? (p.preventDefault(), !0) : l(p.clipboardData?.getData("text/html"), p);
+ }, u = (p) => {
if (e !== "protected")
return !1;
- const m = w();
- return m && Qn(m) ? (p.preventDefault(), !0) : l(p.dataTransfer?.getData("text/html"), p);
+ const y = w();
+ return y && ti(y) ? (p.preventDefault(), !0) : l(p.dataTransfer?.getData("text/html"), p);
}, f = () => {
const p = r.current;
p && t.getEditorState().read(() => {
- fd(w(), p) || s(void 0);
+ vd(w(), p) || s(void 0);
});
};
- return Ve(
- t.registerCommand(vr, o, Ne),
+ return Be(
+ t.registerCommand(Nr, o, Oe),
// CUT at CRITICAL, unlike KEY_DOWN above: a refusal has to outrank the actor it refuses,
// not tie with it. The handlers that PERFORM a cut (the Standard-view and Markers-view
// clipboard claims) register at HIGH, and at equal rank the winner is mount order — an
@@ -19888,14 +19892,14 @@ function HS({ structureProtectionMode: e = "off" }) {
// plugin were added between them. `OpaqueBlockGuardPlugin` states the same rule for the same
// reason. KEY_DOWN stays at HIGH: it has no same-priority actor to outrank, and
// `MarkerEditPlugin`'s KEY_DOWN handler must keep running ahead of it on every keystroke.
- t.registerCommand(yn, c, Wt),
- t.registerCommand(yr, u, Ne),
- t.registerCommand(Py, c, Ne),
- t.registerCommand(xc, d, Ne),
- t.registerCommand(Tc, c, Ne),
+ t.registerCommand(vr, c, xt),
+ t.registerCommand(xr, d, Oe),
+ t.registerCommand(Vy, c, Oe),
+ t.registerCommand(Nc, u, Oe),
+ t.registerCommand(Pc, c, Oe),
t.registerUpdateListener(f)
);
- }, [t, e, s]), z(() => {
+ }, [t, e, s]), F(() => {
const o = t.getRootElement();
if (!o)
return;
@@ -19905,21 +19909,21 @@ function HS({ structureProtectionMode: e = "off" }) {
};
}, [t, n]), null;
}
-const AP = {
+const cN = {
ltr: "Left-to-right",
rtl: "Right-to-left",
auto: "Automatic"
};
-function GS({ textDirection: e }) {
- const [t] = ce();
- return JS(t, e), null;
+function pS({ textDirection: e }) {
+ const [t] = ae();
+ return hS(t, e), null;
}
-function JS(e, t) {
- z(() => (Ad(e, t), e.registerUpdateListener(({ dirtyElements: r }) => {
- r.size > 0 && Ad(e, t);
+function hS(e, t) {
+ F(() => (Fd(e, t), e.registerUpdateListener(({ dirtyElements: r }) => {
+ r.size > 0 && Fd(e, t);
})), [e, t]);
}
-function Ad(e, t) {
+function Fd(e, t) {
if (t === "auto")
return;
const r = e.getRootElement();
@@ -19927,19 +19931,19 @@ function Ad(e, t) {
const n = e._config.theme.placeholder, i = document.getElementsByClassName(n)[0];
i && (i.dir = t);
}
-function YS() {
- const [e] = ce();
- return XS(e), null;
+function gS() {
+ const [e] = ae();
+ return mS(e), null;
}
-function XS(e) {
- z(() => {
- if (!e.hasNodes([me, Ct, Ee, Ke, dt]))
+function mS(e) {
+ F(() => {
+ if (!e.hasNodes([ye, St, Ae, je, dt]))
throw new Error("TextSpacingPlugin: CharNode, ImmutableVerseNode, NoteNode, TextNode or VerseNode not registered on editor!");
- return Ve(
- e.registerNodeTransform(Ke, QS),
- e.registerNodeTransform(Ke, (t) => ZS(t, e)),
- e.registerNodeTransform(dt, Pd),
- e.registerNodeTransform(Ct, Pd),
+ return Be(
+ e.registerNodeTransform(je, yS),
+ e.registerNodeTransform(je, (t) => bS(t, e)),
+ e.registerNodeTransform(dt, zd),
+ e.registerNodeTransform(St, zd),
// Self-healing \va/\vp display runs: re-derive them from altnumber/pubnumber whenever
// a verse is dirtied — heals remote collab updates (delta-apply only calls setAltnumber/
// setPubnumber) and structure surgery. Registered here (not a dedicated VerseNodePlugin,
@@ -19949,28 +19953,28 @@ function XS(e) {
// driven `\va` first so `\vp`'s scan and insertion anchor find the healed `\va` wrapper
// already in place.
e.registerNodeTransform(dt, (t) => {
- es(xn("va"), t), es(xn("vp"), t);
+ ss(_n("va"), t), ss(_n("vp"), t);
})
);
}, [e]);
}
-function QS(e) {
+function yS(e) {
if (!e.isAttached())
return;
const t = e.getTextContent(), r = e.getNextSibling(), n = e.getParent();
- if (e.getMode() !== "normal" || t.endsWith(" ") && t.length > 1 || j(r) || D(n) || D(r) || Ce(n) || Ce(r) || De(n) || // An adjacent TextNode is the same logical text run (IME composition and annotation-wrap
+ if (e.getMode() !== "normal" || t.endsWith(" ") && t.length > 1 || z(r) || I(n) || I(r) || _e(n) || _e(r) || Ue(n) || // An adjacent TextNode is the same logical text run (IME composition and annotation-wrap
// splits leave runs as multiple nodes, e.g. a segmented composition node that Lexical
// won't merge). No structural space belongs inside a run — inserting one corrupts the
// word itself (#513, complex scripts worst). This also protects a space-only node from
// the placeholder cleanup below: between two text nodes it is real content.
- v(r) || // An optbreak (`//`) — like a ref — is an inline UnknownNode carrying SIGNIFICANT surrounding
+ M(r) || // An optbreak (`//`) — like a ref — is an inline UnknownNode carrying SIGNIFICANT surrounding
// whitespace (Paratext 9 preserves the spaces around `//` byte-for-byte). Forcing a trailing
// space onto the text before one — or removing a lone space there — corrupts the authored form
// and makes the space impossible to delete (the transform re-adds it every keystroke). Text
// adjacent to an inline unknown is left exactly as authored, the same next-sibling exemption
// already applied to notes, chars, and typed marks. Block-level unknowns (figures, sidebars)
// keep the existing spacing behavior.
- De(r) && r.isInlineTag() || // An attribute display run (char/milestone/verse — attributeDisplay.utils.ts) is engine-owned
+ Ue(r) && r.isInlineTag() || // An attribute display run (char/milestone/verse — attributeDisplay.utils.ts) is engine-owned
// presentation, not paragraph prose: it must never gain a trailing space of its own, even
// when it sits directly in a paragraph (a verse's \va/\vp value has no CharNode parent to
// exempt it the way a char span's own run is already protected).
@@ -19981,89 +19985,89 @@ function QS(e) {
// presentation. The transform still exempts whichever shape — loose attribute text or a
// wrapper's children — is actually in the tree, so a pre-flip loose editor state stays exempt
// too.
- Le(n))
+ De(n))
return;
- if (ge(e.getPreviousSibling()) && (t === "" || t === " ")) {
+ if (he(e.getPreviousSibling()) && (t === "" || t === " ")) {
t !== "" && e.setTextContent("");
return;
}
- ge(r) && Jc(e);
+ he(r) && sl(e);
}
-function ZS(e, t) {
+function bS(e, t) {
const r = e.getParent();
- !De(r) || !e.isAttached() || Ua(t, e.getKey()) && !Ua(t, r.getKey()) && r.insertAfter(e);
+ !Ue(r) || !e.isAttached() || Ba(t, e.getKey()) && !Ba(t, r.getKey()) && r.insertAfter(e);
}
-function Pd(e) {
+function zd(e) {
if (!e.isAttached())
return;
let t = e.getPreviousSibling();
- for (; Ce(t); )
+ for (; _e(t); )
t = t.getLastChild();
- (D(t) || v(t) && Ce(t.getParent())) && e.insertBefore(pe(" "));
+ (I(t) || M(t) && _e(t.getParent())) && e.insertBefore(ge(" "));
}
-function yl(e) {
- if (!j(e) || e.getIsCollapsed() !== !0)
+function Al(e) {
+ if (!z(e) || e.getIsCollapsed() !== !0)
return;
const t = e.getParent();
- return !t || t.isInline() || e.getNextSiblings().some((n) => !qc(n)) ? void 0 : e;
+ return !t || t.isInline() || e.getNextSiblings().some((n) => !Kc(n)) ? void 0 : e;
}
-function ev(e) {
+function kS(e) {
if (e.type !== "element")
return;
const t = e.getNode();
- if (F(t))
+ if (L(t))
return t.getChildAtIndex(e.offset - 1) ?? void 0;
}
-function tv() {
+function TS() {
const e = w();
if (!(!N(e) || !e.isCollapsed()))
- return yl(ev(e.anchor));
+ return Al(kS(e.anchor));
}
-function rv(e) {
+function xS(e) {
const t = w();
let r;
- return N(t) ? t.isCollapsed() && (r = t.anchor.getNode()) : t || (r = tg(e.target)), r ? yl(nt(r, j)) : void 0;
+ return N(t) ? t.isCollapsed() && (r = t.anchor.getNode()) : t || (r = mg(e.target)), r ? Al(Xe(r, z)) : void 0;
}
-function tg(e) {
- const t = Ny(e)?.anchorNode;
- if (Tf(t))
- return li(t) ?? void 0;
+function mg(e) {
+ const t = Wy(e)?.anchorNode;
+ if (Rf(t))
+ return hi(t) ?? void 0;
}
-function nv(e) {
+function _S(e) {
if (w())
return;
- const t = tg(e);
- return t ? yl(nt(t, j)) : void 0;
+ const t = mg(e);
+ return t ? Al(Xe(t, z)) : void 0;
}
-function iv() {
- const [e] = ce(), t = jh(tv);
- return z(() => {
+function CS() {
+ const [e] = ae(), t = ig(TS);
+ return F(() => {
const r = (n) => {
- jr(Br), t(n);
+ Wr(Hr), t(n);
};
- return Ve(e.registerCommand(ur, () => {
- const n = nv(e.getRootElement());
+ return Be(e.registerCommand(ur, () => {
+ const n = _S(e.getRootElement());
return n && r(n), !1;
- }, mn), e.registerCommand(lo, (n) => {
- const i = rv(n);
+ }, bn), e.registerCommand(mo, (n) => {
+ const i = xS(n);
return i && r(i), !1;
- }, mn));
+ }, bn));
}, [e, t]), null;
}
-function sv({ trigger: e, scriptureReference: t, contextMarker: r, getMarkerAction: n }) {
- const { markersMenuItems: i } = C_({
+function vS({ trigger: e, scriptureReference: t, contextMarker: r, getMarkerAction: n }) {
+ const { markersMenuItems: i } = K_({
scriptureReference: t,
contextMarker: r,
getMarkerAction: n
});
- return C(__, { trigger: e, items: i });
+ return S(z_, { trigger: e, items: i });
}
-function ov({ trigger: e, scrRef: t, contextMarker: r, getMarkerAction: n, editableHarness: i }) {
- const { book: s, chapterNum: o, verseNum: a, verse: c, versificationStr: l } = t, u = ze(() => ({ book: s, chapterNum: o, verseNum: a, verse: c, versificationStr: l }), [s, o, a, c, l]);
- return i ? C(lv, { trigger: e, harness: i }) : C(sv, { trigger: e, scriptureReference: u, contextMarker: r, getMarkerAction: n });
+function SS({ trigger: e, scrRef: t, contextMarker: r, getMarkerAction: n, editableHarness: i }) {
+ const { book: s, chapterNum: o, verseNum: a, verse: c, versificationStr: l } = t, d = Ke(() => ({ book: s, chapterNum: o, verseNum: a, verse: c, versificationStr: l }), [s, o, a, c, l]);
+ return i ? S(AS, { trigger: e, harness: i }) : S(vS, { trigger: e, scriptureReference: d, contextMarker: r, getMarkerAction: n });
}
-const av = [" ", "*"];
-function cv(e, t) {
+const MS = [" ", "*"];
+function ES(e, t) {
return {
name: e.marker,
label: e.marker,
@@ -20076,19 +20080,19 @@ function cv(e, t) {
applyOpts: t
};
}
-function lv({ trigger: e, harness: t }) {
- const [r] = ce(), [n, i] = de(void 0), s = Z({ query: "", options: [] }), o = Z(0), a = he((f, p, m) => {
- const g = p.find((y) => y.kind === "note" && y.marker === f);
- if (g) {
- t.apply(g, { trigger: "backslash", literalPrefixLanded: !1 });
+function AS({ trigger: e, harness: t }) {
+ const [r] = ae(), [n, i] = de(void 0), s = Z({ query: "", options: [] }), o = Z(0), a = me((f, p, y) => {
+ const h = p.find((m) => m.kind === "note" && m.marker === f);
+ if (h) {
+ t.apply(h, { trigger: "backslash", literalPrefixLanded: !1 });
return;
}
r.update(() => {
- const y = w();
- N(y) && y.insertText(`${e}${f}${m ? " " : ""}`);
+ const m = w();
+ N(m) && m.insertText(`${e}${f}${y ? " " : ""}`);
});
}, [r, t, e]);
- z(() => Ve(r.registerCommand(vr, (f) => {
+ F(() => Be(r.registerCommand(Nr, (f) => {
if (n) {
if ((f.key === "Enter" || f.key === "Tab") && s.current.options.length === 0)
return f.preventDefault(), f.stopPropagation(), !0;
@@ -20096,29 +20100,29 @@ function lv({ trigger: e, harness: t }) {
return f.preventDefault(), f.stopPropagation(), i(void 0), t.commitTypedCloser(s.current.query), !0;
if (f.key === e && n.trigger === "backslash" && !n.hasTextSelection) {
f.preventDefault(), f.stopPropagation();
- const g = s.current.query;
- return g ? (a(g, n.items, !1), Oy(() => {
- const y = t.getContext();
- s.current = { query: "", options: [] }, o.current += 1, i(y ? {
+ const h = s.current.query;
+ return h ? (a(h, n.items, !1), If(() => {
+ const m = t.getContext();
+ s.current = { query: "", options: [] }, o.current += 1, i(m ? {
trigger: "backslash",
- hasTextSelection: y.hasTextSelection,
- items: t.getItems(y),
+ hasTextSelection: m.hasTextSelection,
+ items: t.getItems(m),
session: o.current
} : void 0);
}), !0) : (i(void 0), r.update(() => {
- const y = w();
- N(y) && y.insertText(e);
+ const m = w();
+ N(m) && m.insertText(e);
}), !0);
}
if (f.key !== " " || n.trigger !== "backslash")
return !1;
f.preventDefault(), f.stopPropagation(), i(void 0);
- const m = s.current.query;
+ const y = s.current.query;
if (n.hasTextSelection) {
- const g = n.items.find((y) => y.marker === m);
- return g && t.apply(g, { trigger: "backslash", literalPrefixLanded: !1 }), !0;
+ const h = n.items.find((m) => m.marker === y);
+ return h && t.apply(h, { trigger: "backslash", literalPrefixLanded: !1 }), !0;
}
- return a(m, n.items, !0), !0;
+ return a(y, n.items, !0), !0;
}
if (f.key !== e)
return !1;
@@ -20129,7 +20133,7 @@ function lv({ trigger: e, harness: t }) {
items: t.getItems(p),
session: o.current
}), !0) : !1;
- }, Ne), r.registerCommand(_f, (f) => {
+ }, Oe), r.registerCommand(Df, (f) => {
if (n || f === null || f.shiftKey)
return !1;
const p = t.getContext();
@@ -20139,61 +20143,61 @@ function lv({ trigger: e, harness: t }) {
items: t.getEnterItems(p),
session: o.current
}), !0);
- }, Jn)), [r, e, t, n, a]);
- const c = he(() => i(void 0), []), l = he((f, p) => {
+ }, Vr)), [r, e, t, n, a]);
+ const c = me(() => i(void 0), []), l = me((f, p) => {
s.current = { query: f, options: p };
- }, []), u = he((f) => {
- const { markerMenuItem: p, applyOpts: m } = f;
- t.apply(p, m);
- }, [t]), d = ze(() => n?.items.map((f) => (
+ }, []), d = me((f) => {
+ const { markerMenuItem: p, applyOpts: y } = f;
+ t.apply(p, y);
+ }, [t]), u = Ke(() => n?.items.map((f) => (
// `literalPrefixLanded` is constant `false` under the active palette: the trigger never
// lands, so an item commit never has a literal prefix to clean up. The field stays in
// the apply contract because hosts whose own palettes DO land literals still pass true.
- cv(f, { trigger: n.trigger, literalPrefixLanded: !1 })
+ ES(f, { trigger: n.trigger, literalPrefixLanded: !1 })
)), [n]);
- return n && C(_h, { isOpen: !0, children: ({ placement: f }) => C(
- vh,
- { options: d ?? [], onSelectOption: u, onClose: c, onFilterChange: l, inverse: f === "top-start", menuOpenKey: e, passthroughKeys: n.trigger === "backslash" ? av : void 0 },
+ return n && S(Ih, { isOpen: !0, children: ({ placement: f }) => S(
+ Fh,
+ { options: u ?? [], onSelectOption: d, onClose: c, onFilterChange: l, inverse: f === "top-start", menuOpenKey: e, passthroughKeys: n.trigger === "backslash" ? MS : void 0 },
n.session
) });
}
-function rg(e) {
- return e.replaceAll(I, "~").replace(/ {2,}/g, (r) => I.repeat(r.length));
+function yg(e) {
+ return e.replaceAll(R, "~").replace(/ {2,}/g, (r) => R.repeat(r.length));
}
-function uv(e) {
- return e.replaceAll(I, " ").replaceAll("~", I);
+function PS(e) {
+ return e.replaceAll(R, " ").replaceAll("~", R);
}
-function dv(e) {
+function NS(e) {
return e.replace(/ {2,}/g, " ");
}
-let Zs;
-function fv(e) {
- e && (Zs = e);
+let no;
+function OS(e) {
+ e && (no = e);
}
-function ng(e) {
- return Ao(e);
+function bg(e) {
+ return Ts(e);
}
-function pv(e, t) {
- return e.isEmpty() ? bf : ig(e.toJSON(), t);
+function wS(e, t) {
+ return e.isEmpty() ? wf : kg(e.toJSON(), t);
}
-function ig(e, t) {
+function kg(e, t) {
if (!e.root || !e.root.children) return;
const r = e.root.children;
- if (r.length === 1 && go(r[0]) && (!r[0].children || r[0].children.length === 0))
- return bf;
- if (r.some(WT)) {
- Zs?.error(
+ if (r.length === 1 && ko(r[0]) && (!r[0].children || r[0].children.length === 0))
+ return wf;
+ if (r.some(dx)) {
+ no?.error(
"Block verse layout is not round-trippable to USJ. VerseBlockNode is read-only view state; use the source USJ instead."
);
return;
}
- const n = sg(r), i = Bt(n, t);
- return i ? { type: kr, version: br, content: i } : void 0;
+ const n = Tg(r), i = Bt(n, t);
+ return i ? { type: Cr, version: _r, content: i } : void 0;
}
-function hv(e, t) {
+function qS(e, t) {
const { type: r, marker: n, unknownAttributes: i } = e;
let s;
- return e.code !== "" && (s = e.code), Ae({
+ return e.code !== "" && (s = e.code), Pe({
type: r,
marker: n,
code: s,
@@ -20201,10 +20205,10 @@ function hv(e, t) {
content: t
});
}
-function gv(e) {
+function RS(e) {
const { marker: t, number: r, sid: n, altnumber: i, pubnumber: s, unknownAttributes: o } = e;
- return Ae({
- type: Nt.getType(),
+ return Pe({
+ type: Ot.getType(),
marker: t,
number: r,
sid: n,
@@ -20213,11 +20217,11 @@ function gv(e) {
...o
});
}
-function mv(e, t) {
+function $S(e, t) {
const { marker: r, sid: n, altnumber: i, pubnumber: s, unknownAttributes: o } = e, a = t && typeof t[0] == "string" ? t[0] : void 0;
let { number: c } = e;
- return c = Cp(r, a, c), Ae({
- type: Nt.getType(),
+ return c = Up(r, a, c), Pe({
+ type: Ot.getType(),
marker: r,
number: c,
sid: n,
@@ -20226,10 +20230,10 @@ function mv(e, t) {
...o
});
}
-function yv(e) {
+function LS(e) {
const { marker: t, sid: r, altnumber: n, pubnumber: i, unknownAttributes: s } = e, { text: o } = e;
let { number: a } = e;
- return a = Cp(t, o, a), Ae({
+ return a = Up(t, o, a), Pe({
type: dt.getType(),
marker: t,
number: a,
@@ -20239,40 +20243,40 @@ function yv(e) {
...s
});
}
-function bv(e, t, r) {
+function IS(e, t, r) {
const { type: n, marker: i, unknownAttributes: s } = e, o = i === "" ? void 0 : i;
- if (r?.markerMode === "editable" && !ng(r) && t) {
+ if (r?.markerMode === "editable" && !bg(r) && t) {
const [a] = t;
- typeof a == "string" && a.startsWith(I) && (t[0] = a.slice(1));
+ typeof a == "string" && a.startsWith(R) && (t[0] = a.slice(1));
}
- return Ae({
+ return Pe({
type: n,
marker: o,
...s,
content: t
});
}
-function kv(e, t) {
+function DS(e, t) {
const { type: r, marker: n, unknownAttributes: i } = e;
- return Ae({
+ return Pe({
type: r,
marker: n,
...i,
content: t
});
}
-function Tv(e, t) {
+function US(e, t) {
const { unknownAttributes: r } = e;
- return Ae({ type: Yp, ...r, content: t });
+ return Pe({ type: fh, ...r, content: t });
}
-function xv(e, t) {
+function FS(e, t) {
const { marker: r, unknownAttributes: n } = e;
- return Ae({ type: Zp, marker: r, ...n, content: t });
+ return Pe({ type: gh, marker: r, ...n, content: t });
}
-function _v(e, t) {
+function zS(e, t) {
const { marker: r, align: n, colspan: i, unknownAttributes: s } = e;
- return Ae({
- type: th,
+ return Pe({
+ type: yh,
marker: r,
align: n,
colspan: i,
@@ -20280,9 +20284,9 @@ function _v(e, t) {
content: t
});
}
-function Cv(e, t) {
+function KS(e, t) {
const { type: r, marker: n, caller: i, category: s, unknownAttributes: o } = e;
- return Ae({
+ return Pe({
type: r,
marker: n,
caller: i,
@@ -20291,183 +20295,183 @@ function Cv(e, t) {
content: t
});
}
-function Gn(e) {
+function Qn(e) {
const { type: t, marker: r, sid: n, eid: i, unknownAttributes: s, attributeOrder: o } = e;
- return Ae({
+ return Pe({
type: t,
marker: r === "" ? void 0 : r,
- ...Op({ sid: n, eid: i, ...s }, o)
+ ...Hp({ sid: n, eid: i, ...s }, o)
});
}
-function Sv(e) {
+function jS(e) {
return e.text;
}
-function vv(e, t) {
+function BS(e, t) {
const { tag: r, marker: n, unknownAttributes: i } = e;
- return Ae({
+ return Pe({
type: r,
marker: n,
...i,
content: t
});
}
-function Mv(e) {
+function VS(e) {
const { marker: t } = e;
return {
- type: js,
+ type: Hs,
marker: t === "" ? void 0 : t
};
}
-function Nd(e, t) {
+function Kd(e, t) {
const r = e[e.length - 1];
r && typeof r == "string" ? e[e.length - 1] = r + t : e.push(t);
}
-function Ev(e, t, r, n, i) {
- const s = Jt.getType(), o = t.filter((l) => !r.includes(l));
+function WS(e, t, r, n, i) {
+ const s = Gt.getType(), o = t.filter((l) => !r.includes(l));
if (r.filter((l) => !t.includes(l)).forEach((l) => {
- const u = Gn({
+ const d = Qn({
type: s,
- marker: Yn,
+ marker: Zn,
eid: l
});
- i.push(u);
+ i.push(d);
}), o.forEach((l) => {
- const u = Gn({
+ const d = Qn({
type: s,
- marker: bn,
+ marker: kn,
sid: l
});
- i.push(u);
+ i.push(d);
}), t.length === 0) {
- const l = Gn({
+ const l = Qn({
type: s,
- marker: bn
+ marker: kn
});
i.push(l);
}
if (i.push(...e), t.length === 0) {
- const l = Gn({
+ const l = Qn({
type: s,
- marker: Yn
+ marker: Zn
});
i.push(l);
}
- (!n || !Qf(n)) && t.forEach((l) => {
- const u = Gn({
+ (!n || !hp(n)) && t.forEach((l) => {
+ const d = Qn({
type: s,
- marker: Yn,
+ marker: Zn,
eid: l
});
- i.push(u);
+ i.push(d);
});
}
function Bt(e, t, r, n = !1) {
const i = [];
let s, o = [];
return e.forEach((a, c) => {
- const l = a, u = a, d = a, f = a, p = a, m = a, g = a, y = a;
+ const l = a, d = a, u = a, f = a, p = a, y = a, h = a, m = a;
switch (a.type) {
- case Ut.getType():
+ case Ft.getType():
i.push(
- hv(
+ qS(
l,
Bt(l.children, t)
)
);
break;
case pr.getType():
- i.push(gv(a));
+ i.push(RS(a));
break;
- case Nt.getType():
+ case Ot.getType():
i.push(
- mv(
- u,
- Bt(u.children, t)
+ $S(
+ d,
+ Bt(d.children, t)
)
);
break;
- case Ct.getType():
+ case St.getType():
case dt.getType():
- i.push(yv(a));
+ i.push(LS(a));
break;
- case me.getType():
+ case ye.getType():
i.push(
- bv(
- d,
- Bt(d.children, t, void 0, !0),
+ IS(
+ u,
+ Bt(u.children, t, void 0, !0),
t
)
);
break;
- case Ze.getType():
+ case rt.getType():
i.push(
- kv(
+ DS(
f,
Bt(f.children, t)
)
);
break;
- case On.getType():
+ case $n.getType():
i.push(
- Tv(
+ US(
a,
Bt(a.children, t)
)
);
break;
- case fi.getType():
+ case mi.getType():
i.push(
- xv(
+ FS(
a,
Bt(a.children, t)
)
);
break;
- case pi.getType():
+ case yi.getType():
i.push(
- _v(
+ zS(
a,
Bt(a.children, t)
)
);
break;
- case Ee.getType():
+ case Ae.getType():
i.push(
- Cv(
+ KS(
p,
Bt(p.children, t, p.caller)
)
);
break;
- case Ar.getType():
- case Er.getType():
- case Gt.getType():
- case Cf.getType():
+ case qr.getType():
+ case wr.getType():
+ case Ht.getType():
+ case Uf.getType():
case hr.getType():
break;
- case Qe.getType():
+ case tt.getType():
if (s = Bt(
- g.children,
+ h.children,
t,
r,
n
), s) {
- const k = g.typedIDs[Kr];
- if (k)
- Ev(s, k, o, e[c + 1], i), o = k;
+ const b = h.typedIDs[Br];
+ if (b)
+ WS(s, b, o, e[c + 1], i), o = b;
else {
- const _ = s.shift();
- _ && (typeof _ == "string" ? Nd(i, _) : i.push(_)), s.length > 0 && i.push(...s);
+ const x = s.shift();
+ x && (typeof x == "string" ? Kd(i, x) : i.push(x)), s.length > 0 && i.push(...s);
}
}
break;
- case Jt.getType():
- i.push(Gn(a));
+ case Gt.getType():
+ i.push(Qn(a));
break;
- case Ke.getType():
- if (m.text && // Drop a bare caret host (EmptyVerseCaretGuardPlugin). A legitimate ZWSP inside real text
+ case je.getType():
+ if (y.text && // Drop a bare caret host (EmptyVerseCaretGuardPlugin). A legitimate ZWSP inside real text
// (Thai/Khmer line breaks) is not placeholder-only, so it still passes and is preserved.
- !ps(m.text) && // A byte test, not (only) the separator state tag, and deliberately so: a lone-NBSP
+ !ys(y.text) && // A byte test, not (only) the separator state tag, and deliberately so: a lone-NBSP
// text node stands in for THREE presentation shapes — the tagged separators the
// forward adaptor builds, the empty-char placeholder, and an orphaned structural
// prefix a split or deletion strands in its own (untagged) node. The known cost is
@@ -20478,65 +20482,65 @@ function Bt(e, t, r, n = !1) {
// exactly this shape, so in standard view only an authored lone-NBSP data string
// (displayed as `~`, never as a bare NBSP node) is at stake — leaving the drop to
// genuinely structural nodes.
- m.text !== I && !m.text.startsWith(Sc) && // Char-span attribute display runs (bare `|…`, no NBSP prefix — see
+ y.text !== R && !y.text.startsWith(qc) && // Char-span attribute display runs (bare `|…`, no NBSP prefix — see
// usj-editor.adaptor's `addCharAttributes`) carry no NBSP prefix to strip against, so
// the prefix check above can't catch them; the textType state tag is the only signal.
- m[ds]?.textType !== "attribute" && (!r || m.text !== At(r))) {
- let k = Sv(m);
- ng(t) && (n && k.startsWith(I) && (k = k.slice(1)), k = dv(uv(k))), Nd(i, k);
+ y[gs]?.textType !== "attribute" && (!r || y.text !== Pt(r))) {
+ let b = jS(y);
+ bg(t) && (n && b.startsWith(R) && (b = b.slice(1)), b = NS(PS(b))), Kd(i, b);
}
break;
- case Pn.getType():
+ case qn.getType():
i.push(
- vv(
- y,
- Bt(y.children, t)
+ BS(
+ m,
+ Bt(m.children, t)
)
);
break;
- case Or.getType():
- i.push(Mv(a));
+ case Lr.getType():
+ i.push(VS(a));
break;
- case hi.getType():
- Zs?.error("Block verse layout is not round-trippable to USJ; skipping the block.");
+ case bi.getType():
+ no?.error("Block verse layout is not round-trippable to USJ; skipping the block.");
break;
default:
- Zs?.error(`Unexpected node type '${a.type}'!`);
+ no?.error(`Unexpected node type '${a.type}'!`);
}
}), i && i.length > 0 ? i : void 0;
}
-function sg(e) {
- const t = e.findIndex((r) => go(r));
+function Tg(e) {
+ const t = e.findIndex((r) => ko(r));
if (t >= 0) {
- const r = e.slice(0, t), n = e[t].children, i = sg(e.slice(t + 1));
+ const r = e.slice(0, t), n = e[t].children, i = Tg(e.slice(t + 1));
e = [...r, ...n, ...i];
}
return e;
}
-const Os = {
- initialize: fv,
- deserializeEditorState: pv
-}, Av = /^sd\d*$/, Pv = /* @__PURE__ */ new Set([
- ...Object.entries(va).filter(
- ([e, t]) => t.category === T.TitlesHeadings && t.type === b.Paragraph && !Av.test(e)
+const Ls = {
+ initialize: OS,
+ deserializeEditorState: wS
+}, HS = /^sd\d*$/, GS = /* @__PURE__ */ new Set([
+ ...Object.entries(Na).filter(
+ ([e, t]) => t.category === T.TitlesHeadings && t.type === k.Paragraph && !HS.test(e)
).map(([e]) => e),
"qa"
]);
-function Nv(e, t) {
+function JS(e, t) {
const r = [];
let n;
for (const i of e) {
- if (sp(i) || kp(i)) {
+ if (xp(i) || $p(i)) {
n = void 0, r.push(i);
continue;
}
- if (!kk(i)) {
- t && eo(i) && t.warn(
+ if (!Dk(i)) {
+ t && io(i) && t.warn(
`Verses inside a '${i.type}' are not grouped into blocks; the whole node stays with the surrounding verse.`
), n ? n.children.push(i) : r.push(i);
continue;
}
- if (wc(i) && Pv.has(i.marker) && !eo(i)) {
+ if (zc(i) && GS.has(i.marker) && !io(i)) {
n = void 0, r.push(i);
continue;
}
@@ -20544,78 +20548,78 @@ function Nv(e, t) {
n ? n.children.push(i) : r.push(i);
continue;
}
- og(i.children, t).forEach((s) => {
- const o = Ov(i, s.nodes);
+ xg(i.children, t).forEach((s) => {
+ const o = YS(i, s.nodes);
if (!s.verse) {
if (!o) return;
n ? n.children.push(o) : r.push(o);
return;
}
- n = wv(s.verse), r.push(n), o && n.children.push(o);
+ n = XS(s.verse), r.push(n), o && n.children.push(o);
});
}
return r;
}
-function og(e, t) {
+function xg(e, t) {
const r = [{ nodes: [] }], n = (i) => r[r.length - 1].nodes.push(i);
return e.forEach((i) => {
- if (ag(i)) {
+ if (_g(i)) {
r.push({ verse: i, nodes: [i] });
return;
}
- if (Qf(i)) {
- const s = og(i.children, t), [o, ...a] = s;
- o.nodes.length > 0 && n(Od(i, o.nodes)), a.forEach((c) => {
- r.push({ verse: c.verse, nodes: [Od(i, c.nodes)] });
+ if (hp(i)) {
+ const s = xg(i.children, t), [o, ...a] = s;
+ o.nodes.length > 0 && n(jd(i, o.nodes)), a.forEach((c) => {
+ r.push({ verse: c.verse, nodes: [jd(i, c.nodes)] });
});
return;
}
- t && eo(i) && t?.warn(
+ t && io(i) && t?.warn(
`Verse marker nested inside a '${i.type}' node was not grouped into a block.`
), n(i);
}), r;
}
-function Od(e, t) {
+function jd(e, t) {
return { ...e, children: t };
}
-function ag(e) {
- return dh(e) && e.number !== "";
+function _g(e) {
+ return Mh(e) && e.number !== "";
}
-function eo(e) {
+function io(e) {
const t = e.children;
- return Array.isArray(t) ? t.some((r) => ag(r) || eo(r)) : !1;
+ return Array.isArray(t) ? t.some((r) => _g(r) || io(r)) : !1;
}
-function Ov(e, t) {
+function YS(e, t) {
if (t.length !== 0)
return { ...e, children: t };
}
-function wv(e) {
+function XS(e) {
return {
- type: Bs,
+ type: Gs,
number: e.number,
children: [],
direction: null,
format: "",
indent: 0,
- version: ch
+ version: Ch
};
}
-const wd = lg([]), qv = {
- type: Cf.getType(),
+const Bd = vg([]), QS = {
+ type: Uf.getType(),
version: 1
};
-let bl = [], ee, Sn, cg, _t;
-function Rv(e, t) {
- bl = [], Lv(e), Dv(t);
+let Pl = [], te, Sn, Cg, vt;
+function ZS(e, t) {
+ Pl = [], rM(e), nM(t);
}
-function $v(e = 0) {
+function eM(e = 0) {
}
-function Iv(e, t) {
- ee = t ?? Eo();
+function tM(e, t) {
+ te = t ?? Oo();
let r;
- return e ? (e.type !== kr && _t?.warn(`This USJ type '${e.type}' didn't match the expected type '${kr}'.`), e.version !== br && _t?.warn(
- `This USJ version '${e.version}' didn't match the expected version '${br}'.`
- ), e.content.length > 0 ? (r = Qa(Ur(e.content)), ns(ee) && (r = Nv(r, _t))) : r = [wd]) : r = [wd], cg?.(bl), {
+ return e ? (e.type !== Cr && vt?.warn(`This USJ type '${e.type}' didn't match the expected type '${Cr}'.`), e.version !== _r && vt?.warn(
+ `This USJ version '${e.version}' didn't match the expected version '${_r}'.`
+ ), e.content.length > 0 ? (r = nc(zr(e.content)), cs(te) && (r = JS(r, vt))) : r = [Bd]) : r = [Bd], Cg?.(Pl), {
root: {
children: r,
direction: null,
@@ -20626,32 +20630,32 @@ function Iv(e, t) {
}
};
}
-function Lv(e) {
- e && (Sn = e), e?.addMissingComments && (cg = e.addMissingComments);
+function rM(e) {
+ e && (Sn = e), e?.addMissingComments && (Cg = e.addMissingComments);
}
-function Dv(e) {
- e && (_t = e);
+function nM(e) {
+ e && (vt = e);
}
-function kl() {
- return Ao(ee);
+function Nl() {
+ return Ts(te);
}
-function Uv(e) {
+function iM(e) {
return !e || e.length !== 1 || typeof e[0] != "string" ? "" : e[0];
}
-function Fv(e) {
+function sM(e) {
let { marker: t } = e;
- t !== Gi && _t?.warn(`Unexpected book marker '${t}'!`), t = t ?? Gi;
+ t !== Zi && vt?.warn(`Unexpected book marker '${t}'!`), t = t ?? Zi;
const { code: r } = e;
- (!r || !Ut.isValidBookCode(r)) && _t?.warn(`Unexpected book code '${r}'!`);
+ (!r || !Ft.isValidBookCode(r)) && vt?.warn(`Unexpected book code '${r}'!`);
const n = [];
- ee?.markerMode === "editable" || ee?.markerMode === "visible" ? n.push(
- kt("marker", we(t) + " " + r + I)
- ) : ee?.hasGutterParaMarkers && n.push(kt("marker", we(t) + I, !0));
- const i = Uv(e.content);
- i && n.push(at(kl() ? rg(i) : i));
- const s = Ue(e, Qb);
- return Ae({
- type: Ut.getType(),
+ te?.markerMode === "editable" || te?.markerMode === "visible" ? n.push(
+ Tt("marker", qe(t) + " " + r + R)
+ ) : te?.hasGutterParaMarkers && n.push(Tt("marker", qe(t) + R, !0));
+ const i = iM(e.content);
+ i && n.push(lt(Nl() ? yg(i) : i));
+ const s = Fe(e, yk);
+ return Pe({
+ type: Ft.getType(),
marker: t,
code: r ?? "",
unknownAttributes: s,
@@ -20659,20 +20663,20 @@ function Fv(e) {
direction: null,
format: "",
indent: 0,
- version: np
+ version: kp
});
}
-function zv(e) {
+function oM(e) {
let { marker: t } = e;
- t !== Fs && _t?.warn(`Unexpected chapter marker '${t}'!`), t = t ?? Fs;
- const { number: r, sid: n, altnumber: i, pubnumber: s } = e, o = Ue(e, Zb);
+ t !== Bs && vt?.warn(`Unexpected chapter marker '${t}'!`), t = t ?? Bs;
+ const { number: r, sid: n, altnumber: i, pubnumber: s } = e, o = Fe(e, bk);
let a;
- ee?.markerMode === "visible" && (a = !0);
+ te?.markerMode === "visible" && (a = !0);
const c = [
- at(Lt(t, r) ?? "")
+ lt(Dt(t, r) ?? "")
];
- return ee?.markerMode === "editable" && iM(i, s, c), ee?.markerMode === "editable" ? Ae({
- type: Nt.getType(),
+ return te?.markerMode === "editable" && CM(i, s, c), te?.markerMode === "editable" ? Pe({
+ type: Ot.getType(),
marker: t,
number: r ?? "",
sid: n,
@@ -20683,8 +20687,8 @@ function zv(e) {
direction: null,
format: "",
indent: 0,
- version: op
- }) : Ae({
+ version: _p
+ }) : Pe({
type: pr.getType(),
marker: t,
number: r ?? "",
@@ -20693,17 +20697,17 @@ function zv(e) {
altnumber: i,
pubnumber: s,
unknownAttributes: o,
- version: dp
+ version: Ep
});
}
-function Kv(e) {
+function aM(e) {
let { marker: t } = e;
- t !== zs && _t?.warn(`Unexpected verse marker '${t}'!`), t = t ?? zs;
- const { number: r, sid: n, altnumber: i, pubnumber: s } = e, a = (w_(ee) ?? Ct).getType(), c = ee?.markerMode === "editable" ? mp : uh;
- let l, u;
- ee?.markerMode === "editable" ? l = Lt(t, r) : ee?.markerMode === "visible" && (u = !0);
- const d = Ue(e, fk);
- return Ae({
+ t !== Vs && vt?.warn(`Unexpected verse marker '${t}'!`), t = t ?? Vs;
+ const { number: r, sid: n, altnumber: i, pubnumber: s } = e, a = (X_(te) ?? St).getType(), c = te?.markerMode === "editable" ? wp : Sh;
+ let l, d;
+ te?.markerMode === "editable" ? l = Dt(t, r) : te?.markerMode === "visible" && (d = !0);
+ const u = Fe(e, Ok);
+ return Pe({
type: a,
text: l,
...l === void 0 ? void 0 : { detail: 0, format: 0, mode: "normal", style: "" },
@@ -20712,66 +20716,66 @@ function Kv(e) {
sid: n,
altnumber: i,
pubnumber: s,
- showMarker: u,
- unknownAttributes: d,
+ showMarker: d,
+ unknownAttributes: u,
version: c
});
}
-function jv(e, t = [], r = !1) {
+function cM(e, t = [], r = !1) {
let { marker: n } = e;
- me.isValidMarker(n, Sn?.extraValidMarkers) || _t?.warn(`Unexpected char marker '${n}'!`), n = n ?? "";
+ ye.isValidMarker(n, Sn?.extraValidMarkers) || vt?.warn(`Unexpected char marker '${n}'!`), n = n ?? "";
const i = [];
- if (ee?.markerMode === "editable") {
+ if (te?.markerMode === "editable") {
const [a] = t;
- ri(a) ? a.text = I + a.text : a && t.unshift(at(I));
+ ai(a) ? a.text = R + a.text : a && t.unshift(lt(R));
}
- t.length === 0 && t.push(at(Dt)), Ja(e.marker ?? "", i, r), i.push(...t);
- const s = e.closed === "false", o = Ue(e, rk);
- return s || eM(n, o, i), s || Ya(e.marker ?? "", i, !1, r), Ae({
- type: me.getType(),
+ t.length === 0 && t.push(lt(Ut)), ec(e.marker ?? "", i, r), i.push(...t);
+ const s = e.closed === "false", o = Fe(e, xk);
+ return s || kM(n, o, i), s || tc(e.marker ?? "", i, !1, r), Pe({
+ type: ye.getType(),
marker: n,
unknownAttributes: o,
children: i,
direction: null,
format: "",
indent: 0,
- version: up
+ version: Mp
});
}
-function lg(e) {
+function vg(e) {
return {
- type: Wr.getType(),
+ type: Jr.getType(),
children: e,
direction: null,
format: "",
indent: 0,
textFormat: 0,
textStyle: "",
- version: hp
+ version: Np
};
}
-function Bv(e, t = []) {
+function lM(e, t = []) {
let { marker: r } = e;
- Ze.isValidMarker(r, Sn?.extraValidMarkers) || _t?.warn(`Unexpected para marker '${r}'!`), r = r ?? cr;
+ rt.isValidMarker(r, Sn?.extraValidMarkers) || vt?.warn(`Unexpected para marker '${r}'!`), r = r ?? cr;
const n = [];
- if (gi(ee) && (ee?.markerMode === "editable" ? n.push(
+ if (ki(te) && (te?.markerMode === "editable" ? n.push(
ft(r),
- at(I, fr, "token")
- ) : (ee?.markerMode === "visible" || ee?.hasGutterParaMarkers) && n.push(
- kt(
+ lt(R, fr, "token")
+ ) : (te?.markerMode === "visible" || te?.hasGutterParaMarkers) && n.push(
+ Tt(
"marker",
- we(r) + I,
- ee?.hasGutterParaMarkers
+ qe(r) + R,
+ te?.hasGutterParaMarkers
)
- )), n.push(...t), kl()) {
+ )), n.push(...t), Nl()) {
const s = n.find(
- (o) => !Dc(o) && !(ri(o) && o.text === I)
+ (o) => !Hc(o) && !(ai(o) && o.text === R)
);
- ri(s) && !/^ +$/.test(s.text) && (s.text = s.text.replace(/^ +/, (o) => I.repeat(o.length)));
+ ai(s) && !/^ +$/.test(s.text) && (s.text = s.text.replace(/^ +/, (o) => R.repeat(o.length)));
}
- const i = Ue(e, uk);
- return Ae({
- type: Ze.getType(),
+ const i = Fe(e, Pk);
+ return Pe({
+ type: rt.getType(),
marker: r,
unknownAttributes: i,
children: n,
@@ -20780,134 +20784,134 @@ function Bv(e, t = []) {
indent: 0,
textFormat: 0,
textStyle: "",
- version: gp
+ version: Op
});
}
-function Tl() {
+function Ol() {
return {
direction: null,
format: "",
indent: 0
};
}
-function Vv(e, t = []) {
- const r = Ue(e, hT);
- return Ae({
- ...Tl(),
- type: On.getType(),
+function uM(e, t = []) {
+ const r = Fe(e, qT);
+ return Pe({
+ ...Ol(),
+ type: $n.getType(),
unknownAttributes: r,
children: t,
- version: Xp
+ version: ph
});
}
-function Wv(e, t = []) {
- const r = Ue(e, yT), n = e.marker ?? Ra, i = [];
- return ee?.markerMode === "editable" ? i.push(
+function dM(e, t = []) {
+ const r = Fe(e, LT), n = e.marker ?? Ua, i = [];
+ return te?.markerMode === "editable" ? i.push(
ft(n),
- at(I, fr, "token")
- ) : (ee?.markerMode === "visible" || ee?.hasGutterParaMarkers) && i.push(
- kt(
+ lt(R, fr, "token")
+ ) : (te?.markerMode === "visible" || te?.hasGutterParaMarkers) && i.push(
+ Tt(
"marker",
- we(n) + I,
- ee?.hasGutterParaMarkers
+ qe(n) + R,
+ te?.hasGutterParaMarkers
)
- ), i.push(...t), Ae({
- ...Tl(),
- type: fi.getType(),
+ ), i.push(...t), Pe({
+ ...Ol(),
+ type: mi.getType(),
marker: n,
unknownAttributes: r,
children: i,
- version: eh
+ version: mh
});
}
-function Hv(e, t = []) {
- const { marker: r, align: n, colspan: i } = e, s = [], o = r ?? $a, a = jp(o, i) ?? o;
- ee?.markerMode === "editable" ? s.push(
+function fM(e, t = []) {
+ const { marker: r, align: n, colspan: i } = e, s = [], o = r ?? Fa, a = sh(o, i) ?? o;
+ te?.markerMode === "editable" ? s.push(
ft(a),
- at(I, fr, "token")
- ) : (ee?.markerMode === "visible" || ee?.hasGutterParaMarkers) && s.push(
- kt(
+ lt(R, fr, "token")
+ ) : (te?.markerMode === "visible" || te?.hasGutterParaMarkers) && s.push(
+ Tt(
"marker",
- we(a) + I,
- ee?.hasGutterParaMarkers
+ qe(a) + R,
+ te?.hasGutterParaMarkers
)
), s.push(...t);
- const c = Ue(
+ const c = Fe(
e,
- kT
+ DT
);
- return Ae({
- ...Tl(),
- type: pi.getType(),
+ return Pe({
+ ...Ol(),
+ type: yi.getType(),
marker: o,
align: n,
colspan: i,
unknownAttributes: c,
children: s,
- version: rh
+ version: bh
});
}
-function Gv(e, t) {
- const r = vk(t);
+function pM(e, t) {
+ const r = Bk(t);
let n = () => {
};
- return Sn?.noteCallerOnClick && (n = Sn.noteCallerOnClick), Ae({
- type: Gt.getType(),
+ return Sn?.noteCallerOnClick && (n = Sn.noteCallerOnClick), Pe({
+ type: Ht.getType(),
caller: e,
previewText: r,
onClick: n,
- version: bh
+ version: qh
});
}
-function Jv(e, t) {
+function hM(e, t) {
let { marker: r } = e;
- Ee.isValidMarker(r, Sn?.extraValidMarkers) || _t?.warn(`Unexpected note marker '${r}'!`), r = r ?? Mc;
- const { category: n } = e, i = e.caller ?? "*", s = e.closed === "false", o = s ? !1 : rl(ee?.noteMode), a = Ue(e, gb), c = ee?.isNoteShellEditable === !1 ? "token" : "normal";
- let l, u;
- ee?.markerMode === "editable" ? (l = ft(r, "opening", !1, c), s || (u = ft(r, "closing"))) : ee?.markerMode === "visible" && (l = kt("marker", we(r) + " "), s || (u = kt("marker", rt(r))));
- const d = [];
+ Ae.isValidMarker(r, Sn?.extraValidMarkers) || vt?.warn(`Unexpected note marker '${r}'!`), r = r ?? $c;
+ const { category: n } = e, i = e.caller ?? "*", s = e.closed === "false", o = s ? !1 : fl(te?.noteMode), a = Fe(e, Rb), c = te?.isNoteShellEditable === !1 ? "token" : "normal";
+ let l, d;
+ te?.markerMode === "editable" ? (l = ft(r, "opening", !1, c), s || (d = ft(r, "closing"))) : te?.markerMode === "visible" && (l = Tt("marker", qe(r) + " "), s || (d = Tt("marker", st(r))));
+ const u = [];
let f;
- if (l && d.push(l), ee?.markerMode === "editable" && !o)
- f = at(At(i), void 0, c), d.push(f), nM(n, d), d.push(...t);
+ if (l && u.push(l), te?.markerMode === "editable" && !o)
+ f = lt(Pt(i), void 0, c), u.push(f), _M(n, u), u.push(...t);
else {
- const p = at(I, fr, "token");
- f = Gv(i, t), d.push(f, p, ...t.flatMap(Yv(p)));
+ const p = lt(R, fr, "token");
+ f = pM(i, t), u.push(f, p, ...t.flatMap(gM(p)));
}
- return u && d.push(u), Ae({
- type: Ee.getType(),
+ return d && u.push(d), Pe({
+ type: Ae.getType(),
marker: r,
caller: i,
isCollapsed: o,
category: n,
unknownAttributes: a,
- children: d,
+ children: u,
direction: null,
format: "",
indent: 0,
- version: Bf
+ version: op
});
}
-function Yv(e) {
- return (t) => Yf(t) ? [t] : [t, e];
+function gM(e) {
+ return (t) => fp(t) ? [t] : [t, e];
}
-function Xv(e) {
+function mM(e) {
let { marker: t } = e;
- (!t || !Jt.isValidMarker(t, Sn?.extraValidMarkers)) && _t?.warn(`Unexpected milestone marker '${t}'!`), t = t ?? "";
- const { sid: r, eid: n } = e, i = Ue(e, vc), s = wp(e);
- return Ae({
- type: Jt.getType(),
+ (!t || !Gt.isValidMarker(t, Sn?.extraValidMarkers)) && vt?.warn(`Unexpected milestone marker '${t}'!`), t = t ?? "";
+ const { sid: r, eid: n } = e, i = Fe(e, Rc), s = Gp(e);
+ return Pe({
+ type: Gt.getType(),
marker: t,
sid: r,
eid: n,
unknownAttributes: i,
attributeOrder: s,
- version: zf
+ version: np
});
}
-function qd(e, t = []) {
+function Vd(e, t = []) {
return {
- type: Qe.getType(),
- typedIDs: { [Kr]: t },
+ type: tt.getType(),
+ typedIDs: { [Br]: t },
children: e,
direction: null,
format: "",
@@ -20915,21 +20919,21 @@ function qd(e, t = []) {
version: 1
};
}
-function Qv(e, t) {
- const { marker: r } = e, n = e.type, i = Ue(e, Hb), s = [];
- if (ee?.markerMode === "editable") {
- const { opening: o, attributes: a, closingAttributes: c, closing: l } = Bp(
+function yM(e, t) {
+ const { marker: r } = e, n = e.type, i = Fe(e, fk), s = [];
+ if (te?.markerMode === "editable") {
+ const { opening: o, attributes: a, closingAttributes: c, closing: l } = oh(
n,
r,
i
);
- o && s.push(kt("marker", o)), a && s.push(kt("attribute", a)), s.push(...t), c && s.push(kt("attribute", c)), l && s.push(kt("marker", l));
+ o && s.push(Tt("marker", o)), a && s.push(Tt("attribute", a)), s.push(...t), c && s.push(Tt("attribute", c)), l && s.push(Tt("marker", l));
} else
s.push(...t);
return s.forEach((o) => {
- ri(o) && (o.mode = "token");
- }), Ae({
- type: Pn.getType(),
+ ai(o) && (o.mode = "token");
+ }), Pe({
+ type: qn.getType(),
tag: n,
marker: r,
unknownAttributes: i,
@@ -20937,22 +20941,22 @@ function Qv(e, t) {
direction: null,
format: "",
indent: 0,
- version: ep
+ version: mp
});
}
-function Zv(e) {
+function bM(e) {
return {
- type: Or.getType(),
+ type: Lr.getType(),
marker: e,
- text: Ui(e),
+ text: Bi(e),
detail: 0,
format: 0,
// Editable marker mode edits the flagged bytes in place (the marker-edit engine pends and
// settles them); every other mode has no engine to settle such an edit, so the node stays
// atomic "token" text there — steppable and deletable whole, but not editable inside.
- mode: ee?.markerMode === "editable" ? "normal" : "token",
+ mode: te?.markerMode === "editable" ? "normal" : "token",
style: "",
- version: Gp
+ version: uh
};
}
function ft(e, t = "opening", r = !1, n = "normal") {
@@ -20970,9 +20974,9 @@ function ft(e, t = "opening", r = !1, n = "normal") {
version: 1
};
}
-function at(e, t = void 0, r = "normal") {
+function lt(e, t = void 0, r = "normal") {
const n = {
- type: Ke.getType(),
+ type: je.getType(),
text: e,
detail: 0,
format: 0,
@@ -20980,285 +20984,285 @@ function at(e, t = void 0, r = "normal") {
style: "",
version: 1
};
- return t !== void 0 && (n[ds] = { textType: t }), n;
+ return t !== void 0 && (n[gs] = { textType: t }), n;
}
-function kt(e, t, r = !1) {
+function Tt(e, t, r = !1) {
const n = {
- type: Er.getType(),
+ type: wr.getType(),
text: t,
textType: e,
- version: Jf
+ version: dp
};
- return r && (n[ds] = { [Pc.key]: !0 }), n;
+ return r && (n[gs] = { [Dc.key]: !0 }), n;
}
-function is(e, t) {
+function ls(e, t) {
return {
- type: Ar.getType(),
+ type: qr.getType(),
runKind: e,
children: t,
direction: null,
format: "",
indent: 0,
- version: tp
+ version: yp
};
}
-function Ja(e, t, r = !1) {
- ee?.markerMode === "editable" ? t.push(ft(e, "opening", r)) : ee?.markerMode === "visible" && t.push(kt("marker", we(e, r)));
+function ec(e, t, r = !1) {
+ te?.markerMode === "editable" ? t.push(ft(e, "opening", r)) : te?.markerMode === "visible" && t.push(Tt("marker", qe(e, r)));
}
-function Ya(e, t, r = !1, n = !1) {
- ee?.markerMode === "editable" ? r ? t.push(ft("", "selfClosing")) : t.push(ft(e, "closing", n)) : ee?.markerMode === "visible" && t.push(
- kt(
+function tc(e, t, r = !1, n = !1) {
+ te?.markerMode === "editable" ? r ? t.push(ft("", "selfClosing")) : t.push(ft(e, "closing", n)) : te?.markerMode === "visible" && t.push(
+ Tt(
"marker",
- r ? rt("") : rt(e, n)
+ r ? st("") : st(e, n)
)
);
}
-function eM(e, t, r) {
- if (ee?.markerMode !== "editable" || !t) return;
- const n = ar(t, po(e));
- n && r.push(at(n, "attribute"));
+function kM(e, t, r) {
+ if (te?.markerMode !== "editable" || !t) return;
+ const n = ar(t, yo(e));
+ n && r.push(lt(n, "attribute"));
}
-function Rd(e, t) {
- if (e.type !== "ms" || ee?.markerMode !== "editable" && ee?.markerMode !== "visible") return;
- const { marker: r, sid: n, eid: i } = e, s = Ue(e, vc), o = qp(
+function Wd(e, t) {
+ if (e.type !== "ms" || te?.markerMode !== "editable" && te?.markerMode !== "visible") return;
+ const { marker: r, sid: n, eid: i } = e, s = Fe(e, Rc), o = Jp(
n,
i,
s,
- wp(e)
- ), a = ar(o, ho(r ?? ""));
+ Gp(e)
+ ), a = ar(o, bo(r ?? ""));
if (!a) return;
- const c = I + a;
- ee?.markerMode === "editable" ? t.push(at(c, "attribute")) : t.push(kt("attribute", c));
+ const c = R + a;
+ te?.markerMode === "editable" ? t.push(lt(c, "attribute")) : t.push(Tt("attribute", c));
}
-function tM(e, t) {
+function TM(e, t) {
const r = e.marker ?? "";
- if (ee?.markerMode === "editable") {
+ if (te?.markerMode === "editable") {
const n = [];
- Ja(r, n), Rd(e, n), Ya(r, n, !0), t.push(is("milestone", n));
+ ec(r, n), Wd(e, n), tc(r, n, !0), t.push(ls("milestone", n));
} else
- Ja(r, t), Rd(e, t), Ya(r, t, !0);
+ ec(r, t), Wd(e, t), tc(r, t, !0);
}
-function $d(e, t, r) {
+function Hd(e, t, r) {
t !== void 0 && r.push(
- is(e, [
+ ls(e, [
ft(e, "opening"),
- at(I + t, "attribute"),
+ lt(R + t, "attribute"),
ft(e, "closing")
])
);
}
-function rM(e, t) {
- ee?.markerMode === "editable" && ($d("va", e.altnumber, t), $d("vp", e.pubnumber, t));
+function xM(e, t) {
+ te?.markerMode === "editable" && (Hd("va", e.altnumber, t), Hd("vp", e.pubnumber, t));
}
-function nM(e, t) {
+function _M(e, t) {
e !== void 0 && t.push(
- is("cat", [
+ ls("cat", [
ft("cat", "opening"),
- at(I + e, "attribute"),
+ lt(R + e, "attribute"),
ft("cat", "closing")
])
);
}
-function iM(e, t, r) {
+function CM(e, t, r) {
e !== void 0 && r.push(
- is("ca", [
+ ls("ca", [
ft("ca", "opening"),
- at(I + e, "attribute"),
+ lt(R + e, "attribute"),
ft("ca", "closing")
])
), t !== void 0 && r.push(
- is("cp", [
+ ls("cp", [
ft("cp", "opening"),
- at(I + t, "attribute")
+ lt(R + t, "attribute")
])
);
}
-function Id(e, t) {
+function Gd(e, t) {
return e.length <= 0 || t === 0 ? e : e.map((r) => r - t);
}
-function sM(e, t) {
+function vM(e, t) {
const r = e.indexOf(t, 0);
r > -1 && e.splice(r, 1);
}
-function Ld(e, t) {
- t.marker === bn && t.sid !== void 0 && e.push(t.sid), t.marker === Yn && t.eid !== void 0 && sM(e, t.eid);
+function Jd(e, t) {
+ t.marker === kn && t.sid !== void 0 && e.push(t.sid), t.marker === Zn && t.eid !== void 0 && vM(e, t.eid);
}
-function Xa(e, t, r = !1, n = []) {
+function rc(e, t, r = !1, n = []) {
if (t.length <= 0 || t[0] >= e.length) return e;
const i = t.shift(), s = t.length > 0 ? t.shift() : e.length - 1;
if (i === void 0 || s === void 0 || s >= e.length || e.length <= 0)
return e;
- const o = e.slice(0, i), a = r ? [qd(o, [...n])] : o, c = e[i];
- Ld(n, c);
- const l = Xa(
+ const o = e.slice(0, i), a = r ? [Vd(o, [...n])] : o, c = e[i];
+ Jd(n, c);
+ const l = rc(
e.slice(i + 1, s),
- Id(t, i + 1),
- c.marker === bn,
+ Gd(t, i + 1),
+ c.marker === kn,
n
- ), u = qd(l, [...n]), d = e[s];
- Ld(n, d);
- const f = Xa(
+ ), d = Vd(l, [...n]), u = e[s];
+ Jd(n, u);
+ const f = rc(
e.slice(s + 1),
- Id(t, s + 1),
- d.marker === bn,
+ Gd(t, s + 1),
+ u.marker === kn,
n
);
- return [...a, u, ...f];
+ return [...a, d, ...f];
}
-function Ur(e, t = !1) {
+function zr(e, t = !1) {
const r = [], n = [];
return e?.forEach((i) => {
if (typeof i == "string")
- i && n.push(at(kl() ? rg(i) : i));
+ i && n.push(lt(Nl() ? yg(i) : i));
else if (!i.type)
- _t?.error("Marker type is missing!");
+ vt?.error("Marker type is missing!");
else
switch (i.type) {
- case Ut.getType():
- n.push(Fv(i));
+ case Ft.getType():
+ n.push(sM(i));
break;
- case Nt.getType():
- n.push(zv(i));
+ case Ot.getType():
+ n.push(oM(i));
break;
case dt.getType():
- ee?.hasSpacing || n.push(qv), n.push(Kv(i)), rM(i, n);
+ te?.hasSpacing || n.push(QS), n.push(aM(i)), xM(i, n);
break;
- case me.getType():
+ case ye.getType():
n.push(
- jv(i, Ur(i.content, !0), t)
+ cM(i, zr(i.content, !0), t)
);
break;
- case Ze.getType():
- n.push(Bv(i, Ur(i.content)));
+ case rt.getType():
+ n.push(lM(i, zr(i.content)));
break;
- case Ee.getType():
- n.push(Jv(i, Ur(i.content)));
+ case Ae.getType():
+ n.push(hM(i, zr(i.content)));
break;
- case Jt.getType():
- Kf(i.marker ?? "") && (r.push(n.length), i.sid !== void 0 && bl?.push(i.sid)), n.push(Xv(i)), tM(i, n);
+ case Gt.getType():
+ ip(i.marker ?? "") && (r.push(n.length), i.sid !== void 0 && Pl?.push(i.sid)), n.push(mM(i)), TM(i, n);
break;
- case Or.getType():
- n.push(Zv(i.marker ?? ""));
+ case Lr.getType():
+ n.push(bM(i.marker ?? ""));
break;
- case Yp:
- n.push(Vv(i, Ur(i.content)));
+ case fh:
+ n.push(uM(i, zr(i.content)));
break;
- case Zp:
- n.push(Wv(i, Ur(i.content)));
+ case gh:
+ n.push(dM(i, zr(i.content)));
break;
- case th:
- n.push(Hv(i, Ur(i.content)));
+ case yh:
+ n.push(fM(i, zr(i.content)));
break;
default:
- _t?.warn(`Unknown type-marker '${i.type}-${i.marker}'!`), n.push(Qv(i, Ur(i.content)));
+ vt?.warn(`Unknown type-marker '${i.type}-${i.marker}'!`), n.push(yM(i, zr(i.content)));
}
- }), Xa(n, r);
+ }), rc(n, r);
}
-function Qa(e) {
+function nc(e) {
const t = e.findIndex(
- (n) => sp(n) || kp(n) || wc(n) || // A table is a block root in its own right; without this it would be swept into an implied
+ (n) => xp(n) || $p(n) || zc(n) || // A table is a block root in its own right; without this it would be swept into an implied
// para alongside any sibling text/verse nodes.
- mT(n)
+ $T(n)
);
if (t >= 0) {
- const n = Qa(e.slice(0, t)), i = e[t], s = Qa(e.slice(t + 1));
+ const n = nc(e.slice(0, t)), i = e[t], s = nc(e.slice(t + 1));
return [...n, i, ...s];
- } else if (e.some((n) => "text" in n && "mode" in n || dh(n)))
- return [lg(e)];
+ } else if (e.some((n) => "text" in n && "mode" in n || Mh(n)))
+ return [vg(e)];
return e;
}
-const Cr = {
- initialize: Rv,
- reset: $v,
- serializeEditorState: Iv
+const Ar = {
+ initialize: ZS,
+ reset: eM,
+ serializeEditorState: tM
};
-function ug(e) {
+function Sg(e) {
if (e && !O(e)) {
- if (v(e)) return e;
- if (F(e))
+ if (M(e)) return e;
+ if (L(e))
for (const t of e.getChildren()) {
- const r = ug(t);
+ const r = Sg(t);
if (r) return r;
}
}
}
-function oM() {
+function SM() {
const e = w();
if (!N(e)) return !1;
if (e.isCollapsed()) {
const t = e.anchor.getNode(), r = e.anchor.offset;
- if ((v(t) && !O(t) ? Cn(t) : void 0) && v(t)) {
- const i = pe(" ");
+ if ((M(t) && !O(t) ? vn(t) : void 0) && M(t)) {
+ const i = ge(" ");
if (r <= 0) t.insertBefore(i);
else if (r >= t.getTextContentSize()) t.insertAfter(i);
else {
const [o] = t.splitText(r);
o.insertAfter(i);
}
- ni(i, { renderGlyphs: !0, closeImplicitSpans: !0 });
- const s = ug(i.getNextSibling());
+ ci(i, { renderGlyphs: !0, closeImplicitSpans: !0 });
+ const s = Sg(i.getNextSibling());
if (s) {
- const o = s.getTextContent(), a = o.startsWith(I) ? I : "", c = o.slice(a.length);
+ const o = s.getTextContent(), a = o.startsWith(R) ? R : "", c = o.slice(a.length);
c.startsWith(" ") && s.setTextContent(a + c.slice(1));
}
return i.select(1, 1), !0;
}
- return v(t) && t.getTextContent()[r] === " " ? (t.select(r + 1, r + 1), !0) : (e.insertText(" "), !0);
+ return M(t) && t.getTextContent()[r] === " " ? (t.select(r + 1, r + 1), !0) : (e.insertText(" "), !0);
}
- for (const t of dg(e)) {
- if (!Cn(t)) continue;
- ni(t, { renderGlyphs: !0, closeImplicitSpans: !0 });
+ for (const t of Mg(e)) {
+ if (!vn(t)) continue;
+ ci(t, { renderGlyphs: !0, closeImplicitSpans: !0 });
const r = t.getLatest(), n = r.getTextContent();
- n.startsWith(I) && r.setTextContent(n.slice(I.length));
+ n.startsWith(R) && r.setTextContent(n.slice(R.length));
}
return !0;
}
-function dg(e) {
- const [t, r] = kc(e), [n, i] = e.isBackward() ? [r, t] : [t, r], s = e.getNodes(), o = [];
+function Mg(e) {
+ const [t, r] = Ac(e), [n, i] = e.isBackward() ? [r, t] : [t, r], s = e.getNodes(), o = [];
return s.forEach((a, c) => {
- if (!v(a) || O(a) || ne(a, oe) === "attribute") return;
- const l = a.getTextContentSize(), u = c === 0 ? n : 0, d = c === s.length - 1 ? Math.min(i, l) : l;
- if (u >= d) return;
- const f = a.splitText(u, d), p = f.length === 3 ? f[1] : d === l ? f[f.length - 1] : f[0];
+ if (!M(a) || O(a) || ne(a, oe) === "attribute") return;
+ const l = a.getTextContentSize(), d = c === 0 ? n : 0, u = c === s.length - 1 ? Math.min(i, l) : l;
+ if (d >= u) return;
+ const f = a.splitText(d, u), p = f.length === 3 ? f[1] : u === l ? f[f.length - 1] : f[0];
p && o.push(p);
}), o;
}
-function aM() {
+function MM() {
const e = w();
if (!N(e)) return !1;
const t = e.focus.getNode();
- return Cn(t) ? Se(Bc(t)) : !1;
+ return vn(t) ? Ce(Zc(t)) : !1;
}
-function fg() {
+function Eg() {
let e = w();
if (!N(e) || !e.isCollapsed()) return !1;
let t = e.anchor.getNode();
- if (O(t) && !Hc(t, e.anchor.offset)) {
+ if (O(t) && !nl(t, e.anchor.offset)) {
const c = t.getParent();
- if (D(c) && t.is(c.getLastChild())) {
+ if (I(c) && t.is(c.getLastChild())) {
if (c.selectNext(0, 0), e = w(), !N(e) || !e.isCollapsed()) return !1;
t = e.anchor.getNode();
}
}
- if (!v(t) || O(t) || !Cn(t)) return !1;
- const r = Bc(t);
- if (!Se(r)) return !1;
- const n = pe(""), i = e.anchor.offset;
+ if (!M(t) || O(t) || !vn(t)) return !1;
+ const r = Zc(t);
+ if (!Ce(r)) return !1;
+ const n = ge(""), i = e.anchor.offset;
if (i <= 0) t.insertBefore(n);
else if (i >= t.getTextContentSize()) t.insertAfter(n);
else {
const [, c] = t.splitText(i);
c.insertBefore(n);
}
- ni(n, { renderGlyphs: !0 });
+ ci(n, { renderGlyphs: !0 });
const s = n.getNextSiblings();
n.remove();
const o = r.insertNewAfter(e, !1);
o.append(...s);
const [a] = s;
- return D(a) ? Vc(a) : o.select(0, 0), !0;
+ return I(a) ? el(a) : o.select(0, 0), !0;
}
-const pg = {
+const Ag = {
c: {
// Deliberately still trusts reference.chapterNum, unlike `v` below - the chapter-number
// reinstatement work (a separate branch/PR) owns rewriting this action to scan the tree.
@@ -21267,23 +21271,23 @@ const pg = {
return { content: [{
type: "chapter",
marker: "c",
- number: `${Tp(Fe().getChildren(), t) !== void 0 ? t + 1 : t}`
+ number: `${Lp(ze().getChildren(), t) !== void 0 ? t + 1 : t}`
}] };
}
},
v: {
action: () => {
- const e = w(), t = $c(e), r = Xc(t);
+ const e = w(), t = Bc(e), r = al(t);
let n, i = !1;
if (!r)
n = "1";
else {
const o = r.getNumber();
- n = Ek(0, o);
- const a = sx(r);
+ n = Wk(0, o);
+ const a = vx(r);
if (a) {
const c = a.getNumber();
- i = n === c || Ep(c) && Ic(parseInt(n, 10), c);
+ i = n === c || jp(c) && Vc(parseInt(n, 10), c);
}
}
return { content: [{
@@ -21294,32 +21298,32 @@ const pg = {
}
}
};
-function Za(e, t) {
- return Ee.isValidMarker(e, t) || !!pg[e] || Ze.isValidMarker(e, t) || me.isValidMarker(e, t);
+function ic(e, t) {
+ return Ae.isValidMarker(e, t) || !!Ag[e] || rt.isValidMarker(e, t) || ye.isValidMarker(e, t);
}
-function cM(e, t) {
- return me.isNoteContentMarker(e) ? !1 : me.isValidMarker(e, t);
+function EM(e, t) {
+ return ye.isNoteContentMarker(e) ? !1 : ye.isValidMarker(e, t);
}
-function hg(e, t, r, n, i, s) {
- const o = Th(
+function Pg(e, t, r, n, i, s) {
+ const o = $h(
e,
void 0,
void 0,
t,
- n ?? Eo(),
+ n ?? Oo(),
i ?? {},
s
);
return o && !o.getIsCollapsed() && (r.current = o.getKey()), o?.getKey();
}
-function ec(e, t, r, n, i, s, o) {
- if (Ee.isValidMarker(e, n?.extraValidMarkers)) {
+function sc(e, t, r, n, i, s, o) {
+ if (Ae.isValidMarker(e, n?.extraValidMarkers)) {
let l;
- return { action: (d) => {
- d.editor.update(() => {
- l = hg(
+ return { action: (u) => {
+ u.editor.update(() => {
+ l = Pg(
e,
- d.reference,
+ u.reference,
t,
r,
n,
@@ -21328,126 +21332,126 @@ function ec(e, t, r, n, i, s, o) {
}, s);
}, label: void 0, getInsertedNoteKey: () => l };
}
- const a = hM(e, n?.extraValidMarkers);
+ const a = qM(e, n?.extraValidMarkers);
return a ? { action: (l) => {
l.editor.update(() => {
- const u = w();
- N(u) && (ah(u), l.noteText = u.getTextContent());
- const { content: d, highlightInserted: f } = a.action(l), p = Uu(d, Cr, r), m = Go(p);
- if (N(u)) {
- const g = u.anchor.getNode(), y = g.getParent(), k = Cn(g), _ = u.anchor.key === u.focus.key;
- if (D(m) && k && _ && !ga(m, o))
- dM(
- u,
- m,
- g,
+ const d = w();
+ N(d) && (_h(d), l.noteText = d.getTextContent());
+ const { content: u, highlightInserted: f } = a.action(l), p = Xu(u, Ar, r), y = Xo(p);
+ if (N(d)) {
+ const h = d.anchor.getNode(), m = h.getParent(), b = vn(h), x = d.anchor.key === d.focus.key;
+ if (I(y) && b && x && !ba(y, o))
+ NM(
+ d,
+ y,
+ h,
r?.markerMode === "editable"
);
- else if (D(m) && !_ && !ga(m, o) && fM(u))
- pM(u, m, r?.markerMode === "editable");
- else if (u.getTextContent().length > 0)
- gM(
- u,
- () => Go(p)
+ else if (I(y) && !x && !ba(y, o) && OM(d))
+ wM(d, y, r?.markerMode === "editable");
+ else if (d.getTextContent().length > 0)
+ RM(
+ d,
+ () => Xo(p)
);
- else if (F(m) && !m.isInline()) {
- const S = u.insertParagraph();
- if (S) {
- const P = S.getChildren();
- m.append(...P), S.replace(m), Se(m) && yi(m) || m.selectStart();
+ else if (L(y) && !y.isInline()) {
+ const _ = d.insertParagraph();
+ if (_) {
+ const A = _.getChildren();
+ y.append(...A), _.replace(y), Ce(y) && xi(y) || y.selectStart();
}
- } else if (D(m) && v(g) && !O(g) && D(g.getParent()) && u.isCollapsed() && // NEST-able only. A non-NEST style at a caret inside ANY char span — nested or note-level
+ } else if (I(y) && M(h) && !O(h) && I(h.getParent()) && d.isCollapsed() && // NEST-able only. A non-NEST style at a caret inside ANY char span — nested or note-level
// — is already claimed by the `$applyNonNestInsideChar` branch above, whose guard is this
// one minus this test. Stating it here rather than branching on it inside keeps that
// division visible at the guard instead of implying a second non-NEST path exists.
- ga(m, o)) {
- const S = g.getParent();
- if (D(S)) {
- const P = u.anchor.offset;
- if (P === 0) g.insertBefore(m);
- else if (P >= g.getTextContentSize()) g.insertAfter(m);
+ ba(y, o)) {
+ const _ = h.getParent();
+ if (I(_)) {
+ const A = d.anchor.offset;
+ if (A === 0) h.insertBefore(y);
+ else if (A >= h.getTextContentSize()) h.insertAfter(y);
else {
- const [B] = g.splitText(P);
- B.insertAfter(m);
+ const [D] = h.splitText(A);
+ D.insertAfter(y);
}
- m.getChildren().forEach((B) => {
- O(B) && B.setNested(!0);
+ y.getChildren().forEach((D) => {
+ O(D) && D.setNested(!0);
});
- const A = m.getChildren().find((B) => v(B) && !O(B));
- A && v(A) ? A.select(
- A.getTextContentSize(),
- A.getTextContentSize()
- ) : m.selectEnd();
+ const E = y.getChildren().find((D) => M(D) && !O(D));
+ E && M(E) ? E.select(
+ E.getTextContentSize(),
+ E.getTextContentSize()
+ ) : y.selectEnd();
}
- } else if (v(g) && !O(g) && u.isCollapsed() && (j(y) || D(y) && j(y.getParent()))) {
- const S = D(y) ? y : void 0, P = S ? lM(g, u.anchor.offset) : [];
- let B = (S ?? g).insertAfter(m);
- if (Pr(m)) {
- const M = {
- ...r || Eo(),
+ } else if (M(h) && !O(h) && d.isCollapsed() && (z(m) || I(m) && z(m.getParent()))) {
+ const _ = I(m) ? m : void 0, A = _ ? AM(h, d.anchor.offset) : [];
+ let D = (_ ?? h).insertAfter(y);
+ if (Rr(y)) {
+ const V = {
+ ...r || Oo(),
markerMode: "hidden"
- }, q = Uu(
- d,
- Cr,
- M
- ), $ = Go(q);
- B = B.insertAfter($);
+ }, j = Xu(
+ u,
+ Ar,
+ V
+ ), v = Xo(j);
+ D = D.insertAfter(v);
}
- if (P.length > 0 && S) {
- const M = to(S).append(...P);
- B.insertAfter(M), S.isEmpty() && S.remove();
- } else v(B.getNextSibling()) || B.insertAfter(pe(I));
- F(B) && B.selectEnd();
- } else if (u.insertNodes([m]), vM(m), f) {
- const S = xf();
- S.add(m.getKey()), ei(S);
- } else if (D(m)) {
- const S = m.getChildren().find((P) => v(P) && !O(P));
- S && v(S) ? S.select(
- S.getTextContentSize(),
- S.getTextContentSize()
- ) : m.selectEnd();
+ if (A.length > 0 && _) {
+ const V = so(_).append(...A);
+ D.insertAfter(V), _.isEmpty() && _.remove();
+ } else M(D.getNextSibling()) || D.insertAfter(ge(R));
+ L(D) && D.selectEnd();
+ } else if (d.insertNodes([y]), BM(y), f) {
+ const _ = Lf();
+ _.add(y.getKey()), ii(_);
+ } else if (I(y)) {
+ const _ = y.getChildren().find((A) => M(A) && !O(A));
+ _ && M(_) ? _.select(
+ _.getTextContentSize(),
+ _.getTextContentSize()
+ ) : y.selectEnd();
} else {
- const S = m.getNextSibling();
- S ? S.selectStart() : m.selectStart();
+ const _ = y.getNextSibling();
+ _ ? _.selectStart() : y.selectStart();
}
} else
- u?.insertNodes([m]);
+ d?.insertNodes([y]);
}, s);
}, label: a?.label } : { action: () => {
}, label: void 0 };
}
-function lM(e, t) {
+function AM(e, t) {
const r = e.getTextContentSize();
let n;
return t <= 0 ? n = e : t >= r ? n = e.getNextSibling() : n = e.splitText(t)[1] ?? e.getNextSibling(), n ? [n, ...n.getNextSiblings()] : [];
}
-function ga(e, t) {
- return ((t ?? Vs).markers[e.getMarker()]?.occursUnder ?? []).includes("NEST") && e.getUnknownAttributes()?.closed !== "false";
+function ba(e, t) {
+ return ((t ?? Js).markers[e.getMarker()]?.occursUnder ?? []).includes("NEST") && e.getUnknownAttributes()?.closed !== "false";
}
-function uM(e, t) {
+function PM(e, t) {
t && e.getChildren().forEach((i) => {
O(i) && i.setNested(!0);
- }), e.getChildren().some((i) => O(i) && i.getMarkerSyntax() === "closing") || e.append(ot(e.getMarker(), "closing", t));
+ }), e.getChildren().some((i) => O(i) && i.getMarkerSyntax() === "closing") || e.append(ct(e.getMarker(), "closing", t));
const n = e.getUnknownAttributes();
if (n?.closed === "false") {
const i = { ...n };
delete i.closed, e.setUnknownAttributes(Object.keys(i).length > 0 ? i : void 0);
}
}
-function dM(e, t, r, n) {
+function NM(e, t, r, n) {
let i = t;
- if (e.anchor.type === "element" && D(r)) {
+ if (e.anchor.type === "element" && I(r)) {
const o = r.getChildren(), a = o[e.anchor.offset - 1], c = o[e.anchor.offset];
a ? a.insertAfter(t) : c ? c.insertBefore(t) : r.append(t);
- } else if (e.isCollapsed() || !v(r)) {
+ } else if (e.isCollapsed() || !M(r)) {
const o = e.anchor.offset;
- if (v(r) && o > 0 && o < r.getTextContentSize()) {
+ if (M(r) && o > 0 && o < r.getTextContentSize()) {
const [a] = r.splitText(o);
a.insertAfter(t);
- } else v(r) && o >= r.getTextContentSize() ? r.insertAfter(t) : r.insertBefore(t);
+ } else M(r) && o >= r.getTextContentSize() ? r.insertAfter(t) : r.insertBefore(t);
} else {
- const [o, a] = ai(e);
+ const [o, a] = fi(e);
let c = r;
if (o > 0) {
const l = c.splitText(o);
@@ -21455,39 +21459,39 @@ function dM(e, t, r, n) {
}
c.getTextContentSize() > a - o && (c = c.splitText(a - o)[0]), i = c;
}
- if (ni(i, { renderGlyphs: n }), i !== t) {
- i.insertBefore(t), v(i) && !i.getTextContent().startsWith(I) && i.setTextContent(I + i.getTextContent());
- const o = t.getChildren().find((a) => v(a) && !O(a));
+ if (ci(i, { renderGlyphs: n }), i !== t) {
+ i.insertBefore(t), M(i) && !i.getTextContent().startsWith(R) && i.setTextContent(R + i.getTextContent());
+ const o = t.getChildren().find((a) => M(a) && !O(a));
o ? o.replace(i) : t.append(i);
}
- const s = t.getChildren().find((o) => v(o) && !O(o));
- v(s) ? s.select(s.getTextContentSize(), s.getTextContentSize()) : t.selectEnd();
+ const s = t.getChildren().find((o) => M(o) && !O(o));
+ M(s) ? s.select(s.getTextContentSize(), s.getTextContentSize()) : t.selectEnd();
}
-function fM(e) {
+function OM(e) {
let t, r = !1;
for (const n of e.getNodes()) {
- if (O(n) || D(n)) continue;
- if (!v(n) || n.getType() !== Ke.getType() || ne(n, oe) === "attribute") return !1;
- const i = Bc(n);
+ if (O(n) || I(n)) continue;
+ if (!M(n) || n.getType() !== je.getType() || ne(n, oe) === "attribute") return !1;
+ const i = Zc(n);
if (!i) return !1;
if (t === void 0) t = i;
else if (!t.is(i)) return !1;
- Cn(n) && (r = !0);
+ vn(n) && (r = !0);
}
return r;
}
-function pM(e, t, r) {
- const n = dg(e);
+function wM(e, t, r) {
+ const n = Mg(e);
if (n.length === 0) return;
n.forEach((a) => {
- if (!Cn(a)) return;
- ni(a, { renderGlyphs: r });
+ if (!vn(a)) return;
+ ci(a, { renderGlyphs: r });
const c = a.getLatest(), l = c.getTextContent();
- l.startsWith(I) && c.setTextContent(l.slice(I.length));
+ l.startsWith(R) && c.setTextContent(l.slice(R.length));
});
const i = n[0].getLatest();
- i.insertBefore(t), i.getTextContent().startsWith(I) || i.setTextContent(I + i.getTextContent());
- const s = t.getChildren().find((a) => v(a) && !O(a));
+ i.insertBefore(t), i.getTextContent().startsWith(R) || i.setTextContent(R + i.getTextContent());
+ const s = t.getChildren().find((a) => M(a) && !O(a));
s ? s.replace(i) : t.append(i);
let o = i.getLatest();
n.slice(1).forEach((a) => {
@@ -21495,24 +21499,24 @@ function pM(e, t, r) {
o.insertAfter(c), o = c;
}), o.select(o.getTextContentSize(), o.getTextContentSize());
}
-function hM(e, t) {
- let r = pg[e];
- return r || (Ze.isValidMarker(e, t) ? r = {
- action: () => ({ content: [{ type: Ze.getType(), marker: e, content: [] }] })
- } : me.isValidMarker(e, t) && (r = {
+function qM(e, t) {
+ let r = Ag[e];
+ return r || (rt.isValidMarker(e, t) ? r = {
+ action: () => ({ content: [{ type: rt.getType(), marker: e, content: [] }] })
+ } : ye.isValidMarker(e, t) && (r = {
action: () => {
- const n = { type: me.getType(), marker: e };
- return (me.isValidFootnoteMarker(e) || me.isValidCrossReferenceMarker(e)) && (n.closed = "false"), { content: [n] };
+ const n = { type: ye.getType(), marker: e };
+ return (ye.isValidFootnoteMarker(e) || ye.isValidCrossReferenceMarker(e)) && (n.closed = "false"), { content: [n] };
}
})), r;
}
-function gM(e, t) {
- const r = e.getNodes(), [n, i] = ai(e);
+function RM(e, t) {
+ const r = e.getNodes(), [n, i] = fi(e);
let s;
r.forEach((o, a) => {
- if (F(s) && s.isParentOf(o))
+ if (L(s) && s.isParentOf(o))
return;
- const c = gg(
+ const c = Ng(
o,
a === 0,
a === r.length - 1,
@@ -21524,35 +21528,35 @@ function gM(e, t) {
return;
}
let l = !1;
- s || (s = t(), c.insertBefore(s), l = !0, D(s) && s.getChildren().some((d) => O(d) && d.getMarkerSyntax() === "opening") && uM(s, D(s.getParent()))), yM(c, s, l);
- }), (v(s) || F(s)) && s.selectEnd();
+ s || (s = t(), c.insertBefore(s), l = !0, I(s) && s.getChildren().some((u) => O(u) && u.getMarkerSyntax() === "opening") && PM(s, I(s.getParent()))), LM(c, s, l);
+ }), (M(s) || L(s)) && s.selectEnd();
}
-function ai(e) {
+function fi(e) {
const t = e.anchor.offset, r = e.focus.offset;
return e.isBackward() ? [r, t] : [t, r];
}
-function xl(e) {
- return Ce(e) || j(e) || j(e.getParent());
+function wl(e) {
+ return _e(e) || z(e) || z(e.getParent());
}
-function gg(e, t, r, n, i) {
- if (!xl(e)) {
- if (v(e))
- return mM(e, t, r, n, i);
- if (F(e) && e.isInline())
+function Ng(e, t, r, n, i) {
+ if (!wl(e)) {
+ if (M(e))
+ return $M(e, t, r, n, i);
+ if (L(e) && e.isInline())
return e;
}
}
-function mM(e, t, r, n, i) {
+function $M(e, t, r, n, i) {
const s = e.getTextContentSize(), o = t ? n : 0, a = r ? i : s;
if (o === 0 && a === 0) return;
const c = e.splitText(o, a);
return c.length === 1 ? c[0] : c.length === 3 || a === s ? c[1] : c[0];
}
-function yM(e, t, r) {
- if (v(t)) {
- const n = tc(e, t);
+function LM(e, t, r) {
+ if (M(t)) {
+ const n = oc(e, t);
t.setTextContent(n), e.remove();
- } else if (F(t)) {
+ } else if (L(t)) {
const n = t.getChildren(), i = n.find(
(s) => O(s) && s.getMarkerSyntax() !== "opening"
);
@@ -21564,190 +21568,190 @@ function yM(e, t, r) {
for (let o = 0; o < s; o++) t.getFirstChild()?.remove();
} else
t.append(e);
- tc(e, t), r && D(t) && t.getChildren().some((s) => O(s)) && v(e) && !O(e) && !e.getTextContent().startsWith(I) && e.setTextContent(I + e.getTextContent());
+ oc(e, t), r && I(t) && t.getChildren().some((s) => O(s)) && M(e) && !O(e) && !e.getTextContent().startsWith(R) && e.setTextContent(R + e.getTextContent());
}
}
-function tc(e, t) {
+function oc(e, t) {
let r = e.getTextContent();
- if (v(e) && t.isInline() && r.startsWith(" ") && r.trimStart() !== "") {
+ if (M(e) && t.isInline() && r.startsWith(" ") && r.trimStart() !== "") {
r = r.trimStart(), e.setTextContent(r);
const n = t.getPreviousSibling();
- Jc(n), v(n) || t.insertBefore(pe(" "));
+ sl(n), M(n) || t.insertBefore(ge(" "));
}
return r;
}
-function mg(e, t, r) {
+function Og(e, t, r) {
if (e.isCollapsed()) {
- const u = e.anchor.getNode(), d = e.anchor.offset, f = vn(u, t);
+ const d = e.anchor.getNode(), u = e.anchor.offset, f = Mn(d, t);
if (!f) return !1;
- const p = v(u) ? u.getTextContentSize() : 0;
- if (Dd(f, r), v(u) && u.isAttached()) {
- const m = u.getTextContentSize(), g = Math.max(p - m, 0), y = Math.max(0, Math.min(d - g, m)), k = w();
- N(k) && k.setTextNodeRange(u, y, u, y);
+ const p = M(d) ? d.getTextContentSize() : 0;
+ if (Yd(f, r), M(d) && d.isAttached()) {
+ const y = d.getTextContentSize(), h = Math.max(p - y, 0), m = Math.max(0, Math.min(u - h, y)), b = w();
+ N(b) && b.setTextNodeRange(d, m, d, m);
}
return !0;
}
- const n = e.getNodes(), i = e.isBackward(), [s, o] = ai(e);
- if (!Cl(n, t, s, o)) return !1;
- const a = _l(n, s, o);
+ const n = e.getNodes(), i = e.isBackward(), [s, o] = fi(e);
+ if (!Rl(n, t, s, o)) return !1;
+ const a = ql(n, s, o);
if (a.length === 0) return !1;
const c = /* @__PURE__ */ new Set();
let l = !1;
- return a.forEach((u) => {
- const d = vn(u, t);
- if (!d || c.has(d.getKey())) return;
- c.add(d.getKey());
- const f = Tg(d, a);
- f && (Dd(f, r), l = !0);
- }), xg(a, i), l;
-}
-function Dd(e, t) {
+ return a.forEach((d) => {
+ const u = Mn(d, t);
+ if (!u || c.has(u.getKey())) return;
+ c.add(u.getKey());
+ const f = $g(u, a);
+ f && (Yd(f, r), l = !0);
+ }), Lg(a, i), l;
+}
+function Yd(e, t) {
e.getChildren().forEach((n) => {
- Ft(n) && n.remove();
+ zt(n) && n.remove();
});
const r = e.getChildren();
- if (r.length === 0 || e.getTextContent() === Dt) {
+ if (r.length === 0 || e.getTextContent() === Ut) {
e.remove();
return;
}
t?.markerMode === "editable" && r.forEach((n) => {
const i = n.getTextContent();
- v(n) && i.startsWith(I) && n.setTextContent(i.slice(I.length));
- }), _a(e);
+ M(n) && i.startsWith(R) && n.setTextContent(i.slice(R.length));
+ }), Ea(e);
}
-function _l(e, t, r) {
+function ql(e, t, r) {
const n = [];
return e.forEach((i, s) => {
- const o = gg(
+ const o = Ng(
i,
s === 0,
s === e.length - 1,
t,
r
);
- v(o) && n.push(o);
+ M(o) && n.push(o);
}), n;
}
-function vn(e, t) {
+function Mn(e, t) {
let r = e, n;
- for (; r && !Se(r); ) {
- if (j(r)) return;
- !n && D(r) && (t === void 0 || r.getMarker() === t) && (n = r), r = r.getParent();
+ for (; r && !Ce(r); ) {
+ if (z(r)) return;
+ !n && I(r) && (t === void 0 || r.getMarker() === t) && (n = r), r = r.getParent();
}
return n;
}
-function yg(e) {
- const t = nt(
+function wg(e) {
+ const t = Xe(
e,
- (r) => j(r) || Se(r)
+ (r) => z(r) || Ce(r)
);
- return j(t);
+ return z(t);
}
-function bg(e) {
+function qg(e) {
return e.filter(
- (t) => !xl(t) && (v(t) || F(t) && t.isInline())
+ (t) => !wl(t) && (M(t) || L(t) && t.isInline())
);
}
-function bM(e, t, r) {
+function IM(e, t, r) {
const n = /* @__PURE__ */ new Set();
return e.forEach((i, s) => {
- if (!v(i) || xl(i)) return;
+ if (!M(i) || wl(i)) return;
const o = i.getTextContentSize(), a = s === 0 ? t : 0, c = s === e.length - 1 ? r : o;
a === 0 && c === o && n.add(i.getKey());
}), n;
}
-function kM(e, t, r) {
+function DM(e, t, r) {
return e.getChildren().some(
- (n) => F(n) && t.some((i) => n.isParentOf(i)) && !kg(n, r)
+ (n) => L(n) && t.some((i) => n.isParentOf(i)) && !Rg(n, r)
);
}
-function Cl(e, t, r, n, i) {
- const s = bg(e), o = bM(e, r, n), a = /* @__PURE__ */ new Set();
+function Rl(e, t, r, n, i) {
+ const s = qg(e), o = IM(e, r, n), a = /* @__PURE__ */ new Set();
return s.some((c) => {
- const l = vn(c, t);
- return !l || a.has(l.getKey()) || (a.add(l.getKey()), l.getMarker() === i) ? !1 : !kM(l, s, o);
+ const l = Mn(c, t);
+ return !l || a.has(l.getKey()) || (a.add(l.getKey()), l.getMarker() === i) ? !1 : !DM(l, s, o);
});
}
-function kg(e, t) {
- return e.getAllTextNodes().every((r) => t.has(r.getKey()) || Ft(r));
+function Rg(e, t) {
+ return e.getAllTextNodes().every((r) => t.has(r.getKey()) || zt(r));
}
-function Tg(e, t) {
+function $g(e, t) {
const r = new Set(t.map((l) => l.getKey())), n = e.getChildren(), i = [];
- for (const [l, u] of n.entries())
- if (r.has(u.getKey()))
+ for (const [l, d] of n.entries())
+ if (r.has(d.getKey()))
i.push(l);
- else if (F(u) && t.some((d) => u.isParentOf(d))) {
- if (!kg(u, r)) return;
+ else if (L(d) && t.some((u) => d.isParentOf(u))) {
+ if (!Rg(d, r)) return;
i.push(l);
}
if (i.length === 0) return;
let s = i[0], o = i[i.length - 1];
- if (s > 0 && Ft(n[s - 1]) && (s -= 1), o < n.length - 1 && Ft(n[o + 1]) && (o += 1), s === 0 && o === n.length - 1) return e;
+ if (s > 0 && zt(n[s - 1]) && (s -= 1), o < n.length - 1 && zt(n[o + 1]) && (o += 1), s === 0 && o === n.length - 1) return e;
const a = n.slice(o + 1);
- a.length > 0 && e.insertAfter(to(e).append(...a));
+ a.length > 0 && e.insertAfter(so(e).append(...a));
const c = n.slice(0, s);
- return c.length > 0 && e.insertBefore(to(e).append(...c)), e;
+ return c.length > 0 && e.insertBefore(so(e).append(...c)), e;
}
-function to(e) {
- return wy(e);
+function so(e) {
+ return Hy(e);
}
-function xg(e, t) {
+function Lg(e, t) {
const r = w(), n = e[0], i = e[e.length - 1];
if (!N(r) || !n.isAttached() || !i.isAttached())
return;
const s = i.getTextContentSize();
t ? r.setTextNodeRange(i, s, n, 0) : r.setTextNodeRange(n, 0, i, s);
}
-function TM(e, t, r) {
+function UM(e, t, r) {
if (e.isCollapsed()) {
- const l = vn(e.anchor.getNode(), r);
- return !l || l.getMarker() === t ? !1 : (Su(l, t), !0);
+ const l = Mn(e.anchor.getNode(), r);
+ return !l || l.getMarker() === t ? !1 : (Lu(l, t), !0);
}
- const n = e.getNodes(), [i, s] = ai(e);
- if (!Cl(n, r, i, s, t)) return !1;
- const o = _l(n, i, s);
+ const n = e.getNodes(), [i, s] = fi(e);
+ if (!Rl(n, r, i, s, t)) return !1;
+ const o = ql(n, i, s);
if (o.length === 0) return !1;
const a = /* @__PURE__ */ new Set();
let c = !1;
return o.forEach((l) => {
- const u = vn(l, r);
- if (!u || a.has(u.getKey()) || (a.add(u.getKey()), u.getMarker() === t)) return;
- const d = Tg(u, o);
- d && (Su(d, t), c = !0);
+ const d = Mn(l, r);
+ if (!d || a.has(d.getKey()) || (a.add(d.getKey()), d.getMarker() === t)) return;
+ const u = $g(d, o);
+ u && (Lu(u, t), c = !0);
}), c;
}
-function xM(e, t, r, n) {
+function FM(e, t, r, n) {
if (e.isCollapsed()) return !1;
const i = r?.filter(
- (y) => y !== t
- ), s = e.getNodes(), [o, a] = ai(e);
+ (m) => m !== t
+ ), s = e.getNodes(), [o, a] = fi(e);
if (!!!i?.some(
- (y) => Cl(s, y, o, a)
- ) && !_M(s, t)) return !1;
+ (m) => Rl(s, m, o, a)
+ ) && !zM(s, t)) return !1;
let l = !1;
- i?.forEach((y) => {
- const k = w();
- N(k) && mg(k, y, n) && (l = !0);
+ i?.forEach((m) => {
+ const b = w();
+ N(b) && Og(b, m, n) && (l = !0);
});
- const u = w();
- if (!N(u)) return l;
- const d = u.isBackward(), [f, p] = ai(u), m = _l(
- u.getNodes(),
+ const d = w();
+ if (!N(d)) return l;
+ const u = d.isBackward(), [f, p] = fi(d), y = ql(
+ d.getNodes(),
f,
p
);
- if (m.length === 0) return l;
- const g = m.filter(
- (y) => !yg(y) && !vn(y, t)
+ if (y.length === 0) return l;
+ const h = y.filter(
+ (m) => !wg(m) && !Mn(m, t)
);
- return g.length > 0 && (CM(g).forEach((y) => SM(y, t)), l = !0), xg(m, d), l;
+ return h.length > 0 && (KM(h).forEach((m) => jM(m, t)), l = !0), Lg(y, u), l;
}
-function _M(e, t) {
- return bg(e).some(
- (r) => !yg(r) && !vn(r, t)
+function zM(e, t) {
+ return qg(e).some(
+ (r) => !wg(r) && !Mn(r, t)
);
}
-function CM(e) {
+function KM(e) {
const t = [];
let r;
return e.forEach((n) => {
@@ -21755,16 +21759,16 @@ function CM(e) {
r && i?.getNextSibling()?.is(n) ? r.push(n) : (r = [n], t.push(r));
}), t;
}
-function SM(e, t) {
+function jM(e, t) {
const r = e[0].getPreviousSibling(), n = e[e.length - 1].getNextSibling(), i = [r, n].find(
- (a) => D(a) && a.getMarker() === t
- ), s = i ? to(i) : xr(t);
- e[0].insertBefore(s), s.append(...e), i === r || tc(e[0], s);
+ (a) => I(a) && a.getMarker() === t
+ ), s = i ? so(i) : Mr(t);
+ e[0].insertBefore(s), s.append(...e), i === r || oc(e[0], s);
}
-function vM(e) {
- ge(e) && (Jc(e.getPreviousSibling()), ph(e.getNextSibling()));
+function BM(e) {
+ he(e) && (sl(e.getPreviousSibling()), Ah(e.getNextSibling()));
}
-const _g = {
+const Ig = {
chapter: "chapter",
verse: "verse",
char: "char",
@@ -21779,13 +21783,13 @@ const _g = {
strikethrough: "editor-text-strikethrough",
underlineStrikethrough: "editor-text-underlineStrikethrough"
}
-}, Ud = "psc-active-text", Ms = "psc-empty-text";
-function MM({ viewOptions: e }) {
- const [t] = ce(), r = Z(void 0), n = e?.hasActiveTextFocusBox ?? !1;
- return z(() => {
+}, Xd = "psc-active-text", Os = "psc-empty-text";
+function VM({ viewOptions: e }) {
+ const [t] = ae(), r = Z(void 0), n = e?.hasActiveTextFocusBox ?? !1;
+ return F(() => {
if (!n) return;
function i(o) {
- r.current && t.getElementByKey(r.current)?.classList.remove(Ud), r.current = o, o && t.getElementByKey(o)?.classList.add(Ud);
+ r.current && t.getElementByKey(r.current)?.classList.remove(Xd), r.current = o, o && t.getElementByKey(o)?.classList.add(Xd);
}
const s = [
// Clicking the ellipsis placeholder (rendered as ::after on the empty verse span) hits the
@@ -21795,59 +21799,59 @@ function MM({ viewOptions: e }) {
// handlers already run inside an editor update, so we set selection directly here rather than
// calling editor.update() from a DOM listener.
t.registerCommand(
- lo,
+ mo,
(o) => {
const a = o.target;
if (!(a instanceof Element)) return !1;
- const c = a.closest(`.${Ms}`);
+ const c = a.closest(`.${Os}`);
if (!c) return !1;
- const l = li(c);
- if (!ge(l)) return !1;
- const u = l.getParent();
- if (!F(u)) return !1;
- const d = l.getIndexWithinParent() + 1;
- return u.select(d, d), !1;
+ const l = hi(c);
+ if (!he(l)) return !1;
+ const d = l.getParent();
+ if (!L(d)) return !1;
+ const u = l.getIndexWithinParent() + 1;
+ return d.select(u, u), !1;
},
- Tt
+ _t
),
t.registerUpdateListener(({ editorState: o }) => {
- const { newActiveKey: a, activeVerseKey: c, emptyKeys: l, nonEmptyKeys: u } = o.read(() => {
- const d = ma(), f = EM(), p = [], m = [];
- return Fe().getChildren().forEach((g) => {
- if (!F(g)) return;
- const { emptyKeys: y, nonEmptyKeys: k } = PM(g);
- p.push(...y), m.push(...k);
- }), { newActiveKey: d, activeVerseKey: f, emptyKeys: p, nonEmptyKeys: m };
+ const { newActiveKey: a, activeVerseKey: c, emptyKeys: l, nonEmptyKeys: d } = o.read(() => {
+ const u = ka(), f = WM(), p = [], y = [];
+ return ze().getChildren().forEach((h) => {
+ if (!L(h)) return;
+ const { emptyKeys: m, nonEmptyKeys: b } = GM(h);
+ p.push(...m), y.push(...b);
+ }), { newActiveKey: u, activeVerseKey: f, emptyKeys: p, nonEmptyKeys: y };
});
- a !== r.current && i(a), l.forEach((d) => {
- d === c ? t.getElementByKey(d)?.classList.remove(Ms) : t.getElementByKey(d)?.classList.add(Ms);
- }), u.forEach((d) => t.getElementByKey(d)?.classList.remove(Ms));
+ a !== r.current && i(a), l.forEach((u) => {
+ u === c ? t.getElementByKey(u)?.classList.remove(Os) : t.getElementByKey(u)?.classList.add(Os);
+ }), d.forEach((u) => t.getElementByKey(u)?.classList.remove(Os));
}),
t.registerCommand(
- _c,
+ Oc,
() => (i(void 0), !1),
- Tt
+ _t
),
t.registerCommand(
- qy,
+ Gy,
() => {
- const o = t.getEditorState().read(ma);
+ const o = t.getEditorState().read(ka);
return o !== r.current && i(o), !1;
},
- Tt
+ _t
)
];
- return i(t.getEditorState().read(ma)), Ve(...s);
+ return i(t.getEditorState().read(ka)), Be(...s);
}, [t, n]), null;
}
-function ma() {
- return AM(w() ?? void 0)?.getKey();
+function ka() {
+ return HM(w() ?? void 0)?.getKey();
}
-function EM() {
+function WM() {
const e = w();
if (!N(e)) return;
const t = e.anchor, r = t.getNode(), n = r.getTopLevelElement();
- if (!F(n)) return;
+ if (!L(n)) return;
let i;
if (r.is(n))
i = t.offset;
@@ -21861,23 +21865,23 @@ function EM() {
const s = n.getChildren();
let o;
for (let a = 0; a < i && a < s.length; a++)
- ge(s[a]) && (o = s[a].getKey());
+ he(s[a]) && (o = s[a].getKey());
return o;
}
-function AM(e) {
+function HM(e) {
if (N(e))
return e.anchor.getNode().getTopLevelElement() ?? void 0;
}
-function PM(e) {
+function GM(e) {
const t = e.getChildren(), r = [], n = [];
for (let i = 0; i < t.length; i++) {
const s = t[i];
- if (!ge(s)) continue;
+ if (!he(s)) continue;
let o = !1;
for (let a = i + 1; a < t.length; a++) {
const c = t[a];
- if (ge(c)) break;
- if (!(Yt(c) || O(c)) && c.getTextContent().replaceAll(Is, "").trim() !== "") {
+ if (he(c)) break;
+ if (!(Jt(c) || O(c)) && c.getTextContent().replaceAll(Fs, "").trim() !== "") {
o = !0;
break;
}
@@ -21886,131 +21890,803 @@ function PM(e) {
}
return { emptyKeys: r, nonEmptyKeys: n };
}
-const NM = /^\+/;
-function Sl(e, t) {
- const r = t.replace(NM, "");
- return Object.hasOwn(e.markers, r) ? e.markers[r] : void 0;
-}
-function Cg(e, t) {
- if (e.length === 0) return { keep: 0, joins: !0 };
- if (t.occursUnder.length === 0) return { keep: e.length, joins: !1 };
- for (let r = e.length - 1; r >= 0; r--)
- if (t.occursUnder.includes(e[r].marker) && (r === e.length - 1 || t.rank === 0 || e[r + 1].rank <= t.rank))
- return { keep: r + 1, joins: !0 };
+function ac(e) {
+ if (e === void 0) return;
+ const t = Number(e);
+ return Number.isFinite(t) ? Math.max(0, Math.floor(t)) : 0;
}
-function Sg(e, t) {
- return Cg(e, t) !== void 0;
+function Dg(e, t) {
+ if (!t || !("clipboardData" in t)) return !1;
+ const r = e.getRootElement()?.ownerDocument.getSelection(), n = r?.anchorNode, i = r?.focusNode;
+ return !!n && !!i && !$f(e, n, i);
}
-function rc(e, t) {
- const r = Cg(e, t);
- return r ? (r.joins && (e.length = r.keep, e.push(t)), !0) : !1;
+function Ug(e, t) {
+ return e.length <= t ? e : e.slice(0, $l(e, t));
}
-function ro(e, t, r) {
- const n = F(e) ? e.getChildren().filter(O) : [];
- if (n.length === 0) {
- r.set(e.getKey(), t);
- return;
+function $l(e, t) {
+ if (t >= e.length) return e.length;
+ let r = 0;
+ for (const { index: n, segment: i } of rb(e)) {
+ if (n + i.length > t) break;
+ r = n + i.length;
}
- for (const i of n) r.set(i.getKey(), t);
+ return r;
}
-function OM(e, t, r, n, i) {
- const s = Sl(n, t);
- if (!s) {
- ro(e, "unknown", i);
- return;
+const JM = 16;
+function YM(e, { $isHidden: t, $measure: r } = {}) {
+ const n = (x) => t ? Ll(x, t).length : x.getTextContent().length;
+ r ??= n;
+ const i = w();
+ if (!N(i) || i.isCollapsed() || r(i) <= e) return !1;
+ const [s, o] = i.isBackward() ? [i.focus, i.anchor] : [i.anchor, i.focus], a = { key: s.key, offset: s.offset, type: s.type }, c = oo(s), l = oo(o), d = i.getNodes(), u = new Map(d.map((x) => [x.getKey(), ZM(x)])), f = d.length > 0 ? u.get(d[0].getKey()) : void 0, p = f && XM(s, f) ? f : void 0, y = (x) => {
+ const _ = x > 0 ? QM(d, u, p, c, l, x, t) : void 0;
+ return i.anchor.set(a.key, a.offset, a.type), _ ? i.focus.set(_.node.getKey(), _.offset, "text") : i.focus.set(a.key, a.offset, a.type), i.setCachedNodes(null), !!_;
+ };
+ let h = Math.min(e, n(i));
+ if (y(h) && r(i) <= e) return !0;
+ let m = 0, b = 0;
+ h -= 1;
+ for (let x = 0; x < JM && m <= h; x++) {
+ const _ = Math.floor((m + h) / 2);
+ y(_) && r(i) <= e ? (b = _, m = _ + 1) : h = _ - 1;
+ }
+ return y(b), !0;
+}
+function cc(e) {
+ return e.isToken() || rl(e) || Ee(e.getParent());
+}
+function XM(e, t) {
+ const r = L(t) ? t.getAllTextNodes()[0] : void 0;
+ return !!r && Ma(r.getKey(), 0, "text").isBefore(e);
+}
+function QM(e, t, r, n, i, s, o) {
+ const a = e.length - 1;
+ let c = 0, l = !0, d, u;
+ const f = /* @__PURE__ */ new Map(), p = (h) => {
+ const m = t.get(h.getKey());
+ return m && r?.is(m) ? void 0 : m;
+ }, y = (h) => {
+ let m = u ? u.endBefore : h;
+ for (; ; ) {
+ const b = eE(m, e, n);
+ if (!b || !m || b.node.is(m.node)) return b;
+ const x = p(b.node);
+ if (!x || x.is(p(m.node))) return b;
+ m = f.get(x.getKey());
+ }
+ };
+ for (let h = 0; h <= a; h++) {
+ const m = e[h], b = p(m);
+ if (u && !u.node.is(b) && (d && u.node.isParentOf(d.node) && (d = u.endBefore), u = void 0), b && !u && (u = { node: b, endBefore: d }, f.set(b.getKey(), d)), !o?.(m)) {
+ if (L(m) && !m.isInline()) {
+ if (!l) {
+ if (c + 1 > s) return y(d);
+ c += 1;
+ }
+ l = !m.isEmpty();
+ continue;
+ }
+ if (l = !1, M(m)) {
+ const x = h === 0 ? n : 0, _ = h === a ? i : m.getTextContentSize(), A = _ - x, E = cc(m);
+ if (E && c + A > s) return y(d);
+ if (!E && c + A >= s) {
+ const D = $l(m.getTextContent(), x + (s - c));
+ return y({ node: m, offset: Math.max(x, D) });
+ }
+ c += A, d = { node: m, offset: _ };
+ } else if (On(m) || Nn(m)) {
+ const x = m.getTextContentSize();
+ if (c + x > s) return y(d);
+ c += x;
+ }
+ }
}
- const o = s.occursUnder ?? [];
- o.length > 0 && !o.includes(r) && ro(e, "invalid", i);
+ return y(d);
}
-function zi(e, t, r, n, i) {
- for (const s of e.getChildren())
- if (D(s)) {
- const o = s.getMarker();
- i || OM(s, o, t, r, n), zi(s, t, r, n, i || o === "xq");
- } else if (ge(s)) {
- if (i) continue;
- const o = Sl(r, "v");
- o ? (o.occursUnder ?? []).length > 0 && !(o.occursUnder ?? []).includes(t) && n.set(s.getKey(), "invalid") : n.set(s.getKey(), "unknown");
- } else j(s) ? zi(s, s.getMarker(), r, n, i) : De(s) || F(s) && zi(s, t, r, n, i);
+function ZM(e) {
+ let t;
+ for (let r = e; r; r = r.getParent())
+ (z(r) || xl(r)) && (t = r);
+ return t;
}
-function wM(e, t) {
- const r = /* @__PURE__ */ new Map(), n = [], i = (o, a) => {
- const c = Sl(e, a);
- if (!c) {
- ro(o, "unknown", r), rc(n, { marker: a, rank: 0, occursUnder: [] });
- return;
+function eE(e, t, r) {
+ if (!e) return e;
+ const n = Xe(e.node, (m) => L(m) && !m.isInline()), i = L(n) ? n.getAllTextNodes() : [e.node], s = i.findIndex((m) => m.is(e.node));
+ if (s < 0) return e;
+ const o = Math.max(0, s - 1), a = Math.min(i.length - 1, s + 1);
+ let c = "";
+ const l = [];
+ for (let m = o; m <= a; m++)
+ l.push(c.length), c += i[m].getTextContent();
+ const d = l[s - o], u = d + e.offset, f = $l(c, u);
+ if (f === u) return e;
+ const p = (m) => t[0]?.is(m) ?? !1;
+ if (cc(e.node)) return { ...e, offset: p(e.node) ? r : 0 };
+ if (f >= d) {
+ const m = f - d;
+ return m >= (p(e.node) ? r : 0) ? { ...e, offset: m } : void 0;
+ }
+ const y = s > o ? i[s - 1] : void 0;
+ if (!y || !t.some((m) => m.is(y))) return;
+ if (cc(y))
+ return { node: y, offset: p(y) ? r : 0 };
+ const h = f - l[0];
+ return h >= (p(y) ? r : 0) ? { node: y, offset: h } : void 0;
+}
+function Ll(e, t) {
+ const r = e.getNodes(), n = r.length - 1, [i, s] = e.isBackward() ? [e.focus, e.anchor] : [e.anchor, e.focus], o = n === 0 && i.type === "element" && s.type === "element" && i.offset !== s.offset;
+ let a = "", c = !0;
+ return r.forEach((l, d) => {
+ if (!t(l)) {
+ if (L(l) && !l.isInline()) {
+ c || (a += `
+`), c = !l.isEmpty();
+ return;
+ }
+ if (c = !1, M(l)) {
+ const u = l.getTextContent();
+ o ? a += u : a += u.slice(
+ d === 0 ? oo(i) : 0,
+ d === n ? oo(s) : u.length
+ );
+ } else (On(l) || Nn(l)) && (d !== n || !e.isCollapsed()) && (a += l.getTextContent());
}
- const l = {
- marker: a,
- rank: c.rank ?? 0,
- occursUnder: c.occursUnder ?? []
- };
- rc(n, l) || ro(o, "invalid", r);
- }, s = (o) => !t || t.has(o.getKey());
- for (const o of Fe().getChildren())
- De(o) || (pt(o) || Ge(o) ? i(o, o.getMarker()) : ae(o) ? (i(o, o.getMarker()), s(o) && zi(o, o.getMarker(), e, r, !1)) : F(o) && s(o) && zi(o, "p", e, r, !1));
- return r;
-}
-function qM(e) {
- return !!e?.includes("(basic)");
+ }), a;
}
-function RM(e) {
- if (e !== void 0)
- return e.replace(/\s*\(basic\)/g, "").trim();
+function lc(e) {
+ const t = e.getParent();
+ return !!t && Xe(t, (r) => z(r) && !!r.getIsCollapsed()) !== null;
}
-function vg(e, t) {
- return !e.startsWith("zpa") && e !== "c" && Za(e, t);
+function Fg(e) {
+ const t = /* @__PURE__ */ new Map(), r = (i) => {
+ ht(i) && t.set(i.getKey(), i.getPreviewText().length);
+ };
+ for (const i of e)
+ if (r(i), L(i)) for (const { node: s } of wn(i)) r(s);
+ let n = 0;
+ for (const i of t.values()) n += i;
+ return n;
}
-function vl(e, t) {
- const r = Object.hasOwn(e.markers, t) ? e.markers[t] : void 0;
- if (r)
- return { marker: t, rank: r.rank ?? 0, occursUnder: r.occursUnder ?? [] };
+function tE(e) {
+ return e.getNodes().some(
+ (t) => On(t) || z(t) || he(t) || Ge(t) || xl(t)
+ );
}
-function Mg(e, t) {
- const r = [];
- for (const n of t) {
- const i = vl(e, n);
- i && rc(r, i);
- }
- return r;
+function oo(e) {
+ if (e.type === "text") return e.offset;
+ const t = e.getNode();
+ return L(t) && e.offset === t.getChildrenSize() ? t.getTextContent().length : 0;
+}
+const Zt = String.raw`\w-`, zg = "a-z0-9", rE = `[a-z][${zg}]*`, nE = new RegExp(
+ String.raw`^\\(\+?[${Zt}]+)[ \u00A0]$`
+), Kg = new RegExp(String.raw`^\\(\+?[${Zt}]+)$`), iE = new RegExp(String.raw`^\\\+?[${Zt}]*\*$`), sE = new RegExp(
+ String.raw`^\\(\+?[${Zt}]+)(?:[ \u00A0]|$)`
+), oE = new RegExp(
+ String.raw`^\\(\+?)([${Zt}]+)`
+), aE = new RegExp(
+ String.raw`\\\+?[${Zt}]+(?:\\?\*|[ \u00A0])`
+), cE = new RegExp(
+ String.raw`\\\+?[${Zt}]*$`
+), lE = new RegExp(
+ String.raw`^\\(${rE})( |$)`
+), uE = new RegExp(
+ String.raw`\\[${zg}+*]*$`,
+ "i"
+), ao = "usfm:", jg = "usfmopen", Bg = "usfmclosed";
+function dE(e) {
+ return e.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
-function ws(e, t) {
- return {
- marker: e.marker,
- kind: t,
- // `isBasic` reads the ORIGINAL description; the emitted one has the token removed.
- description: RM(e.description),
- isBasic: qM(e.description)
- };
+const fE = new RegExp(
+ [ao, jg, Bg].map(dE).join("|")
+), pE = "\uFEFF", hE = /^usfm_(.+)$/;
+function gE(e) {
+ return e.nodeType === Node.ELEMENT_NODE;
}
-function $M(e, t) {
- const r = (s) => {
- const o = /^(.*?)(\d+)$/.exec(s);
- return o ? { prefix: o[1], digits: Number(o[2]) } : { prefix: s };
- }, n = r(e), i = r(t);
- return n.prefix !== i.prefix ? n.prefix < i.prefix ? -1 : 1 : n.digits === void 0 ? i.digits === void 0 ? 0 : -1 : i.digits === void 0 ? 1 : n.digits - i.digits;
+function mE(e) {
+ return e.replace(
+ /%([0-9a-fA-F]{4})/g,
+ (t, r) => String.fromCharCode(Number.parseInt(r, 16))
+ );
}
-function nc(e, t) {
- return e.isBasic !== t.isBasic ? e.isBasic ? -1 : 1 : $M(e.marker, t.marker);
+function yE(e) {
+ return e.startsWith(ao) ? mE(e.slice(ao.length)).replace(/\r\n?|\n/g, " ") : "";
}
-function ic(e, t, r) {
- if (t.noteMarker) return [];
- const n = Mg(e, t.previousParaMarkers);
- return Object.values(e.markers).filter(
- (i) => i.styleType === "paragraph" && vg(i.marker, r)
- ).filter((i) => {
- const s = vl(e, i.marker);
- return s !== void 0 && Sg(n, s);
- }).map((i) => ws(i, "paragraph")).sort(nc);
+function Vg(e) {
+ for (const t of e.classList) {
+ const r = hE.exec(t);
+ if (r) return r[1];
+ }
}
-function IM(e, t, r) {
+function bE(e) {
+ const t = Vg(e);
+ if (t !== void 0)
+ return e.classList.contains("nested") ? `+${t}` : t;
+}
+function kE(e) {
+ const t = e.ownerDocument.createTreeWalker(e, NodeFilter.SHOW_COMMENT);
+ for (let r = t.nextNode(); r; r = t.nextNode())
+ if ((r.nodeValue ?? "").startsWith(ao)) return !0;
+ for (const r of e.querySelectorAll("*")) {
+ const { classList: n } = r;
+ if (!(!n.contains(jg) && !n.contains(Bg)) && Vg(r) !== void 0)
+ return !0;
+ }
+ return !1;
+}
+function Wg(e, t, r) {
+ if (e.nodeType === Node.TEXT_NODE) {
+ t || r.push(e.nodeValue ?? "");
+ return;
+ }
+ if (e.nodeType === Node.COMMENT_NODE) {
+ r.push(yE(e.nodeValue ?? ""));
+ return;
+ }
+ if (!gE(e)) return;
+ const { classList: n } = e, i = (d) => e.childNodes.forEach((u) => Wg(u, d, r));
+ if (t) {
+ i(!n.contains("include"));
+ return;
+ }
+ if (n.contains("exclude")) {
+ i(!0);
+ return;
+ }
+ const s = e.tagName.toLowerCase();
+ if (s === "br") {
+ r.push(`
+`);
+ return;
+ }
+ if (s === "span" && e.getAttribute("class") === "attribute") {
+ r.push(e.textContent ?? "");
+ return;
+ }
+ const o = s === "div" || s === "tr", a = o || s === "span" || s === "th" || s === "td" ? bE(e) : void 0, c = a !== void 0 && n.contains("usfmopen"), l = a !== void 0 && n.contains("usfmclosed");
+ o && r.push(`
+`), (c || l) && r.push(`\\${a} `), i(!1), l && r.push(`\\${a}*`), o && r.push(`
+`);
+}
+function TE(e) {
+ if (!fE.test(e)) return;
+ const { body: t } = new DOMParser().parseFromString(e, "text/html");
+ if (t.querySelectorAll("script,style,template").forEach((n) => n.remove()), !kE(t)) return;
+ const r = [];
+ return t.childNodes.forEach((n) => Wg(n, !1, r)), r.join("").replaceAll(pE, "").replaceAll(R, " ").replace(/\r\n?/g, `
+`).replace(/\n+/g, `
+`).replace(/^\n|\n$/g, "");
+}
+function xE(e) {
+ const t = e.getTextContent();
+ if (!t.includes(" ")) return;
+ const r = ne(e, oe);
+ if (r === "attribute" || r === fr) return;
+ for (let o = e.getParent(); o; o = o.getParent())
+ if (pt(o) || Ee(o) || Ue(o)) return;
+ const n = t.startsWith(R) && I(e.getParent()), i = n ? t.slice(1) : t, s = (n ? R : "") + i.replace(/ (?=[ \u00A0])/g, R).replace(new RegExp("(?<=\\u00A0) ", "g"), R);
+ s !== t && e.setTextContent(s);
+}
+function _E(e) {
+ const { body: t } = new DOMParser().parseFromString(e, "text/html");
+ return t.querySelectorAll("script,style,template").forEach((r) => r.remove()), t.querySelectorAll("br").forEach((r) => r.replaceWith(`
+`)), t.querySelectorAll("p,div,li,td,th,tr,h1,h2,h3,h4,h5,h6,blockquote,pre").forEach((r) => r.after(`
+`)), (t.textContent ?? "").replace(/\n+/g, `
+`).replace(/^\n|\n$/g, "");
+}
+function CE(e, t) {
+ if (!e) return !1;
+ try {
+ const r = JSON.parse(e);
+ if (typeof r != "object" || r === null) return !1;
+ const { namespace: n, nodes: i } = r;
+ return n === t && Array.isArray(i);
+ } catch {
+ return !1;
+ }
+}
+function uc(e, t) {
+ const r = e && typeof e == "object" && "clipboardData" in e ? e.clipboardData : void 0;
+ if (!r) return;
+ const n = (a) => a.replace(/\r\n?/g, `
+`), i = n(r.getData("text/plain")), s = r.getData("text/html"), o = s ? TE(s) : void 0;
+ return {
+ text: o ? n(o) : i || (s ? n(_E(s)) : ""),
+ isInternal: CE(
+ r.getData("application/x-lexical-editor"),
+ t
+ )
+ };
+}
+const Qd = String.raw`\\(?:\+?[${Zt}]+\*?|\*)`, vE = new RegExp(
+ String.raw`(?<=${Qd})\u00A0|\u00A0(?=${Qd})`,
+ "g"
+);
+function Il(e) {
+ return e.replace(/^\u00A0+/gm, (t) => " ".repeat(t.length)).replace(vE, " ").replaceAll(R, "~");
+}
+const Hg = new RegExp(
+ String.raw`\\c(?![${Zt}])[ \u00A0]*[^\s\\]*`,
+ "g"
+), Gg = new RegExp(String.raw`\\id(?![${Zt}])[^\n\\]*`, "g"), SE = new RegExp(
+ String.raw`^(?:${Hg.source}|${Gg.source})`
+);
+function Dl(e) {
+ return e.split(`
+`).map((t) => {
+ const r = t.replace(Hg, "").replace(Gg, "");
+ return SE.test(t) && r.trim() === "" && t.trim() !== "" ? void 0 : r;
+ }).filter((t) => t !== void 0).join(`
+`);
+}
+function dc(e) {
+ if (M(e) && ne(e, oe) === "attribute") return !0;
+ for (let t = e; t; t = t.getParent())
+ if (De(t)) return !0;
+ return !1;
+}
+function ME(e) {
+ return dc(e.anchor.getNode()) || dc(e.focus.getNode());
+}
+function EE(e) {
+ const { anchor: t, focus: r } = e;
+ return t.key === r.key && dc(t.getNode());
+}
+function AE(e, t) {
+ const n = EE(e) ? t : Il(Dl(t));
+ n && e.insertText(n.replace(/\n/g, " "));
+}
+function PE(e, t = !1, r = () => {
+}) {
+ const n = uc(e, ri()._config.namespace);
+ if (!n) return !1;
+ const i = w(), s = N(i) && ME(i);
+ if (!s && n.isInternal || t && N(i) && ti(i))
+ return !1;
+ const { text: o } = n;
+ if (!o || !N(i)) return !1;
+ if (e?.preventDefault(), s)
+ return AE(i, o), !0;
+ const a = Il(Dl(o));
+ if (!a) return !0;
+ const c = a.split(`
+`);
+ if (t)
+ return i.insertText(c.join(" ")), !0;
+ if (c.length < 2)
+ return i.insertText(a), !0;
+ r(), i.isCollapsed() || i.removeText();
+ const l = ri();
+ return c.forEach((d, u) => {
+ if (u > 0 && l.dispatchCommand(Us, void 0), d === "") return;
+ const f = w();
+ N(f) && f.insertText(d);
+ }), !0;
+}
+function NE(e) {
+ if (e.getTextContent() !== R) return !1;
+ const t = e.getParent();
+ return z(t) ? !ht(e.getPreviousSibling()) : !1;
+}
+function OE(e, t) {
+ if (t || e.getTextContent() !== R) return "";
+ const r = e.getParent();
+ if (!z(r) || !ht(e.getPreviousSibling())) return "";
+ const n = r.getCategory();
+ return n ? `\\cat ${n}\\cat*` : "";
+}
+function wE(e) {
+ const t = e.getParent();
+ return (z(t) ? t.getCaller() : void 0) || Ji;
+}
+function qE(e) {
+ const t = e.getParent();
+ return !t || Xr(t) === void 0;
+}
+function Ul(e) {
+ const t = e.getNodes();
+ if (t.length === 0) return "";
+ const r = t[0], n = t[t.length - 1], { anchor: i, focus: s } = e, o = i.isBefore(s), [a, c] = Ac(e);
+ let l = "", d = !0;
+ for (const u of t) {
+ if (L(u) && !u.isInline()) {
+ !d && qE(u) && (l += `
+`), d = !u.isEmpty();
+ continue;
+ }
+ if (d = !1, ht(u))
+ (u !== n || !e.isCollapsed()) && (l += (u === r ? "" : " ") + wE(u));
+ else if (M(u)) {
+ let f = u.getTextContent();
+ u === r ? u === n ? (i.type !== "element" || s.type !== "element" || s.offset === i.offset) && (f = a < c ? f.slice(a, c) : f.slice(c, a)) : f = o ? f.slice(a) : f.slice(c) : u === n && (f = o ? f.slice(0, c) : f.slice(0, a)), l += NE(u) ? "" : f.replaceAll(R, " ") + OE(u, u === n);
+ } else (On(u) || Nn(u)) && (u !== n || !e.isCollapsed()) && (l += u.getTextContent().replaceAll(R, " "));
+ }
+ return l;
+}
+function Jg(e) {
+ return e.split(`
+`).map((t) => t.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">")).map(
+ (t) => t ? `
${t}
` : ""
+ ).join("");
+}
+function RE(e) {
+ return e.getNodes().some(
+ (t) => [t, ...t.getParents()].some(
+ (r) => pt(r) || Ee(r)
+ )
+ );
+}
+function $E(e) {
+ const t = w();
+ if (!N(t) || t.isCollapsed()) return;
+ const r = Ul(t), n = {
+ "text/plain": r,
+ "text/html": Jg(r)
+ };
+ if (wo() || RE(t)) return n;
+ const i = ib(e);
+ return i && (n["application/x-lexical-editor"] = i), n;
+}
+function Zd(e, t, r, n) {
+ const i = w();
+ if (!N(i) || i.isCollapsed())
+ return (!e || !("clipboardData" in e)) && !rg();
+ const s = $E(t);
+ return s ? (n !== void 0 && delete s["application/x-lexical-editor"], Yg(e, t, i, s, r, n)) : !1;
+}
+function Yg(e, t, r, n, i, s) {
+ const o = ac(s);
+ if (o !== void 0 && Dg(t, e)) return !1;
+ const a = n["text/plain"] ?? "";
+ return o !== void 0 && a.length > o ? (process.env.NODE_ENV !== "production" && console.warn(
+ "@eten-tech-foundation/platform-editor: a copy reached the clipboard over its copy limit; writing shortened plain text only."
+ ), fc(e, t, { "text/plain": Ug(a, o) })) : fc(
+ e,
+ t,
+ n,
+ i && t.isEditable() ? () => r.removeText() : void 0
+ );
+}
+function fc(e, t, r, n) {
+ const i = !r["text/plain"];
+ if (!e || !("clipboardData" in e))
+ return i || nb(t, null, r), n?.(), !0;
+ if (e.clipboardData == null) return !1;
+ if (e.preventDefault(), !i)
+ for (const [s, o] of Object.entries(r)) e.clipboardData.setData(s, o);
+ return n?.(), !0;
+}
+function pc(e, t) {
+ const r = dl();
+ if (!r?.end) return;
+ const n = LE(e, t);
+ if (n)
+ return n.state.read(
+ () => {
+ const i = No(r);
+ return i ? Ul(i) : void 0;
+ },
+ { editor: n.editor }
+ );
+}
+const Ta = /* @__PURE__ */ new WeakMap();
+function LE(e, t) {
+ const r = e.getEditorState();
+ if (Ta.has(r)) {
+ const i = Ta.get(r);
+ if (!i || i.viewOptions === t) return i;
+ }
+ const n = IE(r, t);
+ return Ta.set(r, n), n;
+}
+function IE(e, t) {
+ const r = kl(ml);
+ if (!r) return;
+ const n = Ls.deserializeEditorState(e, t);
+ if (!n) return;
+ const i = Jy({
+ namespace: "markers-view-copy",
+ nodes: [tt, ...pl],
+ onError: (o) => {
+ throw o;
+ }
+ }), s = i.parseEditorState(
+ Ar.serializeEditorState(n, r)
+ );
+ return { viewOptions: t, editor: i, state: s };
+}
+function DE({
+ viewOptions: e,
+ copyLimit: t
+}) {
+ const [r] = ae();
+ return F(() => {
+ const n = (i, s) => {
+ const o = w();
+ if (!N(o) || o.isCollapsed()) return !1;
+ const a = pc(r, e);
+ return a === void 0 ? !1 : Yg(
+ // COPY_COMMAND's payload is `ClipboardEvent | KeyboardEvent | null`, and jsdom (our test
+ // environment) has no `ClipboardEvent` for `instanceof` to narrow against, so this
+ // duck-checks the one property `$writeCopyPayload` needs. `in` throws on a non-object, so
+ // the type check comes first.
+ i && typeof i == "object" && "clipboardData" in i ? i : null,
+ r,
+ o,
+ { "text/plain": a, "text/html": Jg(a) },
+ s,
+ t
+ );
+ };
+ return Be(
+ r.registerCommand(ni, (i) => n(i, !1), Oe),
+ r.registerCommand(vr, (i) => n(i, !0), Oe)
+ );
+ }, [r, e, t]), null;
+}
+const ef = /* @__PURE__ */ new WeakMap();
+function UE({
+ limit: e,
+ viewOptions: t
+}) {
+ const [r] = ae(), n = Z(ac(e)), i = Z(t), s = Z(!1), o = Z(void 0);
+ return F(() => {
+ n.current = ac(e), i.current = t, o.current?.();
+ }, [e, t]), F(() => {
+ let a, c;
+ const l = (b) => {
+ const x = n.current;
+ if (x === void 0) return;
+ const _ = ef.get(b);
+ if (b.defaultPrevented && _ === void 0) return;
+ const A = r.getRootElement();
+ if (!A) return;
+ const E = A.ownerDocument.getSelection();
+ if (!E || !KE(E, A)) return;
+ b.preventDefault();
+ const D = Ug(
+ _ ?? E.toString().replaceAll(R, " "),
+ x
+ );
+ ef.set(b, D);
+ const V = b.clipboardData;
+ if (V) {
+ if (!D) {
+ _ !== void 0 && V.clearData();
+ return;
+ }
+ V.setData("text/plain", D);
+ }
+ }, d = (b) => {
+ n.current !== void 0 && Xy(b, "a", { ctrlKey: !si, metaKey: si }) && (BE(c?.activeElement, r.getRootElement()) || b.preventDefault());
+ }, u = () => {
+ c?.removeEventListener("copy", l), c?.removeEventListener("keydown", d, !0), c = void 0;
+ }, f = () => {
+ const b = n.current === void 0 ? void 0 : a;
+ b !== c && (u(), c = b, c?.addEventListener("copy", l), c?.addEventListener("keydown", d, !0));
+ };
+ o.current = f;
+ const p = (b) => (b?.preventDefault(), !0), y = (b) => {
+ const x = n.current;
+ if (x === void 0 || (s.current = !1, Dg(r, b))) return !1;
+ if (x <= 0) return p(b);
+ const _ = w();
+ if (go(_))
+ return _.getTextContent().length + Fg(_.getNodes()) <= x ? !1 : p(b);
+ if (!N(_) || _.isCollapsed()) return !1;
+ const A = FE(r, i.current);
+ return A.$size(_) <= x ? !1 : (YM(x, A.options), _.isCollapsed() ? p(b) : (r.isEditable() || If(() => zE(r)), s.current = !0, !1));
+ }, h = (b, x) => {
+ if (n.current === void 0 || !s.current) return !1;
+ s.current = !1;
+ const _ = w();
+ if (!N(_) || _.isCollapsed()) return !1;
+ const A = x && r.isEditable() && !tE(_);
+ return fc(
+ b && typeof b == "object" && "clipboardData" in b ? b : null,
+ r,
+ { "text/plain": Ll(_, lc) },
+ A ? () => _.removeText() : void 0
+ );
+ }, m = Be(
+ r.registerCommand(ni, y, xt),
+ r.registerCommand(vr, y, xt),
+ r.registerCommand(
+ ni,
+ (b) => h(b, !1),
+ Vr
+ ),
+ r.registerCommand(
+ vr,
+ (b) => h(b, !0),
+ Vr
+ ),
+ // The page key-down listener's `preventDefault` does not stop an editable editor from
+ // dispatching its own Select All, so this swallows it. A read-only editor never dispatches it.
+ r.registerCommand(
+ Yy,
+ (b) => n.current === void 0 ? !1 : (b?.preventDefault(), !0),
+ xt
+ ),
+ r.registerRootListener((b) => {
+ a = b?.ownerDocument ?? void 0, f();
+ })
+ );
+ return () => {
+ m(), o.current = void 0, u();
+ };
+ }, [r]), null;
+}
+function FE(e, t) {
+ if (Ts(t)) {
+ const n = (i) => Ul(i).length;
+ return { $size: n, options: { $measure: n } };
+ }
+ const r = t?.markerMode === "visible" ? t : void 0;
+ if (r && pc(e, r) !== void 0) {
+ const n = (i) => pc(e, r)?.length ?? Ll(i, lc).length;
+ return { $size: n, options: { $measure: n } };
+ }
+ return {
+ $size: (n) => n.getTextContent().length + Fg(n.getNodes()),
+ options: { $isHidden: lc }
+ };
+}
+function zE(e) {
+ e.getEditorState().read(() => {
+ const t = w(), r = e.getRootElement()?.ownerDocument.getSelection();
+ if (!N(t) || !r) return;
+ const n = tf(e, t.anchor), i = tf(e, t.focus);
+ n && i && r.setBaseAndExtent(...n, ...i);
+ });
+}
+function tf(e, t) {
+ const r = e.getElementByKey(t.key);
+ if (!r) return;
+ if (t.type === "text") {
+ const a = Qy(r);
+ return a ? [a, t.offset] : void 0;
+ }
+ const n = t.getNode(), i = L(n) ? n.getChildAtIndex(t.offset) : null, s = i ? e.getElementByKey(i.getKey()) : null, o = s?.parentNode;
+ return !s || !o ? [r, r.childNodes.length] : [o, Array.prototype.indexOf.call(o.childNodes, s)];
+}
+function KE(e, t) {
+ const r = t.ownerDocument.createRange();
+ r.selectNodeContents(t);
+ for (let n = 0; n < e.rangeCount; n++) {
+ const i = e.getRangeAt(n), s = i.cloneRange();
+ if (i.compareBoundaryPoints(i.START_TO_START, r) < 0 && s.setStart(r.startContainer, r.startOffset), i.compareBoundaryPoints(i.END_TO_END, r) > 0 && s.setEnd(r.endContainer, r.endOffset), !s.collapsed) return !0;
+ }
+ return !1;
+}
+const jE = /* @__PURE__ */ new Set(["text", "search", "email", "url", "tel", "password", "number"]);
+function BE(e, t) {
+ return e ? e instanceof HTMLTextAreaElement ? !0 : e instanceof HTMLInputElement ? jE.has(e.type) : !(e instanceof HTMLElement) || !e.isContentEditable ? !1 : !t?.contains(e) : !1;
+}
+const VE = /^\+/;
+function Fl(e, t) {
+ const r = t.replace(VE, "");
+ return Object.hasOwn(e.markers, r) ? e.markers[r] : void 0;
+}
+function Xg(e, t) {
+ if (e.length === 0) return { keep: 0, joins: !0 };
+ if (t.occursUnder.length === 0) return { keep: e.length, joins: !1 };
+ for (let r = e.length - 1; r >= 0; r--)
+ if (t.occursUnder.includes(e[r].marker) && (r === e.length - 1 || t.rank === 0 || e[r + 1].rank <= t.rank))
+ return { keep: r + 1, joins: !0 };
+}
+function Qg(e, t) {
+ return Xg(e, t) !== void 0;
+}
+function hc(e, t) {
+ const r = Xg(e, t);
+ return r ? (r.joins && (e.length = r.keep, e.push(t)), !0) : !1;
+}
+function co(e, t, r) {
+ const n = L(e) ? e.getChildren().filter(O) : [];
+ if (n.length === 0) {
+ r.set(e.getKey(), t);
+ return;
+ }
+ for (const i of n) r.set(i.getKey(), t);
+}
+function WE(e, t, r, n, i) {
+ const s = Fl(n, t);
+ if (!s) {
+ co(e, "unknown", i);
+ return;
+ }
+ const o = s.occursUnder ?? [];
+ o.length > 0 && !o.includes(r) && co(e, "invalid", i);
+}
+function Wi(e, t, r, n, i) {
+ for (const s of e.getChildren())
+ if (I(s)) {
+ const o = s.getMarker();
+ i || WE(s, o, t, r, n), Wi(s, t, r, n, i || o === "xq");
+ } else if (he(s)) {
+ if (i) continue;
+ const o = Fl(r, "v");
+ o ? (o.occursUnder ?? []).length > 0 && !(o.occursUnder ?? []).includes(t) && n.set(s.getKey(), "invalid") : n.set(s.getKey(), "unknown");
+ } else z(s) ? Wi(s, s.getMarker(), r, n, i) : Ue(s) || L(s) && Wi(s, t, r, n, i);
+}
+function HE(e, t) {
+ const r = /* @__PURE__ */ new Map(), n = [], i = (o, a) => {
+ const c = Fl(e, a);
+ if (!c) {
+ co(o, "unknown", r), hc(n, { marker: a, rank: 0, occursUnder: [] });
+ return;
+ }
+ const l = {
+ marker: a,
+ rank: c.rank ?? 0,
+ occursUnder: c.occursUnder ?? []
+ };
+ hc(n, l) || co(o, "invalid", r);
+ }, s = (o) => !t || t.has(o.getKey());
+ for (const o of ze().getChildren())
+ Ue(o) || (pt(o) || Ge(o) ? i(o, o.getMarker()) : ce(o) ? (i(o, o.getMarker()), s(o) && Wi(o, o.getMarker(), e, r, !1)) : L(o) && s(o) && Wi(o, "p", e, r, !1));
+ return r;
+}
+function GE(e) {
+ return !!e?.includes("(basic)");
+}
+function JE(e) {
+ if (e !== void 0)
+ return e.replace(/\s*\(basic\)/g, "").trim();
+}
+function Zg(e, t) {
+ return !e.startsWith("zpa") && e !== "c" && ic(e, t);
+}
+function zl(e, t) {
+ const r = Object.hasOwn(e.markers, t) ? e.markers[t] : void 0;
+ if (r)
+ return { marker: t, rank: r.rank ?? 0, occursUnder: r.occursUnder ?? [] };
+}
+function em(e, t) {
+ const r = [];
+ for (const n of t) {
+ const i = zl(e, n);
+ i && hc(r, i);
+ }
+ return r;
+}
+function Is(e, t) {
+ return {
+ marker: e.marker,
+ kind: t,
+ // `isBasic` reads the ORIGINAL description; the emitted one has the token removed.
+ description: JE(e.description),
+ isBasic: GE(e.description)
+ };
+}
+function YE(e, t) {
+ const r = (s) => {
+ const o = /^(.*?)(\d+)$/.exec(s);
+ return o ? { prefix: o[1], digits: Number(o[2]) } : { prefix: s };
+ }, n = r(e), i = r(t);
+ return n.prefix !== i.prefix ? n.prefix < i.prefix ? -1 : 1 : n.digits === void 0 ? i.digits === void 0 ? 0 : -1 : i.digits === void 0 ? 1 : n.digits - i.digits;
+}
+function gc(e, t) {
+ return e.isBasic !== t.isBasic ? e.isBasic ? -1 : 1 : YE(e.marker, t.marker);
+}
+function mc(e, t, r) {
+ if (t.noteMarker) return [];
+ const n = em(e, t.previousParaMarkers);
+ return Object.values(e.markers).filter(
+ (i) => i.styleType === "paragraph" && Zg(i.marker, r)
+ ).filter((i) => {
+ const s = zl(e, i.marker);
+ return s !== void 0 && Qg(n, s);
+ }).map((i) => Is(i, "paragraph")).sort(gc);
+}
+function XE(e, t, r) {
const { noteMarker: n, paraMarker: i } = t, s = Object.values(e.markers).filter(
- (c) => vg(c.marker, r)
+ (c) => Zg(c.marker, r)
);
if (n)
return s.filter(
(c) => c.styleType === "character" && (c.occursUnder ?? []).includes(n)
- ).map((c) => ws(c, "character")).sort(nc);
+ ).map((c) => Is(c, "character")).sort(gc);
if (!i) return [];
const o = s.filter((c) => {
if (c.styleType !== "character") return !1;
@@ -22018,63 +22694,48 @@ function IM(e, t, r) {
return l.length === 0 || l.includes(i);
}), a = s.filter((c) => c.styleType === "note");
return [
- ...o.map((c) => ws(c, "character")),
- ...a.map((c) => ws(c, "note"))
- ].sort(nc);
+ ...o.map((c) => Is(c, "character")),
+ ...a.map((c) => Is(c, "note"))
+ ].sort(gc);
}
-function LM(e, t) {
+function QE(e, t) {
return t.map((r, n) => {
const i = e.markers[r]?.endMarker ?? `${r}*`;
return { marker: `${n === t.length - 1 ? "" : "+"}${i}`, kind: "closeTag", isBasic: !1 };
});
}
-function DM(e, t) {
+function ZE(e, t) {
return e.isBasic === t.isBasic ? 0 : e.isBasic ? -1 : 1;
}
-function UM(e, t, r) {
+function eA(e, t, r) {
return [
- ...LM(e, t.openCharMarkers),
- ...IM(e, t, r)
- ].sort(DM);
+ ...QE(e, t.openCharMarkers),
+ ...XE(e, t, r)
+ ].sort(ZE);
}
-function FM(e, t, r) {
- if (t.source === "paragraph") return ic(e, t, r);
- const n = UM(e, t, r);
- return n.length > 0 ? n : ic(e, t, r);
+function tA(e, t, r) {
+ if (t.source === "paragraph") return mc(e, t, r);
+ const n = eA(e, t, r);
+ return n.length > 0 ? n : mc(e, t, r);
}
-function zM(e, t, r) {
- const n = ic(e, t, r), i = Mg(e, t.previousParaMarkers), s = vl(e, "ip"), a = !t.previousParaMarkers.includes("c") && s && Sg(i, s) ? "ip" : "p", c = n.findIndex((u) => u.marker === a);
+function rA(e, t, r) {
+ const n = mc(e, t, r), i = em(e, t.previousParaMarkers), s = zl(e, "ip"), a = !t.previousParaMarkers.includes("c") && s && Qg(i, s) ? "ip" : "p", c = n.findIndex((d) => d.marker === a);
if (c <= 0) return n;
const [l] = n.splice(c, 1);
return [l, ...n];
}
-const er = String.raw`\w-`, Eg = "a-z0-9", KM = `[a-z][${Eg}]*`, jM = new RegExp(
- String.raw`^\\(\+?[${er}]+)[ \u00A0]$`
-), Ag = new RegExp(String.raw`^\\(\+?[${er}]+)$`), BM = new RegExp(String.raw`^\\\+?[${er}]*\*$`), VM = new RegExp(
- String.raw`^\\(\+?[${er}]+)(?:[ \u00A0]|$)`
-), WM = new RegExp(
- String.raw`^\\(\+?)([${er}]+)`
-), HM = new RegExp(
- String.raw`\\\+?[${er}]+(?:\\?\*|[ \u00A0])`
-), GM = new RegExp(
- String.raw`\\\+?[${er}]*$`
-), JM = new RegExp(
- String.raw`^\\(${KM})( |$)`
-), YM = new RegExp(
- String.raw`\\[${Eg}+*]*$`,
- "i"
-), tt = "";
-function Pg(e) {
- return e.length > 1 && e.startsWith(I) && e.charAt(1) !== tt ? e.slice(1) : e;
+const it = "";
+function tm(e) {
+ return e.length > 1 && e.startsWith(R) && e.charAt(1) !== it ? e.slice(1) : e;
}
-function Fd(e) {
- return Dc(e) ? e.markerSyntax ?? "opening" : void 0;
+function rf(e) {
+ return Hc(e) ? e.markerSyntax ?? "opening" : void 0;
}
-function Ng(e, t, r, n) {
- const i = { ...n, noteMode: "expanded" }, s = Cr.serializeEditorState(
+function rm(e, t, r, n) {
+ const i = { ...n, noteMode: "expanded" }, s = Ar.serializeEditorState(
{
- type: kr,
- version: br,
+ type: Cr,
+ version: _r,
content: [
{
...e.getUnknownAttributes(),
@@ -22090,16 +22751,16 @@ function Ng(e, t, r, n) {
).root.children, o = s.length === 1 ? s[0] : void 0, a = Array.isArray(o?.children) ? o.children : void 0;
if (!a) return { failure: "shape" };
let c = 0;
- for (; Fd(a[c]) === "opening"; ) c++;
- if (a[c]?.text !== At(e.getCaller())) return { failure: "caller" };
+ for (; rf(a[c]) === "opening"; ) c++;
+ if (a[c]?.text !== Pt(e.getCaller())) return { failure: "caller" };
c++;
- let u = a.length;
- for (; u > c && Fd(a[u - 1]) === "closing"; )
- u--;
- const d = a.slice(c, u);
- return d.length === 0 ? { failure: "empty" } : { children: d };
+ let d = a.length;
+ for (; d > c && rf(a[d - 1]) === "closing"; )
+ d--;
+ const u = a.slice(c, d);
+ return u.length === 0 ? { failure: "empty" } : { children: u };
}
-function Es(e, t, r) {
+function ws(e, t, r) {
e.spans.push({
key: t.getKey(),
start: e.text.length,
@@ -22107,31 +22768,31 @@ function Es(e, t, r) {
isSentinel: !1
}), e.text += r;
}
-function Oi(e, t) {
- GM.test(e.text) && (e.text += " "), e.spans.push({
+function Li(e, t) {
+ cE.test(e.text) && (e.text += " "), e.spans.push({
key: t[0].getKey(),
start: e.text.length,
end: e.text.length + 1,
isSentinel: !0
- }), e.sentinels.push(t), e.text += tt;
+ }), e.sentinels.push(t), e.text += it;
}
function It(e) {
- return e.replaceAll(I, " ");
+ return e.replaceAll(R, " ");
}
-function XM(e, t, r = !1) {
- if (Ao(t)) return It(e);
- if (e === I) return " ";
- const n = r && e.startsWith(I), i = n ? e.slice(1) : e;
- return (n ? " " : "") + i.replaceAll(I, "~");
+function nA(e, t, r = !1) {
+ if (Ts(t)) return It(e);
+ if (e === R) return " ";
+ const n = r && e.startsWith(R), i = n ? e.slice(1) : e;
+ return (n ? " " : "") + i.replaceAll(R, "~");
}
-function Ki(e) {
+function Hi(e) {
const t = e.getTextContent();
- return Nn(e) && /^[\s\u00A0]*$/.test(t) ? " " : t;
+ return Rn(e) && /^[\s\u00A0]*$/.test(t) ? " " : t;
}
-function Ml(e, t) {
+function Kl(e, t) {
const r = e[t];
- if (!Be(r)) return [];
- const { attribute: n, closing: i, wrapper: s } = ko(r);
+ if (!He(r)) return [];
+ const { attribute: n, closing: i, wrapper: s } = Co(r);
if (s) return [s];
const o = r.getNextSibling();
if (!O(o) || o.getMarkerSyntax() !== "opening" || o.getMarker() !== r.getMarker())
@@ -22139,14 +22800,14 @@ function Ml(e, t) {
const a = [o];
return n && a.push(n), i && a.push(i), a;
}
-function Og(e) {
+function nm(e) {
return e.some((t) => t.getTextContent().length > 0);
}
-function El(e, t) {
+function jl(e, t) {
const r = [];
let n = e[t];
for (const i of ["va", "vp"]) {
- const { opener: s, value: o, closer: a, wrapper: c } = Yi(n, i);
+ const { opener: s, value: o, closer: a, wrapper: c } = ts(n, i);
if (c)
r.push(c), n = c;
else if (s && o && a)
@@ -22156,32 +22817,32 @@ function El(e, t) {
}
return r;
}
-function Al(e) {
+function Bl(e) {
return !!e.getUnknownAttributes();
}
-function wo(e, t) {
+function $o(e, t) {
const r = t(e)?.type;
- return r === b.Milestone || r === void 0 && Ac(e);
+ return r === k.Milestone || r === void 0 && Ic(e);
}
-function wg(e, t) {
- return Be(e) ? !wo(e.getMarker(), t) : j(e) || De(e) ? !0 : Oe(e) ? Al(e) : D(e) ? qg(e, t) : !1;
+function im(e, t) {
+ return He(e) ? !$o(e.getMarker(), t) : z(e) || Ue(e) ? !0 : we(e) ? Bl(e) : I(e) ? sm(e, t) : !1;
}
-function qg(e, t) {
- if (Lk(e)) return !0;
+function sm(e, t) {
+ if (rT(e)) return !0;
const r = e.getMarker();
- return !vb(r) && t(r) === void 0;
+ return !Bb(r) && t(r) === void 0;
}
-const Rt = "", $t = "";
-function zd(e) {
- return e.flatMap((t) => Le(t) ? t.getChildren() : [t]);
+const $t = "", Lt = "";
+function nf(e) {
+ return e.flatMap((t) => De(t) ? t.getChildren() : [t]);
}
-function Ii(e, t, r, n = !1) {
+function zi(e, t, r, n = !1) {
for (let i = 0; i < e.length; i++) {
const s = e[i];
- if (Be(s)) {
- const o = Ml(e, i);
- wo(s.getMarker(), r) && Og(o) ? (t.push(
- Rt,
+ if (He(s)) {
+ const o = Kl(e, i);
+ $o(s.getMarker(), r) && nm(o) ? (t.push(
+ $t,
"ms",
// `marker` is part of the state for the same reason the attributes below are, and it is
// the one the SAVE leg reads: `createMilestoneMarker` (editor-usj.adaptor.ts) emits the
@@ -22205,11 +22866,11 @@ function Ii(e, t, r, n = !1) {
unknownAttributes: s.getUnknownAttributes() ?? null,
attributeOrder: s.getAttributeOrder() ?? null
})
- ), Ii(zd(o), t, r), t.push($t)) : t.push(tt), i += o.length;
- } else if (Oe(s)) {
- const o = El(e, i);
- Al(s) ? t.push(tt) : (t.push(
- Rt,
+ ), zi(nf(o), t, r), t.push(Lt)) : t.push(it), i += o.length;
+ } else if (we(s)) {
+ const o = jl(e, i);
+ Bl(s) ? t.push(it) : (t.push(
+ $t,
"verse",
It(s.getTextContent()),
JSON.stringify({
@@ -22217,48 +22878,48 @@ function Ii(e, t, r, n = !1) {
altnumber: s.getAltnumber() ?? null,
pubnumber: s.getPubnumber() ?? null
})
- ), Ii(zd(o), t, r), t.push($t)), i += o.length;
- } else O(s) ? t.push(Rt, "marker", It(s.getTextContent()), $t) : Qr(s) ? t.push(Rt, "unmatched", It(s.getTextContent()), $t) : wg(s, r) ? t.push(tt) : us(s) ? t.push(" ") : v(s) ? t.push(
+ ), zi(nf(o), t, r), t.push(Lt)), i += o.length;
+ } else O(s) ? t.push($t, "marker", It(s.getTextContent()), Lt) : tn(s) ? t.push($t, "unmatched", It(s.getTextContent()), Lt) : im(s, r) ? t.push(it) : Nn(s) ? t.push(" ") : M(s) ? t.push(
It(
- n ? Pg(Ki(s)) : Ki(s)
+ n ? tm(Hi(s)) : Hi(s)
)
- ) : D(s) ? (t.push(Rt, "char", JSON.stringify(s.getUnknownAttributes() ?? null)), Ii(s.getChildren(), t, r, !0), t.push($t)) : F(s) ? (t.push(Rt, s.getType()), Ii(s.getChildren(), t, r), t.push($t)) : t.push(tt);
+ ) : I(s) ? (t.push($t, "char", JSON.stringify(s.getUnknownAttributes() ?? null)), zi(s.getChildren(), t, r, !0), t.push(Lt)) : L(s) ? (t.push($t, s.getType()), zi(s.getChildren(), t, r), t.push(Lt)) : t.push(it);
}
}
-function bi(e, t) {
+function _i(e, t) {
const r = [];
- return Ii(e, r, t), r.join("");
+ return zi(e, r, t), r.join("");
}
-function Sr(e) {
+function Pr(e) {
const { children: t } = e;
return Array.isArray(t) ? t : void 0;
}
-function ci(e) {
+function pi(e) {
const { text: t } = e;
return typeof t == "string" ? t : void 0;
}
-function Pl(e) {
+function Vl(e) {
return e.type ?? "";
}
-function Rg(e, t, r) {
- return t === "closing" ? rt(e, r) : t === "selfClosing" ? rt("") : we(e, r);
+function om(e, t, r) {
+ return t === "closing" ? st(e, r) : t === "selfClosing" ? st("") : qe(e, r);
}
-function ya(e, t) {
+function xa(e, t) {
const r = e[t];
- if (!(!r || Pl(r) !== "attribute-run"))
- return Sr(r) ?? [];
+ if (!(!r || Vl(r) !== "attribute-run"))
+ return Pr(r) ?? [];
}
-function ki(e, t) {
+function Ci(e, t) {
const r = [];
- return Li(e, r, t), r.join("");
+ return Ki(e, r, t), r.join("");
}
-function Li(e, t, r, n = !1) {
+function Ki(e, t, r, n = !1) {
for (let i = 0; i < e.length; i++) {
- const s = e[i], o = Pl(s);
+ const s = e[i], o = Vl(s);
if (o === "ms") {
- const l = s, u = ya(e, i + 1);
- u && wo(l.marker ?? "", r) ? (t.push(
- Rt,
+ const l = s, d = xa(e, i + 1);
+ d && $o(l.marker ?? "", r) ? (t.push(
+ $t,
"ms",
// `marker` mirrored from `$appendSignature`'s fold: a glyph RENAME leaves the displayed
// bytes identical on both sides, so only the milestone's own stale `marker` reveals the
@@ -22272,17 +22933,17 @@ function Li(e, t, r, n = !1) {
unknownAttributes: l.unknownAttributes ?? null,
attributeOrder: l.attributeOrder ?? null
})
- ), Li(u, t, r), t.push($t), i += 1) : t.push(tt);
+ ), Ki(d, t, r), t.push(Lt), i += 1) : t.push(it);
continue;
}
if (o === "verse") {
const l = s;
if (l.unknownAttributes) {
- t.push(tt);
+ t.push(it);
continue;
}
t.push(
- Rt,
+ $t,
"verse",
It(l.text ?? ""),
JSON.stringify({
@@ -22291,21 +22952,21 @@ function Li(e, t, r, n = !1) {
pubnumber: l.pubnumber ?? null
})
);
- let u = 0, d = ya(e, i + 1 + u);
- for (; d; )
- Li(d, t, r), u++, d = ya(e, i + 1 + u);
- t.push($t), i += u;
+ let d = 0, u = xa(e, i + 1 + d);
+ for (; u; )
+ Ki(u, t, r), d++, u = xa(e, i + 1 + d);
+ t.push(Lt), i += d;
continue;
}
if (o === "marker") {
const l = s;
t.push(
- Rt,
+ $t,
"marker",
It(
- Rg(l.marker ?? "", l.markerSyntax, l.nested)
+ om(l.marker ?? "", l.markerSyntax, l.nested)
),
- $t
+ Lt
);
continue;
}
@@ -22315,44 +22976,44 @@ function Li(e, t, r, n = !1) {
}
if (o === "char") {
const l = s;
- t.push(Rt, "char", JSON.stringify(l.unknownAttributes ?? null)), Li(Sr(s) ?? [], t, r, !0), t.push($t);
+ t.push($t, "char", JSON.stringify(l.unknownAttributes ?? null)), Ki(Pr(s) ?? [], t, r, !0), t.push(Lt);
continue;
}
if (o === "note" || o === "unknown") {
- t.push(tt);
+ t.push(it);
continue;
}
if (o === "unmatched") {
- t.push(Rt, "unmatched", It(ci(s) ?? "")), t.push($t);
+ t.push($t, "unmatched", It(pi(s) ?? "")), t.push(Lt);
continue;
}
- const a = ci(s);
+ const a = pi(s);
if (a !== void 0) {
- t.push(It(n ? Pg(a) : a));
+ t.push(It(n ? tm(a) : a));
continue;
}
- const c = Sr(s);
- c ? (t.push(Rt, o), Li(c, t, r), t.push($t)) : t.push(tt);
+ const c = Pr(s);
+ c ? (t.push($t, o), Ki(c, t, r), t.push(Lt)) : t.push(it);
}
}
-function qo(e) {
+function Lo(e) {
let t = 0;
for (const r of e) {
- const n = Sr(r);
+ const n = Pr(r);
if (n) {
- t += qo(n);
+ t += Lo(n);
continue;
}
- const i = ci(r);
+ const i = pi(r);
if (i !== void 0)
- for (const s of i) s === tt && t++;
+ for (const s of i) s === it && t++;
}
return t;
}
-function ss(e, t, r, n, i) {
- Mn(e.getChildren(), t, r, n, i);
+function us(e, t, r, n, i) {
+ En(e.getChildren(), t, r, n, i);
}
-function Mn(e, t, r, n, i) {
+function En(e, t, r, n, i) {
const s = () => {
const o = i?.pending === !0;
return i && (i.pending = !1), o;
@@ -22360,433 +23021,433 @@ function Mn(e, t, r, n, i) {
for (let o = 0; o < e.length; o++) {
const a = e[o];
if (O(a))
- Es(t, a, It(a.getTextContent()));
- else if (Be(a)) {
+ ws(t, a, It(a.getTextContent()));
+ else if (He(a)) {
s();
- const c = Ml(e, o);
- wo(a.getMarker(), r) && Og(c) ? Mn(c, t, r, n) : Oi(t, [a, ...c]), o += c.length;
- } else if (j(a) || De(a))
- s(), Oi(t, [a]);
- else if (Oe(a)) {
+ const c = Kl(e, o);
+ $o(a.getMarker(), r) && nm(c) ? En(c, t, r, n) : Li(t, [a, ...c]), o += c.length;
+ } else if (z(a) || Ue(a))
+ s(), Li(t, [a]);
+ else if (we(a)) {
s();
- const c = El(e, o);
- Al(a) ? Oi(t, [a, ...c]) : (Es(t, a, It(Ki(a))), Mn(c, t, r, n)), o += c.length;
- } else if (D(a))
- s(), qg(a, r) ? Oi(t, [a]) : ss(a, t, r, n, { pending: !0 });
- else if (us(a))
- s(), Es(t, a, " ");
- else if (v(a)) {
- const c = Nn(a) || ne(a, oe) === "attribute", l = s() && !c;
- Es(
+ const c = jl(e, o);
+ Bl(a) ? Li(t, [a, ...c]) : (ws(t, a, It(Hi(a))), En(c, t, r, n)), o += c.length;
+ } else if (I(a))
+ s(), sm(a, r) ? Li(t, [a]) : us(a, t, r, n, { pending: !0 });
+ else if (Nn(a))
+ s(), ws(t, a, " ");
+ else if (M(a)) {
+ const c = Rn(a) || ne(a, oe) === "attribute", l = s() && !c;
+ ws(
t,
a,
- c ? It(Ki(a)) : XM(Ki(a), n, l)
+ c ? It(Hi(a)) : nA(Hi(a), n, l)
);
- } else F(a) ? ss(a, t, r, n, i) : (s(), Oi(t, [a]));
+ } else L(a) ? us(a, t, r, n, i) : (s(), Li(t, [a]));
}
}
-function Nl(e, t, r) {
+function Wl(e, t, r) {
if (e.getUnknownAttributes()) return;
const n = t(e.getMarker())?.type;
- if (n !== void 0 && n !== b.Unknown && n !== b.Paragraph)
+ if (n !== void 0 && n !== k.Unknown && n !== k.Paragraph)
return;
for (let s = e.getParent(); s !== null; s = s.getParent())
- if (De(s)) return;
+ if (Ue(s)) return;
const i = { text: "", spans: [], sentinels: [] };
- return ss(e, i, t, r), i;
+ return us(e, i, t, r), i;
}
-function $g(e, t) {
+function am(e, t) {
let r = 0;
const n = (i) => {
- if (v(i)) {
+ if (M(i)) {
let s = i;
for (; s; ) {
- const a = s.getTextContent().indexOf(tt);
+ const a = s.getTextContent().indexOf(it);
if (a < 0) break;
let c = s, l;
a > 0 && ([, c] = s.splitText(a)), c.getTextContent().length > 1 && ([c, l] = c.splitText(1));
- const u = t[r++];
- if (u && u.length > 0) {
- let d = c;
- for (const f of u)
- d.insertAfter(f), d = f;
+ const d = t[r++];
+ if (d && d.length > 0) {
+ let u = c;
+ for (const f of d)
+ u.insertAfter(f), u = f;
}
c.remove(), s = l;
}
- } else F(i) && [...i.getChildren()].forEach(n);
+ } else L(i) && [...i.getChildren()].forEach(n);
};
e.forEach(n);
}
-function sc(e, t = []) {
+function yc(e, t = []) {
for (const r of e)
- Oe(r) ? t.push(r) : F(r) && sc(r.getChildren(), t);
+ we(r) ? t.push(r) : L(r) && yc(r.getChildren(), t);
return t;
}
-function Ig(e) {
+function cm(e) {
let t = 0;
const r = (n) => {
- if (v(n))
- for (const i of n.getTextContent()) i === tt && t++;
- else F(n) && n.getChildren().forEach(r);
+ if (M(n))
+ for (const i of n.getTextContent()) i === it && t++;
+ else L(n) && n.getChildren().forEach(r);
};
return e.forEach(r), t;
}
-function qn(e) {
+function In(e) {
let t = 0;
for (const r of e)
if (typeof r == "string")
- for (const n of r) n === tt && t++;
- else r.content && (t += qn(r.content));
+ for (const n of r) n === it && t++;
+ else r.content && (t += In(r.content));
return t;
}
-function QM(e, t, r) {
+function iA(e, t, r) {
const n = { text: "", spans: [], sentinels: [] };
for (const i of e)
- n.text.length > 0 && (n.text += " "), F(i) && ss(i, n, t, r);
+ n.text.length > 0 && (n.text += " "), L(i) && us(i, n, t, r);
return { text: n.text, spans: n.spans };
}
-const os = /\s/;
-function Lg(e) {
- return e.filter(Ro).length;
+const ds = /\s/;
+function lm(e) {
+ return e.filter(Io).length;
}
-function Ro(e) {
+function Io(e) {
if (e.isSentinel) return !1;
const t = se(e.key);
- return v(t) && !O(t) && ne(t, oe) === "attribute";
+ return M(t) && !O(t) && ne(t, oe) === "attribute";
}
-function ZM(e) {
+function sA(e) {
if (e.isSentinel) return !1;
const t = se(e.key);
- return O(t) || Ro(e);
+ return O(t) || Io(e);
}
-function Kd(e, t, r, n) {
+function sf(e, t, r, n) {
let i = 0, s = 0;
for (const o of e.spans) {
const a = o.end - o.start, c = o.key === t;
- if (!c && n && Ro(o)) continue;
+ if (!c && n && Io(o)) continue;
const l = c ? Math.min(o.isSentinel ? 1 : r, a) : a;
- for (let u = 0; u < l; u++)
- os.test(e.text[o.start + u]) ? s++ : (i++, s = 0);
+ for (let d = 0; d < l; d++)
+ ds.test(e.text[o.start + d]) ? s++ : (i++, s = 0);
if (c) return { nonWsBefore: i, wsRun: s };
}
}
-function Ol(e, t, r) {
- const n = Kd(e, t, r, !1);
+function Hl(e, t, r) {
+ const n = sf(e, t, r, !1);
if (!n) return;
- const i = e.spans.find((o) => o.key === t), s = i && !ZM(i) ? Kd(e, t, r, !0) : void 0;
- return { ...n, documentCoords: s, attributeRunSpans: Lg(e.spans) };
+ const i = e.spans.find((o) => o.key === t), s = i && !sA(i) ? sf(e, t, r, !0) : void 0;
+ return { ...n, documentCoords: s, attributeRunSpans: lm(e.spans) };
}
-function ba(e) {
+function _a(e) {
if (e.isSentinel) return !1;
const t = se(e.key);
return O(t) && t.getMarkerSyntax() !== "opening";
}
-function eE(e) {
+function oA(e) {
const t = se(e.key);
if (!O(t)) return !1;
const r = t.getParent();
- return D(r) ? (r.selectNext(0, 0), !0) : !1;
+ return I(r) ? (r.selectNext(0, 0), !0) : !1;
}
-function tE(e) {
+function aA(e) {
const t = se(e.key), r = t?.getParent()?.getChildren();
if (!t || !r) return !1;
const n = r.findIndex((s) => s.is(t));
if (n < 0) return !1;
- const i = Oe(t) ? El(r, n) : Be(t) ? Ml(r, n) : [];
+ const i = we(t) ? jl(r, n) : He(t) ? Kl(r, n) : [];
return (i[i.length - 1] ?? t).selectNext(0, 0), !0;
}
-function Dg(e, t, r) {
- const { text: n, spans: i } = e, s = Lg(i) === t.attributeRunSpans ? t.documentCoords : void 0, o = s !== void 0;
- let a, c = (s ?? t).nonWsBefore, l = (s ?? t).wsRun, u = !1;
- e: for (const d of i) {
- const f = d.end - d.start, p = !d.isSentinel && !ba(d);
- if (!(o && Ro(d))) {
- if (u) {
+function um(e, t, r) {
+ const { text: n, spans: i } = e, s = lm(i) === t.attributeRunSpans ? t.documentCoords : void 0, o = s !== void 0;
+ let a, c = (s ?? t).nonWsBefore, l = (s ?? t).wsRun, d = !1;
+ e: for (const u of i) {
+ const f = u.end - u.start, p = !u.isSentinel && !_a(u);
+ if (!(o && Io(u))) {
+ if (d) {
if (!p) continue;
- a = { key: d.key, offset: 0 };
+ a = { key: u.key, offset: 0 };
break;
}
- for (let m = 0; m < f; m++) {
- const g = n[d.start + m];
- if (c === 0 && (l === 0 || !os.test(g))) {
+ for (let y = 0; y < f; y++) {
+ const h = n[u.start + y];
+ if (c === 0 && (l === 0 || !ds.test(h))) {
if (p) {
- a = { key: d.key, offset: m };
+ a = { key: u.key, offset: y };
break e;
}
- u = !0;
+ d = !0;
continue e;
}
- c > 0 ? os.test(g) || c-- : l--;
+ c > 0 ? ds.test(h) || c-- : l--;
}
if (c === 0 && l === 0) {
if (p) {
- a = { key: d.key, offset: f };
+ a = { key: u.key, offset: f };
break;
}
- u = !0;
+ d = !0;
}
}
}
if (!a) {
- const d = i[i.length - 1];
- if (d && ba(d) && eE(d) || d?.isSentinel && tE(d)) return;
- const f = [...i].reverse().find((p) => !p.isSentinel && !ba(p));
+ const u = i[i.length - 1];
+ if (u && _a(u) && oA(u) || u?.isSentinel && aA(u)) return;
+ const f = [...i].reverse().find((p) => !p.isSentinel && !_a(p));
f && (a = { key: f.key, offset: f.end - f.start });
}
if (a) {
- const d = se(a.key);
- if (d && v(d)) {
- d.select(a.offset, a.offset);
+ const u = se(a.key);
+ if (u && M(u)) {
+ u.select(a.offset, a.offset);
return;
}
}
- r.find(F)?.selectStart();
+ r.find(L)?.selectStart();
}
-function Ug(e, t, r, n, i) {
+function dm(e, t, r, n, i) {
if (r) {
if (t === void 0) {
- e.find(F)?.selectStart();
+ e.find(L)?.selectStart();
return;
}
- Dg(QM(e, n, i), t, e);
+ um(iA(e, n, i), t, e);
}
}
-function rE(e, t, r, n, i) {
+function cA(e, t, r, n, i) {
if (!r) return;
if (t === void 0) {
- e.find(F)?.selectStart();
+ e.find(L)?.selectStart();
return;
}
const s = { text: "", spans: [], sentinels: [] };
- Mn(e, s, n, i), Dg({ text: s.text, spans: s.spans }, t, e);
+ En(e, s, n, i), um({ text: s.text, spans: s.spans }, t, e);
}
-function Fg(e, t) {
+function fm(e, t) {
if (e.length === 0) return !1;
const { viewOptions: r, getMarker: n, logger: i } = t, s = { text: "", spans: [], sentinels: [] };
- for (const g of e) {
- const y = Nl(g, n, r);
- if (!y)
+ for (const h of e) {
+ const m = Wl(h, n, r);
+ if (!m)
return i?.debug("[MarkerEdit] Tier 2 skipped: paragraph excluded by guard rails"), !1;
s.text.length > 0 && (s.text += " ");
- const k = s.text.length;
- y.spans.forEach(
- (_) => s.spans.push({ ..._, start: _.start + k, end: _.end + k })
- ), s.sentinels.push(...y.sentinels), s.text += y.text;
+ const b = s.text.length;
+ m.spans.forEach(
+ (x) => s.spans.push({ ...x, start: x.start + b, end: x.end + b })
+ ), s.sentinels.push(...m.sentinels), s.text += m.text;
}
let o, a = !1;
const c = w();
if (N(c)) {
- for (let g = c.anchor.getNode(); g; g = g.getParent())
- if (e.some((y) => y.is(g))) {
+ for (let h = c.anchor.getNode(); h; h = h.getParent())
+ if (e.some((m) => m.is(h))) {
a = !0;
break;
}
- c.isCollapsed() && (o = Ol(s, c.anchor.key, c.anchor.offset));
+ c.isCollapsed() && (o = Hl(s, c.anchor.key, c.anchor.offset));
}
- const l = Mr(s.text, {
+ const l = Or(s.text, {
getMarker: n
});
if (l.length === 0)
return i?.debug("[MarkerEdit] Tier 2 skipped: tokenizer produced no content"), !1;
- if (qn(l) !== s.sentinels.length)
+ if (In(l) !== s.sentinels.length)
return i?.warn("[MarkerEdit] Tier 2 aborted: sentinel/preserved-node count mismatch"), !1;
- const u = Cr.serializeEditorState(
- { type: kr, version: br, content: l },
+ const d = Ar.serializeEditorState(
+ { type: Cr, version: _r, content: l },
r
);
- if (ki(u.root.children, n) === bi(e, n))
+ if (Ci(d.root.children, n) === _i(e, n))
return i?.debug("[MarkerEdit] Tier 2 skipped: rebuild is a no-op (fixed point)"), !1;
- const d = u.root.children.map((g) => ao(g));
- if (Ig(d) !== s.sentinels.length)
+ const u = d.root.children.map((h) => po(h));
+ if (cm(u) !== s.sentinels.length)
return i?.warn("[MarkerEdit] Tier 2 aborted: serialized sentinel/preserved-node count mismatch"), !1;
- const f = sc(e).map((g) => ({
- number: g.getNumber(),
- sid: g.getSid()
+ const f = yc(e).map((h) => ({
+ number: h.getNumber(),
+ sid: h.getSid()
})), p = e[0];
- d.forEach((g) => p.insertBefore(g)), $g(d, s.sentinels), e.forEach((g) => g.remove());
- const m = sc(d);
- for (let g = 0; g < f.length && g < m.length; g++)
- m[g].getNumber() === f[g].number && m[g].setSid(f[g].sid);
- return Ug(d, o, a, n, r), !0;
-}
-function zg(e, t, r) {
- if (e.getIsCollapsed() !== !1 || !Ee.isValidMarker(e.getMarker())) return;
+ u.forEach((h) => p.insertBefore(h)), am(u, s.sentinels), e.forEach((h) => h.remove());
+ const y = yc(u);
+ for (let h = 0; h < f.length && h < y.length; h++)
+ y[h].getNumber() === f[h].number && y[h].setSid(f[h].sid);
+ return dm(u, o, a, n, r), !0;
+}
+function pm(e, t, r) {
+ if (e.getIsCollapsed() !== !1 || !Ae.isValidMarker(e.getMarker())) return;
const n = e.getChildren();
let i = 0;
for (; i < n.length; ) {
- const u = n[i];
- if (!O(u) || u.getMarkerSyntax() !== "opening") break;
+ const d = n[i];
+ if (!O(d) || d.getMarkerSyntax() !== "opening") break;
i++;
}
const s = n[i];
- if (!s || !(St(s) || v(s) && s.getTextContent() === At(e.getCaller()))) return;
+ if (!s || !(ht(s) || M(s) && s.getTextContent() === Pt(e.getCaller()))) return;
i++;
let a = n.length;
for (; a > i; ) {
- const u = n[a - 1];
- if (!O(u) || u.getMarkerSyntax() !== "closing") break;
+ const d = n[a - 1];
+ if (!O(d) || d.getMarkerSyntax() !== "closing") break;
a--;
}
const c = n.slice(i, a), l = { text: "", spans: [], sentinels: [] };
- return Mn(c, l, t, r), { out: l, contentNodes: c };
+ return En(c, l, t, r), { out: l, contentNodes: c };
}
-function Kg(e) {
+function hm(e) {
const t = e[0];
if (typeof t != "object" || t.type !== "char" || t.marker !== "cat" || !Object.keys(t).every((s) => s === "type" || s === "marker" || s === "content") || !t.content || t.content.length !== 1) return;
const n = t.content[0];
- if (typeof n != "string" || n.includes(tt)) return;
+ if (typeof n != "string" || n.includes(it)) return;
const i = n.trim();
if (i !== "")
return e.shift(), i;
}
-function nE(e, t) {
- const { viewOptions: r, getMarker: n, logger: i } = t, s = zg(e, n, r);
+function lA(e, t) {
+ const { viewOptions: r, getMarker: n, logger: i } = t, s = pm(e, n, r);
if (!s)
return i?.debug("[MarkerEdit] Note Tier 2 skipped: note excluded by guard rails"), !1;
const { out: o, contentNodes: a } = s;
let c, l = !1;
- const u = w();
- if (N(u)) {
- for (let P = u.anchor.getNode(); P; P = P.getParent())
- if (e.is(P)) {
+ const d = w();
+ if (N(d)) {
+ for (let A = d.anchor.getNode(); A; A = A.getParent())
+ if (e.is(A)) {
l = !0;
break;
}
- u.isCollapsed() && (c = Ol(o, u.anchor.key, u.anchor.offset));
+ d.isCollapsed() && (c = Hl(o, d.anchor.key, d.anchor.offset));
}
- const d = Mr(o.text, {
+ const u = Or(o.text, {
getMarker: n,
isNoteContext: !0
});
- if (d.length === 0)
+ if (u.length === 0)
return i?.debug("[MarkerEdit] Note Tier 2 skipped: tokenizer produced no content"), !1;
- if (qn(d) !== o.sentinels.length)
+ if (In(u) !== o.sentinels.length)
return i?.warn("[MarkerEdit] Note Tier 2 aborted: sentinel/preserved-node count mismatch"), !1;
- const [f] = d;
- if (d.length !== 1 || typeof f != "object" || f.type !== "para")
+ const [f] = u;
+ if (u.length !== 1 || typeof f != "object" || f.type !== "para")
return i?.warn("[MarkerEdit] Note Tier 2 aborted: unexpected tokenized shape"), !1;
- const p = f.content ?? [], m = Kg(p), g = Ng(e, p, m, r);
- if (g.failure !== void 0)
- return g.failure === "empty" ? i?.debug("[MarkerEdit] Note Tier 2 skipped: no content nodes after unwrap") : i?.warn(
- g.failure === "caller" ? "[MarkerEdit] Note Tier 2 aborted: serialized note lacks the editable caller" : "[MarkerEdit] Note Tier 2 aborted: unexpected serialized shape"
+ const p = f.content ?? [], y = hm(p), h = rm(e, p, y, r);
+ if (h.failure !== void 0)
+ return h.failure === "empty" ? i?.debug("[MarkerEdit] Note Tier 2 skipped: no content nodes after unwrap") : i?.warn(
+ h.failure === "caller" ? "[MarkerEdit] Note Tier 2 aborted: serialized note lacks the editable caller" : "[MarkerEdit] Note Tier 2 aborted: unexpected serialized shape"
), !1;
- if (qo(g.children) !== o.sentinels.length)
+ if (Lo(h.children) !== o.sentinels.length)
return i?.warn(
"[MarkerEdit] Note Tier 2 aborted: serialized sentinel/preserved-node count mismatch"
), !1;
- const y = e.getCategory() !== m;
- if (y && e.setCategory(m), ki(g.children, n) === bi(a, n))
- return i?.debug("[MarkerEdit] Note Tier 2 skipped: rebuild is a no-op (fixed point)"), y;
- const k = g.children.map((P) => ao(P));
- if (Ig(k) !== o.sentinels.length)
- return i?.warn("[MarkerEdit] Note Tier 2 aborted: parsed sentinel/preserved-node count mismatch"), y;
- const _ = a[0];
- if (_)
- k.forEach((P) => _.insertBefore(P));
+ const m = e.getCategory() !== y;
+ if (m && e.setCategory(y), Ci(h.children, n) === _i(a, n))
+ return i?.debug("[MarkerEdit] Note Tier 2 skipped: rebuild is a no-op (fixed point)"), m;
+ const b = h.children.map((A) => po(A));
+ if (cm(b) !== o.sentinels.length)
+ return i?.warn("[MarkerEdit] Note Tier 2 aborted: parsed sentinel/preserved-node count mismatch"), m;
+ const x = a[0];
+ if (x)
+ b.forEach((A) => x.insertBefore(A));
else {
- const P = e.getChildren().find((A) => O(A) && A.getMarkerSyntax() === "closing");
- k.forEach((A) => P ? P.insertBefore(A) : e.append(A));
+ const A = e.getChildren().find((E) => O(E) && E.getMarkerSyntax() === "closing");
+ b.forEach((E) => A ? A.insertBefore(E) : e.append(E));
}
- $g(k, o.sentinels);
- const S = new Set(o.sentinels.flat().map((P) => P.getKey()));
- return a.forEach((P) => {
- S.has(P.getKey()) || P.remove();
- }), rE(k, c, l, n, r), !0;
+ am(b, o.sentinels);
+ const _ = new Set(o.sentinels.flat().map((A) => A.getKey()));
+ return a.forEach((A) => {
+ _.has(A.getKey()) || A.remove();
+ }), cA(b, c, l, n, r), !0;
}
-const jg = /* @__PURE__ */ new Set(["ca", "cp"]), wl = "cp";
-function Bg(e) {
+const gm = /* @__PURE__ */ new Set(["ca", "cp"]), Gl = "cp";
+function mm(e) {
if (!dr(e)) return !1;
const t = { text: "", spans: [], sentinels: [] };
- if (ss(e, t, lr, void 0), t.sentinels.length > 0) return !1;
+ if (us(e, t, lr, void 0), t.sentinels.length > 0) return !1;
const r = t.text;
if (r.trim() === "") return !1;
- const n = Mr(r, { getMarker: lr }), [i] = n, s = n.length === 1 && typeof i == "object" && i.type === "para" && i.marker === "p" && !/^\s*\\p\s/.test(r) ? i.content ?? [] : n;
+ const n = Or(r, { getMarker: lr }), [i] = n, s = n.length === 1 && typeof i == "object" && i.type === "para" && i.marker === "p" && !/^\s*\\p\s/.test(r) ? i.content ?? [] : n;
return s.length === 0 ? !1 : s.every(
- (o) => typeof o == "string" ? o.trim() === "" : (o.type === "char" || o.type === "para") && (o.marker === "ca" || o.marker === wl)
+ (o) => typeof o == "string" ? o.trim() === "" : (o.type === "char" || o.type === "para") && (o.marker === "ca" || o.marker === Gl)
);
}
-function $o(e) {
+function Do(e) {
const t = [];
for (let r = e.getNextSibling(); r; r = r.getNextSibling()) {
- if (D(r) && jg.has(r.getMarker()) || Bg(r)) {
+ if (I(r) && gm.has(r.getMarker()) || mm(r)) {
t.push(r);
continue;
}
- ae(r) && r.getMarker() === wl && t.push(r);
+ ce(r) && r.getMarker() === Gl && t.push(r);
break;
}
return t;
}
-function iE(e) {
- const t = (n) => D(n) && jg.has(n.getMarker()) || Bg(n);
- if (t(e) || ae(e) && e.getMarker() === wl)
+function uA(e) {
+ const t = (n) => I(n) && gm.has(n.getMarker()) || mm(n);
+ if (t(e) || ce(e) && e.getMarker() === Gl)
for (let n = e.getPreviousSibling(); n; n = n.getPreviousSibling()) {
- if (Pe(n)) return n;
+ if (Ee(n)) return n;
if (!t(n)) return;
}
}
-function Vg(e, t, r) {
+function ym(e, t, r) {
if (Object.keys(e.getUnknownAttributes() ?? {}).length > 0) return;
- const n = $o(e);
- if (n.some((s) => ae(s) && s.getUnknownAttributes())) return;
+ const n = Do(e);
+ if (n.some((s) => ce(s) && s.getUnknownAttributes())) return;
const i = { text: "", spans: [], sentinels: [] };
- if (Mn(e.getChildren(), i, t, r), Mn(n, i, t, r), !(i.sentinels.length > 0))
+ if (En(e.getChildren(), i, t, r), En(n, i, t, r), !(i.sentinels.length > 0))
return i;
}
-function sE(e, t) {
- const { viewOptions: r, getMarker: n, logger: i } = t, s = [e, ...$o(e)], o = Vg(e, n, r);
+function dA(e, t) {
+ const { viewOptions: r, getMarker: n, logger: i } = t, s = [e, ...Do(e)], o = ym(e, n, r);
if (!o)
return i?.debug("[MarkerEdit] Chapter Tier 2 skipped: chapter excluded by guard rails"), !1;
let a, c = !1;
const l = w();
if (N(l)) {
- for (let m = l.anchor.getNode(); m; m = m.getParent())
- if (s.some((g) => g.is(m))) {
+ for (let y = l.anchor.getNode(); y; y = y.getParent())
+ if (s.some((h) => h.is(y))) {
c = !0;
break;
}
- l.isCollapsed() && (a = Ol(o, l.anchor.key, l.anchor.offset));
+ l.isCollapsed() && (a = Hl(o, l.anchor.key, l.anchor.offset));
}
- const u = Mr(o.text, { getMarker: n }), [d] = u;
- if (u.length === 0 || typeof d != "object" || d.type !== "chapter")
+ const d = Or(o.text, { getMarker: n }), [u] = d;
+ if (d.length === 0 || typeof u != "object" || u.type !== "chapter")
return i?.debug("[MarkerEdit] Chapter Tier 2 skipped: bytes no longer tokenize as a chapter"), !1;
- if (qn(u) !== 0)
+ if (In(d) !== 0)
return i?.warn("[MarkerEdit] Chapter Tier 2 aborted: unexpected preserved-node placeholder"), !1;
- e.getSid() !== void 0 && (d.sid = e.getSid());
- const f = Cr.serializeEditorState(
- { type: kr, version: br, content: u },
+ e.getSid() !== void 0 && (u.sid = e.getSid());
+ const f = Ar.serializeEditorState(
+ { type: Cr, version: _r, content: d },
r
);
- if (ki(f.root.children, n) === bi(s, n)) {
- let m = !1;
- return e.getNumber() !== (d.number ?? "") && (e.setNumber(d.number ?? ""), m = !0), e.getAltnumber() !== d.altnumber && (e.setAltnumber(d.altnumber), m = !0), e.getPubnumber() !== d.pubnumber && (e.setPubnumber(d.pubnumber), m = !0), m || i?.debug("[MarkerEdit] Chapter Tier 2 skipped: rebuild is a no-op (fixed point)"), m;
+ if (Ci(f.root.children, n) === _i(s, n)) {
+ let y = !1;
+ return e.getNumber() !== (u.number ?? "") && (e.setNumber(u.number ?? ""), y = !0), e.getAltnumber() !== u.altnumber && (e.setAltnumber(u.altnumber), y = !0), e.getPubnumber() !== u.pubnumber && (e.setPubnumber(u.pubnumber), y = !0), y || i?.debug("[MarkerEdit] Chapter Tier 2 skipped: rebuild is a no-op (fixed point)"), y;
}
- const p = f.root.children.map((m) => ao(m));
- return Pe(p[0]) ? (p.forEach((m) => e.insertBefore(m)), s.forEach((m) => m.remove()), Ug(p, a, c, n, r), !0) : (i?.warn("[MarkerEdit] Chapter Tier 2 aborted: serialized output is not a chapter"), !1);
+ const p = f.root.children.map((y) => po(y));
+ return Ee(p[0]) ? (p.forEach((y) => e.insertBefore(y)), s.forEach((y) => y.remove()), dm(p, a, c, n, r), !0) : (i?.warn("[MarkerEdit] Chapter Tier 2 aborted: serialized output is not a chapter"), !1);
}
-function as(e) {
+function fs(e) {
let t, r;
for (let n = e; n; n = n.getParent()) {
- if (De(n)) return;
- !t && (j(n) || ae(n) || Pe(n)) && (t = n), Ry(n.getParent()) && (r = n);
+ if (Ue(n)) return;
+ !t && (z(n) || ce(n) || Ee(n)) && (t = n), Zy(n.getParent()) && (r = n);
}
- return t && !t.is(r) ? t : (r ? iE(r) : void 0) ?? t;
+ return t && !t.is(r) ? t : (r ? uA(r) : void 0) ?? t;
}
function Vt(e, t) {
- const r = as(e);
- return r ? j(r) ? nE(r, t) : Pe(r) ? sE(r, t) : Fg([r], t) : !1;
+ const r = fs(e);
+ return r ? z(r) ? lA(r, t) : Ee(r) ? dA(r, t) : fm([r], t) : !1;
}
-const oE = /* @__PURE__ */ new Set(["type", "marker", "content", "closed"]);
-function jd(e, t) {
+const fA = /* @__PURE__ */ new Set(["type", "marker", "content", "closed"]);
+function of(e, t) {
const r = Object.entries(e).filter(
- (n) => typeof n[1] == "string" && !oE.has(n[0])
+ (n) => typeof n[1] == "string" && !fA.has(n[0])
);
if (r.length !== 0) {
t.push("|");
for (const [n, i] of r) t.push(`${n}="${i}"`);
}
}
-function qs(e, t) {
+function Ds(e, t) {
if (e)
for (const r of e) {
if (typeof r == "string") {
@@ -22802,72 +23463,72 @@ function qs(e, t) {
t.push(`\\${n} ${r.number ?? ""}`), r.altnumber !== void 0 && t.push(`\\ca ${r.altnumber}\\ca*`), r.pubnumber !== void 0 && t.push(`\\cp ${r.pubnumber}`);
break;
case "ms":
- t.push(`\\${n}`), jd(r, t), t.push("\\*");
+ t.push(`\\${n}`), of(r, t), t.push("\\*");
break;
case "unmatched":
t.push(`\\${n}`);
break;
case "char":
- t.push(`\\${n} `), qs(r.content, t), jd(r, t), i !== "false" && t.push(`\\${n}*`);
+ t.push(`\\${n} `), Ds(r.content, t), of(r, t), i !== "false" && t.push(`\\${n}*`);
break;
case "note": {
const s = r.caller, o = r.category;
- t.push(`\\${n} ${s ?? ""}`), o !== void 0 && t.push(`\\cat ${o}\\cat*`), qs(r.content, t), i !== "false" && t.push(`\\${n}*`);
+ t.push(`\\${n} ${s ?? ""}`), o !== void 0 && t.push(`\\cat ${o}\\cat*`), Ds(r.content, t), i !== "false" && t.push(`\\${n}*`);
break;
}
default:
- t.push(`\\${n} `), qs(r.content, t);
+ t.push(`\\${n} `), Ds(r.content, t);
}
}
}
-function Bd(e, t, r) {
- const n = as(e);
- if (!ae(n)) return !1;
+function af(e, t, r) {
+ const n = fs(e);
+ if (!ce(n)) return !1;
const i = w();
if (!N(i) || !i.isCollapsed()) return !1;
let s = !1;
- for (let u = i.anchor.getNode(); u; u = u.getParent())
- if (n.is(u)) {
+ for (let d = i.anchor.getNode(); d; d = d.getParent())
+ if (n.is(d)) {
s = !0;
break;
}
if (!s) return !1;
- const o = Nl(n, t, r);
+ const o = Wl(n, t, r);
if (!o) return !1;
- const a = Mr(o.text, { getMarker: t });
+ const a = Or(o.text, { getMarker: t });
if (a.length === 0) return !1;
const c = /* @__PURE__ */ new Map();
- for (const u of o.text)
- os.test(u) || c.set(u, (c.get(u) ?? 0) + 1);
+ for (const d of o.text)
+ ds.test(d) || c.set(d, (c.get(d) ?? 0) + 1);
const l = [];
- qs(a, l);
- for (const u of l.join("").replaceAll(I, "~")) {
- if (os.test(u)) continue;
- const d = c.get(u);
- d !== void 0 && d > 0 && c.set(u, d - 1);
+ Ds(a, l);
+ for (const d of l.join("").replaceAll(R, "~")) {
+ if (ds.test(d)) continue;
+ const u = c.get(d);
+ u !== void 0 && u > 0 && c.set(d, u - 1);
}
- for (const u of c.values()) if (u > 0) return !0;
+ for (const d of c.values()) if (d > 0) return !0;
return !1;
}
-function ql(e, t) {
- return Wg(e, t, b.Paragraph);
+function Jl(e, t) {
+ return bm(e, t, k.Paragraph);
}
-function aE(e, t) {
- return Wg(e, t, b.Character);
+function pA(e, t) {
+ return bm(e, t, k.Character);
}
-function Wg(e, t, r) {
+function bm(e, t, r) {
const n = e.replace(/^\+/, "");
if (n === "v" || n === "c") return !1;
const i = t(n)?.type;
- return i !== void 0 && i !== b.Unknown ? i === r : !(Ee.isValidMarker(n) || Ac(n));
+ return i !== void 0 && i !== k.Unknown ? i === r : !(Ae.isValidMarker(n) || Ic(n));
}
-function cE(e) {
- return [ot(e), mo()];
+function hA(e) {
+ return [ct(e), To()];
}
-function Rl(e) {
- Qt(e, 2);
+function Yl(e) {
+ Xt(e, 2);
}
-function lE(e) {
+function gA(e) {
const t = w();
if (!N(t) || !t.isCollapsed()) return !1;
const { anchor: r } = t;
@@ -22875,38 +23536,38 @@ function lE(e) {
const n = e.getFirstChild();
return n !== null && r.key === n.getKey() && r.offset === 0;
}
-function $l(e) {
- const t = lE(e);
- e.splice(0, 0, cE(e.getMarker())), t && Rl(e);
+function Xl(e) {
+ const t = gA(e);
+ e.splice(0, 0, hA(e.getMarker())), t && Yl(e);
}
-function no(e, t) {
- e.setMarker(t), $l(e), Rl(e);
+function lo(e, t) {
+ e.setMarker(t), Xl(e), Yl(e);
}
-function uE(e, t) {
+function mA(e, t) {
const r = e.getFirstChild();
if (!r) return;
const n = r.getNextSibling();
- if (!Nn(n)) {
- if (v(n) && !O(n) && /^[ \u00A0]$/.test(n.getTextContent())) {
+ if (!Rn(n)) {
+ if (M(n) && !O(n) && /^[ \u00A0]$/.test(n.getTextContent())) {
if (t.collapsedDeleteCaretParas?.has(e.getKey())) {
t.pendingKeys.add(e.getKey());
return;
}
- n.setTextContent(I), bt(n, oe, fr), n.setMode("token");
+ n.setTextContent(R), kt(n, oe, fr), n.setMode("token");
return;
}
- if (Pp(e)) {
+ if (Vp(e)) {
t.pendingKeys.add(e.getKey());
return;
}
- r.insertAfter(mo());
+ r.insertAfter(To());
}
}
-function Vd(e, t, r) {
+function cf(e, t, r) {
const n = e.getNode();
if (n.is(t))
return r === "start" ? e.offset === 0 : e.offset === t.getChildrenSize();
- const i = e.type === "text" ? n.getTextContentSize() : F(n) ? n.getChildrenSize() : 0;
+ const i = e.type === "text" ? n.getTextContentSize() : L(n) ? n.getChildrenSize() : 0;
if (r === "start" ? e.offset !== 0 : e.offset !== i) return !1;
for (let s = n; !s.is(t); ) {
if (r === "start" ? s.getPreviousSibling() : s.getNextSibling()) return !1;
@@ -22916,110 +23577,110 @@ function Vd(e, t, r) {
}
return !0;
}
-function ji(e) {
+function Gi(e) {
for (let t = e; t; t = t.getParent())
- if (ae(t)) return t;
+ if (ce(t)) return t;
}
-function dE(e) {
+function yA(e) {
const t = e.isBackward(), r = t ? e.focus : e.anchor, n = t ? e.anchor : e.focus, i = /* @__PURE__ */ new Set();
for (const s of e.getNodes()) {
- const o = ji(s);
+ const o = Gi(s);
o && i.add(o);
}
return [...i].filter((s) => {
- const o = ji(r.getNode())?.is(s) ?? !1, a = ji(n.getNode())?.is(s) ?? !1;
- return !(o && !Vd(r, s, "start") || a && !Vd(n, s, "end"));
+ const o = Gi(r.getNode())?.is(s) ?? !1, a = Gi(n.getNode())?.is(s) ?? !1;
+ return !(o && !cf(r, s, "start") || a && !cf(n, s, "end"));
});
}
-function oc(e) {
+function bc(e) {
const t = e.wholeParaDeleteExpected;
if (!t) return;
const r = w();
if (!(!N(r) || r.isCollapsed()))
- for (const n of dE(r)) t.add(n.getKey());
+ for (const n of yA(r)) t.add(n.getKey());
}
-function fE(e) {
+function bA(e) {
const t = e.collapsedDeleteCaretParas;
if (!t) return;
const r = w();
if (!N(r) || !r.isCollapsed()) return;
- const n = ji(r.focus.getNode());
+ const n = Gi(r.focus.getNode());
n && t.add(n.getKey());
}
-function pE(e) {
+function kA(e) {
const t = w();
- !N(t) || t.isCollapsed() || t.getNodes().some((r) => O(r)) && (oc(e), t.removeText());
+ !N(t) || t.isCollapsed() || t.getNodes().some((r) => O(r)) && (bc(e), t.removeText());
}
-const hE = new RegExp(
- String.raw`^\\\+?([${er}]+)(?:[ \u00A0]|$)`
+const TA = new RegExp(
+ String.raw`^\\\+?([${Zt}]+)(?:[ \u00A0]|$)`
);
-function gE(e, t) {
- const r = hE.exec(e.getTextContent());
- return !!r && ql(r[1], t);
-}
-function mE(e, t) {
- if (!gi(t.viewOptions)) return;
- if (Ft(e.getFirstChild())) {
- uE(e, t);
+function xA(e, t) {
+ const r = TA.exec(e.getTextContent());
+ return !!r && Jl(r[1], t);
+}
+function _A(e, t) {
+ if (!ki(t.viewOptions)) return;
+ if (zt(e.getFirstChild())) {
+ mA(e, t);
return;
}
if (t.splitExpected.current) {
- if (gE(e, t.getMarker)) return;
- $l(e), t.logger?.debug(`[MarkerEdit] injected prefix for split para "${e.getMarker()}"`);
+ if (xA(e, t.getMarker)) return;
+ Xl(e), t.logger?.debug(`[MarkerEdit] injected prefix for split para "${e.getMarker()}"`);
return;
}
if (e.isEmpty()) {
const n = t.wholeParaDeleteExpected?.has(e.getKey()) ?? !1, i = t.collapsedDeleteCaretParas?.has(e.getKey()) ?? !1;
if (!n && !i) return;
- if (t.wholeParaDeleteExpected?.delete(e.getKey()), t.collapsedDeleteCaretParas?.delete(e.getKey()), !e.getParent()?.getChildren().some((o) => ae(o) && !o.is(e))) {
- no(e, cr), t.logger?.debug("[MarkerEdit] whole-para delete of the last para: reset to \\p");
+ if (t.wholeParaDeleteExpected?.delete(e.getKey()), t.collapsedDeleteCaretParas?.delete(e.getKey()), !e.getParent()?.getChildren().some((o) => ce(o) && !o.is(e))) {
+ lo(e, cr), t.logger?.debug("[MarkerEdit] whole-para delete of the last para: reset to \\p");
return;
}
e.remove(), t.logger?.debug("[MarkerEdit] removed para whose whole representation was deleted");
return;
}
const r = e.getPreviousSibling();
- if (ae(r)) {
- const n = e.getChildren().filter((a) => !Nn(a)), i = w();
+ if (ce(r)) {
+ const n = e.getChildren().filter((a) => !Rn(a)), i = w();
let s = !1;
if (N(i) && i.isCollapsed()) {
const a = i.anchor.getNode();
- a.is(e) ? s = !0 : ji(a)?.is(e) && (s = !n.some(
- (c) => a.is(c) || F(c) && a.getParents().some((l) => l.is(c))
+ a.is(e) ? s = !0 : Gi(a)?.is(e) && (s = !n.some(
+ (c) => a.is(c) || L(c) && a.getParents().some((l) => l.is(c))
));
}
const o = r.getChildrenSize();
- r.append(...n), e.remove(), s && Qt(r, o), t.logger?.debug("[MarkerEdit] merged marker-deleted para into previous");
+ r.append(...n), e.remove(), s && Xt(r, o), t.logger?.debug("[MarkerEdit] merged marker-deleted para into previous");
return;
}
- no(e, cr);
+ lo(e, cr);
}
-function yE(e) {
+function CA(e) {
const t = e.getUnknownAttributes();
if (!t) return;
- const r = ar(t, po(e.getMarker()));
+ const r = ar(t, yo(e.getMarker()));
return r === "" ? void 0 : r;
}
-function bE(e) {
+function vA(e) {
const t = e.getChildren().filter((s) => !O(s) && ne(s, oe) !== "attribute"), r = t[0];
- r && v(r) && r.getTextContent().startsWith(I) && r.setTextContent(r.getTextContent().slice(1));
- const n = yE(e);
- n && t.push(pe(n));
+ r && M(r) && r.getTextContent().startsWith(R) && r.setTextContent(r.getTextContent().slice(1));
+ const n = CA(e);
+ n && t.push(ge(n));
let i = e;
for (const s of t)
i.insertAfter(s), i = s;
e.remove();
}
-function kE(e, t) {
+function SA(e, t) {
const r = e.getChildren(), n = r.some((s) => O(s) && s.getMarkerSyntax() === "opening");
if (e.getIsCollapsed() !== !0) {
if (n) return;
const s = e.getCaller(), o = s !== "" && r.some(
- (c) => v(c) && !O(c) && c.getTextContent() === At(s)
- ), a = ui(e).some(({ node: c }) => O(c));
+ (c) => M(c) && !O(c) && c.getTextContent() === Pt(s)
+ ), a = wn(e).some(({ node: c }) => O(c));
if (!o && !a) return;
r.forEach((c) => {
- O(c) || (v(c) && c.getTextContent() === At(s) && c.setTextContent(` ${s} `), e.insertBefore(c));
+ O(c) || (M(c) && c.getTextContent() === Pt(s) && c.setTextContent(` ${s} `), e.insertBefore(c));
}), e.remove(), t.logger?.debug(
"[MarkerEdit] unwrapped expanded note whose opening glyph was deleted (content preserved)"
);
@@ -23028,93 +23689,93 @@ function kE(e, t) {
const i = r.some((s) => O(s) && s.getMarkerSyntax() === "closing");
n !== i && (e.remove(), t.logger?.debug("[MarkerEdit] removed collapsed note with damaged glyph pair"));
}
-function TE(e, t) {
+function MA(e, t) {
if (e.isEmpty()) return;
const r = e.getFirstChild();
if (!(O(r) && r.getMarkerSyntax() === "opening")) {
- bE(e), t.logger?.debug(`[MarkerEdit] unwrapped char span "${e.getMarker()}"`);
+ vA(e), t.logger?.debug(`[MarkerEdit] unwrapped char span "${e.getMarker()}"`);
return;
}
const i = e.getUnknownAttributes()?.closed !== "false", s = e.getChildren().some((o) => O(o) && o.getMarkerSyntax() === "closing");
i && !s && Vt(e, t);
}
-function Hg(e, t, r) {
- if (!O(e.getFirstChild()) && r?.markerMode === "editable" && gi(r)) {
- no(e, t);
+function km(e, t, r) {
+ if (!O(e.getFirstChild()) && r?.markerMode === "editable" && ki(r)) {
+ lo(e, t);
return;
}
- Ph(e, t);
+ Bh(e, t);
}
-function Gg() {
+function Tm() {
const e = w();
if (!N(e)) return "declined";
if (!e.isCollapsed()) {
- const t = Jg(e);
- return t !== "removed" ? t : (ac(), "handled");
+ const t = xm(e);
+ return t !== "removed" ? t : (kc(), "handled");
}
- return ac() ? "handled" : "declined";
+ return kc() ? "handled" : "declined";
}
-function xE(e, t) {
+function EA(e, t) {
if (!t) return e;
- const r = JM.exec(e);
+ const r = lE.exec(e);
if (!r) return e;
const n = r[1];
- return n === "c" || n === "v" || t(n)?.type !== b.Paragraph ? e : e.slice(r[0].length);
+ return n === "c" || n === "v" || t(n)?.type !== k.Paragraph ? e : e.slice(r[0].length);
}
-function Wd(e, t) {
+function lf(e, t) {
const r = w();
if (!N(r)) return "declined";
if (r.isCollapsed()) {
- if (!Yg())
+ if (!_m())
return "declined";
} else {
- const s = Jg(r);
+ const s = xm(r);
if (s !== "removed") return s;
}
const [n, ...i] = e.map(
- (s) => xE(s, t)
+ (s) => EA(s, t)
);
- Hd(n ?? "");
+ uf(n ?? "");
for (const s of i)
- ac(), Hd(s);
+ kc(), uf(s);
return "handled";
}
-function _E(e) {
+function AA(e) {
const t = e.getRootElement(), r = t?.ownerDocument.getSelection(), n = r?.anchorNode;
if (!t || !r || !n || !t.contains(n)) return !1;
- const i = li(n);
+ const i = hi(n);
if (!i) return !1;
- const s = Xt(i);
- if (!s || s.getIsCollapsed() !== !1 || s.is(i) || !v(i) || O(i)) return !1;
+ const s = Yt(i);
+ if (!s || s.getIsCollapsed() !== !1 || s.is(i) || !M(i) || O(i)) return !1;
const o = Math.min(r.anchorOffset, i.getTextContentSize());
return i.select(o, o), !0;
}
-function Jg(e) {
- const t = Xt(e.anchor.getNode()), r = Xt(e.focus.getNode()), n = t?.getIsCollapsed() === !1, i = r?.getIsCollapsed() === !1;
- return !n && !i ? "declined" : (e.removeText(), CE() ? "removed" : "needs-plain-split");
+function xm(e) {
+ const t = Yt(e.anchor.getNode()), r = Yt(e.focus.getNode()), n = t?.getIsCollapsed() === !1, i = r?.getIsCollapsed() === !1;
+ return !n && !i ? "declined" : (e.removeText(), PA() ? "removed" : "needs-plain-split");
}
-function Hd(e) {
+function uf(e) {
if (e === "") return;
const t = w();
N(t) && t.insertText(e);
}
-function CE() {
+function PA() {
const e = w();
if (!N(e) || !e.isCollapsed()) return !1;
- const t = Xt(e.anchor.getNode());
+ const t = Yt(e.anchor.getNode());
return !t || t.getIsCollapsed() !== !1 ? !1 : t.getChildren().some((r) => O(r) && r.getMarkerSyntax() === "opening");
}
-function Yg() {
+function _m() {
const e = w();
if (!N(e) || !e.isCollapsed()) return;
- const t = e.anchor.getNode(), r = Xt(t);
+ const t = e.anchor.getNode(), r = Yt(t);
if (!(!r || r.getIsCollapsed() !== !1 || r.is(t)))
return r;
}
-function ac() {
+function kc() {
const e = w();
if (!N(e) || !e.isCollapsed()) return !1;
- const t = e.anchor.getNode(), r = Yg();
+ const t = e.anchor.getNode(), r = _m();
if (!r) return !1;
let n = t;
for (; !r.is(n.getParent()); ) {
@@ -23122,9 +23783,9 @@ function ac() {
if (!l) return !1;
n = l;
}
- const i = xr("fp", { closed: "false" });
- i.append(ot("fp"));
- const s = v(t) && !O(t) ? t : void 0, o = e.anchor.offset, a = s?.getTextContentSize() ?? 0, c = s !== void 0 && s.is(n);
+ const i = Mr("fp", { closed: "false" });
+ i.append(ct("fp"));
+ const s = M(t) && !O(t) ? t : void 0, o = e.anchor.offset, a = s?.getTextContentSize() ?? 0, c = s !== void 0 && s.is(n);
if (s && !c) {
if (o <= 0) s.insertBefore(i);
else if (o >= a) s.insertAfter(i);
@@ -23132,73 +23793,73 @@ function ac() {
const [, l] = s.splitText(o);
l.insertBefore(i);
}
- ni(i, { renderGlyphs: !0 });
+ ci(i, { renderGlyphs: !0 });
} else {
const l = s && o < a ? [o === 0 ? s : s.splitText(o)[1]] : [];
n.insertAfter(i);
- const [u] = l;
- u && (Ak(u), i.append(u));
+ const [d] = l;
+ d && (Hk(d), i.append(d));
}
- return i.getChildren().every(O) && i.append(pe(Dt)), Xg(i), !0;
+ return i.getChildren().every(O) && i.append(ge(Ut)), Cm(i), !0;
}
-function Xg(e) {
+function Cm(e) {
const t = e.getChildren().find((r) => !O(r));
- if (v(t)) {
- const r = t.getTextContent().startsWith(I) ? 1 : 0;
+ if (M(t)) {
+ const r = t.getTextContent().startsWith(R) ? 1 : 0;
t.select(r, r);
return;
}
- if (F(t)) {
- Xg(t);
+ if (L(t)) {
+ Cm(t);
return;
}
e.selectEnd();
}
-function SE(e) {
+function NA(e) {
const t = [];
let r = e;
for (; r; )
- D(r) && t.push(r.getMarker()), r = r.getParent();
+ I(r) && t.push(r.getMarker()), r = r.getParent();
return t;
}
-function vE(e) {
+function OA(e) {
const t = e.getTopLevelElement(), r = [];
- for (const n of Fe().getChildren()) {
+ for (const n of ze().getChildren()) {
if (t && n.is(t)) break;
- (pt(n) || Ge(n) || ae(n)) && r.push(n.getMarker());
+ (pt(n) || Ge(n) || ce(n)) && r.push(n.getMarker());
}
return r;
}
-function ME(e) {
+function wA(e) {
let t = e;
- for (; F(t); ) {
+ for (; L(t); ) {
const r = t.getFirstChild();
if (!r) break;
t = r;
}
return t;
}
-function EE(e, t, r) {
+function qA(e, t, r) {
const n = e.getFirstChild();
if (!n) return !1;
let i = n;
- if (Ft(n)) {
+ if (zt(n)) {
if (t.is(n)) return !0;
- if (i = n.getNextSibling(), i && Nn(i)) {
+ if (i = n.getNextSibling(), i && Rn(i)) {
if (t.is(i)) return !0;
i = i.getNextSibling();
}
}
- return i ? t.is(ME(i)) && r === 0 : !1;
+ return i ? t.is(wA(i)) && r === 0 : !1;
}
-function AE(e, t, r) {
+function RA(e, t, r) {
const n = e.getFirstChild();
- if (!n || !Ft(n)) return !1;
+ if (!n || !zt(n)) return !1;
if (t.is(n)) return !0;
const i = n.getNextSibling();
- return i !== null && Nn(i) && t.is(i) && r === 0;
+ return i !== null && Rn(i) && t.is(i) && r === 0;
}
-function PE() {
+function $A() {
if (typeof window > "u" || typeof window.getSelection != "function") return;
const e = window.getSelection();
if (!e || e.rangeCount === 0) return;
@@ -23207,67 +23868,67 @@ function PE() {
const { x: r, y: n, width: i, height: s } = t.getBoundingClientRect();
return { x: r, y: n, width: i, height: s };
}
-function NE() {
+function LA() {
const e = w();
if (!N(e)) return;
- const t = e.focus.getNode(), r = e.focus.offset, n = !e.isCollapsed(), i = nt(t, ae), s = !n && (!i || AE(i, t, r)) ? "paragraph" : "character", o = Xt(t);
+ const t = e.focus.getNode(), r = e.focus.offset, n = !e.isCollapsed(), i = Xe(t, ce), s = !n && (!i || RA(i, t, r)) ? "paragraph" : "character", o = Yt(t);
return {
source: s,
paraMarker: i?.getMarker(),
- previousParaMarkers: vE(t),
- openCharMarkers: SE(t),
+ previousParaMarkers: OA(t),
+ openCharMarkers: NA(t),
noteMarker: o?.getMarker(),
hasTextSelection: n,
// The trailing edge of a canonical closing glyph counts as AFTER the marker, not inside it
// (see $isPointInMarkerGlyphText) — Enter there opens the paragraph menu exactly as at the
// end of a plain-text paragraph.
- inMarkerText: Hc(t, r),
- anchorRect: PE()
+ inMarkerText: nl(t, r),
+ anchorRect: $A()
};
}
-function OE() {
+function IA() {
const e = w();
if (!N(e) || !e.isCollapsed()) return;
const t = e.anchor.getNode();
- if (!v(t) || O(t)) return;
- const r = e.anchor.offset, n = t.getTextContent().slice(0, r), i = YM.exec(n);
+ if (!M(t) || O(t)) return;
+ const r = e.anchor.offset, n = t.getTextContent().slice(0, r), i = uE.exec(n);
i && t.spliceText(r - i[0].length, i[0].length, "", !0);
}
-function wE(e, t, r) {
- Hg(e, t, r), Rl(e);
+function DA(e, t, r) {
+ km(e, t, r), Yl(e);
}
-function qE(e, t, r) {
+function UA(e, t, r) {
const n = w();
if (!N(n)) return;
- const i = n.focus.getNode(), s = nt(i, ae);
- if (t === "backslash" && s && EE(s, i, n.focus.offset)) {
- wE(s, e, r);
+ const i = n.focus.getNode(), s = Xe(i, ce);
+ if (t === "backslash" && s && qA(s, i, n.focus.offset)) {
+ DA(s, e, r);
return;
}
- Zg(e, r);
+ Sm(e, r);
}
-function RE(e, t) {
+function FA(e, t) {
const r = w();
return !N(r) || !r.isCollapsed() ? !1 : (r.insertText(`\\${e}${t?.trailingSpace === !1 ? "" : " "}`), !0);
}
-function Qg(e) {
+function vm(e) {
const t = w();
return N(t) ? (t.insertText(`\\${e}*`), !0) : !1;
}
-function $E(e, t, r, n) {
+function zA(e, t, r, n) {
if (N(w()) || n.logger?.warn(
"$applyMarkerMenuSelection: no range selection — cleanup/insert will no-op (editor blurred?)"
- ), t.literalPrefixLanded && OE(), e.kind === "closeTag") {
- Qg(e.marker.replace(/\*$/, ""));
+ ), t.literalPrefixLanded && IA(), e.kind === "closeTag") {
+ vm(e.marker.replace(/\*$/, ""));
return;
}
- if (e.marker === "fp" && Gg() !== "declined") return;
- if (e.kind === "paragraph" && Ze.isValidMarker(e.marker, n.nodeOptions?.extraValidMarkers)) {
- qE(e.marker, t.trigger, n.viewOptions);
+ if (e.marker === "fp" && Tm() !== "declined") return;
+ if (e.kind === "paragraph" && rt.isValidMarker(e.marker, n.nodeOptions?.extraValidMarkers)) {
+ UA(e.marker, t.trigger, n.viewOptions);
return;
}
- if (Ee.isValidMarker(e.marker, n.nodeOptions?.extraValidMarkers))
- return hg(
+ if (Ae.isValidMarker(e.marker, n.nodeOptions?.extraValidMarkers))
+ return Pg(
e.marker,
r,
n.expandedNoteKeyRef,
@@ -23275,7 +23936,7 @@ function $E(e, t, r, n) {
n.nodeOptions,
n.logger
);
- ec(
+ sc(
e.marker,
n.expandedNoteKeyRef,
n.viewOptions,
@@ -23283,120 +23944,120 @@ function $E(e, t, r, n) {
n.logger,
void 0,
n.styleInfo
- ).action({ editor: Zn(), reference: r });
+ ).action({ editor: ri(), reference: r });
}
-function Zg(e, t) {
+function Sm(e, t) {
const r = w();
if (!N(r)) return;
- const n = gi(t);
- if (fg()) {
+ const n = ki(t);
+ if (Eg()) {
const s = w();
if (!N(s)) return;
- const o = nt(s.anchor.getNode(), ae);
+ const o = Xe(s.anchor.getNode(), ce);
if (!o) return;
- o.setMarker(e), n && $l(o);
+ o.setMarker(e), n && Xl(o);
return;
}
const i = r.insertParagraph();
- ae(i) && (n ? no(i, e) : i.setMarker(e));
+ ce(i) && (n ? lo(i, e) : i.setMarker(e));
}
-function IE() {
- const [e] = ce();
- return z(() => e.registerCommand(Sf, () => !0, Tt), [e]), null;
+function KA() {
+ const [e] = ae();
+ return F(() => e.registerCommand(Ff, () => !0, _t), [e]), null;
}
-function LE(e, t) {
+function jA(e, t) {
if (!e.startsWith("\\")) return !0;
- const r = VM.exec(e)?.[1];
- return r === void 0 ? !1 : !ql(r, t);
+ const r = sE.exec(e)?.[1];
+ return r === void 0 ? !1 : !Jl(r, t);
}
-function em(e, t) {
- if (e.getMarkerSyntax() !== "opening" || !LE(e.getTextContent(), t)) return;
+function Mm(e, t) {
+ if (e.getMarkerSyntax() !== "opening" || !jA(e.getTextContent(), t)) return;
const r = e.getParent();
- if (!ae(r)) return;
+ if (!ce(r)) return;
const n = t(r.getMarker())?.type;
- if (n !== void 0 && n !== b.Unknown || r.getFirstChild()?.is(e) !== !0) return;
+ if (n !== void 0 && n !== k.Unknown || r.getFirstChild()?.is(e) !== !0) return;
const i = r.getPreviousSibling();
- if (ae(i))
+ if (ce(i))
return [i, r];
}
-function tm(e, t) {
- const r = em(e, t.getMarker);
- return r !== void 0 && Fg(r, t);
+function Em(e, t) {
+ const r = Mm(e, t.getMarker);
+ return r !== void 0 && fm(r, t);
}
-function DE(e, t) {
+function BA(e, t) {
const r = w();
N(r) && [r.anchor, r.focus].forEach((n) => {
n.key === e.getKey() && n.offset > t && n.set(e.getKey(), t, "text");
});
}
-function rm(e) {
- const t = WM.exec(e);
+function Am(e) {
+ const t = oE.exec(e);
if (!t) return;
const r = 1 + t[1].length;
return { start: r, end: r + t[2].length };
}
-function UE(e) {
+function VA(e) {
const t = w();
if (!N(t) || !t.isCollapsed() || t.anchor.key !== e.getKey()) return;
- const r = rm(e.getTextContent());
+ const r = Am(e.getTextContent());
if (!r) return;
const { offset: n } = t.anchor;
if (!(n < r.start || n > r.end))
return n - r.start;
}
-function FE(e) {
+function WA(e) {
const t = w();
if (!N(t) || !t.isCollapsed() || t.anchor.key !== e.getKey()) return;
const r = e.getNextSibling();
- if (j(e.getParent()) && v(r)) {
+ if (z(e.getParent()) && M(r)) {
const n = r.getNextSibling();
- if (D(n)) {
- Vc(n);
+ if (I(n)) {
+ el(n);
return;
}
}
- v(r) ? r.select(1, 1) : e.select(e.getTextContentSize(), e.getTextContentSize());
+ M(r) ? r.select(1, 1) : e.select(e.getTextContentSize(), e.getTextContentSize());
}
-function Gd(e, t) {
+function df(e, t) {
if (t !== void 0) {
- const r = e.getLatest(), n = rm(r.getTextContent());
+ const r = e.getLatest(), n = Am(r.getTextContent());
if (n) {
const i = Math.min(n.start + t, n.end);
r.select(i, i);
return;
}
}
- FE(e);
+ WA(e);
}
-function Jd(e, t) {
+function ff(e, t) {
return e.replace(/^\+/, "") !== t.replace(/^\+/, "");
}
-function nm(e, t, r) {
+function Pm(e, t, r) {
if (t.startsWith("+") !== e.getNested())
return Vt(e, r);
- const n = UE(e), i = e.getParent();
- if (ae(i)) {
- if (!ql(t, r.getMarker))
- return tm(e, r) ? (r.logger?.debug(
+ const n = VA(e), i = e.getParent();
+ if (ce(i)) {
+ if (!Jl(t, r.getMarker))
+ return Em(e, r) ? (r.logger?.debug(
`[MarkerEdit] unknown-split paragraph rejoined its predecessor on rename to "${t}"`
), !0) : Vt(e, r);
const s = e.getMarker();
- return i.setMarker(t), e.setMarker(t), Jd(s, t) && Gd(e, n), r.logger?.debug(`[MarkerEdit] para marker renamed to "${t}"`), !0;
+ return i.setMarker(t), e.setMarker(t), ff(s, t) && df(e, n), r.logger?.debug(`[MarkerEdit] para marker renamed to "${t}"`), !0;
}
- if (D(i) || j(i)) {
+ if (I(i) || z(i)) {
const s = t.replace(/^\+/, "");
- if (!(D(i) ? aE(t, r.getMarker) : Ee.isValidMarker(s)))
+ if (!(I(i) ? pA(t, r.getMarker) : Ae.isValidMarker(s)))
return Vt(e, r);
const a = e.getMarker();
if (i.getMarker() !== a)
return Vt(e, r);
i.setMarker(s);
const c = i.getChildren().filter(O).filter((l) => l.getMarkerSyntax() === "closing" && l.getMarker() === a).at(-1);
- return c && (DE(c, rt(s, c.getNested()).length), c.setMarker(s)), e.setMarker(s), Jd(a, s) && Gd(e, n), r.logger?.debug(`[MarkerEdit] ${i.getType()} marker renamed to "${s}"`), !0;
+ return c && (BA(c, st(s, c.getNested()).length), c.setMarker(s)), e.setMarker(s), ff(a, s) && df(e, n), r.logger?.debug(`[MarkerEdit] ${i.getType()} marker renamed to "${s}"`), !0;
}
return Vt(e, r);
}
-function zE(e) {
+function HA(e) {
const t = w();
if (!N(t)) return !1;
const r = t.isCollapsed() ? [t.anchor] : [t.anchor, t.focus];
@@ -23416,29 +24077,29 @@ function zE(e) {
}
return !!(!t.isCollapsed() && t.getNodes().some((n) => n.is(e)));
}
-function KE(e, t) {
+function GA(e, t) {
const r = e.getTextContent();
- if (Xr(e)) {
+ if (en(e)) {
t.pendingKeys.delete(e.getKey());
return;
}
- if (Le(e.getParent()) && Uc(e)) {
+ if (De(e.getParent()) && Gc(e)) {
t.pendingKeys.delete(e.getKey());
return;
}
- if (!t.pendingKeys.has(e.getKey()) && !zE(e)) {
- $k(e), t.logger?.debug(
+ if (!t.pendingKeys.has(e.getKey()) && !HA(e)) {
+ eT(e), t.logger?.debug(
`[MarkerEdit] healed machine-drifted glyph bytes back to "${e.getTextContent()}"`
);
return;
}
if (e.getMarkerSyntax() === "opening") {
- const n = jM.exec(r);
+ const n = nE.exec(r);
if (n) {
- t.pendingKeys.delete(e.getKey()), nm(e, n[1], t);
+ t.pendingKeys.delete(e.getKey()), Pm(e, n[1], t);
return;
}
- if (BM.test(r)) {
+ if (iE.test(r)) {
t.pendingKeys.delete(e.getKey()), Vt(e, t);
return;
}
@@ -23446,28 +24107,28 @@ function KE(e, t) {
return;
}
if (e.getMarkerSyntax() === "closing") {
- const n = e.getParent(), i = rt(e.getMarker(), e.getNested());
- if (D(n) && e.getMarker() === n.getMarker() && n.getLastChild()?.is(e) && r.startsWith(i) && r.length > i.length) {
- const s = w(), o = N(s) && s.isCollapsed() && s.anchor.key === e.getKey() && s.anchor.offset > i.length ? s.anchor.offset - i.length : void 0, a = pe(r.slice(i.length));
+ const n = e.getParent(), i = st(e.getMarker(), e.getNested());
+ if (I(n) && e.getMarker() === n.getMarker() && n.getLastChild()?.is(e) && r.startsWith(i) && r.length > i.length) {
+ const s = w(), o = N(s) && s.isCollapsed() && s.anchor.key === e.getKey() && s.anchor.offset > i.length ? s.anchor.offset - i.length : void 0, a = ge(r.slice(i.length));
e.setTextContent(i), n.insertAfter(a), o !== void 0 && a.select(o, o), t.pendingKeys.delete(e.getKey());
return;
}
}
t.pendingKeys.add(e.getKey());
}
-function jE(e, t) {
+function JA(e, t) {
if (e.getTextContent() === "") {
t.pendingKeys.delete(e.getKey()), e.remove();
return;
}
- if (Jp(e)) {
+ if (dh(e)) {
t.pendingKeys.delete(e.getKey());
return;
}
t.pendingKeys.add(e.getKey());
}
-function im(e) {
- if (!If(e)?.length)
+function Nm(e) {
+ if (!Qf(e)?.length)
throw new Error(`marker "${e}" declares no leading attributes in the markers map`);
return {
// `\m`, separator, value word, then either nothing-yet (unterminated), or a
@@ -23493,18 +24154,18 @@ function im(e) {
valueTerminated: new RegExp(`^\\\\${e}[ ]+([^ \\\\]+)[ ]+$`)
};
}
-const wi = im("v"), BE = im("c"), Yd = /^[ \u00A0]*$/;
-function Xd(e, t, r) {
+const Ii = Nm("v"), YA = Nm("c"), pf = /^[ \u00A0]*$/;
+function hf(e, t, r) {
const n = e.getNextSibling();
- if (v(n) && n.getType() === Ke.getType() && n.getMode() === "normal" && ne(n, oe) !== "attribute") {
+ if (M(n) && n.getType() === je.getType() && n.getMode() === "normal" && ne(n, oe) !== "attribute") {
n.setTextContent(t + n.getTextContent()), r !== void 0 && n.select(r, r);
return;
}
- const i = pe(t);
+ const i = ge(t);
e.insertAfter(i), r !== void 0 && i.select(r, r);
}
-function VE(e, t) {
- const r = e.getTextContent(), n = Lt("v", e.getNumber());
+function XA(e, t) {
+ const r = e.getTextContent(), n = Dt("v", e.getNumber());
if (r === n) {
t.pendingKeys.delete(e.getKey());
return;
@@ -23512,28 +24173,28 @@ function VE(e, t) {
const i = /^[ \u00A0]+/.exec(r);
if (i) {
const c = r.slice(i[0].length);
- if (wi.midEdit.test(c)) {
+ if (Ii.midEdit.test(c)) {
t.pendingKeys.add(e.getKey());
return;
}
- const l = wi.valueAndRest.exec(c);
- if (l && Yd.test(l[2] ?? "")) {
+ const l = Ii.valueAndRest.exec(c);
+ if (l && pf.test(l[2] ?? "")) {
t.pendingKeys.delete(e.getKey()), l[1] !== e.getNumber() && e.setNumber(l[1]);
return;
}
}
- if (wi.midEdit.test(r)) {
+ if (Ii.midEdit.test(r)) {
t.pendingKeys.add(e.getKey());
return;
}
- const s = wi.valueAndRest.exec(r);
+ const s = Ii.valueAndRest.exec(r);
if (!s) {
- const c = wi.markerRest.exec(r);
+ const c = Ii.markerRest.exec(r);
if (c) {
- const [, l, u, d] = c, f = w(), p = N(f) && f.isCollapsed() && f.anchor.key === e.getKey() ? f.anchor.offset : void 0;
- t.pendingKeys.delete(e.getKey()), e.setNumber(u), e.setTextContent(Lt("v", u));
- const m = p !== void 0 && p >= l.length ? Math.min(p - l.length, d.length) : void 0;
- Xd(e, d, m);
+ const [, l, d, u] = c, f = w(), p = N(f) && f.isCollapsed() && f.anchor.key === e.getKey() ? f.anchor.offset : void 0;
+ t.pendingKeys.delete(e.getKey()), e.setNumber(d), e.setTextContent(Dt("v", d));
+ const y = p !== void 0 && p >= l.length ? Math.min(p - l.length, u.length) : void 0;
+ hf(e, u, y);
return;
}
t.pendingKeys.delete(e.getKey()), Vt(e, t);
@@ -23544,16 +24205,16 @@ function VE(e, t) {
t.pendingKeys.add(e.getKey());
return;
}
- if (t.pendingKeys.delete(e.getKey()), Yd.test(a ?? "")) {
+ if (t.pendingKeys.delete(e.getKey()), pf.test(a ?? "")) {
o !== e.getNumber() && e.setNumber(o);
return;
}
- e.setNumber(o), e.setTextContent(Lt("v", o)), a && Xd(e, a, a.length);
+ e.setNumber(o), e.setTextContent(Dt("v", o)), a && hf(e, a, a.length);
}
-const WE = /^[ \u00A0]+([^ \u00A0\\]+)[ \u00A0]+$/;
-function HE(e, t) {
+const QA = /^[ \u00A0]+([^ \u00A0\\]+)[ \u00A0]+$/;
+function ZA(e, t) {
const r = e.getParent();
- if (!j(r) || r.getIsCollapsed() !== !1 || !If(r.getMarker())?.includes("caller")) return !1;
+ if (!z(r) || r.getIsCollapsed() !== !1 || !Qf(r.getMarker())?.includes("caller")) return !1;
const n = r.getChildren();
let i = 0;
for (; i < n.length; ) {
@@ -23563,105 +24224,105 @@ function HE(e, t) {
}
if (!e.is(n[i])) return !1;
const s = e.getTextContent();
- if (s === At(r.getCaller()))
+ if (s === Pt(r.getCaller()))
return t.pendingKeys.delete(e.getKey()), !0;
- const o = WE.exec(s);
+ const o = QA.exec(s);
if (!o) return !1;
const [, a] = o;
- return t.pendingKeys.delete(e.getKey()), r.setCaller(a), e.setTextContent(At(a)), !0;
+ return t.pendingKeys.delete(e.getKey()), r.setCaller(a), e.setTextContent(Pt(a)), !0;
}
-function GE(e) {
+function e1(e) {
if (e.getChildrenSize() === 0) {
e.remove();
return;
}
const t = e.getFirstChild();
- if (!v(t)) return;
- const r = Lt("c", e.getNumber()), n = t.getTextContent();
+ if (!M(t)) return;
+ const r = Dt("c", e.getNumber()), n = t.getTextContent();
if (n === r) return;
- const i = /^[ \u00A0]+/.exec(n), s = i ? n.slice(i[0].length) : n, o = BE.valueTerminated.exec(s);
+ const i = /^[ \u00A0]+/.exec(n), s = i ? n.slice(i[0].length) : n, o = YA.valueTerminated.exec(s);
o && o[1] !== e.getNumber() && e.setNumber(o[1]);
}
-function sm(e) {
- if (Be(e)) {
- const { wrapper: t } = ko(e);
+function Om(e) {
+ if (He(e)) {
+ const { wrapper: t } = Co(e);
return t && t.getChildrenSize() === 0 ? [t] : [];
}
- if (j(e)) {
- const { wrapper: t } = $p(e);
+ if (z(e)) {
+ const { wrapper: t } = Xp(e);
return t && t.getChildrenSize() === 0 ? [t] : [];
}
- if (Pe(e)) {
- const t = [], r = Ip(e);
+ if (Ee(e)) {
+ const t = [], r = Qp(e);
r.wrapper && r.wrapper.getChildrenSize() === 0 && t.push(r.wrapper);
- const n = Dp(e);
+ const n = eh(e);
return n.wrapper && n.wrapper.getChildrenSize() === 0 && t.push(n.wrapper), t;
}
- if (Oe(e)) {
- const t = [], r = Yi(e, "va");
+ if (we(e)) {
+ const t = [], r = ts(e, "va");
r.wrapper && r.wrapper.getChildrenSize() === 0 && t.push(r.wrapper);
- const n = r.wrapper ?? r.closer ?? e, i = Yi(n, "vp");
+ const n = r.wrapper ?? r.closer ?? e, i = ts(n, "vp");
return i.wrapper && i.wrapper.getChildrenSize() === 0 && t.push(i.wrapper), t;
}
return [];
}
-function JE(e) {
+function t1(e) {
const t = w();
if (!N(t) || !t.isCollapsed()) return !1;
const r = t.anchor.getNode();
- return sm(e).some((n) => r.is(n));
+ return Om(e).some((n) => r.is(n));
}
-function YE(e, t, r = "departure") {
+function r1(e, t, r = "departure") {
let n = !1;
const i = r === "departure";
- if (i && ae(e) && Pp(e))
+ if (i && ce(e) && Vp(e))
return t.pendingKeys.add(e.getKey()), { handled: !0, mutated: !1 };
- for (const l of Qi)
- if (l.settleScope !== "none" && l.ownerPredicate(e) && gs(l, e) && (i || JE(e)))
+ for (const l of ns)
+ if (l.settleScope !== "none" && l.ownerPredicate(e) && ks(l, e) && (i || t1(e)))
return t.pendingKeys.add(e.getKey()), { handled: !0, mutated: !1 };
- for (const l of sm(e))
+ for (const l of Om(e))
l.remove(), n = !0;
let s = !1;
- if (D(e)) {
- const l = Fk(e);
- l !== void 0 && xb(l) && (zp(e), s = !0, n = !0);
+ if (I(e)) {
+ const l = sT(e);
+ l !== void 0 && Fb(l) && (nh(e), s = !0, n = !0);
}
let o = !1, a = !1, c = !1;
- for (const l of Qi)
+ for (const l of ns)
if (l.settleScope !== "none" && l.ownerPredicate(e)) {
- if (RT(l, e)) {
- es(l, e), a = !0, n = !0;
+ if (ZT(l, e)) {
+ ss(l, e), a = !0, n = !0;
continue;
}
if (l.deletionPolicy === "none") {
o = !0;
continue;
}
- if (l.deletionPolicy === "remove-owner" && ih(l, e))
+ if (l.deletionPolicy === "remove-owner" && Th(l, e))
return e.remove(), { handled: !0, mutated: !0 };
- Co(l, l.scanPieces(e), l.expectedPieces(e)) && (c = !0);
+ Eo(l, l.scanPieces(e), l.expectedPieces(e)) && (c = !0);
}
return (a || s) && !c ? { handled: !0, mutated: n } : { handled: o, mutated: n };
}
-function Qd(e) {
- return v(e) && e.getType() === Ke.getType() && e.getMode() === "normal" && ne(e, oe) !== "attribute";
+function gf(e) {
+ return M(e) && e.getType() === je.getType() && e.getMode() === "normal" && ne(e, oe) !== "attribute";
}
-function XE(e) {
+function n1(e) {
const t = /* @__PURE__ */ new Set();
if (e === void 0) return t;
t.add(e);
const r = se(e);
if (!r?.isAttached()) return t;
- for (let n = r.getPreviousSibling(); n && Qd(n); n = n.getPreviousSibling())
+ for (let n = r.getPreviousSibling(); n && gf(n); n = n.getPreviousSibling())
t.add(n.getKey());
- for (let n = r.getNextSibling(); n && Qd(n); n = n.getNextSibling())
+ for (let n = r.getNextSibling(); n && gf(n); n = n.getNextSibling())
t.add(n.getKey());
return t;
}
-function As(e, t, r = "departure") {
+function qs(e, t, r = "departure") {
let n = !1;
if (e.pendingKeys.size === 0) return n;
- const i = XE(t), s = [...e.pendingKeys].filter((a) => !i.has(a)), o = /* @__PURE__ */ new Set();
+ const i = n1(t), s = [...e.pendingKeys].filter((a) => !i.has(a)), o = /* @__PURE__ */ new Set();
for (const a of s) {
const c = se(a);
if (!c?.isAttached()) {
@@ -23671,82 +24332,82 @@ function As(e, t, r = "departure") {
if (O(c)) {
e.pendingKeys.delete(a);
const p = c.getTextContent();
- if (Xr(c)) continue;
- const m = Ag.exec(p);
- c.getMarkerSyntax() === "opening" && m ? n = nm(c, m[1], e) || n : r === "idle" && Bd(c, e.getMarker, e.viewOptions) ? e.pendingKeys.add(a) : tm(c, e) ? (n = !0, e.logger?.debug(
+ if (en(c)) continue;
+ const y = Kg.exec(p);
+ c.getMarkerSyntax() === "opening" && y ? n = Pm(c, y[1], e) || n : r === "idle" && af(c, e.getMarker, e.viewOptions) ? e.pendingKeys.add(a) : Em(c, e) ? (n = !0, e.logger?.debug(
"[MarkerEdit] unknown-split paragraph rejoined its predecessor on marker degradation"
)) : n = Vt(c, e) || n;
continue;
}
- const l = _n(c)?.owner, u = l?.isAttached() ? l : c, d = u.getKey();
- if (o.has(d)) {
- a !== d && e.pendingKeys.delete(a);
+ const l = Cn(c)?.owner, d = l?.isAttached() ? l : c, u = d.getKey();
+ if (o.has(u)) {
+ a !== u && e.pendingKeys.delete(a);
continue;
}
- if (i.has(d)) {
- e.pendingKeys.delete(a), e.pendingKeys.add(d);
+ if (i.has(u)) {
+ e.pendingKeys.delete(a), e.pendingKeys.add(u);
continue;
}
- e.pendingKeys.delete(a), a !== d && e.pendingKeys.delete(d), o.add(d);
- const f = YE(u, e, r);
+ e.pendingKeys.delete(a), a !== u && e.pendingKeys.delete(u), o.add(u);
+ const f = r1(d, e, r);
if (n = f.mutated || n, !f.handled) {
- if (r === "idle" && Bd(u, e.getMarker, e.viewOptions)) {
- e.pendingKeys.add(d);
+ if (r === "idle" && af(d, e.getMarker, e.viewOptions)) {
+ e.pendingKeys.add(u);
continue;
}
- n = Vt(u, e) || n;
+ n = Vt(d, e) || n;
}
}
return n;
}
-function om(e) {
- if (Qr(e.getNextSibling())) return !0;
+function wm(e) {
+ if (tn(e.getNextSibling())) return !0;
for (let t = e.getParent(); t; t = t.getParent())
- if (D(t)) return Di(t) !== void 0;
+ if (I(t)) return ji(t) !== void 0;
return !1;
}
-function QE(e) {
- const t = _n(e);
+function i1(e) {
+ const t = Cn(e);
if (!t) return !1;
- const r = xn(t.kind);
- return !Co(
+ const r = _n(t.kind);
+ return !Eo(
r,
r.scanPieces(t.owner),
r.expectedPieces(t.owner)
);
}
-function Zd(e) {
+function mf(e) {
for (let t = e.getParent(); t; t = t.getParent())
- if (pt(t) || De(t) || Qp(t)) return !0;
+ if (pt(t) || Ue(t) || hh(t)) return !0;
return !1;
}
-function ZE(e, t) {
+function s1(e, t) {
const r = e.getTextContent(), n = ne(e, oe), i = e.getParent();
- if (n !== "attribute" && Pe(i)) {
- r.replace(/^[ \u00A0]+/, "") === Lt("c", i.getNumber()) ? t.pendingKeys.delete(e.getKey()) : t.pendingKeys.add(e.getKey());
+ if (n !== "attribute" && Ee(i)) {
+ r.replace(/^[ \u00A0]+/, "") === Dt("c", i.getNumber()) ? t.pendingKeys.delete(e.getKey()) : t.pendingKeys.add(e.getKey());
return;
}
- if (HE(e, t)) return;
+ if (ZA(e, t)) return;
if (n === "attribute") {
- QE(e) ? t.pendingKeys.delete(e.getKey()) : t.pendingKeys.add(e.getKey());
+ i1(e) ? t.pendingKeys.delete(e.getKey()) : t.pendingKeys.add(e.getKey());
return;
}
if (!r.includes("\\")) {
- if (r.includes("|") && om(e)) t.pendingKeys.add(e.getKey());
- else if (r.includes("//") && !Zd(e))
+ if (r.includes("|") && wm(e)) t.pendingKeys.add(e.getKey());
+ else if (r.includes("//") && !mf(e))
t.pendingKeys.add(e.getKey());
- else if (Up(e)) t.pendingKeys.add(e.getKey());
- else if (Pe(as(e))) t.pendingKeys.add(e.getKey());
+ else if (th(e)) t.pendingKeys.add(e.getKey());
+ else if (Ee(fs(e))) t.pendingKeys.add(e.getKey());
else {
const a = e.getParent();
- D(a) && Kp(a) && t.pendingKeys.add(a.getKey()), t.pendingKeys.delete(e.getKey());
+ I(a) && ih(a) && t.pendingKeys.add(a.getKey()), t.pendingKeys.delete(e.getKey());
}
return;
}
- if (Zd(e)) return;
+ if (mf(e)) return;
const s = w(), o = N(s) && s.isCollapsed() && s.anchor.key === e.getKey() ? r.slice(0, s.anchor.offset) : r;
- if (HM.test(o)) {
- if (Nb(r)) {
+ if (aE.test(o)) {
+ if (Jb(r)) {
t.pendingKeys.add(e.getKey());
return;
}
@@ -23758,359 +24419,89 @@ function ZE(e, t) {
} else
t.pendingKeys.add(e.getKey());
}
-function eA(e, t) {
- return e.deletionPolicy !== "remove-owner" || e.byteFormat.writer !== "read-only" ? !1 : ih(e, t);
+function o1(e, t) {
+ return e.deletionPolicy !== "remove-owner" || e.byteFormat.writer !== "read-only" ? !1 : Th(e, t);
}
-function tA(e) {
+function a1(e) {
const t = (r) => {
if (O(r)) {
- Xr(r) || e.pendingKeys.add(r.getKey());
+ en(r) || e.pendingKeys.add(r.getKey());
return;
}
- if (Qr(r)) {
- Jp(r) || e.pendingKeys.add(r.getKey());
+ if (tn(r)) {
+ dh(r) || e.pendingKeys.add(r.getKey());
return;
}
- for (const n of Qi)
- n.settleScope !== "none" && n.ownerPredicate(r) && (gs(n, r) || eA(n, r)) && e.pendingKeys.add(r.getKey());
- if (Oe(r)) {
- r.getTextContent() !== Lt("v", r.getNumber()) && e.pendingKeys.add(r.getKey());
+ for (const n of ns)
+ n.settleScope !== "none" && n.ownerPredicate(r) && (ks(n, r) || o1(n, r)) && e.pendingKeys.add(r.getKey());
+ if (we(r)) {
+ r.getTextContent() !== Dt("v", r.getNumber()) && e.pendingKeys.add(r.getKey());
return;
}
- if (v(r)) {
- if (r.getType() !== Ke.getType() || ne(r, oe) === "attribute") return;
+ if (M(r)) {
+ if (r.getType() !== je.getType() || ne(r, oe) === "attribute") return;
const n = r.getParent();
- if (Pe(n)) {
- r.getTextContent() !== Lt("c", n.getNumber()) && e.pendingKeys.add(r.getKey());
+ if (Ee(n)) {
+ r.getTextContent() !== Dt("c", n.getNumber()) && e.pendingKeys.add(r.getKey());
return;
}
const i = r.getTextContent();
- (i.includes("\\") || i.includes("|") && om(r) || i.includes("//") || Up(r) !== void 0) && e.pendingKeys.add(r.getKey());
+ (i.includes("\\") || i.includes("|") && wm(r) || i.includes("//") || th(r) !== void 0) && e.pendingKeys.add(r.getKey());
return;
}
- if (D(r)) {
+ if (I(r)) {
r.getChildren().forEach(t);
return;
}
- if (!De(r) && !pt(r)) {
- if (Le(r) && r.getChildrenSize() === 0) {
- const n = _n(r)?.owner;
+ if (!Ue(r) && !pt(r)) {
+ if (De(r) && r.getChildrenSize() === 0) {
+ const n = Cn(r)?.owner;
n && e.pendingKeys.add(n.getKey());
return;
}
- F(r) && r.getChildren().forEach(t);
- }
- };
- Fe().getChildren().forEach(t);
-}
-const io = "usfm:", am = "usfmopen", cm = "usfmclosed";
-function rA(e) {
- return e.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
-}
-const nA = new RegExp(
- [io, am, cm].map(rA).join("|")
-), iA = "\uFEFF", sA = /^usfm_(.+)$/;
-function oA(e) {
- return e.nodeType === Node.ELEMENT_NODE;
-}
-function aA(e) {
- return e.replace(
- /%([0-9a-fA-F]{4})/g,
- (t, r) => String.fromCharCode(Number.parseInt(r, 16))
- );
-}
-function cA(e) {
- return e.startsWith(io) ? aA(e.slice(io.length)).replace(/\r\n?|\n/g, " ") : "";
-}
-function lm(e) {
- for (const t of e.classList) {
- const r = sA.exec(t);
- if (r) return r[1];
- }
-}
-function lA(e) {
- const t = lm(e);
- if (t !== void 0)
- return e.classList.contains("nested") ? `+${t}` : t;
-}
-function uA(e) {
- const t = e.ownerDocument.createTreeWalker(e, NodeFilter.SHOW_COMMENT);
- for (let r = t.nextNode(); r; r = t.nextNode())
- if ((r.nodeValue ?? "").startsWith(io)) return !0;
- for (const r of e.querySelectorAll("*")) {
- const { classList: n } = r;
- if (!(!n.contains(am) && !n.contains(cm)) && lm(r) !== void 0)
- return !0;
- }
- return !1;
-}
-function um(e, t, r) {
- if (e.nodeType === Node.TEXT_NODE) {
- t || r.push(e.nodeValue ?? "");
- return;
- }
- if (e.nodeType === Node.COMMENT_NODE) {
- r.push(cA(e.nodeValue ?? ""));
- return;
- }
- if (!oA(e)) return;
- const { classList: n } = e, i = (u) => e.childNodes.forEach((d) => um(d, u, r));
- if (t) {
- i(!n.contains("include"));
- return;
- }
- if (n.contains("exclude")) {
- i(!0);
- return;
- }
- const s = e.tagName.toLowerCase();
- if (s === "br") {
- r.push(`
-`);
- return;
- }
- if (s === "span" && e.getAttribute("class") === "attribute") {
- r.push(e.textContent ?? "");
- return;
- }
- const o = s === "div" || s === "tr", a = o || s === "span" || s === "th" || s === "td" ? lA(e) : void 0, c = a !== void 0 && n.contains("usfmopen"), l = a !== void 0 && n.contains("usfmclosed");
- o && r.push(`
-`), (c || l) && r.push(`\\${a} `), i(!1), l && r.push(`\\${a}*`), o && r.push(`
-`);
-}
-function dA(e) {
- if (!nA.test(e)) return;
- const { body: t } = new DOMParser().parseFromString(e, "text/html");
- if (t.querySelectorAll("script,style,template").forEach((n) => n.remove()), !uA(t)) return;
- const r = [];
- return t.childNodes.forEach((n) => um(n, !1, r)), r.join("").replaceAll(iA, "").replaceAll(I, " ").replace(/\r\n?/g, `
-`).replace(/\n+/g, `
-`).replace(/^\n|\n$/g, "");
-}
-function fA(e) {
- const t = e.getTextContent();
- if (!t.includes(" ")) return;
- const r = ne(e, oe);
- if (r === "attribute" || r === fr) return;
- for (let o = e.getParent(); o; o = o.getParent())
- if (pt(o) || Pe(o) || De(o)) return;
- const n = t.startsWith(I) && D(e.getParent()), i = n ? t.slice(1) : t, s = (n ? I : "") + i.replace(/ (?=[ \u00A0])/g, I).replace(new RegExp("(?<=\\u00A0) ", "g"), I);
- s !== t && e.setTextContent(s);
-}
-function pA(e) {
- const { body: t } = new DOMParser().parseFromString(e, "text/html");
- return t.querySelectorAll("script,style,template").forEach((r) => r.remove()), t.querySelectorAll("br").forEach((r) => r.replaceWith(`
-`)), t.querySelectorAll("p,div,li,td,th,tr,h1,h2,h3,h4,h5,h6,blockquote,pre").forEach((r) => r.after(`
-`)), (t.textContent ?? "").replace(/\n+/g, `
-`).replace(/^\n|\n$/g, "");
-}
-function hA(e, t) {
- if (!e) return !1;
- try {
- const r = JSON.parse(e);
- if (typeof r != "object" || r === null) return !1;
- const { namespace: n, nodes: i } = r;
- return n === t && Array.isArray(i);
- } catch {
- return !1;
- }
-}
-function cc(e, t) {
- const r = e && typeof e == "object" && "clipboardData" in e ? e.clipboardData : void 0;
- if (!r) return;
- const n = (a) => a.replace(/\r\n?/g, `
-`), i = n(r.getData("text/plain")), s = r.getData("text/html"), o = s ? dA(s) : void 0;
- return {
- text: o ? n(o) : i || (s ? n(pA(s)) : ""),
- isInternal: hA(
- r.getData("application/x-lexical-editor"),
- t
- )
- };
-}
-const ef = String.raw`\\(?:\+?[${er}]+\*?|\*)`, gA = new RegExp(
- String.raw`(?<=${ef})\u00A0|\u00A0(?=${ef})`,
- "g"
-);
-function Il(e) {
- return e.replace(/^\u00A0+/gm, (t) => " ".repeat(t.length)).replace(gA, " ").replaceAll(I, "~");
-}
-const dm = new RegExp(
- String.raw`\\c(?![${er}])[ \u00A0]*[^\s\\]*`,
- "g"
-), fm = new RegExp(String.raw`\\id(?![${er}])[^\n\\]*`, "g"), mA = new RegExp(
- String.raw`^(?:${dm.source}|${fm.source})`
-);
-function Ll(e) {
- return e.split(`
-`).map((t) => {
- const r = t.replace(dm, "").replace(fm, "");
- return mA.test(t) && r.trim() === "" && t.trim() !== "" ? void 0 : r;
- }).filter((t) => t !== void 0).join(`
-`);
-}
-function lc(e) {
- if (v(e) && ne(e, oe) === "attribute") return !0;
- for (let t = e; t; t = t.getParent())
- if (Le(t)) return !0;
- return !1;
-}
-function yA(e) {
- return lc(e.anchor.getNode()) || lc(e.focus.getNode());
-}
-function bA(e) {
- const { anchor: t, focus: r } = e;
- return t.key === r.key && lc(t.getNode());
-}
-function kA(e, t) {
- const n = bA(e) ? t : Il(Ll(t));
- n && e.insertText(n.replace(/\n/g, " "));
-}
-function TA(e, t = !1, r = () => {
-}) {
- const n = cc(e, Zn()._config.namespace);
- if (!n) return !1;
- const i = w(), s = N(i) && yA(i);
- if (!s && n.isInternal || t && N(i) && Qn(i))
- return !1;
- const { text: o } = n;
- if (!o || !N(i)) return !1;
- if (e?.preventDefault(), s)
- return kA(i, o), !0;
- const a = Il(Ll(o));
- if (!a) return !0;
- const c = a.split(`
-`);
- if (t)
- return i.insertText(c.join(" ")), !0;
- if (c.length < 2)
- return i.insertText(a), !0;
- r(), i.isCollapsed() || i.removeText();
- const l = Zn();
- return c.forEach((u, d) => {
- if (d > 0 && l.dispatchCommand(Rs, void 0), u === "") return;
- const f = w();
- N(f) && f.insertText(u);
- }), !0;
-}
-function xA(e) {
- if (e.getTextContent() !== I) return !1;
- const t = e.getParent();
- return j(t) ? !St(e.getPreviousSibling()) : !1;
-}
-function _A(e, t) {
- if (t || e.getTextContent() !== I) return "";
- const r = e.getParent();
- if (!j(r) || !St(e.getPreviousSibling())) return "";
- const n = r.getCategory();
- return n ? `\\cat ${n}\\cat*` : "";
-}
-function CA(e) {
- const t = e.getParent();
- return (j(t) ? t.getCaller() : void 0) || Bi;
-}
-function SA(e) {
- const t = e.getParent();
- return !t || Gr(t) === void 0;
-}
-function pm(e) {
- const t = e.getNodes();
- if (t.length === 0) return "";
- const r = t[0], n = t[t.length - 1], { anchor: i, focus: s } = e, o = i.isBefore(s), [a, c] = kc(e);
- let l = "", u = !0;
- for (const d of t) {
- if (F(d) && !d.isInline()) {
- !u && SA(d) && (l += `
-`), u = !d.isEmpty();
- continue;
+ L(r) && r.getChildren().forEach(t);
}
- if (u = !1, St(d))
- (d !== n || !e.isCollapsed()) && (l += (d === r ? "" : " ") + CA(d));
- else if (v(d)) {
- let f = d.getTextContent();
- d === r ? d === n ? (i.type !== "element" || s.type !== "element" || s.offset === i.offset) && (f = a < c ? f.slice(a, c) : f.slice(c, a)) : f = o ? f.slice(a) : f.slice(c) : d === n && (f = o ? f.slice(0, c) : f.slice(0, a)), l += xA(d) ? "" : f.replaceAll(I, " ") + _A(d, d === n);
- } else (uo(d) || us(d)) && (d !== n || !e.isCollapsed()) && (l += d.getTextContent().replaceAll(I, " "));
- }
- return l;
-}
-function hm(e) {
- return e.split(`
-`).map((t) => t.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">")).map(
- (t) => t ? `
${t}
` : ""
- ).join("");
-}
-function vA(e) {
- return e.getNodes().some(
- (t) => [t, ...t.getParents()].some(
- (r) => pt(r) || Pe(r)
- )
- );
-}
-function MA(e) {
- const t = w();
- if (!N(t) || t.isCollapsed()) return;
- const r = pm(t), n = {
- "text/plain": r,
- "text/html": hm(r)
};
- if (Po() || vA(t)) return n;
- const i = Dy(e);
- return i && (n["application/x-lexical-editor"] = i), n;
-}
-function tf(e, t, r) {
- const n = w();
- if (!N(n) || n.isCollapsed())
- return (!e || !("clipboardData" in e)) && !zh();
- const i = MA(t);
- return i ? gm(e, t, n, i, r) : !1;
-}
-function gm(e, t, r, n, i) {
- const s = !n["text/plain"], o = i && t.isEditable();
- if (!e || !("clipboardData" in e))
- return s || Uy(t, null, n), o && r.removeText(), !0;
- if (e.clipboardData == null) return !1;
- if (e.preventDefault(), !s)
- for (const [a, c] of Object.entries(n)) e.clipboardData.setData(a, c);
- return o && r.removeText(), !0;
+ ze().getChildren().forEach(t);
}
-const mm = vf(
+const qm = zf(
"COMMIT_PENDING_MARKERS_COMMAND"
);
-function ka(e) {
+function Ca(e) {
const t = e();
- return jr(kf), jr(Ff), t;
+ return Wr(qf), Wr(rp), t;
}
-const rf = 8, EA = 1e3;
-function Wn(e, t) {
- const r = Oe(e) ? ["va", "vp"] : Be(e) ? ["milestone"] : j(e) ? ["cat"] : (
+const yf = 8, c1 = 1e3;
+function Yn(e, t) {
+ const r = we(e) ? ["va", "vp"] : He(e) ? ["milestone"] : z(e) ? ["cat"] : (
// A chapter's two runs must be driven in this order — `\cp`'s scan and insertion
// anchor both depend on `\ca`'s wrapper already being in place, the same dependency
// a verse's `\vp` has on `\va`.
["ca", "cp"]
);
for (const n of r)
- UT(xn(n), e, t.pendingKeys);
+ ix(_n(n), e, t.pendingKeys);
}
-function AA(e, t) {
+function l1(e, t) {
const r = (n, i) => {
- if (i.updateTags.has(Cc) || i.updateTags.has(Vi)) return;
+ if (i.updateTags.has(wc) || i.updateTags.has(Yi)) return;
const s = [];
i.prevEditorState.read(() => {
for (const [o, a] of n) {
if (a !== "destroyed") continue;
const c = se(o);
if (!c) continue;
- const l = _n(c);
+ const l = Cn(c);
l && s.push({ owner: l.owner, kind: l.kind });
}
}), s.length !== 0 && e.getEditorState().read(() => {
for (const { owner: o, kind: a } of s) {
const c = se(o.getKey());
- c?.isAttached() && xn(a).expectedPieces(c).wantsRun && t.pendingKeys.add(c.getKey());
+ c?.isAttached() && _n(a).expectedPieces(c).wantsRun && t.pendingKeys.add(c.getKey());
}
});
};
- return Ve(
+ return Be(
// Registered for the four node classes a display-run piece (or a whole run
// wrapper) can be — a plain TextNode (a char span's `|…` run, a verse's `\va`/`\vp` value, a
// milestone's attribute text), a MarkerNode (a run's opening/closing glyphs, which
@@ -24121,78 +24512,79 @@ function AA(e, t) {
// subclass does not make the TextNode registration see it, mirroring the transform dispatch
// the TextNode catch-all transform's own comment documents — so each class needs its own
// registration.
- e.registerMutationListener(Ke, r),
+ e.registerMutationListener(je, r),
e.registerMutationListener(hr, r),
- e.registerMutationListener(Er, r),
- e.registerMutationListener(Ar, r)
+ e.registerMutationListener(wr, r),
+ e.registerMutationListener(qr, r)
);
}
-function uc(e, t) {
+function Tc(e, t) {
if (e.structureProtectionMode !== "protected") return !1;
const r = w();
- return r ? t ? Jh(r, t) : N(r) && Qn(r) : !1;
+ return r ? t ? ug(r, t) : N(r) && ti(r) : !1;
}
-function PA(e, t, r) {
- return Ve(
+function u1(e, t, r) {
+ return Be(
e.registerCommand(
- yr,
+ xr,
(n) => {
- if (Po() || uc(t)) return !1;
- const i = cc(n, e._config.namespace);
+ if (wo() || Tc(t)) return !1;
+ const i = uc(n, e._config.namespace);
if (!i || !i.text.includes(`
`)) return !1;
- const s = r ? Il(Ll(i.text)) : i.text;
+ const s = r ? Il(Dl(i.text)) : i.text;
if (s.includes(`
`)) {
const o = s.split(`
`);
- let a = Wd(o, t.getMarker);
- if (a === "declined" && _E(e) && (a = Wd(o, t.getMarker)), a === "handled")
+ let a = lf(o, t.getMarker);
+ if (a === "declined" && AA(e) && (a = lf(o, t.getMarker)), a === "handled")
return n?.preventDefault(), !0;
}
return !1;
},
- Wt
+ xt
),
e.registerCommand(
- yr,
+ xr,
(n) => {
- const i = cc(n, e._config.namespace);
+ const i = uc(n, e._config.namespace);
if (!i || i.isInternal || !i.text) return !1;
const s = i.text.split(`
`);
- if (s.length < 2 || !aM()) return !1;
+ if (s.length < 2 || !MM()) return !1;
const o = w();
- return t.structureProtectionMode === "protected" && N(o) && Qn(o) ? !1 : (n?.preventDefault(), N(o) && !o.isCollapsed() && o.removeText(), s.forEach((a, c) => {
- if (c > 0 && e.dispatchCommand(Rs, void 0), a === "") return;
+ return t.structureProtectionMode === "protected" && N(o) && ti(o) ? !1 : (n?.preventDefault(), N(o) && !o.isCollapsed() && o.removeText(), s.forEach((a, c) => {
+ if (c > 0 && e.dispatchCommand(Us, void 0), a === "") return;
const l = w();
N(l) && l.insertText(a);
}), !0);
},
- Ne
+ Oe
),
e.registerCommand(
- yr,
+ xr,
() => (t.splitExpected.current = !0, !1),
- Tt
+ _t
)
);
}
-function NA({
+function d1({
viewOptions: e,
getMarker: t,
logger: r,
markerSettleDelayMs: n,
- structureProtectionMode: i = "off"
+ structureProtectionMode: i = "off",
+ copyLimit: s
}) {
- const [s] = ce(), o = e?.markerMode === "editable", a = !!e && Ao(e), c = Z(void 0), l = Z(n);
- return z(() => {
- l.current = n;
- const u = c.current;
- u && (e && (u.viewOptions = e), u.getMarker = t ?? lr, u.logger = r, u.structureProtectionMode = i);
- }, [e, t, r, n, i]), z(() => {
- if (!o || !e) return;
- const u = {
+ const [o] = ae(), a = e?.markerMode === "editable", c = !!e && Ts(e), l = Z(void 0), d = Z(n), u = Z(s);
+ return F(() => {
+ d.current = n, u.current = s;
+ const f = l.current;
+ f && (e && (f.viewOptions = e), f.getMarker = t ?? lr, f.logger = r, f.structureProtectionMode = i);
+ }, [e, t, r, n, i, s]), F(() => {
+ if (!a || !e) return;
+ const f = {
viewOptions: e,
getMarker: t ?? lr,
pendingKeys: /* @__PURE__ */ new Set(),
@@ -24203,46 +24595,46 @@ function NA({
logger: r,
structureProtectionMode: i
};
- c.current = u;
- const d = NT(s, u.pendingKeys);
- let f, p = !1, m, g = !1, y = !1, k = 0;
- const _ = () => k < rf ? !1 : (u.logger?.warn(
- `[MarkerEdit] settle cascade exceeded ${rf} consecutive mutating passes; leaving ${u.pendingKeys.size} node(s) pending. This is a rebuild that never reaches a fixed point — pending keys: ${[...u.pendingKeys].join(", ")}`
- ), !0), S = (M, q = "departure") => {
- s.update(() => {
- k = ka(
- () => As(u, M, q)
- ) ? k + 1 : 0;
+ l.current = f;
+ const p = JT(o, f.pendingKeys);
+ let y, h = !1, m, b = !1, x = !1, _ = 0;
+ const A = () => _ < yf ? !1 : (f.logger?.warn(
+ `[MarkerEdit] settle cascade exceeded ${yf} consecutive mutating passes; leaving ${f.pendingKeys.size} node(s) pending. This is a rebuild that never reaches a fixed point — pending keys: ${[...f.pendingKeys].join(", ")}`
+ ), !0), E = (v, K = "departure") => {
+ o.update(() => {
+ _ = Ca(
+ () => qs(f, v, K)
+ ) ? _ + 1 : 0;
});
};
- let P;
- const A = () => {
- if (P !== void 0 && clearTimeout(P), P = void 0, y || u.pendingKeys.size === 0) return;
- const M = l.current ?? EA;
- M < 0 || (P = setTimeout(() => {
- P = void 0, !(y || u.pendingKeys.size === 0) && (p || _() || S(void 0, "idle"));
- }, M));
- }, B = Ve(
- s.registerNodeTransform(hr, (M) => {
- if (s.isComposing()) return;
- KE(M, u);
- const q = _n(M);
- q && (Oe(q.owner) || j(q.owner) || Pe(q.owner) || Be(q.owner) && ko(q.owner).wrapper === void 0) && Wn(q.owner, u);
+ let D;
+ const V = () => {
+ if (D !== void 0 && clearTimeout(D), D = void 0, x || f.pendingKeys.size === 0) return;
+ const v = d.current ?? c1;
+ v < 0 || (D = setTimeout(() => {
+ D = void 0, !(x || f.pendingKeys.size === 0) && (h || A() || E(void 0, "idle"));
+ }, v));
+ }, j = Be(
+ o.registerNodeTransform(hr, (v) => {
+ if (o.isComposing()) return;
+ GA(v, f);
+ const K = Cn(v);
+ K && (we(K.owner) || z(K.owner) || Ee(K.owner) || He(K.owner) && Co(K.owner).wrapper === void 0) && Yn(K.owner, f);
}),
- s.registerNodeTransform(dt, (M) => {
- s.isComposing() || (VE(M, u), Wn(M, u));
+ o.registerNodeTransform(dt, (v) => {
+ o.isComposing() || (XA(v, f), Yn(v, f));
}),
- s.registerNodeTransform(Nt, (M) => {
- s.isComposing() || (GE(M), M.isAttached() && Wn(M, u));
+ o.registerNodeTransform(Ot, (v) => {
+ o.isComposing() || (e1(v), v.isAttached() && Yn(v, f));
}),
- s.registerNodeTransform(Ze, (M) => {
- s.isComposing() || mE(M, u);
+ o.registerNodeTransform(rt, (v) => {
+ o.isComposing() || _A(v, f);
}),
- s.registerNodeTransform(me, (M) => {
- if (!s.isComposing()) {
- TE(M, u);
- for (const q of ["separator", "char"])
- M.isAttached() && gs(xn(q), M) && u.pendingKeys.add(M.getKey());
+ o.registerNodeTransform(ye, (v) => {
+ if (!o.isComposing()) {
+ MA(v, f);
+ for (const K of ["separator", "char"])
+ v.isAttached() && ks(_n(K), v) && f.pendingKeys.add(v.getKey());
}
}),
// Self-healing milestone display run (the shared $syncDisplayRun driver,
@@ -24258,29 +24650,29 @@ function NA({
// representation, so deleting all of it must delete the milestone, not resurrect the run)
// — the sync leaves it alone and the milestone is pended for the caret-departure settle
// ($resolvePendingMarkers).
- s.registerNodeTransform(Jt, (M) => {
- s.isComposing() || Wn(M, u);
+ o.registerNodeTransform(Gt, (v) => {
+ o.isComposing() || Yn(v, f);
}),
- s.registerNodeTransform(Ar, (M) => {
- if (s.isComposing()) return;
- const q = _n(M);
- q && (Be(q.owner) || Oe(q.owner) || j(q.owner) || Pe(q.owner)) && Wn(q.owner, u);
+ o.registerNodeTransform(qr, (v) => {
+ if (o.isComposing()) return;
+ const K = Cn(v);
+ K && (He(K.owner) || we(K.owner) || z(K.owner) || Ee(K.owner)) && Yn(K.owner, f);
}),
- s.registerNodeTransform(Ee, (M) => {
- s.isComposing() || (kE(M, u), Wn(M, u));
+ o.registerNodeTransform(Ae, (v) => {
+ o.isComposing() || (SA(v, f), Yn(v, f));
}),
// Unmatched-marker bytes are editable text in this mode; their edits pend and settle
// exactly like closer-glyph edits (see $unmatchedNodeTransform). Its own registration —
// Lexical dispatches transforms by exact node type, so neither the TextNode catch-all
// below nor the MarkerNode transform above ever fires for this subclass.
- s.registerNodeTransform(Or, (M) => {
- s.isComposing() || jE(M, u);
+ o.registerNodeTransform(Lr, (v) => {
+ o.isComposing() || JA(v, f);
}),
// Plain-TextNode catch-all for typed/pasted literal backslash sequences (Tier 2).
// Lexical dispatches transforms by exact node type, so this never fires for
// MarkerNode/VerseNode subclasses — TextSpacingPlugin relies on the same fact.
- s.registerNodeTransform(Ke, (M) => {
- s.isComposing() || ZE(M, u);
+ o.registerNodeTransform(je, (v) => {
+ o.isComposing() || s1(v, f);
}),
// Plain TextNodes can't emit a DOM class from node state the way
// ImmutableTypedTextNode does in createDOM(), so a char span's own `|…` attribute run
@@ -24311,319 +24703,274 @@ function NA({
// attributeDisplay.utils.ts) gets only the generic dim `.attribute` class below, not the
// marker-specific `usfm_va`/`usfm_vp` superscript coloring, until the wrap lands — a brief,
// imperceptible gap in a transient shape nothing at rest builds anymore.
- s.registerMutationListener(
- Ke,
- (M) => {
- s.getEditorState().read(() => {
- for (const [q, $] of M) {
- if ($ === "destroyed") continue;
- const J = se(q);
- !J || ne(J, oe) !== "attribute" || Le(J.getParent()) || s.getElementByKey(q)?.classList.add("attribute");
+ o.registerMutationListener(
+ je,
+ (v) => {
+ o.getEditorState().read(() => {
+ for (const [K, W] of v) {
+ if (W === "destroyed") continue;
+ const fe = se(K);
+ !fe || ne(fe, oe) !== "attribute" || De(fe.getParent()) || o.getElementByKey(K)?.classList.add("attribute");
}
});
},
{ skipInitialization: !1 }
),
- AA(s, u),
- ...a ? [
- s.registerNodeTransform(Ke, (M) => {
- s.isComposing() || fA(M);
+ l1(o, f),
+ ...c ? [
+ o.registerNodeTransform(je, (v) => {
+ o.isComposing() || xE(v);
}),
- s.registerCommand(
- fo,
- (M) => tf(
+ o.registerCommand(
+ ni,
+ (v) => Zd(
// COPY_COMMAND's payload is `ClipboardEvent | KeyboardEvent | null`. A plain
// `event instanceof ClipboardEvent` narrows this correctly in real browsers,
// but jsdom (our test environment) doesn't implement `ClipboardEvent` at all —
// `instanceof` against the undefined global throws — so this duck-checks the
// one property `$handleCopyForStandardView` actually needs instead.
- M && typeof M == "object" && "clipboardData" in M ? M : null,
- s,
- !1
+ v && typeof v == "object" && "clipboardData" in v ? v : null,
+ o,
+ !1,
+ u.current
),
- Ne
+ Oe
),
- s.registerCommand(
- yn,
- (M) => (
+ o.registerCommand(
+ vr,
+ (v) => (
// A structure-protected document's cut of a selection `StructureKeyboardPlugin`
// refuses to replace is that plugin's to refuse: it registers CUT at CRITICAL for
// exactly that reason, so it has already had its turn by the time this claim runs
// and a selection reaching here is one it allowed.
- tf(
- M && typeof M == "object" && "clipboardData" in M ? M : null,
- s,
- !0
+ Zd(
+ v && typeof v == "object" && "clipboardData" in v ? v : null,
+ o,
+ !0,
+ u.current
)
),
- Ne
+ Oe
),
- s.registerCommand(
- yr,
- (M) => TA(
+ o.registerCommand(
+ xr,
+ (v) => PE(
// Same jsdom-safe duck-check as COPY above.
- M && typeof M == "object" && "clipboardData" in M ? M : null,
- u.structureProtectionMode === "protected",
+ v && typeof v == "object" && "clipboardData" in v ? v : null,
+ f.structureProtectionMode === "protected",
// Consumed by $paraMarkerDeletionTransform below, same as the
// INSERT_PARAGRAPH_COMMAND and LOW-priority PASTE_COMMAND handlers arm it for
// the paste paths that reach them — this HIGH-priority claim reaches the
// former only from its second line on, and the latter never.
() => {
- u.splitExpected.current = !0;
+ f.splitExpected.current = !0;
}
),
- Ne
+ Oe
)
] : [],
- s.registerCommand(
- yn,
- () => (!uc(u) && !Po() && oc(u), !1),
- Wt
+ o.registerCommand(
+ vr,
+ () => (!Tc(f) && !wo() && bc(f), !1),
+ xt
),
- s.registerCommand(
- Tc,
- () => (s.isComposing() || pE(u), !1),
- Jn
+ o.registerCommand(
+ Pc,
+ () => (o.isComposing() || kA(f), !1),
+ Vr
),
- s.registerCommand(
- lo,
- () => (p = !1, k = 0, A(), !1),
- Tt
+ o.registerCommand(
+ mo,
+ () => (h = !1, _ = 0, V(), !1),
+ _t
),
- s.registerCommand(
- vr,
- (M) => (p = !1, k = 0, A(), (M.key === "Backspace" || M.key === "Delete") && !uc(u, Vh(M)) && (oc(u), fE(u), queueMicrotask(() => {
- u.wholeParaDeleteExpected?.clear(), u.collapsedDeleteCaretParas?.clear();
- })), s.isComposing() || !M.ctrlKey || M.altKey || M.shiftKey || M.metaKey || M.key !== " " && M.code !== "Space" || !oM() ? !1 : (M.preventDefault(), !0)),
- Ne
+ o.registerCommand(
+ Nr,
+ (v) => (h = !1, _ = 0, V(), (v.key === "Backspace" || v.key === "Delete") && !Tc(f, og(v)) && (bc(f), bA(f), queueMicrotask(() => {
+ f.wholeParaDeleteExpected?.clear(), f.collapsedDeleteCaretParas?.clear();
+ })), o.isComposing() || !v.ctrlKey || v.altKey || v.shiftKey || v.metaKey || v.key !== " " && v.code !== "Space" || !SM() ? !1 : (v.preventDefault(), !0)),
+ Oe
),
- s.registerCommand(
- _f,
- (M) => {
- const q = Gg();
- q === "needs-plain-split" && s.dispatchCommand(Rs, void 0);
- const $ = q !== "declined" || FT();
- return $ && M?.preventDefault(), As(u), $;
+ o.registerCommand(
+ Df,
+ (v) => {
+ const K = Tm();
+ K === "needs-plain-split" && o.dispatchCommand(Us, void 0);
+ const W = K !== "declined" || sx();
+ return W && v?.preventDefault(), qs(f), W;
},
- Ne
+ Oe
),
- s.registerCommand(
- Rs,
- () => (u.splitExpected.current = !0, fg()),
- Ne
+ o.registerCommand(
+ Us,
+ () => (f.splitExpected.current = !0, Eg()),
+ Oe
),
- PA(s, u, a),
- s.registerCommand(
- mm,
+ u1(o, f, c),
+ o.registerCommand(
+ qm,
() => {
- if (p) return !0;
- const M = s.getRootElement(), q = M?.ownerDocument, $ = !!M && !!q && q.hasFocus() && M.contains(q.activeElement);
- let J;
- if ($) {
- const Q = w();
- J = N(Q) ? Q.focus.key : f;
+ if (h) return !0;
+ const v = o.getRootElement(), K = v?.ownerDocument, W = !!v && !!K && K.hasFocus() && v.contains(K.activeElement);
+ let fe;
+ if (W) {
+ const X = w();
+ fe = N(X) ? X.focus.key : y;
}
- return ka(() => As(u, J)), !0;
+ return Ca(() => qs(f, fe)), !0;
},
- Tt
+ _t
),
- s.registerCommand(
- _c,
+ o.registerCommand(
+ Oc,
() => {
- if (p) return !1;
- const M = w(), q = N(M) ? M.focus.key : f;
- return ka(() => As(u, q)), !1;
+ if (h) return !1;
+ const v = w(), K = N(v) ? v.focus.key : y;
+ return Ca(() => qs(f, K)), !1;
},
- Tt
+ _t
),
- s.registerUpdateListener(({ editorState: M, tags: q }) => {
- u.splitExpected.current = !1, u.wholeParaDeleteExpected?.clear(), u.collapsedDeleteCaretParas?.clear(), u.rebuildAttempted.clear();
- const $ = M.read(() => {
- const Q = w();
- return N(Q) ? Q.focus.key : void 0;
- }), J = m;
- if ($ !== void 0 && (m = $), q.has(Cc)) {
- u.pendingKeys.clear(), M.read(() => tA(u)), p = !0, $ !== void 0 && (f = $);
+ o.registerUpdateListener(({ editorState: v, tags: K }) => {
+ f.splitExpected.current = !1, f.wholeParaDeleteExpected?.clear(), f.collapsedDeleteCaretParas?.clear(), f.rebuildAttempted.clear();
+ const W = v.read(() => {
+ const X = w();
+ return N(X) ? X.focus.key : void 0;
+ }), fe = m;
+ if (W !== void 0 && (m = W), K.has(wc)) {
+ f.pendingKeys.clear(), v.read(() => a1(f)), h = !0, W !== void 0 && (y = W);
return;
}
- if (q.has(Br)) {
- $ !== void 0 && $ !== J && (p = !0);
+ if (K.has(Hr)) {
+ W !== void 0 && W !== fe && (h = !0);
return;
}
- p || ($ !== void 0 && (f = $), A(), !(g || $ === void 0) && [...u.pendingKeys].some((Q) => Q !== $) && (g = !0, queueMicrotask(() => {
- g = !1, !y && (_() || S(f));
+ h || (W !== void 0 && (y = W), V(), !(b || W === void 0) && [...f.pendingKeys].some((X) => X !== W) && (b = !0, queueMicrotask(() => {
+ b = !1, !x && (A() || E(y));
})));
})
);
return () => {
- y = !0, P !== void 0 && clearTimeout(P), P = void 0, d(), B(), c.current = void 0;
+ x = !0, D !== void 0 && clearTimeout(D), D = void 0, p(), j(), l.current = void 0;
};
- }, [s, o, a]), null;
+ }, [o, a, c]), null;
}
-const OA = ["status_unknown", "status_invalid"], ym = {
+const f1 = ["status_unknown", "status_invalid"], Rm = {
unknown: "This marker is not in the stylesheet!",
invalid: "This marker is not valid here!"
-}, wA = Object.values(ym);
-function qA(e, t) {
+}, p1 = Object.values(Rm);
+function h1(e, t) {
e.classList.toggle("status_unknown", t === "unknown"), e.classList.toggle("status_invalid", t === "invalid");
- const r = ym[t];
+ const r = Rm[t];
e.getAttribute("aria-description") !== r && (e.setAttribute("aria-description", r), e.title = r);
}
-function nf(e) {
- e.classList.remove(...OA), e.removeAttribute("aria-description"), wA.includes(e.title) && e.removeAttribute("title");
+function bf(e) {
+ e.classList.remove(...f1), e.removeAttribute("aria-description"), p1.includes(e.title) && e.removeAttribute("title");
}
-function RA(e, t, r, n) {
- const i = (a) => a.read(() => Fe().getChildrenKeys()), s = i(t), o = i(e);
+function g1(e, t, r, n) {
+ const i = (a) => a.read(() => ze().getChildrenKeys()), s = i(t), o = i(e);
if (!(s.length !== o.length || s.some((a, c) => a !== o[c])))
return e.read(() => {
const a = /* @__PURE__ */ new Set(), c = (l) => {
- const u = se(l)?.getTopLevelElement();
- u && a.add(u.getKey());
+ const d = se(l)?.getTopLevelElement();
+ d && a.add(d.getKey());
};
for (const l of r.keys()) c(l);
for (const l of n) c(l);
return a;
});
}
-function $A(e) {
+function m1(e) {
const t = se(e), r = t?.getTopLevelElement();
return !t || !r ? !1 : t.getKey() === r.getKey() ? !0 : O(t) && t.getParent()?.getKey() === r.getKey();
}
-function IA({
+function y1({
viewOptions: e,
styleInfo: t,
logger: r
}) {
- const [n] = ce(), i = e?.markerMode === "editable";
- return z(() => {
+ const [n] = ae(), i = e?.markerMode === "editable";
+ return F(() => {
if (!i) return;
- const s = t ?? Vs;
+ const s = t ?? Js;
let o = /* @__PURE__ */ new Map();
const a = (l) => {
n.isComposing() || n.getEditorState().read(() => {
- const u = wM(s, l);
- let d = u;
+ const d = HE(s, l);
+ let u = d;
if (l) {
- d = new Map(u);
+ u = new Map(d);
for (const [f, p] of o) {
- if (d.has(f) || $A(f)) continue;
- const m = se(f)?.getTopLevelElement();
- !m || l.has(m.getKey()) || d.set(f, p);
+ if (u.has(f) || m1(f)) continue;
+ const y = se(f)?.getTopLevelElement();
+ !y || l.has(y.getKey()) || u.set(f, p);
}
}
for (const [f] of o) {
- if (d.has(f)) continue;
+ if (u.has(f)) continue;
const p = n.getElementByKey(f);
- p && nf(p);
+ p && bf(p);
}
- for (const [f, p] of d) {
- const m = n.getElementByKey(f);
- m && qA(m, p);
+ for (const [f, p] of u) {
+ const y = n.getElementByKey(f);
+ y && h1(y, p);
}
- o = d, r?.debug(`[MarkerValidation] pass: ${d.size} flagged`);
+ o = u, r?.debug(`[MarkerValidation] pass: ${u.size} flagged`);
});
};
a();
const c = n.registerUpdateListener(
- ({ editorState: l, prevEditorState: u, dirtyElements: d, dirtyLeaves: f }) => {
- d.size === 0 && f.size === 0 || a(
- RA(l, u, d, f)
+ ({ editorState: l, prevEditorState: d, dirtyElements: u, dirtyLeaves: f }) => {
+ u.size === 0 && f.size === 0 || a(
+ g1(l, d, u, f)
);
}
);
return () => {
c();
for (const [l] of o) {
- const u = n.getElementByKey(l);
- u && nf(u);
+ const d = n.getElementByKey(l);
+ d && bf(d);
}
};
}, [n, i, t, r]), null;
}
-function LA(e, t) {
- const r = ll(ol), n = tl();
- if (!r || !n?.end) return;
- const i = Os.deserializeEditorState(e.getEditorState(), t);
- if (!i) return;
- const s = $y({
- namespace: "markers-view-copy",
- nodes: [Qe, ...nl],
- onError: (a) => {
- throw a;
- }
- });
- return s.parseEditorState(
- Cr.serializeEditorState(i, r)
- ).read(
- () => {
- const a = Mo(n);
- return a ? pm(a) : void 0;
- },
- { editor: s }
- );
-}
-function DA({ viewOptions: e }) {
- const [t] = ce();
- return z(() => {
- const r = (n, i) => {
- const s = w();
- if (!N(s) || s.isCollapsed()) return !1;
- const o = LA(t, e);
- return o === void 0 ? !1 : gm(
- // COPY_COMMAND's payload is `ClipboardEvent | KeyboardEvent | null`, and jsdom (our test
- // environment) has no `ClipboardEvent` for `instanceof` to narrow against, so this
- // duck-checks the one property `$writeCopyPayload` needs. `in` throws on a non-object, so
- // the type check comes first.
- n && typeof n == "object" && "clipboardData" in n ? n : null,
- t,
- s,
- { "text/plain": o, "text/html": hm(o) },
- i
- );
- };
- return Ve(
- t.registerCommand(fo, (n) => r(n, !1), Ne),
- t.registerCommand(yn, (n) => r(n, !0), Ne)
- );
- }, [t, e]), null;
-}
-function bm(e, t, r) {
+function $m(e, t, r) {
const n = Math.min(e.length, t.length);
for (let i = 0; i < n; i++) {
const s = e[i], o = t[i];
r.set(s.getKey(), { node: o, siblings: t });
- const a = Sr(o);
- a && F(s) && bm(s.getChildren(), a, r);
+ const a = Pr(o);
+ a && L(s) && $m(s.getChildren(), a, r);
}
}
-function km(e, t) {
+function Lm(e, t) {
let r = 0;
const n = (i) => {
for (let s = 0; s < i.length; s++) {
- const o = i[s], a = Sr(o);
+ const o = i[s], a = Pr(o);
if (a) {
n(a);
continue;
}
- const c = ci(o);
- if (c === void 0 || !c.includes(tt)) continue;
- const l = c.split(tt), u = [];
- for (let d = 0; d < l.length; d++) {
- const f = l[d];
- if (d > 0 && u.push(...t[r++] ?? []), f.length > 0) {
+ const c = pi(o);
+ if (c === void 0 || !c.includes(it)) continue;
+ const l = c.split(it), d = [];
+ for (let u = 0; u < l.length; u++) {
+ const f = l[u];
+ if (u > 0 && d.push(...t[r++] ?? []), f.length > 0) {
const p = {
...o,
text: f
};
- u.push(p);
+ d.push(p);
}
}
- i.splice(s, 1, ...u), s += u.length - 1;
+ i.splice(s, 1, ...d), s += d.length - 1;
}
};
n(e);
}
-function Tm(e, t, r) {
+function Im(e, t, r) {
const n = [];
for (const i of e.sentinels) {
const s = [];
@@ -24637,27 +24984,27 @@ function Tm(e, t, r) {
}
return n;
}
-function xm(e, t) {
+function Dm(e, t) {
const r = [];
for (const n of e)
- wg(n, t) || ((ae(n) || D(n)) && r.push(n.getMarker()), F(n) && r.push(...xm(n.getChildren(), t)));
+ im(n, t) || ((ce(n) || I(n)) && r.push(n.getMarker()), L(n) && r.push(...Dm(n.getChildren(), t)));
return r;
}
-function _m(e) {
+function Um(e) {
const t = [];
for (const r of e) {
- const n = Pl(r);
+ const n = Vl(r);
(n === "para" || n === "char") && t.push(r.marker ?? "");
- const i = Sr(r);
- i && t.push(..._m(i));
+ const i = Pr(r);
+ i && t.push(...Um(i));
}
return t;
}
-function Dl(e, t, r) {
- const n = xm(e, r), i = _m(t);
+function Ql(e, t, r) {
+ const n = Dm(e, r), i = Um(t);
return n.length === i.length && n.every((s, o) => s === i[o]);
}
-function UA(e, t) {
+function b1(e, t) {
if (!e || e.input.run.length === 0) return;
const r = w();
let n, i;
@@ -24668,10 +25015,10 @@ function UA(e, t) {
n = se(t.key), i = t.offset;
else
return;
- if (!(!v(n) || !n.isAttached()) && !(e.nodeKey !== void 0 && n.getKey() !== e.nodeKey) && n.getTextContent().slice(0, i).endsWith(e.input.run))
+ if (!(!M(n) || !n.isAttached()) && !(e.nodeKey !== void 0 && n.getKey() !== e.nodeKey) && n.getTextContent().slice(0, i).endsWith(e.input.run))
return { node: n, caretOffset: i, run: e.input.run };
}
-function Ul(e, t) {
+function Zl(e, t) {
const r = t.node.getKey(), n = e.spans.find(
(o) => !o.isSentinel && o.key === r
);
@@ -24679,78 +25026,78 @@ function Ul(e, t) {
const i = n.start + t.caretOffset, s = i - t.run.length;
return s < n.start || e.text.slice(s, i) !== t.run ? e.text : e.text.slice(0, s) + e.text.slice(i);
}
-function FA(e, t, r, n, i) {
+function k1(e, t, r, n, i) {
const { viewOptions: s, getMarker: o, logger: a } = r;
if (e.length === 0) return;
const c = { text: "", spans: [], sentinels: [] };
- for (const y of e) {
- const k = Nl(y, o, s);
- if (!k) return;
+ for (const m of e) {
+ const b = Wl(m, o, s);
+ if (!b) return;
c.text.length > 0 && (c.text += " ");
- const _ = c.text.length;
- k.spans.forEach(
- (S) => c.spans.push({ ...S, start: S.start + _, end: S.end + _ })
- ), c.sentinels.push(...k.sentinels), c.text += k.text;
+ const x = c.text.length;
+ b.spans.forEach(
+ (_) => c.spans.push({ ..._, start: _.start + x, end: _.end + x })
+ ), c.sentinels.push(...b.sentinels), c.text += b.text;
}
- const l = i ? Ul(c, i) : c.text, u = Mr(l, {
+ const l = i ? Zl(c, i) : c.text, d = Or(l, {
getMarker: o
});
- if (u.length === 0) return;
- if (qn(u) !== c.sentinels.length) {
+ if (d.length === 0) return;
+ if (In(d) !== c.sentinels.length) {
a?.warn("[MarkerEdit] Settled USJ skipped: sentinel/preserved-node count mismatch");
return;
}
- const d = Cr.serializeEditorState(
- { type: kr, version: br, content: u },
+ const u = Ar.serializeEditorState(
+ { type: Cr, version: _r, content: d },
s
).root.children;
- if (qo(d) !== c.sentinels.length) {
+ if (Lo(u) !== c.sentinels.length) {
a?.warn(
"[MarkerEdit] Settled USJ skipped: serialized sentinel/preserved-node count mismatch"
);
return;
}
- const f = Tm(c, t, n);
+ const f = Im(c, t, n);
if (!f) {
a?.warn("[MarkerEdit] Settled USJ skipped: a preserved node had no serialized form");
return;
}
- if (ki(d, o) === bi(e, o) && Dl(e, d, o)) {
+ if (Ci(u, o) === _i(e, o) && Ql(e, u, o)) {
a?.debug("[MarkerEdit] Settled USJ skipped: rebuild is a no-op (fixed point)");
return;
}
- km(d, f);
- const m = zA(e), g = Cm(d);
- for (let y = 0; y < m.length && y < g.length; y++)
- m[y].sid !== void 0 && g[y].number === m[y].number && (g[y].sid = m[y].sid);
- return d;
+ Lm(u, f);
+ const y = T1(e), h = Fm(u);
+ for (let m = 0; m < y.length && m < h.length; m++)
+ y[m].sid !== void 0 && h[m].number === y[m].number && (h[m].sid = y[m].sid);
+ return u;
}
-function zA(e) {
+function T1(e) {
const t = [], r = (n) => {
- Oe(n) ? t.push({ number: n.getNumber(), sid: n.getSid() }) : F(n) && n.getChildren().forEach(r);
+ we(n) ? t.push({ number: n.getNumber(), sid: n.getSid() }) : L(n) && n.getChildren().forEach(r);
};
return e.forEach(r), t;
}
-function Cm(e) {
+function Fm(e) {
const t = [];
for (const r of e) {
- bp(r) && t.push(r);
- const n = Sr(r);
- n && t.push(...Cm(n));
+ Rp(r) && t.push(r);
+ const n = Pr(r);
+ n && t.push(...Fm(n));
}
return t;
}
-function KA(e, t, r, n, i) {
- const { viewOptions: s, getMarker: o, logger: a } = r, c = zg(e, o, s);
+function x1(e, t, r, n, i) {
+ const { viewOptions: s, getMarker: o, logger: a } = r, c = pm(e, o, s);
if (!c) return;
- const { out: l, contentNodes: u } = c;
- if (u.length === 0) return;
- const d = i ? Ul(l, i) : l.text, f = Mr(d, {
+ const { out: l, contentNodes: d } = c;
+ if (d.length === 0) return;
+ const u = i ? Zl(l, i) : l.text, f = Or(u, {
getMarker: o,
isNoteContext: !0
});
if (f.length === 0) return;
- if (qn(f) !== l.sentinels.length) {
+ if (In(f) !== l.sentinels.length) {
a?.warn("[MarkerEdit] Settled note USJ skipped: sentinel/preserved-node count mismatch");
return;
}
@@ -24759,53 +25106,53 @@ function KA(e, t, r, n, i) {
a?.warn("[MarkerEdit] Settled note USJ skipped: unexpected tokenized shape");
return;
}
- const m = p.content ?? [], g = Kg(m), y = e.getCategory() !== g, k = Ng(e, m, g, s);
- if (k.failure !== void 0) {
- k.failure === "shape" ? a?.warn("[MarkerEdit] Settled note USJ skipped: unexpected serialized shape") : k.failure === "caller" && a?.warn("[MarkerEdit] Settled note USJ skipped: serialized note lacks the caller");
+ const y = p.content ?? [], h = hm(y), m = e.getCategory() !== h, b = rm(e, y, h, s);
+ if (b.failure !== void 0) {
+ b.failure === "shape" ? a?.warn("[MarkerEdit] Settled note USJ skipped: unexpected serialized shape") : b.failure === "caller" && a?.warn("[MarkerEdit] Settled note USJ skipped: serialized note lacks the caller");
return;
}
- const _ = k.children;
- if (qo(_) !== l.sentinels.length) {
+ const x = b.children;
+ if (Lo(x) !== l.sentinels.length) {
a?.warn(
"[MarkerEdit] Settled note USJ skipped: serialized sentinel/preserved-node count mismatch"
);
return;
}
- const S = Tm(l, t, n);
- if (!S) {
+ const _ = Im(l, t, n);
+ if (!_) {
a?.warn("[MarkerEdit] Settled note USJ skipped: a preserved node had no serialized form");
return;
}
- if (ki(_, o) === bi(u, o) && Dl(u, _, o)) {
- if (y)
- return { rebuilt: void 0, contentNodes: u, category: g, categoryChanged: y };
+ if (Ci(x, o) === _i(d, o) && Ql(d, x, o)) {
+ if (m)
+ return { rebuilt: void 0, contentNodes: d, category: h, categoryChanged: m };
a?.debug("[MarkerEdit] Settled note USJ skipped: rebuild is a no-op (fixed point)");
return;
}
- return km(_, S), { rebuilt: _, contentNodes: u, category: g, categoryChanged: y };
+ return Lm(x, _), { rebuilt: x, contentNodes: d, category: h, categoryChanged: m };
}
-function sf(e) {
+function kf(e) {
return e.$?.textType;
}
-function jA(e, t) {
+function _1(e, t) {
const r = e, n = t;
- return r.type === "text" && n.type === "text" && r.format === n.format && r.style === n.style && r.mode === n.mode && r.detail === n.detail && sf(e) === sf(t);
+ return r.type === "text" && n.type === "text" && r.format === n.format && r.style === n.style && r.mode === n.mode && r.detail === n.detail && kf(e) === kf(t);
}
-function BA(e) {
+function C1(e) {
const t = [];
for (const r of e) {
const n = se(r);
- n?.isAttached() && De(n) && n.getTag() === "optbreak" && n.getChildrenSize() === 0 && t.push(n);
+ n?.isAttached() && Ue(n) && n.getTag() === "optbreak" && n.getChildrenSize() === 0 && t.push(n);
}
return t;
}
-function VA(e) {
+function v1(e) {
if (!O(e) || e.getMarkerSyntax() !== "opening") return;
const t = e.getParent();
- if (!j(t)) return;
+ if (!z(t)) return;
const r = e.getTextContent();
- if (Xr(e)) return;
- const n = Ag.exec(r);
+ if (en(e)) return;
+ const n = Kg.exec(r);
if (!n) return;
const i = n[1];
if (i.startsWith("+")) return;
@@ -24813,175 +25160,175 @@ function VA(e) {
if (t.getMarker() === s)
return { glyph: e, note: t, oldMarker: s, newMarker: i };
}
-function of(e, t) {
+function Tf(e, t) {
const r = e;
- r.marker = t, r.text = Rg(t, r.markerSyntax, r.nested);
+ r.marker = t, r.text = om(t, r.markerSyntax, r.nested);
}
-function WA(e, t) {
+function S1(e, t) {
const { glyph: r, note: n, oldMarker: i, newMarker: s } = e;
- if (!Ee.isValidMarker(s)) return;
+ if (!Ae.isValidMarker(s)) return;
const o = t.get(n.getKey());
o && (o.node.marker = s);
const a = t.get(r.getKey());
- a && of(a.node, s);
- const c = n.getChildren().filter(O).filter((u) => u.getMarkerSyntax() === "closing" && u.getMarker() === i).at(-1), l = c && t.get(c.getKey());
- l && of(l.node, s);
+ a && Tf(a.node, s);
+ const c = n.getChildren().filter(O).filter((d) => d.getMarkerSyntax() === "closing" && d.getMarker() === i).at(-1), l = c && t.get(c.getKey());
+ l && Tf(l.node, s);
}
-function HA(e, t, r) {
- const { viewOptions: n, getMarker: i, logger: s } = t, o = Vg(e, i, n);
+function M1(e, t, r) {
+ const { viewOptions: n, getMarker: i, logger: s } = t, o = ym(e, i, n);
if (!o) return;
- const a = r ? Ul(o, r) : o.text, c = Mr(a, {
+ const a = r ? Zl(o, r) : o.text, c = Or(a, {
getMarker: i
}), [l] = c;
if (c.length === 0 || typeof l != "object" || l.type !== "chapter")
return;
- if (qn(c) !== 0) {
+ if (In(c) !== 0) {
s?.warn("[MarkerEdit] Settled chapter USJ skipped: unexpected preserved-node placeholder");
return;
}
e.getSid() !== void 0 && (l.sid = e.getSid());
- const u = Cr.serializeEditorState(
- { type: kr, version: br, content: c },
+ const d = Ar.serializeEditorState(
+ { type: Cr, version: _r, content: c },
n
).root.children;
- if (u.length === 0) return;
- const d = [e, ...$o(e)];
- if (!(e.getNumber() !== (l.number ?? "") || e.getAltnumber() !== l.altnumber || e.getPubnumber() !== l.pubnumber) && ki(u, i) === bi(d, i) && Dl(d, u, i)) {
+ if (d.length === 0) return;
+ const u = [e, ...Do(e)];
+ if (!(e.getNumber() !== (l.number ?? "") || e.getAltnumber() !== l.altnumber || e.getPubnumber() !== l.pubnumber) && Ci(d, i) === _i(u, i) && Ql(u, d, i)) {
s?.debug("[MarkerEdit] Settled chapter USJ skipped: rebuild is a no-op (fixed point)");
return;
}
- return u;
+ return d;
}
-function GA(e, t, r, n, i) {
- const s = UA(n, i);
+function E1(e, t, r, n, i) {
+ const s = b1(n, i);
if (t.size === 0 && !s) return;
- const o = /* @__PURE__ */ new Map(), a = [], c = /* @__PURE__ */ new Map(), l = /* @__PURE__ */ new Map(), u = /* @__PURE__ */ new Map(), d = (y) => {
- j(y) ? c.set(y.getKey(), y) : Pe(y) ? l.set(y.getKey(), y) : o.set(y.getKey(), [y]);
+ const o = /* @__PURE__ */ new Map(), a = [], c = /* @__PURE__ */ new Map(), l = /* @__PURE__ */ new Map(), d = /* @__PURE__ */ new Map(), u = (m) => {
+ z(m) ? c.set(m.getKey(), m) : Ee(m) ? l.set(m.getKey(), m) : o.set(m.getKey(), [m]);
};
- for (const y of t) {
- const k = se(y);
- if (!k?.isAttached()) continue;
- const _ = as(k);
- if (_) {
- if (d(_), O(k)) {
- const S = em(k, r.getMarker);
- S && a.push(S);
+ for (const m of t) {
+ const b = se(m);
+ if (!b?.isAttached()) continue;
+ const x = fs(b);
+ if (x) {
+ if (u(x), O(b)) {
+ const _ = Mm(b, r.getMarker);
+ _ && a.push(_);
}
- if (j(_)) {
- const S = VA(k);
- S && u.set(_.getKey(), S);
+ if (z(x)) {
+ const _ = v1(b);
+ _ && d.set(x.getKey(), _);
}
}
}
const f = /* @__PURE__ */ new Set();
- for (const y of a)
- y.some((k) => f.has(k.getKey())) || (y.forEach((k) => {
- f.add(k.getKey()), o.delete(k.getKey());
- }), o.set(y[0].getKey(), y));
+ for (const m of a)
+ m.some((b) => f.has(b.getKey())) || (m.forEach((b) => {
+ f.add(b.getKey()), o.delete(b.getKey());
+ }), o.set(m[0].getKey(), m));
if (s) {
- const y = as(s.node);
- y && d(y);
+ const m = fs(s.node);
+ m && u(m);
}
- const p = BA(t);
+ const p = C1(t);
if (o.size === 0 && c.size === 0 && l.size === 0 && p.length === 0)
return;
- const m = new Set(p.map((y) => y.getKey())), g = /* @__PURE__ */ new Map();
- bm(Fe().getChildren(), e.root.children, g);
- for (const y of u.values()) WA(y, g);
- for (const y of c.values()) {
- const k = g.get(y.getKey()), _ = k ? Sr(k.node) : void 0;
- if (!k || !_) continue;
- const S = KA(y, g, r, m, s);
- if (!S) continue;
- if (S.categoryChanged) {
- const B = k.node;
- S.category === void 0 ? delete B.category : B.category = S.category;
- }
- if (!S.rebuilt) continue;
- const P = g.get(S.contentNodes[0].getKey());
- if (!P) continue;
- const A = _.indexOf(P.node);
- A < 0 || _.splice(A, S.contentNodes.length, ...S.rebuilt);
- }
- for (const y of o.values()) {
- const k = g.get(y[0].getKey());
- if (!k) continue;
- const _ = FA(y, g, r, m, s);
+ const y = new Set(p.map((m) => m.getKey())), h = /* @__PURE__ */ new Map();
+ $m(ze().getChildren(), e.root.children, h);
+ for (const m of d.values()) S1(m, h);
+ for (const m of c.values()) {
+ const b = h.get(m.getKey()), x = b ? Pr(b.node) : void 0;
+ if (!b || !x) continue;
+ const _ = x1(m, h, r, y, s);
if (!_) continue;
- const S = k.siblings.indexOf(k.node);
- S < 0 || k.siblings.splice(S, y.length, ..._);
- }
- for (const y of l.values()) {
- const k = g.get(y.getKey());
- if (!k) continue;
- const _ = 1 + $o(y).length, S = HA(y, r, s);
- if (!S) continue;
- const P = k.siblings.indexOf(k.node);
- P < 0 || k.siblings.splice(P, _, ...S);
- }
- for (const y of p) {
- const k = g.get(y.getKey());
- if (!k) continue;
- const _ = k.siblings.indexOf(k.node);
- if (_ < 0) continue;
- k.siblings.splice(_, 1);
- const S = k.siblings[_ - 1], P = k.siblings[_], A = S && ci(S), B = P && ci(P);
- S && P && A !== void 0 && B !== void 0 && jA(S, P) && (S.text = A + B, k.siblings.splice(_, 1));
- }
- return ig(e, r.viewOptions);
-}
-function JA({
+ if (_.categoryChanged) {
+ const D = b.node;
+ _.category === void 0 ? delete D.category : D.category = _.category;
+ }
+ if (!_.rebuilt) continue;
+ const A = h.get(_.contentNodes[0].getKey());
+ if (!A) continue;
+ const E = x.indexOf(A.node);
+ E < 0 || x.splice(E, _.contentNodes.length, ..._.rebuilt);
+ }
+ for (const m of o.values()) {
+ const b = h.get(m[0].getKey());
+ if (!b) continue;
+ const x = k1(m, h, r, y, s);
+ if (!x) continue;
+ const _ = b.siblings.indexOf(b.node);
+ _ < 0 || b.siblings.splice(_, m.length, ...x);
+ }
+ for (const m of l.values()) {
+ const b = h.get(m.getKey());
+ if (!b) continue;
+ const x = 1 + Do(m).length, _ = M1(m, r, s);
+ if (!_) continue;
+ const A = b.siblings.indexOf(b.node);
+ A < 0 || b.siblings.splice(A, x, ..._);
+ }
+ for (const m of p) {
+ const b = h.get(m.getKey());
+ if (!b) continue;
+ const x = b.siblings.indexOf(b.node);
+ if (x < 0) continue;
+ b.siblings.splice(x, 1);
+ const _ = b.siblings[x - 1], A = b.siblings[x], E = _ && pi(_), D = A && pi(A);
+ _ && A && E !== void 0 && D !== void 0 && _1(_, A) && (_.text = E + D, b.siblings.splice(x, 1));
+ }
+ return kg(e, r.viewOptions);
+}
+function A1({
viewOptions: e,
logger: t
}) {
- const [r] = ce(), n = gi(e) && (e?.markerMode === "visible" || (e?.hasGutterParaMarkers ?? !1));
- return z(() => {
+ const [r] = ae(), n = ki(e) && (e?.markerMode === "visible" || (e?.hasGutterParaMarkers ?? !1));
+ return F(() => {
if (n)
return r.registerNodeTransform(
- Ze,
- (i) => YA(i, t)
+ rt,
+ (i) => P1(i, t)
);
}, [r, n, t]), null;
}
-function YA(e, t) {
- e.getMarker() !== cr && (e.isEmpty() || Ft(e.getFirstChild()) || (t?.debug(
+function P1(e, t) {
+ e.getMarker() !== cr && (e.isEmpty() || zt(e.getFirstChild()) || (t?.debug(
`[ParaMarkerPrefixGuard] Resetting paragraph "${e.getMarker()}" → "${cr}" (key ${e.getKey()})`
), e.setMarker(cr)));
}
-function XA({
+function N1({
scrRef: e,
onScrRefChange: t
}) {
- const [r] = ce(), n = Z({
+ const [r] = ae(), n = Z({
phase: "idle",
pendingEchoes: [],
scrRef: e,
onScrRefChange: t,
sawDocument: !1
});
- return z(() => {
+ return F(() => {
const i = n.current, s = i.scrRef;
- i.scrRef = e, i.onScrRefChange = t, so(s, e) || QA(i, r, e);
- }, [r, e, t]), z(
+ i.scrRef = e, i.onScrRefChange = t, uo(s, e) || O1(i, r, e);
+ }, [r, e, t]), F(
() => r.registerMutationListener(
- Ut,
+ Ft,
(i, { prevEditorState: s }) => {
const o = [...i.values()];
if (o.every((c) => c === "destroyed")) return;
- const a = dc(r);
- af(n.current, r, a, {
+ const a = xc(r);
+ xf(n.current, r, a, {
hasCreated: o.includes("created"),
hasDestroyed: o.includes("destroyed"),
- isSameDocumentReload: Ps(s) === Ps(r.getEditorState())
+ isSameDocumentReload: Rs(s) === Rs(r.getEditorState())
});
},
{ skipInitialization: !1 }
),
[r]
- ), z(() => {
+ ), F(() => {
const i = (a) => a.read(
() => new Set(
- Fe().getChildren().filter(Ge).map((c) => c.getKey())
+ ze().getChildren().filter(Ge).map((c) => c.getKey())
)
);
let s;
@@ -24989,15 +25336,15 @@ function XA({
const c = r.getEditorState();
if (s === c) return;
s = c;
- const u = a === c ? /* @__PURE__ */ new Set() : i(a), d = i(c), f = [...d].some((p) => !u.has(p));
- f && (dc(r) || af(n.current, r, void 0, {
+ const d = a === c ? /* @__PURE__ */ new Set() : i(a), u = i(c), f = [...u].some((p) => !d.has(p));
+ f && (xc(r) || xf(n.current, r, void 0, {
hasCreated: f,
- hasDestroyed: [...u].some((p) => !d.has(p)),
- isSameDocumentReload: Ps(a) === Ps(c)
+ hasDestroyed: [...d].some((p) => !u.has(p)),
+ isSameDocumentReload: Rs(a) === Rs(c)
}));
};
- return Ve(
- ...[Nt, pr].map(
+ return Be(
+ ...[Ot, pr].map(
(a) => r.registerMutationListener(
a,
(c, { prevEditorState: l }) => o(l),
@@ -25005,140 +25352,140 @@ function XA({
)
)
);
- }, [r]), z(
+ }, [r]), F(
() => r.registerCommand(
ur,
() => {
const i = n.current;
- return i.phase === "idle" && r1(i, e1()), !1;
+ return i.phase === "idle" && $1(i, q1()), !1;
},
- Tt
+ _t
),
[r]
- ), z(() => {
+ ), F(() => {
const i = (s) => {
[...s.values()].some(
(a) => a === "created" || a === "destroyed"
) && queueMicrotask(() => r.dispatchCommand(ur, void 0));
};
- return Ve(
- r.registerMutationListener(Ct, i),
+ return Be(
+ r.registerMutationListener(St, i),
r.registerMutationListener(dt, i)
);
- }, [r]), z(() => {
- const i = () => o1(n.current);
+ }, [r]), F(() => {
+ const i = () => U1(n.current);
return r.registerRootListener((s, o) => {
o?.removeEventListener("pointerdown", i), o?.removeEventListener("keydown", i), o?.removeEventListener("beforeinput", i), s?.addEventListener("pointerdown", i), s?.addEventListener("keydown", i), s?.addEventListener("beforeinput", i);
});
}, [r]), null;
}
-function QA(e, t, r) {
- if (ZA(e, r)) return;
+function O1(e, t, r) {
+ if (w1(e, r)) return;
e.phase = "navigating", e.pendingEchoes.length = 0;
- const n = dc(t);
- (!n || n === r.book) && t.update(() => Sm(r.chapterNum, r.verseNum), {
- tag: Br
+ const n = xc(t);
+ (!n || n === r.book) && t.update(() => zm(r.chapterNum, r.verseNum), {
+ tag: Hr
});
}
-function ZA(e, t) {
- const r = e.pendingEchoes.findIndex((n) => so(n, t));
+function w1(e, t) {
+ const r = e.pendingEchoes.findIndex((n) => uo(n, t));
return r < 0 ? !1 : (e.pendingEchoes.splice(0, r + 1), e.phase = "idle", !0);
}
-function e1() {
- const e = w(), t = $c(e);
+function q1() {
+ const e = w(), t = Bc(e);
if (!t) return;
- const r = Fl(), n = xp(t);
+ const r = eu(), n = Ip(t);
if (!n && !r) return;
const i = n ? parseInt(n.getNumber() ?? "1", 10) : 1;
if (Number.isNaN(i)) return;
- const s = Yc(t, e), { verseNum: o, verse: a } = lx(s ?? void 0, e);
+ const s = ol(t, e), { verseNum: o, verse: a } = Ax(s ?? void 0, e);
if (!Number.isNaN(o))
return { book: r?.getCode() || void 0, chapterNum: i, verseNum: o, verse: a };
}
-function dc(e) {
- return e.getEditorState().read(() => Fl()?.getCode() || void 0);
+function xc(e) {
+ return e.getEditorState().read(() => eu()?.getCode() || void 0);
}
-function Fl() {
- return Fe().getChildren().find(pt);
+function eu() {
+ return ze().getChildren().find(pt);
}
-function af(e, t, r, n) {
+function xf(e, t, r, n) {
const i = n.hasCreated && !e.sawDocument;
if (n.hasCreated && (e.sawDocument = !0), e.phase === "navigating") {
- n.hasCreated && (!r || r === e.scrRef.book) && Ta(e, t);
+ n.hasCreated && (!r || r === e.scrRef.book) && va(e, t);
return;
}
- n.hasCreated && n.hasDestroyed ? (n.isSameDocumentReload || Ta(e, t), e.phase = "navigating") : i && Ta(e, t), r && r !== e.scrRef.book && Em(e, { ...e.scrRef, book: r }) && (e.phase = "navigating");
+ n.hasCreated && n.hasDestroyed ? (n.isSameDocumentReload || va(e, t), e.phase = "navigating") : i && va(e, t), r && r !== e.scrRef.book && Bm(e, { ...e.scrRef, book: r }) && (e.phase = "navigating");
}
-function Ta(e, t) {
+function va(e, t) {
queueMicrotask(() => {
t.update(
- () => Sm(e.scrRef.chapterNum, e.scrRef.verseNum),
- { tag: Br }
+ () => zm(e.scrRef.chapterNum, e.scrRef.verseNum),
+ { tag: Hr }
);
});
}
-function Sm(e, t) {
- const r = $c(w()), n = Xc(r)?.getNumber(), i = xp(r);
- if ((i ? parseInt(i.getNumber() ?? "1", 10) : 1) === e && n && (Ep(n) ? Mm(t, n) : parseInt(n, 10) === t))
+function zm(e, t) {
+ const r = Bc(w()), n = al(r)?.getNumber(), i = Ip(r);
+ if ((i ? parseInt(i.getNumber() ?? "1", 10) : 1) === e && n && (jp(n) ? jm(t, n) : parseInt(n, 10) === t))
return;
- const o = Fe().getChildren(), a = Tp(o, e);
+ const o = ze().getChildren(), a = Lp(o, e);
if (!a) return;
- const c = Ck(o, a), l = mk(c, !0);
- _k(c, l);
- let u;
+ const c = Kk(o, a), l = $k(c, !0);
+ zk(c, l);
+ let d;
try {
- u = ix(c, t);
+ d = Cx(c, t);
} catch {
return;
}
- u && (ae(u) ? !v(u.getFirstChild()) && yi(u) || Qt(u, 0) : t1(u));
+ d && (ce(d) ? !M(d.getFirstChild()) && xi(d) || Xt(d, 0) : R1(d));
}
-function t1(e) {
+function R1(e) {
const t = e.getParent();
if (!t) return;
const r = e.getIndexWithinParent() + 1, n = t.getChildAtIndex(r);
- if (!n || ge(n)) {
- Qt(t, r);
+ if (!n || he(n)) {
+ Xt(t, r);
return;
}
- const i = _o(t, r);
+ const i = Mo(t, r);
if (i) {
i.select(0, 0);
return;
}
- if (v(e)) {
+ if (M(e)) {
const o = e.getTextContentSize();
e.select(o, o);
return;
}
- const s = F(n) && !j(n) ? vm(n) : void 0;
- s ? s.select(0, 0) : Qt(t, r);
+ const s = L(n) && !z(n) ? Km(n) : void 0;
+ s ? s.select(0, 0) : Xt(t, r);
}
-function vm(e) {
+function Km(e) {
const t = e.getFirstChild();
- if (v(t)) return t;
- if (F(t) && !j(t)) return vm(t);
+ if (M(t)) return t;
+ if (L(t) && !z(t)) return Km(t);
}
-function Ps(e) {
+function Rs(e) {
return e.read(() => {
- const t = Fe().getChildren().find(Ge);
- return `${Fl()?.getCode() ?? ""}|${t?.getNumber() ?? ""}`;
+ const t = ze().getChildren().find(Ge);
+ return `${eu()?.getCode() ?? ""}|${t?.getNumber() ?? ""}`;
});
}
-function r1(e, t) {
- e.phase !== "navigating" && t && (n1(t, e.scrRef) || Em(e, i1(t, e.scrRef)));
+function $1(e, t) {
+ e.phase !== "navigating" && t && (L1(t, e.scrRef) || Bm(e, I1(t, e.scrRef)));
}
-function n1(e, t) {
- return e.book && e.book !== t.book || e.chapterNum !== t.chapterNum ? !1 : e.verse ? Mm(t.verseNum, e.verse) : t.verseNum === e.verseNum;
+function L1(e, t) {
+ return e.book && e.book !== t.book || e.chapterNum !== t.chapterNum ? !1 : e.verse ? jm(t.verseNum, e.verse) : t.verseNum === e.verseNum;
}
-function Mm(e, t) {
+function jm(e, t) {
try {
- return Ic(e, t);
+ return Vc(e, t);
} catch {
return !1;
}
}
-function i1(e, t) {
+function I1(e, t) {
const r = {
book: e.book || t.book,
chapterNum: e.chapterNum,
@@ -25146,26 +25493,26 @@ function i1(e, t) {
};
return e.verse != null && (r.verse = e.verse), t.versificationStr != null && (r.versificationStr = t.versificationStr), r;
}
-const s1 = 8;
-function Em(e, t) {
- return so(t, e.scrRef) || e.pendingEchoes.some((r) => so(r, t)) ? !1 : (e.pendingEchoes.push(t), e.pendingEchoes.length > s1 && e.pendingEchoes.shift(), e.onScrRefChange(t), !0);
+const D1 = 8;
+function Bm(e, t) {
+ return uo(t, e.scrRef) || e.pendingEchoes.some((r) => uo(r, t)) ? !1 : (e.pendingEchoes.push(t), e.pendingEchoes.length > D1 && e.pendingEchoes.shift(), e.onScrRefChange(t), !0);
}
-function so(e, t) {
+function uo(e, t) {
return e.book === t.book && e.chapterNum === t.chapterNum && e.verseNum === t.verseNum && (e.verse ?? void 0) === (t.verse ?? void 0);
}
-function o1(e) {
+function U1(e) {
e.phase = "idle";
}
-function a1(e) {
- return pt(e) ? `${e.__code}` : Pe(e) ? `${e.__marker} "${e.__number}"` : D(e) ? `${e.__marker}` : fs(e) ? `${e.__marker} "${e.__number}"` : St(e) ? `${e.__caller}` : wn(e) ? `${e.__marker} "${e.__number}"` : j(e) ? `${e.__marker} "${e.__caller}"` + (e.__isCollapsed ? " (collapsed)" : " (expanded)") : ae(e) ? `${e.__marker}` : v(e) ? `"${e.__text}"${c1(e)}` : Ce(e) ? `ids: [ ${JSON.stringify(e.getTypedIDs())} ]` : Oe(e) ? `${e.__marker} "${e.__number}"` : "";
+function F1(e) {
+ return pt(e) ? `${e.__code}` : Ee(e) ? `${e.__marker} "${e.__number}"` : I(e) ? `${e.__marker}` : ms(e) ? `${e.__marker} "${e.__number}"` : ht(e) ? `${e.__caller}` : Ln(e) ? `${e.__marker} "${e.__number}"` : z(e) ? `${e.__marker} "${e.__caller}"` + (e.__isCollapsed ? " (collapsed)" : " (expanded)") : ce(e) ? `${e.__marker}` : M(e) ? `"${e.__text}"${z1(e)}` : _e(e) ? `ids: [ ${JSON.stringify(e.getTypedIDs())} ]` : we(e) ? `${e.__marker} "${e.__number}"` : "";
}
-function c1(e) {
- return e.__state ? " " + JSON.stringify(e.__state.toJSON()[ds]) : "";
+function z1(e) {
+ return e.__state ? " " + JSON.stringify(e.__state.toJSON()[gs]) : "";
}
-function l1() {
- const [e] = ce();
- return /* @__PURE__ */ C(
- Fy,
+function K1() {
+ const [e] = ae();
+ return /* @__PURE__ */ S(
+ sb,
{
viewClassName: "tree-view-output",
treeTypeButtonClassName: "debug-treetype-button",
@@ -25173,51 +25520,51 @@ function l1() {
timeTravelButtonClassName: "debug-timetravel-button",
timeTravelPanelSliderClassName: "debug-timetravel-panel-slider",
timeTravelPanelButtonClassName: "debug-timetravel-panel-button",
- customPrintNode: a1,
+ customPrintNode: F1,
editor: e
}
);
}
-const Am = gf(null), cf = 4;
-function u1({
+const Vm = Pf(null), _f = 4;
+function j1({
children: e,
className: t,
onClick: r,
title: n
}) {
- const i = Z(null), s = mf(Am);
+ const i = Z(null), s = Nf(Vm);
if (s === null)
throw new Error("DropDownItem must be used within a DropDown");
const { registerItem: o } = s;
- return z(() => {
+ return F(() => {
i && i.current && o(i);
- }, [i, o]), /* @__PURE__ */ C("button", { className: t, onClick: r, ref: i, title: n, type: "button", children: e });
+ }, [i, o]), /* @__PURE__ */ S("button", { className: t, onClick: r, ref: i, title: n, type: "button", children: e });
}
-function d1({
+function B1({
children: e,
dropDownRef: t,
onClose: r
}) {
- const [n, i] = de(), [s, o] = de(), a = he(
- (u) => {
- i((d) => d ? [...d, u] : [u]);
+ const [n, i] = de(), [s, o] = de(), a = me(
+ (d) => {
+ i((u) => u ? [...u, d] : [d]);
},
[i]
- ), c = (u) => {
+ ), c = (d) => {
if (!n) return;
- const d = u.key;
- ["Escape", "ArrowUp", "ArrowDown", "Tab"].includes(d) && u.preventDefault(), d === "Escape" || d === "Tab" ? r() : d === "ArrowUp" ? o((f) => {
+ const u = d.key;
+ ["Escape", "ArrowUp", "ArrowDown", "Tab"].includes(u) && d.preventDefault(), u === "Escape" || u === "Tab" ? r() : u === "ArrowUp" ? o((f) => {
if (!f) return n[0];
const p = n.indexOf(f) - 1;
return n[p === -1 ? n.length - 1 : p];
- }) : d === "ArrowDown" && o((f) => f ? n[n.indexOf(f) + 1] : n[0]);
- }, l = ze(() => ({ registerItem: a }), [a]);
- return z(() => {
- const u = s ?? n?.[0];
- u?.current && u.current.focus();
- }, [n, s]), /* @__PURE__ */ C(Am.Provider, { value: l, children: /* @__PURE__ */ C("div", { className: "dropdown", ref: t, onKeyDown: c, children: e }) });
-}
-function f1({
+ }) : u === "ArrowDown" && o((f) => f ? n[n.indexOf(f) + 1] : n[0]);
+ }, l = Ke(() => ({ registerItem: a }), [a]);
+ return F(() => {
+ const d = s ?? n?.[0];
+ d?.current && d.current.focus();
+ }, [n, s]), /* @__PURE__ */ S(Vm.Provider, { value: l, children: /* @__PURE__ */ S("div", { className: "dropdown", ref: t, onKeyDown: c, children: e }) });
+}
+function V1({
disabled: e = !1,
buttonLabel: t,
buttonAriaLabel: r,
@@ -25226,21 +25573,21 @@ function f1({
children: s,
stopCloseOnClickSelf: o
}) {
- const a = Z(null), c = Z(null), [l, u] = de(!1), d = () => {
- u(!1), c && c.current && c.current.focus();
+ const a = Z(null), c = Z(null), [l, d] = de(!1), u = () => {
+ d(!1), c && c.current && c.current.focus();
};
- return z(() => {
+ return F(() => {
const f = c.current, p = a.current;
if (l && f !== null && p !== null) {
- const { top: m, left: g } = f.getBoundingClientRect();
- p.style.top = `${m + f.offsetHeight + cf}px`, p.style.left = `${Math.min(g, window.innerWidth - p.offsetWidth - 20)}px`;
+ const { top: y, left: h } = f.getBoundingClientRect();
+ p.style.top = `${y + f.offsetHeight + _f}px`, p.style.left = `${Math.min(h, window.innerWidth - p.offsetWidth - 20)}px`;
}
- }, [a, c, l]), z(() => {
+ }, [a, c, l]), F(() => {
const f = c.current;
if (f !== null && l) {
- const p = (m) => {
- const g = m.target;
- o && a.current && a.current.contains(g) || f.contains(g) || u(!1);
+ const p = (y) => {
+ const h = y.target;
+ o && a.current && a.current.contains(h) || f.contains(h) || d(!1);
};
return document.addEventListener("click", p), () => {
document.removeEventListener("click", p);
@@ -25248,20 +25595,20 @@ function f1({
}
return () => {
};
- }, [a, c, l, o]), z(() => {
+ }, [a, c, l, o]), F(() => {
const f = () => {
if (l) {
- const p = c.current, m = a.current;
- if (p !== null && m !== null) {
- const { top: g } = p.getBoundingClientRect(), y = g + p.offsetHeight + cf;
- y !== m.getBoundingClientRect().top && (m.style.top = `${y}px`);
+ const p = c.current, y = a.current;
+ if (p !== null && y !== null) {
+ const { top: h } = p.getBoundingClientRect(), m = h + p.offsetHeight + _f;
+ m !== y.getBoundingClientRect().top && (y.style.top = `${m}px`);
}
}
};
return document.addEventListener("scroll", f), () => {
document.removeEventListener("scroll", f);
};
- }, [c, a, l]), /* @__PURE__ */ Te(gn, { children: [
+ }, [c, a, l]), /* @__PURE__ */ Te(yn, { children: [
/* @__PURE__ */ Te(
"button",
{
@@ -25269,22 +25616,22 @@ function f1({
disabled: e,
"aria-label": r || t,
className: n,
- onClick: () => u(!l),
+ onClick: () => d(!l),
ref: c,
children: [
- i && /* @__PURE__ */ C("span", { className: i }),
- t && /* @__PURE__ */ C("span", { className: "text dropdown-button-text", children: t }),
- /* @__PURE__ */ C("i", { className: "chevron-down" })
+ i && /* @__PURE__ */ S("span", { className: i }),
+ t && /* @__PURE__ */ S("span", { className: "text dropdown-button-text", children: t }),
+ /* @__PURE__ */ S("i", { className: "chevron-down" })
]
}
),
- l && hn(
- /* @__PURE__ */ C(d1, { dropDownRef: a, onClose: d, children: s }),
+ l && mn(
+ /* @__PURE__ */ S(B1, { dropDownRef: a, onClose: u, children: s }),
document.body
)
] });
}
-const fc = {
+const _c = {
m: "m - Paragraph - Margin - No First Line Indent",
ms: "ms - Heading - Major Section Level 1",
nb: "nb - Paragraph - No Break with Previous Paragraph",
@@ -25295,8 +25642,8 @@ const fc = {
r: "r - Heading - Parallel References",
s: "s - Heading - Section Level 1"
// do not allow `b - Poetry - Stanza Break (Blank Line)` here to avoid a USFM validity issue.
-}, pc = {
- ...fc,
+}, Cc = {
+ ..._c,
// File / header
cl: "cl - Chapter - Publishing Label",
h: "h - File - Header",
@@ -25401,27 +25748,27 @@ const fc = {
// Tables
tr: "tr - Table - Row"
};
-function p1({
+function W1({
editorRef: e,
blockMarker: t,
disabled: r = !1
}) {
- return /* @__PURE__ */ C(
- f1,
+ return /* @__PURE__ */ S(
+ V1,
{
disabled: r,
buttonClassName: "toolbar-item block-controls",
- buttonIconClassName: "icon block-marker " + h1(t),
- buttonLabel: g1(t),
+ buttonIconClassName: "icon block-marker " + H1(t),
+ buttonLabel: G1(t),
buttonAriaLabel: "Formatting options for block type",
- children: Object.keys(fc).map((n) => /* @__PURE__ */ Te(
- u1,
+ children: Object.keys(_c).map((n) => /* @__PURE__ */ Te(
+ j1,
{
- className: "item block-marker " + m1(t === n),
+ className: "item block-marker " + J1(t === n),
onClick: () => e.current?.formatPara(n),
children: [
- /* @__PURE__ */ C("i", { className: "icon block-marker " + n }),
- /* @__PURE__ */ C("span", { className: "text usfm_" + n, children: fc[n] })
+ /* @__PURE__ */ S("i", { className: "icon block-marker " + n }),
+ /* @__PURE__ */ S("span", { className: "text usfm_" + n, children: _c[n] })
]
},
n
@@ -25429,90 +25776,90 @@ function p1({
}
);
}
-function h1(e) {
- return e && e in pc ? e : "ban";
+function H1(e) {
+ return e && e in Cc ? e : "ban";
}
-function g1(e) {
- return e && e in pc ? pc[e] : "No Style";
+function G1(e) {
+ return e && e in Cc ? Cc[e] : "No Style";
}
-function m1(e) {
+function J1(e) {
return e ? "active dropdown-item-active" : "";
}
-function lf() {
- return /* @__PURE__ */ C("div", { className: "divider" });
+function Cf() {
+ return /* @__PURE__ */ S("div", { className: "divider" });
}
-const y1 = En(function({ editorRef: t, isReadonly: r = !1, onStateChange: n }, i) {
- const [s] = ce(), [o, a] = de(s), [c, l] = de(), [u, d] = de(!1), [f, p] = de(!1), m = he(
+const Y1 = An(function({ editorRef: t, isReadonly: r = !1, onStateChange: n }, i) {
+ const [s] = ae(), [o, a] = de(s), [c, l] = de(), [d, u] = de(!1), [f, p] = de(!1), y = me(
({
- canUndo: g,
- canRedo: y,
- blockMarker: k,
- contextMarker: _
+ canUndo: h,
+ canRedo: m,
+ blockMarker: b,
+ contextMarker: x
}) => {
- d(g), p(y), l(k), n?.({
- canUndo: g,
- canRedo: y,
- blockMarker: k,
- contextMarker: _
+ u(h), p(m), l(b), n?.({
+ canUndo: h,
+ canRedo: m,
+ blockMarker: b,
+ contextMarker: x
});
},
[n]
);
- return z(() => s.registerCommand(
+ return F(() => s.registerCommand(
ur,
- (g, y) => (a(y), !1),
- Wt
- ), [s]), /* @__PURE__ */ Te(gn, { children: [
- /* @__PURE__ */ C(Bh, { onStateChange: m }),
+ (h, m) => (a(m), !1),
+ xt
+ ), [s]), /* @__PURE__ */ Te(yn, { children: [
+ /* @__PURE__ */ S(sg, { onStateChange: y }),
/* @__PURE__ */ Te("div", { className: "toolbar", children: [
- /* @__PURE__ */ C(
+ /* @__PURE__ */ S(
"button",
{
- disabled: !u || r,
+ disabled: !d || r,
onClick: () => {
- o.dispatchCommand(Mf, void 0);
+ o.dispatchCommand(Kf, void 0);
},
- title: $s ? "Undo (⌘Z)" : "Undo (Ctrl+Z)",
+ title: si ? "Undo (⌘Z)" : "Undo (Ctrl+Z)",
type: "button",
className: "toolbar-item spaced",
"aria-label": "Undo",
- children: /* @__PURE__ */ C("i", { className: "format undo" })
+ children: /* @__PURE__ */ S("i", { className: "format undo" })
}
),
- /* @__PURE__ */ C(
+ /* @__PURE__ */ S(
"button",
{
disabled: !f || r,
onClick: () => {
- o.dispatchCommand(Ef, void 0);
+ o.dispatchCommand(jf, void 0);
},
- title: $s ? "Redo (⌘Y)" : "Redo (Ctrl+Y)",
+ title: si ? "Redo (⌘Y)" : "Redo (Ctrl+Y)",
type: "button",
className: "toolbar-item",
"aria-label": "Redo",
- children: /* @__PURE__ */ C("i", { className: "format redo" })
+ children: /* @__PURE__ */ S("i", { className: "format redo" })
}
),
- /* @__PURE__ */ C(lf, {}),
- o === s && /* @__PURE__ */ Te(gn, { children: [
- /* @__PURE__ */ C(
- p1,
+ /* @__PURE__ */ S(Cf, {}),
+ o === s && /* @__PURE__ */ Te(yn, { children: [
+ /* @__PURE__ */ S(
+ W1,
{
editorRef: t,
blockMarker: c,
disabled: r
}
),
- /* @__PURE__ */ C(lf, {})
+ /* @__PURE__ */ S(Cf, {})
] }),
- /* @__PURE__ */ C("div", { ref: i, className: "end-container" })
+ /* @__PURE__ */ S("div", { ref: i, className: "end-container" })
] })
] });
-}), b1 = Eo(), k1 = {}, T1 = {};
-function x1() {
- return /* @__PURE__ */ C("div", { className: "editor-placeholder", children: "Enter some Scripture..." });
+}), X1 = Oo(), Q1 = {}, Z1 = {};
+function eP() {
+ return /* @__PURE__ */ S("div", { className: "editor-placeholder", children: "Enter some Scripture..." });
}
-const Pm = En(function({
+const Wm = An(function({
defaultUsj: t,
scrRef: r,
onScrRefChange: n,
@@ -25522,612 +25869,615 @@ const Pm = En(function({
options: a,
logger: c,
children: l
-}, u) {
- const d = Z(null), f = Z(null), p = Z(null), m = Z(t), g = Z(void 0), y = Z(void 0), k = Z(void 0), _ = Z(void 0), S = Z(!1), [P, A] = de(t), [B, M] = de(0), [q, $] = de(), {
- isReadonly: J = !1,
- structureProtectionMode: Q = "off",
- hasExternalUI: Me = !1,
- hasSpellCheck: re = !1,
- textDirection: qe = "ltr",
+}, d) {
+ const u = Z(null), f = Z(null), p = Z(null), y = Z(t), h = Z(void 0), m = Z(void 0), b = Z(void 0), x = Z(void 0), _ = Z(!1), [A, E] = de(t), [D, V] = de(0), [j, v] = de(), {
+ isReadonly: K = !1,
+ structureProtectionMode: W = "off",
+ hasExternalUI: fe = !1,
+ hasSpellCheck: X = !1,
+ textDirection: Re = "ltr",
markerMenuTrigger: be = "\\",
- view: tr,
- nodes: Re,
- debug: en = !1,
+ view: er,
+ nodes: $e,
+ debug: nn = !1,
contextMenu: gr,
- styleInfo: vt,
- markerSettleDelayMs: te
- } = a ?? T1, E = tr ?? b1, Y = ns(E) && (E.markerMode !== "hidden" || !E.hasSpacing || E.hasGutterParaMarkers || E.hasActiveTextFocusBox) ? {
- ...E,
+ styleInfo: Mt,
+ markerSettleDelayMs: re,
+ copyLimit: P
+ } = a ?? Z1, G = er ?? X1, ue = cs(G) && (G.markerMode !== "hidden" || !G.hasSpacing || G.hasGutterParaMarkers || G.hasActiveTextFocusBox) ? {
+ ...G,
markerMode: "hidden",
hasSpacing: !0,
hasGutterParaMarkers: !1,
hasActiveTextFocusBox: !1
- } : E, le = Z(Y);
- wt(le.current, Y) || (le.current = Y);
- const W = le.current, xe = ze(() => Re ?? k1, [Re]), ht = ze(() => gr, [gr]), zt = ze(
- () => JT(vt ?? Vs),
- [vt]
- ), gt = Z(c);
- wt(gt.current, c) || (gt.current = c);
- const We = gt.current, ct = ns(W), ue = J || ct, Rn = Y !== E;
- z(() => {
- ct && !J && We?.error(
+ } : G, Me = Z(ue);
+ qt(Me.current, ue) || (Me.current = ue);
+ const Q = Me.current, ve = Ke(() => $e ?? Q1, [$e]), mr = Ke(() => gr, [gr]), wt = Ke(
+ () => hx(Mt ?? Js),
+ [Mt]
+ ), sn = Z(c);
+ qt(sn.current, c) || (sn.current = c);
+ const We = sn.current, le = cs(Q), gt = K || le, xe = ue !== G;
+ F(() => {
+ le && !K && We?.error(
"Editor: the block verse layout is read-only; ignoring `isReadonly: false`. Set `isReadonly: true` alongside `verseLayout: 'block'`."
- ), Rn && We?.warn(
+ ), xe && We?.warn(
"Editor: a visible `markerMode`, `hasSpacing: false`, `hasGutterParaMarkers` and `hasActiveTextFocusBox` are not supported with the block verse layout and are ignored."
- ), W?.markerMode === "visible" && !J && We?.warn(
+ ), Q?.markerMode === "visible" && !K && We?.warn(
"Editor: `markerMode: 'visible'` renders markers as read-only glyphs, but the editor is editable and no marker repair runs there. Pass `isReadonly: true` alongside it."
);
- }, [ct, J, Rn, We, W?.markerMode]);
- const ye = Z(null), Ti = ze(() => {
- if (W.markerMode !== "editable") return;
- const R = vt ?? Vs;
+ }, [le, K, xe, We, Q?.markerMode]);
+ const yr = Z(null), Ne = Ke(() => {
+ if (Q.markerMode !== "editable") return;
+ const q = Mt ?? Js;
return {
- getContext: () => ye.current?.getMarkerMenuContext(),
+ getContext: () => yr.current?.getMarkerMenuContext(),
// The context object is always one this same harness produced via `getContext()` above
// (never externally supplied), so it really is a full `MarkerMenuContext` at runtime -
// the cast bridges shared-react's structural `MarkerMenuContextLike` back to it.
- getItems: (K) => FM(
- R,
- K,
- xe.extraValidMarkers
+ getItems: (B) => tA(
+ q,
+ B,
+ ve.extraValidMarkers
),
- getEnterItems: (K) => zM(
- R,
- K,
- xe.extraValidMarkers
+ getEnterItems: (B) => rA(
+ q,
+ B,
+ ve.extraValidMarkers
),
- apply: (K, G) => {
- const X = ye.current;
- X && (G.trigger === "enter" ? X.splitParagraphWithMarker(K.marker) : X.applyMarkerMenuSelection(K, G));
+ apply: (B, J) => {
+ const ee = yr.current;
+ ee && (J.trigger === "enter" ? ee.splitParagraphWithMarker(B.marker) : ee.applyMarkerMenuSelection(B, J));
},
- commitTypedCloser: (K) => {
- ye.current?.commitTypedCloser(K);
+ commitTypedCloser: (B) => {
+ yr.current?.commitTypedCloser(B);
}
};
- }, [W, vt, xe.extraValidMarkers]), ve = (R) => {
- S.current || (S.current = !0, gt.current?.warn(
- `Editor: cannot ${R} in the block verse layout; its paragraphs are split across verse blocks, so editor content indexes do not match the source USJ.`
+ }, [Q, Mt, ve.extraValidMarkers]), br = (q) => {
+ _.current || (_.current = !0, sn.current?.warn(
+ `Editor: cannot ${q} in the block verse layout; its paragraphs are split across verse blocks, so editor content indexes do not match the source USJ.`
));
- }, wr = (R) => {
- if (ct)
+ }, vi = (q) => {
+ if (le)
throw new Error(
- `Cannot ${R} in the block verse layout; it is a read-only view whose structure does not match the source USJ.`
+ `Cannot ${q} in the block verse layout; it is a read-only view whose structure does not match the source USJ.`
);
- }, qr = (R) => {
- if (wr(R), ue) throw new Error(`Cannot ${R} in readonly mode`);
- }, rr = ze(
+ }, mt = (q) => {
+ if (vi(q), gt) throw new Error(`Cannot ${q} in readonly mode`);
+ }, xs = Ke(
() => ({
namespace: "platformEditor",
- theme: { ..._g, showCharMarkerTitles: W.showCharMarkerTitles },
- editable: !ue,
+ theme: { ...Ig, showCharMarkerTitles: Q.showCharMarkerTitles },
+ editable: !gt,
editorState: void 0,
// Handling of errors during update
- onError(R) {
- throw R;
+ onError(q) {
+ throw q;
},
// Registered per layout so an editor that isn't using block verse never holds its node.
- nodes: [Qe, ...ct ? a_ : nl]
+ nodes: [tt, ...le ? M_ : pl]
}),
- [ue, ct, W.showCharMarkerTitles]
+ [gt, le, Q.showCharMarkerTitles]
);
- Os.initialize(We);
- function Rr(R) {
- if (R !== void 0 && !cM(R, xe.extraValidMarkers))
- throw new Error(`Unsupported character marker '${R}'`);
- }
- const tn = he(() => {
- const R = d.current;
- if (!R) return m.current;
- const K = Ou(R), G = y.current;
- if ((!K || K.size === 0) && !G) return m.current;
- const X = R.getEditorState(), _e = X.toJSON();
- return X.read(
- () => GA(
- _e,
- K ?? /* @__PURE__ */ new Set(),
- { viewOptions: W, getMarker: zt, logger: We },
- G,
- k.current
+ Ls.initialize(We);
+ function kr(q) {
+ if (q !== void 0 && !EM(q, ve.extraValidMarkers))
+ throw new Error(`Unsupported character marker '${q}'`);
+ }
+ const Dn = me(() => {
+ const q = u.current;
+ if (!q) return y.current;
+ const B = ju(q), J = m.current;
+ if ((!B || B.size === 0) && !J) return y.current;
+ const ee = q.getEditorState(), Se = ee.toJSON();
+ return ee.read(
+ () => E1(
+ Se,
+ B ?? /* @__PURE__ */ new Set(),
+ { viewOptions: Q, getMarker: wt, logger: We },
+ J,
+ b.current
)
- ) ?? m.current;
- }, [W, zt, We]), xi = {
+ ) ?? y.current;
+ }, [Q, wt, We]), Si = {
focus() {
- d.current?.focus();
+ u.current?.focus();
},
isFocused() {
- const R = d.current?.getRootElement();
- return !!R && R.ownerDocument.activeElement === R;
+ const q = u.current?.getRootElement();
+ return !!q && q.ownerDocument.activeElement === q;
},
undo() {
- d.current?.dispatchCommand(Mf, void 0);
+ u.current?.dispatchCommand(Kf, void 0);
},
redo() {
- d.current?.dispatchCommand(Ef, void 0);
+ u.current?.dispatchCommand(jf, void 0);
},
// Both leave the clipboard untouched when nothing is selected, rather than writing a
// placeholder over it — `ClipboardPlugin`'s guard claims the command (shared-react's
// `registerEmptyCopyGuard`). Going through `copySelection`/`cutSelection` rather than
// dispatching here keeps that one seam named.
cut() {
- qr("cut"), d.current && pl(d.current);
+ mt("cut"), u.current && vl(u.current);
},
copy() {
- d.current && fl(d.current);
+ u.current && Cl(u.current);
},
paste() {
- qr("paste"), d.current && hl(d.current);
+ mt("paste"), u.current && Sl(u.current);
},
pastePlainText() {
- qr("paste as plain text"), d.current && gl(d.current);
+ mt("paste as plain text"), u.current && Ml(u.current);
},
getUsj() {
- return tn();
+ return Dn();
},
commitPendingMarkerEdits() {
- d.current?.update(
+ u.current?.update(
() => {
- d.current?.dispatchCommand(mm, void 0);
+ u.current?.dispatchCommand(qm, void 0);
},
{ discrete: !0 }
);
},
- setTransientInput(R) {
- if (!R) {
- y.current = void 0;
+ setTransientInput(q) {
+ if (!q) {
+ m.current = void 0;
return;
}
- const K = d.current?.getEditorState().read(() => {
- const G = w();
- return N(G) && G.isCollapsed() ? G.focus.key : void 0;
+ const B = u.current?.getEditorState().read(() => {
+ const J = w();
+ return N(J) && J.isCollapsed() ? J.focus.key : void 0;
});
- y.current = { input: R, nodeKey: K ?? k.current?.key };
+ m.current = { input: q, nodeKey: B ?? b.current?.key };
},
- setUsj(R) {
- if (!wt(m.current, R)) {
- m.current = R, y.current = void 0;
- const K = wt(P, R);
- A(R), K && M((G) => G + 1);
+ setUsj(q) {
+ if (!qt(y.current, q)) {
+ y.current = q, m.current = void 0;
+ const B = qt(A, q);
+ E(q), B && V((J) => J + 1);
}
},
- applyUpdate(R, K = "remote") {
- if (ct && K === "remote") {
- gt.current?.error(
+ applyUpdate(q, B = "remote") {
+ if (le && B === "remote") {
+ sn.current?.error(
"Editor: ignoring a remote update in the block verse layout; reload the view with the new USJ instead."
);
return;
}
- wr("apply an update"), d.current?.update(
+ vi("apply an update"), u.current?.update(
() => {
- K === "remote" && jr(Vi), R_(R, W, xe, We);
+ B === "remote" && Wr(Yi), Z_(q, Q, ve, We);
},
{ discrete: !0 }
);
- const G = d.current?.getEditorState();
- if (!G) return;
- const X = Os.deserializeEditorState(G, W);
- if (X) {
- const _e = !wt(m.current, X);
- if (_e && (m.current = X), _e || !wt(P, X)) {
- const lt = ju(R, G, "apply");
- _.current = X, s?.(X, R, K, lt);
+ const J = u.current?.getEditorState();
+ if (!J) return;
+ const ee = Ls.deserializeEditorState(J, Q);
+ if (ee) {
+ const Se = !qt(y.current, ee);
+ if (Se && (y.current = ee), Se || !qt(A, ee)) {
+ const Qe = td(q, J, "apply");
+ x.current = ee, s?.(ee, q, B, Qe);
}
}
},
- replaceEmbedUpdate(R, K) {
- const G = d.current?.read(() => xx(R, K));
- G ? this.applyUpdate(G) : c?.warn(
- `replaceEmbedUpdate: no embed found for key "${R}" — update dropped (stale key after a setUsj reload?)`
+ replaceEmbedUpdate(q, B) {
+ const J = u.current?.read(() => Fx(q, B));
+ J ? this.applyUpdate(J) : c?.warn(
+ `replaceEmbedUpdate: no embed found for key "${q}" — update dropped (stale key after a setUsj reload?)`
);
},
getSelection() {
- if (ct) {
- ve("get the selection");
+ if (le) {
+ br("get the selection");
return;
}
- return d.current?.read(tl);
+ return u.current?.read(dl);
},
- setSelection(R) {
- if (ct) {
- ve("set the selection");
+ setSelection(q) {
+ if (le) {
+ br("set the selection");
return;
}
- d.current?.update(() => {
- const K = Mo(R);
- K !== void 0 && (ei(K), jr(Uf));
+ u.current?.update(() => {
+ const B = No(q);
+ B !== void 0 && (ii(B), Wr(tp));
});
},
- setAnnotation(R, K, G, X, _e) {
- if (ct) {
- ve("set an annotation");
+ setAnnotation(q, B, J, ee, Se) {
+ if (le) {
+ br("set an annotation");
return;
}
- let lt, Ot, rn, nn;
- typeof X == "function" || X === void 0 ? (lt = X, Ot = _e) : (lt = X.onClick, Ot = X.onRemove, rn = X.onMouseEnter, nn = X.onMouseLeave), f.current?.setAnnotation(
- R,
- ku(K),
- G,
- lt,
- Ot,
- rn,
- nn
+ let Qe, rr, on, Mi;
+ typeof ee == "function" || ee === void 0 ? (Qe = ee, rr = Se) : (Qe = ee.onClick, rr = ee.onRemove, on = ee.onMouseEnter, Mi = ee.onMouseLeave), f.current?.setAnnotation(
+ q,
+ Ou(B),
+ J,
+ Qe,
+ rr,
+ on,
+ Mi
);
},
- removeAnnotation(R, K) {
- f.current?.removeAnnotation(ku(R), K);
+ removeAnnotation(q, B) {
+ f.current?.removeAnnotation(Ou(q), B);
},
- formatPara(R) {
- qr("format a paragraph"), d.current?.update(
+ formatPara(q) {
+ mt("format a paragraph"), u.current?.update(
() => {
- const K = w();
- if (!N(K)) {
+ const B = w();
+ if (!N(B)) {
c?.warn(
- `formatPara refused: no range selection to retag with "${R}" (restore the caret before applying, as the marker palettes do)`
+ `formatPara refused: no range selection to retag with "${q}" (restore the caret before applying, as the marker palettes do)`
);
return;
}
- jy(K, () => Ji(R));
- const G = w();
- if (!N(G)) return;
- const X = /* @__PURE__ */ new Set();
- G.getNodes().forEach((_e) => {
- const lt = _e.getTopLevelElement();
- ae(lt) && X.add(lt);
- }), X.forEach((_e) => Hg(_e, R, W));
+ cb(B, () => es(q));
+ const J = w();
+ if (!N(J)) return;
+ const ee = /* @__PURE__ */ new Set();
+ J.getNodes().forEach((Se) => {
+ const Qe = Se.getTopLevelElement();
+ ce(Qe) && ee.add(Qe);
+ }), ee.forEach((Se) => km(Se, q, Q));
},
{ discrete: !0 }
);
},
- getElementByKey(R) {
- return d.current?.read(
- () => d.current?.getElementByKey(R) ?? void 0
+ getElementByKey(q) {
+ return u.current?.read(
+ () => u.current?.getElementByKey(q) ?? void 0
);
},
- removeCharacterMarker(R) {
- if (ue) throw new Error("Cannot remove character marker in readonly mode");
- Rr(R);
- let K = !1;
- return d.current?.update(
+ removeCharacterMarker(q) {
+ if (gt) throw new Error("Cannot remove character marker in readonly mode");
+ kr(q);
+ let B = !1;
+ return u.current?.update(
() => {
- const G = w();
- N(G) && (K = mg(G, R, W));
+ const J = w();
+ N(J) && (B = Og(J, q, Q));
},
{ discrete: !0 }
- ), K;
+ ), B;
},
- replaceCharacterMarker(R, K) {
- if (ue) throw new Error("Cannot replace character marker in readonly mode");
- Rr(R), Rr(K);
- let G = !1;
- return d.current?.update(
+ replaceCharacterMarker(q, B) {
+ if (gt) throw new Error("Cannot replace character marker in readonly mode");
+ kr(q), kr(B);
+ let J = !1;
+ return u.current?.update(
() => {
- const X = w();
- N(X) && (G = TM(X, R, K));
+ const ee = w();
+ N(ee) && (J = UM(ee, q, B));
},
{ discrete: !0 }
- ), G;
+ ), J;
},
- extendCharacterMarker(R, K) {
- if (ue) throw new Error("Cannot extend character marker in readonly mode");
- Rr(R), K?.forEach(
- (X) => Rr(X)
+ extendCharacterMarker(q, B) {
+ if (gt) throw new Error("Cannot extend character marker in readonly mode");
+ kr(q), B?.forEach(
+ (ee) => kr(ee)
);
- let G = !1;
- return d.current?.update(
+ let J = !1;
+ return u.current?.update(
() => {
- const X = w();
- N(X) && (G = xM(
- X,
- R,
- K,
- W
+ const ee = w();
+ N(ee) && (J = FM(
+ ee,
+ q,
+ B,
+ Q
));
},
{ discrete: !0 }
- ), G;
+ ), J;
},
- insertMarker(R) {
- if (ue) throw new Error("Cannot insert marker in readonly mode");
+ insertMarker(q) {
+ if (gt) throw new Error("Cannot insert marker in readonly mode");
if (!r) throw new Error("Cannot insert marker without a scripture reference (scrRef)");
- if (!d.current) return;
- if (!Za(R, xe.extraValidMarkers))
- throw new Error(`Unsupported marker '${R}'`);
- const K = ec(
- R,
- g,
- W,
- xe,
+ if (!u.current) return;
+ if (!ic(q, ve.extraValidMarkers))
+ throw new Error(`Unsupported marker '${q}'`);
+ const B = sc(
+ q,
+ h,
+ Q,
+ ve,
We,
void 0,
- vt
+ Mt
);
- return K.action({ editor: d.current, reference: r }), K.getInsertedNoteKey?.();
+ return B.action({ editor: u.current, reference: r }), B.getInsertedNoteKey?.();
},
getMarkerMenuContext() {
- if (!J)
- return d.current?.getEditorState().read(() => NE());
+ if (!K)
+ return u.current?.getEditorState().read(() => LA());
},
- applyMarkerMenuSelection(R, K) {
- if (J) throw new Error("Cannot apply marker menu selection in readonly mode");
+ applyMarkerMenuSelection(q, B) {
+ if (K) throw new Error("Cannot apply marker menu selection in readonly mode");
if (!r)
throw new Error(
"Cannot apply marker menu selection without a scripture reference (scrRef)"
);
- if (!d.current) return;
- if (R.kind !== "closeTag" && !Za(R.marker, xe.extraValidMarkers))
- throw new Error(`Unsupported marker '${R.marker}'`);
- let G;
- return d.current.update(() => {
- G = $E(R, K, r, {
- expandedNoteKeyRef: g,
- viewOptions: W,
- nodeOptions: xe,
+ if (!u.current) return;
+ if (q.kind !== "closeTag" && !ic(q.marker, ve.extraValidMarkers))
+ throw new Error(`Unsupported marker '${q.marker}'`);
+ let J;
+ return u.current.update(() => {
+ J = zA(q, B, r, {
+ expandedNoteKeyRef: h,
+ viewOptions: Q,
+ nodeOptions: ve,
logger: c,
- styleInfo: vt
+ styleInfo: Mt
});
- }), G;
+ }), J;
},
- splitParagraphWithMarker(R) {
- if (J) throw new Error("Cannot split paragraph in readonly mode");
- d.current && d.current.update(() => {
- Zg(R, W);
+ splitParagraphWithMarker(q) {
+ if (K) throw new Error("Cannot split paragraph in readonly mode");
+ u.current && u.current.update(() => {
+ Sm(q, Q);
});
},
- commitTypedMarker(R, K) {
- if (J) throw new Error("Cannot commit a typed marker in readonly mode");
- if (!d.current) return !1;
- let G = !1;
- return d.current.update(() => {
- G = RE(R, K), G || c?.warn(
+ commitTypedMarker(q, B) {
+ if (K) throw new Error("Cannot commit a typed marker in readonly mode");
+ if (!u.current) return !1;
+ let J = !1;
+ return u.current.update(() => {
+ J = FA(q, B), J || c?.warn(
"commitTypedMarker refused: requires a collapsed range selection (wrap a selection via applyMarkerMenuSelection instead)"
);
- }), G;
- },
- commitTypedCloser(R) {
- if (J) throw new Error("Cannot commit a typed closing marker in readonly mode");
- if (!d.current) return !1;
- let K = !1;
- return d.current.update(() => {
- K = Qg(R), K || c?.warn(
+ }), J;
+ },
+ commitTypedCloser(q) {
+ if (K) throw new Error("Cannot commit a typed closing marker in readonly mode");
+ if (!u.current) return !1;
+ let B = !1;
+ return u.current.update(() => {
+ B = vm(q), B || c?.warn(
"commitTypedCloser refused: requires a range selection to commit the closer at"
);
- }), K;
+ }), B;
},
- insertNote(R, K, G) {
- qr("insert a note"), d.current?.update(
+ insertNote(q, B, J) {
+ mt("insert a note"), u.current?.update(
() => {
- const X = Th(
- R,
- K,
- G,
+ const ee = $h(
+ q,
+ B,
+ J,
r,
- W,
- xe,
+ Q,
+ ve,
We
);
- X && !X.getIsCollapsed() && (g.current = X.getKey());
+ ee && !ee.getIsCollapsed() && (h.current = ee.getKey());
},
{ discrete: !0 }
);
},
- selectNote(R) {
- d.current?.update(() => {
- const K = Ju(R);
- K && (i_(K, W), K.getIsCollapsed() || (g.current = K.getKey()));
+ selectNote(q) {
+ u.current?.update(() => {
+ const B = ad(q);
+ B && (C_(B, Q), B.getIsCollapsed() || (h.current = B.getKey()));
});
},
- getNoteOps(R) {
- return d.current?.read(() => {
- const K = Ju(R);
- if (K)
- return Zc(K);
+ getNoteOps(q) {
+ return u.current?.read(() => {
+ const B = ad(q);
+ if (B)
+ return ll(B);
});
},
get toolbarEndRef() {
return p;
}
};
- ye.current = xi, gc(u, () => xi), z(() => {
- const R = d.current;
- if (R)
- return R.registerUpdateListener(({ editorState: K }) => {
- K.read(() => {
- const G = w();
- if (!N(G) || !G.isCollapsed()) return;
- const X = G.focus.getNode();
- v(X) && (k.current = { key: X.getKey(), offset: G.focus.offset });
+ yr.current = Si, Sc(d, () => Si), F(() => {
+ const q = u.current;
+ if (q)
+ return q.registerUpdateListener(({ editorState: B }) => {
+ B.read(() => {
+ const J = w();
+ if (!N(J) || !J.isCollapsed()) return;
+ const ee = J.focus.getNode();
+ M(ee) && (b.current = { key: ee.getKey(), offset: J.focus.offset });
});
});
}, []);
- const ms = he(
- (R, K, G, X) => {
- if (ct) return;
- const _e = Os.deserializeEditorState(R, W);
- if (_e) {
- const lt = !wt(m.current, _e);
- if (lt && (m.current = _e), lt || !wt(P, _e)) {
- const Ot = ju(X, R);
- _.current = _e, s?.(_e, X, "local", Ot);
+ const tr = me(
+ (q, B, J, ee) => {
+ if (le) return;
+ const Se = Ls.deserializeEditorState(q, Q);
+ if (Se) {
+ const Qe = !qt(y.current, Se);
+ if (Qe && (y.current = Se), Qe || !qt(A, Se)) {
+ const rr = td(ee, q);
+ x.current = Se, s?.(Se, ee, "local", rr);
}
}
},
- [P, s, W, ct]
+ [A, s, Q, le]
);
- z(() => {
- const R = d.current;
- if (!(!R || !s))
- return R.registerUpdateListener(({ tags: K, dirtyElements: G, dirtyLeaves: X }) => {
- !K.has(Cc) && (G.size === 0 && X.size === 0 || K.has(Vi) || !Ou(R)?.size) || queueMicrotask(() => {
- const _e = tn();
- !_e || wt(_.current, _e) || (_.current = _e, s(_e, void 0, "local", void 0));
+ F(() => {
+ const q = u.current;
+ if (!(!q || !s))
+ return q.registerUpdateListener(({ tags: B, dirtyElements: J, dirtyLeaves: ee }) => {
+ !B.has(wc) && (J.size === 0 && ee.size === 0 || B.has(Yi) || !ju(q)?.size) || queueMicrotask(() => {
+ const Se = Dn();
+ !Se || qt(x.current, Se) || (x.current = Se, s(Se, void 0, "local", void 0));
});
});
- }, [s, tn]);
- const Kt = he(
- (R) => {
- $(R.contextMarker), o?.(R);
+ }, [s, Dn]);
+ const Kt = me(
+ (q) => {
+ v(q.contextMarker), o?.(q);
},
[o]
);
return (
// A Lexical editor's node types are fixed when it is created, so switching layouts has to
// recreate it. The key never changes for the inline layouts, which leave `verseLayout` unset.
- /* @__PURE__ */ Te(Nf, { initialConfig: rr, children: [
- /* @__PURE__ */ C(DC, { isEditable: !ue }),
+ /* @__PURE__ */ Te(Wf, { initialConfig: xs, children: [
+ /* @__PURE__ */ S(nv, { isEditable: !gt }),
/* @__PURE__ */ Te("div", { className: "editor-container", children: [
- Me ? /* @__PURE__ */ C(Bh, { onStateChange: Kt }) : /* @__PURE__ */ C(
+ fe ? /* @__PURE__ */ S(sg, { onStateChange: Kt }) : /* @__PURE__ */ S(
"div",
{
- className: "editor-toolbar-container" + (ue ? "-readonly" : "-editable"),
- children: /* @__PURE__ */ C(
- y1,
+ className: "editor-toolbar-container" + (gt ? "-readonly" : "-editable"),
+ children: /* @__PURE__ */ S(
+ Y1,
{
ref: p,
- editorRef: ye,
- isReadonly: ue,
+ editorRef: yr,
+ isReadonly: gt,
onStateChange: Kt
}
)
}
),
/* @__PURE__ */ Te("div", { className: "editor-inner", children: [
- /* @__PURE__ */ C(wf, { editorRef: d }),
- /* @__PURE__ */ C(
- Ky,
+ /* @__PURE__ */ S(Gf, { editorRef: u }),
+ /* @__PURE__ */ S(
+ ab,
{
- contentEditable: /* @__PURE__ */ C(
- Of,
+ contentEditable: /* @__PURE__ */ S(
+ Hf,
{
- className: `editor-input usfm ${q_(W).join(" ")}${W.hasGutterParaMarkers ? " psc-gutter-markers" : ""}${W.hasActiveTextFocusBox ? " psc-active-focus" : ""}`,
- spellCheck: re
+ className: `editor-input usfm ${Q_(Q).join(" ")}${Q.hasGutterParaMarkers ? " psc-gutter-markers" : ""}${Q.hasActiveTextFocusBox ? " psc-active-focus" : ""}`,
+ spellCheck: X
}
),
- placeholder: /* @__PURE__ */ C(x1, {}),
- ErrorBoundary: qf
+ placeholder: /* @__PURE__ */ S(eP, {}),
+ ErrorBoundary: Jf
}
),
- Me && /* @__PURE__ */ C(LC, {}),
- /* @__PURE__ */ C(Rf, {}),
- r && n && /* @__PURE__ */ C(XA, { scrRef: r, onScrRefChange: n }),
- r && !Me && /* @__PURE__ */ C(
- ov,
+ fe && /* @__PURE__ */ S(rv, {}),
+ /* @__PURE__ */ S(Yf, {}),
+ r && n && /* @__PURE__ */ S(N1, { scrRef: r, onScrRefChange: n }),
+ r && !fe && /* @__PURE__ */ S(
+ SS,
{
trigger: be,
scrRef: r,
- contextMarker: q,
- getMarkerAction: (R) => ec(
- R,
- g,
- W,
- xe,
+ contextMarker: j,
+ getMarkerAction: (q) => sc(
+ q,
+ h,
+ Q,
+ ve,
We,
void 0,
- vt
+ Mt
),
- editableHarness: Ti
+ editableHarness: Ne
}
),
- /* @__PURE__ */ C(
- zC,
+ /* @__PURE__ */ S(
+ ov,
{
- scripture: P,
- scriptureRef: m,
- nodeOptions: xe,
- editorAdaptor: Cr,
- viewOptions: W,
+ scripture: A,
+ scriptureRef: y,
+ nodeOptions: ve,
+ editorAdaptor: Ar,
+ viewOptions: Q,
logger: We
},
- B
+ D
),
- /* @__PURE__ */ C(aS, { onChange: i }),
- /* @__PURE__ */ C(
- E_,
+ /* @__PURE__ */ S(Mv, { onChange: i }),
+ /* @__PURE__ */ S(
+ W_,
{
- onChange: ms,
+ onChange: tr,
ignoreSelectionChange: !0,
ignoreHistoryMergeTagChange: !0,
- ignoreTags: ub
+ ignoreTags: Pb
}
),
- /* @__PURE__ */ C(MM, { viewOptions: W }),
- /* @__PURE__ */ C(v_, { ref: f, logger: We }),
- /* @__PURE__ */ C(oC, { viewOptions: W }),
- /* @__PURE__ */ C(TC, {}),
- /* @__PURE__ */ C(MC, {}),
- W?.markerMode !== "editable" && /* @__PURE__ */ C(EC, { logger: We }),
- /* @__PURE__ */ C(OC, { options: ht }),
- /* @__PURE__ */ C(IC, {}),
- /* @__PURE__ */ C(FC, {}),
- /* @__PURE__ */ C(IE, {}),
- /* @__PURE__ */ C(
- NA,
+ /* @__PURE__ */ S(VM, { viewOptions: Q }),
+ /* @__PURE__ */ S(B_, { ref: f, logger: We }),
+ /* @__PURE__ */ S(SC, { viewOptions: Q }),
+ /* @__PURE__ */ S(UC, {}),
+ /* @__PURE__ */ S(VC, {}),
+ Q?.markerMode !== "editable" && /* @__PURE__ */ S(WC, { logger: We }),
+ /* @__PURE__ */ S(YC, { options: mr }),
+ /* @__PURE__ */ S(UE, { limit: P, viewOptions: Q }),
+ /* @__PURE__ */ S(tv, {}),
+ /* @__PURE__ */ S(sv, {}),
+ /* @__PURE__ */ S(KA, {}),
+ /* @__PURE__ */ S(
+ d1,
{
- viewOptions: W,
- getMarker: zt,
+ viewOptions: Q,
+ getMarker: wt,
logger: We,
- markerSettleDelayMs: te,
- structureProtectionMode: Q
+ markerSettleDelayMs: re,
+ structureProtectionMode: W,
+ copyLimit: P
}
),
- W?.markerMode === "visible" && /* @__PURE__ */ C(DA, { viewOptions: W }),
- /* @__PURE__ */ C(
- IA,
+ Q?.markerMode === "visible" && /* @__PURE__ */ S(DE, { viewOptions: Q, copyLimit: P }),
+ /* @__PURE__ */ S(
+ y1,
{
- styleInfo: vt,
- viewOptions: W,
+ styleInfo: Mt,
+ viewOptions: Q,
logger: We
}
),
- /* @__PURE__ */ C(
- KC,
+ /* @__PURE__ */ S(
+ av,
{
- expandedNoteKeyRef: g,
- nodeOptions: xe,
- viewOptions: W,
+ expandedNoteKeyRef: h,
+ nodeOptions: ve,
+ viewOptions: Q,
logger: We
}
),
- /* @__PURE__ */ C(oS, {}),
- /* @__PURE__ */ C(tC, {}),
- /* @__PURE__ */ C(X_, {}),
- /* @__PURE__ */ C(JA, { viewOptions: W, logger: We }),
- /* @__PURE__ */ C(cS, {}),
- /* @__PURE__ */ C(HS, { structureProtectionMode: Q }),
- /* @__PURE__ */ C(GS, { textDirection: qe }),
- /* @__PURE__ */ C(YS, {}),
- /* @__PURE__ */ C(iv, {}),
+ /* @__PURE__ */ S(Sv, {}),
+ /* @__PURE__ */ S(TC, {}),
+ /* @__PURE__ */ S(mC, {}),
+ /* @__PURE__ */ S(A1, { viewOptions: Q, logger: We }),
+ /* @__PURE__ */ S(Ev, {}),
+ /* @__PURE__ */ S(fS, { structureProtectionMode: W }),
+ /* @__PURE__ */ S(pS, { textDirection: Re }),
+ /* @__PURE__ */ S(gS, {}),
+ /* @__PURE__ */ S(CS, {}),
l
] }),
- en && /* @__PURE__ */ C(l1, {})
+ nn && /* @__PURE__ */ S(K1, {})
] })
- ] }, W.verseLayout ?? "inline")
+ ] }, Q.verseLayout ?? "inline")
);
-}), PP = En(function(t, r) {
+}), lN = An(function(t, r) {
const { children: n, ...i } = t;
- return /* @__PURE__ */ C(Pm, { ref: r, ...i });
+ return /* @__PURE__ */ S(Wm, { ref: r, ...i });
});
-function Nm() {
+function Hm() {
return Math.random().toString(36).replace(/[^a-z]+/g, "").substr(0, 5);
}
-function oo(e, t, r, n, i) {
+function fo(e, t, r, n, i) {
return {
author: t,
content: e,
deleted: i === void 0 ? !1 : i,
- id: r === void 0 ? Nm() : r,
+ id: r === void 0 ? Hm() : r,
timeStamp: n === void 0 ? performance.timeOrigin + performance.now() : n,
type: "comment"
};
}
-function Om(e, t, r) {
+function Gm(e, t, r) {
return {
comments: t,
- id: r === void 0 ? Nm() : r,
+ id: r === void 0 ? Hm() : r,
quote: e,
type: "thread"
};
}
-function uf(e) {
+function vf(e) {
return {
comments: Array.from(e.comments),
id: e.id,
@@ -26135,7 +26485,7 @@ function uf(e) {
type: "thread"
};
}
-function _1(e) {
+function tP(e) {
return {
author: e.author,
content: "[Deleted Comment]",
@@ -26145,12 +26495,12 @@ function _1(e) {
type: "comment"
};
}
-function xa(e) {
+function Sa(e) {
const t = e._changeListeners;
for (const r of t)
r();
}
-class C1 {
+class rP {
_editor;
_comments;
_changeListeners;
@@ -26187,7 +26537,7 @@ class C1 {
* @param comments - The new Comments array.
*/
setComments(t) {
- this._comments = t, xa(this);
+ this._comments = t, Sa(this);
}
/**
* Adds a comment or thread to the store.
@@ -26202,14 +26552,14 @@ class C1 {
for (let o = 0; o < i.length; o++) {
const a = i[o];
if (a.type === "thread" && a.id === r.id) {
- const c = uf(a);
+ const c = vf(a);
i.splice(o, 1, c);
const l = n !== void 0 ? n : c.comments.length;
if (this.isCollaborative() && s !== null) {
- const u = s.get(o).get("comments");
+ const d = s.get(o).get("comments");
this._withRemoteTransaction(() => {
- const d = this._createCollabSharedMap(t);
- u.insert(l, [d]);
+ const u = this._createCollabSharedMap(t);
+ d.insert(l, [u]);
});
}
c.comments.splice(l, 0, t);
@@ -26223,7 +26573,7 @@ class C1 {
s.insert(o, [a]);
}), i.splice(o, 0, t);
}
- this._comments = i, xa(this);
+ this._comments = i, Sa(this);
}
/**
* Deletes a comment or thread from the store.
@@ -26239,13 +26589,13 @@ class C1 {
for (let o = 0; o < n.length; o++) {
const a = n[o];
if (a.type === "thread" && a.id === r.id) {
- const c = uf(a);
+ const c = vf(a);
n.splice(o, 1, c);
const l = c.comments;
if (s = l.indexOf(t), this.isCollaborative() && i !== null) {
- const u = i.get(o).get("comments"), d = s;
+ const d = i.get(o).get("comments"), u = s;
this._withRemoteTransaction(() => {
- u.delete(d);
+ d.delete(u);
});
}
l.splice(s, 1);
@@ -26256,9 +26606,9 @@ class C1 {
s = n.indexOf(t), this.isCollaborative() && i !== null && this._withRemoteTransaction(() => {
i.delete(s);
}), n.splice(s, 1);
- return this._comments = n, xa(this), t.type === "comment" ? {
+ return this._comments = n, Sa(this), t.type === "comment" ? {
index: s,
- markedComment: _1(t)
+ markedComment: tP(t)
} : null;
}
/**
@@ -26288,16 +26638,16 @@ class C1 {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
_getCollabComments() {
const t = this._collabProvider;
- return t !== null ? t.doc.get("comments", ou) : null;
+ return t !== null ? t.doc.get("comments", bu) : null;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
_createCollabSharedMap(t) {
- const r = new au(), n = t.type, i = t.id;
+ const r = new ku(), n = t.type, i = t.id;
if (r.set("type", n), r.set("id", i), n === "comment")
r.set("author", t.author), r.set("content", t.content), r.set("deleted", t.deleted), r.set("timeStamp", t.timeStamp);
else {
r.set("quote", t.quote);
- const s = new ou();
+ const s = new bu();
t.comments.forEach((o, a) => {
const c = this._createCollabSharedMap(o);
s.insert(a, [c]);
@@ -26321,50 +26671,50 @@ class C1 {
} catch {
}
}, s = this._editor.registerCommand(
- ib,
+ Cb,
(a) => (n !== void 0 && i !== void 0 && (a ? (this.logger?.info("Comments connected!"), n()) : (this.logger?.info("Comments disconnected!"), i())), !1),
- Tt
+ _t
), o = (a, c) => {
if (c.origin !== this) {
for (const l of a)
- if (l instanceof sb) {
- const u = l.target, d = l.delta;
+ if (l instanceof vb) {
+ const d = l.target, u = l.delta;
let f = 0;
- for (const p of d) {
- const m = p.insert, g = p.retain, y = p.delete, k = u.parent, _ = u === r ? void 0 : k instanceof au && this._comments.find((S) => S.id === k.get("id"));
- if (Array.isArray(m)) {
- const S = f;
- m.slice().reverse().forEach((P) => {
- const A = P.get("id"), M = P.get("type") === "thread" ? Om(
- P.get("quote"),
- P.get("comments").toArray().map(
- (q) => oo(
- q.get("content"),
- q.get("author"),
- q.get("id"),
- q.get("timeStamp"),
- q.get("deleted")
+ for (const p of u) {
+ const y = p.insert, h = p.retain, m = p.delete, b = d.parent, x = d === r ? void 0 : b instanceof ku && this._comments.find((_) => _.id === b.get("id"));
+ if (Array.isArray(y)) {
+ const _ = f;
+ y.slice().reverse().forEach((A) => {
+ const E = A.get("id"), V = A.get("type") === "thread" ? Gm(
+ A.get("quote"),
+ A.get("comments").toArray().map(
+ (j) => fo(
+ j.get("content"),
+ j.get("author"),
+ j.get("id"),
+ j.get("timeStamp"),
+ j.get("deleted")
)
),
- A
- ) : oo(
- P.get("content"),
- P.get("author"),
- A,
- P.get("timeStamp"),
- P.get("deleted")
+ E
+ ) : fo(
+ A.get("content"),
+ A.get("author"),
+ E,
+ A.get("timeStamp"),
+ A.get("deleted")
);
this._withLocalTransaction(() => {
- this.addComment(M, _, S);
+ this.addComment(V, x, _);
});
});
- } else if (typeof g == "number")
- f += g;
- else if (typeof y == "number")
- for (let S = 0; S < y; S++) {
- const P = _ === void 0 || _ === !1 ? this._comments[f] : _.comments[f];
+ } else if (typeof h == "number")
+ f += h;
+ else if (typeof m == "number")
+ for (let _ = 0; _ < m; _++) {
+ const A = x === void 0 || x === !1 ? this._comments[f] : x.comments[f];
this._withLocalTransaction(() => {
- this.deleteCommentOrThread(P, _);
+ this.deleteCommentOrThread(A, x);
}), f++;
}
}
@@ -26376,35 +26726,35 @@ class C1 {
});
}
}
-function S1(e) {
+function nP(e) {
const [t, r] = de(e.getComments());
- return z(() => e.registerOnChange(() => {
+ return F(() => e.registerOnChange(() => {
r(e.getComments());
}), [e]), t;
}
-function v1({
+function iP({
onClose: e,
children: t,
title: r,
closeOnClickOutside: n
}) {
const i = Z(null);
- return z(() => {
+ return F(() => {
i.current !== null && i.current.focus();
- }, []), z(() => {
+ }, []), F(() => {
let s = null;
const o = (l) => {
l.key === "Escape" && e();
}, a = (l) => {
- const u = l.target;
- i.current !== null && !i.current.contains(u) && n && e();
+ const d = l.target;
+ i.current !== null && !i.current.contains(d) && n && e();
}, c = i.current;
return c !== null && (s = c.parentElement, s !== null && s.addEventListener("click", a)), window.addEventListener("keydown", o), () => {
window.removeEventListener("keydown", o), s !== null && s?.removeEventListener("click", a);
};
- }, [n, e]), /* @__PURE__ */ C("div", { className: "Modal__overlay", role: "dialog", children: /* @__PURE__ */ Te("div", { className: "Modal__modal", tabIndex: -1, ref: i, children: [
- /* @__PURE__ */ C("h2", { className: "Modal__title", children: r }),
- /* @__PURE__ */ C(
+ }, [n, e]), /* @__PURE__ */ S("div", { className: "Modal__overlay", role: "dialog", children: /* @__PURE__ */ Te("div", { className: "Modal__modal", tabIndex: -1, ref: i, children: [
+ /* @__PURE__ */ S("h2", { className: "Modal__title", children: r }),
+ /* @__PURE__ */ S(
"button",
{
className: "Modal__closeButton",
@@ -26414,29 +26764,29 @@ function v1({
children: "X"
}
),
- /* @__PURE__ */ C("div", { className: "Modal__content", children: t })
+ /* @__PURE__ */ S("div", { className: "Modal__content", children: t })
] }) });
}
-function M1({
+function sP({
onClose: e,
children: t,
title: r,
closeOnClickOutside: n = !1
}) {
- return hn(
- /* @__PURE__ */ C(v1, { onClose: e, title: r, closeOnClickOutside: n, children: t }),
+ return mn(
+ /* @__PURE__ */ S(iP, { onClose: e, title: r, closeOnClickOutside: n, children: t }),
document.body
);
}
-function wm() {
- const [e, t] = de(null), r = he(() => {
+function Jm() {
+ const [e, t] = de(null), r = me(() => {
t(null);
- }, []), n = ze(() => {
+ }, []), n = Ke(() => {
if (e === null)
return null;
const { title: s, content: o, closeOnClickOutside: a } = e;
- return /* @__PURE__ */ C(M1, { onClose: r, title: s, closeOnClickOutside: a, children: o });
- }, [e, r]), i = he(
+ return /* @__PURE__ */ S(sP, { onClose: r, title: s, closeOnClickOutside: a, children: o });
+ }, [e, r]), i = me(
(s, o, a = !1) => {
t({
closeOnClickOutside: a,
@@ -26448,14 +26798,14 @@ function wm() {
);
return [n, i];
}
-const E1 = {
- ..._g,
+const oP = {
+ ...Ig,
paragraph: "CommentEditorTheme__paragraph"
};
-function A1(...e) {
+function aP(...e) {
return e.filter(Boolean).join(" ");
}
-function Yr({
+function Zr({
"data-test-id": e,
children: t,
className: r,
@@ -26464,11 +26814,11 @@ function Yr({
small: s,
title: o
}) {
- return /* @__PURE__ */ C(
+ return /* @__PURE__ */ S(
"button",
{
disabled: i,
- className: A1(
+ className: aP(
"Button__root",
i && "Button__disabled",
s && "Button__small",
@@ -26482,46 +26832,46 @@ function Yr({
}
);
}
-function P1({
+function cP({
className: e
}) {
- return /* @__PURE__ */ C(Of, { className: e || "ContentEditable__root" });
+ return /* @__PURE__ */ S(Hf, { className: e || "ContentEditable__root" });
}
-function N1({
+function lP({
children: e,
className: t
}) {
- return /* @__PURE__ */ C("div", { className: t || "Placeholder__root", children: e });
+ return /* @__PURE__ */ S("div", { className: t || "Placeholder__root", children: e });
}
-const df = vf("INSERT_INLINE_COMMAND");
-function O1({
+const Sf = zf("INSERT_INLINE_COMMAND");
+function uP({
anchorKey: e,
editor: t,
showComments: r,
onAddComment: n
}) {
- const i = Z(null), s = he(() => {
+ const i = Z(null), s = me(() => {
const o = i.current, a = t.getRootElement(), c = t.getElementByKey(e);
if (o !== null && a !== null && c !== null) {
- const { right: l } = a.getBoundingClientRect(), { top: u } = c.getBoundingClientRect();
- o.style.left = `${l - 20}px`, o.style.top = `${u - 30}px`;
+ const { right: l } = a.getBoundingClientRect(), { top: d } = c.getBoundingClientRect();
+ o.style.left = `${l - 20}px`, o.style.top = `${d - 30}px`;
}
}, [e, t]);
- return z(() => (window.addEventListener("resize", s), () => {
+ return F(() => (window.addEventListener("resize", s), () => {
window.removeEventListener("resize", s);
- }), [t, s]), cs(() => {
+ }), [t, s]), ps(() => {
s();
- }, [e, t, r, s]), /* @__PURE__ */ C("div", { className: "CommentPlugin_AddCommentBox", ref: i, children: /* @__PURE__ */ C("button", { className: "CommentPlugin_AddCommentBox_button", onClick: n, children: /* @__PURE__ */ C("i", { className: "icon add-comment" }) }) });
+ }, [e, t, r, s]), /* @__PURE__ */ S("div", { className: "CommentPlugin_AddCommentBox", ref: i, children: /* @__PURE__ */ S("button", { className: "CommentPlugin_AddCommentBox_button", onClick: n, children: /* @__PURE__ */ S("i", { className: "icon add-comment" }) }) });
}
-function w1({ onEscape: e }) {
- const [t] = ce();
- return z(() => t.registerCommand(
- Sf,
+function dP({ onEscape: e }) {
+ const [t] = ae();
+ return F(() => t.registerCommand(
+ Ff,
(r) => e(r),
- Jn
+ Vr
), [t, e]), null;
}
-function qm({
+function Ym({
className: e,
autoFocus: t,
onEscape: r,
@@ -26529,120 +26879,120 @@ function qm({
editorRef: i,
placeholder: s = "Type a comment..."
}) {
- return /* @__PURE__ */ C(Nf, { initialConfig: {
+ return /* @__PURE__ */ S(Wf, { initialConfig: {
namespace: "Commenting",
nodes: [],
onError: (a) => {
throw a;
},
- theme: E1
+ theme: oP
}, children: /* @__PURE__ */ Te("div", { className: "CommentPlugin_CommentInputBox_EditorContainer", children: [
- /* @__PURE__ */ C(
- tb,
+ /* @__PURE__ */ S(
+ Tb,
{
- contentEditable: /* @__PURE__ */ C(P1, { className: e }),
- placeholder: /* @__PURE__ */ C(N1, { children: s }),
- ErrorBoundary: qf
+ contentEditable: /* @__PURE__ */ S(cP, { className: e }),
+ placeholder: /* @__PURE__ */ S(lP, { children: s }),
+ ErrorBoundary: Jf
}
),
- /* @__PURE__ */ C(eb, { onChange: n }),
- /* @__PURE__ */ C(Rf, {}),
- t !== !1 && /* @__PURE__ */ C(Xy, {}),
- /* @__PURE__ */ C(w1, { onEscape: r }),
- /* @__PURE__ */ C(Qy, {}),
- i !== void 0 && /* @__PURE__ */ C(wf, { editorRef: i })
+ /* @__PURE__ */ S(kb, { onChange: n }),
+ /* @__PURE__ */ S(Yf, {}),
+ t !== !1 && /* @__PURE__ */ S(mb, {}),
+ /* @__PURE__ */ S(dP, { onEscape: r }),
+ /* @__PURE__ */ S(yb, {}),
+ i !== void 0 && /* @__PURE__ */ S(Gf, { editorRef: i })
] }) });
}
-function Rm(e, t) {
- return he(
+function Xm(e, t) {
+ return me(
(r, n) => {
r.read(() => {
- e(rb()), t(!nb(n.isComposing(), !0));
+ e(xb()), t(!_b(n.isComposing(), !0));
});
},
[t, e]
);
}
-function q1({
+function fP({
editor: e,
cancelAddComment: t,
submitAddComment: r
}) {
- const [n, i] = de(""), [s, o] = de(!1), a = Z(null), c = ze(
+ const [n, i] = de(""), [s, o] = de(!1), a = Z(null), c = Ke(
() => ({
container: document.createElement("div"),
elements: []
}),
[]
- ), l = Z(null), u = Im(), d = he(() => {
+ ), l = Z(null), d = Zm(), u = me(() => {
e.getEditorState().read(() => {
- const g = w();
- if (N(g)) {
- l.current = g.clone();
- const y = g.anchor, k = g.focus, _ = By(
+ const h = w();
+ if (N(h)) {
+ l.current = h.clone();
+ const m = h.anchor, b = h.focus, x = lb(
e,
- y.getNode(),
- y.offset,
- k.getNode(),
- k.offset
- ), S = a.current;
- if (_ !== null && S !== null) {
- const { left: P, bottom: A, width: B } = _.getBoundingClientRect(), M = Vy(e, _);
- let q = M.length === 1 ? P + B / 2 - 125 : P - 125;
- q < 10 && (q = 10), S.style.left = `${q}px`, S.style.top = `${A + 20 + (window.pageYOffset || document.documentElement.scrollTop)}px`;
- const $ = M.length, { container: J } = c, Q = c.elements, Me = Q.length;
- for (let re = 0; re < $; re++) {
- const qe = M[re];
- let be = Q[re];
- be === void 0 && (be = document.createElement("span"), Q[re] = be, J.appendChild(be));
- const Re = `position:absolute;top:${qe.top + (window.pageYOffset || document.documentElement.scrollTop)}px;left:${qe.left}px;height:${qe.height}px;width:${qe.width}px;background-color:rgba(255, 212, 0, 0.3);pointer-events:none;z-index:5;`;
- be.style.cssText = Re;
+ m.getNode(),
+ m.offset,
+ b.getNode(),
+ b.offset
+ ), _ = a.current;
+ if (x !== null && _ !== null) {
+ const { left: A, bottom: E, width: D } = x.getBoundingClientRect(), V = ub(e, x);
+ let j = V.length === 1 ? A + D / 2 - 125 : A - 125;
+ j < 10 && (j = 10), _.style.left = `${j}px`, _.style.top = `${E + 20 + (window.pageYOffset || document.documentElement.scrollTop)}px`;
+ const v = V.length, { container: K } = c, W = c.elements, fe = W.length;
+ for (let X = 0; X < v; X++) {
+ const Re = V[X];
+ let be = W[X];
+ be === void 0 && (be = document.createElement("span"), W[X] = be, K.appendChild(be));
+ const $e = `position:absolute;top:${Re.top + (window.pageYOffset || document.documentElement.scrollTop)}px;left:${Re.left}px;height:${Re.height}px;width:${Re.width}px;background-color:rgba(255, 212, 0, 0.3);pointer-events:none;z-index:5;`;
+ be.style.cssText = $e;
}
- for (let re = Me - 1; re >= $; re--) {
- const qe = Q[re];
- J.removeChild(qe), Q.pop();
+ for (let X = fe - 1; X >= v; X--) {
+ const Re = W[X];
+ K.removeChild(Re), W.pop();
}
}
}
});
}, [e, c]);
- cs(() => {
- d();
- const g = c.container, y = document.body;
- return y !== null ? (y.appendChild(g), () => {
- y.removeChild(g);
+ ps(() => {
+ u();
+ const h = c.container, m = document.body;
+ return m !== null ? (m.appendChild(h), () => {
+ m.removeChild(h);
}) : () => {
};
- }, [c.container, d]), z(() => (window.addEventListener("resize", d), () => {
- window.removeEventListener("resize", d);
- }), [d]);
- const f = (g) => (g.preventDefault(), t(), !0), p = () => {
+ }, [c.container, u]), F(() => (window.addEventListener("resize", u), () => {
+ window.removeEventListener("resize", u);
+ }), [u]);
+ const f = (h) => (h.preventDefault(), t(), !0), p = () => {
if (s) {
- let g = e.getEditorState().read(() => {
- const y = l.current;
- return y ? y.getTextContent() : "";
+ let h = e.getEditorState().read(() => {
+ const m = l.current;
+ return m ? m.getTextContent() : "";
});
- g.length > 100 && (g = g.slice(0, 99) + "…"), r(
- Om(g, [oo(n, u)]),
+ h.length > 100 && (h = h.slice(0, 99) + "…"), r(
+ Gm(h, [fo(n, d)]),
!0,
void 0,
l.current
), l.current = null;
}
- }, m = Rm(i, o);
+ }, y = Xm(i, o);
return /* @__PURE__ */ Te("div", { className: "CommentPlugin_CommentInputBox", ref: a, children: [
- /* @__PURE__ */ C(
- qm,
+ /* @__PURE__ */ S(
+ Ym,
{
className: "CommentPlugin_CommentInputBox_Editor",
onEscape: f,
- onChange: m
+ onChange: y
}
),
/* @__PURE__ */ Te("div", { className: "CommentPlugin_CommentInputBox_Buttons", children: [
- /* @__PURE__ */ C(Yr, { onClick: t, className: "CommentPlugin_CommentInputBox_Button", children: "Cancel" }),
- /* @__PURE__ */ C(
- Yr,
+ /* @__PURE__ */ S(Zr, { onClick: t, className: "CommentPlugin_CommentInputBox_Button", children: "Cancel" }),
+ /* @__PURE__ */ S(
+ Zr,
{
onClick: p,
disabled: !s,
@@ -26653,15 +27003,15 @@ function q1({
] })
] });
}
-function R1({
+function pP({
submitAddComment: e,
thread: t,
placeholder: r
}) {
- const [n, i] = de(""), [s, o] = de(!1), a = Z(null), c = Im(), l = Rm(i, o);
- return /* @__PURE__ */ Te(gn, { children: [
- /* @__PURE__ */ C(
- qm,
+ const [n, i] = de(""), [s, o] = de(!1), a = Z(null), c = Zm(), l = Xm(i, o);
+ return /* @__PURE__ */ Te(yn, { children: [
+ /* @__PURE__ */ S(
+ Ym,
{
className: "CommentPlugin_CommentsPanel_Editor",
autoFocus: !1,
@@ -26671,36 +27021,36 @@ function R1({
placeholder: r
}
),
- /* @__PURE__ */ C(
- Yr,
+ /* @__PURE__ */ S(
+ Zr,
{
className: "CommentPlugin_CommentsPanel_SendButton",
onClick: () => {
if (s) {
- e(oo(n, c), !1, t);
- const d = a.current;
- d !== null && d.dispatchCommand(Iy, void 0);
+ e(fo(n, c), !1, t);
+ const u = a.current;
+ u !== null && u.dispatchCommand(eb, void 0);
}
},
disabled: !s,
- children: /* @__PURE__ */ C("i", { className: "send" })
+ children: /* @__PURE__ */ S("i", { className: "send" })
}
)
] });
}
-function $m({
+function Qm({
commentOrThread: e,
deleteCommentOrThread: t,
onClose: r,
thread: n = void 0
}) {
- return /* @__PURE__ */ Te(gn, { children: [
+ return /* @__PURE__ */ Te(yn, { children: [
"Are you sure you want to delete this ",
e.type,
"?",
/* @__PURE__ */ Te("div", { className: "Modal__content", children: [
- /* @__PURE__ */ C(
- Yr,
+ /* @__PURE__ */ S(
+ Zr,
{
onClick: () => {
t(e, n), r();
@@ -26709,8 +27059,8 @@ function $m({
}
),
" ",
- /* @__PURE__ */ C(
- Yr,
+ /* @__PURE__ */ S(
+ Zr,
{
onClick: () => {
r();
@@ -26721,57 +27071,57 @@ function $m({
] })
] });
}
-function ff({
+function Mf({
comment: e,
deleteComment: t,
thread: r,
rtf: n
}) {
const [i, s] = de(0);
- z(() => {
- const u = () => {
+ F(() => {
+ const d = () => {
s(performance.timeOrigin + performance.now());
};
- u();
- const d = window.setInterval(u, 6e4);
+ d();
+ const u = window.setInterval(d, 6e4);
return () => {
- window.clearInterval(d);
+ window.clearInterval(u);
};
}, []);
- const o = Math.round((e.timeStamp - i) / 1e3), a = Math.round(o / 60), [c, l] = wm();
+ const o = Math.round((e.timeStamp - i) / 1e3), a = Math.round(o / 60), [c, l] = Jm();
return /* @__PURE__ */ Te("li", { className: "CommentPlugin_CommentsPanel_List_Comment", children: [
/* @__PURE__ */ Te("div", { className: "CommentPlugin_CommentsPanel_List_Details", children: [
- /* @__PURE__ */ C("span", { className: "CommentPlugin_CommentsPanel_List_Comment_Author", children: e.author }),
+ /* @__PURE__ */ S("span", { className: "CommentPlugin_CommentsPanel_List_Comment_Author", children: e.author }),
/* @__PURE__ */ Te("span", { className: "CommentPlugin_CommentsPanel_List_Comment_Time", children: [
"· ",
o > -10 ? "Just now" : n.format(a, "minute")
] })
] }),
- /* @__PURE__ */ C("p", { className: e.deleted ? "CommentPlugin_CommentsPanel_DeletedComment" : "", children: e.content }),
- !e.deleted && /* @__PURE__ */ Te(gn, { children: [
- /* @__PURE__ */ C(
- Yr,
+ /* @__PURE__ */ S("p", { className: e.deleted ? "CommentPlugin_CommentsPanel_DeletedComment" : "", children: e.content }),
+ !e.deleted && /* @__PURE__ */ Te(yn, { children: [
+ /* @__PURE__ */ S(
+ Zr,
{
onClick: () => {
- l("Delete Comment", (u) => /* @__PURE__ */ C(
- $m,
+ l("Delete Comment", (d) => /* @__PURE__ */ S(
+ Qm,
{
commentOrThread: e,
deleteCommentOrThread: t,
thread: r,
- onClose: u
+ onClose: d
}
));
},
className: "CommentPlugin_CommentsPanel_List_DeleteButton",
- children: /* @__PURE__ */ C("i", { className: "delete" })
+ children: /* @__PURE__ */ S("i", { className: "delete" })
}
),
c
] })
] });
}
-function $1({
+function hP({
activeIDs: e,
comments: t,
deleteCommentOrThread: r,
@@ -26779,7 +27129,7 @@ function $1({
submitAddComment: i,
markNodeMap: s
}) {
- const [o] = ce(), [a, c] = de(0), [l, u] = wm(), d = ze(
+ const [o] = ae(), [a, c] = de(0), [l, d] = Jm(), u = Ke(
() => new Intl.RelativeTimeFormat("en", {
localeMatcher: "best fit",
numeric: "auto",
@@ -26787,30 +27137,30 @@ function $1({
}),
[]
);
- return z(() => {
+ return F(() => {
const f = setTimeout(() => {
c(a + 1);
}, 1e4);
return () => {
clearTimeout(f);
};
- }, [a]), /* @__PURE__ */ C("ul", { className: "CommentPlugin_CommentsPanel_List", ref: n, children: t.map((f) => {
+ }, [a]), /* @__PURE__ */ S("ul", { className: "CommentPlugin_CommentsPanel_List", ref: n, children: t.map((f) => {
const p = f.id;
return f.type === "thread" ? /* @__PURE__ */ Te(
"li",
{
onClick: () => {
- const g = s.get(p);
- if (g !== void 0 && (e === null || e.indexOf(p) === -1)) {
- const y = document.activeElement;
+ const h = s.get(p);
+ if (h !== void 0 && (e === null || e.indexOf(p) === -1)) {
+ const m = document.activeElement;
o.update(
() => {
- const k = Array.from(g)[0], _ = se(k);
- Ce(_) && _.selectStart();
+ const b = Array.from(h)[0], x = se(b);
+ _e(x) && x.selectStart();
},
{
onUpdate() {
- y !== null && y.focus();
+ m !== null && m.focus();
}
}
);
@@ -26821,39 +27171,39 @@ function $1({
/* @__PURE__ */ Te("div", { className: "CommentPlugin_CommentsPanel_List_Thread_QuoteBox", children: [
/* @__PURE__ */ Te("blockquote", { className: "CommentPlugin_CommentsPanel_List_Thread_Quote", children: [
"> ",
- /* @__PURE__ */ C("span", { children: f.quote })
+ /* @__PURE__ */ S("span", { children: f.quote })
] }),
- /* @__PURE__ */ C(
- Yr,
+ /* @__PURE__ */ S(
+ Zr,
{
onClick: () => {
- u("Delete Thread", (g) => /* @__PURE__ */ C(
- $m,
+ d("Delete Thread", (h) => /* @__PURE__ */ S(
+ Qm,
{
commentOrThread: f,
deleteCommentOrThread: r,
- onClose: g
+ onClose: h
}
));
},
className: "CommentPlugin_CommentsPanel_List_DeleteButton",
- children: /* @__PURE__ */ C("i", { className: "delete" })
+ children: /* @__PURE__ */ S("i", { className: "delete" })
}
),
l
] }),
- /* @__PURE__ */ C("ul", { className: "CommentPlugin_CommentsPanel_List_Thread_Comments", children: f.comments.map((g) => /* @__PURE__ */ C(
- ff,
+ /* @__PURE__ */ S("ul", { className: "CommentPlugin_CommentsPanel_List_Thread_Comments", children: f.comments.map((h) => /* @__PURE__ */ S(
+ Mf,
{
- comment: g,
+ comment: h,
deleteComment: r,
thread: f,
- rtf: d
+ rtf: u
},
- g.id
+ h.id
)) }),
- /* @__PURE__ */ C("div", { className: "CommentPlugin_CommentsPanel_List_Thread_Editor", children: /* @__PURE__ */ C(
- R1,
+ /* @__PURE__ */ S("div", { className: "CommentPlugin_CommentsPanel_List_Thread_Editor", children: /* @__PURE__ */ S(
+ pP,
{
submitAddComment: i,
thread: f,
@@ -26863,18 +27213,18 @@ function $1({
]
},
p
- ) : /* @__PURE__ */ C(
- ff,
+ ) : /* @__PURE__ */ S(
+ Mf,
{
comment: f,
deleteComment: r,
- rtf: d
+ rtf: u
},
p
);
}) });
}
-function I1({
+function gP({
activeIDs: e,
deleteCommentOrThread: t,
comments: r,
@@ -26883,9 +27233,9 @@ function I1({
}) {
const s = Z(null), o = r.length === 0;
return /* @__PURE__ */ Te("div", { className: "CommentPlugin_CommentsPanel", children: [
- /* @__PURE__ */ C("h2", { className: "CommentPlugin_CommentsPanel_Heading", children: "Comments" }),
- o ? /* @__PURE__ */ C("div", { className: "CommentPlugin_CommentsPanel_Empty", children: "No Comments" }) : /* @__PURE__ */ C(
- $1,
+ /* @__PURE__ */ S("h2", { className: "CommentPlugin_CommentsPanel_Heading", children: "Comments" }),
+ o ? /* @__PURE__ */ S("div", { className: "CommentPlugin_CommentsPanel_Empty", children: "No Comments" }) : /* @__PURE__ */ S(
+ hP,
{
activeIDs: e,
comments: r,
@@ -26897,11 +27247,11 @@ function I1({
)
] });
}
-function Im() {
- const e = $f(), { yjsDocMap: t, name: r } = e;
+function Zm() {
+ const e = Xf(), { yjsDocMap: t, name: r } = e;
return t.has("comments") ? r : "Scripture User";
}
-function L1({
+function mP({
providerFactory: e,
setCommentStore: t,
onChange: r,
@@ -26909,189 +27259,189 @@ function L1({
commentContainerRef: i,
logger: s
}) {
- const o = $f(), [a] = ce(), c = ze(() => {
- const q = new C1(a, s);
- return r && q.registerOnChange(r), t?.(q), q;
- }, [a, s, r, t]), l = S1(c), u = ze(() => /* @__PURE__ */ new Map(), []), [d, f] = de(), [p, m] = de([]), [g, y] = de(!1), [k, _] = de(!1), { yjsDocMap: S } = o;
- z(() => {
+ const o = Xf(), [a] = ae(), c = Ke(() => {
+ const j = new rP(a, s);
+ return r && j.registerOnChange(r), t?.(j), j;
+ }, [a, s, r, t]), l = nP(c), d = Ke(() => /* @__PURE__ */ new Map(), []), [u, f] = de(), [p, y] = de([]), [h, m] = de(!1), [b, x] = de(!1), { yjsDocMap: _ } = o;
+ F(() => {
if (e) {
- const q = e("comments", S);
- return c.registerCollaboration(q);
+ const j = e("comments", _);
+ return c.registerCollaboration(j);
}
return () => {
};
- }, [c, e, S]);
- const P = he(() => {
+ }, [c, e, _]);
+ const A = me(() => {
a.update(() => {
- const q = w();
- q !== null && (q.dirty = !0);
- }), y(!1);
- }, [a]), A = he(
- (q, $) => {
- if (q.type === "comment") {
- const J = c.deleteCommentOrThread(q, $);
- if (!J)
+ const j = w();
+ j !== null && (j.dirty = !0);
+ }), m(!1);
+ }, [a]), E = me(
+ (j, v) => {
+ if (j.type === "comment") {
+ const K = c.deleteCommentOrThread(j, v);
+ if (!K)
return;
- const { markedComment: Q, index: Me } = J;
- c.addComment(Q, $, Me);
+ const { markedComment: W, index: fe } = K;
+ c.addComment(W, v, fe);
} else {
- c.deleteCommentOrThread(q);
- const J = $ !== void 0 ? $.id : q.id, Q = u.get(J);
- Q !== void 0 && setTimeout(() => {
+ c.deleteCommentOrThread(j);
+ const K = v !== void 0 ? v.id : j.id, W = d.get(K);
+ W !== void 0 && setTimeout(() => {
a.update(() => {
- for (const Me of Q) {
- const re = se(Me);
- Ce(re) && (re.deleteID(Kr, J), re.hasNoIDsForEveryType() && Us(re));
+ for (const fe of W) {
+ const X = se(fe);
+ _e(X) && (X.deleteID(Br, K), X.hasNoIDsForEveryType() && js(X));
}
});
});
}
},
- [c, a, u]
- ), B = he(
- (q, $, J, Q) => {
- c.addComment(q, J), $ && (a.update(() => {
- N(Q) && Zf(Q, Kr, q.id);
- }), y(!1));
+ [c, a, d]
+ ), D = me(
+ (j, v, K, W) => {
+ c.addComment(j, K), v && (a.update(() => {
+ N(W) && gp(W, Br, j.id);
+ }), m(!1));
},
[c, a]
);
- z(() => {
- const q = [];
- let $;
- for (const J of p) {
- const Q = u.get(J);
- if (Q !== void 0)
- for (const Me of Q) {
- const re = a.getElementByKey(Me);
- re !== null && (re.classList.add("selected"), q.push(re), $ = window.setTimeout(() => {
- _(!0);
+ F(() => {
+ const j = [];
+ let v;
+ for (const K of p) {
+ const W = d.get(K);
+ if (W !== void 0)
+ for (const fe of W) {
+ const X = a.getElementByKey(fe);
+ X !== null && (X.classList.add("selected"), j.push(X), v = window.setTimeout(() => {
+ x(!0);
}, 0));
}
}
return () => {
- $ !== void 0 && window.clearTimeout($);
- for (const J of q)
- J.classList.remove("selected");
+ v !== void 0 && window.clearTimeout(v);
+ for (const K of j)
+ K.classList.remove("selected");
};
- }, [p, a, u]), z(() => {
- if (!a.hasNodes([Qe]))
+ }, [p, a, d]), F(() => {
+ if (!a.hasNodes([tt]))
throw new Error("CommentPlugin: TypedMarkNode not registered on editor!");
- const q = /* @__PURE__ */ new Map();
- return Ve(
- Pf(
+ const j = /* @__PURE__ */ new Map();
+ return Be(
+ Vf(
a,
- Qe,
- ($) => Hi($.getTypedIDs()),
- ($, J) => {
- for (const [Q, Me] of Object.entries($.getTypedIDs()))
- Me.forEach((re) => {
- J.addID(Q, re);
+ tt,
+ (v) => Qi(v.getTypedIDs()),
+ (v, K) => {
+ for (const [W, fe] of Object.entries(v.getTypedIDs()))
+ fe.forEach((X) => {
+ K.addID(W, X);
});
}
),
a.registerMutationListener(
- Qe,
- ($) => {
+ tt,
+ (v) => {
a.getEditorState().read(() => {
- for (const [J, Q] of $) {
- const Me = se(J);
- let re = [];
- Q === "destroyed" ? re = q.get(J) ?? [] : Ce(Me) && (re = Me.getTypedIDs()[Kr] ?? []);
- for (const qe of re) {
- let be = u.get(qe);
- q.set(J, re), Q === "destroyed" ? be !== void 0 && (be.delete(J), be.size === 0 && u.delete(qe)) : (be === void 0 && (be = /* @__PURE__ */ new Set(), u.set(qe, be)), be.has(J) || be.add(J));
+ for (const [K, W] of v) {
+ const fe = se(K);
+ let X = [];
+ W === "destroyed" ? X = j.get(K) ?? [] : _e(fe) && (X = fe.getTypedIDs()[Br] ?? []);
+ for (const Re of X) {
+ let be = d.get(Re);
+ j.set(K, X), W === "destroyed" ? be !== void 0 && (be.delete(K), be.size === 0 && d.delete(Re)) : (be === void 0 && (be = /* @__PURE__ */ new Set(), d.set(Re, be)), be.has(K) || be.add(K));
}
}
});
},
{ skipInitialization: !1 }
),
- a.registerUpdateListener(({ editorState: $, tags: J }) => {
- $.read(() => {
- const Q = w();
- let Me = !1, re = !1;
- if (N(Q)) {
- const qe = Q.anchor.getNode();
- if (v(qe)) {
- const be = Wb(qe, Kr, Q.anchor.offset) ?? [];
- be !== null && (m(be), Me = !0), Q.isCollapsed() || (f(qe.getKey()), re = !0);
+ a.registerUpdateListener(({ editorState: v, tags: K }) => {
+ v.read(() => {
+ const W = w();
+ let fe = !1, X = !1;
+ if (N(W)) {
+ const Re = W.anchor.getNode();
+ if (M(Re)) {
+ const be = dk(Re, Br, W.anchor.offset) ?? [];
+ be !== null && (y(be), fe = !0), W.isCollapsed() || (f(Re.getKey()), X = !0);
}
}
- Me || m((qe) => qe.length === 0 ? qe : []), re || f(null), !J.has("collaboration") && N(Q) && y(!1);
+ fe || y((Re) => Re.length === 0 ? Re : []), X || f(null), !K.has("collaboration") && N(W) && m(!1);
});
}),
a.registerCommand(
- df,
+ Sf,
() => {
- const $ = window.getSelection();
- return $ !== null && $.removeAllRanges(), y(!0), !0;
+ const v = window.getSelection();
+ return v !== null && v.removeAllRanges(), m(!0), !0;
},
- mn
+ bn
)
);
- }, [a, u]);
- const M = () => {
- a.dispatchCommand(df, void 0);
+ }, [a, d]);
+ const V = () => {
+ a.dispatchCommand(Sf, void 0);
};
- return /* @__PURE__ */ Te(gn, { children: [
- g && hn(
- /* @__PURE__ */ C(
- q1,
+ return /* @__PURE__ */ Te(yn, { children: [
+ h && mn(
+ /* @__PURE__ */ S(
+ fP,
{
editor: a,
- cancelAddComment: P,
- submitAddComment: B
+ cancelAddComment: A,
+ submitAddComment: D
}
),
document.body
),
- d != null && !g && hn(
- /* @__PURE__ */ C(
- O1,
+ u != null && !h && mn(
+ /* @__PURE__ */ S(
+ uP,
{
- anchorKey: d,
+ anchorKey: u,
editor: a,
- showComments: k,
- onAddComment: M
+ showComments: b,
+ onAddComment: V
}
),
document.body
),
- n !== null && hn(
- /* @__PURE__ */ C(
- Yr,
+ n !== null && mn(
+ /* @__PURE__ */ S(
+ Zr,
{
- className: `CommentPlugin_ShowCommentsButton ${k ? "active" : ""}`,
- onClick: () => _(!k),
- title: k ? "Hide Comments" : "Show Comments",
- children: /* @__PURE__ */ C("i", { className: "comments" })
+ className: `CommentPlugin_ShowCommentsButton ${b ? "active" : ""}`,
+ onClick: () => x(!b),
+ title: b ? "Hide Comments" : "Show Comments",
+ children: /* @__PURE__ */ S("i", { className: "comments" })
}
),
n?.current ?? document.body
),
- k && hn(
- /* @__PURE__ */ C(
- I1,
+ b && mn(
+ /* @__PURE__ */ S(
+ gP,
{
comments: l,
- submitAddComment: B,
- deleteCommentOrThread: A,
+ submitAddComment: D,
+ deleteCommentOrThread: E,
activeIDs: p,
- markNodeMap: u
+ markNodeMap: d
}
),
i?.current ?? document.body
)
] });
}
-function D1() {
- const e = Z(void 0), t = he((r) => {
+function yP() {
+ const e = Z(void 0), t = me((r) => {
e.current = r;
}, []);
return [e, t];
}
-function U1(e, t) {
+function bP(e, t) {
const r = t.current?.getComments() ?? [], n = r?.map((s) => s.id), i = e.map((s) => {
const o = n.findIndex((a) => a === s);
return o !== void 0 && o >= 0 ? r[o] : {
@@ -27114,21 +27464,21 @@ function U1(e, t) {
e.includes(s.id) || i.push(s);
}), i && t.current?.setComments(i);
}
-function F1(e, t) {
- z(() => {
+function kP(e, t) {
+ F(() => {
e.options ??= {}, e.options.nodes ??= {}, e.options.nodes.addMissingComments = (r) => {
- U1(r, t);
+ bP(r, t);
};
}, [t, e]);
}
-const NP = En(function(t, r) {
- const n = Z(null), i = Z(!0), s = Z(null), [o, a] = de(null), { children: c, onCommentChange: l, onUsjChange: u, showCommentsContainerRef: d, ...f } = t, { logger: p, options: { isReadonly: m, view: g } = {} } = t, y = (m ?? !1) || ns(g), [k, _] = D1();
- F1(f, k), z(() => {
+const uN = An(function(t, r) {
+ const n = Z(null), i = Z(!0), s = Z(null), [o, a] = de(null), { children: c, onCommentChange: l, onUsjChange: d, showCommentsContainerRef: u, ...f } = t, { logger: p, options: { isReadonly: y, view: h } = {} } = t, m = (y ?? !1) || cs(h), [b, x] = yP();
+ kP(f, b), F(() => {
if (process.env.NODE_ENV !== "production") {
- const A = "@eten-tech-foundation/platform-editor: Marginal is deprecated and will be removed in a future release.";
- p?.warn(A), p || console.warn(A);
+ const E = "@eten-tech-foundation/platform-editor: Marginal is deprecated and will be removed in a future release.";
+ p?.warn(E), p || console.warn(E);
}
- }, [p]), gc(r, () => ({
+ }, [p]), Sc(r, () => ({
focus() {
n.current?.focus();
},
@@ -27159,203 +27509,203 @@ const NP = En(function(t, r) {
commitPendingMarkerEdits() {
n.current?.commitPendingMarkerEdits();
},
- setTransientInput(A) {
- n.current?.setTransientInput(A);
+ setTransientInput(E) {
+ n.current?.setTransientInput(E);
},
- setUsj(A) {
- n.current?.setUsj(A);
+ setUsj(E) {
+ n.current?.setUsj(E);
},
- applyUpdate(A, B) {
- n.current?.applyUpdate(A, B);
+ applyUpdate(E, D) {
+ n.current?.applyUpdate(E, D);
},
- replaceEmbedUpdate(A, B) {
- return n.current?.replaceEmbedUpdate(A, B);
+ replaceEmbedUpdate(E, D) {
+ return n.current?.replaceEmbedUpdate(E, D);
},
getSelection() {
return n.current?.getSelection();
},
- setSelection(A) {
- n.current?.setSelection(A);
+ setSelection(E) {
+ n.current?.setSelection(E);
},
- setAnnotation(A, B, M, q, $) {
- typeof q == "function" || q === void 0 ? n.current?.setAnnotation(A, B, M, q, $) : n.current?.setAnnotation(A, B, M, q);
+ setAnnotation(E, D, V, j, v) {
+ typeof j == "function" || j === void 0 ? n.current?.setAnnotation(E, D, V, j, v) : n.current?.setAnnotation(E, D, V, j);
},
- removeAnnotation(A, B) {
- n.current?.removeAnnotation(A, B);
+ removeAnnotation(E, D) {
+ n.current?.removeAnnotation(E, D);
},
- formatPara(A) {
- n.current?.formatPara(A);
+ formatPara(E) {
+ n.current?.formatPara(E);
},
- getElementByKey(A) {
- return n.current?.getElementByKey(A);
+ getElementByKey(E) {
+ return n.current?.getElementByKey(E);
},
- removeCharacterMarker(A) {
- return n.current?.removeCharacterMarker(A) ?? !1;
+ removeCharacterMarker(E) {
+ return n.current?.removeCharacterMarker(E) ?? !1;
},
- replaceCharacterMarker(A, B) {
- return n.current?.replaceCharacterMarker(A, B) ?? !1;
+ replaceCharacterMarker(E, D) {
+ return n.current?.replaceCharacterMarker(E, D) ?? !1;
},
- extendCharacterMarker(A, B) {
- return n.current?.extendCharacterMarker(A, B) ?? !1;
+ extendCharacterMarker(E, D) {
+ return n.current?.extendCharacterMarker(E, D) ?? !1;
},
- insertMarker(A) {
- return n.current?.insertMarker(A);
+ insertMarker(E) {
+ return n.current?.insertMarker(E);
},
getMarkerMenuContext() {
return n.current?.getMarkerMenuContext();
},
- applyMarkerMenuSelection(A, B) {
- return n.current?.applyMarkerMenuSelection(A, B);
+ applyMarkerMenuSelection(E, D) {
+ return n.current?.applyMarkerMenuSelection(E, D);
},
- splitParagraphWithMarker(A) {
- n.current?.splitParagraphWithMarker(A);
+ splitParagraphWithMarker(E) {
+ n.current?.splitParagraphWithMarker(E);
},
- commitTypedMarker(A, B) {
- return n.current?.commitTypedMarker(A, B) ?? !1;
+ commitTypedMarker(E, D) {
+ return n.current?.commitTypedMarker(E, D) ?? !1;
},
- commitTypedCloser(A) {
- return n.current?.commitTypedCloser(A) ?? !1;
+ commitTypedCloser(E) {
+ return n.current?.commitTypedCloser(E) ?? !1;
},
- insertNote(A, B, M) {
- n.current?.insertNote(A, B, M);
+ insertNote(E, D, V) {
+ n.current?.insertNote(E, D, V);
},
- selectNote(A) {
- n.current?.selectNote(A);
+ selectNote(E) {
+ n.current?.selectNote(E);
},
- getNoteOps(A) {
- return n.current?.getNoteOps(A);
+ getNoteOps(E) {
+ return n.current?.getNoteOps(E);
},
- setComments(A) {
- k.current?.setComments(A), i.current = !0;
+ setComments(E) {
+ b.current?.setComments(E), i.current = !0;
},
get toolbarEndRef() {
return o;
}
}));
- const S = he(
- (A, B, M, q) => {
- if (!u) return;
- const $ = k.current?.getComments();
- u(A, $, B, M, q);
- },
- [k, u]
- ), P = he(() => {
+ const _ = me(
+ (E, D, V, j) => {
+ if (!d) return;
+ const v = b.current?.getComments();
+ d(E, v, D, V, j);
+ },
+ [b, d]
+ ), A = me(() => {
if (!l || i.current) {
i.current = !1;
return;
}
- const A = k.current?.getComments();
- l(A);
- }, [k, i, l]);
- return z(() => (a(n.current?.toolbarEndRef ?? null), () => a(null)), []), /* @__PURE__ */ C(Zy, { children: /* @__PURE__ */ Te(Pm, { ref: n, onUsjChange: S, ...f, children: [
- /* @__PURE__ */ C(
- L1,
+ const E = b.current?.getComments();
+ l(E);
+ }, [b, i, l]);
+ return F(() => (a(n.current?.toolbarEndRef ?? null), () => a(null)), []), /* @__PURE__ */ S(bb, { children: /* @__PURE__ */ Te(Wm, { ref: n, onUsjChange: _, ...f, children: [
+ /* @__PURE__ */ S(
+ mP,
{
- setCommentStore: _,
- onChange: P,
- showCommentsContainerRef: y ? null : d ?? o,
+ setCommentStore: x,
+ onChange: A,
+ showCommentsContainerRef: m ? null : u ?? o,
commentContainerRef: s,
logger: f.logger
}
),
- /* @__PURE__ */ C("div", { ref: s, className: "comment-container" })
+ /* @__PURE__ */ S("div", { ref: s, className: "comment-container" })
] }) });
});
-function pn(e) {
+function gn(e) {
return e.toFixed(3).replace(/0+$/, "").replace(/\.$/, "");
}
-function z1(e) {
+function TP(e) {
return e.replace(/["\\\n\r\f<>]/g, (t) => t === `
` ? "\\a " : t === "\r" ? "\\d " : t === "\f" ? "\\c " : t === "<" ? "\\3C " : t === ">" ? "\\3E " : `\\${t}`);
}
-function K1(e) {
+function xP(e) {
return typeof CSS < "u" && typeof CSS.escape == "function" ? CSS.escape(e) : e.replace(/[^\w-]/g, (t) => `\\${t}`);
}
-const j1 = /^[#\w().,%/\s-]+$/;
-function mr(e) {
+const _P = /^[#\w().,%/\s-]+$/;
+function Tr(e) {
return e != null;
}
-const B1 = {
+const CP = {
left: "left",
right: "right",
center: "center",
both: "justify"
-}, V1 = {
+}, vP = {
left: "right",
right: "left"
-}, W1 = "var(--usj-font-fallback, serif)";
-function Lm(e, t) {
+}, SP = "var(--usj-font-fallback, serif)";
+function ey(e, t) {
const r = [e];
- return t && t !== e && r.push(t), `font-family: ${r.map((i) => `"${z1(i)}"`).join(", ")}, ${W1}`;
+ return t && t !== e && r.push(t), `font-family: ${r.map((i) => `"${TP(i)}"`).join(", ")}, ${SP}`;
}
-const hc = ".editor-input.usfm", H1 = /^[\w.#[\]="':()>+~*,\s-]+$/;
-function G1(e) {
- return H1.test(e) ? e : (console.warn(
- `[generateUsjCss] Ignoring unsafe containerSelector "${e}"; using "${hc}".`
- ), hc);
+const vc = ".editor-input.usfm", MP = /^[\w.#[\]="':()>+~*,\s-]+$/;
+function EP(e) {
+ return MP.test(e) ? e : (console.warn(
+ `[generateUsjCss] Ignoring unsafe containerSelector "${e}"; using "${vc}".`
+ ), vc);
}
-function J1(e, t, r, n, i) {
+function AP(e, t, r, n, i) {
const s = [];
- if (t.fontName && s.push(Lm(t.fontName, i)), t.bold && s.push("font-weight: bold"), t.italic && s.push("font-style: italic"), t.color && (j1.test(t.color) ? s.push(`color: ${t.color}`) : console.warn(
+ if (t.fontName && s.push(ey(t.fontName, i)), t.bold && s.push("font-weight: bold"), t.italic && s.push("font-style: italic"), t.color && (_P.test(t.color) ? s.push(`color: ${t.color}`) : console.warn(
`[generateUsjCss] Skipping unsafe color "${t.color}" for marker "${e}".`
- )), mr(t.fontSize) && t.fontSize > 0 && s.push(`font-size: ${Math.floor(t.fontSize * 100 / 12)}%`), mr(t.firstLineIndent) && s.push(`text-indent: ${pn(t.firstLineIndent * 20 * r)}vw`), mr(t.leftMargin) && t.leftMargin >= 0 && s.push(`margin-${n ? "right" : "left"}: ${pn(t.leftMargin * 20 * r)}vw`), mr(t.rightMargin) && t.rightMargin >= 0 && s.push(
- `margin-${n ? "left" : "right"}: ${pn(t.rightMargin * 20 * r)}vw`
- ), mr(t.spaceBefore) && t.spaceBefore >= 0 && s.push(`margin-top: ${pn(t.spaceBefore * r)}pt`), mr(t.spaceAfter) && t.spaceAfter >= 0 && s.push(`margin-bottom: ${pn(t.spaceAfter * r)}pt`), t.lineSpacing === 1 ? s.push("line-height: 1.5") : t.lineSpacing === 2 && s.push("line-height: 2"), t.subscript ? s.push("vertical-align: text-bottom", "font-size: 66%") : t.superscript && s.push("vertical-align: text-top", "font-size: 66%"), t.underline && s.push("text-decoration: underline"), t.smallCaps && s.push("font-variant: small-caps"), t.justification) {
- const o = B1[n ? V1[t.justification] ?? t.justification : t.justification];
+ )), Tr(t.fontSize) && t.fontSize > 0 && s.push(`font-size: ${Math.floor(t.fontSize * 100 / 12)}%`), Tr(t.firstLineIndent) && s.push(`text-indent: ${gn(t.firstLineIndent * 20 * r)}vw`), Tr(t.leftMargin) && t.leftMargin >= 0 && s.push(`margin-${n ? "right" : "left"}: ${gn(t.leftMargin * 20 * r)}vw`), Tr(t.rightMargin) && t.rightMargin >= 0 && s.push(
+ `margin-${n ? "left" : "right"}: ${gn(t.rightMargin * 20 * r)}vw`
+ ), Tr(t.spaceBefore) && t.spaceBefore >= 0 && s.push(`margin-top: ${gn(t.spaceBefore * r)}pt`), Tr(t.spaceAfter) && t.spaceAfter >= 0 && s.push(`margin-bottom: ${gn(t.spaceAfter * r)}pt`), t.lineSpacing === 1 ? s.push("line-height: 1.5") : t.lineSpacing === 2 && s.push("line-height: 2"), t.subscript ? s.push("vertical-align: text-bottom", "font-size: 66%") : t.superscript && s.push("vertical-align: text-top", "font-size: 66%"), t.underline && s.push("text-decoration: underline"), t.smallCaps && s.push("font-variant: small-caps"), t.justification) {
+ const o = CP[n ? vP[t.justification] ?? t.justification : t.justification];
o && s.push(`text-align: ${o}`);
}
return t.textProperties?.includes("verse") && s.push("white-space: nowrap", "unicode-bidi: embed"), s;
}
-const pf = { c: 150, ca: 133, cp: 150 };
-function hf(e, t) {
- return e && mr(e.fontSize) && e.fontSize > 0 ? Math.floor(e.fontSize * 100 / 12) : t;
+const Ef = { c: 150, ca: 133, cp: 150 };
+function Af(e, t) {
+ return e && Tr(e.fontSize) && e.fontSize > 0 ? Math.floor(e.fontSize * 100 / 12) : t;
}
-function Y1(e, t) {
+function PP(e, t) {
if (["c", "ca", "cp"].filter((i) => {
const s = e.markers[i];
- return s && mr(s.fontSize) && s.fontSize > 0;
+ return s && Tr(s.fontSize) && s.fontSize > 0;
}).length === 0) return [];
- const n = hf(e.markers.c, pf.c);
+ const n = Af(e.markers.c, Ef.c);
return ["ca", "cp"].map((i) => {
- const s = hf(
+ const s = Af(
e.markers[i],
- pf[i]
- ), o = pn(s * 100 / n);
+ Ef[i]
+ ), o = gn(s * 100 / n);
return `${t} .usfm_c .usfm_${i}.usfm_${i} { font-size: ${o}%; }`;
});
}
-function OP(e, t = {}) {
- const { zoom: r = 1, rtl: n = !1, containerSelector: i = hc } = t, s = G1(i), o = [], a = [];
- e.defaultFont && a.push(Lm(e.defaultFont)), mr(e.defaultFontSize) && e.defaultFontSize > 0 && a.push(`font-size: ${pn(e.defaultFontSize * r)}pt`), a.length > 0 && o.push(`${s} { ${a.join("; ")}; }`);
+function dN(e, t = {}) {
+ const { zoom: r = 1, rtl: n = !1, containerSelector: i = vc } = t, s = EP(i), o = [], a = [];
+ e.defaultFont && a.push(ey(e.defaultFont)), Tr(e.defaultFontSize) && e.defaultFontSize > 0 && a.push(`font-size: ${gn(e.defaultFontSize * r)}pt`), a.length > 0 && o.push(`${s} { ${a.join("; ")}; }`);
for (const [c, l] of Object.entries(e.markers)) {
- const u = J1(c, l, r, n, e.defaultFont);
- u.length > 0 && o.push(`${s} .usfm_${K1(c)} { ${u.join("; ")}; }`);
+ const d = AP(c, l, r, n, e.defaultFont);
+ d.length > 0 && o.push(`${s} .usfm_${xP(c)} { ${d.join("; ")}; }`);
}
- return o.push(...Y1(e, s)), o.join(`
+ return o.push(...PP(e, s)), o.join(`
`);
}
export {
- Ah as BLOCK_VERSE_VIEW_MODE,
+ jh as BLOCK_VERSE_VIEW_MODE,
T as CategoryType,
- PP as Editorial,
- Bi as GENERATOR_NOTE_CALLER,
- Lf as HIDDEN_NOTE_CALLER,
- NP as Marginal,
- b as MarkerType,
- Eh as PARAGRAPH_STRUCTURE_VIEW_MODE,
- ol as STANDARD_VIEW_MODE,
- Vs as defaultStyleInfo,
- AP as directionToNames,
- m_ as filterAndRankItems,
- OP as generateUsjCss,
- MP as getDefaultViewMode,
- Eo as getDefaultViewOptions,
- zM as getEnterMenuItems,
- FM as getMarkerMenuItems,
- EP as getViewMode,
- ll as getViewOptions,
- ns as isBlockVerseLayout,
- Fr as isInsertEmbedOpOfType,
- P_ as viewModeToViewNames
+ lN as Editorial,
+ Ji as GENERATOR_NOTE_CALLER,
+ Zf as HIDDEN_NOTE_CALLER,
+ uN as Marginal,
+ k as MarkerType,
+ Kh as PARAGRAPH_STRUCTURE_VIEW_MODE,
+ ml as STANDARD_VIEW_MODE,
+ Js as defaultStyleInfo,
+ cN as directionToNames,
+ $_ as filterAndRankItems,
+ dN as generateUsjCss,
+ oN as getDefaultViewMode,
+ Oo as getDefaultViewOptions,
+ rA as getEnterMenuItems,
+ tA as getMarkerMenuItems,
+ aN as getViewMode,
+ kl as getViewOptions,
+ cs as isBlockVerseLayout,
+ Kr as isInsertEmbedOpOfType,
+ G_ as viewModeToViewNames
};
//# sourceMappingURL=index.js.map
diff --git a/packages/platform/dist/index.js.map b/packages/platform/dist/index.js.map
index 7556555c3..f6c2f65f8 100644
--- a/packages/platform/dist/index.js.map
+++ b/packages/platform/dist/index.js.map
@@ -1 +1 @@
-{"version":3,"file":"index.js","sources":["../../../libs/shared/dist/converters/usfm/emptyUsfmNodes.js","../../../libs/shared/dist/converters/usfm/leadingAttributes.js","../../../libs/shared/dist/nodes/usj/node-constants.js","../../../libs/shared/dist/nodes/usj/MilestoneNode.js","../../../libs/shared/dist/nodes/usj/NoteNode.js","../../../libs/shared/dist/utils/usfm/usfmTypes.js","../../../libs/shared/dist/utils/usfm/usfmMarkers.js","../../../libs/shared/dist/utils/usfm/usfmMarkersOverwrites.js","../../../libs/shared/dist/utils/usfm/getMarker.js","../../../libs/shared/dist/converters/usfm/usfmFragmentToUsj.js","../../../libs/shared/dist/nodes/collab/delta.state.js","../../../libs/shared/dist/nodes/features/ImmutableTypedTextNode.js","../../../libs/shared/dist/nodes/features/TypedMarkNode.js","../../../libs/shared/dist/nodes/features/UnknownNode.js","../../../libs/shared/dist/nodes/usj/AttributeRunNode.js","../../../libs/shared/dist/nodes/usj/BookNode.js","../../../libs/shared/dist/nodes/usj/ChapterNode.js","../../../libs/shared/dist/nodes/usj/CharNode.js","../../../libs/shared/dist/nodes/usj/ImmutableChapterNode.js","../../../libs/shared/dist/nodes/usj/ImpliedParaNode.js","../../../libs/shared/dist/nodes/usj/ParaNode.js","../../../libs/shared/dist/nodes/usj/VerseNode.js","../../../libs/shared/dist/plugins/CursorHandler/core/utils/constants.js","../../../libs/shared/dist/plugins/CursorHandler/core/utils/index.js","../../../libs/shared/dist/nodes/usj/node.utils.js","../../../libs/shared/dist/nodes/features/MarkerNode.js","../../../libs/shared/dist/nodes/usj/attributeDisplay.utils.js","../../../libs/shared/dist/nodes/usj/nestedGlyphs.utils.js","../../../libs/shared/dist/nodes/usj/markerSeparators.utils.js","../../../libs/shared/dist/nodes/features/unknownUsfm.utils.js","../../../libs/shared/dist/displayRun/displayRunRegistry.js","../../../libs/shared/dist/displayRun/displayRunOwner.utils.js","../../../libs/shared/dist/nodes/features/ImmutableUnmatchedNode.js","../../../libs/shared/dist/nodes/usj/ImmutableTableNode.js","../../../libs/shared/dist/nodes/usj/ImmutableTableRowNode.js","../../../libs/shared/dist/nodes/usj/ImmutableTableCellNode.js","../../../libs/shared/dist/nodes/usj/caretBoundaries.utils.js","../../../libs/shared/dist/nodes/usj/charGlyphs.utils.js","../../../libs/shared/dist/nodes/usj/charStack.utils.js","../../../libs/shared/dist/nodes/usj/pendedDisplayOwners.utils.js","../../../libs/shared/dist/nodes/usj/displayRunSync.utils.js","../../../libs/shared/dist/nodes/usj/glyphPositions.utils.js","../../../libs/shared/dist/nodes/usj/VerseBlockNode.js","../../../libs/shared/dist/nodes/usj/index.js","../../../libs/shared/dist/utils/usfm/defaultStyleInfo.js","../../../libs/shared/dist/utils/usfm/styleInfo.js","../../../libs/shared/dist/utils/usj/contentToLexicalNode.js","../../../libs/shared-react/dist/nodes/usj/ImmutableVerseNode.js","../../../libs/shared-react/dist/nodes/usj/node-react.utils.js","../../../libs/shared-react/dist/plugins/usj/collab/rich-text-ot.model.js","../../../libs/shared-react/dist/plugins/usj/collab/delta-common.utils.js","../../../libs/shared-react/dist/plugins/usj/collab/editor-delta.adaptor.js","../../../libs/shared-react/dist/nodes/usj/ImmutableNoteCallerNode.js","../../../libs/shared-react/dist/plugins/usj/annotation/selection.utils.js","../../../libs/shared-react/dist/nodes/usj/note.utils.js","../../../libs/shared-react/dist/nodes/usj/index.js","../../../libs/shared-react/dist/plugins/FloatingBox/FloatingBox.js","../../../libs/shared-react/dist/plugins/FloatingBox/useFloatingPosition.js","../../../libs/shared-react/dist/plugins/FloatingBox/useCursorCoords.js","../../../libs/shared-react/dist/plugins/FloatingBox/FloatingBoxAtCursor.js","../../../libs/shared-react/dist/plugins/NodesMenu/Menu/MenuContext.js","../../../libs/shared-react/dist/plugins/NodesMenu/Menu/useMenuCore.js","../../../libs/shared-react/dist/plugins/NodesMenu/Menu/Root.js","../../../libs/shared-react/dist/plugins/NodesMenu/Menu/Option.js","../../../libs/shared-react/dist/plugins/NodesMenu/Menu/Options.js","../../../libs/shared-react/dist/plugins/NodesMenu/Menu/filterAndRankItems.js","../../../libs/shared-react/dist/plugins/NodesMenu/Menu/index.js","../../../libs/shared-react/dist/plugins/NodesMenu/Menu/useFilteredItems.js","../../../libs/shared-react/dist/plugins/NodesMenu/Menu/useMenuActions.js","../../../libs/shared-react/dist/plugins/NodesMenu/Menu/useLexicalMenuNavigation.js","../../../libs/shared-react/dist/plugins/NodesMenu/LexicalMenuNavigation.js","../../../libs/shared-react/dist/plugins/NodesMenu/NodeSelectionMenu.js","../../../libs/shared-react/dist/plugins/NodesMenu/index.js","../../../libs/shared-react/dist/plugins/PerfNodesItems/useUsfmMarkersForMenu.js","../../../libs/shared-react/dist/plugins/usj/annotation/AnnotationPlugin.js","../../../libs/shared-react/dist/plugins/usj/collab/DeltaOnChangePlugin.js","../../../libs/shared-react/dist/views/view-mode.model.js","../../../libs/shared-react/dist/views/view-options.utils.js","../../../libs/shared-react/dist/plugins/usj/collab/delta-apply-update.utils.js","../../../libs/shared-react/dist/plugins/usj/ParaMarkerPrefixCursorGuardPlugin.js","../../../libs/shared-react/dist/plugins/usj/OpaqueBlockGuardPlugin.js","../../../libs/shared-react/dist/plugins/usj/ArrowNavigationPlugin.js","../../../libs/shared-react/dist/plugins/usj/CharNodePlugin.js","../../../libs/shared-react/dist/plugins/usj/clipboard.utils.js","../../../libs/shared-react/dist/plugins/usj/ClipboardPlugin.js","../../../libs/shared-react/dist/plugins/usj/CommandMenuPlugin.js","../../../libs/shared-react/dist/plugins/usj/ContextMenuPlugin.js","../../../libs/shared-react/dist/plugins/usj/DecoratorBoundarySelectionPlugin.js","../../../libs/shared-react/dist/plugins/usj/DisableHistoryShortcutsPlugin.js","../../../libs/shared-react/dist/plugins/usj/EditablePlugin.js","../../../libs/shared-react/dist/plugins/usj/transientCaretHost.js","../../../libs/shared-react/dist/plugins/usj/EmptyVerseCaretGuardPlugin.js","../../../libs/shared-react/dist/plugins/usj/LoadStatePlugin.js","../../../libs/shared-react/dist/plugins/usj/NoteNodePlugin.js","../../../libs/shared-react/dist/plugins/usj/NoteShellCaretGuardPlugin.js","../../../libs/shared-react/dist/plugins/usj/OnSelectionChangePlugin.js","../../../libs/shared-react/dist/plugins/usj/ParaNodePlugin.js","../../../libs/shared-react/dist/plugins/usj/StateChangePlugin.js","../../../libs/shared-react/dist/plugins/usj/structureKeyboard.utils.js","../../../node_modules/.pnpm/dompurify@3.4.13/node_modules/dompurify/dist/purify.es.mjs","../../../libs/shared-react/dist/plugins/usj/StructureKeyboardPlugin.js","../../../libs/shared-react/dist/plugins/usj/text-direction.model.js","../../../libs/shared-react/dist/plugins/usj/TextDirectionPlugin.js","../../../libs/shared-react/dist/plugins/usj/TextSpacingPlugin.js","../../../libs/shared-react/dist/plugins/usj/TrailingNoteCaretGuardPlugin.js","../../../libs/shared-react/dist/plugins/UsfmNodesMenuPlugin.js","../../../libs/shared-react/dist/plugins/usj/UsjNodesMenuPlugin.js","../src/editor/markerEdit/whitespaceDisplay.utils.ts","../src/editor/adaptors/editor-usj.adaptor.ts","../src/editor/adaptors/verse-block.utils.ts","../src/editor/adaptors/usj-editor.adaptor.ts","../src/editor/markerEdit/charFormatting.utils.ts","../src/editor/adaptors/usj-marker-action.utils.ts","../src/editor/editor.theme.ts","../src/editor/ActiveTextPlugin.tsx","../src/editor/markerEdit/markerValidation.utils.ts","../src/editor/markerMenu/markerItemSource.ts","../src/editor/markerEdit/markerName.pattern.ts","../src/editor/markerEdit/settleShared.utils.ts","../src/editor/markerEdit/tier2Rebuild.utils.ts","../src/editor/markerEdit/markerKind.utils.ts","../src/editor/markerEdit/markerEditDeletion.utils.ts","../src/editor/markerEdit/applyParaMarker.utils.ts","../src/editor/markerEdit/markerEditNote.utils.ts","../src/editor/markerMenu/markerMenuContext.utils.ts","../src/editor/markerMenu/markerMenuApply.utils.ts","../src/editor/EscapeKeyPlugin.tsx","../src/editor/markerEdit/markerEditTier1.utils.ts","../src/editor/markerEdit/markerEditTier2Trigger.utils.ts","../src/editor/markerEdit/paratext9Clipboard.utils.ts","../src/editor/markerEdit/whitespaceDisplay.plugin.utils.ts","../src/editor/markerEdit/MarkerEditPlugin.tsx","../src/editor/markerEdit/MarkerValidationPlugin.tsx","../src/editor/MarkersViewCopyPlugin.tsx","../src/editor/markerEdit/virtualSettle.utils.ts","../src/editor/ParaMarkerPrefixGuardPlugin.tsx","../src/editor/ScriptureReferencePlugin.tsx","../src/editor/TreeViewPlugin.tsx","../src/editor/toolbar/DropDown.tsx","../src/editor/toolbar/BlockFormatDropDown.tsx","../src/editor/toolbar/ToolbarPlugin.tsx","../src/editor/Editor.tsx","../src/Editorial.tsx","../src/marginal/comments/commenting.ts","../src/marginal/comments/ui/Modal.tsx","../src/marginal/comments/use-modal.hook.tsx","../src/marginal/comments/comment-editor.theme.ts","../src/marginal/comments/join-classes.util.ts","../src/marginal/comments/ui/Button.tsx","../src/marginal/comments/ui/ContentEditable.tsx","../src/marginal/comments/ui/Placeholder.tsx","../src/marginal/comments/CommentPlugin.tsx","../src/marginal/comments/use-comment-store-ref.hook.tsx","../src/marginal/comments/use-missing-comments-props.hook.tsx","../src/marginal/Marginal.tsx","../src/editor/generateUsjCss.ts"],"sourcesContent":["import { $applyNodeReplacement, $createParagraphNode, $insertNodes, $isRootOrShadowRoot, $parseSerializedNode, } from \"lexical\";\nimport { $wrapNodeInElement } from \"@lexical/utils\";\nexport const $createNodeFromSerializedNode = (serializedNode) => {\n return $applyNodeReplacement($parseSerializedNode(serializedNode));\n};\nexport const $insertUsfmNode = (serializedNode) => {\n const newNode = $createNodeFromSerializedNode(serializedNode);\n $insertNodes([newNode]);\n if ($isRootOrShadowRoot(newNode.getParentOrThrow())) {\n $wrapNodeInElement(newNode, $createParagraphNode).selectEnd();\n }\n return newNode;\n};\n","/**\n * The markers map's `leadingAttributes` relation — which markers take a value written directly\n * after the marker, separated only by whitespace, and stored as an attribute rather than as\n * text: `\\v`/`\\c`'s number, the note-marker family's caller, `\\id`'s code.\n *\n * ONE rule follows from the declaration, with no per-marker exceptions: whitespace between the\n * marker and its leading-attribute value is structural and collapses to one, so `\\v 5` is\n * verse 5, `\\f +` has caller `+`, and `\\id MAT` has code `MAT`. The fragment tokenizer's own\n * word scan (`getNextWord`, usfmFragmentToUsj.ts) already implements the collapse; this module\n * is the declarative side, so Tier-1 glyph arms (platform's marker-edit engine) can consume the\n * same marker/attribute knowledge instead of hardcoding per-marker lists.\n *\n * The table is a VENDORED SLICE of paranext-core's\n * `lib/platform-bible-utils/src/scripture/markers-maps/markers-map-3.0.model.ts`\n * (`USFM_MARKERS_MAP.markers`, every `leadingAttributes` field verbatim; 3.1 declares the\n * identical set) — the source of truth lives there; re-copy when it changes (the same\n * convention as `attributeMarkersMapAgreement.test.ts`'s slice and the vendored\n * `testUsfmCorpus` fixtures). scripture-editors has no dependency on `platform-bible-utils`,\n * so a live import is not available.\n */\n/** Vendored from `USFM_MARKERS_MAP.markers`: every marker with a `leadingAttributes` field,\n * verbatim, ORDERED by the order the values must appear in after the marker. */\nconst LEADING_ATTRIBUTES = {\n c: [\"number\"],\n ef: [\"caller\"],\n efe: [\"caller\"],\n ex: [\"caller\"],\n f: [\"caller\"],\n fe: [\"caller\"],\n id: [\"code\"],\n v: [\"number\"],\n x: [\"caller\"],\n};\n/**\n * The ordered leading-attribute names the markers map declares for `marker`, or `undefined`\n * for a marker that declares none. Read-only map data — callable from any context.\n */\nexport function leadingAttributeNames(marker) {\n return LEADING_ATTRIBUTES[marker];\n}\n","/** This file avoids a circular dependency between `CharNode.ts` and `node.utils.ts`. */\n/** Non-breaking space (U+00A0). */\nexport const NBSP = \"\\u00A0\";\n/** Zero-width space (U+200B). */\nexport const ZWSP = \"\\u200B\";\nexport const EMPTY_CHAR_PLACEHOLDER_TEXT = NBSP;\nexport const NODE_ATTRIBUTE_PREFIX = `${NBSP}|`;\nexport const PARA_MARKER_DEFAULT = \"p\";\n/**\n * Note caller will be auto-generated.\n * @public\n */\nexport const GENERATOR_NOTE_CALLER = \"+\";\n/**\n * Hidden note caller will not be auto-generated, and will not be displayed in some views.\n * @public\n */\nexport const HIDDEN_NOTE_CALLER = \"-\";\nexport const CHAPTER_CLASS_NAME = \"chapter\";\nexport const VERSE_CLASS_NAME = \"verse\";\nexport const INVALID_CLASS_NAME = \"invalid\";\nexport const TEXT_SPACING_CLASS_NAME = \"text-spacing\";\nexport const FORMATTED_FONT_CLASS_NAME = \"formatted-font\";\nexport const MARKER_MODE_CLASS_NAME_PREFIX = \"marker-\";\nexport const EXTERNAL_USJ_MUTATION_TAG = \"external-usj-mutation\";\nexport const SELECTION_CHANGE_TAG = \"selection-change\";\nexport const CURSOR_CHANGE_TAG = \"cursor-change\";\nexport const ANNOTATION_CHANGE_TAG = \"annotation-change\";\nexport const DELTA_CHANGE_TAG = \"delta-change\";\n/**\n * Marks a commit that carries Lexical's `HISTORY_MERGE_TAG` yet still CHANGES the document — a\n * marker-edit settle, which must never become its own undo entry but is a real content change the\n * host has to see. The merge tag carries two meanings that normally coincide (\"do not push a\n * history entry\" and \"nothing to report\"); this tag is how such a commit says it means only the\n * first, so USJ-change consumers do not skip it.\n */\nexport const MARKER_SETTLE_TAG = \"marker-settle\";\n/** Tags that should not be present when handling a USJ change. */\nexport const blackListedChangeTags = [\n EXTERNAL_USJ_MUTATION_TAG,\n SELECTION_CHANGE_TAG,\n CURSOR_CHANGE_TAG,\n ANNOTATION_CHANGE_TAG,\n DELTA_CHANGE_TAG,\n];\n","/** Conforms with USJ v3.1 @see https://docs.usfm.bible/usfm/3.1/ms/index.html */\nimport { $applyNodeReplacement, DecoratorNode, } from \"lexical\";\nexport const STARTING_MS_COMMENT_MARKER = \"zmsc-s\";\nexport const ENDING_MS_COMMENT_MARKER = \"zmsc-e\";\n/** Milestone markers used to mark a comment annotation */\nconst milestoneCommentMarkers = [STARTING_MS_COMMENT_MARKER, ENDING_MS_COMMENT_MARKER];\n/** @see https://docs.usfm.bible/usfm/3.1/ms/index.html */\nconst VALID_MILESTONE_MARKERS = [\n \"ts-s\",\n \"ts-e\",\n \"t-s\",\n \"t-e\",\n \"ts\",\n \"qt1-s\",\n \"qt1-e\",\n \"qt2-s\",\n \"qt2-e\",\n \"qt3-s\",\n \"qt3-e\",\n \"qt4-s\",\n \"qt4-e\",\n \"qt5-s\",\n \"qt5-e\",\n \"qt-s\",\n \"qt-e\",\n // custom markers used for annotations\n STARTING_MS_COMMENT_MARKER,\n ENDING_MS_COMMENT_MARKER,\n];\nexport const MILESTONE_VERSION = 1;\n/** List of known properties of `MarkerObject` */\nexport const MS_MARKER_OBJECT_PROPS = [\n \"type\",\n \"marker\",\n \"sid\",\n \"eid\",\n \"content\",\n];\n/**\n * The milestone `MarkerObject` properties that are never attribute bytes. Everything ELSE a\n * milestone carries — `sid` and `eid` just as much as an unknown attribute like `who` — is written\n * into its `|…` run, so those are the names that have an order relative to one another. Derived\n * from {@link MS_MARKER_OBJECT_PROPS} so the two cannot drift.\n */\nexport const MS_NON_ATTRIBUTE_PROPS = MS_MARKER_OBJECT_PROPS.filter((property) => property !== \"sid\" && property !== \"eid\");\nexport class MilestoneNode extends DecoratorNode {\n __marker;\n __sid;\n __eid;\n __unknownAttributes;\n __attributeOrder;\n // `attributeOrder` rides AFTER `key`, never ahead of it: a node's key is the last argument\n // every Lexical node constructor took before this field existed, and slotting the new field\n // ahead of it would silently reinterpret an existing 5-argument call's NodeKey as the order\n // list (TypeScript consumers get a compile error; JavaScript consumers get corruption). Same\n // rule as MarkerNode's constructor.\n constructor(marker = \"\", sid, eid, unknownAttributes, key, attributeOrder) {\n super(key);\n this.__marker = marker;\n this.__sid = sid;\n this.__eid = eid;\n this.__unknownAttributes = unknownAttributes;\n this.__attributeOrder = attributeOrder;\n }\n static getType() {\n return \"ms\";\n }\n static clone(node) {\n const { __marker, __sid, __eid, __unknownAttributes, __attributeOrder, __key } = node;\n return new MilestoneNode(__marker, __sid, __eid, __unknownAttributes, __key, __attributeOrder);\n }\n static importJSON(serializedNode) {\n return $createMilestoneNode().updateFromJSON(serializedNode);\n }\n static isValidMarker(marker, extraValidMarkers) {\n return (marker !== undefined &&\n (VALID_MILESTONE_MARKERS.includes(marker) ||\n marker.startsWith(\"z\") ||\n (extraValidMarkers?.includes(marker) ?? false)));\n }\n updateFromJSON(serializedNode) {\n return super\n .updateFromJSON(serializedNode)\n .setMarker(serializedNode.marker)\n .setSid(serializedNode.sid)\n .setEid(serializedNode.eid)\n .setUnknownAttributes(serializedNode.unknownAttributes)\n .setAttributeOrder(serializedNode.attributeOrder);\n }\n setMarker(marker) {\n if (this.__marker === marker)\n return this;\n const self = this.getWritable();\n self.__marker = marker;\n return self;\n }\n getMarker() {\n const self = this.getLatest();\n return self.__marker;\n }\n setSid(sid) {\n if (this.__sid === sid)\n return this;\n const self = this.getWritable();\n self.__sid = sid;\n return self;\n }\n getSid() {\n const self = this.getLatest();\n return self.__sid;\n }\n setEid(eid) {\n if (this.__eid === eid)\n return this;\n const self = this.getWritable();\n self.__eid = eid;\n return self;\n }\n getEid() {\n const self = this.getLatest();\n return self.__eid;\n }\n setUnknownAttributes(unknownAttributes) {\n const self = this.getWritable();\n self.__unknownAttributes = unknownAttributes;\n return self;\n }\n getUnknownAttributes() {\n const self = this.getLatest();\n return self.__unknownAttributes;\n }\n setAttributeOrder(attributeOrder) {\n const self = this.getWritable();\n self.__attributeOrder = attributeOrder;\n return self;\n }\n /**\n * The authored attribute order this milestone was loaded with, or `undefined` when its order is\n * the canonical one. Feed it to `milestoneAttributes` (attributeDisplay.utils.ts) — every site\n * that turns a milestone back into bytes or back into USJ goes through there, so the order\n * cannot be honored in one place and dropped in another.\n */\n getAttributeOrder() {\n const self = this.getLatest();\n return self.__attributeOrder;\n }\n createDOM() {\n const dom = document.createElement(\"span\");\n dom.setAttribute(\"data-marker\", this.__marker);\n dom.classList.add(this.__type, `usfm_${this.__marker}`);\n return dom;\n }\n updateDOM() {\n // Returning false tells Lexical that this node does not need its\n // DOM element replacing with a new copy from createDOM.\n return false;\n }\n /**\n * A milestone paints nothing of its own: in editable marker mode its `\\qt-s …\\*` glyphs are real\n * sibling nodes, and in the other modes an `ImmutableTypedTextNode` carries them.\n *\n * Keep this payload EMPTY. A decorator payload with STABLE IDENTITY is unsound for any node that\n * can be re-parented, and a milestone can be — it rides a Tier-2 paragraph rebuild as a preserved\n * sentinel and is moved into the freshly created paragraph, whose children Lexical builds new\n * elements for. Lexical skips notifying its decorator listener whenever the payload is unchanged\n * (`reconcileDecorator` bails on `currentDecorators[key] === decorator`, and equal strings always\n * compare equal), so `@lexical/react`'s portal would stay bound to the OLD, detached element and\n * the live one would render nothing from then on. `\"\"` is what makes that harmless here: there is\n * nothing to lose. Giving this node visible payload would reintroduce exactly that defect — render\n * such bytes from `createDOM` instead, the way `ImmutableTypedTextNode` does.\n */\n decorate() {\n return \"\";\n }\n exportJSON() {\n return {\n type: this.getType(),\n marker: this.getMarker(),\n sid: this.getSid(),\n eid: this.getEid(),\n unknownAttributes: this.getUnknownAttributes(),\n attributeOrder: this.getAttributeOrder(),\n version: MILESTONE_VERSION,\n };\n }\n // Mutation\n isKeyboardSelectable() {\n return false;\n }\n}\nexport function isMilestoneCommentMarker(marker) {\n return milestoneCommentMarkers.includes(marker);\n}\nexport function $createMilestoneNode(marker, sid, eid, unknownAttributes, attributeOrder) {\n return $applyNodeReplacement(new MilestoneNode(marker, sid, eid, unknownAttributes, undefined, attributeOrder));\n}\nexport function $isMilestoneNode(node) {\n return node instanceof MilestoneNode;\n}\nexport function isSerializedMilestoneNode(node) {\n return node?.type === MilestoneNode.getType();\n}\n","/** Conforms with USJ v3.1 @see https://docs.usfm.bible/usfm/3.1/note/index.html */\nimport { GENERATOR_NOTE_CALLER, HIDDEN_NOTE_CALLER } from \"./node-constants.js\";\nimport { $applyNodeReplacement, ElementNode, isHTMLElement, } from \"lexical\";\nexport const DEFAULT_NOTE_MARKER = \"f\";\n/** @see https://docs.usfm.bible/usfm/3.1/note/index.html */\nconst VALID_NOTE_MARKERS = [\n // Footnote\n DEFAULT_NOTE_MARKER,\n \"fe\",\n \"ef\",\n \"efe\",\n // Cross Reference\n \"x\",\n \"ex\",\n];\n/**\n * Classifies a note marker into its caller family, matching PT9's Standard view rule\n * (Paratext repo `ParatextInternalShared/ScriptureViews/Standard.xslt` lines 446-449): a note is a\n * footnote iff its style starts with `f` or starts with `ef`; EVERY other note style — `x`, `ex`,\n * and any custom note marker — uses the cross-reference caller sequence.\n *\n * Note the deliberate consequence for custom markers: a custom note marker that does not start\n * with `f`/`ef` (e.g. `zfn`) classifies as a cross-reference — auto-callers from the\n * cross-reference sequence, hidden-caller default — exactly as PT9 renders it, even if the marker\n * is semantically a footnote.\n *\n * @param marker - The note marker to classify (e.g. `f`, `x`, `ef`, or a custom marker).\n * @returns `\"footnote\"` for `f*`/`ef*` markers, `\"crossref\"` for everything else.\n */\nexport function getNoteKind(marker) {\n return marker.startsWith(\"f\") || marker.startsWith(\"ef\") ? \"footnote\" : \"crossref\";\n}\n/** List of known properties of `MarkerObject` */\nexport const NOTE_MARKER_OBJECT_PROPS = [\n \"type\",\n \"marker\",\n \"caller\",\n \"category\",\n \"content\",\n];\nexport const NOTE_VERSION = 1;\nexport class NoteNode extends ElementNode {\n __marker;\n __caller;\n __isCollapsed;\n __category;\n __unknownAttributes;\n constructor(marker = DEFAULT_NOTE_MARKER, caller, isCollapsed = true, category, unknownAttributes, key) {\n super(key);\n this.__marker = marker;\n this.__caller =\n caller ?? (getNoteKind(marker) === \"crossref\" ? HIDDEN_NOTE_CALLER : GENERATOR_NOTE_CALLER);\n this.__isCollapsed = isCollapsed;\n this.__category = category;\n this.__unknownAttributes = unknownAttributes;\n }\n static getType() {\n return \"note\";\n }\n static clone(node) {\n const { __marker, __caller, __isCollapsed, __category, __unknownAttributes, __key } = node;\n return new NoteNode(__marker, __caller, __isCollapsed, __category, __unknownAttributes, __key);\n }\n static importDOM() {\n return {\n span: (node) => {\n if (!isNoteElement(node))\n return null;\n return {\n conversion: $convertNoteElement,\n priority: 1,\n };\n },\n };\n }\n static importJSON(serializedNode) {\n return $createNoteNode().updateFromJSON(serializedNode);\n }\n static isValidMarker(marker, extraValidMarkers) {\n return (marker !== undefined &&\n (VALID_NOTE_MARKERS.includes(marker) ||\n (extraValidMarkers?.includes(marker) ?? false)));\n }\n updateFromJSON(serializedNode) {\n return super\n .updateFromJSON(serializedNode)\n .setMarker(serializedNode.marker)\n .setCaller(serializedNode.caller)\n .setIsCollapsed(serializedNode.isCollapsed)\n .setCategory(serializedNode.category)\n .setUnknownAttributes(serializedNode.unknownAttributes);\n }\n setMarker(marker) {\n if (this.__marker === marker)\n return this;\n const self = this.getWritable();\n self.__marker = marker;\n return self;\n }\n getMarker() {\n const self = this.getLatest();\n return self.__marker;\n }\n setCaller(caller) {\n if (this.__caller === caller)\n return this;\n const self = this.getWritable();\n self.__caller = caller;\n return self;\n }\n getCaller() {\n const self = this.getLatest();\n return self.__caller;\n }\n setIsCollapsed(isCollapsed) {\n if (this.__isCollapsed === isCollapsed)\n return this;\n const self = this.getWritable();\n self.__isCollapsed = isCollapsed;\n return self;\n }\n toggleIsCollapsed() {\n const self = this.getWritable();\n self.__isCollapsed = !self.__isCollapsed;\n return self;\n }\n getIsCollapsed() {\n const self = this.getLatest();\n return self.__isCollapsed;\n }\n setCategory(category) {\n if (this.__category === category)\n return this;\n const self = this.getWritable();\n self.__category = category;\n return self;\n }\n getCategory() {\n const self = this.getLatest();\n return self.__category;\n }\n setUnknownAttributes(unknownAttributes) {\n const self = this.getWritable();\n self.__unknownAttributes = unknownAttributes;\n return self;\n }\n getUnknownAttributes() {\n const self = this.getLatest();\n return self.__unknownAttributes;\n }\n createDOM() {\n const dom = document.createElement(\"span\");\n dom.setAttribute(\"data-marker\", this.__marker);\n dom.classList.add(this.__type, `usfm_${this.__marker}`, this.__isCollapsed ? \"collapsed\" : \"expanded\");\n dom.setAttribute(\"data-caller\", this.__caller);\n // The stylesheet's `+`-caller counter rules select on the caller family, not per-marker\n // classes, so custom note markers number correctly (see `getNoteKind` for the PT9 rule).\n dom.setAttribute(\"data-note-kind\", getNoteKind(this.__marker));\n return dom;\n }\n updateDOM(prevNode, dom) {\n // Returning true tells Lexical that this node needs its DOM element\n // replacing with a new copy from createDOM.\n if (prevNode.__isCollapsed !== this.__isCollapsed)\n return true;\n if (prevNode.__marker !== this.__marker) {\n dom.setAttribute(\"data-marker\", this.__marker);\n dom.classList.remove(`usfm_${prevNode.__marker}`);\n dom.classList.add(`usfm_${this.__marker}`);\n dom.setAttribute(\"data-note-kind\", getNoteKind(this.__marker));\n }\n if (prevNode.__caller !== this.__caller)\n dom.setAttribute(\"data-caller\", this.__caller);\n return false;\n }\n exportDOM(editor) {\n const { element } = super.exportDOM(editor);\n if (element && isHTMLElement(element)) {\n element.setAttribute(\"data-marker\", this.getMarker());\n element.classList.add(this.getType(), `usfm_${this.getMarker()}`, this.getIsCollapsed() ? \"collapsed\" : \"expanded\");\n element.setAttribute(\"data-caller\", this.getCaller());\n element.setAttribute(\"data-note-kind\", getNoteKind(this.getMarker()));\n }\n return { element };\n }\n exportJSON() {\n return {\n ...super.exportJSON(),\n type: this.getType(),\n marker: this.getMarker(),\n caller: this.getCaller(),\n isCollapsed: this.getIsCollapsed(),\n category: this.getCategory(),\n unknownAttributes: this.getUnknownAttributes(),\n version: NOTE_VERSION,\n };\n }\n // Mutation\n canBeEmpty() {\n return false;\n }\n isInline() {\n return true;\n }\n}\nfunction $convertNoteElement(element) {\n const marker = element.getAttribute(\"data-marker\") ?? \"f\";\n const caller = element.getAttribute(\"data-caller\") ?? \"\";\n const isCollapsed = element.classList.contains(\"collapsed\");\n const node = $createNoteNode(marker, caller, isCollapsed);\n return { node };\n}\nexport function $createNoteNode(marker, caller, isCollapsed, category, unknownAttributes) {\n return $applyNodeReplacement(new NoteNode(marker, caller, isCollapsed, category, unknownAttributes));\n}\nfunction isNoteElement(node) {\n if (!node)\n return false;\n const marker = node.getAttribute(\"data-marker\") ?? \"\";\n return NoteNode.isValidMarker(marker) && node.classList.contains(NoteNode.getType());\n}\nexport function $isNoteNode(node) {\n return node instanceof NoteNode;\n}\nexport function isSerializedNoteNode(node) {\n return node?.type === NoteNode.getType();\n}\n","/** Generated file using `nx generate markers-data` with 'tools/usfm-markers/src/generators/markers-data/data/usfm.sty' */\n/** @public */\nexport var CategoryType;\n(function (CategoryType) {\n CategoryType[\"FileIdentification\"] = \"FileIdentification\";\n CategoryType[\"Headers\"] = \"Headers\";\n CategoryType[\"Remarks\"] = \"Remarks\";\n CategoryType[\"Introduction\"] = \"Introduction\";\n CategoryType[\"DivisionMarks\"] = \"DivisionMarks\";\n CategoryType[\"Paragraphs\"] = \"Paragraphs\";\n CategoryType[\"Poetry\"] = \"Poetry\";\n CategoryType[\"TitlesHeadings\"] = \"TitlesHeadings\";\n CategoryType[\"Tables\"] = \"Tables\";\n CategoryType[\"CenterTables\"] = \"CenterTables\";\n CategoryType[\"RightTables\"] = \"RightTables\";\n CategoryType[\"Lists\"] = \"Lists\";\n CategoryType[\"Footnotes\"] = \"Footnotes\";\n CategoryType[\"CrossReferences\"] = \"CrossReferences\";\n CategoryType[\"SpecialText\"] = \"SpecialText\";\n CategoryType[\"CharacterStyling\"] = \"CharacterStyling\";\n CategoryType[\"Breaks\"] = \"Breaks\";\n CategoryType[\"SpecialFeatures\"] = \"SpecialFeatures\";\n CategoryType[\"PeripheralReferences\"] = \"PeripheralReferences\";\n CategoryType[\"PeripheralMaterials\"] = \"PeripheralMaterials\";\n CategoryType[\"Uncategorized\"] = \"Uncategorized\";\n})(CategoryType || (CategoryType = {}));\n/** @public */\nexport var MarkerType;\n(function (MarkerType) {\n MarkerType[\"Paragraph\"] = \"Paragraph\";\n MarkerType[\"Character\"] = \"Character\";\n MarkerType[\"Note\"] = \"Note\";\n MarkerType[\"Milestone\"] = \"Milestone\";\n MarkerType[\"Unknown\"] = \"Unknown\";\n})(MarkerType || (MarkerType = {}));\n","/** Generated file using `nx generate markers-data` with 'tools/usfm-markers/src/generators/markers-data/data/usfm.sty' */\nimport { CategoryType, MarkerType } from \"./usfmTypes.js\";\nexport const usfmMarkers = {\n id: {\n category: CategoryType.FileIdentification,\n type: MarkerType.Paragraph,\n description: \"File identification information (BOOKID, FILENAME, EDITOR, MODIFICATION DATE)\",\n hasEndMarker: false,\n children: {\n FileIdentification: [\"usfm\", \"ide\"],\n Headers: [\"h\", \"h1\", \"h2\", \"h3\", \"toc1\", \"toc2\", \"toc3\"],\n Remarks: [\"rem\", \"sts\", \"restore\"],\n Introduction: [\n \"imt\",\n \"imt1\",\n \"imt2\",\n \"imt3\",\n \"imt4\",\n \"imte\",\n \"imte1\",\n \"imte2\",\n \"is\",\n \"is1\",\n \"is2\",\n \"iot\",\n \"io\",\n \"io1\",\n \"io2\",\n \"io3\",\n \"io4\",\n \"ior\",\n \"ip\",\n \"im\",\n \"ipi\",\n \"imi\",\n \"ili\",\n \"ili1\",\n \"ili2\",\n \"ipq\",\n \"imq\",\n \"ipr\",\n \"ib\",\n \"iq\",\n \"iq1\",\n \"iq2\",\n \"iq3\",\n \"iex\",\n \"ie\",\n ],\n DivisionMarks: [\"c\", \"cl\"],\n TitlesHeadings: [\"mt\", \"mt1\", \"mt2\", \"mt3\", \"mt4\"],\n },\n },\n usfm: {\n category: CategoryType.FileIdentification,\n type: MarkerType.Paragraph,\n description: \"File markup version information\",\n hasEndMarker: false,\n children: undefined,\n },\n ide: {\n category: CategoryType.FileIdentification,\n type: MarkerType.Paragraph,\n description: \"File encoding information\",\n hasEndMarker: false,\n children: {\n Remarks: [\"rem\", \"sts\"],\n },\n },\n h: {\n category: CategoryType.Headers,\n type: MarkerType.Paragraph,\n description: \"Running header text for a book (basic)\",\n hasEndMarker: false,\n children: {\n Headers: [\"toc1\", \"toc2\", \"toc3\", \"toca1\", \"toca2\", \"toca3\"],\n },\n },\n h1: {\n category: CategoryType.Headers,\n type: MarkerType.Paragraph,\n description: \"Running header text\",\n hasEndMarker: false,\n children: {\n Headers: [\"toc1\", \"toc2\", \"toc3\", \"toca1\", \"toca2\", \"toca3\"],\n },\n },\n h2: {\n category: CategoryType.Headers,\n type: MarkerType.Paragraph,\n description: \"Running header text, left side of page\",\n hasEndMarker: false,\n children: {\n Headers: [\"toc1\", \"toc2\", \"toc3\", \"toca1\", \"toca2\", \"toca3\"],\n },\n },\n h3: {\n category: CategoryType.Headers,\n type: MarkerType.Paragraph,\n description: \"Running header text, right side of page\",\n hasEndMarker: false,\n children: {\n Headers: [\"toc1\", \"toc2\", \"toc3\", \"toca1\", \"toca2\", \"toca3\"],\n },\n },\n toc1: {\n category: CategoryType.Headers,\n type: MarkerType.Paragraph,\n description: \"Long table of contents text\",\n hasEndMarker: false,\n children: undefined,\n },\n toc2: {\n category: CategoryType.Headers,\n type: MarkerType.Paragraph,\n description: \"Short table of contents text\",\n hasEndMarker: false,\n children: undefined,\n },\n toc3: {\n category: CategoryType.Headers,\n type: MarkerType.Paragraph,\n description: \"Book Abbreviation\",\n hasEndMarker: false,\n children: undefined,\n },\n toca1: {\n category: CategoryType.Headers,\n type: MarkerType.Paragraph,\n description: \"Alternative language long table of contents text\",\n hasEndMarker: false,\n children: undefined,\n },\n toca2: {\n category: CategoryType.Headers,\n type: MarkerType.Paragraph,\n description: \"Alternative language short table of contents text\",\n hasEndMarker: false,\n children: undefined,\n },\n toca3: {\n category: CategoryType.Headers,\n type: MarkerType.Paragraph,\n description: \"Alternative language book Abbreviation\",\n hasEndMarker: false,\n children: undefined,\n },\n rem: {\n category: CategoryType.Remarks,\n type: MarkerType.Paragraph,\n description: \"Comments and remarks\",\n hasEndMarker: false,\n children: undefined,\n },\n sts: {\n category: CategoryType.Remarks,\n type: MarkerType.Paragraph,\n description: \"Status of this file\",\n hasEndMarker: false,\n children: undefined,\n },\n restore: {\n category: CategoryType.Remarks,\n type: MarkerType.Paragraph,\n description: \"Project restore information\",\n hasEndMarker: false,\n children: undefined,\n },\n imt: {\n category: CategoryType.Introduction,\n type: MarkerType.Paragraph,\n description: \"Introduction major title, level 1 (if single level) (basic)\",\n hasEndMarker: false,\n children: {\n Introduction: [\"iqt\"],\n SpecialText: [\"bk\"],\n },\n },\n imt1: {\n category: CategoryType.Introduction,\n type: MarkerType.Paragraph,\n description: \"Introduction major title, level 1 (if multiple levels)\",\n hasEndMarker: false,\n children: {\n Introduction: [\"iqt\"],\n SpecialText: [\"bk\"],\n },\n },\n imt2: {\n category: CategoryType.Introduction,\n type: MarkerType.Paragraph,\n description: \"Introduction major title, level 2\",\n hasEndMarker: false,\n children: {\n Introduction: [\"iqt\"],\n SpecialText: [\"bk\"],\n },\n },\n imt3: {\n category: CategoryType.Introduction,\n type: MarkerType.Paragraph,\n description: \"Introduction major title, level 3\",\n hasEndMarker: false,\n children: {\n Introduction: [\"iqt\"],\n SpecialText: [\"bk\"],\n },\n },\n imt4: {\n category: CategoryType.Introduction,\n type: MarkerType.Paragraph,\n description: \"Introduction major title, level 4 (usually within parenthesis)\",\n hasEndMarker: false,\n children: {\n Introduction: [\"iqt\"],\n SpecialText: [\"bk\"],\n },\n },\n imte: {\n category: CategoryType.Introduction,\n type: MarkerType.Paragraph,\n description: \"Introduction major title at introduction end, level 1 (if single level)\",\n hasEndMarker: false,\n children: {\n Introduction: [\"iqt\"],\n SpecialText: [\"bk\"],\n },\n },\n imte1: {\n category: CategoryType.Introduction,\n type: MarkerType.Paragraph,\n description: \"Introduction major title at introduction end, level 1 (if multiple levels)\",\n hasEndMarker: false,\n children: {\n Introduction: [\"iqt\"],\n SpecialText: [\"bk\"],\n },\n },\n imte2: {\n category: CategoryType.Introduction,\n type: MarkerType.Paragraph,\n description: \"Introduction major title at introduction end, level 2\",\n hasEndMarker: false,\n children: {\n Introduction: [\"iqt\"],\n SpecialText: [\"bk\"],\n },\n },\n is: {\n category: CategoryType.Introduction,\n type: MarkerType.Paragraph,\n description: \"Introduction section heading, level 1 (if single level) (basic)\",\n hasEndMarker: false,\n children: {\n Introduction: [\"iqt\"],\n SpecialText: [\"bk\"],\n CharacterStyling: [\"no\"],\n },\n },\n is1: {\n category: CategoryType.Introduction,\n type: MarkerType.Paragraph,\n description: \"Introduction section heading, level 1 (if multiple levels)\",\n hasEndMarker: false,\n children: {\n Introduction: [\"iqt\"],\n SpecialText: [\"bk\"],\n },\n },\n is2: {\n category: CategoryType.Introduction,\n type: MarkerType.Paragraph,\n description: \"Introduction section heading, level 2\",\n hasEndMarker: false,\n children: {\n Introduction: [\"iqt\"],\n SpecialText: [\"bk\"],\n },\n },\n iot: {\n category: CategoryType.Introduction,\n type: MarkerType.Paragraph,\n description: \"Introduction outline title (basic)\",\n hasEndMarker: false,\n children: {\n Introduction: [\"iqt\"],\n CharacterStyling: [\"no\"],\n },\n },\n io: {\n category: CategoryType.Introduction,\n type: MarkerType.Paragraph,\n description: \"Introduction outline text, level 1 (if single level)\",\n hasEndMarker: false,\n children: {\n Introduction: [\"ior\", \"iqt\"],\n CrossReferences: [\"xt\"],\n SpecialText: [\n \"qt\",\n \"nd\",\n \"tl\",\n \"dc\",\n \"bk\",\n \"sig\",\n \"pn\",\n \"png\",\n \"addpn\",\n \"wj\",\n \"k\",\n \"ord\",\n \"add\",\n ],\n CharacterStyling: [\"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n io1: {\n category: CategoryType.Introduction,\n type: MarkerType.Paragraph,\n description: \"Introduction outline text, level 1 (if multiple levels) (basic)\",\n hasEndMarker: false,\n children: {\n Introduction: [\"ior\", \"iqt\"],\n CrossReferences: [\"xt\"],\n SpecialText: [\n \"qt\",\n \"nd\",\n \"tl\",\n \"dc\",\n \"bk\",\n \"sig\",\n \"pn\",\n \"png\",\n \"addpn\",\n \"wj\",\n \"k\",\n \"ord\",\n \"add\",\n ],\n CharacterStyling: [\"no\", \"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n io2: {\n category: CategoryType.Introduction,\n type: MarkerType.Paragraph,\n description: \"Introduction outline text, level 2\",\n hasEndMarker: false,\n children: {\n Introduction: [\"ior\", \"iqt\"],\n CrossReferences: [\"xt\"],\n SpecialText: [\n \"qt\",\n \"nd\",\n \"tl\",\n \"dc\",\n \"bk\",\n \"sig\",\n \"pn\",\n \"png\",\n \"addpn\",\n \"wj\",\n \"k\",\n \"ord\",\n \"add\",\n ],\n CharacterStyling: [\"no\", \"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n io3: {\n category: CategoryType.Introduction,\n type: MarkerType.Paragraph,\n description: \"Introduction outline text, level 3\",\n hasEndMarker: false,\n children: {\n Introduction: [\"ior\", \"iqt\"],\n CrossReferences: [\"xt\"],\n SpecialText: [\n \"qt\",\n \"nd\",\n \"tl\",\n \"dc\",\n \"bk\",\n \"sig\",\n \"pn\",\n \"png\",\n \"addpn\",\n \"wj\",\n \"k\",\n \"ord\",\n \"add\",\n ],\n CharacterStyling: [\"no\", \"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n io4: {\n category: CategoryType.Introduction,\n type: MarkerType.Paragraph,\n description: \"Introduction outline text, level 4\",\n hasEndMarker: false,\n children: {\n Introduction: [\"ior\", \"iqt\"],\n CrossReferences: [\"xt\"],\n SpecialText: [\n \"qt\",\n \"nd\",\n \"tl\",\n \"dc\",\n \"bk\",\n \"sig\",\n \"pn\",\n \"png\",\n \"addpn\",\n \"wj\",\n \"k\",\n \"ord\",\n \"add\",\n ],\n CharacterStyling: [\"no\", \"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n ior: {\n category: CategoryType.Introduction,\n type: MarkerType.Character,\n description: \"Introduction references range for outline entry; for marking references separately\",\n hasEndMarker: true,\n children: undefined,\n },\n ip: {\n category: CategoryType.Introduction,\n type: MarkerType.Paragraph,\n description: \"Introduction prose paragraph (basic)\",\n hasEndMarker: false,\n children: {\n Introduction: [\"iqt\"],\n Footnotes: [\"f\", \"fe\", \"fm\"],\n CrossReferences: [\"xt\"],\n SpecialText: [\n \"qt\",\n \"nd\",\n \"tl\",\n \"dc\",\n \"bk\",\n \"sig\",\n \"pn\",\n \"png\",\n \"addpn\",\n \"wj\",\n \"k\",\n \"ord\",\n \"add\",\n ],\n CharacterStyling: [\"no\", \"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n im: {\n category: CategoryType.Introduction,\n type: MarkerType.Paragraph,\n description: \"Introduction prose paragraph, with no first line indent (may occur after poetry)\",\n hasEndMarker: false,\n children: {\n Introduction: [\"iqt\"],\n CrossReferences: [\"xt\"],\n SpecialText: [\n \"qt\",\n \"nd\",\n \"tl\",\n \"dc\",\n \"bk\",\n \"sig\",\n \"pn\",\n \"png\",\n \"addpn\",\n \"wj\",\n \"k\",\n \"ord\",\n \"add\",\n ],\n CharacterStyling: [\"no\", \"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n ipi: {\n category: CategoryType.Introduction,\n type: MarkerType.Paragraph,\n description: \"Introduction prose paragraph, indented, with first line indent\",\n hasEndMarker: false,\n children: {\n Introduction: [\"iqt\"],\n CrossReferences: [\"xt\"],\n SpecialText: [\n \"qt\",\n \"nd\",\n \"tl\",\n \"dc\",\n \"bk\",\n \"sig\",\n \"pn\",\n \"png\",\n \"addpn\",\n \"wj\",\n \"k\",\n \"ord\",\n \"add\",\n ],\n CharacterStyling: [\"no\", \"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n imi: {\n category: CategoryType.Introduction,\n type: MarkerType.Paragraph,\n description: \"Introduction prose paragraph text, indented, with no first line indent\",\n hasEndMarker: false,\n children: {\n Introduction: [\"iqt\"],\n CrossReferences: [\"xt\"],\n SpecialText: [\n \"qt\",\n \"nd\",\n \"tl\",\n \"dc\",\n \"bk\",\n \"sig\",\n \"pn\",\n \"png\",\n \"addpn\",\n \"wj\",\n \"k\",\n \"ord\",\n \"add\",\n ],\n CharacterStyling: [\"no\", \"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n ili: {\n category: CategoryType.Introduction,\n type: MarkerType.Paragraph,\n description: \"A list entry, level 1 (if single level)\",\n hasEndMarker: false,\n children: {\n Introduction: [\"iqt\"],\n CrossReferences: [\"xt\"],\n SpecialText: [\n \"qt\",\n \"nd\",\n \"tl\",\n \"dc\",\n \"bk\",\n \"sig\",\n \"pn\",\n \"png\",\n \"addpn\",\n \"wj\",\n \"k\",\n \"ord\",\n \"add\",\n ],\n CharacterStyling: [\"no\", \"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n ili1: {\n category: CategoryType.Introduction,\n type: MarkerType.Paragraph,\n description: \"A list entry, level 1 (if multiple levels)\",\n hasEndMarker: false,\n children: {\n Introduction: [\"iqt\"],\n CrossReferences: [\"xt\"],\n SpecialText: [\n \"qt\",\n \"nd\",\n \"tl\",\n \"dc\",\n \"bk\",\n \"sig\",\n \"pn\",\n \"png\",\n \"addpn\",\n \"wj\",\n \"k\",\n \"ord\",\n \"add\",\n ],\n CharacterStyling: [\"no\", \"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n ili2: {\n category: CategoryType.Introduction,\n type: MarkerType.Paragraph,\n description: \"A list entry, level 2\",\n hasEndMarker: false,\n children: {\n Introduction: [\"iqt\"],\n CrossReferences: [\"xt\"],\n SpecialText: [\n \"qt\",\n \"nd\",\n \"tl\",\n \"dc\",\n \"bk\",\n \"sig\",\n \"pn\",\n \"png\",\n \"addpn\",\n \"wj\",\n \"k\",\n \"ord\",\n \"add\",\n ],\n CharacterStyling: [\"no\", \"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n ipq: {\n category: CategoryType.Introduction,\n type: MarkerType.Paragraph,\n description: \"Introduction prose paragraph, quote from the body text\",\n hasEndMarker: false,\n children: {\n Introduction: [\"iqt\"],\n CrossReferences: [\"xt\"],\n SpecialText: [\n \"qt\",\n \"nd\",\n \"tl\",\n \"dc\",\n \"bk\",\n \"sig\",\n \"pn\",\n \"png\",\n \"addpn\",\n \"wj\",\n \"k\",\n \"ord\",\n \"add\",\n ],\n CharacterStyling: [\"no\", \"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n imq: {\n category: CategoryType.Introduction,\n type: MarkerType.Paragraph,\n description: \"Introduction prose paragraph, quote from the body text, with no first line indent\",\n hasEndMarker: false,\n children: {\n Introduction: [\"iqt\"],\n CrossReferences: [\"xt\"],\n SpecialText: [\n \"qt\",\n \"nd\",\n \"tl\",\n \"dc\",\n \"bk\",\n \"sig\",\n \"pn\",\n \"png\",\n \"addpn\",\n \"wj\",\n \"k\",\n \"ord\",\n \"add\",\n ],\n CharacterStyling: [\"no\", \"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n ipr: {\n category: CategoryType.Introduction,\n type: MarkerType.Paragraph,\n description: \"Introduction prose paragraph, right aligned\",\n hasEndMarker: false,\n children: {\n Introduction: [\"iqt\"],\n CrossReferences: [\"xt\"],\n SpecialText: [\n \"qt\",\n \"nd\",\n \"tl\",\n \"dc\",\n \"bk\",\n \"sig\",\n \"pn\",\n \"png\",\n \"addpn\",\n \"wj\",\n \"k\",\n \"ord\",\n \"add\",\n ],\n CharacterStyling: [\"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n ib: {\n category: CategoryType.Introduction,\n type: MarkerType.Paragraph,\n description: \"Introduction blank line\",\n hasEndMarker: false,\n children: {\n Introduction: [\"iqt\"],\n },\n },\n iq: {\n category: CategoryType.Introduction,\n type: MarkerType.Paragraph,\n description: \"Introduction poetry text, level 1 (if single level)\",\n hasEndMarker: false,\n children: {\n Introduction: [\"iqt\"],\n CrossReferences: [\"xt\"],\n SpecialText: [\n \"qt\",\n \"nd\",\n \"tl\",\n \"dc\",\n \"bk\",\n \"sig\",\n \"pn\",\n \"png\",\n \"addpn\",\n \"wj\",\n \"k\",\n \"ord\",\n \"add\",\n ],\n CharacterStyling: [\"no\", \"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n iq1: {\n category: CategoryType.Introduction,\n type: MarkerType.Paragraph,\n description: \"Introduction poetry text, level 1 (if multiple levels)\",\n hasEndMarker: false,\n children: {\n Introduction: [\"iqt\"],\n CrossReferences: [\"xt\"],\n SpecialText: [\n \"qt\",\n \"nd\",\n \"tl\",\n \"dc\",\n \"bk\",\n \"sig\",\n \"pn\",\n \"png\",\n \"addpn\",\n \"wj\",\n \"k\",\n \"ord\",\n \"add\",\n ],\n CharacterStyling: [\"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n iq2: {\n category: CategoryType.Introduction,\n type: MarkerType.Paragraph,\n description: \"Introduction poetry text, level 2\",\n hasEndMarker: false,\n children: {\n Introduction: [\"iqt\"],\n CrossReferences: [\"xt\"],\n SpecialText: [\n \"qt\",\n \"nd\",\n \"tl\",\n \"dc\",\n \"bk\",\n \"sig\",\n \"pn\",\n \"png\",\n \"addpn\",\n \"wj\",\n \"k\",\n \"ord\",\n \"add\",\n ],\n CharacterStyling: [\"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n iq3: {\n category: CategoryType.Introduction,\n type: MarkerType.Paragraph,\n description: \"Introduction poetry text, level 3\",\n hasEndMarker: false,\n children: {\n Introduction: [\"iqt\"],\n CrossReferences: [\"xt\"],\n SpecialText: [\n \"qt\",\n \"nd\",\n \"tl\",\n \"dc\",\n \"bk\",\n \"sig\",\n \"pn\",\n \"png\",\n \"addpn\",\n \"wj\",\n \"k\",\n \"ord\",\n \"add\",\n ],\n CharacterStyling: [\"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n iex: {\n category: CategoryType.Introduction,\n type: MarkerType.Paragraph,\n description: \"Introduction explanatory or bridge text (e.g. explanation of missing book in Short Old Testament)\",\n hasEndMarker: false,\n children: {\n Introduction: [\"iqt\"],\n CharacterStyling: [\"no\"],\n },\n },\n iqt: {\n category: CategoryType.Introduction,\n type: MarkerType.Character,\n description: \"For quoted scripture text appearing in the introduction\",\n hasEndMarker: true,\n children: undefined,\n },\n ie: {\n category: CategoryType.Introduction,\n type: MarkerType.Paragraph,\n description: \"Introduction ending marker\",\n hasEndMarker: false,\n children: undefined,\n },\n c: {\n category: CategoryType.DivisionMarks,\n type: MarkerType.Paragraph,\n description: \"Chapter number (necessary for normal Paratext operation)\",\n hasEndMarker: false,\n children: {\n DivisionMarks: [\"ca\", \"cp\", \"cl\", \"cd\"],\n Paragraphs: [\"p\", \"m\", \"po\", \"pr\", \"cls\", \"pi\", \"pi1\", \"pi2\", \"pi3\", \"pc\", \"mi\", \"nb\"],\n Poetry: [\"q\", \"q1\", \"q2\", \"q3\", \"q4\", \"qc\", \"qr\", \"qa\", \"qd\", \"b\"],\n TitlesHeadings: [\n \"mte\",\n \"ms\",\n \"ms1\",\n \"ms2\",\n \"ms3\",\n \"s\",\n \"s1\",\n \"s2\",\n \"s3\",\n \"s4\",\n \"r\",\n \"sp\",\n \"d\",\n \"sd\",\n \"sd1\",\n \"sd2\",\n \"sd3\",\n \"sd4\",\n ],\n Lists: [\"lh\", \"li\", \"li1\", \"li2\", \"li3\", \"li4\", \"lf\", \"lim\", \"lim1\", \"lim2\", \"lim3\", \"lim4\"],\n Footnotes: [\"f\", \"fe\"],\n SpecialText: [\"lit\"],\n Breaks: [\"pb\"],\n },\n },\n ca: {\n category: CategoryType.DivisionMarks,\n type: MarkerType.Character,\n description: \"Second (alternate) chapter number (for coding dual versification; useful for places where different traditions of chapter breaks need to be supported in the same translation)\",\n hasEndMarker: true,\n children: undefined,\n },\n cp: {\n category: CategoryType.DivisionMarks,\n type: MarkerType.Paragraph,\n description: \"Published chapter number (chapter string that should appear in the published text)\",\n hasEndMarker: false,\n children: {\n Footnotes: [\"f\"],\n },\n },\n cl: {\n category: CategoryType.DivisionMarks,\n type: MarkerType.Paragraph,\n description: \"Chapter label used for translations that add a word such as 'Chapter' before chapter numbers (e.g. Psalms). The subsequent text is the chapter label.\",\n hasEndMarker: false,\n children: undefined,\n },\n cd: {\n category: CategoryType.DivisionMarks,\n type: MarkerType.Paragraph,\n description: \"Chapter Description (Publishing option D, e.g. in Russian Bibles)\",\n hasEndMarker: false,\n children: {\n DivisionMarks: [\"vp\"],\n CrossReferences: [\"xt\"],\n SpecialText: [\n \"qt\",\n \"nd\",\n \"tl\",\n \"dc\",\n \"bk\",\n \"sig\",\n \"pn\",\n \"png\",\n \"addpn\",\n \"wj\",\n \"k\",\n \"ord\",\n \"add\",\n ],\n CharacterStyling: [\"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n v: {\n category: CategoryType.DivisionMarks,\n type: MarkerType.Character,\n description: \"A verse number (Necessary for normal paratext operation) (basic)\",\n hasEndMarker: false,\n children: undefined,\n },\n va: {\n category: CategoryType.DivisionMarks,\n type: MarkerType.Character,\n description: \"Second (alternate) verse number (for coding dual numeration in Psalms; see also NRSV Exo 22.1-4)\",\n hasEndMarker: true,\n children: undefined,\n },\n vp: {\n category: CategoryType.DivisionMarks,\n type: MarkerType.Character,\n description: \"Published verse marker (verse string that should appear in the published text)\",\n hasEndMarker: true,\n children: undefined,\n },\n p: {\n category: CategoryType.Paragraphs,\n type: MarkerType.Paragraph,\n description: \"Paragraph text, with first line indent (basic)\",\n hasEndMarker: false,\n children: {\n Paragraphs: [\"pmo\", \"pm\", \"pmc\", \"pmr\"],\n Poetry: [\"qm\", \"qm1\", \"qm2\", \"qm3\"],\n TitlesHeadings: [\"mte1\"],\n Footnotes: [\"f\", \"fe\", \"fm\"],\n CrossReferences: [\"x\", \"xt\", \"rq\"],\n SpecialText: [\n \"qt\",\n \"nd\",\n \"tl\",\n \"dc\",\n \"bk\",\n \"sig\",\n \"pn\",\n \"png\",\n \"addpn\",\n \"wj\",\n \"k\",\n \"sls\",\n \"ord\",\n \"add\",\n ],\n CharacterStyling: [\"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n m: {\n category: CategoryType.Paragraphs,\n type: MarkerType.Paragraph,\n description: \"Paragraph text, with no first line indent (may occur after poetry) (basic)\",\n hasEndMarker: false,\n children: {\n Paragraphs: [\"pmo\", \"pm\", \"pmc\", \"pmr\"],\n Poetry: [\"qm\", \"qm1\", \"qm2\", \"qm3\"],\n TitlesHeadings: [\"mte1\"],\n Footnotes: [\"f\", \"fe\", \"fm\"],\n CrossReferences: [\"x\", \"xt\", \"rq\"],\n SpecialText: [\n \"qt\",\n \"nd\",\n \"tl\",\n \"dc\",\n \"bk\",\n \"sig\",\n \"pn\",\n \"png\",\n \"addpn\",\n \"wj\",\n \"k\",\n \"sls\",\n \"ord\",\n \"add\",\n ],\n CharacterStyling: [\"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n po: {\n category: CategoryType.Paragraphs,\n type: MarkerType.Paragraph,\n description: \"Letter opening\",\n hasEndMarker: false,\n children: {\n Paragraphs: [\"pmo\", \"pm\", \"pmc\", \"pmr\"],\n TitlesHeadings: [\"mte1\"],\n Footnotes: [\"f\", \"fe\", \"fm\"],\n CrossReferences: [\"x\", \"xt\", \"rq\"],\n SpecialText: [\n \"qt\",\n \"nd\",\n \"tl\",\n \"dc\",\n \"bk\",\n \"sig\",\n \"pn\",\n \"png\",\n \"addpn\",\n \"wj\",\n \"k\",\n \"sls\",\n \"ord\",\n \"add\",\n ],\n CharacterStyling: [\"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n pr: {\n category: CategoryType.Paragraphs,\n type: MarkerType.Paragraph,\n description: \"Text refrain (paragraph text, right aligned)\",\n hasEndMarker: false,\n children: {\n Paragraphs: [\"pmo\", \"pm\", \"pmc\", \"pmr\"],\n Poetry: [\"qm\", \"qm1\", \"qm2\", \"qm3\"],\n TitlesHeadings: [\"mte1\"],\n Footnotes: [\"f\", \"fe\", \"fm\"],\n CrossReferences: [\"x\", \"xt\", \"rq\"],\n SpecialText: [\n \"qt\",\n \"nd\",\n \"tl\",\n \"dc\",\n \"bk\",\n \"sig\",\n \"pn\",\n \"png\",\n \"addpn\",\n \"wj\",\n \"k\",\n \"sls\",\n \"ord\",\n \"add\",\n ],\n CharacterStyling: [\"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n cls: {\n category: CategoryType.Paragraphs,\n type: MarkerType.Paragraph,\n description: \"Letter Closing\",\n hasEndMarker: false,\n children: {\n SpecialText: [\"tl\", \"sig\", \"pn\", \"png\", \"addpn\", \"add\"],\n },\n },\n pmo: {\n category: CategoryType.Paragraphs,\n type: MarkerType.Paragraph,\n description: \"Embedded text opening\",\n hasEndMarker: false,\n children: {\n TitlesHeadings: [\"mte1\"],\n Footnotes: [\"f\", \"fe\", \"fm\"],\n CrossReferences: [\"x\", \"xt\", \"rq\"],\n SpecialText: [\n \"qt\",\n \"nd\",\n \"tl\",\n \"dc\",\n \"bk\",\n \"sig\",\n \"pn\",\n \"png\",\n \"addpn\",\n \"wj\",\n \"k\",\n \"sls\",\n \"ord\",\n \"add\",\n ],\n CharacterStyling: [\"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n pm: {\n category: CategoryType.Paragraphs,\n type: MarkerType.Paragraph,\n description: \"Embedded text paragraph\",\n hasEndMarker: false,\n children: {\n TitlesHeadings: [\"mte1\"],\n Footnotes: [\"f\", \"fe\", \"fm\"],\n CrossReferences: [\"x\", \"xt\", \"rq\"],\n SpecialText: [\n \"qt\",\n \"nd\",\n \"tl\",\n \"dc\",\n \"bk\",\n \"sig\",\n \"pn\",\n \"png\",\n \"addpn\",\n \"wj\",\n \"k\",\n \"sls\",\n \"ord\",\n \"add\",\n ],\n CharacterStyling: [\"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n pmc: {\n category: CategoryType.Paragraphs,\n type: MarkerType.Paragraph,\n description: \"Embedded text closing\",\n hasEndMarker: false,\n children: {\n TitlesHeadings: [\"mte1\"],\n Footnotes: [\"f\", \"fe\", \"fm\"],\n CrossReferences: [\"x\", \"xt\", \"rq\"],\n SpecialText: [\n \"qt\",\n \"nd\",\n \"tl\",\n \"dc\",\n \"bk\",\n \"sig\",\n \"pn\",\n \"png\",\n \"addpn\",\n \"wj\",\n \"k\",\n \"sls\",\n \"ord\",\n \"add\",\n ],\n CharacterStyling: [\"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n pmr: {\n category: CategoryType.Paragraphs,\n type: MarkerType.Paragraph,\n description: \"Embedded text refrain (e.g. Then all the people shall say, 'Amen!')\",\n hasEndMarker: false,\n children: {\n TitlesHeadings: [\"mte1\"],\n Footnotes: [\"f\", \"fe\", \"fm\"],\n CrossReferences: [\"x\", \"xt\", \"rq\"],\n SpecialText: [\n \"qt\",\n \"nd\",\n \"tl\",\n \"dc\",\n \"bk\",\n \"sig\",\n \"pn\",\n \"png\",\n \"addpn\",\n \"wj\",\n \"k\",\n \"sls\",\n \"ord\",\n \"add\",\n ],\n CharacterStyling: [\"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n pi: {\n category: CategoryType.Paragraphs,\n type: MarkerType.Paragraph,\n description: \"Paragraph text, level 1 indent (if single level), with first line indent; often used for discourse (basic)\",\n hasEndMarker: false,\n children: {\n Poetry: [\"qm\", \"qm1\", \"qm2\", \"qm3\"],\n TitlesHeadings: [\"mte1\"],\n Footnotes: [\"f\", \"fe\", \"fm\"],\n CrossReferences: [\"x\", \"xt\", \"rq\"],\n SpecialText: [\n \"qt\",\n \"nd\",\n \"tl\",\n \"dc\",\n \"bk\",\n \"sig\",\n \"pn\",\n \"png\",\n \"addpn\",\n \"wj\",\n \"k\",\n \"sls\",\n \"ord\",\n \"add\",\n ],\n CharacterStyling: [\"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n pi1: {\n category: CategoryType.Paragraphs,\n type: MarkerType.Paragraph,\n description: \"Paragraph text, level 1 indent (if multiple levels), with first line indent; often used for discourse\",\n hasEndMarker: false,\n children: {\n Poetry: [\"qm\", \"qm1\", \"qm2\", \"qm3\"],\n TitlesHeadings: [\"mte1\"],\n Footnotes: [\"f\", \"fe\", \"fm\"],\n CrossReferences: [\"x\", \"xt\", \"rq\"],\n SpecialText: [\n \"qt\",\n \"nd\",\n \"tl\",\n \"dc\",\n \"bk\",\n \"sig\",\n \"pn\",\n \"png\",\n \"addpn\",\n \"wj\",\n \"k\",\n \"sls\",\n \"ord\",\n \"add\",\n ],\n CharacterStyling: [\"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n pi2: {\n category: CategoryType.Paragraphs,\n type: MarkerType.Paragraph,\n description: \"Paragraph text, level 2 indent, with first line indent; often used for discourse\",\n hasEndMarker: false,\n children: {\n Poetry: [\"qm\", \"qm1\", \"qm2\", \"qm3\"],\n TitlesHeadings: [\"mte1\"],\n Footnotes: [\"f\", \"fe\", \"fm\"],\n CrossReferences: [\"x\", \"xt\", \"rq\"],\n SpecialText: [\n \"qt\",\n \"nd\",\n \"tl\",\n \"dc\",\n \"bk\",\n \"sig\",\n \"pn\",\n \"png\",\n \"addpn\",\n \"wj\",\n \"k\",\n \"sls\",\n \"ord\",\n \"add\",\n ],\n CharacterStyling: [\"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n pi3: {\n category: CategoryType.Paragraphs,\n type: MarkerType.Paragraph,\n description: \"Paragraph text, level 3 indent, with first line indent; often used for discourse\",\n hasEndMarker: false,\n children: {\n Poetry: [\"qm\", \"qm1\", \"qm2\", \"qm3\"],\n TitlesHeadings: [\"mte1\"],\n Footnotes: [\"f\", \"fe\", \"fm\"],\n CrossReferences: [\"x\", \"xt\", \"rq\"],\n SpecialText: [\n \"qt\",\n \"nd\",\n \"tl\",\n \"dc\",\n \"bk\",\n \"sig\",\n \"pn\",\n \"png\",\n \"addpn\",\n \"wj\",\n \"k\",\n \"sls\",\n \"ord\",\n \"add\",\n ],\n CharacterStyling: [\"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n pc: {\n category: CategoryType.Paragraphs,\n type: MarkerType.Paragraph,\n description: \"Paragraph text, centered (for Inscription)\",\n hasEndMarker: false,\n children: {\n Poetry: [\"qm\", \"qm1\", \"qm2\", \"qm3\"],\n TitlesHeadings: [\"mte1\"],\n Footnotes: [\"f\", \"fe\", \"fm\"],\n CrossReferences: [\"x\", \"xt\", \"rq\"],\n SpecialText: [\n \"qt\",\n \"nd\",\n \"tl\",\n \"dc\",\n \"bk\",\n \"sig\",\n \"pn\",\n \"png\",\n \"addpn\",\n \"wj\",\n \"k\",\n \"sls\",\n \"ord\",\n \"add\",\n ],\n CharacterStyling: [\"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n mi: {\n category: CategoryType.Paragraphs,\n type: MarkerType.Paragraph,\n description: \"Paragraph text, indented, with no first line indent; often used for discourse\",\n hasEndMarker: false,\n children: {\n Poetry: [\"qm\", \"qm1\", \"qm2\", \"qm3\"],\n TitlesHeadings: [\"mte1\"],\n Footnotes: [\"f\", \"fe\", \"fm\"],\n CrossReferences: [\"x\", \"xt\", \"rq\"],\n SpecialText: [\n \"qt\",\n \"nd\",\n \"tl\",\n \"dc\",\n \"bk\",\n \"sig\",\n \"pn\",\n \"png\",\n \"addpn\",\n \"wj\",\n \"k\",\n \"sls\",\n \"ord\",\n \"add\",\n ],\n CharacterStyling: [\"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n nb: {\n category: CategoryType.Paragraphs,\n type: MarkerType.Paragraph,\n description: \"Paragraph text, with no break from previous paragraph text (at chapter boundary) (basic)\",\n hasEndMarker: false,\n children: {\n Poetry: [\"qm\", \"qm1\", \"qm2\", \"qm3\"],\n TitlesHeadings: [\"mte1\"],\n Footnotes: [\"f\", \"fe\", \"fm\"],\n CrossReferences: [\"x\", \"xt\", \"rq\"],\n SpecialText: [\n \"qt\",\n \"nd\",\n \"tl\",\n \"dc\",\n \"bk\",\n \"sig\",\n \"pn\",\n \"png\",\n \"addpn\",\n \"wj\",\n \"k\",\n \"sls\",\n \"ord\",\n \"add\",\n ],\n CharacterStyling: [\"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n q: {\n category: CategoryType.Poetry,\n type: MarkerType.Paragraph,\n description: \"Poetry text, level 1 indent (if single level)\",\n hasEndMarker: false,\n children: {\n Poetry: [\"qs\", \"qac\", \"qm\", \"qm1\", \"qm2\", \"qm3\"],\n TitlesHeadings: [\"mte1\"],\n Footnotes: [\"f\", \"fe\", \"fm\"],\n CrossReferences: [\"x\", \"xt\", \"rq\"],\n SpecialText: [\n \"qt\",\n \"nd\",\n \"tl\",\n \"dc\",\n \"bk\",\n \"sig\",\n \"pn\",\n \"png\",\n \"addpn\",\n \"wj\",\n \"k\",\n \"sls\",\n \"ord\",\n \"add\",\n ],\n CharacterStyling: [\"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n q1: {\n category: CategoryType.Poetry,\n type: MarkerType.Paragraph,\n description: \"Poetry text, level 1 indent (if multiple levels) (basic)\",\n hasEndMarker: false,\n children: {\n Poetry: [\"qs\", \"qac\", \"qm\", \"qm1\", \"qm2\", \"qm3\"],\n TitlesHeadings: [\"mte1\"],\n Footnotes: [\"f\", \"fe\", \"fm\"],\n CrossReferences: [\"x\", \"xt\", \"rq\"],\n SpecialText: [\n \"qt\",\n \"nd\",\n \"tl\",\n \"dc\",\n \"bk\",\n \"sig\",\n \"pn\",\n \"png\",\n \"addpn\",\n \"wj\",\n \"k\",\n \"sls\",\n \"ord\",\n \"add\",\n ],\n CharacterStyling: [\"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n q2: {\n category: CategoryType.Poetry,\n type: MarkerType.Paragraph,\n description: \"Poetry text, level 2 indent (basic)\",\n hasEndMarker: false,\n children: {\n Poetry: [\"qs\", \"qac\", \"qm\", \"qm1\", \"qm2\", \"qm3\"],\n TitlesHeadings: [\"mte1\"],\n Footnotes: [\"f\", \"fe\", \"fm\"],\n CrossReferences: [\"x\", \"xt\", \"rq\"],\n SpecialText: [\n \"qt\",\n \"nd\",\n \"tl\",\n \"dc\",\n \"bk\",\n \"sig\",\n \"pn\",\n \"png\",\n \"addpn\",\n \"wj\",\n \"k\",\n \"sls\",\n \"ord\",\n \"add\",\n ],\n CharacterStyling: [\"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n q3: {\n category: CategoryType.Poetry,\n type: MarkerType.Paragraph,\n description: \"Poetry text, level 3 indent\",\n hasEndMarker: false,\n children: {\n Poetry: [\"qs\", \"qac\", \"qm\", \"qm1\", \"qm2\", \"qm3\"],\n TitlesHeadings: [\"mte1\"],\n Footnotes: [\"f\", \"fe\", \"fm\"],\n CrossReferences: [\"x\", \"xt\", \"rq\"],\n SpecialText: [\n \"qt\",\n \"nd\",\n \"tl\",\n \"dc\",\n \"bk\",\n \"sig\",\n \"pn\",\n \"png\",\n \"addpn\",\n \"wj\",\n \"k\",\n \"sls\",\n \"ord\",\n \"add\",\n ],\n CharacterStyling: [\"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n q4: {\n category: CategoryType.Poetry,\n type: MarkerType.Paragraph,\n description: \"Poetry text, level 4 indent\",\n hasEndMarker: false,\n children: {\n Poetry: [\"qs\", \"qac\", \"qm\", \"qm1\", \"qm2\", \"qm3\"],\n TitlesHeadings: [\"mte1\"],\n Footnotes: [\"f\", \"fe\", \"fm\"],\n CrossReferences: [\"x\", \"xt\", \"rq\"],\n SpecialText: [\n \"qt\",\n \"nd\",\n \"tl\",\n \"dc\",\n \"bk\",\n \"sig\",\n \"pn\",\n \"png\",\n \"addpn\",\n \"wj\",\n \"k\",\n \"sls\",\n \"ord\",\n \"add\",\n ],\n CharacterStyling: [\"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n qc: {\n category: CategoryType.Poetry,\n type: MarkerType.Paragraph,\n description: \"Poetry text, centered\",\n hasEndMarker: false,\n children: {\n Poetry: [\"qs\", \"qac\", \"qm\", \"qm1\", \"qm2\", \"qm3\"],\n TitlesHeadings: [\"mte1\"],\n Footnotes: [\"f\", \"fe\", \"fm\"],\n CrossReferences: [\"x\", \"xt\", \"rq\"],\n SpecialText: [\n \"qt\",\n \"nd\",\n \"tl\",\n \"dc\",\n \"bk\",\n \"sig\",\n \"pn\",\n \"png\",\n \"addpn\",\n \"wj\",\n \"k\",\n \"sls\",\n \"ord\",\n \"add\",\n ],\n CharacterStyling: [\"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n qr: {\n category: CategoryType.Poetry,\n type: MarkerType.Paragraph,\n description: \"Poetry text, Right Aligned\",\n hasEndMarker: false,\n children: {\n Poetry: [\"qs\", \"qac\", \"qm\", \"qm1\", \"qm2\", \"qm3\"],\n TitlesHeadings: [\"mte1\"],\n Footnotes: [\"f\", \"fe\", \"fm\"],\n CrossReferences: [\"x\", \"xt\", \"rq\"],\n SpecialText: [\n \"qt\",\n \"nd\",\n \"tl\",\n \"dc\",\n \"bk\",\n \"sig\",\n \"pn\",\n \"png\",\n \"addpn\",\n \"wj\",\n \"k\",\n \"sls\",\n \"ord\",\n \"add\",\n ],\n CharacterStyling: [\"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n qs: {\n category: CategoryType.Poetry,\n type: MarkerType.Character,\n description: \"Poetry text, Selah\",\n hasEndMarker: true,\n children: {\n Footnotes: [\"f\"],\n CrossReferences: [\"x\"],\n },\n },\n qa: {\n category: CategoryType.Poetry,\n type: MarkerType.Paragraph,\n description: \"Poetry text, Acrostic marker/heading\",\n hasEndMarker: false,\n children: undefined,\n },\n qac: {\n category: CategoryType.Poetry,\n type: MarkerType.Character,\n description: \"Poetry text, Acrostic markup of the first character of a line of acrostic poetry\",\n hasEndMarker: true,\n children: undefined,\n },\n qm: {\n category: CategoryType.Poetry,\n type: MarkerType.Paragraph,\n description: \"Poetry text, embedded, level 1 indent (if single level)\",\n hasEndMarker: false,\n children: {\n TitlesHeadings: [\"mte1\"],\n Footnotes: [\"f\", \"fe\", \"fm\"],\n CrossReferences: [\"x\", \"xt\", \"rq\"],\n SpecialText: [\n \"qt\",\n \"nd\",\n \"tl\",\n \"dc\",\n \"bk\",\n \"sig\",\n \"pn\",\n \"png\",\n \"addpn\",\n \"wj\",\n \"k\",\n \"sls\",\n \"ord\",\n \"add\",\n ],\n CharacterStyling: [\"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n qm1: {\n category: CategoryType.Poetry,\n type: MarkerType.Paragraph,\n description: \"Poetry text, embedded, level 1 indent (if multiple levels)\",\n hasEndMarker: false,\n children: {\n TitlesHeadings: [\"mte1\"],\n Footnotes: [\"f\", \"fe\", \"fm\"],\n CrossReferences: [\"x\", \"xt\", \"rq\"],\n SpecialText: [\n \"qt\",\n \"nd\",\n \"tl\",\n \"dc\",\n \"bk\",\n \"sig\",\n \"pn\",\n \"png\",\n \"addpn\",\n \"wj\",\n \"k\",\n \"sls\",\n \"ord\",\n \"add\",\n ],\n CharacterStyling: [\"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n qm2: {\n category: CategoryType.Poetry,\n type: MarkerType.Paragraph,\n description: \"Poetry text, embedded, level 2 indent\",\n hasEndMarker: false,\n children: {\n TitlesHeadings: [\"mte1\"],\n Footnotes: [\"f\", \"fe\", \"fm\"],\n CrossReferences: [\"x\", \"xt\", \"rq\"],\n SpecialText: [\n \"qt\",\n \"nd\",\n \"tl\",\n \"dc\",\n \"bk\",\n \"sig\",\n \"pn\",\n \"png\",\n \"addpn\",\n \"wj\",\n \"k\",\n \"sls\",\n \"ord\",\n \"add\",\n ],\n CharacterStyling: [\"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n qm3: {\n category: CategoryType.Poetry,\n type: MarkerType.Paragraph,\n description: \"Poetry text, embedded, level 3 indent\",\n hasEndMarker: false,\n children: {\n TitlesHeadings: [\"mte1\"],\n Footnotes: [\"f\", \"fe\", \"fm\"],\n CrossReferences: [\"x\", \"xt\", \"rq\"],\n SpecialText: [\n \"qt\",\n \"nd\",\n \"tl\",\n \"dc\",\n \"bk\",\n \"sig\",\n \"pn\",\n \"png\",\n \"addpn\",\n \"wj\",\n \"k\",\n \"sls\",\n \"ord\",\n \"add\",\n ],\n CharacterStyling: [\"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n qd: {\n category: CategoryType.Poetry,\n type: MarkerType.Paragraph,\n description: \"A Hebrew musical performance annotation, similar in content to Hebrew descriptive title.\",\n hasEndMarker: false,\n children: {\n TitlesHeadings: [\"mte1\"],\n Footnotes: [\"f\", \"fe\", \"fm\"],\n CrossReferences: [\"x\", \"xt\", \"rq\"],\n SpecialText: [\n \"qt\",\n \"nd\",\n \"tl\",\n \"dc\",\n \"bk\",\n \"sig\",\n \"pn\",\n \"png\",\n \"addpn\",\n \"wj\",\n \"k\",\n \"sls\",\n \"ord\",\n \"add\",\n ],\n CharacterStyling: [\"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n b: {\n category: CategoryType.Poetry,\n type: MarkerType.Paragraph,\n description: \"Poetry text stanza break (e.g. stanza break) (basic)\",\n hasEndMarker: false,\n children: undefined,\n },\n mt: {\n category: CategoryType.TitlesHeadings,\n type: MarkerType.Paragraph,\n description: \"The main title of the book (if single level)\",\n hasEndMarker: false,\n children: {\n Footnotes: [\"f\"],\n CrossReferences: [\"x\"],\n },\n },\n mt1: {\n category: CategoryType.TitlesHeadings,\n type: MarkerType.Paragraph,\n description: \"The main title of the book (if multiple levels) (basic)\",\n hasEndMarker: false,\n children: {\n Footnotes: [\"f\"],\n CrossReferences: [\"x\"],\n },\n },\n mt2: {\n category: CategoryType.TitlesHeadings,\n type: MarkerType.Paragraph,\n description: \"A secondary title usually occurring before the main title (basic)\",\n hasEndMarker: false,\n children: {\n Footnotes: [\"f\"],\n CrossReferences: [\"x\"],\n },\n },\n mt3: {\n category: CategoryType.TitlesHeadings,\n type: MarkerType.Paragraph,\n description: \"A secondary title occurring after the main title\",\n hasEndMarker: false,\n children: {\n Footnotes: [\"f\"],\n CrossReferences: [\"x\"],\n },\n },\n mt4: {\n category: CategoryType.TitlesHeadings,\n type: MarkerType.Paragraph,\n description: \"A small secondary title sometimes occurring within parentheses\",\n hasEndMarker: false,\n children: undefined,\n },\n mte: {\n category: CategoryType.TitlesHeadings,\n type: MarkerType.Paragraph,\n description: \"The main title of the book repeated at the end of the book, level 1 (if single level)\",\n hasEndMarker: false,\n children: undefined,\n },\n mte1: {\n category: CategoryType.TitlesHeadings,\n type: MarkerType.Paragraph,\n description: \"The main title of the book repeated at the end of the book, level 1 (if multiple levels)\",\n hasEndMarker: false,\n children: {\n TitlesHeadings: [\"mte2\"],\n },\n },\n mte2: {\n category: CategoryType.TitlesHeadings,\n type: MarkerType.Paragraph,\n description: \"A secondary title occurring before or after the 'ending' main title\",\n hasEndMarker: false,\n children: undefined,\n },\n ms: {\n category: CategoryType.TitlesHeadings,\n type: MarkerType.Paragraph,\n description: \"A major section division heading, level 1 (if single level) (basic)\",\n hasEndMarker: false,\n children: {\n TitlesHeadings: [\"mr\"],\n Footnotes: [\"f\", \"fe\", \"fm\"],\n CrossReferences: [\"x\", \"xt\"],\n SpecialText: [\n \"qt\",\n \"nd\",\n \"tl\",\n \"dc\",\n \"bk\",\n \"sig\",\n \"pn\",\n \"png\",\n \"addpn\",\n \"wj\",\n \"k\",\n \"ord\",\n \"add\",\n ],\n CharacterStyling: [\"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n ms1: {\n category: CategoryType.TitlesHeadings,\n type: MarkerType.Paragraph,\n description: \"A major section division heading, level 1 (if multiple levels)\",\n hasEndMarker: false,\n children: {\n TitlesHeadings: [\"mr\"],\n Footnotes: [\"f\", \"fe\", \"fm\"],\n CrossReferences: [\"x\", \"xt\"],\n SpecialText: [\n \"qt\",\n \"nd\",\n \"tl\",\n \"dc\",\n \"bk\",\n \"sig\",\n \"pn\",\n \"png\",\n \"addpn\",\n \"wj\",\n \"k\",\n \"ord\",\n \"add\",\n ],\n CharacterStyling: [\"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n ms2: {\n category: CategoryType.TitlesHeadings,\n type: MarkerType.Paragraph,\n description: \"A major section division heading, level 2\",\n hasEndMarker: false,\n children: {\n TitlesHeadings: [\"mr\"],\n Footnotes: [\"f\", \"fe\", \"fm\"],\n CrossReferences: [\"x\", \"xt\"],\n SpecialText: [\n \"qt\",\n \"nd\",\n \"tl\",\n \"dc\",\n \"bk\",\n \"sig\",\n \"pn\",\n \"png\",\n \"addpn\",\n \"wj\",\n \"k\",\n \"ord\",\n \"add\",\n ],\n CharacterStyling: [\"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n ms3: {\n category: CategoryType.TitlesHeadings,\n type: MarkerType.Paragraph,\n description: \"A major section division heading, level 3\",\n hasEndMarker: false,\n children: {\n TitlesHeadings: [\"mr\"],\n Footnotes: [\"f\", \"fe\"],\n },\n },\n mr: {\n category: CategoryType.TitlesHeadings,\n type: MarkerType.Paragraph,\n description: \"A major section division references range heading (basic)\",\n hasEndMarker: false,\n children: undefined,\n },\n s: {\n category: CategoryType.TitlesHeadings,\n type: MarkerType.Paragraph,\n description: \"A section heading, level 1 (if single level) (basic)\",\n hasEndMarker: false,\n children: {\n TitlesHeadings: [\"sr\", \"r\"],\n Footnotes: [\"f\", \"fe\", \"fm\"],\n CrossReferences: [\"x\", \"xt\"],\n SpecialText: [\n \"qt\",\n \"nd\",\n \"tl\",\n \"dc\",\n \"bk\",\n \"sig\",\n \"pn\",\n \"png\",\n \"addpn\",\n \"wj\",\n \"k\",\n \"ord\",\n \"add\",\n ],\n CharacterStyling: [\"no\", \"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n s1: {\n category: CategoryType.TitlesHeadings,\n type: MarkerType.Paragraph,\n description: \"A section heading, level 1 (if multiple levels)\",\n hasEndMarker: false,\n children: {\n TitlesHeadings: [\"sr\", \"r\"],\n Footnotes: [\"f\", \"fe\", \"fm\"],\n CrossReferences: [\"x\", \"xt\"],\n SpecialText: [\n \"qt\",\n \"nd\",\n \"tl\",\n \"dc\",\n \"bk\",\n \"sig\",\n \"pn\",\n \"png\",\n \"addpn\",\n \"wj\",\n \"k\",\n \"ord\",\n \"add\",\n ],\n CharacterStyling: [\"no\", \"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n s2: {\n category: CategoryType.TitlesHeadings,\n type: MarkerType.Paragraph,\n description: \"A section heading, level 2 (e.g. Proverbs 22-24)\",\n hasEndMarker: false,\n children: {\n TitlesHeadings: [\"sr\", \"r\"],\n Footnotes: [\"f\", \"fe\", \"fm\"],\n CrossReferences: [\"x\", \"xt\"],\n SpecialText: [\n \"qt\",\n \"nd\",\n \"tl\",\n \"dc\",\n \"bk\",\n \"sig\",\n \"pn\",\n \"png\",\n \"addpn\",\n \"wj\",\n \"k\",\n \"ord\",\n \"add\",\n ],\n CharacterStyling: [\"no\", \"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n s3: {\n category: CategoryType.TitlesHeadings,\n type: MarkerType.Paragraph,\n description: \"A section heading, level 3 (e.g. Genesis 'The First Day')\",\n hasEndMarker: false,\n children: {\n TitlesHeadings: [\"sr\", \"r\"],\n Footnotes: [\"f\", \"fe\", \"fm\"],\n CrossReferences: [\"x\", \"xt\"],\n SpecialText: [\n \"qt\",\n \"nd\",\n \"tl\",\n \"dc\",\n \"bk\",\n \"sig\",\n \"pn\",\n \"png\",\n \"addpn\",\n \"wj\",\n \"k\",\n \"ord\",\n \"add\",\n ],\n CharacterStyling: [\"no\", \"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n s4: {\n category: CategoryType.TitlesHeadings,\n type: MarkerType.Paragraph,\n description: \"A section heading, level 4\",\n hasEndMarker: false,\n children: {\n TitlesHeadings: [\"sr\", \"r\"],\n CrossReferences: [\"xt\"],\n SpecialText: [\n \"qt\",\n \"nd\",\n \"tl\",\n \"dc\",\n \"bk\",\n \"sig\",\n \"pn\",\n \"png\",\n \"addpn\",\n \"wj\",\n \"k\",\n \"ord\",\n \"add\",\n ],\n CharacterStyling: [\"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n sr: {\n category: CategoryType.TitlesHeadings,\n type: MarkerType.Paragraph,\n description: \"A section division references range heading\",\n hasEndMarker: false,\n children: undefined,\n },\n r: {\n category: CategoryType.TitlesHeadings,\n type: MarkerType.Paragraph,\n description: \"Parallel reference(s) (basic)\",\n hasEndMarker: false,\n children: undefined,\n },\n sp: {\n category: CategoryType.TitlesHeadings,\n type: MarkerType.Paragraph,\n description: \"A heading, to identify the speaker (e.g. Job)\",\n hasEndMarker: false,\n children: {\n Footnotes: [\"f\", \"fe\", \"fm\"],\n CrossReferences: [\"x\", \"xt\"],\n SpecialText: [\n \"qt\",\n \"nd\",\n \"tl\",\n \"dc\",\n \"bk\",\n \"sig\",\n \"pn\",\n \"png\",\n \"addpn\",\n \"wj\",\n \"k\",\n \"sls\",\n \"ord\",\n \"add\",\n ],\n CharacterStyling: [\"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n d: {\n category: CategoryType.TitlesHeadings,\n type: MarkerType.Paragraph,\n description: \"A Hebrew text heading, to provide description (e.g. Psalms)\",\n hasEndMarker: false,\n children: {\n Footnotes: [\"f\", \"fe\", \"fm\"],\n CrossReferences: [\"x\", \"xt\"],\n SpecialText: [\n \"qt\",\n \"nd\",\n \"tl\",\n \"dc\",\n \"bk\",\n \"sig\",\n \"pn\",\n \"png\",\n \"addpn\",\n \"wj\",\n \"k\",\n \"ord\",\n \"add\",\n ],\n CharacterStyling: [\"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n sd: {\n category: CategoryType.TitlesHeadings,\n type: MarkerType.Paragraph,\n description: \"Vertical space used to divide the text into sections, level 1 (if single level)\",\n hasEndMarker: false,\n children: undefined,\n },\n sd1: {\n category: CategoryType.TitlesHeadings,\n type: MarkerType.Paragraph,\n description: \"Vertical space used to divide the text into sections, level 1 (if multiple levels)\",\n hasEndMarker: false,\n children: undefined,\n },\n sd2: {\n category: CategoryType.TitlesHeadings,\n type: MarkerType.Paragraph,\n description: \"Vertical space used to divide the text into sections, level 2\",\n hasEndMarker: false,\n children: undefined,\n },\n sd3: {\n category: CategoryType.TitlesHeadings,\n type: MarkerType.Paragraph,\n description: \"Vertical space used to divide the text into sections, level 3\",\n hasEndMarker: false,\n children: undefined,\n },\n sd4: {\n category: CategoryType.TitlesHeadings,\n type: MarkerType.Paragraph,\n description: \"Vertical space used to divide the text into sections, level 4\",\n hasEndMarker: false,\n children: undefined,\n },\n lh: {\n category: CategoryType.Lists,\n type: MarkerType.Paragraph,\n description: \"List header (introductory remark)\",\n hasEndMarker: false,\n children: {\n Footnotes: [\"f\", \"fe\", \"fm\"],\n CrossReferences: [\"x\", \"xt\", \"rq\"],\n SpecialText: [\n \"qt\",\n \"nd\",\n \"tl\",\n \"dc\",\n \"bk\",\n \"sig\",\n \"pn\",\n \"png\",\n \"addpn\",\n \"wj\",\n \"k\",\n \"sls\",\n \"ord\",\n \"add\",\n ],\n CharacterStyling: [\"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n li: {\n category: CategoryType.Lists,\n type: MarkerType.Paragraph,\n description: \"A list entry, level 1 (if single level)\",\n hasEndMarker: false,\n children: {\n Lists: [\"litl\", \"lik\", \"liv\", \"liv1\", \"liv2\", \"liv3\", \"liv4\", \"liv5\"],\n Footnotes: [\"f\", \"fe\", \"fm\"],\n CrossReferences: [\"x\", \"xt\", \"rq\"],\n SpecialText: [\n \"qt\",\n \"nd\",\n \"tl\",\n \"dc\",\n \"bk\",\n \"sig\",\n \"pn\",\n \"png\",\n \"addpn\",\n \"wj\",\n \"k\",\n \"sls\",\n \"ord\",\n \"add\",\n ],\n CharacterStyling: [\"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n li1: {\n category: CategoryType.Lists,\n type: MarkerType.Paragraph,\n description: \"A list entry, level 1 (if multiple levels)\",\n hasEndMarker: false,\n children: {\n Lists: [\"litl\", \"lik\", \"liv\", \"liv1\", \"liv2\", \"liv3\", \"liv4\", \"liv5\"],\n Footnotes: [\"f\", \"fe\", \"fm\"],\n CrossReferences: [\"x\", \"xt\", \"rq\"],\n SpecialText: [\n \"qt\",\n \"nd\",\n \"tl\",\n \"dc\",\n \"bk\",\n \"sig\",\n \"pn\",\n \"png\",\n \"addpn\",\n \"wj\",\n \"k\",\n \"sls\",\n \"ord\",\n \"add\",\n ],\n CharacterStyling: [\"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n li2: {\n category: CategoryType.Lists,\n type: MarkerType.Paragraph,\n description: \"A list entry, level 2\",\n hasEndMarker: false,\n children: {\n Lists: [\"litl\", \"lik\", \"liv\", \"liv1\", \"liv2\", \"liv3\", \"liv4\", \"liv5\"],\n Footnotes: [\"f\", \"fe\", \"fm\"],\n CrossReferences: [\"x\", \"xt\", \"rq\"],\n SpecialText: [\n \"qt\",\n \"nd\",\n \"tl\",\n \"dc\",\n \"bk\",\n \"sig\",\n \"pn\",\n \"png\",\n \"addpn\",\n \"wj\",\n \"k\",\n \"sls\",\n \"ord\",\n \"add\",\n ],\n CharacterStyling: [\"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n li3: {\n category: CategoryType.Lists,\n type: MarkerType.Paragraph,\n description: \"A list entry, level 3\",\n hasEndMarker: false,\n children: {\n Lists: [\"litl\", \"lik\", \"liv\", \"liv1\", \"liv2\", \"liv3\", \"liv4\", \"liv5\"],\n Footnotes: [\"f\", \"fe\", \"fm\"],\n CrossReferences: [\"x\", \"xt\", \"rq\"],\n SpecialText: [\n \"qt\",\n \"nd\",\n \"tl\",\n \"dc\",\n \"bk\",\n \"sig\",\n \"pn\",\n \"png\",\n \"addpn\",\n \"wj\",\n \"k\",\n \"sls\",\n \"ord\",\n \"add\",\n ],\n CharacterStyling: [\"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n li4: {\n category: CategoryType.Lists,\n type: MarkerType.Paragraph,\n description: \"A list entry, level 4\",\n hasEndMarker: false,\n children: {\n Lists: [\"litl\", \"lik\", \"liv\", \"liv1\", \"liv2\", \"liv3\", \"liv4\", \"liv5\"],\n Footnotes: [\"f\", \"fe\", \"fm\"],\n CrossReferences: [\"x\", \"xt\", \"rq\"],\n SpecialText: [\n \"qt\",\n \"nd\",\n \"tl\",\n \"dc\",\n \"bk\",\n \"sig\",\n \"pn\",\n \"png\",\n \"addpn\",\n \"wj\",\n \"k\",\n \"sls\",\n \"ord\",\n \"add\",\n ],\n CharacterStyling: [\"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n lf: {\n category: CategoryType.Lists,\n type: MarkerType.Paragraph,\n description: \"List footer (concluding remark)\",\n hasEndMarker: false,\n children: {\n Footnotes: [\"f\", \"fe\", \"fm\"],\n CrossReferences: [\"x\", \"xt\", \"rq\"],\n SpecialText: [\n \"qt\",\n \"nd\",\n \"tl\",\n \"dc\",\n \"bk\",\n \"sig\",\n \"pn\",\n \"png\",\n \"addpn\",\n \"wj\",\n \"k\",\n \"sls\",\n \"ord\",\n \"add\",\n ],\n CharacterStyling: [\"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n lim: {\n category: CategoryType.Lists,\n type: MarkerType.Paragraph,\n description: \"An embedded list entry, level 1 (if single level)\",\n hasEndMarker: false,\n children: {\n Lists: [\"litl\", \"lik\", \"liv\", \"liv1\", \"liv2\", \"liv3\", \"liv4\", \"liv5\"],\n Footnotes: [\"f\", \"fe\", \"fm\"],\n CrossReferences: [\"x\", \"xt\", \"rq\"],\n SpecialText: [\n \"qt\",\n \"nd\",\n \"tl\",\n \"dc\",\n \"bk\",\n \"sig\",\n \"pn\",\n \"png\",\n \"addpn\",\n \"wj\",\n \"k\",\n \"sls\",\n \"ord\",\n \"add\",\n ],\n CharacterStyling: [\"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n lim1: {\n category: CategoryType.Lists,\n type: MarkerType.Paragraph,\n description: \"An embedded list entry, level 1 (if multiple levels)\",\n hasEndMarker: false,\n children: {\n Lists: [\"litl\", \"lik\", \"liv\", \"liv1\", \"liv2\", \"liv3\", \"liv4\", \"liv5\"],\n Footnotes: [\"f\", \"fe\", \"fm\"],\n CrossReferences: [\"x\", \"xt\", \"rq\"],\n SpecialText: [\n \"qt\",\n \"nd\",\n \"tl\",\n \"dc\",\n \"bk\",\n \"sig\",\n \"pn\",\n \"png\",\n \"addpn\",\n \"wj\",\n \"k\",\n \"sls\",\n \"ord\",\n \"add\",\n ],\n CharacterStyling: [\"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n lim2: {\n category: CategoryType.Lists,\n type: MarkerType.Paragraph,\n description: \"An embedded list entry, level 2\",\n hasEndMarker: false,\n children: {\n Lists: [\"litl\", \"lik\", \"liv\", \"liv1\", \"liv2\", \"liv3\", \"liv4\", \"liv5\"],\n Footnotes: [\"f\", \"fe\", \"fm\"],\n CrossReferences: [\"x\", \"xt\", \"rq\"],\n SpecialText: [\n \"qt\",\n \"nd\",\n \"tl\",\n \"dc\",\n \"bk\",\n \"sig\",\n \"pn\",\n \"png\",\n \"addpn\",\n \"wj\",\n \"k\",\n \"sls\",\n \"ord\",\n \"add\",\n ],\n CharacterStyling: [\"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n lim3: {\n category: CategoryType.Lists,\n type: MarkerType.Paragraph,\n description: \"An embedded list item, level 3\",\n hasEndMarker: false,\n children: {\n Lists: [\"litl\", \"lik\", \"liv\", \"liv1\", \"liv2\", \"liv3\", \"liv4\", \"liv5\"],\n Footnotes: [\"f\", \"fe\", \"fm\"],\n CrossReferences: [\"x\", \"xt\", \"rq\"],\n SpecialText: [\n \"qt\",\n \"nd\",\n \"tl\",\n \"dc\",\n \"bk\",\n \"sig\",\n \"pn\",\n \"png\",\n \"addpn\",\n \"wj\",\n \"k\",\n \"sls\",\n \"ord\",\n \"add\",\n ],\n CharacterStyling: [\"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n lim4: {\n category: CategoryType.Lists,\n type: MarkerType.Paragraph,\n description: \"An embedded list entry, level 4\",\n hasEndMarker: false,\n children: {\n Lists: [\"litl\", \"lik\", \"liv\", \"liv1\", \"liv2\", \"liv3\", \"liv4\", \"liv5\"],\n Footnotes: [\"f\", \"fe\", \"fm\"],\n CrossReferences: [\"x\", \"xt\", \"rq\"],\n SpecialText: [\n \"qt\",\n \"nd\",\n \"tl\",\n \"dc\",\n \"bk\",\n \"sig\",\n \"pn\",\n \"png\",\n \"addpn\",\n \"wj\",\n \"k\",\n \"sls\",\n \"ord\",\n \"add\",\n ],\n CharacterStyling: [\"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n litl: {\n category: CategoryType.Lists,\n type: MarkerType.Character,\n description: \"List entry total text\",\n hasEndMarker: true,\n children: undefined,\n },\n lik: {\n category: CategoryType.Lists,\n type: MarkerType.Character,\n description: \"Structured list entry key text\",\n hasEndMarker: true,\n children: undefined,\n },\n liv: {\n category: CategoryType.Lists,\n type: MarkerType.Character,\n description: \"Structured list entry value 1 content (if single value)\",\n hasEndMarker: true,\n children: undefined,\n },\n liv1: {\n category: CategoryType.Lists,\n type: MarkerType.Character,\n description: \"Structured list entry value 1 content (if multiple values)\",\n hasEndMarker: true,\n children: undefined,\n },\n liv2: {\n category: CategoryType.Lists,\n type: MarkerType.Character,\n description: \"Structured list entry value 2 content\",\n hasEndMarker: true,\n children: undefined,\n },\n liv3: {\n category: CategoryType.Lists,\n type: MarkerType.Character,\n description: \"Structured list entry value 3 content\",\n hasEndMarker: true,\n children: undefined,\n },\n liv4: {\n category: CategoryType.Lists,\n type: MarkerType.Character,\n description: \"Structured list entry value 4 content\",\n hasEndMarker: true,\n children: undefined,\n },\n liv5: {\n category: CategoryType.Lists,\n type: MarkerType.Character,\n description: \"Structured list entry value 5 content\",\n hasEndMarker: true,\n children: undefined,\n },\n f: {\n category: CategoryType.Footnotes,\n type: MarkerType.Note,\n description: \"A Footnote text item (basic)\",\n hasEndMarker: true,\n children: {\n Footnotes: [\"fr\", \"ft\", \"fk\", \"fq\", \"fqa\", \"fl\", \"fw\", \"fp\", \"fv\", \"fdc\"],\n CrossReferences: [\"xt\"],\n SpecialText: [\n \"qt\",\n \"nd\",\n \"tl\",\n \"dc\",\n \"bk\",\n \"sig\",\n \"pn\",\n \"png\",\n \"addpn\",\n \"wj\",\n \"k\",\n \"sls\",\n \"ord\",\n \"add\",\n ],\n CharacterStyling: [\"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n fe: {\n category: CategoryType.Footnotes,\n type: MarkerType.Note,\n description: \"An Endnote text item\",\n hasEndMarker: true,\n children: {\n Footnotes: [\"fr\", \"ft\", \"fk\", \"fq\", \"fqa\", \"fl\", \"fw\", \"fp\", \"fv\", \"fdc\"],\n CrossReferences: [\"xt\"],\n SpecialText: [\n \"qt\",\n \"nd\",\n \"tl\",\n \"dc\",\n \"bk\",\n \"sig\",\n \"pn\",\n \"png\",\n \"addpn\",\n \"wj\",\n \"k\",\n \"sls\",\n \"ord\",\n \"add\",\n ],\n CharacterStyling: [\"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n fr: {\n category: CategoryType.Footnotes,\n type: MarkerType.Character,\n description: \"The origin reference for the footnote (basic)\",\n hasEndMarker: true,\n children: undefined,\n },\n ft: {\n category: CategoryType.Footnotes,\n type: MarkerType.Character,\n description: \"Footnote text, Protocanon (basic)\",\n hasEndMarker: true,\n children: undefined,\n },\n fk: {\n category: CategoryType.Footnotes,\n type: MarkerType.Character,\n description: \"A footnote keyword (basic)\",\n hasEndMarker: true,\n children: undefined,\n },\n fq: {\n category: CategoryType.Footnotes,\n type: MarkerType.Character,\n description: \"A footnote scripture quote or alternate rendering (basic)\",\n hasEndMarker: true,\n children: undefined,\n },\n fqa: {\n category: CategoryType.Footnotes,\n type: MarkerType.Character,\n description: \"A footnote alternate rendering for a portion of scripture text\",\n hasEndMarker: true,\n children: undefined,\n },\n fl: {\n category: CategoryType.Footnotes,\n type: MarkerType.Character,\n description: \"A footnote label text item, for marking or 'labelling' the type or alternate translation being provided in the note.\",\n hasEndMarker: true,\n children: undefined,\n },\n fw: {\n category: CategoryType.Footnotes,\n type: MarkerType.Character,\n description: \"A footnote witness list, for distinguishing a list of sigla representing witnesses in critical editions.\",\n hasEndMarker: true,\n children: undefined,\n },\n fp: {\n category: CategoryType.Footnotes,\n type: MarkerType.Character,\n description: \"A Footnote additional paragraph marker\",\n hasEndMarker: true,\n children: undefined,\n },\n fv: {\n category: CategoryType.Footnotes,\n type: MarkerType.Character,\n description: \"A verse number within the footnote text\",\n hasEndMarker: true,\n children: undefined,\n },\n fdc: {\n category: CategoryType.Footnotes,\n type: MarkerType.Character,\n description: \"Footnote text, applies to Deuterocanon only\",\n hasEndMarker: true,\n children: undefined,\n },\n fm: {\n category: CategoryType.Footnotes,\n type: MarkerType.Character,\n description: \"An additional footnote marker location for a previous footnote\",\n hasEndMarker: true,\n children: undefined,\n },\n x: {\n category: CategoryType.CrossReferences,\n type: MarkerType.Note,\n description: \"A list of cross references (basic)\",\n hasEndMarker: true,\n children: {\n CrossReferences: [\"xo\", \"xop\", \"xt\", \"xta\", \"xk\", \"xq\", \"xot\", \"xnt\", \"xdc\"],\n CharacterStyling: [\"it\", \"bd\", \"bdit\", \"em\", \"sc\", \"sup\"],\n },\n },\n xo: {\n category: CategoryType.CrossReferences,\n type: MarkerType.Character,\n description: \"The cross reference origin reference (basic)\",\n hasEndMarker: true,\n children: undefined,\n },\n xop: {\n category: CategoryType.CrossReferences,\n type: MarkerType.Character,\n description: \"Published cross reference origin reference (origin reference that should appear in the published text)\",\n hasEndMarker: true,\n children: undefined,\n },\n xt: {\n category: CategoryType.CrossReferences,\n type: MarkerType.Character,\n description: \"The cross reference target reference(s), protocanon only (basic)\",\n hasEndMarker: true,\n children: undefined,\n },\n xta: {\n category: CategoryType.CrossReferences,\n type: MarkerType.Character,\n description: \"Cross reference target references added text\",\n hasEndMarker: true,\n children: undefined,\n },\n xk: {\n category: CategoryType.CrossReferences,\n type: MarkerType.Character,\n description: \"A cross reference keyword\",\n hasEndMarker: true,\n children: undefined,\n },\n xq: {\n category: CategoryType.CrossReferences,\n type: MarkerType.Character,\n description: \"A cross-reference quotation from the scripture text\",\n hasEndMarker: true,\n children: undefined,\n },\n xot: {\n category: CategoryType.CrossReferences,\n type: MarkerType.Character,\n description: \"Cross-reference target reference(s), Old Testament only\",\n hasEndMarker: true,\n children: undefined,\n },\n xnt: {\n category: CategoryType.CrossReferences,\n type: MarkerType.Character,\n description: \"Cross-reference target reference(s), New Testament only\",\n hasEndMarker: true,\n children: undefined,\n },\n xdc: {\n category: CategoryType.CrossReferences,\n type: MarkerType.Character,\n description: \"Cross-reference target reference(s), Deuterocanon only\",\n hasEndMarker: true,\n children: undefined,\n },\n rq: {\n category: CategoryType.CrossReferences,\n type: MarkerType.Character,\n description: \"A cross-reference indicating the source text for the preceding quotation.\",\n hasEndMarker: true,\n children: undefined,\n },\n qt: {\n category: CategoryType.SpecialText,\n type: MarkerType.Character,\n description: \"For Old Testament quoted text appearing in the New Testament (basic)\",\n hasEndMarker: true,\n children: undefined,\n },\n nd: {\n category: CategoryType.SpecialText,\n type: MarkerType.Character,\n description: \"For name of deity (basic)\",\n hasEndMarker: true,\n children: undefined,\n },\n tl: {\n category: CategoryType.SpecialText,\n type: MarkerType.Character,\n description: \"For transliterated words\",\n hasEndMarker: true,\n children: undefined,\n },\n dc: {\n category: CategoryType.SpecialText,\n type: MarkerType.Character,\n description: \"Deuterocanonical/LXX additions or insertions in the Protocanonical text\",\n hasEndMarker: true,\n children: undefined,\n },\n bk: {\n category: CategoryType.SpecialText,\n type: MarkerType.Character,\n description: \"For the quoted name of a book\",\n hasEndMarker: true,\n children: undefined,\n },\n sig: {\n category: CategoryType.SpecialText,\n type: MarkerType.Character,\n description: \"For the signature of the author of an Epistle\",\n hasEndMarker: true,\n children: undefined,\n },\n pn: {\n category: CategoryType.SpecialText,\n type: MarkerType.Character,\n description: \"For a proper name\",\n hasEndMarker: true,\n children: undefined,\n },\n png: {\n category: CategoryType.SpecialText,\n type: MarkerType.Character,\n description: \"For a geographic proper name\",\n hasEndMarker: true,\n children: undefined,\n },\n addpn: {\n category: CategoryType.SpecialText,\n type: MarkerType.Character,\n description: \"For chinese words to be dot underline & underline\",\n hasEndMarker: true,\n children: undefined,\n },\n wj: {\n category: CategoryType.SpecialText,\n type: MarkerType.Character,\n description: \"For marking the words of Jesus\",\n hasEndMarker: true,\n children: undefined,\n },\n k: {\n category: CategoryType.SpecialText,\n type: MarkerType.Character,\n description: \"For a keyword\",\n hasEndMarker: true,\n children: undefined,\n },\n sls: {\n category: CategoryType.SpecialText,\n type: MarkerType.Character,\n description: \"To represent where the original text is in a secondary language or from an alternate text source\",\n hasEndMarker: true,\n children: undefined,\n },\n ord: {\n category: CategoryType.SpecialText,\n type: MarkerType.Character,\n description: \"For the text portion of an ordinal number\",\n hasEndMarker: true,\n children: undefined,\n },\n add: {\n category: CategoryType.SpecialText,\n type: MarkerType.Character,\n description: \"For a translational addition to the text\",\n hasEndMarker: true,\n children: undefined,\n },\n lit: {\n category: CategoryType.SpecialText,\n type: MarkerType.Paragraph,\n description: \"For a comment or note inserted for liturgical use\",\n hasEndMarker: false,\n children: undefined,\n },\n no: {\n category: CategoryType.CharacterStyling,\n type: MarkerType.Character,\n description: \"A character style, use normal text\",\n hasEndMarker: true,\n children: undefined,\n },\n it: {\n category: CategoryType.CharacterStyling,\n type: MarkerType.Character,\n description: \"A character style, use italic text\",\n hasEndMarker: true,\n children: undefined,\n },\n bd: {\n category: CategoryType.CharacterStyling,\n type: MarkerType.Character,\n description: \"A character style, use bold text\",\n hasEndMarker: true,\n children: undefined,\n },\n bdit: {\n category: CategoryType.CharacterStyling,\n type: MarkerType.Character,\n description: \"A character style, use bold + italic text\",\n hasEndMarker: true,\n children: undefined,\n },\n em: {\n category: CategoryType.CharacterStyling,\n type: MarkerType.Character,\n description: \"A character style, use emphasized text style\",\n hasEndMarker: true,\n children: undefined,\n },\n sc: {\n category: CategoryType.CharacterStyling,\n type: MarkerType.Character,\n description: \"A character style, for small capitalization text\",\n hasEndMarker: true,\n children: undefined,\n },\n sup: {\n category: CategoryType.CharacterStyling,\n type: MarkerType.Character,\n description: \"A character style, for superscript text. Typically for use in critical edition footnotes.\",\n hasEndMarker: true,\n children: undefined,\n },\n pb: {\n category: CategoryType.Breaks,\n type: MarkerType.Paragraph,\n description: \"Page Break used for new reader portions and children's bibles where content is controlled by the page\",\n hasEndMarker: false,\n children: undefined,\n },\n};\n","import { CategoryType, MarkerType } from \"./usfmTypes.js\";\nconst paragraphChildren = {\n DivisionMarks: { add: [\"v\", \"c\"], remove: [] },\n Paragraphs: { add: [\"p\"], remove: [] },\n Poetry: { add: [\"q\", \"q1\", \"q2\", \"q3\", \"q4\", \"b\"], remove: [] },\n TitlesHeadings: {\n add: [\n \"mte\",\n \"ms\",\n \"ms1\",\n \"ms2\",\n \"ms3\",\n \"s\",\n \"s1\",\n \"s2\",\n \"s3\",\n \"s4\",\n \"r\",\n \"sp\",\n \"d\",\n \"sd\",\n \"sd1\",\n \"sd2\",\n \"sd3\",\n \"sd4\",\n ],\n remove: [],\n },\n};\nconst usfmMarkersOverwrites = {\n p: { children: paragraphChildren },\n q: { children: paragraphChildren },\n q1: { children: paragraphChildren },\n q2: { children: paragraphChildren },\n q3: { children: paragraphChildren },\n q4: { children: paragraphChildren },\n b: { children: paragraphChildren },\n qm: {\n children: {\n Paragraphs: { add: [\"p\"], remove: [] },\n },\n },\n c: {\n type: MarkerType.Paragraph,\n children: null,\n },\n v: {\n children: null,\n },\n // The following are attribute-bearing character markers present in usfm.sty and in\n // CharNode's VALID_CHAR_MARKERS, but absent from the generated usfmMarkers data.\n // They are defined here as complete entries rather than hand-edited into the\n // generated file, which would be silently lost on regeneration.\n w: {\n category: CategoryType.SpecialFeatures,\n type: MarkerType.Character,\n description: \"A wordlist/glossary/dictionary entry marker for study/analysis purposes\",\n hasEndMarker: true,\n },\n rb: {\n category: CategoryType.SpecialFeatures,\n type: MarkerType.Character,\n description: \"A ruby glossing marker for study/analysis purposes\",\n hasEndMarker: true,\n },\n jmp: {\n category: CategoryType.SpecialFeatures,\n type: MarkerType.Character,\n description: \"A hyperlink marker for study/analysis purposes\",\n hasEndMarker: true,\n },\n // The generated table has no `fig`, but `usfm.sty` does (and so does the stylesheet data every\n // project supplies). Without an entry here, a document parsed BEFORE its project stylesheet\n // resolves falls back to this table, reads `\\fig` as an unknown marker, and breaks the figure\n // into its own paragraph with the closer stranded as unmatched.\n fig: {\n category: CategoryType.SpecialFeatures,\n type: MarkerType.Character,\n description: \"Illustration [Columns to span, height, filename, caption text]\",\n hasEndMarker: true,\n },\n};\nexport default usfmMarkersOverwrites;\n","import { usfmMarkers } from \"./usfmMarkers.js\";\nimport usfmMarkersOverwrites from \"./usfmMarkersOverwrites.js\";\n//NOTE: We can make this reusable if we agree on a common usfmMarkers object for all editors and use the overwrites objects as a parameter for this function.\nfunction getMarker(marker) {\n // Own-property lookups: both tables are plain object literals, so a bare index resolves\n // Object.prototype members — a marker named `constructor`, `toString`, `__proto__`, … answered\n // with a Function, which spreads to a truthy Marker carrying no category/type/hasEndMarker.\n // Callers read a truthy result as \"this marker is known\", so a stray `\\constructor` typed by a\n // user classified as a real marker with an undefined type. (`createMarkerLookup` in\n // styleInfo.ts guards its own table the same way.)\n const baseMarker = Object.hasOwn(usfmMarkers, marker) ? usfmMarkers[marker] : undefined;\n const overwrite = Object.hasOwn(usfmMarkersOverwrites, marker)\n ? usfmMarkersOverwrites[marker]\n : undefined;\n if (!baseMarker) {\n // The overwrites file can ADD markers the generated data lacks — but with no base to fill\n // gaps, only an overwrite carrying the FULL required Marker shape may stand alone. A partial\n // overwrite (e.g. `type` set but no category/description/hasEndMarker) is not a valid Marker,\n // so the `as Marker` cast would lie about it; refuse rather than return a malformed object.\n if (overwrite?.category !== undefined &&\n overwrite.type !== undefined &&\n overwrite.description !== undefined &&\n overwrite.hasEndMarker !== undefined)\n // A COPY: the table entry is module-level shared state, and handing it out lets any caller\n // that edits the returned Marker corrupt that marker for the whole process.\n return { ...overwrite };\n return undefined;\n }\n if (!overwrite) {\n return baseMarker;\n }\n let mergedChildren = baseMarker.children ? { ...baseMarker.children } : undefined;\n if (overwrite.children === null) {\n mergedChildren = undefined;\n }\n if (overwrite.children) {\n mergedChildren = mergedChildren || {};\n for (const [category, modification] of Object.entries(overwrite.children)) {\n const categoryType = category;\n if (modification === null) {\n // Remove the entire category if it exists\n Reflect.deleteProperty(mergedChildren, categoryType);\n }\n else {\n // Update children for this category\n let currentChildren = mergedChildren[categoryType] || [];\n if (modification.remove) {\n currentChildren = currentChildren.filter((child) => !modification.remove.includes(child));\n }\n if (modification.add) {\n currentChildren = [...new Set([...currentChildren, ...modification.add])];\n }\n if (currentChildren.length > 0) {\n mergedChildren[categoryType] = currentChildren;\n }\n else {\n Reflect.deleteProperty(mergedChildren, categoryType);\n }\n }\n }\n // If mergedChildren is empty, set it to undefined\n if (Object.keys(mergedChildren).length === 0) {\n mergedChildren = undefined;\n }\n }\n return {\n ...baseMarker,\n ...overwrite,\n children: mergedChildren,\n };\n}\nexport default getMarker;\n","/**\n * StyleInfo-driven USFM fragment tokenizer for Tier 2 paragraph re-tokenization.\n * Reference semantics: ParatextData `UsfmToken.Tokenize` —\n * fragment-level tokenization only; document-level validation stays out. Marker\n * kinds come from the bundled usfm.sty-derived `usfmMarkers` map via `getMarker`\n * by default, or from `options.getMarker` (a project `StyleInfo`-backed\n * `MarkerLookup`) when supplied — classification is stylesheet-first:\n * a marker the lookup KNOWS is classified by its declared type, full stop.\n *\n * Markers the effective stylesheet does NOT know (`kind === undefined`) fall\n * back to name-pattern heuristics — `NoteNode.isValidMarker`, and for\n * milestones only the stylesheet-family names (`\\qt#-s/-e`, `\\ts-s/-e`, plus\n * the `zmsc-*` comment markers; see `isMilestoneHeuristicName`) — and\n * failing those, resolve by context exactly like PT9's\n * `UsfmParser.DetermineUnknownTokenType`: PARAGRAPH in body text, CHARACTER\n * inside a note (`options.isNoteContext`), except `esb`/`esbe`, which PT9\n * always treats as paragraphs (`UsfmToken.cs:405-421`). An unmatched closer\n * (known or unknown marker, no open frame to close) becomes a\n * `{ type: \"unmatched\", marker: \"*\" }` USJ object (PT9 `sink.Unmatched`,\n * `UsxUsfmParserSink.cs:262-266`), not literal text.\n *\n * Literal-text degradation remains only for fragments the\n * tokenizer cannot confidently parse at all: a bare `\\`, or an unterminated\n * milestone (stylesheet-declared, or matching the suffix convention above).\n * Content between a milestone's marker and its `\\*` is NOT literal: a\n * milestone cannot hold content, so it ends where the content begins, the\n * content follows it as a sibling, and the author's `\\*` becomes an unmatched\n * element. A stray `\\*` with no milestone to close is NOT literal either — it\n * becomes an unmatched element (see above).\n *\n * Figures, tables, sidebars, and peripheral divisions assemble to their faithful\n * USJ shapes at the assembly level, marker-name driven (they are parser-level\n * structures in ParatextData, independent of stylesheet classification):\n * `\\fig …\\fig*` folds to an inline `figure` object (USFM's `src` attribute\n * renamed to USX/USJ's `file`), `\\tr` plus `t[hc][rc]#(-#)` cell markers build\n * `table` → `table:row` → `table:cell` with name-derived `align`/`colspan`,\n * `\\esb`…`\\esbe` wraps the following blocks in a `sidebar` (`\\cat` directly\n * after `\\esb` folds to its `category`), and `\\periph` opens a `periph`\n * division that takes every following block until the next `\\periph`, a\n * chapter, or the fragment end — its marker line splitting at the first `|`\n * into the division title (`alt`) and an ordinary attribute list. Anything off\n * the clean shapes — nested markup or positional (USFM 2.0) attributes in a\n * figure, a missing `\\fig*`, a cell marker with no open row, a periph\n * attribute list that does not parse — degrades to the plain char/para output\n * the marker classification produces on its own.\n *\n * Input is USFM text: `~` means NBSP; U+FFFC sentinels (atomic-node placeholders\n * from the Tier 2 fragment builder) ride through as ordinary text characters.\n */\nimport { NBSP, PARA_MARKER_DEFAULT, ZWSP } from \"../../nodes/usj/node-constants.js\";\nimport { isMilestoneCommentMarker } from \"../../nodes/usj/MilestoneNode.js\";\nimport { NoteNode } from \"../../nodes/usj/NoteNode.js\";\nimport getMarker from \"../../utils/usfm/getMarker.js\";\nimport { MarkerType } from \"../../utils/usfm/usfmTypes.js\";\nconst VERSE_MARKER = \"v\";\nconst CHAPTER_MARKER = \"c\";\nconst FIGURE_MARKER = \"fig\";\nconst TABLE_ROW_MARKER = \"tr\";\nconst SIDEBAR_MARKER = \"esb\";\nconst SIDEBAR_END_MARKER = \"esbe\";\nconst PERIPH_MARKER = \"periph\";\n/** The USJ property a periph's marker-line title lands on — the same name a pipe pair would use,\n * which is why a line carrying both spellings is refused rather than resolved. */\nconst PERIPH_TITLE_ATTRIBUTE = \"alt\";\n/**\n * Table cell marker names: `t` + header/cell (`h`/`c`) + optional alignment infix (`r`/`c`) +\n * starting column + optional span end column (`th1`, `tc13`, `thr5`, `thc3-4`, `tcr1-4`).\n * ParatextData derives the whole cell shape from the name alone — no stylesheet entry needed.\n */\nconst TABLE_CELL_MARKER_REGEX = /^t[hc]([rc]?)(\\d+)(?:-(\\d+))?$/;\n/** Cell alignment from the marker-name infix after `t[hc]`: `th1`/`tc1` → start,\n * `thc1`/`tcc1` → center, `thr1`/`tcr1` → end (ParatextData's name-derived alignment). */\nconst TABLE_CELL_ALIGN_BY_INFIX = {\n \"\": \"start\",\n c: \"center\",\n r: \"end\",\n};\n/**\n * Whether a cell-marker name match is a cell ParatextData recognizes. A rangeless cell\n * covers columns 1–12 spelled with no leading zeros: usfm.sty declares exactly\n * th1–th12/tc1–tc12 (and their r/c variants) and ParatextData's tag lookup is by LITERAL\n * name (`ScrStylesheet.GetTagIndex` is a string-keyed dictionary, no numeric parse), so\n * `\\tc13` and `\\tc01` are both unknown markers. A RANGED cell is narrower: ParatextData's\n * range test (`ScrStylesheet.IsCellRange`, regex `^(t[ch][cr]?[1-5])-([2-5])$` plus\n * `colSpan >= 2`) takes only single-digit spans that start in columns 1–5, end in 2–5, and\n * grow left-to-right — `\\thc4-2` (reversed), `\\tc2-2` (no growth), and `\\thc11-13`\n * (multi-digit columns) are all unknown markers too.\n */\nfunction isRecognizedTableCell(cellMatch) {\n const [, , spanStart, spanEnd] = cellMatch;\n if (!spanEnd)\n return /^(?:[1-9]|1[0-2])$/.test(spanStart);\n return (/^[1-5]$/.test(spanStart) && /^[2-5]$/.test(spanEnd) && Number(spanEnd) > Number(spanStart));\n}\n/**\n * PT9 `UsfmToken.IsNonSemanticWhiteSpace`: everything .NET counts as whitespace EXCEPT IDEOGRAPHIC\n * SPACE (U+3000), plus ZWSP (U+200B). U+3000 is excluded so it survives as authored content, and\n * ZWJ/ZWNJ are deliberately absent — Paratext treats them as content, not spacing.\n */\nfunction isNonSemanticWhiteSpace(ch) {\n const code = ch.charCodeAt(0);\n // TAB, LF, VT, FF, CR — the control characters .NET counts as whitespace\n if (code >= 0x09 && code <= 0x0d)\n return true;\n return (code === 0x20 || // SPACE\n code === 0x85 || // NEXT LINE\n code === 0xa0 || // NO-BREAK SPACE\n code === 0x1680 || // OGHAM SPACE MARK\n (code >= 0x2000 && code <= 0x200a) || // EN QUAD through HAIR SPACE\n code === 0x2028 || // LINE SEPARATOR\n code === 0x2029 || // PARAGRAPH SEPARATOR\n code === 0x202f || // NARROW NO-BREAK SPACE\n code === 0x205f || // MEDIUM MATHEMATICAL SPACE\n code === 0x200b // ZERO WIDTH SPACE\n );\n}\n/**\n * PT9 `CharExtensions.IsInvisibleCharOrWhitespace`: the invisible characters Paratext supports\n * explicitly. Used only to collapse a character repeated immediately after itself.\n */\nconst INVISIBLE_CHAR_OR_WHITESPACE = /[\\u200D\\u2003\\u2002\\u0020\\u00A0\\u202F\\u2009\\u200A\\u3000\\u200B\\u200C\\u2060\\u200E\\u200F]/;\n/**\n * Collapse whitespace runs (PT9 `UsfmToken.RegularizeSpaces`); keep U+FFFC.\n *\n * A run collapses to its FIRST character kept verbatim, not to a plain space: NBSP stays NBSP and a\n * ZWSP between two words stays ZWSP. That distinction is not cosmetic — ZWSP is the word-break\n * character in Thai, Khmer, and Lao, so flattening it to a space would rewrite those texts on every\n * settle. `platform-bible-utils`' `normalizeScriptureSpaces` is the same algorithm; the two are\n * independent ports of one Paratext function and must agree.\n *\n * The one deliberate departure from PT9: a run containing a line break collapses to `\"\\n\"` rather\n * than `\" \"`, so the attribute-marker fold logic can tell STRUCTURAL whitespace (line-end, folds\n * across: `\\ca 1 ca\\ca*` ⏎ `\\cp 1 cp`) from same-line spaces (content per Paratext, blocking the\n * fold: `\\va 12 va\\va* \\vp` keeps vp standalone). Content text normalizes the `\"\\n\"` back to `\" \"`\n * in `toUsjText`.\n *\n * Exported so the Paratext test vectors it must reproduce can be asserted directly, without the\n * marker-boundary handling of a full fragment parse in between.\n */\nexport function regularizeSpaces(text) {\n let result = \"\";\n let lastCharWasSpace = false;\n // Placeholder previous character, so the first iteration cannot report a duplicate\n let prevCh = \"\\0\";\n // Where the character standing in for the run being collapsed was written, so a line break\n // arriving later in the same run can promote it to \"\\n\" after the fact\n let runIndex = -1;\n for (let i = 0; i < text.length; i += 1) {\n const ch = text[i];\n if (ch.charCodeAt(0) < 32) {\n // Control characters, CR/LF, and TAB become spaces\n if (!lastCharWasSpace) {\n runIndex = result.length;\n result += \" \";\n }\n lastCharWasSpace = true;\n }\n else if (!lastCharWasSpace &&\n ch === ZWSP &&\n i + 1 < text.length &&\n isNonSemanticWhiteSpace(text[i + 1])) {\n // ZWSP is redundant when a space follows it\n }\n else if (isNonSemanticWhiteSpace(ch)) {\n // Keep the run's first space exactly as authored\n if (!lastCharWasSpace) {\n runIndex = result.length;\n result += ch;\n }\n lastCharWasSpace = true;\n }\n else if (INVISIBLE_CHAR_OR_WHITESPACE.test(ch) && ch === prevCh) {\n // An invisible character repeated immediately collapses to one\n }\n else {\n result += ch;\n lastCharWasSpace = false;\n runIndex = -1;\n }\n if ((ch === \"\\n\" || ch === \"\\r\") && runIndex >= 0) {\n result = `${result.slice(0, runIndex)}\\n${result.slice(runIndex + 1)}`;\n }\n prevCh = ch;\n }\n return result;\n}\n/**\n * The tokenize-identity predicate for an engine-owned separator: whether deleting the structural\n * space that ends a marker's name leaves the surrounding bytes tokenizing IDENTICALLY, given the\n * bytes that follow the separator. Defined here, beside {@link scanMarkerName}, because it IS that\n * scan's rule read backwards — the two must never drift.\n *\n * | Following bytes | Without the separator | Same meaning? |\n * | --- | --- | --- |\n * | `\\wj stuff` | `\\nd\\wj stuff` | yes — the name scan stops at `\\` either way. Heal. |\n * | `\\|x=\"y\"` | `\\nd\\|x=\"y\"` | yes — the name scan stops at `\\|` either way. Heal. |\n * | `things` | `\\ndthings` | no — the marker is now `ndthings`. Rename. |\n * | `*stuff` | `\\nd*stuff` | no — that is a CLOSING marker. Do not heal. |\n *\n * The `*` row is why this is defined by MEANING and not by a terminator character class: `*` is\n * one of the scan's terminators, but it ends the token AND changes what the token is, so an\n * allowlist built from terminators would wrongly heal it and silently prevent the user from\n * typing a closer.\n *\n * The question is local — it depends only on the first following byte — so callers get an O(1)\n * answer for the common shapes and fall back to a scoped re-tokenization only when this returns\n * `false` (the bytes then genuinely mean something new).\n *\n * @param followingBytes - The displayed bytes immediately after the separator's site.\n * @returns `true` when removing the separator cannot change the token stream.\n */\nexport function separatorRemovalTokenizesIdentically(followingBytes) {\n if (followingBytes.length === 0)\n return true;\n const ch = followingBytes[0];\n if (ch === \"*\")\n return false; // completes a closing marker\n if (ch === \"\\\\\" || ch === \"|\")\n return true; // the name scan stops here either way\n if (/[\\s\\u200B]/.test(ch))\n return true; // more whitespace: the run still separates\n return false; // a name character: the marker name grows through it\n}\n/** Marker name chars per PT9 scan: stop at `\\`, `|`, whitespace; `*` ends and is included. */\nfunction scanMarkerName(fragment, start) {\n let index = start;\n while (index < fragment.length) {\n const ch = fragment[index];\n if (ch === \"\\\\\" || ch === \"|\")\n break;\n if (ch === \"*\") {\n index++;\n break;\n }\n if (/[\\s\\u200B]/.test(ch))\n break;\n index++;\n }\n return { name: fragment.slice(start, index), next: index };\n}\n/** Milestone names the Paratext stylesheet family declares: `\\qt-s/-e`, `\\qt1-s`…`\\qt5-e`,\n * `\\ts-s/-e`. */\nconst STYLESHEET_MILESTONE_NAME_REGEX = /^(?:qt[1-5]?|ts)-[se]$/;\n/**\n * Milestone-name heuristic for markers ABSENT from the effective stylesheet: only names the\n * Paratext stylesheet family actually declares as milestones, plus the editor's own annotation\n * comment markers — so the heuristic predicts what ParatextData's stylesheet-driven parse\n * produces for the same bytes. Deliberately EXCLUDED: bare `ts`, `t-s`, `t-e` — syntactically\n * valid milestones, but no stylesheet declares them, so ParatextData parses them as unknown\n * markers (paragraph in body text) and any milestone produced here would flip to that shape on\n * the next chapter read. Also excluded: `MilestoneNode.isValidMarker`'s generic `z`-prefix\n * wildcard, which would classify any custom.sty-style marker (e.g. `\\zfoo`) as a milestone and\n * keep unknown-marker resolution (`DetermineUnknownTokenType`) from ever seeing it. A project\n * that declares any of these in custom.sty gets them classified stylesheet-first with no code\n * change.\n */\nexport function isMilestoneHeuristicName(name) {\n return STYLESHEET_MILESTONE_NAME_REGEX.test(name) || isMilestoneCommentMarker(name);\n}\n/** PT9 `GetNextWord`: skip leading whitespace, take up to whitespace or `\\`. */\nfunction getNextWord(fragment, start) {\n let index = start;\n while (index < fragment.length && /[\\s\\u00A0\\u200B]/.test(fragment[index]))\n index++;\n const wordStart = index;\n while (index < fragment.length && !/[\\s\\u00A0\\u200B\\\\]/.test(fragment[index]))\n index++;\n const word = fragment.slice(wordStart, index);\n while (index < fragment.length && /[\\s\\u00A0\\u200B]/.test(fragment[index]))\n index++;\n return { word, next: index };\n}\nfunction tokenize(fragment, getMarkerFn, isNoteContext) {\n const tokens = [];\n let index = 0;\n // PT9 resolves an unknown marker against the OPEN ELEMENT STACK, not against the fragment it\n // started in (`State.Stack.Exists(e => e.Type == Note)`, UsfmParser.cs). A note opened inside\n // this fragment puts one on that stack just as surely as being handed note content does, so\n // track it: without this, an unknown marker inside `\\f ... \\f*` classifies as a PARAGRAPH and\n // tears the note in half, stranding its closer.\n let openNoteMarker;\n const pushText = (text) => {\n if (!text)\n return;\n const prev = tokens[tokens.length - 1];\n if (prev?.kind === \"text\")\n prev.text += text;\n else\n tokens.push({ kind: \"text\", text });\n };\n // `//` mid-text is USFM's discretionary line break (PT9 tokenizes it wherever it appears,\n // spec-blind — even inside a URL); the surrounding text is kept byte-exact.\n const pushTextWithOptbreaks = (text) => {\n const parts = text.split(\"//\");\n parts.forEach((part, partIndex) => {\n if (partIndex > 0)\n tokens.push({ kind: \"optbreak\" });\n pushText(part);\n });\n };\n while (index < fragment.length) {\n if (fragment[index] !== \"\\\\\") {\n const nextMarker = fragment.indexOf(\"\\\\\", index);\n const end = nextMarker === -1 ? fragment.length : nextMarker;\n pushTextWithOptbreaks(regularizeSpaces(fragment.slice(index, end)));\n index = end;\n continue;\n }\n const rawStart = index;\n const { name, next } = scanMarkerName(fragment, index + 1);\n index = next;\n if (name === \"\") {\n // Bare `\\` — literal (degradation property).\n pushText(fragment.slice(rawStart, index));\n continue;\n }\n if (name === \"*\") {\n // Stray `\\*` with no milestone to close (milestone closes are consumed by\n // scanMilestone): PT9 sink.Unmatched — route through the end-token path so it becomes\n // an `{ type: \"unmatched\", marker: \"*\" }` object, which serializes back to `\\*`.\n tokens.push({ kind: \"end\", marker: \"\" });\n continue;\n }\n if (name.endsWith(\"*\")) {\n if (name.slice(0, -1) === openNoteMarker)\n openNoteMarker = undefined;\n tokens.push({ kind: \"end\", marker: name.slice(0, -1) });\n continue;\n }\n // Consume the separator whitespace after an opening marker (PT9 skips it) — all leading\n // whitespace, not just a single space.\n const consumeSeparator = () => {\n while (index < fragment.length && /[\\s\\u00A0\\u200B]/.test(fragment[index]))\n index++;\n };\n if (name === VERSE_MARKER) {\n const { word, next: afterWord } = getNextWord(fragment, index);\n index = afterWord;\n tokens.push({ kind: \"verse\", number: word });\n continue;\n }\n if (name === CHAPTER_MARKER) {\n const { word, next: afterWord } = getNextWord(fragment, index);\n index = afterWord;\n openNoteMarker = undefined; // PT9 CloseAll: a chapter empties the element stack\n tokens.push({ kind: \"chapter\", number: word });\n continue;\n }\n // Stylesheet-first (PT9: the stylesheet always classifies; our pattern\n // heuristics only stand in for markers ABSENT from the effective sheet).\n const isNested = name.startsWith(\"+\");\n const clean = isNested ? name.slice(1) : name;\n const kind = getMarkerFn(clean)?.type;\n if (kind === MarkerType.Note || (kind === undefined && NoteNode.isValidMarker(name))) {\n const { word, next: afterWord } = getNextWord(fragment, index);\n index = afterWord;\n openNoteMarker = name;\n tokens.push({ kind: \"note\", marker: name, caller: word || \"+\" });\n continue;\n }\n if (kind === MarkerType.Milestone || (kind === undefined && isMilestoneHeuristicName(name))) {\n const milestone = scanMilestone(fragment, rawStart, name, index);\n if (milestone) {\n tokens.push(milestone.token);\n if (milestone.ejectedText)\n pushText(milestone.ejectedText);\n index = milestone.next;\n }\n else {\n // Not `\\*`-terminated: keep the raw text through the next `\\` (PT9 behavior).\n const endOfText = fragment.indexOf(\"\\\\\", index);\n const end = endOfText === -1 ? fragment.length : endOfText;\n pushText(fragment.slice(rawStart, end));\n index = end;\n }\n continue;\n }\n if (kind === MarkerType.Paragraph) {\n consumeSeparator();\n tokens.push({ kind: \"para\", marker: name });\n }\n else if (kind === MarkerType.Character) {\n consumeSeparator();\n tokens.push({ kind: \"charOpen\", marker: clean, isNested });\n }\n else if (attributeMarker(clean)) {\n // Attribute markers (ca/cp/va/vp/cat) are parser-level in ParatextData, not stylesheet\n // entries — classify them by their fixed shape when the sheet doesn't know them, so\n // the assembly loop can fold them onto their target (or keep them standalone).\n consumeSeparator();\n if (attributeMarker(clean)?.shape === \"para\")\n tokens.push({ kind: \"para\", marker: name });\n else\n tokens.push({ kind: \"charOpen\", marker: clean, isNested });\n }\n else {\n // Unknown to the effective stylesheet: PT9 resolves by context\n // (DetermineUnknownTokenType): PARAGRAPH in body text, CHARACTER inside\n // a note; `esb`/`esbe` are explicitly paragraphs (UsfmToken.cs:405-421).\n // A non-`+` char run closes any open char style unconditionally, same as\n // a known Character token (UsfmParser.cs:247).\n consumeSeparator();\n const insideNote = isNoteContext || openNoteMarker !== undefined;\n if (!insideNote || name === SIDEBAR_MARKER || name === SIDEBAR_END_MARKER)\n tokens.push({ kind: \"para\", marker: name });\n else\n tokens.push({ kind: \"charOpen\", marker: clean, isNested });\n }\n }\n return tokens;\n}\n/**\n * USFM attribute markers: markers that describe the PREVIOUS marker and become an attribute on\n * it in USX/USJ when they directly follow it (whitespace only between, plain-text content) —\n * `\\c 1 \\ca 2\\ca*` → `chapter{altnumber:\"2\"}`. Standalone occurrences (not adjacent to a\n * supporting target, or carrying markup in their content) stay ordinary markers, exactly as\n * ParatextData emits them. The relation data is parser-level in ParatextData (not in any\n * stylesheet), so it is hardcoded here; `cat` supports two target types — an open note\n * (`f`/`fe`/`x`/`ef`/`efe`/`ex`) and an `esb` sidebar — so `targetTypes` is a list.\n *\n * This table models the PARSER, keyed by USJ node type, deliberately matching ParatextData's\n * fold-at-parse behavior (which is keyed by token TYPE, not by a host-marker list). The markers\n * map in paranext-core (`markers-map-3.0.model.ts`) models the SERIALIZER and keys the same\n * relation by host MARKER NAME. The two agree on every shared fact — the agreement test beside\n * this module (`attributeMarkersMapAgreement.test.ts`) pins that, and pins the keying\n * difference explicitly. Parser-only behaviors (a same-line space before the marker blocks the\n * fold; markup in the content aborts it; an empty span never folds) stay local to this\n * converter and are pinned in its own tests.\n */\nexport const ATTRIBUTE_MARKERS = {\n ca: { attrName: \"altnumber\", targetTypes: [\"chapter\"], shape: \"char\" },\n cp: { attrName: \"pubnumber\", targetTypes: [\"chapter\"], shape: \"para\" },\n va: { attrName: \"altnumber\", targetTypes: [\"verse\"], shape: \"char\" },\n vp: { attrName: \"pubnumber\", targetTypes: [\"verse\"], shape: \"char\" },\n cat: { attrName: \"category\", targetTypes: [\"note\", \"sidebar\"], shape: \"char\" },\n};\n/**\n * The attribute-marker entry for `marker`, or undefined if there is none.\n *\n * Goes through `Object.hasOwn` rather than indexing directly: a marker whose name collides with an\n * `Object.prototype` member (`\\toString`, `\\constructor`) would otherwise resolve to the inherited\n * function and read as a declared attribute marker.\n */\nfunction attributeMarker(marker) {\n return Object.hasOwn(ATTRIBUTE_MARKERS, marker) ? ATTRIBUTE_MARKERS[marker] : undefined;\n}\n/**\n * Whether `marker` is one this parser folds onto a host node ({@link ATTRIBUTE_MARKERS}).\n *\n * The distinction a stylesheet cannot make: an attribute marker's round trip is defined by this\n * table, not by usfm.sty, so the tokenizer re-derives one whether or not the stylesheet declares\n * it. `\\cat` is the case that matters — it is the only attribute marker usfm.sty omits, so a\n * stylesheet-keyed \"can the engine re-derive this span?\" test is the one place the two records\n * disagree, and it reads `\\cat` as an unknown custom marker.\n */\nexport function isAttributeMarker(marker) {\n return attributeMarker(marker) !== undefined;\n}\nconst ATTRIBUTE_PAIR_REGEX = /([-\\w]+)\\s*=\\s*\"(.*?)\"/g;\n/**\n * Whitespace run containing a line break, inside attribute text. ParatextData regularizes\n * text BEFORE attribute parsing ever sees it (`UsfmToken.Tokenize` calls `RegularizeSpaces`\n * — control whitespace becomes deduplicated plain spaces — and only then hands the text to\n * `HandleAttributes`), so a line break inside an attribute span reaches its `.*?`-based\n * value pattern as a plain space, never as a newline. Milestone and figure attribute text\n * here is sliced from the raw fragment (or still carries the collapsed `\"\\n\"` run marker),\n * so the same normalization must run before matching — without it the value capture (`.`\n * never matches a line terminator) fails on a wrapped value and the whole string would\n * leak into the default attribute.\n */\nconst ATTRIBUTE_LINE_BREAK_RUN_REGEX = /[\\s\\u200B]*[\\n\\r][\\s\\u200B]*/g;\n/**\n * USFM 3 default attribute per marker (subset; unmapped bare values stay literal).\n *\n * `xt`/`jmp` use the USFM/USX/USJ **3.0** name `link-href`: this pipeline pins USJ 3.0 (chapter\n * data round-trips through ParatextData, and the host downgrades 3.1), and every 3.0 consumer —\n * ParatextData serialization, checks, link handling — reads `link-href`. USFM 3.1 renamed it to\n * `href`; switch these when the pipeline moves to 3.1.\n */\nconst DEFAULT_MARKER_ATTRIBUTES = {\n w: \"lemma\",\n rb: \"gloss\",\n xt: \"link-href\",\n jmp: \"link-href\",\n};\n/**\n * Character marker default attribute lookup for USFM 3 (shared with display builders).\n * Returns the default attribute name for a given character marker, or undefined if the marker\n * has no default attribute.\n */\nexport function defaultMarkerAttribute(marker) {\n return DEFAULT_MARKER_ATTRIBUTES[marker];\n}\n/**\n * USJ structural keys that a parsed attribute may never overwrite: the callers merge parsed\n * attributes straight onto the node object (`Object.assign` / spread), so an attribute literally\n * named `type`, `marker`, or `content` would clobber the node's own identity or replace its\n * content array with a string. Such attributes are dropped.\n */\nconst RESERVED_NODE_KEYS = new Set([\"type\", \"marker\", \"content\"]);\n/**\n * Whether `pairs` consume all of `text` apart from whitespace — i.e. the attribute list parsed\n * completely rather than partly. Each match must start where the previous one ended (whitespace\n * aside), and the last must reach the end.\n */\nfunction pairsCoverList(text, pairs) {\n let cursor = 0;\n for (const pair of pairs) {\n const start = pair.index;\n if (start === undefined)\n return false;\n if (text.slice(cursor, start).trim() !== \"\")\n return false;\n cursor = start + pair[0].length;\n }\n return text.slice(cursor).trim() === \"\";\n}\nfunction parseAttributeText(attributeText, marker, defaultAttributeName = DEFAULT_MARKER_ATTRIBUTES[marker]) {\n const regularizedText = attributeText.replace(ATTRIBUTE_LINE_BREAK_RUN_REGEX, \" \");\n // Null-prototype accumulator: on a plain `{}`, `attributes[\"__proto__\"] = value` hits\n // Object.prototype's accessor and is a silent no-op, so an attribute literally named\n // `__proto__` would parse successfully and lose its pair. An own property on a null-prototype\n // object survives spread and JSON like any other key.\n const attributes = Object.create(null);\n const pairs = [...regularizedText.matchAll(ATTRIBUTE_PAIR_REGEX)];\n if (pairs.length > 0) {\n // The pairs must account for the WHOLE list, with nothing but whitespace between and after\n // them. The regex is global, so without this it happily skips over anything it cannot match:\n // `gloss=\"st\"uff\"` matched `gloss=\"st\"` and the leftover `uff\"` vanished, taking bytes the\n // author wrote and could see. USFM has no way to escape a quote inside a value, so such a list\n // is genuinely ambiguous and there is no reading of it to salvage — refusing the whole list\n // hands it back to the caller, which keeps it as literal text (`extractAttributes`). That is\n // Paratext 9's behavior, and the only lossless one: every byte stays on screen and in the file,\n // where the author can see what is wrong and fix it.\n if (!pairsCoverList(regularizedText, pairs))\n return undefined;\n // An EMPTY value refuses the whole list too, matching Paratext 9. `|who=\"\"` is not a reading\n // Paratext will ever agree with, so parsing it here would make the editor the only thing in the\n // pipeline that believes the attribute exists — and the byte the author actually wrote is the\n // literal text, which the refusal keeps. Note this is the opposite call from the USJ side, where\n // an empty value arriving as real state is honoured and written out; there it is unambiguous\n // data, here it is bytes Paratext reads as text.\n if (pairs.some((pair) => pair[2] === \"\"))\n return undefined;\n for (const [, name, value] of pairs) {\n if (!RESERVED_NODE_KEYS.has(name))\n attributes[name] = value;\n }\n return Object.keys(attributes).length > 0 ? attributes : undefined;\n }\n // Bare (default-attribute) value: keep it byte-exact (past the line-break regularization\n // above), whitespace included — ParatextData treats the space before the closing marker as\n // part of the value (`\\w marker|stuff \\w*` → lemma=\"stuff \"; `\\qt-s |TJ \\*` → who=\"TJ \").\n // The USFM 3 spec calls that space structural, but Paratext keeps it as content, and this\n // pipeline round-trips through ParatextData.\n if (regularizedText.trim() && defaultAttributeName)\n return { [defaultAttributeName]: regularizedText };\n return undefined;\n}\n/**\n * Default attribute for stylesheet-family milestone names (USFM 3.0): quotation starts take\n * `who`, every `-e` end takes `eid`, and other starts (`\\ts-s`, comment markers) take `sid`.\n */\nexport function milestoneDefaultAttribute(name) {\n if (name.endsWith(\"-e\"))\n return \"eid\";\n return name.startsWith(\"qt\") ? \"who\" : \"sid\";\n}\n/**\n * Whether re-tokenizing `text` would EJECT bytes out of a milestone — the shapes where a milestone\n * ends early and content it cannot hold follows it as a sibling, leaving the author's `\\*`\n * unmatched.\n *\n * Answered by running the real tokenizer rather than re-deriving the conditions, so it cannot drift\n * from {@link scanMilestone}. Callers use this to defer such a rebuild to the settle instead of\n * applying it mid-keystroke, because ejection MOVES bytes and doing that under the caret rearranges\n * the line the user is still typing on. Only ejection earns that deferral: a well-formed milestone\n * rearranges nothing and applies where it stands, the instant its `\\*` is typed.\n *\n * The ejected shape is BOTH halves together — content immediately after the milestone AND the\n * author's own `\\*` stranded past that content as an unmatched closing marker. Asking only for\n * the first half counts ordinary body text following a well-formed milestone, which is the common\n * case and not an ejection at all; the leftover unmatched `\\*` is the observable that tells the\n * two apart. The content run may tokenize into several items (an ejected list containing `//`\n * splits around an optbreak), so the closer is looked for anywhere past it rather than adjacent.\n */\nexport function milestoneEjectionPending(text) {\n const content = usfmFragmentToUsjContent(text)[0];\n const items = typeof content === \"object\" && \"content\" in content ? content.content : undefined;\n if (!items)\n return false;\n return items.some((item, index) => {\n if (typeof item !== \"object\" || item.type !== \"ms\")\n return false;\n if (typeof items[index + 1] !== \"string\")\n return false;\n return items\n .slice(index + 2)\n .some((later) => typeof later !== \"string\" && later.type === \"unmatched\" && later.marker === \"*\");\n });\n}\n/**\n * An attribute list sitting AFTER a closed milestone that belongs back INSIDE it — the shape\n * ejection leaves behind once the author repairs the bytes. `\\qt1-s\\*|who=\"person\"\\*` reads as\n * the milestone the author meant, `\\qt1-s |who=\"person\"\\*`.\n *\n * Two conditions, and both are load-bearing.\n *\n * The author's own trailing `\\*` is what marks the run as a milestone's list rather than body\n * text: content that merely begins with `|` has no closer after it, so this can never quietly eat\n * ordinary bytes. Whitespace between the milestone's closer and the `|` is tolerated — it is the\n * separator the author would have typed inside the milestone anyway.\n *\n * The list must PARSE, and that is what keeps absorption and ejection from chasing each other.\n * Ejection puts an unparseable list OUTSIDE the milestone ({@link scanMilestone}); absorbing one\n * back in would produce bytes that eject again, and the two would trade the same run forever.\n * `|who=\"\"` does not parse, so the ejected spelling stays ejected and the pair terminates.\n *\n * Returns the parsed list and where scanning resumes (past its `\\*`), or undefined for every\n * shape that is not this one.\n */\nfunction scanAbsorbableAttributes(fragment, start, name) {\n let index = start;\n while (index < fragment.length && /[\\s\\u00A0\\u200B]/.test(fragment[index]))\n index++;\n if (fragment[index] !== \"|\")\n return undefined;\n const closeIndex = fragment.indexOf(\"\\\\\", index);\n if (closeIndex === -1 || fragment.slice(closeIndex, closeIndex + 2) !== \"\\\\*\")\n return undefined;\n const attributes = parseAttributeText(fragment.slice(index + 1, closeIndex), name, milestoneDefaultAttribute(name));\n if (!attributes)\n return undefined;\n return { attributes, next: closeIndex + 2 };\n}\n/**\n * A milestone must be terminated by `\\*` (PT9 `MilestoneEnded`); attributes may\n * follow a `|` between the marker and the `\\*`.\n */\nfunction scanMilestone(fragment, _rawStart, name, index) {\n const closeIndex = fragment.indexOf(\"\\\\\", index);\n if (closeIndex === -1 || fragment.slice(closeIndex, closeIndex + 2) !== \"\\\\*\")\n return undefined;\n // Slices up to the FIRST backslash at or after `index`, so `between` cannot contain one: the\n // milestone's attribute region ends at whatever markup follows it.\n const between = fragment.slice(index, closeIndex);\n const pipeIndex = between.indexOf(\"|\");\n let attributes;\n if (pipeIndex >= 0) {\n attributes = parseAttributeText(between.slice(pipeIndex + 1), name, milestoneDefaultAttribute(name));\n // A list with CONTENT that will not parse becomes CONTENT, and a milestone cannot hold\n // content — so the milestone ends immediately and the bytes follow it as siblings. Resuming\n // the scan at the `|` is what produces that: the milestone token closes here, the unparsed\n // bytes are read as ordinary text, and the author's `\\*` is left to scan as an unmatched\n // closing marker. `\\qt1-s |who=\"\"\\*` therefore reads exactly as `\\qt1-s\\*|who=\"\"\\*` already\n // does — a closed milestone, the literal text, and one unmatched `\\*` — which is Paratext 9's\n // reading and keeps every byte.\n //\n // Refusing the whole token instead would lose the milestone the author did write, and building\n // it while dropping the attribute bytes would lose those; this loses neither. A bare `|` with\n // nothing after it is not this case — there are no bytes to keep, and dropping it is the\n // ratified answer for a `|` typed into a milestone glyph.\n if (!attributes && between.slice(pipeIndex + 1).trim() !== \"\") {\n // Content BEFORE the `|` is ejected here too, for the same reason the valid-attribute path\n // below ejects it: it is bytes the milestone cannot hold, and resuming the scan at the `|`\n // would skip straight past it. `\\\\qt1-s things|who=\"\"\\\\*` keeps `things` as a sibling\n // rather than deleting it, and still converges — re-tokenizing the ejected spelling\n // `\\\\qt1-s\\\\*things|who=\"\"\\\\*` reaches the same tree.\n const beforePipe = between.slice(0, pipeIndex);\n return {\n token: { kind: \"milestone\", marker: name },\n next: index + pipeIndex,\n ejectedText: beforePipe.trim() !== \"\" ? beforePipe.replace(/^[ \\u00A0]/, \"\") : undefined,\n };\n }\n }\n // Content a milestone cannot hold — the bytes before a valid attribute list, or, when there is\n // no `|` at all, everything between the marker and the closer. A milestone is self-closing, so\n // it ENDS where that content begins: the content follows it as a sibling, and the author's `\\*`\n // is left to scan as an unmatched closing marker. Valid attributes stay, because they are\n // genuinely the milestone's own; only what it cannot hold moves.\n //\n // That is what ParatextData writes to disk — `\\qt1-s stuff\\*` is saved as `\\qt1-s\\*stuff\\*` —\n // so the two spellings must tokenize IDENTICALLY for a reload to be a fixed point. Reading the\n // stray content as literal text instead dissolves the milestone the author did write, leaving\n // the editor disagreeing with the file.\n //\n // Resuming at `closeIndex` is what leaves the `\\*` unmatched, and the leading separator space\n // belongs to the marker rather than the content, so it goes with the marker.\n const ejected = pipeIndex >= 0 ? between.slice(0, pipeIndex) : between;\n if (ejected.trim() !== \"\")\n return {\n token: { kind: \"milestone\", marker: name, attributes },\n next: closeIndex,\n ejectedText: ejected.replace(/^[ \\u00A0]/, \"\"),\n };\n // A repaired attribute list left sitting past the closer folds back in, its values overwriting\n // any the milestone already carries: the list the author just fixed is the one they mean. Only\n // the clean path absorbs: when content was ejected above, the `\\*` is deliberately left to scan\n // as an unmatched closer, so there is no \"past the closer\" position to absorb from.\n const absorbed = scanAbsorbableAttributes(fragment, closeIndex + 2, name);\n if (absorbed)\n return {\n token: {\n kind: \"milestone\",\n marker: name,\n attributes: { ...attributes, ...absorbed.attributes },\n },\n next: absorbed.next,\n };\n return { token: { kind: \"milestone\", marker: name, attributes }, next: closeIndex + 2 };\n}\n/** Convert `~` to NBSP for USJ text content (PT9 read-side `UsfmParser` behavior), and\n * normalize the line-break marker `regularizeSpaces` preserved back to a plain space. */\nfunction toUsjText(text) {\n return text.replaceAll(\"\\n\", \" \").replaceAll(\"~\", NBSP);\n}\n/** Get (and lazily initialize) a marker object's content array without a non-null assertion. */\nfunction getContent(object) {\n if (!object.content)\n object.content = [];\n return object.content;\n}\nexport function usfmFragmentToUsjContent(fragment, options) {\n const result = [];\n const isNoteContext = options?.isNoteContext ?? false;\n let para;\n let note;\n const charStack = [];\n // Char-stack depth at the moment the open note started: frames BELOW it enclose the note\n // (USX nests the note inside them and the span continues after it — `\\wj a \\f …\\f* b\\wj*`\n // puts both the note and \" b\" inside the wj span); frames AT/ABOVE it were opened inside\n // the note's content and close with it. Only meaningful while `note` is set.\n let noteBaseDepth = 0;\n // ---- opaque-structure state (tables, sidebars) ----\n // Current open table and its open row. `\\tr` creates both (consecutive rows share one\n // table); a cell marker then points `para` at the cell object, so ALL ordinary content\n // handling (text, char spans, notes, verses) lands inside the cell unchanged. While the\n // table is open, `para` is always its open row or a cell of that row.\n let table;\n let tableRow;\n // Current open sidebar: `\\esb`…`\\esbe` wraps subsequent top-level blocks (paragraphs and\n // tables). Implicit close (fragment end or a chapter token) marks it closed=\"false\" —\n // ParatextData auto-closes sidebars at the chapter boundary.\n let sidebar;\n // Current open peripheral division: `\\periph` opens one and takes every following block into\n // it. Nothing closes a division but the next `\\periph`, a chapter boundary, or the end of the\n // fragment — USFM gives a division marker no closing bytes at all, so unlike a sidebar there is\n // no terminated/implicit distinction to record. The marker LINE's own text (the division title\n // and its attribute list) is captured first; see `periphCapture`.\n let periph;\n /** Where top-level blocks (paragraphs, tables) land: an open sidebar's content, else an open\n * peripheral division's content, else the fragment result. */\n const blockTarget = () => {\n if (sidebar)\n return getContent(sidebar);\n if (periph)\n return getContent(periph);\n return result;\n };\n // True between a chapter token and the next opened block: loose content there sits at the\n // DOCUMENT ROOT in ParatextData's output, not in an implied paragraph — text typed after\n // `\\c 1` saves as its own ` text` line, and 2SA-2's unclosed `\\ca` strands a root char.\n // Fragment-LEADING bare content keeps the implied-`\\p` wrap (the note-content rebuild\n // depends on unwrapping it), so this is scoped to post-chapter position only.\n let atChapterRootScope = false;\n const container = () => {\n if (note) {\n // Inside the note, only frames opened WITHIN it receive content; the enclosing\n // frames are suspended until the note closes.\n if (charStack.length > noteBaseDepth)\n return getContent(charStack[charStack.length - 1].object);\n return getContent(note);\n }\n if (charStack.length > 0)\n return getContent(charStack[charStack.length - 1].object);\n if (!para) {\n if (atChapterRootScope && !isNoteContext)\n return blockTarget();\n para = { type: \"para\", marker: PARA_MARKER_DEFAULT, content: [] };\n blockTarget().push(para);\n }\n return getContent(para);\n };\n const pushContent = (item) => {\n const target = container();\n if (typeof item === \"string\" && typeof target[target.length - 1] === \"string\") {\n target[target.length - 1] = target[target.length - 1] + item;\n }\n else {\n target.push(item);\n }\n };\n // Implicit close: every still-open char span gets closed=\"false\", mirroring ParatextData\n // (a span only stays unmarked when the user's own `\\marker*` terminated it).\n const markImplicitlyClosed = (fromIndex) => {\n for (let i = fromIndex; i < charStack.length; i += 1) {\n const object = charStack[i].object;\n object.closed = \"false\";\n }\n };\n const closeCharStack = () => {\n markImplicitlyClosed(0);\n charStack.length = 0;\n };\n const closeNote = (terminated) => {\n if (!note)\n return;\n // Chars opened INSIDE the note close with it (implicitly); frames below the note\n // boundary survive — the enclosing span continues after the note.\n if (charStack.length > noteBaseDepth) {\n markImplicitlyClosed(noteBaseDepth);\n charStack.length = noteBaseDepth;\n }\n noteBaseDepth = 0;\n if (!terminated)\n note.closed = \"false\";\n note = undefined;\n };\n // Tables have no closing marker and no `closed` metadata: the table object is already in\n // place, so ending one just drops the open-row state — rows/cells never resume.\n const endTable = () => {\n table = undefined;\n tableRow = undefined;\n };\n /**\n * Open a cell in the current row and make it the content container: text, char spans, notes and\n * verses then flow into it through the ordinary `para`-based container logic. Shared by both\n * token kinds a cell marker can arrive as — a stylesheet that knows `\\th1`/`\\tc1` classifies\n * them as Character (usfm.sty does), one that does not delivers them as unknown paragraph\n * markers, and ParatextData derives a cell from the NAME either way.\n */\n const pushTableCell = (row, marker, cellMatch) => {\n closeCharStack();\n const [, alignInfix, spanStart, spanEnd] = cellMatch;\n // The cell keeps only the starting column in its marker (`thc3-4` → `thc3`); the span width\n // becomes `colspan`, a string of columns spanned (`thc3-4` → \"2\").\n const cell = {\n type: \"table:cell\",\n marker: spanEnd ? marker.slice(0, marker.indexOf(\"-\")) : marker,\n align: TABLE_CELL_ALIGN_BY_INFIX[alignInfix],\n content: [],\n };\n if (spanEnd)\n cell.colspan = String(Number(spanEnd) + 1 - Number(spanStart));\n getContent(row).push(cell);\n para = cell;\n };\n const closeSidebar = (terminated) => {\n if (!sidebar)\n return;\n // Only `\\esbe` terminates a sidebar explicitly; an implicit close (fragment end or a\n // chapter boundary) gets closed=\"false\", mirroring ParatextData's auto-close.\n if (!terminated)\n sidebar.closed = \"false\";\n sidebar = undefined;\n };\n /** End the open division. Nothing to record: a division has no closing bytes to be missing. */\n const closePeriph = () => {\n periph = undefined;\n };\n // ---- attribute-marker folding state (see ATTRIBUTE_MARKERS) ----\n // The most recent chapter/verse/note object, still \"receptive\": an adjacent attribute\n // marker folds onto it as an attribute. Any real content clears it.\n let attrTarget;\n // Whitespace-only text held while attrTarget is receptive: structural (dropped) if an\n // attribute marker follows; ordinary content (flushed) otherwise.\n let heldWhitespace = \"\";\n // An attribute-marker span currently being captured for folding. Aborts to a standalone\n // marker the moment its content turns out to be markup, exactly as ParatextData keeps a\n // `\\cat` with markup or a `\\cp` with markers as its own marker.\n let attrCapture;\n const flushHeldWhitespace = () => {\n if (heldWhitespace)\n pushContent(toUsjText(heldWhitespace));\n heldWhitespace = \"\";\n };\n const clearAttrTarget = (atBlockBoundary = false) => {\n // Line-end whitespace held while a SIDEBAR was receptive is structural: sidebar content\n // is block-level (paragraphs and tables), so the line break between `\\esb`/`\\cat` and\n // the first block never becomes text — ParatextData emits none there. Held whitespace\n // for the other targets (chapter/verse/note) flushes as content, EXCEPT its trailing\n // line break at a block boundary: ParatextData strips the space a line break leaves\n // behind when the next token is a paragraph/book/chapter (UsfmParser's Text case,\n // \"strip final space\"), so `\\c 1` ⏎ `\\ca 2\\ca*` ⏎ `\\p` puts no stray text at the root.\n // Before an INLINE token the space survives — a `\\cat` fold followed by `\\ft` keeps its\n // space inside the note.\n if (attrTarget?.type === \"sidebar\")\n heldWhitespace = \"\";\n else if (atBlockBoundary && heldWhitespace.endsWith(\"\\n\"))\n heldWhitespace = heldWhitespace.slice(0, -1);\n attrTarget = undefined;\n flushHeldWhitespace();\n };\n /** Abort a char-shaped capture: materialize the standalone open span (frame stays open —\n * nested markup and the eventual closer process normally). */\n const materializeCaptureAsChar = () => {\n if (!attrCapture)\n return;\n const object = { type: \"char\", marker: attrCapture.marker, content: [] };\n if (attrCapture.value)\n object.content = [toUsjText(attrCapture.value)];\n container().push(object);\n charStack.push({ object });\n attrCapture = undefined;\n };\n /** Start an ordinary paragraph block. Any non-row/cell paragraph-kind marker also ends an\n * open table — ParatextData never resumes a table across another block. */\n const startParagraph = (marker, initialText) => {\n atChapterRootScope = false;\n endTable();\n closeCharStack();\n closeNote(false);\n para = { type: \"para\", marker, content: [] };\n if (initialText)\n para.content = [toUsjText(initialText)];\n blockTarget().push(para);\n };\n /** Abort a para-shaped capture (`\\cp` with markup): materialize the standalone paragraph. */\n const materializeCaptureAsPara = () => {\n if (!attrCapture)\n return;\n startParagraph(attrCapture.marker, attrCapture.value);\n attrCapture = undefined;\n };\n // ---- peripheral-division capture state ----\n // The text of a `\\periph` marker line, collected between the marker and the next marker token.\n // `\\periph` has no closing bytes, so the next marker is the only thing that ends the line —\n // the same delimiter whether a USFM writer put the division on its own line or a copy laid the\n // whole construct out on one (a line break is ordinary whitespace to a tokenizer). That is what\n // lets a Tier-2 rebuild, which only ever re-tokenizes a single paragraph's bytes, reassemble a\n // whole division from one fragment.\n let periphCapture;\n /**\n * Close the captured marker line and open the division it describes: everything before the\n * line's first `|` is the division title (USX/USJ's `alt`), the rest an ordinary named-attribute\n * list.\n *\n * A list that does not parse is REFUSED, and so is one that spells the TITLE a second time\n * (`alt`, alongside a non-empty marker-line title): the two spellings are the same USJ property,\n * there is no reading that keeps both, and picking one would silently destroy bytes the author\n * can still see. `alt` on a TITLELESS line collides with nothing and is read normally. The editor\n * can never author the colliding shape — periph's display renders `alt` AS the marker-line title\n * (`unknownUsfm.utils.ts`), never as a pipe pair.\n *\n * Refusing the list does NOT refuse the division. `\\periph` opens a peripheral division for its\n * own sake — it has no closing marker and nothing else delimits it, so the division's extent is\n * a property of the marker, not of its attributes. Paratext 9 reads it the same way: a failed\n * `SetAttributes` leaves the marker-line text alone (`ParatextData/UsfmToken.cs`) and the USX\n * writer still opens `` for any `\\periph` token, with an empty `id`\n * (`ParatextData/UsxUsfmParserSink.cs`). Demoting the division to an ordinary paragraph would\n * re-parent every block it contained up to the root instead. The refused bytes survive as the\n * division's own title, where the author can still see and fix them, and they serialize back\n * byte-for-byte (`alt` renders as literal marker content, not a pipe pair).\n *\n * @param atBlockBoundary - Whether the token that ended the line starts a new block (or the\n * fragment ended). The line's trailing line break is structural there, the same rule the text\n * case applies: the line ends where the next block marker begins, and ParatextData emits no\n * content for it.\n */\n const finishPeriphLine = (atBlockBoundary) => {\n if (!periphCapture)\n return;\n let { value } = periphCapture;\n periphCapture = undefined;\n if (atBlockBoundary && value.endsWith(\"\\n\"))\n value = value.slice(0, -1);\n const pipeIndex = value.indexOf(\"|\");\n const parsed = pipeIndex >= 0 ? parseAttributeText(value.slice(pipeIndex + 1), PERIPH_MARKER) : undefined;\n const parsedTitle = pipeIndex >= 0 ? value.slice(0, pipeIndex) : value;\n const refused = pipeIndex >= 0 && (!parsed || (!!parsedTitle && !!parsed[PERIPH_TITLE_ATTRIBUTE]));\n const attributes = refused ? undefined : parsed;\n const title = refused ? value : parsedTitle;\n const opened = {\n type: \"periph\",\n ...(title ? { [PERIPH_TITLE_ATTRIBUTE]: toUsjText(title) } : {}),\n ...attributes,\n };\n opened.content = [];\n // Landed BEFORE `periph` is set, for the same reason the sidebar assembly does it: an already\n // open division would otherwise take the new one into its own content.\n blockTarget().push(opened);\n periph = opened;\n para = undefined;\n };\n // ---- figure capture state ----\n // A `\\fig …\\fig*` span being collected for faithful `figure` emission (ParatextData turns\n // the span into `{ type: \"figure\", … }`). Only a clean span folds: plain-text content with\n // `name=\"value\"` attributes and an explicit `\\fig*`. Anything else — nested markup, USFM\n // 2.0 positional attributes, no closer — degrades to exactly what the marker's own\n // classification produced before figure support (a char span or an unknown paragraph).\n let figCapture;\n /** Degrade an unfoldable figure span: a char frame (stays open — the eventual closer or\n * auto-close processes normally) or an unknown paragraph, matching pre-figure output. */\n const materializeFigCapture = () => {\n if (!figCapture)\n return;\n if (figCapture.shape === \"para\") {\n startParagraph(FIGURE_MARKER, figCapture.value);\n }\n else {\n const object = { type: \"char\", marker: FIGURE_MARKER, content: [] };\n if (figCapture.value)\n object.content = [toUsjText(figCapture.value)];\n container().push(object);\n charStack.push({ object });\n }\n figCapture = undefined;\n };\n const tokens = tokenize(fragment, options?.getMarker ?? getMarker, isNoteContext);\n for (let tokenIndex = 0; tokenIndex < tokens.length; tokenIndex++) {\n const token = tokens[tokenIndex];\n if (attrCapture) {\n if (token.kind === \"text\") {\n attrCapture.value += token.text;\n continue;\n }\n if (attrCapture.shape === \"char\" &&\n token.kind === \"end\" &&\n token.marker.replace(/^\\+/, \"\") === attrCapture.marker) {\n // The fold to an attribute requires NON-EMPTY content (PT9\n // `FindOtherVerseOrChapterNumber` folds only when `tokens[+2].Text != null`, and its\n // tokenizer never produces an empty text token). An EMPTY span (any spelling —\n // `\\va \\va*`, `\\va\\va*`, `\\va \\va*`) is NEVER an empty attribute: it stays a\n // first-class, explicitly-closed char element sitting after its target. The closer is\n // consumed here (no open frame is pushed), so the span is closed with no `closed`\n // metadata, exactly like any user-closed span.\n if (attrCapture.value.trim() === \"\") {\n container().push({ type: \"char\", marker: attrCapture.marker, content: [] });\n attrCapture = undefined;\n // The materialized element is REAL CONTENT, so the target stops being receptive — the\n // same rule a plain-text token applies through `clearAttrTarget` above. Without this, a\n // following attribute marker folded ACROSS the element now separating it from its target\n // (`\\v 11 \\va\\va*\\vp 11 vp\\vp*` put `pubnumber` on the verse while the `\\va` char trailed\n // behind it), and USJ cannot express that order: serializing back put the published\n // number FIRST, silently rewriting the document as `\\v 11 \\vp 11 vp\\vp*\\va \\va*`.\n // ParatextData folds NEITHER marker here — captured from `GetChapterUsx`, it emits\n // `11 vp` in document order.\n clearAttrTarget();\n continue;\n }\n // Explicit close with plain-text content: fold as the target's attribute, TRIMMED —\n // ParatextData trims every folded value (`FindOtherVerseOrChapterNumber` reads\n // `tokens[index + skip + 2].Text.Trim()`, and the note/sidebar category lookups do\n // `tokens[index + 2].Text.Trim()`), so `\\ca 2 \\ca*` yields altnumber \"2\". Any space\n // AFTER the closer is content, not structural — Paratext keeps it in the text\n // (`\\vp 11 vp\\vp* This…` → text starts with the space), per its\n // treat-space-after-attribute-markers-as-content behavior.\n Object.assign(attrCapture.target, {\n [attrCapture.attrName]: toUsjText(attrCapture.value.trim()),\n });\n const foldedMarker = attrCapture.marker;\n attrCapture = undefined;\n // The chapter path alone consumes ONE whitespace-only token after a successful fold,\n // unconditionally — not gated on a `\\cp` following — so a same-line space between\n // `\\ca*` and `\\cp` is structural and `\\cp` still folds, and the space before an\n // ordinary char span vanishes too. The verse path has no such skip: the identical\n // space between `\\va*` and `\\vp` BLOCKS the `\\vp` fold (the v12 rule). Both sides are\n // captured through ParatextData in paranext-core's\n // VerseAttributeFoldRoundTripCaptureTests (`FilledCaThenCp_BothFold`,\n // `SpaceAfterFoldedCa_IsConsumedEvenWithoutCp`,\n // `SpaceBetweenVaCloserAndVp_BlocksVpFold_SpaceIsContent`).\n if (foldedMarker === \"ca\") {\n const next = tokens[tokenIndex + 1];\n if (next?.kind === \"text\" && /^[\\s\\u200B]*$/.test(next.text))\n tokenIndex++;\n }\n continue;\n }\n if (attrCapture.shape === \"para\" && (token.kind === \"para\" || token.kind === \"chapter\")) {\n const cpValue = attrCapture.value.replace(/[\\s\\u200B]+$/, \"\");\n if (cpValue === \"\") {\n // Empty `\\cp` (paragraph-shaped, no end marker): PT9 never yields an empty pubnumber \\u2014\n // it stays a first-class (empty) para element. Materialize the standalone paragraph\n // with no content, then fall through to process the boundary token normally.\n //\n // Unlike the char-shaped empty branch above, this one deliberately does NOT clear\n // `attrTarget` (`startParagraph` leaves it alone), so the receptive window survives a\n // materialized empty `\\cp`. That is audited, not overlooked, on two counts.\n //\n // A CHAR-shaped attribute marker cannot reach the surviving window at all, so the\n // document-order rewrite the char branch closes has no para-shaped twin. This branch only\n // runs when the token that ENDS the capture is a para/chapter boundary; `\\ca` is\n // char-shaped, so it arrives while the capture is still open and falls to the\n // unfoldable-markup arm below, which clears `attrTarget` BEFORE materializing the\n // paragraph. `\\c 1` \\u23ce `\\cp ` \\u23ce `\\ca 2\\ca*` \\u23ce `\\p body` therefore leaves the chapter with\n // no altnumber and the `\\ca` an ordinary char span (pinned in usfmFragmentToUsj.test.ts).\n //\n // What DOES re-enter the window is a foldable PARA token, and `cp` is the only\n // `shape: \"para\"` attribute marker \\u2014 so that means a degenerate duplicate `\\cp` on one\n // chapter, which does still fold across the materialized empty para (probed). Closing\n // the window here would add a branch for a shape no document has.\n startParagraph(attrCapture.marker);\n attrCapture = undefined;\n }\n else {\n // The cp \"paragraph\" ended with plain-text content only: fold (trailing line\n // whitespace is structural).\n Object.assign(attrCapture.target, { [attrCapture.attrName]: toUsjText(cpValue) });\n attrCapture = undefined;\n }\n // fall through to process the boundary token normally\n }\n else {\n // Markup inside the span (or a mismatched closer): not foldable — materialize the\n // standalone marker, then reprocess this token normally. A trailing line break in the\n // captured value before a BLOCK boundary is structural, same as the text-case rule\n // (`\\ca 2 ca` ⏎ `\\cp` materializes content \"2 ca\", not \"2 ca \").\n attrTarget = undefined;\n if ((token.kind === \"para\" || token.kind === \"chapter\") && attrCapture.value.endsWith(\"\\n\"))\n attrCapture.value = attrCapture.value.slice(0, -1);\n if (attrCapture.shape === \"para\")\n materializeCaptureAsPara();\n else\n materializeCaptureAsChar();\n tokenIndex--;\n continue;\n }\n }\n if (periphCapture) {\n // The marker line runs to the next marker token. An optbreak rejoins as the literal `//`\n // the author typed, for the same reason the figure capture rejoins one: `//` inside an\n // attribute value (a URL, say) is plain value bytes to ParatextData, which strips the\n // attribute segment out at tokenize-time before its `//` pass ever runs.\n if (token.kind === \"text\" || token.kind === \"optbreak\") {\n periphCapture.value += token.kind === \"text\" ? token.text : \"//\";\n continue;\n }\n finishPeriphLine(token.kind === \"para\" || token.kind === \"chapter\");\n tokenIndex--;\n continue;\n }\n if (figCapture) {\n if (token.kind === \"text\" || token.kind === \"optbreak\") {\n // The tokenizer splits `//` into optbreak tokens spec-blind — including inside what\n // will become the `|attributes` segment, where ParatextData treats `//` as plain\n // value bytes (`UsfmToken.HandleAttributes` strips the segment out of the text run at\n // tokenize-time, before the parser's `//`→optbreak pass ever runs), so an attribute\n // value carrying a URL must reach `parseAttributeText` below byte-exact. Rejoining\n // every optbreak here (not just post-`|` ones) is safe: a clean figure's description\n // already folds to one flat string with no discretionary-break fidelity of its own\n // (see the `figure.content` assembly below and `materializeFigCapture`'s degrade\n // path), so a pre-`|` `//` landing back as literal text is no regression.\n //\n // This deliberately duplicates `extractAttributes`' optbreak→`//` byte mapping rather\n // than sharing a helper: that function rejoins an already-ASSEMBLED content array\n // post-hoc (strings and `{type:\"optbreak\"}` objects), while this path rejoins the token\n // STREAM as it arrives into a plain string accumulator — the two operate on different\n // shapes at different pipeline stages, and the shared knowledge is just the single\n // byte fact that an optbreak was a literal `//`.\n figCapture.value += token.kind === \"text\" ? token.text : \"//\";\n continue;\n }\n if (token.kind === \"end\" && token.marker.replace(/^\\+/, \"\") === FIGURE_MARKER) {\n const pipeIndex = figCapture.value.indexOf(\"|\");\n const attributes = pipeIndex >= 0\n ? parseAttributeText(figCapture.value.slice(pipeIndex + 1), FIGURE_MARKER)\n : undefined;\n if (attributes) {\n // Clean span: emit the faithful figure. USFM's `src` attribute is `file` in\n // USX/USJ (renamed in place to keep the author's attribute order); the pre-`|`\n // description is the figure's content, omitted when empty (as ParatextData does).\n const figureAttributes = {};\n for (const [name, value] of Object.entries(attributes))\n figureAttributes[name === \"src\" ? \"file\" : name] = value;\n const figure = {\n type: \"figure\",\n marker: FIGURE_MARKER,\n ...figureAttributes,\n };\n const description = figCapture.value.slice(0, pipeIndex);\n if (description)\n figure.content = [toUsjText(description)];\n pushContent(figure);\n figCapture = undefined;\n continue;\n }\n // No `name=\"value\"` attributes (USFM 2.0 positional syntax, or no `|` at all): fall\n // through to degrade — the reprocessed closer then closes the materialized char\n // frame (or lands unmatched in the materialized paragraph), exactly as before.\n }\n // Markup inside the span, a foreign closer, or unfoldable attributes: degrade, then\n // reprocess this token against the materialized span.\n materializeFigCapture();\n tokenIndex--;\n continue;\n }\n if (attrTarget) {\n if (token.kind === \"text\") {\n // Only LINE-BREAK whitespace between the target and its attribute marker is\n // structural (`\\ca 1 ca\\ca*` \\u23CE `\\cp 1 cp` still folds cp). A same-line space is\n // content per Paratext and BLOCKS the fold \\u2014 `\\va 12 va\\va* \\vp 12 vp\\vp*` keeps\n // altnumber but leaves vp a standalone marker with the space in the text.\n if (token.text.includes(\"\\n\") && /^[\\s\\u200B]*$/.test(token.text)) {\n heldWhitespace += token.text;\n continue;\n }\n clearAttrTarget();\n }\n else if (token.kind === \"charOpen\" || token.kind === \"para\") {\n const foldable = token.kind === \"para\" || !token.isNested ? attributeMarker(token.marker) : undefined;\n if (foldable && foldable.targetTypes.includes(attrTarget.type)) {\n // Whitespace between the target and its attribute marker is structural — dropped.\n heldWhitespace = \"\";\n attrCapture = {\n target: attrTarget,\n attrName: foldable.attrName,\n marker: token.marker,\n shape: foldable.shape,\n value: \"\",\n };\n // attrTarget stays receptive: a chapter takes BOTH \\ca and \\cp.\n continue;\n }\n clearAttrTarget(token.kind === \"para\");\n }\n else {\n clearAttrTarget(token.kind === \"chapter\");\n }\n }\n // ---- `\\fig` interception (marker-name driven, like ParatextData's parser figures) ----\n // `\\fig` reaches assembly as charOpen (a sheet that knows it as Character) or para\n // (unknown marker); BOTH fold to a `figure` when the span is clean. Note content keeps\n // the plain char behavior — this tokenizer does not build figures inside notes. Opening\n // a figure auto-closes open char spans exactly as its Character classification would\n // (UsfmParser.cs:247), so the success and degrade paths continue from the same stack.\n if (!note &&\n !isNoteContext &&\n ((token.kind === \"charOpen\" && !token.isNested && token.marker === FIGURE_MARKER) ||\n (token.kind === \"para\" && token.marker === FIGURE_MARKER))) {\n closeCharStack();\n figCapture = { shape: token.kind === \"charOpen\" ? \"char\" : \"para\", value: \"\" };\n continue;\n }\n switch (token.kind) {\n case \"text\": {\n let text = token.text;\n // A text run's trailing LINE BREAK before a block boundary (a paragraph-kind or\n // chapter token, or fragment end) is structural — the line ends where the next block\n // marker begins, and ParatextData emits no content there. A line break before an\n // INLINE token (char/note/verse/milestone: an ordinary line wrap) stays a content\n // space, as does any typed same-line space. Engine fragments carry no line breaks,\n // so only whole-file/direct-converter input reaches this path; `regularizeSpaces`\n // collapsed the run to `\"\\n\"`. Note interiors keep the pre-existing space (a note\n // terminated by a line-end block boundary is not corpus-attested).\n if (!note && text.endsWith(\"\\n\")) {\n const next = tokens[tokenIndex + 1];\n if (next === undefined || next.kind === \"para\" || next.kind === \"chapter\")\n text = text.slice(0, -1);\n }\n if (text)\n pushContent(toUsjText(text));\n break;\n }\n case \"para\": {\n // ---- table assembly ----\n // Row/cell markers reach assembly as para tokens (paragraph styles, or unknown to\n // the sheet). Table shapes never engage inside note content — a row/cell marker\n // there keeps its plain resolution, and ParatextData builds no tables there either.\n const tableEligible = !note && !isNoteContext;\n if (tableEligible && token.marker === TABLE_ROW_MARKER) {\n closeCharStack();\n // Consecutive `\\tr`s share one table; the first creates it.\n if (!table) {\n table = { type: \"table\", content: [] };\n blockTarget().push(table);\n }\n tableRow = { type: \"table:row\", marker: TABLE_ROW_MARKER, content: [] };\n getContent(table).push(tableRow);\n // Content before the first cell marker (degenerate) lands in the row itself.\n para = tableRow;\n atChapterRootScope = false;\n break;\n }\n if (tableEligible && tableRow) {\n const cellMatch = TABLE_CELL_MARKER_REGEX.exec(token.marker);\n // A name outside what ParatextData recognizes as a cell (see\n // isRecognizedTableCell) is an unknown marker that ENDS the table (and the next\n // `\\tr` starts a fresh one).\n if (cellMatch && isRecognizedTableCell(cellMatch)) {\n pushTableCell(tableRow, token.marker, cellMatch);\n break;\n }\n }\n // Any other paragraph-kind token (esb/esbe included) ends an open table; the token\n // itself then processes normally. A cell marker with NO open row is not table\n // content — it stays an unknown paragraph, exactly as ParatextData splits it out.\n endTable();\n // ---- sidebar assembly ----\n if (!isNoteContext && token.marker === SIDEBAR_MARKER) {\n closeCharStack();\n closeNote(false);\n // Sidebars never nest: an unterminated previous sidebar closes implicitly.\n closeSidebar(false);\n // Landed BEFORE `sidebar` is set: `blockTarget()` answers \"the open sidebar's content\"\n // whenever one is open, so assigning first would nest the new sidebar inside itself.\n const opened = {\n type: \"sidebar\",\n marker: SIDEBAR_MARKER,\n content: [],\n };\n blockTarget().push(opened);\n sidebar = opened;\n para = undefined;\n attrTarget = sidebar; // receptive to \\cat (directly after \\esb only)\n atChapterRootScope = false;\n break;\n }\n if (token.marker === SIDEBAR_END_MARKER && sidebar) {\n closeCharStack();\n closeNote(false);\n // `\\esbe` terminates the sidebar and is consumed — it emits nothing itself. With\n // no open sidebar it falls through to today's unknown-paragraph behavior.\n closeSidebar(true);\n para = undefined;\n break;\n }\n // ---- peripheral-division assembly ----\n if (!isNoteContext && token.marker === PERIPH_MARKER) {\n closeCharStack();\n closeNote(false);\n // A division ends every block structure open inside it, including a sidebar the author\n // never terminated; divisions themselves never nest.\n closeSidebar(false);\n closePeriph();\n periphCapture = { value: \"\" };\n para = undefined;\n atChapterRootScope = false;\n break;\n }\n startParagraph(token.marker);\n break;\n }\n case \"verse\": {\n // A verse closes an open note but — for USFM ≤3.0 — does NOT close open char styles: the\n // unclosed span continues across the verse and the verse milestone nests inside it (PT9\n // UsfmParser Verse case: `if (!RequiresPlusOnNestedStyles()) CloseCharStyles()`, and\n // RequiresPlusOnNestedStyles() is true for ≤3.0). This pipeline targets ≤3.0; when the\n // ParatextData dependency moves past 9.6 the close-at-verse must become version-switched\n // (guarded by the upgrade tripwire, alongside close-on-bare and `+` emission).\n closeNote(false);\n const verse = { type: \"verse\", marker: VERSE_MARKER, number: token.number };\n pushContent(verse);\n attrTarget = verse; // receptive to \\va/\\vp\n break;\n }\n case \"chapter\": {\n closeCharStack();\n closeNote(false);\n // A chapter boundary ends any open table and implicitly closes an open sidebar\n // (ParatextData auto-closes sidebars at the end of the chapter). A peripheral division\n // ends there too: chapters are book body, which is never division content.\n endTable();\n closeSidebar(false);\n closePeriph();\n para = undefined;\n const chapter = {\n type: \"chapter\",\n marker: CHAPTER_MARKER,\n number: token.number,\n };\n result.push(chapter);\n attrTarget = chapter; // receptive to \\ca/\\cp\n atChapterRootScope = true;\n break;\n }\n case \"note\": {\n // A note does NOT close open char spans: USX nests the note inside them and the\n // enclosing span continues after it (`\\wj a \\f …\\f* b\\wj*` → wj contains [text,\n // note, text]). Notes themselves never nest, so a previous open note closes first.\n closeNote(false);\n const target = container();\n note = { type: \"note\", marker: token.marker, caller: token.caller, content: [] };\n noteBaseDepth = charStack.length;\n target.push(note);\n attrTarget = note; // receptive to \\cat (right after the caller only)\n break;\n }\n case \"charOpen\": {\n // A cell marker inside an OPEN row is a cell, whichever token kind the stylesheet's\n // classification made it: usfm.sty declares `\\th1`/`\\tc1`/… as Character styles, so with a\n // real project sheet they arrive here rather than as paragraph tokens, and without this a\n // table copied out of the editor pastes back as loose char spans inside empty rows.\n // Guarded on an open row — only `\\tr` opens one — so a char span that merely shares a\n // cell marker's name outside a table keeps its ordinary resolution, and a NESTED span\n // (`\\+tc1`) is never a cell.\n if (!note && !isNoteContext && tableRow && !token.isNested) {\n const cellMatch = TABLE_CELL_MARKER_REGEX.exec(token.marker);\n if (cellMatch && isRecognizedTableCell(cellMatch)) {\n pushTableCell(tableRow, token.marker, cellMatch);\n break;\n }\n }\n // A new non-nested char marker auto-closes open char styles (PT9) — but never\n // across an open note's boundary: the frames enclosing the note stay open.\n if (!token.isNested) {\n const base = note ? noteBaseDepth : 0;\n markImplicitlyClosed(base);\n charStack.length = base;\n }\n const target = container();\n const object = { type: \"char\", marker: token.marker, content: [] };\n target.push(object);\n charStack.push({ object });\n break;\n }\n case \"end\": {\n const marker = token.marker.replace(/^\\+/, \"\");\n // While a note is open, a closer only matches frames opened INSIDE it — it must not\n // reach across the note boundary and close an enclosing span from within the note.\n const searchBase = note ? noteBaseDepth : 0;\n const frameIndex = charStack.findLastIndex((frame, index) => index >= searchBase && frame.object.marker === marker);\n if (frameIndex >= 0) {\n extractAttributes(charStack[frameIndex].object);\n // Nested spans above the explicitly-closed frame are closed IMPLICITLY by it.\n markImplicitlyClosed(frameIndex + 1);\n charStack.length = frameIndex;\n }\n else if (note && note.marker === marker) {\n // Explicit note close: chars opened inside the note close implicitly with it;\n // frames enclosing the note survive (closeNote truncates to the note boundary).\n closeNote(true);\n }\n else {\n // Unmatched closer: PT9 first pops every open char style above the note/para boundary\n // (UsfmParser End token: the while-loop closes each Char element on top of the stack\n // until it matches or hits a non-char element; with no match here they all close as\n // closed=\"false\"), THEN flags the stray closer (PT9 sink.Unmatched,\n // UsxUsfmParserSink.cs:262-266) — an unmatched element rendered as ImmutableUnmatchedNode\n // with the existing `.invalid` styling that serializes back to the same text. Closing\n // the open frames first keeps the following text in the paragraph/note instead of\n // swallowing it into a span PT9 has already terminated.\n markImplicitlyClosed(searchBase);\n charStack.length = searchBase;\n pushContent({ type: \"unmatched\", marker: `${token.marker}*` });\n }\n break;\n }\n case \"milestone\":\n pushContent({ type: \"ms\", marker: token.marker, ...token.attributes });\n break;\n case \"optbreak\":\n pushContent({ type: \"optbreak\" });\n break;\n }\n }\n // Fragment ended on a `\\periph` marker line: the division has its title and attributes but no\n // content, which is a complete division — a periph never had a closer to be missing.\n if (periphCapture)\n finishPeriphLine(true);\n // Fragment ended mid-figure: no `\\fig*` closer — degrade to the plain char/para span.\n if (figCapture)\n materializeFigCapture();\n if (attrCapture) {\n // Fragment ended mid-capture. A para-shaped capture (`\\cp 1 cp` at fragment end) folds —\n // the paragraph ended with plain-text content. A char-shaped capture never saw its\n // closer, so it stays a standalone (implicitly closed) span.\n if (attrCapture.shape === \"para\") {\n const cpValue = attrCapture.value.replace(/[\\s\\u200B]+$/, \"\");\n // Empty `\\cp` at fragment end stays a first-class empty para (never an empty pubnumber);\n // a non-empty one folds as the pubnumber.\n if (cpValue === \"\")\n startParagraph(attrCapture.marker);\n else\n Object.assign(attrCapture.target, { [attrCapture.attrName]: toUsjText(cpValue) });\n attrCapture = undefined;\n }\n else {\n // Trailing line break at the fragment-end boundary is structural (text-case rule).\n if (attrCapture.value.endsWith(\"\\n\"))\n attrCapture.value = attrCapture.value.slice(0, -1);\n materializeCaptureAsChar();\n }\n }\n closeCharStack();\n closeNote(false);\n // Fragment ended without `\\esbe`: the sidebar closes implicitly (closed=\"false\").\n closeSidebar(false);\n // Drop empty content arrays (USJ omits `content` for empty paras).\n const dropEmpty = (items) => {\n for (const item of items) {\n if (typeof item === \"string\")\n continue;\n if (item.content) {\n dropEmpty(item.content);\n if (item.content.length === 0)\n delete item.content;\n }\n }\n };\n dropEmpty(result);\n return result;\n}\n/**\n * On explicit close, split a trailing `|attributes` chunk off the span's trailing text run.\n *\n * The tokenizer splits `//` into optbreak tokens spec-blind, so an attribute segment whose\n * VALUE contains `//` (a URL in `link-href`, say) arrives here as interleaved strings and\n * optbreak objects. ParatextData never does that: `UsfmToken.HandleAttributes` strips the\n * attribute segment out of the text run at tokenize-time — splitting the run at its FIRST `|` —\n * and only the remaining content text ever reaches the `//`→optbreak pass (UsfmParser's Text\n * case). So the trailing run (the maximal contiguous string/optbreak suffix, which by\n * construction came from ONE original text run) is rejoined byte-exact — each optbreak was a\n * literal `//` — before attribute parsing, and only pre-pipe optbreaks survive as content.\n * When the segment does not parse, the content stays as-is, split optbreaks included: that too\n * is ParatextData (a failed `SetAttributes` leaves the run literal TEXT, whose post-pipe `//`\n * then genuinely becomes optional breaks).\n */\nfunction extractAttributes(object) {\n const content = object.content;\n if (!content || content.length === 0)\n return;\n // Trailing text run: the maximal contiguous suffix of strings and optbreak objects.\n let runStart = content.length;\n while (runStart > 0) {\n const item = content[runStart - 1];\n if (typeof item !== \"string\" && item.type !== \"optbreak\")\n break;\n runStart--;\n }\n // First pipe-carrying string within the run (PT9: `text.IndexOf('|')` on the whole run).\n const pipeItemIndex = content.findIndex((item, index) => index >= runStart && typeof item === \"string\" && item.includes(\"|\"));\n if (pipeItemIndex < 0)\n return;\n const rejoined = content\n .slice(pipeItemIndex)\n .map((item) => (typeof item === \"string\" ? item : \"//\"))\n .join(\"\");\n const pipeIndex = rejoined.indexOf(\"|\");\n const attributes = parseAttributeText(rejoined.slice(pipeIndex + 1), object.marker ?? \"\");\n if (!attributes)\n return;\n const text = rejoined.slice(0, pipeIndex);\n content.length = pipeItemIndex;\n if (text)\n content.push(text);\n Object.assign(object, attributes);\n}\n","import { createState } from \"lexical\";\n/** Should only be used with CharNodes. */\nexport const charIdState = createState(\"cid\", {\n parse: (v) => (typeof v === \"string\" ? v : undefined),\n});\n/** Can be used on any standard USJ node. */\nexport const segmentState = createState(\"segment\", {\n parse: (v) => (typeof v === \"string\" ? v : undefined),\n});\n/** Should be used with TextNodes. */\nexport const textTypeState = createState(\"textType\", {\n parse: (v) => (typeof v === \"string\" ? v : undefined),\n});\n/**\n * `textTypeState` value tagging the engine-owned NBSP separator between an editable marker glyph\n * and its content (the `[glyph, separator, ...content]` prefix layout). The runtime creator and\n * reader key on this constant — `$createMarkerTrailingSeparator` and\n * `$isMarkerTrailingSeparator` (node.utils.ts) — as does the forward adaptor building the\n * serialized twin for a paragraph's own prefix.\n *\n * It is NOT the only spelling in the codebase: four sites write the tag as a raw string, so\n * changing this value means changing them too. One is a CREATOR — the forward adaptor's table-cell\n * separator (usj-editor.adaptor.ts), whose sibling paragraph-prefix creator a few lines away does\n * use the constant. The other three are readers: the two collab paths that keep the separator's\n * text out of content ops (editor-delta.adaptor.ts and delta-common.utils.ts), and the settled-note\n * unwrap that reads the tag off a SERIALIZED node's `$.textType` (virtualSettle.utils.ts).\n */\nexport const MARKER_TRAILING_SPACE_TEXT_TYPE = \"marker-trailing-space\";\n","import { $applyNodeReplacement, $getState, $setState, createState, DecoratorNode, isHTMLElement, } from \"lexical\";\nexport const IMMUTABLE_TYPED_TEXT_VERSION = 1;\n/** The `textType` every USFM marker glyph carries, whichever way the view renders it. */\nconst MARKER_TEXT_TYPE = \"marker\";\n/**\n * Marks a marker glyph as one the view renders in the GUTTER — the fixed column beside the text\n * (`hasGutterParaMarkers`) — rather than inline among the words.\n *\n * The distinction cannot be read off the node's class or its `textType`: the gutter aid and\n * markerMode \"visible\"'s INLINE glyph are both an `ImmutableTypedTextNode` with\n * `textType: \"marker\"`. Nor can it be read off the view, because \"is this marker in the gutter?\" is\n * asked one node at a time — a document can carry gutter markers and inline glyphs at once (a\n * book's `\\id` line, for one). So the fact travels on the node that has it, set where the glyph is\n * built, and it is what makes gutter markers unclickable in\n * `ParaMarkerPrefixCursorGuardPlugin` (shared-react) while inline glyphs keep their caret.\n *\n * Set on the SERIALIZED twin by the USJ→editor adaptor's `createImmutableTypedText`\n * (usj-editor.adaptor.ts, platform), which builds JSON rather than live nodes — the same split the\n * `textType` state already has.\n */\nexport const gutterMarkerState = createState(\"isGutterMarker\", {\n parse: (value) => value === true,\n});\nexport class ImmutableTypedTextNode extends DecoratorNode {\n __textType;\n __text;\n constructor(textType = \"\", text = \"\", key) {\n super(key);\n this.__textType = textType;\n this.__text = text;\n }\n static getType() {\n return \"immutable-typed-text\";\n }\n static clone(node) {\n const { __textType, __text, __key } = node;\n return new ImmutableTypedTextNode(__textType, __text, __key);\n }\n static importDOM() {\n return {\n span: (node) => {\n if (!isTypedTextElement(node))\n return null;\n return {\n conversion: $convertImmutableTypedTextElement,\n priority: 1,\n };\n },\n };\n }\n static importJSON(serializedNode) {\n return $createImmutableTypedTextNode().updateFromJSON(serializedNode);\n }\n updateFromJSON(serializedNode) {\n return super\n .updateFromJSON(serializedNode)\n .setTextType(serializedNode.textType)\n .setTextContent(serializedNode.text);\n }\n setTextType(textType) {\n if (this.__textType === textType)\n return this;\n const self = this.getWritable();\n self.__textType = textType;\n return self;\n }\n getTextType() {\n const self = this.getLatest();\n return self.__textType;\n }\n setTextContent(text) {\n if (this.__text === text)\n return this;\n const self = this.getWritable();\n self.__text = text;\n return self;\n }\n getTextContent() {\n const self = this.getLatest();\n return self.__text;\n }\n createDOM() {\n const dom = document.createElement(\"span\");\n dom.setAttribute(\"data-text-type\", this.__textType);\n dom.classList.add(this.__textType);\n // The glyph bytes are written into the element itself, NOT rendered through the decorator\n // portal — see `decorate` for why. The resulting DOM is identical either way (React rendered\n // the same string as this element's only text child), so nothing downstream changes shape.\n dom.textContent = this.__text;\n return dom;\n }\n updateDOM(prevNode, dom) {\n // Keep the rendered bytes in step with an in-place `setTextContent` (the JSON update path is\n // its only caller); the guard means an untouched node writes nothing. `__textType` is\n // deliberately NOT re-applied here — it is only ever set while building a node from JSON,\n // before this element exists, so the class list has never been able to go stale, and\n // re-deriving it would be a behavior change this fix does not need.\n if (prevNode.__text !== this.__text)\n dom.textContent = this.__text;\n // Returning false tells Lexical that this node does not need its\n // DOM element replacing with a new copy from createDOM.\n return false;\n }\n exportDOM(editor) {\n const { element } = super.exportDOM(editor);\n if (element && isHTMLElement(element)) {\n element.setAttribute(\"data-text-type\", this.getTextType());\n }\n return { element };\n }\n /**\n * No decorator payload: the glyph bytes are rendered by {@link createDOM} instead.\n *\n * This node used to return its text here, so `@lexical/react`'s `useDecorators` painted the\n * bytes into this element through a React portal. That is unsound for a value with STABLE\n * IDENTITY. Lexical only notifies its decorator listener when the decorator value actually\n * changes — `reconcileDecorator` bails on `currentDecorators[key] === decorator` — and two equal\n * strings always compare equal. So whenever Lexical DESTROYS and RE-CREATES this node's element\n * while the node itself survives (`$createNode` runs for every child of a freshly created parent,\n * which is exactly what re-parenting a node does), the map never changed, no listener fired,\n * `useDecorators` never rebuilt its portal list, and the portal stayed pointed at the OLD,\n * detached element. The new element was left permanently EMPTY — the glyph vanished from the\n * screen while the node, the USJ, and the file on disk all still carried it, and only remounting\n * the editor brought it back.\n *\n * The marker-edit engine re-parents preserved nodes on every Tier-2 paragraph rebuild\n * (`$replaceSentinels`, tier2Rebuild.utils.ts, moves each preserved node into the rebuilt\n * paragraph), so an `\\optbreak`'s `//` token — a child of the preserved `UnknownNode` — blanked\n * out the first time anything else in its paragraph settled. Rendering from `createDOM` removes\n * the portal indirection entirely: the bytes travel with the element that carries them, so any\n * number of re-parents keeps them, and there is one less React portal per glyph.\n */\n decorate() {\n return null;\n }\n exportJSON() {\n return {\n // Spread first so this node's own properties win: super contributes the NodeState (e.g.\n // `gutterMarkerState`), which `updateFromJSON` reads back, so a glyph that round-trips\n // through JSON stays the same KIND of glyph.\n ...super.exportJSON(),\n type: this.getType(),\n textType: this.getTextType(),\n text: this.getTextContent(),\n version: IMMUTABLE_TYPED_TEXT_VERSION,\n };\n }\n // Mutation\n isKeyboardSelectable() {\n return false;\n }\n}\nfunction $convertImmutableTypedTextElement(element) {\n const textType = element.getAttribute(\"data-text-type\") ?? \"\";\n const text = element.textContent ?? \"\";\n const node = $createImmutableTypedTextNode(textType, text);\n return { node };\n}\nexport function $createImmutableTypedTextNode(textType, text) {\n return $applyNodeReplacement(new ImmutableTypedTextNode(textType, text));\n}\n/**\n * Creates a marker glyph that renders in the gutter — see {@link gutterMarkerState}.\n *\n * @param text - The glyph's bytes, e.g. `\\q1` plus its separator.\n * @returns the marker node, flagged as gutter-rendered.\n */\nexport function $createGutterMarkerNode(text) {\n return $setState($createImmutableTypedTextNode(MARKER_TEXT_TYPE, text), gutterMarkerState, true);\n}\n/**\n * Whether the node is a marker glyph the view renders in the gutter, which is never a place a\n * caret may come to rest — see {@link gutterMarkerState}.\n *\n * @param node - The node to check.\n * @returns `true` for a gutter-rendered marker glyph.\n */\nexport function $isGutterMarkerNode(node) {\n return $isImmutableTypedTextNode(node) && $getState(node, gutterMarkerState);\n}\nfunction isTypedTextElement(node) {\n // NOTE: `tagName` is upper-cased for HTML-namespace elements, so this comparison never matches\n // and the `span` conversion above is unreachable. Correcting the case ALONE is not the fix: this\n // predicate's only discriminator is the tag, so a case-insensitive compare makes this node claim\n // EVERY pasted `` at priority 1 — including the verse spans that\n // `StructureKeyboardPlugin`'s paste sanitizer expects to strip down to text. A real fix also has\n // to narrow the predicate (the `data-text-type` attribute `exportDOM` emits is the natural\n // discriminator) and decide what pasting a marker glyph should do, which is a behavior decision\n // rather than a typo fix.\n return node?.tagName === \"span\";\n}\nexport function $isImmutableTypedTextNode(node) {\n return node instanceof ImmutableTypedTextNode;\n}\nexport function isSerializedImmutableTypedTextNode(node) {\n return node?.type === ImmutableTypedTextNode.getType();\n}\n","/**\n * Adapted from https://github.com/facebook/lexical/blob/92c47217244f9d3c22a59728633fb41a10420724/packages/lexical-mark/src/MarkNode.ts\n * This adaption allows for different types of marks while still only requiring one mark to enclose\n * a selection.\n */\nimport { assertSafeKey } from \"@eten-tech-foundation/scripture-utilities\";\nimport { addClassNamesToElement, removeClassNamesFromElement } from \"@lexical/utils\";\nimport { $applyNodeReplacement, $isElementNode, $isRangeSelection, $isTextNode, ElementNode, } from \"lexical\";\n/** Reserved mark type for CommentPlugin. */\nexport const COMMENT_MARK_TYPE = \"internal-comment\";\nconst reservedTypes = [COMMENT_MARK_TYPE];\nconst NO_IDS = Object.freeze({});\nconst NO_ON_CLICKS = Object.freeze({});\nconst NO_ON_REMOVES = Object.freeze({});\nconst NO_ON_MOUSE_ENTERS = Object.freeze({});\nconst NO_ON_MOUSE_LEAVES = Object.freeze({});\nconst TYPED_MARK_VERSION = 1;\nconst typedOnClickRegistry = new Map();\nconst typedOnRemoveRegistry = new Map();\nconst typedOnMouseEnterRegistry = new Map();\nconst typedOnMouseLeaveRegistry = new Map();\nexport class TypedMarkNode extends ElementNode {\n __typedIDs;\n __typedOnClicks;\n __typedOnRemoves;\n __typedOnMouseEnters;\n __typedOnMouseLeaves;\n __domOnClickListener;\n __domOnMouseEnterListener;\n __domOnMouseLeaveListener;\n __suppressOnRemoveCallbacks;\n constructor(typedIds = NO_IDS, typedOnClicks, typedOnRemoves, typedOnMouseEnters, typedOnMouseLeaves, key) {\n super(key);\n this.__typedIDs = cloneTypedIDs(typedIds);\n this.__typedOnClicks = cloneTypedOnClicks(typedOnClicks);\n this.__typedOnRemoves = cloneTypedOnRemoves(typedOnRemoves);\n this.__typedOnMouseEnters = cloneTypedOnMouseEnters(typedOnMouseEnters);\n this.__typedOnMouseLeaves = cloneTypedOnMouseLeaves(typedOnMouseLeaves);\n this.pruneTypedOnClicks();\n this.pruneTypedOnRemoves();\n this.pruneTypedOnMouseEnters();\n this.pruneTypedOnMouseLeaves();\n this.syncTypedOnClicksToRegistry();\n this.syncTypedOnRemovesToRegistry();\n this.syncTypedOnMouseEntersToRegistry();\n this.syncTypedOnMouseLeavesToRegistry();\n }\n static getType() {\n return \"typed-mark\";\n }\n static clone(node) {\n const __typedIDs = cloneTypedIDs(node.__typedIDs);\n const __typedOnClicks = cloneTypedOnClicks(node.__typedOnClicks);\n const __typedOnRemoves = cloneTypedOnRemoves(node.__typedOnRemoves);\n const __typedOnMouseEnters = cloneTypedOnMouseEnters(node.__typedOnMouseEnters);\n const __typedOnMouseLeaves = cloneTypedOnMouseLeaves(node.__typedOnMouseLeaves);\n return new TypedMarkNode(__typedIDs, __typedOnClicks, __typedOnRemoves, __typedOnMouseEnters, __typedOnMouseLeaves, node.__key);\n }\n static isReservedType(type) {\n return reservedTypes.includes(type);\n }\n static importDOM() {\n return null;\n }\n static importJSON(serializedNode) {\n return $createTypedMarkNode().updateFromJSON(serializedNode);\n }\n exportJSON() {\n return {\n ...super.exportJSON(),\n type: this.getType(),\n typedIDs: this.getTypedIDs(),\n version: TYPED_MARK_VERSION,\n };\n }\n createDOM(config, editor) {\n const element = document.createElement(\"mark\");\n for (const [type, ids] of Object.entries(this.__typedIDs)) {\n addClassNamesToElement(element, getTypedClassName(config.theme.typedMark, type));\n if (ids.length > 1) {\n addClassNamesToElement(element, getTypedClassName(config.theme.typedMarkOverlap, type));\n }\n for (const id of ids) {\n addClassNamesToElement(element, getTypedClassName(\"annotationId\", id));\n }\n }\n const clickListener = this.getOrCreateDOMClickListener(editor);\n element.addEventListener(\"click\", clickListener);\n const mouseEnterListener = this.getOrCreateDOMMouseEnterListener(editor);\n element.addEventListener(\"mouseenter\", mouseEnterListener);\n const mouseLeaveListener = this.getOrCreateDOMMouseLeaveListener(editor);\n element.addEventListener(\"mouseleave\", mouseLeaveListener);\n return element;\n }\n updateDOM(prevNode, element, config) {\n const types = new Set([\n ...Object.keys(prevNode.__typedIDs ?? {}),\n ...Object.keys(this.__typedIDs ?? {}),\n ]);\n for (const type of types) {\n const prevIDs = prevNode.__typedIDs[type] ?? [];\n const nextIDs = this.__typedIDs[type] ?? [];\n const prevIDsCount = prevIDs.length;\n const nextIDsCount = nextIDs.length;\n const markTheme = getTypedClassName(config.theme.typedMark, type);\n const overlapTheme = getTypedClassName(config.theme.typedMarkOverlap, type);\n if (prevIDsCount !== nextIDsCount) {\n if (prevIDsCount === 0) {\n if (nextIDsCount === 1)\n addClassNamesToElement(element, markTheme);\n }\n else if (nextIDsCount === 0) {\n removeClassNamesFromElement(element, markTheme);\n }\n if (prevIDsCount === 1) {\n if (nextIDsCount === 2)\n addClassNamesToElement(element, overlapTheme);\n }\n else if (nextIDsCount === 1) {\n removeClassNamesFromElement(element, overlapTheme);\n }\n }\n const prevIDsSet = new Set(prevIDs);\n const nextIDsSet = new Set(nextIDs);\n for (const id of prevIDs) {\n if (!nextIDsSet.has(id)) {\n removeClassNamesFromElement(element, getTypedClassName(\"annotationId\", id));\n }\n }\n for (const id of nextIDs) {\n if (!prevIDsSet.has(id)) {\n addClassNamesToElement(element, getTypedClassName(\"annotationId\", id));\n }\n }\n }\n return false;\n }\n updateFromJSON(serializedNode) {\n return super.updateFromJSON(serializedNode).setTypedIDs(serializedNode.typedIDs);\n }\n hasID(type, id) {\n const typedIDs = this.getTypedIDs();\n const ids = typedIDs[type];\n if (!ids)\n return false;\n for (const existingId of ids) {\n if (id === existingId) {\n return true;\n }\n }\n return false;\n }\n getTypedIDs() {\n const self = this.getLatest();\n return $isTypedMarkNode(self) ? self.__typedIDs : {};\n }\n setTypedIDs(ids) {\n const self = this.getWritable();\n const previousIDs = cloneTypedIDs(self.__typedIDs);\n self.__typedIDs = cloneTypedIDs(ids);\n self.dispatchRemovedIDs(previousIDs, self.__typedIDs, \"removed\");\n self.pruneTypedOnClicks();\n self.pruneTypedOnRemoves();\n self.pruneTypedOnMouseEnters();\n self.pruneTypedOnMouseLeaves();\n self.syncTypedOnClicksToRegistry();\n self.syncTypedOnRemovesToRegistry();\n self.syncTypedOnMouseEntersToRegistry();\n self.syncTypedOnMouseLeavesToRegistry();\n const mergedNode = self.mergeWithAdjacentTypedMarks();\n if (mergedNode.hasNoIDsForEveryType() && mergedNode.getParent() !== null)\n $unwrapTypedMarkNode(mergedNode);\n return mergedNode;\n }\n setTypedOnClicks(typedOnClicks) {\n const self = this.getWritable();\n self.__typedOnClicks = cloneTypedOnClicks(typedOnClicks);\n self.pruneTypedOnClicks();\n self.syncTypedOnClicksToRegistry();\n return self;\n }\n getTypedOnClicks() {\n const self = this.getLatest();\n if (!$isTypedMarkNode(self))\n return {};\n const stored = typedOnClickRegistry.get(self.getKey());\n return stored ?? {};\n }\n setTypedOnRemoves(typedOnRemoves) {\n const self = this.getWritable();\n self.__typedOnRemoves = cloneTypedOnRemoves(typedOnRemoves);\n self.pruneTypedOnRemoves();\n self.syncTypedOnRemovesToRegistry();\n return self;\n }\n getTypedOnRemoves() {\n const self = this.getLatest();\n if (!$isTypedMarkNode(self))\n return {};\n const stored = typedOnRemoveRegistry.get(self.getKey());\n return stored ?? {};\n }\n setTypedOnMouseEnters(typedOnMouseEnters) {\n const self = this.getWritable();\n self.__typedOnMouseEnters = cloneTypedOnMouseEnters(typedOnMouseEnters);\n self.pruneTypedOnMouseEnters();\n self.syncTypedOnMouseEntersToRegistry();\n return self;\n }\n getTypedOnMouseEnters() {\n const self = this.getLatest();\n if (!$isTypedMarkNode(self))\n return {};\n const stored = typedOnMouseEnterRegistry.get(self.getKey());\n return stored ?? {};\n }\n setTypedOnMouseLeaves(typedOnMouseLeaves) {\n const self = this.getWritable();\n self.__typedOnMouseLeaves = cloneTypedOnMouseLeaves(typedOnMouseLeaves);\n self.pruneTypedOnMouseLeaves();\n self.syncTypedOnMouseLeavesToRegistry();\n return self;\n }\n getTypedOnMouseLeaves() {\n const self = this.getLatest();\n if (!$isTypedMarkNode(self))\n return {};\n const stored = typedOnMouseLeaveRegistry.get(self.getKey());\n return stored ?? {};\n }\n addID(type, id, onClick, onRemove, onMouseEnter, onMouseLeave) {\n const self = this.getWritable();\n if (!$isTypedMarkNode(self))\n return;\n assertSafeKey(type);\n assertSafeKey(id);\n let ids = self.__typedIDs[type];\n if (!ids) {\n ids = [];\n self.__typedIDs[type] = ids;\n }\n for (const existingId of ids) {\n if (id === existingId) {\n if (onClick)\n self.setOnClickFor(type, id, onClick);\n if (onRemove)\n self.setOnRemoveFor(type, id, onRemove);\n if (onMouseEnter)\n self.setOnMouseEnterFor(type, id, onMouseEnter);\n if (onMouseLeave)\n self.setOnMouseLeaveFor(type, id, onMouseLeave);\n return;\n }\n }\n ids.push(id);\n if (onClick)\n self.setOnClickFor(type, id, onClick);\n if (onRemove)\n self.setOnRemoveFor(type, id, onRemove);\n if (onMouseEnter)\n self.setOnMouseEnterFor(type, id, onMouseEnter);\n if (onMouseLeave)\n self.setOnMouseLeaveFor(type, id, onMouseLeave);\n }\n deleteID(type, id) {\n const self = this.getWritable();\n if (!$isTypedMarkNode(self))\n return;\n const ids = self.__typedIDs[type];\n if (!ids || ids.length === 0)\n return;\n for (let i = 0; i < ids.length; i++) {\n if (id === ids[i]) {\n ids.splice(i, 1);\n self.invokeOnRemove(type, id, \"removed\");\n break;\n }\n }\n self.removeOnClickFor(type, id);\n self.removeOnRemoveFor(type, id);\n self.removeOnMouseEnterFor(type, id);\n self.removeOnMouseLeaveFor(type, id);\n self.pruneTypedOnClicks();\n self.pruneTypedOnRemoves();\n self.pruneTypedOnMouseEnters();\n self.pruneTypedOnMouseLeaves();\n const mergedNode = self.mergeWithAdjacentTypedMarks();\n if (mergedNode.hasNoIDsForEveryType() && mergedNode.getParent() !== null)\n $unwrapTypedMarkNode(mergedNode);\n }\n hasNoIDsForEveryType() {\n return Object.values(this.getTypedIDs()).every((ids) => ids === undefined || ids.length === 0);\n }\n insertNewAfter(_selection, restoreSelection = true) {\n const node = $createTypedMarkNode(this.__typedIDs, this.getTypedOnClicks());\n this.insertAfter(node, restoreSelection);\n return node;\n }\n canInsertTextBefore() {\n return false;\n }\n canInsertTextAfter() {\n return false;\n }\n canBeEmpty() {\n return false;\n }\n isInline() {\n return true;\n }\n extractWithChild(_child, selection, destination) {\n if (!$isRangeSelection(selection) || destination === \"html\") {\n return false;\n }\n const anchor = selection.anchor;\n const focus = selection.focus;\n const anchorNode = anchor.getNode();\n const focusNode = focus.getNode();\n const isBackward = selection.isBackward();\n const selectionLength = isBackward\n ? anchor.offset - focus.offset\n : focus.offset - anchor.offset;\n return (this.isParentOf(anchorNode) &&\n this.isParentOf(focusNode) &&\n this.getTextContent().length === selectionLength);\n }\n excludeFromCopy(destination) {\n return destination !== \"clone\";\n }\n remove(preserveEmptyParent) {\n const self = this.getWritable();\n const typedIDs = this.getTypedIDs();\n if (self.__suppressOnRemoveCallbacks) {\n self.__suppressOnRemoveCallbacks = undefined;\n }\n else {\n self.dispatchOnRemoveForTypedIDs(typedIDs, \"destroyed\");\n }\n typedOnClickRegistry.delete(self.getKey());\n typedOnRemoveRegistry.delete(self.getKey());\n typedOnMouseEnterRegistry.delete(self.getKey());\n typedOnMouseLeaveRegistry.delete(self.getKey());\n self.__typedOnClicks = undefined;\n self.__typedOnRemoves = undefined;\n self.__typedOnMouseEnters = undefined;\n self.__typedOnMouseLeaves = undefined;\n super.remove.call(self, preserveEmptyParent);\n }\n getOrCreateDOMClickListener(editor) {\n if (!this.__domOnClickListener) {\n this.__domOnClickListener = (event) => {\n this.handleDOMClick(event, editor);\n };\n }\n return this.__domOnClickListener;\n }\n handleDOMClick(event, editor) {\n const typedOnClicks = typedOnClickRegistry.get(this.getKey());\n if (!typedOnClicks)\n return;\n const callbacks = [];\n for (const [type, callbacksById] of Object.entries(typedOnClicks)) {\n for (const [id, callback] of Object.entries(callbacksById)) {\n if (callback)\n callbacks.push([callback, type, id]);\n }\n }\n if (callbacks.length === 0)\n return;\n const textContent = editor.read(() => this.getTextContent());\n for (const [callback, type, id] of callbacks) {\n callback(event, type, id, textContent);\n }\n }\n getOrCreateDOMMouseEnterListener(editor) {\n if (!this.__domOnMouseEnterListener) {\n this.__domOnMouseEnterListener = (event) => {\n this.handleDOMMouseEnter(event, editor);\n };\n }\n return this.__domOnMouseEnterListener;\n }\n handleDOMMouseEnter(event, editor) {\n const typedOnMouseEnters = typedOnMouseEnterRegistry.get(this.getKey());\n if (!typedOnMouseEnters)\n return;\n const callbacks = [];\n for (const [type, callbacksById] of Object.entries(typedOnMouseEnters)) {\n for (const [id, callback] of Object.entries(callbacksById)) {\n if (callback)\n callbacks.push([callback, type, id]);\n }\n }\n if (callbacks.length === 0)\n return;\n const textContent = editor.read(() => this.getTextContent());\n for (const [callback, type, id] of callbacks) {\n callback(event, type, id, textContent);\n }\n }\n getOrCreateDOMMouseLeaveListener(editor) {\n if (!this.__domOnMouseLeaveListener) {\n this.__domOnMouseLeaveListener = (event) => {\n this.handleDOMMouseLeave(event, editor);\n };\n }\n return this.__domOnMouseLeaveListener;\n }\n handleDOMMouseLeave(event, editor) {\n const typedOnMouseLeaves = typedOnMouseLeaveRegistry.get(this.getKey());\n if (!typedOnMouseLeaves)\n return;\n const callbacks = [];\n for (const [type, callbacksById] of Object.entries(typedOnMouseLeaves)) {\n for (const [id, callback] of Object.entries(callbacksById)) {\n if (callback)\n callbacks.push([callback, type, id]);\n }\n }\n if (callbacks.length === 0)\n return;\n const textContent = editor.read(() => this.getTextContent());\n for (const [callback, type, id] of callbacks) {\n callback(event, type, id, textContent);\n }\n }\n ensureOnClickMapMutable() {\n if (this.__typedOnClicks === undefined || this.__typedOnClicks === NO_ON_CLICKS) {\n const existing = typedOnClickRegistry.get(this.getKey());\n this.__typedOnClicks = existing ?? {};\n }\n return this.__typedOnClicks;\n }\n syncTypedOnClicksToRegistry() {\n if (!this.__typedOnClicks || Object.keys(this.__typedOnClicks).length === 0) {\n typedOnClickRegistry.delete(this.getKey());\n if (this.__typedOnClicks && Object.keys(this.__typedOnClicks).length === 0) {\n this.__typedOnClicks = undefined;\n }\n return;\n }\n typedOnClickRegistry.set(this.getKey(), this.__typedOnClicks);\n }\n setOnClickFor(type, id, onClick) {\n assertSafeKey(type);\n assertSafeKey(id);\n const callbacks = this.ensureOnClickMapMutable();\n const typeCallbacks = callbacks[type] ?? (callbacks[type] = {});\n typeCallbacks[id] = onClick;\n this.syncTypedOnClicksToRegistry();\n }\n removeOnClickFor(type, id) {\n if (!this.__typedOnClicks)\n return;\n const typeCallbacks = this.__typedOnClicks[type];\n if (!typeCallbacks)\n return;\n const updatedTypeCallbacks = omitRecordKey(typeCallbacks, id);\n const hasRemainingCallbacks = Object.keys(updatedTypeCallbacks).length > 0;\n if (hasRemainingCallbacks) {\n this.__typedOnClicks = {\n ...this.__typedOnClicks,\n [type]: updatedTypeCallbacks,\n };\n }\n else {\n const remainingCallbacks = omitRecordKey(this.__typedOnClicks, type);\n this.__typedOnClicks =\n Object.keys(remainingCallbacks).length > 0 ? remainingCallbacks : undefined;\n }\n this.syncTypedOnClicksToRegistry();\n }\n pruneTypedOnClicks() {\n if (!this.__typedOnClicks || this.__typedOnClicks === NO_ON_CLICKS) {\n this.__typedOnClicks = undefined;\n this.syncTypedOnClicksToRegistry();\n return;\n }\n const nextTypedOnClicks = {};\n for (const [type, callbacks] of Object.entries(this.__typedOnClicks)) {\n const ids = this.__typedIDs[type];\n if (!ids || ids.length === 0) {\n continue;\n }\n const idSet = new Set(ids);\n const filteredCallbacks = {};\n for (const [id, callback] of Object.entries(callbacks)) {\n if (idSet.has(id)) {\n filteredCallbacks[id] = callback;\n }\n }\n if (Object.keys(filteredCallbacks).length > 0) {\n nextTypedOnClicks[type] = filteredCallbacks;\n }\n }\n this.__typedOnClicks =\n Object.keys(nextTypedOnClicks).length > 0 ? nextTypedOnClicks : undefined;\n this.syncTypedOnClicksToRegistry();\n }\n ensureOnRemoveMapMutable() {\n if (this.__typedOnRemoves === undefined || this.__typedOnRemoves === NO_ON_REMOVES) {\n const existing = typedOnRemoveRegistry.get(this.getKey());\n this.__typedOnRemoves = existing ?? {};\n }\n return this.__typedOnRemoves;\n }\n syncTypedOnRemovesToRegistry() {\n if (!this.__typedOnRemoves || Object.keys(this.__typedOnRemoves).length === 0) {\n typedOnRemoveRegistry.delete(this.getKey());\n if (this.__typedOnRemoves && Object.keys(this.__typedOnRemoves).length === 0) {\n this.__typedOnRemoves = undefined;\n }\n return;\n }\n typedOnRemoveRegistry.set(this.getKey(), this.__typedOnRemoves);\n }\n setOnRemoveFor(type, id, onRemove) {\n assertSafeKey(type);\n assertSafeKey(id);\n const callbacks = this.ensureOnRemoveMapMutable();\n const typeCallbacks = callbacks[type] ?? (callbacks[type] = {});\n typeCallbacks[id] = onRemove;\n this.syncTypedOnRemovesToRegistry();\n }\n removeOnRemoveFor(type, id) {\n if (!this.__typedOnRemoves)\n return;\n const typeCallbacks = this.__typedOnRemoves[type];\n if (!typeCallbacks)\n return;\n const updatedTypeCallbacks = omitRecordKey(typeCallbacks, id);\n const hasRemainingCallbacks = Object.keys(updatedTypeCallbacks).length > 0;\n if (hasRemainingCallbacks) {\n this.__typedOnRemoves = {\n ...this.__typedOnRemoves,\n [type]: updatedTypeCallbacks,\n };\n }\n else {\n const remainingCallbacks = omitRecordKey(this.__typedOnRemoves, type);\n this.__typedOnRemoves =\n Object.keys(remainingCallbacks).length > 0 ? remainingCallbacks : undefined;\n }\n this.syncTypedOnRemovesToRegistry();\n }\n pruneTypedOnRemoves() {\n if (!this.__typedOnRemoves || this.__typedOnRemoves === NO_ON_REMOVES) {\n this.__typedOnRemoves = undefined;\n this.syncTypedOnRemovesToRegistry();\n return;\n }\n const nextTypedOnRemoves = {};\n for (const [type, callbacks] of Object.entries(this.__typedOnRemoves)) {\n const ids = this.__typedIDs[type];\n if (!ids || ids.length === 0)\n continue;\n const idSet = new Set(ids);\n const filteredCallbacks = {};\n for (const [id, callback] of Object.entries(callbacks)) {\n if (idSet.has(id))\n filteredCallbacks[id] = callback;\n }\n if (Object.keys(filteredCallbacks).length > 0) {\n nextTypedOnRemoves[type] = filteredCallbacks;\n }\n }\n this.__typedOnRemoves =\n Object.keys(nextTypedOnRemoves).length > 0 ? nextTypedOnRemoves : undefined;\n this.syncTypedOnRemovesToRegistry();\n }\n ensureOnMouseEnterMapMutable() {\n if (this.__typedOnMouseEnters === undefined ||\n this.__typedOnMouseEnters === NO_ON_MOUSE_ENTERS) {\n const existing = typedOnMouseEnterRegistry.get(this.getKey());\n this.__typedOnMouseEnters = existing ?? {};\n }\n return this.__typedOnMouseEnters;\n }\n syncTypedOnMouseEntersToRegistry() {\n if (!this.__typedOnMouseEnters || Object.keys(this.__typedOnMouseEnters).length === 0) {\n typedOnMouseEnterRegistry.delete(this.getKey());\n if (this.__typedOnMouseEnters && Object.keys(this.__typedOnMouseEnters).length === 0) {\n this.__typedOnMouseEnters = undefined;\n }\n return;\n }\n typedOnMouseEnterRegistry.set(this.getKey(), this.__typedOnMouseEnters);\n }\n setOnMouseEnterFor(type, id, onMouseEnter) {\n assertSafeKey(type);\n assertSafeKey(id);\n const callbacks = this.ensureOnMouseEnterMapMutable();\n const typeCallbacks = callbacks[type] ?? (callbacks[type] = {});\n typeCallbacks[id] = onMouseEnter;\n this.syncTypedOnMouseEntersToRegistry();\n }\n removeOnMouseEnterFor(type, id) {\n if (!this.__typedOnMouseEnters)\n return;\n const typeCallbacks = this.__typedOnMouseEnters[type];\n if (!typeCallbacks)\n return;\n const updatedTypeCallbacks = omitRecordKey(typeCallbacks, id);\n const hasRemainingCallbacks = Object.keys(updatedTypeCallbacks).length > 0;\n if (hasRemainingCallbacks) {\n this.__typedOnMouseEnters = {\n ...this.__typedOnMouseEnters,\n [type]: updatedTypeCallbacks,\n };\n }\n else {\n const remainingCallbacks = omitRecordKey(this.__typedOnMouseEnters, type);\n this.__typedOnMouseEnters =\n Object.keys(remainingCallbacks).length > 0 ? remainingCallbacks : undefined;\n }\n this.syncTypedOnMouseEntersToRegistry();\n }\n pruneTypedOnMouseEnters() {\n if (!this.__typedOnMouseEnters || this.__typedOnMouseEnters === NO_ON_MOUSE_ENTERS) {\n this.__typedOnMouseEnters = undefined;\n this.syncTypedOnMouseEntersToRegistry();\n return;\n }\n const nextTypedOnMouseEnters = {};\n for (const [type, callbacks] of Object.entries(this.__typedOnMouseEnters)) {\n const ids = this.__typedIDs[type];\n if (!ids || ids.length === 0) {\n continue;\n }\n const idSet = new Set(ids);\n const filteredCallbacks = {};\n for (const [id, callback] of Object.entries(callbacks)) {\n if (idSet.has(id)) {\n filteredCallbacks[id] = callback;\n }\n }\n if (Object.keys(filteredCallbacks).length > 0) {\n nextTypedOnMouseEnters[type] = filteredCallbacks;\n }\n }\n this.__typedOnMouseEnters =\n Object.keys(nextTypedOnMouseEnters).length > 0 ? nextTypedOnMouseEnters : undefined;\n this.syncTypedOnMouseEntersToRegistry();\n }\n ensureOnMouseLeaveMapMutable() {\n if (this.__typedOnMouseLeaves === undefined ||\n this.__typedOnMouseLeaves === NO_ON_MOUSE_LEAVES) {\n const existing = typedOnMouseLeaveRegistry.get(this.getKey());\n this.__typedOnMouseLeaves = existing ?? {};\n }\n return this.__typedOnMouseLeaves;\n }\n syncTypedOnMouseLeavesToRegistry() {\n if (!this.__typedOnMouseLeaves || Object.keys(this.__typedOnMouseLeaves).length === 0) {\n typedOnMouseLeaveRegistry.delete(this.getKey());\n if (this.__typedOnMouseLeaves && Object.keys(this.__typedOnMouseLeaves).length === 0) {\n this.__typedOnMouseLeaves = undefined;\n }\n return;\n }\n typedOnMouseLeaveRegistry.set(this.getKey(), this.__typedOnMouseLeaves);\n }\n setOnMouseLeaveFor(type, id, onMouseLeave) {\n assertSafeKey(type);\n assertSafeKey(id);\n const callbacks = this.ensureOnMouseLeaveMapMutable();\n const typeCallbacks = callbacks[type] ?? (callbacks[type] = {});\n typeCallbacks[id] = onMouseLeave;\n this.syncTypedOnMouseLeavesToRegistry();\n }\n removeOnMouseLeaveFor(type, id) {\n if (!this.__typedOnMouseLeaves)\n return;\n const typeCallbacks = this.__typedOnMouseLeaves[type];\n if (!typeCallbacks)\n return;\n const updatedTypeCallbacks = omitRecordKey(typeCallbacks, id);\n const hasRemainingCallbacks = Object.keys(updatedTypeCallbacks).length > 0;\n if (hasRemainingCallbacks) {\n this.__typedOnMouseLeaves = {\n ...this.__typedOnMouseLeaves,\n [type]: updatedTypeCallbacks,\n };\n }\n else {\n const remainingCallbacks = omitRecordKey(this.__typedOnMouseLeaves, type);\n this.__typedOnMouseLeaves =\n Object.keys(remainingCallbacks).length > 0 ? remainingCallbacks : undefined;\n }\n this.syncTypedOnMouseLeavesToRegistry();\n }\n pruneTypedOnMouseLeaves() {\n if (!this.__typedOnMouseLeaves || this.__typedOnMouseLeaves === NO_ON_MOUSE_LEAVES) {\n this.__typedOnMouseLeaves = undefined;\n this.syncTypedOnMouseLeavesToRegistry();\n return;\n }\n const nextTypedOnMouseLeaves = {};\n for (const [type, callbacks] of Object.entries(this.__typedOnMouseLeaves)) {\n const ids = this.__typedIDs[type];\n if (!ids || ids.length === 0) {\n continue;\n }\n const idSet = new Set(ids);\n const filteredCallbacks = {};\n for (const [id, callback] of Object.entries(callbacks)) {\n if (idSet.has(id)) {\n filteredCallbacks[id] = callback;\n }\n }\n if (Object.keys(filteredCallbacks).length > 0) {\n nextTypedOnMouseLeaves[type] = filteredCallbacks;\n }\n }\n this.__typedOnMouseLeaves =\n Object.keys(nextTypedOnMouseLeaves).length > 0 ? nextTypedOnMouseLeaves : undefined;\n this.syncTypedOnMouseLeavesToRegistry();\n }\n invokeOnRemove(type, id, cause) {\n const callbacks = this.getTypedOnRemoves();\n const callback = callbacks[type]?.[id];\n if (!callback)\n return;\n callback(type, id, cause, this.getTextContent());\n this.removeOnRemoveFor(type, id);\n }\n dispatchRemovedIDs(previous, next, cause) {\n const removedPairs = collectRemovedTypeIdPairs(previous, next);\n if (removedPairs.length === 0)\n return;\n for (const [type, id] of removedPairs)\n this.invokeOnRemove(type, id, cause);\n }\n dispatchOnRemoveForTypedIDs(typedIDs, cause) {\n for (const [type, ids] of Object.entries(typedIDs)) {\n if (!ids)\n continue;\n for (const id of ids)\n this.invokeOnRemove(type, id, cause);\n }\n }\n mergeWithAdjacentTypedMarks() {\n if (this.hasNoIDsForEveryType())\n return this;\n let previousSibling = this.getPreviousSibling();\n while ($isTypedMarkNode(previousSibling) &&\n typedIDsAreEqual(previousSibling.getTypedIDs(), this.getTypedIDs())) {\n this.mergeWithPreviousTypedMark(previousSibling);\n previousSibling = this.getPreviousSibling();\n }\n let nextSibling = this.getNextSibling();\n while ($isTypedMarkNode(nextSibling) &&\n typedIDsAreEqual(this.getTypedIDs(), nextSibling.getTypedIDs())) {\n this.mergeWithNextTypedMark(nextSibling);\n nextSibling = this.getNextSibling();\n }\n return this;\n }\n mergeWithPreviousTypedMark(previous) {\n this.mergeOnClicksFrom(previous.getTypedOnClicks());\n this.mergeOnRemovesFrom(previous.getTypedOnRemoves());\n this.mergeOnMouseEntersFrom(previous.getTypedOnMouseEnters());\n this.mergeOnMouseLeavesFrom(previous.getTypedOnMouseLeaves());\n const previousChildren = previous.getChildren();\n if (previousChildren.length > 0) {\n this.splice(0, 0, previousChildren);\n }\n previous.getWritable().__suppressOnRemoveCallbacks = true;\n previous.remove();\n }\n mergeWithNextTypedMark(next) {\n this.mergeOnClicksFrom(next.getTypedOnClicks());\n this.mergeOnRemovesFrom(next.getTypedOnRemoves());\n this.mergeOnMouseEntersFrom(next.getTypedOnMouseEnters());\n this.mergeOnMouseLeavesFrom(next.getTypedOnMouseLeaves());\n const nextChildren = next.getChildren();\n if (nextChildren.length > 0) {\n this.append(...nextChildren);\n }\n next.getWritable().__suppressOnRemoveCallbacks = true;\n next.remove();\n }\n mergeOnClicksFrom(additional) {\n if (!additional || Object.keys(additional).length === 0)\n return;\n const merged = mergeTypedOnClickMaps(this.getTypedOnClicks(), additional);\n if (Object.keys(merged).length === 0)\n return;\n this.setTypedOnClicks(merged);\n }\n mergeOnRemovesFrom(additional) {\n if (!additional || Object.keys(additional).length === 0)\n return;\n const merged = mergeTypedOnRemoveMaps(this.getTypedOnRemoves(), additional);\n if (Object.keys(merged).length === 0)\n return;\n this.setTypedOnRemoves(merged);\n }\n mergeOnMouseEntersFrom(additional) {\n if (!additional || Object.keys(additional).length === 0)\n return;\n const merged = mergeTypedOnMouseEnterMaps(this.getTypedOnMouseEnters(), additional);\n if (Object.keys(merged).length === 0)\n return;\n this.setTypedOnMouseEnters(merged);\n }\n mergeOnMouseLeavesFrom(additional) {\n if (!additional || Object.keys(additional).length === 0)\n return;\n const merged = mergeTypedOnMouseLeaveMaps(this.getTypedOnMouseLeaves(), additional);\n if (Object.keys(merged).length === 0)\n return;\n this.setTypedOnMouseLeaves(merged);\n }\n}\nfunction cloneTypedIDs(typedIds = NO_IDS) {\n const clone = {};\n for (const [type, ids] of Object.entries(typedIds)) {\n assertSafeKey(type);\n if (!Array.isArray(ids)) {\n clone[type] = [];\n continue;\n }\n const clonedIds = [];\n for (const id of ids) {\n assertSafeKey(id);\n clonedIds.push(id);\n }\n clone[type] = clonedIds;\n }\n return clone;\n}\nfunction cloneTypedOnClicks(typedOnClicks) {\n if (!typedOnClicks || typedOnClicks === NO_ON_CLICKS)\n return undefined;\n const clone = {};\n for (const [type, callbacks] of Object.entries(typedOnClicks)) {\n assertSafeKey(type);\n const clonedCallbacks = {};\n for (const [id, callback] of Object.entries(callbacks)) {\n assertSafeKey(id);\n clonedCallbacks[id] = callback;\n }\n if (Object.keys(clonedCallbacks).length > 0)\n clone[type] = clonedCallbacks;\n }\n return Object.keys(clone).length > 0 ? clone : undefined;\n}\nfunction cloneTypedOnRemoves(typedOnRemoves) {\n if (!typedOnRemoves || typedOnRemoves === NO_ON_REMOVES)\n return undefined;\n const clone = {};\n for (const [type, callbacks] of Object.entries(typedOnRemoves)) {\n assertSafeKey(type);\n const clonedCallbacks = {};\n for (const [id, callback] of Object.entries(callbacks)) {\n assertSafeKey(id);\n clonedCallbacks[id] = callback;\n }\n if (Object.keys(clonedCallbacks).length > 0)\n clone[type] = clonedCallbacks;\n }\n return Object.keys(clone).length > 0 ? clone : undefined;\n}\nfunction cloneTypedOnMouseEnters(typedOnMouseEnters) {\n if (!typedOnMouseEnters || typedOnMouseEnters === NO_ON_MOUSE_ENTERS)\n return undefined;\n const clone = {};\n for (const [type, callbacks] of Object.entries(typedOnMouseEnters)) {\n assertSafeKey(type);\n const clonedCallbacks = {};\n for (const [id, callback] of Object.entries(callbacks)) {\n assertSafeKey(id);\n clonedCallbacks[id] = callback;\n }\n if (Object.keys(clonedCallbacks).length > 0)\n clone[type] = clonedCallbacks;\n }\n return Object.keys(clone).length > 0 ? clone : undefined;\n}\nfunction cloneTypedOnMouseLeaves(typedOnMouseLeaves) {\n if (!typedOnMouseLeaves || typedOnMouseLeaves === NO_ON_MOUSE_LEAVES)\n return undefined;\n const clone = {};\n for (const [type, callbacks] of Object.entries(typedOnMouseLeaves)) {\n assertSafeKey(type);\n const clonedCallbacks = {};\n for (const [id, callback] of Object.entries(callbacks)) {\n assertSafeKey(id);\n clonedCallbacks[id] = callback;\n }\n if (Object.keys(clonedCallbacks).length > 0)\n clone[type] = clonedCallbacks;\n }\n return Object.keys(clone).length > 0 ? clone : undefined;\n}\nfunction omitRecordKey(source, keyToOmit) {\n const result = {};\n for (const [currentKey, value] of Object.entries(source)) {\n if (currentKey !== keyToOmit) {\n result[currentKey] = value;\n }\n }\n return result;\n}\nfunction normalizeTypedIDs(source) {\n const normalized = {};\n for (const [type, ids] of Object.entries(source)) {\n if (!ids || ids.length === 0)\n continue;\n normalized[type] = [...ids].sort();\n }\n return normalized;\n}\nfunction collectRemovedTypeIdPairs(previous, next) {\n const removed = [];\n for (const [type, previousIds] of Object.entries(previous)) {\n const nextIds = new Set(next[type] ?? []);\n for (const id of previousIds ?? []) {\n if (!nextIds.has(id))\n removed.push([type, id]);\n }\n }\n return removed;\n}\nfunction typedIDsAreEqual(a, b) {\n const normalizedA = normalizeTypedIDs(a);\n const normalizedB = normalizeTypedIDs(b);\n const typesA = Object.keys(normalizedA).sort();\n const typesB = Object.keys(normalizedB).sort();\n if (typesA.length !== typesB.length)\n return false;\n for (let i = 0; i < typesA.length; i++) {\n const type = typesA[i];\n if (type !== typesB[i])\n return false;\n const idsA = normalizedA[type];\n const idsB = normalizedB[type];\n if (!idsA || !idsB || idsA.length !== idsB.length)\n return false;\n for (let j = 0; j < idsA.length; j++) {\n if (idsA[j] !== idsB[j])\n return false;\n }\n }\n return true;\n}\nfunction mergeTypedOnClickMaps(primary, secondary) {\n const merged = {};\n const types = new Set([...Object.keys(primary), ...Object.keys(secondary)]);\n for (const type of types) {\n const primaryCallbacks = primary[type] ?? {};\n const secondaryCallbacks = secondary[type] ?? {};\n const ids = new Set([...Object.keys(primaryCallbacks), ...Object.keys(secondaryCallbacks)]);\n const mergedCallbacks = {};\n for (const id of ids) {\n const callback = primaryCallbacks[id] ?? secondaryCallbacks[id];\n if (callback)\n mergedCallbacks[id] = callback;\n }\n if (Object.keys(mergedCallbacks).length > 0)\n merged[type] = mergedCallbacks;\n }\n return merged;\n}\nfunction mergeTypedOnRemoveMaps(primary, secondary) {\n const merged = {};\n const types = new Set([...Object.keys(primary), ...Object.keys(secondary)]);\n for (const type of types) {\n const primaryCallbacks = primary[type] ?? {};\n const secondaryCallbacks = secondary[type] ?? {};\n const ids = new Set([...Object.keys(primaryCallbacks), ...Object.keys(secondaryCallbacks)]);\n const mergedCallbacks = {};\n for (const id of ids) {\n const callback = primaryCallbacks[id] ?? secondaryCallbacks[id];\n if (callback)\n mergedCallbacks[id] = callback;\n }\n if (Object.keys(mergedCallbacks).length > 0)\n merged[type] = mergedCallbacks;\n }\n return merged;\n}\nfunction mergeTypedOnMouseEnterMaps(primary, secondary) {\n const merged = {};\n const types = new Set([...Object.keys(primary), ...Object.keys(secondary)]);\n for (const type of types) {\n const primaryCallbacks = primary[type] ?? {};\n const secondaryCallbacks = secondary[type] ?? {};\n const ids = new Set([...Object.keys(primaryCallbacks), ...Object.keys(secondaryCallbacks)]);\n const mergedCallbacks = {};\n for (const id of ids) {\n const callback = primaryCallbacks[id] ?? secondaryCallbacks[id];\n if (callback)\n mergedCallbacks[id] = callback;\n }\n if (Object.keys(mergedCallbacks).length > 0)\n merged[type] = mergedCallbacks;\n }\n return merged;\n}\nfunction mergeTypedOnMouseLeaveMaps(primary, secondary) {\n const merged = {};\n const types = new Set([...Object.keys(primary), ...Object.keys(secondary)]);\n for (const type of types) {\n const primaryCallbacks = primary[type] ?? {};\n const secondaryCallbacks = secondary[type] ?? {};\n const ids = new Set([...Object.keys(primaryCallbacks), ...Object.keys(secondaryCallbacks)]);\n const mergedCallbacks = {};\n for (const id of ids) {\n const callback = primaryCallbacks[id] ?? secondaryCallbacks[id];\n if (callback)\n mergedCallbacks[id] = callback;\n }\n if (Object.keys(mergedCallbacks).length > 0)\n merged[type] = mergedCallbacks;\n }\n return merged;\n}\nfunction getTypedClassName(className, type) {\n return `${className}-${type}`;\n}\n/**\n * Gets the external typed mark type.\n *\n * @remarks\n * This is used to ensure unique identification of external typed marks. Along with reserved types\n * prefaced with 'internal-' for internal typed marks @see {@link COMMENT_MARK_TYPE}.\n */\nexport function externalTypedMarkType(type) {\n return `external-${type}`;\n}\nexport function $createTypedMarkNode(typedIds, typedOnClicks, typedOnRemoves, typedOnMouseEnters, typedOnMouseLeaves) {\n return $applyNodeReplacement(new TypedMarkNode(typedIds, typedOnClicks, typedOnRemoves, typedOnMouseEnters, typedOnMouseLeaves));\n}\nexport function $isTypedMarkNode(node) {\n return node instanceof TypedMarkNode;\n}\nexport function isSerializedTypedMarkNode(node) {\n return node?.type === TypedMarkNode.getType();\n}\n// #region adapted from https://github.com/facebook/lexical/blob/92c47217244f9d3c22a59728633fb41a10420724/packages/lexical-mark/src/index.ts\nexport function $unwrapTypedMarkNode(node) {\n const children = node.getChildren();\n let target = null;\n for (const child of children) {\n if (target === null) {\n node.insertBefore(child);\n }\n else {\n target.insertAfter(child);\n }\n target = child;\n }\n node.remove();\n}\nexport function $wrapSelectionInTypedMarkNode(selection, type, id, onClick, onRemove, onMouseEnter, onMouseLeave) {\n const nodes = selection.getNodes();\n const anchorOffset = selection.anchor.offset;\n const focusOffset = selection.focus.offset;\n const nodesLength = nodes.length;\n const isBackward = selection.isBackward();\n const startOffset = isBackward ? focusOffset : anchorOffset;\n const endOffset = isBackward ? anchorOffset : focusOffset;\n let currentNodeParent;\n let lastCreatedMarkNode;\n // We only want wrap adjacent text nodes, line break nodes and inline element nodes. For decorator\n // nodes and block element nodes, we step out of their boundary and start again after, if there\n // are more nodes.\n for (let i = 0; i < nodesLength; i++) {\n const node = nodes[i];\n if ($isElementNode(lastCreatedMarkNode) && lastCreatedMarkNode.isParentOf(node)) {\n // If the current node is a child of the last created mark node, there is nothing to do here\n continue;\n }\n const isFirstNode = i === 0;\n const isLastNode = i === nodesLength - 1;\n let targetNode = null;\n if ($isTextNode(node)) {\n // Case 1: The node is a text node and we can split it\n const textContentSize = node.getTextContentSize();\n const startTextOffset = isFirstNode ? startOffset : 0;\n const endTextOffset = isLastNode ? endOffset : textContentSize;\n if (startTextOffset === 0 && endTextOffset === 0) {\n continue;\n }\n const splitNodes = node.splitText(startTextOffset, endTextOffset);\n targetNode =\n splitNodes.length > 1 &&\n (splitNodes.length === 3 ||\n (isFirstNode && !isLastNode) ||\n endTextOffset === textContentSize)\n ? splitNodes[1]\n : splitNodes[0];\n }\n else if ($isTypedMarkNode(node)) {\n // Case 2: the node is a mark node and we can ignore it as a target, moving on to its\n // children. Note that when we make a mark inside another mark, it may ultimately be un-nested\n // by a call to `registerNestedElementResolver` somewhere else in the\n // codebase.\n continue;\n }\n else if ($isElementNode(node) && node.isInline()) {\n // Case 3: inline element nodes can be added in their entirety to the new mark\n targetNode = node;\n }\n if (targetNode !== null) {\n // Now that we have a target node for wrapping with a mark, we can run through special cases.\n if (targetNode && targetNode.is(currentNodeParent)) {\n // The current node is a child of the target node to be wrapped, there is nothing to do\n // here.\n continue;\n }\n const parentNode = targetNode.getParent();\n if (parentNode == null || !parentNode.is(currentNodeParent)) {\n // If the parent node is not the current node's parent node, we can clear the last created\n // mark node.\n lastCreatedMarkNode = undefined;\n }\n currentNodeParent = parentNode;\n if (lastCreatedMarkNode === undefined) {\n lastCreatedMarkNode = $createTypedMarkNode();\n lastCreatedMarkNode.addID(type, id, onClick, onRemove, onMouseEnter, onMouseLeave);\n targetNode.insertBefore(lastCreatedMarkNode);\n }\n // Add the target node to be wrapped in the latest created mark node\n lastCreatedMarkNode.append(targetNode);\n }\n else {\n // If we don't have a target node to wrap we can clear our state and continue on with the next\n // node\n currentNodeParent = undefined;\n lastCreatedMarkNode = undefined;\n }\n }\n // Make selection collapsed at the end for comments.\n if (type === COMMENT_MARK_TYPE && $isElementNode(lastCreatedMarkNode)) {\n if (isBackward)\n lastCreatedMarkNode.selectStart();\n else\n lastCreatedMarkNode.selectEnd();\n }\n}\nexport function $getMarkIDs(node, type, offset) {\n let currentNode = node;\n while (currentNode !== null) {\n if ($isTypedMarkNode(currentNode)) {\n return currentNode.getTypedIDs()[type];\n }\n else if ($isTextNode(currentNode) && offset === currentNode.getTextContentSize()) {\n const nextSibling = currentNode.getNextSibling();\n if ($isTypedMarkNode(nextSibling)) {\n return nextSibling.getTypedIDs()[type];\n }\n }\n currentNode = currentNode.getParent();\n }\n return undefined;\n}\n// #endregion\n","import { $applyNodeReplacement, $getSelection, $isNodeSelection, ElementNode, } from \"lexical\";\n/** List of known properties of `MarkerObject` */\nexport const UNKNOWN_MARKER_OBJECT_PROPS = [\"type\", \"marker\", \"content\"];\nexport const UNKNOWN_TAG_NAME = \"unknown\";\nexport const UNKNOWN_VERSION = 1;\n/**\n * `UnknownNode` tags that render inline instead of as a subdued block container: a line-level box\n * in the middle of a sentence would be visibly wrong. These are the two corpus-proven\n * mid-paragraph constructs — both nest INSIDE a ``'s running text in the corpus fixtures\n * (\"optional line break (optbreak)\" and \"cross-reference ref target\"), and\n * `packages/utilities/src/converters/usj/converter-test.data.ts:2571,2581` shows both becoming\n * `UnknownNode`s (tags \"optbreak\" and \"ref\"):\n *\n * - `\\optbreak` — PT9 renders it as a literal `//` token mid-sentence; `createUnknown`\n * (usj-editor.adaptor.ts) renders that token as the node's own display child in editable\n * mode, via `unknownDisplayParts` (unknownUsfm.utils.ts).\n * - `\\ref` — a cross-reference target with real child text that must display inline; it carries\n * no USFM bytes of its own, so it gets no display children at all.\n *\n * Everything else (table/figure/sidebar/periph/...) stays block-level.\n */\nconst INLINE_UNKNOWN_TAGS = new Set([\"optbreak\", \"ref\"]);\nexport class UnknownNode extends ElementNode {\n __tag;\n __marker;\n __unknownAttributes;\n constructor(tag = \"\", marker, unknownAttributes, key) {\n super(key);\n this.__tag = tag;\n this.__marker = marker;\n this.__unknownAttributes = unknownAttributes;\n }\n static getType() {\n return \"unknown\";\n }\n static clone(node) {\n const { __tag, __marker, __unknownAttributes, __key } = node;\n return new UnknownNode(__tag, __marker, __unknownAttributes, __key);\n }\n static importDOM() {\n return {\n [UNKNOWN_TAG_NAME]: (node) => {\n if (!isUnknownElement(node))\n return null;\n return {\n conversion: $convertUnknownElement,\n priority: 1,\n };\n },\n };\n }\n static importJSON(serializedNode) {\n return $createUnknownNode().updateFromJSON(serializedNode);\n }\n updateFromJSON(serializedNode) {\n return super\n .updateFromJSON(serializedNode)\n .setTag(serializedNode.tag)\n .setMarker(serializedNode.marker)\n .setUnknownAttributes(serializedNode.unknownAttributes);\n }\n setTag(tag) {\n if (this.__tag === tag)\n return this;\n const self = this.getWritable();\n self.__tag = tag;\n return self;\n }\n getTag() {\n const self = this.getLatest();\n return self.__tag;\n }\n /**\n * Whether this unknown renders inline (optbreak, ref) rather than as a block box (figure,\n * sidebar, periph, ...). Inline unknowns sit within paragraph prose and carry SIGNIFICANT\n * surrounding whitespace — the spaces Paratext 9 preserves byte-for-byte around `//` — so\n * callers must not add or strip spaces next to them.\n */\n isInlineTag() {\n return INLINE_UNKNOWN_TAGS.has(this.getTag());\n }\n setMarker(marker) {\n if (this.__marker === marker)\n return this;\n const self = this.getWritable();\n self.__marker = marker;\n return self;\n }\n getMarker() {\n const self = this.getLatest();\n return self.__marker;\n }\n setUnknownAttributes(unknownAttributes) {\n const self = this.getWritable();\n self.__unknownAttributes = unknownAttributes;\n return self;\n }\n getUnknownAttributes() {\n const self = this.getLatest();\n return self.__unknownAttributes;\n }\n createDOM() {\n const dom = document.createElement(UNKNOWN_TAG_NAME);\n // data-tag records the UnknownNode's USJ type so importDOM's $convertUnknownElement can read it\n // back on a DOM round-trip. The inline-vs-block CSS treatment is driven by the class chosen from\n // INLINE_UNKNOWN_TAGS below, not by data-tag; optbreak's `//` token renders as a real child text\n // node (see createUnknown in usj-editor.adaptor.ts), not a CSS-generated label.\n dom.setAttribute(\"data-tag\", this.getTag());\n dom.setAttribute(\"data-marker\", this.getMarker() ?? \"\");\n dom.classList.add(this.isInlineTag() ? \"unknown-inline\" : \"unknown-block\");\n // Read-only whole-block: no inline display:none here (that hid the content in every view).\n // Visibility is CSS-mode-gated in usj-nodes.css (hidden by default, revealed as a subdued\n // block/token in standard view's .marker-editable scope). contentEditable=false stops the\n // browser from placing a native caret inside it, so caret navigation skips over the whole\n // node like any decorator node.\n dom.contentEditable = \"false\";\n return dom;\n }\n updateDOM(prevNode, dom) {\n // On a key-reused node whose tag/marker changed, sync the attributes and the inline-vs-block\n // class in place so createDOM's discriminators (data-tag/data-marker, unknown-inline vs\n // unknown-block) don't go stale. tag drives both data-tag and the class; marker drives\n // data-marker (empty string when absent, mirroring createDOM).\n if (prevNode.__tag !== this.__tag) {\n dom.setAttribute(\"data-tag\", this.__tag);\n const inline = this.isInlineTag();\n dom.classList.toggle(\"unknown-inline\", inline);\n dom.classList.toggle(\"unknown-block\", !inline);\n }\n if ((prevNode.__marker ?? \"\") !== (this.__marker ?? \"\"))\n dom.setAttribute(\"data-marker\", this.__marker ?? \"\");\n // Returning false keeps the existing DOM element (updated in place, never recreated — this\n // preserves contentEditable=false and the caret-skip behavior).\n return false;\n }\n exportDOM() {\n return { element: null };\n }\n exportJSON() {\n return {\n ...super.exportJSON(),\n type: this.getType(),\n tag: this.getTag(),\n marker: this.getMarker(),\n unknownAttributes: this.getUnknownAttributes(),\n version: UNKNOWN_VERSION,\n };\n }\n // Mutation\n canBeEmpty() {\n return true;\n }\n isInline() {\n return true;\n }\n extractWithChild() {\n return false;\n }\n // A CHILD-BEARING `UnknownNode` of any kind stays IN the copy. This affects ONLY the\n // `application/x-lexical-editor` (lexical-JSON) flavor, for two independent reasons: an\n // editable-marker view's own `text/html` is not a DOM export at all (Standard view renders the\n // copy walker's USFM bytes — `usfmToClipboardHtml`, platform's whitespaceDisplay.plugin.utils.ts),\n // and where Lexical's exporter DOES run, `$appendNodesToHTML` (`@lexical/html`) computes this same\n // `excludeFromCopy('html')` value but returns early on `exportDOM()`'s unconditional\n // `{element: null}` BEFORE ever consulting it. `'clone'` is never passed by any Lexical-shipped\n // code path in the installed version, so an unconditional `destination !== \"clone\"` would\n // exclude every `UnknownNode` from the lexical-JSON flavor outright.\n //\n // Excluding a node does not drop it silently: `$appendNodesToJSON` HOISTS the excluded node's own\n // children into its parent's list in its place. Every kind's marker and attribute bytes are\n // content-free `ImmutableTypedTextNode` display decorators (`unknownDisplayParts` builds them\n // identically for all of them), so hoisting strands decorators that still `decorate()` their own\n // literal text as loose siblings with no owning wrapper: the pasted document RENDERS the\n // construct's full USFM bytes while its USJ has lost the node and every attribute on it — a\n // convincing display over missing data, which a save then persists with no error.\n //\n // The `getChildrenSize() > 0` guard excludes a node with no children of its OWN — of any kind,\n // and for whatever reason it has none — because that is exactly when `text/plain` also emits\n // nothing for it, and the two carriers must agree. Three shapes reach it, and only the first is\n // a husk: an optbreak whose `//` display child was deleted (`markerEditTier1.utils.ts`'s\n // husk-removal check recognizes it by that same zero-child shape); a content-less construct in\n // an editable-marker view, where the marker/attribute display bytes `createUnknown`\n // (`usj-editor.adaptor.ts`) prepends are themselves children, so only a kind with no display\n // bytes at all can be childless; and, in a HIDDEN-marker view, ANY content-less construct — a\n // caption-less `figure`, an empty `ref`, every optbreak — because `createUnknown` builds no\n // display children there at all. The last is prose-copy semantics rather than a husk: a\n // hidden-marker view copies what it shows, and it shows no bytes for a construct with no\n // content.\n //\n // The guard does NOT, on its own, cover a DIFFERENT carrier-agreement gap: a selection whose\n // ending boundary resolves to an ELEMENT-type point ON this node at offset 0 (touching the\n // wrapper without covering any of its content) still marks a CHILD-BEARING node \"selected\" under\n // Lexical's default `isSelected` (key-membership in `selection.getNodes()`, unaffected by this\n // node's own child count), so `$appendNodesToJSON` would serialize a CHILDLESS placeholder for a\n // node that has children, disagreeing with `text/plain` (`$selectionToUsfmText`, which walks that\n // same `getNodes()` list and correctly emits nothing for this boundary). The `isSelected`\n // override below closes that second gap at its actual source, since `excludeFromCopy` has no\n // visibility into which of a node's children a given selection will include — for every\n // construct whose display bytes lead, which is all of them but `ref`; see that override for the\n // one shape it deliberately does not close, and why.\n excludeFromCopy(destination) {\n return this.getChildrenSize() > 0 ? false : destination !== \"clone\";\n }\n // An `UnknownNode` is only meaningfully \"selected\" (and so only copy-included, per the\n // `excludeFromCopy` guard above) when at least one of its own children is — mirrors\n // `$selectionToUsfmText`'s copy walker so both clipboard carriers agree at the same selection\n // boundary, the same way Lexical's own base `isSelected` (`LexicalNode.prototype.isSelected`)\n // already special-cases an inline DECORATOR node sitting as a parent's last child at an\n // exactly-there boundary point, for the identical reason. Without this, a selection ending\n // exactly at this node's own start (an ElementNode touch-boundary that covers none of its\n // content) still counts the WRAPPER as selected via the default `ElementNode.isSelected`\n // (key-membership in `selection.getNodes()`), while no child is — producing a childless entry in\n // the `application/x-lexical-editor` copy with nothing corresponding to it in `text/plain`.\n //\n // Child MEMBERSHIP is the test, deliberately, and not the narrower \"does the selection cover a\n // child's CONTENT\". The two differ for exactly one shape, because that boundary reaches the\n // children two ways depending on what the first child IS. A construct whose display bytes lead\n // (a `figure`'s `\\fig ` glyph, an optbreak's `//`) starts with a DECORATOR: the element-type\n // point stays one, no child is in `getNodes()`, and both carriers agree. A construct with no\n // display bytes (a `ref`, whose container USFM never carried) starts with a real `TextNode`, and\n // Lexical normalizes that same point into a TEXT point at the child's offset 0 — the child is in\n // `getNodes()` contributing zero characters, so `text/plain` emits nothing for it while this\n // predicate still answers true and the construct rides along in the lexical flavor.\n //\n // Answering false there is WORSE, not better, and measurably so. Excluding the wrapper does not\n // drop it quietly: `$appendNodesToJSON` HOISTS its children in its place, and `createUnknown`\n // stamps `mode:\"token\"` on every text child, which `$sliceSelectedTextNodeContent` refuses to\n // slice — so the zero-width child keeps its full text, the emptied-text reset never fires, and\n // the copy ends up carrying the construct's CHARACTERS with the wrapper and its attributes\n // silently gone. That is the convincing-lie hazard this whole pair exists to prevent. Membership\n // keeps the construct whole, so the lexical flavor is a SUPERSET of `text/plain` at that one\n // boundary rather than a structural loss. That residual stands deliberately: closing it needs a\n // lever Lexical does not offer — `exportNodeToJSON` requires every ElementNode's `exportJSON()`\n // to return a `children` array, so a node cannot say \"drop me AND my children\".\n isSelected(selection) {\n const targetSelection = selection ?? $getSelection();\n if (!targetSelection)\n return false;\n // A `NodeSelection` marks a node selected by its OWN key, never by its children's, so the\n // child-membership test below cannot see one and the base predicate has to answer first.\n // Answering false there does not drop the construct quietly — `$appendNodesToJSON` HOISTS its\n // (also unselected) children in its place — so a node the user selected outright would copy as\n // nothing at all, the same convincing-lie hazard the range case above exists to prevent.\n if ($isNodeSelection(targetSelection) && super.isSelected(targetSelection))\n return true;\n // A caret selects nothing, though its one point puts the child it rests in into `getNodes()`.\n // (`NodeSelection.isCollapsed()` is always false, so this cannot shadow the branch above.)\n if (targetSelection.isCollapsed())\n return false;\n const selectedNodes = targetSelection.getNodes();\n return this.getChildren().some((child) => selectedNodes.some((node) => node.is(child)));\n }\n}\nfunction $convertUnknownElement(element) {\n const tag = element.getAttribute(\"data-tag\") ?? \"\";\n const marker = element.getAttribute(\"data-marker\") ?? \"\";\n const node = $createUnknownNode(tag, marker);\n return { node };\n}\nexport function $createUnknownNode(tag, marker, unknownAttributes) {\n return $applyNodeReplacement(new UnknownNode(tag, marker, unknownAttributes));\n}\nfunction isUnknownElement(node) {\n // `tagName` is upper-cased for HTML-namespace elements, so compare case-insensitively.\n return node?.tagName.toLowerCase() === UNKNOWN_TAG_NAME;\n}\nexport function $isUnknownNode(node) {\n return node instanceof UnknownNode;\n}\nexport function isSerializedUnknownNode(node) {\n return node?.type === UnknownNode.getType();\n}\n","/**\n * `AttributeRunNode` — the ONE sibling `ElementNode` that holds a leaf display owner's attribute\n * display run: a verse's `\\va`/`\\vp` value triplet, or a milestone's attribute value. Both owners\n * are leaves that cannot hold children of their own — a `VerseNode` is itself a `TextNode`, and a\n * `MilestoneNode` is a `DecoratorNode` — unlike a `CharNode`, whose attribute run lives INSIDE the\n * span as ordinary children. `AttributeRunNode` gives those two owners the same \"run lives inside\n * a container\" shape a char span already has, without changing either owner's own node type.\n *\n * Its children are the run's pieces — `MarkerNode` opening/closing (or self-closing) glyphs and,\n * in between, the attribute-tagged value `TextNode` (textType \"attribute\") — the same pieces that\n * used to ride as bare following siblings directly on the owner's parent. The wrapper itself\n * contributes no USFM bytes of its own: it is pure editor-owned structure, never part of a\n * conversion to USJ in its own right — only its children's bytes matter.\n *\n * Ownership is POSITION-derived, not stored: the wrapper directly follows the leaf it belongs to —\n * a verse's `\\va` wrapper follows the `VerseNode` itself, and its `\\vp` wrapper follows the `\\va`\n * wrapper (or the verse, when no `\\va` wrapper exists); a milestone's wrapper follows the\n * `MilestoneNode`. Nothing stores a back-reference — a caller locates a wrapper by walking from\n * its owner, the same sibling-walk shape the pre-wrapper display-run code already used.\n *\n * An `AttributeRunNode` with no children is a transient husk — every piece of its run was deleted,\n * leaving an empty wrapper with nothing left to display. It is not itself meaningful state: the\n * marker-edit engine's deletion driver removes empty wrappers as part of settling a deletion, the\n * same way a milestone with its run entirely gone is itself removed. `canBeEmpty()` reports `true`\n * so Lexical's own empty-element normalization does not race that driver by deleting the wrapper\n * on its own schedule.\n */\nimport { $applyNodeReplacement, ElementNode, } from \"lexical\";\nexport const ATTRIBUTE_RUN_VERSION = 1;\n/** The class every `AttributeRunNode` carries, regardless of `runKind`. */\nexport const ATTRIBUTE_RUN_CLASS_NAME = \"attribute-run\";\n/** The `usfm_` DOM class a runKind's wrapper carries so the stylesheet styles the run\n * exactly like the standalone `char ` span it folds from/unfolds to — or `undefined` for\n * the kinds with no such stylesheet hook (see `createDOM`'s comment). */\nfunction runKindMarkerClass(runKind) {\n return runKind === \"va\" || runKind === \"vp\" || runKind === \"ca\" || runKind === \"cp\"\n ? `usfm_${runKind}`\n : undefined;\n}\nexport class AttributeRunNode extends ElementNode {\n __runKind;\n constructor(runKind, key) {\n super(key);\n this.__runKind = runKind;\n }\n static getType() {\n return \"attribute-run\";\n }\n static clone(node) {\n const { __runKind, __key } = node;\n return new AttributeRunNode(__runKind, __key);\n }\n static importJSON(serializedNode) {\n return $createAttributeRunNode(serializedNode.runKind).updateFromJSON(serializedNode);\n }\n // No HTML shape ever round-trips: `exportDOM` below contributes no wrapper element of its own\n // (a DocumentFragment leaves no markup behind), so there is nothing for a paste to hand back\n // for conversion.\n // Declared explicitly (rather than left unimplemented) so Lexical's dev-mode registration check\n // — which otherwise warns that a custom `exportDOM` needs a matching `importDOM` — recognizes the\n // omission as deliberate.\n static importDOM() {\n return null;\n }\n updateFromJSON(serializedNode) {\n return super.updateFromJSON(serializedNode).setRunKind(serializedNode.runKind);\n }\n setRunKind(runKind) {\n if (this.__runKind === runKind)\n return this;\n const self = this.getWritable();\n self.__runKind = runKind;\n return self;\n }\n getRunKind() {\n const self = this.getLatest();\n return self.__runKind;\n }\n createDOM() {\n const dom = document.createElement(\"span\");\n dom.classList.add(ATTRIBUTE_RUN_CLASS_NAME);\n // va/vp/ca/cp carry a marker-specific class (matching a standalone CharNode's\n // `usfm_` class, per CharNode.test.ts's standalone-attribute-marker pin), so the\n // stylesheet styles the run and the standalone span IDENTICALLY — the run must not look\n // different just because the same bytes currently ride as an attribute. \"milestone\" gets\n // nothing extra — a milestone's marker varies per instance (ts-s, qt1-e, ...) and is not a\n // fixed stylesheet hook — and \"cat\" has no standalone stylesheet styling to match.\n const markerClass = runKindMarkerClass(this.__runKind);\n if (markerClass !== undefined)\n dom.classList.add(markerClass);\n return dom;\n }\n updateDOM(prevNode, dom) {\n // On a key-reused node whose runKind changed, sync the usfm_ class in place so\n // createDOM's discriminator doesn't go stale. `attribute-run` never changes (always present on\n // every runKind), so only the marker-class toggle needs syncing here.\n if (prevNode.__runKind !== this.__runKind) {\n const prevClass = runKindMarkerClass(prevNode.__runKind);\n if (prevClass !== undefined)\n dom.classList.remove(prevClass);\n const nextClass = runKindMarkerClass(this.__runKind);\n if (nextClass !== undefined)\n dom.classList.add(nextClass);\n }\n // Returning false keeps the existing DOM element (updated in place, never recreated — the\n // run-piece children reconcile independently).\n return false;\n }\n exportDOM() {\n // A DocumentFragment rather than null: @lexical/html's $appendNodesToHTML treats a null element\n // as \"skip this subtree\" and never walks the children, which would drop the run's glyphs AND its\n // value text (the \"2\" of `\\va 2\\va*`) from any DOM-export html while getTextContent() keeps them\n // on text/plain. That export is what the HIDDEN-marker views' copy still ships, and what any\n // consumer re-importing html through $generateNodesFromDOM reads; an editable-marker view's own\n // `text/html` is the copy walker's USFM bytes instead and never reaches this method. The\n // fragment exports the children while still contributing no wrapper markup of its own.\n return { element: document.createDocumentFragment() };\n }\n exportJSON() {\n return {\n ...super.exportJSON(),\n type: this.getType(),\n runKind: this.getRunKind(),\n version: ATTRIBUTE_RUN_VERSION,\n };\n }\n // Mutation\n canBeEmpty() {\n return true;\n }\n isInline() {\n return true;\n }\n}\nexport function $createAttributeRunNode(runKind) {\n return $applyNodeReplacement(new AttributeRunNode(runKind));\n}\nexport function $isAttributeRunNode(node) {\n return node instanceof AttributeRunNode;\n}\nexport function isSerializedAttributeRunNode(node) {\n return node?.type === AttributeRunNode.getType();\n}\n","/** Conforms with USJ v3.1 @see https://docs.usfm.bible/usfm/3.1/doc/id.html */\nimport { isValidBookCode } from \"@eten-tech-foundation/scripture-utilities\";\nimport { $applyNodeReplacement, ElementNode, } from \"lexical\";\nexport const BOOK_MARKER = \"id\";\nexport const BOOK_VERSION = 1;\n/** List of known properties of `MarkerObject` */\nexport const BOOK_MARKER_OBJECT_PROPS = [\n \"type\",\n \"marker\",\n \"code\",\n \"content\",\n];\nexport class BookNode extends ElementNode {\n __marker = BOOK_MARKER;\n __code;\n __unknownAttributes;\n constructor(code = \"\", unknownAttributes, key) {\n super(key);\n this.__code = code;\n this.__unknownAttributes = unknownAttributes;\n }\n static getType() {\n return \"book\";\n }\n static clone(node) {\n const { __code, __unknownAttributes, __key } = node;\n return new BookNode(__code, __unknownAttributes, __key);\n }\n static importJSON(serializedNode) {\n const { code } = serializedNode;\n return $createBookNode(code).updateFromJSON(serializedNode);\n }\n static isValidBookCode(code) {\n return isValidBookCode(code);\n }\n updateFromJSON(serializedNode) {\n return super\n .updateFromJSON(serializedNode)\n .setCode(serializedNode.code)\n .setUnknownAttributes(serializedNode.unknownAttributes);\n }\n getMarker() {\n const self = this.getLatest();\n return self.__marker;\n }\n setCode(code) {\n if (this.__code === code)\n return this;\n const self = this.getWritable();\n self.__code = code;\n return self;\n }\n /**\n * Get the book code (ID).\n * @returns the book code (ID).\n */\n getCode() {\n const self = this.getLatest();\n return self.__code;\n }\n setUnknownAttributes(unknownAttributes) {\n const self = this.getWritable();\n self.__unknownAttributes = unknownAttributes;\n return self;\n }\n getUnknownAttributes() {\n const self = this.getLatest();\n return self.__unknownAttributes;\n }\n createDOM() {\n // The `.book` class is targeted by platform gutter-positioning rules in usj-nodes.css\n // alongside `.para` (paragraph-like layout). Changes here that affect the first-child marker\n // node, the data-code attribute, or the element tag may require corresponding CSS updates.\n const dom = document.createElement(\"p\");\n dom.setAttribute(\"data-marker\", this.__marker);\n dom.classList.add(this.__type, `usfm_${this.__marker}`);\n dom.setAttribute(\"data-code\", this.__code);\n return dom;\n }\n updateDOM() {\n // Returning false tells Lexical that this node does not need its\n // DOM element replacing with a new copy from createDOM.\n return false;\n }\n exportJSON() {\n return {\n ...super.exportJSON(),\n type: this.getType(),\n marker: this.getMarker(),\n code: this.getCode(),\n unknownAttributes: this.getUnknownAttributes(),\n version: BOOK_VERSION,\n };\n }\n}\nexport function $createBookNode(code, unknownAttributes) {\n return $applyNodeReplacement(new BookNode(code, unknownAttributes));\n}\nexport function $isBookNode(node) {\n return node instanceof BookNode;\n}\nexport function isSerializedBookNode(node) {\n return node?.type === BookNode.getType();\n}\n","/** Conforms with USJ v3.1 @see https://docs.usfm.bible/usfm/3.1/cv/c.html */\nimport { CHAPTER_CLASS_NAME } from \"./node-constants.js\";\nimport { $applyNodeReplacement, ElementNode, } from \"lexical\";\nexport const CHAPTER_MARKER = \"c\";\nexport const CHAPTER_VERSION = 1;\n/** List of known properties of `MarkerObject` */\nexport const CHAPTER_MARKER_OBJECT_PROPS = [\n \"type\",\n \"marker\",\n \"number\",\n \"sid\",\n \"altnumber\",\n \"pubnumber\",\n \"content\",\n];\nexport class ChapterNode extends ElementNode {\n __marker;\n __number;\n __sid;\n __altnumber;\n __pubnumber;\n __unknownAttributes;\n constructor(chapterNumber = \"\", sid, altnumber, pubnumber, unknownAttributes, key) {\n super(key);\n this.__marker = CHAPTER_MARKER;\n this.__number = chapterNumber;\n this.__sid = sid;\n this.__altnumber = altnumber;\n this.__pubnumber = pubnumber;\n this.__unknownAttributes = unknownAttributes;\n }\n static getType() {\n return \"chapter\";\n }\n static clone(node) {\n const { __number, __sid, __altnumber, __pubnumber, __unknownAttributes, __key } = node;\n return new ChapterNode(__number, __sid, __altnumber, __pubnumber, __unknownAttributes, __key);\n }\n static importJSON(serializedNode) {\n return $createChapterNode().updateFromJSON(serializedNode);\n }\n updateFromJSON(serializedNode) {\n return super\n .updateFromJSON(serializedNode)\n .setMarker(serializedNode.marker)\n .setNumber(serializedNode.number)\n .setSid(serializedNode.sid)\n .setAltnumber(serializedNode.altnumber)\n .setPubnumber(serializedNode.pubnumber)\n .setUnknownAttributes(serializedNode.unknownAttributes);\n }\n setMarker(marker) {\n if (this.__marker === marker)\n return this;\n const self = this.getWritable();\n self.__marker = marker;\n return self;\n }\n getMarker() {\n const self = this.getLatest();\n return self.__marker;\n }\n setNumber(chapterNumber) {\n if (this.__number === chapterNumber)\n return this;\n const self = this.getWritable();\n self.__number = chapterNumber;\n return self;\n }\n getNumber() {\n const self = this.getLatest();\n return self.__number;\n }\n setSid(sid) {\n if (this.__sid === sid)\n return this;\n const self = this.getWritable();\n self.__sid = sid;\n return self;\n }\n getSid() {\n const self = this.getLatest();\n return self.__sid;\n }\n setAltnumber(altnumber) {\n if (this.__altnumber === altnumber)\n return this;\n const self = this.getWritable();\n self.__altnumber = altnumber;\n return self;\n }\n getAltnumber() {\n const self = this.getLatest();\n return self.__altnumber;\n }\n setPubnumber(pubnumber) {\n if (this.__pubnumber === pubnumber)\n return this;\n const self = this.getWritable();\n self.__pubnumber = pubnumber;\n return self;\n }\n getPubnumber() {\n const self = this.getLatest();\n return self.__pubnumber;\n }\n setUnknownAttributes(unknownAttributes) {\n const self = this.getWritable();\n self.__unknownAttributes = unknownAttributes;\n return self;\n }\n getUnknownAttributes() {\n const self = this.getLatest();\n return self.__unknownAttributes;\n }\n createDOM() {\n const dom = document.createElement(\"p\");\n dom.setAttribute(\"data-marker\", this.__marker);\n dom.classList.add(CHAPTER_CLASS_NAME, `usfm_${this.__marker}`);\n dom.setAttribute(\"data-number\", this.__number);\n return dom;\n }\n updateDOM(prevNode, dom) {\n if (prevNode.__number !== this.__number)\n dom.setAttribute(\"data-number\", this.__number);\n // Returning false keeps the existing DOM element (children reconcile independently).\n return false;\n }\n exportJSON() {\n return {\n ...super.exportJSON(),\n type: this.getType(),\n marker: this.getMarker(),\n number: this.getNumber(),\n sid: this.getSid(),\n altnumber: this.getAltnumber(),\n pubnumber: this.getPubnumber(),\n unknownAttributes: this.getUnknownAttributes(),\n version: CHAPTER_VERSION,\n };\n }\n}\nexport function $createChapterNode(chapterNumber, sid, altnumber, pubnumber, unknownAttributes) {\n return $applyNodeReplacement(new ChapterNode(chapterNumber, sid, altnumber, pubnumber, unknownAttributes));\n}\nexport function $isChapterNode(node) {\n return node instanceof ChapterNode;\n}\nexport function isSerializedChapterNode(node) {\n return node?.type === ChapterNode.getType();\n}\n","/** Conforms with USJ v3.1 @see https://docs.usfm.bible/usfm/3.1/char/index.html */\nimport { $applyNodeReplacement, ElementNode, isHTMLElement, } from \"lexical\";\n/** @see https://docs.usfm.bible/usfm/3.1/char/notes/footnote/index.html */\nconst VALID_CHAR_FOOTNOTE_MARKERS = [\n \"fr\",\n \"fq\",\n \"fqa\",\n \"fk\",\n \"ft\",\n \"fl\",\n \"fw\",\n \"fp\",\n \"fv\",\n \"fm\",\n \"fdc\", // Deprecated marker.\n];\n/** @see https://docs.usfm.bible/usfm/3.1/char/notes/crossref/index.html */\nconst VALID_CHAR_CROSS_REFERENCE_MARKERS = [\n \"xo\",\n \"xop\",\n \"xk\",\n \"xq\",\n \"xt\",\n \"xta\",\n \"xot\",\n \"xnt\",\n \"xdc\", // Deprecated marker.\n];\n/** @see https://docs.usfm.bible/usfm/3.1/char/index.html */\nconst VALID_CHAR_MARKERS = [\n // Chapter & Verse\n \"ca\",\n \"cp\",\n \"va\",\n \"vp\",\n // Text Features\n \"add\",\n \"bk\",\n \"dc\",\n \"em\",\n \"jmp\",\n \"k\",\n \"nd\",\n \"ord\",\n \"pn\",\n \"png\",\n \"qt\",\n \"rb\",\n \"rq\",\n // \"ref\", // This has its own tag and is not a Char\n \"sig\",\n \"sls\",\n \"tl\",\n \"w\",\n \"wa\",\n \"wg\",\n \"wh\",\n \"wj\",\n \"addpn\", // Deprecated marker.\n \"pro\", // Deprecated marker.\n // Text Formatting\n \"bd\",\n \"it\",\n \"bdit\",\n \"no\",\n \"sc\",\n \"sup\",\n // Introductions\n \"ior\",\n \"iqt\",\n // Poetry\n \"qac\",\n \"qs\",\n // Lists\n \"litl\",\n \"lik\",\n \"liv\",\n \"liv1\",\n \"liv2\",\n \"liv3\",\n \"liv4\",\n \"liv5\",\n ...VALID_CHAR_FOOTNOTE_MARKERS,\n ...VALID_CHAR_CROSS_REFERENCE_MARKERS,\n];\nexport const CHAR_VERSION = 1;\n/** List of known properties of `MarkerObject` */\nexport const CHAR_MARKER_OBJECT_PROPS = [\"type\", \"marker\", \"content\"];\nexport class CharNode extends ElementNode {\n __marker;\n __unknownAttributes;\n constructor(marker = \"\", unknownAttributes, key) {\n super(key);\n this.__marker = marker;\n this.__unknownAttributes = unknownAttributes;\n }\n static getType() {\n return \"char\";\n }\n static clone(node) {\n const { __marker, __unknownAttributes, __key } = node;\n return new CharNode(__marker, __unknownAttributes, __key);\n }\n static isValidMarker(marker, extraValidMarkers) {\n return (marker !== undefined &&\n (VALID_CHAR_MARKERS.includes(marker) || (extraValidMarkers?.includes(marker) ?? false)));\n }\n static isValidFootnoteMarker(marker) {\n return marker !== undefined && VALID_CHAR_FOOTNOTE_MARKERS.includes(marker);\n }\n static isValidCrossReferenceMarker(marker) {\n return marker !== undefined && VALID_CHAR_CROSS_REFERENCE_MARKERS.includes(marker);\n }\n /**\n * Whether a character marker belongs to the note-content families - footnote or cross-reference.\n *\n * These markers only ever occur inside a `NoteNode`, and unlike every other character marker they\n * are written without a closing marker. Callers branch on this for one reason or the other, so the\n * predicate names the family rather than either consequence; each call site documents which\n * consequence it cares about.\n *\n * @param marker - The character marker to check.\n * @returns `true` if the marker is a footnote or cross-reference marker, `false` otherwise.\n */\n static isNoteContentMarker(marker) {\n return CharNode.isValidFootnoteMarker(marker) || CharNode.isValidCrossReferenceMarker(marker);\n }\n static importDOM() {\n return {\n span: (node) => {\n if (!isCharElement(node))\n return null;\n return {\n conversion: $convertCharElement,\n priority: 1,\n };\n },\n };\n }\n static importJSON(serializedNode) {\n return $createCharNode().updateFromJSON(serializedNode);\n }\n updateFromJSON(serializedNode) {\n return super\n .updateFromJSON(serializedNode)\n .setMarker(serializedNode.marker)\n .setUnknownAttributes(serializedNode.unknownAttributes);\n }\n setMarker(marker) {\n if (this.__marker === marker)\n return this;\n const self = this.getWritable();\n self.__marker = marker;\n return self;\n }\n getMarker() {\n const self = this.getLatest();\n return self.__marker;\n }\n setUnknownAttributes(unknownAttributes) {\n const self = this.getWritable();\n self.__unknownAttributes = unknownAttributes;\n return self;\n }\n getUnknownAttributes() {\n const self = this.getLatest();\n return self.__unknownAttributes;\n }\n createDOM(config) {\n const dom = document.createElement(\"span\");\n applyMarkerToDom(dom, this.__marker, config);\n dom.classList.add(this.__type);\n return dom;\n }\n updateDOM(prevNode, dom, config) {\n // Returning false tells Lexical the element can be reused — but reuse means createDOM does not\n // run again, so a marker change has to be written onto the existing element by hand: the\n // data-marker, the title (gated by showCharMarkerTitles, same as createDOM), and the usfm_*\n // class.\n //\n // Scope: this span's own attributes and classes, nothing else. The synthesized marker children\n // that markerMode \"editable\"/\"visible\" add are not touched here - $setCharNodeMarker\n // (node.utils.ts) is what retargets those, and callers changing a marker on a rendered node\n // should go through it. The collaboration path in delta-apply-update.utils.ts still calls\n // setMarker directly, so it gets this attribute refresh but not the child retargeting.\n //\n // No super.updateDOM() call, unlike ParaNode.updateDOM: ParaNode extends ParagraphNode, which\n // implements it. CharNode extends ElementNode, which does not, so super would reach\n // LexicalNode's base method and throw.\n if (prevNode.__marker !== this.__marker) {\n dom.classList.remove(`usfm_${prevNode.__marker}`);\n // The same writes createDOM makes, so the two paths cannot drift: a reused element ends up\n // indistinguishable from a freshly created one, title gating included.\n applyMarkerToDom(dom, this.__marker, config);\n }\n return false;\n }\n exportDOM(editor) {\n const { element } = super.exportDOM(editor);\n if (element && isHTMLElement(element)) {\n element.setAttribute(\"data-marker\", this.getMarker());\n element.classList.add(this.getType(), `usfm_${this.getMarker()}`);\n }\n return { element };\n }\n exportJSON() {\n return {\n ...super.exportJSON(),\n type: this.getType(),\n marker: this.getMarker(),\n unknownAttributes: this.getUnknownAttributes(),\n version: CHAR_VERSION,\n };\n }\n // Mutation\n insertNewAfter(_selection, restoreSelection) {\n // The continuation span keeps the implicit-close convention when this span has it: splitting\n // an unclosed (closed=\"false\") span yields two unclosed spans — the same structural-state rule\n // as the marker-edit split paths ($splitCharNodeAt, $liftOutOfChar). Other unknownAttributes\n // are deliberately NOT copied (duplicating them would double the `|name=\"value\"` bytes on\n // serialization).\n const isUnclosed = this.getUnknownAttributes()?.closed === \"false\";\n const newElement = $createCharNode(this.getMarker(), isUnclosed ? { closed: \"false\" } : undefined);\n newElement.setDirection(this.getDirection());\n newElement.setFormat(this.getFormatType());\n newElement.setStyle(this.getTextStyle());\n this.insertAfter(newElement, restoreSelection);\n return newElement;\n }\n canBeEmpty() {\n return false;\n }\n isInline() {\n return true;\n }\n}\n/**\n * Write a marker onto a `CharNode`'s rendered span.\n *\n * Shared by `createDOM` and `updateDOM` so the created and the reused element can't drift. Only the\n * `usfm_*` class is added, never removed — `updateDOM` removes the previous marker's class itself,\n * and `createDOM` has no previous marker to remove.\n *\n * @param dom - The span to write to.\n * @param marker - The character marker to apply.\n * @param config - The editor config, read for the `showCharMarkerTitles` theme flag.\n */\nfunction applyMarkerToDom(dom, marker, config) {\n dom.setAttribute(\"data-marker\", marker);\n // Consumers can suppress the per-char marker tooltip via\n // `ViewOptions.showCharMarkerTitles = false` - useful when the marker name shouldn't\n // surface as a browser tooltip on every char span. Default (undefined or true) preserves\n // the marker hint for consumers that want it while authoring USFM.\n if (config.theme?.showCharMarkerTitles !== false) {\n dom.setAttribute(\"title\", marker);\n }\n else {\n // Clear any title a previous render left behind; on a reused element the gate can flip.\n dom.removeAttribute(\"title\");\n }\n dom.classList.add(`usfm_${marker}`);\n}\nfunction $convertCharElement(element) {\n const marker = element.getAttribute(\"data-marker\") ?? \"f\";\n const node = $createCharNode(marker);\n return { node };\n}\nexport function $createCharNode(marker, unknownAttributes) {\n return $applyNodeReplacement(new CharNode(marker, unknownAttributes));\n}\nfunction isCharElement(node) {\n if (!node)\n return false;\n const marker = node.getAttribute(\"data-marker\") ?? \"\";\n return CharNode.isValidMarker(marker) && node.classList.contains(CharNode.getType());\n}\nexport function $isCharNode(node) {\n return node instanceof CharNode;\n}\nexport function isSerializedCharNode(node) {\n return node?.type === CharNode.getType();\n}\n","/** Conforms with USJ v3.1 @see https://docs.usfm.bible/usfm/3.1/cv/c.html */\nimport { $applyNodeReplacement, DecoratorNode, isHTMLElement, } from \"lexical\";\nimport { CHAPTER_CLASS_NAME } from \"./node-constants.js\";\nimport { getVisibleOpenMarkerText } from \"./node.utils.js\";\nexport const IMMUTABLE_CHAPTER_VERSION = 1;\nconst CHAPTER_MARKER = \"c\";\nconst IMMUTABLE_CHAPTER_TAG_NAME = \"span\";\nexport class ImmutableChapterNode extends DecoratorNode {\n __marker;\n __number;\n __showMarker;\n __sid;\n __altnumber;\n __pubnumber;\n __unknownAttributes;\n constructor(chapterNumber = \"\", showMarker = false, sid, altnumber, pubnumber, unknownAttributes, key) {\n super(key);\n this.__marker = CHAPTER_MARKER;\n this.__number = chapterNumber;\n this.__showMarker = showMarker;\n this.__sid = sid;\n this.__altnumber = altnumber;\n this.__pubnumber = pubnumber;\n this.__unknownAttributes = unknownAttributes;\n }\n static getType() {\n return \"immutable-chapter\";\n }\n static clone(node) {\n const { __number, __showMarker, __sid, __altnumber, __pubnumber, __unknownAttributes, __key } = node;\n return new ImmutableChapterNode(__number, __showMarker, __sid, __altnumber, __pubnumber, __unknownAttributes, __key);\n }\n static importDOM() {\n return {\n span: (node) => {\n if (!isImmutableChapterElement(node))\n return null;\n return {\n conversion: $convertImmutableChapterElement,\n priority: 1,\n };\n },\n };\n }\n static importJSON(serializedNode) {\n return $createImmutableChapterNode().updateFromJSON(serializedNode);\n }\n updateFromJSON(serializedNode) {\n return super\n .updateFromJSON(serializedNode)\n .setMarker(serializedNode.marker)\n .setNumber(serializedNode.number)\n .setShowMarker(serializedNode.showMarker)\n .setSid(serializedNode.sid)\n .setAltnumber(serializedNode.altnumber)\n .setPubnumber(serializedNode.pubnumber)\n .setUnknownAttributes(serializedNode.unknownAttributes);\n }\n setMarker(marker) {\n if (this.__marker === marker)\n return this;\n const self = this.getWritable();\n self.__marker = marker;\n return self;\n }\n getMarker() {\n const self = this.getLatest();\n return self.__marker;\n }\n setNumber(chapterNumber) {\n if (this.__number === chapterNumber)\n return this;\n const self = this.getWritable();\n self.__number = chapterNumber;\n return self;\n }\n getNumber() {\n const self = this.getLatest();\n return self.__number;\n }\n setShowMarker(showMarker = false) {\n if (this.__showMarker === showMarker)\n return this;\n const self = this.getWritable();\n self.__showMarker = showMarker;\n return self;\n }\n getShowMarker() {\n const self = this.getLatest();\n return self.__showMarker;\n }\n setSid(sid) {\n if (this.__sid === sid)\n return this;\n const self = this.getWritable();\n self.__sid = sid;\n return self;\n }\n getSid() {\n const self = this.getLatest();\n return self.__sid;\n }\n setAltnumber(altnumber) {\n if (this.__altnumber === altnumber)\n return this;\n const self = this.getWritable();\n self.__altnumber = altnumber;\n return self;\n }\n getAltnumber() {\n const self = this.getLatest();\n return self.__altnumber;\n }\n setPubnumber(pubnumber) {\n if (this.__pubnumber === pubnumber)\n return this;\n const self = this.getWritable();\n self.__pubnumber = pubnumber;\n return self;\n }\n getPubnumber() {\n const self = this.getLatest();\n return self.__pubnumber;\n }\n setUnknownAttributes(unknownAttributes) {\n const self = this.getWritable();\n self.__unknownAttributes = unknownAttributes;\n return self;\n }\n getUnknownAttributes() {\n const self = this.getLatest();\n return self.__unknownAttributes;\n }\n createDOM() {\n const dom = document.createElement(IMMUTABLE_CHAPTER_TAG_NAME);\n dom.setAttribute(\"data-marker\", this.__marker);\n dom.classList.add(CHAPTER_CLASS_NAME, `usfm_${this.__marker}`);\n if (this.__showMarker)\n dom.classList.add(\"marker\");\n dom.setAttribute(\"data-number\", this.__number);\n return dom;\n }\n updateDOM() {\n // Returning false tells Lexical that this node does not need its\n // DOM element replacing with a new copy from createDOM.\n return false;\n }\n exportDOM(editor) {\n const { element } = super.exportDOM(editor);\n if (element && isHTMLElement(element)) {\n element.setAttribute(\"data-marker\", this.getMarker());\n element.classList.add(CHAPTER_CLASS_NAME, `usfm_${this.getMarker()}`);\n element.setAttribute(\"data-number\", this.getNumber());\n }\n return { element };\n }\n /**\n * VISIBLE bytes with STABLE IDENTITY, which is only safe because of where this node lives.\n *\n * `@lexical/react` paints a decorator payload into the node's element through a portal, and\n * Lexical only rebuilds that portal when the payload CHANGES (`reconcileDecorator` bails on\n * `currentDecorators[key] === decorator`, and equal strings always compare equal). So any node\n * whose element is destroyed and re-created while the node itself survives — which is precisely\n * what re-parenting does, since Lexical builds fresh elements for every child of a newly created\n * parent — keeps a portal bound to the OLD, detached element and renders permanently EMPTY.\n *\n * A chapter escapes that only by POSITION: it is a root-level block, and the marker engine's\n * Tier-2 rebuild re-parents preserved nodes only within paragraph-kind blocks, so nothing ever\n * moves a chapter. That invariant is not enforced anywhere. Moving chapter nodes into any\n * rebuilt/re-created container would blank this glyph on screen while the node, the USJ, and the\n * file all stayed correct — a silent rendering loss with no error. If that day comes, render\n * these bytes from `createDOM` instead, the way `ImmutableTypedTextNode` does.\n */\n decorate() {\n return this.getShowMarker()\n ? getVisibleOpenMarkerText(this.getMarker(), this.getNumber())\n : this.getNumber();\n }\n exportJSON() {\n return {\n type: this.getType(),\n marker: this.getMarker(),\n number: this.getNumber(),\n showMarker: this.getShowMarker(),\n sid: this.getSid(),\n altnumber: this.getAltnumber(),\n pubnumber: this.getPubnumber(),\n unknownAttributes: this.getUnknownAttributes(),\n version: IMMUTABLE_CHAPTER_VERSION,\n };\n }\n // Mutation\n isInline() {\n return false;\n }\n isKeyboardSelectable() {\n return false;\n }\n}\nfunction $convertImmutableChapterElement(element) {\n const chapterNumber = element.getAttribute(\"data-number\") ?? \"0\";\n const node = $createImmutableChapterNode(chapterNumber);\n return { node };\n}\nexport function $createImmutableChapterNode(chapterNumber, showMarker, sid, altnumber, pubnumber, unknownAttributes) {\n return $applyNodeReplacement(new ImmutableChapterNode(chapterNumber, showMarker, sid, altnumber, pubnumber, unknownAttributes));\n}\nexport function isImmutableChapterElement(element) {\n if (!element)\n return false;\n return (element.classList.contains(CHAPTER_CLASS_NAME) &&\n element.tagName.toLowerCase() === IMMUTABLE_CHAPTER_TAG_NAME);\n}\nexport function $isImmutableChapterNode(node) {\n return node instanceof ImmutableChapterNode;\n}\nexport function isSerializedImmutableChapterNode(node) {\n return node?.type === ImmutableChapterNode.getType();\n}\n","/** Conforms with USJ v3.1 and adapted from @see https://docs.usfm.bible/usfm/3.1/para/index.html */\nimport { $applyNodeReplacement, ParagraphNode, } from \"lexical\";\nimport { PARA_MARKER_DEFAULT } from \"./node-constants.js\";\nexport const IMPLIED_PARA_VERSION = 1;\nexport class ImpliedParaNode extends ParagraphNode {\n static getType() {\n return \"implied-para\";\n }\n static clone(node) {\n return new ImpliedParaNode(node.__key);\n }\n static importJSON(serializedNode) {\n return $createImpliedParaNode().updateFromJSON(serializedNode);\n }\n getMarker() {\n return PARA_MARKER_DEFAULT;\n }\n exportJSON() {\n return {\n ...super.exportJSON(),\n type: this.getType(),\n version: IMPLIED_PARA_VERSION,\n };\n }\n // Mutation\n insertNewAfter(rangeSelection, restoreSelection) {\n const newElement = $createImpliedParaNode();\n newElement.setTextFormat(rangeSelection.format);\n newElement.setTextStyle(rangeSelection.style);\n newElement.setDirection(this.getDirection());\n newElement.setFormat(this.getFormatType());\n newElement.setStyle(this.getTextStyle());\n this.insertAfter(newElement, restoreSelection);\n return newElement;\n }\n}\nexport function $createImpliedParaNode() {\n return $applyNodeReplacement(new ImpliedParaNode());\n}\nexport function $isImpliedParaNode(node) {\n return node instanceof ImpliedParaNode;\n}\nexport function isSerializedImpliedParaNode(node) {\n return node?.type === ImpliedParaNode.getType();\n}\n","/** Conforms with USJ v3.1 @see https://docs.usfm.bible/usfm/3.1/para/index.html */\nimport { PARA_MARKER_DEFAULT } from \"./node-constants.js\";\nimport { $applyNodeReplacement, ParagraphNode, isHTMLElement, } from \"lexical\";\n/** @see https://docs.usfm.bible/usfm/3.1/para/index.html */\nconst VALID_PARA_MARKERS = [\n // Identification\n \"ide\",\n \"sts\",\n \"rem\",\n \"h\",\n \"toc1\",\n \"toc2\",\n \"toc3\",\n \"toca1\",\n \"toca2\",\n \"toca3\",\n // Introductions\n \"imt\",\n \"imt1\",\n \"imt2\",\n \"imt3\",\n \"imt4\",\n \"is\",\n \"is1\",\n \"is2\",\n \"ip\",\n \"ipi\",\n \"im\",\n \"imi\",\n \"ipq\",\n \"imq\",\n \"ipr\",\n \"iq\",\n \"iq1\",\n \"iq2\",\n \"iq3\",\n \"ili\",\n \"ili1\",\n \"ili2\",\n \"ib\",\n \"iot\",\n \"io\",\n \"io1\",\n \"io2\",\n \"io3\",\n \"io4\",\n \"iex\",\n \"imte\",\n \"imte1\",\n \"imte2\",\n \"ie\",\n // Titles and Headings\n \"mt\",\n \"mt1\",\n \"mt2\",\n \"mt3\",\n \"mt4\",\n \"mte\",\n \"mte1\",\n \"mte2\",\n \"cl\",\n \"cd\",\n \"ms\",\n \"ms1\",\n \"ms2\",\n \"ms3\",\n \"mr\",\n \"s\",\n \"s1\",\n \"s2\",\n \"s3\",\n \"s4\",\n \"sr\",\n \"r\",\n \"d\",\n \"sp\",\n \"sd\",\n \"sd1\",\n \"sd2\",\n \"sd3\",\n \"sd4\",\n // Body Paragraphs\n PARA_MARKER_DEFAULT,\n \"m\",\n \"po\",\n \"cls\",\n \"pr\",\n \"pc\",\n \"pm\",\n \"pmo\",\n \"pmc\",\n \"pmr\",\n \"pi\",\n \"pi1\",\n \"pi2\",\n \"pi3\",\n \"mi\",\n \"lit\",\n \"nb\",\n \"ph\", // Deprecated marker.\n \"ph1\", // Deprecated marker.\n \"ph2\", // Deprecated marker.\n \"ph3\", // Deprecated marker.\n // Poetry\n \"q\",\n \"q1\",\n \"q2\",\n \"q3\",\n \"q4\",\n \"qr\",\n \"qc\",\n \"qa\",\n \"qm\",\n \"qm1\",\n \"qm2\",\n \"qm3\",\n \"qd\",\n \"b\",\n // Lists\n \"lh\",\n \"li\",\n \"li1\",\n \"li2\",\n \"li3\",\n \"li4\",\n \"lf\",\n \"lim\",\n \"lim1\",\n \"lim2\",\n \"lim3\",\n \"lim4\",\n // Breaks - see https://docs.usfm.bible/usfm/3.1/char/breaks/pb.html\n \"pb\",\n];\nexport const PARA_VERSION = 1;\n/** List of known properties of `MarkerObject` */\nexport const PARA_MARKER_OBJECT_PROPS = [\"type\", \"marker\", \"content\"];\nexport class ParaNode extends ParagraphNode {\n __marker;\n __unknownAttributes;\n constructor(marker = PARA_MARKER_DEFAULT, unknownAttributes, key) {\n super(key);\n this.__marker = marker;\n this.__unknownAttributes = unknownAttributes;\n }\n static getType() {\n return \"para\";\n }\n static clone(node) {\n const { __marker, __unknownAttributes, __key } = node;\n return new ParaNode(__marker, __unknownAttributes, __key);\n }\n static isValidMarker(marker, extraValidMarkers) {\n return (marker !== undefined &&\n (VALID_PARA_MARKERS.includes(marker) ||\n (extraValidMarkers?.includes(marker) ?? false)));\n }\n static importDOM() {\n return {\n p: () => ({\n conversion: $convertParaElement,\n priority: 1,\n }),\n };\n }\n static importJSON(serializedNode) {\n return $createParaNode().updateFromJSON(serializedNode);\n }\n updateFromJSON(serializedNode) {\n return super\n .updateFromJSON(serializedNode)\n .setMarker(serializedNode.marker)\n .setUnknownAttributes(serializedNode.unknownAttributes);\n }\n setMarker(marker) {\n if (this.__marker === marker)\n return this;\n const self = this.getWritable();\n self.__marker = marker;\n return self;\n }\n getMarker() {\n const self = this.getLatest();\n return self.__marker;\n }\n setUnknownAttributes(unknownAttributes) {\n const self = this.getWritable();\n self.__unknownAttributes = unknownAttributes;\n return self;\n }\n getUnknownAttributes() {\n const self = this.getLatest();\n return self.__unknownAttributes;\n }\n createDOM() {\n // Define the DOM element here\n const dom = document.createElement(\"p\");\n dom.setAttribute(\"data-marker\", this.__marker);\n dom.classList.add(this.__type, `usfm_${this.__marker}`);\n return dom;\n }\n updateDOM(prevNode, dom, config) {\n const recreate = super.updateDOM(prevNode, dom, config);\n if (!recreate && prevNode.__marker !== this.__marker) {\n dom.setAttribute(\"data-marker\", this.__marker);\n dom.classList.remove(`usfm_${prevNode.__marker}`);\n dom.classList.add(`usfm_${this.__marker}`);\n }\n return recreate;\n }\n exportDOM(editor) {\n const { element } = super.exportDOM(editor);\n if (element && isHTMLElement(element)) {\n element.setAttribute(\"data-marker\", this.getMarker());\n element.classList.add(this.getType(), `usfm_${this.getMarker()}`);\n }\n return { element };\n }\n exportJSON() {\n return {\n ...super.exportJSON(),\n type: this.getType(),\n marker: this.getMarker(),\n unknownAttributes: this.getUnknownAttributes(),\n version: PARA_VERSION,\n };\n }\n // Mutation\n insertNewAfter(rangeSelection, restoreSelection) {\n const newElement = $createParaNode(this.getMarker());\n newElement.setTextFormat(rangeSelection.format);\n newElement.setTextStyle(rangeSelection.style);\n newElement.setDirection(this.getDirection());\n newElement.setFormat(this.getFormatType());\n newElement.setStyle(this.getTextStyle());\n this.insertAfter(newElement, restoreSelection);\n return newElement;\n }\n}\nfunction $convertParaElement(element) {\n const marker = element.getAttribute(\"data-marker\") ?? undefined;\n const node = $createParaNode(marker);\n if (element.style) {\n node.setFormat(element.style.textAlign);\n const indent = parseInt(element.style.textIndent, 10) / 20;\n if (indent > 0) {\n node.setIndent(indent);\n }\n }\n return { node };\n}\nexport function $createParaNode(marker, unknownAttributes) {\n return $applyNodeReplacement(new ParaNode(marker, unknownAttributes));\n}\nexport function $isParaNode(node) {\n return node instanceof ParaNode;\n}\nexport function isSerializedParaNode(node) {\n return node?.type === ParaNode.getType();\n}\n","/** Conforms with USJ v3.1 @see https://docs.usfm.bible/usfm/3.1/cv/v.html */\nimport { VERSE_CLASS_NAME } from \"./node-constants.js\";\nimport { $applyNodeReplacement, TextNode, } from \"lexical\";\nexport const VERSE_MARKER = \"v\";\nexport const VERSE_VERSION = 1;\n/** List of known properties of `MarkerObject` */\nexport const VERSE_MARKER_OBJECT_PROPS = [\n \"type\",\n \"marker\",\n \"number\",\n \"sid\",\n \"altnumber\",\n \"pubnumber\",\n \"content\",\n];\nexport class VerseNode extends TextNode {\n __marker;\n __number;\n __sid;\n __altnumber;\n __pubnumber;\n __unknownAttributes;\n constructor(verseNumber = \"\", text, sid, altnumber, pubnumber, unknownAttributes, key) {\n super(text ?? verseNumber, key);\n this.__marker = VERSE_MARKER;\n this.__number = verseNumber;\n this.__sid = sid;\n this.__altnumber = altnumber;\n this.__pubnumber = pubnumber;\n this.__unknownAttributes = unknownAttributes;\n }\n static getType() {\n return \"verse\";\n }\n static clone(node) {\n const { __number, __text, __sid, __altnumber, __pubnumber, __unknownAttributes, __key } = node;\n return new VerseNode(__number, __text, __sid, __altnumber, __pubnumber, __unknownAttributes, __key);\n }\n static importJSON(serializedNode) {\n return $createVerseNode().updateFromJSON(serializedNode);\n }\n updateFromJSON(serializedNode) {\n return super\n .updateFromJSON(serializedNode)\n .setMarker(serializedNode.marker)\n .setNumber(serializedNode.number)\n .setSid(serializedNode.sid)\n .setAltnumber(serializedNode.altnumber)\n .setPubnumber(serializedNode.pubnumber)\n .setUnknownAttributes(serializedNode.unknownAttributes);\n }\n setMarker(marker) {\n if (this.__marker === marker)\n return this;\n const self = this.getWritable();\n self.__marker = marker;\n return self;\n }\n getMarker() {\n const self = this.getLatest();\n return self.__marker;\n }\n setNumber(verseNumber) {\n if (this.__number === verseNumber)\n return this;\n const self = this.getWritable();\n self.__number = verseNumber;\n return self;\n }\n getNumber() {\n const self = this.getLatest();\n return self.__number;\n }\n setSid(sid) {\n if (this.__sid === sid)\n return this;\n const self = this.getWritable();\n self.__sid = sid;\n return self;\n }\n getSid() {\n const self = this.getLatest();\n return self.__sid;\n }\n setAltnumber(altnumber) {\n if (this.__altnumber === altnumber)\n return this;\n const self = this.getWritable();\n self.__altnumber = altnumber;\n return self;\n }\n getAltnumber() {\n const self = this.getLatest();\n return self.__altnumber;\n }\n setPubnumber(pubnumber) {\n if (this.__pubnumber === pubnumber)\n return this;\n const self = this.getWritable();\n self.__pubnumber = pubnumber;\n return self;\n }\n getPubnumber() {\n const self = this.getLatest();\n return self.__pubnumber;\n }\n setUnknownAttributes(unknownAttributes) {\n const self = this.getWritable();\n self.__unknownAttributes = unknownAttributes;\n return self;\n }\n getUnknownAttributes() {\n const self = this.getLatest();\n return self.__unknownAttributes;\n }\n createDOM(config) {\n const dom = super.createDOM(config);\n dom.setAttribute(\"data-marker\", this.__marker);\n dom.classList.add(VERSE_CLASS_NAME, `usfm_${this.__marker}`);\n dom.setAttribute(\"data-number\", this.__number);\n return dom;\n }\n updateDOM(prevNode, dom, config) {\n const recreate = super.updateDOM(prevNode, dom, config);\n if (!recreate && prevNode.__number !== this.__number)\n dom.setAttribute(\"data-number\", this.__number);\n return recreate;\n }\n exportJSON() {\n return {\n ...super.exportJSON(),\n type: this.getType(),\n marker: this.getMarker(),\n number: this.getNumber(),\n sid: this.getSid(),\n altnumber: this.getAltnumber(),\n pubnumber: this.getPubnumber(),\n unknownAttributes: this.getUnknownAttributes(),\n version: VERSE_VERSION,\n };\n }\n}\nexport function $createVerseNode(verseNumber, text, sid, altnumber, pubnumber, unknownAttributes) {\n return $applyNodeReplacement(new VerseNode(verseNumber, text, sid, altnumber, pubnumber, unknownAttributes));\n}\nexport function $isVerseNode(node) {\n return node instanceof VerseNode;\n}\nexport function isSerializedVerseNode(node) {\n return node?.type === VerseNode.getType();\n}\n","export const ZERO_WIDTH_SPACE = \"\\u200B\";\nexport const CARET = \"\\u2038\";\nexport const CURSOR_PLACEHOLDER_CHAR = ZERO_WIDTH_SPACE;\nexport var CursorMovementDirection;\n(function (CursorMovementDirection) {\n CursorMovementDirection[\"LEFT\"] = \"left\";\n CursorMovementDirection[\"RIGHT\"] = \"right\";\n})(CursorMovementDirection || (CursorMovementDirection = {}));\nexport var CharSelectionOffset;\n(function (CharSelectionOffset) {\n CharSelectionOffset[CharSelectionOffset[\"BEFORE\"] = 0] = \"BEFORE\";\n CharSelectionOffset[CharSelectionOffset[\"AFTER\"] = 1] = \"AFTER\";\n})(CharSelectionOffset || (CharSelectionOffset = {}));\n","import { $createTextNode, $isElementNode, $isRootNode, $isTextNode, } from \"lexical\";\nimport { CURSOR_PLACEHOLDER_CHAR } from \"./constants.js\";\nimport { CursorPosition } from \"./CursorSelectionContext.js\";\nexport function $createCursorPlaceholderNode() {\n return $createTextNode(CURSOR_PLACEHOLDER_CHAR);\n}\nexport function $insertCursorPlaceholder(node, position, restoreSelection = false) {\n const cursorPlaceholderNode = $createCursorPlaceholderNode();\n if (position === CursorPosition.Start)\n node.insertBefore(cursorPlaceholderNode, restoreSelection);\n else\n node.insertAfter(cursorPlaceholderNode, restoreSelection);\n return cursorPlaceholderNode;\n}\nexport function $removeCursorPlaceholder(node) {\n const textContent = node.getTextContent();\n node.setTextContent(textContent.replaceAll(CURSOR_PLACEHOLDER_CHAR, \"\"));\n}\n/**\n * Whether `text` is a bare cursor host: non-empty but made up entirely of placeholder characters.\n * A zero-width space embedded in real text (e.g. a Thai/Khmer line break) is NOT placeholder-only,\n * so it is preserved. State consumers (serializers, OT positions, content indexes) use this to\n * treat a transient host as if it were not there.\n *\n * @param text - The text content to test.\n * @returns `true` when `text` is non-empty and every character is a cursor placeholder.\n */\nexport function isCursorPlaceholderOnly(text) {\n // Fast path: the common case (no placeholder at all) short-circuits before any allocation, so\n // this stays cheap when called per text node in serialization hot paths.\n return (text.length > 0 &&\n text.includes(CURSOR_PLACEHOLDER_CHAR) &&\n text.replaceAll(CURSOR_PLACEHOLDER_CHAR, \"\") === \"\");\n}\n/**\n * Whether `node` is a text node holding only cursor placeholder(s) — see {@link isCursorPlaceholderOnly}.\n *\n * @param node - The node to test; may be `null`/`undefined`.\n * @returns `true` when `node` is a `TextNode` whose content is entirely cursor placeholders.\n */\nexport function $isCursorPlaceholderOnlyText(node) {\n return $isTextNode(node) && isCursorPlaceholderOnly(node.getTextContent());\n}\nexport function $getValidAncestor(node, cursor, canHavePlaceholder) {\n const ancestor = node.getParent();\n if (!ancestor || !$isElementNode(ancestor) || !ancestor.isInline()) {\n return { ancestor: null, ancestorSibling: null };\n }\n const ancestorParent = ancestor.getParent();\n if (!ancestorParent || $isRootNode(ancestorParent)) {\n return { ancestor: null, ancestorSibling: null };\n }\n const parentCanHavePlaceholder = canHavePlaceholder(ancestorParent);\n const canHavePlaceholderAsSibling = cursor.isMovingRight\n ? ancestor.canInsertTextAfter()\n : ancestor.canInsertTextBefore();\n const canInsert = parentCanHavePlaceholder && canHavePlaceholderAsSibling;\n const ancestorSibling = cursor.isMovingRight\n ? ancestor.getNextSibling()\n : ancestor.getPreviousSibling();\n if (!ancestorSibling && !canInsert) {\n return $getValidAncestor(ancestor, cursor, canHavePlaceholder);\n }\n if ($isTextNode(ancestorSibling) && !canInsert) {\n return { ancestor: null, ancestorSibling: null };\n }\n return { ancestor, ancestorSibling };\n}\nexport function $findDescendantEligibleForPlaceholder(node, cursor, canHavePlaceholder) {\n if (canHavePlaceholder(node)) {\n return node;\n }\n if ($isElementNode(node)) {\n const child = cursor.isMovingRight ? node.getFirstChild() : node.getLastChild();\n if (child) {\n return $findDescendantEligibleForPlaceholder(child, cursor, canHavePlaceholder);\n }\n }\n return null;\n}\n","/** Utility functions for editor nodes */\nimport { MARKER_OBJECT_PROPS } from \"@eten-tech-foundation/scripture-utilities\";\nimport { $findMatchingParent } from \"@lexical/utils\";\nimport { $createTextNode, $getCommonAncestor, $getSelection, $getState, $isElementNode, $isLineBreakNode, $isRangeSelection, $isTextNode, $setState, NODE_STATE_KEY, TextNode, } from \"lexical\";\nimport { charIdState, MARKER_TRAILING_SPACE_TEXT_TYPE, textTypeState, } from \"../collab/delta.state.js\";\nimport { $isImmutableTypedTextNode, isSerializedImmutableTypedTextNode, } from \"../features/ImmutableTypedTextNode.js\";\nimport { $isMarkerNode, isSerializedMarkerNode } from \"../features/MarkerNode.js\";\nimport { $isTypedMarkNode } from \"../features/TypedMarkNode.js\";\nimport { $isUnknownNode } from \"../features/UnknownNode.js\";\nimport { $isAttributeRunNode } from \"./AttributeRunNode.js\";\nimport { $isBookNode } from \"./BookNode.js\";\nimport { $isChapterNode, isSerializedChapterNode, } from \"./ChapterNode.js\";\nimport { $isCharNode, CharNode, isSerializedCharNode } from \"./CharNode.js\";\nimport { $isImmutableChapterNode, isSerializedImmutableChapterNode, } from \"./ImmutableChapterNode.js\";\nimport { $isImpliedParaNode, isSerializedImpliedParaNode, } from \"./ImpliedParaNode.js\";\nimport { $isMilestoneNode } from \"./MilestoneNode.js\";\nimport { $isNoteNode } from \"./NoteNode.js\";\nimport { $isParaNode, isSerializedParaNode } from \"./ParaNode.js\";\nimport { $isVerseNode } from \"./VerseNode.js\";\nimport { EMPTY_CHAR_PLACEHOLDER_TEXT, NBSP } from \"./node-constants.js\";\nimport { isCursorPlaceholderOnly } from \"../../plugins/CursorHandler/index.js\";\n/** RegEx to test for a string only containing digits. */\nconst ONLY_DIGITS_TEST = /^\\d+$/;\n/**\n * Check if the marker is valid and numbered.\n * @param marker - Marker to check.\n * @param numberedMarkers - List of valid numbered markers ('#' removed).\n * @returns true if the marker is a valid numbered marker, false otherwise.\n */\nexport function isValidNumberedMarker(marker, numberedMarkers) {\n if (!marker)\n return false;\n // Starts with a valid numbered marker.\n const numberedMarker = numberedMarkers.find((markerNumbered) => marker.startsWith(markerNumbered));\n if (!numberedMarker)\n return false;\n // Ends with a number.\n const maybeNumber = marker.slice(numberedMarker.length);\n return ONLY_DIGITS_TEST.test(maybeNumber);\n}\n/**\n * Checks if the given node is a SerializedChapterNode or SerializedImmutableChapterNode.\n * @param node - The serialized node to check.\n * @returns `true` if the node is a SerializedChapterNode or SerializedImmutableChapterNode, `false` otherwise.\n */\nexport function isSomeSerializedChapterNode(node) {\n return isSerializedChapterNode(node) || isSerializedImmutableChapterNode(node);\n}\n/**\n * Checks if the given node is a ChapterNode or ImmutableChapterNode.\n * @param node - The node to check.\n * @returns `true` if the node is a ChapterNode or ImmutableChapterNode, `false` otherwise.\n */\nexport function $isSomeChapterNode(node) {\n return $isChapterNode(node) || $isImmutableChapterNode(node);\n}\n/**\n * Finds the chapter node with the given chapter number amongst the nodes.\n * @param nodes - Nodes to look in.\n * @param chapterNum - Chapter number to look for.\n * @returns the chapter node if found, `undefined` otherwise.\n */\nexport function $findChapter(nodes, chapterNum) {\n return nodes.find((node) => $isSomeChapterNode(node) && node.getNumber() === chapterNum.toString());\n}\n/**\n * Finds the next chapter.\n * @param nodes - Nodes to look in.\n * @param isCurrentChapterAtFirstNode - If `true` ignore the first node.\n * @returns the next chapter node if found, `undefined` otherwise.\n */\nexport function $findNextChapter(nodes, isCurrentChapterAtFirstNode = false) {\n return nodes.find((node, index) => (!isCurrentChapterAtFirstNode || index > 0) && $isSomeChapterNode(node));\n}\n/**\n * Finds the nearest previous node by checking the node's previous sibling, then walking up\n * through ancestors and checking their previous siblings. Stops at root.\n * @param node - Node to start from.\n * @returns the nearest previous node, or `undefined` if none exists.\n */\nexport function $findNearestPreviousNode(node) {\n let current = node;\n while (current && current.getParent() !== null) {\n const prev = current.getPreviousSibling();\n if (prev)\n return prev;\n current = current.getParent();\n }\n return undefined;\n}\n/**\n * Find the chapter that this node is in.\n * @param node - Node to find the chapter it's in.\n * @returns the chapter node if found, `undefined` otherwise.\n */\nexport function $findThisChapter(node) {\n if (!node)\n return undefined;\n // is this node a chapter\n if ($isSomeChapterNode(node))\n return node;\n // is the chapter a previous top level sibling\n let previousSibling = node.getTopLevelElement()?.getPreviousSibling();\n while (previousSibling && !$isSomeChapterNode(previousSibling)) {\n previousSibling = previousSibling.getPreviousSibling();\n }\n if (previousSibling && $isSomeChapterNode(previousSibling))\n return previousSibling;\n return undefined;\n}\n/**\n * Traverses up the node tree from startNode (itself included) to find the first ancestor NoteNode.\n * A named convenience over `$findMatchingParent` — the one shared ancestor-walk.\n * @param startNode - The node to start the upward search from.\n * @returns The first ancestor NoteNode found, or `undefined` if none exists before the root.\n */\nexport function $findFirstAncestorNoteNode(startNode) {\n return $findMatchingParent(startNode, $isNoteNode) ?? undefined;\n}\n/**\n * Checks if the node has a `getMarker` method. Excludes React nodes - consider using\n * `$isReactNodeWithMarker` instead.\n * @param node - LexicalNode to check.\n * @returns `true` if the node has a `getMarker` method, `false` otherwise.\n */\nexport function $isNodeWithMarker(node) {\n return ($isBookNode(node) ||\n $isChapterNode(node) ||\n $isCharNode(node) ||\n $isImmutableChapterNode(node) ||\n $isImpliedParaNode(node) ||\n $isMilestoneNode(node) ||\n $isParaNode(node) ||\n $isNoteNode(node) ||\n $isVerseNode(node) ||\n $isUnknownNode(node)\n // ImmutableUnmatchedNode & MarkerNode also have the `getMarker` method but they left out for\n // now until we know we need them.\n );\n}\n/**\n * Get the next node in the document tree.\n * @param selection - The current selection to get the next node from.\n * @returns The next node or null if there is no next node.\n */\nexport function $getNextNode(selection) {\n if (selection.anchor.type === \"element\") {\n const anchorNode = selection.anchor.getNode();\n const offset = selection.anchor.offset;\n if (offset < anchorNode.getChildrenSize())\n return anchorNode.getChildAtIndex(offset);\n }\n const anchorNode = selection.anchor.getNode();\n return anchorNode.getNextSibling() ?? anchorNode.getParent()?.getNextSibling() ?? null;\n}\n/**\n * Get the previous node in the document tree.\n * @param selection - The current selection to get the previous node from.\n * @returns The previous node or null if there is no previous node.\n */\nexport function $getPreviousNode(selection) {\n const offset = selection.anchor.offset;\n if (selection.anchor.type === \"element\" && offset > 0) {\n const anchorNode = selection.anchor.getNode();\n return anchorNode.getChildAtIndex(offset - 1);\n }\n const anchorNode = selection.anchor.getNode();\n return anchorNode.getPreviousSibling() ?? anchorNode.getParent()?.getPreviousSibling() ?? null;\n}\n/**\n * Type guard to check if a node is para-like. Para-like nodes have an OT length of 1 that is\n * counted on its close (rather than its open).\n */\nexport function $isParaLikeNode(node) {\n return $isSomeParaNode(node) || $isBookNode(node);\n}\n/**\n * Checks if the given node is a ParaNode or ImpliedParaNode.\n * @param node - The node to check.\n * @returns `true` if the node is a ParaNode or ImpliedParaNode, `false` otherwise.\n */\nexport function $isSomeParaNode(node) {\n return $isParaNode(node) || $isImpliedParaNode(node);\n}\n/**\n * Checks if the given serialized node is a SerializedParaNode or SerializedImpliedParaNode.\n * @param node - The serialized node to check.\n * @returns `true` if the node is a SerializedParaNode or SerializedImpliedParaNode, `false`\n * otherwise.\n */\nexport function isSomeSerializedParaNode(node) {\n return isSerializedParaNode(node) || isSerializedImpliedParaNode(node);\n}\n/**\n * Check if a node is a descendant of a potential ancestor node.\n *\n * Deliberately NOT delegated to `$findMatchingParent`: this walk excludes the starting node (a\n * node is not its own descendant) and must be able to match the RootNode's key, which\n * `$findMatchingParent` never tests.\n *\n * @param node - The node to check.\n * @param ancestorKey - The key of the potential ancestor node.\n * @returns `true` if the node is a descendant of the ancestor, `false` otherwise.\n */\nexport function $isDescendantOf(node, ancestorKey) {\n let parent = node.getParent();\n while (parent) {\n if (parent.getKey() === ancestorKey)\n return true;\n parent = parent.getParent();\n }\n return false;\n}\n/**\n * Check if the given char attributes are the same as the ones in the CharNode.\n * @param charAttributes - The char attributes to compare.\n * @param charNode - The character node to compare against.\n * @returns `true` if the attributes are the same, `false` otherwise.\n */\nexport function $hasSameCharAttributes(charAttributes, charNode) {\n const charNodeCid = $getState(charNode, charIdState);\n const bothHaveCid = !!(charAttributes.cid && charNodeCid);\n const bothHaveNoCid = !charAttributes.cid && !charNodeCid;\n return (charAttributes.style === charNode.getMarker() &&\n (bothHaveNoCid || (bothHaveCid && charAttributes.cid === charNodeCid)));\n}\n/**\n * Find a common ancestor of a and b and return the common ancestor,\n * or undefined if there is no common ancestor between the two nodes.\n *\n * This function is compatible with the deprecated `LexicalNode.getCommonAncestor` function but\n * uses the new (as of Lexical v0.26.0) NodeCaret APIs.\n *\n * @param a A LexicalNode\n * @param b A LexicalNode\n * @returns The common ancestor between the two nodes or undefined if they have no common ancestor\n */\nexport function $getCommonAncestorCompatible(a, b) {\n const a1 = $isElementNode(a) ? a : a.getParent();\n const b1 = $isElementNode(b) ? b : b.getParent();\n const result = a1 && b1 ? $getCommonAncestor(a1, b1) : undefined;\n return result ? result.commonAncestor : undefined;\n}\n/**\n * Moves the selection to the end of the current range, accounting for backward selections.\n * @param selection - The range selection to move to the end.\n */\nexport function $moveSelectionToEnd(selection) {\n const startEndPoints = selection.getStartEndPoints();\n if (!startEndPoints)\n return undefined;\n const [start, end] = startEndPoints;\n const actualEnd = selection.isBackward() ? start : end;\n selection.focus.set(actualEnd.key, actualEnd.offset, actualEnd.type);\n selection.anchor.set(actualEnd.key, actualEnd.offset, actualEnd.type);\n}\n/**\n * Checks if the given node is a SerializedTextNode.\n * @param node - The node to check.\n * @returns `true` if the node is a SerializedTextNode, `false` otherwise.\n */\nexport function isSerializedTextNode(node) {\n return node?.type === TextNode.getType();\n}\n/**\n * Remove the given node and all the nodes after.\n * @param nodes - Nodes to prune.\n * @param pruneNode - Node to prune and all nodes after.\n */\nexport function removeNodeAndAfter(nodes, pruneNode) {\n if (!pruneNode)\n return;\n const pruneNodeIndex = nodes.findIndex((node) => node === pruneNode);\n // prune node and after\n if (pruneNodeIndex)\n nodes.length = pruneNodeIndex;\n}\n/**\n * Removes all the nodes that proceed the given node.\n * @param nodes - Nodes to prune.\n * @param firstNode - Node to prune before.\n * @returns the nodes from the node and after.\n */\nexport function removeNodesBeforeNode(nodes, firstNode) {\n if (!firstNode)\n return nodes;\n const firstNodeIndex = firstNode.getIndexWithinParent();\n return nodes.splice(firstNodeIndex + 1, nodes.length - firstNodeIndex - 1);\n}\n/**\n * Gets the opening marker text.\n * @param marker - The USFM marker.\n * @param nested - Whether the span nests inside another char span. A nested span's marker carries\n * the `+` prefix (`\\+w`) — ParatextData's writer rule and PT9's on-screen display for USFM ≤3.0,\n * where `+` is what makes a bare char marker nest instead of closing the enclosing span. The\n * glyph must show it so a re-tokenization of the visible text reproduces the same nesting.\n * @returns the opening marker text.\n */\nexport function openingMarkerText(marker, nested = false) {\n return `\\\\${nested ? \"+\" : \"\"}${marker}`;\n}\n/**\n * Gets the closing marker text.\n * @param marker - The USFM marker.\n * @param nested - Whether the span nests inside another char span (see {@link openingMarkerText}).\n * @returns the closing marker text.\n */\nexport function closingMarkerText(marker, nested = false) {\n return `\\\\${nested ? \"+\" : \"\"}${marker}*`;\n}\n/**\n * Parse number from marker text.\n * @param marker - Chapter or verse marker.\n * @param text - Text to parse.\n * @param number - Default number to use if none is found.\n * @returns the parsed number or the default value as a string.\n */\nexport function parseNumberFromMarkerText(marker, text, number) {\n const openMarkerText = openingMarkerText(marker);\n if (text?.startsWith(openMarkerText)) {\n // Skip the NBSP/space separator inserted by `getVisibleOpenMarkerText`.\n const rest = text.slice(openMarkerText.length).replace(/^[\\s ]+/, \"\");\n // The number is the whole WORD, valid or not — the same scan Paratext 9's GetNextWord applies\n // and the same one Tier 1 uses to keep the glyph and the node in step\n // (`leadingAttributeGlyphRegexes`). Anything narrower drops displayed bytes on the way to the\n // file: a bridge the user is still typing (`5-`), a typo (`5--`, `5*`), a half-typed segment\n // (`5-Da`) are all on screen and in the node's own number, so a grammar that recognized only\n // well-formed numbers would save something the editor is not showing.\n //\n // The word still ends where the leading-attribute rule says it does, which is what keeps this\n // from swallowing content: whitespace ends it (`\\v 7 5` is verse 7 plus body text `5`) and so\n // does a backslash (`\\v 2\\ Da` is verse 2 plus the literal), because both are the tokenizer's\n // own name-scan terminators.\n const match = /^([^ \\u00A0\\\\]+)/.exec(rest);\n if (match)\n number = match[1];\n }\n return number;\n}\n/**\n * Gets the open marker text with the marker visible.\n * @param marker - Verse marker.\n * @param content - Content such as chapter or verse number.\n * @returns the marker text with the open marker visible.\n */\nexport function getVisibleOpenMarkerText(marker, content) {\n let text = openingMarkerText(marker);\n if (content)\n text += `${NBSP}${content}`;\n text += \" \";\n return text;\n}\n/** The `textType` NodeState of a serialized node, if any — the serialize-only mirror of the live\n * `$getState(node, textTypeState)`. */\nfunction serializedTextType(node) {\n const state = node[NODE_STATE_KEY];\n if (state && typeof state === \"object\" && \"textType\" in state) {\n const textType = state.textType;\n if (typeof textType === \"string\")\n return textType;\n }\n return undefined;\n}\n/**\n * Recursively extracts text content from a serialized Lexical node and its descendants.\n * Excludes marker nodes (both MarkerNode and ImmutableTypedTextNode with type \"marker\").\n * @param node - The serialized node to process.\n * @returns The concatenated text content.\n */\n// Keep this function in sync with `$getTextContentExcludingMarkers`. The two are deliberately\n// parallel rather than merged: this one walks SERIALIZED nodes (plain objects, `children` arrays),\n// the other walks LIVE nodes (Lexical accessors inside a read) — a shared core would need a\n// node-accessor abstraction that costs more than the ~20 duplicated lines it would save.\nfunction extractTextFromNode(node) {\n // Skip marker nodes - they're structural/formatting elements, not content\n if (isSerializedMarkerNode(node))\n return \"\";\n if (isSerializedImmutableTypedTextNode(node) && node.textType === \"marker\")\n return \"\";\n // The attribute display run (textType \"attribute\") is engine-owned presentation, not content —\n // exclude its bytes (`|gloss`) from note-preview text.\n if (isSerializedTextNode(node) && serializedTextType(node) === \"attribute\")\n return \"\";\n if (isSerializedTextNode(node) && node.text !== NBSP)\n return node.text;\n if (isSerializedCharNode(node)) {\n // If it's an ElementNode, process its children recursively and join their text\n // We join with '' here because spacing is usually handled by spaces within TextNodes\n // or potentially by joining results from the top-level nodes with spaces later.\n return node.children.map((child) => extractTextFromNode(child)).join(\"\");\n }\n // Ignore other node types (e.g., LineBreakNode, custom nodes without text/children)\n return \"\";\n}\n/**\n * Gets the preview text from an array of serialized Lexical nodes,\n * handling nested elements like the modified CharNode.\n * @param childNodes - Child nodes (e.g., from a NoteNode or ParagraphNode).\n * @returns The preview text.\n */\nexport function getPreviewTextFromSerializedNodes(childNodes) {\n const previewText = childNodes\n .map((node) => extractTextFromNode(node))\n .filter((text) => text.length > 0)\n .join(\" \")\n .trim();\n return previewText;\n}\n/**\n * Get editable note caller text.\n * @param noteCaller - Note caller.\n * @returns caller text.\n */\nexport function getEditableCallerText(noteCaller) {\n return \" \" + noteCaller + NBSP;\n}\n/**\n * Gets the preview text for a note caller.\n * Excludes marker nodes from the text content.\n * @param childNodes - Child nodes of the NoteNode.\n * @returns the preview text.\n */\nexport function $getNoteCallerPreviewText(childNodes) {\n const parts = [];\n for (const node of childNodes) {\n if (!$isCharNode(node))\n continue;\n const textContent = $getTextContentExcludingMarkers(node);\n if (textContent === EMPTY_CHAR_PLACEHOLDER_TEXT)\n continue;\n if (textContent.length > 0)\n parts.push(textContent);\n }\n return parts.join(\" \").trim();\n}\n/**\n * Recursively gets text content from a node, excluding marker nodes.\n * @param node - The node to extract text from.\n * @returns The text content without markers.\n */\n// Keep this function in sync with `extractTextFromNode`.\nfunction $getTextContentExcludingMarkers(node) {\n // Skip marker nodes\n if ($isMarkerNode(node))\n return \"\";\n if ($isVisibleMarkerNode(node))\n return \"\";\n // The attribute display run (textType \"attribute\") is engine-owned presentation, not content.\n if ($isTextNode(node) && $getState(node, textTypeState) === \"attribute\")\n return \"\";\n // For text nodes, return the text\n if ($isTextNode(node))\n return node.getTextContent();\n // For element nodes, recursively process children\n if ($isElementNode(node)) {\n return node\n .getChildren()\n .map((child) => $getTextContentExcludingMarkers(child))\n .join(\"\");\n }\n return \"\";\n}\n/**\n * Checks whether a node is a visible marker node.\n *\n * Visible marker nodes are immutable typed text nodes whose text type is \"marker\".\n *\n * @param node - The node to check.\n * @returns `true` if the node is an ImmutableTypedTextNode with text type \"marker\".\n */\nexport function $isVisibleMarkerNode(node) {\n return $isImmutableTypedTextNode(node) && node.getTextType() === \"marker\";\n}\n/**\n * True for either flavor of synthesized marker node: a `MarkerNode` (markerMode \"editable\") or an\n * `ImmutableTypedTextNode` with `textType: \"marker\"` (markerMode \"visible\" or gutter views).\n * These are the two node shapes used to render a USFM marker as visible content — a paragraph's\n * marker (e.g. `\\p`, `\\s2`, `\\q1`) as the first child of its `ParaNode`, or a character marker's\n * opening and closing markers inside its `CharNode`.\n *\n * @param node - The node to check.\n * @returns `true` if the node is a `MarkerNode` or a visible marker node.\n */\nexport function $isSynthesizedMarkerNode(node) {\n return $isMarkerNode(node) || $isVisibleMarkerNode(node);\n}\n/**\n * Set a `CharNode`'s marker, keeping its content and identity.\n *\n * Use this rather than `CharNode.setMarker` whenever an existing `CharNode`'s marker changes in a\n * rendered editor: it pairs the `setMarker` with the retargeting of the node's synthesized marker\n * children, in the one order that works.\n *\n * Coalescing with an identically-marked adjacent sibling is deliberately not handled here:\n * `$charNodeTransform` (`shared-react`'s `CharNodePlugin.tsx`) already does it, and\n * `CharNodePlugin.test.tsx` proves it for exactly this call. Don't hand-roll it, and don't fight it.\n *\n * The merge is not deferred to a later update: Lexical runs node transforms to fixpoint inside the\n * *same* `editor.update()`, before reconciliation and before any update listener fires, and the node\n * this touches is dirty and therefore transformed. So no committed `EditorState` is ever observable\n * with the un-coalesced pair in it. That matters wherever a caller copies an existing `CharNode`'s\n * cid onto a new sibling and leans on this merge to reunite them — the duplicate cid has no window\n * in which anything can read it.\n *\n * Callers must pre-validate `marker`: passing a footnote or cross-reference marker (e.g. `\"ft\"`,\n * `\"xt\"`) removes the node's closing marker child rather than rewriting it, because\n * `addClosingMarker` never emits one for those families. That is a deliberate contract rather than a\n * guard - see `$retargetSynthesizedMarkers` - and no current caller reaches it, since\n * `EditorRef.replaceCharacterMarker` rejects those markers up front.\n *\n * @param charNode - The `CharNode` to change.\n * @param marker - The character marker to change to. Must not be a footnote or cross-reference\n * marker; see above.\n */\nexport function $setCharNodeMarker(charNode, marker) {\n // Before setMarker, not after. $retargetSynthesizedMarkers's ImmutableTypedTextNode branch\n // (markerMode \"visible\") matches a child's text against the *old* marker's opening/closing form,\n // read via charNode.getMarker() — reversed, that read would already return the new marker, and\n // the stale child would silently go unmatched. Its MarkerNode branch (markerMode \"editable\")\n // doesn't care about this order: MarkerNode.setMarker recomputes text from the new marker it's\n // given plus its own stored __markerSyntax, not from anything read off charNode.\n // Guarded by \"matches marker children against the old marker, not the new one\" in\n // node-utils.test.ts - reverse these two calls and it fails.\n $retargetSynthesizedMarkers(charNode, marker);\n charNode.setMarker(marker);\n}\n/**\n * Point a `CharNode`'s synthesized marker children at a new marker.\n *\n * Under `markerMode: \"editable\"` those children are `MarkerNode`s and under `\"visible\"` they are\n * `ImmutableTypedTextNode`s with `textType: \"marker\"`; both are produced by the USJ editor\n * adaptor's `addOpeningMarker` / `addClosingMarker` and neither is touched by `CharNode.setMarker`,\n * so without this every marker change in those modes leaves the old marker's text on screen.\n *\n * Retargets rather than strips: stripping would leave the changed span looking unmarked, which\n * reads worse than stale. The one exception is the closing marker child when `toMarker` is a\n * note-content marker: `addClosingMarker` never emits a closing marker for those families, so the\n * child is removed rather than rewritten to a form the adaptor would never produce (e.g. `\\ft*`).\n * That is a contract of the function, not a live path — `EditorRef.replaceCharacterMarker` rejects\n * those markers up front.\n *\n * The old marker is read from the node itself rather than taken from a caller-supplied \"from\"\n * marker, which callers may not know when the innermost marker was targeted.\n *\n * A child whose text is neither the opening nor the closing form of the old marker — in either the\n * plain or the nested (`\\\\+nd`) spelling — is left verbatim rather than rewritten by guesswork. This applies to both synthesized child flavors: a\n * `MarkerNode` is a `TextNode` in default (non-token) mode, so a selection anchored inside its\n * visible text can `splitText` it into a fragment whose `__marker` is stale but whose text no\n * longer matches — rewriting that verbatim would re-expand it to the wrong marker.\n *\n * @param charNode - The `CharNode` whose marker is about to change.\n * @param toMarker - The character marker to change to.\n */\nfunction $retargetSynthesizedMarkers(charNode, toMarker) {\n const fromMarker = charNode.getMarker();\n // Both spellings: a nested span's glyphs carry the `+` (`\\\\+nd`), and matching only the plain\n // form left them unmatched, so a marker change inside another span rewrote the node while its\n // glyphs — and the bytes they serialize to — kept the old marker.\n const openingText = openingMarkerText(fromMarker);\n const nestedOpeningText = openingMarkerText(fromMarker, true);\n const closingText = closingMarkerText(fromMarker);\n const nestedClosingText = closingMarkerText(fromMarker, true);\n // Note-content markers are written without a closing marker, so a closing child is removed rather\n // than retargeted to a form `addClosingMarker` would never emit.\n const dropsClosingMarker = CharNode.isNoteContentMarker(toMarker);\n charNode.getChildren().forEach((child) => {\n // Gate on the node type first: only a synthesized marker child is ever a candidate for\n // rewriting or removal here, regardless of what its text happens to contain.\n if (!$isSynthesizedMarkerNode(child))\n return;\n const text = child.getTextContent();\n const isOpening = text === openingText || text === nestedOpeningText;\n const isClosing = !isOpening && (text === closingText || text === nestedClosingText);\n if (!isOpening && !isClosing)\n return;\n if (isClosing && dropsClosingMarker) {\n child.remove();\n return;\n }\n // MarkerNode.setMarker recomputes the node's text for us, nesting included — it re-derives\n // from its own stored nesting rather than from anything read here.\n if ($isMarkerNode(child))\n child.setMarker(toMarker);\n else if ($isVisibleMarkerNode(child)) {\n // A visible glyph stores no nesting, so carry over the nesting its text already showed.\n const nested = text.startsWith(openingMarkerText(\"\", true));\n child.setTextContent(isOpening ? openingMarkerText(toMarker, nested) : closingMarkerText(toMarker, nested));\n }\n });\n}\n/**\n * Remove all known properties of the `markerObject`.\n * @param markerObject - Scripture marker and its contents.\n * @param markerObjectProps - List of known properties to remove. Defaults to `MARKER_OBJECT_PROPS`.\n * @returns all the unknown properties or `undefined` if all are known.\n */\nexport function getUnknownAttributes(markerObject, markerObjectProps = MARKER_OBJECT_PROPS) {\n const attributes = { ...markerObject };\n markerObjectProps.forEach((property) => {\n Reflect.deleteProperty(attributes, property);\n });\n return Object.keys(attributes).length === 0 ? undefined : attributes;\n}\n/**\n * Retrieves the lowercase tag name of the DOM element associated with a LexicalNode.\n * @param node - The LexicalNode for which to find the corresponding DOM element's tag name.\n * @param editor - The LexicalEditor instance used to access the DOM.\n * @returns The lowercase tag name of the DOM element if found, or `undefined` if no corresponding\n * DOM element exists.\n * @deprecated Not used anymore.\n */\nexport function getNodeElementTagName(node, editor) {\n const domElement = editor.getElementByKey(node.getKey());\n return domElement ? domElement.tagName.toLowerCase() : undefined;\n}\n/**\n * Removes properties with undefined values from an object.\n *\n * @param obj - The object to remove undefined properties from.\n * @returns A new object with the same type as the input, but with undefined properties removed.\n *\n * @example\n * const input = { a: 1, b: undefined, c: 'hello' };\n * const result = removeUndefinedProperties(input);\n * // result: { a: 1, c: 'hello' }\n *\n * @remarks\n * This function creates a new object and does not modify the original input object.\n */\nexport function removeUndefinedProperties(obj) {\n return Object.fromEntries(Object.entries(obj).filter(([, value]) => value !== undefined));\n}\n/**\n * Returns true when the error is Lexical's getNodes() throw (selection on DecoratorNode).\n * Message-based; may break if Lexical changes error text. Prefer pre-checking anchor\n * node type before calling getSelectionStartNode.\n */\nexport function isSelectionStartNodeExpectedError(err) {\n const message = err instanceof Error ? err.message : String(err);\n return (message.includes(\"$caretFromPoint\") &&\n (message.includes(\"does not inherit from ElementNode\") ||\n message.includes(\"does not inherit from TextNode\")));\n}\n/**\n * Get the start node of the selection.\n * For range selections, avoids throws from `getNodes()` when the anchor is on a node type that\n * does not match the anchor type (e.g. DecoratorNode with an element selection), by returning the\n * anchor node or applying `isSelectionStartNodeExpectedError` fallback.\n * @param selection - The selection to get the start node from.\n * @returns The start node of the selection or `undefined` if no selection is provided.\n */\nexport function getSelectionStartNode(selection) {\n if (!$isRangeSelection(selection)) {\n return getSelectionStartNodeInner(selection);\n }\n const anchorNode = selection.anchor.getNode();\n const isAnchorTypeMismatch = anchorNode &&\n ((selection.anchor.type === \"element\" && !$isElementNode(anchorNode)) ||\n (selection.anchor.type === \"text\" && !$isTextNode(anchorNode)));\n if (isAnchorTypeMismatch) {\n return anchorNode ?? undefined;\n }\n try {\n const node = getSelectionStartNodeInner(selection);\n return node ?? anchorNode ?? undefined;\n }\n catch (err) {\n if (isSelectionStartNodeExpectedError(err)) {\n return anchorNode ?? undefined;\n }\n throw err;\n }\n}\n/**\n * Get the next verse number or segment.\n *\n * A verse range increments the end of the range (even if the range includes segments), and a verse\n * segment increments the segment character. This is intentional to simplify the UX.\n * @param verseNum - The current verse number.\n * @param verse - The current verse string, which can be a single verse, a range, or a segment.\n * @returns The next verse number or segment as a string.\n */\nexport function getNextVerse(verseNum, verse) {\n if (!verse)\n return (verseNum + 1).toString();\n const verseParts = verse.split(\"-\");\n if (verseParts.length === 2)\n return parseInt(verseParts[1])\n ? `${parseInt(verseParts[1]) + 1}`\n : `${parseInt(verseParts[0]) + 1}`;\n // Don't increment beyond 'z' or 'Z'.\n const verseSegment = RegExp(/^(\\d+)([a-yA-Y]{1,3})$/).exec(verse);\n if (!verseSegment)\n return (parseInt(verse) + 1).toString();\n const nextSegmentChar = String.fromCharCode(verseSegment[2].charCodeAt(0) + 1);\n return `${verseSegment[1]}${nextSegmentChar}`;\n}\n/**\n * Determines if the verse number is in the given verse range. Verse segments are accounted for.\n * @param verseNum - The current verse number.\n * @param verseRange - The verse range including segments.\n * @returns `true` if the verse number is in the range, `false` otherwise.\n * @example\n * verseRange \"1-2\" - verseNum 1 and 2 are `true`\n * verseRange \"1a-2b\" - verseNum 1 and 2 are `true`\n * verseRange \"1-3\" - verseNum 1, 2, and 3 are `true`\n */\nexport function isVerseInRange(verseNum, verseRange) {\n if (!verseRange)\n return false;\n const verseNumParts = verseRange.split(\"-\").map((v) => parseInt(v));\n if (verseNumParts.length < 1 || verseNumParts.length > 2 || verseNumParts[0] > verseNumParts[1])\n throw new Error(\"isVerseInRange: invalid range\");\n if (verseNumParts.length === 1)\n return verseNum === verseNumParts[0];\n if (verseNumParts.length === 2 && isNaN(verseNumParts[1]))\n return verseNum >= verseNumParts[0];\n if (verseNumParts.length === 2 && isNaN(verseNumParts[0]))\n return verseNum <= verseNumParts[1];\n return verseNum >= verseNumParts[0] && verseNum <= verseNumParts[1];\n}\n/**\n * Checks if the given verse range is a range (i.e. contains a dash).\n * @param verseRange - The verse range to check.\n * @returns `true` if the verse range is a range, `false` otherwise.\n */\nexport function isVerseRange(verseRange) {\n return !!verseRange && verseRange.includes(\"-\");\n}\n/**\n * Parses a (possibly combined/partial) verse marker into its numeric bounds.\n *\n * Unlike {@link isVerseInRange}, this never throws: a marker that isn't numeric yields `NaN` bounds\n * for the caller to reject. Verse numbers come from imported USFM and are not guaranteed to be\n * well-formed.\n *\n * @param verseRange - The verse marker, e.g. `\"5\"`, `\"14-15\"`, `\"3a\"`.\n * @returns the first and last verse numbers the marker covers.\n * @example\n * \"5\" - `{ start: 5, end: 5 }`\n * \"14-15\" - `{ start: 14, end: 15 }`\n * \"1-3a\" - `{ start: 1, end: 3 }`\n * \"3a\" - `{ start: 3, end: 3 }`\n * \"abc\" - `{ start: NaN, end: NaN }`\n */\nexport function parseVerseRange(verseRange) {\n const parts = verseRange.split(\"-\");\n const start = parseInt(parts[0], 10);\n const end = parts.length > 1 ? parseInt(parts[parts.length - 1], 10) : start;\n return { start, end };\n}\nfunction getSelectionStartNodeInner(selection) {\n if (!selection)\n return undefined;\n const nodes = selection.getNodes();\n if (nodes.length > 0) {\n return selection.isBackward() ? nodes[nodes.length - 1] : nodes[0];\n }\n return undefined;\n}\n/**\n * Checks whether a node is presentation-only and therefore not part of USJ content:\n * line breaks, marker scaffolding (editable and visible), the `AttributeRunNode` wrapper that\n * carries a verse's, milestone's, chapter's or note's attribute display run,\n * marker-trailing-space or attribute text (as a plain TextNode or as an opaque block's folded\n * ImmutableTypedTextNode display run, e.g. an UnknownNode's `\\cat` byte display), and empty or\n * NBSP-only spacer text (which the editor→USJ conversion drops as well; ideally the USJ→editor\n * conversion would create such spacers as presentation-typed text nodes instead — follow-up work).\n * @param node - The node to check.\n * @returns `true` if the node must be skipped when computing USJ content indexes.\n */\nexport function $shouldIgnoreNodeForContentIndexes(node) {\n if (!node)\n return false;\n if ($isLineBreakNode(node))\n return true;\n if ($isMarkerNode(node))\n return true;\n if ($isVisibleMarkerNode(node))\n return true;\n // The owner's attributes are what the exporter reads; the wrapper and everything in it is the\n // display of them, skipped wholesale by the editor→USJ conversion as well.\n if ($isAttributeRunNode(node))\n return true;\n // ImmutableTypedTextNode's \"attribute\" flavor (an opaque block's folded attribute-byte display\n // run, e.g. an UnknownNode's `\\cat ...\\cat*`) is a DecoratorNode, not a TextNode, so it never\n // reaches the $isTextNode branch below — mirror the \"marker\" flavor handled above by\n // $isVisibleMarkerNode.\n if ($isImmutableTypedTextNode(node) && node.getTextType() === \"attribute\")\n return true;\n if ($isTextNode(node)) {\n const textType = $getState(node, textTypeState);\n if (textType === MARKER_TRAILING_SPACE_TEXT_TYPE || textType === \"attribute\")\n return true;\n const text = node.getTextContent();\n // \"\" / NBSP are presentation-only; a bare cursor host (EmptyVerseCaretGuardPlugin) likewise\n // carries no content, so it must not shift annotation content indexes while it rests.\n if (text === \"\" || text === NBSP || isCursorPlaceholderOnly(text))\n return true;\n }\n return false;\n}\n/**\n * Creates the engine-owned NBSP separator that sits between an editable marker glyph and its\n * content (the `[glyph, separator, ...content]` prefix layout). Token mode so typing at the\n * separator's boundary can never insert INTO it — Lexical routes boundary insertions into a new\n * plain sibling TextNode instead. Without token mode, a fresh empty paragraph (whose caret\n * fallback is the separator's end) absorbed typed text into this node (`asdf`), which the\n * serializer — matching the separator by exact-NBSP text — then leaked into USJ content (`\\p\n * ~asdf` in USFM, and a non-convergent PDP echo loop in the host). The forward adaptor builds the\n * SERIALIZED twin of this node with the same {@link MARKER_TRAILING_SPACE_TEXT_TYPE} tag and\n * token mode.\n *\n * Mutating factory (creates a node): call inside `editor.update()`.\n */\nexport function $createMarkerTrailingSeparator() {\n const separator = $createTextNode(NBSP);\n $setState(separator, textTypeState, MARKER_TRAILING_SPACE_TEXT_TYPE);\n separator.setMode(\"token\");\n return separator;\n}\n/**\n * Prepends the structural NBSP that leads an editable char span's text content (after the opening\n * glyph), if not already present. The prefix separates the glyph from the content so caret\n * placement and Tier-2 fragment building can address the content boundary; the reverse adaptor\n * strips it on serialization. String-building code paths (the forward adaptor,\n * `$createNoteContentChar`, the delta materializer) prepend the same `NBSP` when constructing\n * content text.\n *\n * Mutating: call inside `editor.update()`.\n */\nexport function $withCharContentNbspPrefix(node) {\n const text = node.getTextContent();\n if (!text.startsWith(NBSP))\n node.setTextContent(NBSP + text);\n}\n/**\n * Whether `node` is the engine-owned marker-trailing NBSP separator (see\n * {@link $createMarkerTrailingSeparator}). Read-only: call inside\n * `editor.getEditorState().read(...)` or an update.\n *\n * Deliberately NOT a `node is TextNode` type predicate: a false result must not narrow the node\n * away from `TextNode` (an untagged plain text node also returns false), which a type predicate's\n * false branch would wrongly do.\n */\nexport function $isMarkerTrailingSeparator(node) {\n return $isTextNode(node) && $getState(node, textTypeState) === MARKER_TRAILING_SPACE_TEXT_TYPE;\n}\n/**\n * Whether `element`'s para-prefix separator is MISSING while the collapsed caret sits at its\n * site — on the prefix glyph, on the element itself (an element point), or at the very start of\n * the node after the glyph. This is where the caret lands when the user deletes the separator,\n * and it is the para-prefix twin of the char opener's caret-boundary rule\n * (markerSeparators.utils.ts): while it holds, healing the byte back would be healing against a\n * user edit, so the heal and the settle both defer to caret departure. One definition, used by\n * both the deletion transform's grace and the departure settle's re-pend, so the two can never\n * disagree about what \"at the site\" means. Read-only: call inside\n * `editor.getEditorState().read(...)` or an update.\n */\nexport function $paraPrefixSeparatorCaretHeld(element) {\n const glyph = element.getFirstChild();\n if (!$isSynthesizedMarkerNode(glyph) || glyph === null)\n return false;\n if ($isMarkerTrailingSeparator(glyph.getNextSibling()))\n return false;\n const selection = $getSelection();\n if (!$isRangeSelection(selection) || !selection.isCollapsed())\n return false;\n const anchorNode = selection.anchor.getNode();\n if (anchorNode.is(glyph) || anchorNode.is(element))\n return true;\n const next = glyph.getNextSibling();\n return next !== null && anchorNode.is(next) && selection.anchor.offset === 0;\n}\n/**\n * Maps a parent element's Lexical children to its logical USJ content items — the items the\n * editor→USJ conversion would export: presentation-only nodes skipped, TypedMarkNodes\n * transparent (children spliced in, recursively), contiguous text coalesced into single items.\n *\n * Known exclusion: comment-type TypedMarkNodes are treated as transparent like every other\n * mark, even though the exporter still serializes them as milestone items. That milestone\n * serialization is deprecated and pending removal, so the model intentionally ignores it.\n * @param parent - The parent element node.\n * @returns the logical content items in document order.\n */\nexport function $getLogicalContentItems(parent) {\n const items = [];\n let run;\n const flushRun = () => {\n if (run) {\n items.push({ type: \"text\", segments: run.segments, length: run.length });\n run = undefined;\n }\n };\n const visit = (node) => {\n if ($shouldIgnoreNodeForContentIndexes(node))\n return;\n if ($isTypedMarkNode(node)) {\n // Recursion is defense-in-depth: nested marks only exist transiently before the\n // AnnotationPlugin's nested-element resolver flattens them into siblings.\n node.getChildren().forEach(visit);\n return;\n }\n // Only plain TextNodes (exact \"text\" type) join a coalesced run, mirroring the exporter.\n // TextNode subclasses (e.g. VerseNode) fall through to become standalone items.\n if ($isTextNode(node) && node.getType() === TextNode.getType()) {\n run ??= { segments: [], length: 0 };\n run.segments.push({ node, start: run.length });\n run.length += node.getTextContentSize();\n return;\n }\n flushRun();\n items.push({ type: \"element\", node });\n };\n parent.getChildren().forEach(visit);\n flushRun();\n return items;\n}\n/**\n * Gets the nearest ancestor that is not a TypedMarkNode — the element that owns the node's\n * logical content index (annotation wrappers are transparent in USJ).\n * @param node - The node to get the logical parent of.\n * @returns the logical parent element, or `null` at the root.\n */\nexport function $getLogicalParent(node) {\n let parent = node.getParent();\n while (parent && $isTypedMarkNode(parent))\n parent = parent.getParent();\n return parent;\n}\n/**\n * Gets the logical content index of the item containing the child (the child may be nested\n * inside TypedMarkNodes under the parent).\n * @param parent - The logical parent element.\n * @param child - The node to find.\n * @returns the logical index, or -1 if the child is presentation-only or not found.\n */\nexport function $getLogicalIndexOfChild(parent, child) {\n return $getLogicalContentItems(parent).findIndex((item) => item.type === \"element\"\n ? item.node.is(child)\n : item.segments.some((segment) => segment.node.is(child)));\n}\n/**\n * Converts a (TextNode, local offset) point to logical USJ text coordinates: the index of the\n * coalesced text item within the logical parent and the cumulative offset within it.\n * @param textNode - The Lexical text node.\n * @param offset - The offset within the text node.\n * @returns the logical parent, item index, and cumulative offset, or `undefined` if the text\n * node is not part of any logical text item (e.g. presentation-only text).\n */\nexport function $getLogicalTextLocation(textNode, offset) {\n const parent = $getLogicalParent(textNode);\n if (!parent)\n return undefined;\n const items = $getLogicalContentItems(parent);\n for (let index = 0; index < items.length; index++) {\n const item = items[index];\n if (item.type !== \"text\")\n continue;\n const segment = item.segments.find((segment) => segment.node.is(textNode));\n if (segment)\n return { parent, index, offset: segment.start + offset };\n }\n return undefined;\n}\n/**\n * Finds the TextNode and local offset at a cumulative offset within a logical text item.\n * At internal segment boundaries the next segment's start is preferred, so a point at an\n * annotation edge targets the start of the following content rather than the end of the\n * previous piece.\n * @param item - The logical text item.\n * @param offset - The cumulative offset within the item.\n * @returns the text node and local offset, or `undefined` when out of range.\n */\nexport function $getTextNodeAtLogicalOffset(item, offset) {\n if (offset < 0 || offset > item.length)\n return undefined;\n for (const segment of item.segments) {\n const segmentLength = segment.node.getTextContentSize();\n if (offset >= segment.start && offset < segment.start + segmentLength)\n return [segment.node, offset - segment.start];\n }\n // offset === item.length: end of the last segment.\n const lastSegment = item.segments[item.segments.length - 1];\n if (!lastSegment)\n return undefined;\n return [lastSegment.node, offset - lastSegment.start];\n}\n/**\n * Converts an element point (parent + child index) to a logical point. Boundaries that fall\n * inside a coalesced text item (e.g. at an annotation edge) become text points; boundaries\n * between logical items become index points.\n * @param parent - The parent element node of the element point.\n * @param elementOffset - The child index of the element point.\n * @returns the logical point.\n */\nexport function $getLogicalPointFromElementPoint(parent, elementOffset) {\n const items = $getLogicalContentItems(parent);\n const child = parent.getChildAtIndex(elementOffset);\n if (!child)\n return { type: \"index\", index: items.length };\n // Boundary before a presentation-only node: use the boundary before the next content child.\n if ($shouldIgnoreNodeForContentIndexes(child))\n return $getLogicalPointFromElementPoint(parent, elementOffset + 1);\n for (let index = 0; index < items.length; index++) {\n const item = items[index];\n if (item.type === \"element\") {\n if (item.node.is(child) || $isDescendantOf(item.node, child.getKey()))\n return { type: \"index\", index };\n continue;\n }\n for (const segment of item.segments) {\n if (segment.node.is(child) || $isDescendantOf(segment.node, child.getKey())) {\n return segment.start === 0\n ? { type: \"index\", index }\n : { type: \"text\", index, offset: segment.start };\n }\n }\n }\n return { type: \"index\", index: items.length };\n}\n/**\n * Converts a logical boundary index to the earliest element child offset at that boundary\n * (the inverse of `$getLogicalPointFromElementPoint` for index points).\n * @param parent - The parent element node.\n * @param logicalIndex - The logical boundary index (0 = before the first item).\n * @returns the element child offset.\n */\nexport function $getElementOffsetFromLogicalIndex(parent, logicalIndex) {\n if (logicalIndex <= 0)\n return 0;\n const items = $getLogicalContentItems(parent);\n if (items.length === 0 || logicalIndex > items.length)\n return parent.getChildrenSize();\n const previousItem = items[logicalIndex - 1];\n const lastNode = previousItem.type === \"element\"\n ? previousItem.node\n : previousItem.segments[previousItem.segments.length - 1]?.node;\n const topLevelChild = lastNode ? $findChildOfParent(parent, lastNode) : undefined;\n return topLevelChild ? topLevelChild.getIndexWithinParent() + 1 : parent.getChildrenSize();\n}\n/** Walks up from a descendant to the direct child of the given parent. */\nfunction $findChildOfParent(parent, descendant) {\n let current = descendant;\n while (current) {\n const currentParent = current.getParent();\n if (currentParent?.is(parent))\n return current;\n current = currentParent;\n }\n return undefined;\n}\n","/** Marker node used when displaying USFM */\nimport { closingMarkerText, openingMarkerText } from \"../usj/node.utils.js\";\nimport { $applyNodeReplacement, TextNode, } from \"lexical\";\nexport const MARKER_VERSION = 1;\nexport class MarkerNode extends TextNode {\n __marker;\n __markerSyntax;\n __nested;\n // `key` stays in Lexical's own third-parameter slot (`TextNode(text, key)`), with `nested`\n // appended after it: a node's key is the last argument every Lexical node constructor takes, and\n // slotting a new field ahead of it would silently reinterpret an existing 3-argument call's\n // `NodeKey` as this flag.\n constructor(marker = \"\", markerSyntax = \"opening\", key, nested = false) {\n super(getMarkerText(marker, markerSyntax, nested), key);\n this.__marker = marker;\n this.__markerSyntax = markerSyntax;\n this.__nested = nested;\n }\n static getType() {\n return \"marker\";\n }\n static clone(node) {\n return new MarkerNode(node.__marker, node.__markerSyntax, node.__key, node.__nested);\n }\n static importJSON(serializedNode) {\n return $createMarkerNode().updateFromJSON(serializedNode);\n }\n updateFromJSON(serializedNode) {\n const { marker, markerSyntax = \"opening\", nested = false } = serializedNode;\n const self = super.updateFromJSON({\n ...serializedNode,\n // An EMPTY serialized text is the \"build canonical bytes\" sentinel — the adaptor's\n // createMarker serializes glyphs with `text: \"\"` and relies on the import deriving them.\n // Any non-empty text is the glyph's actual displayed bytes and is kept verbatim.\n text: serializedNode.text || getMarkerText(marker, markerSyntax, nested),\n });\n // Assigned directly rather than via setMarker/setMarkerSyntax/setNested, which rewrite the\n // just-applied text to canonical and lose a serialized mid-edit divergence: a glyph whose\n // `*` the user deleted must round-trip through parseEditorState or clipboard\n // deserialization still divergent — rewriting it here heals against a user edit, and\n // $isCanonicalMarkerNode then wrongly reports the glyph at rest. Same treatment as\n // ImmutableUnmatchedNode.updateFromJSON.\n const writable = self.getWritable();\n writable.__marker = marker;\n writable.__markerSyntax = markerSyntax;\n writable.__nested = nested;\n return writable;\n }\n setMarker(marker) {\n if (this.__marker === marker)\n return this;\n const self = this.getWritable();\n self.__marker = marker;\n self.__text = getMarkerText(marker, self.__markerSyntax, self.__nested);\n return self;\n }\n getMarker() {\n const self = this.getLatest();\n return self.__marker;\n }\n setMarkerSyntax(markerSyntax) {\n if (this.__markerSyntax === markerSyntax)\n return this;\n const self = this.getWritable();\n self.__markerSyntax = markerSyntax;\n self.__text = getMarkerText(self.__marker, markerSyntax, self.__nested);\n return self;\n }\n getMarkerSyntax() {\n const self = this.getLatest();\n return self.__markerSyntax;\n }\n setNested(nested) {\n if (this.__nested === nested)\n return this;\n const self = this.getWritable();\n self.__nested = nested;\n self.__text = getMarkerText(self.__marker, self.__markerSyntax, nested);\n return self;\n }\n getNested() {\n const self = this.getLatest();\n return self.__nested;\n }\n createDOM(config) {\n const dom = super.createDOM(config);\n dom.setAttribute(\"data-marker\", this.__marker);\n dom.classList.add(this.__markerSyntax);\n return dom;\n }\n updateDOM(prevNode, dom, config) {\n // TextNode implements updateDOM, so unlike CharNode.updateDOM this can - and must - defer to\n // super: its return value decides whether Lexical reuses the element or rebuilds it via\n // createDOM. On reuse, createDOM does not run again, so the marker-derived attribute and class\n // it set have to be rewritten here by hand. setMarker/setMarkerSyntax already reconcile the\n // visible text; without this the presentational data-marker and syntax class go stale, which is\n // the same gap CharNode.updateDOM closes for the char span.\n const isRecreated = super.updateDOM(prevNode, dom, config);\n if (prevNode.__marker !== this.__marker)\n dom.setAttribute(\"data-marker\", this.__marker);\n if (prevNode.__markerSyntax !== this.__markerSyntax) {\n dom.classList.remove(prevNode.__markerSyntax);\n dom.classList.add(this.__markerSyntax);\n }\n return isRecreated;\n }\n exportJSON() {\n return {\n ...super.exportJSON(),\n type: this.getType(),\n text: this.getTextContent(),\n marker: this.getMarker(),\n markerSyntax: this.getMarkerSyntax(),\n // Only serialize the flag for genuinely nested glyphs; absence means non-nested, so\n // existing states (and the overwhelmingly common non-nested markers) stay unchanged.\n ...(this.getNested() ? { nested: true } : {}),\n version: MARKER_VERSION,\n };\n }\n}\nexport function $createMarkerNode(marker, markerSyntax, nested) {\n return $applyNodeReplacement(new MarkerNode(marker, markerSyntax, undefined, nested));\n}\nexport function $isMarkerNode(node) {\n return node instanceof MarkerNode;\n}\nexport function isSerializedMarkerNode(node) {\n return node?.type === MarkerNode.getType();\n}\n/**\n * Whether `node`'s RENDERED bytes still spell the glyph its own state describes — the single\n * definition of \"this glyph is at rest\", as opposed to mid-edit (pend-shaped).\n *\n * A glyph's text is a cache of (marker, syntax, nested) that only the setters below rewrite, so a\n * user edit to the displayed characters — deleting the `*` from `\\va*`, backspacing into `\\qt-s` —\n * leaves the node's state fully intact while the bytes on screen say something else. Anything that\n * classifies a glyph must ask this, not just the state: a glyph whose bytes have drifted is\n * mid-edit, and treating it as canonical is how a run comes to be canonical to one subsystem and\n * pending to another at the same time. That standing disagreement is the defect shape this\n * predicate exists to make impossible, so it belongs next to the `__text` writer it mirrors, not\n * beside any one of its callers — the marker transform, the pending-marker resolve, the historic\n * re-pend scan, the read-only settle, and the display-run piece scanners all key on it. A nested\n * span's `\\+w*` is canonical FOR A NESTED GLYPH; the `+` comes from the node's own stored nesting,\n * so a rest-state nested glyph is never mistaken for a damaged one.\n *\n * Read-only: call inside `editor.getEditorState().read(...)` or an update.\n */\nexport function $isCanonicalMarkerNode(node) {\n return (node.getTextContent() ===\n getMarkerText(node.getMarker(), node.getMarkerSyntax(), node.getNested()));\n}\n/**\n * Rewrite `node`'s rendered bytes to the canonical spelling of its own (marker, syntax, nested)\n * state — the HEAL arm for machine drift on a glyph's displayed characters. The one writer of the\n * canonical form lives in this module ({@link getMarkerText}); exposing the restore here keeps a\n * healer from re-deriving the spelling and drifting from it. Callers own the provenance decision\n * (invariants: machine drift heals, a user edit pends) — this function only writes the bytes.\n *\n * Mutating: call inside `editor.update()` (dispatched from the marker-edit engine's `MarkerNode`\n * transform when a non-user divergence is detected).\n */\nexport function $restoreCanonicalMarkerText(node) {\n node.setTextContent(getMarkerText(node.getMarker(), node.getMarkerSyntax(), node.getNested()));\n}\n/**\n * The single writer of a glyph's `__text` — the ONLY place the `+` becomes literal characters.\n * `marker` is always clean (`\"w\"`); `nested` contributes the `+` (`\\+w`). Called from the\n * constructor and every setter, so `__text` always reflects (marker, syntax, nested) — keeping\n * the cached text honest is therefore exactly the job of keeping `nested` honest, which\n * `$syncNestedGlyphs` (nestedGlyphs.utils.ts) does from tree position.\n */\nfunction getMarkerText(marker, markerSyntax, nested = false) {\n // The self-closing form is a milestone terminator (`\\*`); milestones never nest inside a char\n // span, so the `+` prefix does not apply to it.\n if (markerSyntax === \"closing\")\n return closingMarkerText(marker, nested);\n if (markerSyntax === \"selfClosing\")\n return closingMarkerText(\"\");\n return openingMarkerText(marker, nested);\n}\n","/**\n * Attribute display runs: the single place that owns HOW a node's USFM attribute bytes\n * (`|lemma=\"grace\" strong=\"G5485\"`, `|gloss`) are rendered as engine-owned display text and kept\n * in sync. Sibling of nestedGlyphs.utils.ts (glyph `+`) and markerSeparators.utils.ts (opener\n * separators), following the same owning-module shape.\n *\n * ## The representations (who owns what)\n *\n * - **Node state is the truth.** Char-span attributes live in `CharNode.__unknownAttributes`;\n * milestone attributes in `MilestoneNode` props + `__unknownAttributes`; a verse's `\\va`/`\\vp`\n * values in `VerseNode.__altnumber`/`__pubnumber`. The display run is a derived cache, never a\n * second store.\n * - **The display run** is a TextNode tagged textType \"attribute\" holding the canonical PT9 byte\n * form produced by {@link canonicalAttributeText}: a lone default attribute collapses to\n * `|value`; anything else is `|name=\"value\" …` (double quotes, single spaces, insertion\n * order). `closed` is derived metadata, never displayed. Char runs are bare `|…` directly\n * before the closing glyph (PT9's shape; an NBSP prefix would flatten to a space and leak\n * into span content on a Tier-2 rebuild). Milestone runs keep the NBSP+`|` prefix — that NBSP\n * flattens to the space genuinely in the file (`\\qt-s |sid=\"…\"\\*`). A verse's `\\va`/`\\vp`\n * values aren't `name=\"value\"` attribute bytes at all — PT9 displays them as their own\n * `MarkerNode` open + NBSP-prefixed value TextNode + `MarkerNode` close triplet, riding as the\n * verse's FOLLOWING SIBLINGS (a `VerseNode` is itself a TextNode, not a container). A\n * milestone's run is shaped the same way — opening `MarkerNode` + optional NBSP-prefixed\n * attribute TextNode + self-closing `MarkerNode` `\\*`, riding as the `MilestoneNode`'s\n * FOLLOWING SIBLINGS (a `MilestoneNode` is a `DecoratorNode`, so it cannot hold children\n * either) — except the glyphs themselves are unconditional: a milestone always shows its\n * opening/self-closing pair, with only the middle attribute text coming and going.\n * - **Excluded from data paths**: textType \"attribute\" text never enters OT content ops or the\n * editor→USJ conversion; the Tier-2 fragment is the one place it DOES flow, so edited bytes\n * re-tokenize back into node state (extractAttributes / scanMilestone).\n *\n * ## Keeping the cache honest\n *\n * Builders construct the run (usj-editor.adaptor's `createChar`/`addAttributes`/\n * `addVerseAttributes`; transforms do not run on `setEditorState`). A char span's own run, a\n * verse's `\\va`/`\\vp` runs, and a milestone's run all follow the identical contract through the\n * shared `$syncDisplayRun` driver (displayRunSync.utils.ts), parameterized by each kind's own\n * descriptor rather than defined in this module — re-deriving the run whenever its owner is\n * dirtied, healing remote collab updates (the collab materializer's `$createMilestone` builds a\n * BARE `MilestoneNode` with no run at all — delta-apply-update.utils.ts) and structure surgery.\n * While the collapsed caret sits inside the run the sync leaves it alone (mid-edit grace); the\n * marker-edit engine settles it on caret departure by pending the edited run into its Tier-2\n * completion path — the displayed bytes re-tokenize back into node state (last-write-wins,\n * uniformly across chars, verses, and milestones), and a milestone whose run was deleted OUTRIGHT\n * (the shared `$runEntirelyAbsent` check, displayRunSync.utils.ts, parameterized by the milestone\n * descriptor) is itself removed, since the run is its entire byte representation. `MilestoneNode`\n * is the SAME type in every mode — unlike the char/verse EDITABLE node types, which never appear\n * outside editable mode — so its sync is registered only in `MarkerEditPlugin.tsx`, which is itself\n * markerMode-\"editable\"-gated, to keep visible/hidden mode's `ImmutableTypedTextNode`-based\n * milestone runs (built by the adaptor, never edited) untouched.\n */\nimport { $isCanonicalMarkerNode, $isMarkerNode } from \"../features/MarkerNode.js\";\nimport { textTypeState } from \"../collab/delta.state.js\";\nimport { $isAttributeRunNode } from \"./AttributeRunNode.js\";\nimport { $isCharNode } from \"./CharNode.js\";\nimport { MS_NON_ATTRIBUTE_PROPS } from \"./MilestoneNode.js\";\nimport { getEditableCallerText, openingMarkerText } from \"./node.utils.js\";\nimport { $isVerseNode } from \"./VerseNode.js\";\nimport { $getState, $isTextNode } from \"lexical\";\n/** USJ artifacts that are not USFM attribute bytes and must never display. */\nexport const ATTRIBUTE_EXCLUDED_KEYS = new Set([\"closed\"]);\n/**\n * The canonical PT9 byte form of an attribute set, including the leading `|` — or `\"\"` when\n * nothing displays. A lone attribute that IS the marker's default collapses to the bare value\n * (`|gloss`); everything else is explicit `name=\"value\"` pairs, double-quoted, single-spaced,\n * insertion order. Values are kept byte-exact (ParatextData treats trailing space as value).\n */\nexport function canonicalAttributeText(attributes, defaultAttributeName) {\n const entries = Object.entries(attributes).filter(([name, value]) => value !== undefined && !ATTRIBUTE_EXCLUDED_KEYS.has(name));\n if (entries.length === 0)\n return \"\";\n // The bare-value collapse requires a NON-EMPTY value: a lone `|` is a byte sequence the\n // tokenizer refuses outright (`parseAttributeText`, PT9 parity), so collapsing an empty\n // default value displayed a run whose settle re-read it as plain content — the attribute\n // name vanished from the file and a stray `|` landed in the scripture text. The explicit\n // `name=\"\"` form keeps the name on screen, and a refused re-tokenize then degrades to\n // visible literal bytes instead of silently corrupting.\n if (entries.length === 1 && entries[0][0] === defaultAttributeName && entries[0][1] !== \"\")\n return `|${entries[0][1]}`;\n return `|${entries.map(([name, value]) => `${name}=\"${value}\"`).join(\" \")}`;\n}\n/**\n * Re-keys `attributes` into `attributeOrder`, appending any name the order does not mention after\n * the ones it does, in their existing order. Both kinds of mismatch are expected rather than\n * exceptional, so neither is an error: the settle re-derives a milestone's attributes from its\n * DISPLAYED BYTES, so an edit can drop a name the order still lists or add one it never knew\n * about, and the surviving names must keep their authored positions either way.\n *\n * Membership is tested by presence, not against `undefined`, so a genuinely empty value (`sid=\"\"`)\n * keeps its authored slot rather than being silently relocated to the end. `Object.hasOwn` rather\n * than `in`: USFM attribute names are unconstrained, so an attribute literally named `toString` or\n * `constructor` reads as already-present on a plain object and would be dropped from the output —\n * deleting the author's bytes — while `in attributes` would copy a native function into the bag.\n */\nexport function orderedAttributes(attributes, attributeOrder) {\n if (!attributeOrder || attributeOrder.length === 0)\n return attributes;\n const ordered = {};\n attributeOrder.forEach((name) => {\n if (Object.hasOwn(attributes, name))\n ordered[name] = attributes[name];\n });\n Object.entries(attributes).forEach(([name, value]) => {\n if (!Object.hasOwn(ordered, name))\n ordered[name] = value;\n });\n return ordered;\n}\n/**\n * The order `markerObject` authored its attributes in — or `undefined` when that is already the\n * CANONICAL order, which is `sid`, then `eid`, then everything else in the order it appeared.\n *\n * Returning `undefined` for the canonical case is what keeps this change invisible to every\n * document that does not need it: `MilestoneNode.attributeOrder` then stays absent, so canonically\n * ordered milestones serialize byte-identically to before and no stored state has to be migrated.\n *\n * Which names take part is defined by {@link MS_NON_ATTRIBUTE_PROPS}: `type`, `marker`, and\n * `content` are never attribute bytes; `sid` and `eid` are ordinary keys of the USJ marker object\n * that render into the same `|…` run as `who`, so they order against the rest with no special case.\n */\nexport function milestoneAttributeOrder(markerObject) {\n const authored = Object.keys(markerObject).filter((name) => !MS_NON_ATTRIBUTE_PROPS.includes(name));\n const canonical = [\n ...authored.filter((name) => name === \"sid\"),\n ...authored.filter((name) => name === \"eid\"),\n ...authored.filter((name) => name !== \"sid\" && name !== \"eid\"),\n ];\n return authored.every((name, index) => name === canonical[index]) ? undefined : authored;\n}\n/**\n * The attribute object a milestone's canonical display text is derived from: `sid`/`eid` folded\n * in first (their real USJ-object positions), then whatever else the marker carries (chiefly\n * `who`). Shared by usj-editor.adaptor's `addAttributes` (building the run from a `MarkerObject`)\n * and the milestone descriptor's `expectedPieces` (displayRun/displayRunRegistry.ts, healing it\n * from a live `MilestoneNode`'s fields through the shared `$syncDisplayRun` driver) so the two\n * sites — one USJ-shaped, one node-shaped — can never drift on WHICH fields make up a milestone's\n * displayed attributes.\n *\n * `attributeOrder` overrides that sid-first default with the order the document actually authored\n * ({@link milestoneAttributeOrder}), which Paratext 9 preserves and the USJ-to-USFM writer emits\n * verbatim — so folding in a fixed order would rewrite bytes in the file. It is `undefined` for\n * every milestone whose source was already canonical, which is the overwhelming majority.\n */\nexport function milestoneAttributes(sid, eid, unknownAttributes, attributeOrder) {\n return orderedAttributes(\n // Presence, not truthiness: an authored `sid=\"\"` is a byte the document holds, and folding it\n // out here deletes it from the displayed run — which a settle then re-derives node state from,\n // so the empty value would be gone from the file. Matches `orderedAttributes`' own `in` test\n // directly above, which exists for exactly this reason.\n {\n ...(sid !== undefined && { sid }),\n ...(eid !== undefined && { eid }),\n ...unknownAttributes,\n }, attributeOrder);\n}\n/**\n * `char`'s own closing glyph among its direct children, if any — the display run's insertion\n * anchor, and the tree signal for whether a run may exist at all. A span whose closing glyph is\n * skipped (a `closed=\"false\"` span — the state that makes footnote/cross-ref content chars and any\n * other genuinely-unclosed span render closer-less) or simply absent never renders one: `createChar`\n * (usj-editor.adaptor) never builds a run there, so the sync must not fabricate one either —\n * deriving the rule from tree shape rather than viewOptions also keeps the sync a no-op outside\n * editable mode, where char spans carry no MarkerNode glyphs at all.\n */\nexport function $charClosingGlyph(char) {\n return char\n .getChildren()\n .find((child) => $isMarkerNode(child) &&\n child.getMarkerSyntax() === \"closing\" &&\n child.getMarker() === char.getMarker());\n}\n/**\n * True when `char` carries attribute bytes that Tier-2 re-tokenization can never recover: real\n * (non-`closed`) attributes with NEITHER a closing glyph ({@link $charClosingGlyph}) NOR an\n * existing display run ({@link $charAttributeDisplayNode}) anywhere among its children. EITHER\n * anchor alone is enough to recover: a live display run carries the bytes into the fragment\n * regardless of the closer (a closer edit — deleted, damaged — re-tokenizes and settles, possibly\n * degrading to literal content, exactly like any other char content); a live closing glyph gives\n * `extractAttributes` a well-defined close event even if the run itself was just deleted (settles\n * to no attributes). Only when BOTH are absent — a `closed=\"false\"` span skips the glyph AND never\n * gets a run built for it — does an attribute such as `link-href` on an unclosed span have no\n * visible representation anywhere in the tree for the Tier-2 fragment builder to pick up. (An\n * explicitly-closed `\\xt`, by contrast, renders its closing glyph, so its attribute run IS built\n * and the span is recoverable.) A span with unrecoverable attributes must stay a Tier-2 sentinel\n * (preserve-or-refuse, tier2Rebuild.utils.ts): recursing into it would silently drop the attribute.\n */\nexport function $hasUnrecoverableAttributes(char) {\n const attributes = char.getUnknownAttributes();\n if (!attributes)\n return false;\n const hasRealAttributes = Object.keys(attributes).some((name) => name !== \"closed\");\n if (!hasRealAttributes)\n return false;\n return $charClosingGlyph(char) === undefined && $charAttributeDisplayNode(char) === undefined;\n}\n/**\n * `char`'s direct-child display run — the TextNode tagged textType \"attribute\" — or `undefined`\n * if none exists.\n */\nexport function $charAttributeDisplayNode(char) {\n return char\n .getChildren()\n .find((child) => $isTextNode(child) && $getState(child, textTypeState) === \"attribute\");\n}\n/**\n * A verse attribute marker's run pieces — opener `MarkerNode` (matching `marker`), value TextNode\n * (textType \"attribute\"), closer `MarkerNode` — scanned tolerantly in their fixed order starting\n * immediately after `after`, with EACH piece individually optional. Mirrors\n * {@link $milestoneAttributeRunPieces}: a mid-edit tree can be missing any subset — deleting just\n * the value leaves opener + closer debris — and the tolerant scan lets callers recognize and grace\n * that partial state and repair only the genuinely missing pieces around whatever survives, never\n * duplicating a leftover. The old all-or-nothing model returned \"no run at all\" for a\n * value-deleted run and re-derived a whole new opener/value/closer over the surviving debris on the\n * next sync (the value-deletion resurrect/duplicate bug).\n *\n * When `after`'s immediately following sibling is an `AttributeRunNode` whose `runKind` matches\n * `marker`, the SAME tolerant scan runs over the wrapper's CHILDREN instead of `after`'s siblings\n * — a wrapper's children are the run's pieces in the identical fixed order, so redirecting the\n * cursor's starting point is the only change needed. The adaptor always builds this shape now; the\n * sync still heals whichever shape — loose siblings (a pre-flip editor state, an undo stack, or a\n * collab-materialized bare owner) or an existing wrapper — is actually in the tree.\n *\n * A glyph is a piece only while its RENDERED BYTES still spell what its state describes\n * ({@link $isCanonicalMarkerNode}). Editing a glyph's characters leaves its marker and syntax\n * untouched, so a state-only scan reports a damaged `\\va` (the `*` deleted) as a perfectly good\n * closer: the run reads canonical here while the marker engine holds the same glyph pending,\n * and that standing disagreement is what silently suppressed the run's mid-edit caret grace —\n * `$runDiverges` saw nothing wrong, so the settle re-tokenized the whole paragraph out from\n * under the caret two keystrokes into the edit. Byte-damaged glyphs are therefore reported\n * ABSENT, which is the truth the rest of the pipeline already knows how to handle: the run\n * diverges, the caret graces it, and departure settles it.\n *\n * Exported (mirrors {@link $milestoneAttributeRunPieces}) so `markerEditTier1.utils.ts`'s\n * deletion-settle path (`packages/platform`) can locate a verse's wrapper(s) directly to detect\n * and clean up an emptied husk.\n */\nexport function $verseAttributeRunPieces(after, marker) {\n return $attributeMarkerRunPieces(after.getNextSibling(), marker);\n}\n/** The separator-run alphabet — plain space and NBSP, the whitespace class the marker-edit\n * engine's separator runs are built from. */\nconst TYPED_SPACING_ONLY_REGEX = /^[ \\u00A0]+$/;\n/**\n * Whether a display-run OPENING glyph's bytes are canonical, or canonical plus trailing TYPED\n * SPACING. A space typed at the end of a run's opening glyph is the same gesture as one typed\n * beside its value — the typed-space-stays rule: the writer emits structural whitespace itself,\n * so the byte never reaches the file. Reporting the glyph byte-damaged instead routed the\n * keystroke into a settle that discarded the space while the caret advanced past the run — a\n * keystroke accepted and then discarded (\"no silent no-ops\"). Openers only: a closer's `*` is\n * its final byte, so trailing bytes there genuinely respell it. Only for glyphs the CALLER knows\n * are run pieces — a char or para opener's spacing story belongs to its separator machinery\n * (markerSeparators.utils.ts), never to this license.\n *\n * Read-only: safe inside `editor.update()` or either read form.\n */\nexport function $isCanonicalRunOpenerGlyph(node) {\n if ($isCanonicalMarkerNode(node))\n return true;\n if (node.getMarkerSyntax() !== \"opening\")\n return false;\n const canonical = openingMarkerText(node.getMarker(), node.getNested());\n const text = node.getTextContent();\n return text.startsWith(canonical) && TYPED_SPACING_ONLY_REGEX.test(text.slice(canonical.length));\n}\n/**\n * The shared tolerant scan behind {@link $verseAttributeRunPieces} and\n * {@link $noteCategoryRunPieces}: an attribute MARKER's run pieces — opener `MarkerNode`\n * (matching `marker`), value TextNode (textType \"attribute\"), closer `MarkerNode` — read in their\n * fixed order starting at `cursor`, each piece individually optional, descending into an\n * `AttributeRunNode` wrapper whose `runKind` matches `marker` when one sits at `cursor`. The two\n * public entry points differ only in where the run RIDES (a verse's following siblings vs a\n * note's children after the caller), which is entirely captured by the starting cursor.\n */\nfunction $attributeMarkerRunPieces(cursor, marker) {\n let opener;\n let value;\n let closer;\n let wrapper;\n if ($isAttributeRunNode(cursor) && cursor.getRunKind() === marker) {\n wrapper = cursor;\n cursor = cursor.getFirstChild();\n }\n if ($isMarkerNode(cursor) &&\n cursor.getMarkerSyntax() === \"opening\" &&\n cursor.getMarker() === marker &&\n // Typed-spacing licensed ({@link $isCanonicalRunOpenerGlyph}): a trailing space typed on the\n // opener is at rest, not byte damage.\n $isCanonicalRunOpenerGlyph(cursor)) {\n opener = cursor;\n cursor = cursor.getNextSibling();\n }\n // A MarkerNode is itself a TextNode subclass, so the attribute-state check (never set on a\n // glyph) is what keeps a closer from being misread as the value.\n if ($isTextNode(cursor) && $getState(cursor, textTypeState) === \"attribute\") {\n value = cursor;\n cursor = cursor.getNextSibling();\n }\n if ($isMarkerNode(cursor) &&\n cursor.getMarkerSyntax() === \"closing\" &&\n cursor.getMarker() === marker &&\n $isCanonicalMarkerNode(cursor))\n closer = cursor;\n return { opener, value, closer, wrapper };\n}\n/**\n * The TextNode carrying a note's EDITABLE caller (` + ` with an NBSP tail —\n * `getEditableCallerText`), skipping any leading opening glyph(s) — the anchor a note's `\\cat`\n * run scans from and is inserted after. `undefined` outside the expanded editable shape: a\n * collapsed note renders its caller as a DecoratorNode and deliberately shows no category run,\n * and visible/hidden modes build no editable caller at all. Deriving the anchor from tree shape\n * (rather than viewOptions) keeps the cat sync a structural no-op in every mode that never\n * builds the run, the same rule {@link $charClosingGlyph} applies for a char span's run.\n */\nexport function $noteEditableCallerNode(note) {\n const children = note.getChildren();\n let index = 0;\n while (index < children.length) {\n const child = children[index];\n if (!$isMarkerNode(child) || child.getMarkerSyntax() !== \"opening\")\n break;\n index++;\n }\n const caller = children[index];\n if ($isTextNode(caller) && caller.getTextContent() === getEditableCallerText(note.getCaller()))\n return caller;\n return undefined;\n}\n/**\n * A note's `\\cat` category display run — the same opener/value/closer triplet shape a verse's\n * `\\va`/`\\vp` runs take ({@link $verseAttributeRunPieces}), riding as the note's CHILDREN\n * directly after the editable caller (a `NoteNode` is an ElementNode, so unlike the leaf owners\n * its run needs no sibling position). Empty pieces when the note has no editable caller anchor —\n * the collapsed and non-editable shapes, which never carry a run.\n */\nexport function $noteCategoryRunPieces(note) {\n const caller = $noteEditableCallerNode(note);\n if (!caller)\n return {};\n return $attributeMarkerRunPieces(caller.getNextSibling(), \"cat\");\n}\n/**\n * The plain TextNode carrying an editable chapter's `\\c N` glyph — its first child — the anchor\n * a chapter's `\\ca` run scans from and is inserted after. Accepts the node while it remains a\n * plain (non-glyph, non-attribute) TextNode even when its BYTES are mid-edit — unlike the note's\n * caller, the chapter glyph is itself editable display text, so an exact-text requirement would\n * dissolve the anchor on the first keystroke of a number rename. `undefined` when the glyph text\n * was deleted outright or the chapter is not the editable element shape.\n */\nexport function $chapterGlyphTextNode(chapter) {\n const first = chapter.getFirstChild();\n if (!$isTextNode(first) || $isMarkerNode(first))\n return undefined;\n if ($getState(first, textTypeState) === \"attribute\")\n return undefined;\n return first;\n}\n/**\n * A chapter's `\\ca` alternate-number display run — the identical triplet shape and child\n * position as a note's `\\cat` run ({@link $noteCategoryRunPieces}): an editable `ChapterNode` is\n * an ElementNode, and the run rides directly after its `\\c N` glyph text, where the file puts\n * the span (`\\c 1 \\ca 2\\ca*`). Empty pieces when the glyph anchor is gone.\n */\nexport function $chapterAltnumberRunPieces(chapter) {\n const glyph = $chapterGlyphTextNode(chapter);\n if (!glyph)\n return {};\n return $attributeMarkerRunPieces(glyph.getNextSibling(), \"ca\");\n}\n/** The child a chapter's `\\cp` run is anchored after: `\\ca`'s wrapper (or, while caret-grace\n * defers the wrap, its loose closer), else the `\\c N` glyph text — the chapter twin of\n * `$verseRunAnchor`'s `\\vp` arm. Shared by the scanner and the writer so the two can never\n * disagree about where the run belongs. `undefined` when the glyph anchor is gone. */\nexport function $chapterCpAnchor(chapter) {\n const glyph = $chapterGlyphTextNode(chapter);\n if (!glyph)\n return undefined;\n const ca = $attributeMarkerRunPieces(glyph.getNextSibling(), \"ca\");\n return ca.wrapper ?? ca.closer ?? glyph;\n}\n/**\n * A chapter's `\\cp` published-number display run — opener glyph + NBSP-prefixed value, with NO\n * closing glyph: `\\cp`'s span closes implicitly at the next block boundary in the file, so its\n * displayed run is bounded by its wrapper alone. Rides directly after the `\\ca` run (or the\n * `\\c N` glyph when there is none) — document order `ca` before `cp`, the order ParatextData\n * preserves on disk. Empty pieces when the glyph anchor is gone.\n */\nexport function $chapterPubnumberRunPieces(chapter) {\n const anchor = $chapterCpAnchor(chapter);\n if (!anchor)\n return {};\n return $attributeMarkerRunPieces(anchor.getNextSibling(), \"cp\");\n}\n/**\n * The VerseNode whose `\\va`/`\\vp` SOURCE span `node` is content of, or `undefined`. A settled\n * empty run leaves a standalone `char va`/`char vp` span in the verse's run position (displayed\n * `\\va \\va*`); a value typed into it is an ordinary content edit that no textType tag marks, so\n * the pend decision must key on the SITE — content of a va/vp span whose sibling chain reaches\n * back to a verse over run pieces only — for departure's re-tokenize to fold the bytes onto the\n * verse (the tokenizer's attrCapture). A va/vp span NOT in a verse's run position re-tokenizes\n * to itself (fixed point) and settles nothing — pending it is harmless. A preceding run piece may\n * be loose (a bare `MarkerNode`/attribute `TextNode`) or a whole `AttributeRunNode` wrapper\n * crossed in one step — the adaptor always builds a wanted run wrapped now, so a `\\vp` span\n * sitting behind a WRAPPED `\\va` run (the only shape an altnumber-bearing verse can have\n * post-migration) must still walk past it to reach the verse. Sibling walk to `$ownerOfRunPiece`'s\n * verse descriptors (displayRunRegistry.ts): those start from a run PIECE — including an opening\n * glyph, the shape MarkerEditPlugin.tsx's MarkerNode transform re-drives its sync/pend from — and\n * walk back to find the owning verse; this one starts from a SOURCE SPAN's content text and walks\n * back to find the owning verse for the pend decision. Both classify the same run-piece shapes\n * over the same sibling chain and must keep agreeing on what counts as one.\n */\nexport function $verseOfAttributeSourceText(node) {\n const span = node.getParent();\n if (!$isCharNode(span))\n return undefined;\n const marker = span.getMarker();\n if (marker !== \"va\" && marker !== \"vp\")\n return undefined;\n for (let prev = span.getPreviousSibling(); prev; prev = prev.getPreviousSibling()) {\n if ($isVerseNode(prev))\n return prev;\n const isRunPiece = ($isMarkerNode(prev) && (prev.getMarker() === \"va\" || prev.getMarker() === \"vp\")) ||\n ($isTextNode(prev) && $getState(prev, textTypeState) === \"attribute\") ||\n ($isCharNode(prev) && (prev.getMarker() === \"va\" || prev.getMarker() === \"vp\")) ||\n $isAttributeRunNode(prev);\n if (!isRunPiece)\n return undefined;\n }\n return undefined;\n}\n/**\n * A milestone's display-run pieces, scanned tolerantly in their fixed order — opening\n * `MarkerNode` (matching `milestone`'s own marker), attribute TextNode (textType \"attribute\"),\n * self-closing `MarkerNode` — with EACH piece individually optional: a bare collab-materialized\n * milestone has none of them, and a mid-edit tree can be missing any subset (only the opening\n * deleted leaves attribute + closer debris; only the closer deleted leaves opening + attribute).\n * The tolerant scan lets callers repair only the genuinely missing/stale pieces around whatever\n * survives — never duplicating a leftover — and lets the shared `$runEntirelyAbsent` check\n * (displayRunSync.utils.ts, parameterized by the milestone descriptor) distinguish \"every byte of\n * the run deleted\" from a partial mangle. Exported as the single definition of \"a milestone's run\"\n * — the Tier-2 rebuild's `$milestoneDisplayRun` delegates to it so the sync and the rebuild can\n * never disagree about which siblings make up the run.\n *\n * When `milestone`'s immediately following sibling is an `AttributeRunNode` whose `runKind` is\n * `\"milestone\"`, the SAME tolerant scan runs over the wrapper's CHILDREN instead of `milestone`'s\n * siblings — a wrapper's children are the run's pieces in the identical fixed order, so\n * redirecting the cursor's starting point is the only change needed. The adaptor always builds\n * this shape now; the sync still heals whichever shape — loose siblings (a pre-flip editor state,\n * an undo stack, or a collab-materialized bare milestone) or an existing wrapper — is actually in\n * the tree.\n *\n * As in {@link $verseAttributeRunPieces}, a glyph counts as a piece only while its rendered bytes\n * still spell what its state describes ({@link $isCanonicalMarkerNode}) — a byte-damaged glyph is\n * reported absent so the run diverges and the caret can grace the mid-edit shape.\n */\nexport function $milestoneAttributeRunPieces(milestone) {\n let opening;\n let attribute;\n let closing;\n let wrapper;\n let cursor = milestone.getNextSibling();\n if ($isAttributeRunNode(cursor) && cursor.getRunKind() === \"milestone\") {\n wrapper = cursor;\n cursor = cursor.getFirstChild();\n }\n if ($isMarkerNode(cursor) &&\n cursor.getMarkerSyntax() === \"opening\" &&\n cursor.getMarker() === milestone.getMarker() &&\n // Typed-spacing licensed ({@link $isCanonicalRunOpenerGlyph}): a trailing space typed on the\n // opener is at rest, not byte damage.\n $isCanonicalRunOpenerGlyph(cursor)) {\n opening = cursor;\n cursor = cursor.getNextSibling();\n }\n if ($isTextNode(cursor) && $getState(cursor, textTypeState) === \"attribute\") {\n attribute = cursor;\n cursor = cursor.getNextSibling();\n }\n if ($isMarkerNode(cursor) &&\n cursor.getMarkerSyntax() === \"selfClosing\" &&\n $isCanonicalMarkerNode(cursor))\n closing = cursor;\n return { opening, attribute, closing, wrapper };\n}\n","/**\n * Nested inline-marker glyphs: the single place that owns HOW the `+` prefix (`\\+w …\\+w*`) is\n * represented and kept in sync.\n *\n * ## The representations (who owns what)\n *\n * - **Tree containment is the truth.** A char span is nested iff its parent is another CharNode\n * ({@link $isNestedCharNode}) — exactly USJ/USX's model, where nesting is element containment\n * and no `+` exists in any marker name.\n * - **Markers are always CLEAN** (`\"w\"`, never `\"+w\"`), in CharNode state, in USJ, and in the\n * OT/collab delta (which conveys nesting by char-ARRAY position, outermost-first; see\n * `$buildCharItem` in editor-delta.adaptor and `$createNestedChars` in delta-apply-update —\n * both in `shared-react`'s collab plugin).\n * - **Only the rendered glyph text carries the `+`** — USFM's serialization of nesting, and PT9's\n * on-screen display for USFM ≤3.0. `MarkerNode` caches it: Lexical renders a TextNode's stored\n * `__text` (there is no computed-text hook, and nothing re-runs when an ANCESTOR moves), so the\n * `+` must be baked into `__text`. `MarkerNode.__nested` is the flag that `getMarkerText`\n * derives it from — see MarkerNode.ts, which points back here.\n *\n * ## Keeping the cache honest\n *\n * Because `__text` is a cache of tree-derived state, every path that BUILDS glyphs sets `nested`\n * at construction (the USJ load adaptor's `createChar`, `$splitCharNodeAt`, the marker-apply\n * paths, the collab materializer) — construction-time correctness matters because transforms do\n * not run on `setEditorState` (initial load / restored states render straight from serialized\n * `__text`). {@link $syncNestedGlyphs} is the safety net for everything AFTER load: registered as\n * a CharNode transform (CharNodePlugin in `shared-react`), it re-derives each glyph's `nested`\n * from tree position whenever a span is dirtied, so structure surgery that forgets to refresh\n * glyphs (a move, an unwrap, a merge) self-heals instead of leaving a stale `+` that Tier-2\n * re-tokenization would misread (`\\+w` with nothing open parses as an unknown marker; a missing\n * `+` flattens the nesting via close-on-bare).\n */\nimport { $isMarkerNode } from \"../features/MarkerNode.js\";\nimport { $isCharNode } from \"./CharNode.js\";\nimport { $getLogicalParent } from \"./node.utils.js\";\n/** Whether `char` is a nested char span — its LOGICAL parent is another char span. Read through\n * `$getLogicalParent`, never the raw parent: an annotation `TypedMarkNode` wrapped around the\n * span is presentation-transparent in USJ, and a raw-parent read made a genuinely nested span\n * read as un-nested — the sync then stripped its glyphs' `+`, and the bare inner marker\n * re-tokenized as closing the outer span. The one derivation rule for the glyph `+`; every\n * representation above follows from this. */\nexport function $isNestedCharNode(char) {\n return $isCharNode($getLogicalParent(char));\n}\n/**\n * Which char span a glyph that is a DIRECT child of `char` describes:\n *\n * - `char` itself (`glyph.getMarker() === char.getMarker()`) — the span's own opener/closer;\n * nested iff `char` is nested.\n * - A nested CHILD span with that marker (the collab-flattened shape, where an inner span's\n * glyphs sit as siblings around the inner CharNode) — nested by construction.\n * - Neither — e.g. a milestone's display run (`\\qt-s` … `\\*`) rendered inside the span. Not a\n * char glyph at all: milestones never take the `+`, so it is left untouched.\n *\n * Exported as the shared \"is this a char-span glyph, and of which span?\" classifier — the\n * display-separator sync (markerSeparators.utils.ts) uses the same distinction to know which\n * opening glyphs take a separator.\n *\n * @returns the nested value the glyph must carry, or `undefined` to leave the glyph alone.\n */\nexport function $charGlyphNestedValue(glyph, char) {\n if (glyph.getMarkerSyntax() === \"selfClosing\")\n return undefined;\n const marker = glyph.getMarker();\n if (marker === char.getMarker())\n return $isNestedCharNode(char);\n const describesNestedChild = char\n .getChildren()\n .some((child) => $isCharNode(child) && child.getMarker() === marker);\n return describesNestedChild ? true : undefined;\n}\n/**\n * Re-derive the `nested` flag (and thereby the `+` in the glyph text) of `char`'s direct marker\n * glyphs from tree position. Idempotent — `setNested` writes only on change, so a second pass is\n * a no-op and the registering transform converges.\n *\n * @param char - The char span whose glyphs to sync. Must be called inside `editor.update()`.\n */\nexport function $syncNestedGlyphs(char) {\n // An earlier transform in the same pass may have merged/removed the span (adjacent-span\n // combining); a detached span has no tree position to derive from.\n if (!char.isAttached())\n return;\n char.getChildren().forEach((child) => {\n if (!$isMarkerNode(child))\n return;\n const nested = $charGlyphNestedValue(child, char);\n if (nested !== undefined)\n child.setNested(nested);\n });\n}\n","/**\n * Editable-mode display separators after opening char glyphs: the single place that owns HOW the\n * space the user sees after `\\nd` is represented and kept in sync. Sibling of\n * nestedGlyphs.utils.ts, which owns the glyph `+` the same way.\n *\n * ## The convention\n *\n * In editable marker mode an opening char glyph (`\\nd`) is followed on screen by a separator —\n * the space PT9 shows and the serializer writes after the marker. That separator is\n * PRESENTATION-ONLY state: the USFM writer emits the space after an opening marker structurally\n * and the tokenizer consumes it, so no separator ever lives in USJ content or in the saved bytes\n * as data. In the editor it is an NBSP (never a plain space, so it cannot word-wrap away from its\n * glyph), stored as:\n *\n * - a prefix of the following text (`\\nd` + `⍽one`) when the glyph is directly followed by plain\n * text — the shape `createChar` (usj-editor.adaptor), `$splitCharNodeAt`, and the marker-apply\n * paths build, and the reverse adaptor strips on save;\n * - a standalone NBSP text node when the glyph is directly followed by an element (a nested char\n * span, note, milestone, or verse: `\\nd` + `⍽` + `\\+wj …`) — NBSP-only text nodes are\n * presentation-only by convention (`$shouldIgnoreNodeForContentIndexes`) and dropped by the\n * editor→USJ conversion.\n *\n * ## Keeping it in sync\n *\n * Builders construct the separator (transforms do not run on `setEditorState`, so loaded states\n * must render correctly as-is), and {@link $syncOpenerSeparators} — registered as a CharNode\n * transform in CharNodePlugin — re-derives it whenever a span is dirtied, healing paths that\n * restructure spans without rebuilding them through an adaptor. Deleting the separator is\n * semantically a no-op (the writer emits the space regardless), so healing it back is display\n * canonicalization, exactly like Tier-2's rebuild produces — but deleting must still be\n * ALLOWED: while the collapsed caret sits at the deletion point the sync leaves the gap alone\n * (mid-edit grace), and the marker-edit engine settles it back on caret departure by pending\n * spans reported by {@link $hasCaretHeldSeparatorGap} into its Tier-2 completion path.\n *\n * Only char-span glyphs take a separator — a milestone's display run inside a span is left\n * alone — so which glyphs qualify is decided by the same classifier the nested-`+` sync uses\n * ({@link $charGlyphNestedValue}).\n */\nimport { $isMarkerNode } from \"../features/MarkerNode.js\";\nimport { textTypeState } from \"../collab/delta.state.js\";\nimport { $charGlyphNestedValue } from \"./nestedGlyphs.utils.js\";\nimport { NBSP } from \"./node-constants.js\";\nimport { $createTextNode, $getSelection, $getState, $isRangeSelection, $isTextNode, TextNode, } from \"lexical\";\n/**\n * Whether `node` is text that may carry a char-span separator NBSP as its own PREFIX: exactly a\n * plain `TextNode` — subclasses (`VerseNode`, `ImmutableUnmatchedNode`, `MarkerNode`) render their\n * own marker bytes, and splicing an NBSP into those rewrites a glyph — and not an attribute\n * display run (textType \"attribute\"), whose `|…` bytes are engine-owned canonical output that an\n * NBSP prefix would corrupt. THE one predicate for every site that splices a separator into\n * leading text ({@link $openerSeparatorGap} here, plus the continuation/absorb span builders in\n * charGlyphs.utils.ts and charStack.utils.ts), so the rule cannot drift between them; anything\n * else takes a standalone NBSP spacer instead.\n *\n * Read-only: safe inside `editor.update()` or either read form.\n */\nexport function $isSeparatorPrefixHostText(node) {\n return ($isTextNode(node) &&\n node.getType() === TextNode.getType() &&\n $getState(node, textTypeState) !== \"attribute\");\n}\n/**\n * Where a separator is missing after `opener` (a direct child of `char`):\n *\n * - `\"prefix\"` — the glyph is followed by plain text that lacks the NBSP prefix;\n * - `\"spacer\"` — the glyph is followed by an element (or, in the collab-flattened shape, a nested\n * span's opening glyph) with no standalone NBSP spacer between them;\n * - `undefined` — no separator is owed: the glyph is not a char-span glyph (a milestone's display\n * run), has nothing after it, sits directly before a non-nested glyph, or its separator exists.\n */\nfunction $openerSeparatorGap(opener, char) {\n if (opener.getMarkerSyntax() !== \"opening\")\n return undefined;\n // Only char-span glyphs take a separator (not a milestone's display run).\n if ($charGlyphNestedValue(opener, char) === undefined)\n return undefined;\n const next = opener.getNextSibling();\n if (next === null)\n return undefined;\n if ($isMarkerNode(next)) {\n // Opening glyph directly before another glyph: in the collab-flattened shape that next glyph\n // opens a nested span (`\\add\\+wj …`) and the separator goes between them. Any other adjacent\n // glyph (the span's own closer on a degenerate empty span) takes none.\n return $charGlyphNestedValue(next, char) === true ? \"spacer\" : undefined;\n }\n // Plain text directly after the glyph carries the separator as its prefix — see\n // $isSeparatorPrefixHostText for what qualifies and why.\n if ($isSeparatorPrefixHostText(next))\n return next.getTextContent().startsWith(NBSP) ? undefined : \"prefix\";\n // Element content (nested char span, note, milestone, verse), TextNode subclasses, and\n // attribute-run text: standalone NBSP spacer.\n return \"spacer\";\n}\n/**\n * The displayed bytes immediately after `char`'s first missing-separator site, or `undefined`\n * when every opening glyph's separator is present (or none is owed). This is the input the\n * tokenize-identity predicate (`separatorRemovalTokenizesIdentically`, the fragment tokenizer's\n * sibling) needs to decide whether the missing byte may be healed in place or the bytes now mean\n * something new and must re-tokenize. Read-only: call inside `editor.getEditorState().read(...)`\n * or an update.\n */\nexport function $openerSeparatorGapFollowingBytes(char) {\n if (!char.isAttached())\n return undefined;\n for (const child of char.getChildren()) {\n if (!$isMarkerNode(child))\n continue;\n if ($openerSeparatorGap(child, char) === undefined)\n continue;\n return child.getNextSibling()?.getTextContent() ?? \"\";\n }\n return undefined;\n}\n/**\n * Whether the collapsed caret sits at `opener`'s separator site — on the glyph itself, on the\n * span (an element point), or at the very start of the node after the glyph. This is where the\n * caret lands when the user deletes the separator, and deleting must always be allowed: while\n * the caret stays here the sync leaves the gap alone (mid-edit grace), and the marker-edit\n * engine settles the span back to canonical on caret departure (it pends spans reported by\n * {@link $hasCaretHeldSeparatorGap} and routes them to a Tier-2 rebuild, the same completion\n * path as a pending marker literal).\n */\nfunction $isCaretAtOpenerBoundary(opener, char) {\n const selection = $getSelection();\n if (!$isRangeSelection(selection) || !selection.isCollapsed())\n return false;\n const anchorNode = selection.anchor.getNode();\n if (anchorNode.is(opener) || anchorNode.is(char))\n return true;\n const next = opener.getNextSibling();\n return next !== null && anchorNode.is(next) && selection.anchor.offset === 0;\n}\n/**\n * Ensure every opening char glyph among `char`'s direct children is followed by its display\n * separator — except one whose separator site holds the collapsed caret (see\n * {@link $isCaretAtOpenerBoundary}). Idempotent — a healed span passes untouched, so the\n * registering transform converges.\n *\n * @param char - The char span whose separators to sync. Must be called inside `editor.update()`.\n */\nexport function $syncOpenerSeparators(char) {\n // An earlier transform in the same pass may have merged/removed the span.\n if (!char.isAttached())\n return;\n char.getChildren().forEach((child) => {\n if (!$isMarkerNode(child))\n return;\n const gap = $openerSeparatorGap(child, char);\n if (gap === undefined)\n return;\n if ($isCaretAtOpenerBoundary(child, char))\n return;\n if (gap === \"prefix\") {\n const next = child.getNextSibling();\n if ($isTextNode(next))\n next.setTextContent(NBSP + next.getTextContent());\n }\n else {\n child.insertAfter($createTextNode(NBSP));\n }\n });\n}\n/**\n * True when `char` has a separator gap the sync is deliberately leaving alone because the caret\n * sits at it (a just-deleted separator). The marker-edit engine pends such spans so caret\n * departure settles them back to canonical via Tier-2.\n */\nexport function $hasCaretHeldSeparatorGap(char) {\n if (!char.isAttached())\n return false;\n return char\n .getChildren()\n .some((child) => $isMarkerNode(child) &&\n $openerSeparatorGap(child, char) !== undefined &&\n $isCaretAtOpenerBoundary(child, char));\n}\n","/**\n * Unknown-node USFM byte rendering: the single place that owns HOW an `UnknownNode`'s opening\n * marker, attribute, and closing marker bytes are computed for read-only display. Sibling of\n * attributeDisplay.utils.ts (char/verse attribute display runs), following the same owning-module\n * shape, but for a different tree: `UnknownNode`'s kinds (figure, table/table:row/table:cell,\n * sidebar, periph, ref, optbreak) carry no USFM byte representation anywhere in the tree —\n * markers and attributes are simply invisible today. The renderer that flanks a node's existing\n * content children with the bytes this module computes lives elsewhere; this module is the pure\n * byte-computation function it calls.\n *\n * ## Why these bytes carry no round-trip obligation\n *\n * Every kind here stays a Tier-2 sentinel — unlike char spans and verses (attributeDisplay.utils.ts),\n * which grew live display runs that re-tokenize on edit. These bytes only need to be CORRECT\n * USFM, not byte-identical to whatever produced the node; nothing here ever re-tokenizes back\n * into node state, so there is no cache to keep honest and no sync to register.\n *\n * ## Per-kind shapes\n *\n * All four parts are OPAQUE bytes for the consumer to concatenate around the node's content,\n * never to parse. The `attributes` part carries only bytes that belong BETWEEN the opening and\n * the content (sidebar's `\\cat …\\cat*` marker run, periph's marker-line pipe pairs; `\"\"` for\n * every other kind). USFM pipe attributes on span-shaped kinds (the generic default, figure)\n * come AFTER content, directly before the closer, so those kinds populate `closingAttributes`\n * instead — kept as its own part (rather than folded into `closing`) so the consumer can style\n * the `|…` run dimmer than the closer glyph, matching PT9's attribute-run display. Concatenating\n * `closingAttributes + closing` reproduces the exact same bytes a single `closing` string used\n * to carry.\n *\n * - **Generic default** (any kind without a special case below): `\\{marker} ` opening, a\n * `closingAttributes` of {@link canonicalAttributeText} named pairs — always explicit\n * `name=\"value\"`, never the default-attribute collapse, because unknown kinds carry no\n * StyleInfo default to collapse against — and a `closing` of `\\{marker}*` (the char-span shape:\n * `\\zzz content|foo=\"bar\"\\zzz*`).\n * - **optbreak** — PT9 renders `\\optbreak` as the literal token `//`, not a marker: the opening\n * IS the bare text `//`, with no attributes and no closing glyph.\n * - **figure** — USFM 3.0 puts the caption first (`\\fig caption|src=\"…\"\\fig*`), so the attribute\n * bytes go in `closingAttributes`, ahead of the `closing` glyph. USX/USJ's `file` attribute is\n * USFM's `src` (the tokenizer performs the same rename in the other direction;\n * usfmFragmentToUsj.ts); rendering reverses it back so the bytes match what a `\\fig …\\fig*`\n * span actually carries in the file.\n * - **table / table:row / table:cell** — tables have no USFM pipe-attribute syntax at all; a\n * cell's `align`/`colspan` are ENCODED in the marker name itself (`\\thc3-4`), never rendered\n * as attribute bytes. The container contributes no bytes of its own; a row opens with `\\tr `;\n * a cell opens with its own marker, re-encoding a `colspan` back into the span suffix the\n * tokenizer trimmed off (marker `thc3` + colspan `2` → `\\thc3-4 `; see\n * {@link tableCellMarkerWithSpan}); neither ever closes.\n * - **sidebar** — `\\esb`/`\\esbe` bracket the block. A `category` attribute is not a pipe\n * attribute at all but its own char-shaped marker directly after `\\esb`\n * (`\\esb \\cat Missions\\cat*`), mirroring how the tokenizer folds a `\\cat` onto a receptive\n * sidebar (usfmFragmentToUsj.ts).\n * - **periph** — `alt` is a text-content attribute: its value renders as literal marker content\n * right after `\\periph` (`\\periph Title`) rather than a pipe attribute; any remaining\n * attributes (e.g. `id`) are ordinary named pairs. `periph` is an open-ended division marker,\n * so it never closes.\n * - **ref** — a generated wrapper around cross-reference target text; USJ invented this\n * container, USFM never carried it, so it contributes no bytes at all — only its child text\n * renders, as-is.\n *\n * ## Unterminated constructs render no closer\n *\n * Cutting across every kind above: a construct the source never terminated (`closed=\"false\"`) has\n * no closing bytes in the file, so it displays none. Char spans already work this way — see\n * `$charClosingGlyph` (attributeDisplay.utils.ts) — and the rule is the same one for the same\n * reason: a closer shown for bytes the document does not contain is a byte the user can neither\n * edit away nor save.\n */\nimport { canonicalAttributeText } from \"../usj/attributeDisplay.utils.js\";\n/** The USJ attribute name a figure's file reference is stored under — renamed from USFM's `src`\n * by the tokenizer (usfmFragmentToUsj.ts) on the way in; rendering reverses it back. */\nconst FIGURE_FILE_ATTRIBUTE = \"file\";\nconst FIGURE_SRC_ATTRIBUTE = \"src\";\n/** The cell attribute holding the span COUNT the tokenizer derived from the marker's trimmed\n * span suffix; re-encoded back into the opening marker rather than rendered as a pipe pair. */\nconst TABLE_CELL_COLSPAN_ATTRIBUTE = \"colspan\";\n/** The sidebar attribute that renders as its own `\\cat …\\cat*` marker rather than a pipe pair. */\nconst SIDEBAR_CATEGORY_ATTRIBUTE = \"category\";\n/** The periph attribute that renders as literal marker content rather than a pipe pair. */\nconst PERIPH_TEXT_CONTENT_ATTRIBUTE = \"alt\";\n/** The USJ metadata flagging a construct the source never terminated. */\nconst IMPLICITLY_CLOSED_ATTRIBUTE = \"closed\";\nconst IMPLICITLY_CLOSED_VALUE = \"false\";\n/**\n * Whether the source terminated this construct with its own closing bytes. An implicitly closed\n * one (`closed=\"false\"` — the tokenizer's mark for a sidebar the fragment or chapter boundary\n * auto-closed, and USX's same flag read back) has no closing bytes in the file, so it must display\n * none: char spans already follow exactly this rule (`$charClosingGlyph`,\n * attributeDisplay.utils.ts — `createChar` builds no closing glyph for such a span and the display\n * sync must not fabricate one). A closer shown for bytes the document does not contain is one the\n * user can neither edit away nor save.\n */\nfunction isExplicitlyClosed(attributes) {\n return attributes[IMPLICITLY_CLOSED_ATTRIBUTE] !== IMPLICITLY_CLOSED_VALUE;\n}\n/** `attributes` with `file` renamed to `src` in place — the USX/USJ naming reversed back to the\n * byte name a figure's file attribute is actually written with in USFM. Key order is preserved\n * (a `Object.entries` map, not a delete-then-add) so a renamed `file` renders wherever it\n * originally sat among the figure's other attributes. */\nfunction renameFigureFileToSrc(attributes) {\n return Object.fromEntries(Object.entries(attributes).map(([name, value]) => [\n name === FIGURE_FILE_ATTRIBUTE ? FIGURE_SRC_ATTRIBUTE : name,\n value,\n ]));\n}\n/**\n * The cell's opening marker with its span suffix re-encoded from `colspan`. Exported because a\n * cell's display bytes are built by `ImmutableTableCellNode`'s own adaptor path (the kind stopped\n * being an `UnknownNode`), and both builders must spell a spanning cell the same way. The tokenizer\n * (usfmFragmentToUsj.ts, table-cell assembly) splits a spanning cell marker apart on the way in —\n * `\\thc3-4` becomes marker `thc3` (span suffix trimmed off after the start column) plus colspan\n * `\"2\"` (the COUNT of columns spanned, end − start + 1) — so rendering the stored marker bare\n * would silently narrow the cell to a single column. Re-encoding reverses the split: end column =\n * start + count − 1. A `colspan` that is not a real span (absent, non-numeric, or below 2) or a\n * marker with no trailing start column to count from yields the bare marker rather than a garbage\n * suffix.\n */\nexport function tableCellMarkerWithSpan(marker, colspan) {\n if (marker === undefined)\n return undefined;\n const spanCount = Number(colspan);\n if (!Number.isInteger(spanCount) || spanCount < 2)\n return marker;\n // The trailing start column is found by a manual backward scan rather than `/(\\d+)$/`: an\n // end-anchored greedy digit run makes the regex engine retry the match from every position of a\n // long digit run (quadratic on adversarial markers like \"000…0X\"), while the scan is linear.\n // Byte-identical to the regex for every input — a marker that is ALL digits contributes the\n // whole string as its start column, exactly as `(\\d+)$` would match it.\n let digitsStart = marker.length;\n while (digitsStart > 0) {\n const charCode = marker.charCodeAt(digitsStart - 1);\n if (charCode < 0x30 || charCode > 0x39)\n break; // outside '0'..'9'\n digitsStart -= 1;\n }\n if (digitsStart === marker.length)\n return marker;\n return `${marker}-${Number(marker.slice(digitsStart)) + spanCount - 1}`;\n}\n/**\n * The USFM byte strings to render around an `UnknownNode`'s existing content children —\n * `opening` and `attributes` before the content, `closingAttributes` then `closing` after it (see\n * {@link UnknownDisplayParts} for what each part carries per kind) — computed purely from the\n * node's stored `tag` (USJ `type`), `marker`, and `unknownAttributes`. Pure function; read-only\n * display only (see module doc for why these bytes carry no round-trip obligation).\n *\n * @param tag - The `UnknownNode`'s USJ type (e.g. \"figure\", \"table:row\", \"optbreak\").\n * @param marker - The node's stored USFM marker, when the USJ shape carries one.\n * @param unknownAttributes - The node's stored attribute bag, when the USJ shape carries one.\n */\nexport function unknownDisplayParts(tag, marker, unknownAttributes) {\n const attributes = unknownAttributes ?? {};\n const closed = isExplicitlyClosed(attributes);\n switch (tag) {\n case \"optbreak\":\n return { opening: \"//\", attributes: \"\", closingAttributes: \"\", closing: \"\" };\n case \"ref\":\n case \"table\":\n // Generated wrapper (ref) and bare container (table): neither carries USFM bytes of its\n // own — a table's bytes live entirely on its table:row/table:cell children.\n return { opening: \"\", attributes: \"\", closingAttributes: \"\", closing: \"\" };\n case \"table:row\":\n return { opening: `\\\\${marker} `, attributes: \"\", closingAttributes: \"\", closing: \"\" };\n case \"table:cell\":\n // The marker name itself IS the cell's shape (`\\tc1`, `\\thc3-4`, …): `align` is already\n // encoded in it, and a `colspan` re-encodes into its span suffix (see\n // tableCellMarkerWithSpan) — neither ever renders as pipe-attribute bytes, which USFM\n // tables do not have.\n return {\n opening: `\\\\${tableCellMarkerWithSpan(marker, attributes[TABLE_CELL_COLSPAN_ATTRIBUTE])} `,\n attributes: \"\",\n closingAttributes: \"\",\n closing: \"\",\n };\n case \"figure\":\n // USFM 3.0 figures put the caption FIRST (`\\fig caption|src=\"…\"\\fig*`), so the attribute\n // bytes go in closingAttributes, ahead of the closing glyph — rendering them between the\n // opening and the caption would strand the caption after the attribute list, which is\n // invalid USFM. Kept as its own part (rather than folded into `closing`) so the caller can\n // style the `|…` run dimmer than the `\\fig*` glyph, matching PT9's attribute-run display.\n return {\n opening: `\\\\${marker} `,\n attributes: \"\",\n closingAttributes: canonicalAttributeText(renameFigureFileToSrc(attributes), undefined),\n closing: closed ? `\\\\${marker}*` : \"\",\n };\n case \"sidebar\": {\n const { [SIDEBAR_CATEGORY_ATTRIBUTE]: category, ...rest } = attributes;\n const categoryBytes = category === undefined ? \"\" : ` \\\\cat ${category}\\\\cat*`;\n return {\n opening: \"\\\\esb\",\n attributes: categoryBytes + canonicalAttributeText(rest, undefined),\n closingAttributes: \"\",\n closing: closed ? \"\\\\esbe\" : \"\",\n };\n }\n case \"periph\": {\n const { [PERIPH_TEXT_CONTENT_ATTRIBUTE]: alt, ...rest } = attributes;\n return {\n opening: `\\\\periph ${alt ?? \"\"}`,\n attributes: canonicalAttributeText(rest, undefined),\n closingAttributes: \"\",\n closing: \"\",\n };\n }\n default:\n // Char-span shape is the natural default for an attributed unknown span: content first,\n // pipe attributes directly before the closer (`\\zzz content|foo=\"bar\"\\zzz*`) — kept as its\n // own closingAttributes part (rather than folded into `closing`) so the caller can style\n // the `|…` run dimmer than the `\\zzz*` glyph, matching PT9's attribute-run display.\n return {\n opening: `\\\\${marker} `,\n attributes: \"\",\n closingAttributes: canonicalAttributeText(attributes, undefined),\n closing: closed ? `\\\\${marker}*` : \"\",\n };\n }\n}\n","/**\n * The display-run registry: one {@link DisplayRunDescriptor} per engine-owned display kind.\n *\n * Assembled HERE rather than in `nodes/usj` because a descriptor's byte derivation needs the\n * converters (`defaultMarkerAttribute`, `milestoneDefaultAttribute`) and `nodes/usj` must not\n * import from `converters/usfm`, which already imports FROM `nodes/usj`. This module sits above\n * both, so it can hold the assembly without a cycle — the same layering `plugins/PerfOperations`\n * uses. The drivers that CONSUME descriptors take one as a parameter and stay in `nodes/usj`.\n *\n * Each descriptor's `ownerOf` implements the ONE owner walk for its kind, keyed on marker\n * identity: only pieces of that same kind's run may sit between a candidate piece and its owner,\n * so a foreign glyph or unrelated content ends the walk with no owner. `$ownerOfRunPiece`\n * (displayRunOwner.utils.ts) is the single classifier that consults every descriptor in order.\n */\nimport { $chapterAltnumberRunPieces, $chapterCpAnchor, $chapterGlyphTextNode, $chapterPubnumberRunPieces, $charAttributeDisplayNode, $charClosingGlyph, $milestoneAttributeRunPieces, $noteCategoryRunPieces, $noteEditableCallerNode, $verseAttributeRunPieces, canonicalAttributeText, milestoneAttributes, } from \"../nodes/usj/attributeDisplay.utils.js\";\nimport { $isAttributeRunNode } from \"../nodes/usj/AttributeRunNode.js\";\nimport { $isChapterNode } from \"../nodes/usj/ChapterNode.js\";\nimport { $isCharNode } from \"../nodes/usj/CharNode.js\";\nimport { $isNoteNode } from \"../nodes/usj/NoteNode.js\";\nimport { $hasCaretHeldSeparatorGap } from \"../nodes/usj/markerSeparators.utils.js\";\nimport { $isMilestoneNode } from \"../nodes/usj/MilestoneNode.js\";\nimport { NBSP } from \"../nodes/usj/node-constants.js\";\nimport { $isVerseNode } from \"../nodes/usj/VerseNode.js\";\nimport { textTypeState } from \"../nodes/collab/delta.state.js\";\nimport { $isImmutableTypedTextNode } from \"../nodes/features/ImmutableTypedTextNode.js\";\nimport { $isMarkerNode } from \"../nodes/features/MarkerNode.js\";\nimport { $isUnknownNode } from \"../nodes/features/UnknownNode.js\";\nimport { unknownDisplayParts } from \"../nodes/features/unknownUsfm.utils.js\";\nimport { defaultMarkerAttribute, milestoneDefaultAttribute, } from \"../converters/usfm/usfmFragmentToUsj.js\";\nimport { $getSelection, $getState, $isElementNode, $isRangeSelection, $isTextNode, } from \"lexical\";\n/** No run wanted and no value — the answer for an owner whose state carries nothing to display,\n * and the safe answer when a descriptor is handed a node of the wrong type. */\nconst NO_RUN = { wantsRun: false, valueText: undefined };\n/** No pieces found — the answer when a descriptor is handed a node of the wrong type. */\nconst NO_PIECES = {};\n/** The sibling a verse's run for `marker` is anchored after: the verse itself for `\\va`, and\n * `\\va`'s wrapper (or, while caret-grace defers the wrap, its loose closer) for `\\vp`. Shared by\n * the scanner and the writer so the two can never disagree about where a run belongs. */\nfunction $verseRunAnchor(verse, marker) {\n if (marker === \"va\")\n return verse;\n const va = $verseAttributeRunPieces(verse, \"va\");\n return va.wrapper ?? va.closer ?? verse;\n}\n/** The caret arm a run graces when NO piece survives: the run's insertion point is the end of\n * its anchor or the very start of the anchor's next sibling, where a range deletion collapses\n * the caret. An ELEMENT anchor (a chapter `\\cp` run's anchor is the `\\ca` run's WRAPPER) has no\n * text end of its own — the collapse point is the end of its last descendant text piece (the\n * `\\ca` closer glyph), so that arm is checked too. The shared reporter already graces the\n * wrapper's subtree and the live value node. */\nfunction $verseFlankGrace(anchor) {\n const selection = $getSelection();\n if (!$isRangeSelection(selection) || !selection.isCollapsed())\n return false;\n const anchorNode = selection.anchor.getNode();\n if (anchorNode.is(anchor) && selection.anchor.offset === anchor.getTextContentSize())\n return true;\n if ($isElementNode(anchor)) {\n const last = anchor.getLastDescendant();\n if (last !== null &&\n anchorNode.is(last) &&\n selection.anchor.offset === last.getTextContentSize())\n return true;\n }\n const next = anchor.getNextSibling();\n return next !== null && anchorNode.is(next) && selection.anchor.offset === 0;\n}\n/** The caret arm shared by verse and milestone runs when only the VALUE was deleted beside a\n * surviving opening glyph: the end of the opening glyph's own text, or the trailing glyph. */\nfunction $glyphDebrisGrace(pieces) {\n const { opener, closer } = pieces;\n if (!opener)\n return false;\n const selection = $getSelection();\n if (!$isRangeSelection(selection) || !selection.isCollapsed())\n return false;\n const anchorNode = selection.anchor.getNode();\n const atOpenerEnd = anchorNode.is(opener) && selection.anchor.offset === opener.getTextContentSize();\n if (closer)\n return atOpenerEnd || anchorNode.is(closer);\n return atOpenerEnd;\n}\n/** Whether `node` is a piece of a verse's `\\va`/`\\vp` run — a whole wrapper (crossed in one step,\n * so a `\\vp` piece's walk passes its own `\\va` wrapper), a `va`/`vp` glyph riding loose, or a\n * loose attribute-tagged value. Loose shapes are transient (an undo stack, a collab-materialized\n * bare verse, a mid-edit tree with one marker wrapped and the other not) but real for a commit. */\nfunction $isVerseRunPiece(node) {\n if ($isAttributeRunNode(node))\n return node.getRunKind() === \"va\" || node.getRunKind() === \"vp\";\n if ($isMarkerNode(node))\n return node.getMarker() === \"va\" || node.getMarker() === \"vp\";\n return $isTextNode(node) && $getState(node, textTypeState) === \"attribute\";\n}\n/** The `va`/`vp` marker a loose piece belongs to: its own marker for a glyph, and for a value the\n * marker of the glyph immediately before it — the run pieces' fixed order puts a value's own opener\n * exactly one step back, even in the previous state where that opener is also being destroyed.\n *\n * The value arm requires the candidate to BE a run piece (an attribute-tagged TextNode), not merely\n * to sit behind one, mirroring the milestone side's `$isMilestoneRunPiece` rule. Position alone is\n * not enough: the ordinary verse text that follows a settled `\\va …\\va*` run also has that run's\n * closing glyph as its previous sibling, and claiming it would pend the verse — and run a settle\n * plus a whole-paragraph rebuild — for a deletion in unrelated content. */\nfunction $loosePieceMarker(node) {\n if ($isMarkerNode(node)) {\n const marker = node.getMarker();\n return marker === \"va\" || marker === \"vp\" ? marker : undefined;\n }\n if (!$isTextNode(node) || $getState(node, textTypeState) !== \"attribute\")\n return undefined;\n const previous = node.getPreviousSibling();\n if (!$isMarkerNode(previous))\n return undefined;\n const marker = previous.getMarker();\n return marker === \"va\" || marker === \"vp\" ? marker : undefined;\n}\n/** Walk back from `start` over `marker`'s own run pieces to the VerseNode the run rides on. */\nfunction $verseOfRunChain(start) {\n for (let previous = start.getPreviousSibling(); previous; previous = previous.getPreviousSibling()) {\n if ($isVerseNode(previous))\n return previous;\n if (!$isVerseRunPiece(previous))\n return undefined;\n }\n return undefined;\n}\nfunction verseDescriptor(marker) {\n return {\n kind: marker,\n ownerPredicate: (node) => $isVerseNode(node),\n ownerOf: (node) => {\n // A wrapper of this marker is its own walk start; a piece INSIDE one is only positioned\n // relative to its siblings within the wrapper, so the walk starts from the wrapper instead.\n if ($isAttributeRunNode(node))\n return node.getRunKind() === marker ? $verseOfRunChain(node) : undefined;\n const parent = node.getParent();\n if ($isAttributeRunNode(parent))\n return parent.getRunKind() === marker ? $verseOfRunChain(parent) : undefined;\n return $loosePieceMarker(node) === marker ? $verseOfRunChain(node) : undefined;\n },\n expectedPieces: (owner) => {\n if (!$isVerseNode(owner))\n return NO_RUN;\n const value = marker === \"va\" ? owner.getAltnumber() : owner.getPubnumber();\n if (value === undefined)\n return NO_RUN;\n return { wantsRun: true, valueText: NBSP + value };\n },\n scanPieces: (owner) => $isVerseNode(owner)\n ? $verseAttributeRunPieces($verseRunAnchor(owner, marker), marker)\n : NO_PIECES,\n graceSite: (owner, pieces) => {\n if (!$isVerseNode(owner))\n return false;\n if (!pieces.opener && !pieces.closer)\n return $verseFlankGrace($verseRunAnchor(owner, marker));\n return $glyphDebrisGrace(pieces);\n },\n settleScope: \"owner\",\n deletionPolicy: \"retokenize\",\n byteFormat: {\n writer: \"wrapper\",\n runKind: marker,\n glyphs: \"with-value\",\n glyphMarker: () => marker,\n closerSyntax: \"closing\",\n insertRunAfter: (owner) => ($isVerseNode(owner) ? $verseRunAnchor(owner, marker) : undefined),\n },\n };\n}\nconst separatorDescriptor = {\n kind: \"separator\",\n // The NBSP a char span shows after its opening glyph. Its \"deletion\" is a TEXT mutation (an NBSP\n // prefix edit), not node destruction, so it has no owner walk and no destruction pend — its\n // caret-grace path is what settles it, exactly as before joining the registry.\n ownerPredicate: (node) => $isCharNode(node),\n ownerOf: () => undefined,\n expectedPieces: () => NO_RUN,\n scanPieces: () => NO_PIECES,\n graceSite: (owner) => $isCharNode(owner) && $hasCaretHeldSeparatorGap(owner),\n settleScope: \"owner\",\n deletionPolicy: \"retokenize\",\n byteFormat: { writer: \"kind-owned\", glyphs: \"none\" },\n};\nconst charDescriptor = {\n kind: \"char\",\n ownerPredicate: (node) => $isCharNode(node),\n ownerOf: (node) => {\n // A char span's run is a direct TextNode child, never wrapped and never a glyph.\n if (!$isTextNode(node) || $getState(node, textTypeState) !== \"attribute\")\n return undefined;\n const parent = node.getParent();\n return $isCharNode(parent) ? parent : undefined;\n },\n expectedPieces: (owner) => {\n if (!$isCharNode(owner))\n return NO_RUN;\n // A span with no closing glyph never carries a run regardless of its attributes: the adaptor\n // never builds one there, so the sync must not fabricate one either.\n if ($charClosingGlyph(owner) === undefined)\n return NO_RUN;\n const text = canonicalAttributeText(owner.getUnknownAttributes() ?? {}, defaultMarkerAttribute(owner.getMarker()));\n return text === \"\" ? NO_RUN : { wantsRun: true, valueText: text };\n },\n scanPieces: (owner) => $isCharNode(owner) ? { value: $charAttributeDisplayNode(owner) } : NO_PIECES,\n graceSite: (owner, pieces) => {\n // The insertion-point arms for a run that is missing: the closing glyph the run would be\n // inserted before, or the text-end of the content immediately preceding that glyph.\n if (!$isCharNode(owner) || pieces.value)\n return false;\n const closingGlyph = $charClosingGlyph(owner);\n if (!closingGlyph)\n return false;\n const selection = $getSelection();\n if (!$isRangeSelection(selection) || !selection.isCollapsed())\n return false;\n const anchorNode = selection.anchor.getNode();\n if (anchorNode.is(closingGlyph))\n return true;\n const lastContent = closingGlyph.getPreviousSibling();\n return (lastContent !== null &&\n anchorNode.is(lastContent) &&\n selection.anchor.offset === lastContent.getTextContentSize());\n },\n settleScope: \"owner\",\n deletionPolicy: \"retokenize\",\n byteFormat: {\n writer: \"owner-children\",\n glyphs: \"none\",\n insertRunBefore: (owner) => ($isCharNode(owner) ? $charClosingGlyph(owner) : undefined),\n },\n};\n/** Whether `node` is a loose piece of a note's `\\cat` run — a `cat` glyph, or an attribute-tagged\n * value whose own opener (one step back, the run pieces' fixed order) is a `cat` glyph. The value\n * arm requires the candidate to BE a run piece, mirroring `$loosePieceMarker`'s rule for verses:\n * position alone must not claim ordinary note content that happens to follow a settled run. */\nfunction $isLooseCatPiece(node) {\n if ($isMarkerNode(node))\n return node.getMarker() === \"cat\";\n if (!$isTextNode(node) || $getState(node, textTypeState) !== \"attribute\")\n return false;\n const previous = node.getPreviousSibling();\n return $isMarkerNode(previous) && previous.getMarker() === \"cat\";\n}\n/** Walk back from `start` over the cat run's own pieces to the editable caller anchor, and from\n * there to the NoteNode the run rides in. A piece's parent must BE the note (loose pieces are the\n * note's direct children); crossing anything that is not a cat piece ends the walk with no owner. */\nfunction $noteOfCatChain(start) {\n const parent = start.getParent();\n if (!$isNoteNode(parent))\n return undefined;\n const anchor = $noteEditableCallerNode(parent);\n if (!anchor)\n return undefined;\n for (let previous = start.getPreviousSibling(); previous; previous = previous.getPreviousSibling()) {\n if (previous.is(anchor))\n return parent;\n if (!$isLooseCatPiece(previous))\n return undefined;\n }\n return undefined;\n}\nconst catDescriptor = {\n kind: \"cat\",\n ownerPredicate: (node) => $isNoteNode(node),\n ownerOf: (node) => {\n // A note's run rides as its CHILDREN (a NoteNode is an ElementNode), so the wrapper's parent\n // IS the owner — no sibling chain to walk for the wrapped shape. Loose pieces walk back to\n // the caller anchor exactly like a verse's loose pieces walk back to their verse.\n if ($isAttributeRunNode(node))\n return node.getRunKind() === \"cat\" && $isNoteNode(node.getParent())\n ? (node.getParent() ?? undefined)\n : undefined;\n const parent = node.getParent();\n if ($isAttributeRunNode(parent))\n return parent.getRunKind() === \"cat\" && $isNoteNode(parent.getParent())\n ? (parent.getParent() ?? undefined)\n : undefined;\n return $isLooseCatPiece(node) ? $noteOfCatChain(node) : undefined;\n },\n expectedPieces: (owner) => {\n if (!$isNoteNode(owner))\n return NO_RUN;\n // Collapsed notes deliberately do not display the category (their content is not inline\n // display text at all), and only the expanded editable shape carries the caller anchor the\n // run is positioned by.\n if (owner.getIsCollapsed() !== false)\n return NO_RUN;\n const category = owner.getCategory();\n if (category === undefined)\n return NO_RUN;\n return { wantsRun: true, valueText: NBSP + category };\n },\n scanPieces: (owner) => ($isNoteNode(owner) ? $noteCategoryRunPieces(owner) : NO_PIECES),\n graceSite: (owner, pieces) => {\n if (!$isNoteNode(owner))\n return false;\n if (!pieces.opener && !pieces.closer) {\n const anchor = $noteEditableCallerNode(owner);\n return anchor !== undefined && $verseFlankGrace(anchor);\n }\n return $glyphDebrisGrace(pieces);\n },\n settleScope: \"owner\",\n deletionPolicy: \"retokenize\",\n byteFormat: {\n writer: \"wrapper\",\n runKind: \"cat\",\n glyphs: \"with-value\",\n glyphMarker: () => \"cat\",\n closerSyntax: \"closing\",\n insertRunAfter: (owner) => ($isNoteNode(owner) ? $noteEditableCallerNode(owner) : undefined),\n },\n};\n/** Whether `node` is a piece of a chapter's `\\ca`/`\\cp` runs — a whole wrapper of either kind\n * (crossed in one step, so a loose `\\cp` piece's walk passes the `\\ca` wrapper), a `ca`/`cp`\n * glyph riding loose, or a loose attribute-tagged value — the chapter twin of\n * `$isVerseRunPiece`. */\nfunction $isChapterRunPiece(node) {\n if ($isAttributeRunNode(node))\n return node.getRunKind() === \"ca\" || node.getRunKind() === \"cp\";\n if ($isMarkerNode(node))\n return node.getMarker() === \"ca\" || node.getMarker() === \"cp\";\n return $isTextNode(node) && $getState(node, textTypeState) === \"attribute\";\n}\n/** The `ca`/`cp` marker a loose chapter piece belongs to — the chapter twin of\n * `$loosePieceMarker`, with the same value-arm rule (the candidate must BE a run piece whose own\n * opener sits one step back; position alone must not claim unrelated content). */\nfunction $chapterLoosePieceMarker(node) {\n if ($isMarkerNode(node)) {\n const marker = node.getMarker();\n return marker === \"ca\" || marker === \"cp\" ? marker : undefined;\n }\n if (!$isTextNode(node) || $getState(node, textTypeState) !== \"attribute\")\n return undefined;\n const previous = node.getPreviousSibling();\n if (!$isMarkerNode(previous))\n return undefined;\n const marker = previous.getMarker();\n return marker === \"ca\" || marker === \"cp\" ? marker : undefined;\n}\n/** Walk back from `start` over the chapter runs' own pieces (of EITHER kind — a `\\cp` piece's\n * walk crosses the whole `\\ca` wrapper in one step) to the chapter's `\\c N` glyph anchor, and\n * from there to the ChapterNode the runs ride in — the chapter twin of `$verseOfRunChain`. */\nfunction $chapterOfRunChain(start) {\n const parent = start.getParent();\n if (!$isChapterNode(parent))\n return undefined;\n const anchor = $chapterGlyphTextNode(parent);\n if (!anchor)\n return undefined;\n for (let previous = start.getPreviousSibling(); previous; previous = previous.getPreviousSibling()) {\n if (previous.is(anchor))\n return parent;\n if (!$isChapterRunPiece(previous))\n return undefined;\n }\n return undefined;\n}\n/** One chapter attribute-marker descriptor — `\"ca\"` (altnumber; opener + value + closer) or\n * `\"cp\"` (pubnumber; opener + value, NO closer: the span closes implicitly at the next block\n * boundary in the file, so its wrapper alone bounds the value). The runs ride as the editable\n * chapter's CHILDREN in document order — glyph text, then `\\ca`'s wrapper, then `\\cp`'s — the\n * same-line byte position the chapter fragment re-tokenizes and ParatextData's own chapter-path\n * folding accepts. */\nfunction chapterDescriptor(marker) {\n const $anchor = (chapter) => !$isChapterNode(chapter)\n ? undefined\n : marker === \"ca\"\n ? $chapterGlyphTextNode(chapter)\n : $chapterCpAnchor(chapter);\n return {\n kind: marker,\n ownerPredicate: (node) => $isChapterNode(node),\n ownerOf: (node) => {\n if ($isAttributeRunNode(node))\n return node.getRunKind() === marker && $isChapterNode(node.getParent())\n ? (node.getParent() ?? undefined)\n : undefined;\n const parent = node.getParent();\n if ($isAttributeRunNode(parent))\n return parent.getRunKind() === marker && $isChapterNode(parent.getParent())\n ? (parent.getParent() ?? undefined)\n : undefined;\n return $chapterLoosePieceMarker(node) === marker ? $chapterOfRunChain(node) : undefined;\n },\n expectedPieces: (owner) => {\n if (!$isChapterNode(owner))\n return NO_RUN;\n const value = marker === \"ca\" ? owner.getAltnumber() : owner.getPubnumber();\n if (value === undefined)\n return NO_RUN;\n return { wantsRun: true, valueText: NBSP + value };\n },\n scanPieces: (owner) => !$isChapterNode(owner)\n ? NO_PIECES\n : marker === \"ca\"\n ? $chapterAltnumberRunPieces(owner)\n : $chapterPubnumberRunPieces(owner),\n graceSite: (owner, pieces) => {\n if (!$isChapterNode(owner))\n return false;\n if (!pieces.opener && !pieces.closer) {\n const anchor = $anchor(owner);\n return anchor !== undefined && $verseFlankGrace(anchor);\n }\n return $glyphDebrisGrace(pieces);\n },\n settleScope: \"owner\",\n deletionPolicy: \"retokenize\",\n byteFormat: {\n writer: \"wrapper\",\n runKind: marker,\n glyphs: \"with-value\",\n glyphMarker: () => marker,\n closerSyntax: marker === \"ca\" ? \"closing\" : \"none\",\n insertRunAfter: $anchor,\n },\n };\n}\n/** Whether `node` is a loose piece of a milestone's run — an opening glyph, a self-closing glyph,\n * or an attribute-tagged value. A milestone's opening glyph carries the milestone's OWN marker,\n * which the chain walk re-checks against the candidate owner. */\nfunction $isMilestoneRunPiece(node) {\n if ($isMarkerNode(node)) {\n const syntax = node.getMarkerSyntax();\n return syntax === \"selfClosing\" || syntax === \"opening\";\n }\n return $isTextNode(node) && $getState(node, textTypeState) === \"attribute\";\n}\n/** Walk back from a LOOSE milestone run piece over the run's other loose pieces to the milestone,\n * requiring a matching marker on any opening glyph crossed. */\nfunction $milestoneOfLooseChain(start) {\n for (let previous = start.getPreviousSibling(); previous; previous = previous.getPreviousSibling()) {\n if ($isMilestoneNode(previous)) {\n const opening = $isMarkerNode(start) && start.getMarkerSyntax() === \"opening\" ? start : undefined;\n return !opening || opening.getMarker() === previous.getMarker() ? previous : undefined;\n }\n if (!$isMilestoneRunPiece(previous))\n return undefined;\n }\n return undefined;\n}\nconst milestoneDescriptor = {\n kind: \"milestone\",\n ownerPredicate: (node) => $isMilestoneNode(node),\n ownerOf: (node) => {\n const start = $isAttributeRunNode(node)\n ? node.getRunKind() === \"milestone\"\n ? node\n : undefined\n : $isAttributeRunNode(node.getParent())\n ? node.getParent()\n : $isMilestoneRunPiece(node)\n ? node\n : undefined;\n if (!start)\n return undefined;\n if ($isAttributeRunNode(start) && start.getRunKind() !== \"milestone\")\n return undefined;\n const previous = start.getPreviousSibling();\n // A milestone's run is a SINGLE wrapper (or one contiguous loose group) directly following its\n // milestone — there is no second marker to cross, unlike a verse's `\\va`/`\\vp` pair. A WRAPPER\n // requires direct adjacency to the milestone (the builder always creates/heals it immediately\n // after — never behind intervening debris), so it gets no chain walk of its own; a LOOSE piece\n // delegates entirely to $milestoneOfLooseChain, which both walks the chain AND re-checks marker\n // identity against any opening glyph it crosses — the check the retired sibling walk lacked, so\n // a foreign opening glyph (any marker) adjacent to a milestone must not classify it as owner.\n if ($isAttributeRunNode(start))\n return $isMilestoneNode(previous) ? previous : undefined;\n return $milestoneOfLooseChain(start);\n },\n expectedPieces: (owner) => {\n if (!$isMilestoneNode(owner))\n return NO_RUN;\n const attributes = milestoneAttributes(owner.getSid(), owner.getEid(), owner.getUnknownAttributes(), owner.getAttributeOrder());\n const text = canonicalAttributeText(attributes, milestoneDefaultAttribute(owner.getMarker()));\n // The glyph pair is unconditional: a milestone always displays `\\qt-s …\\*`, so the run is\n // wanted even when no attribute text rides between the glyphs.\n return { wantsRun: true, valueText: text === \"\" ? undefined : NBSP + text };\n },\n scanPieces: (owner) => {\n if (!$isMilestoneNode(owner))\n return NO_PIECES;\n // $milestoneAttributeRunPieces names its fields opening/attribute/closing (its own long-lived\n // vocabulary, shared with the Tier-2 rebuild); ScannedRun's fields are opener/value/closer —\n // the descriptor registry's cross-kind vocabulary. Translate rather than rename either side.\n //\n // This translation is NOT enforced by the compiler: MilestoneRunPieces's fields\n // (opening/attribute/closing/wrapper) and ScannedRun's fields (opener/value/closer/wrapper)\n // are ALL optional on both sides, so returning $milestoneAttributeRunPieces(owner) directly —\n // the untranslated shape — type-checks with zero errors (verified directly: `tsc --build`\n // reports nothing). Excess-property checking only fires on fresh object literals, not on a\n // value flowing through a function call, and an all-optional target type has no required\n // field whose absence would fail assignability either. The wrong shape compiles clean and\n // then reads as permanently empty at runtime (opener/value/closer come back undefined\n // forever) — displayRunRegistry.test.ts's scanPieces suite is what actually catches this.\n const { opening, attribute, closing, wrapper } = $milestoneAttributeRunPieces(owner);\n return { opener: opening, value: attribute, closer: closing, wrapper };\n },\n graceSite: (owner, pieces) => {\n if (!$isMilestoneNode(owner))\n return false;\n if (!pieces.opener && !pieces.closer) {\n const selection = $getSelection();\n if (!$isRangeSelection(selection) || !selection.isCollapsed())\n return false;\n const anchorNode = selection.anchor.getNode();\n const previous = owner.getPreviousSibling();\n if (previous !== null &&\n anchorNode.is(previous) &&\n selection.anchor.offset === previous.getTextContentSize())\n return true;\n const next = owner.getNextSibling();\n return next !== null && anchorNode.is(next) && selection.anchor.offset === 0;\n }\n return $glyphDebrisGrace(pieces);\n },\n settleScope: \"owner\",\n deletionPolicy: \"remove-owner\",\n byteFormat: {\n writer: \"wrapper\",\n runKind: \"milestone\",\n glyphs: \"unconditional\",\n glyphMarker: (owner) => ($isMilestoneNode(owner) ? owner.getMarker() : \"\"),\n closerSyntax: \"selfClosing\",\n insertRunAfter: (owner) => owner,\n },\n};\n/** The optbreak's entire byte representation — the literal `//` token — derived from the ONE\n * renderer of unknown-kind bytes so the registry can never drift from what is actually drawn. */\nconst optbreakDisplayText = unknownDisplayParts(\"optbreak\", undefined, undefined).opening;\nconst optbreakDescriptor = {\n kind: \"optbreak\",\n ownerPredicate: (node) => $isUnknownNode(node) && node.getTag() === \"optbreak\",\n ownerOf: (node) => {\n // The adaptor renders the `//` token as an ImmutableTypedTextNode (a read-only DecoratorNode),\n // but an edited optbreak can hold a plain TextNode instead, so both are recognized.\n const parent = node.getParent();\n if (!$isUnknownNode(parent) || parent.getTag() !== \"optbreak\")\n return undefined;\n return $isTextNode(node) || $isImmutableTypedTextNode(node) ? parent : undefined;\n },\n // `valueText` is the RENDERED BYTES the kind owes — so `$runDiverges`'s value-byte comparison\n // classifies the scanned token by what it actually spells: a canonical `//` is at rest, a\n // byte-damaged or deleted token diverges. With `valueText: undefined` (the pre-audit shape)\n // both answers were backwards: a canonical token's text never equalled `undefined`, so a\n // CANONICAL optbreak read as diverged while a GUTTED one read as at rest. Nothing ever WRITES\n // from this (the `\"read-only\"` writer returns before any sync write), so the value is purely\n // classificatory.\n expectedPieces: () => ({ wantsRun: true, valueText: optbreakDisplayText }),\n scanPieces: (owner) => $isUnknownNode(owner) ? { value: owner.getFirstChild() ?? undefined } : NO_PIECES,\n graceSite: () => false,\n settleScope: \"owner\",\n deletionPolicy: \"remove-owner\",\n byteFormat: { writer: \"read-only\", glyphs: \"none\" },\n};\nconst opaqueUnknownDescriptor = {\n kind: \"opaqueUnknown\",\n // Scope is every UnknownNode kind EXCEPT optbreak — `ownerPredicate` excludes it explicitly, so\n // `optbreakDescriptor` above is the sole owner of that kind. A non-optbreak UnknownNode is a\n // permanent Tier-2 sentinel whose bytes are read-only rendering, never re-tokenized: it owns no\n // display run, but is recognized so the settle reports it handled and the caller never routes one\n // through a rebuild that would bail. (A pended optbreak that does NOT match `optbreakDescriptor`'s\n // `remove-owner` shape — i.e. isn't entirely absent — falls through unhandled by either\n // descriptor instead; harmlessly inert, since `$settleScopeForNode` refuses every `UnknownNode`\n // outright, so the caller's `$requestTier2ForNode` fallback always bails on it too.)\n ownerPredicate: (node) => $isUnknownNode(node) && node.getTag() !== \"optbreak\",\n ownerOf: () => undefined,\n expectedPieces: () => NO_RUN,\n scanPieces: () => NO_PIECES,\n graceSite: () => false,\n settleScope: \"owner\",\n deletionPolicy: \"none\",\n byteFormat: { writer: \"read-only\", glyphs: \"none\" },\n};\nconst nestedGlyphDescriptor = {\n kind: \"nestedGlyph\",\n // The `+` on a nested span's glyphs. Purely tree-derived and rewritten in place by its own sync;\n // there is no state a user edit can leave half-finished, so it owes no pend or deletion duty.\n ownerPredicate: (node) => $isCharNode(node),\n ownerOf: () => undefined,\n expectedPieces: () => NO_RUN,\n scanPieces: () => NO_PIECES,\n graceSite: () => false,\n settleScope: \"none\",\n deletionPolicy: \"none\",\n byteFormat: { writer: \"kind-owned\", glyphs: \"none\" },\n};\n/** Every registered kind, in the order the pend/settle driver consults them. THREE descriptors\n * declare `ownerPredicate: $isCharNode` — `separator` (the NBSP gap after an opening glyph), `char`\n * (the span's own `|…` attribute run), and `nestedGlyph` (the `+` on a nested span's glyphs) — so a\n * `CharNode` matches all three. `separator` is listed before `char`, so its grace is checked first,\n * preserving the order the per-kind arms ran in. `cat`, `ca`, and `cp` are listed before `milestone` so their\n * loose glyphs are claimed by their own descriptors first — the milestone loose-piece test\n * accepts ANY opening glyph and only rejects it deeper in its chain walk. `nestedGlyph` never acts in the\n * settle loops at all: its `settleScope` is `\"none\"`, so those loops skip it outright — its `+` is\n * purely tree-derived and rewritten in place by its own sync, with no state a user edit can leave\n * half-finished. */\nexport const displayRunDescriptors = [\n separatorDescriptor,\n charDescriptor,\n verseDescriptor(\"va\"),\n verseDescriptor(\"vp\"),\n catDescriptor,\n chapterDescriptor(\"ca\"),\n chapterDescriptor(\"cp\"),\n milestoneDescriptor,\n optbreakDescriptor,\n opaqueUnknownDescriptor,\n nestedGlyphDescriptor,\n];\nconst byKind = new Map(displayRunDescriptors.map((descriptor) => [descriptor.kind, descriptor]));\n/** The descriptor for `kind`. Throws for an unregistered kind — a kind is only nameable once its\n * descriptor exists, so a miss is a wiring bug, never a runtime condition to handle. */\nexport function displayRunDescriptor(kind) {\n const descriptor = byKind.get(kind);\n if (!descriptor)\n throw new Error(`No display-run descriptor registered for kind \"${kind}\"`);\n return descriptor;\n}\n","/**\n * The ONE walk from a display-run piece back to its owner, for every kind.\n *\n * A piece can be a live node (an edit inside a run dirties the piece or its wrapper, never the\n * leaf owner, whose own transform would then not fire) or a node read from the PREVIOUS editor\n * state (a destroyed piece, which still has its tree position there). The walk is identical in\n * both cases — it reads only tree position — so one function serves the live re-sync path and the\n * destruction-pend path alike.\n *\n * The chain classification is keyed on MARKER IDENTITY, not on \"is this a glyph\": only pieces of\n * the same kind's run may sit between a piece and its owner. Anything else — a char span's own\n * opener riding beside a verse, a note glyph — ends the walk with no owner, so a deletion in\n * unrelated content can never pend a nearby verse or milestone.\n */\nimport { displayRunDescriptors } from \"./displayRunRegistry.js\";\n/**\n * The owner whose run `piece` belongs to, and that run's kind — or `undefined` when `piece` is not\n * part of any registered display run. Descriptors are consulted in registry order and the first\n * match wins; each kind's `ownerOf` recognizes only its own pieces, so at most one can match.\n */\nexport function $ownerOfRunPiece(piece) {\n for (const descriptor of displayRunDescriptors) {\n const owner = descriptor.ownerOf(piece);\n if (owner)\n return { owner, kind: descriptor.kind };\n }\n return undefined;\n}\n/**\n * True when `node` is a piece — a run glyph or an attribute value — of a display run whose kind\n * implements an owner walk (currently `va`/`vp`, `milestone`, `char`, and `optbreak`; see each\n * descriptor's `ownerOf` in displayRunRegistry.ts). A kind with no owner walk (`separator`,\n * `opaqueUnknown`, `nestedGlyph` — their `ownerOf` always returns `undefined`) never reports true\n * here, regardless of what `node` is. Engine-owned presentation, never content, for the kinds it\n * does cover: it must not enter OT content ops or the editor→USJ conversion.\n *\n * Keyed on the piece's KIND (via {@link $ownerOfRunPiece}) rather than on tree shape, so both the\n * wrapped shape the adaptor builds and the loose shape a mid-edit commit, an undo stack, or a\n * collab-materialized bare owner can leave behind are recognized by the same rule — WHEN the\n * piece is directly adjacent to (or one wrapper-hop from) its owner, which is what each `ownerOf`\n * walk requires. It is therefore NOT a full ancestor check: a wrapped piece separated from its\n * owner by an intervening node, or nested more than one level below its wrapper, reports false\n * here even though it is still presentation — callers that also need that shape covered pair this\n * with an ancestry check (see `editor-delta.adaptor.ts`'s glyph gate).\n */\nexport function $isDisplayRunPiece(node) {\n return $ownerOfRunPiece(node) !== undefined;\n}\n","import { INVALID_CLASS_NAME } from \"../usj/node-constants.js\";\nimport { $applyNodeReplacement, TextNode, } from \"lexical\";\nexport const UNMATCHED_TAG_NAME = \"unmatched\";\nexport const IMMUTABLE_UNMATCHED_VERSION = 2;\n/** The displayed bytes for an unmatched marker: `marker` keeps its own trailing `*` (`\"nd*\"`,\n * or `\"*\"` for a bare stray closer), so the glyph is just a backslash prefix away. */\nexport function unmatchedGlyphText(marker) {\n return `\\\\${marker}`;\n}\n/**\n * A marker with no counterpart to pair with — an unmatched closer (`\\nd*` with no open span, PT9\n * `sink.Unmatched`) or a stray `\\*`. Ordinary editable TEXT, not a decorator: under Invariant I\n * the flagged bytes are document bytes that happen to re-tokenize to nothing yet, so they must\n * remain caret-addressable and must flow through a Tier-2 rebuild as bytes — which is exactly how\n * an unmatched closer RE-matches when the document later supplies its opener (the tokenizer's own\n * frame matching consumes it). The `marker` field mirrors the node's rest-state bytes the same\n * way `MarkerNode`'s does: a user edit diverges the text from the state, the divergence pends,\n * and the settle re-tokenizes the displayed bytes.\n *\n * Despite the historical name, only the DEFAULT text mode is immutable-ish: \"token\", which makes\n * the node atomic (caret steps over it whole, deletion removes it whole, no in-place typing) —\n * the right behavior for view modes with no marker-edit engine to settle an in-place edit\n * (visible/hidden marker modes, and collab-materialized nodes). The editable-marker adaptor\n * serializes these nodes with mode \"normal\" so Standard view can edit the bytes in place.\n */\nexport class ImmutableUnmatchedNode extends TextNode {\n __marker;\n constructor(marker = \"\", key) {\n super(unmatchedGlyphText(marker), key);\n this.__marker = marker;\n // Direct assignment, not setMode: the constructor owns the instance, and the writable-node\n // machinery is not available until the node is attached to an update.\n this.__mode = 1; // \"token\"\n }\n static getType() {\n return \"unmatched\";\n }\n static clone(node) {\n const { __marker, __key } = node;\n // Text, format, style, mode, and detail are copied by afterCloneFrom (TextNode), so a\n // mid-edit divergence between the bytes and __marker survives cloning.\n return new ImmutableUnmatchedNode(__marker, __key);\n }\n static importDOM() {\n return {\n [UNMATCHED_TAG_NAME]: (node) => {\n if (!isUnmatchedElement(node))\n return null;\n return {\n conversion: $convertImmutableUnmatchedElement,\n priority: 1,\n };\n },\n };\n }\n static importJSON(serializedNode) {\n return $createImmutableUnmatchedNode().updateFromJSON(serializedNode);\n }\n updateFromJSON(serializedNode) {\n // Version-1 serializations (and the collab materializer's embed data) carry only `marker` —\n // none of the TextNode fields — so every text field defaults at runtime even though the\n // serialized type declares them required: bytes derive from the marker, and the mode\n // defaults to the constructor's atomic \"token\".\n const marker = serializedNode.marker ?? \"\";\n const self = super.updateFromJSON({\n ...serializedNode,\n detail: serializedNode.detail ?? 0,\n format: serializedNode.format ?? 0,\n mode: serializedNode.mode ?? \"token\",\n style: serializedNode.style ?? \"\",\n text: serializedNode.text ?? unmatchedGlyphText(marker),\n });\n // Assigned directly rather than via setMarker, which would rewrite the just-applied text to\n // canonical and lose a serialized mid-edit divergence.\n const writable = self.getWritable();\n writable.__marker = marker;\n return writable;\n }\n setMarker(marker) {\n if (this.__marker === marker)\n return this;\n const self = this.getWritable();\n self.__marker = marker;\n self.__text = unmatchedGlyphText(marker);\n return self;\n }\n getMarker() {\n const self = this.getLatest();\n return self.__marker;\n }\n createDOM(config) {\n const dom = super.createDOM(config);\n dom.setAttribute(\"data-marker\", this.__marker);\n dom.classList.add(INVALID_CLASS_NAME);\n dom.title = unmatchedTitle(this.__marker);\n return dom;\n }\n updateDOM(prevNode, dom, config) {\n // TextNode reconciles the visible text; on element reuse createDOM does not run again, so the\n // marker-derived attribute and title are rewritten by hand — same gap MarkerNode.updateDOM\n // closes for glyphs.\n const isRecreated = super.updateDOM(prevNode, dom, config);\n if (prevNode.__marker !== this.__marker) {\n dom.setAttribute(\"data-marker\", this.__marker);\n dom.title = unmatchedTitle(this.__marker);\n }\n return isRecreated;\n }\n exportDOM() {\n // The dedicated element round-trips through importDOM above, so an HTML copy of the flagged\n // bytes pastes back as the same construct.\n const element = document.createElement(UNMATCHED_TAG_NAME);\n element.setAttribute(\"data-marker\", this.getMarker());\n element.classList.add(INVALID_CLASS_NAME);\n element.textContent = this.getTextContent();\n return { element };\n }\n exportJSON() {\n return {\n ...super.exportJSON(),\n type: this.getType(),\n marker: this.getMarker(),\n version: IMMUTABLE_UNMATCHED_VERSION,\n };\n }\n canInsertTextBefore() {\n // Typing at either edge belongs to the surrounding content, not to the flagged bytes — the\n // same reason a completed closer keeps appended typing outside the glyph.\n return false;\n }\n canInsertTextAfter() {\n return false;\n }\n}\n/** Whether the node's rendered bytes still spell its own state's glyph — at rest, as opposed to\n * mid-edit (pend-shaped). The unmatched counterpart of `$isCanonicalMarkerNode`.\n * Read-only: call inside `editor.getEditorState().read(...)` or an update. */\nexport function $isCanonicalUnmatchedNode(node) {\n return node.getTextContent() === unmatchedGlyphText(node.getMarker());\n}\nfunction unmatchedTitle(marker) {\n return marker.endsWith(\"*\")\n ? `This closing marker has no matching opening marker!`\n : `This opening marker has no matching closing marker!`;\n}\nfunction $convertImmutableUnmatchedElement(element) {\n const marker = element.getAttribute(\"data-marker\") ?? \"\";\n const node = $createImmutableUnmatchedNode(marker);\n return { node };\n}\nexport function $createImmutableUnmatchedNode(marker) {\n return $applyNodeReplacement(new ImmutableUnmatchedNode(marker));\n}\nfunction isUnmatchedElement(node) {\n // `tagName` is upper-cased for HTML-namespace elements, so compare case-insensitively.\n return node?.tagName.toLowerCase() === UNMATCHED_TAG_NAME;\n}\nexport function $isImmutableUnmatchedNode(node) {\n return node instanceof ImmutableUnmatchedNode;\n}\nexport function isSerializedImmutableUnmatchedNode(node) {\n return node?.type === ImmutableUnmatchedNode.getType();\n}\n","/** Conforms with USJ v3.1 tables @see https://docs.usfm.bible/usfm/3.1/para/table.html */\nimport { $applyNodeReplacement, ElementNode, } from \"lexical\";\n/** USJ marker type this node renders; the forward adaptor matches USJ input against it. */\nexport const TABLE_TYPE = \"table\";\nexport const IMMUTABLE_TABLE_TYPE = \"immutable-table\";\nexport const IMMUTABLE_TABLE_VERSION = 1;\n/** List of known properties of `MarkerObject` */\nexport const TABLE_MARKER_OBJECT_PROPS = [\"type\", \"marker\", \"content\"];\nexport class ImmutableTableNode extends ElementNode {\n __unknownAttributes;\n constructor(unknownAttributes, key) {\n super(key);\n this.__unknownAttributes = unknownAttributes;\n }\n static getType() {\n return IMMUTABLE_TABLE_TYPE;\n }\n static clone(node) {\n return new ImmutableTableNode(node.__unknownAttributes, node.__key);\n }\n static importJSON(serializedNode) {\n return $createImmutableTableNode().updateFromJSON(serializedNode);\n }\n updateFromJSON(serializedNode) {\n return super\n .updateFromJSON(serializedNode)\n .setUnknownAttributes(serializedNode.unknownAttributes);\n }\n setUnknownAttributes(unknownAttributes) {\n const self = this.getWritable();\n self.__unknownAttributes = unknownAttributes;\n return self;\n }\n getUnknownAttributes() {\n return this.getLatest().__unknownAttributes;\n }\n createDOM() {\n const dom = document.createElement(\"table\");\n dom.classList.add(\"table\");\n // Read-only whole-block: the same treatment tables had while they were `UnknownNode`s, and the\n // one `UnknownNode` still gives the other Tier-3 kinds (figures, sidebars, periph) — visible\n // and byte-preserved, but not editable. contentEditable=false stops the browser from placing a\n // native caret inside, so caret navigation skips the table like a decorator node. Set on the\n // container so it covers every row and cell. Editing tables is not supported yet; whether and\n // how they become editable is still open. Set as an ATTRIBUTE rather than via the\n // `contentEditable` IDL property (which `UnknownNode` uses): the two are equivalent in a\n // browser, but jsdom does not reflect the property back to the attribute, so only this form is\n // assertable in tests.\n dom.setAttribute(\"contenteditable\", \"false\");\n return dom;\n }\n updateDOM() {\n return false;\n }\n exportJSON() {\n const unknownAttributes = this.getUnknownAttributes();\n return {\n ...super.exportJSON(),\n type: IMMUTABLE_TABLE_TYPE,\n ...(unknownAttributes !== undefined && { unknownAttributes }),\n version: IMMUTABLE_TABLE_VERSION,\n };\n }\n // Shadow root: isolate selection so content doesn't merge across the table boundary.\n isShadowRoot() {\n return true;\n }\n}\nexport function $createImmutableTableNode(unknownAttributes) {\n return $applyNodeReplacement(new ImmutableTableNode(unknownAttributes));\n}\nexport function $isImmutableTableNode(node) {\n return node instanceof ImmutableTableNode;\n}\nexport function isSerializedImmutableTableNode(node) {\n return node?.type === IMMUTABLE_TABLE_TYPE;\n}\n","/** Conforms with USJ v3.1 table rows @see https://docs.usfm.bible/usfm/3.1/para/table.html */\nimport { $applyNodeReplacement, ElementNode, } from \"lexical\";\n/** USJ marker type this node renders; the forward adaptor matches USJ input against it. */\nexport const TABLE_ROW_TYPE = \"table:row\";\nexport const IMMUTABLE_TABLE_ROW_TYPE = \"immutable-table-row\";\nexport const IMMUTABLE_TABLE_ROW_VERSION = 1;\nexport const TABLE_ROW_DEFAULT_MARKER = \"tr\";\n/** List of known properties of `MarkerObject` */\nexport const TABLE_ROW_MARKER_OBJECT_PROPS = [\"type\", \"marker\", \"content\"];\nexport class ImmutableTableRowNode extends ElementNode {\n __marker;\n __unknownAttributes;\n constructor(marker = TABLE_ROW_DEFAULT_MARKER, unknownAttributes, key) {\n super(key);\n this.__marker = marker;\n this.__unknownAttributes = unknownAttributes;\n }\n static getType() {\n return IMMUTABLE_TABLE_ROW_TYPE;\n }\n static clone(node) {\n return new ImmutableTableRowNode(node.__marker, node.__unknownAttributes, node.__key);\n }\n static importJSON(serializedNode) {\n return $createImmutableTableRowNode().updateFromJSON(serializedNode);\n }\n updateFromJSON(serializedNode) {\n return super\n .updateFromJSON(serializedNode)\n .setMarker(serializedNode.marker ?? TABLE_ROW_DEFAULT_MARKER)\n .setUnknownAttributes(serializedNode.unknownAttributes);\n }\n setMarker(marker) {\n if (this.__marker === marker)\n return this;\n const self = this.getWritable();\n self.__marker = marker;\n return self;\n }\n getMarker() {\n return this.getLatest().__marker;\n }\n setUnknownAttributes(unknownAttributes) {\n const self = this.getWritable();\n self.__unknownAttributes = unknownAttributes;\n return self;\n }\n getUnknownAttributes() {\n return this.getLatest().__unknownAttributes;\n }\n createDOM() {\n const dom = document.createElement(\"tr\");\n dom.setAttribute(\"data-marker\", this.__marker);\n dom.classList.add(\"table-row\", `usfm_${this.__marker}`);\n // A first-line indent is meaningful for a `\\tr` rendered as a BLOCK of text and meaningless\n // inside a real table, where it only drags content left. It reaches two places here: the cells\n // (reset by `.table-cell` in usj-nodes.css) and the row's own marker glyph, which — being the\n // only non-cell content of a
— the browser wraps in an ANONYMOUS table cell. That box has\n // no class to target, so a negative indent pulls the glyph clear outside the table.\n //\n // Inline rather than a stylesheet rule because a stylesheet rule cannot reliably win: a project\n // StyleInfo that gives `tr` a firstLineIndent emits `.editor-input.usfm .usfm_tr { text-indent }`\n // (generateUsjCss.ts), injected after the static sheet and at a specificity any reasonable\n // static selector ties at best. Paratext 9 resolves it the same way, stamping\n // `style=\"TEXT-INDENT: 0in\"` on every `
` it emits. Cells need nothing of their own: with the\n // row at zero they inherit zero.\n dom.style.textIndent = \"0\";\n return dom;\n }\n updateDOM(prevNode) {\n return prevNode.__marker !== this.__marker;\n }\n exportJSON() {\n const unknownAttributes = this.getUnknownAttributes();\n return {\n ...super.exportJSON(),\n type: IMMUTABLE_TABLE_ROW_TYPE,\n marker: this.getMarker(),\n ...(unknownAttributes !== undefined && { unknownAttributes }),\n version: IMMUTABLE_TABLE_ROW_VERSION,\n };\n }\n}\nexport function $createImmutableTableRowNode(marker, unknownAttributes) {\n return $applyNodeReplacement(new ImmutableTableRowNode(marker, unknownAttributes));\n}\nexport function $isImmutableTableRowNode(node) {\n return node instanceof ImmutableTableRowNode;\n}\nexport function isSerializedImmutableTableRowNode(node) {\n return node?.type === IMMUTABLE_TABLE_ROW_TYPE;\n}\n","/** Conforms with USJ v3.1 table cells @see https://docs.usfm.bible/usfm/3.1/para/table.html */\nimport { $applyNodeReplacement, ElementNode, } from \"lexical\";\n/** USJ marker type this node renders; the forward adaptor matches USJ input against it. */\nexport const TABLE_CELL_TYPE = \"table:cell\";\nexport const IMMUTABLE_TABLE_CELL_TYPE = \"immutable-table-cell\";\nexport const IMMUTABLE_TABLE_CELL_VERSION = 1;\nexport const TABLE_CELL_DEFAULT_MARKER = \"tc1\";\n/** List of known properties of a table cell `MarkerObject` */\nexport const TABLE_CELL_MARKER_OBJECT_PROPS = [\n \"type\",\n \"marker\",\n \"align\",\n \"colspan\",\n \"content\",\n];\n// USJ `align` is logical (start/center/end), which CSS `text-align` supports natively and mirrors\n// correctly under `dir` (RTL). Pass it straight through, ignoring unrecognized values, rather than\n// mapping to the physical left/right — that would flip alignment in RTL scripts.\nfunction toLogicalTextAlign(align) {\n return align === \"start\" || align === \"center\" || align === \"end\" ? align : undefined;\n}\nexport class ImmutableTableCellNode extends ElementNode {\n __marker;\n __align;\n __colspan;\n __unknownAttributes;\n constructor(marker = TABLE_CELL_DEFAULT_MARKER, align, colspan, unknownAttributes, key) {\n super(key);\n this.__marker = marker;\n this.__align = align;\n this.__colspan = colspan;\n this.__unknownAttributes = unknownAttributes;\n }\n static getType() {\n return IMMUTABLE_TABLE_CELL_TYPE;\n }\n static clone(node) {\n const { __marker, __align, __colspan, __unknownAttributes, __key } = node;\n return new ImmutableTableCellNode(__marker, __align, __colspan, __unknownAttributes, __key);\n }\n static importJSON(serializedNode) {\n return $createImmutableTableCellNode().updateFromJSON(serializedNode);\n }\n updateFromJSON(serializedNode) {\n return super\n .updateFromJSON(serializedNode)\n .setMarker(serializedNode.marker ?? TABLE_CELL_DEFAULT_MARKER)\n .setAlign(serializedNode.align)\n .setColspan(serializedNode.colspan)\n .setUnknownAttributes(serializedNode.unknownAttributes);\n }\n setMarker(marker) {\n if (this.__marker === marker)\n return this;\n const self = this.getWritable();\n self.__marker = marker;\n return self;\n }\n getMarker() {\n return this.getLatest().__marker;\n }\n setAlign(align) {\n if (this.__align === align)\n return this;\n const self = this.getWritable();\n self.__align = align;\n return self;\n }\n getAlign() {\n return this.getLatest().__align;\n }\n setColspan(colspan) {\n if (this.__colspan === colspan)\n return this;\n const self = this.getWritable();\n self.__colspan = colspan;\n return self;\n }\n getColspan() {\n return this.getLatest().__colspan;\n }\n setUnknownAttributes(unknownAttributes) {\n const self = this.getWritable();\n self.__unknownAttributes = unknownAttributes;\n return self;\n }\n getUnknownAttributes() {\n return this.getLatest().__unknownAttributes;\n }\n createDOM() {\n const isHeader = this.__marker.startsWith(\"th\");\n const dom = document.createElement(isHeader ? \"th\" : \"td\");\n dom.setAttribute(\"data-marker\", this.__marker);\n dom.classList.add(\"table-cell\", `usfm_${this.__marker}`);\n const textAlign = toLogicalTextAlign(this.__align);\n if (textAlign)\n dom.style.textAlign = textAlign;\n if (this.__colspan)\n dom.setAttribute(\"colspan\", this.__colspan);\n return dom;\n }\n updateDOM(prevNode) {\n // Recreate the DOM element when marker, align, or colspan changes.\n return (prevNode.__marker !== this.__marker ||\n prevNode.__align !== this.__align ||\n prevNode.__colspan !== this.__colspan);\n }\n exportJSON() {\n const align = this.getAlign();\n const colspan = this.getColspan();\n const unknownAttributes = this.getUnknownAttributes();\n return {\n ...super.exportJSON(),\n type: IMMUTABLE_TABLE_CELL_TYPE,\n marker: this.getMarker(),\n ...(align !== undefined && { align }),\n ...(colspan !== undefined && { colspan }),\n ...(unknownAttributes !== undefined && { unknownAttributes }),\n version: IMMUTABLE_TABLE_CELL_VERSION,\n };\n }\n}\nexport function $createImmutableTableCellNode(marker, align, colspan, unknownAttributes) {\n return $applyNodeReplacement(new ImmutableTableCellNode(marker, align, colspan, unknownAttributes));\n}\nexport function $isImmutableTableCellNode(node) {\n return node instanceof ImmutableTableCellNode;\n}\nexport function isSerializedImmutableTableCellNode(node) {\n return node?.type === IMMUTABLE_TABLE_CELL_TYPE;\n}\n","/**\n * Caret positions at a content boundary: the single place that owns HOW a boundary between an\n * element's children becomes a Lexical caret position, and WHICH node renders the caret there. A\n * \"boundary\" is the location just before a parent's child at some index — the location the child\n * count itself names being the end of the parent's content.\n *\n * Every path that parks a caret at the start of some content answers this same question, and each\n * one used to answer it by hand: the para-marker prefix cursor guard, the marker-edit prefix\n * injection, verse-start placement, and the `\\fp` visual-line hop. The empty-verse caret guard asks\n * the other half of it — whether a boundary has a host at all — because that is exactly the state it\n * exists to repair.\n *\n * ## The convention\n *\n * - **A boundary is hosted by the text node that FOLLOWS it**, and a text point at that node's\n * offset 0 is the caret position — not the end of whatever text precedes the boundary, and not the\n * element point, even when a text node sits on both sides. The reason is what the caller is doing:\n * it has chosen a boundary because the content AFTER it is where the user's next keystroke\n * belongs, and at these boundaries what precedes is structure — a paragraph's marker glyph, its\n * token-mode separator, a verse marker — that typed text must never merge into.\n * - **The element point is the fallback, and it renders no caret of its own.** Where the following\n * child is a decorator (an immutable verse or chapter number, an immutable marker glyph) or where\n * there is no child at all, the boundary can only be expressed as an element point, and the\n * browser draws nothing at it. That is not a failure of this module — it is the condition\n * `EmptyVerseCaretGuardPlugin` detects with {@link $caretHostAtBoundary} and repairs by\n * materializing a text node to host the caret.\n * - **Hosting is a question about the TREE, not about the view.** A `MarkerNode` is a `TextNode`\n * subclass, so an editable-mode marker glyph hosts a caret exactly as content text does. That is\n * why one implementation serves every marker mode: it never asks which nodes are visible, only\n * which node can carry a text point.\n *\n * ## What this module does NOT own\n *\n * WHICH boundary is the legal one. That rule is genuinely different in each caller, and deliberately\n * different per marker mode, so it is stated at each site rather than merged here:\n *\n * - `$guardCursorAtParaStart` (shared-react) rules that a click may not come to rest before or\n * inside a paragraph's structural prefix, and advances FORWARD past it. It runs in every marker\n * mode — it is what stops a click in the hanging-indent gutter of `\\li`/`\\ili`/poetry from landing\n * before the marker — and its prefix scan therefore enumerates both marker flavors.\n * - `ArrowNavigationPlugin`'s visible-stop canonicalizer picks, among the tree positions sharing one\n * screen location, the outermost and earliest: the end of the nearest PRECEDING visible text — the\n * opposite preference to this module's, and correct there, because what precedes an arrow landing\n * is rendered content the caret just walked over rather than structure. It is also scoped to\n * markerMode \"editable\", where the display runs and glyph text that stack those positions exist at\n * all. Merging the two preferences would be a behavior change in both directions.\n * - `$selectParaContentStart` (platform's marker-edit paths) knows its paragraphs have the fixed\n * editable-mode `[glyph, separator, …content]` shape and names boundary 2 outright, where\n * `$advancePastParaPrefixes` scans for its boundary. Same convention, different way of finding the\n * index — which is precisely the part that is not shareable.\n *\n * Nor the mirror question, the caret position at the END of a node: `$placeCaretAtEnd`\n * (shared-react's structureKeyboard.utils.ts) owns that for the delete/merge paths, and it descends\n * into the node rather than resting at a boundary between children.\n */\nimport { $isTextNode } from \"lexical\";\n/**\n * The text node hosting a caret at the boundary before `parent`'s child at `index`, or `undefined`\n * when nothing there can carry a text point — a decorator child, or a boundary past the last child.\n *\n * Deliberately forward-looking: text preceding the boundary occupies the same screen location at its\n * own end, but it is not what this answers. See the module's convention.\n *\n * @param parent - The element whose children the boundary lies between.\n * @param index - The boundary: the index of the child that follows it.\n */\nexport function $caretHostAtBoundary(parent, index) {\n const child = parent.getChildAtIndex(index);\n return $isTextNode(child) ? child : undefined;\n}\n/**\n * Collapse the caret to the boundary before `parent`'s child at `index`: offset 0 of the text node\n * hosting it, or the element point when nothing hosts it.\n *\n * @param parent - The element whose children the boundary lies between.\n * @param index - The boundary: the index of the child that follows it.\n */\nexport function $placeCaretAtBoundary(parent, index) {\n const host = $caretHostAtBoundary(parent, index);\n if (host)\n host.select(0, 0);\n else\n parent.select(index, index);\n}\n","/**\n * Char-span glyph shape: the single place that owns WHETHER a char span carries an opening and a\n * closing glyph, and HOW a fresh span's pair is built. Sibling of nestedGlyphs.utils.ts (the `+` a\n * nested span's glyphs carry), markerSeparators.utils.ts (the NBSP between an opening glyph and its\n * content), and attributeDisplay.utils.ts (the `|name=\"value\"` run) — between them those three own\n * everything about a rendered char glyph EXCEPT whether there is one at all, which is this module.\n *\n * ## The conventions\n *\n * - **Glyphs are markerMode \"editable\" presentation.** A `MarkerNode` exists only there — \"visible\"\n * mode renders `ImmutableTypedTextNode` byte runs instead and \"hidden\" mode renders no glyph at\n * all — so a builder that fabricates one unconditionally puts literal `\\ft ` text (and its\n * separator NBSP) into the content of a document that displays no glyphs, where it is visible in\n * the span's text content and wrong on serialization. Callers that can run outside editable mode\n * pass `renderGlyphs` false. It is a plain boolean rather than `ViewOptions` because that type\n * lives in `shared-react`, which this package must not depend on.\n * - **Closer display keys on the span's STATE, never on its marker family**\n * ({@link $charOwesClosingGlyph}): a span renders a closing glyph iff it does NOT carry\n * `closed=\"false\"`. ParatextData stamps that flag on every genuinely-unclosed span — chiefly\n * footnote/cross-reference content chars (`\\fr`, `\\ft`, `\\xo`, `\\xt`), which the next bare marker\n * closes — so those render closer-less, while an explicitly-closed `\\xt` keeps its `\\xt*`. The\n * same state decides whether a missing closer is deletion damage (`$charNodeDeletionTransform`)\n * and whether an attribute run may exist at all (attributeDisplay.utils.ts).\n * - **A span CONTINUING another reproduces that span's shape**, not the convention's default\n * ({@link $buildContinuationCharSpan}): the PT9 close-and-reopen (`$liftOutOfChar`) and the\n * Ctrl+Space-style split (`$splitCharNodeAt`) both build a right-hand span keeping the source's\n * marker, nesting, and `closed` state, and take its closing glyph from the source's actual\n * CHILDREN ({@link $charHasClosingGlyph}) rather than from that state — the tree is what says\n * which glyphs this document renders, so a mode with no glyphs reopens with none.\n *\n * ## What this module does NOT own\n *\n * Only the shape of a span BUILT here. Keeping an existing span's glyphs honest afterwards belongs\n * to the syncs and the marker-edit engine: `$syncNestedGlyphs` re-derives the `+`,\n * `$syncOpenerSeparators` heals the separator, `$charNodeDeletionTransform` reads a missing\n * opener/closer as deletion damage, and the Tier-1 rename engine retargets a live pair. Nor are\n * these the only glyph MATERIALIZATIONS: the load adaptor's `createChar`/`addOpeningMarker`/\n * `addClosingMarker` build the same shape as SERIALIZED nodes, the collab materializer builds it\n * for \"visible\" mode as `ImmutableTypedTextNode`s, and `CharNode`'s `applyMarkerToDom` writes the\n * marker onto the rendered DOM. Each is a different layer with its own node kinds; this module is\n * the live-`MarkerNode` one, and states the conventions the others follow.\n */\nimport { $createMarkerNode, $isMarkerNode } from \"../features/MarkerNode.js\";\nimport { $isSeparatorPrefixHostText } from \"./markerSeparators.utils.js\";\nimport { $isNestedCharNode } from \"./nestedGlyphs.utils.js\";\nimport { NBSP } from \"./node-constants.js\";\n/**\n * Whether `char` owes a closing glyph — the implicit-close convention read from the span's own\n * state: everything closes except a span explicitly marked `closed=\"false\"`. This is the rule for\n * whether a closer SHOULD exist; {@link $charHasClosingGlyph} is the separate question of whether\n * one actually does.\n */\nexport function $charOwesClosingGlyph(char) {\n return char.getUnknownAttributes()?.closed !== \"false\";\n}\n/**\n * Whether `char` currently renders a closing glyph — a direct-child `MarkerNode` with closing\n * syntax. Outside markerMode \"editable\" a span has no glyph children at all, so this is also the\n * tree's answer to \"does this document display glyphs for this span\".\n *\n * Deliberately marker-AGNOSTIC, unlike `$charClosingGlyph` (attributeDisplay.utils.ts), which\n * requires the glyph to name `char`'s own marker because it must anchor `char`'s own attribute run\n * and the collab-flattened shape parks a nested child span's glyphs among the same children. Here\n * the question is only whether the span was built with a closer to reproduce.\n */\nexport function $charHasClosingGlyph(char) {\n return char\n .getChildren()\n .some((child) => $isMarkerNode(child) && child.getMarkerSyntax() === \"closing\");\n}\n/**\n * The unknown attributes a span continuing `char` must be created with: `closed=\"false\"` when\n * `char` carries the implicit-close convention, otherwise none.\n *\n * `closed` is structural state, not attribute bytes — an implicitly-closed span splits into TWO\n * implicitly-closed spans, and without the flag the continuation's (correct) missing closer reads\n * as deletion damage and gets routed through Tier 2. The DISPLAY attributes (`|name=\"value\"`) are\n * deliberately not copied: they stay on the left half only, since duplicating them would double\n * those bytes on serialization.\n */\nexport function $continuationCharAttributes(char) {\n return $charOwesClosingGlyph(char) ? undefined : { closed: \"false\" };\n}\n/**\n * Fill `span` — a freshly created, not-yet-inserted char span continuing `source` — with `content`\n * and the glyphs that content is owed. The caller creates `span` (with `source`'s marker and\n * {@link $continuationCharAttributes}) and inserts it; this decides its shape:\n *\n * - an opening glyph, first, only when `renderGlyphs` — the document has to display glyphs at all;\n * - the separator NBSP on `content`'s leading text, only when an opening glyph was emitted, since\n * the NBSP is the separator BETWEEN the glyph and the content it opens (markerSeparators.utils.ts);\n * - a closing glyph, last, only when `source` itself renders one ({@link $charHasClosingGlyph});\n * - the `+` on both glyphs when `source` is nested ({@link $isNestedCharNode}) — the continuation\n * becomes `source`'s sibling, so it inherits `source`'s nesting.\n *\n * @param span - The continuation span to fill. Must be empty; must be called inside `editor.update()`.\n * @param source - The span being continued, read for marker, nesting, and closing-glyph shape.\n * @param content - The nodes moving into `span`, in order. May be empty.\n * @param renderGlyphs - Whether this document displays char glyphs (markerMode \"editable\").\n */\nexport function $buildContinuationCharSpan(span, source, content, renderGlyphs) {\n const marker = source.getMarker();\n const nested = $isNestedCharNode(source);\n const hasCloser = $charHasClosingGlyph(source);\n if (renderGlyphs) {\n span.append($createMarkerNode(marker, \"opening\", nested));\n // Plain-text-first content carries the separator as its prefix — the same host predicate the\n // separator sync uses ($isSeparatorPrefixHostText): TextNode SUBCLASSES (VerseNode,\n // ImmutableUnmatchedNode, MarkerNode) render their own marker bytes and attribute runs are\n // engine-owned canonical output, so splicing an NBSP into either rewrites a glyph or corrupts\n // the run. Everything else takes a standalone NBSP spacer, which `$syncOpenerSeparators` adds\n // when the span is next dirtied.\n const [firstContent] = content;\n if ($isSeparatorPrefixHostText(firstContent) && !firstContent.getTextContent().startsWith(NBSP))\n firstContent.setTextContent(NBSP + firstContent.getTextContent());\n }\n span.append(...content);\n if (hasCloser)\n span.append($createMarkerNode(marker, \"closing\", nested));\n}\n","/**\n * The character-style STACK: closing every char span open at a point and reopening the ones that\n * still have content after it. Sibling of charGlyphs.utils.ts, which owns the shape of each span\n * this module builds.\n *\n * ## Why one primitive\n *\n * Paratext 9 has no bespoke algorithm for this. Its `StyleApplicator` closes the open character\n * styles before the point and reopens the still-active ones after it, and every operation that\n * interrupts styled text — applying a non-nesting style, an unformatted space, a footnote-paragraph\n * break, a paragraph split — is the same close-and-reopen with a different thing placed in the gap.\n * Reimplemented per caller, that operation came out right in one place and wrong in four.\n *\n * ## Ordering falls out of the loop, not out of ordering code\n *\n * {@link $liftOutOfChar} closes the ONE span holding the node and reopens a continuation after it.\n * {@link $liftOutOfCharStack} iterates that outwards. Each iteration's continuation lands in the\n * next iteration's \"content after the node\" set, so the closers emerge innermost-to-outermost and\n * the openers outermost-to-innermost with no explicit ordering step:\n *\n * ```\n * \\wj \\+nd thing\\+nd*\\wj* -> \\wj \\+nd thi\\+nd*\\wj* \\wj \\+nd ng\\+nd*\\wj*\n * ```\n *\n * PT9 gets the reopen order WRONG for nested styles — its reopen loop walks the style list\n * innermost-first and emits index 0 without the `+`, so a two-deep stack comes back with the\n * markers swapped. That path has no PT9 test coverage. We port the intent, not the code.\n */\nimport { $createMarkerNode, $isMarkerNode } from \"../features/MarkerNode.js\";\nimport { $buildContinuationCharSpan, $charOwesClosingGlyph, $continuationCharAttributes, } from \"./charGlyphs.utils.js\";\nimport { $createCharNode, $isCharNode } from \"./CharNode.js\";\nimport { $isSeparatorPrefixHostText } from \"./markerSeparators.utils.js\";\nimport { $isNestedCharNode } from \"./nestedGlyphs.utils.js\";\nimport { canonicalAttributeText } from \"./attributeDisplay.utils.js\";\nimport { textTypeState } from \"../collab/delta.state.js\";\nimport { defaultMarkerAttribute } from \"../../converters/usfm/usfmFragmentToUsj.js\";\nimport { NBSP } from \"./node-constants.js\";\nimport { $findMatchingParent } from \"@lexical/utils\";\nimport { $createTextNode, $getState, $isElementNode, $isTextNode } from \"lexical\";\n/**\n * The innermost char span a point sits in: the nearest `CharNode` at or above `node`, or\n * `undefined` when the point is not inside one.\n *\n * Read-only: safe inside `editor.update()` or either read form.\n */\nexport function $innermostCharAncestor(node) {\n return $findMatchingParent(node, $isCharNode) ?? undefined;\n}\n/**\n * Where a lifted node comes to rest: the nearest non-char ancestor of `node` — the note or\n * paragraph a bare marker would land in. Returns `node`'s own parent when `node` is not inside a\n * char span at all.\n *\n * Deliberately NOT delegated to `$findMatchingParent`: for a root-level char span the resting\n * place is the RootNode itself, which `$findMatchingParent` never yields (it stops before testing\n * the root).\n *\n * Read-only: safe inside `editor.update()` or either read form.\n */\nexport function $charStackContainer(node) {\n let parent = node.getParent();\n while ($isCharNode(parent))\n parent = parent.getParent();\n return parent;\n}\n/**\n * Whether `char` holds no CONTENT — only its own glyphs and the structural NBSP separator those\n * glyphs own (markerSeparators.utils.ts). Such a span serializes to `\\nd \\nd*`: a marker pair\n * around nothing, which is not what the user asked for when a close-and-reopen happened to land at\n * a run's edge. A genuine space is content and does NOT make a span empty — only the NBSP does.\n *\n * Read-only: safe inside `editor.update()` or either read form.\n */\nexport function $isCharContentEmpty(char) {\n // The display run is a rendering of the span's attribute state, not content — a span holding\n // nothing but its own `|name=\"value\"` bytes has had its content taken away and is empty. Only a\n // span that closes explicitly can own a run at all ({@link $ownsDisplayRun}); inside an\n // implicitly-closed one, `attribute`-tagged text is ordinary content.\n const ownsDisplayRun = $ownsDisplayRun(char);\n return char\n .getChildren()\n .every((child) => $isMarkerNode(child) ||\n (ownsDisplayRun && $getState(child, textTypeState) === \"attribute\") ||\n ($isTextNode(child) && child.getTextContent().replaceAll(NBSP, \"\") === \"\"));\n}\n/**\n * Whether `char` can own a display run at all. The run is anchored to the closing glyph, and a\n * `closed=\"false\"` span renders neither (attributeDisplay.utils.ts), so inside one — note content,\n * chiefly — `attribute`-tagged text is not a run but ordinary content.\n *\n * Read-only: safe inside `editor.update()` or either read form.\n */\nfunction $ownsDisplayRun(char) {\n return $charOwesClosingGlyph(char);\n}\n/**\n * Remove `char`, leaving its attribute bytes behind as plain text after `contentEnd` when it has\n * any. Paratext 9 keeps an unwrapped span's attributes as literal bytes, and the span is the only\n * thing that carried them — dropping it silently would delete `|lemma=\"grace\"` from the file.\n * Routed through {@link canonicalAttributeText}, the same one serializer `$unwrapCharNode` uses, so\n * the two paths cannot spell the same attributes differently: a lone default attribute collapses to\n * `|value`, anything else stays `|name=\"value\" …`.\n *\n * Mutating: call inside `editor.update()`.\n */\nfunction $removeCharKeepingAttributeBytes(char, contentEnd) {\n const attributes = char.getUnknownAttributes();\n const bytes = attributes\n ? canonicalAttributeText(attributes, defaultMarkerAttribute(char.getMarker()))\n : \"\";\n if (bytes !== \"\")\n contentEnd.insertAfter($createTextNode(bytes));\n char.remove();\n}\n/**\n * Leave `char` EXPLICITLY closed: drop the `closed=\"false\"` convention it was carrying and, where\n * this document renders glyphs, give it the closing glyph that state now owes. Already-explicit\n * spans are untouched.\n *\n * The state is the load-bearing half — it is what serializes — and the glyph follows it, the same\n * pairing `$charOwesClosingGlyph` reads everywhere else. Leaving one without the other makes the\n * marker-edit engine read the mismatch as deletion damage.\n *\n * Mutating: call inside `editor.update()`.\n */\nfunction $closeCharExplicitly(char, renderGlyphs) {\n if ($charOwesClosingGlyph(char))\n return;\n const attributes = char.getUnknownAttributes();\n if (attributes) {\n const rest = { ...attributes };\n delete rest.closed;\n char.setUnknownAttributes(Object.keys(rest).length > 0 ? rest : undefined);\n }\n if (renderGlyphs)\n char.append($createMarkerNode(char.getMarker(), \"closing\", $isNestedCharNode(char)));\n}\n/**\n * Whether writing `node`'s own opening marker is ITSELF how `char` ends, so there is nothing to\n * close and nothing to reopen.\n *\n * True when both spans close implicitly — the `closed=\"false\"` convention ParatextData stamps on\n * note-content and cross-reference markers (`\\ft`, `\\fq`, `\\fr`, `\\fp`, `\\xt`, …), which are\n * terminated by the next bare marker rather than by an end marker. Putting `\\fq` at a caret inside\n * `\\ft` therefore emits no `\\ft*` and no reopened `\\ft`: the remainder of `\\ft`'s content simply\n * belongs to `\\fq` now.\n *\n * False whenever either side closes explicitly. An explicitly-closed `char` (`\\wj`) must emit its\n * `\\wj*` and reopen afterwards, because the new marker cannot terminate it; and an\n * explicitly-closed `node` (`\\add`) ends at its own `\\add*`, so `char`'s remaining content is not\n * part of it and does need reopening.\n *\n * Read-only: safe inside `editor.update()` or either read form.\n */\nfunction $endsImplicitly(node, char) {\n return $isCharNode(node) && !$charOwesClosingGlyph(node) && !$charOwesClosingGlyph(char);\n}\n/**\n * Move `content` into `span`, which is a freshly built, still content-less marker taking over the\n * remainder of the span it just ended ({@link $endsImplicitly}). Any placeholder the builder gave\n * `span` is dropped so the moved nodes become its real content, and a leading text run takes the\n * structural separator the opening glyph owes it (markerSeparators.utils.ts) — the same shape\n * {@link $buildContinuationCharSpan} gives a reopened clone.\n *\n * Mutating: call inside `editor.update()`.\n */\nfunction $absorbIntoCharSpan(span, content, renderGlyphs) {\n if ($isCharContentEmpty(span))\n span.getChildren().forEach((child) => {\n if (!$isMarkerNode(child))\n child.remove();\n });\n // Only PLAIN leading text takes the separator as its prefix — the same host predicate the\n // separator sync uses ($isSeparatorPrefixHostText): TextNode subclasses (VerseNode,\n // ImmutableUnmatchedNode) render their own marker bytes and attribute runs are engine-owned\n // canonical output, so splicing an NBSP into either rewrites a glyph or corrupts the run.\n // Everything else gets its standalone spacer from `$syncOpenerSeparators` on the next dirty.\n const [first] = content;\n if (renderGlyphs && $isSeparatorPrefixHostText(first) && !first.getTextContent().startsWith(NBSP))\n first.setTextContent(NBSP + first.getTextContent());\n span.append(...content);\n}\n/**\n * Lift `node` OUT of the char span `char` to `char`'s parent, splitting `char` around it: content\n * before `node` stays in `char` (its \"before\" half), content after `node` moves to a fresh reopened\n * clone inserted after `node`, and `node` itself becomes a sibling of `char`. A half left with no\n * content ({@link $isCharContentEmpty}) is dropped rather than serialized as an empty marker pair.\n * The reopened clone keeps `char`'s marker, closer convention, and nesting (its glyphs carry the\n * `+` when `char` was itself nested).\n *\n * The one shape with no reopened clone is a `node` that ends `char` implicitly\n * ({@link $endsImplicitly}): there the content after `node` moves INTO it instead.\n *\n * An implicitly-closed `char` is left EXPLICITLY closed when `options.closeImplicitSpans` says the\n * gap cannot terminate it — see {@link CharStackLiftOptions}, which also documents `renderGlyphs`.\n *\n * Mutating: call inside `editor.update()`.\n */\nexport function $liftOutOfChar(node, char, options) {\n const { renderGlyphs, closeImplicitSpans = false } = options;\n // Content strictly after `node`. Two kinds of child stay behind rather than move: `char`'s own\n // closing glyph, which terminates the \"before\" half (the clone gets a fresh one of its own), and\n // its display run — the `|name=\"value\"` bytes are a rendering of `char`'s OWN attribute state,\n // which stays on `char` (`$continuationCharAttributes` deliberately does not copy it, since\n // duplicating it would double those bytes on serialization). Carrying the bytes to the clone\n // left the attributes displayed on a span that does not have them and missing from the span that\n // does — the same content-versus-presentation split `$unwrapCharNode` makes.\n //\n // Only a span that closes EXPLICITLY can own a display run ({@link $ownsDisplayRun}). So inside\n // an implicitly-closed one — note content, chiefly — `attribute`-tagged text is not a run and is\n // ordinary content that must ride along with everything else after the caret.\n const ownsDisplayRun = $ownsDisplayRun(char);\n const after = [];\n for (let sibling = node.getNextSibling(); sibling;) {\n const next = sibling.getNextSibling();\n const isCloser = $isMarkerNode(sibling) && sibling.getMarkerSyntax() === \"closing\";\n const isDisplayRun = ownsDisplayRun && $getState(sibling, textTypeState) === \"attribute\";\n if (!isCloser && !isDisplayRun)\n after.push(sibling);\n sibling = next;\n }\n const endsImplicitly = $endsImplicitly(node, char);\n char.insertAfter(node); // node leaves char, becomes its next sibling\n // Where whatever came out of `char` ends, so a dropped span's attribute bytes land after its\n // former content rather than in front of it.\n let contentEnd = node;\n if (after.length > 0) {\n if (endsImplicitly) {\n $absorbIntoCharSpan(node, after, renderGlyphs);\n }\n else {\n const right = $createCharNode(char.getMarker(), $continuationCharAttributes(char));\n $buildContinuationCharSpan(right, char, after, renderGlyphs);\n node.insertAfter(right);\n if ($isCharContentEmpty(right))\n right.remove();\n else\n contentEnd = right;\n }\n }\n // The gap cannot terminate `char` on its own, so an implicitly-closed one has to be closed for\n // real: `\\ft` runs to the next note marker or `\\f*`, and anything merely placed after its\n // content would re-read as more of that content. Done AFTER the continuation is built, which\n // takes its own closing glyph from `char`'s children — the continuation reopens implicitly, as\n // the span it continues did.\n if (closeImplicitSpans && !endsImplicitly)\n $closeCharExplicitly(char, renderGlyphs);\n if ($isCharContentEmpty(char))\n $removeCharKeepingAttributeBytes(char, contentEnd);\n}\n/**\n * Lift `node` out of EVERY char span enclosing it, closing each on the way out and reopening the\n * ones with content after it — the whole close-and-reopen. `node` comes to rest in the nearest\n * non-char container ({@link $charStackContainer}), carrying with it the content of any span it\n * ended implicitly on the way ({@link $endsImplicitly}).\n *\n * Callers that place something in the gap (an unformatted space, a new non-nesting char span, a\n * paragraph-split marker) insert it at the caret first and lift THAT; the caret point they want\n * afterwards differs per caller, so this function deliberately does not move the selection.\n *\n * @param node - The node to lift. Must already be attached at the point the gap belongs.\n * @param options - How the gap is treated; see {@link CharStackLiftOptions}.\n *\n * Mutating: call inside `editor.update()`.\n */\nexport function $liftOutOfCharStack(node, options) {\n let parent = node.getParent();\n while ($isCharNode(parent)) {\n $liftOutOfChar(node, parent, options);\n parent = node.getParent();\n }\n}\n/**\n * Put the caret at the START of `node`'s content — immediately after the structural separator the\n * opening glyph owns, and descending through nested spans to the innermost one. This is where\n * typing belongs after a close-and-reopen: the user interrupted the text at that point, so the\n * next keystroke continues the reopened run rather than landing outside it. Offset 0 would sit\n * BEFORE the separator, splicing typed text between the glyph and the space it owns.\n *\n * Mutating (moves the selection): call inside `editor.update()`.\n */\nexport function $selectCharContentStart(node) {\n if ($isTextNode(node) && !$isMarkerNode(node)) {\n const offset = node.getTextContent().startsWith(NBSP) ? 1 : 0;\n node.select(offset, offset);\n return;\n }\n if ($isElementNode(node)) {\n const first = node.getChildren().find((child) => !$isMarkerNode(child));\n if (first) {\n $selectCharContentStart(first);\n return;\n }\n node.selectEnd(); // defensive: glyphs only, no content to sit in front of\n }\n}\n","/**\n * Editor-scoped registry of display-run OWNER keys the marker-edit engine currently holds\n * pending. The engine (MarkerEditPlugin, platform) registers its live pending set here so the\n * self-healing display syncs — which live in shared/shared-react and cannot import the engine —\n * can leave a pended owner's run alone instead of resurrecting a deletion the engine has not\n * settled yet. Keyed per editor (main editor and footnote popover each register their own set).\n */\nimport { $getEditor } from \"lexical\";\nconst pendedOwnersByEditor = new WeakMap();\nexport function registerPendedDisplayOwners(editor, pendedKeys) {\n pendedOwnersByEditor.set(editor, pendedKeys);\n return () => {\n if (pendedOwnersByEditor.get(editor) === pendedKeys)\n pendedOwnersByEditor.delete(editor);\n };\n}\n/**\n * The live pending-owner set registered for `editor`, or `undefined` when no marker-edit engine is\n * mounted on it (a non-editable marker mode, or an editor that has torn down). The SAME mutable Set\n * the engine holds, not a snapshot — a reader that keeps the reference sees later pends — so\n * callers must treat it as read-only. Takes the editor explicitly rather than reading `$getEditor()`\n * so it can be called from outside a read/update (the editor-facing `getUsj()` path decides whether\n * to enter a read at all based on whether anything is pending).\n */\nexport function getPendedDisplayOwners(editor) {\n return pendedOwnersByEditor.get(editor);\n}\n/** Whether `node`'s key is pended in the active editor. Call inside a read/update. */\nexport function $isDisplayOwnerPended(node) {\n return pendedOwnersByEditor.get($getEditor())?.has(node.getKey()) ?? false;\n}\n/**\n * Lets the shared self-healing display-run sync (`$syncDisplayRun`, displayRunSync.utils.ts)\n * report that it just found an owner's run destroyed by something other than itself, so the\n * marker-edit engine settles it on caret departure instead of the sync resurrecting it. Writes\n * directly into the SAME mutable Set `registerPendedDisplayOwners` was given, rather than\n * routing the report back through one of the engine's own node transforms: which plugin's\n * transform runs first on a shared dirty node depends on mount order (the sync and the engine\n * are registered by separate, independently ordered plugins), so a report that only took effect\n * via a later engine-side transform would still lose the race whenever the sync happens to run\n * first. A direct write has no such ordering dependency. Call inside a read/update. No-op if no\n * engine is currently registered for the active editor.\n */\nexport function $reportDestroyedDisplayOwner(node) {\n pendedOwnersByEditor.get($getEditor())?.add(node.getKey());\n}\n","/**\n * The shared display-run drivers: ONE self-healing sync transform and ONE caret-held reporter,\n * both parameterized by a {@link DisplayRunDescriptor}. Every engine-owned display kind runs\n * through these, so the four duties (construct, self-heal-with-grace, pend-on-edit/delete,\n * settle-on-departure) cannot diverge per kind.\n *\n * Descriptor INSTANCES live one layer up (displayRun/displayRunRegistry.ts) because they need the\n * converters; taking a descriptor as a parameter keeps these drivers importable from anywhere in\n * `nodes/usj`.\n */\nimport { $createAttributeRunNode } from \"./AttributeRunNode.js\";\nimport { DELTA_CHANGE_TAG } from \"./node-constants.js\";\nimport { $isDescendantOf } from \"./node.utils.js\";\nimport { $isDisplayOwnerPended, $reportDestroyedDisplayOwner, } from \"./pendedDisplayOwners.utils.js\";\nimport { $createMarkerNode } from \"../features/MarkerNode.js\";\nimport { textTypeState } from \"../collab/delta.state.js\";\nimport { $createTextNode, $getEditor, $getNodeByKey, $getSelection, $hasUpdateTag, $isRangeSelection, $isTextNode, $setState, } from \"lexical\";\n/** Whether any piece of the run is currently in the tree. */\nfunction runHasPieces(pieces) {\n return Boolean(pieces.opener || pieces.value || pieces.closer || pieces.wrapper);\n}\n/**\n * Whether any of the run's BYTE pieces — opener, value, closer — is currently in the tree. A\n * surviving `AttributeRunNode` wrapper alone does not count: an emptied wrapper is the transient\n * husk `$clearRun` deliberately leaves behind, not displayed bytes. Destruction detection reads\n * THIS predicate, so deleting a wrapped run's visible bytes reports the owner even when the\n * deletion left the husk standing ({@link runHasPieces} would see the husk and call the run\n * alive, and the sync would then resurrect bytes the user just deleted).\n */\nfunction runHasByteContent(pieces) {\n return Boolean(pieces.opener || pieces.value || pieces.closer);\n}\n/**\n * Whether a display kind's canonical value bytes OPEN with the structural whitespace separator\n * that stands between a marker and its value (`\\va` + NBSP + `2`, `\\cat` + NBSP + `People`).\n * Kinds whose value carries no such separator — a char span's `|…` attribute bytes, an optbreak's\n * `//` token — are outside the licence {@link valueDiverges} grants and compare byte-for-byte.\n */\nfunction opensWithSeparator(text) {\n return /^\\s/.test(text);\n}\n/**\n * Whether the run's DISPLAYED value bytes diverge from the ones the owner's own state calls for.\n *\n * Whitespace FLANKING the value is not a divergence for a kind whose canonical bytes carry a\n * separator ({@link opensWithSeparator}). Whitespace between an attribute marker and its value is\n * structural: the writer emits exactly one separator space whatever the screen shows, so those\n * bytes never reach the document — the same licence a trailing space at the end of a paragraph\n * already has. Counting them as divergence made every such run canonicalize a space the user had\n * just typed with the caret sitting right after it: while the caret held the site the run showed\n * `\\va 2 \\va*`, and on departure the settle re-tokenized it back to `\\va 2\\va*`. That is a\n * keystroke accepted and then discarded, which \"no silent no-ops\" forbids.\n *\n * Two neighbouring edits stay divergences, because neither is whitespace the writer would supply:\n * deleting the separator outright (the value no longer opens with whitespace at all), and\n * whitespace typed INSIDE the value, which genuinely respells it (`12` → `1 2`) and must settle\n * onto owner state like any other value edit.\n */\nfunction valueDiverges(actual, expected) {\n if (actual === expected)\n return false;\n if (actual === undefined || expected === undefined)\n return true;\n if (!opensWithSeparator(expected) || !opensWithSeparator(actual))\n return true;\n const value = expected.trim();\n return value === \"\" || actual.trim() !== value;\n}\n/**\n * Whether `pieces` diverge from `expected`.\n *\n * A run that should not exist diverges the moment any piece survives. A run that should exist\n * diverges when its value's bytes differ ({@link valueDiverges}, which excuses the structural\n * whitespace flanking a separator-bearing value), when either glyph of a glyph-bearing kind is\n * missing, or — for a wrapper-written kind — when the pieces are still riding LOOSE: the wrap\n * migration is itself a divergence to heal, and treating it as one here is what lets the caret\n * grace it and the settle finish it, instead of the migration being deferred forever with nothing\n * pending it.\n */\nexport function $runDiverges(descriptor, pieces, expected) {\n if (!expected.wantsRun)\n return runHasPieces(pieces);\n if (valueDiverges(pieces.value?.getTextContent(), expected.valueText))\n return true;\n // A closer-less kind (`closerSyntax: \"none\"` — a chapter's `\\cp`) owes only its opener; every\n // other glyph-bearing kind owes both glyphs.\n if (descriptor.byteFormat.glyphs !== \"none\") {\n if (!pieces.opener)\n return true;\n if (descriptor.byteFormat.closerSyntax !== \"none\" && !pieces.closer)\n return true;\n }\n return descriptor.byteFormat.writer === \"wrapper\" && pieces.wrapper === undefined;\n}\n/**\n * True when `owner`'s run diverges from `$runDiverges` for EXACTLY the wrap-migration reason: the\n * value's bytes already match, both glyphs of a glyph-bearing kind are present, and the ONLY thing\n * missing is the wrapper itself. This is the one slice of `$runDiverges` the marker-edit engine's\n * departure settle may finish by calling `$syncDisplayRun` directly, instead of routing to a Tier-2\n * re-tokenize: every OTHER divergence (a missing/stale value, a missing glyph) means the DISPLAYED\n * bytes have genuinely drifted from `owner`'s own state — deleted or edited content — and only\n * re-tokenizing (which reads the displayed bytes back into state) can settle that without\n * resurrecting what the user just changed. A kind whose `byteFormat.writer` is not `\"wrapper\"`\n * (nothing to migrate) always returns `false`.\n */\nexport function $runNeedsOnlyWrapMigration(descriptor, owner) {\n if (descriptor.byteFormat.writer !== \"wrapper\")\n return false;\n const expected = descriptor.expectedPieces(owner);\n if (!expected.wantsRun)\n return false;\n const pieces = descriptor.scanPieces(owner);\n if (valueDiverges(pieces.value?.getTextContent(), expected.valueText))\n return false;\n if (descriptor.byteFormat.glyphs !== \"none\") {\n if (!pieces.opener)\n return false;\n if (descriptor.byteFormat.closerSyntax !== \"none\" && !pieces.closer)\n return false;\n }\n return pieces.wrapper === undefined;\n}\n/** True when NO piece of `owner`'s run remains — the run was deleted outright, as opposed to a\n * partial mangle that still leaves debris to repair around. */\nexport function $runEntirelyAbsent(descriptor, owner) {\n return !runHasByteContent(descriptor.scanPieces(owner));\n}\n/**\n * True when `owner`'s run diverges from what its state calls for but the collapsed caret holds the\n * run's SITE, so the sync must leave it alone and the marker-edit engine settle it on departure.\n *\n * Two arms are shared by every WRITER-DRIVEN kind: the caret anywhere inside the run's wrapper\n * subtree (an element point can land on the wrapper itself, which no piece-specific arm\n * recognizes), and the caret inside a live value node. Everything else is the descriptor's own\n * `graceSite` — the insertion-point and glyph-debris anchors that differ by tree shape.\n *\n * A `\"kind-owned\"` writer (the separator, the nested glyph) skips both shared arms and `$runDiverges`\n * entirely: its `expectedPieces`/`scanPieces` are deliberately empty, so the shared divergence rule\n * would never see anything to grace. Its `graceSite` is authoritative on its own instead.\n */\nexport function $caretHoldsRunSite(descriptor, owner) {\n if (!owner.isAttached())\n return false;\n // A kind-owned writer keeps its own divergence rule (a separator's missing NBSP is not a run\n // piece at all), so its graceSite is authoritative on its own.\n if (descriptor.byteFormat.writer === \"kind-owned\")\n return descriptor.graceSite(owner, {});\n const expected = descriptor.expectedPieces(owner);\n const pieces = descriptor.scanPieces(owner);\n if (!$runDiverges(descriptor, pieces, expected))\n return false;\n const selection = $getSelection();\n if (!$isRangeSelection(selection) || !selection.isCollapsed())\n return false;\n const anchorNode = selection.anchor.getNode();\n const { wrapper, value } = pieces;\n if (wrapper && (anchorNode.is(wrapper) || $isDescendantOf(anchorNode, wrapper.getKey())))\n return true;\n // A live value node is the mid-edit case: the caret is IN the bytes, so nothing else matters.\n if (value)\n return anchorNode.is(value);\n return descriptor.graceSite(owner, pieces);\n}\n/**\n * Whether `owner`'s run was destroyed by something other than this sync since the last committed\n * state. Gated on `wantsRun` so a call can never react to its own heal-removal: the writer below\n * only removes pieces when the run is NOT wanted, the opposite of this condition.\n *\n * Detecting the destruction from the last-committed state, inside the sync's own decision path,\n * keeps the result independent of which plugin's transforms happen to run first on a shared dirty\n * node — mount order varies across hosts. A remote collab apply is excluded: it clears owner state\n * directly, so the run is already unwanted before this sync next runs.\n */\nfunction $runDestroyedSinceLastCommit(descriptor, owner, expected, pieces) {\n if (!expected.wantsRun)\n return false;\n // Byte pieces only: a deletion that empties the wrapper but leaves the husk standing is still\n // a destroyed run — see runHasByteContent.\n if (runHasByteContent(pieces))\n return false;\n if ($hasUpdateTag(DELTA_CHANGE_TAG))\n return false;\n return $getEditor()\n .getEditorState()\n .read(() => {\n const previous = $getNodeByKey(owner.getKey());\n if (!previous || !descriptor.ownerPredicate(previous))\n return false;\n return runHasByteContent(descriptor.scanPieces(previous));\n });\n}\n/** Remove every surviving piece — the \"no run wanted\" path. An emptied wrapper is left in place:\n * it is a transient husk the marker-edit engine's settle removes, so the removal and the owner's\n * own deletion policy stay in one place. */\nfunction $clearRun(pieces) {\n pieces.opener?.remove();\n pieces.value?.remove();\n pieces.closer?.remove();\n}\nfunction $createValueNode(text) {\n const value = $createTextNode(text);\n $setState(value, textTypeState, \"attribute\");\n return value;\n}\n/** Ensure the run's wrapper exists, healing any loose survivors forward into a freshly created one\n * inserted where the run belongs. This is the one migration path from loose to wrapped. */\nfunction $ensureWrapper(descriptor, owner, pieces) {\n if (pieces.wrapper)\n return pieces.wrapper;\n const { runKind, insertRunAfter } = descriptor.byteFormat;\n const anchor = insertRunAfter?.(owner);\n if (!runKind || !anchor)\n return undefined;\n const created = $createAttributeRunNode(runKind);\n anchor.insertAfter(created);\n if (pieces.opener)\n created.append(pieces.opener);\n if (pieces.value)\n created.append(pieces.value);\n if (pieces.closer)\n created.append(pieces.closer);\n return created;\n}\n/** Build or repair the run AROUND whatever pieces survive, in their fixed order. A found piece\n * already sits in its correct position (the scan reads them in order), so a missing one is\n * inserted into its gap and a leftover is reused in place, never duplicated. */\nfunction $writeRun(descriptor, owner, pieces, expected) {\n const { writer, glyphs, glyphMarker, closerSyntax, insertRunBefore } = descriptor.byteFormat;\n if (writer === \"owner-children\") {\n const anchor = insertRunBefore?.(owner);\n if (!anchor || expected.valueText === undefined)\n return;\n if ($isTextNode(pieces.value))\n pieces.value.setTextContent(expected.valueText);\n else\n anchor.insertBefore($createValueNode(expected.valueText));\n return;\n }\n const wrapper = $ensureWrapper(descriptor, owner, pieces);\n if (!wrapper || glyphs === \"none\" || !glyphMarker || !closerSyntax)\n return;\n const opener = pieces.opener ??\n (() => {\n const created = $createMarkerNode(glyphMarker(owner), \"opening\");\n const first = wrapper.getFirstChild();\n if (first)\n first.insertBefore(created);\n else\n wrapper.append(created);\n return created;\n })();\n let value = pieces.value;\n if (expected.valueText === undefined) {\n value?.remove();\n value = undefined;\n }\n else if ($isTextNode(value)) {\n // The same licence the divergence rule grants: a value the user only padded with structural\n // whitespace is left exactly as typed, even when a missing glyph is what brought the writer here.\n if (valueDiverges(value.getTextContent(), expected.valueText))\n value.setTextContent(expected.valueText);\n }\n else {\n value = $createValueNode(expected.valueText);\n opener.insertAfter(value);\n }\n // A closer-less kind never builds a trailing glyph; its wrapper bounds the value instead.\n if (closerSyntax !== \"none\" && !pieces.closer)\n (value ?? opener).insertAfter($createMarkerNode(closerSyntax === \"selfClosing\" ? \"\" : glyphMarker(owner), closerSyntax));\n}\n/**\n * Heal `owner`'s display run to what its own state calls for: insert a missing run, rewrite a\n * stale one, migrate a loose one into its wrapper, or remove one that is no longer wanted —\n * except while the engine holds the owner pended, while the run was just destroyed by something\n * else (reported so the engine settles it), or while the caret holds the run's site. Idempotent —\n * writes only on change, so the registering transform converges.\n *\n * Kinds whose pieces the driver does not write (`\"kind-owned\"` and `\"read-only\"` byte formats)\n * return immediately: they join the registry for their pend/settle duties only.\n *\n * @param descriptor - The kind's descriptor.\n * @param owner - The owner whose run to sync. Must be called inside `editor.update()`.\n */\nexport function $syncDisplayRun(descriptor, owner) {\n const { writer } = descriptor.byteFormat;\n if (writer === \"kind-owned\" || writer === \"read-only\")\n return;\n // An earlier transform in the same pass may have merged or removed the owner; a detached node\n // has no tree position to derive from.\n if (!owner.isAttached())\n return;\n const expected = descriptor.expectedPieces(owner);\n const pieces = descriptor.scanPieces(owner);\n if (!$runDiverges(descriptor, pieces, expected))\n return;\n if ($isDisplayOwnerPended(owner))\n return;\n if ($runDestroyedSinceLastCommit(descriptor, owner, expected, pieces)) {\n $reportDestroyedDisplayOwner(owner);\n return;\n }\n if ($caretHoldsRunSite(descriptor, owner))\n return;\n if (!expected.wantsRun) {\n $clearRun(pieces);\n return;\n }\n $writeRun(descriptor, owner, pieces, expected);\n}\n/**\n * Sync `owner`'s run for `descriptor`, then pend `owner` while the caret holds the run's site so\n * caret departure settles it. The pairing every registration home needs: the sync leaves a\n * caret-held divergence alone, and without the matching pend nothing would ever settle it — the\n * run would silently resurrect from the owner's still-set state on the next unrelated dirtying.\n *\n * @param descriptor - The kind's descriptor.\n * @param owner - The owner whose run to sync. Must be called inside `editor.update()`.\n * @param pendingKeys - The marker-edit engine's live pending set.\n */\nexport function $syncAndPendDisplayRun(descriptor, owner, pendingKeys) {\n $syncDisplayRun(descriptor, owner);\n if (owner.isAttached() && $caretHoldsRunSite(descriptor, owner))\n pendingKeys.add(owner.getKey());\n}\n","/**\n * Glyph text and document positions: the single place that owns WHICH rendered bytes are display\n * rather than document, and HOW a caret or selection point that names those bytes is re-expressed\n * so an edit never operates on them.\n *\n * ## The property, not a list of node types\n *\n * A glyph text node is a `TextNode` whose rendered text is a PICTURE OF ITS OWN STATE — the\n * document owns none of those bytes. A `MarkerNode`'s `__text` is a cache of (marker, syntax,\n * nested) that only its setters write ({@link $isCanonicalMarkerNode} exists because that cache can\n * drift). A `VerseNode`'s `__text` is a rendering of its `__number`. An attribute display run's\n * text is a rendering of the char span's attributes, and the marker-trailing separator is a\n * rendering of the `[glyph, separator, …content]` layout itself. Membership is decided by that\n * property, and {@link $isGlyphTextNode} is the one place it is decided — a kind wired in\n * separately somewhere else is the recurring defect this module exists to end.\n *\n * ## Two spellings of one place, and why each gesture picks a different one\n *\n * A glyph's bytes are visible, so the caret really can sit between two of them, and the arrow keys\n * really do walk through them one character at a time. Both of a glyph's ENDS, though, name a place\n * on screen that a neighbouring node also names, and nothing the user does can choose between the\n * two spellings. So an edit must pick the spelling under which the glyph is NOT one of its\n * operands, and which spelling that is depends on what the edit does with a point:\n *\n * - **A collapsed caret is an insertion point**, and it names no node as an operand. Its illegal\n * region is a glyph's INTERIOR: a node inserted between two display bytes cuts the picture in\n * half and hands the right-hand half to the document as content — a verse number arriving in the\n * file as text, a closing glyph's tail stranded as literal paragraph bytes. An interior point\n * therefore resolves to the glyph's TRAILING end, the end a caret that has already crossed the\n * glyph's first byte is heading for. Both ends are ordinary positions and are left exactly as\n * they are.\n * - **A range endpoint drags its own node into the range.** Its illegal region is the mirror image:\n * a glyph's ENDS, where the endpoint takes the WHOLE glyph node as a wrap target even when it\n * selects none of its bytes. A glyph is its span's identity, so moving one into another span\n * deletes the span it came from — the marker vanishes from the file while its glyph is still on\n * screen. An endpoint at an end is therefore re-spelled onto the neighbouring node, INWARD, so\n * the glyph falls outside the range. An endpoint strictly inside a glyph is left alone: the user\n * can see exactly which bytes are highlighted, and re-tokenizing them is Invariant I working as\n * intended rather than a divergence.\n *\n * Stated once: express the point so the glyph is not an operand. The two arms differ in spelling\n * because insertion and range membership differ in what naming a node means, not because the\n * exclusion rule differs.\n *\n * ## Agreeing with arrow traversal\n *\n * `ArrowNavigationPlugin` walks glyph text one character at a time, so its idea of where a glyph\n * begins and ends is the same as this module's: the interior is inside, the two ends are the\n * boundary positions, and a caret at an end has crossed the glyph whole. Nothing here calls a\n * position outside a glyph that a press treats as inside it, or the reverse. The spelling agrees\n * too — the trailing end resolved to above is written as (glyph, its length), which is exactly the\n * spelling that plugin's canonicalizer prefers for that screen location (the end of the nearest\n * preceding visible text) over the neighbouring node's offset 0. The one thing this module does\n * NOT do is move the caret for traversal's sake: it re-expresses the point an EDIT acts at.\n *\n * ## What this module does NOT own\n *\n * - WHICH boundary an edit should prefer once the point is legal — `caretBoundaries.utils.ts` owns\n * the content-boundary convention, and `ArrowNavigationPlugin` owns the traversal one.\n * - Which nodes are skipped when counting USJ content indexes\n * (`$shouldIgnoreNodeForContentIndexes`, node.utils.ts). That question is broader: it also skips\n * empty and NBSP-only text and line breaks, which are legal insertion hosts and must stay so.\n * - Whether a glyph's bytes are at rest or mid-edit ({@link $isCanonicalMarkerNode}), and what to do\n * about it — the marker-edit engine owns healing and settling.\n */\nimport { $isImmutableUnmatchedNode } from \"../features/ImmutableUnmatchedNode.js\";\nimport { $isCanonicalMarkerNode, $isMarkerNode } from \"../features/MarkerNode.js\";\nimport { MARKER_TRAILING_SPACE_TEXT_TYPE, textTypeState } from \"../collab/delta.state.js\";\nimport { $isCharNode } from \"./CharNode.js\";\nimport { $isVerseNode } from \"./VerseNode.js\";\nimport { $getSelection, $getState, $isRangeSelection, $isTextNode, } from \"lexical\";\n/**\n * Whether `node`'s rendered text is engine-owned DISPLAY bytes — a picture of the node's own state\n * — rather than document content. See the module doc for why this is one property and not four\n * unrelated node types; anything new whose text is derived from state belongs here, and nowhere\n * else.\n *\n * Read-only: call inside `editor.getEditorState().read(...)` or an update.\n */\nexport function $isGlyphTextNode(node) {\n if (!$isTextNode(node))\n return false;\n // A marker glyph (`\\add`, `\\add*`, a paragraph's `\\p` prefix) and an editable verse marker\n // (`\\v` + NBSP + number + space) both render their own state as text.\n if ($isMarkerNode(node) || $isVerseNode(node))\n return true;\n // An unmatched-closer glyph (`\\f*` with no opener) is likewise a picture of its own\n // `__marker` state. It is a TextNode subclass, so without this arm the caret is never\n // normalized out of it and an insert can split the glyph (`\\f` + inserted note + a stranded\n // `*`).\n if ($isImmutableUnmatchedNode(node))\n return true;\n // The attribute display run (`|gloss=\"x\"`) renders the span's attributes; the marker-trailing\n // separator renders the prefix layout. Both are tagged rather than typed, so they are read off\n // the tag the engine writes when it builds them.\n const textType = $getState(node, textTypeState);\n return textType === \"attribute\" || textType === MARKER_TRAILING_SPACE_TEXT_TYPE;\n}\n/**\n * Whether a caret point (`node`, `offset`) sits INSIDE marker glyph text — as opposed to at the\n * TRAILING EDGE of a char span's canonical closing glyph, which is genuinely AFTER the span:\n * arrow traversal and clicks park the caret there at the end of a paragraph whose last child is\n * an inline span, and Enter there is a paragraph action (open the Enter menu), not a marker\n * edit. A NON-canonical (pended, mid-edit) closer keeps its trailing edge \"inside\": the caret is\n * there because the user is editing the glyph byte-by-byte, and Enter must keep settling that\n * edit instead of splitting. An OPENING glyph's trailing edge stays \"inside\" too — it is the\n * span's interior (the separator/content follows it). Deliberately scoped to CHAR-parented\n * closers: a display-run wrapper's closer (`\\va*`, `\\cat*`, a milestone's `\\*`) keeps today's\n * swallow — a split at that caret would land inside the `AttributeRunNode`, a path with no\n * close-and-reopen story yet.\n *\n * This asks whether the caret is in a marker the user may be EDITING, which is why an opening\n * glyph's trailing edge counts. It is not the same question as whether the point is a legal\n * document POSITION ({@link $normalizeSelectionOutOfGlyphText}), where both of a glyph's ends are\n * ordinary places to stand. Keeping the two in one module is deliberate: they read the same bytes\n * and the difference between them is the thing that is easy to get wrong.\n *\n * Read-only: call inside `editor.getEditorState().read(...)` or an update.\n */\nexport function $isPointInMarkerGlyphText(node, offset) {\n if (!$isMarkerNode(node))\n return false;\n return !(offset === node.getTextContentSize() &&\n node.getMarkerSyntax() !== \"opening\" &&\n $isCanonicalMarkerNode(node) &&\n $isCharNode(node.getParent()));\n}\n/**\n * Whether a range selection's caret end sits inside marker glyph text — the guard\n * `MarkerEditPlugin` and `UsjNodesMenuPlugin` use to swallow Enter presses inside a marker. Read\n * from the FOCUS point (the live cursor end, correct even for a backward range — the project's\n * standing rule for \"the node the caret is in\"); for the common collapsed caret the two points\n * coincide. The trailing edge of a canonical closer does NOT count (see\n * {@link $isPointInMarkerGlyphText}).\n * Read-only: call inside `editor.getEditorState().read(...)` or an update.\n */\nexport function $isSelectionInMarkerNode() {\n const selection = $getSelection();\n if (!$isRangeSelection(selection))\n return false;\n return $isPointInMarkerGlyphText(selection.focus.getNode(), selection.focus.offset);\n}\n/** The glyph text node a point is expressed against, if it is expressed against one. */\nfunction $glyphAtPoint(point) {\n if (point.type !== \"text\")\n return undefined;\n const node = point.getNode();\n return $isTextNode(node) && $isGlyphTextNode(node) ? node : undefined;\n}\n/** The glyph node a point sits strictly between two display bytes of, if it does. */\nfunction $glyphAroundPoint(point) {\n const node = $glyphAtPoint(point);\n if (!node)\n return undefined;\n return point.offset > 0 && point.offset < node.getTextContentSize() ? node : undefined;\n}\n/** The glyph node a point names one of the ENDS of, if it does. */\nfunction $glyphAtPointEnd(point) {\n const node = $glyphAtPoint(point);\n if (!node)\n return undefined;\n return point.offset === 0 || point.offset === node.getTextContentSize() ? node : undefined;\n}\nfunction snapshotPoint(point) {\n return { key: point.key, offset: point.offset, type: point.type };\n}\nfunction restorePoint(point, snapshot) {\n point.set(snapshot.key, snapshot.offset, snapshot.type);\n}\n/**\n * The position a range endpoint naming a glyph's end should move to, so the glyph stops being part\n * of the range — or `undefined` when it should not move.\n *\n * Walks siblings in `direction`, because glyphs sit next to each other (a paragraph's prefix is a\n * glyph followed by its separator) and one step would only move the problem along. The walk is\n * ALL-OR-NOTHING: it must reach a text node that is not itself a glyph, or the endpoint stays put.\n * Landing anywhere else would trade one glyph for another, and the next thing along is often an\n * element — a char span — where stepping in would change which span the range starts in. Deciding\n * span membership is not this module's call.\n */\nfunction $landingPastGlyphs(point, direction) {\n let cursor = $glyphAtPointEnd(point);\n while (cursor) {\n const sibling = direction === \"next\" ? cursor.getNextSibling() : cursor.getPreviousSibling();\n if (!$isTextNode(sibling))\n return undefined;\n if (!$isGlyphTextNode(sibling))\n return { node: sibling, offset: direction === \"next\" ? 0 : sibling.getTextContentSize() };\n cursor = sibling;\n }\n return undefined;\n}\n/** Apply {@link $landingPastGlyphs} to `point`, reporting whether it moved. */\nfunction $stepPointPastGlyphs(point, direction) {\n const landing = $landingPastGlyphs(point, direction);\n if (!landing)\n return false;\n point.set(landing.node.getKey(), landing.offset, \"text\");\n return true;\n}\n/**\n * Re-express `selection` so no glyph is an operand of the edit about to run, and report whether\n * anything moved. The one exclusion point: every gesture that places a node at a caret or acts on a\n * selected range routes through this instead of testing for its own favourite glyph shape.\n *\n * A COLLAPSED selection is an insertion point: a point in a glyph's interior resolves to that\n * glyph's trailing end, and the ends are left alone. A RANGE has its two endpoints stepped inward\n * past any glyph they name an end of, and interior endpoints are left alone. The module doc carries\n * the reasoning for the asymmetry — it is one rule about operands, not two policies.\n *\n * Both range steps are all-or-nothing (see {@link $landingPastGlyphs}), and a trim that would\n * invert or collapse the selection is abandoned whole — that happens when both endpoints name the\n * same glyph, as a selection of exactly one glyph does. Leaving the range as the user made it beats\n * guessing at a position this module cannot justify.\n *\n * Mutating: call inside `editor.update()`, before the edit reads the selection.\n */\nexport function $normalizeSelectionOutOfGlyphText(selection) {\n if (selection.isCollapsed()) {\n const glyph = $glyphAroundPoint(selection.anchor);\n if (!glyph)\n return false;\n const trailingEnd = glyph.getTextContentSize();\n selection.anchor.set(glyph.getKey(), trailingEnd, \"text\");\n selection.focus.set(glyph.getKey(), trailingEnd, \"text\");\n return true;\n }\n const wasBackward = selection.isBackward();\n const start = wasBackward ? selection.focus : selection.anchor;\n const end = wasBackward ? selection.anchor : selection.focus;\n const before = [snapshotPoint(start), snapshotPoint(end)];\n const movedStart = $stepPointPastGlyphs(start, \"next\");\n const movedEnd = $stepPointPastGlyphs(end, \"previous\");\n if (!movedStart && !movedEnd)\n return false;\n if (selection.isCollapsed() || selection.isBackward() !== wasBackward) {\n restorePoint(start, before[0]);\n restorePoint(end, before[1]);\n return false;\n }\n return true;\n}\n","/**\n * A block-level container for one verse, used by the block verse view (`ViewOptions.verseLayout`).\n *\n * A verse is normally an inline milestone marker with no element around its text, so there is no\n * per-verse box for a layout to position. This node is that box: it holds the verse's paragraphs -\n * plural, so poetry keeps its `q1`/`q2` line structure - while still occupying a single row.\n *\n * Read-only. It is produced by the USJ-to-editor adaptor and has no editing surface; it is not\n * round-trippable back to USJ, because a source paragraph spanning several verses is split across\n * their blocks.\n */\nimport { $applyNodeReplacement, ElementNode, } from \"lexical\";\nimport { parseVerseRange } from \"./node.utils.js\";\nexport const VERSE_BLOCK_TYPE = \"verse-block\";\nexport const VERSE_BLOCK_VERSION = 1;\nexport const VERSE_BLOCK_CLASS_NAME = \"verse-block\";\nexport class VerseBlockNode extends ElementNode {\n /** The verse marker verbatim. Authoritative: the range is derived from it, never stored. */\n __number;\n constructor(verseNumber = \"\", key) {\n super(key);\n this.__number = verseNumber;\n }\n static getType() {\n return VERSE_BLOCK_TYPE;\n }\n static clone(node) {\n return new VerseBlockNode(node.__number, node.__key);\n }\n static importJSON(serializedNode) {\n return $createVerseBlockNode().updateFromJSON(serializedNode);\n }\n updateFromJSON(serializedNode) {\n return super.updateFromJSON(serializedNode).setNumber(serializedNode.number);\n }\n setNumber(verseNumber) {\n if (this.__number === verseNumber)\n return this;\n const self = this.getWritable();\n self.__number = verseNumber;\n return self;\n }\n getNumber() {\n return this.getLatest().__number;\n }\n /** The first and last verse numbers this block covers. A bridge covers more than one. */\n getRange() {\n return parseVerseRange(this.getNumber());\n }\n createDOM() {\n const dom = document.createElement(\"div\");\n dom.classList.add(VERSE_BLOCK_CLASS_NAME);\n setVerseAttributes(dom, this.__number);\n return dom;\n }\n updateDOM(prevNode, dom) {\n if (prevNode.__number !== this.__number)\n setVerseAttributes(dom, this.__number);\n return false;\n }\n // No `exportDOM`/`importDOM`: Lexical's default export already emits `createDOM`'s element, so\n // the HTML flavor of a copied passage carries these wrappers, which an ordinary paste unwraps\n // because there is no import counterpart. Block verse is read-only, so pasting one back in is\n // not a supported flow.\n /**\n * Keeps the wrapper out of the `application/x-lexical-editor` clipboard payload; its paragraphs\n * travel in its place.\n *\n * Every platform editor shares one Lexical namespace, so a payload copied from a block verse\n * editor is accepted when pasted into an ordinary one - which registers no `VerseBlockNode`, so\n * `$parseSerializedNode` would throw on the unknown type and `onError` would rethrow and tear the\n * editor down. `TypedMarkNode` and `UnknownNode` exclude themselves the same way; Lexical's\n * clipboard only ever asks with `\"html\"`.\n */\n excludeFromCopy(destination) {\n return destination !== \"clone\";\n }\n exportJSON() {\n return {\n ...super.exportJSON(),\n type: VERSE_BLOCK_TYPE,\n number: this.getNumber(),\n version: VERSE_BLOCK_VERSION,\n };\n }\n canBeEmpty() {\n return false;\n }\n}\n/**\n * Attributes a layout consumes to place the block on a row; `data-verse-start`/`-end` let a bridge\n * span rows. Deliberately not `data-number`, which the inner verse marker span already uses.\n */\nfunction setVerseAttributes(dom, verseNumber) {\n const { start, end } = parseVerseRange(verseNumber);\n // Imported USFM can carry a reversed bridge like `3-1`, which would hand a layout a negative\n // span. Publish the range only when it describes one, and remove rather than skip the\n // attributes otherwise, so a stale range from a previous number cannot be read as this verse's.\n const isPublishable = !isNaN(start) && !isNaN(end) && start <= end;\n dom.setAttribute(\"data-verse-number\", verseNumber);\n setOrRemoveAttribute(dom, \"data-verse-start\", isPublishable ? start : NaN);\n setOrRemoveAttribute(dom, \"data-verse-end\", isPublishable ? end : NaN);\n}\nfunction setOrRemoveAttribute(dom, name, value) {\n if (isNaN(value))\n dom.removeAttribute(name);\n else\n dom.setAttribute(name, value.toString());\n}\nexport function $createVerseBlockNode(verseNumber) {\n return $applyNodeReplacement(new VerseBlockNode(verseNumber));\n}\nexport function $isVerseBlockNode(node) {\n return node instanceof VerseBlockNode;\n}\nexport function isSerializedVerseBlockNode(node) {\n return node?.type === VERSE_BLOCK_TYPE;\n}\n","import { ImmutableTypedTextNode } from \"../features/ImmutableTypedTextNode.js\";\nimport { ImmutableUnmatchedNode } from \"../features/ImmutableUnmatchedNode.js\";\nimport { MarkerNode } from \"../features/MarkerNode.js\";\nimport { UnknownNode } from \"../features/UnknownNode.js\";\nimport { AttributeRunNode } from \"./AttributeRunNode.js\";\nimport { BookNode } from \"./BookNode.js\";\nimport { ChapterNode } from \"./ChapterNode.js\";\nimport { CharNode } from \"./CharNode.js\";\nimport { ImmutableChapterNode } from \"./ImmutableChapterNode.js\";\nimport { ImmutableTableNode } from \"./ImmutableTableNode.js\";\nimport { ImmutableTableRowNode } from \"./ImmutableTableRowNode.js\";\nimport { ImmutableTableCellNode } from \"./ImmutableTableCellNode.js\";\nimport { $createImpliedParaNode, ImpliedParaNode } from \"./ImpliedParaNode.js\";\nimport { MilestoneNode } from \"./MilestoneNode.js\";\nimport { NoteNode } from \"./NoteNode.js\";\nimport { ParaNode } from \"./ParaNode.js\";\nimport { VerseNode } from \"./VerseNode.js\";\nimport { ParagraphNode } from \"lexical\";\nexport * from \"./attributeDisplay.utils.js\";\nexport * from \"./AttributeRunNode.js\";\nexport * from \"./BookNode.js\";\nexport * from \"./caretBoundaries.utils.js\";\nexport * from \"./ChapterNode.js\";\nexport * from \"./charGlyphs.utils.js\";\nexport * from \"./CharNode.js\";\nexport * from \"./charStack.utils.js\";\nexport * from \"./displayRunDescriptor.js\";\nexport * from \"./displayRunSync.utils.js\";\nexport * from \"./glyphPositions.utils.js\";\nexport * from \"./ImmutableChapterNode.js\";\nexport * from \"./ImmutableTableNode.js\";\nexport * from \"./ImmutableTableRowNode.js\";\nexport * from \"./ImmutableTableCellNode.js\";\nexport * from \"./ImpliedParaNode.js\";\nexport * from \"./markerSeparators.utils.js\";\nexport * from \"./MilestoneNode.js\";\nexport * from \"./nestedGlyphs.utils.js\";\nexport * from \"./node-constants.js\";\nexport * from \"./node.utils.js\";\nexport * from \"./NoteNode.js\";\nexport * from \"./ParaNode.js\";\nexport * from \"./pendedDisplayOwners.utils.js\";\nexport * from \"./VerseBlockNode.js\";\nexport * from \"./VerseNode.js\";\n// `VerseBlockNode` is deliberately absent below: it is registered only by editors using the block\n// verse layout, so every other editor's node registry is unchanged. See `usjBlockVerseNodes`.\n// Safe because the node excludes itself from the clipboard payload, so a passage copied out of a\n// block verse editor cannot reach an editor that has no class for it.\nexport const usjBaseNodes = [\n BookNode,\n ImmutableChapterNode,\n ChapterNode,\n VerseNode,\n CharNode,\n NoteNode,\n MilestoneNode,\n MarkerNode,\n UnknownNode,\n ImmutableTypedTextNode,\n ImmutableUnmatchedNode,\n ParaNode,\n ImpliedParaNode,\n ImmutableTableNode,\n ImmutableTableRowNode,\n ImmutableTableCellNode,\n // The forward adaptor (usj-editor.adaptor.ts, platform) serializes editable-mode verse/milestone\n // display runs as AttributeRunNode wrappers, and this package's own self-healing sync\n // (displayRunSync.utils.ts's shared $syncDisplayRun driver, parameterized by each kind's own\n // descriptor) constructs one whenever it heals a run forward from a loose or missing shape —\n // every USJ-shaped editor needs the class registered, not only shared-react's (a non-react host,\n // e.g. packages/scribe's NoteEditor, builds its editor straight from usjBaseNodes with no\n // react-specific node list).\n AttributeRunNode,\n {\n replace: ParagraphNode,\n with: () => $createImpliedParaNode(),\n withKlass: ImpliedParaNode,\n },\n];\n","/** Generated file using `nx generate markers-data` with 'tools/usfm-markers/src/generators/markers-data/data/usfm.sty' */\n/**\n * Default project `StyleInfo` derived from the bundled `usfm.sty` stylesheet.\n *\n * @public\n */\nexport const defaultStyleInfo = {\n markers: {\n id: {\n marker: \"id\",\n styleType: \"paragraph\",\n textType: \"Other\",\n textProperties: [\"paragraph\", \"nonpublishable\", \"nonvernacular\", \"book\"],\n description: \"File identification information (BOOKID, FILENAME, EDITOR, MODIFICATION DATE)\",\n fontSize: 12,\n },\n usfm: {\n marker: \"usfm\",\n styleType: \"paragraph\",\n occursUnder: [\"id\"],\n rank: 1,\n textType: \"Other\",\n textProperties: [\"paragraph\", \"nonpublishable\", \"nonvernacular\"],\n description: \"File markup version information\",\n fontSize: 12,\n },\n ide: {\n marker: \"ide\",\n styleType: \"paragraph\",\n occursUnder: [\"id\"],\n rank: 1,\n textType: \"Other\",\n textProperties: [\"paragraph\", \"nonpublishable\", \"nonvernacular\"],\n description: \"File encoding information\",\n fontSize: 12,\n },\n h: {\n marker: \"h\",\n styleType: \"paragraph\",\n occursUnder: [\"id\"],\n rank: 1,\n textType: \"Other\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\"],\n description: \"Running header text for a book (basic)\",\n fontSize: 12,\n },\n h1: {\n marker: \"h1\",\n styleType: \"paragraph\",\n occursUnder: [\"id\"],\n rank: 1,\n textType: \"Other\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\"],\n description: \"Running header text\",\n fontSize: 12,\n },\n h2: {\n marker: \"h2\",\n styleType: \"paragraph\",\n occursUnder: [\"id\"],\n rank: 1,\n textType: \"Other\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\"],\n description: \"Running header text, left side of page\",\n fontSize: 12,\n },\n h3: {\n marker: \"h3\",\n styleType: \"paragraph\",\n occursUnder: [\"id\"],\n rank: 1,\n textType: \"Other\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\"],\n description: \"Running header text, right side of page\",\n fontSize: 12,\n },\n toc1: {\n marker: \"toc1\",\n styleType: \"paragraph\",\n occursUnder: [\"h\", \"h1\", \"h2\", \"h3\", \"id\"],\n rank: 1,\n textType: \"Other\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\"],\n description: \"Long table of contents text\",\n fontSize: 12,\n bold: true,\n italic: true,\n color: \"#004000\",\n },\n toc2: {\n marker: \"toc2\",\n styleType: \"paragraph\",\n occursUnder: [\"h\", \"h1\", \"h2\", \"h3\", \"id\"],\n rank: 1,\n textType: \"Other\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\"],\n description: \"Short table of contents text\",\n fontSize: 12,\n italic: true,\n color: \"#004000\",\n },\n toc3: {\n marker: \"toc3\",\n styleType: \"paragraph\",\n occursUnder: [\"h\", \"h1\", \"h2\", \"h3\", \"id\"],\n rank: 1,\n textType: \"Other\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\"],\n description: \"Book Abbreviation\",\n fontSize: 12,\n bold: true,\n italic: true,\n color: \"#800000\",\n },\n toca1: {\n marker: \"toca1\",\n styleType: \"paragraph\",\n occursUnder: [\"h\", \"h1\", \"h2\", \"h3\"],\n rank: 1,\n textType: \"Other\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\"],\n description: \"Alternative language long table of contents text\",\n fontSize: 10,\n italic: true,\n color: \"#808080\",\n },\n toca2: {\n marker: \"toca2\",\n styleType: \"paragraph\",\n occursUnder: [\"h\", \"h1\", \"h2\", \"h3\"],\n rank: 1,\n textType: \"Other\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\"],\n description: \"Alternative language short table of contents text\",\n fontSize: 10,\n italic: true,\n color: \"#808080\",\n },\n toca3: {\n marker: \"toca3\",\n styleType: \"paragraph\",\n occursUnder: [\"h\", \"h1\", \"h2\", \"h3\"],\n rank: 1,\n textType: \"Other\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\"],\n description: \"Alternative language book Abbreviation\",\n fontSize: 10,\n italic: true,\n color: \"#808080\",\n },\n rem: {\n marker: \"rem\",\n styleType: \"paragraph\",\n occursUnder: [\"id\", \"ide\", \"c\"],\n textType: \"Other\",\n textProperties: [\"paragraph\", \"nonpublishable\", \"nonvernacular\"],\n description: \"Comments and remarks\",\n fontSize: 12,\n color: \"#0000FF\",\n },\n sts: {\n marker: \"sts\",\n styleType: \"paragraph\",\n occursUnder: [\"id\", \"ide\", \"c\"],\n textType: \"Other\",\n textProperties: [\"paragraph\", \"nonpublishable\", \"nonvernacular\"],\n description: \"Status of this file\",\n fontSize: 12,\n color: \"#0000FF\",\n },\n restore: {\n marker: \"restore\",\n styleType: \"paragraph\",\n occursUnder: [\"id\"],\n rank: 99,\n textType: \"Other\",\n textProperties: [\"paragraph\", \"nonpublishable\", \"nonvernacular\"],\n description: \"Project restore information\",\n fontSize: 12,\n color: \"#0000FF\",\n },\n imt: {\n marker: \"imt\",\n styleType: \"paragraph\",\n occursUnder: [\"id\"],\n rank: 5,\n textType: \"Other\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"level_1\"],\n description: \"Introduction major title, level 1 (if single level) (basic)\",\n fontSize: 14,\n bold: true,\n justification: \"center\",\n spaceBefore: 8,\n spaceAfter: 4,\n },\n imt1: {\n marker: \"imt1\",\n styleType: \"paragraph\",\n occursUnder: [\"id\"],\n rank: 5,\n textType: \"Other\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"level_1\"],\n description: \"Introduction major title, level 1 (if multiple levels)\",\n fontSize: 14,\n bold: true,\n justification: \"center\",\n spaceBefore: 8,\n spaceAfter: 4,\n },\n imt2: {\n marker: \"imt2\",\n styleType: \"paragraph\",\n occursUnder: [\"id\"],\n rank: 5,\n textType: \"other\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"level_2\"],\n description: \"Introduction major title, level 2\",\n fontSize: 13,\n italic: true,\n justification: \"center\",\n spaceBefore: 6,\n spaceAfter: 3,\n },\n imt3: {\n marker: \"imt3\",\n styleType: \"paragraph\",\n occursUnder: [\"id\"],\n rank: 5,\n textType: \"Other\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"level_3\"],\n description: \"Introduction major title, level 3\",\n fontSize: 12,\n bold: true,\n justification: \"center\",\n spaceBefore: 2,\n spaceAfter: 2,\n },\n imt4: {\n marker: \"imt4\",\n styleType: \"paragraph\",\n occursUnder: [\"id\"],\n rank: 5,\n textType: \"Other\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"level_4\"],\n description: \"Introduction major title, level 4 (usually within parenthesis)\",\n fontSize: 12,\n italic: true,\n justification: \"center\",\n spaceBefore: 2,\n spaceAfter: 2,\n },\n imte: {\n marker: \"imte\",\n styleType: \"paragraph\",\n occursUnder: [\"id\"],\n rank: 7,\n textType: \"Other\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"level_1\"],\n description: \"Introduction major title at introduction end, level 1 (if single level)\",\n fontSize: 20,\n bold: true,\n justification: \"center\",\n spaceBefore: 8,\n spaceAfter: 4,\n },\n imte1: {\n marker: \"imte1\",\n styleType: \"paragraph\",\n occursUnder: [\"id\"],\n rank: 7,\n textType: \"Other\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"level_1\"],\n description: \"Introduction major title at introduction end, level 1 (if multiple levels)\",\n fontSize: 20,\n bold: true,\n justification: \"center\",\n spaceBefore: 8,\n spaceAfter: 4,\n },\n imte2: {\n marker: \"imte2\",\n styleType: \"paragraph\",\n occursUnder: [\"id\"],\n rank: 7,\n textType: \"Other\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"level_2\"],\n description: \"Introduction major title at introduction end, level 2\",\n fontSize: 16,\n italic: true,\n justification: \"center\",\n spaceAfter: 2,\n },\n is: {\n marker: \"is\",\n styleType: \"paragraph\",\n occursUnder: [\"id\"],\n rank: 6,\n textType: \"Other\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"level_1\"],\n description: \"Introduction section heading, level 1 (if single level) (basic)\",\n fontSize: 14,\n bold: true,\n justification: \"center\",\n spaceBefore: 8,\n spaceAfter: 4,\n },\n is1: {\n marker: \"is1\",\n styleType: \"paragraph\",\n occursUnder: [\"id\"],\n rank: 6,\n textType: \"Other\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"level_1\"],\n description: \"Introduction section heading, level 1 (if multiple levels)\",\n fontSize: 14,\n bold: true,\n justification: \"center\",\n spaceBefore: 8,\n spaceAfter: 4,\n },\n is2: {\n marker: \"is2\",\n styleType: \"paragraph\",\n occursUnder: [\"id\"],\n rank: 6,\n textType: \"Other\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"level_2\"],\n description: \"Introduction section heading, level 2\",\n fontSize: 12,\n bold: true,\n justification: \"center\",\n spaceBefore: 8,\n spaceAfter: 4,\n },\n iot: {\n marker: \"iot\",\n styleType: \"paragraph\",\n occursUnder: [\"id\"],\n rank: 6,\n textType: \"Other\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\"],\n description: \"Introduction outline title (basic)\",\n fontSize: 12,\n bold: true,\n justification: \"center\",\n spaceBefore: 8,\n spaceAfter: 4,\n },\n io: {\n marker: \"io\",\n styleType: \"paragraph\",\n occursUnder: [\"id\"],\n rank: 6,\n textType: \"Other\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"level_1\"],\n description: \"Introduction outline text, level 1 (if single level)\",\n fontSize: 12,\n leftMargin: 0.5,\n },\n io1: {\n marker: \"io1\",\n styleType: \"paragraph\",\n occursUnder: [\"id\"],\n rank: 6,\n textType: \"Other\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"level_1\"],\n description: \"Introduction outline text, level 1 (if multiple levels) (basic)\",\n fontSize: 12,\n leftMargin: 0.5,\n },\n io2: {\n marker: \"io2\",\n styleType: \"paragraph\",\n occursUnder: [\"id\"],\n rank: 6,\n textType: \"Other\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"level_2\"],\n description: \"Introduction outline text, level 2\",\n fontSize: 12,\n leftMargin: 0.75,\n },\n io3: {\n marker: \"io3\",\n styleType: \"paragraph\",\n occursUnder: [\"id\"],\n rank: 6,\n textType: \"Other\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"level_3\"],\n description: \"Introduction outline text, level 3\",\n fontSize: 12,\n leftMargin: 1,\n },\n io4: {\n marker: \"io4\",\n styleType: \"paragraph\",\n occursUnder: [\"id\"],\n rank: 6,\n textType: \"Other\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"level_4\"],\n description: \"Introduction outline text, level 4\",\n fontSize: 12,\n leftMargin: 1.25,\n },\n ior: {\n marker: \"ior\",\n styleType: \"character\",\n endMarker: \"ior*\",\n occursUnder: [\"id\", \"io\", \"io1\", \"io2\", \"io3\", \"io4\", \"NEST\"],\n textType: \"Other\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"Introduction references range for outline entry; for marking references separately\",\n fontSize: 12,\n },\n ip: {\n marker: \"ip\",\n styleType: \"paragraph\",\n occursUnder: [\"id\"],\n rank: 6,\n textType: \"Other\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\"],\n description: \"Introduction prose paragraph (basic)\",\n fontSize: 12,\n firstLineIndent: 0.125,\n },\n im: {\n marker: \"im\",\n styleType: \"paragraph\",\n occursUnder: [\"id\"],\n rank: 6,\n textType: \"Other\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\"],\n description: \"Introduction prose paragraph, with no first line indent (may occur after poetry)\",\n fontSize: 12,\n },\n ipi: {\n marker: \"ipi\",\n styleType: \"paragraph\",\n occursUnder: [\"id\"],\n rank: 6,\n textType: \"Other\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\"],\n description: \"Introduction prose paragraph, indented, with first line indent\",\n fontSize: 12,\n firstLineIndent: 0.125,\n leftMargin: 0.25,\n rightMargin: 0.25,\n },\n imi: {\n marker: \"imi\",\n styleType: \"paragraph\",\n occursUnder: [\"id\"],\n rank: 6,\n textType: \"Other\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\"],\n description: \"Introduction prose paragraph text, indented, with no first line indent\",\n fontSize: 12,\n leftMargin: 0.25,\n rightMargin: 0.25,\n },\n ili: {\n marker: \"ili\",\n styleType: \"paragraph\",\n occursUnder: [\"id\"],\n rank: 6,\n textType: \"Other\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"level_1\"],\n description: \"A list entry, level 1 (if single level)\",\n fontSize: 12,\n firstLineIndent: -0.375,\n leftMargin: 0.5,\n },\n ili1: {\n marker: \"ili1\",\n styleType: \"paragraph\",\n occursUnder: [\"id\"],\n rank: 6,\n textType: \"Other\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"level_1\"],\n description: \"A list entry, level 1 (if multiple levels)\",\n fontSize: 12,\n firstLineIndent: -0.375,\n leftMargin: 0.5,\n },\n ili2: {\n marker: \"ili2\",\n styleType: \"paragraph\",\n occursUnder: [\"id\"],\n rank: 6,\n textType: \"Other\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"level_2\"],\n description: \"A list entry, level 2\",\n fontSize: 12,\n firstLineIndent: -0.375,\n leftMargin: 0.75,\n },\n ipq: {\n marker: \"ipq\",\n styleType: \"paragraph\",\n occursUnder: [\"id\"],\n rank: 6,\n textType: \"Other\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\"],\n description: \"Introduction prose paragraph, quote from the body text\",\n fontSize: 12,\n italic: true,\n firstLineIndent: 0.125,\n leftMargin: 0.25,\n rightMargin: 0.25,\n },\n imq: {\n marker: \"imq\",\n styleType: \"paragraph\",\n occursUnder: [\"id\"],\n rank: 6,\n textType: \"Other\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\"],\n description: \"Introduction prose paragraph, quote from the body text, with no first line indent\",\n fontSize: 12,\n italic: true,\n leftMargin: 0.25,\n rightMargin: 0.25,\n },\n ipr: {\n marker: \"ipr\",\n styleType: \"paragraph\",\n occursUnder: [\"id\"],\n rank: 6,\n textType: \"Other\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\"],\n description: \"Introduction prose paragraph, right aligned\",\n fontSize: 12,\n italic: true,\n justification: \"right\",\n leftMargin: 0.25,\n rightMargin: 0.25,\n },\n ib: {\n marker: \"ib\",\n styleType: \"paragraph\",\n occursUnder: [\"id\"],\n rank: 6,\n textType: \"Other\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"poetic\"],\n description: \"Introduction blank line\",\n fontSize: 10,\n },\n iq: {\n marker: \"iq\",\n styleType: \"paragraph\",\n occursUnder: [\"id\"],\n rank: 6,\n textType: \"Other\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"poetic\", \"level_1\"],\n description: \"Introduction poetry text, level 1 (if single level)\",\n fontSize: 12,\n italic: true,\n firstLineIndent: -0.75,\n leftMargin: 1,\n },\n iq1: {\n marker: \"iq1\",\n styleType: \"paragraph\",\n occursUnder: [\"id\"],\n rank: 6,\n textType: \"Other\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"poetic\", \"level_1\"],\n description: \"Introduction poetry text, level 1 (if multiple levels)\",\n fontSize: 12,\n italic: true,\n firstLineIndent: -0.75,\n leftMargin: 1,\n },\n iq2: {\n marker: \"iq2\",\n styleType: \"paragraph\",\n occursUnder: [\"id\"],\n rank: 6,\n textType: \"Other\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"poetic\", \"level_2\"],\n description: \"Introduction poetry text, level 2\",\n fontSize: 12,\n italic: true,\n firstLineIndent: -0.5,\n leftMargin: 1,\n },\n iq3: {\n marker: \"iq3\",\n styleType: \"paragraph\",\n occursUnder: [\"id\"],\n rank: 6,\n textType: \"Other\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"poetic\", \"level_3\"],\n description: \"Introduction poetry text, level 3\",\n fontSize: 12,\n italic: true,\n firstLineIndent: -0.25,\n leftMargin: 1,\n },\n iex: {\n marker: \"iex\",\n styleType: \"paragraph\",\n occursUnder: [\"id\", \"c\"],\n textType: \"Other\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\"],\n description: \"Introduction explanatory or bridge text (e.g. explanation of missing book in Short Old Testament)\",\n fontSize: 12,\n firstLineIndent: 0.125,\n spaceBefore: 4,\n spaceAfter: 4,\n },\n iqt: {\n marker: \"iqt\",\n styleType: \"character\",\n endMarker: \"iqt*\",\n occursUnder: [\n \"imt\",\n \"imt1\",\n \"imt2\",\n \"imt3\",\n \"imt4\",\n \"ib\",\n \"ie\",\n \"ili\",\n \"ili1\",\n \"ili2\",\n \"im\",\n \"imi\",\n \"imq\",\n \"io\",\n \"io1\",\n \"io2\",\n \"io3\",\n \"io4\",\n \"iot\",\n \"ip\",\n \"ipi\",\n \"ipq\",\n \"ipr\",\n \"iq\",\n \"iq1\",\n \"iq2\",\n \"iq3\",\n \"is\",\n \"is1\",\n \"is2\",\n \"imte\",\n \"imte1\",\n \"imte2\",\n \"iex\",\n ],\n textType: \"Other\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"For quoted scripture text appearing in the introduction\",\n fontSize: 12,\n italic: true,\n },\n ie: {\n marker: \"ie\",\n styleType: \"paragraph\",\n occursUnder: [\"id\"],\n rank: 6,\n textType: \"Other\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\"],\n description: \"Introduction ending marker\",\n fontSize: 10,\n },\n c: {\n marker: \"c\",\n styleType: \"paragraph\",\n occursUnder: [\"id\"],\n rank: 8,\n textType: \"ChapterNumber\",\n textProperties: [\"chapter\"],\n description: \"Chapter number (necessary for normal Paratext operation)\",\n fontSize: 18,\n bold: true,\n spaceBefore: 8,\n spaceAfter: 4,\n },\n ca: {\n marker: \"ca\",\n styleType: \"character\",\n endMarker: \"ca*\",\n occursUnder: [\"c\"],\n textType: \"Other\",\n description: \"Second (alternate) chapter number (for coding dual versification; useful for places where different traditions of chapter breaks need to be supported in the same translation)\",\n fontSize: 16,\n italic: true,\n color: \"#228B22\",\n },\n cp: {\n marker: \"cp\",\n styleType: \"paragraph\",\n occursUnder: [\"c\"],\n rank: 4,\n textType: \"Other\",\n textProperties: [\"paragraph\"],\n description: \"Published chapter number (chapter string that should appear in the published text)\",\n fontSize: 18,\n bold: true,\n color: \"#0000FF\",\n spaceBefore: 8,\n spaceAfter: 4,\n },\n cl: {\n marker: \"cl\",\n styleType: \"paragraph\",\n occursUnder: [\"id\", \"c\", \"ms\", \"ms1\", \"ms2\", \"ms3\", \"mr\"],\n textType: \"Other\",\n textProperties: [\"paragraph\"],\n description: 'Chapter label used for translations that add a word such as \"Chapter\" before chapter numbers (e.g. Psalms). The subsequent text is the chapter label.',\n fontSize: 18,\n bold: true,\n justification: \"center\",\n spaceBefore: 8,\n spaceAfter: 4,\n },\n cd: {\n marker: \"cd\",\n styleType: \"paragraph\",\n occursUnder: [\"c\"],\n textType: \"Other\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\"],\n description: \"Chapter Description (Publishing option D, e.g. in Russian Bibles)\",\n fontSize: 11,\n spaceBefore: 8,\n spaceAfter: 4,\n },\n v: {\n marker: \"v\",\n styleType: \"character\",\n occursUnder: [\n \"lh\",\n \"li\",\n \"li1\",\n \"li2\",\n \"li3\",\n \"li4\",\n \"lf\",\n \"lim\",\n \"lim1\",\n \"lim2\",\n \"lim3\",\n \"lim4\",\n \"m\",\n \"mi\",\n \"nb\",\n \"p\",\n \"pc\",\n \"ph\",\n \"phi\",\n \"pi\",\n \"pi1\",\n \"pi2\",\n \"pi3\",\n \"pr\",\n \"pmo\",\n \"pm\",\n \"pmc\",\n \"pmr\",\n \"po\",\n \"q\",\n \"q1\",\n \"q2\",\n \"q3\",\n \"q4\",\n \"qc\",\n \"qr\",\n \"qd\",\n \"qm\",\n \"qm1\",\n \"qm2\",\n \"qm3\",\n \"tr\",\n \"tc1\",\n \"tc2\",\n \"tc3\",\n \"tc4\",\n \"tcr1\",\n \"tcr2\",\n \"tcr3\",\n \"tcr4\",\n \"s3\",\n \"d\",\n \"sp\",\n ],\n textType: \"VerseNumber\",\n textProperties: [\"verse\"],\n description: \"A verse number (Necessary for normal paratext operation) (basic)\",\n fontSize: 12,\n superscript: true,\n },\n va: {\n marker: \"va\",\n styleType: \"character\",\n endMarker: \"va*\",\n occursUnder: [\n \"lh\",\n \"li\",\n \"li1\",\n \"li2\",\n \"li3\",\n \"li4\",\n \"lf\",\n \"lim\",\n \"lim1\",\n \"lim2\",\n \"lim3\",\n \"lim4\",\n \"m\",\n \"mi\",\n \"nb\",\n \"p\",\n \"pc\",\n \"ph\",\n \"phi\",\n \"pi\",\n \"pi1\",\n \"pi2\",\n \"pi3\",\n \"pr\",\n \"pmo\",\n \"pm\",\n \"pmc\",\n \"pmr\",\n \"po\",\n \"q\",\n \"q1\",\n \"q2\",\n \"q3\",\n \"q4\",\n \"qc\",\n \"qr\",\n \"qd\",\n \"qm\",\n \"qm1\",\n \"qm2\",\n \"qm3\",\n \"tr\",\n \"tc1\",\n \"tc2\",\n \"tc3\",\n \"tc4\",\n \"tcr1\",\n \"tcr2\",\n \"tcr3\",\n \"tcr4\",\n \"s3\",\n \"d\",\n \"sp\",\n ],\n textType: \"Other\",\n description: \"Second (alternate) verse number (for coding dual numeration in Psalms; see also NRSV Exo 22.1-4)\",\n fontSize: 12,\n superscript: true,\n color: \"#228B22\",\n },\n vp: {\n marker: \"vp\",\n styleType: \"character\",\n endMarker: \"vp*\",\n occursUnder: [\n \"cd\",\n \"lh\",\n \"li\",\n \"li1\",\n \"li2\",\n \"li3\",\n \"li4\",\n \"lf\",\n \"lim\",\n \"lim1\",\n \"lim2\",\n \"lim3\",\n \"lim4\",\n \"m\",\n \"mi\",\n \"nb\",\n \"p\",\n \"pc\",\n \"ph\",\n \"phi\",\n \"pi\",\n \"pi1\",\n \"pi2\",\n \"pi3\",\n \"pr\",\n \"pmo\",\n \"pm\",\n \"pmc\",\n \"pmr\",\n \"po\",\n \"q\",\n \"q1\",\n \"q2\",\n \"q3\",\n \"q4\",\n \"qc\",\n \"qr\",\n \"qd\",\n \"qm\",\n \"qm1\",\n \"qm2\",\n \"qm3\",\n \"tr\",\n \"tc1\",\n \"tc2\",\n \"tc3\",\n \"tc4\",\n \"tcr1\",\n \"tcr2\",\n \"tcr3\",\n \"tcr4\",\n \"s3\",\n \"d\",\n \"sp\",\n ],\n textType: \"Other\",\n description: \"Published verse marker (verse string that should appear in the published text)\",\n fontSize: 12,\n superscript: true,\n color: \"#0000FF\",\n },\n p: {\n marker: \"p\",\n styleType: \"paragraph\",\n occursUnder: [\"c\"],\n rank: 4,\n textType: \"VerseText\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\"],\n description: \"Paragraph text, with first line indent (basic)\",\n fontSize: 12,\n firstLineIndent: 0.125,\n },\n m: {\n marker: \"m\",\n styleType: \"paragraph\",\n occursUnder: [\"c\"],\n rank: 4,\n textType: \"VerseText\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\"],\n description: \"Paragraph text, with no first line indent (may occur after poetry) (basic)\",\n fontSize: 12,\n },\n po: {\n marker: \"po\",\n styleType: \"paragraph\",\n occursUnder: [\"c\"],\n rank: 4,\n textType: \"VerseText\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\"],\n description: \"Letter opening\",\n fontSize: 12,\n firstLineIndent: 0.125,\n spaceBefore: 4,\n spaceAfter: 4,\n },\n pr: {\n marker: \"pr\",\n styleType: \"paragraph\",\n occursUnder: [\"c\"],\n rank: 4,\n textType: \"VerseText\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\"],\n description: \"Text refrain (paragraph text, right aligned)\",\n fontSize: 12,\n justification: \"right\",\n },\n cls: {\n marker: \"cls\",\n styleType: \"paragraph\",\n occursUnder: [\"c\"],\n rank: 4,\n textType: \"VerseText\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\"],\n description: \"Letter Closing\",\n fontSize: 12,\n justification: \"right\",\n },\n pmo: {\n marker: \"pmo\",\n styleType: \"paragraph\",\n occursUnder: [\n \"m\",\n \"mi\",\n \"nb\",\n \"p\",\n \"pc\",\n \"ph\",\n \"phi\",\n \"pi\",\n \"pi1\",\n \"pi2\",\n \"pi3\",\n \"pr\",\n \"po\",\n \"q\",\n \"q1\",\n \"q2\",\n \"q3\",\n \"q4\",\n \"qc\",\n \"qr\",\n \"b\",\n \"s1\",\n \"s2\",\n \"s3\",\n \"s4\",\n ],\n textType: \"VerseText\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\"],\n description: \"Embedded text opening\",\n fontSize: 12,\n leftMargin: 0.25,\n rightMargin: 0.25,\n },\n pm: {\n marker: \"pm\",\n styleType: \"paragraph\",\n occursUnder: [\n \"m\",\n \"mi\",\n \"nb\",\n \"p\",\n \"pc\",\n \"ph\",\n \"phi\",\n \"pi\",\n \"pi1\",\n \"pi2\",\n \"pi3\",\n \"pr\",\n \"po\",\n \"psi\",\n \"q\",\n \"q1\",\n \"q2\",\n \"q3\",\n \"q4\",\n \"qc\",\n \"qr\",\n \"b\",\n \"s1\",\n \"s2\",\n \"s3\",\n \"s4\",\n ],\n textType: \"VerseText\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\"],\n description: \"Embedded text paragraph\",\n fontSize: 12,\n firstLineIndent: 0.125,\n leftMargin: 0.25,\n rightMargin: 0.25,\n },\n pmc: {\n marker: \"pmc\",\n styleType: \"paragraph\",\n occursUnder: [\n \"m\",\n \"mi\",\n \"nb\",\n \"p\",\n \"pc\",\n \"ph\",\n \"phi\",\n \"pi\",\n \"pi1\",\n \"pi2\",\n \"pi3\",\n \"pr\",\n \"po\",\n \"q\",\n \"q1\",\n \"q2\",\n \"q3\",\n \"q4\",\n \"qc\",\n \"qr\",\n \"b\",\n \"s1\",\n \"s2\",\n \"s3\",\n \"s4\",\n ],\n textType: \"VerseText\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\"],\n description: \"Embedded text closing\",\n fontSize: 12,\n leftMargin: 0.25,\n rightMargin: 0.25,\n },\n pmr: {\n marker: \"pmr\",\n styleType: \"paragraph\",\n occursUnder: [\n \"li\",\n \"li1\",\n \"li2\",\n \"li3\",\n \"li4\",\n \"lim\",\n \"lim1\",\n \"lim2\",\n \"lim3\",\n \"lim4\",\n \"m\",\n \"mi\",\n \"nb\",\n \"p\",\n \"pc\",\n \"ph\",\n \"phi\",\n \"pi\",\n \"pi1\",\n \"pi2\",\n \"pi3\",\n \"pr\",\n \"po\",\n \"q\",\n \"q1\",\n \"q2\",\n \"q3\",\n \"q4\",\n \"qc\",\n \"qr\",\n \"b\",\n \"s1\",\n \"s2\",\n \"s3\",\n \"s4\",\n ],\n textType: \"VerseText\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\"],\n description: 'Embedded text refrain (e.g. Then all the people shall say, \"Amen!\")',\n fontSize: 12,\n justification: \"right\",\n leftMargin: 0.25,\n rightMargin: 0.25,\n },\n pi: {\n marker: \"pi\",\n styleType: \"paragraph\",\n occursUnder: [\"c\"],\n rank: 4,\n textType: \"VerseText\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"level_1\"],\n description: \"Paragraph text, level 1 indent (if single level), with first line indent; often used for discourse (basic)\",\n fontSize: 12,\n firstLineIndent: 0.125,\n leftMargin: 0.25,\n rightMargin: 0.25,\n },\n pi1: {\n marker: \"pi1\",\n styleType: \"paragraph\",\n occursUnder: [\"c\"],\n rank: 4,\n textType: \"VerseText\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"level_1\"],\n description: \"Paragraph text, level 1 indent (if multiple levels), with first line indent; often used for discourse\",\n fontSize: 12,\n firstLineIndent: 0.125,\n leftMargin: 0.25,\n rightMargin: 0.25,\n },\n pi2: {\n marker: \"pi2\",\n styleType: \"paragraph\",\n occursUnder: [\"c\"],\n rank: 4,\n textType: \"VerseText\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"level_2\"],\n description: \"Paragraph text, level 2 indent, with first line indent; often used for discourse\",\n fontSize: 12,\n firstLineIndent: 0.125,\n leftMargin: 0.5,\n rightMargin: 0.25,\n },\n pi3: {\n marker: \"pi3\",\n styleType: \"paragraph\",\n occursUnder: [\"c\"],\n rank: 4,\n textType: \"VerseText\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"level_3\"],\n description: \"Paragraph text, level 3 indent, with first line indent; often used for discourse\",\n fontSize: 12,\n firstLineIndent: 0.125,\n leftMargin: 0.75,\n rightMargin: 0.25,\n },\n pc: {\n marker: \"pc\",\n styleType: \"paragraph\",\n occursUnder: [\"c\"],\n rank: 4,\n textType: \"VerseText\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\"],\n description: \"Paragraph text, centered (for Inscription)\",\n fontSize: 12,\n justification: \"center\",\n },\n mi: {\n marker: \"mi\",\n styleType: \"paragraph\",\n occursUnder: [\"c\"],\n rank: 4,\n textType: \"VerseText\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\"],\n description: \"Paragraph text, indented, with no first line indent; often used for discourse\",\n fontSize: 12,\n leftMargin: 0.25,\n rightMargin: 0.25,\n },\n nb: {\n marker: \"nb\",\n styleType: \"paragraph\",\n occursUnder: [\"c\"],\n rank: 4,\n textType: \"VerseText\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\"],\n description: \"Paragraph text, with no break from previous paragraph text (at chapter boundary) (basic)\",\n fontSize: 12,\n },\n q: {\n marker: \"q\",\n styleType: \"paragraph\",\n occursUnder: [\"c\"],\n rank: 4,\n textType: \"VerseText\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"poetic\", \"level_1\"],\n description: \"Poetry text, level 1 indent (if single level)\",\n fontSize: 12,\n firstLineIndent: -0.5,\n leftMargin: 0.75,\n },\n q1: {\n marker: \"q1\",\n styleType: \"paragraph\",\n occursUnder: [\"c\"],\n rank: 4,\n textType: \"VerseText\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"poetic\", \"level_1\"],\n description: \"Poetry text, level 1 indent (if multiple levels) (basic)\",\n fontSize: 12,\n firstLineIndent: -0.5,\n leftMargin: 0.75,\n },\n q2: {\n marker: \"q2\",\n styleType: \"paragraph\",\n occursUnder: [\"c\"],\n rank: 4,\n textType: \"VerseText\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"poetic\", \"level_2\"],\n description: \"Poetry text, level 2 indent (basic)\",\n fontSize: 12,\n firstLineIndent: -0.375,\n leftMargin: 0.75,\n },\n q3: {\n marker: \"q3\",\n styleType: \"paragraph\",\n occursUnder: [\"c\"],\n rank: 4,\n textType: \"VerseText\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"poetic\", \"level_3\"],\n description: \"Poetry text, level 3 indent\",\n fontSize: 12,\n firstLineIndent: -0.25,\n leftMargin: 0.75,\n },\n q4: {\n marker: \"q4\",\n styleType: \"paragraph\",\n occursUnder: [\"c\"],\n rank: 4,\n textType: \"VerseText\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"poetic\", \"level_4\"],\n description: \"Poetry text, level 4 indent\",\n fontSize: 12,\n firstLineIndent: -0.125,\n leftMargin: 0.75,\n },\n qc: {\n marker: \"qc\",\n styleType: \"paragraph\",\n occursUnder: [\"c\"],\n rank: 4,\n textType: \"VerseText\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"poetic\"],\n description: \"Poetry text, centered\",\n fontSize: 12,\n justification: \"center\",\n },\n qr: {\n marker: \"qr\",\n styleType: \"paragraph\",\n occursUnder: [\"c\"],\n rank: 4,\n textType: \"VerseText\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"poetic\"],\n description: \"Poetry text, Right Aligned\",\n fontSize: 12,\n justification: \"right\",\n },\n qs: {\n marker: \"qs\",\n styleType: \"character\",\n endMarker: \"qs*\",\n occursUnder: [\"q\", \"q1\", \"q2\", \"q3\", \"q4\", \"qc\", \"qr\", \"qd\", \"NEST\"],\n rank: 4,\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\", \"poetic\"],\n description: \"Poetry text, Selah\",\n fontSize: 12,\n italic: true,\n },\n qa: {\n marker: \"qa\",\n styleType: \"paragraph\",\n occursUnder: [\"c\"],\n rank: 4,\n textType: \"Other\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"poetic\"],\n description: \"Poetry text, Acrostic marker/heading\",\n fontSize: 12,\n italic: true,\n },\n qac: {\n marker: \"qac\",\n styleType: \"character\",\n endMarker: \"qac*\",\n occursUnder: [\"q\", \"q1\", \"q2\", \"q3\", \"q4\", \"qc\", \"qr\", \"\", \"NEST\"],\n rank: 4,\n textType: \"Other\",\n textProperties: [\"publishable\", \"vernacular\", \"poetic\"],\n description: \"Poetry text, Acrostic markup of the first character of a line of acrostic poetry\",\n fontSize: 12,\n italic: true,\n },\n qm: {\n marker: \"qm\",\n styleType: \"paragraph\",\n occursUnder: [\n \"m\",\n \"mi\",\n \"nb\",\n \"p\",\n \"pc\",\n \"ph\",\n \"phi\",\n \"pi\",\n \"pi1\",\n \"pi2\",\n \"pi3\",\n \"pr\",\n \"q\",\n \"q1\",\n \"q2\",\n \"q3\",\n \"q4\",\n \"qc\",\n \"qr\",\n \"b\",\n ],\n textType: \"VerseText\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"poetic\"],\n description: \"Poetry text, embedded, level 1 indent (if single level)\",\n fontSize: 12,\n firstLineIndent: -0.75,\n leftMargin: 1,\n },\n qm1: {\n marker: \"qm1\",\n styleType: \"paragraph\",\n occursUnder: [\n \"m\",\n \"mi\",\n \"nb\",\n \"p\",\n \"pc\",\n \"ph\",\n \"phi\",\n \"pi\",\n \"pi1\",\n \"pi2\",\n \"pi3\",\n \"pr\",\n \"q\",\n \"q1\",\n \"q2\",\n \"q3\",\n \"q4\",\n \"qc\",\n \"qr\",\n \"b\",\n ],\n textType: \"VerseText\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"poetic\", \"level_1\"],\n description: \"Poetry text, embedded, level 1 indent (if multiple levels)\",\n fontSize: 12,\n firstLineIndent: -0.75,\n leftMargin: 1,\n },\n qm2: {\n marker: \"qm2\",\n styleType: \"paragraph\",\n occursUnder: [\n \"m\",\n \"mi\",\n \"nb\",\n \"p\",\n \"pc\",\n \"ph\",\n \"phi\",\n \"pi\",\n \"pi1\",\n \"pi2\",\n \"pi3\",\n \"pr\",\n \"q\",\n \"q1\",\n \"q2\",\n \"q3\",\n \"q4\",\n \"qc\",\n \"qr\",\n \"b\",\n ],\n textType: \"VerseText\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"poetic\", \"level_2\"],\n description: \"Poetry text, embedded, level 2 indent\",\n fontSize: 12,\n firstLineIndent: -0.5,\n leftMargin: 1,\n },\n qm3: {\n marker: \"qm3\",\n styleType: \"paragraph\",\n occursUnder: [\n \"m\",\n \"mi\",\n \"nb\",\n \"p\",\n \"pc\",\n \"ph\",\n \"phi\",\n \"pi\",\n \"pi1\",\n \"pi2\",\n \"pi3\",\n \"pr\",\n \"q\",\n \"q1\",\n \"q2\",\n \"q3\",\n \"q4\",\n \"qc\",\n \"qr\",\n \"b\",\n ],\n textType: \"VerseText\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"poetic\", \"level_3\"],\n description: \"Poetry text, embedded, level 3 indent\",\n fontSize: 12,\n firstLineIndent: -0.25,\n leftMargin: 1,\n },\n qd: {\n marker: \"qd\",\n styleType: \"paragraph\",\n occursUnder: [\"c\"],\n rank: 4,\n textType: \"VerseText\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"poetic\"],\n description: \"A Hebrew musical performance annotation, similar in content to Hebrew descriptive title.\",\n fontSize: 12,\n italic: true,\n leftMargin: 0.25,\n },\n b: {\n marker: \"b\",\n styleType: \"paragraph\",\n occursUnder: [\"c\"],\n rank: 4,\n textType: \"VerseText\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"poetic\"],\n description: \"Poetry text stanza break (e.g. stanza break) (basic)\",\n fontSize: 10,\n },\n mt: {\n marker: \"mt\",\n styleType: \"paragraph\",\n occursUnder: [\"id\"],\n rank: 3,\n textType: \"Title\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"level_1\"],\n description: \"The main title of the book (if single level)\",\n fontSize: 20,\n bold: true,\n justification: \"center\",\n spaceBefore: 8,\n spaceAfter: 4,\n },\n mt1: {\n marker: \"mt1\",\n styleType: \"paragraph\",\n occursUnder: [\"id\"],\n rank: 3,\n textType: \"Title\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"level_1\"],\n description: \"The main title of the book (if multiple levels) (basic)\",\n fontSize: 20,\n bold: true,\n justification: \"center\",\n spaceBefore: 2,\n spaceAfter: 4,\n },\n mt2: {\n marker: \"mt2\",\n styleType: \"paragraph\",\n occursUnder: [\"id\"],\n rank: 3,\n textType: \"Title\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"level_2\"],\n description: \"A secondary title usually occurring before the main title (basic)\",\n fontSize: 16,\n italic: true,\n justification: \"center\",\n spaceAfter: 2,\n },\n mt3: {\n marker: \"mt3\",\n styleType: \"paragraph\",\n occursUnder: [\"id\"],\n rank: 3,\n textType: \"Title\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"level_3\"],\n description: \"A secondary title occurring after the main title\",\n fontSize: 16,\n bold: true,\n justification: \"center\",\n spaceBefore: 2,\n spaceAfter: 2,\n },\n mt4: {\n marker: \"mt4\",\n styleType: \"paragraph\",\n occursUnder: [\"id\"],\n rank: 3,\n textType: \"Title\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"level_4\"],\n description: \"A small secondary title sometimes occurring within parentheses\",\n fontSize: 12,\n justification: \"center\",\n spaceBefore: 2,\n spaceAfter: 2,\n },\n mte: {\n marker: \"mte\",\n styleType: \"paragraph\",\n occursUnder: [\"c\"],\n rank: 2,\n textType: \"Title\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"level_1\"],\n description: \"The main title of the book repeated at the end of the book, level 1 (if single level)\",\n fontSize: 20,\n bold: true,\n justification: \"center\",\n spaceBefore: 8,\n spaceAfter: 4,\n },\n mte1: {\n marker: \"mte1\",\n styleType: \"paragraph\",\n occursUnder: [\n \"lh\",\n \"li\",\n \"li1\",\n \"li2\",\n \"li3\",\n \"li4\",\n \"lf\",\n \"lim\",\n \"lim1\",\n \"lim2\",\n \"lim3\",\n \"lim4\",\n \"m\",\n \"mi\",\n \"nb\",\n \"p\",\n \"pc\",\n \"ph\",\n \"phi\",\n \"pi\",\n \"pi1\",\n \"pi2\",\n \"pi3\",\n \"pr\",\n \"pmo\",\n \"pm\",\n \"pmc\",\n \"pmr\",\n \"po\",\n \"q\",\n \"q1\",\n \"q2\",\n \"q3\",\n \"q4\",\n \"qc\",\n \"qr\",\n \"qd\",\n \"qm\",\n \"qm1\",\n \"qm2\",\n \"qm3\",\n \"tc1\",\n \"tc2\",\n \"tc3\",\n \"tc4\",\n \"s3\",\n \"d\",\n ],\n rank: 2,\n textType: \"Title\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"level_1\"],\n description: \"The main title of the book repeated at the end of the book, level 1 (if multiple levels)\",\n fontSize: 20,\n bold: true,\n justification: \"center\",\n spaceBefore: 8,\n spaceAfter: 4,\n },\n mte2: {\n marker: \"mte2\",\n styleType: \"paragraph\",\n occursUnder: [\"mte1\"],\n rank: 2,\n textType: \"Title\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"level_2\"],\n description: \"A secondary title occurring before or after the 'ending' main title\",\n fontSize: 16,\n italic: true,\n justification: \"center\",\n spaceAfter: 2,\n },\n ms: {\n marker: \"ms\",\n styleType: \"paragraph\",\n occursUnder: [\"c\"],\n rank: 4,\n textType: \"Section\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"level_1\"],\n description: \"A major section division heading, level 1 (if single level) (basic)\",\n fontSize: 14,\n bold: true,\n justification: \"center\",\n spaceBefore: 16,\n spaceAfter: 4,\n },\n ms1: {\n marker: \"ms1\",\n styleType: \"paragraph\",\n occursUnder: [\"c\"],\n rank: 4,\n textType: \"Section\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"level_1\"],\n description: \"A major section division heading, level 1 (if multiple levels)\",\n fontSize: 14,\n bold: true,\n justification: \"center\",\n spaceBefore: 16,\n spaceAfter: 4,\n },\n ms2: {\n marker: \"ms2\",\n styleType: \"paragraph\",\n occursUnder: [\"c\"],\n rank: 4,\n textType: \"Section\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"level_1\"],\n description: \"A major section division heading, level 2\",\n fontSize: 14,\n bold: true,\n justification: \"center\",\n spaceBefore: 16,\n spaceAfter: 4,\n },\n ms3: {\n marker: \"ms3\",\n styleType: \"paragraph\",\n occursUnder: [\"c\"],\n rank: 4,\n textType: \"Section\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"level_1\"],\n description: \"A major section division heading, level 3\",\n fontSize: 14,\n italic: true,\n justification: \"center\",\n spaceBefore: 16,\n spaceAfter: 4,\n },\n mr: {\n marker: \"mr\",\n styleType: \"paragraph\",\n occursUnder: [\"ms\", \"ms1\", \"ms2\", \"ms3\"],\n textType: \"Section\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"level_1\"],\n description: \"A major section division references range heading (basic)\",\n fontSize: 12,\n italic: true,\n justification: \"center\",\n spaceAfter: 4,\n },\n s: {\n marker: \"s\",\n styleType: \"paragraph\",\n occursUnder: [\"c\"],\n rank: 4,\n textType: \"Section\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"level_1\"],\n description: \"A section heading, level 1 (if single level) (basic)\",\n fontSize: 12,\n bold: true,\n justification: \"center\",\n spaceBefore: 8,\n spaceAfter: 4,\n },\n s1: {\n marker: \"s1\",\n styleType: \"paragraph\",\n occursUnder: [\"c\"],\n rank: 4,\n textType: \"Section\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"level_1\"],\n description: \"A section heading, level 1 (if multiple levels)\",\n fontSize: 12,\n bold: true,\n justification: \"center\",\n spaceBefore: 8,\n spaceAfter: 4,\n },\n s2: {\n marker: \"s2\",\n styleType: \"paragraph\",\n occursUnder: [\"c\"],\n rank: 4,\n textType: \"Section\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"level_2\"],\n description: \"A section heading, level 2 (e.g. Proverbs 22-24)\",\n fontSize: 12,\n italic: true,\n justification: \"center\",\n spaceBefore: 8,\n spaceAfter: 4,\n },\n s3: {\n marker: \"s3\",\n styleType: \"paragraph\",\n occursUnder: [\"c\"],\n rank: 4,\n textType: \"Section\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"level_3\"],\n description: 'A section heading, level 3 (e.g. Genesis \"The First Day\")',\n fontSize: 12,\n italic: true,\n justification: \"left\",\n spaceBefore: 6,\n spaceAfter: 3,\n },\n s4: {\n marker: \"s4\",\n styleType: \"paragraph\",\n occursUnder: [\"c\"],\n rank: 4,\n textType: \"Section\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"level_4\"],\n description: \"A section heading, level 4\",\n fontSize: 12,\n italic: true,\n justification: \"left\",\n spaceBefore: 6,\n spaceAfter: 3,\n },\n sr: {\n marker: \"sr\",\n styleType: \"paragraph\",\n occursUnder: [\"s\", \"s1\", \"s2\", \"s3\", \"s4\"],\n textType: \"Section\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"level_1\"],\n description: \"A section division references range heading\",\n fontSize: 12,\n bold: true,\n justification: \"center\",\n spaceAfter: 4,\n },\n r: {\n marker: \"r\",\n styleType: \"paragraph\",\n occursUnder: [\"c\", \"s\", \"s1\", \"s2\", \"s3\", \"s4\"],\n textType: \"Section\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"level_1\"],\n description: \"Parallel reference(s) (basic)\",\n fontSize: 12,\n italic: true,\n justification: \"center\",\n spaceAfter: 4,\n },\n sp: {\n marker: \"sp\",\n styleType: \"paragraph\",\n occursUnder: [\"c\"],\n rank: 4,\n textType: \"Section\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"level_1\"],\n description: \"A heading, to identify the speaker (e.g. Job)\",\n fontSize: 12,\n italic: true,\n justification: \"left\",\n spaceBefore: 8,\n spaceAfter: 4,\n },\n d: {\n marker: \"d\",\n styleType: \"paragraph\",\n occursUnder: [\"c\"],\n rank: 4,\n textType: \"VerseText\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"level_1\"],\n description: \"A Hebrew text heading, to provide description (e.g. Psalms)\",\n fontSize: 12,\n italic: true,\n justification: \"center\",\n spaceBefore: 4,\n spaceAfter: 4,\n },\n sd: {\n marker: \"sd\",\n styleType: \"paragraph\",\n occursUnder: [\"c\"],\n rank: 4,\n textType: \"Section\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"level_1\"],\n description: \"Vertical space used to divide the text into sections, level 1 (if single level)\",\n spaceBefore: 24,\n spaceAfter: 24,\n },\n sd1: {\n marker: \"sd1\",\n styleType: \"paragraph\",\n occursUnder: [\"c\"],\n rank: 4,\n textType: \"Section\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"level_1\"],\n description: \"Vertical space used to divide the text into sections, level 1 (if multiple levels)\",\n spaceBefore: 24,\n spaceAfter: 24,\n },\n sd2: {\n marker: \"sd2\",\n styleType: \"paragraph\",\n occursUnder: [\"c\"],\n rank: 4,\n textType: \"Section\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"level_2\"],\n description: \"Vertical space used to divide the text into sections, level 2\",\n spaceBefore: 18,\n spaceAfter: 18,\n },\n sd3: {\n marker: \"sd3\",\n styleType: \"paragraph\",\n occursUnder: [\"c\"],\n rank: 4,\n textType: \"Section\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"level_3\"],\n description: \"Vertical space used to divide the text into sections, level 3\",\n spaceBefore: 12,\n spaceAfter: 12,\n },\n sd4: {\n marker: \"sd4\",\n styleType: \"paragraph\",\n occursUnder: [\"c\"],\n rank: 4,\n textType: \"Section\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"level_4\"],\n description: \"Vertical space used to divide the text into sections, level 4\",\n spaceBefore: 8,\n spaceAfter: 8,\n },\n tr: {\n marker: \"tr\",\n styleType: \"paragraph\",\n occursUnder: [\"c\"],\n rank: 4,\n textType: \"VerseText\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\"],\n description: \"A new table row\",\n fontSize: 12,\n firstLineIndent: -0.25,\n leftMargin: 0.5,\n },\n th1: {\n marker: \"th1\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table heading, column 1\",\n fontSize: 12,\n italic: true,\n },\n th2: {\n marker: \"th2\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table heading, column 2\",\n fontSize: 12,\n italic: true,\n },\n th3: {\n marker: \"th3\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table heading, column 3\",\n fontSize: 12,\n italic: true,\n },\n th4: {\n marker: \"th4\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table heading, column 4\",\n fontSize: 12,\n italic: true,\n },\n th5: {\n marker: \"th5\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table heading, column 5\",\n fontSize: 12,\n italic: true,\n },\n th6: {\n marker: \"th6\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table heading, column 6\",\n fontSize: 12,\n italic: true,\n },\n th7: {\n marker: \"th7\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table heading, column 7\",\n fontSize: 12,\n italic: true,\n },\n th8: {\n marker: \"th8\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table heading, column 8\",\n fontSize: 12,\n italic: true,\n },\n th9: {\n marker: \"th9\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table heading, column 9\",\n fontSize: 12,\n italic: true,\n },\n th10: {\n marker: \"th10\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table heading, column 10\",\n fontSize: 12,\n italic: true,\n },\n th11: {\n marker: \"th11\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table heading, column 11\",\n fontSize: 12,\n italic: true,\n },\n th12: {\n marker: \"th12\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table heading, column 12\",\n fontSize: 12,\n italic: true,\n },\n tc1: {\n marker: \"tc1\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table cell item, column 1\",\n fontSize: 12,\n },\n tc2: {\n marker: \"tc2\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table cell item, column 2\",\n fontSize: 12,\n },\n tc3: {\n marker: \"tc3\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table cell item, column 3\",\n fontSize: 12,\n },\n tc4: {\n marker: \"tc4\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table cell item, column 4\",\n fontSize: 12,\n },\n tc5: {\n marker: \"tc5\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table cell item, column 5\",\n fontSize: 12,\n },\n tc6: {\n marker: \"tc6\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table cell item, column 6\",\n fontSize: 12,\n },\n tc7: {\n marker: \"tc7\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table cell item, column 7\",\n fontSize: 12,\n },\n tc8: {\n marker: \"tc8\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table cell item, column 8\",\n fontSize: 12,\n },\n tc9: {\n marker: \"tc9\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table cell item, column 9\",\n fontSize: 12,\n },\n tc10: {\n marker: \"tc10\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table cell item, column 10\",\n fontSize: 12,\n },\n tc11: {\n marker: \"tc11\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table cell item, column 11\",\n fontSize: 12,\n },\n tc12: {\n marker: \"tc12\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table cell item, column 12\",\n fontSize: 12,\n },\n thc1: {\n marker: \"thc1\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table heading, column 1, center aligned\",\n fontSize: 12,\n italic: true,\n justification: \"center\",\n },\n thc2: {\n marker: \"thc2\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table heading, column 2, center aligned\",\n fontSize: 12,\n italic: true,\n justification: \"center\",\n },\n thc3: {\n marker: \"thc3\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table heading, column 3, center aligned\",\n fontSize: 12,\n italic: true,\n justification: \"center\",\n },\n thc4: {\n marker: \"thc4\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table heading, column 4, center aligned\",\n fontSize: 12,\n italic: true,\n justification: \"center\",\n },\n thc5: {\n marker: \"thc5\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table heading, column 5, center aligned\",\n fontSize: 12,\n italic: true,\n justification: \"center\",\n },\n thc6: {\n marker: \"thc6\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table heading, column 6, center aligned\",\n fontSize: 12,\n italic: true,\n justification: \"center\",\n },\n thc7: {\n marker: \"thc7\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table heading, column 7, center aligned\",\n fontSize: 12,\n italic: true,\n justification: \"center\",\n },\n thc8: {\n marker: \"thc8\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table heading, column 8, center aligned\",\n fontSize: 12,\n italic: true,\n justification: \"center\",\n },\n thc9: {\n marker: \"thc9\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table heading, column 9, center aligned\",\n fontSize: 12,\n italic: true,\n justification: \"center\",\n },\n thc10: {\n marker: \"thc10\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table heading, column 10, center aligned\",\n fontSize: 12,\n italic: true,\n justification: \"center\",\n },\n thc11: {\n marker: \"thc11\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table heading, column 11, center aligned\",\n fontSize: 12,\n italic: true,\n justification: \"center\",\n },\n thc12: {\n marker: \"thc12\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table heading, column 12, center aligned\",\n fontSize: 12,\n italic: true,\n justification: \"center\",\n },\n tcc1: {\n marker: \"tcc1\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table cell item, column 1, center aligned\",\n fontSize: 12,\n justification: \"center\",\n },\n tcc2: {\n marker: \"tcc2\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table cell item, column 2, center aligned\",\n fontSize: 12,\n justification: \"center\",\n },\n tcc3: {\n marker: \"tcc3\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table cell item, column 3, center aligned\",\n fontSize: 12,\n justification: \"center\",\n },\n tcc4: {\n marker: \"tcc4\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table cell item, column 4, center aligned\",\n fontSize: 12,\n justification: \"center\",\n },\n tcc5: {\n marker: \"tcc5\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table cell item, column 5, center aligned\",\n fontSize: 12,\n justification: \"center\",\n },\n tcc6: {\n marker: \"tcc6\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table cell item, column 6, center aligned\",\n fontSize: 12,\n justification: \"center\",\n },\n tcc7: {\n marker: \"tcc7\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table cell item, column 7, center aligned\",\n fontSize: 12,\n justification: \"center\",\n },\n tcc8: {\n marker: \"tcc8\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table cell item, column 8, center aligned\",\n fontSize: 12,\n justification: \"center\",\n },\n tcc9: {\n marker: \"tcc9\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table cell item, column 9, center aligned\",\n fontSize: 12,\n justification: \"center\",\n },\n tcc10: {\n marker: \"tcc10\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table cell item, column 10, center aligned\",\n fontSize: 12,\n justification: \"center\",\n },\n tcc11: {\n marker: \"tcc11\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table cell item, column 11, center aligned\",\n fontSize: 12,\n justification: \"center\",\n },\n tcc12: {\n marker: \"tcc12\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table cell item, column 12, center aligned\",\n fontSize: 12,\n justification: \"center\",\n },\n thr1: {\n marker: \"thr1\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table heading, column 1, right aligned\",\n fontSize: 12,\n italic: true,\n justification: \"right\",\n },\n thr2: {\n marker: \"thr2\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table heading, column 2, right aligned\",\n fontSize: 12,\n italic: true,\n justification: \"right\",\n },\n thr3: {\n marker: \"thr3\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table heading, column 3, right aligned\",\n fontSize: 12,\n italic: true,\n justification: \"right\",\n },\n thr4: {\n marker: \"thr4\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table heading, column 4, right aligned\",\n fontSize: 12,\n italic: true,\n justification: \"right\",\n },\n thr5: {\n marker: \"thr5\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table heading, column 5, right aligned\",\n fontSize: 12,\n italic: true,\n justification: \"right\",\n },\n thr6: {\n marker: \"thr6\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table heading, column 6, right aligned\",\n fontSize: 12,\n italic: true,\n justification: \"right\",\n },\n thr7: {\n marker: \"thr7\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table heading, column 7, right aligned\",\n fontSize: 12,\n italic: true,\n justification: \"right\",\n },\n thr8: {\n marker: \"thr8\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table heading, column 8, right aligned\",\n fontSize: 12,\n italic: true,\n justification: \"right\",\n },\n thr9: {\n marker: \"thr9\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table heading, column 9, right aligned\",\n fontSize: 12,\n italic: true,\n justification: \"right\",\n },\n thr10: {\n marker: \"thr10\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table heading, column 10, right aligned\",\n fontSize: 12,\n italic: true,\n justification: \"right\",\n },\n thr11: {\n marker: \"thr11\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table heading, column 11, right aligned\",\n fontSize: 12,\n italic: true,\n justification: \"right\",\n },\n thr12: {\n marker: \"thr12\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table heading, column 12, right aligned\",\n fontSize: 12,\n italic: true,\n justification: \"right\",\n },\n tcr1: {\n marker: \"tcr1\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table cell item, column 1, right aligned\",\n fontSize: 12,\n justification: \"right\",\n },\n tcr2: {\n marker: \"tcr2\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table cell item, column 2, right aligned\",\n fontSize: 12,\n justification: \"right\",\n },\n tcr3: {\n marker: \"tcr3\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table cell item, column 3, right aligned\",\n fontSize: 12,\n justification: \"right\",\n },\n tcr4: {\n marker: \"tcr4\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table cell item, column 4, right aligned\",\n fontSize: 12,\n justification: \"right\",\n },\n tcr5: {\n marker: \"tcr5\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table cell item, column 5, right aligned\",\n fontSize: 12,\n justification: \"right\",\n },\n tcr6: {\n marker: \"tcr6\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table cell item, column 6, right aligned\",\n fontSize: 12,\n justification: \"right\",\n },\n tcr7: {\n marker: \"tcr7\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table cell item, column 7, right aligned\",\n fontSize: 12,\n justification: \"right\",\n },\n tcr8: {\n marker: \"tcr8\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table cell item, column 8, right aligned\",\n fontSize: 12,\n justification: \"right\",\n },\n tcr9: {\n marker: \"tcr9\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table cell item, column 9, right aligned\",\n fontSize: 12,\n justification: \"right\",\n },\n tcr10: {\n marker: \"tcr10\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table cell item, column 10, right aligned\",\n fontSize: 12,\n justification: \"right\",\n },\n tcr11: {\n marker: \"tcr11\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table cell item, column 11, right aligned\",\n fontSize: 12,\n justification: \"right\",\n },\n tcr12: {\n marker: \"tcr12\",\n styleType: \"character\",\n occursUnder: [\"tr\"],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A table cell item, column 12, right aligned\",\n fontSize: 12,\n justification: \"right\",\n },\n lh: {\n marker: \"lh\",\n styleType: \"paragraph\",\n occursUnder: [\"c\"],\n rank: 4,\n textType: \"VerseText\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\"],\n description: \"List header (introductory remark)\",\n fontSize: 12,\n firstLineIndent: 0.125,\n },\n li: {\n marker: \"li\",\n styleType: \"paragraph\",\n occursUnder: [\"c\"],\n rank: 4,\n textType: \"VerseText\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"level_1\"],\n description: \"A list entry, level 1 (if single level)\",\n fontSize: 12,\n firstLineIndent: -0.375,\n leftMargin: 0.5,\n },\n li1: {\n marker: \"li1\",\n styleType: \"paragraph\",\n occursUnder: [\"c\"],\n rank: 4,\n textType: \"VerseText\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"level_1\"],\n description: \"A list entry, level 1 (if multiple levels)\",\n fontSize: 12,\n firstLineIndent: -0.375,\n leftMargin: 0.5,\n },\n li2: {\n marker: \"li2\",\n styleType: \"paragraph\",\n occursUnder: [\"c\"],\n rank: 4,\n textType: \"VerseText\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"level_2\"],\n description: \"A list entry, level 2\",\n fontSize: 12,\n firstLineIndent: -0.375,\n leftMargin: 0.75,\n },\n li3: {\n marker: \"li3\",\n styleType: \"paragraph\",\n occursUnder: [\"c\"],\n rank: 4,\n textType: \"VerseText\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"level_3\"],\n description: \"A list entry, level 3\",\n fontSize: 12,\n firstLineIndent: -0.375,\n leftMargin: 1,\n },\n li4: {\n marker: \"li4\",\n styleType: \"paragraph\",\n occursUnder: [\"c\"],\n rank: 4,\n textType: \"VerseText\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"level_4\"],\n description: \"A list entry, level 4\",\n fontSize: 12,\n firstLineIndent: -0.375,\n leftMargin: 1.25,\n },\n lf: {\n marker: \"lf\",\n styleType: \"paragraph\",\n occursUnder: [\"c\"],\n rank: 4,\n textType: \"VerseText\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\"],\n description: \"List footer (concluding remark)\",\n fontSize: 12,\n },\n lim: {\n marker: \"lim\",\n styleType: \"paragraph\",\n occursUnder: [\"c\"],\n rank: 4,\n textType: \"VerseText\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"level_1\"],\n description: \"An embedded list entry, level 1 (if single level)\",\n fontSize: 12,\n firstLineIndent: -0.375,\n leftMargin: 0.75,\n rightMargin: 0.25,\n },\n lim1: {\n marker: \"lim1\",\n styleType: \"paragraph\",\n occursUnder: [\"c\"],\n rank: 4,\n textType: \"VerseText\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"level_1\"],\n description: \"An embedded list entry, level 1 (if multiple levels)\",\n fontSize: 12,\n firstLineIndent: -0.375,\n leftMargin: 0.75,\n rightMargin: 0.25,\n },\n lim2: {\n marker: \"lim2\",\n styleType: \"paragraph\",\n occursUnder: [\"c\"],\n rank: 4,\n textType: \"VerseText\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"level_2\"],\n description: \"An embedded list entry, level 2\",\n fontSize: 12,\n firstLineIndent: -0.375,\n leftMargin: 1,\n },\n lim3: {\n marker: \"lim3\",\n styleType: \"paragraph\",\n occursUnder: [\"c\"],\n rank: 4,\n textType: \"VerseText\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"level_3\"],\n description: \"An embedded list item, level 3\",\n fontSize: 12,\n firstLineIndent: -0.375,\n leftMargin: 1.25,\n },\n lim4: {\n marker: \"lim4\",\n styleType: \"paragraph\",\n occursUnder: [\"c\"],\n rank: 4,\n textType: \"VerseText\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"level_4\"],\n description: \"An embedded list entry, level 4\",\n fontSize: 12,\n firstLineIndent: -0.375,\n leftMargin: 1.5,\n },\n litl: {\n marker: \"litl\",\n styleType: \"character\",\n endMarker: \"litl*\",\n occursUnder: [\n \"li\",\n \"li1\",\n \"li2\",\n \"li3\",\n \"li4\",\n \"lim\",\n \"lim1\",\n \"lim2\",\n \"lim3\",\n \"lim4\",\n \"NEST\",\n ],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"List entry total text\",\n fontSize: 12,\n italic: true,\n },\n lik: {\n marker: \"lik\",\n styleType: \"character\",\n endMarker: \"lik*\",\n occursUnder: [\n \"li\",\n \"li1\",\n \"li2\",\n \"li3\",\n \"li4\",\n \"lim\",\n \"lim1\",\n \"lim2\",\n \"lim3\",\n \"lim4\",\n \"NEST\",\n ],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"Structured list entry key text\",\n fontSize: 12,\n italic: true,\n },\n liv: {\n marker: \"liv\",\n styleType: \"character\",\n endMarker: \"liv*\",\n occursUnder: [\n \"li\",\n \"li1\",\n \"li2\",\n \"li3\",\n \"li4\",\n \"lim\",\n \"lim1\",\n \"lim2\",\n \"lim3\",\n \"lim4\",\n \"NEST\",\n ],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"Structured list entry value 1 content (if single value)\",\n fontSize: 12,\n },\n liv1: {\n marker: \"liv1\",\n styleType: \"character\",\n endMarker: \"liv1*\",\n occursUnder: [\n \"li\",\n \"li1\",\n \"li2\",\n \"li3\",\n \"li4\",\n \"lim\",\n \"lim1\",\n \"lim2\",\n \"lim3\",\n \"lim4\",\n \"NEST\",\n ],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"Structured list entry value 1 content (if multiple values)\",\n fontSize: 12,\n },\n liv2: {\n marker: \"liv2\",\n styleType: \"character\",\n endMarker: \"liv2*\",\n occursUnder: [\n \"li\",\n \"li1\",\n \"li2\",\n \"li3\",\n \"li4\",\n \"lim\",\n \"lim1\",\n \"lim2\",\n \"lim3\",\n \"lim4\",\n \"NEST\",\n ],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"Structured list entry value 2 content\",\n fontSize: 12,\n },\n liv3: {\n marker: \"liv3\",\n styleType: \"character\",\n endMarker: \"liv3*\",\n occursUnder: [\n \"li\",\n \"li1\",\n \"li2\",\n \"li3\",\n \"li4\",\n \"lim\",\n \"lim1\",\n \"lim2\",\n \"lim3\",\n \"lim4\",\n \"NEST\",\n ],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"Structured list entry value 3 content\",\n fontSize: 12,\n },\n liv4: {\n marker: \"liv4\",\n styleType: \"character\",\n endMarker: \"liv4*\",\n occursUnder: [\n \"li\",\n \"li1\",\n \"li2\",\n \"li3\",\n \"li4\",\n \"lim\",\n \"lim1\",\n \"lim2\",\n \"lim3\",\n \"lim4\",\n \"NEST\",\n ],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"Structured list entry value 4 content\",\n fontSize: 12,\n },\n liv5: {\n marker: \"liv5\",\n styleType: \"character\",\n endMarker: \"liv5*\",\n occursUnder: [\n \"li\",\n \"li1\",\n \"li2\",\n \"li3\",\n \"li4\",\n \"lim\",\n \"lim1\",\n \"lim2\",\n \"lim3\",\n \"lim4\",\n \"NEST\",\n ],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"Structured list entry value 5 content\",\n fontSize: 12,\n },\n f: {\n marker: \"f\",\n styleType: \"note\",\n endMarker: \"f*\",\n occursUnder: [\n \"c\",\n \"cp\",\n \"lh\",\n \"li\",\n \"li1\",\n \"li2\",\n \"li3\",\n \"li4\",\n \"lf\",\n \"lim\",\n \"lim1\",\n \"lim2\",\n \"lim3\",\n \"lim4\",\n \"m\",\n \"mi\",\n \"nb\",\n \"p\",\n \"pc\",\n \"ph\",\n \"phi\",\n \"pi\",\n \"pi1\",\n \"pi2\",\n \"pi3\",\n \"pr\",\n \"pmo\",\n \"pm\",\n \"pmc\",\n \"pmr\",\n \"po\",\n \"q\",\n \"q1\",\n \"q2\",\n \"q3\",\n \"q4\",\n \"qc\",\n \"qr\",\n \"qd\",\n \"qm\",\n \"qm1\",\n \"qm2\",\n \"qm3\",\n \"qs\",\n \"sp\",\n \"tc1\",\n \"tc2\",\n \"tc3\",\n \"tc4\",\n \"mt\",\n \"mt1\",\n \"mt2\",\n \"mt3\",\n \"ms\",\n \"ms1\",\n \"ms2\",\n \"ms3\",\n \"s\",\n \"s1\",\n \"s2\",\n \"s3\",\n \"d\",\n \"ip\",\n ],\n textType: \"NoteText\",\n textProperties: [\"publishable\", \"vernacular\", \"note\"],\n description: \"A Footnote text item (basic)\",\n fontSize: 12,\n },\n fe: {\n marker: \"fe\",\n styleType: \"note\",\n endMarker: \"fe*\",\n occursUnder: [\n \"c\",\n \"lh\",\n \"li\",\n \"li1\",\n \"li2\",\n \"li3\",\n \"li4\",\n \"lf\",\n \"lim\",\n \"lim1\",\n \"lim2\",\n \"lim3\",\n \"lim4\",\n \"m\",\n \"mi\",\n \"nb\",\n \"p\",\n \"pc\",\n \"ph\",\n \"phi\",\n \"pi\",\n \"pi1\",\n \"pi2\",\n \"pi3\",\n \"pr\",\n \"pmo\",\n \"pm\",\n \"pmc\",\n \"pmr\",\n \"po\",\n \"q\",\n \"q1\",\n \"q2\",\n \"q3\",\n \"q4\",\n \"qc\",\n \"qr\",\n \"qd\",\n \"qm\",\n \"qm1\",\n \"qm2\",\n \"qm3\",\n \"sp\",\n \"tc1\",\n \"tc2\",\n \"tc3\",\n \"tc4\",\n \"ms\",\n \"ms1\",\n \"ms2\",\n \"ms3\",\n \"s\",\n \"s1\",\n \"s2\",\n \"s3\",\n \"d\",\n \"ip\",\n ],\n textType: \"NoteText\",\n textProperties: [\"publishable\", \"vernacular\", \"note\"],\n description: \"An Endnote text item\",\n fontSize: 12,\n },\n fr: {\n marker: \"fr\",\n styleType: \"character\",\n endMarker: \"fr*\",\n occursUnder: [\"f\", \"fe\"],\n textType: \"NoteText\",\n textProperties: [\"publishable\", \"vernacular\", \"note\"],\n description: \"The origin reference for the footnote (basic)\",\n fontSize: 12,\n bold: true,\n },\n ft: {\n marker: \"ft\",\n styleType: \"character\",\n endMarker: \"ft*\",\n occursUnder: [\"f\", \"fe\"],\n textType: \"NoteText\",\n textProperties: [\"publishable\", \"vernacular\", \"note\"],\n description: \"Footnote text, Protocanon (basic)\",\n fontSize: 12,\n },\n fk: {\n marker: \"fk\",\n styleType: \"character\",\n endMarker: \"fk*\",\n occursUnder: [\"f\", \"fe\"],\n textType: \"NoteText\",\n textProperties: [\"publishable\", \"vernacular\", \"note\"],\n description: \"A footnote keyword (basic)\",\n fontSize: 12,\n bold: true,\n italic: true,\n },\n fq: {\n marker: \"fq\",\n styleType: \"character\",\n endMarker: \"fq*\",\n occursUnder: [\"f\", \"fe\"],\n textType: \"NoteText\",\n textProperties: [\"publishable\", \"vernacular\", \"note\"],\n description: \"A footnote scripture quote or alternate rendering (basic)\",\n fontSize: 12,\n italic: true,\n },\n fqa: {\n marker: \"fqa\",\n styleType: \"character\",\n endMarker: \"fqa*\",\n occursUnder: [\"f\", \"fe\"],\n textType: \"NoteText\",\n textProperties: [\"publishable\", \"vernacular\", \"note\"],\n description: \"A footnote alternate rendering for a portion of scripture text\",\n fontSize: 12,\n italic: true,\n },\n fl: {\n marker: \"fl\",\n styleType: \"character\",\n endMarker: \"fl*\",\n occursUnder: [\"f\", \"fe\"],\n textType: \"NoteText\",\n textProperties: [\"publishable\", \"vernacular\", \"note\"],\n description: 'A footnote label text item, for marking or \"labelling\" the type or alternate translation being provided in the note.',\n fontSize: 12,\n bold: true,\n italic: true,\n },\n fw: {\n marker: \"fw\",\n styleType: \"character\",\n endMarker: \"fw*\",\n occursUnder: [\"f\", \"fe\"],\n textType: \"NoteText\",\n textProperties: [\"publishable\", \"vernacular\", \"note\"],\n description: \"A footnote witness list, for distinguishing a list of sigla representing witnesses in critical editions.\",\n fontSize: 12,\n },\n fp: {\n marker: \"fp\",\n styleType: \"character\",\n endMarker: \"fp*\",\n occursUnder: [\"f\", \"fe\"],\n textType: \"NoteText\",\n textProperties: [\"publishable\", \"vernacular\", \"note\"],\n description: \"A Footnote additional paragraph marker\",\n fontSize: 12,\n },\n fv: {\n marker: \"fv\",\n styleType: \"character\",\n endMarker: \"fv*\",\n occursUnder: [\"f\", \"fe\"],\n textType: \"NoteText\",\n textProperties: [\"publishable\", \"vernacular\", \"note\"],\n description: \"A verse number within the footnote text\",\n fontSize: 12,\n superscript: true,\n },\n fdc: {\n marker: \"fdc\",\n styleType: \"character\",\n endMarker: \"fdc*\",\n occursUnder: [\"f\", \"fe\"],\n textType: \"NoteText\",\n textProperties: [\"publishable\", \"vernacular\", \"note\"],\n description: \"Footnote text, applies to Deuterocanon only\",\n fontSize: 12,\n },\n fm: {\n marker: \"fm\",\n styleType: \"character\",\n endMarker: \"fm*\",\n occursUnder: [\n \"lh\",\n \"li\",\n \"li1\",\n \"li2\",\n \"li3\",\n \"li4\",\n \"lf\",\n \"lim\",\n \"lim1\",\n \"lim2\",\n \"lim3\",\n \"lim4\",\n \"m\",\n \"mi\",\n \"nb\",\n \"p\",\n \"pc\",\n \"ph\",\n \"phi\",\n \"pi\",\n \"pi1\",\n \"pi2\",\n \"pi3\",\n \"pr\",\n \"pmo\",\n \"pm\",\n \"pmc\",\n \"pmr\",\n \"po\",\n \"q\",\n \"q1\",\n \"q2\",\n \"q3\",\n \"q4\",\n \"qc\",\n \"qr\",\n \"qd\",\n \"qm\",\n \"qm1\",\n \"qm2\",\n \"qm3\",\n \"sp\",\n \"tc1\",\n \"tc2\",\n \"tc3\",\n \"tc4\",\n \"ms\",\n \"ms1\",\n \"ms2\",\n \"s\",\n \"s1\",\n \"s2\",\n \"s3\",\n \"d\",\n \"ip\",\n ],\n textType: \"Other\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\"],\n description: \"An additional footnote marker location for a previous footnote\",\n fontSize: 12,\n superscript: true,\n },\n x: {\n marker: \"x\",\n styleType: \"note\",\n endMarker: \"x*\",\n occursUnder: [\n \"lh\",\n \"li\",\n \"li1\",\n \"li2\",\n \"li3\",\n \"li4\",\n \"lf\",\n \"lim\",\n \"lim1\",\n \"lim2\",\n \"lim3\",\n \"lim4\",\n \"m\",\n \"mi\",\n \"nb\",\n \"p\",\n \"pc\",\n \"ph\",\n \"phi\",\n \"pi\",\n \"pi1\",\n \"pi2\",\n \"pi3\",\n \"pr\",\n \"pmo\",\n \"pm\",\n \"pmc\",\n \"pmr\",\n \"po\",\n \"q\",\n \"q1\",\n \"q2\",\n \"q3\",\n \"q4\",\n \"qc\",\n \"qr\",\n \"qd\",\n \"qm\",\n \"qm1\",\n \"qm2\",\n \"qm3\",\n \"qs\",\n \"sp\",\n \"tc1\",\n \"tc2\",\n \"tc3\",\n \"tc4\",\n \"mt\",\n \"mt1\",\n \"mt2\",\n \"mt3\",\n \"ms\",\n \"ms1\",\n \"ms2\",\n \"s\",\n \"s1\",\n \"s2\",\n \"s3\",\n \"d\",\n ],\n textType: \"NoteText\",\n textProperties: [\"publishable\", \"vernacular\", \"note\", \"crossreference\"],\n description: \"A list of cross references (basic)\",\n fontSize: 12,\n },\n xo: {\n marker: \"xo\",\n styleType: \"character\",\n endMarker: \"xo*\",\n occursUnder: [\"x\"],\n textType: \"NoteText\",\n textProperties: [\"publishable\", \"vernacular\", \"note\"],\n description: \"The cross reference origin reference (basic)\",\n fontSize: 12,\n bold: true,\n },\n xop: {\n marker: \"xop\",\n styleType: \"character\",\n endMarker: \"xop*\",\n occursUnder: [\"x\"],\n textType: \"NoteText\",\n textProperties: [\"publishable\", \"vernacular\", \"note\"],\n description: \"Published cross reference origin reference (origin reference that should appear in the published text)\",\n fontSize: 12,\n },\n xt: {\n marker: \"xt\",\n styleType: \"character\",\n endMarker: \"xt*\",\n occursUnder: [\n \"ip\",\n \"im\",\n \"ipi\",\n \"imi\",\n \"ipq\",\n \"imq\",\n \"ipr\",\n \"iq\",\n \"iq1\",\n \"iq2\",\n \"iq3\",\n \"ili\",\n \"ili1\",\n \"ili2\",\n \"io\",\n \"io1\",\n \"io2\",\n \"io3\",\n \"io4\",\n \"ms\",\n \"ms1\",\n \"ms2\",\n \"s\",\n \"s1\",\n \"s2\",\n \"s3\",\n \"s4\",\n \"cd\",\n \"sp\",\n \"d\",\n \"lh\",\n \"li\",\n \"li1\",\n \"li2\",\n \"li3\",\n \"li4\",\n \"lf\",\n \"lim\",\n \"lim1\",\n \"lim2\",\n \"lim3\",\n \"lim4\",\n \"m\",\n \"mi\",\n \"nb\",\n \"p\",\n \"pc\",\n \"ph\",\n \"phi\",\n \"pi\",\n \"pi1\",\n \"pi2\",\n \"pi3\",\n \"pr\",\n \"pmo\",\n \"pm\",\n \"pmc\",\n \"pmr\",\n \"po\",\n \"q\",\n \"q1\",\n \"q2\",\n \"q3\",\n \"q4\",\n \"qc\",\n \"qr\",\n \"qd\",\n \"qm\",\n \"qm1\",\n \"qm2\",\n \"qm3\",\n \"tr\",\n \"th1\",\n \"th2\",\n \"th3\",\n \"th4\",\n \"thr1\",\n \"thr2\",\n \"thr3\",\n \"thr4\",\n \"tc1\",\n \"tc2\",\n \"tc3\",\n \"tc4\",\n \"tcr1\",\n \"tcr2\",\n \"tcr3\",\n \"tcr4\",\n \"f\",\n \"fe\",\n \"x\",\n \"ef\",\n \"ex\",\n \"NEST\",\n ],\n textType: \"NoteText\",\n textProperties: [\"publishable\", \"vernacular\", \"note\"],\n description: \"The cross reference target reference(s), protocanon only (basic)\",\n fontSize: 12,\n },\n xta: {\n marker: \"xta\",\n styleType: \"character\",\n endMarker: \"xta*\",\n occursUnder: [\"x\"],\n textType: \"NoteText\",\n textProperties: [\"publishable\", \"vernacular\", \"note\"],\n description: \"Cross reference target references added text\",\n fontSize: 12,\n },\n xk: {\n marker: \"xk\",\n styleType: \"character\",\n endMarker: \"xk*\",\n occursUnder: [\"x\"],\n textType: \"NoteText\",\n textProperties: [\"publishable\", \"vernacular\", \"note\"],\n description: \"A cross reference keyword\",\n fontSize: 12,\n italic: true,\n },\n xq: {\n marker: \"xq\",\n styleType: \"character\",\n endMarker: \"xq*\",\n occursUnder: [\"x\"],\n textType: \"NoteText\",\n textProperties: [\"publishable\", \"vernacular\", \"note\"],\n description: \"A cross-reference quotation from the scripture text\",\n fontSize: 12,\n italic: true,\n },\n xot: {\n marker: \"xot\",\n styleType: \"character\",\n endMarker: \"xot*\",\n occursUnder: [\"x\"],\n textType: \"NoteText\",\n textProperties: [\"publishable\", \"vernacular\", \"note\"],\n description: \"Cross-reference target reference(s), Old Testament only\",\n fontSize: 12,\n },\n xnt: {\n marker: \"xnt\",\n styleType: \"character\",\n endMarker: \"xnt*\",\n occursUnder: [\"x\"],\n textType: \"NoteText\",\n textProperties: [\"publishable\", \"vernacular\", \"note\"],\n description: \"Cross-reference target reference(s), New Testament only\",\n fontSize: 12,\n },\n xdc: {\n marker: \"xdc\",\n styleType: \"character\",\n endMarker: \"xdc*\",\n occursUnder: [\"x\"],\n textType: \"NoteText\",\n textProperties: [\"publishable\", \"vernacular\", \"note\"],\n description: \"Cross-reference target reference(s), Deuterocanon only\",\n fontSize: 12,\n },\n rq: {\n marker: \"rq\",\n styleType: \"character\",\n endMarker: \"rq*\",\n occursUnder: [\n \"lh\",\n \"li\",\n \"li1\",\n \"li2\",\n \"li3\",\n \"li4\",\n \"lf\",\n \"lim\",\n \"lim1\",\n \"lim2\",\n \"lim3\",\n \"lim4\",\n \"m\",\n \"mi\",\n \"nb\",\n \"p\",\n \"pc\",\n \"ph\",\n \"phi\",\n \"pi\",\n \"pi1\",\n \"pi2\",\n \"pi3\",\n \"pr\",\n \"pmo\",\n \"pm\",\n \"pmc\",\n \"pmr\",\n \"po\",\n \"q\",\n \"q1\",\n \"q2\",\n \"q3\",\n \"q4\",\n \"qc\",\n \"qr\",\n \"qd\",\n \"qm\",\n \"qm1\",\n \"qm2\",\n \"qm3\",\n \"NEST\",\n ],\n textType: \"Other\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A cross-reference indicating the source text for the preceding quotation.\",\n fontSize: 10,\n italic: true,\n },\n qt: {\n marker: \"qt\",\n styleType: \"character\",\n endMarker: \"qt*\",\n occursUnder: [\n \"ip\",\n \"im\",\n \"ipi\",\n \"imi\",\n \"ipq\",\n \"imq\",\n \"ipr\",\n \"iq\",\n \"iq1\",\n \"iq2\",\n \"iq3\",\n \"ili\",\n \"ili1\",\n \"ili2\",\n \"io\",\n \"io1\",\n \"io2\",\n \"io3\",\n \"io4\",\n \"ms\",\n \"ms1\",\n \"ms2\",\n \"s\",\n \"s1\",\n \"s2\",\n \"s3\",\n \"s4\",\n \"cd\",\n \"sp\",\n \"d\",\n \"lh\",\n \"li\",\n \"li1\",\n \"li2\",\n \"li3\",\n \"li4\",\n \"lf\",\n \"lim\",\n \"lim1\",\n \"lim2\",\n \"lim3\",\n \"lim4\",\n \"m\",\n \"mi\",\n \"nb\",\n \"p\",\n \"pc\",\n \"ph\",\n \"phi\",\n \"pi\",\n \"pi1\",\n \"pi2\",\n \"pi3\",\n \"pr\",\n \"pmo\",\n \"pm\",\n \"pmc\",\n \"pmr\",\n \"po\",\n \"q\",\n \"q1\",\n \"q2\",\n \"q3\",\n \"q4\",\n \"qc\",\n \"qr\",\n \"qd\",\n \"qm\",\n \"qm1\",\n \"qm2\",\n \"qm3\",\n \"tr\",\n \"th1\",\n \"th2\",\n \"th3\",\n \"th4\",\n \"thr1\",\n \"thr2\",\n \"thr3\",\n \"thr4\",\n \"tc1\",\n \"tc2\",\n \"tc3\",\n \"tc4\",\n \"tcr1\",\n \"tcr2\",\n \"tcr3\",\n \"tcr4\",\n \"f\",\n \"fe\",\n \"NEST\",\n ],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"For Old Testament quoted text appearing in the New Testament (basic)\",\n fontSize: 12,\n italic: true,\n },\n nd: {\n marker: \"nd\",\n styleType: \"character\",\n endMarker: \"nd*\",\n occursUnder: [\n \"ip\",\n \"im\",\n \"ipi\",\n \"imi\",\n \"ipq\",\n \"imq\",\n \"ipr\",\n \"iq\",\n \"iq1\",\n \"iq2\",\n \"iq3\",\n \"ili\",\n \"ili1\",\n \"ili2\",\n \"io\",\n \"io1\",\n \"io2\",\n \"io3\",\n \"io4\",\n \"ms\",\n \"ms1\",\n \"ms2\",\n \"s\",\n \"s1\",\n \"s2\",\n \"s3\",\n \"s4\",\n \"cd\",\n \"sp\",\n \"d\",\n \"lh\",\n \"li\",\n \"li1\",\n \"li2\",\n \"li3\",\n \"li4\",\n \"lf\",\n \"lim\",\n \"lim1\",\n \"lim2\",\n \"lim3\",\n \"lim4\",\n \"m\",\n \"mi\",\n \"nb\",\n \"p\",\n \"pc\",\n \"ph\",\n \"phi\",\n \"pi\",\n \"pi1\",\n \"pi2\",\n \"pi3\",\n \"pr\",\n \"pmo\",\n \"pm\",\n \"pmc\",\n \"pmr\",\n \"po\",\n \"q\",\n \"q1\",\n \"q2\",\n \"q3\",\n \"q4\",\n \"qc\",\n \"qr\",\n \"qd\",\n \"qm\",\n \"qm1\",\n \"qm2\",\n \"qm3\",\n \"tr\",\n \"th1\",\n \"th2\",\n \"th3\",\n \"th4\",\n \"thr1\",\n \"thr2\",\n \"thr3\",\n \"thr4\",\n \"tc1\",\n \"tc2\",\n \"tc3\",\n \"tc4\",\n \"tcr1\",\n \"tcr2\",\n \"tcr3\",\n \"tcr4\",\n \"f\",\n \"fe\",\n \"NEST\",\n ],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"For name of deity (basic)\",\n fontSize: 12,\n underline: true,\n },\n tl: {\n marker: \"tl\",\n styleType: \"character\",\n endMarker: \"tl*\",\n occursUnder: [\n \"ip\",\n \"im\",\n \"ipi\",\n \"imi\",\n \"ipq\",\n \"imq\",\n \"ipr\",\n \"iq\",\n \"iq1\",\n \"iq2\",\n \"iq3\",\n \"ili\",\n \"ili1\",\n \"ili2\",\n \"io\",\n \"io1\",\n \"io2\",\n \"io3\",\n \"io4\",\n \"ms\",\n \"ms1\",\n \"ms2\",\n \"s\",\n \"s1\",\n \"s2\",\n \"s3\",\n \"s4\",\n \"cd\",\n \"sp\",\n \"d\",\n \"lh\",\n \"li\",\n \"li1\",\n \"li2\",\n \"li3\",\n \"li4\",\n \"lf\",\n \"lim\",\n \"lim1\",\n \"lim2\",\n \"lim3\",\n \"lim4\",\n \"m\",\n \"mi\",\n \"nb\",\n \"p\",\n \"pc\",\n \"ph\",\n \"phi\",\n \"pi\",\n \"pi1\",\n \"pi2\",\n \"pi3\",\n \"pr\",\n \"pmo\",\n \"pm\",\n \"pmc\",\n \"pmr\",\n \"po\",\n \"q\",\n \"q1\",\n \"q2\",\n \"q3\",\n \"q4\",\n \"qc\",\n \"qr\",\n \"qd\",\n \"qm\",\n \"qm1\",\n \"qm2\",\n \"qm3\",\n \"cls\",\n \"tr\",\n \"th1\",\n \"th2\",\n \"th3\",\n \"th4\",\n \"thr1\",\n \"thr2\",\n \"thr3\",\n \"thr4\",\n \"tc1\",\n \"tc2\",\n \"tc3\",\n \"tc4\",\n \"tcr1\",\n \"tcr2\",\n \"tcr3\",\n \"tcr4\",\n \"f\",\n \"fe\",\n \"NEST\",\n ],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"nonvernacular\"],\n description: \"For transliterated words\",\n fontSize: 12,\n italic: true,\n },\n dc: {\n marker: \"dc\",\n styleType: \"character\",\n endMarker: \"dc*\",\n occursUnder: [\n \"ip\",\n \"im\",\n \"ipi\",\n \"imi\",\n \"ipq\",\n \"imq\",\n \"ipr\",\n \"iq\",\n \"iq1\",\n \"iq2\",\n \"iq3\",\n \"ili\",\n \"ili1\",\n \"ili2\",\n \"io\",\n \"io1\",\n \"io2\",\n \"io3\",\n \"io4\",\n \"ms\",\n \"ms1\",\n \"ms2\",\n \"s\",\n \"s1\",\n \"s2\",\n \"s3\",\n \"s4\",\n \"cd\",\n \"sp\",\n \"d\",\n \"lh\",\n \"li\",\n \"li1\",\n \"li2\",\n \"li3\",\n \"li4\",\n \"lf\",\n \"lim\",\n \"lim1\",\n \"lim2\",\n \"lim3\",\n \"lim4\",\n \"m\",\n \"mi\",\n \"nb\",\n \"p\",\n \"pc\",\n \"ph\",\n \"phi\",\n \"pi\",\n \"pi1\",\n \"pi2\",\n \"pi3\",\n \"pr\",\n \"pmo\",\n \"pm\",\n \"pmc\",\n \"pmr\",\n \"po\",\n \"q\",\n \"q1\",\n \"q2\",\n \"q3\",\n \"q4\",\n \"qc\",\n \"qr\",\n \"qd\",\n \"qm\",\n \"qm1\",\n \"qm2\",\n \"qm3\",\n \"tr\",\n \"th1\",\n \"th2\",\n \"th3\",\n \"th4\",\n \"thr1\",\n \"thr2\",\n \"thr3\",\n \"thr4\",\n \"tc1\",\n \"tc2\",\n \"tc3\",\n \"tc4\",\n \"tcr1\",\n \"tcr2\",\n \"tcr3\",\n \"tcr4\",\n \"f\",\n \"fe\",\n \"NEST\",\n ],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"Deuterocanonical/LXX additions or insertions in the Protocanonical text\",\n italic: true,\n },\n bk: {\n marker: \"bk\",\n styleType: \"character\",\n endMarker: \"bk*\",\n occursUnder: [\n \"imt\",\n \"imt1\",\n \"imt2\",\n \"imt3\",\n \"imt4\",\n \"imte\",\n \"imte1\",\n \"imte2\",\n \"is\",\n \"is1\",\n \"is2\",\n \"ip\",\n \"im\",\n \"ipi\",\n \"imi\",\n \"ipq\",\n \"imq\",\n \"ipr\",\n \"iq\",\n \"iq1\",\n \"iq2\",\n \"iq3\",\n \"ili\",\n \"ili1\",\n \"ili2\",\n \"io\",\n \"io1\",\n \"io2\",\n \"io3\",\n \"io4\",\n \"ms\",\n \"ms1\",\n \"ms2\",\n \"s\",\n \"s1\",\n \"s2\",\n \"s3\",\n \"s4\",\n \"cd\",\n \"sp\",\n \"d\",\n \"lh\",\n \"li\",\n \"li1\",\n \"li2\",\n \"li3\",\n \"li4\",\n \"lf\",\n \"lim\",\n \"lim1\",\n \"lim2\",\n \"lim3\",\n \"lim4\",\n \"m\",\n \"mi\",\n \"nb\",\n \"p\",\n \"pc\",\n \"ph\",\n \"phi\",\n \"pi\",\n \"pi1\",\n \"pi2\",\n \"pi3\",\n \"pr\",\n \"pmo\",\n \"pm\",\n \"pmc\",\n \"pmr\",\n \"po\",\n \"q\",\n \"q1\",\n \"q2\",\n \"q3\",\n \"q4\",\n \"qc\",\n \"qr\",\n \"qd\",\n \"qm\",\n \"qm1\",\n \"qm2\",\n \"qm3\",\n \"tr\",\n \"th1\",\n \"th2\",\n \"th3\",\n \"th4\",\n \"thr1\",\n \"thr2\",\n \"thr3\",\n \"thr4\",\n \"tc1\",\n \"tc2\",\n \"tc3\",\n \"tc4\",\n \"tcr1\",\n \"tcr2\",\n \"tcr3\",\n \"tcr4\",\n \"f\",\n \"fe\",\n \"NEST\",\n ],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"For the quoted name of a book\",\n fontSize: 12,\n italic: true,\n },\n sig: {\n marker: \"sig\",\n styleType: \"character\",\n endMarker: \"sig*\",\n occursUnder: [\n \"ip\",\n \"im\",\n \"ipi\",\n \"imi\",\n \"ipq\",\n \"imq\",\n \"ipr\",\n \"iq\",\n \"iq1\",\n \"iq2\",\n \"iq3\",\n \"ili\",\n \"ili1\",\n \"ili2\",\n \"io\",\n \"io1\",\n \"io2\",\n \"io3\",\n \"io4\",\n \"ms\",\n \"ms1\",\n \"ms2\",\n \"s\",\n \"s1\",\n \"s2\",\n \"s3\",\n \"s4\",\n \"cd\",\n \"sp\",\n \"d\",\n \"lh\",\n \"li\",\n \"li1\",\n \"li2\",\n \"li3\",\n \"li4\",\n \"lf\",\n \"lim\",\n \"lim1\",\n \"lim2\",\n \"lim3\",\n \"lim4\",\n \"m\",\n \"mi\",\n \"nb\",\n \"p\",\n \"pc\",\n \"ph\",\n \"phi\",\n \"pi\",\n \"pi1\",\n \"pi2\",\n \"pi3\",\n \"pr\",\n \"pmo\",\n \"pm\",\n \"pmc\",\n \"pmr\",\n \"po\",\n \"q\",\n \"q1\",\n \"q2\",\n \"q3\",\n \"q4\",\n \"qc\",\n \"qr\",\n \"qd\",\n \"qm\",\n \"qm1\",\n \"qm2\",\n \"qm3\",\n \"cls\",\n \"tr\",\n \"th1\",\n \"th2\",\n \"th3\",\n \"th4\",\n \"thr1\",\n \"thr2\",\n \"thr3\",\n \"thr4\",\n \"tc1\",\n \"tc2\",\n \"tc3\",\n \"tc4\",\n \"tcr1\",\n \"tcr2\",\n \"tcr3\",\n \"tcr4\",\n \"f\",\n \"fe\",\n \"NEST\",\n ],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"For the signature of the author of an Epistle\",\n fontSize: 12,\n italic: true,\n },\n pn: {\n marker: \"pn\",\n styleType: \"character\",\n endMarker: \"pn*\",\n occursUnder: [\n \"ip\",\n \"im\",\n \"ipi\",\n \"imi\",\n \"ipq\",\n \"imq\",\n \"ipr\",\n \"iq\",\n \"iq1\",\n \"iq2\",\n \"iq3\",\n \"ili\",\n \"ili1\",\n \"ili2\",\n \"io\",\n \"io1\",\n \"io2\",\n \"io3\",\n \"io4\",\n \"ms\",\n \"ms1\",\n \"ms2\",\n \"s\",\n \"s1\",\n \"s2\",\n \"s3\",\n \"s4\",\n \"cd\",\n \"sp\",\n \"d\",\n \"lh\",\n \"li\",\n \"li1\",\n \"li2\",\n \"li3\",\n \"li4\",\n \"lf\",\n \"lim\",\n \"lim1\",\n \"lim2\",\n \"lim3\",\n \"lim4\",\n \"m\",\n \"mi\",\n \"nb\",\n \"p\",\n \"pc\",\n \"ph\",\n \"phi\",\n \"pi\",\n \"pi1\",\n \"pi2\",\n \"pi3\",\n \"pr\",\n \"pmo\",\n \"pm\",\n \"pmc\",\n \"pmr\",\n \"po\",\n \"q\",\n \"q1\",\n \"q2\",\n \"q3\",\n \"q4\",\n \"qc\",\n \"qr\",\n \"qd\",\n \"qm\",\n \"qm1\",\n \"qm2\",\n \"qm3\",\n \"cls\",\n \"tr\",\n \"th1\",\n \"th2\",\n \"th3\",\n \"th4\",\n \"thr1\",\n \"thr2\",\n \"thr3\",\n \"thr4\",\n \"tc1\",\n \"tc2\",\n \"tc3\",\n \"tc4\",\n \"tcr1\",\n \"tcr2\",\n \"tcr3\",\n \"tcr4\",\n \"f\",\n \"fe\",\n \"NEST\",\n ],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"For a proper name\",\n fontSize: 12,\n bold: true,\n underline: true,\n },\n png: {\n marker: \"png\",\n styleType: \"character\",\n endMarker: \"png*\",\n occursUnder: [\n \"ip\",\n \"im\",\n \"ipi\",\n \"imi\",\n \"ipq\",\n \"imq\",\n \"ipr\",\n \"iq\",\n \"iq1\",\n \"iq2\",\n \"iq3\",\n \"ili\",\n \"ili1\",\n \"ili2\",\n \"io\",\n \"io1\",\n \"io2\",\n \"io3\",\n \"io4\",\n \"ms\",\n \"ms1\",\n \"ms2\",\n \"s\",\n \"s1\",\n \"s2\",\n \"s3\",\n \"s4\",\n \"cd\",\n \"sp\",\n \"d\",\n \"lh\",\n \"li\",\n \"li1\",\n \"li2\",\n \"li3\",\n \"li4\",\n \"lf\",\n \"lim\",\n \"lim1\",\n \"lim2\",\n \"lim3\",\n \"lim4\",\n \"m\",\n \"mi\",\n \"nb\",\n \"p\",\n \"pc\",\n \"ph\",\n \"phi\",\n \"pi\",\n \"pi1\",\n \"pi2\",\n \"pi3\",\n \"pr\",\n \"pmo\",\n \"pm\",\n \"pmc\",\n \"pmr\",\n \"po\",\n \"q\",\n \"q1\",\n \"q2\",\n \"q3\",\n \"q4\",\n \"qc\",\n \"qr\",\n \"qd\",\n \"qm\",\n \"qm1\",\n \"qm2\",\n \"qm3\",\n \"cls\",\n \"tr\",\n \"th1\",\n \"th2\",\n \"th3\",\n \"th4\",\n \"thr1\",\n \"thr2\",\n \"thr3\",\n \"thr4\",\n \"tc1\",\n \"tc2\",\n \"tc3\",\n \"tc4\",\n \"tcr1\",\n \"tcr2\",\n \"tcr3\",\n \"tcr4\",\n \"f\",\n \"fe\",\n \"NEST\",\n ],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"For a geographic proper name\",\n fontSize: 12,\n underline: true,\n },\n addpn: {\n marker: \"addpn\",\n styleType: \"character\",\n endMarker: \"addpn*\",\n occursUnder: [\n \"ip\",\n \"im\",\n \"ipi\",\n \"imi\",\n \"ipq\",\n \"imq\",\n \"ipr\",\n \"iq\",\n \"iq1\",\n \"iq2\",\n \"iq3\",\n \"ili\",\n \"ili1\",\n \"ili2\",\n \"io\",\n \"io1\",\n \"io2\",\n \"io3\",\n \"io4\",\n \"ms\",\n \"ms1\",\n \"ms2\",\n \"s\",\n \"s1\",\n \"s2\",\n \"s3\",\n \"s4\",\n \"cd\",\n \"sp\",\n \"d\",\n \"lh\",\n \"li\",\n \"li1\",\n \"li2\",\n \"li3\",\n \"li4\",\n \"lf\",\n \"lim\",\n \"lim1\",\n \"lim2\",\n \"lim3\",\n \"lim4\",\n \"m\",\n \"mi\",\n \"nb\",\n \"p\",\n \"pc\",\n \"ph\",\n \"phi\",\n \"pi\",\n \"pi1\",\n \"pi2\",\n \"pi3\",\n \"pr\",\n \"pmo\",\n \"pm\",\n \"pmc\",\n \"pmr\",\n \"po\",\n \"q\",\n \"q1\",\n \"q2\",\n \"q3\",\n \"q4\",\n \"qc\",\n \"qr\",\n \"qd\",\n \"qm\",\n \"qm1\",\n \"qm2\",\n \"qm3\",\n \"cls\",\n \"tr\",\n \"th1\",\n \"th2\",\n \"th3\",\n \"th4\",\n \"thr1\",\n \"thr2\",\n \"thr3\",\n \"thr4\",\n \"tc1\",\n \"tc2\",\n \"tc3\",\n \"tc4\",\n \"tcr1\",\n \"tcr2\",\n \"tcr3\",\n \"tcr4\",\n \"f\",\n \"fe\",\n \"NEST\",\n ],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"For chinese words to be dot underline & underline\",\n fontSize: 12,\n bold: true,\n italic: true,\n underline: true,\n },\n wj: {\n marker: \"wj\",\n styleType: \"character\",\n endMarker: \"wj*\",\n occursUnder: [\n \"ip\",\n \"im\",\n \"ipi\",\n \"imi\",\n \"ipq\",\n \"imq\",\n \"ipr\",\n \"iq\",\n \"iq1\",\n \"iq2\",\n \"iq3\",\n \"ili\",\n \"ili1\",\n \"ili2\",\n \"io\",\n \"io1\",\n \"io2\",\n \"io3\",\n \"io4\",\n \"ms\",\n \"ms1\",\n \"ms2\",\n \"s\",\n \"s1\",\n \"s2\",\n \"s3\",\n \"s4\",\n \"cd\",\n \"sp\",\n \"d\",\n \"lh\",\n \"li\",\n \"li1\",\n \"li2\",\n \"li3\",\n \"li4\",\n \"lf\",\n \"lim\",\n \"lim1\",\n \"lim2\",\n \"lim3\",\n \"lim4\",\n \"m\",\n \"mi\",\n \"nb\",\n \"p\",\n \"pc\",\n \"ph\",\n \"phi\",\n \"pi\",\n \"pi1\",\n \"pi2\",\n \"pi3\",\n \"pr\",\n \"pmo\",\n \"pm\",\n \"pmc\",\n \"pmr\",\n \"po\",\n \"q\",\n \"q1\",\n \"q2\",\n \"q3\",\n \"q4\",\n \"qc\",\n \"qr\",\n \"qd\",\n \"qm\",\n \"qm1\",\n \"qm2\",\n \"qm3\",\n \"tr\",\n \"th1\",\n \"th2\",\n \"th3\",\n \"th4\",\n \"thr1\",\n \"thr2\",\n \"thr3\",\n \"thr4\",\n \"tc1\",\n \"tc2\",\n \"tc3\",\n \"tc4\",\n \"tcr1\",\n \"tcr2\",\n \"tcr3\",\n \"tcr4\",\n \"f\",\n \"fe\",\n \"NEST\",\n ],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"For marking the words of Jesus\",\n fontSize: 12,\n color: \"#FF0000\",\n },\n k: {\n marker: \"k\",\n styleType: \"character\",\n endMarker: \"k*\",\n occursUnder: [\n \"ip\",\n \"im\",\n \"ipi\",\n \"imi\",\n \"ipq\",\n \"imq\",\n \"ipr\",\n \"iq\",\n \"iq1\",\n \"iq2\",\n \"iq3\",\n \"ili\",\n \"ili1\",\n \"ili2\",\n \"io\",\n \"io1\",\n \"io2\",\n \"io3\",\n \"io4\",\n \"ms\",\n \"ms1\",\n \"ms2\",\n \"s\",\n \"s1\",\n \"s2\",\n \"s3\",\n \"s4\",\n \"cd\",\n \"sp\",\n \"d\",\n \"lh\",\n \"li\",\n \"li1\",\n \"li2\",\n \"li3\",\n \"li4\",\n \"lf\",\n \"lim\",\n \"lim1\",\n \"lim2\",\n \"lim3\",\n \"lim4\",\n \"m\",\n \"mi\",\n \"nb\",\n \"p\",\n \"pc\",\n \"ph\",\n \"phi\",\n \"pi\",\n \"pi1\",\n \"pi2\",\n \"pi3\",\n \"pr\",\n \"pmo\",\n \"pm\",\n \"pmc\",\n \"pmr\",\n \"po\",\n \"q\",\n \"q1\",\n \"q2\",\n \"q3\",\n \"q4\",\n \"qc\",\n \"qr\",\n \"qd\",\n \"qm\",\n \"qm1\",\n \"qm2\",\n \"qm3\",\n \"tr\",\n \"th1\",\n \"th2\",\n \"th3\",\n \"th4\",\n \"thr1\",\n \"thr2\",\n \"thr3\",\n \"thr4\",\n \"tc1\",\n \"tc2\",\n \"tc3\",\n \"tc4\",\n \"tcr1\",\n \"tcr2\",\n \"tcr3\",\n \"tcr4\",\n \"f\",\n \"fe\",\n \"NEST\",\n ],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"For a keyword\",\n fontSize: 12,\n bold: true,\n italic: true,\n },\n sls: {\n marker: \"sls\",\n styleType: \"character\",\n endMarker: \"sls*\",\n occursUnder: [\n \"lh\",\n \"li\",\n \"li1\",\n \"li2\",\n \"li3\",\n \"li4\",\n \"lf\",\n \"lim\",\n \"lim1\",\n \"lim2\",\n \"lim3\",\n \"lim4\",\n \"m\",\n \"mi\",\n \"nb\",\n \"p\",\n \"pc\",\n \"ph\",\n \"phi\",\n \"pi\",\n \"pi1\",\n \"pi2\",\n \"pi3\",\n \"pr\",\n \"pmo\",\n \"pm\",\n \"pmc\",\n \"pmr\",\n \"po\",\n \"q\",\n \"q1\",\n \"q2\",\n \"q3\",\n \"q4\",\n \"qc\",\n \"qr\",\n \"qd\",\n \"qm\",\n \"qm1\",\n \"qm2\",\n \"qm3\",\n \"sp\",\n \"tr\",\n \"th1\",\n \"th2\",\n \"th3\",\n \"th4\",\n \"thr1\",\n \"thr2\",\n \"thr3\",\n \"thr4\",\n \"tc1\",\n \"tc2\",\n \"tc3\",\n \"tc4\",\n \"tcr1\",\n \"tcr2\",\n \"tcr3\",\n \"tcr4\",\n \"f\",\n \"fe\",\n \"NEST\",\n ],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"To represent where the original text is in a secondary language or from an alternate text source\",\n fontSize: 12,\n italic: true,\n },\n ord: {\n marker: \"ord\",\n styleType: \"character\",\n endMarker: \"ord*\",\n occursUnder: [\n \"ip\",\n \"im\",\n \"ipi\",\n \"imi\",\n \"ipq\",\n \"imq\",\n \"ipr\",\n \"iq\",\n \"iq1\",\n \"iq2\",\n \"iq3\",\n \"ili\",\n \"ili1\",\n \"ili2\",\n \"io\",\n \"io1\",\n \"io2\",\n \"io3\",\n \"io4\",\n \"ms\",\n \"ms1\",\n \"ms2\",\n \"s\",\n \"s1\",\n \"s2\",\n \"s3\",\n \"s4\",\n \"cd\",\n \"sp\",\n \"d\",\n \"lh\",\n \"li\",\n \"li1\",\n \"li2\",\n \"li3\",\n \"li4\",\n \"lf\",\n \"lim\",\n \"lim1\",\n \"lim2\",\n \"lim3\",\n \"lim4\",\n \"m\",\n \"mi\",\n \"nb\",\n \"p\",\n \"pc\",\n \"ph\",\n \"phi\",\n \"pi\",\n \"pi1\",\n \"pi2\",\n \"pi3\",\n \"pr\",\n \"pmo\",\n \"pm\",\n \"pmc\",\n \"pmr\",\n \"po\",\n \"q\",\n \"q1\",\n \"q2\",\n \"q3\",\n \"q4\",\n \"qc\",\n \"qr\",\n \"qd\",\n \"qm\",\n \"qm1\",\n \"qm2\",\n \"qm3\",\n \"tr\",\n \"th1\",\n \"th2\",\n \"th3\",\n \"th4\",\n \"thr1\",\n \"thr2\",\n \"thr3\",\n \"thr4\",\n \"tc1\",\n \"tc2\",\n \"tc3\",\n \"tc4\",\n \"tcr1\",\n \"tcr2\",\n \"tcr3\",\n \"tcr4\",\n \"f\",\n \"fe\",\n \"NEST\",\n ],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"For the text portion of an ordinal number\",\n fontSize: 12,\n superscript: true,\n },\n add: {\n marker: \"add\",\n styleType: \"character\",\n endMarker: \"add*\",\n occursUnder: [\n \"ip\",\n \"im\",\n \"ipi\",\n \"imi\",\n \"ipq\",\n \"imq\",\n \"ipr\",\n \"iq\",\n \"iq1\",\n \"iq2\",\n \"iq3\",\n \"ili\",\n \"ili1\",\n \"ili2\",\n \"io\",\n \"io1\",\n \"io2\",\n \"io3\",\n \"io4\",\n \"ms\",\n \"ms1\",\n \"ms2\",\n \"s\",\n \"s1\",\n \"s2\",\n \"s3\",\n \"s4\",\n \"cd\",\n \"sp\",\n \"d\",\n \"lh\",\n \"li\",\n \"li1\",\n \"li2\",\n \"li3\",\n \"li4\",\n \"lf\",\n \"lim\",\n \"lim1\",\n \"lim2\",\n \"lim3\",\n \"lim4\",\n \"m\",\n \"mi\",\n \"nb\",\n \"p\",\n \"pc\",\n \"ph\",\n \"phi\",\n \"pi\",\n \"pi1\",\n \"pi2\",\n \"pi3\",\n \"pr\",\n \"pmo\",\n \"pm\",\n \"pmc\",\n \"pmr\",\n \"po\",\n \"q\",\n \"q1\",\n \"q2\",\n \"q3\",\n \"q4\",\n \"qc\",\n \"qr\",\n \"qd\",\n \"qm\",\n \"qm1\",\n \"qm2\",\n \"qm3\",\n \"cls\",\n \"tr\",\n \"th1\",\n \"th2\",\n \"th3\",\n \"th4\",\n \"thr1\",\n \"thr2\",\n \"thr3\",\n \"thr4\",\n \"tc1\",\n \"tc2\",\n \"tc3\",\n \"tc4\",\n \"tcr1\",\n \"tcr2\",\n \"tcr3\",\n \"tcr4\",\n \"f\",\n \"fe\",\n \"NEST\",\n ],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"For a translational addition to the text\",\n bold: true,\n italic: true,\n },\n lit: {\n marker: \"lit\",\n styleType: \"paragraph\",\n occursUnder: [\"c\"],\n textType: \"Other\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\"],\n description: \"For a comment or note inserted for liturgical use\",\n fontSize: 12,\n bold: true,\n justification: \"right\",\n },\n no: {\n marker: \"no\",\n styleType: \"character\",\n endMarker: \"no*\",\n occursUnder: [\n \"is\",\n \"ip\",\n \"ipi\",\n \"im\",\n \"imi\",\n \"ili\",\n \"ili1\",\n \"ili2\",\n \"imq\",\n \"ipq\",\n \"iex\",\n \"iq\",\n \"iot\",\n \"io1\",\n \"io2\",\n \"io3\",\n \"io4\",\n \"s\",\n \"s1\",\n \"s2\",\n \"s3\",\n \"NEST\",\n ],\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A character style, use normal text\",\n fontSize: 12,\n },\n it: {\n marker: \"it\",\n styleType: \"character\",\n endMarker: \"it*\",\n occursUnder: [\n \"ip\",\n \"im\",\n \"ipi\",\n \"imi\",\n \"ipq\",\n \"imq\",\n \"ipr\",\n \"iq\",\n \"iq1\",\n \"iq2\",\n \"iq3\",\n \"ili\",\n \"ili1\",\n \"ili2\",\n \"io\",\n \"io1\",\n \"io2\",\n \"io3\",\n \"io4\",\n \"ms\",\n \"ms1\",\n \"ms2\",\n \"s\",\n \"s1\",\n \"s2\",\n \"s3\",\n \"s4\",\n \"cd\",\n \"sp\",\n \"d\",\n \"lh\",\n \"li\",\n \"li1\",\n \"li2\",\n \"li3\",\n \"li4\",\n \"lf\",\n \"lim\",\n \"lim1\",\n \"lim2\",\n \"lim3\",\n \"lim4\",\n \"m\",\n \"mi\",\n \"nb\",\n \"p\",\n \"pc\",\n \"ph\",\n \"phi\",\n \"pi\",\n \"pi1\",\n \"pi2\",\n \"pi3\",\n \"pr\",\n \"pmo\",\n \"pm\",\n \"pmc\",\n \"pmr\",\n \"po\",\n \"q\",\n \"q1\",\n \"q2\",\n \"q3\",\n \"q4\",\n \"qc\",\n \"qr\",\n \"qd\",\n \"qm\",\n \"qm1\",\n \"qm2\",\n \"qm3\",\n \"tr\",\n \"th1\",\n \"th2\",\n \"th3\",\n \"th4\",\n \"thr1\",\n \"thr2\",\n \"thr3\",\n \"thr4\",\n \"tc1\",\n \"tc2\",\n \"tc3\",\n \"tc4\",\n \"tcr1\",\n \"tcr2\",\n \"tcr3\",\n \"tcr4\",\n \"f\",\n \"fe\",\n \"x\",\n \"NEST\",\n ],\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A character style, use italic text\",\n fontSize: 12,\n italic: true,\n },\n bd: {\n marker: \"bd\",\n styleType: \"character\",\n endMarker: \"bd*\",\n occursUnder: [\n \"ip\",\n \"im\",\n \"ipi\",\n \"imi\",\n \"ipq\",\n \"imq\",\n \"ipr\",\n \"iq\",\n \"iq1\",\n \"iq2\",\n \"iq3\",\n \"ili\",\n \"ili1\",\n \"ili2\",\n \"io\",\n \"io1\",\n \"io2\",\n \"io3\",\n \"io4\",\n \"ms\",\n \"ms1\",\n \"ms2\",\n \"s\",\n \"s1\",\n \"s2\",\n \"s3\",\n \"s4\",\n \"cd\",\n \"sp\",\n \"d\",\n \"lh\",\n \"li\",\n \"li1\",\n \"li2\",\n \"li3\",\n \"li4\",\n \"lf\",\n \"lim\",\n \"lim1\",\n \"lim2\",\n \"lim3\",\n \"lim4\",\n \"m\",\n \"mi\",\n \"nb\",\n \"p\",\n \"pc\",\n \"ph\",\n \"phi\",\n \"pi\",\n \"pi1\",\n \"pi2\",\n \"pi3\",\n \"pr\",\n \"pmo\",\n \"pm\",\n \"pmc\",\n \"pmr\",\n \"po\",\n \"q\",\n \"q1\",\n \"q2\",\n \"q3\",\n \"q4\",\n \"qc\",\n \"qr\",\n \"qd\",\n \"qm\",\n \"qm1\",\n \"qm2\",\n \"qm3\",\n \"tr\",\n \"th1\",\n \"th2\",\n \"th3\",\n \"th4\",\n \"thr1\",\n \"thr2\",\n \"thr3\",\n \"thr4\",\n \"tc1\",\n \"tc2\",\n \"tc3\",\n \"tc4\",\n \"tcr1\",\n \"tcr2\",\n \"tcr3\",\n \"tcr4\",\n \"f\",\n \"fe\",\n \"x\",\n \"NEST\",\n ],\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A character style, use bold text\",\n fontSize: 12,\n bold: true,\n },\n bdit: {\n marker: \"bdit\",\n styleType: \"character\",\n endMarker: \"bdit*\",\n occursUnder: [\n \"ip\",\n \"im\",\n \"ipi\",\n \"imi\",\n \"ipq\",\n \"imq\",\n \"ipr\",\n \"iq\",\n \"iq1\",\n \"iq2\",\n \"iq3\",\n \"ili\",\n \"ili1\",\n \"ili2\",\n \"io\",\n \"io1\",\n \"io2\",\n \"io3\",\n \"io4\",\n \"ms\",\n \"ms1\",\n \"ms2\",\n \"s\",\n \"s1\",\n \"s2\",\n \"s3\",\n \"s4\",\n \"cd\",\n \"sp\",\n \"d\",\n \"lh\",\n \"li\",\n \"li1\",\n \"li2\",\n \"li3\",\n \"li4\",\n \"lf\",\n \"lim\",\n \"lim1\",\n \"lim2\",\n \"lim3\",\n \"lim4\",\n \"m\",\n \"mi\",\n \"nb\",\n \"p\",\n \"pc\",\n \"ph\",\n \"phi\",\n \"pi\",\n \"pi1\",\n \"pi2\",\n \"pi3\",\n \"pr\",\n \"pmo\",\n \"pm\",\n \"pmc\",\n \"pmr\",\n \"po\",\n \"q\",\n \"q1\",\n \"q2\",\n \"q3\",\n \"q4\",\n \"qc\",\n \"qr\",\n \"qd\",\n \"qm\",\n \"qm1\",\n \"qm2\",\n \"qm3\",\n \"tr\",\n \"th1\",\n \"th2\",\n \"th3\",\n \"th4\",\n \"thr1\",\n \"thr2\",\n \"thr3\",\n \"thr4\",\n \"tc1\",\n \"tc2\",\n \"tc3\",\n \"tc4\",\n \"tcr1\",\n \"tcr2\",\n \"tcr3\",\n \"tcr4\",\n \"f\",\n \"fe\",\n \"x\",\n \"NEST\",\n ],\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A character style, use bold + italic text\",\n fontSize: 12,\n bold: true,\n italic: true,\n },\n em: {\n marker: \"em\",\n styleType: \"character\",\n endMarker: \"em*\",\n occursUnder: [\n \"ip\",\n \"im\",\n \"ipi\",\n \"imi\",\n \"ipq\",\n \"imq\",\n \"ipr\",\n \"iq\",\n \"iq1\",\n \"iq2\",\n \"iq3\",\n \"ili\",\n \"ili1\",\n \"ili2\",\n \"io\",\n \"io1\",\n \"io2\",\n \"io3\",\n \"io4\",\n \"ms\",\n \"ms1\",\n \"ms2\",\n \"s\",\n \"s1\",\n \"s2\",\n \"s3\",\n \"s4\",\n \"cd\",\n \"sp\",\n \"d\",\n \"lh\",\n \"li\",\n \"li1\",\n \"li2\",\n \"li3\",\n \"li4\",\n \"lf\",\n \"lim\",\n \"lim1\",\n \"lim2\",\n \"lim3\",\n \"lim4\",\n \"m\",\n \"mi\",\n \"nb\",\n \"p\",\n \"pc\",\n \"ph\",\n \"phi\",\n \"pi\",\n \"pi1\",\n \"pi2\",\n \"pi3\",\n \"pr\",\n \"pmo\",\n \"pm\",\n \"pmc\",\n \"pmr\",\n \"po\",\n \"q\",\n \"q1\",\n \"q2\",\n \"q3\",\n \"q4\",\n \"qc\",\n \"qr\",\n \"qd\",\n \"qm\",\n \"qm1\",\n \"qm2\",\n \"qm3\",\n \"tr\",\n \"th1\",\n \"th2\",\n \"th3\",\n \"th4\",\n \"thr1\",\n \"thr2\",\n \"thr3\",\n \"thr4\",\n \"tc1\",\n \"tc2\",\n \"tc3\",\n \"tc4\",\n \"tcr1\",\n \"tcr2\",\n \"tcr3\",\n \"tcr4\",\n \"f\",\n \"fe\",\n \"x\",\n \"NEST\",\n ],\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A character style, use emphasized text style\",\n fontSize: 12,\n italic: true,\n },\n sc: {\n marker: \"sc\",\n styleType: \"character\",\n endMarker: \"sc*\",\n occursUnder: [\n \"ip\",\n \"im\",\n \"ipi\",\n \"imi\",\n \"ipq\",\n \"imq\",\n \"ipr\",\n \"iq\",\n \"iq1\",\n \"iq2\",\n \"iq3\",\n \"ili\",\n \"ili1\",\n \"ili2\",\n \"io\",\n \"io1\",\n \"io2\",\n \"io3\",\n \"io4\",\n \"ms\",\n \"ms1\",\n \"ms2\",\n \"s\",\n \"s1\",\n \"s2\",\n \"s3\",\n \"s4\",\n \"cd\",\n \"sp\",\n \"d\",\n \"lh\",\n \"li\",\n \"li1\",\n \"li2\",\n \"li3\",\n \"li4\",\n \"lf\",\n \"lim\",\n \"lim1\",\n \"lim2\",\n \"lim3\",\n \"lim4\",\n \"m\",\n \"mi\",\n \"nb\",\n \"p\",\n \"pc\",\n \"ph\",\n \"phi\",\n \"pi\",\n \"pi1\",\n \"pi2\",\n \"pi3\",\n \"pr\",\n \"pmo\",\n \"pm\",\n \"pmc\",\n \"pmr\",\n \"po\",\n \"q\",\n \"q1\",\n \"q2\",\n \"q3\",\n \"q4\",\n \"qc\",\n \"qr\",\n \"qd\",\n \"qm\",\n \"qm1\",\n \"qm2\",\n \"qm3\",\n \"tr\",\n \"th1\",\n \"th2\",\n \"th3\",\n \"th4\",\n \"thr1\",\n \"thr2\",\n \"thr3\",\n \"thr4\",\n \"tc1\",\n \"tc2\",\n \"tc3\",\n \"tc4\",\n \"tcr1\",\n \"tcr2\",\n \"tcr3\",\n \"tcr4\",\n \"f\",\n \"fe\",\n \"x\",\n \"NEST\",\n ],\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A character style, for small capitalization text\",\n fontSize: 12,\n smallCaps: true,\n },\n sup: {\n marker: \"sup\",\n styleType: \"character\",\n endMarker: \"sup*\",\n occursUnder: [\n \"ip\",\n \"im\",\n \"ipi\",\n \"imi\",\n \"ipq\",\n \"imq\",\n \"ipr\",\n \"iq\",\n \"iq1\",\n \"iq2\",\n \"iq3\",\n \"ili\",\n \"ili1\",\n \"ili2\",\n \"io\",\n \"io1\",\n \"io2\",\n \"io3\",\n \"io4\",\n \"ms\",\n \"ms1\",\n \"ms2\",\n \"s\",\n \"s1\",\n \"s2\",\n \"s3\",\n \"s4\",\n \"cd\",\n \"sp\",\n \"d\",\n \"lh\",\n \"li\",\n \"li1\",\n \"li2\",\n \"li3\",\n \"li4\",\n \"lf\",\n \"lim\",\n \"lim1\",\n \"lim2\",\n \"lim3\",\n \"lim4\",\n \"m\",\n \"mi\",\n \"nb\",\n \"p\",\n \"pc\",\n \"ph\",\n \"phi\",\n \"pi\",\n \"pi1\",\n \"pi2\",\n \"pi3\",\n \"pr\",\n \"pmo\",\n \"pm\",\n \"pmc\",\n \"pmr\",\n \"po\",\n \"q\",\n \"q1\",\n \"q2\",\n \"q3\",\n \"q4\",\n \"qc\",\n \"qr\",\n \"qd\",\n \"qm\",\n \"qm1\",\n \"qm2\",\n \"qm3\",\n \"tr\",\n \"th1\",\n \"th2\",\n \"th3\",\n \"th4\",\n \"thr1\",\n \"thr2\",\n \"thr3\",\n \"thr4\",\n \"tc1\",\n \"tc2\",\n \"tc3\",\n \"tc4\",\n \"tcr1\",\n \"tcr2\",\n \"tcr3\",\n \"tcr4\",\n \"f\",\n \"fe\",\n \"x\",\n \"NEST\",\n ],\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A character style, for superscript text. Typically for use in critical edition footnotes.\",\n fontSize: 12,\n superscript: true,\n },\n pb: {\n marker: \"pb\",\n styleType: \"paragraph\",\n occursUnder: [\"c\"],\n rank: 4,\n textType: \"Other\",\n textProperties: [\"publishable\"],\n description: \"Page Break used for new reader portions and children's bibles where content is controlled by the page\",\n fontSize: 12,\n },\n fig: {\n marker: \"fig\",\n styleType: \"character\",\n endMarker: \"fig*\",\n occursUnder: [\n \"lh\",\n \"li\",\n \"li1\",\n \"li2\",\n \"li3\",\n \"li4\",\n \"lf\",\n \"lim\",\n \"lim1\",\n \"lim2\",\n \"lim3\",\n \"lim4\",\n \"m\",\n \"mi\",\n \"nb\",\n \"p\",\n \"pc\",\n \"ph\",\n \"phi\",\n \"pi\",\n \"pi1\",\n \"pi2\",\n \"pi3\",\n \"pr\",\n \"pmo\",\n \"pm\",\n \"pmc\",\n \"pmr\",\n \"po\",\n \"q\",\n \"q1\",\n \"q2\",\n \"q3\",\n \"q4\",\n \"qc\",\n \"qr\",\n \"qd\",\n \"qm\",\n \"qm1\",\n \"qm2\",\n \"qm3\",\n \"sp\",\n \"tr\",\n \"th1\",\n \"th2\",\n \"th3\",\n \"th4\",\n \"thr1\",\n \"thr2\",\n \"thr3\",\n \"thr4\",\n \"tc1\",\n \"tc2\",\n \"tc3\",\n \"tc4\",\n \"tcr1\",\n \"tcr2\",\n \"tcr3\",\n \"tcr4\",\n \"ms\",\n \"ms1\",\n \"ms2\",\n \"s\",\n \"s1\",\n \"s2\",\n \"s3\",\n \"d\",\n \"ip\",\n ],\n textType: \"Other\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\"],\n description: \"Illustration [Columns to span, height, filename, caption text]\",\n fontSize: 12,\n },\n jmp: {\n marker: \"jmp\",\n styleType: \"character\",\n endMarker: \"jmp*\",\n occursUnder: [\n \"ip\",\n \"im\",\n \"ipi\",\n \"imi\",\n \"ipq\",\n \"imq\",\n \"ipr\",\n \"iq\",\n \"iq1\",\n \"iq2\",\n \"iq3\",\n \"ili\",\n \"ili1\",\n \"ili2\",\n \"io\",\n \"io1\",\n \"io2\",\n \"io3\",\n \"io4\",\n \"ms\",\n \"ms1\",\n \"ms2\",\n \"s\",\n \"s1\",\n \"s2\",\n \"s3\",\n \"s4\",\n \"cd\",\n \"sp\",\n \"d\",\n \"lh\",\n \"li\",\n \"li1\",\n \"li2\",\n \"li3\",\n \"li4\",\n \"lf\",\n \"lim\",\n \"lim1\",\n \"lim2\",\n \"lim3\",\n \"lim4\",\n \"m\",\n \"mi\",\n \"nb\",\n \"p\",\n \"pc\",\n \"ph\",\n \"phi\",\n \"pi\",\n \"pi1\",\n \"pi2\",\n \"pi3\",\n \"pr\",\n \"pmo\",\n \"pm\",\n \"pmc\",\n \"pmr\",\n \"po\",\n \"q\",\n \"q1\",\n \"q2\",\n \"q3\",\n \"q4\",\n \"qc\",\n \"qr\",\n \"qd\",\n \"qm\",\n \"qm1\",\n \"qm2\",\n \"qm3\",\n \"tr\",\n \"th1\",\n \"th2\",\n \"th3\",\n \"th4\",\n \"thr1\",\n \"thr2\",\n \"thr3\",\n \"thr4\",\n \"tc1\",\n \"tc2\",\n \"tc3\",\n \"tc4\",\n \"tcr1\",\n \"tcr2\",\n \"tcr3\",\n \"tcr4\",\n \"f\",\n \"fe\",\n \"x\",\n \"NEST\",\n ],\n textType: \"Other\",\n description: \"For associating linking attributes to a span of text\",\n underline: true,\n color: \"#0000FF\",\n },\n pro: {\n marker: \"pro\",\n styleType: \"character\",\n endMarker: \"pro*\",\n occursUnder: [\n \"ip\",\n \"im\",\n \"ipi\",\n \"imi\",\n \"ipq\",\n \"imq\",\n \"ipr\",\n \"iq\",\n \"iq1\",\n \"iq2\",\n \"iq3\",\n \"ili\",\n \"ili1\",\n \"ili2\",\n \"io\",\n \"io1\",\n \"io2\",\n \"io3\",\n \"io4\",\n \"lh\",\n \"li\",\n \"li1\",\n \"li2\",\n \"li3\",\n \"li4\",\n \"lf\",\n \"lim\",\n \"lim1\",\n \"lim2\",\n \"lim3\",\n \"lim4\",\n \"m\",\n \"mi\",\n \"nb\",\n \"p\",\n \"pc\",\n \"ph\",\n \"phi\",\n \"pi\",\n \"pi1\",\n \"pi2\",\n \"pi3\",\n \"pr\",\n \"pmo\",\n \"pm\",\n \"pmc\",\n \"pmr\",\n \"po\",\n \"q\",\n \"q1\",\n \"q2\",\n \"q3\",\n \"q4\",\n \"qc\",\n \"qr\",\n \"qd\",\n \"qm\",\n \"qm1\",\n \"qm2\",\n \"qm3\",\n \"sp\",\n \"tr\",\n \"th1\",\n \"th2\",\n \"th3\",\n \"th4\",\n \"thr1\",\n \"thr2\",\n \"thr3\",\n \"thr4\",\n \"tc1\",\n \"tc2\",\n \"tc3\",\n \"tc4\",\n \"tcr1\",\n \"tcr2\",\n \"tcr3\",\n \"tcr4\",\n \"ms\",\n \"ms1\",\n \"ms2\",\n \"s\",\n \"s1\",\n \"s2\",\n \"s3\",\n \"d\",\n \"ip\",\n \"f\",\n \"fe\",\n \"NEST\",\n ],\n textType: \"Other\",\n textProperties: [\"Nonpublishable\"],\n description: \"For indicating pronunciation in CJK texts\",\n fontSize: 10,\n },\n rb: {\n marker: \"rb\",\n styleType: \"character\",\n endMarker: \"rb*\",\n occursUnder: [\n \"ip\",\n \"im\",\n \"ipi\",\n \"imi\",\n \"ipq\",\n \"imq\",\n \"ipr\",\n \"iq\",\n \"iq1\",\n \"iq2\",\n \"iq3\",\n \"ili\",\n \"ili1\",\n \"ili2\",\n \"io\",\n \"io1\",\n \"io2\",\n \"io3\",\n \"io4\",\n \"lh\",\n \"li\",\n \"li1\",\n \"li2\",\n \"li3\",\n \"li4\",\n \"lf\",\n \"lim\",\n \"lim1\",\n \"lim2\",\n \"lim3\",\n \"lim4\",\n \"m\",\n \"mi\",\n \"nb\",\n \"p\",\n \"pc\",\n \"ph\",\n \"phi\",\n \"pi\",\n \"pi1\",\n \"pi2\",\n \"pi3\",\n \"pr\",\n \"pmo\",\n \"pm\",\n \"pmc\",\n \"pmr\",\n \"po\",\n \"q\",\n \"q1\",\n \"q2\",\n \"q3\",\n \"q4\",\n \"qc\",\n \"qr\",\n \"qd\",\n \"qm\",\n \"qm1\",\n \"qm2\",\n \"qm3\",\n \"sp\",\n \"tr\",\n \"th1\",\n \"th2\",\n \"th3\",\n \"th4\",\n \"thr1\",\n \"thr2\",\n \"thr3\",\n \"thr4\",\n \"tc1\",\n \"tc2\",\n \"tc3\",\n \"tc4\",\n \"tcr1\",\n \"tcr2\",\n \"tcr3\",\n \"tcr4\",\n \"ms\",\n \"ms1\",\n \"ms2\",\n \"s\",\n \"s1\",\n \"s2\",\n \"s3\",\n \"d\",\n \"ip\",\n \"f\",\n \"fe\",\n \"NEST\",\n ],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"Most often used to provide a reading / pronunciation guide in ideographic scripts\",\n },\n w: {\n marker: \"w\",\n styleType: \"character\",\n endMarker: \"w*\",\n occursUnder: [\n \"ip\",\n \"im\",\n \"ipi\",\n \"imi\",\n \"ipq\",\n \"imq\",\n \"ipr\",\n \"iq\",\n \"iq1\",\n \"iq2\",\n \"iq3\",\n \"ili\",\n \"ili1\",\n \"ili2\",\n \"io\",\n \"io1\",\n \"io2\",\n \"io3\",\n \"io4\",\n \"ms\",\n \"ms1\",\n \"ms2\",\n \"s\",\n \"s1\",\n \"s2\",\n \"s3\",\n \"s4\",\n \"cd\",\n \"sp\",\n \"d\",\n \"lh\",\n \"li\",\n \"li1\",\n \"li2\",\n \"li3\",\n \"li4\",\n \"lf\",\n \"lim\",\n \"lim1\",\n \"lim2\",\n \"lim3\",\n \"lim4\",\n \"m\",\n \"mi\",\n \"nb\",\n \"p\",\n \"pc\",\n \"ph\",\n \"phi\",\n \"pi\",\n \"pi1\",\n \"pi2\",\n \"pi3\",\n \"pr\",\n \"pmo\",\n \"pm\",\n \"pmc\",\n \"pmr\",\n \"po\",\n \"q\",\n \"q1\",\n \"q2\",\n \"q3\",\n \"q4\",\n \"qc\",\n \"qr\",\n \"qd\",\n \"qm\",\n \"qm1\",\n \"qm2\",\n \"qm3\",\n \"tr\",\n \"th1\",\n \"th2\",\n \"th3\",\n \"th4\",\n \"thr1\",\n \"thr2\",\n \"thr3\",\n \"thr4\",\n \"tc1\",\n \"tc2\",\n \"tc3\",\n \"tc4\",\n \"tcr1\",\n \"tcr2\",\n \"tcr3\",\n \"tcr4\",\n \"f\",\n \"fe\",\n \"x\",\n \"NEST\",\n ],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A wordlist text item\",\n fontSize: 12,\n },\n wh: {\n marker: \"wh\",\n styleType: \"character\",\n endMarker: \"wh*\",\n occursUnder: [\n \"ip\",\n \"im\",\n \"ipi\",\n \"imi\",\n \"ipq\",\n \"imq\",\n \"ipr\",\n \"iq\",\n \"iq1\",\n \"iq2\",\n \"iq3\",\n \"ili\",\n \"ili1\",\n \"ili2\",\n \"io\",\n \"io1\",\n \"io2\",\n \"io3\",\n \"io4\",\n \"ms\",\n \"ms1\",\n \"ms2\",\n \"s\",\n \"s1\",\n \"s2\",\n \"s3\",\n \"s4\",\n \"cd\",\n \"sp\",\n \"d\",\n \"lh\",\n \"li\",\n \"li1\",\n \"li2\",\n \"li3\",\n \"li4\",\n \"lf\",\n \"lim\",\n \"lim1\",\n \"lim2\",\n \"lim3\",\n \"lim4\",\n \"m\",\n \"mi\",\n \"nb\",\n \"p\",\n \"pc\",\n \"ph\",\n \"phi\",\n \"pi\",\n \"pi1\",\n \"pi2\",\n \"pi3\",\n \"pr\",\n \"pmo\",\n \"pm\",\n \"pmc\",\n \"pmr\",\n \"po\",\n \"q\",\n \"q1\",\n \"q2\",\n \"q3\",\n \"q4\",\n \"qc\",\n \"qr\",\n \"qd\",\n \"qm\",\n \"qm1\",\n \"qm2\",\n \"qm3\",\n \"tr\",\n \"th1\",\n \"th2\",\n \"th3\",\n \"th4\",\n \"thr1\",\n \"thr2\",\n \"thr3\",\n \"thr4\",\n \"tc1\",\n \"tc2\",\n \"tc3\",\n \"tc4\",\n \"tcr1\",\n \"tcr2\",\n \"tcr3\",\n \"tcr4\",\n \"f\",\n \"fe\",\n \"x\",\n \"NEST\",\n ],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A Hebrew wordlist text item\",\n fontSize: 12,\n },\n wg: {\n marker: \"wg\",\n styleType: \"character\",\n endMarker: \"wg*\",\n occursUnder: [\n \"ip\",\n \"im\",\n \"ipi\",\n \"imi\",\n \"ipq\",\n \"imq\",\n \"ipr\",\n \"iq\",\n \"iq1\",\n \"iq2\",\n \"iq3\",\n \"ili\",\n \"ili1\",\n \"ili2\",\n \"io\",\n \"io1\",\n \"io2\",\n \"io3\",\n \"io4\",\n \"ms\",\n \"ms1\",\n \"ms2\",\n \"s\",\n \"s1\",\n \"s2\",\n \"s3\",\n \"s4\",\n \"cd\",\n \"sp\",\n \"d\",\n \"lh\",\n \"li\",\n \"li1\",\n \"li2\",\n \"li3\",\n \"li4\",\n \"lf\",\n \"lim\",\n \"lim1\",\n \"lim2\",\n \"lim3\",\n \"lim4\",\n \"m\",\n \"mi\",\n \"nb\",\n \"p\",\n \"pc\",\n \"ph\",\n \"phi\",\n \"pi\",\n \"pi1\",\n \"pi2\",\n \"pi3\",\n \"pr\",\n \"pmo\",\n \"pm\",\n \"pmc\",\n \"pmr\",\n \"po\",\n \"q\",\n \"q1\",\n \"q2\",\n \"q3\",\n \"q4\",\n \"qc\",\n \"qr\",\n \"qd\",\n \"qm\",\n \"qm1\",\n \"qm2\",\n \"qm3\",\n \"tr\",\n \"th1\",\n \"th2\",\n \"th3\",\n \"th4\",\n \"thr1\",\n \"thr2\",\n \"thr3\",\n \"thr4\",\n \"tc1\",\n \"tc2\",\n \"tc3\",\n \"tc4\",\n \"tcr1\",\n \"tcr2\",\n \"tcr3\",\n \"tcr4\",\n \"f\",\n \"fe\",\n \"x\",\n \"NEST\",\n ],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A Greek Wordlist text item\",\n fontSize: 12,\n },\n wa: {\n marker: \"wa\",\n styleType: \"character\",\n endMarker: \"wa*\",\n occursUnder: [\n \"ip\",\n \"im\",\n \"ipi\",\n \"imi\",\n \"ipq\",\n \"imq\",\n \"ipr\",\n \"iq\",\n \"iq1\",\n \"iq2\",\n \"iq3\",\n \"ili\",\n \"ili1\",\n \"ili2\",\n \"io\",\n \"io1\",\n \"io2\",\n \"io3\",\n \"io4\",\n \"ms\",\n \"ms1\",\n \"ms2\",\n \"s\",\n \"s1\",\n \"s2\",\n \"s3\",\n \"s4\",\n \"cd\",\n \"sp\",\n \"d\",\n \"lh\",\n \"li\",\n \"li1\",\n \"li2\",\n \"li3\",\n \"li4\",\n \"lf\",\n \"lim\",\n \"lim1\",\n \"lim2\",\n \"lim3\",\n \"lim4\",\n \"m\",\n \"mi\",\n \"nb\",\n \"p\",\n \"pc\",\n \"ph\",\n \"phi\",\n \"pi\",\n \"pi1\",\n \"pi2\",\n \"pi3\",\n \"pr\",\n \"pmo\",\n \"pm\",\n \"pmc\",\n \"pmr\",\n \"po\",\n \"q\",\n \"q1\",\n \"q2\",\n \"q3\",\n \"q4\",\n \"qc\",\n \"qr\",\n \"qd\",\n \"qm\",\n \"qm1\",\n \"qm2\",\n \"qm3\",\n \"tr\",\n \"th1\",\n \"th2\",\n \"th3\",\n \"th4\",\n \"thr1\",\n \"thr2\",\n \"thr3\",\n \"thr4\",\n \"tc1\",\n \"tc2\",\n \"tc3\",\n \"tc4\",\n \"tcr1\",\n \"tcr2\",\n \"tcr3\",\n \"tcr4\",\n \"f\",\n \"fe\",\n \"x\",\n \"NEST\",\n ],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"An Aramaic Wordlist text item\",\n fontSize: 12,\n },\n ndx: {\n marker: \"ndx\",\n styleType: \"character\",\n endMarker: \"ndx*\",\n occursUnder: [\n \"ip\",\n \"im\",\n \"ipi\",\n \"imi\",\n \"ipq\",\n \"imq\",\n \"ipr\",\n \"iq\",\n \"iq1\",\n \"iq2\",\n \"iq3\",\n \"ili\",\n \"ili1\",\n \"ili2\",\n \"io\",\n \"io1\",\n \"io2\",\n \"io3\",\n \"io4\",\n \"ms\",\n \"ms1\",\n \"ms2\",\n \"s\",\n \"s1\",\n \"s2\",\n \"s3\",\n \"s4\",\n \"cd\",\n \"sp\",\n \"d\",\n \"lh\",\n \"li\",\n \"li1\",\n \"li2\",\n \"li3\",\n \"li4\",\n \"lf\",\n \"lim\",\n \"lim1\",\n \"lim2\",\n \"lim3\",\n \"lim4\",\n \"m\",\n \"mi\",\n \"nb\",\n \"p\",\n \"pc\",\n \"ph\",\n \"phi\",\n \"pi\",\n \"pi1\",\n \"pi2\",\n \"pi3\",\n \"pr\",\n \"pmo\",\n \"pm\",\n \"pmc\",\n \"pmr\",\n \"po\",\n \"q\",\n \"q1\",\n \"q2\",\n \"q3\",\n \"q4\",\n \"qc\",\n \"qr\",\n \"qd\",\n \"qm\",\n \"qm1\",\n \"qm2\",\n \"qm3\",\n \"tr\",\n \"th1\",\n \"th2\",\n \"th3\",\n \"th4\",\n \"thr1\",\n \"thr2\",\n \"thr3\",\n \"thr4\",\n \"tc1\",\n \"tc2\",\n \"tc3\",\n \"tc4\",\n \"tcr1\",\n \"tcr2\",\n \"tcr3\",\n \"tcr4\",\n \"f\",\n \"fe\",\n \"x\",\n \"NEST\",\n ],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A subject index text item\",\n fontSize: 12,\n },\n periph: {\n marker: \"periph\",\n styleType: \"paragraph\",\n textType: \"Section\",\n textProperties: [\"paragraph\", \"nonpublishable\", \"vernacular\"],\n description: \"Peripheral content division marker which should be followed by an additional division argument/title.\",\n fontSize: 14,\n bold: true,\n color: \"#FF8000\",\n spaceBefore: 16,\n spaceAfter: 4,\n },\n p1: {\n marker: \"p1\",\n styleType: \"paragraph\",\n occursUnder: [\"id\"],\n rank: 1,\n textType: \"Other\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\"],\n description: \"Front or back matter text paragraph, level 1 (if multiple levels)\",\n fontSize: 12,\n firstLineIndent: 0.125,\n },\n p2: {\n marker: \"p2\",\n styleType: \"paragraph\",\n occursUnder: [\"id\"],\n rank: 1,\n textType: \"Other\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\"],\n description: \"Front or back matter text paragraph, level 2 (if multiple levels)\",\n fontSize: 12,\n firstLineIndent: 0.125,\n leftMargin: 0.125,\n },\n k1: {\n marker: \"k1\",\n styleType: \"paragraph\",\n occursUnder: [\"id\"],\n rank: 1,\n textType: \"Other\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\"],\n description: \"Concordance main entry text or keyword, level 1\",\n fontSize: 12,\n },\n k2: {\n marker: \"k2\",\n styleType: \"paragraph\",\n occursUnder: [\"id\"],\n rank: 1,\n textType: \"Other\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\"],\n description: \"Concordance main entry text or keyword, level 2\",\n fontSize: 12,\n },\n xtSee: {\n marker: \"xtSee\",\n styleType: \"character\",\n endMarker: \"xtSee*\",\n occursUnder: [\"p\"],\n textType: \"NoteText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"Concordance and Names Index markup for an alternate entry target reference.\",\n fontSize: 12,\n italic: true,\n color: \"#0000FF\",\n },\n xtSeeAlso: {\n marker: \"xtSeeAlso\",\n styleType: \"character\",\n endMarker: \"xtSeeAlso*\",\n occursUnder: [\"p\"],\n textType: \"Other\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"Concordance and Names Index markup for an additional entry target reference.\",\n fontSize: 12,\n italic: true,\n color: \"#0000FF\",\n },\n \"qt-s\": {\n marker: \"qt-s\",\n styleType: \"milestone\",\n endMarker: \"qt-e\",\n occursUnder: [\"id\"],\n description: \"Quotation start/end milestone, level 1 (if single level)\",\n },\n \"qt1-s\": {\n marker: \"qt1-s\",\n styleType: \"milestone\",\n endMarker: \"qt1-e\",\n occursUnder: [\"id\"],\n description: \"Quotation start/end milestone, level 1 (if multiple levels)\",\n },\n \"qt2-s\": {\n marker: \"qt2-s\",\n styleType: \"milestone\",\n endMarker: \"qt2-e\",\n occursUnder: [\"id\"],\n description: \"Quotation start/end milestone, level 2\",\n },\n \"qt3-s\": {\n marker: \"qt3-s\",\n styleType: \"milestone\",\n endMarker: \"qt3-e\",\n occursUnder: [\"id\"],\n description: \"Quotation start/end milestone, level 3\",\n },\n \"qt4-s\": {\n marker: \"qt4-s\",\n styleType: \"milestone\",\n endMarker: \"qt4-e\",\n occursUnder: [\"id\"],\n description: \"Quotation start/end milestone, level 4\",\n },\n \"qt5-s\": {\n marker: \"qt5-s\",\n styleType: \"milestone\",\n endMarker: \"qt5-e\",\n occursUnder: [\"id\"],\n description: \"Quotation start/end milestone, level 5\",\n },\n \"ts-s\": {\n marker: \"ts-s\",\n styleType: \"milestone\",\n endMarker: \"ts-e\",\n occursUnder: [\"id\"],\n description: \"Translator's section start/end milestone\",\n },\n ph: {\n marker: \"ph\",\n styleType: \"paragraph\",\n occursUnder: [\"c\"],\n rank: 4,\n textType: \"VerseText\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\"],\n description: \"Paragraph text, with level 1 hanging indent (if single level)\",\n fontSize: 12,\n firstLineIndent: -0.25,\n leftMargin: 0.5,\n },\n ph1: {\n marker: \"ph1\",\n styleType: \"paragraph\",\n occursUnder: [\"c\"],\n rank: 4,\n textType: \"VerseText\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\"],\n description: \"Paragraph text, with level 1 hanging indent (if multiple levels)\",\n fontSize: 12,\n firstLineIndent: -0.25,\n leftMargin: 0.5,\n },\n ph2: {\n marker: \"ph2\",\n styleType: \"paragraph\",\n occursUnder: [\"c\"],\n rank: 4,\n textType: \"VerseText\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\"],\n description: \"Paragraph text, with level 2 hanging indent\",\n fontSize: 12,\n firstLineIndent: -0.25,\n leftMargin: 0.75,\n },\n ph3: {\n marker: \"ph3\",\n styleType: \"paragraph\",\n occursUnder: [\"c\"],\n rank: 4,\n textType: \"VerseText\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\"],\n description: \"Paragraph text, with level 3 hanging indent\",\n fontSize: 12,\n firstLineIndent: -0.25,\n leftMargin: 1,\n },\n phi: {\n marker: \"phi\",\n styleType: \"paragraph\",\n occursUnder: [\"c\"],\n rank: 4,\n textType: \"VerseText\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\"],\n description: \"Paragraph text, indented with hanging indent\",\n leftMargin: 1,\n },\n tr1: {\n marker: \"tr1\",\n styleType: \"paragraph\",\n occursUnder: [\"c\"],\n rank: 4,\n textType: \"VerseText\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\"],\n description: \"A table Row\",\n fontSize: 12,\n firstLineIndent: -0.25,\n leftMargin: 0.5,\n },\n tr2: {\n marker: \"tr2\",\n styleType: \"paragraph\",\n occursUnder: [\"c\"],\n rank: 4,\n textType: \"VerseText\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\"],\n description: \"A table Row\",\n fontSize: 12,\n firstLineIndent: -0.25,\n leftMargin: 0.75,\n },\n ps: {\n marker: \"ps\",\n styleType: \"paragraph\",\n occursUnder: [\"c\"],\n rank: 4,\n textType: \"VerseText\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\"],\n description: \"Paragraph text, no break with next paragraph text at chapter boundary\",\n fontSize: 12,\n firstLineIndent: 0.125,\n },\n psi: {\n marker: \"psi\",\n styleType: \"paragraph\",\n occursUnder: [\"c\"],\n rank: 4,\n textType: \"VerseText\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\"],\n description: \"Paragraph text, indented, with no break with next paragraph text (at chapter boundary)\",\n fontSize: 12,\n firstLineIndent: 0.125,\n leftMargin: 0.25,\n rightMargin: 0.25,\n },\n fs: {\n marker: \"fs\",\n styleType: \"character\",\n endMarker: \"fs*\",\n occursUnder: [\"f\", \"fe\"],\n textType: \"NoteText\",\n textProperties: [\"publishable\", \"vernacular\", \"note\"],\n description: \"A summary text for the concept/idea/quotation from the scripture translation for which the note is being provided.\",\n fontSize: 12,\n italic: true,\n },\n wr: {\n marker: \"wr\",\n styleType: \"character\",\n endMarker: \"wr*\",\n occursUnder: [\n \"ms\",\n \"s\",\n \"lh\",\n \"li\",\n \"li1\",\n \"li2\",\n \"li3\",\n \"li4\",\n \"lf\",\n \"lim\",\n \"lim1\",\n \"lim2\",\n \"lim3\",\n \"lim4\",\n \"m\",\n \"mi\",\n \"nb\",\n \"p\",\n \"pc\",\n \"ph\",\n \"phi\",\n \"pi\",\n \"pi1\",\n \"pi2\",\n \"pi3\",\n \"pr\",\n \"po\",\n \"q\",\n \"q1\",\n \"q2\",\n \"q3\",\n \"q4\",\n \"qc\",\n \"qr\",\n \"qd\",\n \"tc1\",\n \"tc2\",\n \"tc3\",\n \"tc4\",\n \"f\",\n \"fe\",\n \"NEST\",\n ],\n textType: \"VerseText\",\n textProperties: [\"publishable\", \"vernacular\"],\n description: \"A Wordlist text item\",\n fontSize: 12,\n italic: true,\n },\n pub: {\n marker: \"pub\",\n styleType: \"paragraph\",\n occursUnder: [\"id\"],\n rank: 4,\n textType: \"VerseText\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"poetic\"],\n description: \"Front matter publication data\",\n fontSize: 10,\n },\n toc: {\n marker: \"toc\",\n styleType: \"paragraph\",\n occursUnder: [\"id\"],\n rank: 4,\n textType: \"VerseText\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"poetic\"],\n description: \"Front matter table of contents\",\n fontSize: 10,\n },\n pref: {\n marker: \"pref\",\n styleType: \"paragraph\",\n occursUnder: [\"id\"],\n rank: 4,\n textType: \"VerseText\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"poetic\"],\n description: \"Front matter preface\",\n fontSize: 10,\n },\n intro: {\n marker: \"intro\",\n styleType: \"paragraph\",\n occursUnder: [\"id\"],\n rank: 4,\n textType: \"VerseText\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"poetic\"],\n description: \"Front matter introduction\",\n fontSize: 10,\n },\n conc: {\n marker: \"conc\",\n styleType: \"paragraph\",\n occursUnder: [\"id\"],\n rank: 4,\n textType: \"VerseText\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"poetic\"],\n description: \"Back matter concordance\",\n fontSize: 10,\n },\n glo: {\n marker: \"glo\",\n styleType: \"paragraph\",\n occursUnder: [\"id\"],\n rank: 4,\n textType: \"VerseText\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"poetic\"],\n description: \"Back matter glossary\",\n fontSize: 10,\n },\n idx: {\n marker: \"idx\",\n styleType: \"paragraph\",\n occursUnder: [\"id\"],\n rank: 4,\n textType: \"VerseText\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"poetic\"],\n description: \"Back matter index\",\n fontSize: 10,\n },\n maps: {\n marker: \"maps\",\n styleType: \"paragraph\",\n occursUnder: [\"id\"],\n rank: 4,\n textType: \"VerseText\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"poetic\"],\n description: \"Back matter map index\",\n fontSize: 10,\n },\n cov: {\n marker: \"cov\",\n styleType: \"paragraph\",\n occursUnder: [\"id\"],\n rank: 4,\n textType: \"VerseText\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"poetic\"],\n description: \"Other peripheral materials - cover\",\n fontSize: 10,\n },\n spine: {\n marker: \"spine\",\n styleType: \"paragraph\",\n occursUnder: [\"id\"],\n rank: 4,\n textType: \"VerseText\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\", \"poetic\"],\n description: \"Other peripheral materials - spine\",\n fontSize: 10,\n },\n pubinfo: {\n marker: \"pubinfo\",\n styleType: \"paragraph\",\n occursUnder: [\"id\", \"ide\"],\n textType: \"Other\",\n textProperties: [\"paragraph\", \"nonpublishable\", \"nonvernacular\"],\n description: \"Publication information - Lang,Credit,Version,Copies,Publisher,Id,Logo\",\n fontSize: 12,\n color: \"#0000FF\",\n },\n \"zpa-xb\": {\n marker: \"zpa-xb\",\n styleType: \"character\",\n endMarker: \"zpa-xb*\",\n occursUnder: [\"id\"],\n rank: 1,\n textType: \"Other\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\"],\n description: \"Book Ref\",\n fontSize: 12,\n },\n \"zpa-xc\": {\n marker: \"zpa-xc\",\n styleType: \"character\",\n endMarker: \"zpa-xc*\",\n occursUnder: [\"id\"],\n rank: 1,\n textType: \"Other\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\"],\n description: \"Chapter Ref\",\n fontSize: 12,\n bold: true,\n },\n \"zpa-xv\": {\n marker: \"zpa-xv\",\n styleType: \"character\",\n endMarker: \"zpa-xv*\",\n occursUnder: [\"id\"],\n rank: 1,\n textType: \"Other\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\"],\n description: \"Verse Ref\",\n fontSize: 12,\n },\n \"zpa-d\": {\n marker: \"zpa-d\",\n styleType: \"character\",\n endMarker: \"zpa-d*\",\n occursUnder: [\"id\"],\n rank: 1,\n textType: \"Other\",\n textProperties: [\"paragraph\", \"publishable\", \"vernacular\"],\n description: \"Description\",\n fontSize: 12,\n },\n },\n};\n","/**\n * Project StyleInfo — the host-agnostic shape of a Paratext project's merged\n * stylesheet (usfm.sty + custom.sty).\n *\n * Unit conventions (match usfm.sty as parsed, not PT9's internal ints):\n * - fontSize, spaceBefore, spaceAfter: points\n * - firstLineIndent, leftMargin, rightMargin: inches (PT9 ScrTag stores\n * thousandths of an inch; hosts divide by 1000 when serializing)\n * - color: \"#RRGGBB\", omitted when black (PT9 CSSCreator skips black)\n * - lineSpacing: PT9 quirk — 1 renders as line-height 1.5, 2 as 2, else nothing\n */\nimport getMarker from \"./getMarker.js\";\nimport { CategoryType, MarkerType } from \"./usfmTypes.js\";\nconst STYLE_TYPE_TO_MARKER_TYPE = {\n paragraph: MarkerType.Paragraph,\n character: MarkerType.Character,\n note: MarkerType.Note,\n milestone: MarkerType.Milestone,\n};\n/**\n * StyleInfo-backed replacement for the bundled `getMarker`. With `styleInfo`,\n * the project sheet is authoritative for a marker's existence and\n * classification: markers absent from it return `undefined` (PT9: unknown to\n * the stylesheet), and `usfmMarkersOverwrites` never overrides those fields.\n * `children` (submenu structure) is editor data keyed by marker name, not\n * stylesheet data, so it still comes from the bundled path (table +\n * overwrites) — children-dependent consumers keep working. Without\n * `styleInfo`, the bundled `getMarker` is returned unchanged so non-project\n * consumers keep today's behavior exactly.\n */\nexport function createMarkerLookup(styleInfo) {\n if (!styleInfo)\n return getMarker;\n const cache = new Map();\n return (marker) => {\n if (cache.has(marker))\n return cache.get(marker);\n // Own-property guard: `markers` is a plain object off the wire, so a bare index resolves\n // Object.prototype members (`constructor`, `toString`, …) as if they were stylesheet\n // entries — handing back a Marker built from a Function.\n const entry = Object.hasOwn(styleInfo.markers, marker) ? styleInfo.markers[marker] : undefined;\n const result = entry\n ? {\n // Through `getMarker`, not the raw generated table: `usfmMarkersOverwrites` supplies\n // markers the generated data lacks (`w`, `rb`, `jmp`), and reading the table directly\n // demoted exactly those to Uncategorized whenever a project StyleInfo was active.\n category: getMarker(marker)?.category ?? CategoryType.Uncategorized,\n type: STYLE_TYPE_TO_MARKER_TYPE[entry.styleType] ?? MarkerType.Unknown,\n description: entry.description ?? \"\",\n hasEndMarker: Boolean(entry.endMarker),\n children: getMarker(marker)?.children,\n }\n : undefined;\n cache.set(marker, result);\n return result;\n };\n}\n","import { USJ_TYPE, USJ_VERSION, } from \"@eten-tech-foundation/scripture-utilities\";\nimport { isSerializedImpliedParaNode } from \"../../nodes/usj/ImpliedParaNode.js\";\nexport function createLexicalUsjNode(content, editorAdaptor, viewOptions) {\n const usj = {\n type: USJ_TYPE,\n version: USJ_VERSION,\n content,\n };\n const lexicalSerializedRoot = editorAdaptor.serializeEditorState(usj, viewOptions);\n const lexicalSerializedNode = isSerializedImpliedParaNode(lexicalSerializedRoot.root.children[0])\n ? lexicalSerializedRoot.root.children[0].children[0]\n : lexicalSerializedRoot.root.children[0];\n return lexicalSerializedNode;\n}\n","import { jsx as _jsx } from \"react/jsx-runtime\";\n/** Conforms with USJ v3.1 @see https://docs.usfm.bible/usfm/3.1/cv/v.html */\nimport { $applyNodeReplacement, DecoratorNode, isHTMLElement, } from \"lexical\";\nimport { useLexicalNodeSelection } from \"@lexical/react/useLexicalNodeSelection\";\nimport { getVisibleOpenMarkerText, isSelectionStartNodeExpectedError, VERSE_CLASS_NAME, ZWSP, } from \"shared\";\nexport const VERSE_MARKER = \"v\";\nexport const IMMUTABLE_VERSE_VERSION = 1;\n/**\n * Class applied to the rendered verse marker while it is in a NodeSelection (e.g. armed for the\n * two-step intentional delete). Themeable by the host app; a default selection-style background\n * ships in the platform stylesheet.\n */\nexport const VERSE_SELECTED_CLASS_NAME = \"verse-selected\";\nexport class ImmutableVerseNode extends DecoratorNode {\n __marker;\n __number;\n __showMarker;\n __sid;\n __altnumber;\n __pubnumber;\n __unknownAttributes;\n constructor(verseNumber = \"\", showMarker = false, sid, altnumber, pubnumber, unknownAttributes, key) {\n super(key);\n this.__marker = VERSE_MARKER;\n this.__number = verseNumber;\n this.__showMarker = showMarker;\n this.__sid = sid;\n this.__altnumber = altnumber;\n this.__pubnumber = pubnumber;\n this.__unknownAttributes = unknownAttributes;\n }\n static getType() {\n return \"immutable-verse\";\n }\n static clone(node) {\n const { __number, __showMarker, __sid, __altnumber, __pubnumber, __unknownAttributes, __key } = node;\n return new ImmutableVerseNode(__number, __showMarker, __sid, __altnumber, __pubnumber, __unknownAttributes, __key);\n }\n static importDOM() {\n return {\n span: (node) => {\n if (!isVerseElement(node))\n return null;\n return {\n conversion: $convertImmutableVerseElement,\n priority: 1,\n };\n },\n };\n }\n static importJSON(serializedNode) {\n return $createImmutableVerseNode().updateFromJSON(serializedNode);\n }\n updateFromJSON(serializedNode) {\n return super\n .updateFromJSON(serializedNode)\n .setMarker(serializedNode.marker)\n .setNumber(serializedNode.number)\n .setShowMarker(serializedNode.showMarker)\n .setSid(serializedNode.sid)\n .setAltnumber(serializedNode.altnumber)\n .setPubnumber(serializedNode.pubnumber)\n .setUnknownAttributes(serializedNode.unknownAttributes);\n }\n setMarker(marker) {\n if (this.__marker === marker)\n return this;\n const self = this.getWritable();\n self.__marker = marker;\n return self;\n }\n getMarker() {\n const self = this.getLatest();\n return self.__marker;\n }\n setNumber(verseNumber) {\n if (this.__number === verseNumber)\n return this;\n const self = this.getWritable();\n self.__number = verseNumber;\n return self;\n }\n getNumber() {\n const self = this.getLatest();\n return self.__number;\n }\n setShowMarker(showMarker = false) {\n if (this.__showMarker === showMarker)\n return this;\n const self = this.getWritable();\n self.__showMarker = showMarker;\n return self;\n }\n getShowMarker() {\n const self = this.getLatest();\n return self.__showMarker;\n }\n setSid(sid) {\n if (this.__sid === sid)\n return this;\n const self = this.getWritable();\n self.__sid = sid;\n return self;\n }\n getSid() {\n const self = this.getLatest();\n return self.__sid;\n }\n setAltnumber(altnumber) {\n if (this.__altnumber === altnumber)\n return this;\n const self = this.getWritable();\n self.__altnumber = altnumber;\n return self;\n }\n getAltnumber() {\n const self = this.getLatest();\n return self.__altnumber;\n }\n setPubnumber(pubnumber) {\n if (this.__pubnumber === pubnumber)\n return this;\n const self = this.getWritable();\n self.__pubnumber = pubnumber;\n return self;\n }\n getPubnumber() {\n const self = this.getLatest();\n return self.__pubnumber;\n }\n setUnknownAttributes(unknownAttributes) {\n const self = this.getWritable();\n self.__unknownAttributes = unknownAttributes;\n return self;\n }\n getUnknownAttributes() {\n const self = this.getLatest();\n return self.__unknownAttributes;\n }\n createDOM() {\n const dom = document.createElement(\"span\");\n dom.setAttribute(\"data-marker\", this.__marker);\n dom.classList.add(VERSE_CLASS_NAME, `usfm_${this.__marker}`);\n if (this.__showMarker)\n dom.classList.add(\"marker\");\n dom.setAttribute(\"data-number\", this.__number);\n return dom;\n }\n updateDOM() {\n // Returning false tells Lexical that this node does not need its\n // DOM element replacing with a new copy from createDOM.\n return false;\n }\n exportDOM(editor) {\n const { element } = super.exportDOM(editor);\n if (element && isHTMLElement(element)) {\n element.setAttribute(\"data-marker\", this.getMarker());\n element.classList.add(VERSE_CLASS_NAME, `usfm_${this.getMarker()}`);\n element.setAttribute(\"data-number\", this.getNumber());\n }\n return { element };\n }\n decorate() {\n const text = this.getShowMarker()\n ? getVisibleOpenMarkerText(this.getMarker(), this.getNumber())\n : // ZWSP added so double click word selection works without including this number.\n ZWSP + this.getNumber() + ZWSP;\n return _jsx(VerseDecorator, { nodeKey: this.getKey(), text: text });\n }\n exportJSON() {\n return {\n type: this.getType(),\n marker: this.getMarker(),\n number: this.getNumber(),\n showMarker: this.getShowMarker(),\n sid: this.getSid(),\n altnumber: this.getAltnumber(),\n pubnumber: this.getPubnumber(),\n unknownAttributes: this.getUnknownAttributes(),\n version: IMMUTABLE_VERSE_VERSION,\n };\n }\n isSelected(selection) {\n // The base implementation calls `selection.getNodes()`, which throws when a RangeSelection\n // has an element-type point anchored on this DecoratorNode (the \"cursor on the verse number\"\n // case). Treat that expected throw as \"not selected\", consistent with how\n // `getSelectionStartNode` handles the same selection shape.\n try {\n return super.isSelected(selection);\n }\n catch (err) {\n if (isSelectionStartNodeExpectedError(err))\n return false;\n throw err;\n }\n }\n // Mutation\n isKeyboardSelectable() {\n return false;\n }\n}\n/**\n * Renders the verse marker text and reflects its node-selection state. DecoratorNodes do not get\n * selection styling for free, so this subscribes to the node's selection and toggles\n * {@link VERSE_SELECTED_CLASS_NAME}.\n */\nfunction VerseDecorator({ nodeKey, text }) {\n const [isSelected] = useLexicalNodeSelection(nodeKey);\n // ZWSPs stay inside this span so double-click word selection still excludes the number.\n return _jsx(\"span\", { className: isSelected ? VERSE_SELECTED_CLASS_NAME : undefined, children: text });\n}\nfunction $convertImmutableVerseElement(element) {\n const verseNumber = element.getAttribute(\"data-number\") ?? \"0\";\n const node = $createImmutableVerseNode(verseNumber);\n return { node };\n}\nexport function $createImmutableVerseNode(verseNumber, showMarker, sid, altnumber, pubnumber, unknownAttributes) {\n return $applyNodeReplacement(new ImmutableVerseNode(verseNumber, showMarker, sid, altnumber, pubnumber, unknownAttributes));\n}\nfunction isVerseElement(node) {\n const marker = node?.getAttribute(\"data-marker\") ?? undefined;\n return marker === VERSE_MARKER;\n}\nexport function $isImmutableVerseNode(node) {\n return node instanceof ImmutableVerseNode;\n}\nexport function isSerializedImmutableVerseNode(node) {\n return node?.type === ImmutableVerseNode.getType();\n}\n","import { $isImmutableVerseNode, isSerializedImmutableVerseNode, } from \"./ImmutableVerseNode\";\nimport { $getNodeByKey, $isElementNode, $isRangeSelection, $isTextNode, } from \"lexical\";\nimport { $findNearestPreviousNode, $isNodeWithMarker, $isParaNode, $isSomeChapterNode, $isVerseBlockNode, $isVerseNode, isSerializedVerseNode, isVerseInRange, NBSP, } from \"shared\";\n/**\n * Checks if the given node is a VerseNode or ImmutableVerseNode.\n * @param node - The node to check.\n * @returns `true` if the node is a VerseNode or ImmutableVerseNode, `false` otherwise.\n */\nexport function $isSomeVerseNode(node) {\n return $isVerseNode(node) || $isImmutableVerseNode(node);\n}\n/**\n * Checks if the given node is a SerializedVerseNode or SerializedImmutableVerseNode.\n * @param node - The serialized node to check.\n * @returns `true` if the node is a SerializedVerseNode or SerializedImmutableVerseNode, `false` otherwise.\n */\nexport function isSomeSerializedVerseNode(node) {\n return isSerializedVerseNode(node) || isSerializedImmutableVerseNode(node);\n}\n/**\n * Finds the first paragraph that is not a book or chapter node.\n * @param nodes - Nodes to look in.\n * @returns the first paragraph node.\n */\nexport function $getFirstPara(nodes) {\n return $expandVerseBlocks(nodes).find((node) => $isParaNode(node));\n}\n/**\n * The given nodes with any `VerseBlockNode` replaced by its own paragraphs.\n *\n * In the block verse layout a verse sits two levels below the root - `VerseBlockNode > ParaNode >\n * verse` - so anything searching for verses or paragraphs has to see through the block. Returns\n * the input unchanged when there are no verse blocks, leaving the inline layouts untouched.\n */\nfunction $expandVerseBlocks(nodes) {\n if (!nodes.some($isVerseBlockNode))\n return nodes;\n return nodes.flatMap((node) => ($isVerseBlockNode(node) ? node.getChildren() : node));\n}\n/**\n * The nodes among which a verse marker would sit, for a container that might hold one.\n *\n * For a paragraph that is its own children. For a verse block it is the children of each of its\n * paragraphs, in document order, since the block holds paragraphs and the markers live inside\n * those. Callers can then treat both the same way.\n */\nfunction $getVerseSiblings(node) {\n if (!$isElementNode(node))\n return [];\n if ($isVerseBlockNode(node))\n return node.getChildren().flatMap($getVerseSiblings);\n return node.getChildren();\n}\n/**\n * Find the given verse in the children of the node.\n * @param node - Node with potential verses in children.\n * @param verseNum - Verse number to look for.\n * @returns the verse node if found, `undefined` otherwise.\n */\nexport function $findVerseInNode(node, verseNum) {\n const children = $getVerseSiblings(node);\n const verseNode = children.find((child) => $isSomeVerseNode(child) && isVerseInRange(verseNum, child.getNumber()));\n return verseNode;\n}\n/**\n * Finds the verse node with the given verse number amongst the children of nodes.\n * @param nodes - Nodes to look in.\n * @param verseNum - Verse number to look for.\n * @returns the verse node if found, or the first paragraph if verse 0, `undefined` otherwise.\n */\nexport function $findVerseOrPara(nodes, verseNum) {\n return verseNum === 0\n ? $getFirstPara(nodes)\n : nodes\n .map((node) => $findVerseInNode(node, verseNum))\n // remove any undefined results and take the first found\n .filter((verseNode) => verseNode)[0];\n}\n/**\n * Find the next verse in the children of the node.\n * @param node - Node with potential verses in children.\n * @returns the verse node if found, `undefined` otherwise.\n */\nexport function $findNextVerseInNode(node) {\n const verseNode = $getVerseSiblings(node).find((child) => $isSomeVerseNode(child));\n return verseNode;\n}\n/**\n * Finds the next verse node amongst the children of nodes.\n * @param nodes - Nodes to look in.\n * @returns the verse node if found, `undefined` otherwise.\n */\nexport function $findNextVerse(nodes) {\n return (nodes\n .map((node) => $findNextVerseInNode(node))\n // remove any undefined results and take the first found\n .filter((verseNode) => verseNode)[0]);\n}\n/**\n * Find the previous verse node in a parent's children, walking backward from the given index.\n * @param parent - Element node whose children to search.\n * @param fromIndex - Start index (exclusive); search from fromIndex - 1 down to 0.\n * @returns The verse node if found, `undefined` otherwise.\n */\nexport function $findPreviousVerseInSiblings(parent, fromIndex) {\n if (!$isElementNode(parent) || fromIndex <= 0)\n return;\n const children = parent.getChildren();\n for (let i = fromIndex - 1; i >= 0; i--) {\n const child = children[i];\n if ($isSomeVerseNode(child))\n return child;\n }\n return undefined;\n}\n/**\n * Find the next verse node after `verseNode` in document order, stopping at the next chapter\n * boundary (or the end of the document). Forward counterpart to `$findPreviousVerseInSiblings` /\n * the backward walk in `$findThisVerse`.\n * @param verseNode - The verse node to search forward from.\n * @returns The next verse node, or `undefined` if none exists before the next chapter/document end.\n */\nexport function $findNextVerseAfter(verseNode) {\n const parent = verseNode.getParent();\n if (parent && $isElementNode(parent)) {\n const children = parent.getChildren();\n for (let i = verseNode.getIndexWithinParent() + 1; i < children.length; i++) {\n const child = children[i];\n if ($isSomeVerseNode(child))\n return child;\n }\n }\n let nextPara = parent?.getNextSibling();\n while (nextPara && !$isSomeChapterNode(nextPara)) {\n const verse = $findNextVerseInNode(nextPara);\n if (verse)\n return verse;\n nextPara = nextPara.getNextSibling();\n }\n return undefined;\n}\n/**\n * Find the last verse in the children of the node.\n * @param node - Node with potential verses in children.\n * @returns the verse node if found, `undefined` otherwise.\n */\nexport function $findLastVerseInNode(node) {\n return $getVerseSiblings(node).findLast((child) => $isSomeVerseNode(child));\n}\n/**\n * Finds the last verse node amongst the children of nodes.\n * @param nodes - Nodes to look in.\n * @returns the verse node if found, `undefined` otherwise.\n */\nexport function $findLastVerse(nodes) {\n const verseNodes = nodes\n .map((node) => $findLastVerseInNode(node))\n // remove any undefined results\n .filter((verseNode) => verseNode);\n if (verseNodes.length <= 0)\n return;\n return verseNodes[verseNodes.length - 1];\n}\n/**\n * Length of verse number prefix in verse text for BCV \"before vs after\" check.\n * If text doesn't start with the verse number (e.g. $createVerseNode(\"1\", \" verse one\")\n * or node is non-VerseNode (e.g. ImmutableVerseNode), returns 0 — treats all positions\n * as \"after\" and shows the current verse.\n */\nfunction getVerseNumberPrefixLength(verseNode) {\n if (!$isVerseNode(verseNode))\n return 0;\n const verseNumber = verseNode.getNumber();\n const text = verseNode.getTextContent();\n return text.startsWith(verseNumber) ? verseNumber.length : 0;\n}\n/**\n * Returns true when the selection anchor is positioned before the given verse node in document\n * order. Handles: (1) cursor inside the verse's parent with offset before this verse's index,\n * (2) cursor in the verse's previous sibling.\n */\nfunction $isSelectionBeforeVerseNode(selection, verseNode, anchorNode) {\n if (!anchorNode)\n return false;\n const parent = verseNode.getParent();\n if (anchorNode === parent && $isElementNode(anchorNode)) {\n const verseIndex = verseNode.getIndexWithinParent();\n const anchorOffset = selection.anchor.offset;\n return anchorOffset <= verseIndex;\n }\n if (anchorNode.getNextSibling() === verseNode)\n return true;\n return false;\n}\n/**\n * Returns true when BCV should show the previous verse (cursor is before the verse number).\n * Encapsulates: anchor in parent/previous sibling before verse; anchor in verse node before\n * verse number (TextNode) or on whole node (DecoratorNode).\n */\nfunction $shouldShowPreviousVerseForBcv(verseNode, selection) {\n const anchorNode = selection.anchor.getNode();\n // Anchor not on verse node: check if cursor is before verse (parent offset or previous sibling)\n if (anchorNode !== verseNode) {\n return $isSelectionBeforeVerseNode(selection, verseNode, anchorNode);\n }\n // Anchor on verse node: show previous if cursor is before verse number\n if ($isTextNode(verseNode)) {\n const prefixLength = getVerseNumberPrefixLength(verseNode);\n return selection.anchor.offset < prefixLength;\n }\n // ImmutableVerseNode (DecoratorNode): whole node is verse number; show previous\n return true;\n}\n/** Build result for current verse (no selection or cursor after verse number). */\nfunction currentVerseResult(verseNode) {\n const verse = verseNode.getNumber();\n const selectedVerseNum = Number.parseInt(verse ?? \"0\", 10);\n return {\n verseNum: selectedVerseNum,\n verse: verse != null && selectedVerseNum.toString() !== verse ? verse : undefined,\n };\n}\n/**\n * Returns the verse number (and optional verse range) for BCV display. When the cursor is\n * before the verse number, returns the previous verse so BCV only updates after the number.\n * For \"previous\" verse, only `verseNum` is set (no `verse` range); e.g. cursor before \"2-3\" → `{ verseNum: 1 }`.\n *\n * @param verseNode - The verse node that contains or precedes the cursor.\n * @param selection - The current editor selection.\n * @returns Effective verse number and optional verse range string for BCV display.\n */\nexport function $getEffectiveVerseForBcv(verseNode, selection) {\n if (!verseNode)\n return { verseNum: 0 };\n // No selection or not range: use verse node as-is\n if (!$isRangeSelection(selection)) {\n return currentVerseResult(verseNode);\n }\n const selectedVerseNum = Number.parseInt(verseNode.getNumber() ?? \"0\", 10);\n const prevNum = selectedVerseNum <= 1 ? 0 : selectedVerseNum - 1;\n // Anchor before verse number: show previous verse\n if ($shouldShowPreviousVerseForBcv(verseNode, selection))\n return { verseNum: prevNum };\n return currentVerseResult(verseNode);\n}\n/**\n * Checks if the node has a `getMarker` method. Includes all React nodes.\n * @param node - LexicalNode to check.\n * @returns `true` if the node has a `getMarker` method, `false` otherwise.\n */\nexport function $isReactNodeWithMarker(node) {\n return $isNodeWithMarker(node) || $isImmutableVerseNode(node);\n}\n/**\n * Add trailing space to a TextNode\n * @param node - Text node to add trailing space to.\n */\nexport function $addTrailingSpace(node) {\n if ($isTextNode(node)) {\n const text = node.getTextContent();\n if (!text.endsWith(\" \") && !text.endsWith(NBSP))\n node.setTextContent(`${text} `);\n }\n}\n/**\n * Removes the any leading space from a TextNode.\n * @param node - Text node to remove leading space from.\n */\nexport function $removeLeadingSpace(node) {\n if ($isTextNode(node)) {\n const text = node.getTextContent();\n if (text.startsWith(\" \"))\n node.setTextContent(text.trimStart());\n }\n}\n/**\n * Checks if the node was created since the previous editor state.\n * @param editor - The lexical editor instance.\n * @param nodeKey - The key of the node.\n * @returns `true` if the node was created, and `false` otherwise.\n */\nexport function wasNodeCreated(editor, nodeKey) {\n return editor.getEditorState().read(() => !$getNodeByKey(nodeKey));\n}\n/**\n * Moves the selection to the start of the next verse's content (after the verse marker).\n * Used for ArrowDown navigation so the cursor lands on a position that ScriptureReferencePlugin\n * can resolve for BCV display.\n * @param selection - The current range selection (must be collapsed).\n * @returns `true` if the selection was moved, `false` if not collapsed or no next verse.\n */\nexport function $selectNextVerse(selection) {\n if (!selection.isCollapsed())\n return false;\n const anchorNode = selection.anchor.getNode();\n const currentVerse = $resolveVerseNode(anchorNode, selection);\n let nextVerse;\n if (currentVerse) {\n const parent = currentVerse.getParent();\n // When the cursor is on a block (e.g. para) before the first verse in that block,\n // $resolveVerseNode falls back to the first verse in the paragraph. That verse is\n // ahead of the caret — it should be the ArrowDown target, not skipped as \"current\".\n if (parent &&\n $isElementNode(parent) &&\n $isElementNode(anchorNode) &&\n anchorNode === parent &&\n selection.anchor.offset < currentVerse.getIndexWithinParent()) {\n nextVerse = currentVerse;\n }\n if (!nextVerse && parent && $isElementNode(parent)) {\n const children = parent.getChildren();\n const currentIndex = currentVerse.getIndexWithinParent();\n for (let i = currentIndex + 1; i < children.length; i++) {\n const child = children[i];\n if ($isSomeVerseNode(child)) {\n nextVerse = child;\n break;\n }\n }\n }\n if (!nextVerse && parent) {\n let nextPara = $getNextSearchSibling(parent);\n while (nextPara && !$isSomeChapterNode(nextPara)) {\n const verse = $findNextVerseInNode(nextPara);\n if (verse) {\n nextVerse = verse;\n break;\n }\n nextPara = $getNextSearchSibling(nextPara);\n }\n }\n }\n else {\n const topLevel = anchorNode.getTopLevelElement();\n let para = topLevel ?? anchorNode;\n while (para) {\n const verse = $findNextVerseInNode(para);\n if (verse) {\n nextVerse = verse;\n break;\n }\n para = para.getNextSibling();\n if (para && $isSomeChapterNode(para))\n break;\n }\n }\n if (!nextVerse)\n return false;\n nextVerse.selectNext(0, 0);\n return true;\n}\n/**\n * Moves the selection to the start of the previous verse's content (after the verse marker).\n * Used for ArrowUp navigation so the cursor lands on a position that ScriptureReferencePlugin\n * can resolve for BCV display.\n * @param selection - The current range selection (must be collapsed).\n * @returns `true` if the selection was moved, `false` if not collapsed or no previous verse.\n */\nexport function $selectPreviousVerse(selection) {\n if (!selection.isCollapsed())\n return false;\n const anchorNode = selection.anchor.getNode();\n const currentVerse = $resolveVerseNode(anchorNode, selection);\n let prevVerse;\n if (currentVerse) {\n const parent = currentVerse.getParent();\n // When the cursor is in a different (later) paragraph than currentVerse's parent,\n // $resolveVerseNode found the verse via backward traversal across paragraphs.\n // That verse is behind the caret — it should be the ArrowUp target directly.\n const topLevel = anchorNode.getTopLevelElement();\n // Compare the caret's block against the verse's own block. Everywhere but the block verse\n // layout a paragraph is its own top-level element - including inside a table, whose cells are\n // shadow roots - so this is the same test as comparing against the paragraph. In block verse\n // the paragraph's top-level element is its verse block, which is what makes it correct there.\n if (parent && topLevel && topLevel !== parent.getTopLevelElement()) {\n prevVerse = currentVerse;\n }\n if (!prevVerse && parent && $isElementNode(parent)) {\n prevVerse = $findPreviousVerseInSiblings(parent, currentVerse.getIndexWithinParent());\n }\n if (!prevVerse && parent) {\n let prevPara = $getPreviousSearchSibling(parent);\n while (prevPara && !$isSomeChapterNode(prevPara)) {\n const verse = $findLastVerseInNode(prevPara);\n if (verse) {\n prevVerse = verse;\n break;\n }\n prevPara = $getPreviousSearchSibling(prevPara);\n }\n }\n }\n else {\n const topLevel = anchorNode.getTopLevelElement();\n let prevPara = topLevel?.getPreviousSibling() ?? null;\n while (prevPara && !$isSomeChapterNode(prevPara)) {\n const verse = $findLastVerseInNode(prevPara);\n if (verse) {\n prevVerse = verse;\n break;\n }\n prevPara = prevPara.getPreviousSibling();\n }\n }\n if (!prevVerse)\n return false;\n prevVerse.selectNext(0, 0);\n return true;\n}\n/**\n * The next node to search when walking forward looking for a verse.\n *\n * Normally the node's next sibling. In the block verse layout a paragraph's siblings run out at\n * the end of its verse block, so the walk continues from the block's own next sibling rather than\n * stopping inside it. Behaves exactly like `getNextSibling` for a top-level node.\n */\nfunction $getNextSearchSibling(node) {\n const nextSibling = node.getNextSibling();\n if (nextSibling)\n return nextSibling;\n const topLevel = node.getTopLevelElement();\n return topLevel && topLevel !== node ? topLevel.getNextSibling() : null;\n}\n/** Mirror of {@link $getNextSearchSibling} for a backward walk. */\nfunction $getPreviousSearchSibling(node) {\n const previousSibling = node.getPreviousSibling();\n if (previousSibling)\n return previousSibling;\n const topLevel = node.getTopLevelElement();\n return topLevel && topLevel !== node ? topLevel.getPreviousSibling() : null;\n}\n/**\n * Resolves the verse node for the given start node. When the cursor is on an element\n * (e.g. para) rather than inside a verse, looks at the child at offset or walks backward\n * within that element before falling back to $findThisVerse (which may walk to prior paras).\n */\nexport function $resolveVerseNode(startNode, selection) {\n const isCursorOnElement = $isElementNode(startNode) &&\n $isRangeSelection(selection) &&\n selection.anchor.key === startNode.getKey();\n if (isCursorOnElement) {\n const childAtOffset = startNode.getChildAtIndex(selection.anchor.offset);\n if (childAtOffset && $isSomeVerseNode(childAtOffset))\n return childAtOffset;\n const prev = $findPreviousVerseInSiblings(startNode, selection.anchor.offset);\n if (prev)\n return prev;\n const firstVerseInPara = $findNextVerseInNode(startNode);\n if (firstVerseInPara)\n return firstVerseInPara;\n }\n return $findThisVerse(startNode);\n}\n/**\n * Find the verse that this node is in.\n * @param node - Node to find the verse it's in.\n * @returns the verse node if found, `undefined` otherwise.\n */\nexport function $findThisVerse(node) {\n if (!node || $isSomeChapterNode(node))\n return;\n // is this node a verse\n if ($isSomeVerseNode(node))\n return node;\n let previousSiblingOrParent = $findNearestPreviousNode(node);\n while (previousSiblingOrParent) {\n // If this node is a chapter node, stop searching as we've reached the start of this chapter\n if ($isSomeChapterNode(previousSiblingOrParent))\n return;\n // If this node is a verse node, return it\n if ($isSomeVerseNode(previousSiblingOrParent))\n return previousSiblingOrParent;\n // If this node contains a verse node, return that\n const verseNode = $findLastVerseInNode(previousSiblingOrParent);\n if (verseNode)\n return verseNode;\n previousSiblingOrParent = $findNearestPreviousNode(previousSiblingOrParent);\n }\n return undefined;\n}\n","/**\n * Models for the rich-text Operational Transform documents used in Scripture Forge.\n * `OT_???_PROPS` are the properties that can be set on the corresponding Lexical node. The rest go\n * into unknownAttributes.\n */\nexport const OT_PARA_PROPS = [\"style\"];\nexport const OT_BOOK_PROPS = [\"style\", \"code\"];\nexport const OT_CHAR_PROPS = [\"style\", \"cid\"];\nexport const OT_CHAPTER_PROPS = [\n \"style\",\n \"number\",\n \"sid\",\n \"altnumber\",\n \"pubnumber\",\n];\nexport const OT_VERSE_PROPS = [\n \"style\",\n \"number\",\n \"sid\",\n \"altnumber\",\n \"pubnumber\",\n];\nexport const OT_MILESTONE_PROPS = [\n \"style\",\n \"sid\",\n \"eid\",\n \"attributeOrder\",\n];\n// Note that `contents` is not a property of a NoteNode, but we don't want it in unknownAttributes.\nexport const OT_NOTE_PROPS = [\"style\", \"caller\", \"category\", \"contents\"];\nexport const OT_UNKNOWN_PROPS = [\"tag\", \"marker\", \"contents\"];\nexport const validOTEmbedTypes = [\n \"chapter\",\n \"immutable-chapter\",\n \"verse\",\n \"immutable-verse\",\n \"ms\",\n \"note\",\n \"unknown\",\n \"unmatched\",\n];\n","/** Common utilities used for OT Delta realtime collaborative editing. */\nimport { $isSomeVerseNode } from \"../../../nodes/usj/node-react.utils\";\nimport { validOTEmbedTypes } from \"./rich-text-ot.model\";\nimport { $dfsIterator, $findMatchingParent } from \"@lexical/utils\";\nimport { $getNodeByKey, $getState, $isElementNode, $isTextNode, } from \"lexical\";\nimport { $isAttributeRunNode, $isCharNode, $isCursorPlaceholderOnlyText, $isDescendantOf, $isImmutableUnmatchedNode, $isMarkerNode, $isMilestoneNode, $isNoteNode, $isParaLikeNode, $isParaNode, $isDisplayRunPiece, $isSomeChapterNode, $isSynthesizedMarkerNode, $isUnknownNode, EMPTY_CHAR_PLACEHOLDER_TEXT, getEditableCallerText, NODE_ATTRIBUTE_PREFIX, textTypeState, } from \"shared\";\n/** Line Feed character used to close para-like nodes.*/\nexport const LF = \"\\n\";\n/**\n * Get the replace embed operations for a given embed node key.\n *\n * @remarks\n * The returned ops are host-local: they are meant to be fed straight to `$applyUpdate`, so\n * the retain is computed in `\"apply\"` coordinates (see {@link OTCoordinateSystem}) to agree\n * with `$applyUpdate`'s insert/delete traversals.\n *\n * @param embedNodeKey - The key of the embed node to replace.\n * @param insertEmbedOps - The operations to insert the new embed node.\n * @returns The replace embed operations, or `undefined` if the node is not found.\n */\nexport function $getReplaceEmbedOps(embedNodeKey, insertEmbedOps) {\n const node = $getNodeByKey(embedNodeKey);\n if (!$isEmbedNode(node))\n return;\n const retain = $getOTPositionOfNode(node, \"apply\");\n if (retain === undefined)\n return;\n const ops = [{ retain }, ...insertEmbedOps, { delete: 1 }];\n return ops;\n}\n/**\n * Calculate the OT (Operational Transform) position of a given node in the document.\n *\n * @remarks\n * - Text nodes return their start position\n * - Embed nodes (chapter, verse, milestone, note, unmatched) return their position (length 1)\n * - Para-like nodes (ParaNode, BookNode, ImpliedParaNode) return their closing position (length 1)\n * - CharNodes have no OT length contribution\n *\n * @param node - The Lexical node to find the position for.\n * @param coordinates - The OT coordinate system to count in (see {@link OTCoordinateSystem}).\n * Defaults to the legacy `\"delta-doc\"` counting; pass `\"apply\"` for positions consumed by\n * `$applyUpdate`.\n * @returns The OT position of the node, or `undefined` if the node is not found.\n */\nexport function $getOTPositionOfNode(node, coordinates = \"delta-doc\") {\n if (!node)\n return undefined;\n // LAZY traversal, terminated by the early returns below: this runs per keystroke on\n // DeltaOnChangePlugin's fast path, and the eager `$dfs()` array materialized the ENTIRE\n // document each call regardless of where the target sat.\n const dfsNodes = $dfsIterator();\n let currentIndex = 0;\n const openParaLikeNodes = [];\n const openContentEmbeds = [];\n const targetKey = node.getKey();\n let targetParaLikeNode;\n for (const dfsNode of dfsNodes) {\n const currentNode = dfsNode.node;\n // Before processing the current node, check if any previously opened para-like nodes are\n // closing.\n for (let j = openParaLikeNodes.length - 1; j >= 0; j--) {\n if ($isElementNodeClosing(openParaLikeNodes[j], dfsNode)) {\n const closingPara = openParaLikeNodes[j];\n openParaLikeNodes.splice(j, 1);\n currentIndex += 1;\n // If this is the target para-like node closing, return its position\n if (targetParaLikeNode && closingPara.getKey() === targetParaLikeNode.getKey()) {\n return currentIndex - 1; // Return the position we just incremented\n }\n }\n }\n // Check if any open content embed nodes (note/unknown) are closing\n for (let j = openContentEmbeds.length - 1; j >= 0; j--) {\n if ($isElementNodeClosing(openContentEmbeds[j].node, dfsNode)) {\n openContentEmbeds.splice(j, 1);\n }\n }\n const activeContentEmbed = openContentEmbeds[openContentEmbeds.length - 1];\n if (activeContentEmbed) {\n if (currentNode.getKey() === targetKey) {\n return activeContentEmbed.position;\n }\n continue;\n }\n // Check if we've found the target node\n if (currentNode.getKey() === targetKey) {\n // For text nodes, return the start position (an editable verse is an embed, not text —\n // see $isOTTextNode — so it falls to the embed check below)\n if ($isOTTextNode(currentNode))\n return currentIndex;\n // For embed nodes, return their position\n if ($isEmbedNode(currentNode))\n return currentIndex;\n // For para-like nodes, mark it and continue to find its closing position\n if ($isParaLikeNode(currentNode)) {\n targetParaLikeNode = currentNode;\n // Continue processing to find where this para closes\n }\n // For CharNodes or other nodes that don't have OT positions, continue searching\n // (CharNodes don't have their own position, their text content does)\n }\n // Track opening of para-like nodes after checking for target\n if ($isParaLikeNode(currentNode)) {\n if (!openParaLikeNodes.includes(currentNode)) {\n openParaLikeNodes.push(currentNode);\n }\n }\n // Track when we enter an opaque content container (note/unknown always; any element\n // embed such as an editable chapter in \"apply\" coordinates)\n if ($isOpaqueContentNode(currentNode, coordinates)) {\n if (currentNode.getKey() === targetKey)\n return currentIndex;\n openContentEmbeds.push({ node: currentNode, position: currentIndex });\n currentIndex += 1; // Embeds contribute 1 to OT length\n continue; // Skip normal OT contribution calculation for embed contents\n }\n // Calculate OT length contribution of current node\n currentIndex += $getNodeOTContribution(currentNode, coordinates);\n }\n // If we're looking for a para-like node that didn't close, return current position\n if (targetParaLikeNode)\n return currentIndex;\n // Node not found\n return undefined;\n}\n/**\n * Get the key of the inserted node from the OT delta operations.\n * @param ops - The OT delta operations with potential insertion.\n * @param editorState - The current editor state.\n * @param coordinates - The OT coordinate system the retain in `ops` is expressed in (see\n * {@link OTCoordinateSystem}). Use `\"apply\"` when the ops were applied by `$applyUpdate`\n * (the node was placed at the retain in apply coordinates); use the default `\"delta-doc\"`\n * for retains produced by doc-delta diffs (e.g. `DeltaOnChangePlugin` local-edit ops).\n * @returns The key of the inserted node if found, `undefined` otherwise.\n */\nexport function getInsertedNodeKey(ops, editorState, coordinates = \"delta-doc\") {\n if (ops.length < 2 || !isRetainOp(ops[0]) || !isInsertEmbedOp(ops[1]))\n return undefined;\n const retain = ops[0].retain;\n return editorState.read(() => {\n const node = $getNodeFromOTPosition(retain, coordinates);\n return node?.getKey();\n });\n}\n/**\n * Get the Lexical node at a specific OT delta position.\n *\n * @remarks\n * This is the reverse of {@link $getOTPositionOfNode}: both must count in the SAME\n * coordinate system for round trips to resolve to the same node.\n *\n * @param otPosition - The OT delta position in the doc.\n * @param coordinates - The OT coordinate system to count in (see {@link OTCoordinateSystem}).\n * @returns The Lexical node if found, `undefined` otherwise.\n */\nexport function $getNodeFromOTPosition(otPosition, coordinates = \"delta-doc\") {\n // LAZY traversal, terminated by the early returns below — same reasoning as\n // $getOTPositionOfNode: the walk stops at the resolved position instead of first\n // materializing the entire document.\n const dfsNodes = $dfsIterator();\n let currentIndex = 0;\n const openParaLikeNodes = [];\n const openContentEmbeds = [];\n for (const dfsNode of dfsNodes) {\n const currentNode = dfsNode.node;\n // Before processing the current node, check if any previously opened para-like nodes are\n // closing.\n for (let j = openParaLikeNodes.length - 1; j >= 0; j--) {\n if ($isElementNodeClosing(openParaLikeNodes[j], dfsNode)) {\n const closingPara = openParaLikeNodes[j];\n openParaLikeNodes.splice(j, 1);\n // Check if this closing position matches our target\n if (currentIndex === otPosition) {\n return closingPara;\n }\n currentIndex += 1;\n }\n }\n // Check if any open content embed nodes (note/unknown) are closing\n for (let j = openContentEmbeds.length - 1; j >= 0; j--) {\n if ($isElementNodeClosing(openContentEmbeds[j].node, dfsNode)) {\n openContentEmbeds.splice(j, 1);\n }\n }\n const activeContentEmbed = openContentEmbeds[openContentEmbeds.length - 1];\n if (activeContentEmbed) {\n if (activeContentEmbed.position === otPosition) {\n return activeContentEmbed.node;\n }\n continue;\n }\n // Track opening of para-like nodes\n if ($isParaLikeNode(currentNode)) {\n if (!openParaLikeNodes.includes(currentNode)) {\n openParaLikeNodes.push(currentNode);\n }\n }\n // Track when we enter an opaque content container (note/unknown always; any element\n // embed such as an editable chapter in \"apply\" coordinates)\n if ($isOpaqueContentNode(currentNode, coordinates)) {\n if (currentIndex === otPosition) {\n return currentNode;\n }\n openContentEmbeds.push({ node: currentNode, position: currentIndex });\n currentIndex += 1;\n continue;\n }\n // Calculate OT length contribution of current node\n const contribution = $getNodeOTContribution(currentNode, coordinates);\n // For text nodes, check if the position falls within this node's range (an editable verse is\n // an embed, not text — see $isOTTextNode — so it is matched by the embed check below instead)\n if ($isOTTextNode(currentNode) && contribution > 0) {\n if (otPosition >= currentIndex && otPosition < currentIndex + contribution) {\n return currentNode;\n }\n }\n // For embed nodes (contribution === 1), check exact position\n if ($isEmbedNode(currentNode)) {\n if (currentIndex === otPosition) {\n return currentNode;\n }\n }\n currentIndex += contribution;\n }\n // Check if any remaining open para-like nodes close at the final position\n for (const paraNode of openParaLikeNodes) {\n if (currentIndex === otPosition) {\n return paraNode;\n }\n currentIndex += 1;\n }\n // Position not found or out of bounds\n return undefined;\n}\n/**\n * Check if an element node is being closed at this point in the DFS traversal.\n */\nexport function $isElementNodeClosing(node, nextDfsNode) {\n if (!node)\n return false;\n // An element node is closing if the next node in DFS is not a descendant.\n // In DFS, all descendants of a node appear consecutively after the node.\n if (!nextDfsNode) {\n // End of traversal, so this node is closing\n return true;\n }\n // Check if the next node is a descendant of the current node\n return !$isDescendantOf(nextDfsNode.node, node.getKey());\n}\n/**\n * Type guard for a node that contributes its glyph TEXT to OT length — a genuine text node, and\n * NOT an embed that merely subclasses `TextNode`.\n *\n * The only embed that is also a `TextNode` is an editable `VerseNode` (its `__text` IS the\n * `\"\\v 1 \"` glyph). Like every embed it counts as ONE opaque OT unit, so OT-counting traversals\n * must classify it via {@link $isEmbedNode} and never measure or split its glyph text. Use this\n * in place of `$isTextNode` wherever a text branch precedes an embed branch, so an editable\n * verse falls through to the embed branch. See {@link OTCoordinateSystem}.\n */\nexport function $isOTTextNode(node) {\n return $isTextNode(node) && !$isEmbedNode(node);\n}\n/**\n * Type guard to check if a node is an embed. Embeds have an OT length of 1 and are self-contained\n * (no children to process).\n */\nexport function $isEmbedNode(node) {\n return ($isSomeChapterNode(node) ||\n $isSomeVerseNode(node) ||\n $isMilestoneNode(node) ||\n $isNoteNode(node) ||\n $isUnknownNode(node) ||\n $isImmutableUnmatchedNode(node));\n}\n/**\n * Type guard to check if the given insert embed operation is for the specified embed type.\n *\n * @param embedType - The type of embed to check for, e.g. \"note\".\n * @param op - The OT delta operation to check.\n * @returns `true` if the operation is for the specified embed type, `false` otherwise.\n *\n * @public\n */\nexport function isInsertEmbedOpOfType(embedType, op) {\n return op?.insert != null && typeof op.insert === \"object\" && embedType in op.insert;\n}\n/**\n * Type guard to check if the given insert embed operation is for an embed type.\n * @param op - The OT delta operation to check.\n * @returns `true` if the operation is for an embed type, `false` otherwise.\n */\nfunction isInsertEmbedOp(op) {\n if (op.insert == null || typeof op.insert !== \"object\")\n return false;\n const embedType = Object.keys(op.insert)[0];\n return (op.insert != null &&\n typeof op.insert === \"object\" &&\n embedType in op.insert &&\n validOTEmbedTypes.includes(embedType));\n}\n/**\n * Type guard to check if the given operation is a retain operation.\n * @param op - The OT delta operation to check.\n * @returns `true` if it is a retain operation, `false` otherwise.\n */\nfunction isRetainOp(op) {\n return op.retain != null && typeof op.retain === \"number\";\n}\n/**\n * Whether the node is an opaque content container in the given coordinate system: it\n * contributes exactly 1 OT unit and its descendants are skipped.\n *\n * Note and unknown contents are opaque in BOTH systems (their contents ops nest inside the\n * embed insert op in the doc delta). Other element-based embeds with presentation glyph\n * children — an editable `ChapterNode` — are opaque only in `\"apply\"` coordinates:\n * `$applyUpdate`'s traversals never descend into ANY embed, while the doc delta serializes\n * a chapter's glyph text child as a body text op. See {@link OTCoordinateSystem}.\n */\nfunction $isOpaqueContentNode(node, coordinates) {\n if ($isNoteNode(node) || $isUnknownNode(node))\n return true;\n return coordinates === \"apply\" && $isElementNode(node) && $isEmbedNode(node);\n}\n/**\n * True when `node` is a paragraph's own marker-prefix glyph — the `\\p`-style MarkerNode a\n * ParaNode carries as its first child in editable marker mode ($createMarkerPrefix,\n * markerEditDeletion.utils.ts). The ONE definition, shared by this file's own OT-length\n * accounting below and by `editor-delta.adaptor.ts`'s content-ops gate, so the two coordinate\n * systems can never drift apart on what counts as the prefix glyph. The position check (first\n * child of a ParaNode) is load-bearing, since {@link $isSynthesizedMarkerNode} identifies the node\n * SHAPE, which is reused for every other glyph in the tree too (a char span's own opener/closer,\n * a note's glyphs, a milestone's or verse's bare attribute glyph) — only a MarkerNode sitting in\n * the paragraph's own prefix slot is presentation scaffolding.\n */\nexport function $isOwnParaPrefixGlyph(node) {\n const parent = node.getParent();\n return $isSynthesizedMarkerNode(node) && $isParaNode(parent) && parent.getFirstChild() === node;\n}\n/**\n * True when `node` sits inside an `AttributeRunNode` wrapper (a verse's/milestone's display run,\n * when wrapped — see `AttributeRunNode.ts`). The wrapper is pure presentation scaffolding — its\n * children are the SAME run pieces (glyphs, attribute text) that also ride as loose siblings when\n * nothing has wrapped the run — so this is the wrapped-shape counterpart of\n * `$isOwnParaPrefixGlyph` above: an ANCESTRY check rather than a sibling-adjacency one, so it also\n * catches shapes the loose-piece exclusions in `editor-delta.adaptor.ts` can miss by adjacency\n * alone (e.g. a milestone's glyph pair with no attribute text between them, where neither glyph\n * has an attribute-tagged sibling to key off of).\n */\nexport function $hasAttributeRunAncestor(node) {\n // The walk starts at the PARENT deliberately: an AttributeRunNode is not \"inside\" itself.\n const parent = node.getParent();\n return parent !== null && $findMatchingParent(parent, $isAttributeRunNode) !== null;\n}\n/**\n * Mirror of editor-delta.adaptor.ts's empty-char-placeholder skip, in delta-doc coordinates: the\n * lone stand-in text of an otherwise childless char span, which the ops stream never emits.\n */\nfunction $isEmptyCharPlaceholderText(node) {\n const parent = node.getParent();\n return ($isCharNode(parent) &&\n node.getTextContent() === EMPTY_CHAR_PLACEHOLDER_TEXT &&\n parent.getChildrenSize() === 1);\n}\n/**\n * Mirror of editor-delta.adaptor.ts's positional note-caller skip: the editable-mode caller text\n * directly after a glyph-fronted note's opening glyph, which the ops stream never emits. The\n * positional guard keeps a pathological content text that merely EQUALS the caller text\n * (elsewhere in the note) counting normally.\n */\nfunction $isEditableNoteCallerText(node) {\n const parent = node.getParent();\n if (!$isNoteNode(parent))\n return false;\n const previousSibling = node.getPreviousSibling();\n return ($isMarkerNode(previousSibling) &&\n previousSibling === parent.getFirstChild() &&\n node.getTextContent() === getEditableCallerText(parent.getCaller()));\n}\n/**\n * Whether the single-dirty-leaf fast path (DeltaOnChangePlugin) may emit `node`'s raw text as a\n * content op. ONE authority rather than a re-implemented exclusion list: eligibility derives\n * from the same delta-doc counting the length side uses ({@link $getNodeOTContribution}) — a\n * node whose bytes are presentation (contribution 0: para-prefix glyphs, marker-trailing-space,\n * attribute text and runs, placeholders, caller text, legacy `⍽|`-prefixed attribute text) or an\n * opaque embed (contribution 1 ≠ text size: an editable verse glyph) counts differently from its\n * raw bytes, so an op built from those bytes would be in a different currency than its retain —\n * such an edit must take the full-diff fallback, which applies `$handleTextNodes`' exclusions.\n * `$isDisplayRunPiece` is checked on top because the ops stream excludes a LOOSE display run's\n * glyphs while delta-doc counting deliberately keeps them (the documented asymmetry in\n * editor-delta.adaptor.ts).\n */\nexport function $isFastPathContentText(node) {\n return (!$isDisplayRunPiece(node) &&\n $getNodeOTContribution(node, \"delta-doc\") === node.getTextContentSize());\n}\n/**\n * Calculate the OT length contribution of a single node.\n *\n * @param coordinates - The OT coordinate system to count in (see {@link OTCoordinateSystem}).\n * A paragraph's own marker-prefix glyph and its NBSP separator are presentation scaffolding\n * that `editor-delta.adaptor.ts`'s `$handleTextNodes` excludes from content ops — but ONLY in\n * `\"delta-doc\"` coordinates, which must agree with that ops stream. `\"apply\"` coordinates are\n * DEFINED as whatever `$applyUpdate`'s own insert/delete/attribute traversals do\n * (delta-apply-update.utils.ts), and none of them skip these nodes — every one counts an OT\n * text node's raw `getTextContentSize()` unconditionally. So `\"apply\"` coordinates must keep\n * counting the prefix and separator too, or a replace-embed retain computed here would\n * disagree with where `$applyUpdate` actually walks to (a note \"replace\" landing one-plus-\n * prefix-length short of the note it meant to delete, deleting the wrong node and leaving the\n * replacement appended instead). If `$applyUpdate`'s traversals are ever taught to skip these\n * nodes too, this exclusion should extend to `\"apply\"` at the same time — not before.\n *\n * The SAME reasoning governs the `$hasAttributeRunAncestor` exclusion below: `$applyUpdate`'s\n * own traversal functions (`$traverseAndApplyAttributesRecursive`, `$traverseAndDelete`,\n * `$insertNodeAtCharacterOffset`) do not special-case `AttributeRunNode` at all — every\n * editable-mode verse/milestone run the adaptor builds rides wrapped in one, so this traversal\n * gap is live on every real document, not a hypothetical one. Each traversal treats a wrapper as\n * an ordinary, un-special-cased `ElementNode` (zero contribution of its own, descend into\n * children) and counts each child's raw text length exactly as it already does for a LOOSE run's\n * pieces — i.e. wrapping changes nothing about what `$applyUpdate` actually does. `\"apply\"`\n * coordinates must therefore keep counting a wrapped piece's text too, matching that (unchanged)\n * traversal; only `\"delta-doc\"` excludes it, mirroring `editor-delta.adaptor.ts`'s existing ops\n * exclusion for the identical bytes.\n */\nfunction $getNodeOTContribution(node, coordinates) {\n // Embeds are checked FIRST: an editable VerseNode is a TextNode subclass but counts as one\n // opaque OT unit (its glyph text is engine-owned display, excluded from content ops), the same\n // as it counts in the doc delta and in `$applyUpdate`'s traversals. See {@link $isOTTextNode}.\n if ($isEmbedNode(node))\n return 1;\n if ($isTextNode(node)) {\n // Read before the guard chain below: its type guards narrow `node` in later `||` operands\n // (a false branch of a `node is TextNode` guard leaves `never`), so member access there\n // does not typecheck even though the value is unchanged.\n const nodeText = node.getTextContent();\n if (coordinates === \"delta-doc\" &&\n // A bare cursor host (EmptyVerseCaretGuardPlugin) is a transient, collab-invisible node:\n // its insertion is never emitted, so it contributes nothing to DOC-DELTA positions or the\n // local doc would drift one position ahead of every peer while a host rests. In `\"apply\"`\n // coordinates it MUST count, per the rule in the doc comment above: none of\n // `$applyUpdate`'s traversals skip a placeholder (each classifies with `$isOTTextNode`\n // and adds raw `getTextContentSize()`), so excluding it here left a replace-embed retain\n // one short whenever a host rested before the target — a footnote-popover save then\n // deleted the unit BEFORE the note instead of the note itself.\n ($isCursorPlaceholderOnlyText(node) ||\n $isOwnParaPrefixGlyph(node) ||\n $getState(node, textTypeState) === \"marker-trailing-space\" ||\n // An attribute value keyed by its own state, not by ancestry: a CHAR span's run is a direct\n // TextNode child of the span, never wrapped (`displayRunRegistry.ts`'s char descriptor\n // writes \"owner-children\"), so `$hasAttributeRunAncestor` cannot see it. That shape is at\n // rest on every `\\w …|strong=\"…\"\\w*`, and the ops stream already omits those bytes\n // (`isNodeAttributeText` in editor-delta.adaptor.ts), so counting them here would put this\n // side out of step with the op stream on ordinary Scripture.\n $getState(node, textTypeState) === \"attribute\" ||\n $hasAttributeRunAncestor(node) ||\n // The remaining ops-stream exclusions, so this side and $handleTextNodes count the same\n // bytes (docs/standard-view-invariants.md §II — extend the shared list, never fork it):\n // the legacy NBSP-`|` byte-prefixed attribute text the ops stream still honors for\n // pre-state-tag peers and persisted deltas, the empty-char placeholder, and the\n // editable-mode note caller in caller position.\n nodeText.startsWith(NODE_ATTRIBUTE_PREFIX) ||\n $isEmptyCharPlaceholderText(node) ||\n $isEditableNoteCallerText(node)))\n return 0;\n return node.getTextContentSize();\n }\n // CharNodes and other nodes don't contribute to OT length\n return 0;\n}\n","import { $isSomeVerseNode } from \"../../../nodes/usj/node-react.utils\";\nimport { $hasAttributeRunAncestor, $isElementNodeClosing, $isOwnParaPrefixGlyph, LF, } from \"./delta-common.utils\";\nimport { $dfs } from \"@lexical/utils\";\nimport { $getRoot, $getState, $isTextNode } from \"lexical\";\nimport Delta from \"quill-delta\";\nimport { $findFirstAncestorNoteNode, $isBookNode, $isCharNode, $isDisplayRunPiece, $isImmutableUnmatchedNode, $isImpliedParaNode, $isMarkerNode, $isMilestoneNode, $isNoteNode, $isParaLikeNode, $isParaNode, $isSomeChapterNode, $isUnknownNode, $isVerseNode, BOOK_MARKER, CHAPTER_MARKER, charIdState, EMPTY_CHAR_PLACEHOLDER_TEXT, getEditableCallerText, isCursorPlaceholderOnly, NBSP, NODE_ATTRIBUTE_PREFIX, segmentState, textTypeState, VERSE_MARKER, } from \"shared\";\nexport function $getTextOp(node, openCharNodes) {\n const op = { insert: node.__text };\n const segment = $getState(node, segmentState);\n if (segment)\n op.attributes = { segment };\n if (openCharNodes && openCharNodes.length > 0) {\n const char = $buildCharAttribute(openCharNodes);\n if (char) {\n op.attributes = {\n ...op.attributes,\n char,\n };\n }\n }\n return op;\n}\nexport function getEditorDelta(editorState) {\n const update = new Delta();\n if (editorState.isEmpty())\n return update;\n editorState.read(() => {\n const root = $getRoot();\n if (!root || root.isEmpty())\n return;\n // check for default empty implied-para node\n const rootChildren = root.getChildren();\n if (rootChildren.length === 1 &&\n $isImpliedParaNode(rootChildren[0]) &&\n (!rootChildren[0].getChildren() || rootChildren[0].getChildrenSize() === 0)) {\n return;\n }\n const ops = $getAllNodeOps();\n for (const op of ops)\n update.push(op);\n });\n return update;\n}\n/**\n * Get the operational transform (OT) delta operations for a specific node or range of nodes.\n * Pass nothing to get all nodes.\n *\n * @param startNode - The node to start the search, if omitted, it will start at the root node.\n * @param endNode - The node to end the search, if omitted, it will find all descendants of the\n * startingNode.\n * @returns An array of DeltaOp objects representing the OT operations for the specified nodes.\n */\nexport function $getParticularNodeOps(startNode, endNode) {\n const ops = [];\n const dfsNodes = $dfs(startNode, endNode);\n const openParaLikeNodes = [];\n const openCharNodes = [];\n const openEmbeds = [];\n const charContentProduced = new Set();\n for (let i = 0; i < dfsNodes.length; i++) {\n const currentNode = dfsNodes[i].node;\n ops.push(...$getNodeOps(currentNode, i, dfsNodes, openParaLikeNodes, openCharNodes, openEmbeds, charContentProduced));\n }\n // Close any remaining open nodes\n for (const openNode of openParaLikeNodes) {\n ops.push(...$getNodeOps(openNode, dfsNodes.length, dfsNodes, openParaLikeNodes, openCharNodes, openEmbeds, charContentProduced));\n }\n return ops;\n}\nfunction $getAllNodeOps() {\n return $getParticularNodeOps();\n}\nfunction $getNodeOps(currentNode, currentIndex, dfsNodes, openParaLikeNodes, openCharNodes, openEmbeds, charContentProduced) {\n if (!currentNode)\n return [];\n const ops = [];\n const nextDfsNode = dfsNodes[currentIndex + 1];\n $handleBlockNodes(currentNode, ops, openParaLikeNodes);\n $handleTextNodes(currentNode, ops, openCharNodes, openEmbeds, charContentProduced);\n $handleCharNodes(currentNode, currentIndex, dfsNodes, openCharNodes, charContentProduced, openEmbeds, ops);\n // is an EmbedNode\n if ($isSomeChapterNode(currentNode))\n ops.push($getChapterOp(currentNode));\n if ($isSomeVerseNode(currentNode))\n ops.push($getVerseOp(currentNode));\n if ($isMilestoneNode(currentNode))\n ops.push($getMilestoneOp(currentNode));\n if ($isImmutableUnmatchedNode(currentNode))\n ops.push($getImmutableUnmatchedOp(currentNode));\n $handleUnknownNodes(currentNode, ops, openEmbeds);\n $handleNoteNodes(currentNode, ops, openEmbeds);\n $closeCompletedEmbeds(nextDfsNode, openEmbeds);\n return ops;\n}\nfunction $handleBlockNodes(currentNode, ops, openParaLikeNodes) {\n if (!currentNode.isInline()) {\n // Handle block nodes\n const openNode = openParaLikeNodes.pop();\n if ($isBookNode(openNode))\n ops.push($getBookOp(openNode));\n else if ($isParaNode(openNode))\n ops.push($getParaOp(openNode));\n else if ($isImpliedParaNode(openNode))\n ops.push({ insert: LF });\n }\n if ($isParaLikeNode(currentNode)) {\n // Track when we open para-like nodes\n if (!openParaLikeNodes.includes(currentNode)) {\n openParaLikeNodes.push(currentNode);\n }\n }\n}\nfunction $handleTextNodes(currentNode, ops, openCharNodes, openEmbeds, charContentProduced) {\n if (!$isTextNode(currentNode))\n return;\n // An editable VerseNode's own `__text` is its marker glyph (`\\v 1 `) — VerseNode extends\n // TextNode so the glyph can sit inline for caret placement, but the glyph is engine-owned\n // display, not content. The verse is already conveyed by its own embed op ($getVerseOp,\n // pushed by the caller once $isSomeVerseNode matches). Skip the glyph here so it never ALSO\n // surfaces as a content text op, which would double-count the verse's length in the OT\n // content stream (once as the embed's implicit 1 unit, once as the leaked glyph bytes) and\n // shift every offset that follows it.\n if ($isVerseNode(currentNode))\n return;\n // An ImmutableUnmatchedNode's bytes are its embed's presentation, the same shape as the\n // editable VerseNode glyph above: the node extends TextNode so the flagged `\\nd*` bytes stay\n // caret-addressable and editable, but the construct is conveyed by its own embed op\n // ($getImmutableUnmatchedOp, pushed by the caller once $isImmutableUnmatchedNode matches).\n // Letting the bytes ALSO flow as a content text op would double-count the embed's OT length\n // (once as the embed's 1 unit, once as the leaked bytes) and shift every offset after it.\n if ($isImmutableUnmatchedNode(currentNode))\n return;\n // Skip a note's first text child: in editable modes this is the note's opening marker glyph\n // (MarkerNode extends TextNode), which shouldn't flow into ops. Caller text, when present as a\n // plain text child (expanded editable mode), is never the first child and is not skipped here.\n const parent = currentNode.getParent();\n if ($isNoteNode(parent) && parent.getFirstChild() === currentNode)\n return;\n // Canonical, glyph-free note ops in editable marker mode: note contents ops carry CONTENT\n // only, the same shape non-editable marker modes produce. Presentation-only nodes that\n // `$applyUpdate` re-synthesizes when materializing the note (`$createWholeNote` /\n // `$createNestedChars`) must not flow into ops, otherwise a round-trip doubles them:\n // - MarkerNode glyphs (char-span openers/closers and the note's own closing glyph);\n // - the expanded editable caller text (presentation of the note's `caller` attribute).\n // A char span's OWN opener/closer glyphs OUTSIDE a note legitimately flow through as literal\n // editable-mode text (the `char` attribute wrapper is layered on top, not a substitute) — only\n // a milestone's or a verse's \\va/\\vp display-run glyphs (presentation that duplicates state the\n // embed op already carries) must never leak, in or out of a note. TWO checks are needed together\n // here, neither a superset of the other:\n // - $hasAttributeRunAncestor walks EVERY ancestor, so it catches a wrapped glyph regardless of\n // how deep it rides or whether the wrapper sits directly after its owner — an intervening\n // node between the owner and its wrapper (a remote insert landing at that boundary, an undo\n // stack, a mid-edit tree) still leaves the wrapper's own contents ancestor-reachable, but the\n // registry's per-kind `ownerOf` walks require exactly that adjacency and give up on the first\n // non-run-piece sibling, so it would miss this shape. (A few lines below, `isNodeAttributeText`\n // applies this same ancestor walk unconditionally to every surviving TextNode, wrapped glyph\n // or not — that independently backstops a wrapped piece even if this arm were removed, but\n // keeping it here too means this gate's OWN stated exclusion holds on its own, rather than by\n // incidental coupling to a check several lines away that could itself change independently.)\n // - $isDisplayRunPiece is keyed on the glyph's KIND (the display-run registry's owner walk), so\n // it catches a run's glyphs riding LOOSE — caret-grace, an undo stack, and a\n // collab-materialized bare owner each leave a run's glyphs loose for at least one commit, and\n // a loose glyph is exactly as much engine-owned display as a wrapped one — a shape\n // $hasAttributeRunAncestor (ancestry into a wrapper) cannot see at all.\n // Being kind-keyed rather than shape-keyed, $isDisplayRunPiece also needs no per-piece exemption\n // for a char span's own opener/closer or its own nested `|…` run: neither is a registered piece\n // of any OTHER owner's run.\n //\n // This union widens only the ops-stream exclusion, restoring the historical contract: the\n // delta-doc length side (`$getNodeOTContribution` in delta-common.utils.ts) deliberately keeps\n // counting a LOOSE run's glyphs (via $hasAttributeRunAncestor alone, unchanged) even though the\n // ops stream now excludes them — that is not a drift to fix by widening the length side to\n // match, it is the same asymmetry the ops stream honored before the earlier loose-glyph\n // exclusion was removed.\n const isInNote = $findFirstAncestorNoteNode(currentNode) !== undefined;\n if ($isMarkerNode(currentNode) &&\n (isInNote ||\n $isOwnParaPrefixGlyph(currentNode) ||\n $hasAttributeRunAncestor(currentNode) ||\n $isDisplayRunPiece(currentNode)))\n return;\n // The para prefix's NBSP separator is presentation scaffolding ($createMarkerPrefix,\n // markerEditDeletion.utils.ts); the apply side re-synthesizes the whole prefix, so its text\n // must never enter content ops.\n if ($getState(currentNode, textTypeState) === \"marker-trailing-space\")\n return;\n let text = currentNode.getTextContent();\n // A bare cursor host (EmptyVerseCaretGuardPlugin) is collab-invisible: its insertion is never\n // emitted, so it must never appear in a delta op either.\n if (isCursorPlaceholderOnly(text))\n return;\n // A glyph-fronted note (first child is a MarkerNode) is the editable-mode shape; only\n // there does the caller render as a plain text child, and always in CALLER POSITION —\n // immediately after the opening glyph. The positional guard keeps a pathological content\n // text node that merely EQUALS the caller text (elsewhere in the note) flowing into ops.\n const previousSibling = currentNode.getPreviousSibling();\n if ($isNoteNode(parent) &&\n $isMarkerNode(previousSibling) &&\n previousSibling === parent.getFirstChild() &&\n text === getEditableCallerText(parent.getCaller())) {\n return;\n }\n const parentCharNode = $isCharNode(parent) ? parent : undefined;\n // Strip the structural NBSP separator that editable-mode char spans glue onto their\n // content text after the opening glyph (added by the USJ adaptor's `createChar` and\n // re-added by `$applyUpdate`'s `$createNote`); it is display-only, not content. POSITIONAL,\n // like the reverse adaptor's `content[0]` strip: the separator is prepended solely to the text\n // directly after the span's opening glyph, so only THAT node's leading NBSP is structural — a\n // leading NBSP on any later child (after a nested closer in `\\ft A\\+nd x\\+nd*~B`) is the\n // author's own `~` and must flow into the op on every emit.\n const parentCharFirstChild = parentCharNode?.getFirstChild();\n if (isInNote &&\n !!parentCharNode &&\n $isMarkerNode(parentCharFirstChild) &&\n previousSibling === parentCharFirstChild &&\n text.startsWith(NBSP)) {\n text = text.slice(1);\n }\n // Char-span attribute display runs (bare `|…`, no NBSP prefix — see usj-editor.adaptor's\n // `addCharAttributes`) carry no NBSP prefix to strip against, so the prefix check alone can't\n // catch them; the textType state tag is the other signal, kept alongside the prefix check for\n // the legacy NBSP-prefixed (milestone) attribute text. The legacy byte arm is LOAD-BEARING for\n // compatibility, not a leftover: deployed peers and persisted OT documents (ScriptureForge\n // shares these collab documents) predate the state-tagged format, and their stored deltas\n // replay through here — without the byte check, that replayed attribute text would leak display\n // bytes into content. Do not retire it while any pre-tag document or peer can reach this\n // editor. Text inside an AttributeRunNode wrapper\n // is excluded regardless of its own textType tag: the wrapper is an engine-owned presentation\n // region (see AttributeRunNode.ts), so anything riding inside it is presentation, not content,\n // whether or not it happens to also carry the \"attribute\" state tag.\n const isNodeAttributeText = text.startsWith(NODE_ATTRIBUTE_PREFIX) ||\n $getState(currentNode, textTypeState) === \"attribute\" ||\n $hasAttributeRunAncestor(currentNode);\n const isPlaceholderText = !!parentCharNode &&\n text === EMPTY_CHAR_PLACEHOLDER_TEXT &&\n parentCharNode.getChildrenSize() === 1;\n const activeEmbed = $getActiveEmbedContext(currentNode, openEmbeds);\n // An embed's `contents` ops are a SELF-CONTAINED SUB-DOCUMENT: they are materialized on the\n // receive side into a freshly built note/unknown, with no surrounding document to inherit from,\n // and read back out by `$getParticularNodeOps(embedNode)`, whose DFS starts AT the embed and so\n // has no ambient char stack at all. `openCharNodes` is the whole walk's stack, so inside an\n // embed it still holds char spans opened OUTSIDE it — spans that are not part of the embed's\n // content and have no representation in the sub-document. Scoping the stack to spans within the\n // embed is what makes this producer agree with `$getParticularNodeOps`; without it a note\n // inserted mid-span (`\\nd asdf\\nd*`) shipped `char: [{nd}, {fr}]` on its own first\n // content op, and the apply side built the enclosing `\\nd` INSIDE the note with a nested\n // `\\+fr` under it. `children` is the embed's entire subtree (`$dfs` from the embed node), not\n // just its direct children, so a span opened at any depth inside the embed is kept.\n const embedScopedCharNodes = activeEmbed\n ? openCharNodes.filter((charNode) => activeEmbed.children.includes(charNode))\n : openCharNodes;\n const textOp = $getTextOp(currentNode, embedScopedCharNodes);\n textOp.insert = text;\n if (activeEmbed) {\n if (!text || text === NBSP || isNodeAttributeText)\n return;\n activeEmbed.contentsOps?.push(textOp);\n }\n else {\n // Attribute display text is presentation-only regardless of WHERE it rides — inside a char\n // span (the original, narrower rule) or, like a milestone's or a verse's \\va/\\vp value, as a\n // plain sibling with no CharNode parent at all.\n const shouldSkipTextOp = isPlaceholderText || isNodeAttributeText;\n if (!shouldSkipTextOp) {\n ops.push(textOp);\n }\n }\n const hasMeaningfulText = text !== \"\" && !isPlaceholderText && !(isNodeAttributeText && !!parentCharNode);\n if (openCharNodes.length > 0 && hasMeaningfulText) {\n for (const charNode of openCharNodes) {\n charContentProduced.add(charNode);\n }\n }\n}\nfunction $handleCharNodes(currentNode, currentIndex, dfsNodes, openCharNodes, charContentProduced, openEmbeds, ops) {\n if ($isCharNode(currentNode) && !openCharNodes.includes(currentNode)) {\n openCharNodes.push(currentNode);\n }\n const nextDfsNode = dfsNodes[currentIndex + 1];\n for (const openCharNode of openCharNodes.toReversed()) {\n if ($isElementNodeClosing(openCharNode, nextDfsNode)) {\n openCharNodes.pop();\n if (!charContentProduced.has(openCharNode)) {\n const emptyCharOp = $getEmptyCharOp(openCharNode);\n const activeEmbed = $getActiveEmbedContext(openCharNode, openEmbeds);\n if (activeEmbed) {\n activeEmbed.contentsOps?.push(emptyCharOp);\n }\n else {\n ops.push(emptyCharOp);\n }\n }\n charContentProduced.delete(openCharNode);\n }\n }\n}\nfunction $handleNoteNodes(currentNode, ops, openEmbeds) {\n if (!$isNoteNode(currentNode))\n return;\n const noteOp = $getNoteOp(currentNode);\n const parentEmbed = $getActiveEmbedContext(currentNode, openEmbeds);\n const embedContext = {\n node: currentNode,\n children: $dfs(currentNode).map((n) => n.node),\n contentsOps: noteOp.insert.note?.contents?.ops,\n };\n openEmbeds.push(embedContext);\n if (parentEmbed?.contentsOps)\n parentEmbed.contentsOps.push(noteOp);\n else\n ops.push(noteOp);\n}\nfunction $handleUnknownNodes(currentNode, ops, openEmbeds) {\n if (!$isUnknownNode(currentNode))\n return;\n const unknownOp = $getUnknownOp(currentNode);\n const parentEmbed = $getActiveEmbedContext(currentNode, openEmbeds);\n const embedContext = {\n node: currentNode,\n children: $dfs(currentNode).map((n) => n.node),\n contentsOps: unknownOp.insert.unknown?.contents?.ops,\n };\n openEmbeds.push(embedContext);\n if (parentEmbed?.contentsOps)\n parentEmbed.contentsOps.push(unknownOp);\n else\n ops.push(unknownOp);\n}\n/**\n * Copies `node`'s unknown attributes onto `payload` — the embed or attribute object it is emitted\n * in — so they reach the peer. The node holds them either way; dropping them here is invisible to\n * every USJ round-trip and loses them only on the wire.\n *\n * Reads node state: call inside `editorState.read()`, as every op builder in this module is.\n */\nfunction $assignUnknownAttributes(payload, node) {\n const unknownAttributes = node.getUnknownAttributes();\n if (unknownAttributes)\n Object.assign(payload, unknownAttributes);\n}\nfunction $getBookOp(currentNode) {\n const book = { style: BOOK_MARKER, code: currentNode.__code };\n $assignUnknownAttributes(book, currentNode);\n return { insert: LF, attributes: { book } };\n}\nfunction $getChapterOp(currentNode) {\n const chapter = { style: CHAPTER_MARKER, number: currentNode.__number };\n if (currentNode.__sid) {\n chapter.sid = currentNode.__sid;\n }\n if (currentNode.__altnumber) {\n chapter.altnumber = currentNode.__altnumber;\n }\n if (currentNode.__pubnumber) {\n chapter.pubnumber = currentNode.__pubnumber;\n }\n $assignUnknownAttributes(chapter, currentNode);\n return { insert: { chapter } };\n}\nexport function $getParaOp(node) {\n const para = { style: node.__marker };\n $assignUnknownAttributes(para, node);\n return { insert: LF, attributes: { para } };\n}\nfunction $getVerseOp(currentNode) {\n const verse = { style: VERSE_MARKER, number: currentNode.__number };\n if (currentNode.__sid) {\n verse.sid = currentNode.__sid;\n }\n if (currentNode.__altnumber) {\n verse.altnumber = currentNode.__altnumber;\n }\n if (currentNode.__pubnumber) {\n verse.pubnumber = currentNode.__pubnumber;\n }\n $assignUnknownAttributes(verse, currentNode);\n return { insert: { verse } };\n}\nfunction $getMilestoneOp(currentNode) {\n const milestone = { style: currentNode.__marker };\n if (currentNode.__sid) {\n milestone.sid = currentNode.__sid;\n }\n if (currentNode.__eid) {\n milestone.eid = currentNode.__eid;\n }\n if (currentNode.__attributeOrder) {\n milestone.attributeOrder = currentNode.__attributeOrder;\n }\n $assignUnknownAttributes(milestone, currentNode);\n return { insert: { milestone } };\n}\nfunction $getImmutableUnmatchedOp(currentNode) {\n return { insert: { unmatched: { marker: currentNode.__marker } } };\n}\nfunction $getNoteOp(currentNode) {\n const note = {\n style: currentNode.__marker,\n caller: currentNode.__caller,\n };\n if (currentNode.__category) {\n note.category = currentNode.__category;\n }\n // Carry unknown attributes (e.g. the unclosed-note `closed=\"false\"`) so the round-trip is\n // lossless — `$applyUpdate`'s `$createNote` already reads them back via\n // `getUnknownAttributes(…, OT_NOTE_PROPS)`, matching how unknown-embed ops behave.\n $assignUnknownAttributes(note, currentNode);\n if (currentNode.getChildrenSize() > 1) {\n note.contents = { ops: [] };\n }\n const op = { insert: { note } };\n const segment = $getState(currentNode, segmentState);\n if (segment) {\n op.attributes = { segment };\n }\n return op;\n}\nfunction $getEmptyCharOp(charNode) {\n const op = { insert: \"\" };\n const char = $buildCharAttribute([charNode]);\n if (char) {\n op.attributes = { char };\n }\n return op;\n}\nfunction $getUnknownOp(currentNode) {\n const unknown = { tag: currentNode.getTag() };\n const marker = currentNode.getMarker();\n if (marker)\n unknown.marker = marker;\n $assignUnknownAttributes(unknown, currentNode);\n if (currentNode.getChildrenSize() > 0)\n unknown.contents = { ops: [] };\n return { insert: { unknown } };\n}\nfunction $getActiveEmbedContext(node, openEmbeds) {\n for (let i = openEmbeds.length - 1; i >= 0; i--) {\n const embed = openEmbeds[i];\n if (embed.children.includes(node))\n return embed;\n }\n return undefined;\n}\nfunction $closeCompletedEmbeds(nextDfsNode, openEmbeds) {\n for (let i = openEmbeds.length - 1; i >= 0; i--) {\n if ($isElementNodeClosing(openEmbeds[i].node, nextDfsNode)) {\n openEmbeds.splice(i, 1);\n }\n }\n}\nfunction $buildCharAttribute(charNodes) {\n if (charNodes.length === 0)\n return undefined;\n const items = charNodes.map($buildCharItem);\n return items.length === 1 ? items[0] : items;\n}\nfunction $buildCharItem(charNode) {\n const charItem = { style: charNode.__marker };\n const cid = $getState(charNode, charIdState);\n if (cid)\n charItem.cid = cid;\n $assignUnknownAttributes(charItem, charNode);\n return charItem;\n}\n","import { jsx as _jsx } from \"react/jsx-runtime\";\n/** Conforms with USJ v3.1 @see https://docs.usfm.bible/usfm/3.1/note/index.html */\nimport { $getParticularNodeOps } from \"../../plugins/usj/collab/editor-delta.adaptor\";\nimport { $dfs } from \"@lexical/utils\";\nimport { $applyNodeReplacement, $getNodeByKey, DecoratorNode, isHTMLElement, } from \"lexical\";\nimport { $isNoteNode, GENERATOR_NOTE_CALLER, HIDDEN_NOTE_CALLER } from \"shared\";\nexport const IMMUTABLE_NOTE_CALLER_VERSION = 1;\nexport class ImmutableNoteCallerNode extends DecoratorNode {\n __caller;\n __previewText;\n __onClick;\n constructor(caller = GENERATOR_NOTE_CALLER, previewText = \"\", onClick, key) {\n super(key);\n this.__caller = caller;\n this.__previewText = previewText;\n this.__onClick = onClick ?? (() => undefined);\n }\n static getType() {\n return \"immutable-note-caller\";\n }\n static clone(node) {\n const { __caller, __previewText, __onClick, __key } = node;\n return new ImmutableNoteCallerNode(__caller, __previewText, __onClick, __key);\n }\n static importDOM() {\n return {\n span: (node) => {\n if (!isNoteCallerElement(node))\n return null;\n return {\n conversion: $convertNoteCallerElement,\n priority: 1,\n };\n },\n };\n }\n static importJSON(serializedNode) {\n return $createImmutableNoteCallerNode().updateFromJSON(serializedNode);\n }\n updateFromJSON(serializedNode) {\n return super\n .updateFromJSON(serializedNode)\n .setCaller(serializedNode.caller)\n .setPreviewText(serializedNode.previewText)\n .setOnClick(serializedNode.onClick);\n }\n setCaller(caller) {\n if (this.__caller === caller)\n return this;\n const self = this.getWritable();\n self.__caller = caller;\n return self;\n }\n getCaller() {\n const self = this.getLatest();\n return self.__caller;\n }\n setPreviewText(previewText) {\n if (this.__previewText === previewText)\n return this;\n const self = this.getWritable();\n self.__previewText = previewText;\n return self;\n }\n getPreviewText() {\n const self = this.getLatest();\n return self.__previewText;\n }\n setOnClick(onClick) {\n if (this.__onClick === onClick)\n return this;\n const self = this.getWritable();\n self.__onClick = onClick;\n return self;\n }\n getOnClick() {\n const self = this.getLatest();\n return self.__onClick;\n }\n createDOM() {\n const dom = document.createElement(\"span\");\n dom.classList.add(this.__type);\n dom.setAttribute(\"data-caller\", this.__caller);\n dom.setAttribute(\"data-preview-text\", this.__previewText);\n return dom;\n }\n updateDOM(prevNode) {\n if (prevNode.__caller !== this.__caller)\n return true;\n return false;\n }\n exportDOM(editor) {\n const { element } = super.exportDOM(editor);\n if (element && isHTMLElement(element)) {\n element.classList.add(this.getType());\n element.setAttribute(\"data-caller\", this.getCaller());\n element.setAttribute(\"data-preview-text\", this.getPreviewText());\n }\n return { element };\n }\n decorate(editor) {\n const noteNode = this.getParent();\n if (!noteNode)\n return null;\n const noteNodeKey = noteNode.getKey();\n const noteIsCollapsed = noteNode.getIsCollapsed();\n const callerNodeKey = this.__key;\n const onClick = (event) => this.__onClick?.(event, noteNodeKey, noteIsCollapsed, () => getNoteCaller(editor, noteNodeKey), (caller) => setNoteCaller(editor, noteNodeKey, callerNodeKey, caller), () => getNoteOps(editor, noteNodeKey), () => getNoteIndex(editor, noteNodeKey));\n const callerId = `${this.__caller}_${this.__previewText}}`.replace(/\\s+/g, \"\").substring(0, 25);\n return (_jsx(\"button\", { onClick: onClick, title: this.__previewText, \"data-caller-id\": callerId, children: this.__caller === GENERATOR_NOTE_CALLER && noteIsCollapsed\n ? // Caller is generated by CSS (footnote or cross-reference sequence, per note marker)\n \"\"\n : this.__caller === HIDDEN_NOTE_CALLER && noteIsCollapsed\n ? // PT9: the hidden caller displays as `*` when collapsed\n \"*\"\n : this.__caller }));\n }\n exportJSON() {\n return {\n type: this.getType(),\n caller: this.getCaller(),\n previewText: this.getPreviewText(),\n onClick: this.getOnClick(),\n version: IMMUTABLE_NOTE_CALLER_VERSION,\n };\n }\n // Mutation\n isKeyboardSelectable() {\n return false;\n }\n}\nfunction $convertNoteCallerElement(element) {\n const caller = element.getAttribute(\"data-caller\") ?? \"\";\n const previewText = element.getAttribute(\"data-preview-text\") ?? \"\";\n const node = $createImmutableNoteCallerNode(caller, previewText);\n return { node };\n}\nexport function $createImmutableNoteCallerNode(caller, previewText, onClick) {\n return $applyNodeReplacement(new ImmutableNoteCallerNode(caller, previewText, onClick));\n}\nfunction isNoteCallerElement(node) {\n if (!node)\n return false;\n return node.classList.contains(ImmutableNoteCallerNode.getType());\n}\nexport function $isImmutableNoteCallerNode(node) {\n return node instanceof ImmutableNoteCallerNode;\n}\nexport function isSerializedImmutableNoteCallerNode(node) {\n return node?.type === ImmutableNoteCallerNode.getType();\n}\n// `getEditorState().read`, NOT `editor.read`, in this and the two getters below: all three are\n// handed to the caller-click callback as lazy thunks, and `editor.read()` force-flushes any\n// in-flight update when invoked mid-dispatch — the frozen-state hazard the project rule exists\n// to prevent.\nfunction getNoteCaller(editor, noteNodeKey) {\n return editor.getEditorState().read(() => {\n const noteNode = $getNodeByKey(noteNodeKey);\n if (!$isNoteNode(noteNode))\n throw new Error(`getNoteCaller: Note node not found: ${noteNodeKey}`);\n return noteNode.getCaller();\n });\n}\nfunction setNoteCaller(editor, noteNodeKey, callerNodeKey, caller) {\n editor.update(() => {\n const noteNode = $getNodeByKey(noteNodeKey);\n if (!$isNoteNode(noteNode))\n throw new Error(`setNoteCaller: Note node not found: ${noteNodeKey}`);\n noteNode.setCaller(caller);\n const callerNode = $getNodeByKey(callerNodeKey);\n if (!$isImmutableNoteCallerNode(callerNode))\n throw new Error(`setNoteCaller: Caller node not found: ${callerNodeKey}`);\n callerNode.setCaller(caller);\n });\n}\nfunction getNoteOps(editor, noteNodeKey) {\n return editor.getEditorState().read(() => {\n const noteNode = $getNodeByKey(noteNodeKey);\n if (!$isNoteNode(noteNode))\n throw new Error(`getNoteOps: Note node not found: ${noteNodeKey}`);\n return $getParticularNodeOps(noteNode);\n });\n}\n/**\n * The note's document-order index among all of the document's notes — the same order a USJ walk\n * yields them, so it addresses the corresponding entry of a USJ-built notes list (e.g. a footnotes\n * pane). `undefined` when the note is no longer attached.\n */\nfunction getNoteIndex(editor, noteNodeKey) {\n return editor.getEditorState().read(() => {\n let index = 0;\n for (const { node } of $dfs()) {\n if (!$isNoteNode(node))\n continue;\n if (node.getKey() === noteNodeKey)\n return index;\n index += 1;\n }\n return undefined;\n });\n}\n/** Possible note callers to use when caller is '+'. Up to 2 characters are used, e.g. a-zz */\nexport const defaultNoteCallers = [\n \"a\",\n \"b\",\n \"c\",\n \"d\",\n \"e\",\n \"f\",\n \"g\",\n \"h\",\n \"i\",\n \"j\",\n \"k\",\n \"l\",\n \"m\",\n \"n\",\n \"o\",\n \"p\",\n \"q\",\n \"r\",\n \"s\",\n \"t\",\n \"u\",\n \"v\",\n \"w\",\n \"x\",\n \"y\",\n \"z\",\n];\n/** Default cross-reference caller to use when caller is '+' on a cross-reference note and no\n * `crossRefCallers` option is set. */\nexport const defaultCrossRefCallers = [\"†\"];\n","import { getUsjDocumentLocationTypeName, indexesFromUsjJsonPath, isUsjAttributeKeyLocation, isUsjAttributeMarkerLocation, isUsjClosingAttributeMarkerLocation, isUsjClosingMarkerLocation, isUsjMarkerLocation, isUsjPropertyValueLocation, isUsjTextContentLocation, usjJsonPathFromIndexes, } from \"@eten-tech-foundation/scripture-utilities\";\nimport { $createPoint, $createRangeSelection, $getRoot, $getSelection, $isElementNode, $isRangeSelection, $isTextNode, } from \"lexical\";\nimport { $getElementOffsetFromLogicalIndex, $getLogicalContentItems, $getLogicalIndexOfChild, $getLogicalParent, $getLogicalPointFromElementPoint, $getLogicalTextLocation, $getTextNodeAtLogicalOffset, $isMarkerNode, $isParaLikeNode, $isTypedMarkNode, $isVerseBlockNode, $isVisibleMarkerNode, $shouldIgnoreNodeForContentIndexes, } from \"shared\";\n/**\n * Converts a USJ SelectionRange or AnnotationRange to an editor RangeSelection.\n *\n * This function takes a USJ selection object and creates a corresponding editor RangeSelection.\n * It determines the start and end nodes based on the provided selection range and creates a new\n * RangeSelection with appropriate anchor and focus points.\n *\n * @param selection - The USJ selection range to convert. Can be either a SelectionRange or\n * AnnotationRange.\n * @returns A new editor RangeSelection object if the conversion is successful, or `undefined` if\n * the required nodes or offsets cannot be found.\n *\n * @remarks\n * - If the 'end' property of the selection is undefined (indicating this is a location rather than\n * a range), it defaults to the 'start' value.\n * - If either the start or end node cannot be found, or if their offsets are undefined, the\n * function returns undefined.\n * - In the block verse layout it always returns `undefined`: that layout splits a paragraph\n * spanning verses across their blocks, so the editor's content indexes no longer match the\n * source USJ's and no location can be resolved. Callers that need to tell a host why report it\n * through their own logger - these are `$` functions with none threaded in.\n */\nexport function $getRangeFromUsjSelection(selection) {\n if ($hasVerseBlocks())\n return undefined;\n const { start } = selection;\n let { end } = selection;\n end ??= start;\n // Find the start and end nodes with offsets based on the location.\n let [startNode, startOffset] = $getNodeFromLocation(start);\n let [endNode, endOffset] = $getNodeFromLocation(end);\n if (!startNode || !endNode || startOffset === undefined || endOffset === undefined)\n return undefined;\n [startNode, startOffset] = $normalizeVisibleMarkerPoint(startNode, startOffset);\n [endNode, endOffset] = $normalizeVisibleMarkerPoint(endNode, endOffset);\n // Create selection range.\n const editorSelection = $createRangeSelection();\n editorSelection.anchor = $createPoint(startNode.getKey(), startOffset, $getPointType(startNode));\n editorSelection.focus = $createPoint(endNode.getKey(), endOffset, $getPointType(endNode));\n return editorSelection;\n}\n/**\n * Retrieves the current USJ selection range from the editor.\n *\n * This function extracts the selection range from the editor's current state. It handles both\n * forward and backward selections, as well as collapsed (single point) selections.\n *\n * @returns A USJ `SelectionRange` object containing the start and end positions of the selection,\n * or `undefined` if there is no valid range selection. Always `undefined` in the block verse\n * layout - see {@link $getRangeFromUsjSelection} for why.\n */\nexport function $getUsjSelectionFromEditor() {\n if ($hasVerseBlocks())\n return undefined;\n const editorSelection = $getSelection();\n if (!editorSelection || !$isRangeSelection(editorSelection))\n return;\n const startNode = editorSelection.isBackward()\n ? editorSelection.focus.getNode()\n : editorSelection.anchor.getNode();\n const startOffset = editorSelection.isBackward()\n ? editorSelection.focus.offset\n : editorSelection.anchor.offset;\n const start = $getLocationFromNode(startNode, startOffset);\n if (editorSelection.isCollapsed())\n return { start };\n const endNode = editorSelection.isBackward()\n ? editorSelection.anchor.getNode()\n : editorSelection.focus.getNode();\n const endOffset = editorSelection.isBackward()\n ? editorSelection.anchor.offset\n : editorSelection.focus.offset;\n const end = $getLocationFromNode(endNode, endOffset);\n return { start, end };\n}\nfunction $getNodeFromLocation(location) {\n // Handle UsjTextContentLocation first (most common case)\n if (isUsjTextContentLocation(location)) {\n const jsonPathIndexes = indexesFromUsjJsonPath(location.jsonPath);\n let currentNode = $getRoot();\n for (let i = 0; i < jsonPathIndexes.length; i++) {\n if (!currentNode || !$isElementNode(currentNode))\n return [undefined, undefined];\n const item = $getLogicalContentItems(currentNode)[jsonPathIndexes[i]];\n if (!item)\n return [undefined, undefined];\n if (item.type === \"text\") {\n // Text items are terminal — the path must end here.\n if (i !== jsonPathIndexes.length - 1)\n return [undefined, undefined];\n return $getTextNodeAtLogicalOffset(item, location.offset) ?? [undefined, undefined];\n }\n currentNode = item.node;\n }\n // The jsonPath resolved to an ElementNode (e.g. \"$.content[0]\"): interpret offset as a\n // logical child boundary offset and return an element point.\n if (currentNode && $isElementNode(currentNode)) {\n return [currentNode, $getElementOffsetFromLogicalIndex(currentNode, location.offset)];\n }\n return [undefined, undefined];\n }\n // Handle UsjAttributeKeyLocation and UsjAttributeMarkerLocation BEFORE UsjMarkerLocation\n // because UsjAttributeMarkerLocation has keyName but no offsets, similar to UsjMarkerLocation.\n // Checking for keyName first ensures correct type narrowing.\n // Note: Attribute markers are not yet represented in the editor, so we position at the closest\n // available location (the end of the element's content, since attributes come after content).\n if (isUsjAttributeKeyLocation(location) || isUsjAttributeMarkerLocation(location)) {\n const node = $navigateToNode(location.jsonPath);\n if (!node)\n return [undefined, undefined];\n // For ElementNodes, position at end of last text child\n if ($isElementNode(node)) {\n const lastChild = node.getLastChild();\n if (lastChild && $isTextNode(lastChild))\n return [lastChild, lastChild.getTextContent().length];\n }\n // For decorator nodes (e.g., ImmutableChapterNode) or elements with no children,\n // position at the start of the next sibling\n const nextSibling = node.getNextSibling();\n if (nextSibling && $isElementNode(nextSibling))\n return [nextSibling, 0];\n return [undefined, undefined];\n }\n // Handle UsjClosingAttributeMarkerLocation BEFORE UsjMarkerLocation/UsjClosingMarkerLocation.\n // Note: Attribute markers are not yet represented in the editor, so we position at the closest\n // available location (the end of the element's content).\n if (isUsjClosingAttributeMarkerLocation(location)) {\n const node = $navigateToNode(location.jsonPath);\n if (!node)\n return [undefined, undefined];\n // For ElementNodes, position at end of last text child if it exists\n if ($isElementNode(node)) {\n const lastChild = node.getLastChild();\n if (lastChild && $isTextNode(lastChild))\n return [lastChild, lastChild.getTextContent().length];\n }\n // For decorator nodes (e.g., ImmutableChapterNode) or elements with no children,\n // position at the start of the next sibling\n const nextSibling = node.getNextSibling();\n if (nextSibling && $isElementNode(nextSibling))\n return [nextSibling, 0];\n return [undefined, undefined];\n }\n // Handle UsjMarkerLocation - position at the beginning of the opening marker\n if (isUsjMarkerLocation(location)) {\n const node = $navigateToNode(location.jsonPath);\n if (!node || !$isElementNode(node))\n return [undefined, undefined];\n const markerNode = $findMarkerNode(node, \"opening\");\n if (markerNode)\n return [markerNode, 0];\n // Fallback: if no marker node, position at start of first child\n const firstChild = node.getFirstChild();\n if (firstChild && $isTextNode(firstChild))\n return [firstChild, 0];\n return [undefined, undefined];\n }\n // Handle UsjClosingMarkerLocation - position within the closing marker\n if (isUsjClosingMarkerLocation(location)) {\n const node = $navigateToNode(location.jsonPath);\n if (!node || !$isElementNode(node))\n return [undefined, undefined];\n const markerNode = $findMarkerNode(node, \"closing\");\n if (markerNode) {\n // Validate offset is within bounds\n const text = markerNode.getTextContent();\n const offset = Math.min(location.closingMarkerOffset, text.length);\n return [markerNode, offset];\n }\n // Fallback: if no closing marker, position at end of last text child\n const lastChild = node.getLastChild();\n if (lastChild && $isTextNode(lastChild))\n return [lastChild, lastChild.getTextContent().length];\n return [undefined, undefined];\n }\n // Handle UsjPropertyValueLocation - position within a property value (e.g., marker name)\n if (isUsjPropertyValueLocation(location)) {\n // Extract the property name from the jsonPath (e.g., \"$.content[0].marker\" -> \"marker\")\n const propertyMatch = location.jsonPath.match(/\\.(\\w+)$|^\\$\\.(\\w+)$|\\['([^']+)'\\]$/);\n const propertyName = propertyMatch?.[1] ?? propertyMatch?.[2] ?? propertyMatch?.[3];\n const node = $navigateToNode(location.jsonPath);\n if (!node || !$isElementNode(node))\n return [undefined, undefined];\n if (propertyName === \"marker\") {\n // Position within the marker name in the opening MarkerNode\n const markerNode = $findMarkerNode(node, \"opening\");\n if (markerNode) {\n // The text is \"\\marker \" - propertyOffset 0 maps to offset 1 (after backslash)\n const offset = location.propertyOffset + 1;\n const text = markerNode.getTextContent();\n return [markerNode, Math.min(offset, text.length)];\n }\n }\n // Fallback for other properties or if marker node not found\n const firstChild = node.getFirstChild();\n if (firstChild && $isTextNode(firstChild))\n return [firstChild, 0];\n return [undefined, undefined];\n }\n // All UsjDocumentLocation subtypes should be handled above\n throw new Error(`Unsupported UsjDocumentLocation type: ${getUsjDocumentLocationTypeName(location)}. ` +\n \"All UsjDocumentLocation subtypes should be supported: UsjMarkerLocation, \" +\n \"UsjClosingMarkerLocation, UsjTextContentLocation, UsjPropertyValueLocation, \" +\n \"UsjAttributeKeyLocation, UsjAttributeMarkerLocation, and\" +\n `UsjClosingAttributeMarkerLocation. Received: ${JSON.stringify(location)}`);\n}\nfunction $normalizeVisibleMarkerPoint(node, offset) {\n if (!$isVisibleMarkerNode(node))\n return [node, offset];\n const textLength = node.getTextContent().length;\n // If selection resolves inside or at the beginning of a visible marker,\n // normalize to a parent ElementNode point at the marker's child index.\n if (offset < 0 || offset >= textLength)\n return [node, offset];\n const parent = node.getParent();\n if (!parent || !$isElementNode(parent))\n return [node, offset];\n const indexWithinParent = node.getIndexWithinParent();\n if (indexWithinParent < 0)\n return [node, offset];\n return [parent, indexWithinParent];\n}\nfunction $getPointType(node) {\n return $isElementNode(node) ? \"element\" : \"text\";\n}\n/**\n * Finds a MarkerNode or ImmutableTypedTextNode marker child with the specified syntax.\n * @param parent - The parent element node to search in.\n * @param syntax - The marker syntax to find (\"opening\" or \"closing\").\n * @returns The MarkerNode or ImmutableTypedTextNode if found, undefined otherwise.\n */\nfunction $findMarkerNode(parent, syntax) {\n const children = parent.getChildren();\n for (const child of children) {\n // Check for editable MarkerNode\n if ($isMarkerNode(child) && child.getMarkerSyntax() === syntax)\n return child;\n // Also check for selfClosing when looking for closing\n if (syntax === \"closing\" && $isMarkerNode(child) && child.getMarkerSyntax() === \"selfClosing\") {\n return child;\n }\n if ($isVisibleMarkerNode(child)) {\n const text = child.getTextContent();\n const isClosing = text.endsWith(\"*\");\n if ((syntax === \"opening\" && !isClosing) || (syntax === \"closing\" && isClosing)) {\n return child;\n }\n }\n }\n return undefined;\n}\n/**\n * Navigates to a node using jsonPath indexes.\n * @param jsonPath - The jsonPath string to navigate.\n * @returns The node at the path, or undefined if not found.\n */\nfunction $navigateToNode(jsonPath) {\n // Extract just the content path portion (strip property suffix if present)\n const contentPathMatch = new RegExp(/^(\\$(?:\\.content\\[\\d+\\])*)(?:\\.|$|\\[)/).exec(jsonPath);\n const contentPath = contentPathMatch ? contentPathMatch[1] : jsonPath;\n const jsonPathIndexes = indexesFromUsjJsonPath(contentPath);\n let currentNode = $getRoot();\n for (const index of jsonPathIndexes) {\n if (!currentNode || !$isElementNode(currentNode))\n return undefined;\n const item = $getLogicalContentItems(currentNode)[index];\n currentNode = item?.type === \"element\" ? item.node : undefined;\n }\n return currentNode;\n}\n/**\n * Gets the location from a Lexical node and offset, emitting the appropriate UsjDocumentLocation\n * subtype based on the node type.\n *\n * - For MarkerNode with \"opening\" syntax at offset 0: UsjMarkerLocation\n * - For MarkerNode with \"opening\" syntax at offset > 0: UsjPropertyValueLocation (within marker name)\n * - For MarkerNode with \"closing\" syntax: UsjClosingMarkerLocation\n * - For an element point before a read-only marker glyph: UsjMarkerLocation for the opening glyph,\n * UsjClosingMarkerLocation for the closing one\n * - For regular TextNode: UsjTextContentLocation\n *\n * @param node - The Lexical node.\n * @param offset - The offset within the node's text content.\n * @returns The appropriate UsjDocumentLocation subtype.\n */\nfunction $getLocationFromNode(node, offset) {\n if ($isMarkerNode(node)) {\n const markerSyntax = node.getMarkerSyntax();\n // Prefer anchoring to the previous node if the marker is scaffolding for it.\n const anchorNode = $getMarkerAnchorNode(node);\n const anchorJsonPath = anchorNode\n ? usjJsonPathFromIndexes($getJsonPathIndexes(anchorNode))\n : usjJsonPathFromIndexes($getJsonPathIndexes(node));\n if (markerSyntax === \"closing\" || markerSyntax === \"selfClosing\") {\n // UsjClosingMarkerLocation: position within the closing marker (e.g., \\nd*)\n return {\n jsonPath: anchorJsonPath,\n closingMarkerOffset: offset,\n };\n }\n // Opening marker\n if (offset === 0) {\n // UsjMarkerLocation: at the very beginning (the backslash)\n return {\n jsonPath: anchorJsonPath,\n };\n }\n // Within the marker name text (after the backslash)\n // The text is \"\\marker \" so offset 1 is the first char of the marker name\n // UsjPropertyValueLocation points to the marker property value\n const propertyJsonPath = `${anchorJsonPath}.marker`;\n // propertyOffset is the offset within the marker name itself (not including backslash)\n // Text is \"\\p \" - offset 1 is 'p', so propertyOffset = offset - 1\n const propertyOffset = Math.max(0, offset - 1);\n return {\n jsonPath: propertyJsonPath,\n propertyOffset,\n };\n }\n if ($isTypedMarkNode(node)) {\n // An element point on the annotation wrapper: convert to the equivalent point as if the\n // mark did not exist.\n const childrenSize = node.getChildrenSize();\n const childAtOffset = node.getChildAtIndex(Math.min(offset, childrenSize - 1));\n if ($isTextNode(childAtOffset)) {\n const localOffset = offset >= childrenSize ? childAtOffset.getTextContentSize() : 0;\n return $getLocationFromNode(childAtOffset, localOffset);\n }\n // Non-text child (e.g. a CharNode wrapped in the mark) or an empty mark (childAtOffset is\n // null): anchor on the logical parent at the mark's own position instead of falling through\n // to treat the mark itself as the logical parent, which would drop the mark's content index.\n // The mark contributes no content of its own, so the boundary before/after it is the\n // boundary before/after its own position in the logical parent.\n // Known approximation: for a mark with several children of different kinds, an INTERIOR\n // boundary (offset between two of the mark's children) does not place the point between\n // those exact children — it snaps to the front (or back) edge of the whole mark, so the\n // reported position can be off by the length of the mark's preceding text. That is a valid\n // nearby point in the correct text run; placing it exactly would require the resolution\n // side to express points inside a mark.\n const logicalParent = $getLogicalParent(node);\n if (logicalParent?.is(node.getParent())) {\n const markIndex = node.getIndexWithinParent();\n const elementOffset = offset >= childrenSize ? markIndex + 1 : markIndex;\n return $getLocationFromNode(logicalParent, elementOffset);\n }\n }\n // Element selection - offset is a child index, convert to a logical point.\n if ($isElementNode(node)) {\n const childAtOffset = node.getChildAtIndex(offset);\n if ($isVisibleMarkerNode(childAtOffset)) {\n // Which glyph the point sits before decides which location this is, by the same\n // trailing-`*` test `$findMarkerNode` resolves them back with. Keyed only on the parent, an\n // element point before the CLOSING glyph resolved to the same location as one before the\n // opening glyph, so a range framing a whole char span (or note) mapped to a collapsed one.\n const isClosingGlyph = childAtOffset.getTextContent().endsWith(\"*\");\n const jsonPath = usjJsonPathFromIndexes($getJsonPathIndexes(node));\n // Offset 0: the point is before the closing glyph's first byte, not inside it.\n return isClosingGlyph\n ? { jsonPath, closingMarkerOffset: 0 }\n : { jsonPath };\n }\n const logicalPoint = $getLogicalPointFromElementPoint(node, offset);\n if (logicalPoint.type === \"text\") {\n // The boundary falls inside a coalesced USJ text item (e.g. at an annotation edge).\n return {\n jsonPath: usjJsonPathFromIndexes([...$getJsonPathIndexes(node), logicalPoint.index]),\n offset: logicalPoint.offset,\n };\n }\n return {\n jsonPath: usjJsonPathFromIndexes($getJsonPathIndexes(node)),\n offset: logicalPoint.index,\n };\n }\n // Regular text node - UsjTextContentLocation in coalesced-USJ coordinates.\n if ($isTextNode(node)) {\n const logicalTextLocation = $getLogicalTextLocation(node, offset);\n if (logicalTextLocation) {\n return {\n jsonPath: usjJsonPathFromIndexes([\n ...$getJsonPathIndexes(logicalTextLocation.parent),\n logicalTextLocation.index,\n ]),\n offset: logicalTextLocation.offset,\n };\n }\n }\n // Fallback for nodes outside the logical content model (e.g. presentation-only text).\n return { jsonPath: usjJsonPathFromIndexes($getJsonPathIndexes(node)), offset };\n}\nfunction $getMarkerAnchorNode(markerNode) {\n const parent = markerNode.getParent();\n if (!parent || !$isElementNode(parent))\n return undefined;\n const previousContentSibling = $getPreviousContentSibling(markerNode);\n if (previousContentSibling &&\n !$isParaLikeNode(previousContentSibling) &&\n !$isTextNode(previousContentSibling) &&\n !$isTypedMarkNode(previousContentSibling)) {\n return previousContentSibling;\n }\n return parent;\n}\nfunction $getPreviousContentSibling(child) {\n let sibling = child.getPreviousSibling();\n while (sibling) {\n if (!$shouldIgnoreNodeForContentIndexes(sibling))\n return sibling;\n sibling = sibling.getPreviousSibling();\n }\n return undefined;\n}\n/**\n * Gets the jsonPath indexes from a node by traversing up to the root using logical\n * (annotation-transparent) content indexes.\n * @param node - The node to get the path for.\n * @returns An array of indexes representing the path from root to node.\n */\nfunction $getJsonPathIndexes(node) {\n const jsonPathIndexes = [];\n let current = node;\n while (current) {\n const parent = $getLogicalParent(current);\n if (!parent)\n break;\n const index = $getLogicalIndexOfChild(parent, current);\n if (index >= 0)\n jsonPathIndexes.unshift(index);\n current = parent;\n }\n return jsonPathIndexes;\n}\n/**\n * Whether the document uses the block verse layout.\n *\n * USJ locations are indexes into the source USJ's content. That layout regroups verses into blocks,\n * splitting any paragraph that spans verses into one fragment per verse, so the editor's content\n * indexes no longer line up with the USJ's - and no amount of treating the block itself as\n * transparent fixes the renumbering underneath. Locations are therefore unavailable there, rather\n * than confidently wrong.\n */\nfunction $hasVerseBlocks() {\n // Both callers run on every selection change, so this walks siblings and exits at the first\n // block rather than calling `getChildren()`, which would build an array of every root child.\n //\n // The layout is known from `ViewOptions.verseLayout`, and an editor registers `VerseBlockNode`\n // only for that layout, so `editor.hasNodes([VerseBlockNode])` looks like a cheaper answer. It\n // is not available here: `$getEditor()` needs an active *editor*, and these functions are\n // called from `editorState.read()` as well, which establishes only an active editor state.\n // Reading the layout instead would mean threading `ViewOptions` through every caller.\n for (let child = $getRoot().getFirstChild(); child; child = child.getNextSibling()) {\n if ($isVerseBlockNode(child))\n return true;\n }\n return false;\n}\n","import { $getRangeFromUsjSelection } from \"../../plugins/usj/annotation/selection.utils\";\nimport { $createImmutableNoteCallerNode, $isImmutableNoteCallerNode, } from \"./ImmutableNoteCallerNode\";\nimport { $isImmutableVerseNode } from \"./ImmutableVerseNode\";\nimport { $isSomeVerseNode } from \"./node-react.utils\";\nimport { $dfs, $findMatchingParent } from \"@lexical/utils\";\nimport { $createTextNode, $getCharacterOffsets, $getNodeByKey, $getSelection, $getState, $isElementNode, $isRangeSelection, $isTextNode, $setState, } from \"lexical\";\nimport { $createCharNode, $createImmutableTypedTextNode, $createMarkerNode, $createMarkerTrailingSeparator, $createNoteNode, $getNoteCallerPreviewText, $isCharNode, $isImmutableTypedTextNode, $isImmutableUnmatchedNode, $isMarkerNode, $isNoteNode, $moveSelectionToEnd, $normalizeSelectionOutOfGlyphText, closingMarkerText, EMPTY_CHAR_PLACEHOLDER_TEXT, getEditableCallerText, getNoteKind, NBSP, NoteNode, openingMarkerText, segmentState, textTypeState, } from \"shared\";\n/**\n * Find all ImmutableNoteCallerNodes in the given nodes tree.\n * @param nodes - Lexical node array to look in.\n * @returns an array of all ImmutableNoteCallerNodes in the tree.\n */\nexport function $findImmutableNoteCallerNodes(nodes) {\n const immutableNoteCallerNodes = [];\n function $traverse(node) {\n if ($isImmutableNoteCallerNode(node))\n immutableNoteCallerNodes.push(node);\n if (!$isElementNode(node))\n return;\n const children = node.getChildren();\n children.forEach($traverse);\n }\n nodes.forEach($traverse);\n return immutableNoteCallerNodes;\n}\n/**\n * Inserts a note at the specified selection, e.g. footnote, cross-reference, endnote.\n * @param marker - The marker type for the note.\n * @param caller - Optional note caller to override the default for the given marker.\n * @param selectionRange - Optional selection range where the note should be inserted. By default it will\n * use the current selection in the editor.\n * @param scriptureReference - Scripture reference for the note.\n * @param viewOptions - The current editor view options.\n * @param nodeOptions - The current editor node options.\n * @param logger - Logger instance.\n * @returns The inserted note node, or `undefined` if insertion failed.\n * @throws Will throw an error if the marker is not a valid note marker.\n */\nexport function $insertNote(marker, caller, selectionRange, scriptureReference, viewOptions, nodeOptions, logger) {\n if (!NoteNode.isValidMarker(marker))\n throw new Error(`$insertNote: Invalid note marker '${marker}'`);\n const selection = selectionRange ? $getRangeFromUsjSelection(selectionRange) : $getSelection();\n if (!$isRangeSelection(selection))\n return undefined;\n const children = $createNoteChildren(selection, marker, scriptureReference, viewOptions, nodeOptions, logger);\n if (children === undefined)\n return undefined;\n // PT9's caller-family rule (see `getNoteKind`): custom note markers deliberately take the\n // cross-reference default, not the footnote one.\n const resolvedCaller = caller ??\n (getNoteKind(marker) === \"crossref\"\n ? (nodeOptions.defaultCrossRefCaller ?? \"-\")\n : (nodeOptions.defaultFootnoteCaller ?? \"+\"));\n const noteNode = $createWholeNote(marker, resolvedCaller, children, viewOptions, nodeOptions, undefined, undefined);\n $insertNoteWithSelect(noteNode, selection, viewOptions);\n return noteNode;\n}\n/**\n * Whether notes BUILD collapsed under the given note mode: only `\"expanded\"` builds expanded\n * notes; `\"collapsed\"`, `\"expandInline\"`, and an unset mode all build collapsed ones (under\n * `expandInline` the NoteNodePlugin expands a note only while the caret is adjacent).\n *\n * This is the ONE predicate for constructing a note's collapsed flag and child layout — used at\n * document load (the platform adaptor's `createNote`) and at insert time (`$createWholeNote`,\n * `$insertNoteWithSelect`) so a freshly inserted note is indistinguishable from a loaded one.\n * When the two ever disagree, the flag and the layout drift apart (e.g. a collapsed-layout note\n * flagged expanded). Note: this governs CONSTRUCTION only — `$selectNote` intentionally uses a\n * different rule for expanding an existing note on selection.\n *\n * @param noteMode - The note display mode from the editor view options.\n * @returns `true` when notes are built collapsed under this mode.\n */\nexport function isCollapsedNoteMode(noteMode) {\n return noteMode !== \"expanded\";\n}\n/**\n * The closing marker glyph a collapsed caret sits immediately before, when that glyph closes the\n * char span the caret is in — otherwise `undefined`.\n *\n * This is the one insertion point where Lexical's `selection.insertNodes()` SPLITS the enclosing\n * char span rather than inserting into it, because the caret is at a child boundary with nothing\n * but the closer beyond it. The split leaves a second span holding nothing but the orphaned\n * closing glyph, which the standard-view marker-edit engine reads as a span whose opener was\n * deleted and dissolves — taking the closer's bytes with it. A caret in the MIDDLE of the span's\n * text, or outside the span entirely, does not split it and needs none of this.\n *\n * Both spellings of the same boundary are recognized: the caret at the end of the span's content\n * text (a click at the end of the word) and the caret at offset 0 of the closing glyph itself\n * (an arrow-left back off the closer).\n *\n * Read-only: safe inside `editor.update()` or either read form.\n */\nfunction $closingGlyphAfterCaret(selection) {\n if (!selection.isCollapsed())\n return undefined;\n const { anchor } = selection;\n if (anchor.type !== \"text\")\n return undefined;\n const node = anchor.getNode();\n if (!$isTextNode(node) || !$isCharNode(node.getParent()))\n return undefined;\n // MarkerNode extends TextNode, so the caret may be parked in the closing glyph itself.\n if ($isMarkerNode(node))\n return anchor.offset === 0 && node.getMarkerSyntax() === \"closing\" ? node : undefined;\n if (anchor.offset !== node.getTextContentSize())\n return undefined;\n const next = node.getNextSibling();\n return $isMarkerNode(next) && next.getMarkerSyntax() === \"closing\" ? next : undefined;\n}\n/**\n * Insert note node at the given selection, and select the note content if expanded.\n *\n * Whether the note lands collapsed is `isCollapsedNoteMode` — the same predicate that governs\n * both the child structure and the collapsed flag when notes are built at document load — so a\n * freshly inserted note matches a loaded one.\n *\n * @param noteNode - The note node to insert.\n * @param selection - The selection where to insert the note.\n * @param viewOptions - The current editor view options.\n */\nexport function $insertNoteWithSelect(noteNode, selection, viewOptions) {\n const isCollapsed = isCollapsedNoteMode(viewOptions?.noteMode);\n noteNode.setIsCollapsed(isCollapsed);\n if (!selection.isCollapsed())\n $moveSelectionToEnd(selection);\n // The caret may be parked between two of a glyph's display bytes — inside `\\v 5 `, inside a\n // closing `\\add*`. Those bytes are a picture of the node's own state, so a note dropped between\n // them would cut the picture in half and hand the right-hand half to the document as content\n // (the reported verse number arriving in the file twice). One place decides where such a point\n // really is; here it resolves to the glyph's trailing end, the ordinary position just past it.\n $normalizeSelectionOutOfGlyphText(selection);\n // At a char span's content end, place the note explicitly rather than letting `insertNodes`\n // split the span there and strand its closing glyph (see `$closingGlyphAfterCaret`). The\n // resulting shape — note inside the span, ahead of the closer — is what re-tokenizing the\n // displayed bytes gives, and what a mid-content caret already produced.\n const closingGlyph = $closingGlyphAfterCaret(selection);\n if (closingGlyph) {\n closingGlyph.insertBefore(noteNode);\n noteNode.selectNext(0, 0); // caret past the note, where `insertNodes` leaves it\n }\n else {\n selection.insertNodes([noteNode]);\n }\n if (!isCollapsed) {\n const lastCharChild = noteNode.getChildren().reverse().find($isCharNode);\n lastCharChild?.selectEnd();\n }\n}\n/**\n * Build a single note-content char span matching the reverse adaptor's `createChar` output for\n * the active `markerMode`. In editable markerMode a char span MUST begin with its opening\n * MarkerNode glyph and carry a structural NBSP content prefix; otherwise the standard-view\n * marker-edit engine's `$charNodeDeletionTransform` treats it as \"opener deleted\" and\n * unwraps it back to plain text in the same commit — which was silently emptying freshly\n * inserted footnotes. `content === \"\"` yields the lone-NBSP empty-char placeholder (matching\n * `createChar`, which prepends the NBSP prefix only to real content, then adds the placeholder).\n */\nfunction $createNoteContentChar(marker, content, viewOptions) {\n const char = $createCharNode(marker);\n // Note-content chars are built without their own closing markers, i.e. they are implicitly\n // closed — exactly what ParatextData records as closed=\"false\" (near universal on \\fr/\\ft/\n // \\xo/\\xt). Carrying the flag from creation keeps these nodes signature-identical to what\n // Tier-2 re-tokenization produces (the rebuild's fixed-point refusal depends on that) and\n // round-trips the correct USJ shape.\n char.setUnknownAttributes({ closed: \"false\" });\n const isEditable = viewOptions?.markerMode === \"editable\";\n if (isEditable)\n char.append($createMarkerNode(marker));\n // Visible marker mode shows a bare opening glyph inside the span, matching `createChar` in\n // the load adaptor (implicitly-closed note-content chars get no closer there either).\n else if (viewOptions?.markerMode === \"visible\")\n char.append($createImmutableTypedTextNode(\"marker\", openingMarkerText(marker)));\n const text = content === \"\" ? EMPTY_CHAR_PLACEHOLDER_TEXT : isEditable ? NBSP + content : content;\n char.append($createTextNode(text));\n return char;\n}\nexport function $createNoteChildren(selection, marker, scriptureReference, viewOptions, nodeOptions, logger) {\n const children = [];\n const { chapterNum, verseNum, verse } = scriptureReference ?? {};\n const chapterVerseSeparator = nodeOptions.chapterVerseSeparator ?? \":\";\n const verseRangeSeparator = nodeOptions.verseRangeSeparator ?? \"-\";\n // `verse` (e.g. \"16-18\") is only populated for a verse bridge; replace the raw \"-\" bridge\n // separator with the project's configured verseRangeSeparator (PT9 `GetFormattedVerse`). Passed as\n // a REPLACER FUNCTION, not a replacement string, so a separator containing `$` is inserted\n // literally instead of being read as a `$&`/`$1` substitution pattern.\n const referenceText = chapterNum !== undefined && verseNum !== undefined\n ? `${chapterNum}${chapterVerseSeparator}${(verse ?? `${verseNum}`).replace(/-/g, () => verseRangeSeparator)} `\n : undefined;\n switch (marker) {\n case \"f\":\n case \"fe\":\n case \"ef\":\n case \"efe\":\n if (referenceText !== undefined) {\n children.push($createNoteContentChar(\"fr\", referenceText, viewOptions));\n }\n if (!selection.isCollapsed()) {\n const quotation = $stripSelectionToQuotation(selection);\n if (quotation.length > 0) {\n children.push($createNoteContentChar(\"fq\", quotation, viewOptions));\n }\n }\n children.push($createNoteContentChar(\"ft\", \"\", viewOptions));\n break;\n case \"x\":\n case \"ex\":\n if (referenceText !== undefined) {\n children.push($createNoteContentChar(\"xo\", referenceText, viewOptions));\n }\n if (!selection.isCollapsed()) {\n const quotation = $stripSelectionToQuotation(selection);\n if (quotation.length > 0) {\n children.push($createNoteContentChar(\"xq\", quotation, viewOptions));\n }\n }\n children.push($createNoteContentChar(\"xt\", \"\", viewOptions));\n break;\n default:\n logger?.warn(`$createNoteChildren: Unsupported note marker '${marker}'`);\n return undefined;\n }\n return children;\n}\n/**\n * Creates a note node including children with the given parameters.\n * @param marker - The marker for the note.\n * @param caller - The caller for the note.\n * @param contentNodes - The content nodes for the note.\n * @param viewOptions - The view options for the note.\n * @param nodeOptions - The node options for the note.\n * @param segment - The segment for the note.\n * @param closed - The source `closed` attribute (`\"false\"` for an unterminated note). Unclosed\n * notes render expanded inline (PT9 `opennote`) regardless of `noteMode`. `undefined` (the\n * default for freshly inserted notes) behaves as closed.\n * @returns The created note node.\n */\n// Keep this function updated with logic from\n// `packages/platform/src/editor/adaptors/usj-editor.adaptor.ts` > `createNote`\nexport function $createWholeNote(marker, caller, contentNodes, viewOptions, nodeOptions, segment, closed) {\n // Unclosed notes (closed=\"false\") render expanded inline (PT9 `opennote`); only closed\n // notes honor noteMode collapse.\n const isUnclosed = closed === \"false\";\n const isCollapsed = isUnclosed ? false : isCollapsedNoteMode(viewOptions?.noteMode);\n const note = $createNoteNode(marker, caller, isCollapsed);\n if (segment)\n $setState(note, segmentState, () => segment);\n // The note's shell — its opening glyph and its caller — is atomic when the host governs those\n // two through its own UI (see ViewOptions.isNoteShellEditable). Mirrors the load path\n // (`createNote`, usj-editor.adaptor): an INSERTED note must be shaped like a reloaded one.\n const isShellAtomic = viewOptions?.isNoteShellEditable === false;\n let openingMarkerNode;\n let closingMarkerNode;\n if (viewOptions?.markerMode === \"editable\") {\n openingMarkerNode = $createMarkerNode(marker);\n if (isShellAtomic)\n openingMarkerNode.setMode(\"token\");\n // An unclosed note has no closer to display.\n if (!isUnclosed)\n closingMarkerNode = $createMarkerNode(marker, \"closing\");\n }\n else if (viewOptions?.markerMode === \"visible\") {\n // Same glyph text shapes as the load path (`createNote`): opening glyph with a plain\n // trailing space, closer bare. Glyphs are presentation-only (never serialized), so a\n // reloaded note shows the load path's shape — an inserted note must look identical.\n openingMarkerNode = $createImmutableTypedTextNode(\"marker\", openingMarkerText(marker) + \" \");\n if (!isUnclosed)\n closingMarkerNode = $createImmutableTypedTextNode(\"marker\", closingMarkerText(marker));\n }\n let callerNode;\n if (openingMarkerNode)\n note.append(openingMarkerNode);\n // Expanded layout whenever the note is expanded (either noteMode expanded OR unclosed).\n // Unlike the load path (`createNote`, usj-editor.adaptor), no `\\cat` category run is built\n // here: this constructs NEW notes, which never carry a category at insert time — there is no\n // category input on the insert path. A category acquired later heals its run through the\n // shared display-run sync.\n if (viewOptions?.markerMode === \"editable\" && !isCollapsed) {\n if (caller === \"\")\n note.append(...contentNodes);\n else {\n callerNode = $createTextNode(getEditableCallerText(note.__caller));\n if (isShellAtomic)\n callerNode.setMode(\"token\");\n note.append(callerNode, ...contentNodes);\n }\n }\n else {\n // The engine-owned NBSP separators of a collapsed note's layout, in the same tagged token\n // shape as the para-marker prefix separator (and as the load path's `createNote` builds\n // them): a bare NBSP TextNode merged into adjacent plain content on the first normalization\n // pass, after which serialization's exact-NBSP drop could no longer see the separator and\n // one display byte leaked into USJ as a data space.\n const $createSpaceNodeFn = () => $createMarkerTrailingSeparator();\n const spacedContentNodes = contentNodes.flatMap($addSpaceNodes($createSpaceNodeFn));\n if (caller === \"\")\n note.append(...spacedContentNodes);\n else {\n const previewText = $getNoteCallerPreviewText(contentNodes);\n let onClick = () => undefined;\n if (nodeOptions?.noteCallerOnClick) {\n onClick = nodeOptions.noteCallerOnClick;\n }\n callerNode = $createImmutableNoteCallerNode(note.__caller, previewText, onClick);\n note.append(callerNode, $createSpaceNodeFn(), ...spacedContentNodes);\n }\n }\n if (closingMarkerNode)\n note.append(closingMarkerNode);\n return note;\n}\n/**\n * Gets the note using the editor key or at the specified note index.\n * @param noteKeyOrIndex - The note key or index, e.g. 1 would select the second note in the editor.\n * @returns The note at the specified index, or `undefined` if not found.\n */\nexport function $getNoteByKeyOrIndex(noteKeyOrIndex) {\n if (typeof noteKeyOrIndex === \"string\") {\n const node = $getNodeByKey(noteKeyOrIndex);\n if (!$isNoteNode(node))\n return;\n return node;\n }\n const dfsNodes = $dfs();\n if (dfsNodes.length <= 0)\n return;\n const dfsNotes = dfsNodes.filter((dfsNode) => $isNoteNode(dfsNode.node));\n const note = dfsNotes[noteKeyOrIndex]?.node;\n if (!$isNoteNode(note))\n return;\n return note;\n}\n/**\n * Selects the given note node, expanding or collapsing it based on the current view options.\n *\n * Deliberately NOT `isCollapsedNoteMode` (nor its inverse): that predicate is for CONSTRUCTING\n * notes, where `expandInline` builds collapsed. Here the user is navigating INTO the note, so\n * `expandInline` must expand it (the caret is about to be adjacent — the same condition under\n * which the NoteNodePlugin keeps it open); only an always-`\"collapsed\"` mode keeps it closed.\n *\n * @param noteNode - The note node to select.\n * @param viewOptions - The current editor view options.\n */\nexport function $selectNote(noteNode, viewOptions) {\n const isCollapsed = viewOptions?.noteMode === \"collapsed\";\n noteNode.setIsCollapsed(isCollapsed);\n if (isCollapsed) {\n const nodeBefore = noteNode.getPreviousSibling();\n if ($isImmutableVerseNode(nodeBefore) || !nodeBefore) {\n const parent = noteNode.getParent();\n if (parent) {\n const nodeIndex = noteNode.getIndexWithinParent();\n parent.select(nodeIndex, nodeIndex);\n }\n }\n else\n nodeBefore.selectEnd();\n }\n else {\n const lastCharChild = noteNode.getChildren().reverse().find($isCharNode);\n lastCharChild?.selectEnd();\n }\n}\n/** Add the given space node after each child node */\nfunction $addSpaceNodes($createSpaceNodeFn) {\n return (node) => {\n if ($isImmutableTypedTextNode(node))\n return [node];\n return [node, $createSpaceNodeFn()];\n };\n}\n/**\n * Returns `true` when `node` is inside a NoteNode (i.e. an existing footnote/cross-reference\n * embedded in the selected body text) — its entire content, markers included, must be dropped.\n * `selection.getNodes()` flattens a NoteNode's descendants into the returned list alongside the\n * note itself, so skipping just the NoteNode entry is not enough; every descendant must also be\n * excluded by walking its ancestor chain.\n */\nfunction $isInsideNote(node) {\n // The walk starts at the PARENT deliberately: the NoteNode entry itself is handled by the\n // caller, and a note is not \"inside\" itself.\n const parent = node.getParent();\n return parent !== null && $findMatchingParent(parent, $isNoteNode) !== null;\n}\n/**\n * TS port of PT9 `RemoveMarkersAndFootnotes(text, isFootnote=true)`\n * (`UsfmSnippetInserter.cs:444-489`): builds a footnote/cross-reference quotation (`\\fq`/`\\xq`)\n * from a selection over body text — plain text only, with USFM markers and any nested\n * notes stripped, and embedded verse numbers converted to `\\+fv \\+fv*`.\n *\n * Endpoint handling: Lexical's `RangeSelection.getNodes()` returns whole boundary nodes even for\n * a partial selection, so the first/last selected plain TextNode is sliced to the\n * anchor/focus offset here — reusing Lexical's own `$getCharacterOffsets` (which normalizes\n * \"element\"-type points, e.g. a whole-paragraph `select(0, childrenSize)`, to real character\n * offsets) and the same anchor/focus-order slicing Lexical's `RangeSelection.getTextContent()`\n * uses internally, rather than raw `.offset` values (a raw element-point offset is a child\n * index, not a character offset, and slicing with it truncates the last node's text).\n *\n * @param selection - The selection to build the quotation from.\n * @returns The stripped, trimmed quotation text.\n */\nexport function $stripSelectionToQuotation(selection) {\n if (!$isRangeSelection(selection))\n return \"\";\n const nodes = selection.getNodes();\n if (nodes.length === 0)\n return \"\";\n const firstNode = nodes[0];\n const lastNode = nodes[nodes.length - 1];\n const isBefore = selection.anchor.isBefore(selection.focus);\n const [anchorOffset, focusOffset] = $getCharacterOffsets(selection);\n let result = \"\";\n for (const node of nodes) {\n if ($isNoteNode(node) || $isImmutableNoteCallerNode(node) || $isInsideNote(node))\n continue;\n if ($isMarkerNode(node))\n continue;\n // A stray closer's glyph bytes (`\\nd*`) are display text, not quotation content.\n // ImmutableUnmatchedNode is a TextNode subclass, so without this skip it falls through to\n // the TextNode branch below and its bytes land verbatim in the quotation.\n if ($isImmutableUnmatchedNode(node))\n continue;\n // Attribute display text is display too: a char span's `|lemma=\"…\"` run, a verse's `\\va`\n // value, and a milestone's attribute run are engine-owned bytes, not Scripture — without\n // this skip they land verbatim in the inserted note's quotation and reach the file as note\n // content.\n if ($getState(node, textTypeState) === \"attribute\")\n continue;\n // Check verse nodes before TextNode, via the union: in editable markerMode a VerseNode IS a\n // TextNode subclass, so a TextNode-first check would emit its raw glyph text instead of\n // `\\+fv` — and in visible/hidden marker mode the verse is an ImmutableVerseNode (a\n // DecoratorNode), which a bare $isVerseNode check misses entirely, silently dropping the\n // verse number from the quotation.\n if ($isSomeVerseNode(node)) {\n result += `\\\\+fv ${node.getNumber()}\\\\+fv*`;\n continue;\n }\n if ($isTextNode(node)) {\n let text = node.getTextContent();\n if (node === firstNode && node === lastNode) {\n text =\n anchorOffset < focusOffset\n ? text.slice(anchorOffset, focusOffset)\n : text.slice(focusOffset, anchorOffset);\n }\n else if (node === firstNode) {\n text = isBefore ? text.slice(anchorOffset) : text.slice(focusOffset);\n }\n else if (node === lastNode) {\n text = isBefore ? text.slice(0, focusOffset) : text.slice(0, anchorOffset);\n }\n result += text;\n }\n }\n // Collapse ASCII whitespace runs only (line joins and indentation from the source paragraph).\n // NBSP, ZWSP, and the other Unicode spaces are authored content that Paratext preserves, so\n // folding them into a plain space here would quietly rewrite the quoted text.\n return result.replace(/[ \\t\\r\\n\\f\\v]+/g, \" \").trim();\n}\n","import { ImmutableNoteCallerNode } from \"./ImmutableNoteCallerNode\";\nimport { ImmutableVerseNode } from \"./ImmutableVerseNode\";\nimport { usjBaseNodes, VerseBlockNode } from \"shared\";\nexport * from \"./ImmutableNoteCallerNode\";\nexport * from \"./ImmutableVerseNode\";\nexport * from \"./node-react.utils\";\nexport * from \"./note.utils\";\nexport * from \"./usj-node-options.model\";\n// AttributeRunNode rides in via usjBaseNodes (shared) — every USJ-shaped editor needs it, not only\n// a react host, since the shared self-healing syncs construct one directly.\nexport const usjReactNodes = [\n ImmutableNoteCallerNode,\n ImmutableVerseNode,\n ...usjBaseNodes,\n];\n/**\n * Nodes for the block verse layout (`ViewOptions.verseLayout`). Register these instead of\n * {@link usjReactNodes} when that layout is active, so every other editor's node registry is\n * unchanged.\n *\n * A Lexical editor's node types are fixed when it is created, so an editor registered with\n * `usjReactNodes` cannot be switched to the block verse layout - it has to be recreated.\n */\nexport const usjBlockVerseNodes = [\n VerseBlockNode,\n ...usjReactNodes,\n];\n","import { jsx as _jsx } from \"react/jsx-runtime\";\nimport { forwardRef } from \"react\";\nexport const FloatingBox = forwardRef((props, ref) => {\n const { coords, children, style, ...extraProps } = props;\n const shouldShow = coords !== undefined;\n return (_jsx(\"div\", { ref: ref, className: \"floating-box\", \"aria-hidden\": !shouldShow, style: {\n ...style,\n position: \"absolute\",\n zIndex: 1000,\n top: coords?.y,\n left: coords?.x,\n visibility: shouldShow ? \"visible\" : \"hidden\",\n opacity: shouldShow ? 1 : 0,\n }, ...extraProps, children: children }));\n});\n","import { useState, useCallback, useRef, useEffect } from \"react\";\nimport { autoUpdate, computePosition, shift, flip } from \"@floating-ui/dom\";\nexport function useFloatingPosition() {\n const [coords, setCoords] = useState(undefined);\n const [placement, setPlacement] = useState();\n const cleanupRef = useRef(null);\n const updatePosition = useCallback((domRange, anchorElement) => {\n if (cleanupRef.current) {\n cleanupRef.current();\n }\n const referenceElement = domRange.commonAncestorContainer.nodeType === domRange.commonAncestorContainer.TEXT_NODE\n ? domRange\n : domRange.commonAncestorContainer;\n cleanupRef.current = autoUpdate(referenceElement, anchorElement, () => {\n computePosition(referenceElement, anchorElement, {\n placement: \"bottom-start\",\n middleware: [shift(), flip()],\n })\n .then((pos) => {\n setPlacement(pos.placement);\n setCoords((prevCoords) => prevCoords?.x === pos.x && prevCoords?.y === pos.y\n ? prevCoords\n : { x: pos.x, y: pos.y });\n })\n .catch(() => {\n setCoords(undefined);\n });\n });\n }, []);\n const cleanup = useCallback(() => {\n if (cleanupRef.current) {\n setCoords(undefined);\n cleanupRef.current();\n cleanupRef.current = null;\n }\n }, []);\n useEffect(() => {\n return cleanup;\n }, [cleanup]);\n return { coords, placement, updatePosition, cleanup };\n}\n","import { useEffect } from \"react\";\nimport { useFloatingPosition } from \"./useFloatingPosition\";\nexport default function useCursorCoords({ isOpen, floatingBoxRef, }) {\n const { coords, updatePosition, cleanup, placement } = useFloatingPosition();\n useEffect(() => {\n if (!isOpen || !floatingBoxRef.current) {\n cleanup();\n return undefined;\n }\n const domRange = window.getSelection()?.getRangeAt(0);\n if (!domRange) {\n cleanup();\n return undefined;\n }\n updatePosition(domRange, floatingBoxRef.current);\n return cleanup;\n }, [cleanup, isOpen, floatingBoxRef, updatePosition]);\n return { coords, placement };\n}\n","import { jsx as _jsx } from \"react/jsx-runtime\";\nimport { memo, useMemo, useRef } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { FloatingBox } from \"./FloatingBox\";\nimport useCursorCoords from \"./useCursorCoords\";\nconst MemoizedFloatingBox = memo(FloatingBox);\n/**\n * FloatingBoxAtCursor component is responsible for rendering a floating menu\n * at the cursor position when the isOpen prop is true\n */\nexport default function FloatingBoxAtCursor({ isOpen = false, children }) {\n const floatingBoxRef = useRef(null);\n const { coords, placement } = useCursorCoords({ isOpen, floatingBoxRef });\n const renderChildren = useMemo(() => (coords ? (typeof children === \"function\" ? children : () => children) : () => null), [children, coords]);\n return createPortal(_jsx(MemoizedFloatingBox, { ref: floatingBoxRef, coords: coords, style: coords ? undefined : { display: \"none\" }, children: renderChildren({ isOpen, placement }) }), \n // Read at render rather than at module scope: this module sits in the import graph of the\n // package's utility entry points, so touching `document` on load throws for any consumer that\n // imports one of them outside a DOM environment (a Node-environment unit test, SSR).\n document.body);\n}\n","import { createContext, useContext } from \"react\";\nconst MenuContext = createContext(undefined);\nexport function useMenuContext() {\n const context = useContext(MenuContext);\n if (!context) {\n throw new Error(\"useMenuContext must be used within a MenuProvider\");\n }\n return context;\n}\nexport { MenuContext };\n","import { useState, useCallback, useMemo } from \"react\";\nexport function useMenuCore(initialMenuItems, onSelectOption) {\n const [activeIndex, setActiveIndex] = useState(0);\n const [selectedIndex, setSelectedIndex] = useState(-1);\n const menuItems = useMemo(() => initialMenuItems ?? [], [initialMenuItems]);\n const state = {\n menuItems,\n activeIndex,\n selectedIndex,\n onSelectOption: onSelectOption ?? (() => undefined),\n };\n const moveUp = useCallback(() => {\n setActiveIndex((prev) => {\n const optionsCount = menuItems.length;\n return optionsCount ? (prev - 1 + optionsCount) % optionsCount : 0;\n });\n }, [menuItems.length]);\n const moveDown = useCallback(() => {\n setActiveIndex((prev) => {\n const optionsCount = menuItems.length;\n return optionsCount ? (prev + 1) % optionsCount : 0;\n });\n }, [menuItems.length]);\n const select = useCallback(() => {\n const optionsCount = menuItems.length;\n if (activeIndex >= 0 && activeIndex < optionsCount) {\n const selectedOption = menuItems[activeIndex];\n onSelectOption?.(selectedOption);\n setSelectedIndex(activeIndex);\n }\n }, [activeIndex, menuItems, onSelectOption]);\n return {\n state,\n moveUp,\n moveDown,\n select,\n setActiveIndex,\n setSelectedIndex,\n };\n}\n","import { jsx as _jsx } from \"react/jsx-runtime\";\nimport { MenuContext } from \"./MenuContext\";\nimport { useMenuCore } from \"./useMenuCore\";\nexport function MenuRoot({ children, menuItems, onSelectOption, ...divProps }) {\n const menuContext = useMenuCore(menuItems, onSelectOption);\n return (_jsx(MenuContext.Provider, { value: menuContext, children: _jsx(\"div\", { ...divProps, children: children }) }));\n}\n","import { jsx as _jsx } from \"react/jsx-runtime\";\nimport { forwardRef, useCallback } from \"react\";\nimport { useMenuContext } from \"./MenuContext\";\nexport const MenuOption = forwardRef(({ index, children, onMouseEnter, onClick, ...props }, ref) => {\n const { state: { activeIndex }, setActiveIndex, setSelectedIndex, select, } = useMenuContext();\n const handleClick = useCallback((event) => {\n select();\n setSelectedIndex(-1);\n onClick?.(event);\n }, [onClick, select, setSelectedIndex]);\n const handleMouseEnter = useCallback((event) => {\n setActiveIndex(index);\n onMouseEnter?.(event);\n }, [index, setActiveIndex, onMouseEnter]);\n return (_jsx(\"button\", { ref: ref, role: \"menuitem\", ...props, onClick: handleClick, onMouseEnter: handleMouseEnter, \"aria-selected\": index !== undefined && activeIndex === index ? \"true\" : undefined, tabIndex: -1, children: children }));\n});\n","import { jsx as _jsx } from \"react/jsx-runtime\";\nimport { Children, cloneElement, isValidElement, useEffect, useMemo, useRef, } from \"react\";\nimport { useMenuContext } from \"./MenuContext\";\nimport { MenuOption } from \"./Option\";\nexport function MenuOptions({ children, autoIndex = true, ...divProps }) {\n const menuRef = useRef(null);\n const { state: { activeIndex, menuItems }, } = useMenuContext();\n const renderChildren = useMemo(() => (menuItems ? (typeof children === \"function\" ? children : () => children) : () => null), [children, menuItems]);\n const mappedChildren = useMemo(() => {\n const children = renderChildren(menuItems);\n if (!autoIndex)\n return children;\n return Children.map(children, (child, index) => {\n if (isValidElement(child) &&\n child.type === MenuOption &&\n child.props.index === undefined) {\n return cloneElement(child, { index });\n }\n return child;\n });\n }, [renderChildren, autoIndex, menuItems]);\n useEffect(() => {\n if (menuRef.current) {\n const menuElement = menuRef.current;\n const selectedElement = menuElement.children[activeIndex];\n if (selectedElement) {\n const menuRect = menuElement.getBoundingClientRect();\n const selectedRect = selectedElement.getBoundingClientRect();\n if (selectedRect.bottom > menuRect.bottom) {\n menuElement.scrollTop += selectedRect.bottom - menuRect.bottom;\n }\n else if (selectedRect.top < menuRect.top) {\n menuElement.scrollTop -= menuRect.top - selectedRect.top;\n }\n }\n }\n }, [activeIndex]);\n return (_jsx(\"div\", { ref: menuRef, role: \"menu\", ...divProps, children: mappedChildren }));\n}\n","// Default filter function\nconst defaultFilter = (item, query, filterBy) => {\n return getSafeValue(item, filterBy).toLowerCase().includes(query.toLowerCase());\n};\n// Helper function to get the first string key of an object\nconst getFirstStringKey = (obj) => {\n return Object.keys(obj).find((key) => typeof obj[key] === \"string\") || \"\";\n};\n// Helper function to safely get a string value from an item\nconst getSafeValue = (item, key) => {\n const value = item[key];\n return typeof value === \"string\" ? value : String(value);\n};\n/**\n * Filters `items` by `query` and ranks the matches exact-first — THE ranking behind the editor's\n * marker palettes (`NodeSelectionMenu` filters with `filterBy: \"name\"`, the marker code): exact\n * match, then prefix matches, then containment matches (nearest occurrence first), with ties\n * keeping the caller's item order (stable sort). Hosts rendering their own palette UI over the\n * editor's marker items should reuse this rather than reimplementing the ordering.\n *\n * @public\n */\nexport function filterAndRankItems(options) {\n const { query, items, filterBy, filter, sortBy, sortingOptions } = options;\n const { caseSensitive = false, priorityOrder = [\"exact\", \"startsWith\", \"contains\"] } = sortingOptions || {};\n const compareQuery = caseSensitive ? query : query.toLowerCase();\n let actualFilterBy;\n let actualFilter;\n if (filter) {\n actualFilter = filter;\n actualFilterBy = items.length > 0 ? getFirstStringKey(items[0]) : \"\";\n }\n else {\n actualFilterBy = filterBy || (items.length > 0 ? getFirstStringKey(items[0]) : \"\");\n actualFilter = (item, query) => defaultFilter(item, query, actualFilterBy);\n }\n const actualSortBy = sortBy || actualFilterBy;\n // Create a Map to cache lowercase versions of sortBy values\n const sortByCache = new Map();\n return items\n .filter((item) => {\n try {\n return actualFilter(item, query);\n }\n catch (error) {\n console.warn(`Error filtering item:`, item, error);\n return false;\n }\n })\n .sort((a, b) => {\n const getTextLower = (item) => {\n if (!sortByCache.has(item)) {\n sortByCache.set(item, getSafeValue(item, actualSortBy).toLowerCase());\n }\n return sortByCache.get(item) ?? \"\";\n };\n const textA = caseSensitive ? getSafeValue(a, actualSortBy) : getTextLower(a);\n const textB = caseSensitive ? getSafeValue(b, actualSortBy) : getTextLower(b);\n for (const priority of priorityOrder) {\n switch (priority) {\n case \"exact\":\n if (textA === compareQuery && textB !== compareQuery)\n return -1;\n if (textB === compareQuery && textA !== compareQuery)\n return 1;\n break;\n case \"startsWith\":\n if (textA.startsWith(compareQuery) && !textB.startsWith(compareQuery))\n return -1;\n if (textB.startsWith(compareQuery) && !textA.startsWith(compareQuery))\n return 1;\n break;\n case \"contains\": {\n const indexA = textA.indexOf(compareQuery);\n const indexB = textB.indexOf(compareQuery);\n if (indexA !== -1 && indexB === -1)\n return -1;\n if (indexB !== -1 && indexA === -1)\n return 1;\n if (indexA !== -1 && indexB !== -1)\n return indexA - indexB;\n break;\n }\n }\n }\n return textA.localeCompare(textB);\n });\n}\n","import { MenuRoot } from \"./Root\";\nimport { MenuOptions } from \"./Options\";\nimport { MenuOption } from \"./Option\";\nexport { filterAndRankItems } from \"./filterAndRankItems\";\nexport default {\n Root: MenuRoot,\n Options: MenuOptions,\n Option: MenuOption,\n};\n","import { useMemo } from \"react\";\nimport { filterAndRankItems } from \"./filterAndRankItems\";\nexport function useFilteredItems(props) {\n const { query, items, filterBy, filter, sortBy, sortingOptions } = props;\n const filteredItems = useMemo(() => {\n return filterAndRankItems({\n query,\n items,\n filterBy,\n filter,\n sortBy,\n sortingOptions,\n });\n }, [query, items, filterBy, filter, sortBy, sortingOptions]);\n return filteredItems;\n}\n","import { useMemo } from \"react\";\nimport { useMenuContext } from \"./MenuContext\";\nexport function useMenuActions() {\n const { moveUp, moveDown, select } = useMenuContext();\n return useMemo(() => ({\n moveUp,\n moveDown,\n select,\n }), [moveUp, moveDown, select]);\n}\n","import { useEffect } from \"react\";\nimport { useLexicalComposerContext } from \"@lexical/react/LexicalComposerContext\";\nimport { useMenuActions } from \"./useMenuActions\";\nimport { COMMAND_PRIORITY_HIGH, KEY_DOWN_COMMAND } from \"lexical\";\nexport const useLexicalMenuNavigation = () => {\n const menu = useMenuActions();\n const [editor] = useLexicalComposerContext();\n useEffect(() => {\n const handleEvent = (event) => {\n const actions = {\n ArrowDown: () => menu?.moveDown(),\n ArrowUp: () => menu?.moveUp(),\n Enter: () => menu?.select(),\n Tab: () => menu?.select(),\n };\n const action = actions[event.key];\n if (action) {\n action();\n event.preventDefault();\n event.stopPropagation();\n return true;\n }\n return false;\n };\n return editor.registerCommand(KEY_DOWN_COMMAND, handleEvent, COMMAND_PRIORITY_HIGH);\n }, [editor, menu]);\n};\n","import { useLexicalMenuNavigation } from \"./Menu/useLexicalMenuNavigation\";\nexport default function LexicalMenuNavigation() {\n useLexicalMenuNavigation();\n return null;\n}\n","import { jsx as _jsx, jsxs as _jsxs } from \"react/jsx-runtime\";\nimport { useEffect, useState } from \"react\";\nimport Menu from \"./Menu\";\nimport { useFilteredItems } from \"./Menu/useFilteredItems\";\nimport { COMMAND_PRIORITY_HIGH, KEY_DOWN_COMMAND } from \"lexical\";\nimport { useLexicalComposerContext } from \"@lexical/react/LexicalComposerContext\";\nimport LexicalMenuNavigation from \"./LexicalMenuNavigation\";\n/**\n * Keys that are a modifier's OWN keydown rather than input. A modifier fires its own keydown\n * before the chord (or the shifted character) it is part of arrives, so the menu must sit still\n * for it: closing here would end the menu mid-chord, e.g. on the Shift of a `\\+w` nested marker.\n */\nconst MODIFIER_KEYS = [\"Shift\", \"Control\", \"Alt\", \"Meta\"];\nexport function NodeSelectionMenu(props) {\n const { options, onSelectOption, onClose, inverse, query: controlledQuery, menuOpenKey, onFilterChange, passthroughKeys, } = props;\n const [editor] = useLexicalComposerContext();\n const isControlled = controlledQuery !== undefined;\n const [query, setQuery] = useState(\"\");\n const localQuery = isControlled ? (controlledQuery ?? \"\") : query;\n const filteredOptions = useFilteredItems({ query: localQuery, items: options, filterBy: \"name\" });\n const handleOptionSelection = (option) => {\n onClose?.();\n if (onSelectOption)\n onSelectOption(option);\n else\n option.action(editor);\n };\n useEffect(() => {\n onFilterChange?.(localQuery, filteredOptions);\n }, [onFilterChange, localQuery, filteredOptions]);\n useEffect(() => {\n return editor.registerCommand(KEY_DOWN_COMMAND, (event) => {\n if (isControlled)\n return false;\n if (passthroughKeys?.includes(event.key))\n return false;\n if (MODIFIER_KEYS.includes(event.key))\n return false;\n if ((event.ctrlKey || event.metaKey || event.altKey) &&\n !event.getModifierState(\"AltGraph\")) {\n // A real chord (Ctrl+Z, Ctrl+C, Cmd+V, Ctrl+A, …) is never query input: ingesting it\n // would append its letter to the filter, and claiming it would leave undo, copy, paste\n // and select-all dead for as long as the menu is open. Close the menu — the marker it\n // was offering is not what the user is reaching for — and let the chord through\n // unclaimed, to whatever handles it. Shift is deliberately absent from the check: a\n // shifted character is still a character, and capitalized markers filter with it —\n // and so is AltGr, which Windows/Linux layouts dispatch as Ctrl+Alt: `@` on a German\n // layout or `ł` on Polish is ordinary character input, not a command chord.\n onClose?.();\n return false;\n }\n const actions = {\n Escape: () => onClose?.(),\n Backspace: () => {\n if (localQuery.length === 0) {\n onClose?.();\n }\n else {\n setQuery((prev) => prev.slice(0, -1));\n }\n },\n };\n const action = actions[event.key];\n if (action) {\n event.stopPropagation();\n event.preventDefault();\n action();\n return true;\n }\n else if (event.key.length === 1) {\n event.stopPropagation();\n event.preventDefault();\n if (event.key !== menuOpenKey)\n setQuery((prev) => prev + event.key);\n return true;\n }\n return false;\n }, COMMAND_PRIORITY_HIGH);\n }, [editor, isControlled, localQuery, menuOpenKey, onClose, passthroughKeys]);\n return (_jsxs(Menu.Root, { className: `autocomplete-menu-container ${inverse ? \"inverse\" : \"\"}`, menuItems: filteredOptions, onSelectOption: (item) => handleOptionSelection(item), children: [!isControlled && _jsx(\"input\", { value: localQuery, type: \"text\", disabled: true }), _jsx(LexicalMenuNavigation, {}), _jsx(Menu.Options, { className: \"autocomplete-menu-options\", autoIndex: false, children: (options) => {\n const mappedOptions = options.map((option, index) => (_jsxs(Menu.Option, { index: index, children: [_jsx(\"span\", { className: \"label\", children: option.label ?? option.name }), _jsx(\"span\", { className: \"description\", children: option.description })] }, option.name)));\n return mappedOptions;\n } })] }));\n}\n","import { jsx as _jsx } from \"react/jsx-runtime\";\nimport { $getSelection, $isRangeSelection } from \"lexical\";\nimport { useLexicalComposerContext } from \"@lexical/react/LexicalComposerContext\";\nimport { useCallback, useEffect, useState } from \"react\";\nimport FloatingBoxAtCursor from \"../FloatingBox/FloatingBoxAtCursor\";\nimport { NodeSelectionMenu } from \"./NodeSelectionMenu\";\nexport * from \"./Menu\";\nexport * from \"./LexicalMenuNavigation\";\nexport * from \"./NodeSelectionMenu\";\nexport default function NodesMenu({ trigger, items }) {\n const [editor] = useLexicalComposerContext();\n const [isOpen, setIsOpen] = useState(false);\n const handleKeyDown = useCallback((e) => {\n if (e.key === \"Escape\" && isOpen) {\n setIsOpen(false);\n editor.focus();\n }\n else if (e.key === trigger && !isOpen) {\n e.preventDefault();\n setIsOpen(true);\n }\n }, [editor, trigger, isOpen]);\n useEffect(() => {\n return editor.registerRootListener((root) => {\n if (!root)\n return;\n root.addEventListener(\"keydown\", handleKeyDown);\n return () => {\n root.removeEventListener(\"keydown\", handleKeyDown);\n };\n });\n }, [editor, handleKeyDown]);\n // Close the menu when the selection changes\n useEffect(() => {\n return editor.registerUpdateListener(({ prevEditorState, editorState }) => {\n const prevSelection = prevEditorState.read(() => {\n const selection = $getSelection();\n if (!$isRangeSelection(selection))\n return;\n return selection;\n });\n editorState.read(() => {\n const selection = $getSelection();\n if (!$isRangeSelection(selection) || prevSelection?.is(selection))\n return;\n setIsOpen(false);\n });\n });\n }, [editor]);\n return (items && (_jsx(FloatingBoxAtCursor, { isOpen: isOpen, children: ({ placement }) => (_jsx(NodeSelectionMenu, { options: items, onClose: () => setIsOpen(false), inverse: placement === \"top-start\", menuOpenKey: trigger })) })));\n}\n","import { useMemo } from \"react\";\nimport { getMarker } from \"shared\";\n// getMarker() takes a marker string and gets its data from a usfm markers map object that is merged with overwrites that fit the PERF editor context.\n// getMarkerAction() returns a function to generate a LexicalNode and insert it in the editor, this lexical node is a custom node made for the PERF editor\n// NOTE: You can create your own typeahead plugin by creating your own getMarker() and getMarkerAction() functions adapted to your editor needs.\nexport default function useUsfmMarkersForMenu({ scriptureReference, contextMarker, getMarkerAction, }) {\n const markersMenuItems = useMemo(() => {\n if (!contextMarker || !scriptureReference)\n return;\n const marker = getMarker(contextMarker);\n if (!marker?.children)\n return;\n return Object.values(marker.children).flatMap((markers) => markers.map((marker) => {\n const markerData = getMarker(marker);\n const { action } = getMarkerAction(marker, markerData);\n return {\n name: marker,\n label: marker,\n description: markerData?.description ?? \"\",\n action: (editor) => {\n action({ editor, reference: scriptureReference });\n },\n };\n }));\n }, [contextMarker, getMarkerAction, scriptureReference]);\n return { markersMenuItems };\n}\n","import { $getRangeFromUsjSelection } from \"./selection.utils\";\nimport { useLexicalComposerContext } from \"@lexical/react/LexicalComposerContext\";\nimport { mergeRegister, registerNestedElementResolver } from \"@lexical/utils\";\nimport { $getNodeByKey } from \"lexical\";\nimport { forwardRef, useEffect, useImperativeHandle, useMemo } from \"react\";\nimport { $createTypedMarkNode, $isTypedMarkNode, $unwrapTypedMarkNode, $wrapSelectionInTypedMarkNode, ANNOTATION_CHANGE_TAG, TypedMarkNode, } from \"shared\";\nfunction getTypeIDMapKey(type, id) {\n return `${type}:${id}`;\n}\nfunction useAnnotations(editor, markNodeMap) {\n useEffect(() => {\n if (!editor.hasNodes([TypedMarkNode])) {\n throw new Error(\"AnnotationPlugin: TypedMarkNode not registered on editor!\");\n }\n const markNodeKeysToTypedIDs = new Map();\n return mergeRegister(registerNestedElementResolver(editor, TypedMarkNode, (from) => {\n return $createTypedMarkNode(from.getTypedIDs(), from.getTypedOnClicks(), from.getTypedOnRemoves(), from.getTypedOnMouseEnters(), from.getTypedOnMouseLeaves());\n }, (from, to) => {\n // Merge the IDs\n const fromOnClicks = from.getTypedOnClicks();\n const fromOnRemoves = from.getTypedOnRemoves();\n const fromOnMouseEnters = from.getTypedOnMouseEnters();\n const fromOnMouseLeaves = from.getTypedOnMouseLeaves();\n for (const [type, ids] of Object.entries(from.getTypedIDs())) {\n ids.forEach((id) => {\n const onClick = fromOnClicks[type]?.[id];\n const onRemove = fromOnRemoves[type]?.[id];\n const onMouseEnter = fromOnMouseEnters[type]?.[id];\n const onMouseLeave = fromOnMouseLeaves[type]?.[id];\n to.addID(type, id, onClick, onRemove, onMouseEnter, onMouseLeave);\n });\n }\n // The resolver replaces the original node with a new one; suppress callbacks so the\n // transferred IDs do not emit \"destroyed\" notifications during the teardown.\n from.getWritable().__suppressOnRemoveCallbacks = true;\n }), editor.registerMutationListener(TypedMarkNode, (mutations) => {\n editor.getEditorState().read(() => {\n // Keep track of mutated mark node keys so they can be removed later.\n for (const [key, mutation] of mutations) {\n const node = $getNodeByKey(key);\n let typedIDs = {};\n if (mutation === \"destroyed\") {\n typedIDs = markNodeKeysToTypedIDs.get(key) ?? {};\n }\n else if ($isTypedMarkNode(node)) {\n typedIDs = node.getTypedIDs();\n }\n for (const [type, ids] of Object.entries(typedIDs)) {\n // Skip reserved types as they will handle their own keys.\n if (TypedMarkNode.isReservedType(type))\n continue;\n for (const id of ids) {\n let markNodeKeys = markNodeMap.get(getTypeIDMapKey(type, id));\n typedIDs[type] = ids;\n markNodeKeysToTypedIDs.set(key, typedIDs);\n if (mutation === \"destroyed\") {\n if (markNodeKeys !== undefined) {\n markNodeKeys.delete(key);\n if (markNodeKeys.size === 0) {\n markNodeMap.delete(getTypeIDMapKey(type, id));\n }\n }\n }\n else {\n if (markNodeKeys === undefined) {\n markNodeKeys = new Set();\n markNodeMap.set(getTypeIDMapKey(type, id), markNodeKeys);\n }\n if (!markNodeKeys.has(key)) {\n markNodeKeys.add(key);\n }\n }\n }\n }\n }\n });\n }, { skipInitialization: true }));\n }, [editor, markNodeMap]);\n}\nexport const AnnotationPlugin = forwardRef(function AnnotationPlugin({ logger }, ref) {\n const [editor] = useLexicalComposerContext();\n const markNodeMap = useMemo(() => {\n return new Map();\n }, []);\n useAnnotations(editor, markNodeMap);\n /**\n * Removes all mark nodes associated with the given type/id pair.\n *\n * @param type - Annotation type to remove.\n * @param id - Annotation ID to remove.\n * @param nodeKeys - Optional set of known node keys for this type/id. When omitted, keys are\n * computed from the shared mark node map.\n */\n const $removeMarkNodesForTypeID = (type, id, nodeKeys) => {\n const keys = Array.from(nodeKeys ?? markNodeMap.get(getTypeIDMapKey(type, id)) ?? []);\n if (keys.length === 0)\n return;\n for (const key of keys) {\n const node = $getNodeByKey(key);\n if ($isTypedMarkNode(node)) {\n node.deleteID(type, id);\n if (node.hasNoIDsForEveryType()) {\n $unwrapTypedMarkNode(node);\n }\n }\n }\n };\n useImperativeHandle(ref, () => ({\n setAnnotation(selection, type, id, onClick, onRemove, onMouseEnter, onMouseLeave) {\n if (TypedMarkNode.isReservedType(type))\n throw new Error(`setAnnotation: Can't directly set this reserved annotation type '${type}'.` +\n \" Use the appropriate plugin instead.\");\n editor.update(() => {\n // Apply the annotation to the selected range.\n const editorSelection = $getRangeFromUsjSelection(selection);\n if (editorSelection === undefined) {\n logger?.error(\"Failed to find start or end node of the annotation.\");\n return;\n }\n $removeMarkNodesForTypeID(type, id);\n $wrapSelectionInTypedMarkNode(editorSelection, type, id, onClick, onRemove, onMouseEnter, onMouseLeave);\n }, { tag: ANNOTATION_CHANGE_TAG });\n },\n removeAnnotation(type, id) {\n if (TypedMarkNode.isReservedType(type))\n throw new Error(`removeAnnotation: Can't directly remove this reserved annotation type '${type}'.` +\n \" Use the appropriate plugin instead.\");\n const markNodeKeys = markNodeMap.get(getTypeIDMapKey(type, id));\n if (markNodeKeys === undefined || markNodeKeys.size === 0)\n return;\n editor.update(() => {\n $removeMarkNodesForTypeID(type, id, markNodeKeys);\n }, { tag: ANNOTATION_CHANGE_TAG });\n },\n }));\n return null;\n});\n","/**\n * Adapted from https://github.com/facebook/lexical/blob/d0456a81955bc6fef7cc7f87907f2a172d41bbf2/packages/lexical-react/src/LexicalOnChangePlugin.ts\n */\nimport { $getOTPositionOfNode, $isFastPathContentText } from \"./delta-common.utils\";\nimport { $getTextOp, getEditorDelta } from \"./editor-delta.adaptor\";\nimport { useLexicalComposerContext } from \"@lexical/react/LexicalComposerContext\";\nimport { $getNodeByKey, $isTextNode, HISTORY_MERGE_TAG } from \"lexical\";\nimport Delta from \"quill-delta\";\nimport { useLayoutEffect } from \"react\";\nimport { $findFirstAncestorNoteNode, MARKER_SETTLE_TAG } from \"shared\";\n/** Stable default for {@link DeltaOnChangePlugin}'s `ignoreTags` so the effect deps stay stable. */\nconst EMPTY_TAGS = [];\n/** Adapted from the LexicalOnChangePlugin to include collaborative editing operations. */\nexport function DeltaOnChangePlugin({ ignoreHistoryMergeTagChange = true, ignoreSelectionChange = false, ignoreTags = EMPTY_TAGS, onChange, }) {\n const [editor] = useLexicalComposerContext();\n useLayoutEffect(() => {\n if (!onChange)\n return;\n return editor.registerUpdateListener((payload) => {\n const { editorState, dirtyElements, dirtyLeaves, prevEditorState, tags } = payload;\n if ((ignoreSelectionChange && dirtyElements.size === 0 && dirtyLeaves.size === 0) ||\n // A `MARKER_SETTLE_TAG` commit carries the merge tag only to stay out of the undo\n // stack — its bytes really did change, so it must reach `onChange` like any edit.\n // Without this exemption the cached USJ and the emitted delta both keep showing the\n // pre-settle bytes, and the host saves a document the editor is no longer displaying.\n (ignoreHistoryMergeTagChange &&\n tags.has(HISTORY_MERGE_TAG) &&\n !tags.has(MARKER_SETTLE_TAG)) ||\n ignoreTags.some((tag) => tags.has(tag)) ||\n prevEditorState.isEmpty()) {\n return;\n }\n const ops = $getUpdateOps(editor, payload);\n // TODO: this may have been added because nodes are made dirty when they shouldn't be as a\n // result of NoteNode collapsing/expanding. If so, we should fix that instead.\n if (ops.length === 0)\n return;\n onChange(editorState, editor, tags, ops);\n });\n }, [editor, ignoreHistoryMergeTagChange, ignoreSelectionChange, ignoreTags, onChange]);\n return null;\n}\nfunction $getUpdateOps(editor, { dirtyLeaves, prevEditorState }) {\n let update = new Delta();\n editor.getEditorState().read(() => {\n const nodeKey = dirtyLeaves.values().next().value ?? \"\";\n const dirtyNode = $getNodeByKey(nodeKey);\n // Note-internal edits must NOT take the fast path: a note is ONE opaque embed unit in\n // delta-doc coordinates, so $getOTPositionOfNode for a text node INSIDE it resolves to the\n // note's OUTER position and the emitted op would land the edit AFTER the note. The full-diff\n // fallback replaces the note embed wholesale instead.\n const isInsideNote = dirtyNode !== null && $findFirstAncestorNoteNode(dirtyNode) !== undefined;\n // Presentation-carrying text must not take the fast path either: the fast path's insert is\n // the node's RAW bytes while its retain is counted in delta-doc coordinates, and for a node\n // the ops-stream exclusions treat specially (a `\\q1` prefix glyph, a `\\va 2\\va*` run,\n // attribute text) those are different currencies — a peer would receive display bytes as\n // Scripture and every later offset would shift. $isFastPathContentText derives eligibility\n // from the same delta-doc counting instead of re-listing the exclusions; anything ineligible\n // falls to the full diff, whose $handleTextNodes applies the one authoritative list.\n if (dirtyLeaves.size === 1 &&\n $isTextNode(dirtyNode) &&\n !isInsideNote &&\n $isFastPathContentText(dirtyNode)) {\n // Handle the most common case of text changing in a single text node.\n // Default \"delta-doc\" coordinates (NOT \"apply\"): this fast path and the `getEditorDelta`\n // diff fallback below feed the same doc-delta op stream emitted to the host via\n // `onChange`, so they must agree. They do: `$getOTPositionOfNode` counts a preceding\n // editable verse as its 1-unit embed (matching the doc delta, which emits only the verse\n // embed op — the glyph text is engine-owned display, excluded from content ops), and a\n // preceding editable chapter as its glyph-length body text (matching the doc delta too).\n // See `OTCoordinateSystem` in delta-common.utils.ts.\n const retain = $getOTPositionOfNode(dirtyNode);\n if (retain !== undefined) {\n const prevTextDoc = prevEditorState.read(() => {\n const prevNode = $getNodeByKey(nodeKey);\n return new Delta([$isTextNode(prevNode) ? $getTextOp(prevNode) : { insert: \"\" }]);\n });\n const textDoc = new Delta([$getTextOp(dirtyNode)]);\n const nodePositionRetain = new Delta(retain > 0 ? [{ retain }] : []);\n update = update.concat(nodePositionRetain).concat(prevTextDoc.diff(textDoc));\n }\n }\n else {\n const prevDoc = getEditorDelta(prevEditorState);\n const currentDoc = getEditorDelta(editor.getEditorState());\n update = prevDoc.diff(currentDoc);\n }\n });\n return update.ops;\n}\n","/**\n * Constant representing the formatted view mode.\n * Used to display content with formatting applied.\n *\n * @public\n */\nexport const FORMATTED_VIEW_MODE = \"formatted\";\n/**\n * Constant representing the unformatted view mode.\n * Used to display content without formatting applied.\n *\n * @public\n */\nexport const UNFORMATTED_VIEW_MODE = \"unformatted\";\n/**\n * Constant representing the paragraph structure view mode.\n * Displays formatted text with visible USFM paragraph markers in a gutter column,\n * styled verse numbers, decorative chapter numbers, and an active-text outline on\n * the focused paragraph section.\n *\n * @public\n */\nexport const PARAGRAPH_STRUCTURE_VIEW_MODE = \"paragraph-structure\";\n/**\n * Constant representing the standard view mode (PT9 \"Standard\" equivalent).\n * Displays formatted text with USFM markers visible inline as editable text and\n * notes collapsed to callers.\n *\n * @public\n */\nexport const STANDARD_VIEW_MODE = \"standard\";\n/**\n * Constant representing the block verse view mode.\n * Displays formatted text with each verse wrapped in a block-level element, so a verse can be\n * placed on a layout row - for example a grid aligning the same verse across several resources.\n * This view is read-only; see `ViewOptions.verseLayout`.\n *\n * @public\n */\nexport const BLOCK_VERSE_VIEW_MODE = \"block-verse\";\n/**\n * Maps view mode keys to their human-readable display names.\n * Used for UI components that need to show view mode options to users.\n *\n * @public\n */\nexport const viewModeToViewNames = {\n [FORMATTED_VIEW_MODE]: \"Formatted\",\n [UNFORMATTED_VIEW_MODE]: \"Unformatted\",\n [PARAGRAPH_STRUCTURE_VIEW_MODE]: \"Paragraph Structure\",\n [STANDARD_VIEW_MODE]: \"Standard\",\n [BLOCK_VERSE_VIEW_MODE]: \"Block Verse\",\n};\n","import { TEXT_SPACING_CLASS_NAME, FORMATTED_FONT_CLASS_NAME, MARKER_MODE_CLASS_NAME_PREFIX, VerseNode, } from \"shared\";\nimport { ImmutableVerseNode } from \"../nodes/usj/ImmutableVerseNode\";\nimport { BLOCK_VERSE_VIEW_MODE, FORMATTED_VIEW_MODE, UNFORMATTED_VIEW_MODE, PARAGRAPH_STRUCTURE_VIEW_MODE, STANDARD_VIEW_MODE, viewModeToViewNames, } from \"./view-mode.model\";\nimport { deepEqual } from \"fast-equals\";\n/**\n * Whether `viewOptions` renders paragraph marker prefixes at all — the single spelling of the\n * `showParaMarkerPrefixes !== false` default. The adaptor (which builds the glyph), the\n * marker-edit transforms (which police or heal it), and the prefix-deletion guard (which reacts\n * to its absence) must all answer this identically: a surface that never builds the prefix must\n * never treat its absence as user intent to change the paragraph.\n *\n * @public\n */\nexport function showParaMarkerPrefix(viewOptions) {\n return viewOptions?.showParaMarkerPrefixes !== false;\n}\nlet defaultViewMode;\nlet defaultViewOptions;\n/**\n * Sets the default view mode and options.\n *\n * @param viewMode - View mode of the editor.\n *\n * @public\n */\nexport function setDefaultView(viewMode) {\n const _viewOptions = getViewOptions(viewMode);\n if (!_viewOptions)\n throw new Error(`Invalid view mode: ${viewMode}`);\n defaultViewMode = viewMode;\n defaultViewOptions = _viewOptions;\n}\nsetDefaultView(FORMATTED_VIEW_MODE);\n/**\n * Gets the default view mode.\n *\n * @returns the default view mode.\n *\n * @public\n */\nexport const getDefaultViewMode = () => defaultViewMode;\n/**\n * Gets the default view options.\n *\n * @returns the default view options.\n *\n * @public\n */\nexport const getDefaultViewOptions = () => defaultViewOptions;\n/**\n * Get view option properties based on the view mode.\n *\n * @param viewMode - View mode of the editor.\n * @returns the view options if the view exists, the default options if the viewMode is undefined,\n * `undefined` otherwise.\n *\n * @public\n */\nexport function getViewOptions(viewMode) {\n let viewOptions;\n switch (viewMode ?? defaultViewMode) {\n case FORMATTED_VIEW_MODE:\n viewOptions = {\n markerMode: \"hidden\",\n noteMode: \"collapsed\",\n hasSpacing: true,\n isFormattedFont: true,\n };\n break;\n case UNFORMATTED_VIEW_MODE:\n viewOptions = {\n markerMode: \"editable\",\n noteMode: \"expanded\",\n hasSpacing: false,\n isFormattedFont: false,\n };\n break;\n case PARAGRAPH_STRUCTURE_VIEW_MODE:\n viewOptions = {\n markerMode: \"hidden\",\n noteMode: \"collapsed\",\n hasSpacing: true,\n isFormattedFont: true,\n hasGutterParaMarkers: true,\n hasActiveTextFocusBox: true,\n };\n break;\n case STANDARD_VIEW_MODE:\n viewOptions = {\n markerMode: \"editable\",\n noteMode: \"collapsed\",\n hasSpacing: true,\n isFormattedFont: true,\n };\n break;\n case BLOCK_VERSE_VIEW_MODE:\n viewOptions = {\n markerMode: \"hidden\",\n noteMode: \"collapsed\",\n hasSpacing: true,\n isFormattedFont: true,\n verseLayout: \"block\",\n };\n break;\n default:\n break;\n }\n return viewOptions;\n}\n/**\n * Convert view options to view mode if the view exists.\n *\n * Inverts {@link getViewOptions} by comparison, so a view option added later cannot be forgotten\n * here and leave two modes indistinguishable. Matching is exact once each unset optional field is\n * filled in with its default, so spelling a default out still matches, but options derived from a\n * mode and then genuinely tweaked describe a view that is no longer that mode and yield\n * `undefined`.\n *\n * @remarks\n * This is narrower than the field-by-field matching it replaced, which tested only `markerMode`,\n * `hasSpacing`, `isFormattedFont`, `hasGutterParaMarkers` and `hasActiveTextFocusBox`. Every other\n * field now counts, `noteMode` included - it has no default to fill in (call sites read `undefined`\n * inconsistently, some as collapsed and some as not), so it is part of what identifies a mode and\n * has to be given. Options built from a mode with `noteMode` changed - say\n * `{ ...getViewOptions(PARAGRAPH_STRUCTURE_VIEW_MODE), noteMode: \"expanded\" }` - used to return the\n * mode they started from and now return `undefined`.\n *\n * @param viewOptions - View options of the editor.\n * @returns the view mode if the view is defined, `undefined` otherwise.\n *\n * @public\n */\nexport function getViewMode(viewOptions) {\n if (!viewOptions)\n return undefined;\n const normalized = canonicalize(viewOptions);\n return Object.keys(viewModeToViewNames).find((viewMode) => deepEqual(canonicalize(getViewOptions(viewMode)), normalized));\n}\n/**\n * Each optional field's documented default, so options that spell a default out compare equal to\n * options that leave it out - they describe the same view, and the comparison counts keys.\n *\n * `noteMode` is absent deliberately: it has no default (call sites read `undefined` inconsistently,\n * some as collapsed and some as not), so it is part of what identifies a mode and has to be given.\n */\nconst optionalViewOptionDefaults = {\n showCharMarkerTitles: true,\n hasGutterParaMarkers: false,\n hasActiveTextFocusBox: false,\n verseLayout: \"inline\",\n};\n/** The options with every unset optional field filled in with its default. */\nfunction canonicalize(viewOptions) {\n if (!viewOptions)\n return viewOptions;\n // Strip the `undefined`-valued keys first - spreading them over the defaults would reinstate the\n // very \"not set\" state the defaults exist to resolve.\n const setOptions = Object.fromEntries(Object.entries(viewOptions).filter(([, value]) => value !== undefined));\n return { ...optionalViewOptionDefaults, ...setOptions };\n}\n/**\n * Whether the standard-view whitespace/display normalization rules apply to these view options.\n *\n * These rules — the display NBSP/`~` mapping at load time, the live display-whitespace transform\n * and clipboard normalization, and the inverse normalization on serialization — are all gated on\n * this ONE predicate, so a document always serializes under the same whitespace regime it was\n * loaded with; no combination of options can apply the display mapping without its inversion.\n *\n * The invariant is the STANDARD view fingerprint with the `noteMode` axis dropped: editable\n * markers in a spacing+formatted view with NEITHER `hasGutterParaMarkers` NOR\n * `hasActiveTextFocusBox`. It is `true` for both collapsed (the named `standard` mode) and\n * expanded notes. It is `false` for the Unformatted view (editable but neither spaced nor\n * formatted, where whitespace is shown literally) and for gutter/focus-box views: those render\n * paragraph markers as immutable typed text — a different whitespace regime with no\n * display-mapped text to invert (their named mode hides markers entirely, so the editable\n * engine's separators never combine with them). Deliberately NOT expressed via\n * {@link getViewMode}: expanded is not the named `standard` mode, and overloading `getViewMode`\n * would break its invertibility contract and the user-facing mode labels. `getViewMode` compares\n * whole option objects against {@link getViewOptions}, so this predicate stays independent of it:\n * it is the one place the whitespace fingerprint — the STANDARD options with the `noteMode` axis\n * dropped — is written down.\n *\n * @param viewOptions - View options of the editor.\n * @returns `true` when standard-view whitespace normalization applies.\n *\n * @public\n */\nexport function hasStandardViewWhitespace(viewOptions) {\n if (!viewOptions)\n return false;\n const { markerMode, hasSpacing, isFormattedFont, hasGutterParaMarkers, hasActiveTextFocusBox } = viewOptions;\n return (markerMode === \"editable\" &&\n hasSpacing &&\n isFormattedFont &&\n !hasGutterParaMarkers &&\n !hasActiveTextFocusBox);\n}\n/**\n * Get the verse node class for the given view options.\n *\n * @param viewOptions - View options of the editor.\n * @returns the verse node class if the view is defined, `undefined` otherwise.\n *\n * @public\n */\nexport function getVerseNodeClass(viewOptions) {\n if (!viewOptions)\n return;\n // Block verse is read-only, so its marker is never the editable `VerseNode`. Today the marker\n // mode below would reach the same answer - block verse hides markers - but dispatching on the\n // layout first keeps that independent of how markers happen to be configured.\n if (isBlockVerseLayout(viewOptions))\n return ImmutableVerseNode;\n return viewOptions.markerMode === \"editable\" ? VerseNode : ImmutableVerseNode;\n}\n/**\n * Whether the view options select the block verse layout.\n *\n * That layout is read-only: its paragraphs are split across verse blocks, so an edit has no\n * correct USJ to go back to. Anything that offers editing - or an affordance that depends on\n * editing, such as comment authoring - should treat it as read-only whatever `isReadonly` says.\n *\n * @param viewOptions - View options of the editor.\n * @returns `true` if verses are laid out as blocks.\n *\n * @public\n */\nexport function isBlockVerseLayout(viewOptions) {\n return viewOptions?.verseLayout === \"block\";\n}\n/**\n * Get the class name list for the given view options.\n *\n * @param viewOptions - View options of the editor.\n * @returns the element class name list based on view options.\n *\n * @public\n */\nexport function getViewClassList(viewOptions) {\n const classList = [];\n const _viewOptions = viewOptions ?? defaultViewOptions;\n if (_viewOptions) {\n classList.push(`${MARKER_MODE_CLASS_NAME_PREFIX}${_viewOptions.markerMode}`);\n if (_viewOptions.hasSpacing)\n classList.push(TEXT_SPACING_CLASS_NAME);\n if (_viewOptions.isFormattedFont)\n classList.push(FORMATTED_FONT_CLASS_NAME);\n }\n return classList;\n}\n","import { $createImmutableVerseNode } from \"../../../nodes/usj/ImmutableVerseNode\";\nimport { $isSomeVerseNode } from \"../../../nodes/usj/node-react.utils\";\nimport { $createWholeNote } from \"../../../nodes/usj/note.utils\";\nimport { showParaMarkerPrefix } from \"../../../views/view-options.utils\";\nimport { $isEmbedNode, $isOTTextNode, isInsertEmbedOpOfType, LF, } from \"./delta-common.utils\";\nimport { OT_BOOK_PROPS, OT_CHAPTER_PROPS, OT_CHAR_PROPS, OT_MILESTONE_PROPS, OT_NOTE_PROPS, OT_PARA_PROPS, OT_UNKNOWN_PROPS, OT_VERSE_PROPS, } from \"./rich-text-ot.model\";\nimport { $unwrapNode } from \"@lexical/utils\";\nimport { $getRoot, $createTextNode, $isElementNode, $isTextNode, $setState, } from \"lexical\";\nimport { $createBookNode, $createChapterNode, $createCharNode, $createImmutableChapterNode, $createImmutableTypedTextNode, $createImmutableUnmatchedNode, $createImpliedParaNode, $createMarkerNode, $createMarkerTrailingSeparator, $createMilestoneNode, $createParaNode, $createUnknownNode, $createVerseNode, $hasSameCharAttributes, $isBookNode, $isCharNode, $isImmutableTypedTextNode, $isImpliedParaNode, $isMarkerNode, $isMilestoneNode, $isNoteNode, $isParaLikeNode, $isParaNode, $isSomeChapterNode, $isSomeParaNode, $isUnknownNode, BOOK_MARKER, BookNode, charIdState, closingMarkerText, EMPTY_CHAR_PLACEHOLDER_TEXT, getUnknownAttributes, getVisibleOpenMarkerText, NBSP, $createGutterMarkerNode, openingMarkerText, segmentState, } from \"shared\";\n/*\nFor implied paragraphs, we use the following logic:\n - An ImpliedParaNode (or ParaNode) takes up OT index space 1 but only at the end of the block.\n - An ImpliedParaNode is created when an inline node is inserted where there is no ParaNode.\n - If an LF is inserted, it closes the ImpliedParaNode if there are no attributes or it is replaced\n by a ParaNode specified by the attributes.\n - Our empty Lexical editor defaults to an empty ImpliedParaNode, so the first inline insertion\n should go inside it.\n\nFor CharNodes, we use the following logic:\n - CharNodes are created when attributes.char is present in a text insert operation.\n - CharNodes are inserted at the current index, and they can contain TextNodes with additional\n formatting attributes.\n - CharNodes have no OT length contribution themselves, but their text content does.\n - CharNodes can be nested inside SomeParaNode or other CharNodes.\n - CharNodes use the attributes style and cid to uniquely identify themselves.\n - A single CharNode can use an attributes object `{ char: { style: \"bd\", cid: \"456\" } }`.\n - A nested CharNode will use attributes.char object\n `{ char: [{ style: \"it\", cid: \"123\" }, { style: \"bd\", cid: \"456\" }] }`\n where \"it\" is the parent CharNode and \"bd\" is the child CharNode.\n*/\n/**\n * Apply Operational Transform rich-text updates to the editor.\n * @param ops - Operations array.\n * @param viewOptions - View options of the editor.\n * @param nodeOptions - Node options for USJ nodes.\n * @param logger - Logger to use, if any.\n *\n * @see https://github.com/ottypes/rich-text\n */\nexport function $applyUpdate(ops, viewOptions, nodeOptions, logger) {\n /** Tracks the current position in the OT document */\n let currentIndex = 0;\n ops.forEach((op) => {\n if (\"retain\" in op) {\n currentIndex += $retain(op, currentIndex, viewOptions, logger);\n }\n else if (\"delete\" in op) {\n if (typeof op.delete !== \"number\" || op.delete <= 0) {\n logger?.error(`Invalid delete operation: ${JSON.stringify(op)}`);\n return; // Skip malformed operation\n }\n logger?.debug(`Delete: ${op.delete}`);\n $delete(currentIndex, op.delete, logger);\n // Delete operations do not advance the currentIndex in the OT Delta model\n }\n else if (\"insert\" in op) {\n if (typeof op.insert === \"string\") {\n logger?.debug(`Insert: '${op.insert}'`);\n currentIndex += $insertTextAtCurrentIndex(currentIndex, op.insert, op.attributes, viewOptions, logger);\n }\n else if (typeof op.insert === \"object\" && op.insert !== null) {\n logger?.debug(`Insert embed: ${JSON.stringify(op.insert)}`);\n if ($insertEmbedAtCurrentIndex(currentIndex, op, viewOptions, nodeOptions, logger)) {\n currentIndex += 1;\n }\n else {\n // If embed insertion fails, currentIndex is not advanced to prevent de-sync.\n logger?.error(`Failed to process insert embed operation: ${JSON.stringify(op.insert)} at index ${currentIndex}. Document may be inconsistent.`);\n }\n }\n else {\n logger?.error(`Insert of unknown type: ${JSON.stringify(op.insert)}`);\n }\n }\n else {\n logger?.error(`Unknown operation: ${JSON.stringify(op)}`);\n }\n });\n}\nfunction $retain(op, currentIndex, viewOptions, logger) {\n if (typeof op.retain !== \"number\" || op.retain < 0) {\n logger?.error(`Invalid retain operation: ${JSON.stringify(op)}`);\n return 0;\n }\n logger?.debug(`Retain: ${op.retain}`);\n if (op.attributes) {\n logger?.debug(`Retain attributes: ${JSON.stringify(op.attributes)}`);\n $applyAttributes(currentIndex, op.retain, op.attributes, viewOptions, logger);\n }\n return op.retain;\n}\n/** Traverse and apply attributes to the retained range, or transform text to CharNode */\nfunction $applyAttributes(targetIndex, retain, attributes, viewOptions, logger) {\n // Apply attributes using standard traversal logic\n logger?.debug(`Applying attributes for range [${targetIndex}, ${targetIndex + retain - 1}] with attributes: ${JSON.stringify(attributes)}`);\n let lengthToFormat = retain;\n let currentIndex = 0;\n /** The nested CharNode depth */\n let nestedCharCount = -1;\n const root = $getRoot();\n function $traverseAndApplyAttributesRecursive(currentNode) {\n if (lengthToFormat <= 0)\n return true;\n if ($isOTTextNode(currentNode)) {\n const textLength = currentNode.getTextContentSize();\n if (targetIndex < currentIndex + textLength && currentIndex < targetIndex + retain) {\n const offsetInNode = Math.max(0, targetIndex - currentIndex);\n const lengthAvailableInNodeAfterOffset = textLength - offsetInNode;\n const lengthToApplyInThisNode = Math.min(lengthToFormat, lengthAvailableInNodeAfterOffset);\n if (lengthToApplyInThisNode > 0) {\n let targetNode = currentNode;\n const needsSplitAtStart = offsetInNode > 0;\n const needsSplitAtEnd = lengthToApplyInThisNode < textLength - offsetInNode;\n if (needsSplitAtStart && needsSplitAtEnd) {\n const [, middleNode] = currentNode.splitText(offsetInNode);\n [targetNode] = middleNode.splitText(lengthToApplyInThisNode);\n }\n else if (needsSplitAtStart) {\n [, targetNode] = currentNode.splitText(offsetInNode);\n }\n else if (needsSplitAtEnd) {\n [targetNode] = currentNode.splitText(lengthToApplyInThisNode);\n }\n // Check if we need to convert TextNode to CharNode\n if (hasCharAttributes(attributes)) {\n // Apply new non-char attributes to TextNode as well\n // Check if this text node is already inside a CharNode\n const parentNode = targetNode.getParent();\n if ($isCharNode(parentNode)) {\n const charAttr = attributes.char;\n let charAttrItem;\n if (Array.isArray(charAttr)) {\n if (nestedCharCount >= 0 && nestedCharCount <= charAttr.length - 1) {\n charAttrItem = charAttr[nestedCharCount];\n }\n }\n else if (nestedCharCount === 0) {\n // Single char attribute\n charAttrItem = charAttr;\n }\n const hasSameCharAttributes = charAttrItem\n ? $hasSameCharAttributes(charAttrItem, parentNode)\n : false;\n if (hasSameCharAttributes && Array.isArray(charAttr) && charAttr.length > 1) {\n const placeholderNode = $createTextNode(\"\");\n targetNode.replace(placeholderNode);\n const segment = typeof attributes.segment === \"string\" ? attributes.segment : undefined;\n const nestedCharNodes = $createNestedChars(charAttr.slice(1), viewOptions, targetNode, segment);\n // Insert all nodes (markers + CharNode) as siblings\n let currentPlaceholder = placeholderNode;\n for (const node of nestedCharNodes) {\n currentPlaceholder.insertAfter(node);\n currentPlaceholder = node;\n }\n placeholderNode.remove();\n // Apply text attributes to the innermost node\n $applyTextAttributes(attributes, targetNode);\n // No need to update parent marker/cid, as it already matches\n }\n else if (!hasSameCharAttributes) {\n // If parent does not match, extract text and create new CharNode as sibling\n // Remove the text node from inside the parent CharNode\n targetNode.remove();\n // Create new CharNode(s) with the text\n const charNodes = $wrapInNestedCharNodes(targetNode, attributes, viewOptions, logger);\n // Insert the new CharNodes as siblings to the parent CharNode\n if (charNodes && charNodes.length > 0) {\n let currentNode = parentNode;\n for (const node of charNodes) {\n currentNode.insertAfter(node);\n currentNode = node;\n }\n }\n }\n else {\n // Parent CharNode matches and no further nesting needed, just apply attributes\n $applyTextAttributes(attributes, targetNode);\n }\n }\n else {\n const placeholderNode = $createTextNode(\"\");\n targetNode.replace(placeholderNode);\n const charNodes = $wrapInNestedCharNodes(targetNode, attributes, viewOptions, logger);\n if (charNodes && charNodes.length > 0) {\n let currentNode = placeholderNode;\n for (const node of charNodes) {\n currentNode.insertAfter(node);\n currentNode = node;\n }\n placeholderNode.remove();\n }\n else {\n placeholderNode.replace(targetNode);\n }\n }\n }\n else {\n $applyTextAttributes(attributes, targetNode);\n }\n lengthToFormat -= lengthToApplyInThisNode;\n }\n }\n currentIndex += textLength;\n }\n else if ($isEmbedNode(currentNode)) {\n const embedNodeOtLength = 1;\n if (targetIndex <= currentIndex &&\n currentIndex < targetIndex + retain &&\n lengthToFormat > 0) {\n $applyEmbedAttributes(currentNode, attributes);\n lengthToFormat -= embedNodeOtLength;\n }\n currentIndex += embedNodeOtLength;\n }\n else if ($isCharNode(currentNode)) {\n // CharNodes don't contribute to OT length, they're just formatted text containers\n nestedCharCount += 1;\n let shouldRemoveCharNode = false;\n if (targetIndex <= currentIndex &&\n currentIndex < targetIndex + retain &&\n lengthToFormat > 0) {\n if (hasCharAttributes(attributes)) {\n // Support nested char arrays for deep char attribute application\n const charAttr = attributes.char;\n let charAttrItem;\n if (Array.isArray(charAttr)) {\n if (nestedCharCount >= 0 && nestedCharCount <= charAttr.length - 1) {\n charAttrItem = charAttr[nestedCharCount];\n }\n }\n else if (nestedCharCount === 0) {\n // Single char attribute\n charAttrItem = charAttr;\n }\n // Only set attributes if needed\n if (charAttrItem) {\n // Update the CharNode's marker (and its glyphs, in editable marker mode) and\n // attributes to match the retain attributes\n $syncCharMarkerGlyphs(currentNode, charAttrItem.style);\n if (typeof charAttrItem.cid === \"string\") {\n $setState(currentNode, charIdState, () => charAttrItem.cid);\n }\n const unknownAttributes = getUnknownAttributes(charAttrItem, OT_CHAR_PROPS);\n if (unknownAttributes && Object.keys(unknownAttributes).length > 0) {\n currentNode.setUnknownAttributes({\n ...(currentNode.getUnknownAttributes() ?? {}),\n ...unknownAttributes,\n });\n }\n else {\n // If no unknown attributes, clear them\n // TODO: this was added - review if this is right and add elsewhere?\n currentNode.setUnknownAttributes(undefined);\n }\n }\n }\n else if (attributes.char === false ||\n attributes.char === null ||\n isEmptyObject(attributes.char)) {\n shouldRemoveCharNode = true;\n }\n }\n // Process children of CharNodes (no OT length contribution)\n if (lengthToFormat > 0) {\n const children = currentNode.getChildren();\n for (const child of children) {\n if (lengthToFormat <= 0)\n break;\n if ($traverseAndApplyAttributesRecursive(child)) {\n if (lengthToFormat <= 0) {\n if (shouldRemoveCharNode)\n $unwrapNode(currentNode);\n return true;\n }\n }\n }\n }\n if (shouldRemoveCharNode) {\n $unwrapNode(currentNode);\n }\n nestedCharCount -= 1;\n }\n else if ($isParaLikeNode(currentNode)) {\n // Process children first, then account for the block's own closing OT length.\n const children = currentNode.getChildren();\n for (const child of children) {\n if (lengthToFormat <= 0)\n break;\n if ($traverseAndApplyAttributesRecursive(child)) {\n if (lengthToFormat <= 0)\n return true; // Early exit if formatting complete\n }\n }\n // After children, account for the block's closing marker (OT length 1)\n const blockClosingOtLength = 1;\n // currentIndex is now positioned after all children of this block node.\n // Check if the retain operation targets this closing marker.\n if (targetIndex <= currentIndex &&\n currentIndex < targetIndex + lengthToFormat &&\n lengthToFormat > 0) {\n if (!$isImpliedParaNode(currentNode))\n $applyEmbedAttributes(currentNode, attributes);\n else if (hasParaAttributes(attributes)) {\n const newPara = $createPara(attributes.para, viewOptions);\n // `replace(…, true)` appends the implied para's children AFTER the new paragraph's\n // marker prefix, so content stays on the content side of the glyphs.\n if (newPara)\n currentNode.replace(newPara, true);\n }\n lengthToFormat -= blockClosingOtLength;\n }\n currentIndex += blockClosingOtLength;\n }\n else if ($isElementNode(currentNode)) {\n // Other ElementNodes that don't contribute to the OT length (like RootNode)\n const children = currentNode.getChildren();\n for (const child of children) {\n if (lengthToFormat <= 0)\n break;\n if ($traverseAndApplyAttributesRecursive(child)) {\n if (lengthToFormat <= 0)\n return true;\n }\n }\n }\n // Else: Non-text, non-element, non-handled nodes (e.g. LineBreakNode, DecoratorNode if not\n // explicitly handled). These typically don't contribute to OT length in this model or are\n // handled by Lexical internally.\n return lengthToFormat <= 0;\n }\n $traverseAndApplyAttributesRecursive(root);\n if (lengthToFormat > 0) {\n logger?.warn(`$applyAttributes: Not all characters in the retain operation (length ${retain}) could be processed. Remaining: ${lengthToFormat}. targetIndex: ${targetIndex}, final currentIndex: ${currentIndex}`);\n }\n}\n/**\n * Applies the given attributes to the specified text node wrapped in nested CharNodes.\n * @param textNode - The text node to wrap and to which attributes should be applied.\n * @param attributes - The attributes to apply.\n * @param textAttributes - The text attributes to apply.\n * @param logger - The logger to use for logging, if any.\n * @returns A CharNode if the operation was successful, otherwise undefined.\n */\nfunction $wrapInNestedCharNodes(textNode, attributes, viewOptions, logger) {\n // Create new CharNode(s) with the attributes, supporting nested char arrays\n const segment = typeof attributes.segment === \"string\" ? attributes.segment : undefined;\n const newCharNodes = $createNestedChars(attributes.char, viewOptions, textNode, segment);\n const charNode = newCharNodes.find($isCharNode);\n if (!charNode) {\n logger?.error(`Failed to create CharNode for text transformation. Style: ${Array.isArray(attributes.char) ? attributes.char[0].style : attributes.char?.style}. Falling back to standard text attributes.`);\n $applyTextAttributes(attributes, textNode);\n return undefined;\n }\n // Copy original text formatting to CharNode's unknownAttributes\n const textFormatAttributes = {};\n TEXT_FORMAT_TYPES.forEach((format) => {\n if (textNode.hasFormat(format)) {\n textFormatAttributes[format] = \"true\";\n }\n });\n // Convert attributes to string values for unknownAttributes\n const stringifiedAttributes = {};\n Object.entries(attributes).forEach(([key, value]) => {\n if (key === \"segment\" || key === \"char\")\n return;\n if (typeof value === \"string\") {\n stringifiedAttributes[key] = value;\n }\n else if (value === true) {\n stringifiedAttributes[key] = \"true\";\n }\n else if (value === false) {\n stringifiedAttributes[key] = \"false\";\n }\n // Skip other types that can't be serialized to string\n });\n // Combine all attributes for the CharNode\n const combinedUnknownAttributes = {\n ...(charNode.getUnknownAttributes() ?? {}),\n ...textFormatAttributes,\n ...stringifiedAttributes,\n };\n if (Object.keys(combinedUnknownAttributes).length > 0) {\n charNode.setUnknownAttributes(combinedUnknownAttributes);\n }\n $applyTextAttributes(attributes, textNode);\n return newCharNodes;\n}\n/**\n * Sets a paragraph's marker and rewrites its visible marker-glyph prefix to match, when one is\n * present. In editable marker mode a paragraph carries its marker as an editable `MarkerNode`\n * first child (`\\q1`); in visible marker mode and gutter para-marker rendering it carries an\n * immutable typed-text first child instead (`\\q1` glyph + NBSP separator in one node). Either\n * way, marker state and glyph text must change together: a stale editable glyph re-tokenizes\n * as a DIFFERENT marker than the paragraph claims on the next serialization, and a stale\n * typed-text glyph keeps displaying the old marker. Purely structural — trees whose paragraphs\n * carry no glyph prefix (hidden marker rendering) get the bare marker state change, and a\n * missing prefix is never injected. The glyph text is restored unconditionally so drifted\n * glyph text (an abandoned in-glyph rename literal) canonicalizes even when the marker value\n * itself is unchanged.\n *\n * @param para - The paragraph to retag. Must be called inside `editor.update()`.\n * @param marker - The new paragraph marker (e.g. `\"q1\"`).\n *\n * @public\n */\nexport function $syncParaMarkerGlyph(para, marker) {\n para.setMarker(marker);\n const glyph = para.getFirstChild();\n if ($isMarkerNode(glyph)) {\n glyph.setMarker(marker);\n glyph.setTextContent(openingMarkerText(marker));\n }\n else if ($isImmutableTypedTextNode(glyph) && glyph.getTextType() === \"marker\") {\n // Keep in sync with the adaptor's visible/gutter para-prefix shape in `createPara`\n // (usj-editor.adaptor.ts): opening marker text + NBSP.\n glyph.setTextContent(openingMarkerText(marker) + NBSP);\n }\n}\n/**\n * Sets a char span's marker and rewrites its own opening/closing marker glyphs to match, when\n * present. In editable marker mode a char span carries its `\\wj`…`\\wj*` glyph pair as\n * `MarkerNode` children; renaming the span without rewriting the pair leaves glyph text that\n * re-tokenizes as a different span than the node claims. In visible marker mode the pair is\n * immutable typed-text nodes instead (bare `\\wj` opener and `\\wj*` closer, as the USJ adaptor\n * and `$addOpeningMarker`/`$addClosingMarker` build them); their display text still names the\n * marker, so it is rewritten too. Only DIRECT children matching the span's OLD marker are\n * rewritten: glyphs that an ancestor span carries for a nested child (the delta-materialized\n * flattened shape) name the nested span's marker, not this one's, and are deliberately left\n * alone — the marker-edit engine's in-place rename refuses that shape for the same reason.\n * Glyph-less trees (hidden marker mode) get the bare marker state change only, and a missing\n * glyph (e.g. an unclosed span's absent closer) is never injected.\n *\n * @param char - The char span to retag. Must be called inside `editor.update()`.\n * @param marker - The new char marker (e.g. `\"nd\"`).\n *\n * @public\n */\nexport function $syncCharMarkerGlyphs(char, marker) {\n const oldMarker = char.getMarker();\n char.setMarker(marker);\n if (marker === oldMarker)\n return;\n char.getChildren().forEach((child) => {\n if ($isMarkerNode(child) && child.getMarker() === oldMarker)\n child.setMarker(marker);\n });\n // Visible-mode typed-text pair: the span's own opener/closer are its first/last children\n // (a flattened nested child's glyphs sit between them). Matching the OLD marker's exact\n // glyph text keeps this to the span's own pair, like the MarkerNode path above. A span nested\n // inside another char renders `\\+marker`, so match and rewrite the nested-aware text.\n const nested = $isCharNode(char.getParent());\n const opener = char.getFirstChild();\n if ($isImmutableTypedTextNode(opener) &&\n opener.getTextType() === \"marker\" &&\n opener.getTextContent() === openingMarkerText(oldMarker, nested)) {\n opener.setTextContent(openingMarkerText(marker, nested));\n }\n const closer = char.getLastChild();\n if ($isImmutableTypedTextNode(closer) &&\n closer.getTextType() === \"marker\" &&\n closer.getTextContent() === closingMarkerText(oldMarker, nested)) {\n closer.setTextContent(closingMarkerText(marker, nested));\n }\n}\n// Apply attributes to the given embed node\nfunction $applyEmbedAttributes(node, attributes) {\n for (const key of Object.keys(attributes)) {\n const value = attributes[key];\n // Special handling for char attributes on CharNodes\n if (key === \"char\" && $isCharNode(node) && hasCharAttributes(attributes)) {\n const charAttributes = cleanCharStyle(value);\n $syncCharMarkerGlyphs(node, charAttributes.style);\n // Set charIdState if cid is present\n if (typeof charAttributes.cid === \"string\") {\n const cid = charAttributes.cid;\n $setState(node, charIdState, () => cid);\n }\n // Apply other char attributes to unknownAttributes\n const unknownAttributes = getUnknownAttributes(charAttributes, OT_CHAR_PROPS);\n if (unknownAttributes && Object.keys(unknownAttributes).length > 0) {\n node.setUnknownAttributes({\n ...(node.getUnknownAttributes() ?? {}),\n ...unknownAttributes,\n });\n }\n continue;\n }\n if (typeof value !== \"string\") {\n // Skip non-string attributes (except char which is handled above)\n continue;\n }\n if ($isSomeChapterNode(node) ||\n $isSomeVerseNode(node) ||\n $isMilestoneNode(node) ||\n $isNoteNode(node) ||\n $isUnknownNode(node)) {\n node.setUnknownAttributes({\n ...(node.getUnknownAttributes() ?? {}),\n [key]: value,\n });\n }\n else if ($isBookNode(node) || $isParaNode(node) || $isCharNode(node)) {\n if (key === \"style\" && $isParaNode(node)) {\n $syncParaMarkerGlyph(node, value);\n }\n else if (key === \"style\" && $isCharNode(node)) {\n $syncCharMarkerGlyphs(node, value);\n }\n else if (key === \"code\" && $isBookNode(node)) {\n node.setCode(value);\n }\n else {\n node.setUnknownAttributes({\n ...(node.getUnknownAttributes() ?? {}),\n [key]: value,\n });\n }\n }\n if (key === \"segment\") {\n $setState(node, segmentState, () => value);\n }\n }\n}\n// Helper function to delete items starting at a given flat index from the document\nfunction $delete(targetIndex, otLength, logger) {\n if (otLength <= 0)\n return;\n const root = $getRoot();\n let currentIndex = 0; // Tracks characters traversed so far in the document's text content\n let remainingToDelete = otLength;\n // Inner recursive function to find and delete text\n function $traverseAndDelete(currentNode) {\n if (remainingToDelete <= 0)\n return true;\n if ($isOTTextNode(currentNode)) {\n let textLength = currentNode.getTextContentSize();\n if (targetIndex < currentIndex + textLength &&\n currentIndex < targetIndex + remainingToDelete) {\n const offsetInNode = Math.max(0, targetIndex - currentIndex);\n const deletableLengthInNode = textLength - offsetInNode;\n const lengthToDeleteFromThisNode = Math.min(remainingToDelete, deletableLengthInNode);\n if (lengthToDeleteFromThisNode > 0) {\n currentNode.spliceText(offsetInNode, lengthToDeleteFromThisNode, \"\");\n // Remove the TextNode if it becomes empty\n if (currentNode.getTextContentSize() === 0) {\n currentNode.remove();\n }\n logger?.debug(`Deleted ${lengthToDeleteFromThisNode} length from TextNode ` +\n `(key: ${currentNode.getKey()}) at nodeOffset ${offsetInNode}. ` +\n `Original targetIndex: ${targetIndex}, current currentIndex: ${currentIndex}.`);\n remainingToDelete -= lengthToDeleteFromThisNode;\n // Adjust textLength to account for the text that was deleted\n textLength -= lengthToDeleteFromThisNode;\n }\n }\n currentIndex += textLength;\n }\n else if ($isEmbedNode(currentNode)) {\n // Check if the deletion should remove this embed\n if (targetIndex <= currentIndex && currentIndex < targetIndex + remainingToDelete) {\n // The deletion spans this embed - remove it\n currentNode.remove();\n logger?.debug(`Deleted embed node (key: ${currentNode.getKey()}) at currentIndex: ${currentIndex}. ` +\n `Original targetIndex: ${targetIndex}, remainingToDelete: ${remainingToDelete}.`);\n remainingToDelete -= 1;\n }\n else {\n // Deletion doesn't affect this embed, just advance past it\n currentIndex += 1;\n }\n }\n else if ($isParaLikeNode(currentNode)) {\n // Process children first, then handle the symbolic close.\n const childrenBefore = currentNode.getChildren().slice(); // Save original children\n // Process children\n const children = currentNode.getChildren();\n for (const child of children) {\n if (remainingToDelete <= 0)\n break;\n if ($traverseAndDelete(child)) {\n if (remainingToDelete <= 0)\n return true;\n }\n }\n // Check if the deletion targets the symbolic close of this block node\n if (targetIndex <= currentIndex &&\n currentIndex < targetIndex + remainingToDelete &&\n $isParaLikeNode(currentNode)) {\n // Deleting the symbolic close of a block node\n remainingToDelete -= 1;\n // Determine if this entire paragraph should be removed\n const currentChildrenLength = currentNode.getChildren().length;\n const hadChildren = childrenBefore.length > 0;\n const deletedAllContent = hadChildren && currentChildrenLength === 0;\n if (deletedAllContent) {\n // This paragraph had content that was entirely deleted, and now we're deleting its symbolic close\n // Remove the entire paragraph\n const parent = currentNode.getParent();\n const siblings = parent?.getChildren() ?? [];\n if (siblings.length > 1) {\n // There are other paragraphs, safe to remove this one\n currentNode.remove();\n logger?.debug(`Removed entire ParaNode that had all its content deleted at currentIndex: ${currentIndex}. ` +\n `Original targetIndex: ${targetIndex}, remainingToDelete: ${remainingToDelete}.`);\n }\n else {\n // This is the only paragraph, replace with ImpliedParaNode instead of removing\n currentNode.replace($createImpliedParaNode(), true);\n logger?.debug(`Replaced last ParaNode with ImpliedParaNode at currentIndex: ${currentIndex}. ` +\n `Original targetIndex: ${targetIndex}, remainingToDelete: ${remainingToDelete}.`);\n }\n }\n else if (remainingToDelete > 0) {\n // We're deleting the symbolic close and continuing to next content\n const nextSibling = currentNode.getNextSibling();\n if (nextSibling && $isSomeParaNode(nextSibling)) {\n // Standard merge logic: merge next paragraph into current one\n let tempCurrentIndex = currentIndex + 1;\n const nextChildren = nextSibling.getChildren();\n for (const nextChild of nextChildren) {\n if (remainingToDelete <= 0)\n break;\n const originalCurrentIndex = currentIndex;\n currentIndex = tempCurrentIndex;\n if ($traverseAndDelete(nextChild)) {\n currentIndex = originalCurrentIndex;\n break;\n }\n if ($isOTTextNode(nextChild)) {\n tempCurrentIndex += nextChild.getTextContentSize();\n }\n else if ($isEmbedNode(nextChild)) {\n tempCurrentIndex += 1;\n }\n currentIndex = originalCurrentIndex;\n }\n // Move remaining content from next paragraph to current paragraph\n const remainingNextChildren = nextSibling.getChildren();\n for (const remainingChild of remainingNextChildren) {\n remainingChild.remove();\n currentNode.append(remainingChild);\n }\n nextSibling.remove();\n logger?.debug(`Merged next paragraph into current one after deleting symbolic close at currentIndex: ${currentIndex}. ` +\n `Original targetIndex: ${targetIndex}, remainingToDelete: ${remainingToDelete}.`);\n }\n else {\n // No next paragraph to merge, replace with ImpliedParaNode\n currentNode.replace($createImpliedParaNode(), true);\n }\n }\n else if ($isParaNode(currentNode)) {\n // Only deleting the symbolic close, replace with ImpliedParaNode\n currentNode.replace($createImpliedParaNode(), true);\n }\n else {\n currentNode.remove();\n }\n }\n currentIndex += 1;\n }\n else if ($isElementNode(currentNode)) {\n // Other ElementNodes that don't contribute to the OT length (like RootNode, CharNode)\n const children = currentNode.getChildren();\n for (const child of children) {\n if (remainingToDelete <= 0)\n break;\n if ($traverseAndDelete(child)) {\n if (remainingToDelete <= 0)\n return true;\n }\n }\n }\n return remainingToDelete <= 0;\n }\n $traverseAndDelete(root);\n if (remainingToDelete > 0) {\n logger?.warn(`Delete operation could not remove all requested characters. Remaining to delete: ${remainingToDelete}. Original targetIndex: ${targetIndex}, OT length: ${otLength}. Final currentIndex: ${currentIndex}`);\n }\n}\n/**\n * Inserts text or a CharNode at a given flat index in the document.\n * If attributes.char is present, a CharNode is created and inserted.\n * Otherwise, rich text is inserted, potentially with formatting attributes.\n * @param targetIndex - The index in the document's flat representation.\n * @param textToInsert - The string to insert.\n * @param attributes - Optional attributes for the insert operation.\n * @param logger - Logger to use, if any.\n * @returns The length to advance the currentIndex in $applyUpdate (1 for CharNode, text.length for\n * text).\n */\nfunction $insertTextAtCurrentIndex(targetIndex, textToInsert, attributes, viewOptions, logger) {\n if (textToInsert === LF) {\n return $handleNewline(targetIndex, attributes, viewOptions, logger);\n }\n else if (textToInsert.endsWith(LF) && !hasParaAttributes(attributes)) {\n // Split the operation: insert text without LF, then handle the LF separately as an implied para\n const textWithoutLF = textToInsert.slice(0, -1);\n let deltaOTLength = 0;\n if (textWithoutLF.length > 0) {\n if (hasCharAttributes(attributes))\n throw new Error(\"Text + LF should not have char attributes\");\n deltaOTLength += $insertRichText(targetIndex, textWithoutLF, attributes, logger);\n }\n deltaOTLength += $handleNewline(targetIndex + deltaOTLength, attributes, viewOptions, logger);\n return deltaOTLength;\n }\n else if (hasCharAttributes(attributes)) {\n return $handleCharText(targetIndex, textToInsert, attributes, viewOptions, logger);\n }\n else {\n return $insertRichText(targetIndex, textToInsert, attributes, logger);\n }\n}\nfunction $handleCharText(targetIndex, textToInsert, attributes, viewOptions, logger) {\n logger?.debug(`Attempting to insert CharNode with text \"${textToInsert}\" and attributes ${JSON.stringify(attributes.char)} at index ${targetIndex}`);\n const textNode = $createTextNode(textToInsert === \"\" ? EMPTY_CHAR_PLACEHOLDER_TEXT : textToInsert);\n // Apply other non-char attributes to the TextNode inside the CharNode if necessary.\n $applyTextAttributes(attributes, textNode);\n // Find parent CharNode at insertion point, if any\n let parentCharNode;\n {\n // Traverse to find the parent node at the insertion point\n const root = $getRoot();\n let currentIndex = 0;\n function findParentCharNode(node) {\n if ($isOTTextNode(node)) {\n const textLength = node.getTextContentSize();\n if (targetIndex >= currentIndex && targetIndex < currentIndex + textLength) {\n const parent = node.getParent();\n if ($isCharNode(parent)) {\n parentCharNode = parent;\n }\n return true;\n }\n currentIndex += textLength;\n }\n else if ($isEmbedNode(node)) {\n currentIndex += 1;\n }\n else if ($isCharNode(node)) {\n // CharNodes don't contribute to OT length, but may contain text\n const children = node.getChildren();\n for (const child of children) {\n if (findParentCharNode(child))\n return true;\n }\n }\n else if ($isElementNode(node)) {\n const children = node.getChildren();\n for (const child of children) {\n if (findParentCharNode(child))\n return true;\n }\n if ($isParaLikeNode(node)) {\n currentIndex += 1;\n }\n }\n return false;\n }\n findParentCharNode(root);\n }\n // If inserting a nested char array, and parent matches the first char, skip nesting that one\n // If parent doesn't match, clear it so we insert as sibling instead\n let charAttr = attributes.char;\n if (Array.isArray(charAttr)) {\n if (parentCharNode) {\n const first = charAttr[0];\n if (first && $hasSameCharAttributes(first, parentCharNode)) {\n // Only nest the remaining char attributes\n charAttr = charAttr.slice(1);\n // If only one left, treat as single\n if (charAttr.length === 1)\n charAttr = charAttr[0];\n // Keep parentCharNode - we're nesting into it\n }\n else {\n // Parent doesn't match, don't use it\n parentCharNode = undefined;\n }\n }\n }\n else if (parentCharNode) {\n // Single char attribute - check if it matches parent\n if (!$hasSameCharAttributes(charAttr, parentCharNode)) {\n // Parent doesn't match, don't use it\n parentCharNode = undefined;\n }\n }\n const segment = typeof attributes.segment === \"string\" ? attributes.segment : undefined;\n const existingNodes = parentCharNode ? [parentCharNode] : undefined;\n const charNodes = $createNestedChars(charAttr, viewOptions, textNode, segment, existingNodes);\n // If charNodes is empty, it means we merged into existingNodes - no insertion needed\n if (charNodes.length === 0) {\n return textToInsert.length; // Successfully merged into existing CharNode\n }\n const charNode = charNodes.find($isCharNode);\n if (!charNode) {\n logger?.error(`CharNode style is missing for text \"${textToInsert}\". Attributes: ${JSON.stringify(attributes.char)}. Falling back to rich text insertion.`);\n // Fallback to rich text insertion\n return $insertRichText(targetIndex, textToInsert, undefined, logger);\n }\n // Set unknownAttributes for non-char, non-segment attributes\n const unknownAttributes = {};\n for (const [key, value] of Object.entries(attributes)) {\n if (key !== \"char\" && key !== \"segment\" && typeof value === \"string\") {\n unknownAttributes[key] = value;\n }\n }\n if (Object.keys(unknownAttributes).length > 0) {\n charNode.setUnknownAttributes(unknownAttributes);\n }\n // Insert all nodes (might include markers) at the target position\n let allInserted = true;\n for (const node of charNodes) {\n if (!$insertNodeAtCharacterOffset(targetIndex, node, logger)) {\n allInserted = false;\n break;\n }\n // Only advance index for non-marker nodes (markers don't contribute to OT length)\n // CharNodes themselves don't contribute, only their text content does\n }\n if (allInserted) {\n return textToInsert.length; // CharNode itself has no OT length, just its text content\n }\n else {\n logger?.error(`Failed to insert CharNode with text \"${textToInsert}\" at index ${targetIndex}. Falling back to rich text.`);\n // Fallback to rich text insertion if CharNode insertion fails\n return $insertRichText(targetIndex, textToInsert, undefined, logger);\n }\n}\n/**\n * Helper to insert rich text, i.e. potentially with formatting or other attributes.\n * This function contains the core logic for text node insertion and splitting.\n * @returns The length of the inserted text.\n */\nfunction $insertRichText(targetIndex, textToInsert, attributes, logger) {\n if (textToInsert.length <= 0) {\n logger?.debug(\"Attempted to insert empty string. No action taken.\");\n return 0;\n }\n const root = $getRoot();\n let currentIndex = 0;\n let insertionPointFound = false;\n function $findAndInsertRecursive(currentNode) {\n if (insertionPointFound)\n return true;\n if ($isOTTextNode(currentNode)) {\n const textLength = currentNode.getTextContentSize();\n // Check if targetIndex is within this TextNode's range\n if (targetIndex >= currentIndex && targetIndex <= currentIndex + textLength) {\n const offsetInNode = targetIndex - currentIndex;\n const newTextNode = $createTextNode(textToInsert);\n $applyTextAttributes(attributes, newTextNode);\n if (offsetInNode === 0) {\n currentNode.insertBefore(newTextNode);\n }\n else if (offsetInNode === textLength) {\n // Special case: if this TextNode is inside a CharNode and we're inserting plain text at the end,\n // check if we should insert after the CharNode instead of inside it\n const parent = currentNode.getParent();\n if ($isCharNode(parent) && !hasCharAttributes(attributes)) {\n // Plain text (no char attributes) should not be inserted inside CharNodes\n // Instead, insert after the CharNode at the parent level\n parent.insertAfter(newTextNode);\n }\n else {\n // Normal case: insert after this TextNode\n currentNode.insertAfter(newTextNode);\n }\n }\n else {\n const [, tailNode] = currentNode.splitText(offsetInNode);\n tailNode.insertBefore(newTextNode);\n }\n logger?.debug(`Inserted text \"${textToInsert}\" in/around TextNode ` +\n `(key: ${currentNode.getKey()}) at nodeOffset ${offsetInNode}. Original targetIndex: ${targetIndex}, currentIndex at node start: ${currentIndex}.`);\n insertionPointFound = true;\n return true;\n }\n currentIndex += textLength;\n }\n else if ($isEmbedNode(currentNode)) {\n // If targetIndex is exactly at currentIndex, means insert *before* this embed node.\n // This function is for rich text; inserting before/after embed nodes usually involves\n // $insertNodeAtCharacterOffset or ensuring a Para wrapper.\n // For now, just advance offset.\n if (targetIndex === currentIndex && !insertionPointFound) {\n // Potentially insert into a new para before this node if context allows,\n // or let caller handle creating appropriate structure.\n // This function's primary goal is inserting into existing text-compatible locations.\n }\n currentIndex += 1;\n }\n else if ($isCharNode(currentNode)) {\n // CharNodes don't contribute to OT length, they're just formatted text containers\n const offsetAtCharNodeStart = currentIndex;\n // Try inserting at the beginning of the CharNode's content\n if (!insertionPointFound && targetIndex === offsetAtCharNodeStart) {\n // This implies inserting as the first child inside the CharNode\n const newTextNode = $createTextNode(textToInsert);\n $applyTextAttributes(attributes, newTextNode);\n const firstChild = currentNode.getFirstChild();\n if (firstChild) {\n firstChild.insertBefore(newTextNode);\n }\n else {\n currentNode.append(newTextNode);\n }\n logger?.debug(`Inserted text \"${textToInsert}\" at beginning of CharNode ` +\n `${currentNode.getType()} (key: ${currentNode.getKey()}).`);\n insertionPointFound = true;\n return true;\n }\n // No OT length contribution for CharNodes themselves\n const children = currentNode.getChildren();\n for (const child of children) {\n if ($findAndInsertRecursive(child))\n return true;\n if (insertionPointFound)\n break;\n }\n // Try appending to the CharNode if targetIndex matches after children\n if (!insertionPointFound && targetIndex === currentIndex) {\n const newTextNode = $createTextNode(textToInsert);\n $applyTextAttributes(attributes, newTextNode);\n currentNode.append(newTextNode);\n logger?.debug(`Appended text \"${textToInsert}\" to end of CharNode ` +\n `${currentNode.getType()} (key: ${currentNode.getKey()}).`);\n insertionPointFound = true;\n return true;\n }\n }\n else if ($isParaLikeNode(currentNode)) {\n const offsetAtParaStart = currentIndex;\n // Try inserting at the beginning of the block node\n if (!insertionPointFound && targetIndex === offsetAtParaStart) {\n const newTextNode = $createTextNode(textToInsert);\n $applyTextAttributes(attributes, newTextNode);\n const firstChild = currentNode.getFirstChild();\n if (firstChild) {\n firstChild.insertBefore(newTextNode);\n }\n else {\n currentNode.append(newTextNode);\n }\n logger?.debug(`Inserted text \"${textToInsert}\" at beginning of container ` +\n `${currentNode.getType()} (key: ${currentNode.getKey()}).`);\n insertionPointFound = true;\n return true;\n }\n const children = currentNode.getChildren();\n for (const child of children) {\n if ($findAndInsertRecursive(child))\n return true;\n if (insertionPointFound)\n break;\n }\n // After children, currentIndex is at the end of the *content* of the ParaNode.\n // Try appending text if targetIndex matches (before para's own closing marker)\n if (!insertionPointFound && targetIndex === currentIndex) {\n const newTextNode = $createTextNode(textToInsert);\n $applyTextAttributes(attributes, newTextNode);\n currentNode.append(newTextNode);\n logger?.debug(`Appended text \"${textToInsert}\" to end of container ` +\n `${currentNode.getType()} (key: ${currentNode.getKey()}).`);\n insertionPointFound = true;\n return true;\n }\n // After children and potential append, account for ParaNode's closing marker.\n currentIndex += 1;\n }\n else if ($isElementNode(currentNode)) {\n // Other ElementNodes (e.g. RootNode)\n const children = currentNode.getChildren();\n for (const child of children) {\n if ($findAndInsertRecursive(child))\n return true;\n if (insertionPointFound)\n break;\n }\n }\n return insertionPointFound;\n }\n $findAndInsertRecursive(root);\n if (!insertionPointFound && targetIndex === currentIndex) {\n logger?.debug(`Insertion point matches end of document (targetIndex: ${targetIndex}, final currentIndex: ${currentIndex}). Appending text to new ParaNode.`);\n const newTextNode = $createTextNode(textToInsert);\n $applyTextAttributes(attributes, newTextNode);\n const newParaNode = $createImpliedParaNode().append(newTextNode);\n root.append(newParaNode);\n insertionPointFound = true;\n }\n if (!insertionPointFound) {\n logger?.warn(`$insertRichText: Could not find insertion point for text \"${textToInsert}\" at targetIndex ${targetIndex}. Final currentIndex: ${currentIndex}. Text not inserted.`);\n return 0; // Text not inserted\n }\n return textToInsert.length;\n}\n/**\n * Inserts a pre-constructed LexicalNode at a given character-based flat index in the document.\n * This is a complex operation that needs to correctly find the text-based offset.\n * @param targetIndex - The character offset in the document's flat text representation.\n * @param nodeToInsert - The LexicalNode to insert (e.g., a CharNode).\n * @param logger - Logger to use, if any.\n * @returns `true` if the node was successfully inserted, `false` otherwise.\n */\nfunction $insertNodeAtCharacterOffset(targetIndex, nodeToInsert, logger) {\n const root = $getRoot();\n /** Tracks the current OT position during traversal */\n let currentIndex = 0;\n let wasInserted = false;\n function $traverseAndInsertRecursive(currentNode) {\n if (wasInserted)\n return true;\n // Handle insertion at the beginning of the document or into an empty root.\n if (currentNode === root && targetIndex === 0) {\n const firstChild = root.getFirstChild();\n if (!firstChild) {\n // Root is empty\n if (nodeToInsert.isInline()) {\n logger?.debug(`$insertNodeAtCharacterOffset: Inserting inline node ` +\n `${nodeToInsert.getType()} into empty root, wrapped in ImpliedParaNode. ` +\n `targetIndex: ${targetIndex}`);\n root.append($createImpliedParaNode().append(nodeToInsert));\n }\n else {\n // Block node, insert directly into root\n logger?.debug(`$insertNodeAtCharacterOffset: Inserting block node ` +\n `${nodeToInsert.getType()} directly into empty root. targetIndex: ${targetIndex}`);\n root.append(nodeToInsert);\n }\n wasInserted = true;\n return true;\n }\n // If root is not empty, the loop below will handle inserting before the first child.\n }\n if (!$isElementNode(currentNode)) {\n return false; // Should not happen if called with ElementNode initially\n }\n const children = currentNode.getChildren();\n for (const child of children) {\n // Case 1: Insert *before* the current child\n if (targetIndex === currentIndex && !wasInserted) {\n // Check if we're inserting an inline node directly into the root\n if (currentNode === root && nodeToInsert.isInline()) {\n // If the child we're inserting before is a para-like node, insert into it\n if ($isSomeParaNode(child)) {\n logger?.debug(`$insertNodeAtCharacterOffset: Inserting inline node ` +\n `${nodeToInsert.getType()} into existing ${child.getType()} at beginning. ` +\n `targetIndex: ${targetIndex}`);\n // Insert at the beginning of the para by appending to the beginning\n const firstChildOfPara = child.getFirstChild();\n if (firstChildOfPara) {\n firstChildOfPara.insertBefore(nodeToInsert);\n }\n else {\n child.append(nodeToInsert);\n }\n }\n else {\n logger?.debug(`$insertNodeAtCharacterOffset: Inserting inline node ` +\n `${nodeToInsert.getType()} into root before ${child.getType()}, wrapping in ` +\n `ImpliedParaNode. targetIndex: ${targetIndex}`);\n child.insertBefore($createImpliedParaNode().append(nodeToInsert));\n }\n }\n else {\n child.insertBefore(nodeToInsert);\n logger?.debug(`$insertNodeAtCharacterOffset: Inserted node ${nodeToInsert.getType()} ` +\n `(key: ${nodeToInsert.getKey()}) before child ${child.getType()} ` +\n `(key: ${child.getKey()}) in ${currentNode.getType()} ` +\n `(key: ${currentNode.getKey()}). targetIndex: ${targetIndex}, currentIndex: ${currentIndex}`);\n }\n wasInserted = true;\n return true;\n }\n // Case 2: Process current `child` to advance `currentIndex` or insert within/after it.\n if ($isOTTextNode(child)) {\n const textLength = child.getTextContentSize();\n // Case 2a: Insert *within* this TextNode\n if (!wasInserted && targetIndex > currentIndex && targetIndex < currentIndex + textLength) {\n const splitOffset = targetIndex - currentIndex;\n const [headNode] = child.splitText(splitOffset);\n headNode.insertAfter(nodeToInsert);\n logger?.debug(`$insertNodeAtCharacterOffset: Inserted node ${nodeToInsert.getType()} ` +\n `(key: ${nodeToInsert.getKey()}) by splitting TextNode (key: ${child.getKey()}) ` +\n `at offset ${splitOffset}. targetIndex: ${targetIndex}, currentIndex at node start: ${currentIndex}`);\n wasInserted = true;\n return true;\n }\n currentIndex += textLength;\n }\n else if ($isEmbedNode(child)) {\n currentIndex += 1;\n }\n else if ($isCharNode(child)) {\n // CharNodes don't contribute to OT length, they're just formatted text containers\n // No OT length contribution for the CharNode itself\n if ($traverseAndInsertRecursive(child))\n return true;\n // currentIndex is now after child's content and its own recursive calls\n }\n else if ($isParaLikeNode(child)) {\n const paraLikeChild = child;\n // currentIndex is currently at the START of paraLikeChild's content area (or its embed\n // point if empty)\n if ($traverseAndInsertRecursive(paraLikeChild))\n return true;\n // If not inserted inside, `currentIndex` is now at the end of `paraLikeChild`'s content.\n const otIndexForParaChildClosingMarker = currentIndex;\n // Check for replacement: if inserting a block node at the closing marker of an\n // ImpliedParaNode\n if ($isImpliedParaNode(paraLikeChild) &&\n $isParaLikeNode(nodeToInsert) &&\n // Target is at the ImpliedPara's implicit newline\n targetIndex === otIndexForParaChildClosingMarker &&\n !wasInserted // Ensure we haven't already inserted elsewhere\n ) {\n logger?.debug(`$insertNodeAtCharacterOffset: Replacing ImpliedParaNode ` +\n `(key: ${paraLikeChild.getKey()}) with block node '${nodeToInsert.getType()}' ` +\n `(key: ${nodeToInsert.getKey()}) at OT index ${targetIndex}.`);\n child.replace(nodeToInsert, true);\n // The replacement block node also has a closing marker.\n // currentIndex was at otIndexForParaChildClosingMarker (end of content).\n // Now, advance by 1 for the new block node's closing marker.\n currentIndex = otIndexForParaChildClosingMarker + 1;\n wasInserted = true;\n return true;\n }\n // If not replaced, add 1 for the original paraLikeChild's closing marker.\n currentIndex += 1;\n }\n else if ($isElementNode(child)) {\n // Other ElementNode children (e.g. custom, or nested root-like)\n if ($traverseAndInsertRecursive(child))\n return true; // Recurse\n }\n // Else: other node types (LineBreakNode, DecoratorNode) - typically 0 OT length or handled by\n // Lexical.\n if (wasInserted)\n return true;\n } // End for loop over children\n // After iterating all children of `currentNode`, `currentIndex` reflects the OT position\n // *after* `currentNode`'s content and its children's closing markers.\n // This means `targetIndex === currentIndex` implies appending to `currentNode` or inserting\n // after it if `currentNode` is not root. For out-of-bounds cases where\n // `targetIndex > currentIndex`, we also handle appending to root.\n if ($isElementNode(currentNode) &&\n !wasInserted &&\n (targetIndex === currentIndex || (currentNode === root && targetIndex > currentIndex))) {\n if (currentNode === root) {\n // Appending to the root. currentIndex is total document OT length (or targetIndex is beyond\n // document end).\n if (nodeToInsert.isInline()) {\n logger?.debug(`$insertNodeAtCharacterOffset: Appending inline node ` +\n `${nodeToInsert.getType()} to root. Wrapping in new ImpliedParaNode. targetIndex: ${targetIndex}, current document OT length: ${currentIndex}.`);\n root.append($createImpliedParaNode().append(nodeToInsert));\n }\n else {\n // nodeToInsert is block\n logger?.debug(`$insertNodeAtCharacterOffset: Appending block node ${nodeToInsert.getType()} to ` +\n `root. targetIndex: ${targetIndex}, current document OT length: ${currentIndex}.`);\n root.append(nodeToInsert);\n }\n wasInserted = true;\n return true;\n }\n else if (\n // Appending to an existing container (ParaNode, ImpliedParaNode)\n // currentNode here is the container itself. currentIndex is at the point of currentNode's\n // closing marker. targetIndex === currentIndex means we are inserting at the conceptual end\n // of this container.\n $isSomeParaNode(currentNode)) {\n // If trying to insert a ParaNode at the closing marker of an ImpliedParaNode (this\n // container)\n if ($isImpliedParaNode(currentNode) &&\n $isParaNode(nodeToInsert) &&\n targetIndex === currentIndex) {\n logger?.debug(`$insertNodeAtCharacterOffset: Replacing ImpliedParaNode container ` +\n `(key: ${currentNode.getKey()}) with ParaNode ${nodeToInsert.getType()} ` +\n `(key: ${nodeToInsert.getKey()}) via append logic. targetIndex: ${targetIndex}`);\n currentNode.replace(nodeToInsert, true);\n // currentIndex remains correct relative to the start of this operation for the calling\n // $applyUpdate\n wasInserted = true;\n return true;\n }\n else if (nodeToInsert.isInline() || !$isSomeParaNode(nodeToInsert)) {\n // Append inline content, or non-para block content, into the container\n logger?.debug(`$insertNodeAtCharacterOffset: Appending node ${nodeToInsert.getType()} to existing ` +\n `container ${currentNode.getType()} (key: ${currentNode.getKey()}). targetIndex: ${targetIndex}, container end OT index: ${currentIndex}.`);\n currentNode.append(nodeToInsert);\n wasInserted = true;\n return true;\n }\n else {\n // Block node trying to append to a non-root container, insert *after* the container\n logger?.debug(`$insertNodeAtCharacterOffset: Inserting block node ${nodeToInsert.getType()} after ` +\n `container ${currentNode.getType()} (key: ${currentNode.getKey()}). targetIndex: ${targetIndex}, container end OT index: ${currentIndex}.`);\n currentNode.insertAfter(nodeToInsert);\n wasInserted = true;\n return true;\n }\n }\n else {\n // Generic element, try to append, or insert after if block\n // Special case: When at the end of a CharNode, insert after it as a sibling\n // (nested CharNodes are handled elsewhere via $createNestedChars merging logic)\n if ($isCharNode(currentNode)) {\n logger?.debug(`$insertNodeAtCharacterOffset: Inserting node ${nodeToInsert.getType()} after ` +\n `CharNode (key: ${currentNode.getKey()}). targetIndex: ${targetIndex}, element end OT index: ${currentIndex}.`);\n currentNode.insertAfter(nodeToInsert);\n }\n else if (nodeToInsert.isInline() || !$isSomeParaNode(nodeToInsert)) {\n logger?.debug(`$insertNodeAtCharacterOffset: Appending node ${nodeToInsert.getType()} to generic ` +\n `element ${currentNode.getType()} (key: ${currentNode.getKey()}). targetIndex: ${targetIndex}, element end OT index: ${currentIndex}.`);\n currentNode.append(nodeToInsert);\n }\n else {\n logger?.debug(`$insertNodeAtCharacterOffset: Inserting block node ${nodeToInsert.getType()} after ` +\n `generic element ${currentNode.getType()} (key: ${currentNode.getKey()}). ` +\n `targetIndex: ${targetIndex}, element end OT index: ${currentIndex}.`);\n currentNode.insertAfter(nodeToInsert);\n }\n wasInserted = true;\n return true;\n }\n }\n return wasInserted;\n }\n $traverseAndInsertRecursive(root);\n if (!wasInserted) {\n logger?.warn(\"$insertNodeAtCharacterOffset: Could not find insertion point for node \" +\n `${nodeToInsert.getType()} (key: ${nodeToInsert.getKey()}) at targetIndex ${targetIndex}. Final currentIndex: ${currentIndex}. Node not inserted.`);\n }\n return wasInserted;\n}\nfunction $insertEmbedAtCurrentIndex(targetIndex, op, viewOptions, nodeOptions, logger) {\n let newNodeToInsert;\n // Determine the LexicalNode to create based on the embedObject structure\n if (isInsertEmbedOpOfType(\"chapter\", op)) {\n newNodeToInsert = $createChapter(op.insert.chapter, viewOptions);\n }\n else if (isInsertEmbedOpOfType(\"verse\", op)) {\n newNodeToInsert = $createVerse(op.insert.verse, viewOptions);\n }\n else if (isInsertEmbedOpOfType(\"ms\", op)) {\n newNodeToInsert = $createMilestone(op.insert.ms);\n }\n else if (isInsertEmbedOpOfType(\"note\", op)) {\n newNodeToInsert = $createNote(op, viewOptions, nodeOptions, logger);\n }\n else if (isInsertEmbedOpOfType(\"unknown\", op)) {\n newNodeToInsert = $createUnknown(op, viewOptions, nodeOptions, logger);\n }\n else if (isInsertEmbedOpOfType(\"unmatched\", op)) {\n newNodeToInsert = $createImmutableUnmatched(op.insert.unmatched, viewOptions);\n }\n // While it would be technically and structurally possible to add a ParaNode here, it's not the\n // way Quill (and therefore flat rich-text docs) handles paragraphs which is always by inserting a\n // newline (LF) character with a `para` attribute.\n if (!newNodeToInsert) {\n logger?.error(`$insertEmbedAtCurrentIndex: Cannot create LexicalNode for embed object: ${JSON.stringify(op.insert)}`);\n return false;\n }\n return $insertNodeAtCharacterOffset(targetIndex, newNodeToInsert, logger);\n}\n/**\n * Handles inserting a newline (LF) character.\n * This can replace an ImpliedParaNode with a ParaNode or BookNode, or split a regular ParaNode\n * if the para attributes differ from the containing paragraph.\n * When there are no attributes it splits a regular ParaNode into an ImpliedParaNode for the first\n * part and keeps the second part as a ParaNode.\n * @param targetIndex - The index in the document's flat representation.\n * @param attributes - The attributes to use for creating the ParaNode or BookNode.\n * @param viewOptions - View options of the editor (determines the new paragraph's marker prefix).\n * @param logger - Logger to use, if any.\n * @returns Always returns 1 (the LF character's OT length).\n */\nfunction $handleNewline(targetIndex, attributes, viewOptions, logger) {\n let _newBlockNode;\n if (hasParaAttributes(attributes)) {\n _newBlockNode = $createPara(attributes.para, viewOptions);\n }\n else if (hasBookAttributes(attributes)) {\n const attributesWithBook = attributes;\n _newBlockNode = $createBook(attributesWithBook.book);\n }\n _newBlockNode ??= $createImpliedParaNode();\n const newBlockNode = _newBlockNode;\n const isNewParaNode = $isParaNode(newBlockNode);\n const isNewImpliedParaNode = $isImpliedParaNode(newBlockNode);\n let currentIndex = 0;\n let foundTargetBlock = false;\n function $traverseAndHandleNewline(currentNode) {\n if (foundTargetBlock)\n return true;\n if ($isOTTextNode(currentNode)) {\n const textLength = currentNode.getTextContentSize();\n // Check if targetIndex is within this text node\n if (targetIndex >= currentIndex && targetIndex <= currentIndex + textLength) {\n // Split is happening within a text node - need to check if we're in a ParaNode\n const parentPara = currentNode.getParent();\n if ($isParaNode(parentPara) && (isNewParaNode || isNewImpliedParaNode)) {\n // LF with attributes should ALWAYS split a regular ParaNode\n logger?.debug(`Splitting ParaNode (marker: ${parentPara.getMarker()}) with LF attributes at ` +\n `targetIndex ${targetIndex}`);\n // Split the text node at the target position\n const splitOffset = targetIndex - currentIndex;\n const [headNode] = splitOffset > 0 ? currentNode.splitText(splitOffset) : [undefined];\n // Move all content before the split to the new ParaNode. Anchor on the first MOVED\n // node, not the block's first child: the new paragraph may already carry its marker\n // prefix, and content must land after those glyphs, not before them.\n let firstMovedNode;\n let prevSibling = headNode?.getPreviousSibling();\n while (prevSibling) {\n const siblingToMove = prevSibling;\n prevSibling = prevSibling.getPreviousSibling();\n if (firstMovedNode)\n firstMovedNode.insertBefore(siblingToMove);\n else\n newBlockNode.append(siblingToMove);\n firstMovedNode = siblingToMove;\n }\n if (headNode)\n newBlockNode.append(headNode);\n // Insert the new paragraph before the existing one\n parentPara.insertBefore(newBlockNode);\n foundTargetBlock = true;\n return true;\n }\n }\n currentIndex += textLength;\n }\n else if ($isEmbedNode(currentNode)) {\n currentIndex += 1;\n }\n else if ($isParaLikeNode(currentNode)) {\n // First, process children to find current position\n const children = currentNode.getChildren();\n for (const child of children) {\n if ($traverseAndHandleNewline(child))\n return true;\n if (foundTargetBlock)\n break;\n }\n // currentIndex is now at the end of this para's content\n // Check if targetIndex matches the para's closing marker position\n if (targetIndex === currentIndex) {\n if ($isImpliedParaNode(currentNode) && newBlockNode) {\n logger?.debug(`Replacing ImpliedParaNode (key: ${currentNode.getKey()}) with ParaNode at ` +\n `targetIndex ${targetIndex}`);\n // Replace the ImpliedParaNode with the new block node\n currentNode.replace(newBlockNode, true);\n foundTargetBlock = true;\n return true;\n }\n else if ($isParaNode(currentNode) && newBlockNode) {\n const paraNode = currentNode;\n // LF with attributes should ALWAYS create a new block node after regular ParaNode\n logger?.debug(\"Creating new block node with LF attributes after existing ParaNode \" +\n `(marker: ${paraNode.getMarker()}) at targetIndex ${targetIndex}`);\n // Insert the new block node with LF attributes after the current one\n paraNode.insertAfter(newBlockNode);\n foundTargetBlock = true;\n return true;\n }\n }\n // Advance by 1 for the para's closing marker\n currentIndex += 1;\n // Check if targetIndex matches the position after this para (for inserting after the para)\n if (targetIndex === currentIndex) {\n if ($isParaNode(currentNode) && newBlockNode) {\n // LF with attributes should create a new block node after this ParaNode\n logger?.debug(`Creating new block node after existing ParaNode (marker: ${currentNode.getMarker()}) ` +\n `at targetIndex ${targetIndex}`);\n // Insert the new block node with LF attributes after the current one\n currentNode.insertAfter(newBlockNode);\n foundTargetBlock = true;\n return true;\n }\n }\n }\n else if ($isElementNode(currentNode)) {\n // Other ElementNodes that don't contribute to the OT length (like RootNode, CharNode)\n const children = currentNode.getChildren();\n for (const child of children) {\n if ($traverseAndHandleNewline(child))\n return true;\n if (foundTargetBlock)\n break;\n }\n }\n return foundTargetBlock;\n }\n $traverseAndHandleNewline($getRoot());\n if (!foundTargetBlock) {\n logger?.warn(`Could not find location to handle newline with para attributes at targetIndex ${targetIndex}. Final currentIndex: ${currentIndex}.`);\n }\n return 1; // LF always contributes 1 to the OT index\n}\nfunction $createBook(bookAttributes) {\n const { style, code } = bookAttributes;\n if (!style || style !== BOOK_MARKER || !code || !BookNode.isValidBookCode(code))\n return;\n const unknownAttributes = getUnknownAttributes(bookAttributes, OT_BOOK_PROPS);\n return $createBookNode(code, unknownAttributes);\n}\n/**\n * Creates a delta-materialized paragraph with the marker-mode-appropriate prefix the USJ\n * adaptor builds at load time (`createPara` in the platform adaptor): editable marker mode gets\n * the `[MarkerNode glyph, exact-NBSP token separator]` pair — without it the paragraph renders\n * bare AND the marker-edit engine's deletion transform reads the missing prefix as \"marker\n * deleted\" and merges the paragraph into its predecessor; visible marker mode and gutter\n * para-marker rendering get the immutable typed-text `\\marker + NBSP` prefix; hidden markers\n * get no prefix.\n */\nfunction $createPara(paraAttributes, viewOptions) {\n const { style } = paraAttributes;\n if (!style)\n return;\n const unknownAttributes = getUnknownAttributes(paraAttributes, OT_PARA_PROPS);\n const para = $createParaNode(style, unknownAttributes);\n // Gated on the shared prefix predicate, not raw markerMode alone: a surface that suppresses\n // paragraph prefixes (showParaMarkerPrefixes: false — the footnote editor's scaffolding\n // paragraph) must not grow a `\\p ` glyph from a remote op either.\n if (!showParaMarkerPrefix(viewOptions))\n return para;\n if (viewOptions.markerMode === \"editable\") {\n para.append($createMarkerNode(style), $createMarkerTrailingSeparator());\n }\n else if (viewOptions.markerMode === \"visible\" || viewOptions.hasGutterParaMarkers) {\n // A gutter glyph is a non-selectable aid, so it must carry the flag the caret guard keys on —\n // a paragraph arriving from a peer has to be as unclickable as one the load adaptor built.\n // markerMode \"visible\" renders the same node kind INLINE, where the flag must stay off.\n const glyph = openingMarkerText(style) + NBSP;\n para.append(viewOptions.hasGutterParaMarkers\n ? $createGutterMarkerNode(glyph)\n : $createImmutableTypedTextNode(\"marker\", glyph));\n }\n return para;\n}\nfunction $createChapter(chapterData, viewOptions) {\n if (!chapterData)\n return;\n const { number, sid, altnumber, pubnumber } = chapterData;\n if (!number)\n return;\n const unknownAttributes = getUnknownAttributes(chapterData, OT_CHAPTER_PROPS);\n let newNodeToInsert;\n if (viewOptions.markerMode === \"editable\") {\n newNodeToInsert = $createChapterNode(number, sid, altnumber, pubnumber, unknownAttributes);\n }\n else {\n const showMarker = viewOptions.markerMode === \"visible\";\n newNodeToInsert = $createImmutableChapterNode(number, showMarker, sid, altnumber, pubnumber, unknownAttributes);\n }\n return newNodeToInsert;\n}\nfunction $createVerse(verseData, viewOptions) {\n if (!verseData)\n return;\n const { style, number, sid, altnumber, pubnumber } = verseData;\n if (!number)\n return;\n const unknownAttributes = getUnknownAttributes(verseData, OT_VERSE_PROPS);\n let newNodeToInsert;\n if (viewOptions.markerMode === \"editable\") {\n if (!style)\n return;\n const text = getVisibleOpenMarkerText(style, number);\n newNodeToInsert = $createVerseNode(number, text, sid, altnumber, pubnumber, unknownAttributes);\n }\n else {\n const showMarker = viewOptions.markerMode === \"visible\";\n newNodeToInsert = $createImmutableVerseNode(number, showMarker, sid, altnumber, pubnumber, unknownAttributes);\n }\n return newNodeToInsert;\n}\nfunction $createMilestone(msData) {\n if (!msData)\n return;\n const { style, sid, eid, attributeOrder } = msData;\n if (!style)\n return;\n const unknownAttributes = getUnknownAttributes(msData, OT_MILESTONE_PROPS);\n return $createMilestoneNode(style, sid, eid, unknownAttributes, attributeOrder);\n}\nfunction $createNote(op, viewOptions, nodeOptions, logger) {\n const noteEmbed = op.insert;\n if (!noteEmbed.note)\n return;\n const { style, caller, category, contents } = noteEmbed.note;\n if (!style || caller == null)\n return;\n if (caller === \"\")\n logger?.warn(\"Note has empty caller. Only use for note editing.\");\n const unknownAttributes = getUnknownAttributes(noteEmbed.note, OT_NOTE_PROPS);\n // An unclosed note (closed=\"false\") materializes without a closer glyph and renders\n // expanded inline, exactly as the USJ adaptor builds it.\n const closed = typeof unknownAttributes?.closed === \"string\" ? unknownAttributes.closed : undefined;\n const segment = op.attributes?.segment;\n let nodeSegment;\n if (segment && typeof segment === \"string\")\n nodeSegment = segment;\n const contentNodes = [];\n for (const childOp of contents?.ops ?? []) {\n if (typeof childOp.insert !== \"string\")\n continue;\n if (hasCharAttributes(childOp.attributes)) {\n // Note contents ops carry CONTENT only; in editable marker mode a char span's FIRST content\n // text carries a structural NBSP separator after the opening glyph (mirror the USJ\n // adaptor's `createChar`). `$createNestedChars` owns the prepend because only it knows\n // whether this op starts a fresh span (separator) or merges into the preceding span's tail\n // (mid-span content — an NBSP there is fabricated `~` in the file). Empty content stays\n // empty so it inserts the empty-char placeholder instead.\n const charNodes = $createNestedChars(childOp.attributes.char, viewOptions, $createTextNode(childOp.insert), undefined, mergeableNodesForContentOp(childOp.attributes.char, contentNodes), false, viewOptions.markerMode === \"editable\");\n contentNodes.push(...charNodes);\n }\n else {\n contentNodes.push($createTextNode(childOp.insert));\n }\n }\n const note = $createWholeNote(style, caller, contentNodes, viewOptions, nodeOptions, nodeSegment, closed)\n .setCategory(category)\n .setUnknownAttributes(unknownAttributes);\n return note;\n}\nfunction $createUnknown(op, viewOptions, nodeOptions, logger) {\n const unknownData = op.insert.unknown;\n if (!unknownData)\n return;\n const { tag, marker, contents } = unknownData;\n if (!tag)\n return;\n const unknownAttributes = getUnknownAttributes(unknownData, OT_UNKNOWN_PROPS);\n const unknownNode = $createUnknownNode(tag, marker, unknownAttributes);\n const childOps = contents?.ops ?? [];\n if (childOps.length > 0) {\n const childNodes = $createInlineNodesFromOps(childOps, viewOptions, nodeOptions, logger);\n childNodes.forEach((child) => unknownNode.append(child));\n }\n const segment = op.attributes?.segment;\n if (typeof segment === \"string\")\n $setState(unknownNode, segmentState, () => segment);\n return unknownNode;\n}\nfunction $createInlineNodesFromOps(ops, viewOptions, nodeOptions, logger) {\n const nodes = [];\n for (const childOp of ops) {\n if (typeof childOp.insert === \"string\") {\n if (hasCharAttributes(childOp.attributes)) {\n const textNode = $createTextNode(childOp.insert);\n const charNodes = $createNestedChars(childOp.attributes.char, viewOptions, textNode, undefined, mergeableNodesForContentOp(childOp.attributes.char, nodes));\n nodes.push(...charNodes);\n }\n else {\n nodes.push($createTextNode(childOp.insert));\n }\n continue;\n }\n if (!childOp.insert || typeof childOp.insert !== \"object\")\n continue;\n if (isInsertEmbedOpOfType(\"unknown\", childOp)) {\n const nestedUnknown = $createUnknown(childOp, viewOptions, nodeOptions, logger);\n if (nestedUnknown)\n nodes.push(nestedUnknown);\n continue;\n }\n if (isInsertEmbedOpOfType(\"note\", childOp)) {\n const nestedNote = $createNote(childOp, viewOptions, nodeOptions, logger);\n if (nestedNote)\n nodes.push(nestedNote);\n continue;\n }\n logger?.warn(`$createInlineNodesFromOps: Unsupported embed inside unknown contents: ${JSON.stringify(childOp.insert)}`);\n }\n return nodes;\n}\nfunction $createImmutableUnmatched(unmatchedData, viewOptions) {\n if (!unmatchedData)\n return;\n const { marker } = unmatchedData;\n if (!marker)\n return;\n const node = $createImmutableUnmatchedNode(marker);\n // Shape-twin with the forward adaptor's `createUnmatched`: editable marker mode edits the\n // flagged bytes in place (the marker-edit engine settles them), so the node is ordinary\n // \"normal\" text there; every other mode keeps the constructor's atomic \"token\".\n if (viewOptions.markerMode === \"editable\")\n node.setMode(\"normal\");\n return node;\n}\n/**\n * The already-materialized sibling nodes a CONTENT op (a note's or unknown embed's contents)\n * may merge into when it continues the previous op's char span — or `undefined` when the op\n * must start its own span. `\\fp` (footnote-paragraph) spans never merge by style alone: `\\fp`\n * has no closer, so consecutive attribute-identical `\\fp` ops are consecutive footnote\n * PARAGRAPHS, not one split span — merging them collapses two paragraphs into one in the\n * serialized USJ (the same reason `CharNodePlugin` exempts `\\fp` from combining adjacent\n * spans). A `cid` restores merging: ops naming the same char id ARE the same span. Nested-char\n * arrays keep merging, since their outer item continues the enclosing span around a nested\n * child. Positioned edits (retain/insert-at-index) are untouched — there the merge target is\n * the span containing the edit position, not a preceding sibling op's span.\n */\nfunction mergeableNodesForContentOp(charAttr, materializedNodes) {\n if (!Array.isArray(charAttr) && charAttr.style === \"fp\" && !charAttr.cid)\n return undefined;\n return materializedNodes;\n}\n// Helper to create nested CharNodes from OTCharAttribute (array or single)\n// Returns an array of nodes: [opening marker?, CharNode, closing marker?]\n// If existingNodes is provided, will merge with the last CharNode if style/cid match\n/**\n * Defensively normalize an OT char style to a CLEAN marker. Nesting is conveyed by the char\n * ARRAY's position (outermost-first), never by a `+` in the style — the `+` belongs only to the\n * rendered glyph text. No production producer emits a `+`-prefixed style ($buildCharItem sends\n * the CharNode's clean marker verbatim), so this only guards against hand-authored or legacy\n * deltas polluting node markers (and, from there, the saved USJ).\n */\nfunction cleanCharStyle(item) {\n return item.style.startsWith(\"+\") ? { ...item, style: item.style.slice(1) } : item;\n}\nfunction $createNestedChars(charAttr, viewOptions, innerNode, segment, existingNodes, \n// True when the OUTERMOST span created here nests inside an already-open parent char (the\n// merge recursion below appends into an existing outer span), so its own glyphs need the `+`.\n// Every span deeper than the outermost is nested by construction and always gets the `+`.\nnestedInParent = false, \n// True when a text innerNode landing at the START of a NEWLY created span should take the\n// editable-mode structural NBSP separator after that span's opening glyph (mirroring the USJ\n// adaptor's `createChar`). Decided HERE, per branch, because only this function knows whether\n// the text starts a new span or appends into an existing span's tail — an NBSP prepended to a\n// tail-append is fabricated content (`~` in the file), not a separator.\naddEditableSeparator = false) {\n if ($isTextNode(innerNode) && innerNode.getTextContentSize() === 0) {\n innerNode.setTextContent(EMPTY_CHAR_PLACEHOLDER_TEXT);\n }\n // Prepend the structural separator to a text innerNode about to become a fresh span's first\n // content (never to the empty-char placeholder, which stands alone).\n const $prependEditableSeparator = () => {\n if (addEditableSeparator &&\n $isTextNode(innerNode) &&\n innerNode.getTextContent() !== EMPTY_CHAR_PLACEHOLDER_TEXT)\n innerNode.setTextContent(NBSP + innerNode.getTextContent());\n };\n if (Array.isArray(charAttr)) {\n if (charAttr.length === 0)\n throw new Error(\"Empty charAttr array\");\n const cleanAttrs = charAttr.map(cleanCharStyle);\n // Check if we can merge with existing CharNode\n const outerAttr = cleanAttrs[0];\n const lastNode = existingNodes?.[existingNodes.length - 1];\n if ($isCharNode(lastNode) && $hasSameCharAttributes(outerAttr, lastNode)) {\n // Merge into existing CharNode by creating inner nodes only. The inner spans nest inside\n // that existing outer char, so their outermost gets the `+` too (nestedInParent = true).\n if (cleanAttrs.length > 1) {\n // The inner spans are freshly created, so their first text still takes the separator —\n // the recursion's own creation branch prepends it.\n const innerCharNodes = $createNestedChars(cleanAttrs.slice(1), viewOptions, innerNode, undefined, undefined, true, addEditableSeparator);\n innerCharNodes.forEach((node) => lastNode.append(node));\n }\n else {\n // Tail-append into the existing span: mid-span content, NO separator.\n if (innerNode)\n lastNode.append(innerNode);\n }\n return []; // Return empty array since we merged into existing node\n }\n $prependEditableSeparator();\n // Build nested CharNodes from innermost to outermost using reduceRight\n // At each level, we add markers as children if needed\n const outermostCharNode = cleanAttrs.reduceRight((child, attr, idx) => {\n const charNode = $createCharNode(attr.style, getUnknownAttributes(attr, OT_CHAR_PROPS));\n if (typeof attr.cid === \"string\")\n $setState(charNode, charIdState, () => attr.cid);\n if (segment && idx === cleanAttrs.length - 1)\n $setState(charNode, segmentState, () => segment);\n // If there's a child, append it (with markers if it's a CharNode)\n if (child) {\n // If the child is a CharNode, it needs markers around it. The child nests inside this\n // span, so its glyphs carry the `+`.\n if ($isCharNode(child)) {\n // The child was created from attr at idx+1, so get its marker\n const childMarker = child.getMarker();\n const childMarkers = [];\n $addOpeningMarker(childMarker, childMarkers, viewOptions, true);\n childMarkers.forEach((marker) => charNode.append(marker));\n charNode.append(child);\n const closingMarkers = [];\n $addCharNodeClosingMarker(child, closingMarkers, viewOptions, true);\n closingMarkers.forEach((marker) => charNode.append(marker));\n }\n else {\n // Just append the child (it's the innermost text node)\n charNode.append(child);\n }\n }\n return charNode;\n }, innerNode);\n // Add markers inside the outermost CharNode (as children). The outermost gets the `+` only\n // when it in turn nests inside an existing parent char (the merge case above).\n $addOpeningMarker(outerAttr.style, outermostCharNode, viewOptions, nestedInParent);\n $addCharNodeClosingMarker(outermostCharNode, outermostCharNode, viewOptions, nestedInParent);\n return [outermostCharNode];\n }\n else {\n const cleanAttr = cleanCharStyle(charAttr);\n // Single char attribute\n // Check if we can merge with existing CharNode\n const lastNode = existingNodes?.[existingNodes.length - 1];\n if ($isCharNode(lastNode) && $hasSameCharAttributes(cleanAttr, lastNode)) {\n // Tail-append into the existing span: mid-span content, NO separator.\n if (innerNode)\n lastNode.append(innerNode);\n return []; // Return empty array since we merged into existing node\n }\n $prependEditableSeparator();\n const charNode = $createCharNode(cleanAttr.style, getUnknownAttributes(cleanAttr, OT_CHAR_PROPS));\n if (typeof cleanAttr.cid === \"string\")\n $setState(charNode, charIdState, () => cleanAttr.cid);\n if (segment)\n $setState(charNode, segmentState, () => segment);\n if (innerNode)\n charNode.append(innerNode);\n // Add markers inside the CharNode (as children)\n $addOpeningMarker(cleanAttr.style, charNode, viewOptions, nestedInParent);\n $addCharNodeClosingMarker(charNode, charNode, viewOptions, nestedInParent);\n return [charNode];\n }\n}\n/**\n * Add the closing glyph for a delta-materialized char span — or skip it when the span is not\n * explicitly closed. Mirrors `createChar` in the platform USJ adaptor: closer display keys on the\n * span's ACTUAL closed state, never on the marker family. A span carrying `closed=\"false\"` renders\n * WITHOUT a closing glyph (the glyph structure must agree with the node's state) — the delta\n * carries that flag for every genuinely-unclosed span, including footnote/cross-ref content chars\n * (`$buildCharItem` copies `unknownAttributes` into the char op, and `getUnknownAttributes(…,\n * OT_CHAR_PROPS)` reads it back here) — while an explicitly-closed `\\xt` (no `closed=\"false\"`)\n * keeps its closing glyph.\n */\nfunction $addCharNodeClosingMarker(charNode, target, viewOptions, nested = false) {\n const isUnclosed = charNode.getUnknownAttributes()?.closed === \"false\";\n if (isUnclosed)\n return;\n $addClosingMarker(charNode.getMarker(), target, viewOptions, false, nested);\n}\nfunction $addOpeningMarker(marker, target, viewOptions, \n// A span nested inside another char span renders its glyph with the `+` prefix (`\\+w`) — the\n// delta conveys nesting by char-array position, and the glyph must show it (see\n// nestedGlyphs.utils.ts in `shared` for the representation rules).\nnested = false) {\n let markerNode;\n if (viewOptions?.markerMode === \"editable\") {\n markerNode = $createMarkerNode(marker, \"opening\", nested);\n }\n else if (viewOptions?.markerMode === \"visible\") {\n markerNode = $createImmutableTypedTextNode(\"marker\", openingMarkerText(marker, nested));\n }\n if (markerNode) {\n if (Array.isArray(target)) {\n target.push(markerNode);\n }\n else {\n // Prepend to CharNode children\n const firstChild = target.getFirstChild();\n if (firstChild) {\n firstChild.insertBefore(markerNode);\n }\n else {\n target.append(markerNode);\n }\n }\n }\n}\nfunction $addClosingMarker(marker, target, viewOptions, isSelfClosing = false, nested = false) {\n let markerNode;\n if (viewOptions?.markerMode === \"editable\") {\n if (isSelfClosing)\n markerNode = $createMarkerNode(\"\", \"selfClosing\");\n else\n markerNode = $createMarkerNode(marker, \"closing\", nested);\n }\n else if (viewOptions?.markerMode === \"visible\") {\n markerNode = $createImmutableTypedTextNode(\"marker\", isSelfClosing ? closingMarkerText(\"\") : closingMarkerText(marker, nested));\n }\n if (markerNode) {\n if (Array.isArray(target)) {\n target.push(markerNode);\n }\n else {\n // Append to CharNode children\n target.append(markerNode);\n }\n }\n}\n/** Type guard for Book attributes. */\nfunction hasBookAttributes(attributes) {\n return (!!attributes &&\n !!attributes.book &&\n typeof attributes.book === \"object\" &&\n attributes.book !== null &&\n \"style\" in attributes.book &&\n typeof attributes.book.style === \"string\" &&\n \"code\" in attributes.book &&\n typeof attributes.book.code === \"string\");\n}\n/** Type guard for Para attributes. */\nfunction hasParaAttributes(attributes) {\n return (!!attributes &&\n !!attributes.para &&\n typeof attributes.para === \"object\" &&\n attributes.para !== null &&\n \"style\" in attributes.para &&\n typeof attributes.para.style === \"string\");\n}\n/** Type guard for Char attributes. */\nfunction hasCharAttributes(attributes) {\n return (!!attributes &&\n !!attributes.char &&\n typeof attributes.char === \"object\" &&\n attributes.char !== null &&\n ((!Array.isArray(attributes.char) &&\n \"style\" in attributes.char &&\n typeof attributes.char.style === \"string\") ||\n (Array.isArray(attributes.char) &&\n attributes.char.length > 0 &&\n \"style\" in attributes.char[0] &&\n typeof attributes.char[0].style === \"string\")));\n}\nfunction isEmptyObject(obj) {\n return (typeof obj === \"object\" && obj !== null && !Array.isArray(obj) && Object.keys(obj).length === 0);\n}\nfunction $applyTextAttributes(attributes, textNode) {\n if (!attributes)\n return;\n for (const key of Object.keys(attributes)) {\n // Handle segment attribute\n if (key === \"segment\" && typeof attributes[key] === \"string\") {\n const segment = attributes[key];\n $setState(textNode, segmentState, () => segment);\n continue;\n }\n // TODO: Text format attributes probably shouldn't be allowed but are helpful at the moment for\n // testing.\n if (isTextFormatType(key)) {\n const shouldSet = !!attributes[key];\n const formatKey = key;\n const isAlreadySet = textNode.hasFormat(formatKey);\n if ((shouldSet && !isAlreadySet) || (!shouldSet && isAlreadySet)) {\n textNode.toggleFormat(formatKey);\n }\n }\n }\n}\nconst TEXT_FORMAT_TYPES = [\n \"bold\",\n \"underline\",\n \"strikethrough\",\n \"italic\",\n \"highlight\",\n \"code\",\n \"subscript\",\n \"superscript\",\n \"lowercase\",\n \"uppercase\",\n \"capitalize\",\n];\nfunction isTextFormatType(key) {\n // This cast is safe because TEXT_FORMAT_TYPES is readonly TextFormatType[]\n return TEXT_FORMAT_TYPES.includes(key);\n}\n","import { useLexicalComposerContext } from \"@lexical/react/LexicalComposerContext\";\nimport { $getNearestNodeFromDOMNode, $getNodeByKey, $getSelection, $isRangeSelection, $isTextNode, CLICK_COMMAND, COMMAND_PRIORITY_EDITOR, isDOMNode, } from \"lexical\";\nimport { useEffect } from \"react\";\nimport { $isGutterMarkerNode, $isSomeParaNode, $isSynthesizedMarkerNode, $isVisibleMarkerNode, $placeCaretAtBoundary, NBSP, } from \"shared\";\nimport { $isImmutableVerseNode, $isSomeVerseNode } from \"../../nodes/usj\";\n/**\n * Keeps the cursor out of the places a paragraph's structural prefix occupies but no caret may\n * rest in, correcting a click to the first content position in the same update cycle.\n *\n * WHICH marker is caret territory is decided one NODE at a time, never per view: a marker rendered\n * in the gutter is an aid to reading, so it is never a caret position, while a marker rendered as\n * editable text in the flow IS content the user clicks into on purpose. A document can carry both\n * at once, so the two questions this asks — \"did the click land on a gutter marker?\" and \"does the\n * prefix at this paragraph's start host a caret at all?\" — are asked of the nodes in the tree.\n *\n * Using `CLICK_COMMAND` instead of `registerUpdateListener` + `editor.update` ensures the\n * correction is committed in a single cycle — other listeners (e.g. `OnSelectionChangePlugin`)\n * see only the corrected cursor, never the intermediate prefix position.\n */\nexport function ParaMarkerPrefixCursorGuardPlugin() {\n const [editor] = useLexicalComposerContext();\n useEffect(() => {\n return editor.registerCommand(CLICK_COMMAND, (event) => {\n $guardCursorOnClick(event);\n return false;\n }, COMMAND_PRIORITY_EDITOR);\n }, [editor]);\n return null;\n}\n/**\n * The whole click policy, in the order the two corrections must be tried: a click that landed ON a\n * gutter marker is answered from the click's target, because such a click leaves NO selection to\n * inspect; everything else is judged from where the selection came to rest.\n *\n * Exported so the registration above is the only thing a test has to duplicate.\n *\n * @param event - The click that Lexical dispatched through `CLICK_COMMAND`.\n */\nexport function $guardCursorOnClick(event) {\n if ($guardCursorAtGutterMarker(event.target))\n return;\n const selection = $getSelection();\n if ($isRangeSelection(selection))\n $guardCursorAtParaStart(selection);\n}\n/**\n * Advances the cursor past all structural prefix nodes at the start of `para`:\n * - Para-marker prefix (`MarkerNode` or `ImmutableTypedTextNode`) and its trailing NBSP.\n * - Leading verse nodes (`VerseNode` or `ImmutableVerseNode`).\n *\n * Places the cursor at the content boundary just past them, under the shared convention for what a\n * boundary's caret position is (`$placeCaretAtBoundary`): the start of the first content `TextNode`\n * that follows, or an element point at that boundary when no `TextNode` hosts it yet.\n *\n * Also called directly when programmatically navigating to a verse whose paragraph has a\n * non-text first child (e.g. in `ScriptureReferencePlugin`).\n */\nexport function $advancePastParaPrefixes(para) {\n let child = para.getFirstChild();\n let skipCount = 0;\n while (child !== null) {\n if ($isSynthesizedMarkerNode(child)) {\n skipCount++;\n child = child.getNextSibling();\n // In editable mode the para-marker prefix is followed by a NBSP TextNode (marker-trailing-space).\n if ($isTextNode(child) && child.getTextContent() === NBSP) {\n skipCount++;\n child = child.getNextSibling();\n }\n }\n else if ($isSomeVerseNode(child)) {\n skipCount++;\n child = child.getNextSibling();\n }\n else {\n break;\n }\n }\n if (skipCount === 0)\n return false;\n $placeCaretAtBoundary(para, skipCount);\n return true;\n}\n/**\n * Corrects a click that landed ON a gutter marker glyph, moving the cursor to the next visible text\n * position — normally the first content text of the paragraph the glyph belongs to.\n *\n * Takes the click's DOM TARGET rather than the selection because a click on a gutter marker leaves\n * no selection at all to correct: the glyph is a decorator, which Lexical renders\n * `contenteditable=\"false\"`, so the browser's caret lands inside a node Lexical cannot resolve to\n * any point in its tree and the editor's selection is left null. (Measured in Chrome: the DOM\n * selection anchors in the glyph's own text with a drawn caret, while `$getSelection()` is null.)\n *\n * Scoped to the GUTTER flavor by {@link $isGutterMarkerNode}, not to the node class: markerMode\n * \"visible\" renders the same class of node INLINE among the words, and where that glyph is part of\n * the text this rule has no opinion about it.\n *\n * @param target - The click's `event.target`.\n * @returns `true` if the cursor was moved, `false` if the click was not on a gutter marker.\n */\nexport function $guardCursorAtGutterMarker(target) {\n if (!isDOMNode(target))\n return false;\n const glyph = $getNearestNodeFromDOMNode(target);\n if (!$isGutterMarkerNode(glyph))\n return false;\n const owner = glyph.getParent();\n if (!owner)\n return false;\n // A paragraph can carry further structure after its marker (a leading verse number), and its own\n // rule already knows how much of that to skip. Anywhere else a gutter marker appears — a book's\n // `\\id` line, a table cell — the boundary just past the glyph is the next content position.\n if ($isSomeParaNode(owner))\n return $advancePastParaPrefixes(owner);\n $placeCaretAtBoundary(owner, glyph.getIndexWithinParent() + 1);\n return true;\n}\n/**\n * Corrects the cursor when it lands at the very start of a paragraph, before a structural prefix\n * that renders no caret of its own: an immutable marker glyph (the gutter aid, or markerMode\n * \"visible\"'s inline glyph) or an `ImmutableVerseNode`. This happens when the user clicks in the\n * hanging-indent gutter of any marker that sets a negative `text-indent` (e.g. `\\li`, `\\li1`,\n * `\\li2`, `\\ili`, `\\ili1`, `\\ili2`, and poetry markers), which resolves to an element-typed anchor\n * at offset 0 of the `ParaNode`.\n *\n * The cursor is advanced past all structural prefix nodes (marker glyph, trailing NBSP, and leading\n * verse nodes) to the first content `TextNode`, or to the element offset just after all those\n * structural nodes when no content `TextNode` follows yet.\n *\n * An EDITABLE marker glyph (`MarkerNode`, markerMode \"editable\") is deliberately not corrected: it\n * is a `TextNode`, so it hosts a caret, and the user clicks into it on purpose to edit the marker.\n * Hauling that click to the content would both fight the intent and make a position the arrow keys\n * can reach unreachable by mouse. So the question this asks is about the NODE at the paragraph's\n * start — can it hold the caret? — never about which view is on screen.\n *\n * Returns `true` if the selection was corrected, `false` if no correction was needed.\n *\n * Exported only for direct unit testing; production callers reach it through\n * {@link $guardCursorOnClick}.\n */\nexport function $guardCursorAtParaStart(selection) {\n if (!selection.isCollapsed())\n return false;\n const { anchor } = selection;\n if (anchor.type !== \"element\" || anchor.offset !== 0)\n return false;\n const para = $getNodeByKey(anchor.key);\n if (!$isSomeParaNode(para))\n return false;\n const first = para.getFirstChild();\n if (!$isVisibleMarkerNode(first) && !$isImmutableVerseNode(first))\n return false;\n return $advancePastParaPrefixes(para);\n}\n","import { useLexicalComposerContext } from \"@lexical/react/LexicalComposerContext\";\nimport { $findMatchingParent, mergeRegister } from \"@lexical/utils\";\nimport { $getNearestNodeFromDOMNode, $getSelection, $isRangeSelection, COMMAND_PRIORITY_CRITICAL, COMMAND_PRIORITY_HIGH, CONTROLLED_TEXT_INSERTION_COMMAND, CUT_COMMAND, DELETE_CHARACTER_COMMAND, DELETE_LINE_COMMAND, DELETE_WORD_COMMAND, DROP_COMMAND, KEY_DOWN_COMMAND, PASTE_COMMAND, } from \"lexical\";\nimport { useEffect } from \"react\";\nimport { $isImmutableTableNode, $isUnknownNode } from \"shared\";\n/**\n * Refuses an edit aimed INSIDE a construct the editor carries opaquely — an `UnknownNode` (figure,\n * sidebar, `\\periph`, `\\ref`, `\\optbreak`) or a table — so a read-only block is genuinely read-only\n * rather than destructive on contact.\n *\n * Without this, a keystroke reaching such a block does not fail to apply; it applies CATASTROPHICALLY.\n * The adaptor renders an opaque block's content children in Lexical's TOKEN mode so the block reads\n * and navigates as one unit, and inserting into a token node REPLACES THE WHOLE NODE — one typed\n * character turns a figure's `My caption` into `Z`, taking the rest of the caption with it. Silently\n * accepting a keystroke and losing neighbouring content to it is the failure the no-silent-no-ops\n * rule exists to prevent; a refused keystroke, where the character simply never appears, is not.\n *\n * The rule is deliberately narrow: an edit is refused when an END of the selection lands INSIDE an\n * opaque construct. That covers a caret sitting in one, and a selection reaching in from the text\n * outside and stopping partway through — the shape that guts the construct's engine-owned bytes (a\n * row's `\\tr ` glyph and its separator) while leaving the construct itself in the document. Nothing\n * repairs that afterwards: `$settleScopeForNode` returns undefined inside an opaque construct, so no\n * re-tokenization ever reconciles the screen with the file again.\n *\n * A selection that CONTAINS a construct whole — both ends outside it — is deliberately still not\n * this guard's business. That is a request to replace a region of the document, which is the\n * structural-deletion question and is answered elsewhere; annexing it here would turn a targeted\n * guard into a blanket one.\n *\n * Navigation and copying stay untouched, because they are what a read-only block is FOR: the block's\n * bytes are selectable and copyable. Only keys that would insert or delete are refused, and\n * modifier chords (Ctrl+C, Ctrl+Z, the marker engine's Ctrl+Space) are never treated as text.\n * Dragging OUT of a block is likewise left alone so drag-to-copy keeps working; the destructive\n * clipboard directions (cut, and a drop landing inside) are refused by their own commands.\n *\n * The delete COMMANDS are guarded alongside `KEY_DOWN` rather than left to it, because a delete\n * chord never reaches the key filter as typing: Lexical routes Ctrl/Alt+Backspace straight to\n * `DELETE_WORD_COMMAND` and Cmd/Ctrl+Backspace to `DELETE_LINE_COMMAND`, and the filter must keep\n * treating a modifier chord as a command so Ctrl+C and Ctrl+Space still work. Guarding the delete\n * commands themselves closes that without reopening the chords.\n *\n * Mount alongside the other guards. Read-only is not a view mode — a construct the editor cannot\n * model is opaque in every marker mode — so this plugin takes no `viewOptions` and is never gated\n * on one.\n */\nexport function OpaqueBlockGuardPlugin() {\n const [editor] = useLexicalComposerContext();\n useEffect(() => {\n /**\n * Refuse when the caret is inside an opaque construct. Command payloads differ by kind — the\n * clipboard and key commands carry an `Event` to `preventDefault` (which is what stops the\n * browser applying the keystroke itself), while `CONTROLLED_TEXT_INSERTION` carries a bare\n * string — so the event is guarded on rather than assumed.\n */\n const $refuseEditInsideOpaqueBlock = (payload) => {\n if (payload instanceof KeyboardEvent && !isEditingKey(payload))\n return false;\n if (!$selectionReachesIntoOpaqueBlock())\n return false;\n if (payload instanceof Event)\n payload.preventDefault();\n return true;\n };\n return mergeRegister(editor.registerCommand(KEY_DOWN_COMMAND, $refuseEditInsideOpaqueBlock, COMMAND_PRIORITY_HIGH), editor.registerCommand(CONTROLLED_TEXT_INSERTION_COMMAND, $refuseEditInsideOpaqueBlock, COMMAND_PRIORITY_HIGH), \n // CUT and PASTE run at CRITICAL because their standard-view handlers — the ones that\n // actually copy and then remove — are themselves registered at HIGH, where the winner is\n // decided by registration order rather than by intent. A refusal has to outrank the actor it\n // refuses, not tie with it. The engine's own CRITICAL cut arm, which records what a cut would\n // cover, TIES with this refusal, so it consults `$selectionReachesIntoOpaqueBlock` itself\n // rather than relying on order: an arm this refusal leaves behind would outlive the gesture.\n editor.registerCommand(PASTE_COMMAND, $refuseEditInsideOpaqueBlock, COMMAND_PRIORITY_CRITICAL), editor.registerCommand(CUT_COMMAND, $refuseEditInsideOpaqueBlock, COMMAND_PRIORITY_CRITICAL), \n // DROP is judged by the drop TARGET, not the live selection: Lexical dispatches\n // DROP_COMMAND straight from the DOM handler with no selection update, so at drop time\n // `$getSelection()` still holds whatever was selected when the drag STARTED. Testing that\n // inverted both promises above — dragging a caption OUT of a figure was refused (source\n // inside), while dragging outside text INTO a caption was allowed (source outside).\n editor.registerCommand(DROP_COMMAND, (event) => {\n if (!(event instanceof Event) || !(event.target instanceof Node))\n return false;\n const targetNode = $getNearestNodeFromDOMNode(event.target);\n if (!targetNode || !$opaqueBlockAncestor(targetNode))\n return false;\n event.preventDefault();\n return true;\n }, COMMAND_PRIORITY_HIGH), editor.registerCommand(DELETE_CHARACTER_COMMAND, $refuseEditInsideOpaqueBlock, COMMAND_PRIORITY_HIGH), editor.registerCommand(DELETE_WORD_COMMAND, $refuseEditInsideOpaqueBlock, COMMAND_PRIORITY_HIGH), editor.registerCommand(DELETE_LINE_COMMAND, $refuseEditInsideOpaqueBlock, COMMAND_PRIORITY_HIGH));\n }, [editor]);\n return null;\n}\n/**\n * Whether this keystroke would insert or delete text. A modifier chord is a command, not typing —\n * Ctrl+C and Ctrl+Z must keep working inside a read-only block, and the marker engine's Ctrl+Space\n * must reach its own handler — so any of Ctrl/Meta/Alt disqualifies the key outright. Everything\n * else is judged by what the key produces: a single character (a space included), or one of the\n * three keys that remove or break text.\n *\n * Two kinds of typing do not announce themselves as a single character and so are decided before\n * that rule:\n *\n * - An IME composition keystroke inserts CJK/complex-script text but arrives as `key === \"Process\"`\n * (`keyCode === 229` is the DOM's legacy \"handled by IME\" signal, needed because some engines\n * fire the first composition keydown before `isComposing` flips true).\n * - AltGr sets BOTH `ctrlKey` and `altKey` on Windows and Linux, yet it types — AltGr+Q is `@` on a\n * German keyboard. `getModifierState(\"AltGraph\")` is what tells it apart from a real Ctrl+Alt\n * chord, so Ctrl+Alt shortcuts keep reaching their handlers.\n *\n * Exported because it defines what \"typing\" means for the read-only guard, and that definition is\n * worth pinning directly rather than only through the plugin's command wiring.\n */\nexport function isEditingKey(event) {\n if (event.isComposing || event.keyCode === 229)\n return true;\n const typesThroughAltGraph = typeof event.getModifierState === \"function\" && event.getModifierState(\"AltGraph\");\n if (!typesThroughAltGraph && (event.ctrlKey || event.metaKey || event.altKey))\n return false;\n return (event.key.length === 1 ||\n event.key === \"Backspace\" ||\n event.key === \"Delete\" ||\n event.key === \"Enter\");\n}\n/**\n * Read-only: safe inside `editor.getEditorState().read()`, an `editor.update()`, or a command\n * handler — it only walks parents.\n *\n * The nearest opaque-construct ancestor of `node` (itself included), or `undefined`.\n *\n * Exported because it is the ONE place \"this node belongs to a read-only construct\" is decided, and\n * more than the edit guard needs the answer: `ArrowNavigationPlugin` asks it to decide that a\n * construct's marker glyphs are crossed whole rather than walked through. When table editability\n * lands, this predicate is where it comes back out.\n */\nexport function $opaqueBlockAncestor(node) {\n return ($findMatchingParent(node, (current) => $isUnknownNode(current) || $isImmutableTableNode(current)) ?? undefined);\n}\n/**\n * Mutating: read inside an `editor.update()` or a command handler.\n *\n * Whether an END of the selection lands inside an opaque construct — a caret parked in one, or a\n * range that reaches in from the outside and stops partway through. Both ends are resolved\n * separately, and either one being inside is enough, because an edit that lands partway into a\n * construct destroys engine-owned bytes the construct keeps in the file.\n *\n * A range whose ends are BOTH outside is not this guard's business even when it contains a\n * construct whole: that is a region replacement, answered by the structural-deletion rules.\n *\n * Exported for actors that TIE with this plugin's refusals at Lexical's ceiling priority\n * (CRITICAL has no rank above it, so the winner is registration order): the marker engine's\n * in-note paste claim consults this before claiming, so either registration order refuses.\n */\nexport function $selectionReachesIntoOpaqueBlock() {\n const selection = $getSelection();\n if (!$isRangeSelection(selection))\n return false;\n return ($opaqueBlockAncestor(selection.anchor.getNode()) !== undefined ||\n $opaqueBlockAncestor(selection.focus.getNode()) !== undefined);\n}\n","import { $isImmutableNoteCallerNode, $isImmutableVerseNode, $isSomeVerseNode, $selectNextVerse, $selectPreviousVerse, ImmutableVerseNode, } from \"../../nodes/usj\";\nimport { $advancePastParaPrefixes } from \"./ParaMarkerPrefixCursorGuardPlugin\";\nimport { $opaqueBlockAncestor } from \"./OpaqueBlockGuardPlugin\";\nimport { useLexicalComposerContext } from \"@lexical/react/LexicalComposerContext\";\nimport { $findMatchingParent } from \"@lexical/utils\";\nimport { $getRoot, $getSelection, $isDecoratorNode, $isElementNode, $isLineBreakNode, $isRangeSelection, $isTextNode, COMMAND_PRIORITY_HIGH, KEY_DOWN_COMMAND, } from \"lexical\";\nimport { useEffect } from \"react\";\nimport { $findFirstAncestorNoteNode, $getNextNode, $getPreviousNode, $isBookNode, $isCharNode, $isImmutableChapterNode, $isImmutableTypedTextNode, $isMarkerNode, $isMilestoneNode, $isNoteNode, $isSomeParaNode, $placeCaretAtBoundary, ImmutableChapterNode, NoteNode, } from \"shared\";\n/**\n * Is there a visual line beyond the caret in `direction`, among a set of candidate line rects?\n * Pure geometry, kept separate from the DOM so it is unit-testable ({@link $caretHasVisualLineBeyond}\n * supplies the rects). Returns `false` for a zero-height caret (e.g. jsdom has no layout) so callers\n * fall back to their default rather than act on a phantom line.\n *\n * A wrapped line sits clear of the caret's own line, so this compares the line gap (a rect that\n * starts below the caret / ends above it) rather than raw top/bottom — otherwise a taller inline on\n * the caret's *own* line (a verse number or note caller) would read as a wrapped line. The\n * tolerance scales with caret height so it holds across font sizes and zoom.\n *\n * @param caretRect - The collapsed caret's bounding rect.\n * @param lineRects - Candidate per-line rects to test (e.g. from `Range.getClientRects()`).\n * @param direction - `\"down\"` looks for a line below the caret; `\"up\"` a line above.\n */\nexport function hasVisualLineBeyondCaret(caretRect, lineRects, direction) {\n if (caretRect.height === 0)\n return false;\n const tolerance = caretRect.height / 4; // sub-pixel slack, well under a full line gap.\n return lineRects.some((rect) => direction === \"down\"\n ? rect.top >= caretRect.bottom - tolerance\n : rect.bottom <= caretRect.top + tolerance);\n}\n/**\n * Whether the current verse's text has a wrapped line beyond the caret in `direction`. Custom\n * verse-to-verse navigation only fires from a verse's first visual line, so this stops ArrowDown\n * from skipping the rest of a wrapped verse and jumping to the next one.\n *\n * The verse's content is measured across blocks — bounded by the surrounding `[data-marker=\"v\"]`\n * verse markers — because a verse can wrap across several `\\q` poetry paragraphs; measuring only the\n * caret's own paragraph would miss those lines. Returns `false` when layout cannot be measured\n * (e.g. jsdom), so the caller keeps the existing verse-jump.\n */\nfunction $caretHasVisualLineBeyond(editor, direction) {\n if (typeof window === \"undefined\")\n return false;\n const domSelection = window.getSelection();\n if (!domSelection || domSelection.rangeCount === 0)\n return false;\n const root = editor.getRootElement();\n if (!root)\n return false;\n try {\n const caretRange = domSelection.getRangeAt(0);\n // Only measure this editor's caret: ignore a selection that lives elsewhere on the page or in\n // another document (an iframe host), which would otherwise be measured against our markers.\n if (!root.contains(caretRange.startContainer))\n return false;\n const caretRect = caretRange.getBoundingClientRect();\n const caretStart = caretRange.cloneRange();\n caretStart.collapse(true);\n // Bound the measurement to the current verse: the last marker strictly before the caret (so the\n // verse whose content the caret is in) and the first marker after it. The comparison is\n // position-based (a plain sibling walk can't handle element-point carets, e.g. a caret at an\n // element offset between decorator siblings), so it scans markers in document order. This runs\n // only on ArrowUp/ArrowDown at a verse boundary — not on every keystroke — over one editor's\n // worth of verses (a chapter), so the linear scan is not a hot path.\n const markers = Array.from(root.querySelectorAll('[data-marker=\"v\"]'));\n let current;\n let next;\n for (const marker of markers) {\n const markerRange = document.createRange();\n markerRange.selectNode(marker);\n if (caretStart.compareBoundaryPoints(Range.START_TO_START, markerRange) > 0) {\n current = marker;\n }\n else {\n next = marker;\n break;\n }\n }\n if (!current)\n return false;\n const contentRange = document.createRange();\n contentRange.setStartAfter(current);\n if (next)\n contentRange.setEndBefore(next);\n else\n contentRange.setEnd(root, root.childNodes.length);\n return hasVisualLineBeyondCaret(caretRect, Array.from(contentRange.getClientRects()), direction);\n }\n catch {\n // No layout engine (e.g. jsdom: Range has no getBoundingClientRect): cannot detect a wrapped\n // line, so fall back to the existing verse-jump rather than suppressing it.\n return false;\n }\n}\n/**\n * Handles an ArrowUp/ArrowDown press for verse-to-verse navigation. Intercepts only when the caret\n * is at a verse boundary and native movement would leave the verse; when the verse wraps onto\n * further lines it yields to the browser's visual-line movement instead.\n *\n * @returns `true` (and prevents default) when it moved the caret to an adjacent verse; otherwise\n * `false` so Lexical/the browser handles the key.\n */\nfunction $navigateVerseVertically(editor, selection, direction, event) {\n // Don't intercept when the caret isn't at a verse boundary, or when the verse wraps onto a\n // further line in this direction (let the browser move by visual line instead). `||` short-circuits\n // so the DOM measurement only runs once the cheap boundary check passes.\n if (!$shouldAttemptVerticalVerseNavigation(selection) ||\n $caretHasVisualLineBeyond(editor, direction))\n return false;\n const isHandled = direction === \"up\" ? $selectPreviousVerse(selection) : $selectNextVerse(selection);\n if (isHandled)\n event.preventDefault();\n return isHandled;\n}\n/**\n * Registers arrow-key handling for USJ scripture: verse-to-verse vertical movement when needed,\n * and horizontal movement around notes and chapter boundaries. In editable-marker mode it also\n * normalizes horizontal traversal — plain arrows and shift-extensions alike — so every press\n * crosses exactly one piece of rendered content.\n *\n * TODO: When the caret is before an empty verse number in an otherwise empty para, pressing up or\n * down moves the caret to after the verse number in the para above/below rather than staying\n * before the verse number.\n *\n * @param viewOptions - View options (e.g. collapsed note mode) affecting backward navigation.\n * @returns Always `null`; this component has no UI.\n */\nexport function ArrowNavigationPlugin({ viewOptions, }) {\n const [editor] = useLexicalComposerContext();\n useArrowKeys(editor, viewOptions);\n return null;\n}\n/**\n * When moving with arrow keys, it handles navigation around adjacent verse and note nodes.\n * It also handles not moving if a chapter node is the only thing at the beginning.\n * @param editor - The LexicalEditor instance used to access the DOM.\n * @param viewOptions - The current view options, which may affect navigation behavior.\n */\nfunction useArrowKeys(editor, viewOptions) {\n useEffect(() => {\n if (!editor.hasNodes([ImmutableChapterNode, ImmutableVerseNode, NoteNode])) {\n throw new Error(\"ArrowNavigationPlugin: ImmutableChapterNode, ImmutableVerseNode or NoteNode not registered on editor!\");\n }\n const $handleKeyDown = (event) => {\n const selection = $getSelection();\n if (!$isRangeSelection(selection))\n return false;\n // Display runs and glyph text — the stacked invisible positions the normalizer exists for —\n // are built only in editable-marker mode; the other views keep the browser's own traversal.\n const normalizesStops = viewOptions?.markerMode === \"editable\";\n const rootElement = editor.getRootElement();\n // Shift+horizontal arrow grows the selection by the same visible stops the collapsed caret\n // walks: the FOCUS moves one rendered position, the anchor stays put. Without it, selecting\n // through a display run inherits the traversal the normalizer exists to replace — the focus\n // stalls on invisible stops, and at a run's left edge it could not move at all, because\n // Lexical hands an extend across a zero-width decorator to the browser exactly as it does a\n // collapsed move. Shift ONLY: ctrl/alt/meta arrows keep their own word and line granularity.\n if (normalizesStops &&\n rootElement &&\n (event.key === \"ArrowLeft\" || event.key === \"ArrowRight\") &&\n event.shiftKey &&\n !event.altKey &&\n !event.ctrlKey &&\n !event.metaKey) {\n const textDirection = getEditorTextDirection(rootElement);\n const isHandled = $extendOneVisibleStop(selection, isMovingForward(textDirection, event.key) ? \"next\" : \"previous\");\n if (isHandled)\n event.preventDefault();\n return isHandled;\n }\n if (!selection.isCollapsed())\n return false;\n if (event.key === \"ArrowUp\" || event.key === \"ArrowDown\") {\n if (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey)\n return false;\n const direction = event.key === \"ArrowUp\" ? \"up\" : \"down\";\n return $navigateVerseVertically(editor, selection, direction, event);\n }\n if (event.key !== \"ArrowLeft\" && event.key !== \"ArrowRight\")\n return false;\n if (!rootElement)\n return false;\n const direction = getEditorTextDirection(rootElement);\n // The `\\fp` boundary hops apply only to plain arrow moves: modified arrows (shift range\n // extension, word/line jumps) keep native semantics.\n const hasModifier = event.shiftKey || event.altKey || event.ctrlKey || event.metaKey;\n let isHandled = false;\n if (isMovingForward(direction, event.key)) {\n isHandled =\n (!hasModifier && $crossOpaqueConstruct(selection, \"next\")) ||\n (!hasModifier && $handleForwardFpNavigation(selection)) ||\n $handleForwardNavigation(selection) ||\n (!hasModifier && normalizesStops && $moveOneVisibleStop(selection, \"next\"));\n }\n else if (isMovingBackward(direction, event.key)) {\n isHandled =\n (!hasModifier && $crossOpaqueConstruct(selection, \"previous\")) ||\n (!hasModifier && $handleBackwardFpNavigation(selection)) ||\n $handleBackwardNavigation(selection, viewOptions) ||\n (!hasModifier && normalizesStops && $moveOneVisibleStop(selection, \"previous\"));\n }\n if (isHandled)\n event.preventDefault();\n return isHandled;\n };\n return editor.registerCommand(KEY_DOWN_COMMAND, $handleKeyDown, COMMAND_PRIORITY_HIGH);\n }, [editor, viewOptions]);\n}\n// --- Helper functions for direction checking ---\n/**\n * The direction the editor's content reads in, from its root element — what \"forward\" means for a\n * horizontal arrow key. It lives here because arrow navigation is the only caller; it is exported\n * only so the unit test can reach it, as `hasVisualLineBeyondCaret` above is.\n *\n * KNOWN GAP: a project configured for \"auto\" direction reads as `\"ltr\"` here. `TextDirectionPlugin`\n * returns early without setting `dir` on the root when the configured direction is `\"auto\"`, and\n * the host forwards `\"auto\"` through unchanged — so `dir` stays empty and every direction-sensitive\n * behavior below moves the wrong logical way for an RTL script. The likely fix is\n * `getComputedStyle(rootElement).direction`, which reflects what `dir=\"auto\"` resolved to and picks\n * up a direction set in CSS as well; it changes caret behavior for every RTL user and jsdom has no\n * layout to resolve it, so it needs validating against a real RTL project first. Every direction\n * read goes through here, so that is a one-place change.\n */\nexport function getEditorTextDirection(rootElement) {\n return rootElement.dir || \"ltr\";\n}\nfunction isMovingForward(direction, key) {\n return ((direction === \"ltr\" && key === \"ArrowRight\") || (direction === \"rtl\" && key === \"ArrowLeft\"));\n}\nfunction isMovingBackward(direction, key) {\n return ((direction === \"ltr\" && key === \"ArrowLeft\") || (direction === \"rtl\" && key === \"ArrowRight\"));\n}\n// --- Two caret stops around the `\\fp` visual line break ---\n//\n// An expanded note's `\\fp` (footnote paragraph) span renders with a CSS-generated line break\n// before it (`.note.expanded .usfm_fp::before`). The pseudo content has no DOM position, so the\n// browser collapses the caret positions on either side of the visual newline and skips one stop:\n// moving forward it jumps from the end of the previous line straight past the start of the `\\fp`\n// span, and moving backward it can skip the span start on the way out. These handlers restore the\n// two stops: end of the previous line, then the very start of the `\\fp` span on the new line.\n/** The given node if it is a `\\fp` span whose leading line break renders (expanded note). */\nfunction $getFpBoundaryCharNode(node) {\n if (!$isCharNode(node) || node.getMarker() !== \"fp\")\n return undefined;\n const note = $findFirstAncestorNoteNode(node);\n if (!note || note.getIsCollapsed())\n return undefined;\n return node;\n}\n/** Helper to handle the forward hop onto the start of a `\\fp` span at its visual line break. */\nfunction $handleForwardFpNavigation(selection) {\n const fpNode = $getFpBoundaryCharNode($getNextNode(selection));\n if (!fpNode)\n return false;\n // A text caret is only at the boundary when it sits at the very end of its text.\n const anchor = selection.anchor;\n if (anchor.type === \"text\" && anchor.offset !== anchor.getNode().getTextContentSize()) {\n return false;\n }\n // Land at the start of the new visual line: the span's first content boundary — offset 0 of its\n // first text (the editable marker glyph, or content text when glyphs are hidden), or an element\n // point when a non-text first child (the non-editable marker glyph) hosts no caret there.\n $placeCaretAtBoundary(fpNode, 0);\n return true;\n}\n/** Helper to handle backward hops at the start of a `\\fp` span and its visual line break. */\nfunction $handleBackwardFpNavigation(selection) {\n const anchor = selection.anchor;\n const anchorNode = anchor.getNode();\n if (anchor.type === \"text\") {\n const fpNode = $getFpBoundaryCharNode(anchorNode.getParent());\n if (!fpNode || !anchorNode.is(fpNode.getFirstChild()))\n return false;\n if (anchor.offset === 1) {\n // caret after first character of the span's first text → stop at the span start (start of\n // the new visual line) instead of letting the browser collapse the boundary and skip it\n anchorNode.select(0, 0);\n return true;\n }\n if (anchor.offset !== 0)\n return false;\n // caret at span start → hop over the visual newline to the end of the previous line\n return $selectBeforeFpSpan(fpNode);\n }\n // Element caret at the very start of the `\\fp` span (before a non-text first child, e.g. the\n // non-editable marker glyph).\n if (anchor.offset === 0) {\n const fpNode = $getFpBoundaryCharNode(anchorNode);\n if (!fpNode)\n return false;\n return $selectBeforeFpSpan(fpNode);\n }\n return false;\n}\n/** Place the caret at the end of the content preceding the `\\fp` span (end of the previous line). */\nfunction $selectBeforeFpSpan(fpNode) {\n const prevNode = fpNode.getPreviousSibling();\n if (!prevNode)\n return false;\n if ($isTextNode(prevNode)) {\n prevNode.select();\n return true;\n }\n if ($isElementNode(prevNode)) {\n // end of the previous span's content (e.g. the `\\ft` span, or a preceding `\\fp`)\n const lastDescendant = prevNode.getLastDescendant();\n if ($isTextNode(lastDescendant))\n lastDescendant.select();\n else\n prevNode.selectEnd();\n return true;\n }\n // Decorator sibling (e.g. the note caller): place the caret between it and the `\\fp` span so\n // the existing note-boundary handling can take over from there on the next press.\n const parent = fpNode.getParent();\n if (!parent)\n return false;\n const fpIndex = fpNode.getIndexWithinParent();\n parent.select(fpIndex, fpIndex);\n return true;\n}\n/**\n * Segments text into user-perceived characters. The type is in the lib target, but the runtime is\n * not guaranteed to have it — where it is missing, the code-point fallbacks below still keep the\n * caret off the inside of a surrogate pair.\n */\nconst graphemeSegmenter = typeof Intl.Segmenter === \"undefined\"\n ? undefined\n : new Intl.Segmenter(undefined, { granularity: \"grapheme\" });\n/** The offset just past `text`'s first grapheme — where a forward crossing into it lands. */\nfunction firstGraphemeEnd(text) {\n if (graphemeSegmenter) {\n for (const { segment } of graphemeSegmenter.segment(text))\n return segment.length;\n }\n const codePoint = text.codePointAt(0);\n return codePoint === undefined ? 0 : String.fromCodePoint(codePoint).length;\n}\n/**\n * The offset where `text`'s last grapheme begins — where a backward crossing into it lands.\n *\n * Scans the whole string: `Intl.Segmenter` only walks forward, and a bounded tail scan can be wrong,\n * because whether a trailing code point begins a grapheme depends on what precedes it. The strings\n * are marker glyphs and short attribute values, and one runs per boundary press.\n */\nfunction lastGraphemeStart(text) {\n if (graphemeSegmenter) {\n let start = 0;\n for (const { index } of graphemeSegmenter.segment(text))\n start = index;\n return start;\n }\n const codePoint = text.codePointAt(Math.max(0, text.length - 2));\n const isSurrogatePair = codePoint !== undefined && codePoint > 0xffff;\n return Math.max(0, text.length - (isSurrogatePair ? 2 : 1));\n}\n/** The block the traversal is confined to — arrows leave a block through the handlers above. */\nfunction $blockOf(node) {\n for (let current = node; current; current = current.getParent()) {\n if ($isElementNode(current) && !current.isInline())\n return current;\n }\n return undefined;\n}\n/**\n * A marker glyph belonging to a READ-ONLY construct — a table's `\\tr` and `\\tc1`, and whatever a\n * future opaque kind renders the same way.\n *\n * Everywhere else in standard view a marker glyph is editable text, and walking through it IS the\n * affordance: retyping `\\q1` to `\\q2` is how a paragraph gets retagged. Inside an opaque construct\n * that affordance is a lie — every gesture that would edit those bytes is refused\n * (`OpaqueBlockGuardPlugin`), and a table has no settle scope to reconcile a change with the file\n * even if one landed. A caret resting between the `\\` and the `t` of `\\tr` is a position from which\n * nothing is possible, so the glyph is treated as what it is: display, crossed whole.\n */\nfunction $isOpaqueConstructGlyph(node) {\n return !!node && $isMarkerNode(node) && $opaqueBlockAncestor(node) !== undefined;\n}\n/** Text the caret walks through one character at a time. */\nfunction $isTraversableText(node) {\n return ($isTextNode(node) &&\n !node.isToken() &&\n !$isOpaqueConstructGlyph(node) &&\n node.getTextContentSize() > 0);\n}\n/**\n * Occupies space on screen but holds no caret positions of its own: crossing it is a single stop,\n * and the caret never lands inside.\n *\n * The list is deliberately explicit, because a node wrongly called invisible is stepped over\n * silently — the caret sails past it and the press lands a stop too far. Anything new that takes up\n * room without offering caret positions belongs here.\n */\nfunction $isVisibleAtom(node) {\n // A line break occupies the rest of its line and ends it. Its two sides are genuinely different\n // places, so it is crossed like any other glyph — never skipped. The unformatted view puts one\n // before every verse, so getting this wrong strands a whole view's line starts and ends.\n if ($isLineBreakNode(node))\n return true;\n // A COLLAPSED note shows only its caller; its hidden content must not be walked into. The flag is\n // undefined until the note plugin settles it, and an unsettled note counts as expanded, matching\n // what is on screen before the collapse lands.\n if ($isNoteNode(node))\n return node.getIsCollapsed() === true;\n // Token-mode text is indivisible by Lexical's own rule, and a read-only construct's marker glyph\n // is indivisible by ours — see `$isOpaqueConstructGlyph`.\n if ($isTextNode(node))\n return (node.isToken() || $isOpaqueConstructGlyph(node)) && node.getTextContentSize() > 0;\n // Every decorator renders SOMETHING — a note caller, an immutable glyph, a verse or chapter\n // number — except the zero-width anchors listed here, which render nothing at all. A new\n // zero-width decorator must join this list, or traversal will come to rest on it.\n if ($isDecoratorNode(node))\n return !$isMilestoneNode(node);\n return false;\n}\n/** The next node in document order in `direction`, stepping out of ancestors, bounded by `block`. */\nfunction $stepOver(from, direction, block) {\n for (let current = from; current && !current.is(block);) {\n const sibling = direction === \"next\" ? current.getNextSibling() : current.getPreviousSibling();\n if (sibling)\n return sibling;\n current = current.getParent();\n }\n return undefined;\n}\n/**\n * The first node rendering anything, starting AT `seed` and walking `direction` — descending into\n * elements that are not atoms, stepping over everything invisible.\n */\nfunction $scanForRendered(seed, direction, block) {\n for (let cursor = seed; cursor;) {\n if ($isVisibleAtom(cursor))\n return cursor;\n if ($isElementNode(cursor)) {\n const child = direction === \"next\" ? cursor.getFirstChild() : cursor.getLastChild();\n cursor = child ?? $stepOver(cursor, direction, block);\n continue;\n }\n if ($isTraversableText(cursor))\n return cursor;\n cursor = $stepOver(cursor, direction, block);\n }\n return undefined;\n}\n/** The node a scan should consider first when leaving the caret's position in `direction`. */\nfunction $scanSeed(anchorNode, anchorOffset, anchorType, direction, block) {\n if (anchorType === \"element\" && $isElementNode(anchorNode)) {\n const child = anchorNode.getChildAtIndex(direction === \"next\" ? anchorOffset : anchorOffset - 1);\n return child ?? $stepOver(anchorNode, direction, block);\n }\n // A text point INSIDE an atom — token-mode text such as a paragraph's marker-trailing separator\n // — sits at one of that atom's own edges, so the atom is the very thing this press has to cross.\n // Seeding a sibling skips it and lands a stop too far: from the right edge of `\\q1 `'s\n // separator, a backward press sailed past the separator into the glyph and came to rest between\n // `q` and `1`. Only seed the atom when it still has content on the side we are moving toward; at\n // its far edge the atom is already behind the caret and the scan should go on to the sibling.\n if (anchorType === \"text\" &&\n $isVisibleAtom(anchorNode) &&\n (direction === \"next\" ? anchorOffset < anchorNode.getTextContentSize() : anchorOffset > 0))\n return anchorNode;\n return $stepOver(anchorNode, direction, block);\n}\n/**\n * The single resting position for the screen location `landing` sits at.\n *\n * The preference is backward — the end of the nearest preceding visible text — and deliberately the\n * opposite of the forward one the content-boundary convention states (`$placeCaretAtBoundary`,\n * shared): what precedes an arrow landing is rendered content the caret has just walked over, so the\n * end of it is the outermost position at that location, whereas what precedes a content boundary is\n * structure that typed text must not merge into.\n */\nfunction $canonicalize(landing, block) {\n // A text point with a character before it in its own node is already the outermost position at\n // its location — nothing invisible separates it from rendered content on its left.\n if (landing.kind === \"text\" && landing.offset > 0)\n return landing;\n const seed = $scanSeed(landing.node, landing.offset, landing.kind, \"previous\", block);\n const rendered = $scanForRendered(seed, \"previous\", block);\n // Nothing rendered precedes it (the block's leading edge): the landing is already outermost.\n if (!rendered)\n return landing;\n if ($isTraversableText(rendered))\n return { kind: \"text\", node: rendered, offset: rendered.getTextContentSize() };\n const parent = rendered.getParent();\n if (!parent)\n return landing;\n return { kind: \"element\", node: parent, offset: rendered.getIndexWithinParent() + 1 };\n}\n/** Where a single press lands from `point`, or `undefined` when the block edge leaves nothing to cross. */\nfunction $resolveOneVisibleStop(point, direction) {\n const anchorNode = point.getNode();\n const block = $blockOf(anchorNode);\n if (!block)\n return undefined;\n // Inside a text node the browser's own grapheme and bidi handling is better than a tree walk, so\n // those moves are declined — except a backward step off the first character, whose landing at\n // offset 0 is one of the stacked positions and has to be canonicalized.\n if (point.type === \"text\" && $isTraversableText(anchorNode)) {\n if (direction === \"next\" && point.offset < anchorNode.getTextContentSize())\n return undefined;\n if (direction === \"previous\" && point.offset > 1)\n return undefined;\n if (direction === \"previous\" && point.offset === 1)\n return $canonicalize({ kind: \"text\", node: anchorNode, offset: 0 }, block);\n }\n const seed = $scanSeed(anchorNode, point.offset, point.type, direction, block);\n const rendered = $scanForRendered(seed, direction, block);\n if (!rendered)\n return undefined;\n if ($isTraversableText(rendered)) {\n const text = rendered.getTextContent();\n const offset = direction === \"next\" ? firstGraphemeEnd(text) : lastGraphemeStart(text);\n return $canonicalize({ kind: \"text\", node: rendered, offset }, block);\n }\n const parent = rendered.getParent();\n if (!parent)\n return undefined;\n const index = rendered.getIndexWithinParent();\n return $canonicalize({ kind: \"element\", node: parent, offset: direction === \"next\" ? index + 1 : index }, block);\n}\n/**\n * Applies one visible stop in `direction`. `collapse` walks the caret; `extend` moves only the\n * selection's focus, so a shift-arrow grows the range by the same stops. `false` leaves the press to\n * other handling.\n */\nfunction $applyOneVisibleStop(selection, direction, alter) {\n // A collapsed move reads the anchor and an extend reads the focus — the live end of the range,\n // so it stays correct for a selection that was already extended backwards.\n const point = alter === \"collapse\" ? selection.anchor : selection.focus;\n const landing = $resolveOneVisibleStop(point, direction);\n if (!landing)\n return false;\n // Already there (a canonicalization that resolved back onto the point): report the press as\n // unhandled rather than claiming a keystroke that changes nothing.\n if (landing.node.is(point.getNode()) &&\n landing.offset === point.offset &&\n landing.kind === point.type) {\n return false;\n }\n if (alter === \"collapse\") {\n landing.node.select(landing.offset, landing.offset);\n return true;\n }\n selection.focus.set(landing.node.getKey(), landing.offset, landing.kind);\n return true;\n}\n/** Moves the caret one visible stop in `direction`; `false` leaves the press to other handling. */\nfunction $moveOneVisibleStop(selection, direction) {\n return $applyOneVisibleStop(selection, direction, \"collapse\");\n}\n/** Extends the selection's focus one visible stop, leaving its anchor where it is. */\nfunction $extendOneVisibleStop(selection, direction) {\n return $applyOneVisibleStop(selection, direction, \"extend\");\n}\n// --- Crossing a read-only construct whole ---\n//\n// A read-only construct is not a place a caret can BE. `ImmutableTableNode` (and `UnknownNode`)\n// render the whole block `contenteditable=\"false\"`, so the browser draws no caret anywhere inside\n// one and its own arrow handling will not move a caret out of one either. A caret that gets in is\n// therefore invisible AND unrecoverable: neither key brings it back, and ArrowUp/ArrowDown scroll\n// the view rather than move anything.\n//\n// Getting in was never the browser's doing, which is what made this hard to place. Lexical's own\n// `RangeSelection.modify` descends into the next BLOCK whenever a press would leave the caret's\n// block (`$modifySelectionAroundDecoratorsAndBlocks`, core), and `@lexical/rich-text` claims the\n// arrow key at EDITOR priority to apply it and calls `preventDefault` — so the press never reaches\n// the browser, and the caret is placed inside a block Lexical is happy to address and the browser\n// cannot render a caret in.\n//\n// The rule below is therefore about the ONE press that would enter: it crosses the construct WHOLE\n// and lands on the far side, which is the same treatment the construct's own marker glyphs already\n// get one level down. Everything else about read-only constructs is unchanged — the caret still\n// traverses inside one it is already in, shift-extension still reaches in (a construct's bytes are\n// selectable and copyable, which is what read-only is FOR), and nothing is hidden.\n//\n// Scoped by the caret's BLOCK EDGE, so an inline read-only construct — an `\\optbreak`, a `\\ref`\n// sitting among the words of a paragraph — is never what this rule sees: it is inside the caret's\n// own block, never the block's sibling, and the visible-stop rules above continue to own it.\n//\n// NOT gated on marker mode. A construct the editor cannot model is read-only in every view, which\n// is the same reasoning `OpaqueBlockGuardPlugin` records for taking no `viewOptions` at all.\n/**\n * Whether `point` has no rendered content left to cross within `bound` — the press leaves it.\n *\n * Asks the same two questions a visible-stop move asks, in the same vocabulary, so \"the press\n * leaves this block\" cannot drift from \"the normalizer found nothing more to cross in it\".\n */\nfunction $isAtEdgeOf(point, direction, bound) {\n const node = point.getNode();\n // A move that stays inside one text node never reaches an edge, whichever way it goes.\n if (point.type === \"text\" && $isTraversableText(node)) {\n if (direction === \"next\" ? point.offset < node.getTextContentSize() : point.offset > 0)\n return false;\n }\n const seed = $scanSeed(node, point.offset, point.type, direction, bound);\n return $scanForRendered(seed, direction, bound) === undefined;\n}\n/**\n * The first node rendering anything beyond `construct` in `direction`, stepping OVER any further\n * read-only construct rather than coming to rest in it.\n *\n * Two tables back to back have no position between them — there is nothing there to put a caret on\n * — so they are crossed together rather than one per press.\n */\nfunction $renderedBeyondConstructs(construct, direction) {\n const root = $getRoot();\n for (let skipping = construct; skipping;) {\n const seed = $stepOver(skipping, direction, root);\n const rendered = seed && $scanForRendered(seed, direction, root);\n if (!rendered)\n return undefined;\n skipping = $opaqueBlockAncestor(rendered);\n if (!skipping)\n return rendered;\n }\n return undefined;\n}\n/**\n * Crosses a read-only construct WHOLE when this press would otherwise enter it.\n *\n * Runs FIRST in both arrow chains, ahead of the note, `\\fp` and visible-stop handlers, because it\n * decides whether the press leaves the caret's block at all — and because one of those handlers\n * (the hop past a collapsed note at a paragraph's end) would otherwise place the caret inside the\n * construct itself. Its predicate is narrow enough to take that position safely: it claims only\n * when the caret is at its own block's edge AND the block's neighbour is a construct, which is a\n * press no other handler here has an opinion about.\n *\n * @returns `true` when the press was claimed — including the refusal when nothing beyond the\n * construct can hold a caret, where leaving the caret put is the point.\n */\nfunction $crossOpaqueConstruct(selection, direction) {\n const point = selection.anchor;\n const node = point.getNode();\n // From INSIDE a construct this rule has nothing to say: the caret is already somewhere it exists\n // to prevent, and the glyph-atom rules above own the traversal there.\n if ($opaqueBlockAncestor(node))\n return false;\n const block = $blockOf(node);\n if (!block || !$isAtEdgeOf(point, direction, block))\n return false;\n const neighbour = $stepOver(block, direction, $getRoot());\n const construct = neighbour && $opaqueBlockAncestor(neighbour);\n if (!construct)\n return false;\n const rendered = $renderedBeyondConstructs(construct, direction);\n // Nothing beyond it can hold a caret (a document ending in a table): refuse the move and leave\n // the caret where the user can see it. Letting the press through is how it gets lost.\n if (!rendered)\n return true;\n if ($isTraversableText(rendered)) {\n const offset = direction === \"next\" ? 0 : rendered.getTextContentSize();\n rendered.select(offset, offset);\n return true;\n }\n const parent = rendered.getParent();\n // An unparented landing cannot be selected; refuse rather than enter the construct.\n if (!parent)\n return true;\n const index = rendered.getIndexWithinParent() + (direction === \"next\" ? 0 : 1);\n parent.select(index, index);\n return true;\n}\n/**\n * Places the caret in the block just past a collapsed note that ends it.\n *\n * A note that is its block's last child has nothing after it, so the landing is an element point\n * with no text node of its own — and a browser draws no insertion point where there is no rendered\n * text. Giving that position something to render in is `TrailingNoteCaretGuardPlugin`'s job, not\n * this one's: it materializes a transient zero-width caret host past the note once the caret comes\n * to rest here, so the landing stays a plain element point in the tree and this rule stays about\n * WHERE the caret goes rather than what renders it.\n *\n * Which side of the note the caret belongs on is settled here and is not a rendering question. A\n * collapsed note's content is hidden, so a caret inside one is invisible AND typing silently edits\n * the note body instead of the paragraph — the wrong bytes change. Outside the note, nothing has\n * changed and one backward press recovers, so it is strictly the better landing.\n *\n * Mutating: call inside `editor.update()`; dispatched from the arrow handling below.\n */\nfunction $selectPastTrailingNote(note) {\n const parent = note.getParent();\n // Detached from any block: leave the caret where the user can still see it.\n if (!parent)\n return;\n const indexPastNote = note.getIndexWithinParent() + 1;\n parent.select(indexPastNote, indexPastNote);\n}\n/** Helper to handle forward arrow key navigation logic */\nfunction $handleForwardNavigation(selection) {\n const node = selection.anchor.getNode();\n const nextNode = $getNextNode(selection);\n if ($isNoteNode(nextNode) && !$isMarkerNode(nextNode.getFirstChild())) {\n // note is next and markers are not editable\n if ($isSomeParaNode(node)) {\n const isSelectionAtParaEnd = selection.anchor.offset === node.getChildrenSize();\n if (isSelectionAtParaEnd)\n return false;\n }\n else {\n const isSelectionAtNodeEnd = selection.anchor.offset === node.getTextContentSize();\n if (!isSelectionAtNodeEnd)\n return false;\n }\n if (!nextNode.getIsCollapsed()) {\n // caret at end of node before expanded note → move past note caller\n if ($isImmutableTypedTextNode(nextNode.getFirstChild()))\n nextNode.select(2, 2);\n else\n nextNode.select(1, 1);\n return true;\n }\n else if (nextNode.is(nextNode.getParent()?.getLastChild())) {\n // caret at end of node before collapsed note at end of para → move past note\n const nextPara = nextNode.getParent()?.getNextSibling();\n if (nextPara && !($isSomeParaNode(nextPara) && $advancePastParaPrefixes(nextPara)))\n nextPara.selectStart();\n return true;\n }\n }\n if ($isSomeParaNode(node) && $isNoteNode(nextNode) && nextNode.getIsCollapsed()) {\n // caret between verse and collapsed note → move past note\n const nodeAfterNote = nextNode.getNextSibling();\n if (nodeAfterNote)\n nodeAfterNote.selectStart();\n else\n $selectPastTrailingNote(nextNode);\n return true;\n }\n const nextNodeParent = nextNode?.getParent();\n if ($isImmutableTypedTextNode(nextNode) &&\n $isNoteNode(nextNodeParent) &&\n nextNode.is(nextNodeParent?.getLastChild())) {\n // caret before closing note marker → move past note\n const nodeAfterNote = nextNodeParent.getNextSibling();\n if (nodeAfterNote)\n nodeAfterNote.selectStart();\n else if (nextNodeParent.getIsCollapsed())\n $selectPastTrailingNote(nextNodeParent);\n // An EXPANDED note's own end is rendered, so it stays a legitimate resting place.\n else\n nextNodeParent.selectEnd();\n return true;\n }\n return false;\n}\n/** Helper to handle backward arrow key navigation logic */\nfunction $handleBackwardNavigation(selection, viewOptions) {\n const prevNode = $getPreviousNode(selection);\n // If a chapter node is the only thing at the beginning → don't move.\n if ($isImmutableChapterNode(prevNode) && !prevNode.getPreviousSibling())\n return true;\n // If not at the beginning of node text → skip.\n const isSelectionAtNodeStart = selection.anchor.offset === 0;\n if (!isSelectionAtNodeStart)\n return false;\n // If at the beginning of book node text → don't move.\n const node = selection.anchor.getNode();\n if ($isBookNode(node.getParent()))\n return true;\n if ($isNoteNode(prevNode) && prevNode.getIsCollapsed()) {\n // caret at end of collapsed note preceded by verse → move to start of note in para\n const nodeBeforeNote = prevNode.getPreviousSibling();\n if (!$isImmutableVerseNode(nodeBeforeNote))\n return false;\n const parent = prevNode.getParent();\n if (!parent)\n return false;\n const noteIndex = prevNode.getIndexWithinParent();\n parent.select(noteIndex, noteIndex);\n return true;\n }\n // Deliberately gated on the always-\"collapsed\" MODE, not just the note's own collapsed flag:\n // under \"expandInline\" the caret must instead land inside the note's end (Lexical's default\n // move), where the NoteNodePlugin expands it for inline editing — hopping over the note here\n // would defeat that enter-and-expand behavior.\n if ($isSomeParaNode(prevNode) && viewOptions?.noteMode === \"collapsed\") {\n // caret at beginning of para after collapsed note → move to start in previous para\n const lastChild = prevNode.getLastChild();\n if (!lastChild)\n return false;\n const note = $findMatchingParent(lastChild, (n) => $isNoteNode(n));\n if ($isNoteNode(note) && note.getIsCollapsed()) {\n const parent = note.getParent();\n if (!parent)\n return false;\n const noteIndex = note.getIndexWithinParent();\n parent.select(noteIndex, noteIndex);\n return true;\n }\n }\n const noteNode = $findFirstAncestorNoteNode(node);\n if (!noteNode || noteNode.getIsCollapsed())\n return false;\n if ($isImmutableNoteCallerNode(prevNode)) {\n // caret after caller in expanded note (markers hidden) → move to start of note in para\n const parent = noteNode.getParent();\n if (!parent)\n return false;\n const noteIndex = noteNode.getIndexWithinParent();\n parent.select(noteIndex, noteIndex);\n return true;\n }\n return false;\n}\n/**\n * Returns whether custom ArrowUp/ArrowDown verse navigation should run.\n *\n * The verse jump is a SUBSTITUTE for a position the browser cannot move a visual line from — an\n * element point wedged between block nodes, or beside a verse number that is a childless decorator\n * and so hosts no caret of its own. Wherever the caret sits in rendered text the browser's own line\n * movement is the right answer and this declines. That includes inside an editable verse marker's\n * glyph, which is rendered text the caret walks a character at a time exactly like the words beside\n * it: intercepting there turned a line move into a jump to wherever the next verse happened to be —\n * the next paragraph, or sideways along the same line when the next verse shared it.\n *\n * The remaining case is one screen location with two spellings. Lexical normalizes an element point\n * to text offset 0 when the next child is a `TextNode`, so a caret at offset 0 of the text after a\n * caret-less verse number IS that element point, and the jump still owns it. A verse marker\n * rendered as glyph text hosts its own caret, so offset 0 after it is an ordinary text position and\n * both spellings of that location decline alike.\n */\nfunction $shouldAttemptVerticalVerseNavigation(selection) {\n if (selection.anchor.type === \"element\")\n return true;\n if (selection.anchor.offset !== 0)\n return false;\n const previousNode = selection.anchor.getNode().getPreviousSibling();\n return $isSomeVerseNode(previousNode) && $isDecoratorNode(previousNode);\n}\n","import { useLexicalComposerContext } from \"@lexical/react/LexicalComposerContext\";\nimport { mergeRegister } from \"@lexical/utils\";\nimport { deepEqual } from \"fast-equals\";\nimport { $getState, TextNode } from \"lexical\";\nimport { useEffect } from \"react\";\nimport { $hasSameCharAttributes, $isCharNode, $isMarkerNode, $isSeparatorPrefixHostText, $syncDisplayRun, $syncNestedGlyphs, $syncOpenerSeparators, charIdState, CharNode, displayRunDescriptor, EMPTY_CHAR_PLACEHOLDER_TEXT, NBSP, } from \"shared\";\n/** Combine adjacent CharNodes with the same attributes. */\nexport function CharNodePlugin() {\n const [editor] = useLexicalComposerContext();\n useCharNode(editor);\n return null;\n}\nfunction useCharNode(editor) {\n useEffect(() => {\n if (!editor.hasNodes([CharNode])) {\n throw new Error(\"CharNodePlugin: CharNode not registered on editor!\");\n }\n return mergeRegister(editor.registerNodeTransform(CharNode, $charNodeTransform), \n // Self-healing nested glyphs: whenever a char span is dirtied (created, moved, merged,\n // unwrapped), re-derive its glyphs' `+` from tree position — see nestedGlyphs.utils.ts\n // (`shared`) for the full representation rules this enforces.\n editor.registerNodeTransform(CharNode, $syncNestedGlyphs), \n // Self-healing display separators: every opening char glyph is followed by its NBSP\n // separator (text prefix or standalone spacer) — see markerSeparators.utils.ts (`shared`).\n editor.registerNodeTransform(CharNode, $syncOpenerSeparators), \n // Self-healing attribute display run: re-derive the `|…` run from unknownAttributes\n // whenever a span is dirtied — heals remote collab updates (delta-apply only calls\n // setUnknownAttributes) and structure surgery. $syncDisplayRun (displayRunSync.utils.ts,\n // `shared`), driven here with the char descriptor from displayRunRegistry.ts (`shared`).\n editor.registerNodeTransform(CharNode, (node) => $syncDisplayRun(displayRunDescriptor(\"char\"), node)), editor.registerNodeTransform(TextNode, $charTextNodeTransform));\n }, [editor]);\n}\n/**\n * Whether `node` renders its own marker glyphs — the editable-marker views build every char span\n * with an opening `MarkerNode` (and, for a closed span, a matching closer), while the visible and\n * hidden views build none at all.\n *\n * Combining two glyph-bearing spans is not a normalization but a byte change: the survivor keeps\n * BOTH spans' glyphs among its children, so a merged pair displays `\\nd a\\nd*\\nd b\\nd*` while\n * being ONE span. Re-tokenizing those bytes — what every settle does — yields two spans again,\n * which this transform merges again, and the rebuild's fixed-point refusal never fires because\n * each side genuinely differs from the other. That is a live editor freeze, reachable from any\n * edit that leaves two same-attribute spans adjacent (deleting the `*` from a `\\va*` closer\n * re-tokenizes into exactly that shape). Where the glyphs are absent there are no bytes to\n * contradict, so the merge stays: adjacent same-attribute runs really are equivalent there, and\n * that is the case delta-apply and structure surgery rely on.\n *\n * The MIXED pairing — a glyph-less span beside a glyph-bearing one — must still merge, because\n * the marker-apply paths rely on it: `$wrapRunInCharNode` (usj-marker-action.utils.ts) wraps the\n * uncovered run in a deliberately glyph-less span and counts on this transform to reunite it with\n * the glyph-bearing neighbor whose identity it copied. But a plain child move would land the\n * content OUTSIDE the neighbor's glyph pair (`[\\nd, \"Lord\", \\nd*, \" God\"]` — bytes that\n * re-tokenize as content after the closed span, inside it), so the mixed branches below splice\n * the glyph-less content inside the pair instead: after the opening glyph when the partner\n * follows, before the closing glyph when it precedes.\n */\nfunction $rendersOwnGlyphs(node) {\n return node.getChildren().some($isMarkerNode);\n}\n/**\n * Merge glyph-less `node` into the same-attribute glyph-bearing `target` that FOLLOWS it. In\n * document order the merged content starts with `node`'s children, so they belong directly after\n * `target`'s opening glyph — and the NBSP display separator moves with the \"first content\" role:\n * the old first content's presentation NBSP (prefix or standalone spacer) is stripped here, and\n * `$syncOpenerSeparators` re-derives the separator for the new first content when `target` is\n * re-processed as a dirtied span (the sync only ever ADDS a missing separator; it never strips a\n * stale one, so the strip must happen in the same surgery that demotes the old host).\n *\n * @returns `false` (nothing touched) when `target`'s first child is not an opening glyph — a\n * mid-edit shape (e.g. a just-deleted opener) the marker-edit engine settles itself.\n */\nfunction $mergeIntoFollowingGlyphSpan(node, target) {\n const opener = target.getFirstChild();\n if (!$isMarkerNode(opener) || opener.getMarkerSyntax() !== \"opening\")\n return false;\n const oldFirstContent = opener.getNextSibling();\n if ($isSeparatorPrefixHostText(oldFirstContent)) {\n const text = oldFirstContent.getTextContent();\n if (text.startsWith(NBSP)) {\n if (text === NBSP)\n oldFirstContent.remove();\n else\n oldFirstContent.setTextContent(text.slice(NBSP.length));\n }\n }\n target.splice(1, 0, node.getChildren());\n node.remove();\n return true;\n}\n/**\n * Merge glyph-less `node` into the same-attribute glyph-bearing `target` that PRECEDES it. The\n * merged content ends with `node`'s children, so they go before `target`'s closing glyph when it\n * has one; an unclosed span (opening glyph only) takes them as a plain append. The separator\n * needs no attention in this direction — `target`'s first content keeps its role.\n */\nfunction $mergeIntoPrecedingGlyphSpan(node, target) {\n const closer = target.getLastChild();\n const children = node.getChildren();\n if ($isMarkerNode(closer) && closer.getMarkerSyntax() === \"closing\") {\n children.forEach((child) => closer.insertBefore(child));\n }\n else {\n target.append(...children);\n }\n node.remove();\n}\n/**\n * Combine adjacent CharNodes with the same attributes.\n * @param node - CharNode thats needs updating.\n * @param editor - LexicalEditor instance.\n */\nfunction $charNodeTransform(node) {\n if (!$isCharNode(node))\n return;\n if (node.isEmpty()) {\n node.remove();\n return;\n }\n // Glyph-bearing spans are the displayed bytes; see `$rendersOwnGlyphs`.\n if ($rendersOwnGlyphs(node))\n return;\n const style = node.getMarker();\n // `\\fp` (footnote-paragraph) spans are exempt from combining: each span IS a paragraph\n // break inside the note (Enter and a multi-line paste there create consecutive `\\fp`\n // spans), so adjacency is content structure — combining collapsed two footnote paragraphs\n // into one in the serialized USJ. Formatting chars keep combining: for them adjacent\n // same-attribute runs really are equivalent.\n if (style === \"fp\")\n return;\n const cid = $getState(node, charIdState);\n const unknownAttributes = node.getUnknownAttributes();\n const nextNode = node.getNextSibling();\n if ($isCharNode(nextNode) &&\n $hasSameCharAttributes({ style, cid }, nextNode) &&\n deepEqual(unknownAttributes, nextNode.getUnknownAttributes())) {\n // Combine with next CharNode since it has the same attributes. A glyph-bearing partner\n // survives the merge (its glyphs are the displayed bytes) and takes the content inside its\n // glyph pair; `node` is gone after that, so the previous-sibling pass below cannot run — the\n // re-run on the dirtied survivor's siblings picks up any further merge.\n if ($rendersOwnGlyphs(nextNode)) {\n if ($mergeIntoFollowingGlyphSpan(node, nextNode))\n return;\n }\n else {\n node.append(...nextNode.getChildren());\n nextNode.remove();\n }\n }\n const prevNode = node.getPreviousSibling();\n if ($isCharNode(prevNode) &&\n $hasSameCharAttributes({ style, cid }, prevNode) &&\n deepEqual(unknownAttributes, prevNode.getUnknownAttributes())) {\n // Combine with previous CharNode since it has the same attributes.\n if ($rendersOwnGlyphs(prevNode)) {\n $mergeIntoPrecedingGlyphSpan(node, prevNode);\n }\n else {\n prevNode.append(...node.getChildren());\n node.remove();\n }\n }\n}\n/**\n * Remove 'empty' placeholder in CharNode once other text content is added.\n * @param node - TextNode that might be a placeholder.\n */\nfunction $charTextNodeTransform(node) {\n const parent = node.getParent();\n if (!$isCharNode(parent) || parent.getChildrenSize() !== 1)\n return;\n const text = node.getTextContent();\n if (text.length > 1 && text.startsWith(EMPTY_CHAR_PLACEHOLDER_TEXT)) {\n node.setTextContent(text.slice(1));\n node.selectEnd();\n }\n}\n","import { mergeRegister } from \"@lexical/utils\";\nimport { $getSelection, COMMAND_PRIORITY_LOW, COPY_COMMAND, CUT_COMMAND, PASTE_COMMAND, } from \"lexical\";\nfunction cleanupText(text) {\n return text.replaceAll(\"\\t\", \" \");\n}\n/**\n * Whether the selection holds content a copy could put on the clipboard: one exists and covers\n * something. A collapsed caret — or no selection at all — does not.\n *\n * Reads the ACTIVE editor state, so it must be called inside an update or a command listener. That\n * is the whole point: the committed state is a microtask behind, so a selection made earlier in the\n * same tick is invisible to `editor.getEditorState().read()` — a `copy()` right after a\n * programmatic select would read \"nothing selected\" and silently do nothing.\n */\nexport function $hasCopyableSelection() {\n const selection = $getSelection();\n return !!selection && !selection.isCollapsed();\n}\n/**\n * Stops a copy or cut that has nothing to copy, wherever it was dispatched from.\n *\n * A copy with no clipboard event of its own — the keyboard shortcuts below, the context menu's\n * Cut/Copy, an editor-ref `copy()` call — has to have one synthesized before anything can be\n * written: `@lexical/clipboard` appends a hidden placeholder element to the editor, points the DOM\n * selection at it, and runs `document.execCommand(\"copy\")` to provoke a real clipboard event it can\n * fill in. With nothing selected, that filling step declines — and declines BEFORE suppressing the\n * browser's own copy — so the browser copies what it was pointed at, the placeholder, and the user\n * loses whatever the clipboard already held to a character that was never in the document.\n *\n * So the command is CLAIMED here and does nothing, which is what copying an empty selection means.\n * The guard lives on the command rather than in front of each dispatch for two reasons: a command\n * listener runs inside the update, where the selection is authoritative for both pending and\n * committed state, and every dispatcher is covered by the one registration.\n *\n * Registered at `COMMAND_PRIORITY_LOW` — above `@lexical/rich-text`'s own copy/cut fallback at\n * EDITOR, which is the thing that synthesizes the event, and below every feature handler, so a view\n * with its own clipboard payload (Standard view's USFM copy) still claims first and this never runs.\n */\nexport function registerEmptyCopyGuard(editor) {\n const $claimWhenNothingToCopy = () => !$hasCopyableSelection();\n return mergeRegister(editor.registerCommand(COPY_COMMAND, $claimWhenNothingToCopy, COMMAND_PRIORITY_LOW), editor.registerCommand(CUT_COMMAND, $claimWhenNothingToCopy, COMMAND_PRIORITY_LOW));\n}\n/** Copies the selection. Dispatched unconditionally; an empty selection copies nothing because\n * {@link registerEmptyCopyGuard} claims the command, not because the dispatch is withheld. */\nexport const copySelection = (editor) => {\n editor.dispatchCommand(COPY_COMMAND, null);\n};\n/** Cuts the selection, under the same guard as {@link copySelection} — a cut copies first, so it\n * reaches the identical synthesized-copy path. */\nexport const cutSelection = (editor) => {\n editor.dispatchCommand(CUT_COMMAND, null);\n};\nexport const pasteSelection = (editor) => {\n navigator.clipboard.read().then(async (items) => {\n const permission = await navigator.permissions.query({\n // @ts-expect-error These types are incorrect.\n name: \"clipboard-read\",\n });\n if (permission.state === \"denied\") {\n alert(\"Not allowed to paste from clipboard.\");\n return;\n }\n const data = new DataTransfer();\n const item = items[0];\n for (const type of item.types) {\n const dataString = await (await item.getType(type)).text();\n data.setData(type, cleanupText(dataString));\n }\n const event = new ClipboardEvent(\"paste\", {\n clipboardData: data,\n });\n editor.dispatchCommand(PASTE_COMMAND, event);\n });\n};\nexport const pasteSelectionAsPlainText = (editor) => {\n navigator.clipboard.read().then(async () => {\n const permission = await navigator.permissions.query({\n // @ts-expect-error These types are incorrect.\n name: \"clipboard-read\",\n });\n if (permission.state === \"denied\") {\n alert(\"Not allowed to paste from clipboard.\");\n return;\n }\n const data = new DataTransfer();\n const text = await navigator.clipboard.readText();\n data.setData(\"text/plain\", cleanupText(text));\n const event = new ClipboardEvent(\"paste\", {\n clipboardData: data,\n });\n editor.dispatchCommand(PASTE_COMMAND, event);\n });\n};\n","import { copySelection, cutSelection, pasteSelection, pasteSelectionAsPlainText, registerEmptyCopyGuard, } from \"./clipboard.utils\";\nimport { useLexicalComposerContext } from \"@lexical/react/LexicalComposerContext\";\nimport { IS_APPLE, mergeRegister } from \"@lexical/utils\";\nimport { useEffect } from \"react\";\nexport function ClipboardPlugin() {\n const [editor] = useLexicalComposerContext();\n useEffect(() => {\n const onKeyDown = (event) => {\n const { key, shiftKey, metaKey, ctrlKey, altKey } = event;\n if (!(IS_APPLE ? metaKey : ctrlKey) || altKey)\n return;\n if (!shiftKey && key.toLowerCase() === \"c\") {\n event.preventDefault();\n copySelection(editor);\n }\n else if (!shiftKey && key.toLowerCase() === \"x\") {\n event.preventDefault();\n cutSelection(editor);\n }\n else if (key.toLowerCase() === \"v\") {\n event.preventDefault();\n if (shiftKey)\n pasteSelectionAsPlainText(editor);\n else\n pasteSelection(editor);\n }\n };\n return mergeRegister(\n // Every copy/cut this plugin's shortcuts synthesize — and every one the context menu or an\n // editor ref synthesizes against the same editor — passes through this guard.\n registerEmptyCopyGuard(editor), editor.registerRootListener((rootElement, prevRootElement) => {\n if (prevRootElement !== null) {\n prevRootElement.removeEventListener(\"keydown\", onKeyDown);\n }\n if (rootElement !== null) {\n rootElement.addEventListener(\"keydown\", onKeyDown);\n }\n }));\n }, [editor]);\n return null;\n}\n","import { useLexicalComposerContext } from \"@lexical/react/LexicalComposerContext\";\nimport { mergeRegister } from \"@lexical/utils\";\nimport { COMMAND_PRIORITY_NORMAL, DROP_COMMAND, KEY_DOWN_COMMAND, PASTE_COMMAND } from \"lexical\";\nimport { useEffect } from \"react\";\n/**\n * This plugin prevents the backslash or forward slash key from being typed, or pasted or dragged.\n * @returns `null`. This plugin has no DOM presence.\n */\nexport function CommandMenuPlugin({ logger }) {\n const [editor] = useLexicalComposerContext();\n useEffect(() => {\n return mergeRegister(\n // When the backslash or forward slash key is typed.\n editor.registerCommand(KEY_DOWN_COMMAND, (event) => {\n if (event.key !== \"\\\\\" && event.key !== \"/\")\n return false;\n event.preventDefault();\n return true;\n }, COMMAND_PRIORITY_NORMAL), \n // When the backslash or forward slash character is pasted into the editor.\n editor.registerCommand(PASTE_COMMAND, (event) => {\n const text = event.clipboardData?.getData(\"text/plain\");\n if (!text || (!text.includes(\"\\\\\") && !text.includes(\"/\")))\n return false;\n logger?.info(\"CommandMenuPlugin: paste containing backslash or forward slash ignored.\");\n event.preventDefault();\n return true;\n }, COMMAND_PRIORITY_NORMAL), \n // When the backslash or forward slash character is dragged into the editor.\n editor.registerCommand(DROP_COMMAND, (event) => {\n const text = event.dataTransfer?.getData(\"text/plain\");\n if (!text || (!text.includes(\"\\\\\") && !text.includes(\"/\")))\n return false;\n logger?.info(\"CommandMenuPlugin: drag containing backslash or forward slash ignored.\");\n event.preventDefault();\n return true;\n }, COMMAND_PRIORITY_NORMAL));\n }, [editor, logger]);\n return null;\n}\n","import { jsx as _jsx } from \"react/jsx-runtime\";\n/**\n * Adapted from https://github.com/facebook/lexical/blob/main/packages/lexical-playground/src/plugins/ContextMenuPlugin/index.tsx\n */\nimport { copySelection, cutSelection, pasteSelection, pasteSelectionAsPlainText, registerEmptyCopyGuard, } from \"./clipboard.utils\";\nimport { useLexicalComposerContext } from \"@lexical/react/LexicalComposerContext\";\nimport { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, } from \"react\";\nimport * as ReactDOM from \"react-dom\";\nimport { isImmutableChapterElement } from \"shared\";\nfunction ContextMenuItem({ index, isSelected, onClick, onMouseEnter, option, }) {\n let className = \"item\";\n if (isSelected) {\n className += \" selected\";\n }\n if (option.isDisabled) {\n className += \" disabled\";\n }\n return (_jsx(\"li\", { tabIndex: -1, className: className, role: \"option\", \"aria-selected\": isSelected, \"aria-disabled\": option.isDisabled, id: \"typeahead-item-\" + index, onMouseEnter: onMouseEnter, onClick: option.isDisabled ? undefined : onClick, children: _jsx(\"span\", { className: \"text\", children: option.title }) }));\n}\nfunction ContextMenu({ options, selectedItemIndex, onOptionClick, onOptionMouseEnter, }) {\n return (_jsx(\"div\", { className: \"typeahead-popover\", children: _jsx(\"ul\", { children: options.map((option, i) => (_jsx(ContextMenuItem, { index: i, isSelected: selectedItemIndex === i, onClick: () => onOptionClick(option, i), onMouseEnter: () => onOptionMouseEnter(i), option: option }, option.key))) }) }));\n}\nlet optionKeyCounter = 0;\nexport class ContextMenuOption {\n key;\n title;\n onSelect;\n isDisabled;\n constructor(title, options) {\n this.key = `context-menu-option-${optionKeyCounter++}`;\n this.title = title;\n this.onSelect = options.onSelect.bind(this);\n this.isDisabled = options.isDisabled || false;\n }\n}\nexport function ContextMenuPlugin({ options: extraOptions, } = {}) {\n const [editor] = useLexicalComposerContext();\n const [isReadonly, setIsReadonly] = useState(() => !editor.isEditable());\n const [menuState, setMenuState] = useState({\n isOpen: false,\n x: 0,\n y: 0,\n });\n const [selectedIndex, setSelectedIndex] = useState(undefined);\n const options = useMemo(() => {\n const builtIn = [\n // Cut/Copy with nothing selected leave the clipboard alone rather than writing a placeholder\n // over it — `registerEmptyCopyGuard` (mounted below) claims the command, so no selection\n // check is needed here. They are not disabled in that case, because this option list is\n // built once per editor rather than per menu opening, so its `isDisabled` flags cannot track\n // the live selection.\n new ContextMenuOption(`Cut`, {\n onSelect: () => {\n cutSelection(editor);\n },\n isDisabled: isReadonly,\n }),\n new ContextMenuOption(`Copy`, {\n onSelect: () => {\n copySelection(editor);\n },\n }),\n new ContextMenuOption(`Paste`, {\n onSelect: () => {\n pasteSelection(editor);\n },\n isDisabled: isReadonly,\n }),\n new ContextMenuOption(`Paste as Plain Text`, {\n onSelect: () => {\n pasteSelectionAsPlainText(editor);\n },\n isDisabled: isReadonly,\n }),\n ];\n const extra = (extraOptions ?? []).map((opt) => new ContextMenuOption(opt.title, { onSelect: opt.onSelect, isDisabled: opt.isDisabled }));\n return [...builtIn, ...extra];\n }, [editor, isReadonly, extraOptions]);\n const closeMenu = useCallback(() => {\n setMenuState((prev) => ({ ...prev, isOpen: false }));\n setSelectedIndex(undefined);\n }, []);\n // This plugin is exported on its own, so a host can mount it without `ClipboardPlugin` — and its\n // Cut/Copy options would then hit the unguarded synthesized-copy path, overwriting whatever the\n // clipboard already held with `@lexical/clipboard`'s hidden placeholder character. Registering\n // the guard here keeps the plugin self-sufficient; a second registration alongside\n // `ClipboardPlugin`'s is harmless, since both listeners sit at the same priority and the first\n // one to claim an empty selection stops propagation before the other runs.\n useEffect(() => registerEmptyCopyGuard(editor), [editor]);\n // Register context menu event on editor root\n useEffect(() => {\n const handleContextMenu = (event) => {\n const target = event.target;\n if (editor.getRootElement() === target || isImmutableChapterElement(target)) {\n return;\n }\n event.preventDefault();\n setMenuState({ isOpen: true, x: event.clientX, y: event.clientY });\n setSelectedIndex(undefined);\n };\n return editor.registerRootListener((rootElement, prevRootElement) => {\n prevRootElement?.removeEventListener(\"contextmenu\", handleContextMenu);\n if (!rootElement)\n return;\n rootElement.addEventListener(\"contextmenu\", handleContextMenu);\n });\n }, [editor]);\n // Close menu on scroll\n useEffect(() => {\n if (!menuState.isOpen)\n return;\n const handleScroll = () => {\n closeMenu();\n };\n globalThis.addEventListener(\"scroll\", handleScroll, true);\n return () => globalThis.removeEventListener(\"scroll\", handleScroll, true);\n }, [menuState.isOpen, closeMenu]);\n // Close menu on click outside\n useEffect(() => {\n if (!menuState.isOpen)\n return;\n const handlePointerDown = () => {\n closeMenu();\n };\n document.addEventListener(\"pointerdown\", handlePointerDown);\n return () => document.removeEventListener(\"pointerdown\", handlePointerDown);\n }, [menuState.isOpen, closeMenu]);\n // Keyboard navigation and close on Escape\n useEffect(() => {\n if (!menuState.isOpen)\n return;\n const handleKeyDown = (event) => {\n if (event.key === \"Escape\") {\n closeMenu();\n }\n else if (event.key === \"ArrowDown\") {\n event.preventDefault();\n event.stopPropagation();\n setSelectedIndex((prev) => (prev === undefined ? 0 : (prev + 1) % options.length));\n }\n else if (event.key === \"ArrowUp\") {\n event.preventDefault();\n event.stopPropagation();\n setSelectedIndex((prev) => prev === undefined ? options.length - 1 : (prev - 1 + options.length) % options.length);\n }\n else if (event.key === \"Enter\" && selectedIndex !== undefined) {\n event.preventDefault();\n event.stopPropagation();\n const option = options[selectedIndex];\n if (option && !option.isDisabled) {\n editor.update(() => {\n option.onSelect();\n });\n closeMenu();\n }\n }\n };\n // Use capture phase so this fires before Lexical's own keydown handler,\n // which would otherwise consume arrow keys and move the editor cursor.\n document.addEventListener(\"keydown\", handleKeyDown, true);\n return () => document.removeEventListener(\"keydown\", handleKeyDown, true);\n }, [menuState.isOpen, closeMenu, options, selectedIndex, editor]);\n useEffect(() => editor.registerEditableListener((editable) => {\n setIsReadonly(!editable);\n }), [editor]);\n const menuRef = useRef(null);\n // Clamp menu position to viewport bounds before first paint to prevent off-screen rendering.\n useLayoutEffect(() => {\n const menu = menuRef.current;\n if (!menu)\n return;\n const { width, height } = menu.getBoundingClientRect();\n const clampedLeft = Math.max(0, Math.min(menuState.x, globalThis.innerWidth - width));\n const clampedTop = Math.max(0, Math.min(menuState.y, globalThis.innerHeight - height));\n menu.style.left = `${clampedLeft}px`;\n menu.style.top = `${clampedTop}px`;\n menu.style.visibility = \"visible\";\n }, [menuState.isOpen, menuState.x, menuState.y]);\n if (!menuState.isOpen)\n return null;\n return ReactDOM.createPortal(_jsx(\"div\", { ref: menuRef, className: \"typeahead-popover auto-embed-menu\", style: {\n left: menuState.x,\n position: \"fixed\",\n top: menuState.y,\n userSelect: \"none\",\n visibility: \"hidden\",\n width: 200,\n zIndex: 9999,\n }, onPointerDown: (e) => e.stopPropagation(), children: _jsx(ContextMenu, { options: options, selectedItemIndex: selectedIndex, onOptionClick: (option) => {\n if (!option.isDisabled) {\n editor.update(() => {\n option.onSelect();\n });\n closeMenu();\n }\n }, onOptionMouseEnter: (index) => {\n setSelectedIndex(index);\n } }) }), document.body);\n}\n","import { $isImmutableNoteCallerNode } from \"../../nodes/usj\";\nimport { useLexicalComposerContext } from \"@lexical/react/LexicalComposerContext\";\nimport { $createRangeSelectionFromDom, $getNearestNodeFromDOMNode, $getSelection, $isDecoratorNode, $isRangeSelection, $setSelection, COMMAND_PRIORITY_CRITICAL, getDOMSelection, isDOMTextNode, isSelectionWithinEditor, SELECTION_CHANGE_COMMAND, } from \"lexical\";\nimport { useEffect, useRef } from \"react\";\n/**\n * Whether `point` is the DOM-ORDER START of `range` — which is what decides the direction a point\n * inside a decorator has to grow, and cannot be read off anchor/focus: a backward drag makes the\n * anchor the range's end. Both the node and the offset are compared, so a range whose two ends sit\n * in the SAME container is still told apart by its offsets.\n */\nfunction isRangeStart(range, point) {\n return range.startContainer === point.node && range.startOffset === point.offset;\n}\n/**\n * The edge of the decorator a COLLAPSED point is nearest to. A caret is a single position rather\n * than a direction, so the only thing to honour is which side of the glyph the user pointed at:\n * the first half of a text node goes before it, the rest after it. A point that is not in text\n * names no position within the glyph at all, so it takes the leading edge.\n */\nfunction nearestBoundary(point) {\n if (!isDOMTextNode(point.node))\n return \"before\";\n const length = point.node.nodeValue?.length ?? 0;\n // Doubled rather than halved so the comparison stays in integers for an odd-length glyph.\n return point.offset * 2 < length ? \"before\" : \"after\";\n}\n/**\n * Whether another rule already owns where a selection landing on this decorator belongs, so\n * normalizing it here would take the landing away from that rule.\n *\n * A note caller is the one such decorator. `TrailingNoteCaretGuardPlugin` answers a landing on the\n * caller of a collapsed note that ends its block by hosting a caret PAST the whole note, and it\n * recognizes that landing precisely by the selection being unresolved — so supplying a selection\n * for it silently disables the repair and leaves the caret in the note's hidden body, which is the\n * position that repair exists to avoid. Every boundary of the caller is inside the note anyway:\n * between the note's opening glyph and the caller, or between the caller and its hidden content.\n * Neither is a position a collapsed note can draw a caret at, so there is nothing here to win.\n */\nfunction $hasDedicatedSelectionOwner(node) {\n return $isImmutableNoteCallerNode(node);\n}\n/**\n * The DOM point at `boundary` of the decorator that CONTAINS `point`, or `undefined` when `point`\n * is not inside one.\n *\n * \"Inside\" means strictly inside: the decorator's own element with an offset — the boundary form\n * Lexical already resolves to an element point beside the decorator — is left alone, and so is a\n * point in any node the decorator does not contain.\n *\n * Mutating context only in the sense that it reads the active editor state; it changes nothing.\n */\nfunction $decoratorBoundary(editor, point, boundary) {\n const node = $getNearestNodeFromDOMNode(point.node);\n if (!$isDecoratorNode(node) || $hasDedicatedSelectionOwner(node))\n return undefined;\n const element = editor.getElementByKey(node.getKey());\n if (!element || element === point.node || !element.contains(point.node))\n return undefined;\n const parent = element.parentNode;\n if (!parent)\n return undefined;\n const index = Array.prototype.indexOf.call(parent.childNodes, element);\n if (index < 0)\n return undefined;\n return { node: parent, offset: boundary === \"before\" ? index : index + 1 };\n}\n/**\n * Hand Lexical the selection it refused to resolve, with each end that lands inside a decorator moved\n * to that decorator's own boundaries.\n *\n * The DOM selection is READ here and written only by Lexical's own reconciler pass, which\n * `isPointerDown` decides the timing of — see the plugin doc comment for the rule and why it turns on\n * the button.\n *\n * Mutating (sets the editor's selection): call inside `editor.update()` or a command handler.\n *\n * @param isPointerDown Whether a pointer button is held, i.e. whether a drag is in flight.\n * @returns Whether the browser's DOM selection has been left holding points Lexical cannot resolve,\n * so it still has to be materialized when the button comes up.\n */\nfunction $snapSelectionToDecoratorBoundaries(editor, isPointerDown) {\n // Only an UNRESOLVED arrival is this plugin's business. A DOM point inside a decorator always\n // nulls the WHOLE selection (see the plugin doc comment), so a selection Lexical did produce\n // cannot have an end inside one — and reading the DOM over it would clobber a caret that\n // something else placed deliberately, since a programmatic move leaves the DOM a commit behind.\n if ($getSelection())\n return false;\n const rootElement = editor.getRootElement();\n const domSelection = getDOMSelection(rootElement?.ownerDocument.defaultView ?? null);\n if (!domSelection || domSelection.rangeCount === 0)\n return false;\n const { anchorNode, anchorOffset, focusNode, focusOffset } = domSelection;\n if (!anchorNode || !focusNode)\n return false;\n if (!isSelectionWithinEditor(editor, anchorNode, focusNode))\n return false;\n const anchor = { node: anchorNode, offset: anchorOffset };\n const focus = { node: focusNode, offset: focusOffset };\n let snappedAnchor;\n let snappedFocus;\n if (domSelection.isCollapsed) {\n snappedAnchor = $decoratorBoundary(editor, anchor, nearestBoundary(anchor));\n snappedFocus = snappedAnchor;\n }\n else {\n // A range grows OUTWARD, so the decorator ends up wholly inside the selection rather than\n // clipped: whichever end leads in DOM order takes the leading edge and the other the trailing\n // one. That is what makes a drag begun on `\\fig ` carry the whole glyph, and a drag that runs\n // into `|src=\"…\"` carry the whole attribute run.\n const anchorLeads = isRangeStart(domSelection.getRangeAt(0), anchor);\n snappedAnchor = $decoratorBoundary(editor, anchor, anchorLeads ? \"before\" : \"after\");\n snappedFocus = $decoratorBoundary(editor, focus, anchorLeads ? \"after\" : \"before\");\n }\n if (!snappedAnchor && !snappedFocus)\n return false;\n const nextAnchor = snappedAnchor ?? anchor;\n const nextFocus = snappedFocus ?? focus;\n // The snapped ends reach Lexical as a selection-SHAPED value rather than by this plugin writing\n // them into the DOM, because mid-drag the browser's own points have to survive untouched (see the\n // plugin doc comment); the DOM write, when it happens, is Lexical's own reconciler pass. Everything\n // `$createRangeSelectionFromDom` reads is present — see {@link DomSelectionPoints}, which is what\n // makes the one cast honest rather than a widening of a partial object.\n const snapped = {\n anchorNode: nextAnchor.node,\n anchorOffset: nextAnchor.offset,\n focusNode: nextFocus.node,\n focusOffset: nextFocus.offset,\n };\n const selection = $createRangeSelectionFromDom(snapped, editor);\n if (!selection)\n return false;\n $setSelection(selection);\n // `dirty` is how a selection asks `$commitPendingUpdates` (lexical/LexicalUpdates) to sync itself\n // to the browser; with it clear, the commit's `updateDOMSelection` — the ONE place in the commit\n // path that touches the browser's selection, `removeAllRanges()` included — is not reached for an\n // update that dirties no nodes. So clearing it mid-drag is what leaves the browser's base alone,\n // and leaving it set everywhere else is what keeps the DOM in the resolvable boundary form. Order\n // matters: `$setSelection` sets the flag.\n //\n // `SKIP_DOM_SELECTION_TAG` expresses the same intent and is read in the same guard, but it is not\n // confined to one commit: `$commitPendingUpdates` only empties `editor._updateTags` when the update\n // dirtied nodes, and a selection repair dirties none — so the tag would survive into the next\n // commit and swallow the DOM write for the caret move after it.\n selection.dirty = !isPointerDown;\n return isPointerDown;\n}\n/**\n * Keeps a selection that lands inside a DECORATOR usable, by normalizing the offending end(s) to\n * the decorator's own boundaries.\n *\n * A decorator is atomic: no selection point exists inside one, and Lexical says so by refusing such\n * a point outright. `$internalResolveSelectionPoint` (lexical/LexicalSelection) resolves a DOM\n * point whose node sits INSIDE a decorator's element to `null` — the walk up from the DOM node\n * reaches the decorator, which is not a `TextNode`, and the text branch returns `null` — and\n * `$internalResolveSelectionPoints` then nulls the WHOLE selection, not just that end. On commit,\n * `updateDOMSelection` calls `domSelection.removeAllRanges()` because the previous selection was\n * inside the editor, so the visible selection is destroyed too. (A Lexical upgrade that changes\n * either function is the thing to re-read before trusting this plugin.)\n *\n * That matters because Standard view renders read-only USFM bytes as decorators whose glyph text is\n * an ordinary DOM text child of the decorator's own element: an `UnknownNode`'s `\\fig `, its\n * `|src=\"…\"` attribute run, and its `\\fig*` closer are each an `ImmutableTypedTextNode`. Those bytes\n * are selectable and copyable by design — that is what a read-only block is FOR\n * (`OpaqueBlockGuardPlugin`) — but pointing at them is exactly what produces the interior DOM point\n * Lexical will not resolve. The measured results: a drag begun on the `\\fig ` glyph left the editor\n * with no selection at all, so Ctrl+C reached only the empty-copy guard and the clipboard was never\n * written; and a drag from the caption into the attribute run stopped dead at the caption's end.\n *\n * So each offending end is moved to the decorator's boundary IN ITS PARENT — the one form of the\n * position Lexical does resolve, into an element point beside the decorator. A range grows outward,\n * so the glyph is wholly in or wholly out and never clipped; a caret takes the nearer side.\n *\n * WHERE those boundary points go turns on one thing: whether a pointer button is down.\n *\n * While it is down — a drag in flight — they go to the editor state ALONE, and the browser's DOM\n * selection is left exactly as the browser wrote it. Chromium extends a drag only from a base it\n * placed itself, so replacing that base with a scripted element point stops the drag dead: each\n * further mouse move only re-places a collapsed caret under the pointer, no range is ever formed, and\n * Ctrl+C reaches the empty-copy guard again — the very failure this repair exists to fix. (Chromium\n * also clamps a drag begun inside a `contenteditable=\"false\"` island to that island, which is not\n * ours to change either.) So mid-drag the editor state carries the boundary form that copy and every\n * plugin read, while the browser keeps the text position it is willing to extend from.\n *\n * On the release — and immediately for any arrival with NO button down, which is to say a keyboard\n * extend such as Shift+Arrow into a glyph, or a programmatic move — the boundary form is written to\n * the DOM through Lexical's ordinary reconciler pass. That is the hazard this rule closes rather than\n * a cosmetic touch: Lexical re-derives an update's selection FROM THE DOM whenever the update is not\n * attributable to a DOM event it trusts (`$internalCreateRangeSelection` in lexical/LexicalSelection\n * consults `window.event`), so a DOM selection left on raw interior points turns the next event-less\n * `editor.update()` — one opened from a timer, a microtask or a React effect — into a `null`\n * selection, and that commit's `removeAllRanges()` takes the user's visible selection with it.\n * Whenever no drag is in flight the DOM holds the boundary form, so any such re-derivation lands back\n * on the same selection.\n *\n * The release is a safe moment to materialize from: inside a `pointerup` listener `window.event.type`\n * is `\"pointerup\"`, which is not one of the types `$internalCreateRangeSelection` re-reads the DOM\n * for, so that update clones the snapped selection out of the editor state instead of re-resolving\n * the interior points it is there to replace.\n *\n * Runs at `COMMAND_PRIORITY_CRITICAL` so the caret guards that read the selection see the repaired\n * one rather than none, and never claims the command — a selection change is nobody's to own.\n * Lexical dispatches `SELECTION_CHANGE_COMMAND` even for a selection it resolved to `null`, and at\n * that moment the DOM selection is still intact, which is what makes the repair possible at all.\n *\n * Not gated on view options: decorators exist in every view. The one decorator it steps around is\n * the note caller, whose landings another rule already owns — see\n * {@link $hasDedicatedSelectionOwner}.\n *\n * @returns Always `null`; this plugin renders no UI.\n */\nexport function DecoratorBoundarySelectionPlugin() {\n const [editor] = useLexicalComposerContext();\n const isPointerDown = useRef(false);\n const isMaterializePending = useRef(false);\n useEffect(() => {\n // Only the PRIMARY button starts a drag Chromium will extend a selection from, and only a\n // press that starts one may set this flag: a secondary-button press can open the platform's\n // NATIVE context menu (`ContextMenuPlugin` deliberately lets it through for some targets),\n // which on Windows and Linux grabs the mouse so the page never sees the matching `pointerup`.\n // The flag would then stay set with no drag in flight, and every later arrival — a click, a\n // Shift+Arrow extend — would be treated as one, leaving the DOM selection stranded on points\n // Lexical cannot resolve. The button is duck-typed rather than read through `instanceof\n // PointerEvent`: not every host defines that global (jsdom before 27 has none), and a press that\n // carries no button at all still counts as a primary one.\n const markDown = (event) => {\n if (\"button\" in event && event.button !== 0)\n return;\n isPointerDown.current = true;\n };\n // BOTH ends are listened for on the DOCUMENT in the capture phase, and the release on\n // `pointercancel` as well as `pointerup`: a drag that starts in the editor can finish anywhere,\n // and a flag that fails to clear would leave every later keyboard move reading as a drag and the\n // DOM selection stranded on points Lexical cannot resolve. The press has to be heard on the\n // document rather than the root for the mirror-image reason — Chromium places a caret INSIDE\n // the editor for a press that lands in the container just outside the contenteditable root, so\n // a root-scoped listener misses the start of a real drag and the first snap is materialized into\n // the DOM, which is exactly what stops a drag dead. A press with no editor selection behind it\n // costs nothing: the flag is only ever read while repairing a selection inside the editor.\n // (`NoteShellCaretGuardPlugin` registers all three on the document for the same reason.)\n //\n // `release` opens an `editor.update()` from a DOM listener, a deliberate exception to the rule\n // that mutations enter through commands (`NoteNodePlugin` and `ContextMenuPlugin` make the same\n // one). A command would not remove the dependency that makes the call safe: it must run\n // synchronously inside the trusted `pointerup`/`pointercancel` dispatch, with no update of this\n // editor already active. Lexical queues an update requested during another one, and the queued\n // callback runs after that dispatch has ended, where `window.event` no longer says `pointerup`\n // and the update re-derives its selection from the raw interior DOM points instead of cloning the\n // snapped one.\n const release = () => {\n isPointerDown.current = false;\n if (!isMaterializePending.current)\n return;\n isMaterializePending.current = false;\n // Re-dirty the snapped selection so Lexical's ordinary reconciler pass writes the boundary form\n // into the DOM, now that no drag needs the browser's own base any more.\n editor.update(() => {\n const selection = $getSelection();\n if ($isRangeSelection(selection))\n selection.dirty = true;\n });\n };\n // One listener does both the wiring and the unwiring: Lexical calls it with the current root at\n // registration, with each replacement root on a swap, and with `(null, previous)` on teardown.\n return editor.registerRootListener((rootElement, prevRootElement) => {\n const previousDocument = prevRootElement?.ownerDocument;\n previousDocument?.removeEventListener(\"pointerdown\", markDown, true);\n previousDocument?.removeEventListener(\"pointerup\", release, true);\n previousDocument?.removeEventListener(\"pointercancel\", release, true);\n isPointerDown.current = false;\n isMaterializePending.current = false;\n const currentDocument = rootElement?.ownerDocument;\n currentDocument?.addEventListener(\"pointerdown\", markDown, true);\n currentDocument?.addEventListener(\"pointerup\", release, true);\n currentDocument?.addEventListener(\"pointercancel\", release, true);\n });\n }, [editor]);\n useEffect(() => {\n return editor.registerCommand(SELECTION_CHANGE_COMMAND, () => {\n if ($snapSelectionToDecoratorBoundaries(editor, isPointerDown.current))\n isMaterializePending.current = true;\n return false;\n }, COMMAND_PRIORITY_CRITICAL);\n }, [editor]);\n return null;\n}\n","import { useLexicalComposerContext } from \"@lexical/react/LexicalComposerContext\";\nimport { IS_APPLE } from \"@lexical/utils\";\nimport { COMMAND_PRIORITY_CRITICAL, KEY_DOWN_COMMAND } from \"lexical\";\nimport { useEffect } from \"react\";\n/**\n * Prevent undo and redo keyboard shortcuts while preserving command-based undo/redo.\n * @returns `null`. This plugin has no DOM presence.\n */\nexport function DisableHistoryShortcutsPlugin() {\n const [editor] = useLexicalComposerContext();\n useEffect(() => {\n return editor.registerCommand(KEY_DOWN_COMMAND, (event) => {\n const { key, shiftKey, metaKey, ctrlKey, altKey } = event;\n if (!(IS_APPLE ? metaKey : ctrlKey) || altKey)\n return false;\n const normalizedKey = key.toLowerCase();\n const isUndo = normalizedKey === \"z\" && !shiftKey;\n const isRedo = normalizedKey === \"y\" || (normalizedKey === \"z\" && shiftKey);\n if (!isUndo && !isRedo)\n return false;\n event.preventDefault();\n return true;\n }, COMMAND_PRIORITY_CRITICAL);\n }, [editor]);\n return null;\n}\n","import { useLexicalComposerContext } from \"@lexical/react/LexicalComposerContext\";\nimport { useLayoutEffect } from \"react\";\nexport function EditablePlugin({ isEditable }) {\n const [editor] = useLexicalComposerContext();\n useLayoutEffect(() => {\n editor.setEditable(isEditable);\n }, [editor, isEditable]);\n return null;\n}\n","import { useLexicalComposerContext } from \"@lexical/react/LexicalComposerContext\";\nimport { mergeRegister } from \"@lexical/utils\";\nimport { $addUpdateTag, $getNodeByKey, $getSelection, $isRangeSelection, $isTextNode, BLUR_COMMAND, COMMAND_PRIORITY_EDITOR, SELECTION_CHANGE_COMMAND, TextNode, } from \"lexical\";\nimport { useCallback, useEffect, useRef } from \"react\";\nimport { $caretHostAtBoundary, $createCursorPlaceholderNode, $isCursorPlaceholderOnlyText, $placeCaretAtBoundary, $removeCursorPlaceholder, CURSOR_CHANGE_TAG, CURSOR_PLACEHOLDER_CHAR, isCursorPlaceholderOnly, } from \"shared\";\n/** Whether `key` currently resolves to a bare cursor-host text node (only placeholder chars). */\nfunction $isPlaceholderHost(key) {\n return !!key && $isCursorPlaceholderOnlyText($getNodeByKey(key));\n}\n/**\n * The transient caret-host lifecycle, shared by every guard that needs one.\n *\n * Some caret positions in this editor can only be expressed as an ELEMENT point — a boundary with\n * no text node on it — because what surrounds them renders no text the browser can draw an\n * insertion point in: a childless decorator such as an immutable verse number, or a collapsed note\n * whose content is hidden. Lexical is perfectly happy with such a caret; the browser paints\n * nothing, so the user sees no cursor and the next keypress becomes the page's rather than the\n * editor's. The repair is to materialize a zero-width-space text node at that position and move the\n * caret into it.\n *\n * The repair is stated once, as \"put the caret at the boundary past this node, and give that\n * boundary something to render the caret in if nothing there already does\". Stating it that way\n * lets a guard drive it from an arrival the caret's own resting place cannot express — a click that\n * came down inside hidden content, or produced no caret at all — and get the same position and the\n * same single host as the arrival that does announce itself.\n *\n * The host is TRANSIENT, and this hook is what makes that true in one place rather than once per\n * guard: it is created only when the caret comes to rest at such a position, removed as soon as the\n * caret leaves or the editor blurs, and stripped the moment real text is typed into it. It never\n * accumulates and never appears in a document nobody put a caret into.\n *\n * Every mutation carries {@link CURSOR_CHANGE_TAG}, which is in `blackListedChangeTags` — so the\n * commit never reaches the host application's USJ-change handler and produces no delta op. The USJ\n * serializer drops a placeholder-only text node as well, and the collab coordinate systems give it\n * zero length, so a host cannot reach saved Scripture or a peer even if one somehow outlived a\n * commit. All placeholder handling is scoped to the tracked host by node key — a zero-width space\n * is legitimate content in some scripts (Thai/Khmer/Lao line breaks) and is never touched.\n *\n * Unlike the arrow-driven `CursorHandler` placeholder system (perf-react), this hosts a *resting*\n * caret.\n *\n * @param $caretHostAnchor - The guard's own rule for when a host is needed. Must be stable across\n * renders (declare it at module scope), since it is an effect dependency.\n * @returns The repair step itself, for a guard that has an arrival of its own to drive it from.\n */\nexport function useTransientCaretHost($caretHostAnchor) {\n const [editor] = useLexicalComposerContext();\n const hostKeyRef = useRef(undefined);\n // See CaretHostRepair. Deliberately free of `editor`: everything it touches is resolved from the\n // active editor state, so it is safe to hand to a caller that is already inside an update.\n const $repairCaret = useCallback((target) => {\n const selection = $getSelection();\n const anchorKey = $isRangeSelection(selection) && selection.isCollapsed() ? selection.anchor.key : undefined;\n const staleKey = hostKeyRef.current;\n // A tracked host that is no longer a bare placeholder (typed into) is no longer ours.\n const staleIsBareHost = $isPlaceholderHost(staleKey);\n if (staleKey && !staleIsBareHost)\n hostKeyRef.current = undefined;\n // The host the caret was just put into, which is therefore not stale whatever it was before.\n let occupiedKey;\n if (target) {\n // The repair is the same however the caret got here: put it at the boundary just past the\n // named node, materializing a host there only when nothing already renders one. Reusing an\n // existing host is what keeps a second arrival at the same position — a click, once an\n // earlier arrival has already put a host there — from stacking hosts.\n const parent = target.getParentOrThrow();\n const boundary = target.getIndexWithinParent() + 1;\n const existing = $caretHostAtBoundary(parent, boundary);\n if (existing) {\n // Track the adopted host too: every cleanup path (the stale-host pass below, blur,\n // unmount) acts solely on hostKeyRef, so a host reused from another instance — or one\n // this instance forgot after $stripPlaceholderOnEdit cleared the ref — would otherwise\n // never be removed when the caret leaves it.\n hostKeyRef.current = existing.getKey();\n occupiedKey = existing.getKey();\n }\n else {\n const host = $createCursorPlaceholderNode();\n target.insertAfter(host);\n hostKeyRef.current = host.getKey();\n occupiedKey = host.getKey();\n }\n $placeCaretAtBoundary(parent, boundary);\n }\n // A tracked host the caret has left, and has not just been put back into, should go.\n if (staleKey && staleIsBareHost && staleKey !== anchorKey && staleKey !== occupiedKey) {\n const stale = $getNodeByKey(staleKey);\n if ($isTextNode(stale))\n stale.remove();\n if (hostKeyRef.current === staleKey)\n hostKeyRef.current = undefined;\n }\n }, []);\n useEffect(() => {\n // Repair the caret's resting place and/or drop a stale host it has left. Runs from\n // SELECTION_CHANGE (a command, the sanctioned place to mutate), which Lexical dispatches\n // from WITHIN the update that applies the new selection — so `$getSelection()` must be read\n // DIRECTLY here, where it resolves against the pending state. Wrapping the reads in\n // `editor.getEditorState().read(...)` evaluated the PREVIOUS commit: the anchor was the\n // caret's old position, so no host was created where the caret just arrived, and on the\n // next move the stale-host pass treated the still-current host as abandoned and yanked the\n // caret back to the previous boundary. A nested `editor.update(...)` is queued rather than\n // run inline while an update is active, so the repair also runs directly, tagging the\n // active update instead. It converges: after the repair the caret sits in a host past the\n // anchor, so the next SELECTION_CHANGE finds nothing to repair and no stale host.\n const $syncCaretHost = () => {\n const target = $caretHostAnchor();\n const selection = $getSelection();\n const anchorKey = $isRangeSelection(selection) && selection.isCollapsed() ? selection.anchor.key : undefined;\n const staleKey = hostKeyRef.current;\n // Either a position to repair, or a tracked host the caret is no longer resting in.\n const hasWork = !!target || (!!staleKey && staleKey !== anchorKey);\n if (!hasWork)\n return;\n $addUpdateTag(CURSOR_CHANGE_TAG);\n $repairCaret(target);\n };\n /**\n * Strip the placeholder once real text is typed into *our* host, fixing the caret offset.\n * Scoped to the tracked host node by key so a legitimate ZWSP elsewhere (e.g. a Thai/Khmer\n * line-break in real text) is never touched.\n */\n const $stripPlaceholderOnEdit = (node) => {\n if (node.getKey() !== hostKeyRef.current)\n return;\n const text = node.getTextContent();\n // Still a bare host (nothing typed yet), or no placeholder to remove: leave it.\n if (isCursorPlaceholderOnly(text) || !text.includes(CURSOR_PLACEHOLDER_CHAR))\n return;\n const selection = $getSelection();\n const anchorOffset = $isRangeSelection(selection) &&\n selection.isCollapsed() &&\n selection.anchor.key === node.getKey()\n ? selection.anchor.offset\n : undefined;\n $removeCursorPlaceholder(node);\n hostKeyRef.current = undefined;\n if (anchorOffset !== undefined) {\n const removedBefore = text.slice(0, anchorOffset).split(CURSOR_PLACEHOLDER_CHAR).length - 1;\n const nextOffset = Math.max(0, anchorOffset - removedBefore);\n node.select(nextOffset, nextOffset);\n }\n };\n const unregister = mergeRegister(editor.registerCommand(SELECTION_CHANGE_COMMAND, () => {\n $syncCaretHost();\n return false;\n }, COMMAND_PRIORITY_EDITOR), editor.registerCommand(BLUR_COMMAND, () => {\n const key = hostKeyRef.current;\n if (!key)\n return false;\n let isBareHost = false;\n editor.getEditorState().read(() => {\n isBareHost = $isPlaceholderHost(key);\n });\n if (isBareHost) {\n editor.update(() => {\n const host = $getNodeByKey(key);\n if ($isTextNode(host))\n host.remove();\n }, { tag: CURSOR_CHANGE_TAG });\n }\n hostKeyRef.current = undefined;\n return false;\n }, COMMAND_PRIORITY_EDITOR), editor.registerNodeTransform(TextNode, $stripPlaceholderOnEdit));\n // Drop the tracked key when the editor changes so it can't resolve into a different editor.\n return () => {\n unregister();\n hostKeyRef.current = undefined;\n };\n }, [editor, $caretHostAnchor, $repairCaret]);\n return $repairCaret;\n}\n","import { useTransientCaretHost } from \"./transientCaretHost\";\nimport { $getSelection, $isElementNode, $isRangeSelection } from \"lexical\";\nimport { $caretHostAtBoundary } from \"shared\";\nimport { $isSomeVerseNode } from \"../../nodes/usj\";\n/**\n * The verse marker after which an empty-verse caret host is needed, or `undefined`.\n *\n * A verse whose content has all been deleted collapses to just its marker — an\n * `ImmutableVerseNode` is a childless decorator, so the caret can only land as an element point\n * wedged between markers, which the browser renders with no visible caret. This detects that state:\n * a collapsed element-type caret sitting immediately after a verse marker that is followed by\n * nothing, or by another verse marker (i.e. no `TextNode` to host the caret).\n *\n * Returns `undefined` when a text node already follows the marker (real content, or an existing\n * placeholder host), or when the caret is not at such a boundary.\n *\n * Read-only: call inside `editor.getEditorState().read()`.\n */\nexport function $emptyVerseNeedingHost() {\n const selection = $getSelection();\n if (!$isRangeSelection(selection) || !selection.isCollapsed())\n return undefined;\n const { anchor } = selection;\n if (anchor.type !== \"element\")\n return undefined;\n const element = anchor.getNode();\n if (!$isElementNode(element))\n return undefined;\n const children = element.getChildren();\n const verse = children[anchor.offset - 1];\n if (!$isSomeVerseNode(verse))\n return undefined;\n // A text node already hosts the caret at this boundary (real content or an existing placeholder).\n if ($caretHostAtBoundary(element, anchor.offset))\n return undefined;\n const following = children[anchor.offset];\n // Nothing, or another verse marker, follows: this verse has no caret host.\n if (following === undefined || $isSomeVerseNode(following))\n return verse;\n return undefined;\n}\n/**\n * Keeps a visible caret in a verse whose text has been fully deleted.\n *\n * A verse number is rendered by a childless `ImmutableVerseNode` decorator, so once a verse has no\n * text the caret can only rest as an element point between decorators — which the browser draws\n * with no visible caret. This plugin drops a zero-width-space \"caret host\" text node into such an\n * empty verse and moves the caret into it, so the insertion point stays visible and typing lands in\n * the verse. {@link useTransientCaretHost} owns the host's lifetime — created on arrival, removed on\n * departure or blur, stripped the moment real text is typed — and the exclusions that keep it out of\n * saved Scripture and out of collaborative traffic; this file supplies only the rule for WHERE one\n * is needed. `TrailingNoteCaretGuardPlugin` supplies the other rule.\n *\n * Unlike the arrow-driven `CursorHandler` placeholder system (perf-react), this hosts a *resting*\n * caret and is aware of verse markers, so it fits the platform editor's immutable verse numbers.\n *\n * @returns Always `null`; this plugin renders no UI.\n */\nexport function EmptyVerseCaretGuardPlugin() {\n useTransientCaretHost($emptyVerseNeedingHost);\n return null;\n}\n","import { useLexicalComposerContext } from \"@lexical/react/LexicalComposerContext\";\nimport { $addUpdateTag, CLEAR_HISTORY_COMMAND, SKIP_DOM_SELECTION_TAG } from \"lexical\";\nimport { useEffect } from \"react\";\nimport { EXTERNAL_USJ_MUTATION_TAG } from \"shared\";\n/**\n * A plugin component that updates the state of the lexical editor when incoming Scripture changes.\n * @param scripture - Scripture data.\n * @param scriptureRef - Optional ref to scripture data. If provided, reads from ref at update time\n * to get the most current value (useful when options change triggers state updates).\n * @param nodeOptions - Options for each node.\n * @param editorAdaptor - Editor adaptor.\n * @param viewOptions - View options of the editor.\n * @param logger - Logger instance.\n * @returns null, i.e. no DOM elements.\n */\nexport function LoadStatePlugin({ scripture, scriptureRef, nodeOptions, editorAdaptor, viewOptions, logger, }) {\n const [editor] = useLexicalComposerContext();\n useEffect(() => {\n editorAdaptor.initialize?.(nodeOptions, logger);\n }, [editorAdaptor, logger, nodeOptions]);\n useEffect(() => {\n // Read scripture from ref if available (to get latest value after state updates),\n // otherwise fall back to the prop value\n const currentScripture = scriptureRef?.current ?? scripture;\n editorAdaptor.reset?.();\n const serializedEditorState = editorAdaptor.serializeEditorState(currentScripture, viewOptions);\n if (serializedEditorState == null) {\n logger?.warn(\"LoadStatePlugin: serializedEditorState was null or undefined. Skipping editor update.\");\n return;\n }\n try {\n const editorState = editor.parseEditorState(serializedEditorState);\n // Use queueMicrotask to defer the editor update outside of React's lifecycle,\n // preventing flushSync warnings when this is triggered by a parent component update\n queueMicrotask(() => {\n // An external replace parses to a null selection; reconciling that against the\n // SHARED document selection clears/moves the caret of whatever DOES have focus — observed\n // live as the parent editor's PDP echo (~150-250ms after an edit) stealing DOM focus out\n // of the footnote-editor popover mid-typing. An editor without focus has no claim on the\n // DOM selection, so skip DOM-selection reconciliation entirely in that case. Evaluated at\n // apply time (inside the microtask), not schedule time, so a focus change in between is\n // honored. A focused editor keeps the existing behavior.\n const rootElement = editor.getRootElement();\n const activeElement = rootElement?.ownerDocument.activeElement;\n const editorHasFocus = rootElement != null &&\n activeElement != null &&\n (rootElement === activeElement || rootElement.contains(activeElement));\n editor.update(() => {\n if (!editorHasFocus)\n $addUpdateTag(SKIP_DOM_SELECTION_TAG);\n editor.setEditorState(editorState);\n editor.dispatchCommand(CLEAR_HISTORY_COMMAND, undefined);\n }, { tag: EXTERNAL_USJ_MUTATION_TAG });\n });\n }\n catch {\n logger?.error(\"LoadStatePlugin: error parsing or setting editor state.\");\n }\n }, [editor, editorAdaptor, logger, scripture, scriptureRef, viewOptions]);\n return null;\n}\n","import { $isImmutableNoteCallerNode, $isImmutableVerseNode, defaultCrossRefCallers, defaultNoteCallers, ImmutableNoteCallerNode, } from \"../../nodes/usj\";\nimport { useLexicalComposerContext } from \"@lexical/react/LexicalComposerContext\";\nimport { $findMatchingParent, mergeRegister } from \"@lexical/utils\";\nimport { $createRangeSelection, $createTextNode, $getNodeByKey, $getSelection, $isRangeSelection, $isTextNode, $setSelection, COMMAND_PRIORITY_LOW, SELECTION_CHANGE_COMMAND, TextNode, } from \"lexical\";\nimport { useEffect, useRef } from \"react\";\nimport { $findFirstAncestorNoteNode, $getNoteCallerPreviewText, $isCharNode, $isMarkerNode, $isNoteNode, $isSomeParaNode, CharNode, EMPTY_CHAR_PLACEHOLDER_TEXT, GENERATOR_NOTE_CALLER, getEditableCallerText, NBSP, NoteNode, } from \"shared\";\n/**\n * This plugin is responsible for handling NoteNode and NoteNodeCaller interactions. It also\n * updates the counter style symbols for note callers when the node options change.\n * @param expandedNoteKeyRef - Ref to track the currently expanded note key, if any.\n * @param nodeOptions - Node options that includes the list of potential node callers.\n * @param viewOptions - View options of the editor.\n * @param logger - Logger to use, if any.\n * @returns\n */\nexport function NoteNodePlugin({ expandedNoteKeyRef, nodeOptions, viewOptions, logger, }) {\n const [editor] = useLexicalComposerContext();\n useNodeOptions(nodeOptions, logger);\n useNoteNode(editor, expandedNoteKeyRef, viewOptions, logger);\n return null;\n}\n/**\n * This hook is responsible for handling updates to `nodeOptions`.\n * @param nodeOptions - Node options that includes the list of potential note and cross-reference\n * callers.\n * @param logger - Logger to use, if any.\n */\nfunction useNodeOptions(nodeOptions, logger) {\n const previousNoteCallersRef = useRef(undefined);\n const previousCrossRefCallersRef = useRef(undefined);\n const nodeOptionsNoteCallers = nodeOptions.noteCallers;\n const nodeOptionsCrossRefCallers = nodeOptions.crossRefCallers;\n useEffect(() => {\n let noteCallers = nodeOptionsNoteCallers;\n if (!noteCallers || noteCallers.length <= 0)\n noteCallers = defaultNoteCallers;\n if (previousNoteCallersRef.current !== noteCallers) {\n previousNoteCallersRef.current = noteCallers;\n updateCounterStyleSymbols(\"note-callers\", noteCallers, logger);\n }\n }, [logger, nodeOptionsNoteCallers]);\n useEffect(() => {\n let crossRefCallers = nodeOptionsCrossRefCallers;\n if (!crossRefCallers || crossRefCallers.length <= 0)\n crossRefCallers = defaultCrossRefCallers;\n if (previousCrossRefCallersRef.current !== crossRefCallers) {\n previousCrossRefCallersRef.current = crossRefCallers;\n updateCounterStyleSymbols(\"cross-ref-callers\", crossRefCallers, logger);\n }\n }, [logger, nodeOptionsCrossRefCallers]);\n}\n/**\n * This hook is responsible for handling NoteNode and NoteNodeCaller interactions.\n * @param editor - The LexicalEditor instance used to access the DOM.\n * @param expandedNoteKeyRef - Ref to track the currently expanded note key, if any.\n * @param viewOptions - View options of the editor.\n * @param logger - Logger to use, if any.\n */\nfunction useNoteNode(editor, expandedNoteKeyRef, viewOptions, logger) {\n useEffect(() => {\n if (!editor.hasNodes([CharNode, NoteNode, ImmutableNoteCallerNode])) {\n throw new Error(\"NoteNodePlugin: CharNode, NoteNode or ImmutableNoteCallerNode not registered on editor!\");\n }\n const doubleClickListener = (event) => editor.update(() => $handleDoubleClick(event));\n return mergeRegister(\n // Remove NoteNode if it doesn't contain a caller node and ensure typed text goes before it.\n editor.registerNodeTransform(NoteNode, (node) => $noteNodeTransform(node, viewOptions)), \n // Update NoteNodeCaller preview text when NoteNode children text is changed.\n editor.registerNodeTransform(CharNode, $noteCharNodeTransform), editor.registerNodeTransform(TextNode, $noteTextNodeTransform), \n // Ensure NBSP after caller.\n editor.registerNodeTransform(ImmutableNoteCallerNode, $noteCallerNodeTransform), \n // Re-generate all note callers when a note is removed.\n editor.registerMutationListener(ImmutableNoteCallerNode, (nodeMutations, { prevEditorState }) => generateNoteCallersOnDestroy(nodeMutations, prevEditorState)), \n // Handle the cursor moving next to a NoteNode. NoteNode arrow key navigation when note is\n // after a verse node is handled in the ArrowNavigationPlugin.\n editor.registerCommand(SELECTION_CHANGE_COMMAND, () => $handleCursorNextToNoteNode(editor, expandedNoteKeyRef, viewOptions, logger), COMMAND_PRIORITY_LOW), \n // Handle double-click of a word immediately following a NoteNode (no space between).\n editor.registerRootListener((rootElement, prevRootElement) => {\n if (prevRootElement !== null) {\n prevRootElement.removeEventListener(\"dblclick\", doubleClickListener);\n }\n if (rootElement !== null) {\n rootElement.addEventListener(\"dblclick\", doubleClickListener);\n }\n }));\n }, [editor, expandedNoteKeyRef, logger, viewOptions]);\n}\n/**\n * Cleans up a NoteNode to ensure it is valid.\n *\n * @remarks Removes a NoteNode if it does not contain an ImmutableNoteCallerNode child when\n * `markerMode` is not 'editable'. This can happen during certain editing operations or data\n * inconsistencies. When editing notes we may intentionally exclude the caller by setting it empty.\n * Also if the first node is a TextNode move it before the NoteNode, i.e. the user typed when the\n * selection was at the beginning of a NoteNode.\n * @param node - The NoteNode to check.\n * @param viewOptions - The view options that includes the marker mode.\n */\nfunction $noteNodeTransform(node, viewOptions) {\n const nodeChildren = node.getChildren();\n const hasNoteCaller = nodeChildren.some((child) => $isImmutableNoteCallerNode(child));\n if (!hasNoteCaller && viewOptions?.markerMode !== \"editable\" && node.getCaller() !== \"\")\n node.remove();\n if (nodeChildren.length > 0) {\n const firstChild = nodeChildren[0];\n if ($isTextNode(firstChild) && !$isMarkerNode(firstChild)) {\n // Never eject the expanded editable caller text (` caller`, getEditableCallerText):\n // after the opening glyph is deleted it becomes the note's first child, and ejecting it\n // plants the caller word in the paragraph on every serialization round (live-observed as a\n // repeated `word~` spray). It must stay in place so MarkerEditPlugin's note-deletion\n // transform can recognize the damaged editable note and remove it whole. User-typed stray\n // text at the note's start (any other text) is still salvaged out.\n if (firstChild.getTextContent() !== getEditableCallerText(node.getCaller()))\n node.insertBefore(firstChild);\n }\n }\n}\n/**\n * Changes in NoteNode children text are updated in the NoteNodeCaller preview text.\n * Also ensure NBSP after each note top-level node.\n * @param node - CharNode thats needs its preview text updated.\n */\nfunction $noteCharNodeTransform(node) {\n const parent = node.getParentOrThrow();\n const children = parent.getChildren();\n const noteCaller = children.find((child) => $isImmutableNoteCallerNode(child));\n if (!$isCharNode(node) || !$isNoteNode(parent) || !noteCaller)\n return;\n const previewText = $getNoteCallerPreviewText(children);\n if (noteCaller.getPreviewText() !== previewText)\n noteCaller.setPreviewText(previewText);\n // Ensure NBSP after each note top-level CharNode\n const nextSibling = node.getNextSibling();\n if (!$isTextNode(nextSibling))\n node.insertAfter($createTextNode(NBSP));\n else if (nextSibling.getTextContent() !== NBSP)\n nextSibling.setTextContent(NBSP);\n}\n/**\n * Changes in NoteNode children text are updated in the NoteNodeCaller preview text.\n * Also ensure NBSP after each note top-level CharNode isn't modified.\n * Also remove 'empty' placeholder in CharNode inside NoteNode once other text content is added.\n * @param node - TextNode thats needs its preview text updated.\n */\nfunction $noteTextNodeTransform(node) {\n const noteNode = $findFirstAncestorNoteNode(node);\n const children = noteNode?.getChildren();\n const noteCaller = children?.find((child) => $isImmutableNoteCallerNode(child));\n if (!$isTextNode(node) || !$isNoteNode(noteNode) || !noteCaller || !children)\n return;\n const parent = node.getParent();\n if (!$isMarkerNode(node) && $isNoteNode(parent)) {\n if (node.getTextContent() !== NBSP) {\n node.setTextContent(NBSP);\n node.selectEnd();\n }\n }\n if ($isCharNode(parent) && parent.getChildrenSize() === 1) {\n const text = node.getTextContent();\n if (text.length > 1 && text.startsWith(EMPTY_CHAR_PLACEHOLDER_TEXT)) {\n node.setTextContent(text.slice(1));\n node.selectEnd();\n }\n }\n const previewText = $getNoteCallerPreviewText(children);\n if (noteCaller.getPreviewText() !== previewText)\n noteCaller.setPreviewText(previewText);\n}\n/**\n * Ensure NBSP after caller.\n * @param node - TextNode thats needs its preview text updated.\n */\nfunction $noteCallerNodeTransform(node) {\n if (!$isImmutableNoteCallerNode(node))\n return;\n const nextSibling = node.getNextSibling();\n if (!$isTextNode(nextSibling) || $isMarkerNode(nextSibling))\n node.insertAfter($createTextNode(NBSP));\n else if (nextSibling.getTextContent() !== NBSP)\n nextSibling.setTextContent(NBSP);\n}\n/**\n * When a NoteNode is destroyed, check if it was generated and force a CSS reflow.\n * @param nodeMutations - Map of node mutations.\n * @param prevEditorState - The previous EditorState.\n */\nfunction generateNoteCallersOnDestroy(nodeMutations, prevEditorState) {\n for (const [nodeKey, mutation] of nodeMutations) {\n if (mutation !== \"destroyed\")\n continue;\n const nodeWasGenerated = prevEditorState.read(() => {\n const node = $getNodeByKey(nodeKey);\n const parent = node?.getParent();\n return ($isImmutableNoteCallerNode(node) &&\n $isNoteNode(parent) &&\n parent.getCaller() === GENERATOR_NOTE_CALLER);\n });\n const editorElement = document.querySelector(\".editor-input\");\n if (!nodeWasGenerated || !editorElement)\n continue;\n editorElement.classList.add(\"reset-counters\");\n // Force a reflow to ensure the counter reset is applied\n void editorElement.offsetHeight;\n editorElement.classList.remove(\"reset-counters\");\n }\n}\nfunction $handleCursorNextToNoteNode(editor, expandedNoteKeyRef, viewOptions, logger) {\n if (viewOptions?.noteMode !== \"expandInline\")\n return false;\n const selection = $getSelection();\n if (!$isRangeSelection(selection) || !selection.isCollapsed())\n return false;\n const anchor = selection.anchor;\n const node = anchor.getNode();\n // Case 1: caret moved away from a NoteNode → collapse it\n if (expandedNoteKeyRef.current) {\n const noteAncestor = $findMatchingParent(node, (n) => $isNoteNode(n));\n if (!noteAncestor) {\n const note = $getNodeByKey(expandedNoteKeyRef.current);\n if (note && !note.getIsCollapsed()) {\n logger?.debug(\"Cursor moved away from NoteNode, collapsing it\");\n $toggleNoteCollapseWithFallback(editor, expandedNoteKeyRef.current, logger);\n }\n expandedNoteKeyRef.current = undefined;\n }\n else {\n // Still inside a NoteNode → keep tracking it.\n if (expandedNoteKeyRef.current !== noteAncestor.getKey()) {\n // Update key since we moved to a different note.\n expandedNoteKeyRef.current = noteAncestor.getKey();\n }\n }\n }\n // Case 2: caret at start of a text node → check prev sibling\n if (anchor.offset === 0) {\n const prev = node.getPreviousSibling();\n if ($isNoteNode(prev)) {\n logger?.debug(\"Cursor is just after a NoteNode\");\n const noteKey = prev.getKey();\n if (prev.getIsCollapsed())\n expandedNoteKeyRef.current = noteKey;\n else\n expandedNoteKeyRef.current = undefined;\n $toggleNoteCollapseWithFallback(editor, noteKey, logger);\n }\n }\n // Case 3: caret at end of a text node → check next sibling\n if (anchor.offset === node.getTextContentSize()) {\n const next = node.getNextSibling();\n if ($isNoteNode(next)) {\n logger?.debug(\"Cursor is just before a NoteNode\");\n const noteKey = next.getKey();\n if (next.getIsCollapsed())\n expandedNoteKeyRef.current = noteKey;\n else\n expandedNoteKeyRef.current = undefined;\n $toggleNoteCollapseWithFallback(editor, noteKey, logger);\n }\n else if (!next) {\n const noteAncestor = $findMatchingParent(node, (n) => $isNoteNode(n));\n if (noteAncestor &&\n noteAncestor.getIsCollapsed() &&\n $isSomeParaNode(noteAncestor.getParent()) &&\n noteAncestor.is(noteAncestor.getParent()?.getLastChild())) {\n logger?.debug(\"Cursor is at end of note at end of para\");\n const noteKey = noteAncestor.getKey();\n expandedNoteKeyRef.current = noteKey;\n $toggleNoteCollapseWithFallback(editor, noteKey, logger);\n }\n }\n }\n // Case 4: caret between verse and note → toggle note\n if ($isSomeParaNode(node)) {\n const child = node.getChildAtIndex(anchor.offset);\n const prevChild = child?.getPreviousSibling();\n if ($isImmutableVerseNode(prevChild) && $isNoteNode(child)) {\n logger?.debug(\"Cursor is between verse and NoteNode\");\n const noteKey = child.getKey();\n if (child.getIsCollapsed())\n expandedNoteKeyRef.current = noteKey;\n else\n expandedNoteKeyRef.current = undefined;\n $toggleNoteCollapseWithFallback(editor, noteKey, logger);\n }\n }\n return false;\n}\n/**\n * Toggle the collapse state of a NoteNode, with fallback to deferred update if read-only error\n * occurs. This handles the edge case where clicking next to a note immediately after editor load\n * triggers a read-only selection context.\n * @param editor - The LexicalEditor instance used to access the DOM.\n * @param noteKey - The key of the NoteNode to toggle.\n */\nfunction $toggleNoteCollapseWithFallback(editor, noteKey, logger) {\n const noteNode = $getNodeByKey(noteKey);\n try {\n // Try immediate update first (works in most cases)\n noteNode?.toggleIsCollapsed();\n }\n catch (error) {\n // If we get a read-only error, defer the update\n if (error instanceof Error && error.message.includes(\"read only\")) {\n logger?.warn(\"Fallback triggered after stabilization - edge case\");\n setTimeout(() => {\n editor.update(() => {\n noteNode?.toggleIsCollapsed();\n });\n }, 0);\n }\n else {\n throw error; // Re-throw if it's not the expected read-only error\n }\n }\n}\n/**\n * Ensure that when double-clicking on a word after a note node (no space between) that it only\n * selects that word and does not include the note in the selection.\n * @param event - The MouseEvent triggered by the double-click interaction\n */\nfunction $handleDoubleClick(event) {\n const selection = $getSelection();\n if (!$isRangeSelection(selection))\n return;\n const anchor = selection.anchor;\n const focus = selection.focus;\n const anchorNode = anchor.getNode();\n const focusNode = focus.getNode();\n if ($isNoteNode(anchorNode) && $isTextNode(focusNode)) {\n event.preventDefault();\n // Create new selection only including the TextNode\n const newSelection = $createRangeSelection();\n newSelection.anchor.set(focusNode.getKey(), 0, \"text\");\n newSelection.focus.set(focusNode.getKey(), focus.offset, \"text\");\n $setSelection(newSelection);\n }\n}\nfunction updateCounterStyleSymbols(counterStyleName, newSymbols, logger) {\n // Loop through all stylesheets\n for (const styleSheet of document.styleSheets) {\n try {\n const cssRules = styleSheet.cssRules || styleSheet.rules;\n // Loop through all CSS rules in the current stylesheet\n for (const rule of cssRules) {\n if (isCounterStyleRuleLike(rule, counterStyleName)) {\n // Create the symbols string (space-separated symbols)\n const symbolsValue = newSymbols.map((symbol) => `\"${symbol}\"`).join(\" \");\n // Set the new symbols\n rule.symbols = symbolsValue;\n return;\n }\n }\n }\n catch {\n // Skip cross-origin stylesheets that can't be accessed\n continue;\n }\n }\n // If the counter-style wasn't found, you could create it\n logger?.warn(`Editor: counter style \"${counterStyleName}\" not found.`);\n}\nfunction isCounterStyleRuleLike(rule, counterStyleName) {\n return (\n // This check could be simpler but as is also works for test mocks.\n typeof rule === \"object\" &&\n rule !== null &&\n \"name\" in rule &&\n rule.name === counterStyleName &&\n \"symbols\" in rule &&\n typeof rule.symbols === \"string\");\n}\n","import { useLexicalComposerContext } from \"@lexical/react/LexicalComposerContext\";\nimport { $addUpdateTag, $getPreviousSelection, $getSelection, $isRangeSelection, $isTextNode, COMMAND_PRIORITY_EDITOR, SELECTION_CHANGE_COMMAND, } from \"lexical\";\nimport { useEffect, useRef } from \"react\";\nimport { $isMarkerNode, $isNoteNode, $noteEditableCallerNode, $placeCaretAtBoundary, CURSOR_CHANGE_TAG, } from \"shared\";\n/**\n * An expanded note's SHELL — its leading opening glyph(s) and its editable caller, the `\\f + ` a\n * reader sees — as the nodes that carry it, or an empty list when this note has no protected shell.\n *\n * Read off the nodes' MODE rather than the view options, the same way every other note rule is read\n * off the tree: the adaptor puts exactly these nodes in `token` mode when the host governs the\n * marker and the caller through its own UI (`ViewOptions.isNoteShellEditable: false`), so a view\n * that leaves the shell editable builds `normal` nodes and every rule here is structurally a no-op\n * for it. Nothing has to stay in sync with a flag.\n */\nfunction $noteShellNodes(note) {\n if (note.getIsCollapsed() !== false)\n return [];\n const shell = [];\n for (const child of note.getChildren()) {\n if (!$isMarkerNode(child) || child.getMarkerSyntax() !== \"opening\")\n break;\n shell.push(child);\n }\n const caller = $noteEditableCallerNode(note);\n if (caller)\n shell.push(caller);\n // Protected only when the adaptor marked it so. A partly-token shell is not a shape the adaptor\n // builds; requiring ALL of it keeps this from half-applying to one it did not.\n return shell.length > 0 && shell.every((node) => $isTextNode(node) && node.getMode() === \"token\")\n ? shell\n : [];\n}\n/** The note whose protected shell `node` belongs to, or `undefined`. */\nfunction $shellOwner(node) {\n const note = node.getParent();\n if (!$isNoteNode(note))\n return undefined;\n return $noteShellNodes(note).some((shellNode) => shellNode.is(node)) ? note : undefined;\n}\n/**\n * The boundary index just past `note`'s shell — the start of the note's own content, and the only\n * caret position at the shell's trailing edge that is inside the note.\n */\nfunction $contentStartIndex(note) {\n const shell = $noteShellNodes(note);\n const last = shell[shell.length - 1];\n return last ? last.getIndexWithinParent() + 1 : 0;\n}\n/** `note`'s own child that contains `node`, or `undefined` when `node` is not inside `note`. */\nfunction $noteChildContaining(note, node) {\n for (let cursor = node; cursor; cursor = cursor.getParent())\n if (note.is(cursor.getParent()))\n return cursor;\n return undefined;\n}\n/**\n * Whether the caret reached the shell from the note's CONTENT side — which is what a leftward move\n * out of the note looks like, and the one case where pushing it forward again would trap it.\n *\n * Taken from the PREVIOUS selection because the shell is crossed whole in either direction and the\n * landing point alone cannot say which way the user was going. Anything else — a rightward move\n * from before the note, no previous selection at all — reads as travelling forward, which is also\n * the safe default: forward lands in editable content.\n *\n * Only asked of a KEYBOARD move. A pointer is not travelling anywhere — it names a destination\n * outright — so the previous caret says nothing about the user's intent, and reading it as a\n * direction sends a click away from the note it landed in. That is not hypothetical: a popover\n * that focuses its editor with no selection parks the caret at the document end, which for a\n * document holding one note is that note's own closing glyph — so the FIRST click on the shell\n * read as \"coming from the content side\" and threw the caret past the whole note.\n */\nfunction $arrivedFromContentSide(note) {\n const previous = $getPreviousSelection();\n if (!$isRangeSelection(previous))\n return false;\n const { anchor } = previous;\n const node = anchor.getNode();\n if (note.is(node))\n return anchor.offset >= $contentStartIndex(note);\n const child = $noteChildContaining(note, node);\n return child !== undefined && child.getIndexWithinParent() >= $contentStartIndex(note);\n}\n/**\n * The note whose shell `point` rests in ILLEGITIMATELY, or `undefined`.\n *\n * Exactly one offset in the whole shell is a caret position: the trailing edge of its last node.\n * Lexical's `token` mode redirects an insertion at a token node's boundary to a sibling, and only\n * there does it pick the right one — a fresh node after the caller, which is the start of the\n * note's content. At the shell's LEADING edge it inserts a text node inside the note before the\n * opening glyph (a `NoteNode` does not refuse text before it), and at the seam between the glyph\n * and the caller it inserts BETWEEN them. Every other offset is strictly inside a token node,\n * where an insertion replaces that node outright.\n *\n * So the trailing edge is where this guard puts the caret, and the one place it leaves alone —\n * which is also what stops it from correcting its own correction.\n */\nfunction $shellAt(point) {\n if (point.type !== \"text\")\n return undefined;\n const node = point.getNode();\n const note = $shellOwner(node);\n if (!note)\n return undefined;\n return $isShellTrailingEdge(note, node, point.offset) ? undefined : note;\n}\n/** Whether `point` is at the shell's trailing edge — the caret position just past `\\f + `. */\nfunction $isShellTrailingEdge(note, node, offset) {\n const shell = $noteShellNodes(note);\n const last = shell[shell.length - 1];\n return last !== undefined && last.is(node) && offset === last.getTextContentSize();\n}\n/** Collapse the caret to the shell's trailing edge, the start of the note's own content. */\nfunction $placeAtShellTrailingEdge(note) {\n const shell = $noteShellNodes(note);\n const last = shell[shell.length - 1];\n if ($isTextNode(last))\n last.select(last.getTextContentSize(), last.getTextContentSize());\n else\n $placeCaretAtBoundary(note, $contentStartIndex(note));\n}\n/**\n * Move a caret that has come to rest inside an expanded note's protected shell to the nearest\n * position outside it: the start of the note's own content, or — for a KEYBOARD move coming back\n * out of that content — the position before the whole note, so the shell can be crossed leftward\n * instead of trapping the caret against it.\n *\n * `isPointerGesture` says the caret was placed by a pointer, which is a destination rather than a\n * direction: such a caret always goes to the content, the position the user was pointing into.\n *\n * Returns `true` when the selection was corrected.\n *\n * Exported for direct unit testing; production reaches it through\n * {@link NoteShellCaretGuardPlugin}.\n *\n * Mutating (moves the selection): call inside `editor.update()` or a command handler.\n */\nexport function $guardCaretOutOfNoteShell(isPointerGesture = false) {\n const selection = $getSelection();\n if (!$isRangeSelection(selection))\n return false;\n if (!selection.isCollapsed())\n return $expandSelectionPastShell(selection.anchor, selection.focus);\n const note = $shellAt(selection.anchor);\n if (!note)\n return false;\n if (!isPointerGesture && $arrivedFromContentSide(note)) {\n const parent = note.getParent();\n if (!parent)\n return false;\n $placeCaretAtBoundary(parent, note.getIndexWithinParent());\n }\n else {\n $placeAtShellTrailingEdge(note);\n }\n return true;\n}\n/**\n * Push a RANGE's endpoints out of any shell they land in, away from the other endpoint, so the\n * shell ends up wholly inside the selection or wholly outside it.\n *\n * A range that stops partway through the shell is the other way a keystroke reaches it: replacing\n * such a selection edits the shell node the range clipped. Growing the range instead makes the\n * shell behave as the single unit it is drawn as — the same treatment `token` mode gives deletion.\n */\nfunction $expandSelectionPastShell(anchor, focus) {\n const anchorNote = $shellAt(anchor);\n const focusNote = $shellAt(focus);\n if (!anchorNote && !focusNote)\n return false;\n // Which endpoint leads is the range's own direction; each offending one moves to the shell edge\n // that is farther from the other, which is what grows rather than shrinks the selection.\n const anchorLeads = anchor.isBefore(focus);\n if (anchorNote)\n $movePointPastShell(anchor, anchorNote, anchorLeads);\n if (focusNote)\n $movePointPastShell(focus, focusNote, !anchorLeads);\n return true;\n}\n/** Move `point` to the shell's leading edge (`toStart`) or to the start of the note's content. */\nfunction $movePointPastShell(point, note, toStart) {\n const parent = note.getParent();\n if (toStart && parent)\n point.set(parent.getKey(), note.getIndexWithinParent(), \"element\");\n else\n point.set(note.getKey(), $contentStartIndex(note), \"element\");\n}\n/**\n * Keeps the caret out of an expanded note's shell — the opening glyph and caller a host governs\n * through its own UI rather than as text (`ViewOptions.isNoteShellEditable: false`; Paratext 10's\n * footnote editor has a dropdown for each, as does Paratext 9).\n *\n * Rendering those nodes in Lexical's `token` mode is what makes them atomic to the operations that\n * ASK a node whether it can be split, but it does not keep a caret from landing among their\n * characters, and a caret that does land there is not inert: an insertion with the caret strictly\n * inside a token node replaces the WHOLE node with the typed character. The measured results are a\n * caller replaced by the keystroke — which then leaks into the note's content on save — and, for\n * the opening glyph, a note destroyed outright, since a note that has lost its opener is unwrapped\n * as deletion damage. Both read on screen as an edit that was accepted and then quietly reverted.\n *\n * So the caret is corrected the moment it comes to rest there, in the same update, before anything\n * can be typed. It lands at the start of the note's content — where the note IS editable, and where\n * a `\\cat` category run belongs. The one exception is a KEYBOARD move coming back out of that\n * content: that one lands before the whole note, so the shell is crossed in a single hop rather\n * than trapping the caret against it.\n *\n * A pointer is held to a destination, never a direction. It is read from the pointer being DOWN\n * when the selection lands, which is the order a click delivers (`pointerdown`, then the selection\n * change, then `pointerup`) — the click event itself arrives too late to answer in the same update,\n * and correcting twice would let other selection listeners see the wrong position in between.\n *\n * Not gated on view options: the rule reads the shell's own node mode, so it is structurally a\n * no-op in the views that build an editable shell (the main editor's Markers view expands notes\n * precisely so the whole note can be edited as text).\n *\n * @returns Always `null`; this plugin renders no UI.\n */\nexport function NoteShellCaretGuardPlugin() {\n const [editor] = useLexicalComposerContext();\n const isPointerDown = useRef(false);\n useEffect(() => {\n const markDown = () => {\n isPointerDown.current = true;\n };\n const markUp = () => {\n isPointerDown.current = false;\n };\n // Listened for on the DOCUMENT in the capture phase, and released on `pointercancel` as well\n // as `pointerup`: a drag that starts in the editor can finish anywhere, and a pointer flag\n // that fails to clear would make every later keyboard move read as a click.\n return editor.registerRootListener((rootElement, prevRootElement) => {\n const previous = prevRootElement?.ownerDocument;\n previous?.removeEventListener(\"pointerdown\", markDown, true);\n previous?.removeEventListener(\"pointerup\", markUp, true);\n previous?.removeEventListener(\"pointercancel\", markUp, true);\n isPointerDown.current = false;\n const current = rootElement?.ownerDocument;\n current?.addEventListener(\"pointerdown\", markDown, true);\n current?.addEventListener(\"pointerup\", markUp, true);\n current?.addEventListener(\"pointercancel\", markUp, true);\n });\n }, [editor]);\n useEffect(() => {\n return editor.registerCommand(SELECTION_CHANGE_COMMAND, () => {\n // Command handlers already run inside an update, so the tag joins that commit rather than\n // opening a new one. Nothing here changes content, so tagging it costs nothing already\n // excluded from saved Scripture and collaborative traffic.\n if ($guardCaretOutOfNoteShell(isPointerDown.current))\n $addUpdateTag(CURSOR_CHANGE_TAG);\n return false;\n }, COMMAND_PRIORITY_EDITOR);\n }, [editor]);\n return null;\n}\n","import { $getUsjSelectionFromEditor } from \"./annotation/selection.utils\";\nimport { useLexicalComposerContext } from \"@lexical/react/LexicalComposerContext\";\nimport { COMMAND_PRIORITY_LOW, SELECTION_CHANGE_COMMAND } from \"lexical\";\nimport { useEffect } from \"react\";\nexport function OnSelectionChangePlugin({ onChange, }) {\n const [editor] = useLexicalComposerContext();\n useEffect(() => editor.registerCommand(SELECTION_CHANGE_COMMAND, () => {\n // Called as a bare `$` function, NOT via `editor.read()` or a committed-state read:\n // command listeners always run inside the active update, so `$getSelection()` sees the\n // CURRENT (pending) selection. `editor.read()` here would force-flush an in-flight\n // `editor.update()` mid-dispatch (`$commitPendingUpdates` runs unconditionally) — the\n // enabler of the frozen-commit crash class (see OnSelectionChangePlugin.test.tsx) —\n // while reading the last committed state instead would report every ordinary selection\n // change one interaction late, because Lexical dispatches SELECTION_CHANGE from inside\n // a not-yet-committed update on the normal DOM path too.\n const usjSelection = $getUsjSelectionFromEditor();\n onChange?.(usjSelection);\n return false;\n }, COMMAND_PRIORITY_LOW), [editor, onChange]);\n return null;\n}\n","import { $removeLeadingSpace, wasNodeCreated } from \"../../nodes/usj/node-react.utils\";\nimport { useLexicalComposerContext } from \"@lexical/react/LexicalComposerContext\";\nimport { $getNodeByKey } from \"lexical\";\nimport { useEffect } from \"react\";\nimport { $isParaNode, ParaNode } from \"shared\";\nexport function ParaNodePlugin() {\n const [editor] = useLexicalComposerContext();\n useParaNode(editor);\n return null;\n}\nfunction useParaNode(editor) {\n useEffect(() => {\n if (!editor.hasNodes([ParaNode])) {\n throw new Error(\"ParaNodePlugin: ParaNode not registered on editor!\");\n }\n // Update ParaNode to remove leading space and to not contain text if it is a 'b' marker.\n return editor.registerNodeTransform(ParaNode, (node) => $paraNodeTransform(node, editor));\n }, [editor]);\n}\n/**\n * Remove any leading space if the node was just created, e.g. by hitting the enter key in the\n * middle of a paragraph. Update ParaNode to not contain text if it is a 'b' marker (blank line).\n * However, if the 'b' ParaNode already contained text, it can be modified (backwards compatible\n * with PT9 & USFM v3.0).\n * @param node - ParaNode thats needs updating.\n */\nfunction $paraNodeTransform(node, editor) {\n if (wasNodeCreated(editor, node.getKey()))\n $removeLeadingSpace(node.getFirstChild());\n if (!$isParaNode(node) || node.getMarker() !== \"b\" || node.isEmpty())\n return;\n const prevEditorState = editor.getEditorState();\n const wasEmpty = prevEditorState.read(() => {\n const prevNode = $getNodeByKey(node.getKey());\n return $isParaNode(prevNode) && (prevNode?.isEmpty() ?? false);\n });\n if (!wasEmpty)\n return;\n node.clear();\n}\n","import { useLexicalComposerContext } from \"@lexical/react/LexicalComposerContext\";\nimport { $findMatchingParent, mergeRegister } from \"@lexical/utils\";\nimport { CAN_UNDO_COMMAND, COMMAND_PRIORITY_CRITICAL, CAN_REDO_COMMAND, SELECTION_CHANGE_COMMAND, $getSelection, $isRangeSelection, $isRootOrShadowRoot, } from \"lexical\";\nimport { useRef, useEffect, useState, useCallback } from \"react\";\nimport { $getCommonAncestorCompatible, $isBookNode, $isParaNode, $isImmutableChapterNode, $isVerseBlockNode, } from \"shared\";\nimport { $isReactNodeWithMarker } from \"../../nodes/usj/node-react.utils\";\n/** Plugin to track state and update parent component state */\nexport function StateChangePlugin({ onStateChange }) {\n const [editor] = useLexicalComposerContext();\n const [activeEditor, setActiveEditor] = useState(editor);\n const canUndoRef = useRef(false);\n const canRedoRef = useRef(false);\n const blockMarkerRef = useRef(undefined);\n const contextMarkerRef = useRef(undefined);\n const $updateState = useCallback(() => {\n const selection = $getSelection();\n let contextMarker;\n if ($isRangeSelection(selection)) {\n const anchorNode = selection.anchor.getNode();\n const focusNode = selection.focus.getNode();\n let node = anchorNode.getKey() === \"root\"\n ? anchorNode\n : $findMatchingParent(anchorNode, (e) => {\n const parent = e.getParent();\n return parent !== null && $isRootOrShadowRoot(parent);\n });\n if (node === null) {\n node = anchorNode.getTopLevelElementOrThrow();\n }\n // In the block verse layout the top-level element is the verse block, which carries no\n // marker of its own. The block marker the host wants is still the paragraph inside it that\n // holds the caret, so resolve through the block. `$isParaNode`, not `$isSomeParaNode`: an\n // implied paragraph has no marker to report, and the reporting guard below is the same\n // predicate - resolving to a node that guard then rejects would emit no state change at all.\n if ($isVerseBlockNode(node))\n node = $findMatchingParent(anchorNode, $isParaNode) ?? node;\n const nodeKey = node.getKey();\n const elementDOM = activeEditor.getElementByKey(nodeKey);\n const contextNode = $getCommonAncestorCompatible(anchorNode, focusNode);\n if (contextNode && $isReactNodeWithMarker(contextNode)) {\n contextMarker = contextNode.getMarker();\n }\n if (elementDOM !== null &&\n ($isParaNode(node) || $isBookNode(node) || $isImmutableChapterNode(node))) {\n blockMarkerRef.current = node.getMarker();\n contextMarkerRef.current = contextMarker;\n onStateChange?.({\n canUndo: canUndoRef.current,\n canRedo: canRedoRef.current,\n blockMarker: blockMarkerRef.current,\n contextMarker,\n });\n return;\n }\n }\n contextMarkerRef.current = contextMarker;\n }, [activeEditor, onStateChange]);\n useEffect(() => {\n return editor.registerCommand(SELECTION_CHANGE_COMMAND, (_payload, newEditor) => {\n $updateState();\n setActiveEditor(newEditor);\n return false;\n }, COMMAND_PRIORITY_CRITICAL);\n }, [editor, $updateState]);\n useEffect(() => {\n return mergeRegister(activeEditor.registerUpdateListener(({ editorState }) => {\n editorState.read(() => {\n $updateState();\n });\n }), activeEditor.registerCommand(CAN_UNDO_COMMAND, (payload) => {\n canUndoRef.current = payload;\n onStateChange?.({\n canUndo: canUndoRef.current,\n canRedo: canRedoRef.current,\n blockMarker: blockMarkerRef.current,\n contextMarker: contextMarkerRef.current,\n });\n return false;\n }, COMMAND_PRIORITY_CRITICAL), activeEditor.registerCommand(CAN_REDO_COMMAND, (payload) => {\n canRedoRef.current = payload;\n onStateChange?.({\n canUndo: canUndoRef.current,\n canRedo: canRedoRef.current,\n blockMarker: blockMarkerRef.current,\n contextMarker: contextMarkerRef.current,\n });\n return false;\n }, COMMAND_PRIORITY_CRITICAL));\n }, [$updateState, activeEditor, onStateChange]);\n return null;\n}\n","import { $isSomeVerseNode } from \"../../nodes/usj\";\nimport { $advancePastParaPrefixes } from \"./ParaMarkerPrefixCursorGuardPlugin\";\nimport { $findMatchingParent } from \"@lexical/utils\";\nimport { $createTextNode, $isElementNode, $isNodeSelection, $isRangeSelection, $isTextNode, } from \"lexical\";\nimport { $isSomeChapterNode, $isSomeParaNode } from \"shared\";\n/**\n * Maps a keydown to the structural edit it would cause, or undefined for non-editing keys.\n * Deliberately does NOT early-return on Alt/Ctrl/Meta for Backspace/Delete — Alt/Cmd+Backspace\n * (delete-word / delete-line) are destructive and must be classified as deletions.\n */\nexport function keyDownToIntent(event) {\n if (event.key === \"Enter\" && !event.shiftKey)\n return \"insertParagraph\";\n if (event.key === \"Backspace\")\n return \"deleteBackward\";\n if (event.key === \"Delete\")\n return \"deleteForward\";\n // Printable character with no command modifier. Alt allowed (special chars on some layouts).\n if (event.key.length === 1 && !event.ctrlKey && !event.metaKey)\n return \"insertText\";\n return undefined;\n}\n/** Returns the paragraph (ParaNode or ImpliedParaNode) that contains `node`, if any. */\nfunction $getParaAncestor(node) {\n if (!node)\n return undefined;\n if ($isSomeParaNode(node))\n return node;\n const para = $findMatchingParent(node, (n) => $isSomeParaNode(n));\n return para ?? undefined;\n}\n/** True when the selection covers more than one paragraph block. */\nexport function $selectionSpansBlockBoundary(selection) {\n if (!$isRangeSelection(selection))\n return false;\n const paraKeys = new Set();\n for (const node of selection.getNodes()) {\n const para = $getParaAncestor(node);\n if (para)\n paraKeys.add(para.getKey());\n }\n return paraKeys.size > 1;\n}\n/**\n * True when the selection includes any verse marker node. A collapsed caret at an element-type\n * point never counts — that is adjacency, not containment (see {@link $adjacentVerseMarker}).\n */\nexport function $selectionContainsVerseMarker(selection) {\n // A collapsed range covers no nodes, so a non-empty getNodes() for one is Lexical emulating \"the\n // descendant at the caret\" — that is adjacency, not containment, and must not block editing.\n // An element-type point is the only collapsed point that can reach that emulation: it is what\n // Lexical falls back to when there is no TextNode to host the caret, as in an empty verse (a verse\n // marker holds no text of its own, and ImmutableVerseNode is a childless decorator leaf, so an\n // empty verse has no text node at all).\n // Text-type points are excluded deliberately: the mutable VerseNode IS a TextNode, so a caret\n // inside one is genuine containment — editing there would rewrite the verse number, which must\n // stay blocked. Widening this to every collapsed caret reopens exactly that.\n // Adjacency for deletes is decided separately by $adjacentVerseMarker, which reads child indices\n // rather than relying on this emulation.\n if ($isRangeSelection(selection) &&\n selection.isCollapsed() &&\n selection.anchor.type === \"element\")\n return false;\n if (!$isRangeSelection(selection) && !$isNodeSelection(selection))\n return false;\n return selection.getNodes().some((n) => $isSomeVerseNode(n));\n}\n/** True when a collapsed caret sits at the very start of its paragraph. */\nexport function $caretAtParaStart(selection) {\n if (!$isRangeSelection(selection) || !selection.isCollapsed())\n return false;\n const { anchor } = selection;\n const node = anchor.getNode();\n const para = $getParaAncestor(node);\n if (!para)\n return false;\n if (anchor.offset !== 0)\n return false;\n // No content between the caret and the paragraph start.\n let current = node;\n while (current && current.getKey() !== para.getKey()) {\n if (current.getPreviousSibling())\n return false;\n current = current.getParent();\n }\n return true;\n}\n/** True when a collapsed caret sits at the very end of its paragraph. */\nexport function $caretAtParaEnd(selection) {\n if (!$isRangeSelection(selection) || !selection.isCollapsed())\n return false;\n const { anchor } = selection;\n const node = anchor.getNode();\n const para = $getParaAncestor(node);\n if (!para)\n return false;\n if ($isElementNode(node)) {\n if (anchor.offset !== node.getChildrenSize())\n return false;\n }\n else if (anchor.offset !== node.getTextContentSize()) {\n return false;\n }\n let current = node;\n while (current && current.getKey() !== para.getKey()) {\n if (current.getNextSibling())\n return false;\n current = current.getParent();\n }\n return true;\n}\n/**\n * True when the node immediately before/after a collapsed caret is a verse marker.\n *\n * @param selection - The current selection; only a collapsed RangeSelection can be adjacent.\n * @param direction - `\"backward\"` checks the node before the caret; `\"forward\"` the node after.\n * @returns Whether a verse marker sits immediately in that direction.\n */\nexport function $caretAdjacentToVerseMarker(selection, direction) {\n return !!$adjacentVerseMarker(selection, direction);\n}\n/**\n * The verse marker immediately before/after a collapsed caret, or undefined.\n * Node-returning sibling of `$caretAdjacentToVerseMarker`.\n *\n * @param selection - The current selection; only a collapsed RangeSelection can be adjacent.\n * @param direction - `\"backward\"` looks before the caret; `\"forward\"` looks after it.\n * @returns The adjacent verse marker node, or undefined when none is adjacent.\n */\nexport function $adjacentVerseMarker(selection, direction) {\n if (!$isRangeSelection(selection) || !selection.isCollapsed())\n return undefined;\n const { anchor } = selection;\n const node = anchor.getNode();\n if (anchor.type === \"element\" && $isElementNode(node)) {\n const children = node.getChildren();\n const idx = direction === \"backward\" ? anchor.offset - 1 : anchor.offset;\n if (idx < 0)\n return undefined;\n const candidate = children[idx];\n return $isSomeVerseNode(candidate) ? candidate : undefined;\n }\n if (direction === \"backward\") {\n if (anchor.offset !== 0)\n return undefined;\n const prev = node.getPreviousSibling();\n return $isSomeVerseNode(prev) ? prev : undefined;\n }\n if (anchor.offset !== node.getTextContentSize())\n return undefined;\n const next = node.getNextSibling();\n return $isSomeVerseNode(next) ? next : undefined;\n}\n/** True when the paragraph holding the caret has a preceding block sibling. */\nfunction $hasNeighborBlock(selection, direction) {\n if (!$isRangeSelection(selection))\n return false;\n const para = $getParaAncestor(selection.anchor.getNode());\n if (!para)\n return false;\n return !!(direction === \"backward\" ? para.getPreviousSibling() : para.getNextSibling());\n}\n/**\n * Rule 1 (all input vectors): block when the given selection spans a block boundary or\n * touches a verse marker. Used by paste/cut/drop/IME guards.\n */\nexport function $shouldBlockSelectionReplacement(selection) {\n return $selectionContainsVerseMarker(selection) || $selectionSpansBlockBoundary(selection);\n}\n/**\n * Full keyboard decision: combines Rule 1 with collapsed-caret structural rules.\n *\n * @param selection - The current selection to evaluate.\n * @param intent - The structural edit the keystroke would cause (see {@link keyDownToIntent}).\n * @returns Whether the edit should be blocked in a structure-protected document.\n */\nexport function $shouldBlockStructuralEdit(selection, intent) {\n if ($selectionContainsVerseMarker(selection) || $selectionSpansBlockBoundary(selection)) {\n return true;\n }\n if (!$isRangeSelection(selection) || !selection.isCollapsed())\n return false;\n switch (intent) {\n case \"insertParagraph\":\n return true;\n case \"deleteBackward\":\n return (($caretAtParaStart(selection) && $hasNeighborBlock(selection, \"backward\")) ||\n $caretAdjacentToVerseMarker(selection, \"backward\"));\n case \"deleteForward\":\n return (($caretAtParaEnd(selection) && $hasNeighborBlock(selection, \"forward\")) ||\n $caretAdjacentToVerseMarker(selection, \"forward\"));\n case \"insertText\":\n return false;\n }\n}\n/**\n * The marker/section a delete keystroke would remove at a structural boundary, or undefined\n * when the keystroke is ordinary editing. Mirror of `$shouldBlockStructuralEdit`'s boundary\n * conditions, but resolves the target node instead of returning a boolean.\n *\n * Backward: the adjacent verse, else the current paragraph (when a previous block exists).\n * Forward: the adjacent verse, else the NEXT paragraph (when a next block exists) — the block\n * whose marker the merge removes.\n */\nexport function $structuralDeleteTarget(selection, intent) {\n if (!$isRangeSelection(selection) || !selection.isCollapsed())\n return undefined;\n if (intent === \"deleteBackward\") {\n const verse = $adjacentVerseMarker(selection, \"backward\");\n if (verse)\n return { kind: \"verse\", node: verse };\n if ($caretAtParaStart(selection) && $hasNeighborBlock(selection, \"backward\")) {\n const para = $getParaAncestor(selection.anchor.getNode());\n if ($isSomeParaNode(para))\n return { kind: \"para\", node: para };\n }\n return undefined;\n }\n if (intent === \"deleteForward\") {\n const verse = $adjacentVerseMarker(selection, \"forward\");\n if (verse)\n return { kind: \"verse\", node: verse };\n if ($caretAtParaEnd(selection) && $hasNeighborBlock(selection, \"forward\")) {\n const para = $getParaAncestor(selection.anchor.getNode());\n const next = para?.getNextSibling();\n if ($isSomeParaNode(next))\n return { kind: \"para\", node: next };\n }\n return undefined;\n }\n return undefined;\n}\n/** True when the live selection still encodes the armed target. */\nexport function $isArmedSelection(selection, armed) {\n if (!selection)\n return false;\n if (armed.kind === \"verse\") {\n return $isNodeSelection(selection) && selection.has(armed.key);\n }\n if (armed.kind === \"selection\") {\n if (!$isRangeSelection(selection) || selection.isCollapsed())\n return false;\n if (!armed.anchor || !armed.focus)\n return false;\n const { anchor, focus } = selection;\n return (anchor.key === armed.anchor.key &&\n anchor.offset === armed.anchor.offset &&\n anchor.type === armed.anchor.type &&\n focus.key === armed.focus.key &&\n focus.offset === armed.focus.offset &&\n focus.type === armed.focus.type);\n }\n if (!$isRangeSelection(selection) || selection.isCollapsed())\n return false;\n const anchorPara = $getParaAncestor(selection.anchor.getNode());\n const focusPara = $getParaAncestor(selection.focus.getNode());\n return (!!anchorPara &&\n anchorPara.getKey() === armed.key &&\n !!focusPara &&\n focusPara.getKey() === armed.key);\n}\n/** Collapses the caret to the end of `node` (end of its text for a TextNode). */\nexport function $placeCaretAtEnd(node) {\n if ($isTextNode(node)) {\n const size = node.getTextContentSize();\n node.select(size, size);\n }\n else if ($isElementNode(node)) {\n node.selectEnd();\n }\n else {\n node.selectNext(0, 0);\n }\n}\n/**\n * Merge-into-previous semantics for a paragraph delete: move `para`'s children into its\n * previous paragraph sibling (which keeps ITS marker), remove `para` (dropping its marker),\n * and place the caret at the junction. Text is never lost. Only paragraphs merge into\n * paragraphs (ParaNode/ImpliedParaNode either way); any other previous sibling is a no-op.\n * Caller guarantees a previous element sibling exists (checked via `$hasNeighborBlock`).\n *\n * @param para - The paragraph whose marker is being removed by merging it into its predecessor.\n */\nexport function $mergeParaIntoPrevious(para) {\n const prev = para.getPreviousSibling();\n if (!$isSomeParaNode(prev))\n return;\n const junction = prev.getLastChild();\n const moved = para.getChildren();\n prev.append(...moved);\n para.remove();\n // When `prev` had content, the junction is the end of its last child; when it was empty the\n // junction is its start — so the caret lands where the two paragraphs joined, not at the end.\n if (junction)\n $placeCaretAtEnd(junction);\n else if (!$advancePastParaPrefixes(prev))\n prev.selectStart();\n}\n/**\n * Flattens one pasted/dropped node for a structure-protected document: verse and chapter\n * markers vanish (leaf markers), paragraph wrappers are removed but their children kept,\n * and all other (inline) nodes pass through unchanged.\n */\nfunction $flattenForProtectedStructure(node) {\n if ($isSomeVerseNode(node) || $isSomeChapterNode(node))\n return [];\n if ($isSomeParaNode(node))\n return node.getChildren().flatMap($flattenForProtectedStructure);\n return [node];\n}\n/**\n * Sanitizes a flat array of pasted/dropped nodes for a structure-protected document by\n * stripping structural markers (paragraph breaks, verse markers, chapter markers) while\n * preserving text, inline character formatting, and notes. A removed top-level paragraph\n * boundary is replaced with a single space so words from adjacent paragraphs do not fuse;\n * nested paragraphs are flattened without inserting a separator.\n *\n * @param nodes - The top-level nodes produced from the payload (e.g. via `$generateNodesFromDOM`).\n * @returns A new flat array of inline nodes safe to insert without altering document structure.\n */\nexport function $sanitizeNodesForProtectedStructure(nodes) {\n const result = [];\n for (const node of nodes) {\n const flattened = $flattenForProtectedStructure(node);\n if (flattened.length === 0)\n continue;\n if ($isSomeParaNode(node) && result.length > 0)\n result.push($createTextNode(\" \"));\n result.push(...flattened);\n }\n return result;\n}\n","/*! @license DOMPurify 3.4.13 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.4.13/LICENSE */\n\nfunction _arrayLikeToArray(r, a) {\n (null == a || a > r.length) && (a = r.length);\n for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e];\n return n;\n}\nfunction _arrayWithHoles(r) {\n if (Array.isArray(r)) return r;\n}\nfunction _iterableToArrayLimit(r, l) {\n var t = null == r ? null : \"undefined\" != typeof Symbol && r[Symbol.iterator] || r[\"@@iterator\"];\n if (null != t) {\n var e,\n n,\n i,\n u,\n a = [],\n f = true,\n o = false;\n try {\n if (i = (t = t.call(r)).next, 0 === l) ; else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0);\n } catch (r) {\n o = true, n = r;\n } finally {\n try {\n if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return;\n } finally {\n if (o) throw n;\n }\n }\n return a;\n }\n}\nfunction _nonIterableRest() {\n throw new TypeError(\"Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.\");\n}\nfunction _slicedToArray(r, e) {\n return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest();\n}\nfunction _unsupportedIterableToArray(r, a) {\n if (r) {\n if (\"string\" == typeof r) return _arrayLikeToArray(r, a);\n var t = {}.toString.call(r).slice(8, -1);\n return \"Object\" === t && r.constructor && (t = r.constructor.name), \"Map\" === t || \"Set\" === t ? Array.from(r) : \"Arguments\" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0;\n }\n}\n\nconst entries = Object.entries,\n setPrototypeOf = Object.setPrototypeOf,\n isFrozen = Object.isFrozen,\n getPrototypeOf = Object.getPrototypeOf,\n getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;\nlet freeze = Object.freeze,\n seal = Object.seal,\n create = Object.create; // eslint-disable-line import/no-mutable-exports\nlet _ref = typeof Reflect !== 'undefined' && Reflect,\n apply = _ref.apply,\n construct = _ref.construct;\nif (!freeze) {\n freeze = function freeze(x) {\n return x;\n };\n}\nif (!seal) {\n seal = function seal(x) {\n return x;\n };\n}\nif (!apply) {\n apply = function apply(func, thisArg) {\n for (var _len = arguments.length, args = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {\n args[_key - 2] = arguments[_key];\n }\n return func.apply(thisArg, args);\n };\n}\nif (!construct) {\n construct = function construct(Func) {\n for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {\n args[_key2 - 1] = arguments[_key2];\n }\n return new Func(...args);\n };\n}\nconst arrayForEach = unapply(Array.prototype.forEach);\nconst arrayLastIndexOf = unapply(Array.prototype.lastIndexOf);\nconst arrayPop = unapply(Array.prototype.pop);\nconst arrayPush = unapply(Array.prototype.push);\nconst arraySplice = unapply(Array.prototype.splice);\nconst arrayIsArray = Array.isArray;\nconst stringToLowerCase = unapply(String.prototype.toLowerCase);\nconst stringToString = unapply(String.prototype.toString);\nconst stringMatch = unapply(String.prototype.match);\nconst stringReplace = unapply(String.prototype.replace);\nconst stringIndexOf = unapply(String.prototype.indexOf);\nconst stringTrim = unapply(String.prototype.trim);\nconst numberToString = unapply(Number.prototype.toString);\nconst booleanToString = unapply(Boolean.prototype.toString);\nconst bigintToString = typeof BigInt === 'undefined' ? null : unapply(BigInt.prototype.toString);\nconst symbolToString = typeof Symbol === 'undefined' ? null : unapply(Symbol.prototype.toString);\nconst objectHasOwnProperty = unapply(Object.prototype.hasOwnProperty);\nconst objectToString = unapply(Object.prototype.toString);\nconst regExpTest = unapply(RegExp.prototype.test);\nconst typeErrorCreate = unconstruct(TypeError);\n/**\n * Creates a new function that calls the given function with a specified thisArg and arguments.\n *\n * @param func - The function to be wrapped and called.\n * @returns A new function that calls the given function with a specified thisArg and arguments.\n */\nfunction unapply(func) {\n return function (thisArg) {\n if (thisArg instanceof RegExp) {\n thisArg.lastIndex = 0;\n }\n for (var _len3 = arguments.length, args = new Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) {\n args[_key3 - 1] = arguments[_key3];\n }\n return apply(func, thisArg, args);\n };\n}\n/**\n * Creates a new function that constructs an instance of the given constructor function with the provided arguments.\n *\n * @param func - The constructor function to be wrapped and called.\n * @returns A new function that constructs an instance of the given constructor function with the provided arguments.\n */\nfunction unconstruct(Func) {\n return function () {\n for (var _len4 = arguments.length, args = new Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {\n args[_key4] = arguments[_key4];\n }\n return construct(Func, args);\n };\n}\n/**\n * Add properties to a lookup table\n *\n * @param set - The set to which elements will be added.\n * @param array - The array containing elements to be added to the set.\n * @param transformCaseFunc - An optional function to transform the case of each element before adding to the set.\n * @returns The modified set with added elements.\n */\nfunction addToSet(set, array) {\n let transformCaseFunc = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : stringToLowerCase;\n if (setPrototypeOf) {\n // Make 'in' and truthy checks like Boolean(set.constructor)\n // independent of any properties defined on Object.prototype.\n // Prevent prototype setters from intercepting set as a this value.\n setPrototypeOf(set, null);\n }\n if (!arrayIsArray(array)) {\n return set;\n }\n let l = array.length;\n while (l--) {\n let element = array[l];\n if (typeof element === 'string') {\n const lcElement = transformCaseFunc(element);\n if (lcElement !== element) {\n // Config presets (e.g. tags.js, attrs.js) are immutable.\n if (!isFrozen(array)) {\n array[l] = lcElement;\n }\n element = lcElement;\n }\n }\n set[element] = true;\n }\n return set;\n}\n/**\n * Clean up an array to harden against CSPP\n *\n * @param array - The array to be cleaned.\n * @returns The cleaned version of the array\n */\nfunction cleanArray(array) {\n for (let index = 0; index < array.length; index++) {\n const isPropertyExist = objectHasOwnProperty(array, index);\n if (!isPropertyExist) {\n array[index] = null;\n }\n }\n return array;\n}\n/**\n * Shallow clone an object\n *\n * @param object - The object to be cloned.\n * @returns A new object that copies the original.\n */\nfunction clone(object) {\n const newObject = create(null);\n for (const _ref2 of entries(object)) {\n var _ref3 = _slicedToArray(_ref2, 2);\n const property = _ref3[0];\n const value = _ref3[1];\n const isPropertyExist = objectHasOwnProperty(object, property);\n if (isPropertyExist) {\n if (arrayIsArray(value)) {\n newObject[property] = cleanArray(value);\n } else if (value && typeof value === 'object' && value.constructor === Object) {\n newObject[property] = clone(value);\n } else {\n newObject[property] = value;\n }\n }\n }\n return newObject;\n}\n/**\n * Convert non-node values into strings without depending on direct property access.\n *\n * @param value - The value to stringify.\n * @returns A string representation of the provided value.\n */\nfunction stringifyValue(value) {\n switch (typeof value) {\n case 'string':\n {\n return value;\n }\n case 'number':\n {\n return numberToString(value);\n }\n case 'boolean':\n {\n return booleanToString(value);\n }\n case 'bigint':\n {\n return bigintToString ? bigintToString(value) : '0';\n }\n case 'symbol':\n {\n return symbolToString ? symbolToString(value) : 'Symbol()';\n }\n case 'undefined':\n {\n return objectToString(value);\n }\n case 'function':\n case 'object':\n {\n if (value === null) {\n return objectToString(value);\n }\n const valueAsRecord = value;\n const valueToString = lookupGetter(valueAsRecord, 'toString');\n if (typeof valueToString === 'function') {\n const stringified = valueToString(valueAsRecord);\n return typeof stringified === 'string' ? stringified : objectToString(stringified);\n }\n return objectToString(value);\n }\n default:\n {\n return objectToString(value);\n }\n }\n}\n/**\n * This method automatically checks if the prop is function or getter and behaves accordingly.\n *\n * @param object - The object to look up the getter function in its prototype chain.\n * @param prop - The property name for which to find the getter function.\n * @returns The getter function found in the prototype chain or a fallback function.\n */\nfunction lookupGetter(object, prop) {\n while (object !== null) {\n const desc = getOwnPropertyDescriptor(object, prop);\n if (desc) {\n if (desc.get) {\n return unapply(desc.get);\n }\n if (typeof desc.value === 'function') {\n return unapply(desc.value);\n }\n }\n object = getPrototypeOf(object);\n }\n function fallbackValue() {\n return null;\n }\n return fallbackValue;\n}\nfunction isRegex(value) {\n try {\n regExpTest(value, '');\n return true;\n } catch (_unused) {\n return false;\n }\n}\n\nconst html$1 = freeze(['a', 'abbr', 'acronym', 'address', 'area', 'article', 'aside', 'audio', 'b', 'bdi', 'bdo', 'big', 'blink', 'blockquote', 'body', 'br', 'button', 'canvas', 'caption', 'center', 'cite', 'code', 'col', 'colgroup', 'content', 'data', 'datalist', 'dd', 'decorator', 'del', 'details', 'dfn', 'dialog', 'dir', 'div', 'dl', 'dt', 'element', 'em', 'fieldset', 'figcaption', 'figure', 'font', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hgroup', 'hr', 'html', 'i', 'img', 'input', 'ins', 'kbd', 'label', 'legend', 'li', 'main', 'map', 'mark', 'marquee', 'menu', 'menuitem', 'meter', 'nav', 'nobr', 'ol', 'optgroup', 'option', 'output', 'p', 'picture', 'pre', 'progress', 'q', 'rp', 'rt', 'ruby', 's', 'samp', 'search', 'section', 'select', 'shadow', 'slot', 'small', 'source', 'spacer', 'span', 'strike', 'strong', 'style', 'sub', 'summary', 'sup', 'table', 'tbody', 'td', 'template', 'textarea', 'tfoot', 'th', 'thead', 'time', 'tr', 'track', 'tt', 'u', 'ul', 'var', 'video', 'wbr']);\nconst svg$1 = freeze(['svg', 'a', 'altglyph', 'altglyphdef', 'altglyphitem', 'animatecolor', 'animatemotion', 'animatetransform', 'circle', 'clippath', 'defs', 'desc', 'ellipse', 'enterkeyhint', 'exportparts', 'filter', 'font', 'g', 'glyph', 'glyphref', 'hkern', 'image', 'inputmode', 'line', 'lineargradient', 'marker', 'mask', 'metadata', 'mpath', 'part', 'path', 'pattern', 'polygon', 'polyline', 'radialgradient', 'rect', 'stop', 'style', 'switch', 'symbol', 'text', 'textpath', 'title', 'tref', 'tspan', 'view', 'vkern']);\nconst svgFilters = freeze(['feBlend', 'feColorMatrix', 'feComponentTransfer', 'feComposite', 'feConvolveMatrix', 'feDiffuseLighting', 'feDisplacementMap', 'feDistantLight', 'feDropShadow', 'feFlood', 'feFuncA', 'feFuncB', 'feFuncG', 'feFuncR', 'feGaussianBlur', 'feImage', 'feMerge', 'feMergeNode', 'feMorphology', 'feOffset', 'fePointLight', 'feSpecularLighting', 'feSpotLight', 'feTile', 'feTurbulence']);\n// List of SVG elements that are disallowed by default.\n// We still need to know them so that we can do namespace\n// checks properly in case one wants to add them to\n// allow-list.\nconst svgDisallowed = freeze(['animate', 'color-profile', 'cursor', 'discard', 'font-face', 'font-face-format', 'font-face-name', 'font-face-src', 'font-face-uri', 'foreignobject', 'hatch', 'hatchpath', 'mesh', 'meshgradient', 'meshpatch', 'meshrow', 'missing-glyph', 'script', 'set', 'solidcolor', 'unknown', 'use']);\nconst mathMl$1 = freeze(['math', 'menclose', 'merror', 'mfenced', 'mfrac', 'mglyph', 'mi', 'mlabeledtr', 'mmultiscripts', 'mn', 'mo', 'mover', 'mpadded', 'mphantom', 'mroot', 'mrow', 'ms', 'mspace', 'msqrt', 'mstyle', 'msub', 'msup', 'msubsup', 'mtable', 'mtd', 'mtext', 'mtr', 'munder', 'munderover', 'mprescripts']);\n// Similarly to SVG, we want to know all MathML elements,\n// even those that we disallow by default.\nconst mathMlDisallowed = freeze(['maction', 'maligngroup', 'malignmark', 'mlongdiv', 'mscarries', 'mscarry', 'msgroup', 'mstack', 'msline', 'msrow', 'semantics', 'annotation', 'annotation-xml', 'mprescripts', 'none']);\nconst text = freeze(['#text']);\n\nconst html = freeze(['accept', 'action', 'align', 'alt', 'autocapitalize', 'autocomplete', 'autopictureinpicture', 'autoplay', 'background', 'bgcolor', 'border', 'capture', 'cellpadding', 'cellspacing', 'checked', 'cite', 'class', 'clear', 'color', 'cols', 'colspan', 'command', 'commandfor', 'controls', 'controlslist', 'coords', 'crossorigin', 'datetime', 'decoding', 'default', 'dir', 'disabled', 'disablepictureinpicture', 'disableremoteplayback', 'download', 'draggable', 'enctype', 'enterkeyhint', 'exportparts', 'face', 'for', 'headers', 'height', 'hidden', 'high', 'href', 'hreflang', 'id', 'inert', 'inputmode', 'integrity', 'ismap', 'kind', 'label', 'lang', 'list', 'loading', 'loop', 'low', 'max', 'maxlength', 'media', 'method', 'min', 'minlength', 'multiple', 'muted', 'name', 'nonce', 'noshade', 'novalidate', 'nowrap', 'open', 'optimum', 'part', 'pattern', 'placeholder', 'playsinline', 'popover', 'popovertarget', 'popovertargetaction', 'poster', 'preload', 'pubdate', 'radiogroup', 'readonly', 'rel', 'required', 'rev', 'reversed', 'role', 'rows', 'rowspan', 'spellcheck', 'scope', 'selected', 'shape', 'size', 'sizes', 'slot', 'span', 'srclang', 'start', 'src', 'srcset', 'step', 'style', 'summary', 'tabindex', 'title', 'translate', 'type', 'usemap', 'valign', 'value', 'width', 'wrap', 'xmlns']);\nconst svg = freeze(['accent-height', 'accumulate', 'additive', 'alignment-baseline', 'amplitude', 'ascent', 'attributename', 'attributetype', 'azimuth', 'basefrequency', 'baseline-shift', 'begin', 'bias', 'by', 'class', 'clip', 'clippathunits', 'clip-path', 'clip-rule', 'color', 'color-interpolation', 'color-interpolation-filters', 'color-profile', 'color-rendering', 'cx', 'cy', 'd', 'dx', 'dy', 'diffuseconstant', 'direction', 'display', 'divisor', 'dominant-baseline', 'dur', 'edgemode', 'elevation', 'end', 'exponent', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'filterunits', 'flood-color', 'flood-opacity', 'font-family', 'font-size', 'font-size-adjust', 'font-stretch', 'font-style', 'font-variant', 'font-weight', 'fx', 'fy', 'g1', 'g2', 'glyph-name', 'glyphref', 'gradientunits', 'gradienttransform', 'height', 'href', 'id', 'image-rendering', 'in', 'in2', 'intercept', 'k', 'k1', 'k2', 'k3', 'k4', 'kerning', 'keypoints', 'keysplines', 'keytimes', 'lang', 'lengthadjust', 'letter-spacing', 'kernelmatrix', 'kernelunitlength', 'lighting-color', 'local', 'marker-end', 'marker-mid', 'marker-start', 'markerheight', 'markerunits', 'markerwidth', 'maskcontentunits', 'maskunits', 'max', 'mask', 'mask-type', 'media', 'method', 'mode', 'min', 'name', 'numoctaves', 'offset', 'operator', 'opacity', 'order', 'orient', 'orientation', 'origin', 'overflow', 'paint-order', 'path', 'pathlength', 'patterncontentunits', 'patterntransform', 'patternunits', 'points', 'preservealpha', 'preserveaspectratio', 'primitiveunits', 'r', 'rx', 'ry', 'radius', 'refx', 'refy', 'repeatcount', 'repeatdur', 'restart', 'result', 'rotate', 'scale', 'seed', 'shape-rendering', 'slope', 'specularconstant', 'specularexponent', 'spreadmethod', 'startoffset', 'stddeviation', 'stitchtiles', 'stop-color', 'stop-opacity', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke', 'stroke-width', 'style', 'surfacescale', 'systemlanguage', 'tabindex', 'tablevalues', 'targetx', 'targety', 'transform', 'transform-origin', 'text-anchor', 'text-decoration', 'text-orientation', 'text-rendering', 'textlength', 'type', 'u1', 'u2', 'unicode', 'values', 'viewbox', 'visibility', 'version', 'vert-adv-y', 'vert-origin-x', 'vert-origin-y', 'width', 'word-spacing', 'wrap', 'writing-mode', 'xchannelselector', 'ychannelselector', 'x', 'x1', 'x2', 'xmlns', 'y', 'y1', 'y2', 'z', 'zoomandpan']);\nconst mathMl = freeze(['accent', 'accentunder', 'align', 'bevelled', 'close', 'columnalign', 'columnlines', 'columnspacing', 'columnspan', 'denomalign', 'depth', 'dir', 'display', 'displaystyle', 'encoding', 'fence', 'frame', 'height', 'href', 'id', 'largeop', 'length', 'linethickness', 'lquote', 'lspace', 'mathbackground', 'mathcolor', 'mathsize', 'mathvariant', 'maxsize', 'minsize', 'movablelimits', 'notation', 'numalign', 'open', 'rowalign', 'rowlines', 'rowspacing', 'rowspan', 'rspace', 'rquote', 'scriptlevel', 'scriptminsize', 'scriptsizemultiplier', 'selection', 'separator', 'separators', 'stretchy', 'subscriptshift', 'supscriptshift', 'symmetric', 'voffset', 'width', 'xmlns']);\nconst xml = freeze(['xlink:href', 'xml:id', 'xlink:title', 'xml:space', 'xmlns:xlink']);\n\nconst MUSTACHE_EXPR = seal(/{{[\\w\\W]*|^[\\w\\W]*}}/g);\nconst ERB_EXPR = seal(/<%[\\w\\W]*|^[\\w\\W]*%>/g);\nconst TMPLIT_EXPR = seal(/\\${[\\w\\W]*/g);\nconst DATA_ATTR = seal(/^data-[\\-\\w.\\u00B7-\\uFFFF]+$/); // eslint-disable-line no-useless-escape\nconst ARIA_ATTR = seal(/^aria-[\\-\\w]+$/); // eslint-disable-line no-useless-escape\nconst IS_ALLOWED_URI = seal(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|matrix):|[^a-z]|[a-z+.\\-]+(?:[^a-z+.\\-:]|$))/i // eslint-disable-line no-useless-escape\n);\nconst IS_SCRIPT_OR_DATA = seal(/^(?:\\w+script|data):/i);\nconst ATTR_WHITESPACE = seal(/[\\u0000-\\u0020\\u00A0\\u1680\\u180E\\u2000-\\u2029\\u205F\\u3000]/g // eslint-disable-line no-control-regex\n);\nconst DOCTYPE_NAME = seal(/^html$/i);\nconst CUSTOM_ELEMENT = seal(/^[a-z][.\\w]*(-[.\\w]+)+$/i);\n// Markup-significant character probes used by _sanitizeElements.\n// Shared module-level instances are safe despite the sticky /g flags:\n// unapply() resets lastIndex for RegExp receivers before every call.\nconst ELEMENT_MARKUP_PROBE = seal(/<[/\\w!]/g);\nconst COMMENT_MARKUP_PROBE = seal(/<[/\\w]/g);\nconst FALLBACK_TAG_CLOSE = seal(/<\\/no(script|embed|frames)/i);\nconst SELF_CLOSING_TAG = seal(/\\/>/i);\n\n// https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType\nconst NODE_TYPE = {\n element: 1,\n attribute: 2,\n text: 3,\n cdataSection: 4,\n entityReference: 5,\n // Deprecated\n entityNode: 6,\n // Deprecated\n processingInstruction: 7,\n comment: 8,\n document: 9,\n documentType: 10,\n documentFragment: 11,\n notation: 12 // Deprecated\n};\nconst getGlobal = function getGlobal() {\n return typeof window === 'undefined' ? null : window;\n};\n/**\n * Creates a no-op policy for internal use only.\n * Don't export this function outside this module!\n * @param trustedTypes The policy factory.\n * @param purifyHostElement The Script element used to load DOMPurify (to determine policy name suffix).\n * @return The policy created (or null, if Trusted Types\n * are not supported or creating the policy failed).\n */\nconst _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes, purifyHostElement) {\n if (typeof trustedTypes !== 'object' || typeof trustedTypes.createPolicy !== 'function') {\n return null;\n }\n // Allow the callers to control the unique policy name\n // by adding a data-tt-policy-suffix to the script element with the DOMPurify.\n // Policy creation with duplicate names throws in Trusted Types.\n let suffix = null;\n const ATTR_NAME = 'data-tt-policy-suffix';\n if (purifyHostElement && purifyHostElement.hasAttribute(ATTR_NAME)) {\n suffix = purifyHostElement.getAttribute(ATTR_NAME);\n }\n const policyName = 'dompurify' + (suffix ? '#' + suffix : '');\n try {\n return trustedTypes.createPolicy(policyName, {\n createHTML(html) {\n return html;\n },\n createScriptURL(scriptUrl) {\n return scriptUrl;\n }\n });\n } catch (_) {\n // Policy creation failed (most likely another DOMPurify script has\n // already run). Skip creating the policy, as this will only cause errors\n // if TT are enforced.\n console.warn('TrustedTypes policy ' + policyName + ' could not be created.');\n return null;\n }\n};\nconst _createHooksMap = function _createHooksMap() {\n return {\n afterSanitizeAttributes: [],\n afterSanitizeElements: [],\n afterSanitizeShadowDOM: [],\n beforeSanitizeAttributes: [],\n beforeSanitizeElements: [],\n beforeSanitizeShadowDOM: [],\n uponSanitizeAttribute: [],\n uponSanitizeElement: [],\n uponSanitizeShadowNode: []\n };\n};\n/**\n * Resolve a set-valued configuration option: a fresh set built from\n * cfg[key] when it is an own array property (seeded with a clone of\n * options.base when given, case-normalized via options.transform),\n * the fallback set otherwise.\n *\n * @param cfg the cloned, prototype-free configuration object\n * @param key the configuration property to read\n * @param fallback the set to use when the option is absent or not an array\n * @param options transform and optional base set to merge into\n * @returns the resolved set\n */\nconst _resolveSetOption = function _resolveSetOption(cfg, key, fallback, options) {\n return objectHasOwnProperty(cfg, key) && arrayIsArray(cfg[key]) ? addToSet(options.base ? clone(options.base) : {}, cfg[key], options.transform) : fallback;\n};\nfunction createDOMPurify() {\n let window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();\n const DOMPurify = root => createDOMPurify(root);\n DOMPurify.version = '3.4.13';\n DOMPurify.removed = [];\n if (!window || !window.document || window.document.nodeType !== NODE_TYPE.document || !window.Element) {\n // Not running in a browser, provide a factory function\n // so that you can pass your own Window\n DOMPurify.isSupported = false;\n return DOMPurify;\n }\n let document = window.document;\n const originalDocument = document;\n const currentScript = originalDocument.currentScript;\n window.DocumentFragment;\n const HTMLTemplateElement = window.HTMLTemplateElement,\n Node = window.Node,\n Element = window.Element,\n NodeFilter = window.NodeFilter,\n _window$NamedNodeMap = window.NamedNodeMap;\n _window$NamedNodeMap === void 0 ? window.NamedNodeMap || window.MozNamedAttrMap : _window$NamedNodeMap;\n window.HTMLFormElement;\n const DOMParser = window.DOMParser,\n trustedTypes = window.trustedTypes;\n const ElementPrototype = Element.prototype;\n const cloneNode = lookupGetter(ElementPrototype, 'cloneNode');\n const remove = lookupGetter(ElementPrototype, 'remove');\n const getNextSibling = lookupGetter(ElementPrototype, 'nextSibling');\n const getChildNodes = lookupGetter(ElementPrototype, 'childNodes');\n const getParentNode = lookupGetter(ElementPrototype, 'parentNode');\n const getShadowRoot = lookupGetter(ElementPrototype, 'shadowRoot');\n const getAttributes = lookupGetter(ElementPrototype, 'attributes');\n const getNodeType = Node && Node.prototype ? lookupGetter(Node.prototype, 'nodeType') : null;\n const getNodeName = Node && Node.prototype ? lookupGetter(Node.prototype, 'nodeName') : null;\n const getOwnerDocument = Node && Node.prototype ? lookupGetter(Node.prototype, 'ownerDocument') : null;\n // As per issue #47, the web-components registry is inherited by a\n // new document created via createHTMLDocument. As per the spec\n // (http://w3c.github.io/webcomponents/spec/custom/#creating-and-passing-registries)\n // a new empty registry is used when creating a template contents owner\n // document, so we use that as our parent document to ensure nothing\n // is inherited.\n if (typeof HTMLTemplateElement === 'function') {\n const template = document.createElement('template');\n if (template.content && template.content.ownerDocument) {\n document = template.content.ownerDocument;\n }\n }\n let trustedTypesPolicy;\n let emptyHTML = '';\n // The instance's own internal Trusted Types policy. Unlike a caller-supplied\n // `TRUSTED_TYPES_POLICY`, this is created at most once — Trusted Types throws\n // on duplicate policy names — and is the only policy allowed to persist\n // across configurations and survive `clearConfig()`.\n let defaultTrustedTypesPolicy;\n let defaultTrustedTypesPolicyResolved = false;\n // Tracks whether we are already inside a call to the configured Trusted Types\n // policy (`createHTML` or `createScriptURL`). If a supplied policy callback\n // itself calls `DOMPurify.sanitize` (the cause of #1422), `sanitize` would\n // re-enter the policy and recurse until the stack overflows. We detect that\n // re-entry and throw a clear, actionable error instead. The guard is shared\n // across both callbacks, because either one re-entering `sanitize` triggers\n // the same unbounded recursion.\n let IN_TRUSTED_TYPES_POLICY = 0;\n const _assertNotInTrustedTypesPolicy = function _assertNotInTrustedTypesPolicy() {\n if (IN_TRUSTED_TYPES_POLICY > 0) {\n throw typeErrorCreate('A configured TRUSTED_TYPES_POLICY callback (createHTML or ' + 'createScriptURL) must not call DOMPurify.sanitize, as that causes ' + 'infinite recursion. Do not pass a policy whose callbacks wrap ' + 'DOMPurify as TRUSTED_TYPES_POLICY; see the \"DOMPurify and Trusted ' + 'Types\" section of the README.');\n }\n };\n const _createTrustedHTML = function _createTrustedHTML(html) {\n _assertNotInTrustedTypesPolicy();\n IN_TRUSTED_TYPES_POLICY++;\n try {\n return trustedTypesPolicy.createHTML(html);\n } finally {\n IN_TRUSTED_TYPES_POLICY--;\n }\n };\n const _createTrustedScriptURL = function _createTrustedScriptURL(scriptUrl) {\n _assertNotInTrustedTypesPolicy();\n IN_TRUSTED_TYPES_POLICY++;\n try {\n return trustedTypesPolicy.createScriptURL(scriptUrl);\n } finally {\n IN_TRUSTED_TYPES_POLICY--;\n }\n };\n // Lazily resolve (and cache) the instance's internal default policy.\n // Resolution is attempted at most once: a successful `createPolicy` cannot be\n // repeated (Trusted Types throws on duplicate names), and a failed or\n // unsupported attempt must not be retried on every parse.\n const _getDefaultTrustedTypesPolicy = function _getDefaultTrustedTypesPolicy() {\n if (!defaultTrustedTypesPolicyResolved) {\n defaultTrustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, currentScript);\n defaultTrustedTypesPolicyResolved = true;\n }\n return defaultTrustedTypesPolicy;\n };\n const _document = document,\n implementation = _document.implementation,\n createNodeIterator = _document.createNodeIterator,\n createDocumentFragment = _document.createDocumentFragment,\n getElementsByTagName = _document.getElementsByTagName;\n const importNode = originalDocument.importNode;\n let hooks = _createHooksMap();\n /**\n * Expose whether this browser supports running the full DOMPurify.\n */\n DOMPurify.isSupported = typeof entries === 'function' && typeof getParentNode === 'function' && implementation && implementation.createHTMLDocument !== undefined;\n const MUSTACHE_EXPR$1 = MUSTACHE_EXPR,\n ERB_EXPR$1 = ERB_EXPR,\n TMPLIT_EXPR$1 = TMPLIT_EXPR,\n DATA_ATTR$1 = DATA_ATTR,\n ARIA_ATTR$1 = ARIA_ATTR,\n IS_SCRIPT_OR_DATA$1 = IS_SCRIPT_OR_DATA,\n ATTR_WHITESPACE$1 = ATTR_WHITESPACE,\n CUSTOM_ELEMENT$1 = CUSTOM_ELEMENT;\n let IS_ALLOWED_URI$1 = IS_ALLOWED_URI;\n /**\n * We consider the elements and attributes below to be safe. Ideally\n * don't add any new ones but feel free to remove unwanted ones.\n */\n /* allowed element names */\n let ALLOWED_TAGS = null;\n const DEFAULT_ALLOWED_TAGS = addToSet({}, [...html$1, ...svg$1, ...svgFilters, ...mathMl$1, ...text]);\n /* Allowed attribute names */\n let ALLOWED_ATTR = null;\n const DEFAULT_ALLOWED_ATTR = addToSet({}, [...html, ...svg, ...mathMl, ...xml]);\n /*\n * Configure how DOMPurify should handle custom elements and their attributes as well as customized built-in elements.\n * @property {RegExp|Function|null} tagNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any custom elements)\n * @property {RegExp|Function|null} attributeNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any attributes not on the allow list)\n * @property {boolean} allowCustomizedBuiltInElements allow custom elements derived from built-ins if they pass CUSTOM_ELEMENT_HANDLING.tagNameCheck. Default: `false`.\n */\n let CUSTOM_ELEMENT_HANDLING = Object.seal(create(null, {\n tagNameCheck: {\n writable: true,\n configurable: false,\n enumerable: true,\n value: null\n },\n attributeNameCheck: {\n writable: true,\n configurable: false,\n enumerable: true,\n value: null\n },\n allowCustomizedBuiltInElements: {\n writable: true,\n configurable: false,\n enumerable: true,\n value: false\n }\n }));\n /* Explicitly forbidden tags (overrides ALLOWED_TAGS/ADD_TAGS) */\n let FORBID_TAGS = null;\n /* Explicitly forbidden attributes (overrides ALLOWED_ATTR/ADD_ATTR) */\n let FORBID_ATTR = null;\n /* Config object to store ADD_TAGS/ADD_ATTR functions (when used as functions) */\n const EXTRA_ELEMENT_HANDLING = Object.seal(create(null, {\n tagCheck: {\n writable: true,\n configurable: false,\n enumerable: true,\n value: null\n },\n attributeCheck: {\n writable: true,\n configurable: false,\n enumerable: true,\n value: null\n }\n }));\n /* Decide if ARIA attributes are okay */\n let ALLOW_ARIA_ATTR = true;\n /* Decide if custom data attributes are okay */\n let ALLOW_DATA_ATTR = true;\n /* Decide if unknown protocols are okay */\n let ALLOW_UNKNOWN_PROTOCOLS = false;\n /* Decide if self-closing tags in attributes are allowed.\n * Usually removed due to a mXSS issue in jQuery 3.0 */\n let ALLOW_SELF_CLOSE_IN_ATTR = true;\n /* Output should be safe for common template engines.\n * This means, DOMPurify removes data attributes, mustaches and ERB\n */\n let SAFE_FOR_TEMPLATES = false;\n /* Output should be safe even for XML used within HTML and alike.\n * This means, DOMPurify removes comments when containing risky content.\n */\n let SAFE_FOR_XML = true;\n /* Decide if document with ... should be returned */\n let WHOLE_DOCUMENT = false;\n /* Track whether config is already set on this instance of DOMPurify. */\n let SET_CONFIG = false;\n /* Pristine allowlist bindings captured at setConfig() time. On the\n * persistent-config path sanitize() restores the sets from these before\n * the per-walk hook clone-guard, so a hook's in-call widening cannot\n * carry across calls. Null until setConfig() is called; reset by\n * clearConfig(). */\n let SET_CONFIG_ALLOWED_TAGS = null;\n let SET_CONFIG_ALLOWED_ATTR = null;\n /* Decide if all elements (e.g. style, script) must be children of\n * document.body. By default, browsers might move them to document.head */\n let FORCE_BODY = false;\n /* Decide if a DOM `HTMLBodyElement` should be returned, instead of a html\n * string (or a TrustedHTML object if Trusted Types are supported).\n * If `WHOLE_DOCUMENT` is enabled a `HTMLHtmlElement` will be returned instead\n */\n let RETURN_DOM = false;\n /* Decide if a DOM `DocumentFragment` should be returned, instead of a html\n * string (or a TrustedHTML object if Trusted Types are supported) */\n let RETURN_DOM_FRAGMENT = false;\n /* Try to return a Trusted Type object instead of a string, return a string in\n * case Trusted Types are not supported */\n let RETURN_TRUSTED_TYPE = false;\n /* Output should be free from DOM clobbering attacks?\n * This sanitizes markups named with colliding, clobberable built-in DOM APIs.\n */\n let SANITIZE_DOM = true;\n /* Achieve full DOM Clobbering protection by isolating the namespace of named\n * properties and JS variables, mitigating attacks that abuse the HTML/DOM spec rules.\n *\n * HTML/DOM spec rules that enable DOM Clobbering:\n * - Named Access on Window (§7.3.3)\n * - DOM Tree Accessors (§3.1.5)\n * - Form Element Parent-Child Relations (§4.10.3)\n * - Iframe srcdoc / Nested WindowProxies (§4.8.5)\n * - HTMLCollection (§4.2.10.2)\n *\n * Namespace isolation is implemented by prefixing `id` and `name` attributes\n * with a constant string, i.e., `user-content-`\n */\n let SANITIZE_NAMED_PROPS = false;\n const SANITIZE_NAMED_PROPS_PREFIX = 'user-content-';\n /* Keep element content when removing element? */\n let KEEP_CONTENT = true;\n /* If a `Node` is passed to sanitize(), then performs sanitization in-place instead\n * of importing it into a new Document and returning a sanitized copy */\n let IN_PLACE = false;\n /* Allow usage of profiles like html, svg and mathMl */\n let USE_PROFILES = {};\n /* Tags to ignore content of when KEEP_CONTENT is true */\n let FORBID_CONTENTS = null;\n const DEFAULT_FORBID_CONTENTS = addToSet({}, ['annotation-xml', 'audio', 'colgroup', 'desc', 'foreignobject', 'head', 'iframe', 'math', 'mi', 'mn', 'mo', 'ms', 'mtext', 'noembed', 'noframes', 'noscript', 'plaintext', 'script',\n // mirrors the selected