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