UNPKG

@portabletext/editor

Version:

Portable Text Editor made in React

1,070 lines (1,068 loc) 704 kB
import { jsx, jsxs, Fragment } from "react/jsx-runtime"; import { c } from "react/compiler-runtime"; import { useSelector, useActorRef } from "@xstate/react"; import React, { useRef, useEffect, useLayoutEffect, useState, createContext, useContext, useReducer, useCallback, memo, forwardRef, useMemo, Component, useSyncExternalStore, startTransition } from "react"; import { isSpan, isTextBlock, compileSchema } from "@portabletext/schema"; import { defineSchema } from "@portabletext/schema"; import { isKeyedSegment, getNode, getChildren, getAncestor, isObjectNode, getAncestors, getNodeChildren } from "./_chunks-es/get-ancestor.js"; import { getLeaf, getSpanNode, getText, getTextBlockNode, getUnionSchema, isLeaf, getFirstChild } from "./_chunks-es/get-first-child.js"; import { isTextBlockNode, isBlock, hasNode, getEnclosingBlock, isAncestorPath, getNodes, getSibling, isInline, parentPath, isSpanNode, getPathSubSchema, getBlock, resolveContainerAt, getParent, getEnclosingContainer } from "./_chunks-es/get-path-sub-schema.js"; import { rangeEdges, isEditableContainer, isBackwardRange, comparePoints, rangesOverlap, isAfterPoint, comparePaths, isSelectionCollapsed as isSelectionCollapsed$1, getFocusInlineObject, getFocusTextBlock, getFocusSpan, getFragment, isSelectionExpanded, getSelectionStartBlock, getSelectionEndBlock, isOverlappingSelection, getFocusBlock, getSelectedBlocks, isSelectingEntireBlocks, getSelectedValue, isActiveAnnotation, getActiveAnnotationsMarks, getActiveDecorators, getCaretWordSelection, getSelectionStartChild, getSelectionEndChild, getPreviousSpan, getNextSpan, getSelectionStartPoint, getSelectionEndPoint, getFocusBlockObject, getLastBlock, getFirstBlock, isAtTheEndOfBlock, isAtTheStartOfBlock, getMarkState, getFocusListBlock, getNextBlock, getPreviousBlock, getSelectedTextBlocks, getRootAcceptedTypes, isActiveDecorator, getFocusChild, getActiveAnnotations, isActiveListItem, isActiveStyle } from "./_chunks-es/selector.is-at-the-start-of-block.js"; import { isEqualSelectionPoints, blockOffsetToSpanSelectionPoint, getBlockKeyFromSelectionPoint, getBlockEndPoint, getBlockStartPoint, isSelectionCollapsed, getAncestorTextBlock, defaultKeyGenerator, parseBlocks, parseBlock, isListBlock, getSelectionStartPoint as getSelectionStartPoint$1, getSelectionEndPoint as getSelectionEndPoint$1, parseAnnotation, parseMarkDefs, parseSpan, parseInlineObject, isEqualPathSegments } from "./_chunks-es/util.slice-blocks.js"; import rawDebug from "debug"; import scrollIntoView from "scroll-into-view-if-needed"; import { createKeyboardShortcut, undo, redo, code, underline, italic as italic$1, bold as bold$1 } from "@portabletext/keyboard-shortcuts"; import { isEmptyTextBlock, getTextBlockText } from "./_chunks-es/util.is-empty-text-block.js"; import { setup, fromCallback, assign, and, assertEvent, enqueueActions, emit, not, raise as raise$1, createActor } from "xstate"; import { defineBehavior, forward, raise, effect } from "./behaviors/index.js"; import { htmlToPortableText } from "@portabletext/html"; import { toHTML } from "@portabletext/to-html"; import { markdownToPortableText, portableTextToMarkdown } from "@portabletext/markdown"; import { insert, setIfMissing, diffMatchPatch as diffMatchPatch$1, unset, set, applyAll } from "@portabletext/patches"; import { EditorContext as EditorContext$1 } from "./_chunks-es/use-editor.js"; import { useEditor } from "./_chunks-es/use-editor.js"; function isPath(value) { return Array.isArray(value) && (value.length === 0 || typeof value[0] == "number" || typeof value[0] == "string" || isKeyedSegment(value[0])); } const isObject = (value) => typeof value == "object" && value !== null; function isPoint(value) { return isObject(value) && typeof value.offset == "number" && isPath(value.path); } function isRange(value) { return isObject(value) && isPoint(value.anchor) && isPoint(value.focus); } const EDITOR_BRAND = /* @__PURE__ */ Symbol.for("slate-editor"); function isEditor(value) { return isObject(value) && value[EDITOR_BRAND] === !0; } function point(editor, at, options = {}) { const { edge = "start" } = options; if (isPath(at)) { let path2; const deepest = getLeaf(editor, at, { edge: edge === "end" ? "end" : "start" }); if (!deepest) throw new Error(`Cannot get the ${edge} point in the node at path [${at}] because it has no ${edge} text node.`); const { node, path: nodePath } = deepest; if (path2 = nodePath, !isSpan({ schema: editor.schema }, node) && !isTextBlockNode({ schema: editor.schema }, node) && !isEditor(node)) return { path: path2, offset: 0 }; if (!isSpan({ schema: editor.schema }, node)) throw new Error(`Cannot get the ${edge} point in the node at path [${at}] because it has no ${edge} text node.`); return { path: path2, offset: edge === "end" ? node.text.length : 0 }; } if (isRange(at)) { const [start2, end2] = rangeEdges(at, editor); return edge === "start" ? start2 : end2; } return at; } function end(editor, at) { return point(editor, at, { edge: "end" }); } function start(editor, at) { return point(editor, at, { edge: "start" }); } function pathEquals(path2, another) { if (path2.length !== another.length) return !1; for (let i = 0; i < path2.length; i++) { const segment = path2[i], otherSegment = another[i]; if (isKeyedSegment(segment) && isKeyedSegment(otherSegment)) { if (segment._key !== otherSegment._key) return !1; } else if (segment !== otherSegment) return !1; } return !0; } function pointEquals(point2, another) { return point2.offset === another.offset && pathEquals(point2.path, another.path); } function resolveSelection(editor, selection) { if (!selection) return null; if (isEqualSelectionPoints(selection.anchor, selection.focus)) { const anchorPoint2 = resolveSelectionPoint(editor, selection.anchor, selection.backward ? "backward" : "forward"); return anchorPoint2 ? { anchor: anchorPoint2, focus: anchorPoint2 } : null; } const anchorPoint = resolveSelectionPoint(editor, selection.anchor, selection.backward ? "forward" : "backward"), focusPoint = resolveSelectionPoint(editor, selection.focus, selection.backward ? "backward" : "forward"); return !anchorPoint || !focusPoint ? null : { anchor: anchorPoint, focus: focusPoint }; } function resolveSelectionPoint(editor, selectionPoint, direction) { const snapshot = { context: { schema: editor.schema, containers: editor.publicContainers, value: editor.children }, blockIndexMap: editor.blockIndexMap }, entry = getNode(snapshot, selectionPoint.path); if (entry) { if (getChildren(snapshot, entry.path).length === 0) return { path: entry.path, offset: isSpan({ schema: editor.schema }, entry.node) ? Math.min(entry.node.text.length, selectionPoint.offset) : 0 }; const isBlockLevelPath = isBlock(snapshot, selectionPoint.path); if (isTextBlock({ schema: editor.schema }, entry.node) && isBlockLevelPath) { const spanPoint = blockOffsetToSpanSelectionPoint({ snapshot, blockOffset: { path: entry.path, offset: selectionPoint.offset }, direction }); if (spanPoint) return spanPoint; } const leaf2 = getLeaf(snapshot, entry.path, { edge: direction === "forward" ? "start" : "end" }); return leaf2 ? { path: leaf2.path, offset: 0 } : { path: entry.path, offset: 0 }; } const blockKey = getBlockKeyFromSelectionPoint(selectionPoint); if (!blockKey) return; const blockEntry = getNode(snapshot, [{ _key: blockKey }]); if (!blockEntry) return; const leaf = getLeaf(snapshot, blockEntry.path, { edge: "start" }); return leaf ? { path: leaf.path, offset: 0 } : { path: blockEntry.path, offset: 0 }; } function applySelect(editor, target) { const range2 = toRange(editor, target), { selection } = editor; if (selection) { const oldProps = {}, newProps = {}; range2.anchor != null && !pointEquals(range2.anchor, selection.anchor) && (oldProps.anchor = selection.anchor, newProps.anchor = range2.anchor), range2.focus != null && !pointEquals(range2.focus, selection.focus) && (oldProps.focus = selection.focus, newProps.focus = range2.focus), Object.keys(oldProps).length > 0 && editor.apply({ type: "set_selection", properties: oldProps, newProperties: newProps }); } else editor.apply({ type: "set_selection", properties: null, newProperties: range2 }); } function applyDeselect(editor) { const { selection } = editor; selection && editor.apply({ type: "set_selection", properties: selection, newProperties: null }); } function toRange(editor, target) { if (isRange(target)) return target; if (isPoint(target)) return { anchor: target, focus: target }; const start$1 = start(editor, target), end$1 = end(editor, target); return { anchor: start$1, focus: end$1 }; } const rootName = "pte:"; function createDebugger(name) { const namespace = `${rootName}${name}`; return rawDebug && rawDebug.enabled(namespace) ? rawDebug(namespace) : rawDebug(rootName); } const debug = { behaviors: createDebugger("behaviors"), history: createDebugger("history"), mutation: createDebugger("mutation"), normalization: createDebugger("normalization"), operation: createDebugger("operation"), selection: createDebugger("selection"), setup: createDebugger("setup"), state: createDebugger("state"), syncValue: createDebugger("sync:value"), syncPatch: createDebugger("sync:patch") }; function serializePath(path2) { return path2.reduce((result, segment, index) => isKeyedSegment(segment) ? `${result}[_key=="${segment._key}"]` : `${result}${index === 0 ? "" : "."}${segment}`, ""); } function getDomNode(editor, path2) { const editorElement = editor.domElement; if (!editorElement) return; if (path2.length === 0) return editorElement; const serializedPath = serializePath(path2), selector = `[data-pt-path="${CSS.escape(serializedPath)}"]`, blockSegment = path2[0]; if (isKeyedSegment(blockSegment)) { const blockIndex = editor.blockIndexMap.get(blockSegment._key); if (blockIndex !== void 0) { const blockNode = editorElement.children[blockIndex]; if (blockNode instanceof HTMLElement) { if (blockNode.matches(selector)) return blockNode.closest("[data-pt-editor]") !== editorElement ? void 0 : blockNode; const domNode2 = blockNode.querySelector(selector); if (domNode2 instanceof HTMLElement) return domNode2.closest("[data-pt-editor]") !== editorElement ? void 0 : domNode2; } } } const domNode = editorElement.querySelector(selector); if (!(!(domNode instanceof HTMLElement) || domNode.closest("[data-pt-editor]") !== editorElement)) return domNode; } const KEYED_SEGMENT_PATTERN = /\[_key=="(.+?)"\]/; function deserializePath(serializedPath) { const path2 = []; let remaining = serializedPath; for (; remaining.length > 0; ) { remaining.startsWith(".") && (remaining = remaining.slice(1)); const keyMatch = remaining.match(KEYED_SEGMENT_PATTERN); if (keyMatch?.[1] && remaining.startsWith("[")) { path2.push({ _key: keyMatch[1] }), remaining = remaining.slice(keyMatch[0].length); continue; } const nextBracket = remaining.indexOf("["), nextDot = remaining.indexOf("."); let end2; nextBracket === -1 && nextDot === -1 ? end2 = remaining.length : nextBracket === -1 ? end2 = nextDot : nextDot === -1 ? end2 = nextBracket : end2 = Math.min(nextBracket, nextDot); const fieldName = remaining.slice(0, end2); fieldName && path2.push(fieldName), remaining = remaining.slice(end2); } return path2; } function safeStringify(value, space) { try { return JSON.stringify(value, null, space); } catch (error) { return console.error(error), "JSON.stringify failed"; } } function safeParse(text) { try { return JSON.parse(text); } catch (error) { return console.error(error), "JSON.parse failed"; } } function getAncestorObjectNode(snapshot, path2) { const result = getAncestor(snapshot, path2, (node) => isObjectNode({ schema: snapshot.context.schema }, node)); if (result && isObjectNode({ schema: snapshot.context.schema }, result.node)) return { node: result.node, path: result.path }; } function commonPath(path2, another) { const common = []; for (let i = 0; i < path2.length && i < another.length; i++) { const segment = path2.at(i), otherSegment = another.at(i); if (segment === void 0 || otherSegment === void 0) break; if (isKeyedSegment(segment) && isKeyedSegment(otherSegment)) { if (segment._key !== otherSegment._key) break; common.push(segment); } else if (segment === otherSegment) common.push(segment); else break; } return common; } function rangeEnd(range2, root) { const [, end2] = rangeEdges(range2, root); return end2; } function rangeStart(range2, root) { const [start2] = rangeEdges(range2, root); return start2; } function path(editor, at, options = {}) { const { depth, edge } = options; if (isPath(at) && (edge === "start" || edge === "end")) { const leaf = getLeaf(editor, at, { edge }); leaf && (at = leaf.path); } return isRange(at) && (edge === "start" ? at = rangeStart(at, editor) : edge === "end" ? at = rangeEnd(at, editor) : at = commonPath(at.anchor.path, at.focus.path)), isPoint(at) && (at = at.path), depth != null && (at = sliceToNodeDepth(at, depth)), at; } function sliceToNodeDepth(nodePath, depth) { let nodeCount = 0; for (let i = 0; i < nodePath.length; i++) { const segment = nodePath[i]; if ((isKeyedSegment(segment) || typeof segment == "number") && (nodeCount++, nodeCount === depth)) return nodePath.slice(0, i + 1); } return nodePath; } function isVoidNode(snapshot, node, path2) { return isObjectNode({ schema: snapshot.context.schema }, node) && !isEditableContainer(snapshot, node, path2); } function isCollapsedRange(range2) { const { anchor, focus } = range2; return pointEquals(anchor, focus); } const IS_IOS = typeof navigator < "u" && typeof window < "u" && /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream, IS_ANDROID = typeof navigator < "u" && /Android/.test(navigator.userAgent), IS_FIREFOX = typeof navigator < "u" && /^(?!.*Seamonkey)(?=.*Firefox).*/i.test(navigator.userAgent), IS_WEBKIT = typeof navigator < "u" && /AppleWebKit(?!.*Chrome)/i.test(navigator.userAgent), IS_EDGE_LEGACY = typeof navigator < "u" && /Edge?\/(?:[0-6][0-9]|[0-7][0-8])(?:\.)/i.test(navigator.userAgent), IS_CHROME = typeof navigator < "u" && /Chrome/i.test(navigator.userAgent), IS_CHROME_LEGACY = typeof navigator < "u" && /Chrome?\/(?:[0-7][0-5]|[0-6][0-9])(?:\.)/i.test(navigator.userAgent), IS_ANDROID_CHROME_LEGACY = IS_ANDROID && typeof navigator < "u" && /Chrome?\/(?:[0-5]?\d)(?:\.)/i.test(navigator.userAgent), IS_FIREFOX_LEGACY = typeof navigator < "u" && /^(?!.*Seamonkey)(?=.*Firefox\/(?:[0-7][0-9]|[0-8][0-6])(?:\.)).*/i.test(navigator.userAgent), IS_UC_MOBILE = typeof navigator < "u" && /.*UCBrowser/.test(navigator.userAgent), IS_WECHATBROWSER = typeof navigator < "u" && /.*Wechat/.test(navigator.userAgent) && !/.*MacWechat/.test(navigator.userAgent) && // avoid lookbehind (buggy in safari < 16.4) (!IS_CHROME || IS_CHROME_LEGACY), CAN_USE_DOM = typeof window < "u" && typeof window.document < "u" && typeof window.document.createElement < "u", HAS_BEFORE_INPUT_SUPPORT = (!IS_CHROME_LEGACY || !IS_ANDROID_CHROME_LEGACY) && !IS_EDGE_LEGACY && // globalThis is undefined in older browsers typeof globalThis < "u" && globalThis.InputEvent && typeof globalThis.InputEvent.prototype.getTargetRanges == "function", DOMEditor = { blur: (editor) => { const el = getDomNode(editor, []), root = DOMEditor.findDocumentOrShadowRoot(editor); editor.focused = !1, root.activeElement === el && el.blur(); }, findDocumentOrShadowRoot: (editor) => { const el = getDomNode(editor, []); if (!el) throw new Error("Cannot resolve a DOM node: editor is not mounted"); const root = el.getRootNode(); return root instanceof Document || root instanceof ShadowRoot ? root : el.ownerDocument; }, focus: (editor, options = { retries: 5 }) => { if (editor.focused || !editor.domElement) return; if (options.retries <= 0) throw new Error("Could not set focus, editor seems stuck with pending operations"); if (editor.operations.length > 0) { setTimeout(() => { DOMEditor.focus(editor, { retries: options.retries - 1 }); }, 10); return; } const el = getDomNode(editor, []); if (!el) throw new Error("Cannot resolve a DOM node: editor is not mounted"); const root = DOMEditor.findDocumentOrShadowRoot(editor); if (root.activeElement !== el) { if (editor.selection && root instanceof Document) { const domSelection = getSelection(root), domRange = DOMEditor.toDOMRange(editor, editor.selection); domSelection?.removeAllRanges(), domSelection?.addRange(domRange); } if (editor.selection || editor.select(start(editor, [])), editor.focused = !0, el.focus({ preventScroll: !0 }), editor.selection && root instanceof Document) { const domSelection = getSelection(root), domRange = DOMEditor.toDOMRange(editor, editor.selection); domSelection?.removeAllRanges(), domSelection?.addRange(domRange); } } }, getWindow: (editor) => { const window2 = editor.domWindow; if (!window2) throw new Error("Unable to find a host window element for this editor"); return window2; }, hasDOMNode: (editor, target, options = {}) => { const { editable = !1 } = options, editorEl = getDomNode(editor, []); if (!editorEl) return !1; let targetEl = null; try { targetEl = isDOMElement(target) ? target : target.parentElement; } catch (err) { if (err instanceof Error && !err.message.includes('Permission denied to access property "nodeType"')) throw err; } return targetEl ? closestShadowAware(targetEl, "[data-pt-editor]") === editorEl && (!editable || targetEl.isContentEditable ? !0 : typeof targetEl.isContentEditable == "boolean" && // isContentEditable exists only on HTMLElement, and on other nodes it will be undefined // this is the core logic that lets you know you got the right editor.selection instead of null when editor is contenteditable="false"(readOnly) closestShadowAware(targetEl, '[contenteditable="false"]') === editorEl || !!targetEl.getAttribute("data-pt-zero-width")) : !1; }, hasEditableTarget: (editor, target) => isDOMNode(target) && DOMEditor.hasDOMNode(editor, target, { editable: !0 }), hasRange: (editor, range2) => { const { anchor, focus } = range2; return hasNode(editor, anchor.path) && hasNode(editor, focus.path); }, hasSelectableTarget: (editor, target) => DOMEditor.hasEditableTarget(editor, target) || DOMEditor.isTargetInsideNonReadonlyVoid(editor, target), hasTarget: (editor, target) => isDOMNode(target) && DOMEditor.hasDOMNode(editor, target), isTargetInsideNonReadonlyVoid: (editor, target) => editor.readOnly || !DOMEditor.hasTarget(editor, target) ? !1 : !!(isDOMElement(target) ? target : target.parentElement)?.closest('[data-pt-block="object"], [data-pt-inline="object"]'), toDOMPoint: (editor, point2) => { const nodeEntry = getNode(editor, point2.path), el = getDomNode(editor, point2.path); if (!el) throw new Error(`Cannot resolve a DOM node from path: ${point2.path}`); let domPoint; if (nodeEntry && isVoidNode(editor, nodeEntry.node, point2.path)) { const spacer = el.querySelector("[data-pt-zero-width]"); if (spacer) { const domText = spacer.childNodes[0]; if (domText) return [domText, 0]; } const parentEl = el.parentNode; if (parentEl) { const index = Array.from(parentEl.childNodes).indexOf(el); if (index !== -1) return [parentEl, index]; } return [el, 0]; } const pointPath = path(editor, point2), pointEntry = getNode(editor, pointPath), pointObjectNode = pointEntry && isVoidNode(editor, pointEntry.node, pointPath) ? pointEntry : getAncestorObjectNode(editor, point2.path); pointObjectNode && isVoidNode(editor, pointObjectNode.node, pointObjectNode.path) && (point2 = { path: point2.path, offset: 0 }); const texts = Array.from(el.querySelectorAll("[data-pt-text], [data-pt-zero-width]")); let start2 = 0; for (let i = 0; i < texts.length; i++) { const text = texts[i], domNode = text.childNodes[0]; if (domNode == null || domNode.textContent == null) continue; const { length } = domNode.textContent, trueLength = text.hasAttribute("data-pt-zero-width") ? 0 : length, end2 = start2 + trueLength; if (point2.offset <= end2) { const offset = Math.min(length, Math.max(0, point2.offset - start2)); domPoint = [domNode, offset]; break; } start2 = end2; } if (!domPoint) throw new Error(`Cannot resolve a DOM point from Slate point: ${safeStringify(point2)}`); return domPoint; }, toDOMRange: (editor, range2) => { const { anchor, focus } = range2, isBackward = isBackwardRange(range2, editor), domAnchor = DOMEditor.toDOMPoint(editor, anchor), domFocus = isCollapsedRange(range2) ? domAnchor : DOMEditor.toDOMPoint(editor, focus), domRange = DOMEditor.getWindow(editor).document.createRange(), [startNode, startOffset] = isBackward ? domFocus : domAnchor, [endNode, endOffset] = isBackward ? domAnchor : domFocus, isStartAtZeroWidth = !!(isDOMElement(startNode) ? startNode : startNode.parentElement).getAttribute("data-pt-zero-width"), isEndAtZeroWidth = !!(isDOMElement(endNode) ? endNode : endNode.parentElement).getAttribute("data-pt-zero-width"); return domRange.setStart(startNode, isStartAtZeroWidth ? 1 : startOffset), domRange.setEnd(endNode, isEndAtZeroWidth ? 1 : endOffset), domRange; }, toSlatePoint: (editor, domPoint, options) => { const { exactMatch, suppressThrow, searchDirection } = options, [nearestNode, nearestOffset] = exactMatch ? domPoint : normalizeDOMPoint(domPoint), parentNode = nearestNode.parentNode; let textNode = null, offset = 0; if (parentNode) { const editorEl = getDomNode(editor, []); if (!editorEl) throw new Error("Cannot resolve a DOM node: editor is not mounted"); const potentialVoidNode = parentNode.closest('[data-pt-block="object"], [data-pt-inline="object"]'), voidNode = potentialVoidNode && containsShadowAware(editorEl, potentialVoidNode) ? potentialVoidNode : null, potentialNonEditableNode = parentNode.closest('[contenteditable="false"]'), nonEditableNode = potentialNonEditableNode && containsShadowAware(editorEl, potentialNonEditableNode) ? potentialNonEditableNode : null; let leafNode = parentNode.closest("[data-pt-marks]"), domNode = null; if (leafNode) { if (textNode = leafNode.closest('[data-pt-inline="span"]'), textNode) { const range2 = DOMEditor.getWindow(editor).document.createRange(); range2.setStart(textNode, 0), range2.setEnd(nearestNode, nearestOffset); const contents = range2.cloneContents(); [...Array.prototype.slice.call(contents.querySelectorAll("[data-pt-zero-width]")), ...Array.prototype.slice.call(contents.querySelectorAll("[contenteditable=false]"))].forEach((el) => { if (IS_ANDROID && !exactMatch && el.hasAttribute("data-pt-zero-width") && el.textContent.length > 0 && el.textContext !== "\uFEFF") { el.textContent.startsWith("\uFEFF") && (el.textContent = el.textContent.slice(1)); return; } el.parentNode.removeChild(el); }), offset = contents.textContent.length, domNode = textNode; } } else if (voidNode) { const leafNodes = voidNode.querySelectorAll("[data-pt-marks]"); for (let index = 0; index < leafNodes.length; index++) { const current = leafNodes[index]; if (DOMEditor.hasDOMNode(editor, current)) { leafNode = current; break; } } leafNode ? (textNode = leafNode.closest('[data-pt-inline="span"]'), domNode = leafNode, offset = domNode.textContent.length, domNode.querySelectorAll("[data-pt-zero-width]").forEach((el) => { offset -= el.textContent.length; })) : offset = 1; } else if (nonEditableNode) { const getLeafNodes = (node) => node ? node.querySelectorAll( // Exclude leaf nodes in nested editors "[data-pt-marks]:not(:scope [data-pt-editor] [data-pt-marks])" ) : [], elementNode = nonEditableNode.closest("[data-pt-block]"); (searchDirection === "backward" || !searchDirection) && (leafNode = [...getLeafNodes(elementNode?.previousElementSibling), ...getLeafNodes(elementNode)].findLast((leaf) => isBefore(nonEditableNode, leaf)) ?? null), (searchDirection === "forward" || !searchDirection) && (leafNode = [...getLeafNodes(elementNode), ...getLeafNodes(elementNode?.nextElementSibling)].find((leaf) => isAfter(nonEditableNode, leaf)) ?? null), leafNode && (textNode = leafNode.closest('[data-pt-inline="span"]'), domNode = leafNode, searchDirection === "forward" ? offset = 0 : (offset = domNode.textContent.length, domNode.querySelectorAll("[data-pt-zero-width]").forEach((el) => { offset -= el.textContent.length; }))); } domNode && offset === domNode.textContent.length && // COMPAT: Android IMEs might remove the zero width space while composing, // and we don't add it for line-breaks. IS_ANDROID && domNode.hasAttribute("data-pt-zero-width") && !domNode.hasAttribute("data-pt-line-break") && domNode.textContent?.startsWith("\uFEFF") && // COMPAT: If the parent node is a Slate zero-width space, editor is // because the text node should have no characters. However, during IME // composition the ASCII characters will be prepended to the zero-width // space, so subtract 1 from the offset to account for the zero-width // space character. (parentNode.hasAttribute("data-pt-zero-width") || // COMPAT: In Firefox, `range.cloneContents()` returns an extra trailing '\n' // when the document ends with a new-line character. This results in the offset // length being off by one, so we need to subtract one to account for this. IS_FIREFOX && domNode.textContent?.endsWith(` `)) && offset--; } if (IS_ANDROID && !textNode && !exactMatch) { const node = parentNode.hasAttribute("data-pt-block") || parentNode.getAttribute("data-pt-inline") === "span" ? parentNode : parentNode.closest('[data-pt-block], [data-pt-inline="span"]'); if (node && DOMEditor.hasDOMNode(editor, node, { editable: !0 })) { const nodePath = getDomNodePath(node); if (!nodePath) { if (suppressThrow) return null; throw new Error(`Cannot resolve a Slate point from DOM point: ${domPoint}`); } let { path: path22, offset: offset2 } = start(editor, nodePath); return node.querySelector("[data-pt-marks]") || (offset2 = nearestOffset), { path: path22, offset: offset2 }; } } if (!textNode && parentNode) { if (nearestNode instanceof HTMLElement && nearestNode.getAttribute("data-pt-block") === "container") { const childEl = nearestNode.childNodes[nearestOffset]; if (childEl instanceof HTMLElement && DOMEditor.hasDOMNode(editor, childEl)) { const voidEl = childEl.closest('[data-pt-block="object"], [data-pt-inline="object"]'); if (voidEl) { const path22 = getDomNodePath(voidEl); if (path22) return { path: path22, offset: 0 }; } } } const elementNode = parentNode.closest("[data-pt-block]") ?? (parentNode.hasAttribute("data-pt-block") ? parentNode : null); if (elementNode && DOMEditor.hasDOMNode(editor, elementNode)) { const voidEl = elementNode.closest('[data-pt-block="object"], [data-pt-inline="object"]'); if (voidEl) { const path22 = getDomNodePath(voidEl); if (path22) return { path: path22, offset: 0 }; } } } if (!textNode) { if (suppressThrow) return null; throw new Error(`Cannot resolve a Slate point from DOM point: ${domPoint}`); } const path2 = getDomNodePath(textNode); if (!path2) { if (suppressThrow) return null; throw new Error(`Cannot resolve a Slate point from DOM point: ${domPoint}`); } if (path2.length > 1) { const parentPath2 = path2.slice(0, -1), parentEntry = getNode(editor, parentPath2); if (parentEntry && isVoidNode(editor, parentEntry.node, parentPath2)) return { path: parentPath2, offset: 0 }; } return { path: path2, offset }; }, toSlateRange: (editor, domRange, options) => { const { exactMatch, suppressThrow } = options, el = isDOMSelection(domRange) ? domRange.anchorNode : domRange.startContainer; let anchorNode = null, anchorOffset = 0, focusNode = null, focusOffset = 0, isCollapsed = !1; if (el) if (isDOMSelection(domRange)) { if (IS_FIREFOX && domRange.rangeCount > 1) { focusNode = domRange.focusNode; const firstRange = domRange.getRangeAt(0), lastRange = domRange.getRangeAt(domRange.rangeCount - 1); if (focusNode instanceof HTMLTableRowElement && firstRange.startContainer instanceof HTMLTableRowElement && lastRange.startContainer instanceof HTMLTableRowElement) { let getLastChildren2 = function(element) { return element.childElementCount > 0 ? getLastChildren2(element.children[0]) : element; }; const firstNodeRow = firstRange.startContainer, lastNodeRow = lastRange.startContainer, firstNode = getLastChildren2(firstNodeRow.children[firstRange.startOffset]), lastNode = getLastChildren2(lastNodeRow.children[lastRange.startOffset]); focusOffset = 0, lastNode.childNodes.length > 0 ? anchorNode = lastNode.childNodes[0] ?? null : anchorNode = lastNode, firstNode.childNodes.length > 0 ? focusNode = firstNode.childNodes[0] ?? null : focusNode = firstNode, lastNode instanceof HTMLElement ? anchorOffset = lastNode.innerHTML.length : anchorOffset = 0; } else firstRange.startContainer === focusNode ? (anchorNode = lastRange.endContainer, anchorOffset = lastRange.endOffset, focusOffset = firstRange.startOffset) : (anchorNode = firstRange.startContainer, anchorOffset = firstRange.endOffset, focusOffset = lastRange.startOffset); } else anchorNode = domRange.anchorNode, anchorOffset = domRange.anchorOffset, focusNode = domRange.focusNode, focusOffset = domRange.focusOffset; IS_CHROME && hasShadowRoot(anchorNode) || IS_FIREFOX ? isCollapsed = domRange.anchorNode === domRange.focusNode && domRange.anchorOffset === domRange.focusOffset : isCollapsed = domRange.isCollapsed; } else anchorNode = domRange.startContainer, anchorOffset = domRange.startOffset, focusNode = domRange.endContainer, focusOffset = domRange.endOffset, isCollapsed = domRange.collapsed; if (anchorNode == null || focusNode == null || anchorOffset == null || focusOffset == null) { if (suppressThrow) return null; throw new Error(`Cannot resolve a Slate range from DOM range: ${domRange}`); } IS_FIREFOX && focusNode.textContent?.endsWith(` `) && focusOffset === focusNode.textContent.length && focusOffset--; const anchor = DOMEditor.toSlatePoint(editor, [anchorNode, anchorOffset], { exactMatch, suppressThrow }); if (!anchor) return null; const focusBeforeAnchor = isBefore(anchorNode, focusNode) || anchorNode === focusNode && focusOffset < anchorOffset, focus = isCollapsed ? anchor : DOMEditor.toSlatePoint(editor, [focusNode, focusOffset], { exactMatch, suppressThrow, searchDirection: focusBeforeAnchor ? "forward" : "backward" }); return focus ? { anchor, focus } : null; } }, getDefaultView = (value) => value && value.ownerDocument && value.ownerDocument.defaultView || null, isDOMComment = (value) => isDOMNode(value) && value.nodeType === 8, isDOMElement = (value) => isDOMNode(value) && value.nodeType === 1, isDOMNode = (value) => { const window2 = getDefaultView(value); return !!window2 && value instanceof window2.Node; }, isDOMSelection = (value) => { const window2 = value && value.anchorNode && getDefaultView(value.anchorNode); return !!window2 && value instanceof window2.Selection; }, isPlainTextOnlyPaste = (event) => event.clipboardData && event.clipboardData.getData("text/plain") !== "" && event.clipboardData.types.length === 1, normalizeDOMPoint = (domPoint) => { let [node, offset] = domPoint; if (isDOMElement(node) && node.childNodes.length) { let isLast = offset === node.childNodes.length, index = isLast ? offset - 1 : offset; for ([node, index] = getEditableChildAndIndex(node, index, isLast ? "backward" : "forward"), isLast = index < offset; isDOMElement(node) && node.childNodes.length; ) { const i = isLast ? node.childNodes.length - 1 : 0; node = getEditableChild(node, i, isLast ? "backward" : "forward"); } offset = isLast && node.textContent != null ? node.textContent.length : 0; } return [node, offset]; }, hasShadowRoot = (node) => { let parent = node && node.parentNode; for (; parent; ) { if (parent.toString() === "[object ShadowRoot]") return !0; parent = parent.parentNode; } return !1; }, getEditableChildAndIndex = (parent, index, direction) => { const { childNodes } = parent; let child = childNodes[index], i = index, triedForward = !1, triedBackward = !1; for (; (isDOMComment(child) || isDOMElement(child) && child.childNodes.length === 0 || isDOMElement(child) && child.getAttribute("contenteditable") === "false") && !(triedForward && triedBackward); ) { if (i >= childNodes.length) { triedForward = !0, i = index - 1, direction = "backward"; continue; } if (i < 0) { triedBackward = !0, i = index + 1, direction = "forward"; continue; } child = childNodes[i], index = i, i += direction === "forward" ? 1 : -1; } return [child, index]; }, getEditableChild = (parent, index, direction) => { const [child] = getEditableChildAndIndex(parent, index, direction); return child; }, getSelection = (root) => "getSelection" in root && typeof root.getSelection == "function" ? root.getSelection() : document.getSelection(), isTrackedMutation = (editor, mutation, batch) => { const { target } = mutation; if (isDOMElement(target) && target.matches('[contentEditable="false"]')) return !1; const { document: document2 } = DOMEditor.getWindow(editor); if (containsShadowAware(document2, target)) return DOMEditor.hasDOMNode(editor, target, { editable: !0 }); const parentMutation = batch.find(({ addedNodes, removedNodes }) => { for (const node of addedNodes) if (node === target || containsShadowAware(node, target)) return !0; for (const node of removedNodes) if (node === target || containsShadowAware(node, target)) return !0; return !1; }); return !parentMutation || parentMutation === mutation ? !1 : isTrackedMutation(editor, parentMutation, batch); }, getActiveElement = () => { let activeElement = document.activeElement; for (; activeElement?.shadowRoot && activeElement.shadowRoot?.activeElement; ) activeElement = activeElement?.shadowRoot?.activeElement; return activeElement; }, isBefore = (node, otherNode) => !!(node.compareDocumentPosition(otherNode) & Node.DOCUMENT_POSITION_PRECEDING), isAfter = (node, otherNode) => !!(node.compareDocumentPosition(otherNode) & Node.DOCUMENT_POSITION_FOLLOWING), closestShadowAware = (element, selector) => { if (!element) return null; let current = element; for (; current; ) { if (current.matches && current.matches(selector)) return current; if (current.parentElement) current = current.parentElement; else if (current.parentNode && "host" in current.parentNode) current = current.parentNode.host; else return null; } return null; }, containsShadowAware = (parent, child) => { if (!parent || !child) return !1; if (parent.contains(child)) return !0; let current = child; for (; current; ) { if (current === parent) return !0; if (current.parentNode) "host" in current.parentNode ? current = current.parentNode.host : current = current.parentNode; else return !1; } return !1; }; function getDomNodePath(domNode) { let element = isDOMElement(domNode) ? domNode : domNode.parentElement; if (element && !element.hasAttribute("data-pt-path") && (element = element.closest("[data-pt-path]")), !element) return; const dataPath = element.getAttribute("data-pt-path"); if (dataPath !== null) return dataPath === "" ? [] : deserializePath(dataPath); } function getEventPosition({ editorActor, slateEditor, event }) { if (editorActor.getSnapshot().matches({ setup: "setting up" })) return; const eventResult = getEventNode({ slateEditor, event }); if (!eventResult) return; const { node: eventNode, path: eventPath } = eventResult, eventBlockEntry = getEnclosingBlock(slateEditor, eventPath), eventBlock = eventBlockEntry?.node, eventBlockPath = eventBlockEntry?.path, eventPositionBlock = getEventPositionBlock({ nodePath: eventPath, slateEditor, event }), eventSelection = getSelectionFromEvent(slateEditor, event) ?? null; if (eventBlock && eventBlockPath && eventPositionBlock && !eventSelection && !isEventContainer(slateEditor, eventNode, eventPath)) return { block: eventPositionBlock, isEditor: !1, isContainer: !1, selection: { anchor: getBlockStartPoint({ context: slateEditor, block: { node: eventBlock, path: eventBlockPath } }), focus: getBlockEndPoint({ context: slateEditor, block: { node: eventBlock, path: eventBlockPath } }) } }; if (!eventPositionBlock || !eventSelection) return; const eventSelectionFocusBlock = getEnclosingBlock(slateEditor, eventSelection.focus.path); if (eventSelectionFocusBlock) return isSelectionCollapsed(eventSelection) && eventBlock && eventBlockPath && eventSelectionFocusBlock.node._key !== eventBlock._key && !isAncestorPath(eventBlockPath, eventSelectionFocusBlock.path) ? { block: eventPositionBlock, isEditor: !1, isContainer: !1, selection: { anchor: getBlockStartPoint({ context: slateEditor, block: { node: eventBlock, path: eventBlockPath } }), focus: getBlockEndPoint({ context: slateEditor, block: { node: eventBlock, path: eventBlockPath } }) } } : { block: eventPositionBlock, isEditor: isEditor(eventNode), isContainer: isEditor(eventNode) ? !1 : isEditableContainer(slateEditor, eventNode, eventPath), selection: eventSelection }; } function getEventNode({ slateEditor, event }) { if (DOMEditor.hasTarget(slateEditor, event.target)) try { const path2 = getDomNodePath(event.target); if (path2) { if (path2.length === 0) return { node: slateEditor, path: path2 }; { const nodeEntry = getNode(slateEditor, path2); if (nodeEntry) return { node: nodeEntry.node, path: path2 }; } } } catch (error) { console.error(error); } } function getEventPositionBlock({ nodePath, slateEditor, event }) { const firstBlockEntry = getNode(slateEditor, [0]); if (!firstBlockEntry) return; const firstBlockElement = getDomNode(slateEditor, firstBlockEntry.path); if (!firstBlockElement) return; const firstBlockRect = firstBlockElement.getBoundingClientRect(); if (event.pageY < firstBlockRect.top) return "start"; const lastBlock = slateEditor.children.at(-1), lastBlockEntry = lastBlock ? getNode(slateEditor, [{ _key: lastBlock._key }]) : void 0; if (!lastBlockEntry) return; const lastBlockElement = getDomNode(slateEditor, lastBlockEntry.path); if (!lastBlockElement) return; const lastBlockRef = lastBlockElement.getBoundingClientRect(); if (event.pageY > lastBlockRef.bottom) return "end"; const element = getDomNode(slateEditor, nodePath); if (!element) return; const elementRect = element.getBoundingClientRect(), top = elementRect.top, height = elementRect.height; return Math.abs(top - event.pageY) < height / 2 ? "start" : "end"; } function getSelectionFromEvent(editor, event) { if (!event.target || !isDOMNode(event.target)) return; const window2 = DOMEditor.getWindow(editor); let domRange; if (window2.document.caretPositionFromPoint !== void 0) { const position = window2.document.caretPositionFromPoint(event.clientX, event.clientY); if (position) try { domRange = window2.document.createRange(), domRange.setStart(position.offsetNode, position.offset), domRange.setEnd(position.offsetNode, position.offset); } catch { } } else if (window2.document.caretRangeFromPoint !== void 0) domRange = window2.document.caretRangeFromPoint(event.clientX, event.clientY) ?? void 0; else { console.warn("Neither caretPositionFromPoint nor caretRangeFromPoint is supported"); return; } if (domRange) try { return DOMEditor.toSlateRange(editor, domRange, { exactMatch: !1, // It can still throw even with this option set to true suppressThrow: !1 }); } catch { return; } } function isEventContainer(slateEditor, eventNode, eventPath) { return isEditor(eventNode) ? !0 : isEditableContainer(slateEditor, eventNode, eventPath); } function getVoidAncestor(snapshot, path2) { return getAncestor(snapshot, path2, (node, ancestorPath) => isVoidNode(snapshot, node, ancestorPath)); } function collapse(editor, options = {}) { const { edge = "anchor" } = options, { selection } = editor; if (selection) { if (edge === "anchor") editor.select(selection.anchor); else if (edge === "focus") editor.select(selection.focus); else if (edge === "start") { const [start2] = rangeEdges(selection, editor); editor.select(start2); } else if (edge === "end") { const [, end2] = rangeEdges(selection, editor); editor.select(end2); } } else return; } function deselect(editor) { const { selection } = editor; selection && editor.apply({ type: "set_selection", properties: selection, newProperties: null }); } const getCharacterDistance = (str, isRTL = !1) => { const isLTR = !isRTL, codepoints = isRTL ? codepointsIteratorRTL(str) : str; let left = CodepointType.None, right = CodepointType.None, distance = 0, gb11 = null, gb12Or13 = null; for (const char of codepoints) { const code2 = char.codePointAt(0); if (!code2) break; const type = getCodepointType(char, code2); if ([left, right] = isLTR ? [right, type] : [type, left], intersects(left, CodepointType.ZWJ) && intersects(right, CodepointType.ExtPict) && (isLTR ? gb11 = endsWithEmojiZWJ(str.substring(0, distance)) : gb11 = endsWithEmojiZWJ(str.substring(0, str.length - distance)), !gb11) || intersects(left, CodepointType.RI) && intersects(right, CodepointType.RI) && (gb12Or13 !== null ? gb12Or13 = !gb12Or13 : isLTR ? gb12Or13 = !0 : gb12Or13 = endsWithOddNumberOfRIs(str.substring(0, str.length - distance)), !gb12Or13) || left !== CodepointType.None && right !== CodepointType.None && isBoundaryPair(left, right)) break; distance += char.length; } return distance || 1; }, SPACE = /\s/, PUNCTUATION = /[\u002B\u0021-\u0023\u0025-\u002A\u002C-\u002F\u003A\u003B\u003F\u0040\u005B-\u005D\u005F\u007B\u007D\u00A1\u00A7\u00AB\u00B6\u00B7\u00BB\u00BF\u037E\u0387\u055A-\u055F\u0589\u058A\u05BE\u05C0\u05C3\u05C6\u05F3\u05F4\u0609\u060A\u060C\u060D\u061B\u061E\u061F\u066A-\u066D\u06D4\u0700-\u070D\u07F7-\u07F9\u0830-\u083E\u085E\u0964\u0965\u0970\u0AF0\u0DF4\u0E4F\u0E5A\u0E5B\u0F04-\u0F12\u0F14\u0F3A-\u0F3D\u0F85\u0FD0-\u0FD4\u0FD9\u0FDA\u104A-\u104F\u10FB\u1360-\u1368\u1400\u166D\u166E\u169B\u169C\u16EB-\u16ED\u1735\u1736\u17D4-\u17D6\u17D8-\u17DA\u1800-\u180A\u1944\u1945\u1A1E\u1A1F\u1AA0-\u1AA6\u1AA8-\u1AAD\u1B5A-\u1B60\u1BFC-\u1BFF\u1C3B-\u1C3F\u1C7E\u1C7F\u1CC0-\u1CC7\u1CD3\u2010-\u2027\u2030-\u2043\u2045-\u2051\u2053-\u205E\u207D\u207E\u208D\u208E\u2329\u232A\u2768-\u2775\u27C5\u27C6\u27E6-\u27EF\u2983-\u2998\u29D8-\u29DB\u29FC\u29FD\u2CF9-\u2CFC\u2CFE\u2CFF\u2D70\u2E00-\u2E2E\u2E30-\u2E3B\u3001-\u3003\u3008-\u3011\u3014-\u301F\u3030\u303D\u30A0\u30FB\uA4FE\uA4FF\uA60D-\uA60F\uA673\uA67E\uA6F2-\uA6F7\uA874-\uA877\uA8CE\uA8CF\uA8F8-\uA8FA\uA92E\uA92F\uA95F\uA9C1-\uA9CD\uA9DE\uA9DF\uAA5C-\uAA5F\uAADE\uAADF\uAAF0\uAAF1\uABEB\uFD3E\uFD3F\uFE10-\uFE19\uFE30-\uFE52\uFE54-\uFE61\uFE63\uFE68\uFE6A\uFE6B\uFF01-\uFF03\uFF05-\uFF0A\uFF0C-\uFF0F\uFF1A\uFF1B\uFF1F\uFF20\uFF3B-\uFF3D\uFF3F\uFF5B\uFF5D\uFF5F-\uFF65]/, CHAMELEON = /['\u2018\u2019]/, getWordDistance = (text, isRTL = !1) => { let dist = 0, started = !1; for (; text.length > 0; ) { const charDist = getCharacterDistance(text, isRTL), [char, remaining] = splitByCharacterDistance(text, charDist, isRTL); if (isWordCharacter(char, remaining, isRTL)) started = !0, dist += charDist; else if (!started) dist += charDist; else break; text = remaining; } return dist; }, splitByCharacterDistance = (str, dist, isRTL) => { if (isRTL) { const at = str.length - dist; return [str.slice(at, str.length), str.slice(0, at)]; } return [str.slice(0, dist), str.slice(dist)]; }, isWordCharacter = (char, remaining, isRTL = !1) => { if (SPACE.test(char)) return !1; if (CHAMELEON.test(char)) { const charDist = getCharacterDistance(remaining, isRTL), [nextChar, nextRemaining] = splitByCharacterDistance(remaining, charDist, isRTL); if (isWordCharacter(nextChar, nextRemaining, isRTL)) return !0; } return !PUNCTUATION.test(char); }, codepointsIteratorRTL = function* (str) { const end2 = str.length - 1; for (let i = 0; i < str.length; i++) { const char1 = str.charAt(end2 - i); if (isLowSurrogate$1(char1.charCodeAt(0))) { const char2 = str.charAt(end2 - i - 1); if (isHighSurrogate$1(char2.charCodeAt(0))) { yield char2 + char1, i++; continue; } } yield char1; } }, isHighSurrogate$1 = (charCode) => charCode >= 55296 && charCode <= 56319, isLowSurrogate$1 = (charCode) => charCode >= 56320 && charCode <= 57343, CodepointType = { None: 0, Extend: 1, ZWJ: 2, RI: 4, Prepend: 8, SpacingMark: 16, L: 32, V: 64, T: 128, LV: 256, LVT: 512, ExtPict: 1024, Any: 2048 }, reExtend = /^[\p{Gr_Ext}\p{EMod}]$/u, rePrepend = /^[\u0600-\u0605\u06DD\u070F\u0890-\u0891\u08E2\u0D4E\u{110BD}\u{110CD}\u{111C2}-\u{111C3}\u{1193F}\u{11941}\u{11A3A}\u{11A84}-\u{11A89}\u{11D46}]$/u, reSpacingMark = /^[\u0903\u093B\u093E-\u0940\u0949-\u094C\u094E-\u094F\u0982-\u0983\u09BF-\u09C0\u09C7-\u09C8\u09CB-\u09CC\u0A03\u0A3E-\u0A40\u0A83\u0ABE-\u0AC0\u0AC9\u0ACB-\u0ACC\u0B02-\u0B03\u0B40\u0B47-\u0B48\u0B4B-\u0B4C\u0BBF\u0BC1-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCC\u0C01-\u0C03\u0C41-\u0C44\u0C82-\u0C83\u0CBE\u0CC0-\u0CC1\u0CC3-\u0CC4\u0CC7-\u0CC8\u0CCA-\u0CCB\u0D02-\u0D03\u0D3F-\u0D40\u0D46-\u0D48\u0D4A-\u0D4C\u0D82-\u0D83\u0DD0-\u0DD1\u0DD8-\u0DDE\u0DF2-\u0DF3\u0E33\u0EB3\u0F3E-\u0F3F\u0F7F\u1031\u103B-\u103C\u1056-\u1057\u1084\u1715\u1734\u17B6\u17BE-\u17C5\u17C7-\u17C8\u1923-\u1926\u1929-\u192B\u1930-\u1931\u1933-\u1938\u1A19-\u1A1A\u1A55\u1A57\u1A6D-\u1A72\u1B04\u1B3B\u1B3D-\u1B41\u1B43-\u1B44\u1B82\u1BA1\u1BA6-\u1BA7\u1BAA\u1BE7\u1BEA-\u1BEC\u1BEE\u1BF2-\u1BF3\u1C24-\u1C2B\u1C34-\u1C35\u1CE1\u1CF7\uA823-\uA824\uA827\uA880-\uA881\uA8B4-\uA8C3\uA952-\uA953\uA983\uA9B4-\uA9B5\uA9BA-\uA9BB\uA9BE-\uA9C0\uAA2F-\uAA30\uAA33-\uAA34\uAA4D\uAAEB\uAAEE-\uAAEF\uAAF5\uABE3-\uABE4\uABE6-\uABE7\uABE9-\uABEA\uABEC\u{11000}\u{11002}\u{11082}\u{110B0}-\u{110B2}\u{110B7}-\u{110B8}\u{1112C}\u{11145}-\u{11146}\u{11182}\u{111B3}-\u{111B5}\u{111BF}-\u{111C0}\u{111CE}\u{1122C}-\u{1122E}\u{11232}-\u{11233}\u{11235}\u{112E0}-\u{112E2}\u{11302}-\u{11303}\u{1133F}\u{11341}-\u{11344}\u{11347}-\u{11348}\u{1134B}-\u{1134D}\u{11362}-\u{11363}\u{11435}-\u{11437}\u{11440}-\u{11441}\u{11445}\u{114B1}-\u{114B2}\u{114B9}\u{114BB}-\u{114BC}\u{114BE}\u{114C1}\u{115B0}-\u{115B1}\u{115B8}-\u{115B