{
  "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/ImageResize.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\n// Include ZWS - \\u200B - because we use that as a placeholder for focusing\n// text nodes, but will strip it out after.\nconst notWS = /[^ \\t\\r\\n\\u200B]/;\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, notWS } 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        !notWS.test(node.textContent || '')\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    // We can't wrap stuff inside some tags with <div>, because of the HTML\n    // parsing rules \u2014 when you read it out as HTML then write it back, you'll\n    // end up with a different DOM tree (e.g. when inside a <p>, encountering\n    // a <div> will auto-close it). So ignore these and hope for the best.\n    if (/^(?:TABLE|TBODY|TR|TH|TD|P)/.test(container.nodeName)) {\n        return container;\n    }\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';\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        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                    const range = this.getSelection();\n                    if (!range.collapsed && notWS.test(range.toString())) {\n                        const match = this.linkRegExp.exec(text);\n                        const isLink =\n                            !!match && match[0].length === text.length;\n                        if (isLink) {\n                            const href = match[1]\n                                ? /^(?:ht|f)tps?:/i.test(match[1])\n                                    ? match[1]\n                                    : 'http://' + match[1]\n                                : 'mailto:' + match[0];\n                            this.makeLink(href);\n                            return;\n                        }\n                    }\n                    this.insertPlainText(text, true);\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    const handler = this._keyHandlers[key];\n    if (handler) {\n        handler(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 { getNextBlock, getPreviousBlock } from './node/Block';\nimport { createElement } from './node/Node';\nimport { getStartBlockOfRange } from './range/Block';\nimport type { Squire } from './Editor';\n\n// ---\n\n// Configuration for image resizing\nconst RESIZE_HANDLE_SIZE = 8;\nconst MIN_IMAGE_SIZE = 40;\nconst MAX_IMAGE_SIZE = 1200;\n\ntype ResizeHandle = {\n    element: HTMLElement;\n    cursor: string;\n    position: 'nw' | 'n' | 'ne' | 'e' | 'se' | 's' | 'sw' | 'w';\n};\n\nconst handlePositions: Array<{\n    pos: ResizeHandle['position'];\n    cursor: string;\n}> = [\n    { pos: 'nw', cursor: 'nwse-resize' },\n    { pos: 'n', cursor: 'ns-resize' },\n    { pos: 'ne', cursor: 'nesw-resize' },\n    { pos: 'e', cursor: 'ew-resize' },\n    { pos: 'se', cursor: 'nwse-resize' },\n    { pos: 's', cursor: 'ns-resize' },\n    { pos: 'sw', cursor: 'nesw-resize' },\n    { pos: 'w', cursor: 'ew-resize' },\n];\n\nconst keyHandlers = {\n    ArrowUp: (editor: Squire, range: Range, root: HTMLElement) => {\n        const block = getStartBlockOfRange(range, root);\n        if (block) {\n            const prev = getPreviousBlock(block, root);\n            if (prev) {\n                range.selectNodeContents(prev);\n                range.collapse(false);\n                editor.setSelection(range).focus();\n            }\n        }\n    },\n    ArrowDown: (editor: Squire, range: Range, root: HTMLElement) => {\n        const block = getStartBlockOfRange(range, root);\n        if (block) {\n            const next = getNextBlock(block, root);\n            if (next) {\n                range.selectNodeContents(next);\n                range.collapse(true);\n                editor.setSelection(range).focus();\n            }\n        }\n    },\n    Delete: (editor: Squire, range: Range /* , root */) => {\n        editor.replaceWithBlankLine(range);\n    },\n    Backspace: (editor: Squire, range: Range /* , root */) => {\n        editor.replaceWithBlankLine(range);\n    },\n};\n\nclass ImageResizer {\n    private _editor: Squire;\n    private _root: HTMLElement;\n    private _currentImage: HTMLImageElement | null = null;\n    private _resizeContainer: HTMLElement | null = null;\n    private _handles: ResizeHandle[] | null = null;\n    private _currentHandle: ResizeHandle | null = null;\n    private _startX = 0;\n    private _startY = 0;\n    private _startWidth = 0;\n    private _startHeight = 0;\n    private _maxWidth = MAX_IMAGE_SIZE;\n    private _originalRatio = 1;\n\n    constructor(root: HTMLElement, editor: Squire) {\n        this._editor = editor;\n        this._root = root;\n\n        // Register this object as the event handler\n        document.addEventListener('click', this);\n        editor.addEventListener('drop', this);\n    }\n\n    destroy(): void {\n        this._deselectImage();\n        this._editor.removeEventListener('drop', this);\n        document.removeEventListener('click', this);\n    }\n\n    // EventListener interface implementation\n    handleEvent(event: Event): void {\n        switch (event.type) {\n            case 'click':\n                this._onClick(event as PointerEvent);\n                break;\n            case 'pointerdown':\n                this._onPointerDown(event as PointerEvent);\n                break;\n            case 'pointermove':\n                this._onPointerMove(event as PointerEvent);\n                break;\n            case 'pointercancel':\n            case 'pointerup':\n                this._onPointerUp(event as PointerEvent);\n                break;\n            case 'keydown':\n                this._onKeyDown(event as KeyboardEvent);\n                break;\n            case 'drop':\n                this._deselectImage();\n                break;\n        }\n    }\n\n    // ---\n\n    private _onClick(event: PointerEvent): void {\n        const target = event.target as HTMLElement;\n        if (target.nodeName === 'IMG' && this._root.contains(target)) {\n            event.stopPropagation();\n            this._selectImage(target as HTMLImageElement);\n        } else if (\n            this._currentImage &&\n            this._handles &&\n            !this._handles.some((handle) => handle.element === target)\n        ) {\n            this._deselectImage();\n        }\n    }\n\n    private _deselectImage(): void {\n        if (!this._currentImage) {\n            return;\n        }\n\n        document.removeEventListener('keydown', this);\n\n        if (this._currentHandle) {\n            this._onPointerUp({\n                preventDefault() {},\n                target: this._currentHandle.element,\n            });\n        }\n        if (this._handles) {\n            this._handles.forEach(({ element }) =>\n                element.removeEventListener('pointerdown', this),\n            );\n        }\n        if (this._resizeContainer) {\n            this._resizeContainer.remove();\n        }\n\n        this._handles = null;\n        this._resizeContainer = null;\n        this._currentImage = null;\n    }\n\n    private _selectImage(image: HTMLImageElement): void {\n        if (this._currentImage === image) {\n            return; // Already selected\n        }\n\n        // Deselect current image if any\n        this._deselectImage();\n\n        this._root.blur();\n\n        const handles = handlePositions.map(({ pos, cursor }) => {\n            const offset = RESIZE_HANDLE_SIZE / 2;\n            let positionStyle = '';\n\n            switch (pos) {\n                case 'nw':\n                    positionStyle = `left: -${offset}px; top: -${offset}px;`;\n                    break;\n                case 'n':\n                    positionStyle = `left: calc(50% - ${offset}px); top: -${offset}px;`;\n                    break;\n                case 'ne':\n                    positionStyle = `right: -${offset}px; top: -${offset}px;`;\n                    break;\n                case 'e':\n                    positionStyle = `right: -${offset}px; top: calc(50% - ${offset}px);`;\n                    break;\n                case 'se':\n                    positionStyle = `right: -${offset}px; bottom: -${offset}px;`;\n                    break;\n                case 's':\n                    positionStyle = `left: calc(50% - ${offset}px); bottom: -${offset}px;`;\n                    break;\n                case 'sw':\n                    positionStyle = `left: -${offset}px; bottom: -${offset}px;`;\n                    break;\n                case 'w':\n                    positionStyle = `left: -${offset}px; top: calc(50% - ${offset}px);`;\n                    break;\n            }\n\n            const handle = createElement('div', {\n                class: `squire-resize-handle squire-resize-handle-${pos}`,\n                style: `\n                    position: absolute;\n                    width: ${RESIZE_HANDLE_SIZE}px;\n                    height: ${RESIZE_HANDLE_SIZE}px;\n                    background: #0067b9;\n                    border: 1px solid #fff;\n                    cursor: ${cursor};\n                    pointer-events: auto;\n                    touch-action: none;\n                    box-shadow: 0 1px 3px rgba(0,0,0,0.3);\n                    ${positionStyle}\n                `,\n            });\n            handle.addEventListener('pointerdown', this);\n            return {\n                element: handle,\n                cursor,\n                position: pos,\n            };\n        });\n        const resizeContainer = createElement(\n            'div',\n            {\n                class: 'squire-image-resize-container',\n                style: 'position: absolute; pointer-events: none; z-index: 1000;',\n            },\n            handles.map((handle) => handle.element),\n        );\n\n        this._currentImage = image;\n        this._resizeContainer = resizeContainer;\n        this._handles = handles;\n        this._root.appendChild(this._resizeContainer);\n\n        const naturalWidth = image.naturalWidth;\n        this._originalRatio = naturalWidth / image.naturalHeight;\n        this._maxWidth = Math.min(\n            naturalWidth * 2,\n            image.parentElement\n                ? image.parentElement.offsetWidth\n                : MAX_IMAGE_SIZE,\n            MAX_IMAGE_SIZE,\n        );\n\n        this._positionResizeContainer();\n        document.addEventListener('keydown', this);\n    }\n\n    private _positionResizeContainer(): void {\n        const resizeContainer = this._resizeContainer;\n        const root = this._root;\n        const currentImage = this._currentImage;\n        if (!resizeContainer || !currentImage) {\n            return;\n        }\n        const rootRect = root.getBoundingClientRect();\n        const imageRect = currentImage.getBoundingClientRect();\n\n        // Position relative to editor\n        const top = imageRect.top - rootRect.top + root.scrollTop;\n        const left = imageRect.left - rootRect.left + root.scrollLeft;\n        const width = imageRect.width;\n        const height = imageRect.height;\n\n        resizeContainer.style.top = top + 'px';\n        resizeContainer.style.left = left + 'px';\n        resizeContainer.style.width = width + 'px';\n        resizeContainer.style.height = height + 'px';\n    }\n\n    private _onPointerDown(event: PointerEvent): void {\n        if (this._currentHandle || !this._handles) {\n            return;\n        }\n        const target = event.target as HTMLElement;\n        const currentHandle =\n            this._handles.find((h) => h.element === target) || null;\n\n        if (!currentHandle) {\n            return;\n        }\n        const currentImage = this._currentImage;\n        if (!currentImage) {\n            return;\n        }\n        event.preventDefault();\n        event.stopPropagation();\n\n        target.addEventListener('pointermove', this);\n        target.addEventListener('pointerup', this);\n        target.addEventListener('pointercancel', this);\n        target.setPointerCapture(event.pointerId);\n        this._currentHandle = currentHandle;\n\n        this._startX = event.clientX;\n        this._startY = event.clientY;\n\n        const style = getComputedStyle(currentImage);\n        this._startWidth = parseFloat(style.width);\n        this._startHeight = parseFloat(style.height);\n\n        document.body.style.cursor = currentHandle.cursor;\n    }\n\n    private _onPointerMove(event: PointerEvent): void {\n        event.preventDefault();\n\n        const currentHandle = this._currentHandle;\n        const currentImage = this._currentImage;\n        if (!currentHandle || !currentImage) {\n            return;\n        }\n\n        const deltaX = event.clientX - this._startX;\n        const deltaY = event.clientY - this._startY;\n\n        const maxWidth = this._maxWidth;\n        const originalRatio = this._originalRatio;\n        let newWidth = this._startWidth;\n        let newHeight = this._startHeight;\n\n        // Calculate new dimensions based on which handle is being dragged\n        switch (currentHandle.position) {\n            case 'sw':\n            case 'nw':\n            case 'w':\n                newWidth -= 2 * deltaX;\n                break;\n            case 'se':\n            case 'ne':\n            case 'e':\n                newWidth += 2 * deltaX;\n                break;\n            case 'n':\n                newHeight -= deltaY;\n                newWidth = newHeight * originalRatio;\n                break;\n            case 's':\n                newHeight += deltaY;\n                newWidth = newHeight * originalRatio;\n                break;\n        }\n\n        // Apply min/max constraints\n        if (newWidth < MIN_IMAGE_SIZE) {\n            newWidth = MIN_IMAGE_SIZE;\n        } else if (newWidth > maxWidth) {\n            newWidth = maxWidth;\n        }\n\n        // Apply the new size\n        const currentImageStyle = currentImage.style;\n        currentImageStyle.width = newWidth + 'px';\n        currentImageStyle.height = 'auto';\n\n        // Update resize container position\n        this._positionResizeContainer();\n    }\n\n    private _onPointerUp(\n        event: PointerEvent | { preventDefault(): void; target: HTMLElement },\n    ): void {\n        event.preventDefault();\n\n        const target = event.target as HTMLElement;\n        if (target) {\n            target.removeEventListener('pointermove', this);\n            target.removeEventListener('pointerup', this);\n            target.removeEventListener('pointercancel', this);\n        }\n\n        this._currentHandle = null;\n\n        document.body.style.cursor = '';\n    }\n\n    private _onKeyDown(event: KeyboardEvent): void {\n        event.preventDefault();\n        event.stopPropagation();\n        const keyHandler = keyHandlers[event.key as keyof typeof keyHandlers];\n        if (!keyHandler) {\n            return;\n        }\n        const image = this._currentImage;\n        if (!image) {\n            return;\n        }\n        const editor = this._editor;\n        const root = this._root;\n        this._deselectImage();\n        const range = editor.getSelection();\n        range.selectNode(image);\n        keyHandler(editor, range, root);\n    }\n}\n\n// ---\n\nexport { ImageResizer };\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';\nimport { ImageResizer } from './ImageResize';\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 | null>;\n\n    _mutation: MutationObserver;\n    _imageResizer: ImageResizer;\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        // Initialize image resizer\n        this._imageResizer = new ImageResizer(root, this);\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        // Cleanup image resizer\n        this._imageResizer.destroy();\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 | null) {\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 'formatItalic':\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        let rangeIsFromSelection = false;\n        if (!range) {\n            range = this.getSelection();\n            rangeIsFromSelection = true;\n        }\n        this._recordUndoState(range, this._isInUndoState);\n        this._getRangeAndRemoveBookmark(range);\n        if (rangeIsFromSelection) {\n            this.setSelection(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        let html = '';\n        // Avoid triggering an \"input\" event from the DOM modifications when\n        // we get the HTML\n        this.modifyDocument(() => {\n            if (withBookmark) {\n                range = this.getSelection();\n                this._saveRangeToBookmark(range);\n            }\n            const resizeContainer = this._root.querySelector(\n                '.squire-image-resize-container',\n            );\n            if (resizeContainer) {\n                resizeContainer.remove();\n            }\n            html = this._getRawHTML().replace(/\\u200B/g, '');\n            if (resizeContainer) {\n                this._root.appendChild(resizeContainer);\n            }\n            if (withBookmark) {\n                this._getRangeAndRemoveBookmark(range);\n            }\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                if (!node.nextSibling) {\n                    parent.appendChild(createElement('BR'));\n                }\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", "notWS", "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", "parent", "newContents", "html", "node", "createElement", "isWin", "_onCut", "error", "_onCopy", "_monitorShiftKey", "_onPaste", "items", "choosePlain", "hasRTF", "hasImage", "plainItem", "htmlItem", "l", "item", "type", "notWS", "match", "href", "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", "handler", "deleteContentsOfRange", "keyHandlers", "Backspace", "Delete", "Tab", "ShiftTab", "Space", "self", "root", "rangeDoesEndAtBlockBoundary", "moveRangeBoundariesDownTree", "node", "next", "textNode", "supportsInputEvents", "Enter", "isMac", "isIOS", "mapKeyToFormat", "tag", "remove", "ctrlKey", "path", "RESIZE_HANDLE_SIZE", "MIN_IMAGE_SIZE", "MAX_IMAGE_SIZE", "handlePositions", "keyHandlers", "editor", "range", "root", "block", "getStartBlockOfRange", "prev", "getPreviousBlock", "next", "getNextBlock", "ImageResizer", "event", "target", "handle", "element", "image", "handles", "pos", "cursor", "offset", "positionStyle", "createElement", "resizeContainer", "naturalWidth", "currentImage", "rootRect", "imageRect", "top", "left", "width", "height", "currentHandle", "h", "style", "deltaX", "deltaY", "maxWidth", "originalRatio", "newWidth", "newHeight", "currentImageStyle", "keyHandler", "Squire", "root", "config", "createRange", "_onCut", "_onCopy", "_onPaste", "_onDrop", "_monitorShiftKey", "_onKey", "keyHandlers", "mutation", "ImageResizer", "_", "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", "rangeIsFromSelection", "undoStackLength", "child", "block", "getNextBlock", "fixCursor", "withBookmark", "resizeContainer", "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"]
}
