0
Fork 0
mirror of https://github.com/fastmail/Squire.git synced 2024-12-21 23:03:11 -05:00
Squire/dist/squire.mjs.map
2024-08-16 16:02:44 +10:00

7 lines
273 KiB
Text

{
"version": 3,
"sources": ["../source/node/TreeIterator.ts", "../source/Constants.ts", "../source/node/Category.ts", "../source/node/Node.ts", "../source/node/Whitespace.ts", "../source/range/Boundaries.ts", "../source/node/MergeSplit.ts", "../source/Clean.ts", "../source/node/Block.ts", "../source/range/Block.ts", "../source/range/InsertDelete.ts", "../source/range/Contents.ts", "../source/Clipboard.ts", "../source/keyboard/Enter.ts", "../source/keyboard/KeyHelpers.ts", "../source/keyboard/Backspace.ts", "../source/keyboard/Delete.ts", "../source/keyboard/Tab.ts", "../source/keyboard/Space.ts", "../source/keyboard/KeyHandlers.ts", "../source/Editor.ts", "../source/Squire.ts"],
"sourcesContent": ["type NODE_TYPE = 1 | 4 | 5;\nconst SHOW_ELEMENT = 1; // NodeFilter.SHOW_ELEMENT;\nconst SHOW_TEXT = 4; // NodeFilter.SHOW_TEXT;\nconst SHOW_ELEMENT_OR_TEXT = 5; // SHOW_ELEMENT|SHOW_TEXT;\n\nconst always = (): true => true;\n\nclass TreeIterator<T extends Node> {\n root: Node;\n currentNode: Node;\n nodeType: NODE_TYPE;\n filter: (n: T) => boolean;\n\n constructor(root: Node, nodeType: NODE_TYPE, filter?: (n: T) => boolean) {\n this.root = root;\n this.currentNode = root;\n this.nodeType = nodeType;\n this.filter = filter || always;\n }\n\n isAcceptableNode(node: Node): boolean {\n const nodeType = node.nodeType;\n const nodeFilterType =\n nodeType === Node.ELEMENT_NODE\n ? SHOW_ELEMENT\n : nodeType === Node.TEXT_NODE\n ? SHOW_TEXT\n : 0;\n return !!(nodeFilterType & this.nodeType) && this.filter(node as T);\n }\n\n nextNode(): T | null {\n const root = this.root;\n let current: Node | null = this.currentNode;\n let node: Node | null;\n while (true) {\n node = current.firstChild;\n while (!node && current) {\n if (current === root) {\n break;\n }\n node = current.nextSibling;\n if (!node) {\n current = current.parentNode;\n }\n }\n if (!node) {\n return null;\n }\n\n if (this.isAcceptableNode(node)) {\n this.currentNode = node;\n return node as T;\n }\n current = node;\n }\n }\n\n previousNode(): T | null {\n const root = this.root;\n let current: Node | null = this.currentNode;\n let node: Node | null;\n while (true) {\n if (current === root) {\n return null;\n }\n node = current.previousSibling;\n if (node) {\n while ((current = node.lastChild)) {\n node = current;\n }\n } else {\n node = current.parentNode;\n }\n if (!node) {\n return null;\n }\n if (this.isAcceptableNode(node)) {\n this.currentNode = node;\n return node as T;\n }\n current = node;\n }\n }\n\n // Previous node in post-order.\n previousPONode(): T | null {\n const root = this.root;\n let current: Node | null = this.currentNode;\n let node: Node | null;\n while (true) {\n node = current.lastChild;\n while (!node && current) {\n if (current === root) {\n break;\n }\n node = current.previousSibling;\n if (!node) {\n current = current.parentNode;\n }\n }\n if (!node) {\n return null;\n }\n if (this.isAcceptableNode(node)) {\n this.currentNode = node;\n return node as T;\n }\n current = node;\n }\n }\n}\n\n// ---\n\nexport { TreeIterator, SHOW_ELEMENT, SHOW_TEXT, SHOW_ELEMENT_OR_TEXT };\n", "const DOCUMENT_POSITION_PRECEDING = 2; // Node.DOCUMENT_POSITION_PRECEDING\nconst ELEMENT_NODE = 1; // Node.ELEMENT_NODE;\nconst TEXT_NODE = 3; // Node.TEXT_NODE;\nconst DOCUMENT_NODE = 9; // Node.DOCUMENT_NODE;\nconst DOCUMENT_FRAGMENT_NODE = 11; // Node.DOCUMENT_FRAGMENT_NODE;\n\nconst ZWS = '\\u200B';\n\nconst ua = navigator.userAgent;\n\nconst isMac = /Mac OS X/.test(ua);\nconst isWin = /Windows NT/.test(ua);\nconst isIOS =\n /iP(?:ad|hone|od)/.test(ua) || (isMac && !!navigator.maxTouchPoints);\nconst isAndroid = /Android/.test(ua);\n\nconst isGecko = /Gecko\\//.test(ua);\nconst isLegacyEdge = /Edge\\//.test(ua);\nconst isWebKit = !isLegacyEdge && /WebKit\\//.test(ua);\n\nconst ctrlKey = isMac || isIOS ? 'Meta-' : 'Ctrl-';\n\nconst cantFocusEmptyTextNodes = isWebKit;\n\nconst supportsInputEvents =\n 'onbeforeinput' in document && 'inputType' in new InputEvent('input');\n\n// Use [^ \\t\\r\\n] instead of \\S so that nbsp does not count as white-space\nconst notWS = /[^ \\t\\r\\n]/;\n\n// ---\n\nexport {\n DOCUMENT_POSITION_PRECEDING,\n ELEMENT_NODE,\n TEXT_NODE,\n DOCUMENT_NODE,\n DOCUMENT_FRAGMENT_NODE,\n notWS,\n ZWS,\n ua,\n isMac,\n isWin,\n isIOS,\n isAndroid,\n isGecko,\n isLegacyEdge,\n isWebKit,\n ctrlKey,\n cantFocusEmptyTextNodes,\n supportsInputEvents,\n};\n", "import { ELEMENT_NODE, TEXT_NODE, DOCUMENT_FRAGMENT_NODE } from '../Constants';\n\n// ---\n\nconst inlineNodeNames =\n /^(?:#text|A(?:BBR|CRONYM)?|B(?:R|D[IO])?|C(?:ITE|ODE)|D(?:ATA|EL|FN)|EM|FONT|HR|I(?:FRAME|MG|NPUT|NS)?|KBD|Q|R(?:P|T|UBY)|S(?:AMP|MALL|PAN|TR(?:IKE|ONG)|U[BP])?|TIME|U|VAR|WBR)$/;\n\nconst leafNodeNames = new Set(['BR', 'HR', 'IFRAME', 'IMG', 'INPUT']);\n\nconst UNKNOWN = 0;\nconst INLINE = 1;\nconst BLOCK = 2;\nconst CONTAINER = 3;\n\n// ---\n\nlet cache: WeakMap<Node, number> = new WeakMap();\n\nconst resetNodeCategoryCache = (): void => {\n cache = new WeakMap();\n};\n\n// ---\n\nconst isLeaf = (node: Node): boolean => {\n return leafNodeNames.has(node.nodeName);\n};\n\nconst getNodeCategory = (node: Node): number => {\n switch (node.nodeType) {\n case TEXT_NODE:\n return INLINE;\n case ELEMENT_NODE:\n case DOCUMENT_FRAGMENT_NODE:\n if (cache.has(node)) {\n return cache.get(node) as number;\n }\n break;\n default:\n return UNKNOWN;\n }\n\n let nodeCategory: number;\n if (!Array.from(node.childNodes).every(isInline)) {\n // Malformed HTML can have block tags inside inline tags. Need to treat\n // these as containers rather than inline. See #239.\n nodeCategory = CONTAINER;\n } else if (inlineNodeNames.test(node.nodeName)) {\n nodeCategory = INLINE;\n } else {\n nodeCategory = BLOCK;\n }\n cache.set(node, nodeCategory);\n return nodeCategory;\n};\n\nconst isInline = (node: Node): boolean => {\n return getNodeCategory(node) === INLINE;\n};\n\nconst isBlock = (node: Node): boolean => {\n return getNodeCategory(node) === BLOCK;\n};\n\nconst isContainer = (node: Node): boolean => {\n return getNodeCategory(node) === CONTAINER;\n};\n\n// ---\n\nexport {\n getNodeCategory,\n isBlock,\n isContainer,\n isInline,\n isLeaf,\n leafNodeNames,\n resetNodeCategoryCache,\n};\n", "import { isLeaf } from './Category';\n\n// ---\n\nconst createElement = (\n tag: string,\n props?: Record<string, string> | null,\n children?: Node[],\n): HTMLElement => {\n const el = document.createElement(tag);\n if (props instanceof Array) {\n children = props;\n props = null;\n }\n if (props) {\n for (const attr in props) {\n const value = props[attr];\n if (value !== undefined) {\n el.setAttribute(attr, value);\n }\n }\n }\n if (children) {\n children.forEach((node) => el.appendChild(node));\n }\n return el;\n};\n\n// --- Tests\n\nconst areAlike = (\n node: HTMLElement | Node,\n node2: HTMLElement | Node,\n): boolean => {\n if (isLeaf(node)) {\n return false;\n }\n if (node.nodeType !== node2.nodeType || node.nodeName !== node2.nodeName) {\n return false;\n }\n if (node instanceof HTMLElement && node2 instanceof HTMLElement) {\n return (\n node.nodeName !== 'A' &&\n node.className === node2.className &&\n node.style.cssText === node2.style.cssText\n );\n }\n return true;\n};\n\nconst hasTagAttributes = (\n node: Node | Element,\n tag: string,\n attributes?: Record<string, string> | null,\n): boolean => {\n if (node.nodeName !== tag) {\n return false;\n }\n for (const attr in attributes) {\n if (\n !('getAttribute' in node) ||\n node.getAttribute(attr) !== attributes[attr]\n ) {\n return false;\n }\n }\n return true;\n};\n\n// --- Traversal\n\nconst getNearest = (\n node: Node | null,\n root: Element | DocumentFragment,\n tag: string,\n attributes?: Record<string, string> | null,\n): Node | null => {\n while (node && node !== root) {\n if (hasTagAttributes(node, tag, attributes)) {\n return node;\n }\n node = node.parentNode;\n }\n return null;\n};\n\nconst getNodeBeforeOffset = (node: Node, offset: number): Node => {\n let children = node.childNodes;\n while (offset && node instanceof Element) {\n node = children[offset - 1];\n children = node.childNodes;\n offset = children.length;\n }\n return node;\n};\n\nconst getNodeAfterOffset = (node: Node, offset: number): Node | null => {\n let returnNode: Node | null = node;\n if (returnNode instanceof Element) {\n const children = returnNode.childNodes;\n if (offset < children.length) {\n returnNode = children[offset];\n } else {\n while (returnNode && !returnNode.nextSibling) {\n returnNode = returnNode.parentNode;\n }\n if (returnNode) {\n returnNode = returnNode.nextSibling;\n }\n }\n }\n return returnNode;\n};\n\nconst getLength = (node: Node): number => {\n return node instanceof Element || node instanceof DocumentFragment\n ? node.childNodes.length\n : node instanceof CharacterData\n ? node.length\n : 0;\n};\n\n// --- Manipulation\n\nconst empty = (node: Node): DocumentFragment => {\n const frag = document.createDocumentFragment();\n let child = node.firstChild;\n while (child) {\n frag.appendChild(child);\n child = node.firstChild;\n }\n return frag;\n};\n\nconst detach = (node: Node): Node => {\n const parent = node.parentNode;\n if (parent) {\n parent.removeChild(node);\n }\n return node;\n};\n\nconst replaceWith = (node: Node, node2: Node): void => {\n const parent = node.parentNode;\n if (parent) {\n parent.replaceChild(node2, node);\n }\n};\n\n// --- Export\n\nexport {\n areAlike,\n createElement,\n detach,\n empty,\n getLength,\n getNearest,\n getNodeAfterOffset,\n getNodeBeforeOffset,\n hasTagAttributes,\n replaceWith,\n};\n", "import { ZWS, notWS } from '../Constants';\nimport { isInline } from './Category';\nimport { getLength } from './Node';\nimport { SHOW_ELEMENT_OR_TEXT, SHOW_TEXT, TreeIterator } from './TreeIterator';\n\n// ---\n\nconst notWSTextNode = (node: Node): boolean => {\n return node instanceof Element\n ? node.nodeName === 'BR'\n : // okay if data is 'undefined' here.\n notWS.test((node as CharacterData).data);\n};\n\nconst isLineBreak = (br: Element, isLBIfEmptyBlock: boolean): boolean => {\n let block = br.parentNode!;\n while (isInline(block)) {\n block = block.parentNode!;\n }\n const walker = new TreeIterator<Element | Text>(\n block,\n SHOW_ELEMENT_OR_TEXT,\n notWSTextNode,\n );\n walker.currentNode = br;\n return !!walker.nextNode() || (isLBIfEmptyBlock && !walker.previousNode());\n};\n\n// --- Workaround for browsers that can't focus empty text nodes\n\n// WebKit bug: https://bugs.webkit.org/show_bug.cgi?id=15256\n\n// Walk down the tree starting at the root and remove any ZWS. If the node only\n// contained ZWS space then remove it too. We may want to keep one ZWS node at\n// the bottom of the tree so the block can be selected. Define that node as the\n// keepNode.\nconst removeZWS = (root: Node, keepNode?: Node | null): void => {\n const walker = new TreeIterator<Text>(root, SHOW_TEXT);\n let textNode: Text | null;\n let index: number;\n while ((textNode = walker.nextNode())) {\n while (\n (index = textNode.data.indexOf(ZWS)) > -1 &&\n // eslint-disable-next-line no-unmodified-loop-condition\n (!keepNode || textNode.parentNode !== keepNode)\n ) {\n if (textNode.length === 1) {\n let node: Node = textNode;\n let parent = node.parentNode;\n while (parent) {\n parent.removeChild(node);\n walker.currentNode = parent;\n if (!isInline(parent) || getLength(parent)) {\n break;\n }\n node = parent;\n parent = node.parentNode;\n }\n break;\n } else {\n textNode.deleteData(index, 1);\n }\n }\n }\n};\n\n// ---\n\nexport { isLineBreak, removeZWS };\n", "import { isLeaf } from '../node/Category';\nimport { getLength, getNearest } from '../node/Node';\nimport { isLineBreak } from '../node/Whitespace';\nimport { TEXT_NODE } from '../Constants';\n\n// ---\n\nconst START_TO_START = 0; // Range.START_TO_START\nconst START_TO_END = 1; // Range.START_TO_END\nconst END_TO_END = 2; // Range.END_TO_END\nconst END_TO_START = 3; // Range.END_TO_START\n\nconst isNodeContainedInRange = (\n range: Range,\n node: Node,\n partial: boolean,\n): boolean => {\n const nodeRange = document.createRange();\n nodeRange.selectNode(node);\n if (partial) {\n // Node must not finish before range starts or start after range\n // finishes.\n const nodeEndBeforeStart =\n range.compareBoundaryPoints(END_TO_START, nodeRange) > -1;\n const nodeStartAfterEnd =\n range.compareBoundaryPoints(START_TO_END, nodeRange) < 1;\n return !nodeEndBeforeStart && !nodeStartAfterEnd;\n } else {\n // Node must start after range starts and finish before range\n // finishes\n const nodeStartAfterStart =\n range.compareBoundaryPoints(START_TO_START, nodeRange) < 1;\n const nodeEndBeforeEnd =\n range.compareBoundaryPoints(END_TO_END, nodeRange) > -1;\n return nodeStartAfterStart && nodeEndBeforeEnd;\n }\n};\n\n/**\n * Moves the range to an equivalent position with the start/end as deep in\n * the tree as possible.\n */\nconst moveRangeBoundariesDownTree = (range: Range): void => {\n let { startContainer, startOffset, endContainer, endOffset } = range;\n\n while (!(startContainer instanceof Text)) {\n let child: ChildNode | null = startContainer.childNodes[startOffset];\n if (!child || isLeaf(child)) {\n if (startOffset) {\n child = startContainer.childNodes[startOffset - 1];\n if (child instanceof Text) {\n // Need a new variable to satisfy TypeScript's type checker\n // for some reason.\n let textChild: Text = child;\n // If we have an empty text node next to another text node,\n // just skip and remove it.\n let prev: ChildNode | null;\n while (\n !textChild.length &&\n (prev = textChild.previousSibling) &&\n prev instanceof Text\n ) {\n textChild.remove();\n textChild = prev;\n }\n startContainer = textChild;\n startOffset = textChild.data.length;\n }\n }\n break;\n }\n startContainer = child;\n startOffset = 0;\n }\n if (endOffset) {\n while (!(endContainer instanceof Text)) {\n const child = endContainer.childNodes[endOffset - 1];\n if (!child || isLeaf(child)) {\n if (\n child &&\n child.nodeName === 'BR' &&\n !isLineBreak(child as Element, false)\n ) {\n endOffset -= 1;\n continue;\n }\n break;\n }\n endContainer = child;\n endOffset = getLength(endContainer);\n }\n } else {\n while (!(endContainer instanceof Text)) {\n const child = endContainer.firstChild!;\n if (!child || isLeaf(child)) {\n break;\n }\n endContainer = child;\n }\n }\n\n range.setStart(startContainer, startOffset);\n range.setEnd(endContainer, endOffset);\n};\n\nconst moveRangeBoundariesUpTree = (\n range: Range,\n startMax: Node,\n endMax: Node,\n root: Node,\n): void => {\n let startContainer = range.startContainer;\n let startOffset = range.startOffset;\n let endContainer = range.endContainer;\n let endOffset = range.endOffset;\n let parent: Node;\n\n if (!startMax) {\n startMax = range.commonAncestorContainer;\n }\n if (!endMax) {\n endMax = startMax;\n }\n\n while (\n !startOffset &&\n startContainer !== startMax &&\n startContainer !== root\n ) {\n parent = startContainer.parentNode!;\n startOffset = Array.from(parent.childNodes).indexOf(\n startContainer as ChildNode,\n );\n startContainer = parent;\n }\n\n while (true) {\n if (endContainer === endMax || endContainer === root) {\n break;\n }\n if (\n endContainer.nodeType !== TEXT_NODE &&\n endContainer.childNodes[endOffset] &&\n endContainer.childNodes[endOffset].nodeName === 'BR' &&\n !isLineBreak(endContainer.childNodes[endOffset] as Element, false)\n ) {\n endOffset += 1;\n }\n if (endOffset !== getLength(endContainer)) {\n break;\n }\n parent = endContainer.parentNode!;\n endOffset =\n Array.from(parent.childNodes).indexOf(endContainer as ChildNode) +\n 1;\n endContainer = parent;\n }\n\n range.setStart(startContainer, startOffset);\n range.setEnd(endContainer, endOffset);\n};\n\nconst moveRangeBoundaryOutOf = (\n range: Range,\n tag: string,\n root: Element,\n): Range => {\n let parent = getNearest(range.endContainer, root, tag);\n if (parent && (parent = parent.parentNode)) {\n const clone = range.cloneRange();\n moveRangeBoundariesUpTree(clone, parent, parent, root);\n if (clone.endContainer === parent) {\n range.setStart(clone.endContainer, clone.endOffset);\n range.setEnd(clone.endContainer, clone.endOffset);\n }\n }\n return range;\n};\n\n// ---\n\nexport {\n isNodeContainedInRange,\n moveRangeBoundariesDownTree,\n moveRangeBoundariesUpTree,\n moveRangeBoundaryOutOf,\n};\n", "import { ZWS, cantFocusEmptyTextNodes } from '../Constants';\nimport {\n createElement,\n getNearest,\n areAlike,\n getLength,\n detach,\n empty,\n} from './Node';\nimport { isInline, isContainer } from './Category';\n\n// ---\n\nconst fixCursor = (node: Node): Node => {\n // In Webkit and Gecko, block level elements are collapsed and\n // unfocusable if they have no content. To remedy this, a <BR> must be\n // inserted. In Opera and IE, we just need a textnode in order for the\n // cursor to appear.\n let fixer: Element | Text | null = null;\n\n if (node instanceof Text) {\n return node;\n }\n\n if (isInline(node)) {\n let child = node.firstChild;\n if (cantFocusEmptyTextNodes) {\n while (child && child instanceof Text && !child.data) {\n node.removeChild(child);\n child = node.firstChild;\n }\n }\n if (!child) {\n if (cantFocusEmptyTextNodes) {\n fixer = document.createTextNode(ZWS);\n } else {\n fixer = document.createTextNode('');\n }\n }\n } else if (\n (node instanceof Element || node instanceof DocumentFragment) &&\n !node.querySelector('BR')\n ) {\n fixer = createElement('BR');\n let parent: Element | DocumentFragment = node;\n let child: Element | null;\n while ((child = parent.lastElementChild) && !isInline(child)) {\n parent = child;\n }\n node = parent;\n }\n if (fixer) {\n try {\n node.appendChild(fixer);\n } catch (error) {}\n }\n\n return node;\n};\n\n// Recursively examine container nodes and wrap any inline children.\nconst fixContainer = (\n container: Node,\n root: Element | DocumentFragment,\n): Node => {\n let wrapper: HTMLElement | null = null;\n Array.from(container.childNodes).forEach((child) => {\n const isBR = child.nodeName === 'BR';\n if (!isBR && isInline(child)) {\n if (!wrapper) {\n wrapper = createElement('DIV');\n }\n wrapper.appendChild(child);\n } else if (isBR || wrapper) {\n if (!wrapper) {\n wrapper = createElement('DIV');\n }\n fixCursor(wrapper);\n if (isBR) {\n container.replaceChild(wrapper, child);\n } else {\n container.insertBefore(wrapper, child);\n }\n wrapper = null;\n }\n if (isContainer(child)) {\n fixContainer(child, root);\n }\n });\n if (wrapper) {\n container.appendChild(fixCursor(wrapper));\n }\n return container;\n};\n\nconst split = (\n node: Node,\n offset: number | Node | null,\n stopNode: Node,\n root: Element | DocumentFragment,\n): Node | null => {\n if (node instanceof Text && node !== stopNode) {\n if (typeof offset !== 'number') {\n throw new Error('Offset must be a number to split text node!');\n }\n if (!node.parentNode) {\n throw new Error('Cannot split text node with no parent!');\n }\n return split(node.parentNode, node.splitText(offset), stopNode, root);\n }\n\n let nodeAfterSplit: Node | null =\n typeof offset === 'number'\n ? offset < node.childNodes.length\n ? node.childNodes[offset]\n : null\n : offset;\n const parent = node.parentNode;\n if (!parent || node === stopNode || !(node instanceof Element)) {\n return nodeAfterSplit;\n }\n\n // Clone node without children\n const clone = node.cloneNode(false) as Element;\n\n // Add right-hand siblings to the clone\n while (nodeAfterSplit) {\n const next = nodeAfterSplit.nextSibling;\n clone.appendChild(nodeAfterSplit);\n nodeAfterSplit = next;\n }\n\n // Maintain li numbering if inside a quote.\n if (\n node instanceof HTMLOListElement &&\n getNearest(node, root, 'BLOCKQUOTE')\n ) {\n (clone as HTMLOListElement).start =\n (+node.start || 1) + node.childNodes.length - 1;\n }\n\n // DO NOT NORMALISE. This may undo the fixCursor() call\n // of a node lower down the tree!\n // We need something in the element in order for the cursor to appear.\n fixCursor(node);\n fixCursor(clone);\n\n // Inject clone after original node\n parent.insertBefore(clone, node.nextSibling);\n\n // Keep on splitting up the tree\n return split(parent, clone, stopNode, root);\n};\n\nconst _mergeInlines = (\n node: Node,\n fakeRange: {\n startContainer: Node;\n startOffset: number;\n endContainer: Node;\n endOffset: number;\n },\n): void => {\n const children = node.childNodes;\n let l = children.length;\n const frags: DocumentFragment[] = [];\n while (l--) {\n const child = children[l];\n const prev = l ? children[l - 1] : null;\n if (prev && isInline(child) && areAlike(child, prev)) {\n if (fakeRange.startContainer === child) {\n fakeRange.startContainer = prev;\n fakeRange.startOffset += getLength(prev);\n }\n if (fakeRange.endContainer === child) {\n fakeRange.endContainer = prev;\n fakeRange.endOffset += getLength(prev);\n }\n if (fakeRange.startContainer === node) {\n if (fakeRange.startOffset > l) {\n fakeRange.startOffset -= 1;\n } else if (fakeRange.startOffset === l) {\n fakeRange.startContainer = prev;\n fakeRange.startOffset = getLength(prev);\n }\n }\n if (fakeRange.endContainer === node) {\n if (fakeRange.endOffset > l) {\n fakeRange.endOffset -= 1;\n } else if (fakeRange.endOffset === l) {\n fakeRange.endContainer = prev;\n fakeRange.endOffset = getLength(prev);\n }\n }\n detach(child);\n if (child instanceof Text) {\n (prev as Text).appendData(child.data);\n } else {\n frags.push(empty(child));\n }\n } else if (child instanceof Element) {\n let frag: DocumentFragment | undefined;\n while ((frag = frags.pop())) {\n child.appendChild(frag);\n }\n _mergeInlines(child, fakeRange);\n }\n }\n};\n\nconst mergeInlines = (node: Node, range: Range): void => {\n const element = node instanceof Text ? node.parentNode : node;\n if (element instanceof Element) {\n const fakeRange = {\n startContainer: range.startContainer,\n startOffset: range.startOffset,\n endContainer: range.endContainer,\n endOffset: range.endOffset,\n };\n _mergeInlines(element, fakeRange);\n range.setStart(fakeRange.startContainer, fakeRange.startOffset);\n range.setEnd(fakeRange.endContainer, fakeRange.endOffset);\n }\n};\n\nconst mergeWithBlock = (\n block: Node,\n next: Node,\n range: Range,\n root: Element,\n): void => {\n let container = next;\n let parent: Node | null;\n let offset: number;\n while (\n (parent = container.parentNode) &&\n parent !== root &&\n parent instanceof Element &&\n parent.childNodes.length === 1\n ) {\n container = parent;\n }\n detach(container);\n\n offset = block.childNodes.length;\n\n // Remove extra <BR> fixer if present.\n const last = block.lastChild;\n if (last && last.nodeName === 'BR') {\n block.removeChild(last);\n offset -= 1;\n }\n\n block.appendChild(empty(next));\n\n range.setStart(block, offset);\n range.collapse(true);\n mergeInlines(block, range);\n};\n\nconst mergeContainers = (node: Node, root: Element): void => {\n const prev = node.previousSibling;\n const first = node.firstChild;\n const isListItem = node.nodeName === 'LI';\n\n // Do not merge LIs, unless it only contains a UL\n if (isListItem && (!first || !/^[OU]L$/.test(first.nodeName))) {\n return;\n }\n\n if (prev && areAlike(prev, node)) {\n if (!isContainer(prev)) {\n if (isListItem) {\n const block = createElement('DIV');\n block.appendChild(empty(prev));\n prev.appendChild(block);\n } else {\n return;\n }\n }\n detach(node);\n const needsFix = !isContainer(node);\n prev.appendChild(empty(node));\n if (needsFix) {\n fixContainer(prev, root);\n }\n if (first) {\n mergeContainers(first, root);\n }\n } else if (isListItem) {\n const block = createElement('DIV');\n node.insertBefore(block, first);\n fixCursor(block);\n }\n};\n\n// ---\n\nexport {\n fixContainer,\n fixCursor,\n mergeContainers,\n mergeInlines,\n mergeWithBlock,\n split,\n};\n", "import { notWS } from './Constants';\nimport { TreeIterator, SHOW_ELEMENT_OR_TEXT } from './node/TreeIterator';\nimport { createElement, empty, detach, replaceWith } from './node/Node';\nimport { isInline, isLeaf } from './node/Category';\nimport { fixContainer } from './node/MergeSplit';\nimport { isLineBreak } from './node/Whitespace';\n\nimport type { SquireConfig } from './Editor';\n\n// ---\n\ntype StyleRewriter = (\n node: HTMLElement,\n parent: Node,\n config: SquireConfig,\n) => HTMLElement;\n\n// ---\n\nconst styleToSemantic: Record<\n string,\n { regexp: RegExp; replace: (x: any, y: string) => HTMLElement }\n> = {\n 'font-weight': {\n regexp: /^bold|^700/i,\n replace(): HTMLElement {\n return createElement('B');\n },\n },\n 'font-style': {\n regexp: /^italic/i,\n replace(): HTMLElement {\n return createElement('I');\n },\n },\n 'font-family': {\n regexp: notWS,\n replace(\n classNames: { fontFamily: string },\n family: string,\n ): HTMLElement {\n return createElement('SPAN', {\n class: classNames.fontFamily,\n style: 'font-family:' + family,\n });\n },\n },\n 'font-size': {\n regexp: notWS,\n replace(classNames: { fontSize: string }, size: string): HTMLElement {\n return createElement('SPAN', {\n class: classNames.fontSize,\n style: 'font-size:' + size,\n });\n },\n },\n 'text-decoration': {\n regexp: /^underline/i,\n replace(): HTMLElement {\n return createElement('U');\n },\n },\n};\n\nconst replaceStyles = (\n node: HTMLElement,\n _: Node,\n config: SquireConfig,\n): HTMLElement => {\n const style = node.style;\n let newTreeBottom: HTMLElement | undefined;\n let newTreeTop: HTMLElement | undefined;\n\n for (const attr in styleToSemantic) {\n const converter = styleToSemantic[attr];\n const css = style.getPropertyValue(attr);\n if (css && converter.regexp.test(css)) {\n const el = converter.replace(config.classNames, css);\n if (\n el.nodeName === node.nodeName &&\n el.className === node.className\n ) {\n continue;\n }\n if (!newTreeTop) {\n newTreeTop = el;\n }\n if (newTreeBottom) {\n newTreeBottom.appendChild(el);\n }\n newTreeBottom = el;\n node.style.removeProperty(attr);\n }\n }\n\n if (newTreeTop && newTreeBottom) {\n newTreeBottom.appendChild(empty(node));\n if (node.style.cssText) {\n node.appendChild(newTreeTop);\n } else {\n replaceWith(node, newTreeTop);\n }\n }\n\n return newTreeBottom || node;\n};\n\nconst replaceWithTag = (tag: string) => {\n return (node: HTMLElement, parent: Node) => {\n const el = createElement(tag);\n const attributes = node.attributes;\n for (let i = 0, l = attributes.length; i < l; i += 1) {\n const attribute = attributes[i];\n el.setAttribute(attribute.name, attribute.value);\n }\n parent.replaceChild(el, node);\n el.appendChild(empty(node));\n return el;\n };\n};\n\nconst fontSizes: Record<string, string> = {\n '1': '10',\n '2': '13',\n '3': '16',\n '4': '18',\n '5': '24',\n '6': '32',\n '7': '48',\n};\n\nconst stylesRewriters: Record<string, StyleRewriter> = {\n STRONG: replaceWithTag('B'),\n EM: replaceWithTag('I'),\n INS: replaceWithTag('U'),\n STRIKE: replaceWithTag('S'),\n SPAN: replaceStyles,\n FONT: (\n node: HTMLElement,\n parent: Node,\n config: SquireConfig,\n ): HTMLElement => {\n const font = node as HTMLFontElement;\n const face = font.face;\n const size = font.size;\n let color = font.color;\n const classNames = config.classNames;\n let fontSpan: HTMLElement;\n let sizeSpan: HTMLElement;\n let colorSpan: HTMLElement;\n let newTreeBottom: HTMLElement | undefined;\n let newTreeTop: HTMLElement | undefined;\n if (face) {\n fontSpan = createElement('SPAN', {\n class: classNames.fontFamily,\n style: 'font-family:' + face,\n });\n newTreeTop = fontSpan;\n newTreeBottom = fontSpan;\n }\n if (size) {\n sizeSpan = createElement('SPAN', {\n class: classNames.fontSize,\n style: 'font-size:' + fontSizes[size] + 'px',\n });\n if (!newTreeTop) {\n newTreeTop = sizeSpan;\n }\n if (newTreeBottom) {\n newTreeBottom.appendChild(sizeSpan);\n }\n newTreeBottom = sizeSpan;\n }\n if (color && /^#?([\\dA-F]{3}){1,2}$/i.test(color)) {\n if (color.charAt(0) !== '#') {\n color = '#' + color;\n }\n colorSpan = createElement('SPAN', {\n class: classNames.color,\n style: 'color:' + color,\n });\n if (!newTreeTop) {\n newTreeTop = colorSpan;\n }\n if (newTreeBottom) {\n newTreeBottom.appendChild(colorSpan);\n }\n newTreeBottom = colorSpan;\n }\n if (!newTreeTop || !newTreeBottom) {\n newTreeTop = newTreeBottom = createElement('SPAN');\n }\n parent.replaceChild(newTreeTop, font);\n newTreeBottom.appendChild(empty(font));\n return newTreeBottom;\n },\n TT: (node: Node, parent: Node, config: SquireConfig): HTMLElement => {\n const el = createElement('SPAN', {\n class: config.classNames.fontFamily,\n style: 'font-family:menlo,consolas,\"courier new\",monospace',\n });\n parent.replaceChild(el, node);\n el.appendChild(empty(node));\n return el;\n },\n};\n\nconst allowedBlock =\n /^(?:A(?:DDRESS|RTICLE|SIDE|UDIO)|BLOCKQUOTE|CAPTION|D(?:[DLT]|IV)|F(?:IGURE|IGCAPTION|OOTER)|H[1-6]|HEADER|L(?:ABEL|EGEND|I)|O(?:L|UTPUT)|P(?:RE)?|SECTION|T(?:ABLE|BODY|D|FOOT|H|HEAD|R)|COL(?:GROUP)?|UL)$/;\n\nconst blacklist = /^(?:HEAD|META|STYLE)/;\n\n/*\n Two purposes:\n\n 1. Remove nodes we don't want, such as weird <o:p> tags, comment nodes\n and whitespace nodes.\n 2. Convert inline tags into our preferred format.\n*/\nconst cleanTree = (\n node: Node,\n config: SquireConfig,\n preserveWS?: boolean,\n): Node => {\n const children = node.childNodes;\n\n let nonInlineParent = node;\n while (isInline(nonInlineParent)) {\n nonInlineParent = nonInlineParent.parentNode!;\n }\n const walker = new TreeIterator<Element | Text>(\n nonInlineParent,\n SHOW_ELEMENT_OR_TEXT,\n );\n\n for (let i = 0, l = children.length; i < l; i += 1) {\n let child = children[i];\n const nodeName = child.nodeName;\n const rewriter = stylesRewriters[nodeName];\n if (child instanceof HTMLElement) {\n const childLength = child.childNodes.length;\n if (rewriter) {\n child = rewriter(child, node, config);\n } else if (blacklist.test(nodeName)) {\n node.removeChild(child);\n i -= 1;\n l -= 1;\n continue;\n } else if (!allowedBlock.test(nodeName) && !isInline(child)) {\n i -= 1;\n l += childLength - 1;\n node.replaceChild(empty(child), child);\n continue;\n }\n if (childLength) {\n cleanTree(child, config, preserveWS || nodeName === 'PRE');\n }\n } else {\n if (child instanceof Text) {\n let data = child.data;\n const startsWithWS = !notWS.test(data.charAt(0));\n const endsWithWS = !notWS.test(data.charAt(data.length - 1));\n if (preserveWS || (!startsWithWS && !endsWithWS)) {\n continue;\n }\n // Iterate through the nodes; if we hit some other content\n // before the start of a new block we don't trim\n if (startsWithWS) {\n walker.currentNode = child;\n let sibling;\n while ((sibling = walker.previousPONode())) {\n if (\n sibling.nodeName === 'IMG' ||\n (sibling instanceof Text &&\n notWS.test(sibling.data))\n ) {\n break;\n }\n if (!isInline(sibling)) {\n sibling = null;\n break;\n }\n }\n data = data.replace(/^[ \\t\\r\\n]+/g, sibling ? ' ' : '');\n }\n if (endsWithWS) {\n walker.currentNode = child;\n let sibling;\n while ((sibling = walker.nextNode())) {\n if (\n sibling.nodeName === 'IMG' ||\n (sibling instanceof Text &&\n notWS.test(sibling.data))\n ) {\n break;\n }\n if (!isInline(sibling)) {\n sibling = null;\n break;\n }\n }\n data = data.replace(/[ \\t\\r\\n]+$/g, sibling ? ' ' : '');\n }\n if (data) {\n child.data = data;\n continue;\n }\n }\n node.removeChild(child);\n i -= 1;\n l -= 1;\n }\n }\n return node;\n};\n\n// ---\n\nconst removeEmptyInlines = (node: Node): void => {\n const children = node.childNodes;\n let l = children.length;\n while (l--) {\n const child = children[l];\n if (child instanceof Element && !isLeaf(child)) {\n removeEmptyInlines(child);\n if (isInline(child) && !child.firstChild) {\n node.removeChild(child);\n }\n } else if (child instanceof Text && !child.data) {\n node.removeChild(child);\n }\n }\n};\n\n// ---\n\n// <br> elements are treated specially, and differently depending on the\n// browser, when in rich text editor mode. When adding HTML from external\n// sources, we must remove them, replacing the ones that actually affect\n// line breaks by wrapping the inline text in a <div>. Browsers that want <br>\n// elements at the end of each block will then have them added back in a later\n// fixCursor method call.\nconst cleanupBRs = (\n node: Element | DocumentFragment,\n root: Element,\n keepForBlankLine: boolean,\n): void => {\n const brs: NodeListOf<HTMLBRElement> = node.querySelectorAll('BR');\n const brBreaksLine: boolean[] = [];\n let l = brs.length;\n\n // Must calculate whether the <br> breaks a line first, because if we\n // have two <br>s next to each other, after the first one is converted\n // to a block split, the second will be at the end of a block and\n // therefore seem to not be a line break. But in its original context it\n // was, so we should also convert it to a block split.\n for (let i = 0; i < l; i += 1) {\n brBreaksLine[i] = isLineBreak(brs[i], keepForBlankLine);\n }\n while (l--) {\n const br = brs[l];\n // Cleanup may have removed it\n const parent = br.parentNode;\n if (!parent) {\n continue;\n }\n // If it doesn't break a line, just remove it; it's not doing\n // anything useful. We'll add it back later if required by the\n // browser. If it breaks a line, wrap the content in div tags\n // and replace the brs.\n if (!brBreaksLine[l]) {\n detach(br);\n } else if (!isInline(parent)) {\n fixContainer(parent, root);\n }\n }\n};\n\n// ---\n\nconst escapeHTML = (text: string): string => {\n return text\n .split('&')\n .join('&amp;')\n .split('<')\n .join('&lt;')\n .split('>')\n .join('&gt;')\n .split('\"')\n .join('&quot;');\n};\n\n// ---\n\nexport { cleanTree, cleanupBRs, isLineBreak, removeEmptyInlines, escapeHTML };\n", "import { TreeIterator, SHOW_ELEMENT } from './TreeIterator';\nimport { isBlock } from './Category';\n\n// ---\n\nconst getBlockWalker = (\n node: Node,\n root: Element | DocumentFragment,\n): TreeIterator<HTMLElement> => {\n const walker = new TreeIterator<HTMLElement>(root, SHOW_ELEMENT, isBlock);\n walker.currentNode = node;\n return walker;\n};\n\nconst getPreviousBlock = (\n node: Node,\n root: Element | DocumentFragment,\n): HTMLElement | null => {\n const block = getBlockWalker(node, root).previousNode();\n return block !== root ? block : null;\n};\n\nconst getNextBlock = (\n node: Node,\n root: Element | DocumentFragment,\n): HTMLElement | null => {\n const block = getBlockWalker(node, root).nextNode();\n return block !== root ? block : null;\n};\n\nconst isEmptyBlock = (block: Element): boolean => {\n return !block.textContent && !block.querySelector('IMG');\n};\n\n// ---\n\nexport { getBlockWalker, getPreviousBlock, getNextBlock, isEmptyBlock };\n", "import { isInline, isBlock } from '../node/Category';\nimport { getPreviousBlock, getNextBlock } from '../node/Block';\nimport { getNodeBeforeOffset, getNodeAfterOffset } from '../node/Node';\nimport { ZWS, notWS } from '../Constants';\nimport { isNodeContainedInRange } from './Boundaries';\nimport { TreeIterator, SHOW_ELEMENT_OR_TEXT } from '../node/TreeIterator';\n\n// ---\n\n// Returns the first block at least partially contained by the range,\n// or null if no block is contained by the range.\nconst getStartBlockOfRange = (\n range: Range,\n root: Element | DocumentFragment,\n): HTMLElement | null => {\n const container = range.startContainer;\n let block: HTMLElement | null;\n\n // If inline, get the containing block.\n if (isInline(container)) {\n block = getPreviousBlock(container, root);\n } else if (\n container !== root &&\n container instanceof HTMLElement &&\n isBlock(container)\n ) {\n block = container;\n } else {\n const node = getNodeBeforeOffset(container, range.startOffset);\n block = getNextBlock(node, root);\n }\n // Check the block actually intersects the range\n return block && isNodeContainedInRange(range, block, true) ? block : null;\n};\n\n// Returns the last block at least partially contained by the range,\n// or null if no block is contained by the range.\nconst getEndBlockOfRange = (\n range: Range,\n root: Element | DocumentFragment,\n): HTMLElement | null => {\n const container = range.endContainer;\n let block: HTMLElement | null;\n\n // If inline, get the containing block.\n if (isInline(container)) {\n block = getPreviousBlock(container, root);\n } else if (\n container !== root &&\n container instanceof HTMLElement &&\n isBlock(container)\n ) {\n block = container;\n } else {\n let node = getNodeAfterOffset(container, range.endOffset);\n if (!node || !root.contains(node)) {\n node = root;\n let child: Node | null;\n while ((child = node.lastChild)) {\n node = child;\n }\n }\n block = getPreviousBlock(node, root);\n }\n // Check the block actually intersects the range\n return block && isNodeContainedInRange(range, block, true) ? block : null;\n};\n\nconst isContent = (node: Element | Text): boolean => {\n return node instanceof Text\n ? notWS.test(node.data)\n : node.nodeName === 'IMG';\n};\n\nconst rangeDoesStartAtBlockBoundary = (\n range: Range,\n root: Element,\n): boolean => {\n const startContainer = range.startContainer;\n const startOffset = range.startOffset;\n let nodeAfterCursor: Node | null;\n\n // If in the middle or end of a text node, we're not at the boundary.\n if (startContainer instanceof Text) {\n const text = startContainer.data;\n for (let i = startOffset; i > 0; i -= 1) {\n if (text.charAt(i - 1) !== ZWS) {\n return false;\n }\n }\n nodeAfterCursor = startContainer;\n } else {\n nodeAfterCursor = getNodeAfterOffset(startContainer, startOffset);\n if (nodeAfterCursor && !root.contains(nodeAfterCursor)) {\n nodeAfterCursor = null;\n }\n // The cursor was right at the end of the document\n if (!nodeAfterCursor) {\n nodeAfterCursor = getNodeBeforeOffset(startContainer, startOffset);\n if (nodeAfterCursor instanceof Text && nodeAfterCursor.length) {\n return false;\n }\n }\n }\n\n // Otherwise, look for any previous content in the same block.\n const block = getStartBlockOfRange(range, root);\n if (!block) {\n return false;\n }\n const contentWalker = new TreeIterator<Element | Text>(\n block,\n SHOW_ELEMENT_OR_TEXT,\n isContent,\n );\n contentWalker.currentNode = nodeAfterCursor;\n\n return !contentWalker.previousNode();\n};\n\nconst rangeDoesEndAtBlockBoundary = (range: Range, root: Element): boolean => {\n const endContainer = range.endContainer;\n const endOffset = range.endOffset;\n let currentNode: Node;\n\n // If in a text node with content, and not at the end, we're not\n // at the boundary. Ignore ZWS.\n if (endContainer instanceof Text) {\n const text = endContainer.data;\n const length = text.length;\n for (let i = endOffset; i < length; i += 1) {\n if (text.charAt(i) !== ZWS) {\n return false;\n }\n }\n currentNode = endContainer;\n } else {\n currentNode = getNodeBeforeOffset(endContainer, endOffset);\n }\n\n // Otherwise, look for any further content in the same block.\n const block = getEndBlockOfRange(range, root);\n if (!block) {\n return false;\n }\n const contentWalker = new TreeIterator<Element | Text>(\n block,\n SHOW_ELEMENT_OR_TEXT,\n isContent,\n );\n contentWalker.currentNode = currentNode;\n return !contentWalker.nextNode();\n};\n\nconst expandRangeToBlockBoundaries = (range: Range, root: Element): void => {\n const start = getStartBlockOfRange(range, root);\n const end = getEndBlockOfRange(range, root);\n let parent: Node;\n\n if (start && end) {\n parent = start.parentNode!;\n range.setStart(parent, Array.from(parent.childNodes).indexOf(start));\n parent = end.parentNode!;\n range.setEnd(parent, Array.from(parent.childNodes).indexOf(end) + 1);\n }\n};\n\n// ---\n\nexport {\n getStartBlockOfRange,\n getEndBlockOfRange,\n rangeDoesStartAtBlockBoundary,\n rangeDoesEndAtBlockBoundary,\n expandRangeToBlockBoundaries,\n};\n", "import { cleanupBRs } from '../Clean';\nimport {\n split,\n fixCursor,\n mergeWithBlock,\n fixContainer,\n mergeContainers,\n} from '../node/MergeSplit';\nimport { detach, getNearest, getLength } from '../node/Node';\nimport { TreeIterator, SHOW_ELEMENT_OR_TEXT } from '../node/TreeIterator';\nimport { isInline, isContainer, isLeaf } from '../node/Category';\nimport { getNextBlock, isEmptyBlock, getPreviousBlock } from '../node/Block';\nimport {\n getStartBlockOfRange,\n getEndBlockOfRange,\n rangeDoesEndAtBlockBoundary,\n rangeDoesStartAtBlockBoundary,\n} from './Block';\nimport {\n moveRangeBoundariesDownTree,\n moveRangeBoundariesUpTree,\n} from './Boundaries';\n\n// ---\n\nfunction createRange(startContainer: Node, startOffset: number): Range;\nfunction createRange(\n startContainer: Node,\n startOffset: number,\n endContainer: Node,\n endOffset: number,\n): Range;\nfunction createRange(\n startContainer: Node,\n startOffset: number,\n endContainer?: Node,\n endOffset?: number,\n): Range {\n const range = document.createRange();\n range.setStart(startContainer, startOffset);\n if (endContainer && typeof endOffset === 'number') {\n range.setEnd(endContainer, endOffset);\n } else {\n range.setEnd(startContainer, startOffset);\n }\n return range;\n}\n\nconst insertNodeInRange = (range: Range, node: Node): void => {\n // Insert at start.\n let { startContainer, startOffset, endContainer, endOffset } = range;\n let children: NodeListOf<ChildNode>;\n\n // If part way through a text node, split it.\n if (startContainer instanceof Text) {\n const parent = startContainer.parentNode!;\n children = parent.childNodes;\n if (startOffset === startContainer.length) {\n startOffset = Array.from(children).indexOf(startContainer) + 1;\n if (range.collapsed) {\n endContainer = parent;\n endOffset = startOffset;\n }\n } else {\n if (startOffset) {\n const afterSplit = startContainer.splitText(startOffset);\n if (endContainer === startContainer) {\n endOffset -= startOffset;\n endContainer = afterSplit;\n } else if (endContainer === parent) {\n endOffset += 1;\n }\n startContainer = afterSplit;\n }\n startOffset = Array.from(children).indexOf(\n startContainer as ChildNode,\n );\n }\n startContainer = parent;\n } else {\n children = startContainer.childNodes;\n }\n\n const childCount = children.length;\n\n if (startOffset === childCount) {\n startContainer.appendChild(node);\n } else {\n startContainer.insertBefore(node, children[startOffset]);\n }\n\n if (startContainer === endContainer) {\n endOffset += children.length - childCount;\n }\n\n range.setStart(startContainer, startOffset);\n range.setEnd(endContainer, endOffset);\n};\n\n/**\n * Removes the contents of the range and returns it as a DocumentFragment.\n * The range at the end will be at the same position, with the edges just\n * before/after the split. If the start/end have the same parents, it will\n * be collapsed.\n */\nconst extractContentsOfRange = (\n range: Range,\n common: Node | null,\n root: Element,\n): DocumentFragment => {\n const frag = document.createDocumentFragment();\n if (range.collapsed) {\n return frag;\n }\n\n if (!common) {\n common = range.commonAncestorContainer;\n }\n if (common instanceof Text) {\n common = common.parentNode!;\n }\n\n const startContainer = range.startContainer;\n const startOffset = range.startOffset;\n\n let endContainer = split(range.endContainer, range.endOffset, common, root);\n let endOffset = 0;\n\n let node = split(startContainer, startOffset, common, root);\n while (node && node !== endContainer) {\n const next = node.nextSibling;\n frag.appendChild(node);\n node = next;\n }\n\n // Merge text nodes if adjacent\n node = endContainer && endContainer.previousSibling;\n if (node && node instanceof Text && endContainer instanceof Text) {\n endOffset = node.length;\n node.appendData(endContainer.data);\n detach(endContainer);\n endContainer = node;\n }\n\n range.setStart(startContainer, startOffset);\n if (endContainer) {\n range.setEnd(endContainer, endOffset);\n } else {\n // endContainer will be null if at end of parent's child nodes list.\n range.setEnd(common, common.childNodes.length);\n }\n\n fixCursor(common);\n\n return frag;\n};\n\n/**\n * Returns the next/prev node that's part of the same inline content.\n */\nconst getAdjacentInlineNode = (\n iterator: TreeIterator<Node>,\n method: 'nextNode' | 'previousPONode',\n node: Node,\n): Node | null => {\n iterator.currentNode = node;\n let nextNode: Node | null;\n while ((nextNode = iterator[method]())) {\n if (nextNode instanceof Text || isLeaf(nextNode)) {\n return nextNode;\n }\n if (!isInline(nextNode)) {\n return null;\n }\n }\n return null;\n};\n\nconst deleteContentsOfRange = (\n range: Range,\n root: Element,\n): DocumentFragment => {\n const startBlock = getStartBlockOfRange(range, root);\n let endBlock = getEndBlockOfRange(range, root);\n const needsMerge = startBlock !== endBlock;\n\n // Move boundaries up as much as possible without exiting block,\n // to reduce need to split.\n if (startBlock && endBlock) {\n moveRangeBoundariesDownTree(range);\n moveRangeBoundariesUpTree(range, startBlock, endBlock, root);\n }\n\n // Remove selected range\n const frag = extractContentsOfRange(range, null, root);\n\n // Move boundaries back down tree as far as possible.\n moveRangeBoundariesDownTree(range);\n\n // If we split into two different blocks, merge the blocks.\n if (needsMerge) {\n // endBlock will have been split, so need to refetch\n endBlock = getEndBlockOfRange(range, root);\n if (startBlock && endBlock && startBlock !== endBlock) {\n mergeWithBlock(startBlock, endBlock, range, root);\n }\n }\n\n // Ensure block has necessary children\n if (startBlock) {\n fixCursor(startBlock);\n }\n\n // Ensure root has a block-level element in it.\n const child = root.firstChild;\n if (!child || child.nodeName === 'BR') {\n fixCursor(root);\n if (root.firstChild) {\n range.selectNodeContents(root.firstChild);\n }\n }\n\n range.collapse(true);\n\n // Now we may need to swap a space for a nbsp if the browser is going\n // to swallow it due to HTML whitespace rules:\n const startContainer = range.startContainer;\n const startOffset = range.startOffset;\n const iterator = new TreeIterator(root, SHOW_ELEMENT_OR_TEXT);\n\n // Find the character after cursor point\n let afterNode: Node | null = startContainer;\n let afterOffset = startOffset;\n if (!(afterNode instanceof Text) || afterOffset === afterNode.data.length) {\n afterNode = getAdjacentInlineNode(iterator, 'nextNode', afterNode);\n afterOffset = 0;\n }\n\n // Find the character before cursor point\n let beforeNode: Node | null = startContainer;\n let beforeOffset = startOffset - 1;\n if (!(beforeNode instanceof Text) || beforeOffset === -1) {\n beforeNode = getAdjacentInlineNode(\n iterator,\n 'previousPONode',\n afterNode ||\n (startContainer instanceof Text\n ? startContainer\n : startContainer.childNodes[startOffset] || startContainer),\n );\n if (beforeNode instanceof Text) {\n beforeOffset = beforeNode.data.length;\n }\n }\n\n // If range starts at block boundary and character after cursor point\n // is a space, replace with nbsp\n let node = null;\n let offset = 0;\n if (\n afterNode instanceof Text &&\n afterNode.data.charAt(afterOffset) === ' ' &&\n rangeDoesStartAtBlockBoundary(range, root)\n ) {\n node = afterNode;\n offset = afterOffset;\n } else if (\n beforeNode instanceof Text &&\n beforeNode.data.charAt(beforeOffset) === ' '\n ) {\n // If character before cursor point is a space, replace with nbsp\n // if either:\n // a) There is a space after it; or\n // b) The point after is the end of the block\n if (\n (afterNode instanceof Text &&\n afterNode.data.charAt(afterOffset) === ' ') ||\n rangeDoesEndAtBlockBoundary(range, root)\n ) {\n node = beforeNode;\n offset = beforeOffset;\n }\n }\n if (node) {\n node.replaceData(offset, 1, '\u00A0'); // nbsp\n }\n // Range needs to be put back in place\n range.setStart(startContainer, startOffset);\n range.collapse(true);\n\n return frag;\n};\n\n// Contents of range will be deleted.\n// After method, range will be around inserted content\nconst insertTreeFragmentIntoRange = (\n range: Range,\n frag: DocumentFragment,\n root: Element,\n): void => {\n const firstInFragIsInline = frag.firstChild && isInline(frag.firstChild);\n let node: Node | null;\n\n // Fixup content: ensure no top-level inline, and add cursor fix elements.\n fixContainer(frag, root);\n node = frag;\n while ((node = getNextBlock(node, root))) {\n fixCursor(node);\n }\n\n // Delete any selected content.\n if (!range.collapsed) {\n deleteContentsOfRange(range, root);\n }\n\n // Move range down into text nodes.\n moveRangeBoundariesDownTree(range);\n range.collapse(false); // collapse to end\n\n // Where will we split up to? First blockquote parent, otherwise root.\n const stopPoint =\n getNearest(range.endContainer, root, 'BLOCKQUOTE') || root;\n\n // Merge the contents of the first block in the frag with the focused block.\n // If there are contents in the block after the focus point, collect this\n // up to insert in the last block later. This preserves the style that was\n // present in this bit of the page.\n //\n // If the block being inserted into is empty though, replace it instead of\n // merging if the fragment had block contents.\n // e.g. <blockquote><p>Foo</p></blockquote>\n // This seems a reasonable approximation of user intent.\n let block = getStartBlockOfRange(range, root);\n let blockContentsAfterSplit: DocumentFragment | null = null;\n const firstBlockInFrag = getNextBlock(frag, frag);\n const replaceBlock = !firstInFragIsInline && !!block && isEmptyBlock(block);\n if (\n block &&\n firstBlockInFrag &&\n !replaceBlock &&\n // Don't merge table cells or PRE elements into block\n !getNearest(firstBlockInFrag, frag, 'PRE') &&\n !getNearest(firstBlockInFrag, frag, 'TABLE')\n ) {\n moveRangeBoundariesUpTree(range, block, block, root);\n range.collapse(true); // collapse to start\n let container = range.endContainer;\n let offset = range.endOffset;\n // Remove trailing <br> \u2013 we don't want this considered content to be\n // inserted again later\n cleanupBRs(block as HTMLElement, root, false);\n if (isInline(container)) {\n // Split up to block parent.\n const nodeAfterSplit = split(\n container,\n offset,\n getPreviousBlock(container, root) || root,\n root,\n ) as Node;\n container = nodeAfterSplit.parentNode!;\n offset = Array.from(container.childNodes).indexOf(\n nodeAfterSplit as ChildNode,\n );\n }\n if (/*isBlock( container ) && */ offset !== getLength(container)) {\n // Collect any inline contents of the block after the range point\n blockContentsAfterSplit = document.createDocumentFragment();\n while ((node = container.childNodes[offset])) {\n blockContentsAfterSplit.appendChild(node);\n }\n }\n // And merge the first block in.\n mergeWithBlock(container, firstBlockInFrag, range, root);\n\n // And where we will insert\n offset =\n Array.from(container.parentNode!.childNodes).indexOf(\n container as ChildNode,\n ) + 1;\n container = container.parentNode!;\n range.setEnd(container, offset);\n }\n\n // Is there still any content in the fragment?\n if (getLength(frag)) {\n if (replaceBlock && block) {\n range.setEndBefore(block);\n range.collapse(false);\n detach(block);\n }\n moveRangeBoundariesUpTree(range, stopPoint, stopPoint, root);\n // Now split after block up to blockquote (if a parent) or root\n let nodeAfterSplit = split(\n range.endContainer,\n range.endOffset,\n stopPoint,\n root,\n ) as Node | null;\n const nodeBeforeSplit = nodeAfterSplit\n ? nodeAfterSplit.previousSibling\n : stopPoint.lastChild;\n stopPoint.insertBefore(frag, nodeAfterSplit);\n if (nodeAfterSplit) {\n range.setEndBefore(nodeAfterSplit);\n } else {\n range.setEnd(stopPoint, getLength(stopPoint));\n }\n block = getEndBlockOfRange(range, root);\n\n // Get a reference that won't be invalidated if we merge containers.\n moveRangeBoundariesDownTree(range);\n const container = range.endContainer;\n const offset = range.endOffset;\n\n // Merge inserted containers with edges of split\n if (nodeAfterSplit && isContainer(nodeAfterSplit)) {\n mergeContainers(nodeAfterSplit, root);\n }\n nodeAfterSplit = nodeBeforeSplit && nodeBeforeSplit.nextSibling;\n if (nodeAfterSplit && isContainer(nodeAfterSplit)) {\n mergeContainers(nodeAfterSplit, root);\n }\n range.setEnd(container, offset);\n }\n\n // Insert inline content saved from before.\n if (blockContentsAfterSplit && block) {\n const tempRange = range.cloneRange();\n fixCursor(blockContentsAfterSplit);\n mergeWithBlock(block, blockContentsAfterSplit, tempRange, root);\n range.setEnd(tempRange.endContainer, tempRange.endOffset);\n }\n moveRangeBoundariesDownTree(range);\n};\n\n// ---\n\nexport {\n createRange,\n deleteContentsOfRange,\n extractContentsOfRange,\n insertNodeInRange,\n insertTreeFragmentIntoRange,\n};\n", "import { SHOW_ELEMENT_OR_TEXT, TreeIterator } from '../node/TreeIterator';\nimport { isNodeContainedInRange } from './Boundaries';\nimport { isInline } from '../node/Category';\n\n// ---\n\nconst getTextContentsOfRange = (range: Range) => {\n if (range.collapsed) {\n return '';\n }\n const startContainer = range.startContainer;\n const endContainer = range.endContainer;\n const walker = new TreeIterator<Element | Text>(\n range.commonAncestorContainer,\n SHOW_ELEMENT_OR_TEXT,\n (node) => {\n return isNodeContainedInRange(range, node, true);\n },\n );\n walker.currentNode = startContainer;\n\n let node: Node | null = startContainer;\n let textContent = '';\n let addedTextInBlock = false;\n let value: string;\n\n if (\n (!(node instanceof Element) && !(node instanceof Text)) ||\n !walker.filter(node)\n ) {\n node = walker.nextNode();\n }\n\n while (node) {\n if (node instanceof Text) {\n value = node.data;\n if (value && /\\S/.test(value)) {\n if (node === endContainer) {\n value = value.slice(0, range.endOffset);\n }\n if (node === startContainer) {\n value = value.slice(range.startOffset);\n }\n textContent += value;\n addedTextInBlock = true;\n }\n } else if (\n node.nodeName === 'BR' ||\n (addedTextInBlock && !isInline(node))\n ) {\n textContent += '\\n';\n addedTextInBlock = false;\n }\n node = walker.nextNode();\n }\n // Replace nbsp with regular space;\n // eslint-disable-next-line no-irregular-whitespace\n textContent = textContent.replace(/\u00A0/g, ' ');\n\n return textContent;\n};\n\n// ---\n\nexport { getTextContentsOfRange };\n", "import { isWin, isGecko, isLegacyEdge, notWS } from './Constants';\nimport { createElement, detach } from './node/Node';\nimport { getStartBlockOfRange, getEndBlockOfRange } from './range/Block';\nimport { createRange, deleteContentsOfRange } from './range/InsertDelete';\nimport {\n moveRangeBoundariesDownTree,\n moveRangeBoundariesUpTree,\n} from './range/Boundaries';\n\nimport type { Squire } from './Editor';\nimport { getTextContentsOfRange } from './range/Contents';\n\n// ---\n\nconst indexOf = Array.prototype.indexOf;\n\nconst extractRangeToClipboard = (\n event: ClipboardEvent,\n range: Range,\n root: HTMLElement,\n removeRangeFromDocument: boolean,\n toCleanHTML: null | ((html: string) => string),\n toPlainText: null | ((html: string) => string),\n plainTextOnly: boolean,\n): boolean => {\n // Edge only seems to support setting plain text as of 2016-03-11.\n const clipboardData = event.clipboardData;\n if (isLegacyEdge || !clipboardData) {\n return false;\n }\n // First get the plain text version from the range (unless we have a custom\n // HTML -> Text conversion fn)\n let text = toPlainText ? '' : getTextContentsOfRange(range);\n\n // Clipboard content should include all parents within block, or all\n // parents up to root if selection across blocks\n const startBlock = getStartBlockOfRange(range, root);\n const endBlock = getEndBlockOfRange(range, root);\n let copyRoot = root;\n\n // If the content is not in well-formed blocks, the start and end block\n // may be the same, but actually the range goes outside it. Must check!\n if (\n startBlock === endBlock &&\n startBlock?.contains(range.commonAncestorContainer)\n ) {\n copyRoot = startBlock;\n }\n\n // Extract the contents\n let contents: Node;\n if (removeRangeFromDocument) {\n contents = deleteContentsOfRange(range, root);\n } else {\n // Clone range to mutate, then move up as high as possible without\n // passing the copy root node.\n range = range.cloneRange();\n moveRangeBoundariesDownTree(range);\n moveRangeBoundariesUpTree(range, copyRoot, copyRoot, root);\n contents = range.cloneContents();\n }\n\n // Add any other parents not in extracted content, up to copy root\n let parent = range.commonAncestorContainer;\n if (parent instanceof Text) {\n parent = parent.parentNode!;\n }\n while (parent && parent !== copyRoot) {\n const newContents = parent.cloneNode(false);\n newContents.appendChild(contents);\n contents = newContents;\n parent = parent.parentNode!;\n }\n\n // Get HTML version of data\n let html: string | undefined;\n if (\n contents.childNodes.length === 1 &&\n contents.childNodes[0] instanceof Text\n ) {\n // Replace nbsp with regular space;\n // eslint-disable-next-line no-irregular-whitespace\n text = contents.childNodes[0].data.replace(/\u00A0/g, ' ');\n plainTextOnly = true;\n } else {\n const node = createElement('DIV') as HTMLDivElement;\n node.appendChild(contents);\n html = node.innerHTML;\n if (toCleanHTML) {\n html = toCleanHTML(html);\n }\n }\n\n // Get Text version of data if converting from HTML\n if (toPlainText && html !== undefined) {\n text = toPlainText(html);\n }\n\n // Firefox (and others?) returns unix line endings (\\n) even on Windows.\n // If on Windows, normalise to \\r\\n, since Notepad and some other crappy\n // apps do not understand just \\n.\n if (isWin) {\n text = text.replace(/\\r?\\n/g, '\\r\\n');\n }\n\n // Set clipboard data\n if (!plainTextOnly && html && text !== html) {\n html = '<!-- squire -->' + html;\n clipboardData.setData('text/html', html);\n }\n clipboardData.setData('text/plain', text);\n event.preventDefault();\n\n return true;\n};\n\n// ---\n\nconst _onCut = function (this: Squire, event: ClipboardEvent): void {\n const range: Range = this.getSelection();\n const root: HTMLElement = this._root;\n\n // Nothing to do\n if (range.collapsed) {\n event.preventDefault();\n return;\n }\n\n // Save undo checkpoint\n this.saveUndoState(range);\n\n const handled = extractRangeToClipboard(\n event,\n range,\n root,\n true,\n this._config.willCutCopy,\n this._config.toPlainText,\n false,\n );\n if (!handled) {\n setTimeout(() => {\n try {\n // If all content removed, ensure div at start of root.\n this._ensureBottomLine();\n } catch (error) {\n this._config.didError(error);\n }\n }, 0);\n }\n\n this.setSelection(range);\n};\n\nconst _onCopy = function (this: Squire, event: ClipboardEvent): void {\n extractRangeToClipboard(\n event,\n this.getSelection(),\n this._root,\n false,\n this._config.willCutCopy,\n this._config.toPlainText,\n false,\n );\n};\n\n// Need to monitor for shift key like this, as event.shiftKey is not available\n// in paste event.\nconst _monitorShiftKey = function (this: Squire, event: KeyboardEvent): void {\n this._isShiftDown = event.shiftKey;\n};\n\nconst _onPaste = function (this: Squire, event: ClipboardEvent): void {\n const clipboardData = event.clipboardData;\n const items = clipboardData?.items;\n const choosePlain: boolean | undefined = this._isShiftDown;\n let hasRTF = false;\n let hasImage = false;\n let plainItem: null | DataTransferItem = null;\n let htmlItem: null | DataTransferItem = null;\n\n // Current HTML5 Clipboard interface\n // ---------------------------------\n // https://html.spec.whatwg.org/multipage/interaction.html\n if (items) {\n let l = items.length;\n while (l--) {\n const item = items[l];\n const type = item.type;\n if (type === 'text/html') {\n htmlItem = item;\n // iOS copy URL gives you type text/uri-list which is just a list\n // of 1 or more URLs separated by new lines. Can just treat as\n // plain text.\n } else if (type === 'text/plain' || type === 'text/uri-list') {\n plainItem = item;\n } else if (type === 'text/rtf') {\n hasRTF = true;\n } else if (/^image\\/.*/.test(type)) {\n hasImage = true;\n }\n }\n\n // Treat image paste as a drop of an image file. When you copy\n // an image in Chrome/Firefox (at least), it copies the image data\n // but also an HTML version (referencing the original URL of the image)\n // and a plain text version.\n //\n // However, when you copy in Excel, you get html, rtf, text, image;\n // in this instance you want the html version! So let's try using\n // the presence of text/rtf as an indicator to choose the html version\n // over the image.\n if (hasImage && !(hasRTF && htmlItem)) {\n event.preventDefault();\n this.fireEvent('pasteImage', {\n clipboardData,\n });\n return;\n }\n\n // Edge only provides access to plain text as of 2016-03-11 and gives no\n // indication there should be an HTML part. However, it does support\n // access to image data, so we check for that first. Otherwise though,\n // fall through to fallback clipboard handling methods\n if (!isLegacyEdge) {\n event.preventDefault();\n if (htmlItem && (!choosePlain || !plainItem)) {\n htmlItem.getAsString((html) => {\n this.insertHTML(html, true);\n });\n } else if (plainItem) {\n plainItem.getAsString((text) => {\n // If we have a selection and text is solely a URL,\n // just make the text a link.\n let isLink = false;\n const range = this.getSelection();\n if (!range.collapsed && notWS.test(range.toString())) {\n const match = this.linkRegExp.exec(text);\n isLink = !!match && match[0].length === text.length;\n }\n if (isLink) {\n this.makeLink(text);\n } else {\n this.insertPlainText(text, true);\n }\n });\n }\n return;\n }\n }\n\n // Old interface\n // -------------\n\n // Safari (and indeed many other OS X apps) copies stuff as text/rtf\n // rather than text/html; even from a webpage in Safari. The only way\n // to get an HTML version is to fallback to letting the browser insert\n // the content. Same for getting image data. *Sigh*.\n //\n // Firefox is even worse: it doesn't even let you know that there might be\n // an RTF version on the clipboard, but it will also convert to HTML if you\n // let the browser insert the content. I've filed\n // https://bugzilla.mozilla.org/show_bug.cgi?id=1254028\n const types = clipboardData?.types;\n if (\n !isLegacyEdge &&\n types &&\n (indexOf.call(types, 'text/html') > -1 ||\n (!isGecko &&\n indexOf.call(types, 'text/plain') > -1 &&\n indexOf.call(types, 'text/rtf') < 0))\n ) {\n event.preventDefault();\n // Abiword on Linux copies a plain text and html version, but the HTML\n // version is the empty string! So always try to get HTML, but if none,\n // insert plain text instead. On iOS, Facebook (and possibly other\n // apps?) copy links as type text/uri-list, but also insert a **blank**\n // text/plain item onto the clipboard. Why? Who knows.\n let data;\n if (!choosePlain && (data = clipboardData.getData('text/html'))) {\n this.insertHTML(data, true);\n } else if (\n (data = clipboardData.getData('text/plain')) ||\n (data = clipboardData.getData('text/uri-list'))\n ) {\n this.insertPlainText(data, true);\n }\n return;\n }\n\n // No interface. Includes all versions of IE :(\n // --------------------------------------------\n\n const body = document.body;\n const range = this.getSelection();\n const startContainer = range.startContainer;\n const startOffset = range.startOffset;\n const endContainer = range.endContainer;\n const endOffset = range.endOffset;\n\n // We need to position the pasteArea in the visible portion of the screen\n // to stop the browser auto-scrolling.\n let pasteArea: Element = createElement('DIV', {\n contenteditable: 'true',\n style: 'position:fixed; overflow:hidden; top:0; right:100%; width:1px; height:1px;',\n });\n body.appendChild(pasteArea);\n range.selectNodeContents(pasteArea);\n this.setSelection(range);\n\n // A setTimeout of 0 means this is added to the back of the\n // single javascript thread, so it will be executed after the\n // paste event.\n setTimeout(() => {\n try {\n // Get the pasted content and clean\n let html = '';\n let next: Element = pasteArea;\n let first: Node | null;\n\n // #88: Chrome can apparently split the paste area if certain\n // content is inserted; gather them all up.\n while ((pasteArea = next)) {\n next = pasteArea.nextSibling as Element;\n detach(pasteArea);\n // Safari and IE like putting extra divs around things.\n first = pasteArea.firstChild;\n if (\n first &&\n first === pasteArea.lastChild &&\n first instanceof HTMLDivElement\n ) {\n pasteArea = first;\n }\n html += pasteArea.innerHTML;\n }\n\n this.setSelection(\n createRange(\n startContainer,\n startOffset,\n endContainer,\n endOffset,\n ),\n );\n\n if (html) {\n this.insertHTML(html, true);\n }\n } catch (error) {\n this._config.didError(error);\n }\n }, 0);\n};\n\n// On Windows you can drag an drop text. We can't handle this ourselves, because\n// as far as I can see, there's no way to get the drop insertion point. So just\n// save an undo state and hope for the best.\nconst _onDrop = function (this: Squire, event: DragEvent): void {\n // it's possible for dataTransfer to be null, let's avoid it.\n if (!event.dataTransfer) {\n return;\n }\n const types = event.dataTransfer.types;\n let l = types.length;\n let hasPlain = false;\n let hasHTML = false;\n while (l--) {\n switch (types[l]) {\n case 'text/plain':\n hasPlain = true;\n break;\n case 'text/html':\n hasHTML = true;\n break;\n default:\n return;\n }\n }\n if (hasHTML || (hasPlain && this.saveUndoState)) {\n this.saveUndoState();\n }\n};\n\n// ---\n\nexport {\n extractRangeToClipboard,\n _onCut,\n _onCopy,\n _monitorShiftKey,\n _onPaste,\n _onDrop,\n};\n", "import type { Squire } from '../Editor';\n\n// ---\n\nconst Enter = (self: Squire, event: KeyboardEvent, range: Range): void => {\n event.preventDefault();\n self.splitBlock(event.shiftKey, range);\n};\n\n// ---\n\nexport { Enter };\n", "import { ZWS } from '../Constants';\nimport { getPreviousBlock } from '../node/Block';\nimport { isInline, isBlock } from '../node/Category';\nimport { fixCursor } from '../node/MergeSplit';\nimport { createElement, detach, getNearest } from '../node/Node';\nimport { moveRangeBoundariesDownTree } from '../range/Boundaries';\n\nimport type { Squire } from '../Editor';\n\n// ---\n\n// If you delete the content inside a span with a font styling, Webkit will\n// replace it with a <font> tag (!). If you delete all the text inside a\n// link in Opera, it won't delete the link. Let's make things consistent. If\n// you delete all text inside an inline tag, remove the inline tag.\nconst afterDelete = (self: Squire, range?: Range): void => {\n try {\n if (!range) {\n range = self.getSelection();\n }\n let node = range!.startContainer;\n // Climb the tree from the focus point while we are inside an empty\n // inline element\n if (node instanceof Text) {\n node = node.parentNode!;\n }\n let parent = node;\n while (\n isInline(parent) &&\n (!parent.textContent || parent.textContent === ZWS)\n ) {\n node = parent;\n parent = node.parentNode!;\n }\n // If focused in empty inline element\n if (node !== parent) {\n // Move focus to just before empty inline(s)\n range!.setStart(\n parent,\n Array.from(parent.childNodes as NodeListOf<Node>).indexOf(node),\n );\n range!.collapse(true);\n // Remove empty inline(s)\n parent.removeChild(node);\n // Fix cursor in block\n if (!isBlock(parent)) {\n parent = getPreviousBlock(parent, self._root) || self._root;\n }\n fixCursor(parent);\n // Move cursor into text node\n moveRangeBoundariesDownTree(range!);\n }\n // If you delete the last character in the sole <div> in Chrome,\n // it removes the div and replaces it with just a <br> inside the\n // root. Detach the <br>; the _ensureBottomLine call will insert a new\n // block.\n if (\n node === self._root &&\n (node = node.firstChild!) &&\n node.nodeName === 'BR'\n ) {\n detach(node);\n }\n self._ensureBottomLine();\n self.setSelection(range);\n self._updatePath(range, true);\n } catch (error) {\n self._config.didError(error);\n }\n};\n\nconst detachUneditableNode = (node: Node, root: Element): void => {\n let parent: Node | null;\n while ((parent = node.parentNode)) {\n if (parent === root || (parent as HTMLElement).isContentEditable) {\n break;\n }\n node = parent;\n }\n detach(node);\n};\n\n// ---\n\nconst linkifyText = (self: Squire, textNode: Text, offset: number): void => {\n if (getNearest(textNode, self._root, 'A')) {\n return;\n }\n const data = textNode.data || '';\n const searchFrom =\n Math.max(\n data.lastIndexOf(' ', offset - 1),\n data.lastIndexOf('\u00A0', offset - 1),\n ) + 1;\n const searchText = data.slice(searchFrom, offset);\n const match = self.linkRegExp.exec(searchText);\n if (match) {\n // Record an undo point\n const selection = self.getSelection();\n self._docWasChanged();\n self._recordUndoState(selection);\n self._getRangeAndRemoveBookmark(selection);\n\n const index = searchFrom + match.index;\n const endIndex = index + match[0].length;\n const needsSelectionUpdate = selection.startContainer === textNode;\n const newSelectionOffset = selection.startOffset - endIndex;\n if (index) {\n textNode = textNode.splitText(index);\n }\n\n const defaultAttributes = self._config.tagAttributes.a;\n const link = createElement(\n 'A',\n Object.assign(\n {\n href: match[1]\n ? /^(?:ht|f)tps?:/i.test(match[1])\n ? match[1]\n : 'http://' + match[1]\n : 'mailto:' + match[0],\n },\n defaultAttributes,\n ),\n );\n link.textContent = data.slice(index, endIndex);\n textNode.parentNode!.insertBefore(link, textNode);\n textNode.data = data.slice(endIndex);\n\n if (needsSelectionUpdate) {\n selection.setStart(textNode, newSelectionOffset);\n selection.setEnd(textNode, newSelectionOffset);\n }\n self.setSelection(selection);\n }\n};\n\n// ---\n\nexport { afterDelete, detachUneditableNode, linkifyText };\n", "import type { Squire } from '../Editor';\nimport { getPreviousBlock } from '../node/Block';\nimport {\n fixContainer,\n mergeContainers,\n mergeWithBlock,\n} from '../node/MergeSplit';\nimport { getNearest } from '../node/Node';\nimport {\n getStartBlockOfRange,\n rangeDoesStartAtBlockBoundary,\n} from '../range/Block';\nimport { moveRangeBoundariesDownTree } from '../range/Boundaries';\nimport { deleteContentsOfRange } from '../range/InsertDelete';\nimport { afterDelete, detachUneditableNode } from './KeyHelpers';\n\n// ---\n\nconst Backspace = (self: Squire, event: KeyboardEvent, range: Range): void => {\n const root: Element = self._root;\n self._removeZWS();\n // Record undo checkpoint.\n self.saveUndoState(range);\n if (!range.collapsed) {\n // If not collapsed, delete contents\n event.preventDefault();\n deleteContentsOfRange(range, root);\n afterDelete(self, range);\n } else if (rangeDoesStartAtBlockBoundary(range, root)) {\n // If at beginning of block, merge with previous\n event.preventDefault();\n const startBlock = getStartBlockOfRange(range, root);\n if (!startBlock) {\n return;\n }\n let current = startBlock;\n // In case inline data has somehow got between blocks.\n fixContainer(current.parentNode!, root);\n // Now get previous block\n const previous = getPreviousBlock(current, root);\n // Must not be at the very beginning of the text area.\n if (previous) {\n // If not editable, just delete whole block.\n if (!(previous as HTMLElement).isContentEditable) {\n detachUneditableNode(previous, root);\n return;\n }\n // Otherwise merge.\n mergeWithBlock(previous, current, range, root);\n // If deleted line between containers, merge newly adjacent\n // containers.\n current = previous.parentNode as HTMLElement;\n while (current !== root && !current.nextSibling) {\n current = current.parentNode as HTMLElement;\n }\n if (\n current !== root &&\n (current = current.nextSibling as HTMLElement)\n ) {\n mergeContainers(current, root);\n }\n self.setSelection(range);\n // If at very beginning of text area, allow backspace\n // to break lists/blockquote.\n } else if (current) {\n if (\n getNearest(current, root, 'UL') ||\n getNearest(current, root, 'OL')\n ) {\n // Break list\n self.decreaseListLevel(range);\n return;\n } else if (getNearest(current, root, 'BLOCKQUOTE')) {\n // Break blockquote\n self.removeQuote(range);\n return;\n }\n self.setSelection(range);\n self._updatePath(range, true);\n }\n } else {\n // If deleting text inside a link that looks like a URL, delink.\n // This is to allow you to easily correct auto-linked text.\n moveRangeBoundariesDownTree(range);\n const text = range.startContainer;\n const offset = range.startOffset;\n const a = text.parentNode;\n if (\n text instanceof Text &&\n a instanceof HTMLAnchorElement &&\n offset &&\n a.href.includes(text.data)\n ) {\n text.deleteData(offset - 1, 1);\n self.setSelection(range);\n self.removeLink();\n event.preventDefault();\n } else {\n // Otherwise, leave to browser but check afterwards whether it has\n // left behind an empty inline tag.\n self.setSelection(range);\n setTimeout(() => {\n afterDelete(self);\n }, 0);\n }\n }\n};\n\n// ---\n\nexport { Backspace };\n", "import { getNextBlock } from '../node/Block';\nimport {\n fixContainer,\n mergeWithBlock,\n mergeContainers,\n} from '../node/MergeSplit';\nimport { detach } from '../node/Node';\nimport {\n rangeDoesEndAtBlockBoundary,\n getStartBlockOfRange,\n} from '../range/Block';\nimport {\n moveRangeBoundariesUpTree,\n moveRangeBoundariesDownTree,\n} from '../range/Boundaries';\nimport { deleteContentsOfRange } from '../range/InsertDelete';\nimport { afterDelete, detachUneditableNode } from './KeyHelpers';\n\nimport type { Squire } from '../Editor';\n\n// ---\n\nconst Delete = (self: Squire, event: KeyboardEvent, range: Range): void => {\n const root = self._root;\n let current: Node | null;\n let next: Node | null;\n let originalRange: Range;\n let cursorContainer: Node;\n let cursorOffset: number;\n let nodeAfterCursor: Node;\n self._removeZWS();\n // Record undo checkpoint.\n self.saveUndoState(range);\n // If not collapsed, delete contents\n if (!range.collapsed) {\n event.preventDefault();\n deleteContentsOfRange(range, root);\n afterDelete(self, range);\n // If at end of block, merge next into this block\n } else if (rangeDoesEndAtBlockBoundary(range, root)) {\n event.preventDefault();\n current = getStartBlockOfRange(range, root);\n if (!current) {\n return;\n }\n // In case inline data has somehow got between blocks.\n fixContainer(current.parentNode!, root);\n // Now get next block\n next = getNextBlock(current, root);\n // Must not be at the very end of the text area.\n if (next) {\n // If not editable, just delete whole block.\n if (!(next as HTMLElement).isContentEditable) {\n detachUneditableNode(next, root);\n return;\n }\n // Otherwise merge.\n mergeWithBlock(current, next, range, root);\n // If deleted line between containers, merge newly adjacent\n // containers.\n next = current.parentNode!;\n while (next !== root && !next.nextSibling) {\n next = next.parentNode!;\n }\n if (next !== root && (next = next.nextSibling)) {\n mergeContainers(next, root);\n }\n self.setSelection(range);\n self._updatePath(range, true);\n }\n // Otherwise, leave to browser but check afterwards whether it has\n // left behind an empty inline tag.\n } else {\n // But first check if the cursor is just before an IMG tag. If so,\n // delete it ourselves, because the browser won't if it is not\n // inline.\n originalRange = range.cloneRange();\n moveRangeBoundariesUpTree(range, root, root, root);\n cursorContainer = range.endContainer;\n cursorOffset = range.endOffset;\n if (cursorContainer instanceof Element) {\n nodeAfterCursor = cursorContainer.childNodes[cursorOffset];\n if (nodeAfterCursor && nodeAfterCursor.nodeName === 'IMG') {\n event.preventDefault();\n detach(nodeAfterCursor);\n moveRangeBoundariesDownTree(range);\n afterDelete(self, range);\n return;\n }\n }\n self.setSelection(originalRange);\n setTimeout(() => {\n afterDelete(self);\n }, 0);\n }\n};\n\n// ---\n\nexport { Delete };\n", "import {\n rangeDoesStartAtBlockBoundary,\n getStartBlockOfRange,\n} from '../range/Block';\nimport { getNearest } from '../node/Node';\n\nimport type { Squire } from '../Editor';\n\n// ---\n\nconst Tab = (self: Squire, event: KeyboardEvent, range: Range): void => {\n const root = self._root;\n self._removeZWS();\n // If no selection and at start of block\n if (range.collapsed && rangeDoesStartAtBlockBoundary(range, root)) {\n let node: Node = getStartBlockOfRange(range, root)!;\n // Iterate through the block's parents\n let parent: Node | null;\n while ((parent = node.parentNode)) {\n // If we find a UL or OL (so are in a list, node must be an LI)\n if (parent.nodeName === 'UL' || parent.nodeName === 'OL') {\n // Then increase the list level\n event.preventDefault();\n self.increaseListLevel(range);\n break;\n }\n node = parent;\n }\n }\n};\n\nconst ShiftTab = (self: Squire, event: KeyboardEvent, range: Range): void => {\n const root = self._root;\n self._removeZWS();\n // If no selection and at start of block\n if (range.collapsed && rangeDoesStartAtBlockBoundary(range, root)) {\n // Break list\n const node = range.startContainer;\n if (getNearest(node, root, 'UL') || getNearest(node, root, 'OL')) {\n event.preventDefault();\n self.decreaseListLevel(range);\n }\n }\n};\n\n// ---\n\nexport { Tab, ShiftTab };\n", "import { detach, getLength } from '../node/Node';\nimport { moveRangeBoundariesDownTree } from '../range/Boundaries';\nimport { deleteContentsOfRange } from '../range/InsertDelete';\n\nimport type { Squire } from '../Editor';\nimport { linkifyText } from './KeyHelpers';\nimport {\n getStartBlockOfRange,\n rangeDoesEndAtBlockBoundary,\n} from '../range/Block';\nimport { SHOW_TEXT, TreeIterator } from '../node/TreeIterator';\nimport { ZWS } from '../Constants';\n\n// ---\n\nconst Space = (self: Squire, event: KeyboardEvent, range: Range): void => {\n let node: Node | null;\n const root = self._root;\n self._recordUndoState(range);\n self._getRangeAndRemoveBookmark(range);\n\n // Delete the selection if not collapsed\n if (!range.collapsed) {\n deleteContentsOfRange(range, root);\n self._ensureBottomLine();\n self.setSelection(range);\n self._updatePath(range, true);\n } else if (rangeDoesEndAtBlockBoundary(range, root)) {\n const block = getStartBlockOfRange(range, root);\n if (block && block.nodeName !== 'PRE') {\n const text = block.textContent?.trimEnd().replace(ZWS, '');\n if (text === '*' || text === '1.') {\n event.preventDefault();\n self.insertPlainText(' ', false);\n self._docWasChanged();\n self.saveUndoState(range);\n const walker = new TreeIterator<Text>(block, SHOW_TEXT);\n let textNode: Text | null;\n while ((textNode = walker.nextNode())) {\n detach(textNode);\n }\n if (text === '*') {\n self.makeUnorderedList();\n } else {\n self.makeOrderedList();\n }\n return;\n }\n }\n }\n\n // If the cursor is at the end of a link (<a>foo|</a>) then move it\n // outside of the link (<a>foo</a>|) so that the space is not part of\n // the link text.\n node = range.endContainer;\n if (range.endOffset === getLength(node)) {\n do {\n if (node.nodeName === 'A') {\n range.setStartAfter(node);\n break;\n }\n } while (\n !node.nextSibling &&\n (node = node.parentNode) &&\n node !== root\n );\n }\n\n // Linkify text\n if (self._config.addLinks) {\n const linkRange = range.cloneRange();\n moveRangeBoundariesDownTree(linkRange);\n const textNode = linkRange.startContainer as Text;\n const offset = linkRange.startOffset;\n setTimeout(() => {\n linkifyText(self, textNode, offset);\n }, 0);\n }\n\n self.setSelection(range);\n};\n\n// ---\n\nexport { Space };\n", "import {\n isMac,\n isWin,\n isIOS,\n ctrlKey,\n supportsInputEvents,\n} from '../Constants';\nimport { deleteContentsOfRange } from '../range/InsertDelete';\nimport type { Squire } from '../Editor';\nimport { Enter } from './Enter';\nimport { Backspace } from './Backspace';\nimport { Delete } from './Delete';\nimport { ShiftTab, Tab } from './Tab';\nimport { Space } from './Space';\nimport { rangeDoesEndAtBlockBoundary } from '../range/Block';\nimport { moveRangeBoundariesDownTree } from '../range/Boundaries';\n\n// ---\n\nconst _onKey = function (this: Squire, event: KeyboardEvent): void {\n // Ignore key events where event.isComposing, to stop us from blatting\n // Kana-Kanji conversion\n if (event.defaultPrevented || event.isComposing) {\n return;\n }\n\n // We need to apply the Backspace/delete handlers regardless of\n // control key modifiers.\n let key = event.key;\n let modifiers = '';\n const code = event.code;\n // If pressing a number key + Shift, make sure we handle it as the number\n // key and not whatever different character the shift might turn it into.\n if (/^Digit\\d$/.test(code)) {\n key = code.slice(-1);\n }\n if (key !== 'Backspace' && key !== 'Delete') {\n if (event.altKey) {\n modifiers += 'Alt-';\n }\n if (event.ctrlKey) {\n modifiers += 'Ctrl-';\n }\n if (event.metaKey) {\n modifiers += 'Meta-';\n }\n if (event.shiftKey) {\n modifiers += 'Shift-';\n }\n }\n // However, on Windows, Shift-Delete is apparently \"cut\" (WTF right?), so\n // we want to let the browser handle Shift-Delete in this situation.\n if (isWin && event.shiftKey && key === 'Delete') {\n modifiers += 'Shift-';\n }\n key = modifiers + key;\n\n const range: Range = this.getSelection();\n if (this._keyHandlers[key]) {\n this._keyHandlers[key](this, event, range);\n } else if (\n !range.collapsed &&\n !event.ctrlKey &&\n !event.metaKey &&\n key.length === 1\n ) {\n // Record undo checkpoint.\n this.saveUndoState(range);\n // Delete the selection\n deleteContentsOfRange(range, this._root);\n this._ensureBottomLine();\n this.setSelection(range);\n this._updatePath(range, true);\n }\n};\n\n// ---\n\ntype KeyHandler = (self: Squire, event: KeyboardEvent, range: Range) => void;\n\nconst keyHandlers: Record<string, KeyHandler> = {\n 'Backspace': Backspace,\n 'Delete': Delete,\n 'Tab': Tab,\n 'Shift-Tab': ShiftTab,\n ' ': Space,\n 'ArrowLeft'(self: Squire): void {\n self._removeZWS();\n },\n 'ArrowRight'(self: Squire, event: KeyboardEvent, range: Range): void {\n self._removeZWS();\n // Allow right arrow to always break out of <code> block.\n const root = self.getRoot();\n if (rangeDoesEndAtBlockBoundary(range, root)) {\n moveRangeBoundariesDownTree(range);\n let node: Node | null = range.endContainer;\n do {\n if (node.nodeName === 'CODE') {\n let next = node.nextSibling;\n if (!(next instanceof Text)) {\n const textNode = document.createTextNode('\u00A0'); // nbsp\n node.parentNode!.insertBefore(textNode, next);\n next = textNode;\n }\n range.setStart(next, 1);\n self.setSelection(range);\n event.preventDefault();\n break;\n }\n } while (\n !node.nextSibling &&\n (node = node.parentNode) &&\n node !== root\n );\n }\n },\n};\n\nif (!supportsInputEvents) {\n keyHandlers.Enter = Enter;\n keyHandlers['Shift-Enter'] = Enter;\n}\n\n// System standard for page up/down on Mac/iOS is to just scroll, not move the\n// cursor. On Linux/Windows, it should move the cursor, but some browsers don't\n// implement this natively. Override to support it.\nif (!isMac && !isIOS) {\n keyHandlers.PageUp = (self: Squire) => {\n self.moveCursorToStart();\n };\n keyHandlers.PageDown = (self: Squire) => {\n self.moveCursorToEnd();\n };\n}\n\n// ---\n\nconst mapKeyToFormat = (\n tag: string,\n remove?: { tag: string } | null,\n): KeyHandler => {\n remove = remove || null;\n return (self: Squire, event: Event) => {\n event.preventDefault();\n const range = self.getSelection();\n if (self.hasFormat(tag, null, range)) {\n self.changeFormat(null, { tag }, range);\n } else {\n self.changeFormat({ tag }, remove, range);\n }\n };\n};\n\nkeyHandlers[ctrlKey + 'b'] = mapKeyToFormat('B');\nkeyHandlers[ctrlKey + 'i'] = mapKeyToFormat('I');\nkeyHandlers[ctrlKey + 'u'] = mapKeyToFormat('U');\nkeyHandlers[ctrlKey + 'Shift-7'] = mapKeyToFormat('S');\nkeyHandlers[ctrlKey + 'Shift-5'] = mapKeyToFormat('SUB', { tag: 'SUP' });\nkeyHandlers[ctrlKey + 'Shift-6'] = mapKeyToFormat('SUP', { tag: 'SUB' });\n\nkeyHandlers[ctrlKey + 'Shift-8'] = (\n self: Squire,\n event: KeyboardEvent,\n): void => {\n event.preventDefault();\n const path = self.getPath();\n if (!/(?:^|>)UL/.test(path)) {\n self.makeUnorderedList();\n } else {\n self.removeList();\n }\n};\nkeyHandlers[ctrlKey + 'Shift-9'] = (\n self: Squire,\n event: KeyboardEvent,\n): void => {\n event.preventDefault();\n const path = self.getPath();\n if (!/(?:^|>)OL/.test(path)) {\n self.makeOrderedList();\n } else {\n self.removeList();\n }\n};\n\nkeyHandlers[ctrlKey + '['] = (self: Squire, event: KeyboardEvent): void => {\n event.preventDefault();\n const path = self.getPath();\n if (/(?:^|>)BLOCKQUOTE/.test(path) || !/(?:^|>)[OU]L/.test(path)) {\n self.decreaseQuoteLevel();\n } else {\n self.decreaseListLevel();\n }\n};\nkeyHandlers[ctrlKey + ']'] = (self: Squire, event: KeyboardEvent): void => {\n event.preventDefault();\n const path = self.getPath();\n if (/(?:^|>)BLOCKQUOTE/.test(path) || !/(?:^|>)[OU]L/.test(path)) {\n self.increaseQuoteLevel();\n } else {\n self.increaseListLevel();\n }\n};\n\nkeyHandlers[ctrlKey + 'd'] = (self: Squire, event: KeyboardEvent): void => {\n event.preventDefault();\n self.toggleCode();\n};\n\nkeyHandlers[ctrlKey + 'z'] = (self: Squire, event: KeyboardEvent): void => {\n event.preventDefault();\n self.undo();\n};\nkeyHandlers[ctrlKey + 'y'] =\n // Depending on platform, the Shift may cause the key to come through as\n // upper case, but sometimes not. Just add both as shortcuts \u2014 the browser\n // will only ever fire one or the other.\n keyHandlers[ctrlKey + 'Shift-z'] =\n keyHandlers[ctrlKey + 'Shift-Z'] =\n (self: Squire, event: KeyboardEvent): void => {\n event.preventDefault();\n self.redo();\n };\n\nexport { _onKey, keyHandlers };\n", "import {\n TreeIterator,\n SHOW_TEXT,\n SHOW_ELEMENT_OR_TEXT,\n} from './node/TreeIterator';\nimport {\n createElement,\n detach,\n empty,\n getNearest,\n hasTagAttributes,\n replaceWith,\n} from './node/Node';\nimport {\n isLeaf,\n isInline,\n resetNodeCategoryCache,\n isContainer,\n isBlock,\n} from './node/Category';\nimport { isLineBreak, removeZWS } from './node/Whitespace';\nimport {\n moveRangeBoundariesDownTree,\n isNodeContainedInRange,\n moveRangeBoundaryOutOf,\n moveRangeBoundariesUpTree,\n} from './range/Boundaries';\nimport {\n createRange,\n deleteContentsOfRange,\n extractContentsOfRange,\n insertNodeInRange,\n insertTreeFragmentIntoRange,\n} from './range/InsertDelete';\nimport {\n fixContainer,\n fixCursor,\n mergeContainers,\n mergeInlines,\n split,\n} from './node/MergeSplit';\nimport { getBlockWalker, getNextBlock, isEmptyBlock } from './node/Block';\nimport { cleanTree, cleanupBRs, escapeHTML, removeEmptyInlines } from './Clean';\nimport { cantFocusEmptyTextNodes, ZWS } from './Constants';\nimport {\n expandRangeToBlockBoundaries,\n getEndBlockOfRange,\n getStartBlockOfRange,\n rangeDoesEndAtBlockBoundary,\n rangeDoesStartAtBlockBoundary,\n} from './range/Block';\nimport {\n _monitorShiftKey,\n _onCopy,\n _onCut,\n _onDrop,\n _onPaste,\n} from './Clipboard';\nimport { keyHandlers, _onKey } from './keyboard/KeyHandlers';\nimport { linkifyText } from './keyboard/KeyHelpers';\nimport { getTextContentsOfRange } from './range/Contents';\n\ndeclare const DOMPurify: any;\n\n// ---\n\ntype EventHandler = { handleEvent: (e: Event) => void } | ((e: Event) => void);\n\ntype KeyHandlerFunction = (x: Squire, y: KeyboardEvent, z: Range) => void;\n\ntype TagAttributes = {\n [key: string]: { [key: string]: string };\n};\n\ninterface SquireConfig {\n blockTag: string;\n blockAttributes: null | Record<string, string>;\n tagAttributes: TagAttributes;\n classNames: {\n color: string;\n fontFamily: string;\n fontSize: string;\n highlight: string;\n };\n undo: {\n documentSizeThreshold: number;\n undoLimit: number;\n };\n addLinks: boolean;\n willCutCopy: null | ((html: string) => string);\n toPlainText: null | ((html: string) => string);\n sanitizeToDOMFragment: (html: string, editor: Squire) => DocumentFragment;\n didError: (x: any) => void;\n}\n\n// ---\n\nclass Squire {\n _root: HTMLElement;\n _config: SquireConfig;\n\n _isFocused: boolean;\n _lastSelection: Range;\n _willRestoreSelection: boolean;\n _mayHaveZWS: boolean;\n\n _lastAnchorNode: Node | null;\n _lastFocusNode: Node | null;\n _path: string;\n\n _events: Map<string, Array<EventHandler>>;\n\n _undoIndex: number;\n _undoStack: Array<string>;\n _undoStackLength: number;\n _isInUndoState: boolean;\n _ignoreChange: boolean;\n _ignoreAllChanges: boolean;\n\n _isShiftDown: boolean;\n _keyHandlers: Record<string, KeyHandlerFunction>;\n\n _mutation: MutationObserver;\n\n constructor(root: HTMLElement, config?: Partial<SquireConfig>) {\n this._root = root;\n\n this._config = this._makeConfig(config);\n\n this._isFocused = false;\n this._lastSelection = createRange(root, 0);\n this._willRestoreSelection = false;\n this._mayHaveZWS = false;\n\n this._lastAnchorNode = null;\n this._lastFocusNode = null;\n this._path = '';\n\n this._events = new Map();\n\n this._undoIndex = -1;\n this._undoStack = [];\n this._undoStackLength = 0;\n this._isInUndoState = false;\n this._ignoreChange = false;\n this._ignoreAllChanges = false;\n\n // Add event listeners\n this.addEventListener('selectionchange', this._updatePathOnEvent);\n\n // On blur, restore focus except if the user taps or clicks to focus a\n // specific point. Can't actually use click event because focus happens\n // before click, so use mousedown/touchstart\n this.addEventListener('blur', this._enableRestoreSelection);\n this.addEventListener('mousedown', this._disableRestoreSelection);\n this.addEventListener('touchstart', this._disableRestoreSelection);\n this.addEventListener('focus', this._restoreSelection);\n\n // On blur, cleanup any ZWS/empty inlines\n this.addEventListener('blur', this._removeZWS);\n\n // Clipboard support\n this._isShiftDown = false;\n this.addEventListener('cut', _onCut as (e: Event) => void);\n this.addEventListener('copy', _onCopy as (e: Event) => void);\n this.addEventListener('paste', _onPaste as (e: Event) => void);\n this.addEventListener('drop', _onDrop as (e: Event) => void);\n this.addEventListener(\n 'keydown',\n _monitorShiftKey as (e: Event) => void,\n );\n this.addEventListener('keyup', _monitorShiftKey as (e: Event) => void);\n\n // Keyboard support\n this.addEventListener('keydown', _onKey as (e: Event) => void);\n this._keyHandlers = Object.create(keyHandlers);\n\n const mutation = new MutationObserver(() => this._docWasChanged());\n mutation.observe(root, {\n childList: true,\n attributes: true,\n characterData: true,\n subtree: true,\n });\n this._mutation = mutation;\n\n // Make it editable\n root.setAttribute('contenteditable', 'true');\n\n // Modern browsers let you override their default content editable\n // handling!\n this.addEventListener(\n 'beforeinput',\n this._beforeInput as (e: Event) => void,\n );\n\n this.setHTML('');\n }\n\n destroy(): void {\n this._events.forEach((_, type) => {\n this.removeEventListener(type);\n });\n\n this._mutation.disconnect();\n\n this._undoIndex = -1;\n this._undoStack = [];\n this._undoStackLength = 0;\n }\n\n _makeConfig(userConfig?: object): SquireConfig {\n const config = {\n blockTag: 'DIV',\n blockAttributes: null,\n tagAttributes: {},\n classNames: {\n color: 'color',\n fontFamily: 'font',\n fontSize: 'size',\n highlight: 'highlight',\n },\n undo: {\n documentSizeThreshold: -1, // -1 means no threshold\n undoLimit: -1, // -1 means no limit\n },\n addLinks: true,\n willCutCopy: null,\n toPlainText: null,\n sanitizeToDOMFragment: (\n html: string,\n /* editor: Squire, */\n ): DocumentFragment => {\n const frag = DOMPurify.sanitize(html, {\n ALLOW_UNKNOWN_PROTOCOLS: true,\n WHOLE_DOCUMENT: false,\n RETURN_DOM: true,\n RETURN_DOM_FRAGMENT: true,\n FORCE_BODY: false,\n });\n return frag\n ? document.importNode(frag, true)\n : document.createDocumentFragment();\n },\n didError: (error: any): void => console.log(error),\n };\n if (userConfig) {\n Object.assign(config, userConfig);\n config.blockTag = config.blockTag.toUpperCase();\n }\n\n return config;\n }\n\n setKeyHandler(key: string, fn: KeyHandlerFunction) {\n this._keyHandlers[key] = fn;\n return this;\n }\n\n _beforeInput(event: InputEvent): void {\n switch (event.inputType) {\n case 'insertLineBreak':\n event.preventDefault();\n this.splitBlock(true);\n break;\n case 'insertParagraph':\n event.preventDefault();\n this.splitBlock(false);\n break;\n case 'insertOrderedList':\n event.preventDefault();\n this.makeOrderedList();\n break;\n case 'insertUnoderedList':\n event.preventDefault();\n this.makeUnorderedList();\n break;\n case 'historyUndo':\n event.preventDefault();\n this.undo();\n break;\n case 'historyRedo':\n event.preventDefault();\n this.redo();\n break;\n case 'formatBold':\n event.preventDefault();\n this.bold();\n break;\n case 'formaItalic':\n event.preventDefault();\n this.italic();\n break;\n case 'formatUnderline':\n event.preventDefault();\n this.underline();\n break;\n case 'formatStrikeThrough':\n event.preventDefault();\n this.strikethrough();\n break;\n case 'formatSuperscript':\n event.preventDefault();\n this.superscript();\n break;\n case 'formatSubscript':\n event.preventDefault();\n this.subscript();\n break;\n case 'formatJustifyFull':\n case 'formatJustifyCenter':\n case 'formatJustifyRight':\n case 'formatJustifyLeft': {\n event.preventDefault();\n let alignment = event.inputType.slice(13).toLowerCase();\n if (alignment === 'full') {\n alignment = 'justify';\n }\n this.setTextAlignment(alignment);\n break;\n }\n case 'formatRemove':\n event.preventDefault();\n this.removeAllFormatting();\n break;\n case 'formatSetBlockTextDirection': {\n event.preventDefault();\n let dir = event.data;\n if (dir === 'null') {\n dir = null;\n }\n this.setTextDirection(dir);\n break;\n }\n case 'formatBackColor':\n event.preventDefault();\n this.setHighlightColor(event.data);\n break;\n case 'formatFontColor':\n event.preventDefault();\n this.setTextColor(event.data);\n break;\n case 'formatFontName':\n event.preventDefault();\n this.setFontFace(event.data);\n break;\n }\n }\n\n // --- Events\n\n handleEvent(event: Event): void {\n this.fireEvent(event.type, event);\n }\n\n fireEvent(type: string, detail?: Event | object): Squire {\n let handlers = this._events.get(type);\n // UI code, especially modal views, may be monitoring for focus events\n // and immediately removing focus. In certain conditions, this can\n // cause the focus event to fire after the blur event, which can cause\n // an infinite loop. So we detect whether we're actually\n // focused/blurred before firing.\n if (/^(?:focus|blur)/.test(type)) {\n const isFocused = this._root === document.activeElement;\n if (type === 'focus') {\n if (!isFocused || this._isFocused) {\n return this;\n }\n this._isFocused = true;\n } else {\n if (isFocused || !this._isFocused) {\n return this;\n }\n this._isFocused = false;\n }\n }\n if (handlers) {\n const event: Event =\n detail instanceof Event\n ? detail\n : new CustomEvent(type, {\n detail,\n });\n // Clone handlers array, so any handlers added/removed do not\n // affect it.\n handlers = handlers.slice();\n for (const handler of handlers) {\n try {\n if ('handleEvent' in handler) {\n handler.handleEvent(event);\n } else {\n handler.call(this, event);\n }\n } catch (error) {\n this._config.didError(error);\n }\n }\n }\n return this;\n }\n\n /**\n * Subscribing to these events won't automatically add a listener to the\n * document node, since these events are fired in a custom manner by the\n * editor code.\n */\n customEvents = new Set([\n 'pathChange',\n 'select',\n 'input',\n 'pasteImage',\n 'undoStateChange',\n ]);\n\n addEventListener(type: string, fn: EventHandler): Squire {\n let handlers = this._events.get(type);\n let target: Document | HTMLElement = this._root;\n if (!handlers) {\n handlers = [];\n this._events.set(type, handlers);\n if (!this.customEvents.has(type)) {\n if (type === 'selectionchange') {\n target = document;\n }\n target.addEventListener(type, this, true);\n }\n }\n handlers.push(fn);\n return this;\n }\n\n removeEventListener(type: string, fn?: EventHandler): Squire {\n const handlers = this._events.get(type);\n let target: Document | HTMLElement = this._root;\n if (handlers) {\n if (fn) {\n let l = handlers.length;\n while (l--) {\n if (handlers[l] === fn) {\n handlers.splice(l, 1);\n }\n }\n } else {\n handlers.length = 0;\n }\n if (!handlers.length) {\n this._events.delete(type);\n if (!this.customEvents.has(type)) {\n if (type === 'selectionchange') {\n target = document;\n }\n target.removeEventListener(type, this, true);\n }\n }\n }\n return this;\n }\n\n // --- Focus\n\n focus(): Squire {\n this._root.focus({ preventScroll: true });\n return this;\n }\n\n blur(): Squire {\n this._root.blur();\n return this;\n }\n\n // --- Selection and bookmarking\n\n _enableRestoreSelection(): void {\n this._willRestoreSelection = true;\n }\n\n _disableRestoreSelection(): void {\n this._willRestoreSelection = false;\n }\n\n _restoreSelection() {\n if (this._willRestoreSelection) {\n this.setSelection(this._lastSelection);\n }\n }\n\n // ---\n\n _removeZWS(): void {\n if (!this._mayHaveZWS) {\n return;\n }\n removeZWS(this._root);\n this._mayHaveZWS = false;\n }\n\n // ---\n\n startSelectionId = 'squire-selection-start';\n endSelectionId = 'squire-selection-end';\n\n _saveRangeToBookmark(range: Range): void {\n let startNode = createElement('INPUT', {\n id: this.startSelectionId,\n type: 'hidden',\n });\n let endNode = createElement('INPUT', {\n id: this.endSelectionId,\n type: 'hidden',\n });\n let temp: HTMLElement;\n\n insertNodeInRange(range, startNode);\n range.collapse(false);\n insertNodeInRange(range, endNode);\n\n // In a collapsed range, the start is sometimes inserted after the end!\n if (\n startNode.compareDocumentPosition(endNode) &\n Node.DOCUMENT_POSITION_PRECEDING\n ) {\n startNode.id = this.endSelectionId;\n endNode.id = this.startSelectionId;\n temp = startNode;\n startNode = endNode;\n endNode = temp;\n }\n\n range.setStartAfter(startNode);\n range.setEndBefore(endNode);\n }\n\n _getRangeAndRemoveBookmark(range?: Range): Range | null {\n const root = this._root;\n const start = root.querySelector('#' + this.startSelectionId);\n const end = root.querySelector('#' + this.endSelectionId);\n\n if (start && end) {\n let startContainer: Node = start.parentNode!;\n let endContainer: Node = end.parentNode!;\n const startOffset = Array.from(startContainer.childNodes).indexOf(\n start,\n );\n let endOffset = Array.from(endContainer.childNodes).indexOf(end);\n\n if (startContainer === endContainer) {\n endOffset -= 1;\n }\n\n start.remove();\n end.remove();\n\n if (!range) {\n range = document.createRange();\n }\n range.setStart(startContainer, startOffset);\n range.setEnd(endContainer, endOffset);\n\n // Merge any text nodes we split\n mergeInlines(startContainer, range);\n if (startContainer !== endContainer) {\n mergeInlines(endContainer, range);\n }\n\n // If we didn't split a text node, we should move into any adjacent\n // text node to current selection point\n if (range.collapsed) {\n startContainer = range.startContainer;\n if (startContainer instanceof Text) {\n endContainer = startContainer.childNodes[range.startOffset];\n if (!endContainer || !(endContainer instanceof Text)) {\n endContainer =\n startContainer.childNodes[range.startOffset - 1];\n }\n if (endContainer && endContainer instanceof Text) {\n range.setStart(endContainer, 0);\n range.collapse(true);\n }\n }\n }\n }\n return range || null;\n }\n\n getSelection(): Range {\n const selection = window.getSelection();\n const root = this._root;\n let range: Range | null = null;\n // If not focused, always rely on cached selection; another function may\n // have set it but the DOM is not modified until focus again\n if (this._isFocused && selection && selection.rangeCount) {\n range = selection.getRangeAt(0).cloneRange();\n const startContainer = range.startContainer;\n const endContainer = range.endContainer;\n // FF can return the selection as being inside an <img>. WTF?\n if (startContainer && isLeaf(startContainer)) {\n range.setStartBefore(startContainer);\n }\n if (endContainer && isLeaf(endContainer)) {\n range.setEndBefore(endContainer);\n }\n }\n if (range && root.contains(range.commonAncestorContainer)) {\n this._lastSelection = range;\n } else {\n range = this._lastSelection;\n // Check the editor is in the live document; if not, the range has\n // probably been rewritten by the browser and is bogus\n if (!document.contains(range.commonAncestorContainer)) {\n range = null;\n }\n }\n if (!range) {\n range = createRange(root.firstElementChild || root, 0);\n }\n return range;\n }\n\n setSelection(range: Range): Squire {\n this._lastSelection = range;\n // If we're setting selection, that automatically, and synchronously,\n // triggers a focus event. So just store the selection and mark it as\n // needing restore on focus.\n if (!this._isFocused) {\n this._enableRestoreSelection();\n } else {\n const selection = window.getSelection();\n if (selection) {\n if ('setBaseAndExtent' in Selection.prototype) {\n selection.setBaseAndExtent(\n range.startContainer,\n range.startOffset,\n range.endContainer,\n range.endOffset,\n );\n } else {\n selection.removeAllRanges();\n selection.addRange(range);\n }\n }\n }\n return this;\n }\n\n // ---\n\n _moveCursorTo(toStart: boolean): Squire {\n const root = this._root;\n const range = createRange(root, toStart ? 0 : root.childNodes.length);\n moveRangeBoundariesDownTree(range);\n this.setSelection(range);\n return this;\n }\n\n moveCursorToStart(): Squire {\n return this._moveCursorTo(true);\n }\n\n moveCursorToEnd(): Squire {\n return this._moveCursorTo(false);\n }\n\n // ---\n\n getCursorPosition(): DOMRect {\n const range = this.getSelection();\n let rect = range.getBoundingClientRect();\n // If the range is outside of the viewport, some browsers at least\n // will return 0 for all the values; need to get a DOM node to find\n // the position instead.\n if (rect && !rect.top) {\n this._ignoreChange = true;\n const node = createElement('SPAN');\n node.textContent = ZWS;\n insertNodeInRange(range, node);\n rect = node.getBoundingClientRect();\n const parent = node.parentNode!;\n parent.removeChild(node);\n mergeInlines(parent, range);\n }\n return rect;\n }\n\n // --- Path\n\n getPath(): string {\n return this._path;\n }\n\n _updatePathOnEvent(): void {\n if (this._isFocused) {\n this._updatePath(this.getSelection());\n }\n }\n\n _updatePath(range: Range, force?: boolean): void {\n const anchor = range.startContainer;\n const focus = range.endContainer;\n let newPath: string;\n if (\n force ||\n anchor !== this._lastAnchorNode ||\n focus !== this._lastFocusNode\n ) {\n this._lastAnchorNode = anchor;\n this._lastFocusNode = focus;\n newPath =\n anchor && focus\n ? anchor === focus\n ? this._getPath(focus)\n : '(selection)'\n : '';\n if (this._path !== newPath || anchor !== focus) {\n this._path = newPath;\n this.fireEvent('pathChange', {\n path: newPath,\n });\n }\n }\n this.fireEvent(range.collapsed ? 'cursor' : 'select', {\n range: range,\n });\n }\n\n _getPath(node: Node) {\n const root = this._root;\n const config = this._config;\n let path = '';\n if (node && node !== root) {\n const parent = node.parentNode;\n path = parent ? this._getPath(parent) : '';\n if (node instanceof HTMLElement) {\n const id = node.id;\n const classList = node.classList;\n const classNames = Array.from(classList).sort();\n const dir = node.dir;\n const styleNames = config.classNames;\n path += (path ? '>' : '') + node.nodeName;\n if (id) {\n path += '#' + id;\n }\n if (classNames.length) {\n path += '.';\n path += classNames.join('.');\n }\n if (dir) {\n path += '[dir=' + dir + ']';\n }\n if (classList.contains(styleNames.highlight)) {\n path +=\n '[backgroundColor=' +\n node.style.backgroundColor.replace(/ /g, '') +\n ']';\n }\n if (classList.contains(styleNames.color)) {\n path +=\n '[color=' + node.style.color.replace(/ /g, '') + ']';\n }\n if (classList.contains(styleNames.fontFamily)) {\n path +=\n '[fontFamily=' +\n node.style.fontFamily.replace(/ /g, '') +\n ']';\n }\n if (classList.contains(styleNames.fontSize)) {\n path += '[fontSize=' + node.style.fontSize + ']';\n }\n }\n }\n return path;\n }\n\n // --- History\n\n modifyDocument(modificationFn: () => void): Squire {\n const mutation = this._mutation;\n if (mutation) {\n if (mutation.takeRecords().length) {\n this._docWasChanged();\n }\n mutation.disconnect();\n }\n\n this._ignoreAllChanges = true;\n modificationFn();\n this._ignoreAllChanges = false;\n\n if (mutation) {\n mutation.observe(this._root, {\n childList: true,\n attributes: true,\n characterData: true,\n subtree: true,\n });\n this._ignoreChange = false;\n }\n\n return this;\n }\n\n _docWasChanged(): void {\n resetNodeCategoryCache();\n this._mayHaveZWS = true;\n if (this._ignoreAllChanges) {\n return;\n }\n\n if (this._ignoreChange) {\n this._ignoreChange = false;\n return;\n }\n if (this._isInUndoState) {\n this._isInUndoState = false;\n this.fireEvent('undoStateChange', {\n canUndo: true,\n canRedo: false,\n });\n }\n this.fireEvent('input');\n }\n\n /**\n * Leaves bookmark.\n */\n _recordUndoState(range: Range, replace?: boolean): Squire {\n const isInUndoState = this._isInUndoState;\n if (!isInUndoState || replace) {\n // Advance pointer to new position\n let undoIndex = this._undoIndex + 1;\n const undoStack = this._undoStack;\n const undoConfig = this._config.undo;\n const undoThreshold = undoConfig.documentSizeThreshold;\n const undoLimit = undoConfig.undoLimit;\n\n // Truncate stack if longer (i.e. if has been previously undone)\n if (undoIndex < this._undoStackLength) {\n undoStack.length = this._undoStackLength = undoIndex;\n }\n\n // Add bookmark\n if (range) {\n this._saveRangeToBookmark(range);\n }\n\n // Don't record if we're already in an undo state\n if (isInUndoState) {\n return this;\n }\n\n // Get data\n const html = this._getRawHTML();\n\n // If this document is above the configured size threshold,\n // limit the number of saved undo states.\n // Threshold is in bytes, JS uses 2 bytes per character\n if (replace) {\n undoIndex -= 1;\n }\n if (undoThreshold > -1 && html.length * 2 > undoThreshold) {\n if (undoLimit > -1 && undoIndex > undoLimit) {\n undoStack.splice(0, undoIndex - undoLimit);\n undoIndex = undoLimit;\n this._undoStackLength = undoLimit;\n }\n }\n\n // Save data\n undoStack[undoIndex] = html;\n this._undoIndex = undoIndex;\n this._undoStackLength += 1;\n this._isInUndoState = true;\n }\n return this;\n }\n\n saveUndoState(range?: Range): Squire {\n if (!range) {\n range = this.getSelection();\n }\n this._recordUndoState(range, this._isInUndoState);\n this._getRangeAndRemoveBookmark(range);\n\n return this;\n }\n\n undo(): Squire {\n // Sanity check: must not be at beginning of the history stack\n if (this._undoIndex !== 0 || !this._isInUndoState) {\n // Make sure any changes since last checkpoint are saved.\n this._recordUndoState(this.getSelection(), false);\n this._undoIndex -= 1;\n this._setRawHTML(this._undoStack[this._undoIndex]);\n const range = this._getRangeAndRemoveBookmark();\n if (range) {\n this.setSelection(range);\n }\n this._isInUndoState = true;\n this.fireEvent('undoStateChange', {\n canUndo: this._undoIndex !== 0,\n canRedo: true,\n });\n this.fireEvent('input');\n }\n return this.focus();\n }\n\n redo(): Squire {\n // Sanity check: must not be at end of stack and must be in an undo\n // state.\n const undoIndex = this._undoIndex;\n const undoStackLength = this._undoStackLength;\n if (undoIndex + 1 < undoStackLength && this._isInUndoState) {\n this._undoIndex += 1;\n this._setRawHTML(this._undoStack[this._undoIndex]);\n const range = this._getRangeAndRemoveBookmark();\n if (range) {\n this.setSelection(range);\n }\n this.fireEvent('undoStateChange', {\n canUndo: true,\n canRedo: undoIndex + 2 < undoStackLength,\n });\n this.fireEvent('input');\n }\n return this.focus();\n }\n\n // --- Get and set data\n\n getRoot(): HTMLElement {\n return this._root;\n }\n\n _getRawHTML(): string {\n return this._root.innerHTML;\n }\n\n _setRawHTML(html: string): Squire {\n const root = this._root;\n root.innerHTML = html;\n\n let node: Element | null = root;\n const child = node.firstChild;\n if (!child || child.nodeName === 'BR') {\n const block = this.createDefaultBlock();\n if (child) {\n node.replaceChild(block, child);\n } else {\n node.appendChild(block);\n }\n } else {\n while ((node = getNextBlock(node, root))) {\n fixCursor(node);\n }\n }\n\n this._ignoreChange = true;\n\n return this;\n }\n\n getHTML(withBookmark?: boolean): string {\n let range: Range | undefined;\n if (withBookmark) {\n range = this.getSelection();\n this._saveRangeToBookmark(range);\n }\n const html = this._getRawHTML().replace(/\\u200B/g, '');\n if (withBookmark) {\n this._getRangeAndRemoveBookmark(range);\n }\n return html;\n }\n\n setHTML(html: string): Squire {\n // Parse HTML into DOM tree\n const frag = this._config.sanitizeToDOMFragment(html, this);\n const root = this._root;\n\n // Fixup DOM tree\n cleanTree(frag, this._config);\n cleanupBRs(frag, root, false);\n fixContainer(frag, root);\n\n // Fix cursor\n let node: DocumentFragment | HTMLElement | null = frag;\n let child = node.firstChild;\n if (!child || child.nodeName === 'BR') {\n const block = this.createDefaultBlock();\n if (child) {\n node.replaceChild(block, child);\n } else {\n node.appendChild(block);\n }\n } else {\n while ((node = getNextBlock(node, root))) {\n fixCursor(node);\n }\n }\n\n // Don't fire an input event\n this._ignoreChange = true;\n\n // Remove existing root children and insert new content\n while ((child = root.lastChild)) {\n root.removeChild(child);\n }\n root.appendChild(frag);\n\n // Reset the undo stack\n this._undoIndex = -1;\n this._undoStack.length = 0;\n this._undoStackLength = 0;\n this._isInUndoState = false;\n\n // Record undo state\n const range =\n this._getRangeAndRemoveBookmark() ||\n createRange(root.firstElementChild || root, 0);\n this.saveUndoState(range);\n\n // Set inital selection\n this.setSelection(range);\n this._updatePath(range, true);\n\n return this;\n }\n\n /**\n * Insert HTML at the cursor location. If the selection is not collapsed\n * insertTreeFragmentIntoRange will delete the selection so that it is\n * replaced by the html being inserted.\n */\n insertHTML(html: string, isPaste?: boolean): Squire {\n // Parse\n const config = this._config;\n let frag = config.sanitizeToDOMFragment(html, this);\n\n // Record undo checkpoint\n const range = this.getSelection();\n this.saveUndoState(range);\n\n try {\n const root = this._root;\n\n if (config.addLinks) {\n this.addDetectedLinks(frag, frag);\n }\n cleanTree(frag, this._config);\n cleanupBRs(frag, root, false);\n removeEmptyInlines(frag);\n frag.normalize();\n\n let node: HTMLElement | DocumentFragment | null = frag;\n while ((node = getNextBlock(node, frag))) {\n fixCursor(node);\n }\n\n let doInsert = true;\n if (isPaste) {\n const event = new CustomEvent('willPaste', {\n cancelable: true,\n detail: {\n html,\n fragment: frag,\n },\n });\n this.fireEvent('willPaste', event);\n frag = event.detail.fragment;\n doInsert = !event.defaultPrevented;\n }\n\n if (doInsert) {\n insertTreeFragmentIntoRange(range, frag, root);\n range.collapse(false);\n\n // After inserting the fragment, check whether the cursor is\n // inside an <a> element and if so if there is an equivalent\n // cursor position after the <a> element. If there is, move it\n // there.\n moveRangeBoundaryOutOf(range, 'A', root);\n\n this._ensureBottomLine();\n }\n\n this.setSelection(range);\n this._updatePath(range, true);\n // Safari sometimes loses focus after paste. Weird.\n if (isPaste) {\n this.focus();\n }\n } catch (error) {\n this._config.didError(error);\n }\n return this;\n }\n\n insertElement(el: Element, range?: Range): Squire {\n if (!range) {\n range = this.getSelection();\n }\n range.collapse(true);\n if (isInline(el)) {\n insertNodeInRange(range, el);\n range.setStartAfter(el);\n } else {\n // Get containing block node.\n const root = this._root;\n const startNode: HTMLElement | null = getStartBlockOfRange(\n range,\n root,\n );\n let splitNode: Element | Node = startNode || root;\n\n let nodeAfterSplit: Node | null = null;\n // While at end of container node, move up DOM tree.\n while (splitNode !== root && !splitNode.nextSibling) {\n splitNode = splitNode.parentNode!;\n }\n // If in the middle of a container node, split up to root.\n if (splitNode !== root) {\n const parent = splitNode.parentNode!;\n nodeAfterSplit = split(\n parent,\n splitNode.nextSibling,\n root,\n root,\n ) as Node;\n }\n\n // If the startNode was empty remove it so that we don't end up\n // with two blank lines.\n if (startNode && isEmptyBlock(startNode)) {\n detach(startNode);\n }\n\n // Insert element and blank line.\n root.insertBefore(el, nodeAfterSplit);\n const blankLine = this.createDefaultBlock();\n root.insertBefore(blankLine, nodeAfterSplit);\n\n // Move cursor to blank line after inserted element.\n range.setStart(blankLine, 0);\n range.setEnd(blankLine, 0);\n moveRangeBoundariesDownTree(range);\n }\n this.focus();\n this.setSelection(range);\n this._updatePath(range);\n\n return this;\n }\n\n insertImage(\n src: string,\n attributes: Record<string, string>,\n ): HTMLImageElement {\n const img = createElement(\n 'IMG',\n Object.assign(\n {\n src: src,\n },\n attributes,\n ),\n ) as HTMLImageElement;\n this.insertElement(img);\n return img;\n }\n\n insertPlainText(plainText: string, isPaste: boolean): Squire {\n const range = this.getSelection();\n if (\n range.collapsed &&\n getNearest(range.startContainer, this._root, 'PRE')\n ) {\n const startContainer: Node = range.startContainer;\n let offset = range.startOffset;\n let textNode: Text;\n if (!startContainer || !(startContainer instanceof Text)) {\n const text = document.createTextNode('');\n startContainer.insertBefore(\n text,\n startContainer.childNodes[offset],\n );\n textNode = text;\n offset = 0;\n } else {\n textNode = startContainer;\n }\n let doInsert = true;\n if (isPaste) {\n const event = new CustomEvent('willPaste', {\n cancelable: true,\n detail: {\n text: plainText,\n },\n });\n this.fireEvent('willPaste', event);\n plainText = event.detail.text;\n doInsert = !event.defaultPrevented;\n }\n\n if (doInsert) {\n textNode.insertData(offset, plainText);\n range.setStart(textNode, offset + plainText.length);\n range.collapse(true);\n }\n this.setSelection(range);\n return this;\n }\n const lines = plainText.split('\\n');\n const config = this._config;\n const tag = config.blockTag;\n const attributes = config.blockAttributes;\n const closeBlock = '</' + tag + '>';\n let openBlock = '<' + tag;\n\n for (const attr in attributes) {\n openBlock += ' ' + attr + '=\"' + escapeHTML(attributes[attr]) + '\"';\n }\n openBlock += '>';\n\n for (let i = 0, l = lines.length; i < l; i += 1) {\n let line = lines[i];\n line = escapeHTML(line).replace(/ (?=(?: |$))/g, '&nbsp;');\n // We don't wrap the first line in the block, so if it gets inserted\n // into a blank line it keeps that line's formatting.\n // Wrap each line in <div></div>\n if (i) {\n line = openBlock + (line || '<BR>') + closeBlock;\n }\n lines[i] = line;\n }\n return this.insertHTML(lines.join(''), isPaste);\n }\n\n getSelectedText(range?: Range): string {\n return getTextContentsOfRange(range || this.getSelection());\n }\n\n // --- Inline formatting\n\n /**\n * Extracts the font-family and font-size (if any) of the element\n * holding the cursor. If there's a selection, returns an empty object.\n */\n getFontInfo(range?: Range): Record<string, string | undefined> {\n const fontInfo = {\n color: undefined,\n backgroundColor: undefined,\n fontFamily: undefined,\n fontSize: undefined,\n } as Record<string, string | undefined>;\n\n if (!range) {\n range = this.getSelection();\n }\n moveRangeBoundariesDownTree(range);\n\n let seenAttributes = 0;\n let element: Node | null = range.commonAncestorContainer;\n if (range.collapsed || element instanceof Text) {\n if (element instanceof Text) {\n element = element.parentNode!;\n }\n while (seenAttributes < 4 && element) {\n const style = (element as HTMLElement).style;\n if (style) {\n const color = style.color;\n if (!fontInfo.color && color) {\n fontInfo.color = color;\n seenAttributes += 1;\n }\n const backgroundColor = style.backgroundColor;\n if (!fontInfo.backgroundColor && backgroundColor) {\n fontInfo.backgroundColor = backgroundColor;\n seenAttributes += 1;\n }\n const fontFamily = style.fontFamily;\n if (!fontInfo.fontFamily && fontFamily) {\n fontInfo.fontFamily = fontFamily;\n seenAttributes += 1;\n }\n const fontSize = style.fontSize;\n if (!fontInfo.fontSize && fontSize) {\n fontInfo.fontSize = fontSize;\n seenAttributes += 1;\n }\n }\n element = element.parentNode;\n }\n }\n return fontInfo;\n }\n\n /**\n * Looks for matching tag and attributes, so won't work if <strong>\n * instead of <b> etc.\n */\n hasFormat(\n tag: string,\n attributes?: Record<string, string> | null,\n range?: Range,\n ): boolean {\n // 1. Normalise the arguments and get selection\n tag = tag.toUpperCase();\n if (!attributes) {\n attributes = {};\n }\n if (!range) {\n range = this.getSelection();\n }\n\n // Move range up one level in the DOM tree if at the edge of a text\n // node, so we don't consider it included when it's not really.\n if (\n !range.collapsed &&\n range.startContainer instanceof Text &&\n range.startOffset === range.startContainer.length &&\n range.startContainer.nextSibling\n ) {\n range.setStartBefore(range.startContainer.nextSibling);\n }\n if (\n !range.collapsed &&\n range.endContainer instanceof Text &&\n range.endOffset === 0 &&\n range.endContainer.previousSibling\n ) {\n range.setEndAfter(range.endContainer.previousSibling);\n }\n\n // If the common ancestor is inside the tag we require, we definitely\n // have the format.\n const root = this._root;\n const common = range.commonAncestorContainer;\n if (getNearest(common, root, tag, attributes)) {\n return true;\n }\n\n // If common ancestor is a text node and doesn't have the format, we\n // definitely don't have it.\n if (common instanceof Text) {\n return false;\n }\n\n // Otherwise, check each text node at least partially contained within\n // the selection and make sure all of them have the format we want.\n const walker = new TreeIterator<Text>(common, SHOW_TEXT, (node) => {\n return isNodeContainedInRange(range!, node, true);\n });\n\n let seenNode = false;\n let node: Node | null;\n while ((node = walker.nextNode())) {\n if (!getNearest(node, root, tag, attributes)) {\n return false;\n }\n seenNode = true;\n }\n\n return seenNode;\n }\n\n changeFormat(\n add: { tag: string; attributes?: Record<string, string> } | null,\n remove?: { tag: string; attributes?: Record<string, string> } | null,\n range?: Range,\n partial?: boolean,\n ): Squire {\n // Normalise the arguments and get selection\n if (!range) {\n range = this.getSelection();\n }\n\n // Save undo checkpoint\n this.saveUndoState(range);\n\n if (remove) {\n range = this._removeFormat(\n remove.tag.toUpperCase(),\n remove.attributes || {},\n range,\n partial,\n );\n }\n if (add) {\n range = this._addFormat(\n add.tag.toUpperCase(),\n add.attributes || {},\n range,\n );\n }\n\n this.setSelection(range);\n this._updatePath(range, true);\n\n return this.focus();\n }\n\n _addFormat(\n tag: string,\n attributes: Record<string, string> | null,\n range: Range,\n ): Range {\n // If the range is collapsed we simply insert the node by wrapping\n // it round the range and focus it.\n const root = this._root;\n if (range.collapsed) {\n const el = fixCursor(createElement(tag, attributes));\n insertNodeInRange(range, el);\n const focusNode = el.firstChild || el;\n // Focus after the ZWS if present\n const focusOffset =\n focusNode instanceof Text ? focusNode.length : 0;\n range.setStart(focusNode, focusOffset);\n range.collapse(true);\n\n // Clean up any previous formats that may have been set on this\n // block that are unused.\n let block = el;\n while (isInline(block)) {\n block = block.parentNode!;\n }\n removeZWS(block, el);\n // Otherwise we find all the textnodes in the range (splitting\n // partially selected nodes) and if they're not already formatted\n // correctly we wrap them in the appropriate tag.\n } else {\n // Create an iterator to walk over all the text nodes under this\n // ancestor which are in the range and not already formatted\n // correctly.\n //\n // In Blink/WebKit, empty blocks may have no text nodes, just a\n // <br>. Therefore we wrap this in the tag as well, as this will\n // then cause it to apply when the user types something in the\n // block, which is presumably what was intended.\n //\n // IMG tags are included because we may want to create a link around\n // them, and adding other styles is harmless.\n const walker = new TreeIterator<Element | Text>(\n range.commonAncestorContainer,\n SHOW_ELEMENT_OR_TEXT,\n (node: Node) => {\n return (\n (node instanceof Text ||\n node.nodeName === 'BR' ||\n node.nodeName === 'IMG') &&\n isNodeContainedInRange(range, node, true)\n );\n },\n );\n\n // Start at the beginning node of the range and iterate through\n // all the nodes in the range that need formatting.\n let { startContainer, startOffset, endContainer, endOffset } =\n range;\n\n // Make sure we start with a valid node.\n walker.currentNode = startContainer;\n if (\n (!(startContainer instanceof Element) &&\n !(startContainer instanceof Text)) ||\n !walker.filter(startContainer)\n ) {\n const next = walker.nextNode();\n // If there are no interesting nodes in the selection, abort\n if (!next) {\n return range;\n }\n startContainer = next;\n startOffset = 0;\n }\n\n do {\n let node = walker.currentNode;\n const needsFormat = !getNearest(node, root, tag, attributes);\n if (needsFormat) {\n // <br> can never be a container node, so must have a text\n // node if node == (end|start)Container\n if (\n node === endContainer &&\n (node as Text).length > endOffset\n ) {\n (node as Text).splitText(endOffset);\n }\n if (node === startContainer && startOffset) {\n node = (node as Text).splitText(startOffset);\n if (endContainer === startContainer) {\n endContainer = node;\n endOffset -= startOffset;\n } else if (endContainer === startContainer.parentNode) {\n endOffset += 1;\n }\n startContainer = node;\n startOffset = 0;\n }\n const el = createElement(tag, attributes);\n replaceWith(node, el);\n el.appendChild(node);\n }\n } while (walker.nextNode());\n\n // Now set the selection to as it was before\n range = createRange(\n startContainer,\n startOffset,\n endContainer,\n endOffset,\n );\n }\n return range;\n }\n\n _removeFormat(\n tag: string,\n attributes: Record<string, string>,\n range: Range,\n partial?: boolean,\n ): Range {\n // Add bookmark\n this._saveRangeToBookmark(range);\n\n // We need a node in the selection to break the surrounding\n // formatted text.\n let fixer: Node | Text | null | undefined;\n if (range.collapsed) {\n if (cantFocusEmptyTextNodes) {\n fixer = document.createTextNode(ZWS);\n } else {\n fixer = document.createTextNode('');\n }\n insertNodeInRange(range, fixer!);\n }\n\n // Find block-level ancestor of selection\n let root = range.commonAncestorContainer;\n while (isInline(root)) {\n root = root.parentNode!;\n }\n\n // Find text nodes inside formatTags that are not in selection and\n // add an extra tag with the same formatting.\n const startContainer = range.startContainer;\n const startOffset = range.startOffset;\n const endContainer = range.endContainer;\n const endOffset = range.endOffset;\n const toWrap: [Node, Node][] = [];\n const examineNode = (node: Node, exemplar: Node) => {\n // If the node is completely contained by the range then\n // we're going to remove all formatting so ignore it.\n if (isNodeContainedInRange(range, node, false)) {\n return;\n }\n\n let child: Node;\n let next: Node;\n\n // If not at least partially contained, wrap entire contents\n // in a clone of the tag we're removing and we're done.\n if (!isNodeContainedInRange(range, node, true)) {\n // Ignore bookmarks and empty text nodes\n if (\n !(node instanceof HTMLInputElement) &&\n (!(node instanceof Text) || node.data)\n ) {\n toWrap.push([exemplar, node]);\n }\n return;\n }\n\n // Split any partially selected text nodes.\n if (node instanceof Text) {\n if (node === endContainer && endOffset !== node.length) {\n toWrap.push([exemplar, node.splitText(endOffset)]);\n }\n if (node === startContainer && startOffset) {\n node.splitText(startOffset);\n toWrap.push([exemplar, node]);\n }\n } else {\n // If not a text node, recurse onto all children.\n // Beware, the tree may be rewritten with each call\n // to examineNode, hence find the next sibling first.\n for (child = node.firstChild!; child; child = next) {\n next = child.nextSibling!;\n examineNode(child, exemplar);\n }\n }\n };\n const formatTags = Array.from(\n (root as Element).getElementsByTagName(tag),\n ).filter((el: Node): boolean => {\n return (\n isNodeContainedInRange(range, el, true) &&\n hasTagAttributes(el, tag, attributes)\n );\n });\n\n if (!partial) {\n formatTags.forEach((node: Node) => {\n examineNode(node, node);\n });\n }\n\n // Now wrap unselected nodes in the tag\n toWrap.forEach(([el, node]) => {\n el = el.cloneNode(false);\n replaceWith(node, el);\n el.appendChild(node);\n });\n // and remove old formatting tags.\n formatTags.forEach((el: Element) => {\n replaceWith(el, empty(el));\n });\n\n if (cantFocusEmptyTextNodes && fixer) {\n // Clean up any previous ZWS in this block. They are not needed,\n // and this works around a Chrome bug where it doesn't render the\n // text in some situations with multiple ZWS(!)\n fixer = fixer.parentNode;\n let block = fixer;\n while (block && isInline(block)) {\n block = block.parentNode;\n }\n if (block) {\n removeZWS(block, fixer);\n }\n }\n\n // Merge adjacent inlines:\n this._getRangeAndRemoveBookmark(range);\n if (fixer) {\n range.collapse(false);\n }\n mergeInlines(root, range);\n\n return range;\n }\n\n // ---\n\n bold(): Squire {\n return this.changeFormat({ tag: 'B' });\n }\n\n removeBold(): Squire {\n return this.changeFormat(null, { tag: 'B' });\n }\n\n italic(): Squire {\n return this.changeFormat({ tag: 'I' });\n }\n\n removeItalic(): Squire {\n return this.changeFormat(null, { tag: 'I' });\n }\n\n underline(): Squire {\n return this.changeFormat({ tag: 'U' });\n }\n\n removeUnderline(): Squire {\n return this.changeFormat(null, { tag: 'U' });\n }\n\n strikethrough(): Squire {\n return this.changeFormat({ tag: 'S' });\n }\n\n removeStrikethrough(): Squire {\n return this.changeFormat(null, { tag: 'S' });\n }\n\n subscript(): Squire {\n return this.changeFormat({ tag: 'SUB' }, { tag: 'SUP' });\n }\n\n removeSubscript(): Squire {\n return this.changeFormat(null, { tag: 'SUB' });\n }\n\n superscript(): Squire {\n return this.changeFormat({ tag: 'SUP' }, { tag: 'SUB' });\n }\n\n removeSuperscript(): Squire {\n return this.changeFormat(null, { tag: 'SUP' });\n }\n\n // ---\n\n makeLink(url: string, attributes?: Record<string, string>): Squire {\n const range = this.getSelection();\n if (range.collapsed) {\n let protocolEnd = url.indexOf(':') + 1;\n if (protocolEnd) {\n while (url[protocolEnd] === '/') {\n protocolEnd += 1;\n }\n }\n insertNodeInRange(\n range,\n document.createTextNode(url.slice(protocolEnd)),\n );\n }\n attributes = Object.assign(\n {\n href: url,\n },\n this._config.tagAttributes.a,\n attributes,\n );\n\n return this.changeFormat(\n {\n tag: 'A',\n attributes: attributes as Record<string, string>,\n },\n {\n tag: 'A',\n },\n range,\n );\n }\n\n removeLink(): Squire {\n return this.changeFormat(\n null,\n {\n tag: 'A',\n },\n this.getSelection(),\n true,\n );\n }\n\n /*\n linkRegExp = new RegExp(\n // Only look on boundaries\n '\\\\b(?:' +\n // Capture group 1: URLs\n '(' +\n // Add links to URLS\n // Starts with:\n '(?:' +\n // http(s):// or ftp://\n '(?:ht|f)tps?:\\\\/\\\\/' +\n // or\n '|' +\n // www.\n 'www\\\\d{0,3}[.]' +\n // or\n '|' +\n // foo90.com/\n '[a-z0-9][a-z0-9.\\\\-]*[.][a-z]{2,}\\\\/' +\n ')' +\n // Then we get one or more:\n '(?:' +\n // Run of non-spaces, non ()<>\n '[^\\\\s()<>]+' +\n // or\n '|' +\n // balanced parentheses (one level deep only)\n '\\\\([^\\\\s()<>]+\\\\)' +\n ')+' +\n // And we finish with\n '(?:' +\n // Not a space or punctuation character\n '[^\\\\s?&`!()\\\\[\\\\]{};:\\'\".,<>\u00AB\u00BB\u201C\u201D\u2018\u2019]' +\n // or\n '|' +\n // Balanced parentheses.\n '\\\\([^\\\\s()<>]+\\\\)' +\n ')' +\n // Capture group 2: Emails\n ')|(' +\n // Add links to emails\n '[\\\\w\\\\-.%+]+@(?:[\\\\w\\\\-]+\\\\.)+[a-z]{2,}\\\\b' +\n // Allow query parameters in the mailto: style\n '(?:' +\n '[?][^&?\\\\s]+=[^\\\\s?&`!()\\\\[\\\\]{};:\\'\".,<>\u00AB\u00BB\u201C\u201D\u2018\u2019]+' +\n '(?:&[^&?\\\\s]+=[^\\\\s?&`!()\\\\[\\\\]{};:\\'\".,<>\u00AB\u00BB\u201C\u201D\u2018\u2019]+)*' +\n ')?' +\n '))',\n 'i'\n );\n */\n linkRegExp =\n /\\b(?:((?:(?:ht|f)tps?:\\/\\/|www\\d{0,3}[.]|[a-z0-9][a-z0-9.\\-]*[.][a-z]{2,}\\/)(?:[^\\s()<>]+|\\([^\\s()<>]+\\))+(?:[^\\s?&`!()\\[\\]{};:'\".,<>\u00AB\u00BB\u201C\u201D\u2018\u2019]|\\([^\\s()<>]+\\)))|([\\w\\-.%+]+@(?:[\\w\\-]+\\.)+[a-z]{2,}\\b(?:[?][^&?\\s]+=[^\\s?&`!()\\[\\]{};:'\".,<>\u00AB\u00BB\u201C\u201D\u2018\u2019]+(?:&[^&?\\s]+=[^\\s?&`!()\\[\\]{};:'\".,<>\u00AB\u00BB\u201C\u201D\u2018\u2019]+)*)?))/i;\n\n addDetectedLinks(\n searchInNode: DocumentFragment | Node,\n root?: DocumentFragment | HTMLElement,\n ): Squire {\n const walker = new TreeIterator<Text>(\n searchInNode,\n SHOW_TEXT,\n (node) => !getNearest(node, root || this._root, 'A'),\n );\n const linkRegExp = this.linkRegExp;\n const defaultAttributes = this._config.tagAttributes.a;\n let node: Text | null;\n while ((node = walker.nextNode())) {\n const parent = node.parentNode!;\n let data = node.data;\n let match: RegExpExecArray | null;\n while ((match = linkRegExp.exec(data))) {\n const index = match.index;\n const endIndex = index + match[0].length;\n if (index) {\n parent.insertBefore(\n document.createTextNode(data.slice(0, index)),\n node,\n );\n }\n const child = createElement(\n 'A',\n Object.assign(\n {\n href: match[1]\n ? /^(?:ht|f)tps?:/i.test(match[1])\n ? match[1]\n : 'http://' + match[1]\n : 'mailto:' + match[0],\n },\n defaultAttributes,\n ),\n );\n child.textContent = data.slice(index, endIndex);\n parent.insertBefore(child, node);\n node.data = data = data.slice(endIndex);\n }\n }\n return this;\n }\n\n // ---\n\n setFontFace(name: string | null): Squire {\n const className = this._config.classNames.fontFamily;\n return this.changeFormat(\n name\n ? {\n tag: 'SPAN',\n attributes: {\n class: className,\n style: 'font-family: ' + name + ', sans-serif;',\n },\n }\n : null,\n {\n tag: 'SPAN',\n attributes: { class: className },\n },\n );\n }\n\n setFontSize(size: string | null): Squire {\n const className = this._config.classNames.fontSize;\n return this.changeFormat(\n size\n ? {\n tag: 'SPAN',\n attributes: {\n class: className,\n style:\n 'font-size: ' +\n (typeof size === 'number' ? size + 'px' : size),\n },\n }\n : null,\n {\n tag: 'SPAN',\n attributes: { class: className },\n },\n );\n }\n\n setTextColor(color: string | null): Squire {\n const className = this._config.classNames.color;\n return this.changeFormat(\n color\n ? {\n tag: 'SPAN',\n attributes: {\n class: className,\n style: 'color:' + color,\n },\n }\n : null,\n {\n tag: 'SPAN',\n attributes: { class: className },\n },\n );\n }\n\n setHighlightColor(color: string | null): Squire {\n const className = this._config.classNames.highlight;\n return this.changeFormat(\n color\n ? {\n tag: 'SPAN',\n attributes: {\n class: className,\n style: 'background-color:' + color,\n },\n }\n : null,\n {\n tag: 'SPAN',\n attributes: { class: className },\n },\n );\n }\n\n // --- Block formatting\n\n _ensureBottomLine(): void {\n const root = this._root;\n const last = root.lastElementChild;\n if (\n !last ||\n last.nodeName !== this._config.blockTag ||\n !isBlock(last)\n ) {\n root.appendChild(this.createDefaultBlock());\n }\n }\n\n createDefaultBlock(children?: Node[]): HTMLElement {\n const config = this._config;\n return fixCursor(\n createElement(config.blockTag, config.blockAttributes, children),\n ) as HTMLElement;\n }\n\n tagAfterSplit: Record<string, string> = {\n DT: 'DD',\n DD: 'DT',\n LI: 'LI',\n PRE: 'PRE',\n };\n\n splitBlock(lineBreakOnly: boolean, range?: Range): Squire {\n if (!range) {\n range = this.getSelection();\n }\n const root = this._root;\n let block: Node | Element | null;\n let parent: Node | null;\n let node: Node;\n let nodeAfterSplit: Node;\n\n // Save undo checkpoint and remove any zws so we don't think there's\n // content in an empty block.\n this._recordUndoState(range);\n this._removeZWS();\n this._getRangeAndRemoveBookmark(range);\n\n // Selected text is overwritten, therefore delete the contents\n // to collapse selection.\n if (!range.collapsed) {\n deleteContentsOfRange(range, root);\n }\n\n // Linkify text\n if (this._config.addLinks) {\n moveRangeBoundariesDownTree(range);\n const textNode = range.startContainer as Text;\n const offset = range.startOffset;\n setTimeout(() => {\n linkifyText(this, textNode, offset);\n }, 0);\n }\n\n block = getStartBlockOfRange(range, root);\n\n // Inside a PRE, insert literal newline, unless on blank line.\n if (block && (parent = getNearest(block, root, 'PRE'))) {\n moveRangeBoundariesDownTree(range);\n node = range.startContainer;\n const offset = range.startOffset;\n if (!(node instanceof Text)) {\n node = document.createTextNode('');\n parent.insertBefore(node, parent.firstChild);\n }\n // If blank line: split and insert default block\n if (\n !lineBreakOnly &&\n node instanceof Text &&\n (node.data.charAt(offset - 1) === '\\n' ||\n rangeDoesStartAtBlockBoundary(range, root)) &&\n (node.data.charAt(offset) === '\\n' ||\n rangeDoesEndAtBlockBoundary(range, root))\n ) {\n node.deleteData(offset && offset - 1, offset ? 2 : 1);\n nodeAfterSplit = split(\n node,\n offset && offset - 1,\n root,\n root,\n ) as Node;\n node = nodeAfterSplit.previousSibling!;\n if (!node.textContent) {\n detach(node);\n }\n node = this.createDefaultBlock();\n nodeAfterSplit.parentNode!.insertBefore(node, nodeAfterSplit);\n if (!nodeAfterSplit.textContent) {\n detach(nodeAfterSplit);\n }\n range.setStart(node, 0);\n } else {\n (node as Text).insertData(offset, '\\n');\n fixCursor(parent);\n // Firefox bug: if you set the selection in the text node after\n // the new line, it draws the cursor before the line break still\n // but if you set the selection to the equivalent position\n // in the parent, it works.\n if ((node as Text).length === offset + 1) {\n range.setStartAfter(node);\n } else {\n range.setStart(node, offset + 1);\n }\n }\n range.collapse(true);\n this.setSelection(range);\n this._updatePath(range, true);\n this._docWasChanged();\n return this;\n }\n\n // If this is a malformed bit of document or in a table;\n // just play it safe and insert a <br>.\n if (!block || lineBreakOnly || /^T[HD]$/.test(block.nodeName)) {\n // If inside an <a>, move focus out\n moveRangeBoundaryOutOf(range, 'A', root);\n insertNodeInRange(range, createElement('BR'));\n range.collapse(false);\n this.setSelection(range);\n this._updatePath(range, true);\n return this;\n }\n\n // If in a list, we'll split the LI instead.\n if ((parent = getNearest(block, root, 'LI'))) {\n block = parent;\n }\n\n if (isEmptyBlock(block as Element)) {\n if (\n getNearest(block, root, 'UL') ||\n getNearest(block, root, 'OL')\n ) {\n // Break list\n this.decreaseListLevel(range);\n return this;\n // Break blockquote\n } else if (getNearest(block, root, 'BLOCKQUOTE')) {\n this.replaceWithBlankLine(range);\n return this;\n }\n }\n\n // Otherwise, split at cursor point.\n node = range.startContainer;\n const offset = range.startOffset;\n let splitTag = this.tagAfterSplit[block.nodeName];\n nodeAfterSplit = split(\n node,\n offset,\n block.parentNode!,\n this._root,\n ) as Node;\n\n const config = this._config;\n let splitProperties: Record<string, string> | null = null;\n if (!splitTag) {\n splitTag = config.blockTag;\n splitProperties = config.blockAttributes;\n }\n\n // Make sure the new node is the correct type.\n if (!hasTagAttributes(nodeAfterSplit, splitTag, splitProperties)) {\n block = createElement(splitTag, splitProperties);\n if ((nodeAfterSplit as HTMLElement).dir) {\n (block as HTMLElement).dir = (\n nodeAfterSplit as HTMLElement\n ).dir;\n }\n replaceWith(nodeAfterSplit, block);\n block.appendChild(empty(nodeAfterSplit));\n nodeAfterSplit = block;\n }\n\n // Clean up any empty inlines if we hit enter at the beginning of the\n // block\n removeZWS(block);\n removeEmptyInlines(block);\n fixCursor(block);\n\n // Focus cursor\n // If there's a <b>/<i> etc. at the beginning of the split\n // make sure we focus inside it.\n while (nodeAfterSplit instanceof Element) {\n let child = nodeAfterSplit.firstChild;\n let next;\n\n // Don't continue links over a block break; unlikely to be the\n // desired outcome.\n if (\n nodeAfterSplit.nodeName === 'A' &&\n (!nodeAfterSplit.textContent ||\n nodeAfterSplit.textContent === ZWS)\n ) {\n child = document.createTextNode('') as Text;\n replaceWith(nodeAfterSplit, child);\n nodeAfterSplit = child;\n break;\n }\n\n while (child && child instanceof Text && !child.data) {\n next = child.nextSibling;\n if (!next || next.nodeName === 'BR') {\n break;\n }\n detach(child);\n child = next;\n }\n\n // 'BR's essentially don't count; they're a browser hack.\n // If you try to select the contents of a 'BR', FF will not let\n // you type anything!\n if (!child || child.nodeName === 'BR' || child instanceof Text) {\n break;\n }\n nodeAfterSplit = child;\n }\n range = createRange(nodeAfterSplit, 0);\n this.setSelection(range);\n this._updatePath(range, true);\n\n return this;\n }\n\n forEachBlock(\n fn: (el: HTMLElement) => any,\n mutates: boolean,\n range?: Range,\n ): Squire {\n if (!range) {\n range = this.getSelection();\n }\n\n // Save undo checkpoint\n if (mutates) {\n this.saveUndoState(range);\n }\n\n const root = this._root;\n let start = getStartBlockOfRange(range, root);\n const end = getEndBlockOfRange(range, root);\n if (start && end) {\n do {\n if (fn(start) || start === end) {\n break;\n }\n } while ((start = getNextBlock(start, root)));\n }\n\n if (mutates) {\n this.setSelection(range);\n // Path may have changed\n this._updatePath(range, true);\n }\n return this;\n }\n\n modifyBlocks(modify: (x: DocumentFragment) => Node, range?: Range): Squire {\n if (!range) {\n range = this.getSelection();\n }\n\n // 1. Save undo checkpoint and bookmark selection\n this._recordUndoState(range, this._isInUndoState);\n\n // 2. Expand range to block boundaries\n const root = this._root;\n expandRangeToBlockBoundaries(range, root);\n\n // 3. Remove range.\n moveRangeBoundariesUpTree(range, root, root, root);\n const frag = extractContentsOfRange(range, root, root);\n\n // 4. Modify tree of fragment and reinsert.\n if (!range.collapsed) {\n // After extracting contents, the range edges will still be at the\n // level we began the spilt. We want to insert directly in the\n // root, so move the range up there.\n let node = range.endContainer;\n if (node === root) {\n range.collapse(false);\n } else {\n while (node.parentNode !== root) {\n node = node.parentNode!;\n }\n range.setStartBefore(node);\n range.collapse(true);\n }\n }\n insertNodeInRange(range, modify.call(this, frag));\n\n // 5. Merge containers at edges\n if (range.endOffset < range.endContainer.childNodes.length) {\n mergeContainers(\n range.endContainer.childNodes[range.endOffset],\n root,\n );\n }\n mergeContainers(\n range.startContainer.childNodes[range.startOffset],\n root,\n );\n\n // 6. Restore selection\n this._getRangeAndRemoveBookmark(range);\n this.setSelection(range);\n this._updatePath(range, true);\n\n return this;\n }\n\n // ---\n\n setTextAlignment(alignment: string): Squire {\n this.forEachBlock((block: HTMLElement) => {\n const className = block.className\n .split(/\\s+/)\n .filter((klass) => {\n return !!klass && !/^align/.test(klass);\n })\n .join(' ');\n if (alignment) {\n block.className = className + ' align-' + alignment;\n block.style.textAlign = alignment;\n } else {\n block.className = className;\n block.style.textAlign = '';\n }\n }, true);\n return this.focus();\n }\n\n setTextDirection(direction: string | null): Squire {\n this.forEachBlock((block: HTMLElement) => {\n if (direction) {\n block.dir = direction;\n } else {\n block.removeAttribute('dir');\n }\n }, true);\n return this.focus();\n }\n\n // ---\n\n _getListSelection(\n range: Range,\n root: Element,\n ): [Node, Node | null, Node | null] | null {\n let list: Node | null = range.commonAncestorContainer;\n let startLi: Node | null = range.startContainer;\n let endLi: Node | null = range.endContainer;\n while (list && list !== root && !/^[OU]L$/.test(list.nodeName)) {\n list = list.parentNode;\n }\n if (!list || list === root) {\n return null;\n }\n if (startLi === list) {\n startLi = startLi.childNodes[range.startOffset];\n }\n if (endLi === list) {\n endLi = endLi.childNodes[range.endOffset];\n }\n while (startLi && startLi.parentNode !== list) {\n startLi = startLi.parentNode;\n }\n while (endLi && endLi.parentNode !== list) {\n endLi = endLi.parentNode;\n }\n return [list, startLi, endLi];\n }\n\n increaseListLevel(range?: Range) {\n if (!range) {\n range = this.getSelection();\n }\n\n // Get start+end li in single common ancestor\n const root = this._root;\n const listSelection = this._getListSelection(range, root);\n if (!listSelection) {\n return this.focus();\n }\n // eslint-disable-next-line prefer-const\n let [list, startLi, endLi] = listSelection;\n if (!startLi || startLi === list.firstChild) {\n return this.focus();\n }\n\n // Save undo checkpoint and bookmark selection\n this._recordUndoState(range, this._isInUndoState);\n\n // Increase list depth\n const type = list.nodeName;\n let newParent = startLi.previousSibling!;\n let listAttrs: Record<string, string> | null;\n let next: Node | null;\n if (newParent.nodeName !== type) {\n listAttrs = this._config.tagAttributes[type.toLowerCase()];\n newParent = createElement(type, listAttrs);\n list.insertBefore(newParent, startLi);\n }\n do {\n next = startLi === endLi ? null : startLi.nextSibling;\n newParent.appendChild(startLi);\n } while ((startLi = next));\n next = newParent.nextSibling;\n if (next) {\n mergeContainers(next, root);\n }\n\n // Restore selection\n this._getRangeAndRemoveBookmark(range);\n this.setSelection(range);\n this._updatePath(range, true);\n\n return this.focus();\n }\n\n decreaseListLevel(range?: Range) {\n if (!range) {\n range = this.getSelection();\n }\n\n const root = this._root;\n const listSelection = this._getListSelection(range, root);\n if (!listSelection) {\n return this.focus();\n }\n\n // eslint-disable-next-line prefer-const\n let [list, startLi, endLi] = listSelection;\n if (!startLi) {\n startLi = list.firstChild;\n }\n if (!endLi) {\n endLi = list.lastChild!;\n }\n\n // Save undo checkpoint and bookmark selection\n this._recordUndoState(range, this._isInUndoState);\n\n let next: Node | null;\n let insertBefore: Node | null = null;\n if (startLi) {\n // Find the new parent list node\n let newParent = list.parentNode!;\n\n // Split list if necessary\n insertBefore = !endLi.nextSibling\n ? list.nextSibling\n : (split(list, endLi.nextSibling, newParent, root) as Node);\n\n if (newParent !== root && newParent.nodeName === 'LI') {\n newParent = newParent.parentNode!;\n while (insertBefore) {\n next = insertBefore.nextSibling;\n endLi.appendChild(insertBefore);\n insertBefore = next;\n }\n insertBefore = list.parentNode!.nextSibling;\n }\n\n const makeNotList = !/^[OU]L$/.test(newParent.nodeName);\n do {\n next = startLi === endLi ? null : startLi.nextSibling;\n list.removeChild(startLi);\n if (makeNotList && startLi.nodeName === 'LI') {\n startLi = this.createDefaultBlock([empty(startLi)]);\n }\n newParent.insertBefore(startLi!, insertBefore);\n } while ((startLi = next));\n }\n\n if (!list.firstChild) {\n detach(list);\n }\n\n if (insertBefore) {\n mergeContainers(insertBefore, root);\n }\n\n // Restore selection\n this._getRangeAndRemoveBookmark(range);\n this.setSelection(range);\n this._updatePath(range, true);\n\n return this.focus();\n }\n\n _makeList(frag: DocumentFragment, type: string): DocumentFragment {\n const walker = getBlockWalker(frag, this._root);\n const tagAttributes = this._config.tagAttributes;\n const listAttrs = tagAttributes[type.toLowerCase()];\n const listItemAttrs = tagAttributes.li;\n let node: Node | null;\n while ((node = walker.nextNode())) {\n if (node.parentNode! instanceof HTMLLIElement) {\n node = node.parentNode!;\n walker.currentNode = node.lastChild!;\n }\n if (!(node instanceof HTMLLIElement)) {\n const newLi = createElement('LI', listItemAttrs);\n if ((node as HTMLElement).dir) {\n newLi.dir = (node as HTMLElement).dir;\n }\n\n // Have we replaced the previous block with a new <ul>/<ol>?\n const prev: ChildNode | null = node.previousSibling;\n if (prev && prev.nodeName === type) {\n prev.appendChild(newLi);\n detach(node);\n // Otherwise, replace this block with the <ul>/<ol>\n } else {\n replaceWith(node, createElement(type, listAttrs, [newLi]));\n }\n newLi.appendChild(empty(node));\n walker.currentNode = newLi;\n } else {\n node = node.parentNode;\n const tag = node!.nodeName;\n if (tag !== type && /^[OU]L$/.test(tag)) {\n replaceWith(\n node!,\n createElement(type, listAttrs, [empty(node!)]),\n );\n }\n }\n }\n return frag;\n }\n\n makeUnorderedList(): Squire {\n this.modifyBlocks((frag) => this._makeList(frag, 'UL'));\n return this.focus();\n }\n\n makeOrderedList(): Squire {\n this.modifyBlocks((frag) => this._makeList(frag, 'OL'));\n return this.focus();\n }\n\n removeList(): Squire {\n this.modifyBlocks((frag) => {\n const lists = frag.querySelectorAll('UL, OL');\n const items = frag.querySelectorAll('LI');\n const root = this._root;\n for (let i = 0, l = lists.length; i < l; i += 1) {\n const list = lists[i];\n const listFrag = empty(list);\n fixContainer(listFrag, root);\n replaceWith(list, listFrag);\n }\n\n for (let i = 0, l = items.length; i < l; i += 1) {\n const item = items[i];\n if (isBlock(item)) {\n replaceWith(item, this.createDefaultBlock([empty(item)]));\n } else {\n fixContainer(item, root);\n replaceWith(item, empty(item));\n }\n }\n return frag;\n });\n return this.focus();\n }\n\n // ---\n\n increaseQuoteLevel(range?: Range): Squire {\n this.modifyBlocks(\n (frag) =>\n createElement(\n 'BLOCKQUOTE',\n this._config.tagAttributes.blockquote,\n [frag],\n ),\n range,\n );\n return this.focus();\n }\n\n decreaseQuoteLevel(range?: Range): Squire {\n this.modifyBlocks((frag) => {\n Array.from(frag.querySelectorAll('blockquote'))\n .filter((el: Node) => {\n return !getNearest(el.parentNode, frag, 'BLOCKQUOTE');\n })\n .forEach((el: Node) => {\n replaceWith(el, empty(el));\n });\n return frag;\n }, range);\n return this.focus();\n }\n\n removeQuote(range?: Range): Squire {\n this.modifyBlocks((frag) => {\n Array.from(frag.querySelectorAll('blockquote')).forEach(\n (el: Node) => {\n replaceWith(el, empty(el));\n },\n );\n return frag;\n }, range);\n return this.focus();\n }\n\n replaceWithBlankLine(range?: Range): Squire {\n this.modifyBlocks(\n (/* frag */) =>\n this.createDefaultBlock([\n createElement('INPUT', {\n id: this.startSelectionId,\n type: 'hidden',\n }),\n createElement('INPUT', {\n id: this.endSelectionId,\n type: 'hidden',\n }),\n ]),\n range,\n );\n return this.focus();\n }\n\n // ---\n\n code(): Squire {\n const range = this.getSelection();\n if (range.collapsed || isContainer(range.commonAncestorContainer)) {\n this.modifyBlocks((frag) => {\n const root = this._root;\n const output = document.createDocumentFragment();\n const blockWalker = getBlockWalker(frag, root);\n let node: Element | Text | null;\n // 1. Extract inline content; drop all blocks and contains.\n while ((node = blockWalker.nextNode())) {\n // 2. Replace <br> with \\n in content\n let nodes = node.querySelectorAll('BR');\n const brBreaksLine: boolean[] = [];\n let l = nodes.length;\n // Must calculate whether the <br> breaks a line first,\n // because if we have two <br>s next to each other, after\n // the first one is converted to a block split, the second\n // will be at the end of a block and therefore seem to not\n // be a line break. But in its original context it was, so\n // we should also convert it to a block split.\n for (let i = 0; i < l; i += 1) {\n brBreaksLine[i] = isLineBreak(nodes[i], false);\n }\n while (l--) {\n const br = nodes[l];\n if (!brBreaksLine[l]) {\n detach(br);\n } else {\n replaceWith(br, document.createTextNode('\\n'));\n }\n }\n // 3. Remove <code>; its format clashes with <pre>\n nodes = node.querySelectorAll('CODE');\n l = nodes.length;\n while (l--) {\n replaceWith(nodes[l], empty(nodes[l]));\n }\n if (output.childNodes.length) {\n output.appendChild(document.createTextNode('\\n'));\n }\n output.appendChild(empty(node));\n }\n // 4. Replace nbsp with regular sp\n const textWalker = new TreeIterator<Text>(output, SHOW_TEXT);\n while ((node = textWalker.nextNode())) {\n // eslint-disable-next-line no-irregular-whitespace\n node.data = node.data.replace(/\u00A0/g, ' '); // nbsp -> sp\n }\n output.normalize();\n return fixCursor(\n createElement('PRE', this._config.tagAttributes.pre, [\n output,\n ]),\n );\n }, range);\n this.focus();\n } else {\n this.changeFormat(\n {\n tag: 'CODE',\n attributes: this._config.tagAttributes.code,\n },\n null,\n range,\n );\n }\n return this;\n }\n\n removeCode(): Squire {\n const range = this.getSelection();\n const ancestor = range.commonAncestorContainer;\n const inPre = getNearest(ancestor, this._root, 'PRE');\n if (inPre) {\n this.modifyBlocks((frag) => {\n const root = this._root;\n const pres = frag.querySelectorAll('PRE');\n let l = pres.length;\n while (l--) {\n const pre = pres[l];\n const walker = new TreeIterator<Text>(pre, SHOW_TEXT);\n let node: Text | null;\n while ((node = walker.nextNode())) {\n let value = node.data;\n value = value.replace(/ (?= )/g, '\u00A0'); // sp -> nbsp\n const contents = document.createDocumentFragment();\n let index: number;\n while ((index = value.indexOf('\\n')) > -1) {\n contents.appendChild(\n document.createTextNode(value.slice(0, index)),\n );\n contents.appendChild(createElement('BR'));\n value = value.slice(index + 1);\n }\n node.parentNode!.insertBefore(contents, node);\n node.data = value;\n }\n fixContainer(pre, root);\n replaceWith(pre, empty(pre));\n }\n return frag;\n }, range);\n this.focus();\n } else {\n this.changeFormat(null, { tag: 'CODE' }, range);\n }\n return this;\n }\n\n toggleCode(): Squire {\n if (this.hasFormat('PRE') || this.hasFormat('CODE')) {\n this.removeCode();\n } else {\n this.code();\n }\n return this;\n }\n\n // ---\n\n _removeFormatting(\n root: DocumentFragment | Element,\n clean: DocumentFragment | Element,\n ): DocumentFragment | Element {\n for (\n let node = root.firstChild, next: ChildNode | null;\n node;\n node = next\n ) {\n next = node.nextSibling;\n if (isInline(node)) {\n if (\n node instanceof Text ||\n node.nodeName === 'BR' ||\n node.nodeName === 'IMG'\n ) {\n clean.appendChild(node);\n continue;\n }\n } else if (isBlock(node)) {\n clean.appendChild(\n this.createDefaultBlock([\n this._removeFormatting(\n node as Element,\n document.createDocumentFragment(),\n ),\n ]),\n );\n continue;\n }\n this._removeFormatting(node as Element, clean);\n }\n return clean;\n }\n\n removeAllFormatting(range?: Range): Squire {\n if (!range) {\n range = this.getSelection();\n }\n if (range.collapsed) {\n return this.focus();\n }\n\n const root = this._root;\n let stopNode = range.commonAncestorContainer;\n while (stopNode && !isBlock(stopNode)) {\n stopNode = stopNode.parentNode!;\n }\n if (!stopNode) {\n expandRangeToBlockBoundaries(range, root);\n stopNode = root;\n }\n if (stopNode instanceof Text) {\n return this.focus();\n }\n\n // Record undo point\n this.saveUndoState(range);\n\n // Avoid splitting where we're already at edges.\n moveRangeBoundariesUpTree(range, stopNode, stopNode, root);\n\n // Split the selection up to the block, or if whole selection in same\n // block, expand range boundaries to ends of block and split up to root.\n const startContainer = range.startContainer;\n let startOffset = range.startOffset;\n const endContainer = range.endContainer;\n let endOffset = range.endOffset;\n\n // Split end point first to avoid problems when end and start\n // in same container.\n const formattedNodes = document.createDocumentFragment();\n const cleanNodes = document.createDocumentFragment();\n const nodeAfterSplit = split(endContainer, endOffset, stopNode, root);\n let nodeInSplit = split(startContainer, startOffset, stopNode, root);\n let nextNode: ChildNode | null;\n\n // Then replace contents in split with a cleaned version of the same:\n // blocks become default blocks, text and leaf nodes survive, everything\n // else is obliterated.\n while (nodeInSplit !== nodeAfterSplit) {\n nextNode = nodeInSplit!.nextSibling;\n formattedNodes.appendChild(nodeInSplit!);\n nodeInSplit = nextNode;\n }\n this._removeFormatting(formattedNodes, cleanNodes);\n cleanNodes.normalize();\n nodeInSplit = cleanNodes.firstChild;\n nextNode = cleanNodes.lastChild;\n\n // Restore selection\n if (nodeInSplit) {\n stopNode.insertBefore(cleanNodes, nodeAfterSplit);\n const childNodes = Array.from(stopNode.childNodes) as Node[];\n startOffset = childNodes.indexOf(nodeInSplit);\n endOffset = nextNode ? childNodes.indexOf(nextNode) + 1 : 0;\n } else if (nodeAfterSplit) {\n const childNodes = Array.from(stopNode.childNodes) as Node[];\n startOffset = childNodes.indexOf(nodeAfterSplit);\n endOffset = startOffset;\n }\n\n // Merge text nodes at edges, if possible\n range.setStart(stopNode, startOffset);\n range.setEnd(stopNode, endOffset);\n mergeInlines(stopNode, range);\n\n // And move back down the tree\n moveRangeBoundariesDownTree(range);\n\n this.setSelection(range);\n this._updatePath(range, true);\n\n return this.focus();\n }\n}\n\n// ---\n\nexport { Squire };\nexport type { SquireConfig };\n", "import { Squire } from './Editor';\n\nexport default Squire;\n"],
"mappings": "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",
"names": ["always", "TreeIterator", "root", "nodeType", "filter", "node", "current", "ZWS", "ua", "isMac", "isWin", "isIOS", "isAndroid", "isGecko", "isLegacyEdge", "isWebKit", "ctrlKey", "cantFocusEmptyTextNodes", "supportsInputEvents", "notWS", "inlineNodeNames", "leafNodeNames", "UNKNOWN", "INLINE", "BLOCK", "CONTAINER", "cache", "resetNodeCategoryCache", "isLeaf", "node", "getNodeCategory", "nodeCategory", "isInline", "isBlock", "isContainer", "createElement", "tag", "props", "children", "el", "attr", "value", "node", "areAlike", "node2", "isLeaf", "hasTagAttributes", "attributes", "getNearest", "root", "getNodeBeforeOffset", "offset", "getNodeAfterOffset", "returnNode", "getLength", "empty", "frag", "child", "detach", "parent", "replaceWith", "notWSTextNode", "node", "notWS", "isLineBreak", "br", "isLBIfEmptyBlock", "block", "isInline", "walker", "TreeIterator", "removeZWS", "root", "keepNode", "textNode", "index", "ZWS", "parent", "getLength", "START_TO_START", "START_TO_END", "END_TO_END", "END_TO_START", "isNodeContainedInRange", "range", "node", "partial", "nodeRange", "nodeEndBeforeStart", "nodeStartAfterEnd", "nodeStartAfterStart", "nodeEndBeforeEnd", "moveRangeBoundariesDownTree", "startContainer", "startOffset", "endContainer", "endOffset", "child", "isLeaf", "textChild", "prev", "isLineBreak", "getLength", "moveRangeBoundariesUpTree", "startMax", "endMax", "root", "parent", "moveRangeBoundaryOutOf", "tag", "getNearest", "clone", "fixCursor", "node", "fixer", "isInline", "child", "cantFocusEmptyTextNodes", "ZWS", "createElement", "parent", "fixContainer", "container", "root", "wrapper", "isBR", "isContainer", "split", "offset", "stopNode", "nodeAfterSplit", "clone", "next", "getNearest", "_mergeInlines", "fakeRange", "children", "l", "frags", "prev", "areAlike", "getLength", "detach", "empty", "frag", "mergeInlines", "range", "element", "mergeWithBlock", "block", "last", "mergeContainers", "first", "isListItem", "needsFix", "styleToSemantic", "createElement", "notWS", "classNames", "family", "size", "replaceStyles", "node", "_", "config", "style", "newTreeBottom", "newTreeTop", "attr", "converter", "css", "el", "empty", "replaceWith", "replaceWithTag", "tag", "parent", "attributes", "i", "l", "attribute", "fontSizes", "stylesRewriters", "font", "face", "color", "fontSpan", "sizeSpan", "colorSpan", "allowedBlock", "blacklist", "cleanTree", "preserveWS", "children", "nonInlineParent", "isInline", "walker", "TreeIterator", "child", "nodeName", "rewriter", "childLength", "data", "startsWithWS", "endsWithWS", "sibling", "removeEmptyInlines", "isLeaf", "cleanupBRs", "root", "keepForBlankLine", "brs", "brBreaksLine", "isLineBreak", "br", "fixContainer", "detach", "escapeHTML", "text", "getBlockWalker", "node", "root", "walker", "TreeIterator", "isBlock", "getPreviousBlock", "block", "getNextBlock", "isEmptyBlock", "getStartBlockOfRange", "range", "root", "container", "block", "isInline", "getPreviousBlock", "isBlock", "node", "getNodeBeforeOffset", "getNextBlock", "isNodeContainedInRange", "getEndBlockOfRange", "getNodeAfterOffset", "child", "isContent", "notWS", "rangeDoesStartAtBlockBoundary", "startContainer", "startOffset", "nodeAfterCursor", "text", "i", "ZWS", "contentWalker", "TreeIterator", "rangeDoesEndAtBlockBoundary", "endContainer", "endOffset", "currentNode", "length", "expandRangeToBlockBoundaries", "start", "end", "parent", "createRange", "startContainer", "startOffset", "endContainer", "endOffset", "range", "insertNodeInRange", "node", "children", "parent", "afterSplit", "childCount", "extractContentsOfRange", "common", "root", "frag", "split", "next", "detach", "fixCursor", "getAdjacentInlineNode", "iterator", "method", "nextNode", "isLeaf", "isInline", "deleteContentsOfRange", "startBlock", "getStartBlockOfRange", "endBlock", "getEndBlockOfRange", "needsMerge", "moveRangeBoundariesDownTree", "moveRangeBoundariesUpTree", "mergeWithBlock", "child", "TreeIterator", "afterNode", "afterOffset", "beforeNode", "beforeOffset", "offset", "rangeDoesStartAtBlockBoundary", "rangeDoesEndAtBlockBoundary", "insertTreeFragmentIntoRange", "firstInFragIsInline", "fixContainer", "getNextBlock", "stopPoint", "getNearest", "block", "blockContentsAfterSplit", "firstBlockInFrag", "replaceBlock", "isEmptyBlock", "container", "cleanupBRs", "nodeAfterSplit", "getPreviousBlock", "getLength", "nodeBeforeSplit", "isContainer", "mergeContainers", "tempRange", "getTextContentsOfRange", "range", "startContainer", "endContainer", "walker", "TreeIterator", "node", "isNodeContainedInRange", "textContent", "addedTextInBlock", "value", "isInline", "indexOf", "extractRangeToClipboard", "event", "range", "root", "removeRangeFromDocument", "toCleanHTML", "toPlainText", "plainTextOnly", "clipboardData", "isLegacyEdge", "text", "getTextContentsOfRange", "startBlock", "getStartBlockOfRange", "endBlock", "getEndBlockOfRange", "copyRoot", "contents", "deleteContentsOfRange", "moveRangeBoundariesDownTree", "moveRangeBoundariesUpTree", "parent", "newContents", "html", "node", "createElement", "isWin", "_onCut", "error", "_onCopy", "_monitorShiftKey", "_onPaste", "items", "choosePlain", "hasRTF", "hasImage", "plainItem", "htmlItem", "l", "item", "type", "isLink", "notWS", "match", "types", "isGecko", "data", "body", "startContainer", "startOffset", "endContainer", "endOffset", "pasteArea", "next", "first", "detach", "createRange", "_onDrop", "hasPlain", "hasHTML", "Enter", "self", "event", "range", "afterDelete", "self", "range", "node", "parent", "isInline", "ZWS", "isBlock", "getPreviousBlock", "fixCursor", "moveRangeBoundariesDownTree", "detach", "error", "detachUneditableNode", "root", "linkifyText", "textNode", "offset", "getNearest", "data", "searchFrom", "searchText", "match", "selection", "index", "endIndex", "needsSelectionUpdate", "newSelectionOffset", "defaultAttributes", "link", "createElement", "Backspace", "self", "event", "range", "root", "deleteContentsOfRange", "afterDelete", "rangeDoesStartAtBlockBoundary", "startBlock", "getStartBlockOfRange", "current", "fixContainer", "previous", "getPreviousBlock", "detachUneditableNode", "mergeWithBlock", "mergeContainers", "getNearest", "moveRangeBoundariesDownTree", "text", "offset", "a", "Delete", "self", "event", "range", "root", "current", "next", "originalRange", "cursorContainer", "cursorOffset", "nodeAfterCursor", "deleteContentsOfRange", "afterDelete", "rangeDoesEndAtBlockBoundary", "getStartBlockOfRange", "fixContainer", "getNextBlock", "detachUneditableNode", "mergeWithBlock", "mergeContainers", "moveRangeBoundariesUpTree", "detach", "moveRangeBoundariesDownTree", "Tab", "self", "event", "range", "root", "rangeDoesStartAtBlockBoundary", "node", "getStartBlockOfRange", "parent", "ShiftTab", "getNearest", "Space", "self", "event", "range", "node", "root", "deleteContentsOfRange", "rangeDoesEndAtBlockBoundary", "block", "getStartBlockOfRange", "text", "ZWS", "walker", "TreeIterator", "textNode", "detach", "getLength", "linkRange", "moveRangeBoundariesDownTree", "offset", "linkifyText", "_onKey", "event", "key", "modifiers", "code", "isWin", "range", "deleteContentsOfRange", "keyHandlers", "Backspace", "Delete", "Tab", "ShiftTab", "Space", "self", "root", "rangeDoesEndAtBlockBoundary", "moveRangeBoundariesDownTree", "node", "next", "textNode", "supportsInputEvents", "Enter", "isMac", "isIOS", "mapKeyToFormat", "tag", "remove", "ctrlKey", "path", "Squire", "root", "config", "createRange", "_onCut", "_onCopy", "_onPaste", "_onDrop", "_monitorShiftKey", "_onKey", "keyHandlers", "mutation", "_", "type", "userConfig", "html", "frag", "error", "key", "fn", "event", "alignment", "dir", "detail", "handlers", "isFocused", "handler", "target", "l", "removeZWS", "range", "startNode", "createElement", "endNode", "temp", "insertNodeInRange", "start", "end", "startContainer", "endContainer", "startOffset", "endOffset", "mergeInlines", "selection", "isLeaf", "toStart", "moveRangeBoundariesDownTree", "rect", "node", "ZWS", "parent", "force", "anchor", "focus", "newPath", "path", "id", "classList", "classNames", "styleNames", "modificationFn", "resetNodeCategoryCache", "replace", "isInUndoState", "undoIndex", "undoStack", "undoConfig", "undoThreshold", "undoLimit", "undoStackLength", "child", "block", "getNextBlock", "fixCursor", "withBookmark", "cleanTree", "cleanupBRs", "fixContainer", "isPaste", "removeEmptyInlines", "doInsert", "insertTreeFragmentIntoRange", "moveRangeBoundaryOutOf", "el", "isInline", "getStartBlockOfRange", "splitNode", "nodeAfterSplit", "split", "isEmptyBlock", "detach", "blankLine", "src", "attributes", "img", "plainText", "getNearest", "offset", "textNode", "text", "lines", "tag", "closeBlock", "openBlock", "attr", "escapeHTML", "i", "line", "getTextContentsOfRange", "fontInfo", "seenAttributes", "element", "style", "color", "backgroundColor", "fontFamily", "fontSize", "common", "walker", "TreeIterator", "isNodeContainedInRange", "seenNode", "add", "remove", "partial", "focusNode", "focusOffset", "next", "replaceWith", "fixer", "cantFocusEmptyTextNodes", "toWrap", "examineNode", "exemplar", "formatTags", "hasTagAttributes", "empty", "url", "protocolEnd", "searchInNode", "linkRegExp", "defaultAttributes", "data", "match", "index", "endIndex", "name", "className", "size", "last", "isBlock", "children", "lineBreakOnly", "deleteContentsOfRange", "linkifyText", "rangeDoesStartAtBlockBoundary", "rangeDoesEndAtBlockBoundary", "splitTag", "splitProperties", "mutates", "getEndBlockOfRange", "modify", "expandRangeToBlockBoundaries", "moveRangeBoundariesUpTree", "extractContentsOfRange", "mergeContainers", "klass", "direction", "list", "startLi", "endLi", "listSelection", "newParent", "listAttrs", "insertBefore", "makeNotList", "getBlockWalker", "tagAttributes", "listItemAttrs", "newLi", "prev", "lists", "items", "listFrag", "item", "isContainer", "output", "blockWalker", "nodes", "brBreaksLine", "isLineBreak", "br", "textWalker", "ancestor", "pres", "pre", "value", "contents", "clean", "stopNode", "formattedNodes", "cleanNodes", "nodeInSplit", "nextNode", "childNodes", "Squire_default", "Squire"]
}