diff --git a/frontend/vendor/text_editor_v2.js b/frontend/vendor/text_editor_v2.js index 96683089f..f11ea57f1 100644 --- a/frontend/vendor/text_editor_v2.js +++ b/frontend/vendor/text_editor_v2.js @@ -1,2788 +1,3 @@ -var __typeError = (msg) => { - throw TypeError(msg); -}; -var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg); -var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj)); -var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); -var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value); -var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method); -var _timeout, _time, _hasPendingChanges, _onTimeout, _rootNode, _currentNode, _added, _removed, _updated, _textEditor, _selection, _ranges, _range, _focusNode, _focusOffset, _anchorNode, _anchorOffset, _savedSelection, _textNodeIterator, _currentStyle, _inertElement, _debug, _mutations, _styleDefaults, _SelectionController_instances, applyDefaultStylesToCurrentStyle_fn, applyStylesToCurrentStyle_fn, updateCurrentStyle_fn, _onSelectionChange, notifyStyleChange_fn, setup_fn, getSavedRange_fn, applyStylesTo_fn, _element, _events, _root, _changeController, _selectionController, _selectionImposterElement, _styleDefaults2, _TextEditor_instances, setupElementProperties_fn, setupRoot_fn, _onChange, _onStyleChange, setup_fn2, createSelectionImposter_fn, _onBlur, _onFocus, _onPaste, _onCut, _onCopy, _onBeforeInput, _onInput, notifyLayout_fn; -function copy(event, editor) { -} -function cut(event, editor) { -} -let canvas = null; -let context = null; -function getContext() { - if (!canvas) { - canvas = createCanvas(1, 1); - } - if (!context) { - context = canvas.getContext("2d"); - } - return context; -} -function createCanvas(width, height) { - if ("OffscreenCanvas" in globalThis) { - return new OffscreenCanvas(width, height); - } - return document.createElement("canvas"); -} -function getByteAsHex(byte) { - return byte.toString(16).padStart(2, "0"); -} -function getColor(fillStyle) { - const context2 = getContext(); - context2.fillStyle = fillStyle; - context2.fillRect(0, 0, 1, 1); - const imageData = context2.getImageData(0, 0, 1, 1); - const [r, g, b, a] = imageData.data; - return [`#${getByteAsHex(r)}${getByteAsHex(g)}${getByteAsHex(b)}`, a / 255]; -} -function getFills(fillStyle) { - const [color, opacity] = getColor(fillStyle); - return `[["^ ","~:fill-color","${color}","~:fill-opacity",${opacity}]]`; -} -function mergeStyleDeclarations(target, source) { - for (let index = 0; index < source.length; index++) { - const styleName = source.item(index); - target.setProperty(styleName, source.getPropertyValue(styleName)); - } - return target; -} -function getComputedStyle(element) { - const inertElement = document.createElement("div"); - let currentElement = element; - while (currentElement) { - for (let index = 0; index < currentElement.style.length; index++) { - const styleName = currentElement.style.item(index); - const currentValue = inertElement.style.getPropertyValue(styleName); - if (currentValue) { - const priority = currentElement.style.getPropertyPriority(styleName); - if (priority === "important") { - const newValue = currentElement.style.getPropertyValue(styleName); - inertElement.style.setProperty(styleName, newValue); - } - } else { - inertElement.style.setProperty( - styleName, - currentElement.style.getPropertyValue(styleName) - ); - } - } - currentElement = currentElement.parentElement; - } - return inertElement.style; -} -function normalizeStyles(styleDeclaration) { - const color = styleDeclaration.getPropertyValue("color"); - if (color) { - styleDeclaration.removeProperty("color"); - styleDeclaration.setProperty("--fills", getFills(color)); - } - const fontFamily = styleDeclaration.getPropertyValue("font-family"); - const fontId = styleDeclaration.getPropertyPriority("--font-id"); - if (fontFamily && !fontId) { - styleDeclaration.removeProperty("font-family"); - } - return styleDeclaration; -} -function setStyle(element, styleName, styleValue, styleUnit) { - if (styleName.startsWith("--") && typeof styleValue !== "string" && typeof styleValue !== "number") { - if (styleName === "--fills" && styleValue === null) debugger; - element.style.setProperty(styleName, JSON.stringify(styleValue)); - } else { - element.style.setProperty(styleName, styleValue + (styleUnit ?? "")); - } - return element; -} -function getStyleFromDeclaration(style, styleName, styleUnit) { - if (styleName.startsWith("--")) { - return style.getPropertyValue(styleName); - } - const styleValue = style.getPropertyValue(styleName); - if (styleValue.endsWith(styleUnit)) { - return styleValue.slice(0, -styleUnit.length); - } - return styleValue; -} -function setStylesFromObject(element, allowedStyles, styleObject) { - for (const [styleName, styleUnit] of allowedStyles) { - if (!(styleName in styleObject)) { - continue; - } - const styleValue = styleObject[styleName]; - if (styleValue) { - setStyle(element, styleName, styleValue, styleUnit); - } - } - return element; -} -function setStylesFromDeclaration(element, allowedStyles, styleDeclaration) { - for (const [styleName, styleUnit] of allowedStyles) { - const styleValue = getStyleFromDeclaration(styleDeclaration, styleName, styleUnit); - if (styleValue) { - setStyle(element, styleName, styleValue, styleUnit); - } - } - return element; -} -function setStyles(element, allowedStyles, styleObjectOrDeclaration) { - if (styleObjectOrDeclaration instanceof CSSStyleDeclaration) { - return setStylesFromDeclaration( - element, - allowedStyles, - styleObjectOrDeclaration - ); - } - return setStylesFromObject(element, allowedStyles, styleObjectOrDeclaration); -} -function mergeStyles(allowedStyles, styleDeclaration, newStyles) { - const mergedStyles = {}; - for (const [styleName, styleUnit] of allowedStyles) { - if (styleName in newStyles) { - mergedStyles[styleName] = newStyles[styleName]; - } else { - mergedStyles[styleName] = getStyleFromDeclaration(styleDeclaration, styleName, styleUnit); - } - } - return mergedStyles; -} -function isDisplayBlock(style) { - return style.display === "block"; -} -function createRandomId() { - return Math.floor(Math.random() * Number.MAX_SAFE_INTEGER).toString(36); -} -function createElement(tag, options) { - const element = document.createElement(tag); - if (options == null ? void 0 : options.attributes) { - Object.entries(options.attributes).forEach( - ([name, value]) => element.setAttribute(name, value) - ); - } - if (options == null ? void 0 : options.data) { - Object.entries(options.data).forEach( - ([name, value]) => element.dataset[name] = value - ); - } - if ((options == null ? void 0 : options.styles) && (options == null ? void 0 : options.allowedStyles)) { - setStyles(element, options.allowedStyles, options.styles); - } - if (options == null ? void 0 : options.children) { - if (Array.isArray(options.children)) { - element.append(...options.children); - } else { - element.appendChild(options.children); - } - } - return element; -} -function isElement(element, nodeName) { - return element.nodeType === Node.ELEMENT_NODE && element.nodeName === nodeName.toUpperCase(); -} -function isOffsetAtStart(node, offset) { - return offset === 0; -} -function isOffsetAtEnd(node, offset) { - if (node.nodeType === Node.TEXT_NODE) { - return node.nodeValue.length === offset; - } - return true; -} -const TAG$3 = "BR"; -function createLineBreak() { - return document.createElement(TAG$3); -} -function isLineBreak(node) { - return node.nodeType === Node.ELEMENT_NODE && node.nodeName === TAG$3; -} -const TAG$2 = "SPAN"; -const TYPE$2 = "inline"; -const QUERY$1 = `[data-itype="${TYPE$2}"]`; -const STYLES$2 = [ - ["--typography-ref-id"], - ["--typography-ref-file"], - ["--font-id"], - ["--font-variant-id"], - ["--fills"], - ["font-variant"], - ["font-family"], - ["font-size", "px"], - ["font-weight"], - ["font-style"], - ["line-height"], - ["letter-spacing", "px"], - ["text-decoration"], - ["text-transform"] -]; -function isInline(node) { - if (!node) return false; - if (!isElement(node, TAG$2)) return false; - if (node.dataset.itype !== TYPE$2) return false; - return true; -} -function isLikeInline(element) { - return element ? [ - "A", - "ABBR", - "ACRONYM", - "B", - "BDO", - "BIG", - "BR", - "BUTTON", - "CITE", - "CODE", - "DFN", - "EM", - "I", - "IMG", - "INPUT", - "KBD", - "LABEL", - "MAP", - "OBJECT", - "OUTPUT", - "Q", - "SAMP", - "SCRIPT", - "SELECT", - "SMALL", - "SPAN", - "STRONG", - "SUB", - "SUP", - "TEXTAREA", - "TIME", - "TT", - "VAR" - ].includes(element.nodeName) : false; -} -function createInline(textOrLineBreak, styles, attrs) { - if (!(textOrLineBreak instanceof HTMLBRElement) && !(textOrLineBreak instanceof Text)) { - throw new TypeError("Invalid inline child"); - } - if (textOrLineBreak instanceof Text && textOrLineBreak.nodeValue.length === 0) { - console.trace("nodeValue", textOrLineBreak.nodeValue); - throw new TypeError("Invalid inline child, cannot be an empty text"); - } - return createElement(TAG$2, { - attributes: { id: createRandomId(), ...attrs }, - data: { itype: TYPE$2 }, - styles, - allowedStyles: STYLES$2, - children: textOrLineBreak - }); -} -function createInlineFrom(inline, textOrLineBreak, styles, attrs) { - return createInline( - textOrLineBreak, - mergeStyles(STYLES$2, inline.style, styles), - attrs - ); -} -function createEmptyInline(styles) { - return createInline(createLineBreak(), styles); -} -function setInlineStyles(element, styles) { - return setStyles(element, STYLES$2, styles); -} -function getInline(node) { - if (!node) return null; - if (isInline(node)) return node; - if (node.nodeType === Node.TEXT_NODE) { - const inline = node == null ? void 0 : node.parentElement; - if (!inline) return null; - if (!isInline(inline)) return null; - return inline; - } - return node.closest(QUERY$1); -} -function isInlineStart(node, offset) { - const inline = getInline(node); - if (!inline) return false; - return isOffsetAtStart(inline, offset); -} -function isInlineEnd(node, offset) { - const inline = getInline(node); - if (!inline) return false; - return isOffsetAtEnd(inline.firstChild, offset); -} -function splitInline(inline, offset) { - const textNode = inline.firstChild; - const style = inline.style; - const newTextNode = textNode.splitText(offset); - return createInline(newTextNode, style); -} -function getInlinesFrom(startInline) { - const inlines = []; - let currentInline = startInline; - let index = 0; - while (currentInline) { - if (index > 0) inlines.push(currentInline); - currentInline = currentInline.nextElementSibling; - index++; - } - return inlines; -} -function getInlineLength(inline) { - if (!isInline(inline)) throw new Error("Invalid inline"); - if (isLineBreak(inline.firstChild)) return 0; - return inline.firstChild.nodeValue.length; -} -const TAG$1 = "DIV"; -const TYPE$1 = "root"; -const STYLES$1 = [["--vertical-align"]]; -function isRoot(node) { - if (!node) return false; - if (!isElement(node, TAG$1)) return false; - if (node.dataset.itype !== TYPE$1) return false; - return true; -} -function createRoot(paragraphs, styles, attrs) { - if (!Array.isArray(paragraphs) || !paragraphs.every(isParagraph)) - throw new TypeError("Invalid root children"); - return createElement(TAG$1, { - attributes: { id: createRandomId(), ...attrs }, - data: { itype: TYPE$1 }, - styles, - allowedStyles: STYLES$1, - children: paragraphs - }); -} -function createEmptyRoot(styles) { - return createRoot([createEmptyParagraph(styles)], styles); -} -function setRootStyles(element, styles) { - return setStyles(element, STYLES$1, styles); -} -function isTextNode(node) { - if (!node) throw new TypeError("Invalid text node"); - return node.nodeType === Node.TEXT_NODE || isLineBreak(node); -} -function getTextNodeLength(node) { - if (!node) throw new TypeError("Invalid text node"); - if (isLineBreak(node)) return 0; - return node.nodeValue.length; -} -function getClosestTextNode(node) { - if (isTextNode(node)) return node; - if (isInline(node)) return node.firstChild; - if (isParagraph(node)) return node.firstChild.firstChild; - if (isRoot(node)) return node.firstChild.firstChild.firstChild; - throw new Error("Cannot find a text node"); -} -const TAG = "DIV"; -const TYPE = "paragraph"; -const QUERY = `[data-itype="${TYPE}"]`; -const STYLES = [ - ["--typography-ref-id"], - ["--typography-ref-file"], - ["--font-id"], - ["--font-variant-id"], - ["--fills"], - ["font-variant"], - ["font-family"], - ["font-size", "px"], - ["font-weight"], - ["font-style"], - ["line-height"], - ["letter-spacing", "px"], - ["text-decoration"], - ["text-transform"], - ["text-align"], - ["direction"] -]; -function isLikeParagraph(element) { - return !isLikeInline(element); -} -function isEmptyParagraph(element) { - if (!isParagraph(element)) throw new TypeError("Invalid paragraph"); - const inline = element.firstChild; - if (!isInline(inline)) throw new TypeError("Invalid inline"); - return isLineBreak(inline.firstChild); -} -function isParagraph(node) { - if (!node) return false; - if (!isElement(node, TAG)) return false; - if (node.dataset.itype !== TYPE) return false; - return true; -} -function createParagraph(inlines, styles, attrs) { - if (inlines && (!Array.isArray(inlines) || !inlines.every(isInline))) - throw new TypeError("Invalid paragraph children"); - return createElement(TAG, { - attributes: { id: createRandomId(), ...attrs }, - data: { itype: TYPE }, - styles, - allowedStyles: STYLES, - children: inlines - }); -} -function createEmptyParagraph(styles) { - return createParagraph([ - createEmptyInline(styles) - ], styles); -} -function setParagraphStyles(element, styles) { - return setStyles(element, STYLES, styles); -} -function getParagraph(node) { - var _a; - if (!node) return null; - if (isParagraph(node)) return node; - if (node.nodeType === Node.TEXT_NODE || isLineBreak(node)) { - const paragraph = (_a = node == null ? void 0 : node.parentElement) == null ? void 0 : _a.parentElement; - if (!paragraph) { - return null; - } - if (!isParagraph(paragraph)) { - return null; - } - return paragraph; - } - return node.closest(QUERY); -} -function isParagraphStart(node, offset) { - const paragraph = getParagraph(node); - if (!paragraph) throw new Error("Can't find the paragraph"); - const inline = getInline(node); - if (!inline) throw new Error("Can't find the inline"); - return paragraph.firstElementChild === inline && isOffsetAtStart(inline.firstChild, offset); -} -function isParagraphEnd(node, offset) { - const paragraph = getParagraph(node); - if (!paragraph) throw new Error("Cannot find the paragraph"); - const inline = getInline(node); - if (!inline) throw new Error("Cannot find the inline"); - return paragraph.lastElementChild === inline && isOffsetAtEnd(inline.firstChild, offset); -} -function splitParagraph(paragraph, inline, offset) { - const style = paragraph.style; - if (isInlineEnd(inline, offset)) { - const newParagraph2 = createParagraph(getInlinesFrom(inline), style); - return newParagraph2; - } - const newInline = splitInline(inline, offset); - const newParagraph = createParagraph([newInline], style); - return newParagraph; -} -function mergeParagraphs(a, b) { - a.append(...b.children); - b.remove(); - return a; -} -function mapContentFragmentFromDocument(document2, root, styleDefaults) { - const nodeIterator = document2.createNodeIterator(root, NodeFilter.SHOW_TEXT); - const fragment = document2.createDocumentFragment(); - let currentParagraph = null; - let currentNode = nodeIterator.nextNode(); - while (currentNode) { - const parentStyle = normalizeStyles(mergeStyleDeclarations(styleDefaults, getComputedStyle(currentNode.parentElement))); - if (isDisplayBlock(currentNode.parentElement.style) || isDisplayBlock(parentStyle) || isLikeParagraph(currentNode.parentElement)) { - if (currentParagraph) { - fragment.appendChild(currentParagraph); - } - currentParagraph = createParagraph(void 0, parentStyle); - } else { - if (currentParagraph === null) { - currentParagraph = createParagraph(); - } - } - currentParagraph.appendChild( - createInline(new Text(currentNode.nodeValue), parentStyle) - ); - currentNode = nodeIterator.nextNode(); - } - fragment.appendChild(currentParagraph); - return fragment; -} -function mapContentFragmentFromHTML(html, styleDefaults) { - const parser = new DOMParser(); - const htmlDocument = parser.parseFromString(html, "text/html"); - return mapContentFragmentFromDocument( - htmlDocument, - htmlDocument.documentElement, - styleDefaults - ); -} -function mapContentFragmentFromString(string, styleDefaults) { - const lines = string.replace(/\r/g, "").split("\n"); - const fragment = document.createDocumentFragment(); - for (const line of lines) { - if (line === "") { - fragment.appendChild(createEmptyParagraph(styleDefaults)); - } else { - fragment.appendChild(createParagraph([createInline(new Text(line), styleDefaults)], styleDefaults)); - } - } - return fragment; -} -function paste(event, editor, selectionController) { - event.preventDefault(); - let fragment = null; - if (event.clipboardData.types.includes("text/html")) { - const html = event.clipboardData.getData("text/html"); - fragment = mapContentFragmentFromHTML(html, selectionController.currentStyle); - } else if (event.clipboardData.types.includes("text/plain")) { - const plain = event.clipboardData.getData("text/plain"); - fragment = mapContentFragmentFromString(plain, selectionController.currentStyle); - } - if (!fragment) { - return; - } - if (selectionController.isCollapsed) { - selectionController.insertPaste(fragment); - } else { - selectionController.replaceWithPaste(fragment); - } -} -const clipboard = { - copy, - cut, - paste -}; -function insertText(event, editor, selectionController) { - event.preventDefault(); - if (selectionController.isCollapsed) { - if (selectionController.isTextFocus) { - return selectionController.insertText(event.data); - } else if (selectionController.isLineBreakFocus) { - return selectionController.replaceLineBreak(event.data); - } - } else { - if (selectionController.isMultiParagraph) { - return selectionController.replaceParagraphs(event.data); - } else if (selectionController.isMultiInline) { - return selectionController.replaceInlines(event.data); - } else if (selectionController.isTextSame) { - return selectionController.replaceText(event.data); - } - } -} -function insertParagraph(event, editor, selectionController) { - event.preventDefault(); - if (selectionController.isCollapsed) { - return selectionController.insertParagraph(); - } - return selectionController.replaceWithParagraph(); -} -function deleteByCut(event, editor, selectionController) { - event.preventDefault(); - if (selectionController.isCollapsed) { - throw new Error("This should be impossible"); - } - return selectionController.removeSelected(); -} -function deleteContentBackward(event, editor, selectionController) { - event.preventDefault(); - if (editor.isEmpty) return; - if (!selectionController.isCollapsed) { - return selectionController.removeSelected({ direction: "backward" }); - } - if (selectionController.isTextFocus && selectionController.focusOffset > 0) { - return selectionController.removeBackwardText(); - } else if (selectionController.isTextFocus && selectionController.focusAtStart) { - return selectionController.mergeBackwardParagraph(); - } else if (selectionController.isInlineFocus || selectionController.isLineBreakFocus) { - return selectionController.removeBackwardParagraph(); - } -} -function deleteContentForward(event, editor, selectionController) { - event.preventDefault(); - if (editor.isEmpty) return; - if (!selectionController.isCollapsed) { - return selectionController.removeSelected({ direction: "forward" }); - } - if (selectionController.isTextFocus && selectionController.focusOffset >= 0) { - return selectionController.removeForwardText(); - } else if (selectionController.isTextFocus && selectionController.focusAtEnd) { - return selectionController.mergeForwardParagraph(); - } else if ((selectionController.isInlineFocus || selectionController.isLineBreakFocus) && editor.numParagraphs > 1) { - return selectionController.removeForwardParagraph(); - } -} -const commands = { - insertText, - insertParagraph, - deleteByCut, - deleteContentBackward, - deleteContentForward -}; -class ChangeController extends EventTarget { - /** - * Constructor - * - * @param {number} [time=500] - */ - constructor(time = 500) { - super(); - /** - * Keeps the timeout id. - * - * @type {number} - */ - __privateAdd(this, _timeout, null); - /** - * Keeps the time at which we're going to - * call the debounced change calls. - * - * @type {number} - */ - __privateAdd(this, _time, 1e3); - /** - * Keeps if we have some pending changes or not. - * - * @type {boolean} - */ - __privateAdd(this, _hasPendingChanges, false); - __privateAdd(this, _onTimeout, () => { - this.dispatchEvent(new Event("change")); - }); - if (typeof time === "number" && (!Number.isInteger(time) || time <= 0)) { - throw new TypeError("Invalid time"); - } - __privateSet(this, _time, time ?? 500); - } - /** - * Indicates that there are some pending changes. - * - * @type {boolean} - */ - get hasPendingChanges() { - return __privateGet(this, _hasPendingChanges); - } - /** - * Tells the ChangeController that a change has been made - * but that you need to delay the notification (and debounce) - * for sometime. - */ - notifyDebounced() { - __privateSet(this, _hasPendingChanges, true); - clearTimeout(__privateGet(this, _timeout)); - __privateSet(this, _timeout, setTimeout(__privateGet(this, _onTimeout), __privateGet(this, _time))); - } - /** - * Tells the ChangeController that a change should be notified - * immediately. - */ - notifyImmediately() { - clearTimeout(__privateGet(this, _timeout)); - __privateGet(this, _onTimeout).call(this); - } - /** - * Disposes the referenced resources. - */ - dispose() { - if (this.hasPendingChanges) { - this.notifyImmediately(); - } - clearTimeout(__privateGet(this, _timeout)); - } -} -_timeout = new WeakMap(); -_time = new WeakMap(); -_hasPendingChanges = new WeakMap(); -_onTimeout = new WeakMap(); -function tryOffset(offset) { - if (!Number.isInteger(offset) || offset < 0) - throw new TypeError("Invalid offset"); -} -function tryString(str) { - if (typeof str !== "string") throw new TypeError("Invalid string"); -} -function insertInto(str, offset, text) { - tryString(str); - tryOffset(offset); - tryString(text); - return str.slice(0, offset) + text + str.slice(offset); -} -function replaceWith(str, startOffset, endOffset, text) { - tryString(str); - tryOffset(startOffset); - tryOffset(endOffset); - tryString(text); - return str.slice(0, startOffset) + text + str.slice(endOffset); -} -function removeBackward(str, offset) { - tryString(str); - tryOffset(offset); - if (offset === 0) { - return str; - } - return str.slice(0, offset - 1) + str.slice(offset); -} -function removeForward(str, offset) { - tryString(str); - tryOffset(offset); - return str.slice(0, offset) + str.slice(offset + 1); -} -function removeSlice(str, start2, end) { - tryString(str); - tryOffset(start2); - tryOffset(end); - return str.slice(0, start2) + str.slice(end); -} -const TextNodeIteratorDirection = { - FORWARD: 1, - BACKWARD: 0 -}; -const _TextNodeIterator = class _TextNodeIterator { - /** - * Constructor - * - * @param {HTMLElement} rootNode - */ - constructor(rootNode) { - /** - * This is the root text node. - * - * @type {HTMLElement} - */ - __privateAdd(this, _rootNode, null); - /** - * This is the current text node. - * - * @type {Text|null} - */ - __privateAdd(this, _currentNode, null); - if (!(rootNode instanceof HTMLElement)) { - throw new TypeError("Invalid root node"); - } - __privateSet(this, _rootNode, rootNode); - __privateSet(this, _currentNode, _TextNodeIterator.findDown(rootNode, rootNode)); - } - /** - * Returns if a specific node is a text node. - * - * @param {Node} node - * @returns {boolean} - */ - static isTextNode(node) { - return node.nodeType === Node.TEXT_NODE || node.nodeType === Node.ELEMENT_NODE && node.nodeName === "BR"; - } - /** - * Returns if a specific node is a container node. - * - * @param {Node} node - * @returns {boolean} - */ - static isContainerNode(node) { - return node.nodeType === Node.ELEMENT_NODE && node.nodeName !== "BR"; - } - /** - * Finds a node from an initial node and down the tree. - * - * @param {Node} startNode - * @param {Node} rootNode - * @param {Set} skipNodes - * @param {number} direction - * @returns {Node} - */ - static findDown(startNode, rootNode, skipNodes = /* @__PURE__ */ new Set(), direction = TextNodeIteratorDirection.FORWARD) { - if (startNode === rootNode) { - return _TextNodeIterator.findDown( - direction === TextNodeIteratorDirection.FORWARD ? startNode.firstChild : startNode.lastChild, - rootNode, - skipNodes, - direction - ); - } - let safeGuard = Date.now(); - let currentNode = startNode; - while (currentNode) { - if (Date.now() - safeGuard >= 1e3) { - throw new Error("Iteration timeout"); - } - if (skipNodes.has(currentNode)) { - currentNode = direction === TextNodeIteratorDirection.FORWARD ? currentNode.nextSibling : currentNode.previousSibling; - continue; - } - if (_TextNodeIterator.isTextNode(currentNode)) { - return currentNode; - } else if (_TextNodeIterator.isContainerNode(currentNode)) { - return _TextNodeIterator.findDown( - direction === TextNodeIteratorDirection.FORWARD ? currentNode.firstChild : currentNode.lastChild, - rootNode, - skipNodes, - direction - ); - } - currentNode = direction === TextNodeIteratorDirection.FORWARD ? currentNode.nextSibling : currentNode.previousSibling; - } - return null; - } - /** - * Finds a node from an initial node and up the tree. - * - * @param {Node} startNode - * @param {Node} rootNode - * @param {Set} backTrack - * @param {number} direction - * @returns {Node} - */ - static findUp(startNode, rootNode, backTrack = /* @__PURE__ */ new Set(), direction = TextNodeIteratorDirection.FORWARD) { - backTrack.add(startNode); - if (_TextNodeIterator.isTextNode(startNode)) { - return _TextNodeIterator.findUp( - startNode.parentNode, - rootNode, - backTrack, - direction - ); - } else if (_TextNodeIterator.isContainerNode(startNode)) { - const found = _TextNodeIterator.findDown( - startNode, - rootNode, - backTrack, - direction - ); - if (found) { - return found; - } - if (startNode !== rootNode) { - return _TextNodeIterator.findUp( - startNode.parentNode, - rootNode, - backTrack, - direction - ); - } - } - return null; - } - /** - * Current node we're into. - * - * @type {TextNode|HTMLBRElement} - */ - get currentNode() { - return __privateGet(this, _currentNode); - } - set currentNode(newCurrentNode) { - const isContained = (newCurrentNode.compareDocumentPosition(__privateGet(this, _rootNode)) & Node.DOCUMENT_POSITION_CONTAINS) === Node.DOCUMENT_POSITION_CONTAINS; - if (!(newCurrentNode instanceof Node) || !_TextNodeIterator.isTextNode(newCurrentNode) || !isContained) { - throw new TypeError("Invalid new current node"); - } - __privateSet(this, _currentNode, newCurrentNode); - } - /** - * Returns the next Text node or
element or null if there are. - * - * @returns {Text|HTMLBRElement} - */ - nextNode() { - if (!__privateGet(this, _currentNode)) return null; - const nextNode = _TextNodeIterator.findUp( - __privateGet(this, _currentNode), - __privateGet(this, _rootNode), - /* @__PURE__ */ new Set(), - TextNodeIteratorDirection.FORWARD - ); - if (!nextNode) { - return null; - } - __privateSet(this, _currentNode, nextNode); - return __privateGet(this, _currentNode); - } - /** - * Returns the previous Text node or
element or null. - * - * @returns {Text|HTMLBRElement} - */ - previousNode() { - if (!__privateGet(this, _currentNode)) return null; - const previousNode = _TextNodeIterator.findUp( - __privateGet(this, _currentNode), - __privateGet(this, _rootNode), - /* @__PURE__ */ new Set(), - TextNodeIteratorDirection.BACKWARD - ); - if (!previousNode) { - return null; - } - __privateSet(this, _currentNode, previousNode); - return __privateGet(this, _currentNode); - } -}; -_rootNode = new WeakMap(); -_currentNode = new WeakMap(); -let TextNodeIterator = _TextNodeIterator; -class CommandMutations { - constructor(added, updated, removed) { - __privateAdd(this, _added, /* @__PURE__ */ new Set()); - __privateAdd(this, _removed, /* @__PURE__ */ new Set()); - __privateAdd(this, _updated, /* @__PURE__ */ new Set()); - if (added && Array.isArray(added)) __privateSet(this, _added, new Set(added)); - if (updated && Array.isArray(updated)) __privateSet(this, _updated, new Set(updated)); - if (removed && Array.isArray(removed)) __privateSet(this, _removed, new Set(removed)); - } - get added() { - return __privateGet(this, _added); - } - get removed() { - return __privateGet(this, _removed); - } - get updated() { - return __privateGet(this, _updated); - } - clear() { - __privateGet(this, _added).clear(); - __privateGet(this, _removed).clear(); - __privateGet(this, _updated).clear(); - } - dispose() { - __privateGet(this, _added).clear(); - __privateSet(this, _added, null); - __privateGet(this, _removed).clear(); - __privateSet(this, _removed, null); - __privateGet(this, _updated).clear(); - __privateSet(this, _updated, null); - } - add(node) { - __privateGet(this, _added).add(node); - return this; - } - remove(node) { - __privateGet(this, _removed).add(node); - return this; - } - update(node) { - __privateGet(this, _updated).add(node); - return this; - } -} -_added = new WeakMap(); -_removed = new WeakMap(); -_updated = new WeakMap(); -const SelectionDirection = { - /** The anchorNode is behind the focusNode */ - FORWARD: 1, - /** The focusNode and the anchorNode are collapsed */ - NONE: 0, - /** The focusNode is behind the anchorNode */ - BACKWARD: -1 -}; -const SAFE_GUARD_TIME = 1e3; -let startTime = Date.now(); -function start() { - startTime = Date.now(); -} -function update() { - if (Date.now - startTime >= SAFE_GUARD_TIME) { - throw new Error("Safe guard timeout"); - } -} -const SafeGuard = { - start, - update -}; -class SelectionController extends EventTarget { - /** - * Constructor - * - * @param {TextEditor} textEditor - * @param {Selection} selection - * @param {SelectionControllerOptions} [options] - */ - constructor(textEditor, selection, options) { - super(); - __privateAdd(this, _SelectionController_instances); - /** - * Reference to the text editor. - * - * @type {TextEditor} - */ - __privateAdd(this, _textEditor, null); - /** - * Selection. - * - * @type {Selection} - */ - __privateAdd(this, _selection, null); - /** - * Set of ranges (this should always have one) - * - * @type {Set} - */ - __privateAdd(this, _ranges, /* @__PURE__ */ new Set()); - /** - * Current range (.rangeAt 0) - * - * @type {Range} - */ - __privateAdd(this, _range, null); - /** - * @type {Node} - */ - __privateAdd(this, _focusNode, null); - /** - * @type {number} - */ - __privateAdd(this, _focusOffset, 0); - /** - * @type {Node} - */ - __privateAdd(this, _anchorNode, null); - /** - * @type {number} - */ - __privateAdd(this, _anchorOffset, 0); - /** - * Saved selection. - * - * @type {object} - */ - __privateAdd(this, _savedSelection, null); - /** - * TextNodeIterator that allows us to move - * around the root element but only through - *
and #text nodes. - * - * @type {TextNodeIterator} - */ - __privateAdd(this, _textNodeIterator, null); - /** - * CSSStyleDeclaration that we can mutate - * to handle style changes. - * - * @type {CSSStyleDeclaration} - */ - __privateAdd(this, _currentStyle, null); - /** - * Element used to have a custom CSSStyleDeclaration - * that we can modify to handle style changes when the - * selection is changed. - * - * @type {HTMLDivElement} - */ - __privateAdd(this, _inertElement, null); - /** - * @type {SelectionControllerDebug} - */ - __privateAdd(this, _debug, null); - /** - * Command Mutations. - * - * @type {CommandMutations} - */ - __privateAdd(this, _mutations, new CommandMutations()); - /** - * Style defaults. - * - * @type {Object.} - */ - __privateAdd(this, _styleDefaults, null); - /** - * This is called on every `selectionchange` because it is dispatched - * only by the `document` object. - * - * @param {Event} e - */ - __privateAdd(this, _onSelectionChange, (e) => { - if (!this.hasFocus) return; - let focusNodeChanges = false; - if (__privateGet(this, _focusNode) !== __privateGet(this, _selection).focusNode) { - __privateSet(this, _focusNode, __privateGet(this, _selection).focusNode); - focusNodeChanges = true; - } - __privateSet(this, _focusOffset, __privateGet(this, _selection).focusOffset); - if (__privateGet(this, _anchorNode) !== __privateGet(this, _selection).anchorNode) { - __privateSet(this, _anchorNode, __privateGet(this, _selection).anchorNode); - } - __privateSet(this, _anchorOffset, __privateGet(this, _selection).anchorOffset); - if (__privateGet(this, _selection).rangeCount > 1) { - for (let index = 0; index < __privateGet(this, _selection).rangeCount; index++) { - const range = __privateGet(this, _selection).getRangeAt(index); - if (__privateGet(this, _ranges).has(range)) { - __privateGet(this, _ranges).delete(range); - __privateGet(this, _selection).removeRange(range); - } else { - __privateGet(this, _ranges).add(range); - __privateSet(this, _range, range); - } - } - } else if (__privateGet(this, _selection).rangeCount > 0) { - const range = __privateGet(this, _selection).getRangeAt(0); - __privateSet(this, _range, range); - __privateGet(this, _ranges).clear(); - __privateGet(this, _ranges).add(range); - } else { - __privateSet(this, _range, null); - __privateGet(this, _ranges).clear(); - } - if (focusNodeChanges) { - __privateMethod(this, _SelectionController_instances, notifyStyleChange_fn).call(this); - } - if (__privateGet(this, _debug)) { - __privateGet(this, _debug).update(this); - } - }); - __privateSet(this, _debug, options == null ? void 0 : options.debug); - __privateSet(this, _styleDefaults, options == null ? void 0 : options.styleDefaults); - __privateSet(this, _selection, selection); - __privateSet(this, _textEditor, textEditor); - __privateSet(this, _textNodeIterator, new TextNodeIterator(__privateGet(this, _textEditor).element)); - __privateMethod(this, _SelectionController_instances, setup_fn).call(this); - } - /** - * Styles of the current inline. - * - * @type {CSSStyleDeclaration} - */ - get currentStyle() { - return __privateGet(this, _currentStyle); - } - /** - * Saves the current selection and returns the client rects. - * - * @returns {boolean} - */ - saveSelection() { - __privateSet(this, _savedSelection, { - isCollapsed: __privateGet(this, _selection).isCollapsed, - focusNode: __privateGet(this, _selection).focusNode, - focusOffset: __privateGet(this, _selection).focusOffset, - anchorNode: __privateGet(this, _selection).anchorNode, - anchorOffset: __privateGet(this, _selection).anchorOffset, - range: __privateMethod(this, _SelectionController_instances, getSavedRange_fn).call(this) - }); - return true; - } - /** - * Restores a saved selection if there's any. - * - * @returns {boolean} - */ - restoreSelection() { - if (!__privateGet(this, _savedSelection)) return false; - if (__privateGet(this, _savedSelection).anchorNode && __privateGet(this, _savedSelection).focusNode) { - if (__privateGet(this, _savedSelection).anchorNode === __privateGet(this, _savedSelection).focusNode) { - __privateGet(this, _selection).setPosition(__privateGet(this, _savedSelection).focusNode, __privateGet(this, _savedSelection).focusOffset); - } else { - __privateGet(this, _selection).setBaseAndExtent( - __privateGet(this, _savedSelection).anchorNode, - __privateGet(this, _savedSelection).anchorOffset, - __privateGet(this, _savedSelection).focusNode, - __privateGet(this, _savedSelection).focusOffset - ); - } - } - __privateSet(this, _savedSelection, null); - return true; - } - /** - * Marks the start of a mutation. - * - * Clears all the mutations kept in CommandMutations. - */ - startMutation() { - __privateGet(this, _mutations).clear(); - if (!__privateGet(this, _focusNode)) return false; - return true; - } - /** - * Marks the end of a mutation. - * - * @returns - */ - endMutation() { - return __privateGet(this, _mutations); - } - /** - * Selects all content. - */ - selectAll() { - __privateGet(this, _selection).selectAllChildren(__privateGet(this, _textEditor).root); - return this; - } - /** - * Moves cursor to end. - */ - cursorToEnd() { - const range = document.createRange(); - range.selectNodeContents(__privateGet(this, _textEditor).element); - range.collapse(false); - __privateGet(this, _selection).removeAllRanges(); - __privateGet(this, _selection).addRange(range); - return this; - } - /** - * Collapses a selection. - * - * @param {Node} node - * @param {number} offset - */ - collapse(node, offset) { - const nodeOffset = node.nodeType === Node.TEXT_NODE && offset >= node.nodeValue.length ? node.nodeValue.length : offset; - return this.setSelection( - node, - nodeOffset, - node, - nodeOffset - ); - } - /** - * Sets base and extent. - * - * @param {Node} anchorNode - * @param {number} anchorOffset - * @param {Node} [focusNode=anchorNode] - * @param {number} [focusOffset=anchorOffset] - */ - setSelection(anchorNode, anchorOffset, focusNode = anchorNode, focusOffset = anchorOffset) { - if (!anchorNode.isConnected) { - throw new Error("Invalid anchorNode"); - } - if (!focusNode.isConnected) { - throw new Error("Invalid focusNode"); - } - if (__privateGet(this, _savedSelection)) { - __privateGet(this, _savedSelection).isCollapsed = focusNode === anchorNode && anchorOffset === focusOffset; - __privateGet(this, _savedSelection).focusNode = focusNode; - __privateGet(this, _savedSelection).focusOffset = focusOffset; - __privateGet(this, _savedSelection).anchorNode = anchorNode; - __privateGet(this, _savedSelection).anchorOffset = anchorOffset; - __privateGet(this, _savedSelection).range.collapsed = __privateGet(this, _savedSelection).isCollapsed; - const position = focusNode.compareDocumentPosition(anchorNode); - if (position & Node.DOCUMENT_POSITION_FOLLOWING) { - __privateGet(this, _savedSelection).range.startContainer = focusNode; - __privateGet(this, _savedSelection).range.startOffset = focusOffset; - __privateGet(this, _savedSelection).range.endContainer = anchorNode; - __privateGet(this, _savedSelection).range.endOffset = anchorOffset; - } else { - __privateGet(this, _savedSelection).range.startContainer = anchorNode; - __privateGet(this, _savedSelection).range.startOffset = anchorOffset; - __privateGet(this, _savedSelection).range.endContainer = focusNode; - __privateGet(this, _savedSelection).range.endOffset = focusOffset; - } - } else { - __privateSet(this, _anchorNode, anchorNode); - __privateSet(this, _anchorOffset, anchorOffset); - if (anchorNode === focusNode) { - __privateSet(this, _focusNode, __privateGet(this, _anchorNode)); - __privateSet(this, _focusOffset, __privateGet(this, _anchorOffset)); - __privateGet(this, _selection).setPosition(anchorNode, anchorOffset); - } else { - __privateSet(this, _focusNode, focusNode); - __privateSet(this, _focusOffset, focusOffset); - __privateGet(this, _selection).setBaseAndExtent( - anchorNode, - anchorOffset, - focusNode, - focusOffset - ); - } - } - } - /** - * Disposes the current resources. - */ - dispose() { - document.removeEventListener("selectionchange", __privateGet(this, _onSelectionChange)); - __privateSet(this, _textEditor, null); - __privateGet(this, _ranges).clear(); - __privateSet(this, _ranges, null); - __privateSet(this, _range, null); - __privateSet(this, _selection, null); - __privateSet(this, _focusNode, null); - __privateSet(this, _anchorNode, null); - __privateGet(this, _mutations).dispose(); - __privateSet(this, _mutations, null); - } - /** - * Returns the current selection. - * - * @type {Selection} - */ - get selection() { - return __privateGet(this, _selection); - } - /** - * Returns the current range. - * - * @type {Range} - */ - get range() { - return __privateGet(this, _range); - } - /** - * Indicates the direction of the selection - * - * @type {SelectionDirection} - */ - get direction() { - if (this.isCollapsed) { - return SelectionDirection.NONE; - } - if (this.focusNode !== this.anchorNode) { - return this.startContainer === this.focusNode ? SelectionDirection.BACKWARD : SelectionDirection.FORWARD; - } - return this.focusOffset < this.anchorOffset ? SelectionDirection.BACKWARD : SelectionDirection.FORWARD; - } - /** - * Indicates that the editor element has the - * focus. - * - * @type {boolean} - */ - get hasFocus() { - return document.activeElement === __privateGet(this, _textEditor).element; - } - /** - * Returns true if the selection is collapsed (caret) - * or false otherwise. - * - * @type {boolean} - */ - get isCollapsed() { - if (__privateGet(this, _savedSelection)) { - return __privateGet(this, _savedSelection).isCollapsed; - } - return __privateGet(this, _selection).isCollapsed; - } - /** - * Current or saved anchor node. - * - * @type {Node} - */ - get anchorNode() { - if (__privateGet(this, _savedSelection)) { - return __privateGet(this, _savedSelection).anchorNode; - } - return __privateGet(this, _anchorNode); - } - /** - * Current or saved anchor offset. - * - * @type {number} - */ - get anchorOffset() { - if (__privateGet(this, _savedSelection)) { - return __privateGet(this, _savedSelection).anchorOffset; - } - return __privateGet(this, _selection).anchorOffset; - } - /** - * Indicates that the caret is at the start of the node. - * - * @type {boolean} - */ - get anchorAtStart() { - return this.anchorOffset === 0; - } - /** - * Indicates that the caret is at the end of the node. - * - * @type {boolean} - */ - get anchorAtEnd() { - return this.anchorOffset === this.anchorNode.nodeValue.length; - } - /** - * Current or saved focus node. - * - * @type {Node} - */ - get focusNode() { - if (__privateGet(this, _savedSelection)) { - return __privateGet(this, _savedSelection).focusNode; - } - if (!__privateGet(this, _focusNode)) - console.trace("focusNode", __privateGet(this, _focusNode)); - return __privateGet(this, _focusNode); - } - /** - * Current or saved focus offset. - * - * @type {number} - */ - get focusOffset() { - if (__privateGet(this, _savedSelection)) { - return __privateGet(this, _savedSelection).focusOffset; - } - return __privateGet(this, _focusOffset); - } - /** - * Indicates that the caret is at the start of the node. - * - * @type {boolean} - */ - get focusAtStart() { - return this.focusOffset === 0; - } - /** - * Indicates that the caret is at the end of the node. - * - * @type {boolean} - */ - get focusAtEnd() { - return this.focusOffset === this.focusNode.nodeValue.length; - } - /** - * Returns the paragraph in the focus node - * of the current selection. - * - * @type {HTMLElement|null} - */ - get focusParagraph() { - return getParagraph(this.focusNode); - } - /** - * Returns the inline in the focus node - * of the current selection. - * - * @type {HTMLElement|null} - */ - get focusInline() { - return getInline(this.focusNode); - } - /** - * Returns the current paragraph in the anchor - * node of the current selection. - * - * @type {HTMLElement|null} - */ - get anchorParagraph() { - return getParagraph(this.anchorNode); - } - /** - * Returns the current inline in the anchor - * node of the current selection. - * - * @type {HTMLElement|null} - */ - get anchorInline() { - return getInline(this.anchorNode); - } - /** - * Start container of the current range. - */ - get startContainer() { - var _a, _b, _c; - if (__privateGet(this, _savedSelection)) { - return (_b = (_a = __privateGet(this, _savedSelection)) == null ? void 0 : _a.range) == null ? void 0 : _b.startContainer; - } - return (_c = __privateGet(this, _range)) == null ? void 0 : _c.startContainer; - } - /** - * `startOffset` of the current range. - * - * @type {number|null} - */ - get startOffset() { - var _a, _b, _c; - if (__privateGet(this, _savedSelection)) { - return (_b = (_a = __privateGet(this, _savedSelection)) == null ? void 0 : _a.range) == null ? void 0 : _b.startOffset; - } - return (_c = __privateGet(this, _range)) == null ? void 0 : _c.startOffset; - } - /** - * Start paragraph of the current range. - * - * @type {HTMLElement|null} - */ - get startParagraph() { - const startContainer = this.startContainer; - if (!startContainer) return null; - return getParagraph(startContainer); - } - /** - * Start inline of the current page. - * - * @type {HTMLElement|null} - */ - get startInline() { - const startContainer = this.startContainer; - if (!startContainer) return null; - return getInline(startContainer); - } - /** - * End container of the current range. - * - * @type {Node} - */ - get endContainer() { - var _a, _b, _c; - if (__privateGet(this, _savedSelection)) { - return (_b = (_a = __privateGet(this, _savedSelection)) == null ? void 0 : _a.range) == null ? void 0 : _b.endContainer; - } - return (_c = __privateGet(this, _range)) == null ? void 0 : _c.endContainer; - } - /** - * `endOffset` of the current range - * - * @type {HTMLElement|null} - */ - get endOffset() { - var _a, _b, _c; - if (__privateGet(this, _savedSelection)) { - return (_b = (_a = __privateGet(this, _savedSelection)) == null ? void 0 : _a.range) == null ? void 0 : _b.endOffset; - } - return (_c = __privateGet(this, _range)) == null ? void 0 : _c.endOffset; - } - /** - * Paragraph element of the `endContainer` of - * the current range. - * - * @type {HTMLElement|null} - */ - get endParagraph() { - const endContainer = this.endContainer; - if (!endContainer) return null; - return getParagraph(endContainer); - } - /** - * Inline element of the `endContainer` of - * the current range. - * - * @type {HTMLElement|null} - */ - get endInline() { - const endContainer = this.endContainer; - if (!endContainer) return null; - return getInline(endContainer); - } - /** - * Returns true if the anchor node and the focus - * node are the same text nodes. - * - * @type {boolean} - */ - get isTextSame() { - return this.isTextFocus === this.isTextAnchor && this.focusNode === this.anchorNode; - } - /** - * Indicates that focus node is a text node. - * - * @type {boolean} - */ - get isTextFocus() { - return this.focusNode.nodeType === Node.TEXT_NODE; - } - /** - * Indicates that anchor node is a text node. - * - * @type {boolean} - */ - get isTextAnchor() { - return this.anchorNode.nodeType === Node.TEXT_NODE; - } - /** - * Is true if the current focus node is a inline. - * - * @type {boolean} - */ - get isInlineFocus() { - return isInline(this.focusNode); - } - /** - * Is true if the current anchor node is a inline. - * - * @type {boolean} - */ - get isInlineAnchor() { - return isInline(this.anchorNode); - } - /** - * Is true if the current focus node is a paragraph. - * - * @type {boolean} - */ - get isParagraphFocus() { - return isParagraph(this.focusNode); - } - /** - * Is true if the current anchor node is a paragraph. - * - * @type {boolean} - */ - get isParagraphAnchor() { - return isParagraph(this.anchorNode); - } - /** - * Is true if the current focus node is a line break. - * - * @type {boolean} - */ - get isLineBreakFocus() { - return isLineBreak(this.focusNode) || isInline(this.focusNode) && isLineBreak(this.focusNode.firstChild); - } - /** - * Indicates that we have multiple nodes selected. - * - * @type {boolean} - */ - get isMulti() { - return this.focusNode !== this.anchorNode; - } - /** - * Indicates that we have selected multiple - * paragraph elements. - * - * @type {boolean} - */ - get isMultiParagraph() { - return this.isMulti && this.focusParagraph !== this.anchorParagraph; - } - /** - * Indicates that we have selected multiple - * inline elements. - * - * @type {boolean} - */ - get isMultiInline() { - return this.isMulti && this.focusInline !== this.anchorInline; - } - /** - * Indicates that the caret (only the caret) - * is at the start of an inline. - * - * @type {boolean} - */ - get isInlineStart() { - if (!this.isCollapsed) return false; - return isInlineStart(this.focusNode, this.focusOffset); - } - /** - * Indicates that the caret (only the caret) - * is at the end of an inline. This value doesn't - * matter when dealing with selections. - * - * @type {boolean} - */ - get isInlineEnd() { - if (!this.isCollapsed) return false; - return isInlineEnd(this.focusNode, this.focusOffset); - } - /** - * Indicates that we're in the starting position of a paragraph. - * - * @type {boolean} - */ - get isParagraphStart() { - if (!this.isCollapsed) return false; - return isParagraphStart(this.focusNode, this.focusOffset); - } - /** - * Indicates that we're in the ending position of a paragraph. - * - * @type {boolean} - */ - get isParagraphEnd() { - if (!this.isCollapsed) return false; - return isParagraphEnd(this.focusNode, this.focusOffset); - } - /** - * Insert pasted fragment. - * - * @param {DocumentFragment} fragment - */ - insertPaste(fragment) { - fragment.children.length; - if (this.isParagraphStart) { - this.focusParagraph.before(fragment); - } else if (this.isParagraphEnd) { - this.focusParagraph.after(fragment); - } else { - const newParagraph = splitParagraph( - this.focusParagraph, - this.focusInline, - this.focusOffset - ); - this.focusParagraph.after(fragment, newParagraph); - } - } - /** - * Replaces data with pasted fragment - * - * @param {DocumentFragment} fragment - */ - replaceWithPaste(fragment) { - fragment.children.length; - this.removeSelected(); - this.insertPaste(fragment); - } - /** - * Replaces the current line break with text - * - * @param {string} text - */ - replaceLineBreak(text) { - const newText = new Text(text); - this.focusInline.replaceChildren(newText); - this.collapse(newText, text.length); - } - /** - * Removes text forward from the current position. - */ - removeForwardText() { - __privateGet(this, _textNodeIterator).currentNode = this.focusNode; - const removedData = removeForward( - this.focusNode.nodeValue, - this.focusOffset - ); - if (this.focusNode.nodeValue !== removedData) { - this.focusNode.nodeValue = removedData; - } - const paragraph = this.focusParagraph; - if (!paragraph) throw new Error("Cannot find paragraph"); - const inline = this.focusInline; - if (!inline) throw new Error("Cannot find inline"); - const nextTextNode = __privateGet(this, _textNodeIterator).nextNode(); - if (this.focusNode.nodeValue === "") { - this.focusNode.remove(); - } - if (paragraph.childNodes.length === 1 && inline.childNodes.length === 0) { - const lineBreak = createLineBreak(); - inline.appendChild(lineBreak); - return this.collapse(lineBreak, 0); - } else if (paragraph.childNodes.length > 1 && inline.childNodes.length === 0) { - inline.remove(); - return this.collapse(nextTextNode, 0); - } - return this.collapse(this.focusNode, this.focusOffset); - } - /** - * Removes text backward from the current caret position. - */ - removeBackwardText() { - __privateGet(this, _textNodeIterator).currentNode = this.focusNode; - const removedData = removeBackward( - this.focusNode.nodeValue, - this.focusOffset - ); - if (this.focusNode.nodeValue !== removedData) { - this.focusNode.nodeValue = removedData; - } - if (this.focusOffset - 1 > 0) { - return this.collapse(this.focusNode, this.focusOffset - 1); - } - const paragraph = this.focusParagraph; - if (!paragraph) throw new Error("Cannot find paragraph"); - const inline = this.focusInline; - if (!inline) throw new Error("Cannot find inline"); - const previousTextNode = __privateGet(this, _textNodeIterator).previousNode(); - if (this.focusNode.nodeValue === "") { - this.focusNode.remove(); - } - if (paragraph.children.length === 1 && inline.childNodes.length === 0) { - const lineBreak = createLineBreak(); - inline.appendChild(lineBreak); - return this.collapse(lineBreak, 0); - } else if (paragraph.children.length > 1 && inline.childNodes.length === 0) { - inline.remove(); - return this.collapse(previousTextNode, getTextNodeLength(previousTextNode)); - } - return this.collapse(this.focusNode, this.focusOffset - 1); - } - /** - * Inserts some text in the caret position. - * - * @param {string} newText - */ - insertText(newText) { - this.focusNode.nodeValue = insertInto( - this.focusNode.nodeValue, - this.focusOffset, - newText - ); - __privateGet(this, _mutations).update(this.focusInline); - return this.collapse(this.focusNode, this.focusOffset + newText.length); - } - /** - * Replaces currently selected text. - * - * @param {string} newText - */ - replaceText(newText) { - const startOffset = Math.min(this.anchorOffset, this.focusOffset); - const endOffset = Math.max(this.anchorOffset, this.focusOffset); - this.focusNode.nodeValue = replaceWith( - this.focusNode.nodeValue, - startOffset, - endOffset, - newText - ); - __privateGet(this, _mutations).update(this.focusInline); - return this.collapse(this.focusNode, startOffset + newText.length); - } - /** - * Replaces the selected inlines with new text. - * - * @param {string} newText - */ - replaceInlines(newText) { - const currentParagraph = this.focusParagraph; - if (this.startInline === currentParagraph.firstChild && this.startOffset === 0 && this.endInline === currentParagraph.lastChild && this.endOffset === currentParagraph.lastChild.textContent.length) { - const newTextNode = new Text(newText); - currentParagraph.replaceChildren( - createInline(newTextNode, this.anchorInline.style) - ); - return this.collapse(newTextNode, newTextNode.nodeValue.length); - } - this.removeSelected(); - this.focusNode.nodeValue = insertInto( - this.focusNode.nodeValue, - this.focusOffset, - newText - ); - return this.collapse(this.focusNode, this.focusOffset + newText.length); - } - /** - * Replaces paragraphs with text. - * - * @param {string} newText - */ - replaceParagraphs(newText) { - const currentParagraph = this.focusParagraph; - this.removeSelected(); - this.focusNode.nodeValue = insertInto( - this.focusNode.nodeValue, - this.focusOffset, - newText - ); - for (const child of currentParagraph.children) { - if (child.textContent === "") { - child.remove(); - } - } - } - /** - * Inserts a new paragraph after the current paragraph. - */ - insertParagraphAfter() { - const currentParagraph = this.focusParagraph; - const newParagraph = createEmptyParagraph(__privateGet(this, _currentStyle)); - currentParagraph.after(newParagraph); - __privateGet(this, _mutations).update(currentParagraph); - __privateGet(this, _mutations).add(newParagraph); - return this.collapse(newParagraph.firstChild.firstChild, 0); - } - /** - * Inserts a new paragraph before the current paragraph. - */ - insertParagraphBefore() { - const currentParagraph = this.focusParagraph; - const newParagraph = createEmptyParagraph(__privateGet(this, _currentStyle)); - currentParagraph.before(newParagraph); - __privateGet(this, _mutations).update(currentParagraph); - __privateGet(this, _mutations).add(newParagraph); - return this.collapse(currentParagraph.firstChild.firstChild, 0); - } - /** - * Splits the current paragraph. - */ - splitParagraph() { - const currentParagraph = this.focusParagraph; - const newParagraph = splitParagraph( - this.focusParagraph, - this.focusInline, - __privateGet(this, _focusOffset) - ); - this.focusParagraph.after(newParagraph); - __privateGet(this, _mutations).update(currentParagraph); - __privateGet(this, _mutations).add(newParagraph); - return this.collapse(newParagraph.firstChild.firstChild, 0); - } - /** - * Inserts a new paragraph. - */ - insertParagraph() { - if (this.isParagraphEnd) { - return this.insertParagraphAfter(); - } else if (this.isParagraphStart) { - return this.insertParagraphBefore(); - } - return this.splitParagraph(); - } - /** - * Replaces the currently selected content with - * a paragraph. - */ - replaceWithParagraph() { - const currentParagraph = this.focusParagraph; - const currentInline = this.focusInline; - this.removeSelected(); - const newParagraph = splitParagraph( - currentParagraph, - currentInline, - this.focusOffset - ); - currentParagraph.after(newParagraph); - __privateGet(this, _mutations).update(currentParagraph); - __privateGet(this, _mutations).add(newParagraph); - } - /** - * Removes a paragraph in backward direction. - */ - removeBackwardParagraph() { - const previousParagraph = this.focusParagraph.previousElementSibling; - if (!previousParagraph) { - return; - } - const paragraphToBeRemoved = this.focusParagraph; - paragraphToBeRemoved.remove(); - const previousInline = previousParagraph.children.length > 1 ? previousParagraph.lastElementChild : previousParagraph.firstChild; - const previousOffset = isLineBreak(previousInline.firstChild) ? 0 : previousInline.firstChild.nodeValue.length; - __privateGet(this, _mutations).remove(paragraphToBeRemoved); - return this.collapse(previousInline.firstChild, previousOffset); - } - /** - * Merges the previous paragraph with the current paragraph. - */ - mergeBackwardParagraph() { - const currentParagraph = this.focusParagraph; - const previousParagraph = this.focusParagraph.previousElementSibling; - if (!previousParagraph) { - return; - } - let previousInline = previousParagraph.lastChild; - const previousOffset = getInlineLength(previousInline); - if (isEmptyParagraph(previousParagraph)) { - previousParagraph.replaceChildren(...currentParagraph.children); - previousInline = previousParagraph.firstChild; - currentParagraph.remove(); - } else { - mergeParagraphs(previousParagraph, currentParagraph); - } - __privateGet(this, _mutations).remove(currentParagraph); - __privateGet(this, _mutations).update(previousParagraph); - return this.collapse(previousInline.firstChild, previousOffset); - } - /** - * Merges the next paragraph with the current paragraph. - */ - mergeForwardParagraph() { - const currentParagraph = this.focusParagraph; - const nextParagraph = this.focusParagraph.nextElementSibling; - if (!nextParagraph) { - return; - } - mergeParagraphs(this.focusParagraph, nextParagraph); - __privateGet(this, _mutations).update(currentParagraph); - __privateGet(this, _mutations).remove(nextParagraph); - } - /** - * Removes the forward paragraph. - */ - removeForwardParagraph() { - const nextParagraph = this.focusParagraph.nextSibling; - if (!nextParagraph) { - return; - } - const paragraphToBeRemoved = this.focusParagraph; - paragraphToBeRemoved.remove(); - const nextInline = nextParagraph.firstChild; - const nextOffset = this.focusOffset; - __privateGet(this, _mutations).remove(paragraphToBeRemoved); - return this.collapse(nextInline.firstChild, nextOffset); - } - /** - * Cleans up all the affected paragraphs. - * - * @param {Set} affectedParagraphs - * @param {Set} affectedInlines - */ - cleanUp(affectedParagraphs, affectedInlines) { - for (const inline of affectedInlines) { - if (inline.textContent === "") { - inline.remove(); - __privateGet(this, _mutations).remove(inline); - } - } - for (const paragraph of affectedParagraphs) { - if (paragraph.children.length === 0) { - paragraph.remove(); - __privateGet(this, _mutations).remove(paragraph); - } - } - } - /** - * Removes the selected content. - * - * @param {RemoveSelectedOptions} [options] - */ - removeSelected(options) { - if (this.isCollapsed) return; - const affectedInlines = /* @__PURE__ */ new Set(); - const affectedParagraphs = /* @__PURE__ */ new Set(); - const startNode = getClosestTextNode(__privateGet(this, _range).startContainer); - const endNode = getClosestTextNode(__privateGet(this, _range).endContainer); - const startOffset = __privateGet(this, _range).startOffset; - const endOffset = __privateGet(this, _range).endOffset; - if (startNode === endNode) { - __privateGet(this, _textNodeIterator).currentNode = startNode; - __privateGet(this, _textNodeIterator).previousNode(); - __privateGet(this, _textNodeIterator).currentNode = startNode; - __privateGet(this, _textNodeIterator).nextNode(); - const inline = getInline(startNode); - const paragraph = getParagraph(startNode); - affectedInlines.add(inline); - affectedParagraphs.add(paragraph); - const newNodeValue = removeSlice( - startNode.nodeValue, - startOffset, - endOffset - ); - if (newNodeValue === "") { - const lineBreak = createLineBreak(); - inline.replaceChildren(lineBreak); - return this.collapse(lineBreak, 0); - } - startNode.nodeValue = newNodeValue; - return this.collapse(startNode, startOffset); - } - __privateGet(this, _textNodeIterator).currentNode = startNode; - const startInline = getInline(startNode); - const startParagraph = getParagraph(startNode); - const endInline = getInline(endNode); - const endParagraph = getParagraph(endNode); - SafeGuard.start(); - do { - SafeGuard.update(); - const currentNode = __privateGet(this, _textNodeIterator).currentNode; - const inline = getInline(__privateGet(this, _textNodeIterator).currentNode); - const paragraph = getParagraph(__privateGet(this, _textNodeIterator).currentNode); - let shouldRemoveNodeCompletely = false; - if (__privateGet(this, _textNodeIterator).currentNode === startNode) { - if (startOffset === 0) { - shouldRemoveNodeCompletely = true; - } else { - currentNode.nodeValue = currentNode.nodeValue.slice(0, startOffset); - } - } else if (__privateGet(this, _textNodeIterator).currentNode === endNode) { - if (isLineBreak(endNode) || isTextNode(endNode) && endOffset === endNode.nodeValue.length) { - shouldRemoveNodeCompletely = true; - } else { - currentNode.nodeValue = currentNode.nodeValue.slice(endOffset); - } - } else { - shouldRemoveNodeCompletely = true; - } - __privateGet(this, _textNodeIterator).nextNode(); - if (shouldRemoveNodeCompletely) { - currentNode.remove(); - if (currentNode === startNode) { - continue; - } - if (currentNode === endNode) { - break; - } - if (inline.childNodes.length === 0) { - inline.remove(); - } - if (paragraph !== startParagraph && paragraph.children.length === 0) { - paragraph.remove(); - } - } - if (currentNode === endNode) { - break; - } - } while (__privateGet(this, _textNodeIterator).currentNode); - if (startParagraph !== endParagraph) { - const mergedParagraph = mergeParagraphs(startParagraph, endParagraph); - if (mergedParagraph.children.length === 0) { - const newEmptyInline = createEmptyInline(__privateGet(this, _currentStyle)); - mergedParagraph.appendChild(newEmptyInline); - return this.collapse(newEmptyInline.firstChild, 0); - } - } - if (startInline.childNodes.length === 0 && endInline.childNodes.length > 0) { - startInline.remove(); - return this.collapse(endNode, 0); - } else if (startInline.childNodes.length > 0 && endInline.childNodes.length === 0) { - endInline.remove(); - return this.collapse(startNode, startOffset); - } else if (startInline.childNodes.length === 0 && endInline.childNodes.length === 0) { - const previousInline = startInline.previousElementSibling; - const nextInline = endInline.nextElementSibling; - startInline.remove(); - endInline.remove(); - if (previousInline) { - return this.collapse(previousInline.firstChild, previousInline.firstChild.nodeValue.length); - } - if (nextInline) { - return this.collapse(nextInline.firstChild, 0); - } - const newEmptyInline = createEmptyInline(__privateGet(this, _currentStyle)); - startParagraph.appendChild(newEmptyInline); - return this.collapse(newEmptyInline.firstChild, 0); - } - return this.collapse(startNode, startOffset); - } - /** - * Applies styles to selection - * - * @param {Object.} newStyles - * @returns {void} - */ - applyStyles(newStyles) { - return __privateMethod(this, _SelectionController_instances, applyStylesTo_fn).call(this, this.startContainer, this.startOffset, this.endContainer, this.endOffset, newStyles); - } -} -_textEditor = new WeakMap(); -_selection = new WeakMap(); -_ranges = new WeakMap(); -_range = new WeakMap(); -_focusNode = new WeakMap(); -_focusOffset = new WeakMap(); -_anchorNode = new WeakMap(); -_anchorOffset = new WeakMap(); -_savedSelection = new WeakMap(); -_textNodeIterator = new WeakMap(); -_currentStyle = new WeakMap(); -_inertElement = new WeakMap(); -_debug = new WeakMap(); -_mutations = new WeakMap(); -_styleDefaults = new WeakMap(); -_SelectionController_instances = new WeakSet(); -/** - * Applies the default styles to the currentStyle - * CSSStyleDeclaration. - */ -applyDefaultStylesToCurrentStyle_fn = function() { - if (__privateGet(this, _styleDefaults)) { - for (const [name, value] of Object.entries(__privateGet(this, _styleDefaults))) { - __privateGet(this, _currentStyle).setProperty( - name, - value + (name === "font-size" ? "px" : "") - ); - } - } -}; -/** - * Applies some styles to the currentStyle - * CSSStyleDeclaration - * - * @param {HTMLElement} element - */ -applyStylesToCurrentStyle_fn = function(element) { - for (let index = 0; index < element.style.length; index++) { - const styleName = element.style.item(index); - const styleValue = element.style.getPropertyValue(styleName); - __privateGet(this, _currentStyle).setProperty(styleName, styleValue); - } -}; -/** - * Updates current styles based on the currently selected inline. - * - * @param {HTMLSpanElement} inline - * @returns {SelectionController} - */ -updateCurrentStyle_fn = function(inline) { - __privateMethod(this, _SelectionController_instances, applyDefaultStylesToCurrentStyle_fn).call(this); - const root = inline.parentElement.parentElement; - __privateMethod(this, _SelectionController_instances, applyStylesToCurrentStyle_fn).call(this, root); - const paragraph = inline.parentElement; - __privateMethod(this, _SelectionController_instances, applyStylesToCurrentStyle_fn).call(this, paragraph); - __privateMethod(this, _SelectionController_instances, applyStylesToCurrentStyle_fn).call(this, inline); - return this; -}; -_onSelectionChange = new WeakMap(); -/** - * Notifies that the styles have changed. - */ -notifyStyleChange_fn = function() { - const inline = this.focusInline; - if (inline) { - __privateMethod(this, _SelectionController_instances, updateCurrentStyle_fn).call(this, inline); - this.dispatchEvent( - new CustomEvent("stylechange", { - detail: __privateGet(this, _currentStyle) - }) - ); - } -}; -/** - * Setups - */ -setup_fn = function() { - __privateSet(this, _inertElement, document.createElement("div")); - __privateSet(this, _currentStyle, __privateGet(this, _inertElement).style); - __privateMethod(this, _SelectionController_instances, applyDefaultStylesToCurrentStyle_fn).call(this); - if (__privateGet(this, _selection).rangeCount > 0) { - const range = __privateGet(this, _selection).getRangeAt(0); - __privateSet(this, _range, range); - __privateGet(this, _ranges).add(range); - } - if (__privateGet(this, _selection).rangeCount > 1) { - for (let index = 1; index < __privateGet(this, _selection).rangeCount; index++) { - __privateGet(this, _selection).removeRange(index); - } - } - document.addEventListener("selectionchange", __privateGet(this, _onSelectionChange)); -}; -/** - * Returns a Range-like object. - * - * @returns {RangeLike} - */ -getSavedRange_fn = function() { - if (!__privateGet(this, _range)) { - return { - collapsed: true, - commonAncestorContainer: null, - startContainer: null, - startOffset: 0, - endContainer: null, - endOffset: 0 - }; - } - return { - collapsed: __privateGet(this, _range).collapsed, - commonAncestorContainer: __privateGet(this, _range).commonAncestorContainer, - startContainer: __privateGet(this, _range).startContainer, - startOffset: __privateGet(this, _range).startOffset, - endContainer: __privateGet(this, _range).endContainer, - endOffset: __privateGet(this, _range).endOffset - }; -}; -/** - * Applies styles from the startNode to the endNode. - * - * @param {Node} startNode - * @param {number} startOffset - * @param {Node} endNode - * @param {number} endOffset - * @param {Object.|CSSStyleDeclaration} newStyles - * @returns {void} - */ -applyStylesTo_fn = function(startNode, startOffset, endNode, endOffset, newStyles) { - const root = __privateGet(this, _textEditor).root; - setRootStyles(root, newStyles); - if (startNode === endNode && startNode.nodeType === Node.TEXT_NODE) { - if (startOffset === 0 && endOffset === endNode.nodeValue.length) { - const paragraph = this.startParagraph; - const inline = this.startInline; - setParagraphStyles(paragraph, newStyles); - setInlineStyles(inline, newStyles); - } else if (startOffset !== endOffset) { - const paragraph = this.startParagraph; - setParagraphStyles(paragraph, newStyles); - const inline = this.startInline; - const midText = startNode.splitText(startOffset); - const endText = midText.splitText(endOffset - startOffset); - const midInline = createInlineFrom(inline, midText, newStyles); - inline.after(midInline); - if (endText.length > 0) { - const endInline = createInline(endText, inline.style); - midInline.after(endInline); - } - this.setSelection(midText, 0, midText, midText.nodeValue.length); - } else { - const paragraph = this.startParagraph; - setParagraphStyles(paragraph, newStyles); - } - return __privateMethod(this, _SelectionController_instances, notifyStyleChange_fn).call(this); - } else if (startNode !== endNode) { - SafeGuard.start(); - const expectedEndNode = getClosestTextNode(endNode); - __privateGet(this, _textNodeIterator).currentNode = getClosestTextNode(startNode); - do { - SafeGuard.update(); - const paragraph = getParagraph(__privateGet(this, _textNodeIterator).currentNode); - setParagraphStyles(paragraph, newStyles); - const inline = getInline(__privateGet(this, _textNodeIterator).currentNode); - if (__privateGet(this, _textNodeIterator).currentNode === startNode && startOffset > 0) { - const newInline = splitInline(inline, startOffset); - setInlineStyles(newInline, newStyles); - inline.after(newInline); - } else if (__privateGet(this, _textNodeIterator).currentNode === startNode && startOffset === 0 || __privateGet(this, _textNodeIterator).currentNode !== startNode && __privateGet(this, _textNodeIterator).currentNode !== endNode || __privateGet(this, _textNodeIterator).currentNode === endNode && endOffset === endNode.nodeValue.length) { - setInlineStyles(inline, newStyles); - } else if (__privateGet(this, _textNodeIterator).currentNode === endNode && endOffset < endNode.nodeValue.length) { - const newInline = splitInline(inline, endOffset); - setInlineStyles(inline, newStyles); - inline.after(newInline); - } - if (__privateGet(this, _textNodeIterator).currentNode === expectedEndNode) return; - __privateGet(this, _textNodeIterator).nextNode(); - } while (__privateGet(this, _textNodeIterator).currentNode); - } - return __privateMethod(this, _SelectionController_instances, notifyStyleChange_fn).call(this); -}; -function createSelectionImposterFromClientRects(referenceRect, clientRects) { - const fragment = document.createDocumentFragment(); - for (const rect of clientRects) { - const rectElement = document.createElement("div"); - rectElement.className = "selection-imposter-rect"; - rectElement.style.left = `${rect.x - referenceRect.x}px`; - rectElement.style.top = `${rect.y - referenceRect.y}px`; - rectElement.style.width = `${rect.width}px`; - rectElement.style.height = `${rect.height}px`; - fragment.appendChild(rectElement); - } - return fragment; -} -function addEventListeners(target, object, options) { - Object.entries(object).forEach( - ([type, listener]) => target.addEventListener(type, listener, options) - ); -} -function removeEventListeners(target, object) { - Object.entries(object).forEach( - ([type, listener]) => target.removeEventListener(type, listener) - ); -} -const LayoutType = { - FULL: "full", - PARTIAL: "partial" -}; -class TextEditor extends EventTarget { - /** - * Constructor. - * - * @param {HTMLElement} element - */ - constructor(element, options) { - super(); - __privateAdd(this, _TextEditor_instances); - /** - * Element content editable to be used by the TextEditor - * - * @type {HTMLElement} - */ - __privateAdd(this, _element, null); - /** - * Map/Dictionary of events. - * - * @type {Object.} - */ - __privateAdd(this, _events, null); - /** - * Root element that will contain the content. - * - * @type {HTMLElement} - */ - __privateAdd(this, _root, null); - /** - * Change controller controls when we should notify changes. - * - * @type {ChangeController} - */ - __privateAdd(this, _changeController, null); - /** - * Selection controller controls the current/saved selection. - * - * @type {SelectionController} - */ - __privateAdd(this, _selectionController, null); - /** - * Selection imposter keeps selection elements. - * - * @type {HTMLElement} - */ - __privateAdd(this, _selectionImposterElement, null); - /** - * Style defaults. - * - * @type {Object.} - */ - __privateAdd(this, _styleDefaults2, null); - /** - * Dispatchs a `change` event. - * - * @param {CustomEvent} e - * @returns {void} - */ - __privateAdd(this, _onChange, (e) => this.dispatchEvent(new e.constructor(e.type, e))); - /** - * Dispatchs a `stylechange` event. - * - * @param {CustomEvent} e - * @returns {void} - */ - __privateAdd(this, _onStyleChange, (e) => { - if (__privateGet(this, _selectionImposterElement).children.length > 0) { - __privateMethod(this, _TextEditor_instances, createSelectionImposter_fn).call(this); - } - this.dispatchEvent(new e.constructor(e.type, e)); - }); - /** - * On blur we create a new FakeSelection if there's any. - * - * @param {FocusEvent} e - */ - __privateAdd(this, _onBlur, (e) => { - __privateGet(this, _changeController).notifyImmediately(); - __privateGet(this, _selectionController).saveSelection(); - __privateMethod(this, _TextEditor_instances, createSelectionImposter_fn).call(this); - this.dispatchEvent(new FocusEvent(e.type, e)); - }); - /** - * On focus we should restore the FakeSelection from the current - * selection. - * - * @param {FocusEvent} e - */ - __privateAdd(this, _onFocus, (e) => { - __privateGet(this, _selectionController).restoreSelection(); - if (__privateGet(this, _selectionImposterElement)) { - __privateGet(this, _selectionImposterElement).replaceChildren(); - } - this.dispatchEvent(new FocusEvent(e.type, e)); - }); - /** - * Event called when the user pastes some text into the - * editor. - * - * @param {ClipboardEvent} e - */ - __privateAdd(this, _onPaste, (e) => clipboard.paste(e, this, __privateGet(this, _selectionController))); - /** - * Event called when the user cuts some text from the - * editor. - * - * @param {ClipboardEvent} e - */ - __privateAdd(this, _onCut, (e) => clipboard.cut(e, this, __privateGet(this, _selectionController))); - /** - * Event called when the user copies some text from the - * editor. - * - * @param {ClipboardEvent} e - */ - __privateAdd(this, _onCopy, (e) => clipboard.copy(e, this, __privateGet(this, _selectionController))); - /** - * Event called before the DOM is modified. - * - * @param {InputEvent} e - */ - __privateAdd(this, _onBeforeInput, (e) => { - if (e.inputType === "historyUndo" || e.inputType === "historyRedo") { - return; - } - if (!(e.inputType in commands)) { - if (e.inputType !== "insertCompositionText") { - e.preventDefault(); - } - return; - } - if (e.inputType in commands) { - const command = commands[e.inputType]; - if (!__privateGet(this, _selectionController).startMutation()) - return; - command(e, this, __privateGet(this, _selectionController)); - const mutations = __privateGet(this, _selectionController).endMutation(); - __privateMethod(this, _TextEditor_instances, notifyLayout_fn).call(this, LayoutType.FULL, mutations); - } - }); - /** - * Event called after the DOM is modified. - * - * @param {InputEvent} e - */ - __privateAdd(this, _onInput, (e) => { - if (e.inputType === "historyUndo" || e.inputType === "historyRedo") { - return; - } - if (e.inputType === "insertCompositionText") { - __privateMethod(this, _TextEditor_instances, notifyLayout_fn).call(this, LayoutType.FULL, null); - } - }); - if (!(element instanceof HTMLElement)) - throw new TypeError("Invalid text editor element"); - __privateSet(this, _element, element); - __privateSet(this, _selectionImposterElement, options == null ? void 0 : options.selectionImposterElement); - __privateSet(this, _events, { - blur: __privateGet(this, _onBlur), - focus: __privateGet(this, _onFocus), - paste: __privateGet(this, _onPaste), - cut: __privateGet(this, _onCut), - copy: __privateGet(this, _onCopy), - beforeinput: __privateGet(this, _onBeforeInput), - input: __privateGet(this, _onInput) - }); - __privateSet(this, _styleDefaults2, options == null ? void 0 : options.styleDefaults); - __privateMethod(this, _TextEditor_instances, setup_fn2).call(this, options); - } - /** - * Root element that contains all the paragraphs. - * - * @type {HTMLDivElement} - */ - get root() { - return __privateGet(this, _root); - } - set root(newRoot) { - const previousRoot = __privateGet(this, _root); - __privateSet(this, _root, newRoot); - previousRoot.replaceWith(newRoot); - } - /** - * Element that contains the root and that has the - * contenteditable attribute. - * - * @type {HTMLElement} - */ - get element() { - return __privateGet(this, _element); - } - /** - * Returns true if the content is in an empty state. - * - * @type {boolean} - */ - get isEmpty() { - return __privateGet(this, _root).children.length === 1 && __privateGet(this, _root).firstElementChild.children.length === 1 && isLineBreak(__privateGet(this, _root).firstElementChild.firstElementChild.firstChild); - } - /** - * Indicates the amount of paragraphs in the current content. - * - * @type {number} - */ - get numParagraphs() { - return __privateGet(this, _root).children.length; - } - /** - * CSS Style declaration for the current inline. From here we - * can infer root, paragraph and inline declarations. - * - * @type {CSSStyleDeclaration} - */ - get currentStyle() { - return __privateGet(this, _selectionController).currentStyle; - } - /** - * Focus the element - */ - focus() { - return __privateGet(this, _element).focus(); - } - /** - * Blurs the element - */ - blur() { - return __privateGet(this, _element).blur(); - } - /** - * Creates a new root. - * - * @param {...any} args - * @returns {HTMLDivElement} - */ - createRoot(...args) { - return createRoot(...args); - } - /** - * Creates a new paragraph. - * - * @param {...any} args - * @returns {HTMLDivElement} - */ - createParagraph(...args) { - return createParagraph(...args); - } - /** - * Creates a new inline from a string. - * - * @param {string} text - * @param {Object.|CSSStyleDeclaration} styles - * @returns {HTMLSpanElement} - */ - createInlineFromString(text, styles) { - if (text === "") { - return createEmptyInline(styles); - } - return createInline(new Text(text), styles); - } - /** - * Creates a new inline. - * - * @param {...any} args - * @returns {HTMLSpanElement} - */ - createInline(...args) { - return createInline(...args); - } - /** - * Applies the current styles to the selection or - * the current DOM node at the caret. - * - * @param {*} styles - */ - applyStylesToSelection(styles) { - __privateGet(this, _selectionController).startMutation(); - __privateGet(this, _selectionController).applyStyles(styles); - const mutations = __privateGet(this, _selectionController).endMutation(); - __privateMethod(this, _TextEditor_instances, notifyLayout_fn).call(this, LayoutType.FULL, mutations); - __privateGet(this, _changeController).notifyImmediately(); - return this; - } - /** - * Selects all content. - */ - selectAll() { - __privateGet(this, _selectionController).selectAll(); - return this; - } - /** - * Moves cursor to end. - * - * @returns - */ - cursorToEnd() { - __privateGet(this, _selectionController).cursorToEnd(); - return this; - } - /** - * Disposes everything. - */ - dispose() { - __privateGet(this, _changeController).removeEventListener("change", __privateGet(this, _onChange)); - __privateGet(this, _changeController).dispose(); - __privateSet(this, _changeController, null); - __privateGet(this, _selectionController).removeEventListener( - "stylechange", - __privateGet(this, _onStyleChange) - ); - __privateGet(this, _selectionController).dispose(); - __privateSet(this, _selectionController, null); - removeEventListeners(__privateGet(this, _element), __privateGet(this, _events)); - __privateSet(this, _element, null); - __privateSet(this, _root, null); - } -} -_element = new WeakMap(); -_events = new WeakMap(); -_root = new WeakMap(); -_changeController = new WeakMap(); -_selectionController = new WeakMap(); -_selectionImposterElement = new WeakMap(); -_styleDefaults2 = new WeakMap(); -_TextEditor_instances = new WeakSet(); -/** - * Setups editor properties. - */ -setupElementProperties_fn = function() { - if (!__privateGet(this, _element).isContentEditable) { - __privateGet(this, _element).contentEditable = "true"; - if (!__privateGet(this, _element).isContentEditable) { - __privateGet(this, _element).setAttribute("contenteditable", "true"); - } - } - if (__privateGet(this, _element).spellcheck) __privateGet(this, _element).spellcheck = false; - if (__privateGet(this, _element).autocapitalize) __privateGet(this, _element).autocapitalize = false; - if (!__privateGet(this, _element).autofocus) __privateGet(this, _element).autofocus = true; - if (!__privateGet(this, _element).role || __privateGet(this, _element).role !== "textbox") - __privateGet(this, _element).role = "textbox"; - if (__privateGet(this, _element).ariaAutoComplete) __privateGet(this, _element).ariaAutoComplete = false; - if (!__privateGet(this, _element).ariaMultiLine) __privateGet(this, _element).ariaMultiLine = true; - __privateGet(this, _element).dataset.itype = "editor"; -}; -/** - * Setups the root element. - */ -setupRoot_fn = function() { - __privateSet(this, _root, createEmptyRoot(__privateGet(this, _styleDefaults2))); - __privateGet(this, _element).appendChild(__privateGet(this, _root)); -}; -_onChange = new WeakMap(); -_onStyleChange = new WeakMap(); -/** - * Setups the elements, the properties and the - * initial content. - */ -setup_fn2 = function(options) { - __privateMethod(this, _TextEditor_instances, setupElementProperties_fn).call(this); - __privateMethod(this, _TextEditor_instances, setupRoot_fn).call(this); - __privateSet(this, _changeController, new ChangeController(this)); - __privateGet(this, _changeController).addEventListener("change", __privateGet(this, _onChange)); - __privateSet(this, _selectionController, new SelectionController( - this, - document.getSelection(), - options - )); - __privateGet(this, _selectionController).addEventListener( - "stylechange", - __privateGet(this, _onStyleChange) - ); - addEventListeners(__privateGet(this, _element), __privateGet(this, _events), { - capture: true - }); -}; -/** - * Creates the selection imposter. - */ -createSelectionImposter_fn = function() { - var _a; - if (__privateGet(this, _selectionImposterElement) && !__privateGet(this, _selectionController).isCollapsed) { - const rects = (_a = __privateGet(this, _selectionController).range) == null ? void 0 : _a.getClientRects(); - if (rects) { - const rect = __privateGet(this, _selectionImposterElement).getBoundingClientRect(); - __privateGet(this, _selectionImposterElement).replaceChildren( - createSelectionImposterFromClientRects(rect, rects) - ); - } - } -}; -_onBlur = new WeakMap(); -_onFocus = new WeakMap(); -_onPaste = new WeakMap(); -_onCut = new WeakMap(); -_onCopy = new WeakMap(); -_onBeforeInput = new WeakMap(); -_onInput = new WeakMap(); -/** - * Notifies that the edited texts needs layout. - * - * @param {'full'|'partial'} type - * @param {CommandMutations} mutations - */ -notifyLayout_fn = function(type = LayoutType.FULL, mutations) { - this.dispatchEvent( - new CustomEvent("needslayout", { - detail: { - type, - mutations - } - }) - ); -}; -function isEditor(instance) { - return instance instanceof TextEditor; -} -function getRoot(instance) { - if (isEditor(instance)) { - return instance.root; - } else { - return null; - } -} -function setRoot(instance, root) { - if (isEditor(instance)) { - instance.root = root; - } - return instance; -} -function create(element, options) { - return new TextEditor(element, { ...options }); -} -function getCurrentStyle(instance) { - if (isEditor(instance)) { - return instance.currentStyle; - } -} -function applyStylesToSelection(instance, styles) { - if (isEditor(instance)) { - return instance.applyStylesToSelection(styles); - } -} -function dispose(instance) { - if (isEditor(instance)) { - instance.dispose(); - } -} -export { - TextEditor, - applyStylesToSelection, - create, - TextEditor as default, - dispose, - getCurrentStyle, - getRoot, - isEditor, - setRoot -}; -//# sourceMappingURL=TextEditor.js.map +var pe=r=>{throw TypeError(r)};var jt=(r,i,t)=>i.has(r)||pe("Cannot "+t);var e=(r,i,t)=>(jt(r,i,"read from private field"),t?t.call(r):i.get(r)),u=(r,i,t)=>i.has(r)?pe("Cannot add the same private member more than once"):i instanceof WeakSet?i.add(r):i.set(r,t),a=(r,i,t,n)=>(jt(r,i,"write to private field"),n?n.call(r,t):i.set(r,t),t),y=(r,i,t)=>(jt(r,i,"access private method"),t);function de(r,i){}function ge(r,i){}var Qt=null,Jt=null;function gr(){return Qt||(Qt=mr(1,1)),Jt||(Jt=Qt.getContext("2d")),Jt}function mr(r,i){return"OffscreenCanvas"in globalThis?new OffscreenCanvas(r,i):document.createElement("canvas")}function qt(r){return r.toString(16).padStart(2,"0")}function yr(r){let i=gr();i.fillStyle=r,i.fillRect(0,0,1,1);let t=i.getImageData(0,0,1,1),[n,s,o,h]=t.data;return[`#${qt(n)}${qt(s)}${qt(o)}`,h/255]}function me(r){let[i,t]=yr(r);return`[["^ ","~:fill-color","${i}","~:fill-opacity",${t}]]`}function ye(r,i){for(let t=0;tt.setAttribute(n,s)),i?.data&&Object.entries(i.data).forEach(([n,s])=>t.dataset[n]=s),i?.styles&&i?.allowedStyles&&j(t,i.allowedStyles,i.styles),i?.children&&(Array.isArray(i.children)?t.append(...i.children):t.appendChild(i.children)),t}function ht(r,i){return r.nodeType===Node.ELEMENT_NODE&&r.nodeName===i.toUpperCase()}function Ft(r,i){return i===0}function Rt(r,i){return r.nodeType===Node.TEXT_NODE?r.nodeValue.length===i:!0}var Ce="BR";function lt(){return document.createElement(Ce)}function N(r){return r.nodeType===Node.ELEMENT_NODE&&r.nodeName===Ce}var Ie="SPAN",te="inline",Nr=`[data-itype="${te}"]`,ee=[["--typography-ref-id"],["--typography-ref-file"],["--font-id"],["--font-variant-id"],["--fills"],["font-variant"],["font-family"],["font-size","px"],["font-weight"],["font-style"],["line-height"],["letter-spacing","px"],["text-decoration"],["text-transform"]];function A(r){return!(!r||!ht(r,Ie)||r.dataset.itype!==te)}function Se(r){return r?["A","ABBR","ACRONYM","B","BDO","BIG","BR","BUTTON","CITE","CODE","DFN","EM","I","IMG","INPUT","KBD","LABEL","MAP","OBJECT","OUTPUT","Q","SAMP","SCRIPT","SELECT","SMALL","SPAN","STRONG","SUB","SUP","TEXTAREA","TIME","TT","VAR"].includes(r.nodeName):!1}function D(r,i,t){if(!(r instanceof HTMLBRElement)&&!(r instanceof Text))throw new TypeError("Invalid inline child");if(r instanceof Text&&r.nodeValue.length===0)throw console.trace("nodeValue",r.nodeValue),new TypeError("Invalid inline child, cannot be an empty text");return at(Ie,{attributes:{id:ot(),...t},data:{itype:te},styles:i,allowedStyles:ee,children:r})}function ve(r,i,t,n){return D(i,Te(ee,r.style,t),n)}function tt(r){return D(lt(),r)}function gt(r,i){return j(r,ee,i)}function P(r){if(!r)return null;if(A(r))return r;if(r.nodeType===Node.TEXT_NODE){let i=r?.parentElement;return!i||!A(i)?null:i}return r.closest(Nr)}function we(r,i){let t=P(r);return t?Ft(t,i):!1}function Lt(r,i){let t=P(r);return t?Rt(t.firstChild,i):!1}function mt(r,i){let t=r.firstChild,n=r.style,s=t.splitText(i);return D(s,n)}function Oe(r){let i=[],t=r,n=0;for(;t;)n>0&&i.push(t),t=t.nextElementSibling,n++;return i}function Ae(r){if(!A(r))throw new Error("Invalid inline");return N(r.firstChild)?0:r.firstChild.nodeValue.length}var De="DIV",re="root",$r=`[data-itype="${re}"]`,Fe=[["--vertical-align"]];function Re(r){return!(!r||!ht(r,De)||r.dataset.itype!==re)}function ie(r,i,t){if(!Array.isArray(r)||!r.every(M))throw new TypeError("Invalid root children");return at(De,{attributes:{id:ot(),...t},data:{itype:re},styles:i,allowedStyles:Fe,children:r})}function Le(r){return ie([et(r)],r)}function Ve(r,i){return j(r,Fe,i)}function ne(r){if(!r)throw new TypeError("Invalid text node");return r.nodeType===Node.TEXT_NODE||N(r)}function Be(r){if(!r)throw new TypeError("Invalid text node");return N(r)?0:r.nodeValue.length}function yt(r){if(ne(r))return r;if(A(r))return r.firstChild;if(M(r))return r.firstChild.firstChild;if(Re(r))return r.firstChild.firstChild.firstChild;throw new Error("Cannot find a text node")}var be="DIV",se="paragraph",Pr=`[data-itype="${se}"]`,Me=[["--typography-ref-id"],["--typography-ref-file"],["--font-id"],["--font-variant-id"],["--fills"],["font-variant"],["font-family"],["font-size","px"],["font-weight"],["font-style"],["line-height"],["letter-spacing","px"],["text-decoration"],["text-transform"],["text-align"],["direction"]];function Ue(r){return!Se(r)}function ke(r){if(!M(r))throw new TypeError("Invalid paragraph");let i=r.firstChild;if(!A(i))throw new TypeError("Invalid inline");return N(i.firstChild)}function M(r){return!(!r||!ht(r,be)||r.dataset.itype!==se)}function X(r,i,t){if(r&&(!Array.isArray(r)||!r.every(A)))throw new TypeError("Invalid paragraph children");return at(be,{attributes:{id:ot(),...t},data:{itype:se},styles:i,allowedStyles:Me,children:r})}function et(r){return X([tt(r)],r)}function xt(r,i){return j(r,Me,i)}function F(r){if(!r)return null;if(M(r))return r;if(r.nodeType===Node.TEXT_NODE||N(r)){let i=r?.parentElement?.parentElement;return!i||!M(i)?null:i}return r.closest(Pr)}function We(r,i){let t=F(r);if(!t)throw new Error("Can't find the paragraph");let n=P(r);if(!n)throw new Error("Can't find the inline");return t.firstElementChild===n&&Ft(n.firstChild,i)}function _e(r,i){let t=F(r);if(!t)throw new Error("Cannot find the paragraph");let n=P(r);if(!n)throw new Error("Cannot find the inline");return t.lastElementChild===n&&Rt(n.firstChild,i)}function Vt(r,i,t){let n=r.style;if(Lt(i,t))return X(Oe(i),n);let s=mt(i,t);return X([s],n)}function Bt(r,i){return r.append(...i.children),i.remove(),r}function Tr(r,i,t){let n=r.createNodeIterator(i,NodeFilter.SHOW_TEXT),s=r.createDocumentFragment(),o=null,h=n.nextNode();for(;h;){let I=Ee(ye(t,xe(h.parentElement)));Zt(h.parentElement.style)||Zt(I)||Ue(h.parentElement)?(o&&s.appendChild(o),o=X(void 0,I)):o===null&&(o=X()),o.appendChild(D(new Text(h.nodeValue),I)),h=n.nextNode()}return s.appendChild(o),s}function Ge(r,i){let n=new DOMParser().parseFromString(r,"text/html");return Tr(n,n.documentElement,i)}function Xe(r,i){let t=r.replace(/\r/g,"").split(` +`),n=document.createDocumentFragment();for(let s of t)s===""?n.appendChild(et(i)):n.appendChild(X([D(new Text(s),i)],i));return n}function $e(r,i,t){r.preventDefault();let n=null;if(r.clipboardData.types.includes("text/html")){let s=r.clipboardData.getData("text/html");n=Ge(s,t.currentStyle)}else if(r.clipboardData.types.includes("text/plain")){let s=r.clipboardData.getData("text/plain");n=Xe(s,t.currentStyle)}n&&(t.isCollapsed?t.insertPaste(n):t.replaceWithPaste(n))}var bt={copy:de,cut:ge,paste:$e};function Ye(r,i,t){if(r.preventDefault(),t.isCollapsed){if(t.isTextFocus)return t.insertText(r.data);if(t.isLineBreakFocus)return t.replaceLineBreak(r.data)}else{if(t.isMultiParagraph)return t.replaceParagraphs(r.data);if(t.isMultiInline)return t.replaceInlines(r.data);if(t.isTextSame)return t.replaceText(r.data)}}function ze(r,i,t){return r.preventDefault(),t.isCollapsed?t.insertParagraph():t.replaceWithParagraph()}function He(r,i,t){if(r.preventDefault(),t.isCollapsed)throw new Error("This should be impossible");return t.removeSelected()}function Ke(r,i,t){if(r.preventDefault(),!i.isEmpty){if(!t.isCollapsed)return t.removeSelected({direction:"backward"});if(t.isTextFocus&&t.focusOffset>0)return t.removeBackwardText();if(t.isTextFocus&&t.focusAtStart)return t.mergeBackwardParagraph();if(t.isInlineFocus||t.isLineBreakFocus)return t.removeBackwardParagraph()}}function je(r,i,t){if(r.preventDefault(),!i.isEmpty){if(!t.isCollapsed)return t.removeSelected({direction:"forward"});if(t.isTextFocus&&t.focusOffset>=0)return t.removeForwardText();if(t.isTextFocus&&t.focusAtEnd)return t.mergeForwardParagraph();if((t.isInlineFocus||t.isLineBreakFocus)&&i.numParagraphs>1)return t.removeForwardParagraph()}}var Mt={insertText:Ye,insertParagraph:ze,deleteByCut:He,deleteContentBackward:Ke,deleteContentForward:je};var rt,Et,Nt,Pt,oe=class extends EventTarget{constructor(t=500){super();u(this,rt,null);u(this,Et,1e3);u(this,Nt,!1);u(this,Pt,()=>{this.dispatchEvent(new Event("change"))});if(typeof t=="number"&&(!Number.isInteger(t)||t<=0))throw new TypeError("Invalid time");a(this,Et,t??500)}get hasPendingChanges(){return e(this,Nt)}notifyDebounced(){a(this,Nt,!0),clearTimeout(e(this,rt)),a(this,rt,setTimeout(e(this,Pt),e(this,Et)))}notifyImmediately(){clearTimeout(e(this,rt)),e(this,Pt).call(this)}dispose(){this.hasPendingChanges&&this.notifyImmediately(),clearTimeout(e(this,rt))}};rt=new WeakMap,Et=new WeakMap,Nt=new WeakMap,Pt=new WeakMap;var Qe=oe;function it(r){if(!Number.isInteger(r)||r<0)throw new TypeError("Invalid offset")}function nt(r){if(typeof r!="string")throw new TypeError("Invalid string")}function Ut(r,i,t){return nt(r),it(i),nt(t),r.slice(0,i)+t+r.slice(i)}function Je(r,i,t,n){return nt(r),it(i),it(t),nt(n),r.slice(0,i)+n+r.slice(t)}function qe(r,i){return nt(r),it(i),i===0?r:r.slice(0,i-1)+r.slice(i)}function Ze(r,i){return nt(r),it(i),r.slice(0,i)+r.slice(i+1)}function tr(r,i,t){return nt(r),it(i),it(t),r.slice(0,i)+r.slice(t)}var Q={FORWARD:1,BACKWARD:0},st,w,T=class T{constructor(i){u(this,st,null);u(this,w,null);if(!(i instanceof HTMLElement))throw new TypeError("Invalid root node");a(this,st,i),a(this,w,T.findDown(i,i))}static isTextNode(i){return i.nodeType===Node.TEXT_NODE||i.nodeType===Node.ELEMENT_NODE&&i.nodeName==="BR"}static isContainerNode(i){return i.nodeType===Node.ELEMENT_NODE&&i.nodeName!=="BR"}static findDown(i,t,n=new Set,s=Q.FORWARD){if(i===t)return T.findDown(s===Q.FORWARD?i.firstChild:i.lastChild,t,n,s);let o=Date.now(),h=i;for(;h;){if(Date.now()-o>=1e3)throw new Error("Iteration timeout");if(n.has(h)){h=s===Q.FORWARD?h.nextSibling:h.previousSibling;continue}if(T.isTextNode(h))return h;if(T.isContainerNode(h))return T.findDown(s===Q.FORWARD?h.firstChild:h.lastChild,t,n,s);h=s===Q.FORWARD?h.nextSibling:h.previousSibling}return null}static findUp(i,t,n=new Set,s=Q.FORWARD){if(n.add(i),T.isTextNode(i))return T.findUp(i.parentNode,t,n,s);if(T.isContainerNode(i)){let o=T.findDown(i,t,n,s);if(o)return o;if(i!==t)return T.findUp(i.parentNode,t,n,s)}return null}get currentNode(){return e(this,w)}set currentNode(i){let t=(i.compareDocumentPosition(e(this,st))&Node.DOCUMENT_POSITION_CONTAINS)===Node.DOCUMENT_POSITION_CONTAINS;if(!(i instanceof Node)||!T.isTextNode(i)||!t)throw new TypeError("Invalid new current node");a(this,w,i)}nextNode(){if(!e(this,w))return null;let i=T.findUp(e(this,w),e(this,st),new Set,Q.FORWARD);return i?(a(this,w,i),e(this,w)):null}previousNode(){if(!e(this,w))return null;let i=T.findUp(e(this,w),e(this,st),new Set,Q.BACKWARD);return i?(a(this,w,i),e(this,w)):null}};st=new WeakMap,w=new WeakMap;var ae=T,er=ae;var $,Y,z,he=class{constructor(i,t,n){u(this,$,new Set);u(this,Y,new Set);u(this,z,new Set);i&&Array.isArray(i)&&a(this,$,new Set(i)),t&&Array.isArray(t)&&a(this,z,new Set(t)),n&&Array.isArray(n)&&a(this,Y,new Set(n))}get added(){return e(this,$)}get removed(){return e(this,Y)}get updated(){return e(this,z)}clear(){e(this,$).clear(),e(this,Y).clear(),e(this,z).clear()}dispose(){e(this,$).clear(),a(this,$,null),e(this,Y).clear(),a(this,Y,null),e(this,z).clear(),a(this,z,null)}add(i){return e(this,$).add(i),this}remove(i){return e(this,Y).add(i),this}update(i){return e(this,z).add(i),this}};$=new WeakMap,Y=new WeakMap,z=new WeakMap;var rr=he;var ut={FORWARD:1,NONE:0,BACKWARD:-1};var ir=Date.now();function Cr(){ir=Date.now()}function Ir(){if(Date.now-ir>=1e3)throw new Error("Safe guard timeout")}var Tt={start:Cr,update:Ir};var U,f,R,g,L,J,H,ft,l,c,V,Ct,ct,d,pt,x,ue,kt,nr,It,Wt,sr,or,ar,le=class extends EventTarget{constructor(t,n,s){super();u(this,x);u(this,U,null);u(this,f,null);u(this,R,new Set);u(this,g,null);u(this,L,null);u(this,J,0);u(this,H,null);u(this,ft,0);u(this,l,null);u(this,c,null);u(this,V,null);u(this,Ct,null);u(this,ct,null);u(this,d,new rr);u(this,pt,null);u(this,It,t=>{if(!this.hasFocus)return;let n=!1,s=!1;if(e(this,L)!==e(this,f).focusNode&&(a(this,L,e(this,f).focusNode),n=!0),a(this,J,e(this,f).focusOffset),e(this,H)!==e(this,f).anchorNode&&(a(this,H,e(this,f).anchorNode),s=!0),a(this,ft,e(this,f).anchorOffset),e(this,f).rangeCount>1)for(let o=0;o0){let o=e(this,f).getRangeAt(0);a(this,g,o),e(this,R).clear(),e(this,R).add(o)}else a(this,g,null),e(this,R).clear();n&&y(this,x,Wt).call(this),e(this,ct)&&e(this,ct).update(this)});a(this,ct,s?.debug),a(this,pt,s?.styleDefaults),a(this,f,n),a(this,U,t),a(this,c,new er(e(this,U).element)),y(this,x,sr).call(this)}get currentStyle(){return e(this,V)}saveSelection(){return a(this,l,{isCollapsed:e(this,f).isCollapsed,focusNode:e(this,f).focusNode,focusOffset:e(this,f).focusOffset,anchorNode:e(this,f).anchorNode,anchorOffset:e(this,f).anchorOffset,range:y(this,x,or).call(this)}),!0}restoreSelection(){return e(this,l)?(e(this,l).anchorNode&&e(this,l).focusNode&&(e(this,l).anchorNode===e(this,l).focusNode?e(this,f).setPosition(e(this,l).focusNode,e(this,l).focusOffset):e(this,f).setBaseAndExtent(e(this,l).anchorNode,e(this,l).anchorOffset,e(this,l).focusNode,e(this,l).focusOffset)),a(this,l,null),!0):!1}startMutation(){return e(this,d).clear(),!!e(this,L)}endMutation(){return e(this,d)}selectAll(){return e(this,f).selectAllChildren(e(this,U).root),this}cursorToEnd(){let t=document.createRange();return t.selectNodeContents(e(this,U).element),t.collapse(!1),e(this,f).removeAllRanges(),e(this,f).addRange(t),this}collapse(t,n){let s=t.nodeType===Node.TEXT_NODE&&n>=t.nodeValue.length?t.nodeValue.length:n;return this.setSelection(t,s,t,s)}setSelection(t,n,s=t,o=n){if(!t.isConnected)throw new Error("Invalid anchorNode");if(!s.isConnected)throw new Error("Invalid focusNode");e(this,l)?(e(this,l).isCollapsed=s===t&&n===o,e(this,l).focusNode=s,e(this,l).focusOffset=o,e(this,l).anchorNode=t,e(this,l).anchorOffset=n,e(this,l).range.collapsed=e(this,l).isCollapsed,s.compareDocumentPosition(t)&Node.DOCUMENT_POSITION_FOLLOWING?(e(this,l).range.startContainer=s,e(this,l).range.startOffset=o,e(this,l).range.endContainer=t,e(this,l).range.endOffset=n):(e(this,l).range.startContainer=t,e(this,l).range.startOffset=n,e(this,l).range.endContainer=s,e(this,l).range.endOffset=o)):(a(this,H,t),a(this,ft,n),t===s?(a(this,L,e(this,H)),a(this,J,e(this,ft)),e(this,f).setPosition(t,n)):(a(this,L,s),a(this,J,o),e(this,f).setBaseAndExtent(t,n,s,o)))}dispose(){document.removeEventListener("selectionchange",e(this,It)),a(this,U,null),e(this,R).clear(),a(this,R,null),a(this,g,null),a(this,f,null),a(this,L,null),a(this,H,null),e(this,d).dispose(),a(this,d,null)}get selection(){return e(this,f)}get range(){return e(this,g)}get direction(){return this.isCollapsed?ut.NONE:this.focusNode!==this.anchorNode?this.startContainer===this.focusNode?ut.BACKWARD:ut.FORWARD:this.focusOffset1&&s.childNodes.length===0)return s.remove(),this.collapse(o,0);return this.collapse(this.focusNode,this.focusOffset)}removeBackwardText(){e(this,c).currentNode=this.focusNode;let t=qe(this.focusNode.nodeValue,this.focusOffset);if(this.focusNode.nodeValue!==t&&(this.focusNode.nodeValue=t),this.focusOffset-1>0)return this.collapse(this.focusNode,this.focusOffset-1);let n=this.focusParagraph;if(!n)throw new Error("Cannot find paragraph");let s=this.focusInline;if(!s)throw new Error("Cannot find inline");let o=e(this,c).previousNode();if(this.focusNode.nodeValue===""&&this.focusNode.remove(),n.children.length===1&&s.childNodes.length===0){let h=lt();return s.appendChild(h),this.collapse(h,0)}else if(n.children.length>1&&s.childNodes.length===0)return s.remove(),this.collapse(o,Be(o));return this.collapse(this.focusNode,this.focusOffset-1)}insertText(t){return this.focusNode.nodeValue=Ut(this.focusNode.nodeValue,this.focusOffset,t),e(this,d).update(this.focusInline),this.collapse(this.focusNode,this.focusOffset+t.length)}replaceText(t){let n=Math.min(this.anchorOffset,this.focusOffset),s=Math.max(this.anchorOffset,this.focusOffset);return this.focusNode.nodeValue=Je(this.focusNode.nodeValue,n,s,t),e(this,d).update(this.focusInline),this.collapse(this.focusNode,n+t.length)}replaceInlines(t){let n=this.focusParagraph;if(this.startInline===n.firstChild&&this.startOffset===0&&this.endInline===n.lastChild&&this.endOffset===n.lastChild.textContent.length){let s=new Text(t);return n.replaceChildren(D(s,this.anchorInline.style)),this.collapse(s,s.nodeValue.length)}return this.removeSelected(),this.focusNode.nodeValue=Ut(this.focusNode.nodeValue,this.focusOffset,t),this.collapse(this.focusNode,this.focusOffset+t.length)}replaceParagraphs(t){let n=this.focusParagraph;this.removeSelected(),this.focusNode.nodeValue=Ut(this.focusNode.nodeValue,this.focusOffset,t);for(let s of n.children)s.textContent===""&&s.remove()}insertParagraphAfter(){let t=this.focusParagraph,n=et(e(this,V));return t.after(n),e(this,d).update(t),e(this,d).add(n),this.collapse(n.firstChild.firstChild,0)}insertParagraphBefore(){let t=this.focusParagraph,n=et(e(this,V));return t.before(n),e(this,d).update(t),e(this,d).add(n),this.collapse(t.firstChild.firstChild,0)}splitParagraph(){let t=this.focusParagraph,n=Vt(this.focusParagraph,this.focusInline,e(this,J));return this.focusParagraph.after(n),e(this,d).update(t),e(this,d).add(n),this.collapse(n.firstChild.firstChild,0)}insertParagraph(){return this.isParagraphEnd?this.insertParagraphAfter():this.isParagraphStart?this.insertParagraphBefore():this.splitParagraph()}replaceWithParagraph(){let t=this.focusParagraph,n=this.focusInline;this.removeSelected();let s=Vt(t,n,this.focusOffset);t.after(s),e(this,d).update(t),e(this,d).add(s)}removeBackwardParagraph(){let t=this.focusParagraph.previousElementSibling;if(!t)return;let n=this.focusParagraph;n.remove();let s=t.children.length>1?t.lastElementChild:t.firstChild,o=N(s.firstChild)?0:s.firstChild.nodeValue.length;return e(this,d).remove(n),this.collapse(s.firstChild,o)}mergeBackwardParagraph(){let t=this.focusParagraph,n=this.focusParagraph.previousElementSibling;if(!n)return;let s=n.lastChild,o=Ae(s);return ke(n)?(n.replaceChildren(...t.children),s=n.firstChild,t.remove()):Bt(n,t),e(this,d).remove(t),e(this,d).update(n),this.collapse(s.firstChild,o)}mergeForwardParagraph(){let t=this.focusParagraph,n=this.focusParagraph.nextElementSibling;n&&(Bt(this.focusParagraph,n),e(this,d).update(t),e(this,d).remove(n))}removeForwardParagraph(){let t=this.focusParagraph.nextSibling;if(!t)return;let n=this.focusParagraph;n.remove();let s=t.firstChild,o=this.focusOffset;return e(this,d).remove(n),this.collapse(s.firstChild,o)}cleanUp(t,n){for(let s of n)s.textContent===""&&(s.remove(),e(this,d).remove(s));for(let s of t)s.children.length===0&&(s.remove(),e(this,d).remove(s))}removeSelected(t){if(this.isCollapsed)return;let n=new Set,s=new Set,o=yt(e(this,g).startContainer),h=yt(e(this,g).endContainer),I=e(this,g).startOffset,B=e(this,g).endOffset,_=null,S=null;if(o===h){e(this,c).currentNode=o,_=e(this,c).previousNode(),e(this,c).currentNode=o,S=e(this,c).nextNode();let E=P(o),b=F(o);n.add(E),s.add(b);let G=tr(o.nodeValue,I,B);if(G===""){let Z=lt();return E.replaceChildren(Z),this.collapse(Z,0)}return o.nodeValue=G,this.collapse(o,I)}e(this,c).currentNode=o;let v=P(o),q=F(o),K=P(h),ce=F(h);Tt.start();do{Tt.update();let E=e(this,c).currentNode,b=P(e(this,c).currentNode),G=F(e(this,c).currentNode),Z=!1;if(e(this,c).currentNode===o?I===0?Z=!0:E.nodeValue=E.nodeValue.slice(0,I):e(this,c).currentNode===h?N(h)||ne(h)&&B===h.nodeValue.length?Z=!0:E.nodeValue=E.nodeValue.slice(B):Z=!0,e(this,c).nextNode(),Z){if(E.remove(),E===o)continue;if(E===h)break;b.childNodes.length===0&&b.remove(),G!==q&&G.children.length===0&&G.remove()}if(E===h)break}while(e(this,c).currentNode);if(q!==ce){let E=Bt(q,ce);if(E.children.length===0){let b=tt(e(this,V));return E.appendChild(b),this.collapse(b.firstChild,0)}}if(v.childNodes.length===0&&K.childNodes.length>0)return v.remove(),this.collapse(h,0);if(v.childNodes.length>0&&K.childNodes.length===0)return K.remove(),this.collapse(o,I);if(v.childNodes.length===0&&K.childNodes.length===0){let E=v.previousElementSibling,b=K.nextElementSibling;if(v.remove(),K.remove(),E)return this.collapse(E.firstChild,E.firstChild.nodeValue.length);if(b)return this.collapse(b.firstChild,0);let G=tt(e(this,V));return q.appendChild(G),this.collapse(G.firstChild,0)}return this.collapse(o,I)}applyStyles(t){return y(this,x,ar).call(this,this.startContainer,this.startOffset,this.endContainer,this.endOffset,t)}};U=new WeakMap,f=new WeakMap,R=new WeakMap,g=new WeakMap,L=new WeakMap,J=new WeakMap,H=new WeakMap,ft=new WeakMap,l=new WeakMap,c=new WeakMap,V=new WeakMap,Ct=new WeakMap,ct=new WeakMap,d=new WeakMap,pt=new WeakMap,x=new WeakSet,ue=function(){if(e(this,pt))for(let[t,n]of Object.entries(e(this,pt)))e(this,V).setProperty(t,n+(t==="font-size"?"px":""))},kt=function(t){for(let n=0;n0){let t=e(this,f).getRangeAt(0);a(this,g,t),e(this,R).add(t)}if(e(this,f).rangeCount>1)for(let t=1;t0){let K=D(v,_.style);q.after(K)}this.setSelection(S,0,S,S.nodeValue.length)}else{let B=this.startParagraph;xt(B,h)}return y(this,x,Wt).call(this)}else if(t!==s){Tt.start();let B=yt(s);e(this,c).currentNode=yt(t);do{Tt.update();let _=F(e(this,c).currentNode);xt(_,h);let S=P(e(this,c).currentNode);if(e(this,c).currentNode===t&&n>0){let v=mt(S,n);gt(v,h),S.after(v)}else if(e(this,c).currentNode===t&&n===0||e(this,c).currentNode!==t&&e(this,c).currentNode!==s||e(this,c).currentNode===s&&o===s.nodeValue.length)gt(S,h);else if(e(this,c).currentNode===s&&or.addEventListener(n,s,t))}function fr(r,i){Object.entries(i).forEach(([t,n])=>r.removeEventListener(t,n))}var Sr={FULL:"full",PARTIAL:"partial"},St=Sr;var p,dt,O,k,m,W,wt,C,cr,pr,Ot,At,dr,fe,Gt,Xt,$t,Yt,zt,Ht,Kt,_t,vt=class extends EventTarget{constructor(t,n){super();u(this,C);u(this,p,null);u(this,dt,null);u(this,O,null);u(this,k,null);u(this,m,null);u(this,W,null);u(this,wt,null);u(this,Ot,t=>this.dispatchEvent(new t.constructor(t.type,t)));u(this,At,t=>{e(this,W).children.length>0&&y(this,C,fe).call(this),this.dispatchEvent(new t.constructor(t.type,t))});u(this,Gt,t=>{e(this,k).notifyImmediately(),e(this,m).saveSelection(),y(this,C,fe).call(this),this.dispatchEvent(new FocusEvent(t.type,t))});u(this,Xt,t=>{e(this,m).restoreSelection(),e(this,W)&&e(this,W).replaceChildren(),this.dispatchEvent(new FocusEvent(t.type,t))});u(this,$t,t=>bt.paste(t,this,e(this,m)));u(this,Yt,t=>bt.cut(t,this,e(this,m)));u(this,zt,t=>bt.copy(t,this,e(this,m)));u(this,Ht,t=>{if(!(t.inputType==="historyUndo"||t.inputType==="historyRedo")){if(!(t.inputType in Mt)){t.inputType!=="insertCompositionText"&&t.preventDefault();return}if(t.inputType in Mt){let n=Mt[t.inputType];if(!e(this,m).startMutation())return;n(t,this,e(this,m));let s=e(this,m).endMutation();y(this,C,_t).call(this,St.FULL,s)}}});u(this,Kt,t=>{t.inputType==="historyUndo"||t.inputType==="historyRedo"||t.inputType==="insertCompositionText"&&y(this,C,_t).call(this,St.FULL,null)});if(!(t instanceof HTMLElement))throw new TypeError("Invalid text editor element");a(this,p,t),a(this,W,n?.selectionImposterElement),a(this,dt,{blur:e(this,Gt),focus:e(this,Xt),paste:e(this,$t),cut:e(this,Yt),copy:e(this,zt),beforeinput:e(this,Ht),input:e(this,Kt)}),a(this,wt,n?.styleDefaults),y(this,C,dr).call(this,n)}get root(){return e(this,O)}set root(t){let n=e(this,O);a(this,O,t),n.replaceWith(t)}get element(){return e(this,p)}get isEmpty(){return e(this,O).children.length===1&&e(this,O).firstElementChild.children.length===1&&N(e(this,O).firstElementChild.firstElementChild.firstChild)}get numParagraphs(){return e(this,O).children.length}get currentStyle(){return e(this,m).currentStyle}focus(){return e(this,p).focus()}blur(){return e(this,p).blur()}createRoot(...t){return ie(...t)}createParagraph(...t){return X(...t)}createInlineFromString(t,n){return t===""?tt(n):D(new Text(t),n)}createInline(...t){return D(...t)}applyStylesToSelection(t){e(this,m).startMutation(),e(this,m).applyStyles(t);let n=e(this,m).endMutation();return y(this,C,_t).call(this,St.FULL,n),e(this,k).notifyImmediately(),this}selectAll(){return e(this,m).selectAll(),this}cursorToEnd(){return e(this,m).cursorToEnd(),this}dispose(){e(this,k).removeEventListener("change",e(this,Ot)),e(this,k).dispose(),a(this,k,null),e(this,m).removeEventListener("stylechange",e(this,At)),e(this,m).dispose(),a(this,m,null),fr(e(this,p),e(this,dt)),a(this,p,null),a(this,O,null)}};p=new WeakMap,dt=new WeakMap,O=new WeakMap,k=new WeakMap,m=new WeakMap,W=new WeakMap,wt=new WeakMap,C=new WeakSet,cr=function(){e(this,p).isContentEditable||(e(this,p).contentEditable="true",e(this,p).isContentEditable||e(this,p).setAttribute("contenteditable","true")),e(this,p).spellcheck&&(e(this,p).spellcheck=!1),e(this,p).autocapitalize&&(e(this,p).autocapitalize=!1),e(this,p).autofocus||(e(this,p).autofocus=!0),(!e(this,p).role||e(this,p).role!=="textbox")&&(e(this,p).role="textbox"),e(this,p).ariaAutoComplete&&(e(this,p).ariaAutoComplete=!1),e(this,p).ariaMultiLine||(e(this,p).ariaMultiLine=!0),e(this,p).dataset.itype="editor"},pr=function(){a(this,O,Le(e(this,wt))),e(this,p).appendChild(e(this,O))},Ot=new WeakMap,At=new WeakMap,dr=function(t){y(this,C,cr).call(this),y(this,C,pr).call(this),a(this,k,new Qe(this)),e(this,k).addEventListener("change",e(this,Ot)),a(this,m,new hr(this,document.getSelection(),t)),e(this,m).addEventListener("stylechange",e(this,At)),ur(e(this,p),e(this,dt),{capture:!0})},fe=function(){if(e(this,W)&&!e(this,m).isCollapsed){let t=e(this,m).range?.getClientRects();if(t){let n=e(this,W).getBoundingClientRect();e(this,W).replaceChildren(lr(n,t))}}},Gt=new WeakMap,Xt=new WeakMap,$t=new WeakMap,Yt=new WeakMap,zt=new WeakMap,Ht=new WeakMap,Kt=new WeakMap,_t=function(t=St.FULL,n){this.dispatchEvent(new CustomEvent("needslayout",{detail:{type:t,mutations:n}}))};function Dt(r){return r instanceof vt}function pn(r){return Dt(r)?r.root:null}function dn(r,i){return Dt(r)&&(r.root=i),r}function gn(r,i){return new vt(r,{...i})}function mn(r){if(Dt(r))return r.currentStyle}function yn(r,i){if(Dt(r))return r.applyStylesToSelection(i)}function xn(r){Dt(r)&&r.dispose()}var En=vt;export{vt as TextEditor,yn as applyStylesToSelection,gn as create,En as default,xn as dispose,mn as getCurrentStyle,pn as getRoot,Dt as isEditor,dn as setRoot}; +//# sourceMappingURL=text_editor_v2.js.map diff --git a/frontend/vendor/text_editor_v2.js.map b/frontend/vendor/text_editor_v2.js.map new file mode 100644 index 000000000..ae55f1c87 --- /dev/null +++ b/frontend/vendor/text_editor_v2.js.map @@ -0,0 +1,7 @@ +{ + "version": 3, + "sources": ["../editor/clipboard/copy.js", "../editor/clipboard/cut.js", "../editor/content/dom/Color.js", "../editor/content/dom/Style.js", "../editor/content/dom/Element.js", "../editor/content/dom/LineBreak.js", "../editor/content/dom/Inline.js", "../editor/content/dom/Root.js", "../editor/content/dom/TextNode.js", "../editor/content/dom/Paragraph.js", "../editor/content/dom/Content.js", "../editor/clipboard/paste.js", "../editor/clipboard/index.js", "../editor/commands/insertText.js", "../editor/commands/insertParagraph.js", "../editor/commands/deleteByCut.js", "../editor/commands/deleteContentBackward.js", "../editor/commands/deleteContentForward.js", "../editor/commands/index.js", "../editor/controllers/ChangeController.js", "../editor/content/Text.js", "../editor/content/dom/TextNodeIterator.js", "../editor/commands/CommandMutations.js", "../editor/controllers/SelectionDirection.js", "../editor/controllers/SafeGuard.js", "../editor/controllers/SelectionController.js", "../editor/selection/Imposter.js", "../editor/Event.js", "../editor/layout/LayoutType.js", "../editor/TextEditor.js"], + "sourcesContent": ["/**\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at http://mozilla.org/MPL/2.0/.\n *\n * Copyright (c) KALEIDOS INC\n */\n\n/**\n * This event is called when the user copies a text from the\n * editor.\n *\n * TODO: We could transform `--fills` in here to CSS `color`, `background-image`,\n * etc. to be more compatible with other applications.\n *\n * @param {ClipboardEvent} event\n * @param {TextEditor} editor\n */\nexport function copy(event, editor) {}\n", "/**\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at http://mozilla.org/MPL/2.0/.\n *\n * Copyright (c) KALEIDOS INC\n */\n\n/**\n * This event is called when the user copies a text from the\n * editor.\n *\n * TODO: We could transform `--fills` in here to CSS `color`, `background-image`,\n * etc. to be more compatible with other applications.\n *\n * @param {ClipboardEvent} event\n * @param {TextEditor} editor\n */\nexport function cut(event, editor) {}\n", "/**\n * Canvas used to retrieve colors as CSS hexadecimals.\n *\n * @type {OffscreenCanvas|HTMLCanvasElement}\n */\nlet canvas = null; // createCanvas(1, 1);\n\n/**\n * Context used to retrieve colors as CSS hexadecimals.\n *\n * @type {CanvasRenderingContext2D}\n */\nlet context = null; // canvas.getContext(\"2d\");\n\n/**\n * Returns the canvas context.\n *\n * @returns {CanvasRenderingContext2D}\n */\nfunction getContext() {\n if (!canvas) {\n canvas = createCanvas(1, 1);\n }\n if (!context) {\n context = canvas.getContext(\"2d\");\n }\n return context\n}\n\n/**\n * Creates a new canvas element.\n *\n * @param {number} width\n * @param {number} height\n * @returns {OffscreenCanvas|HTMLCanvasElement}\n */\nfunction createCanvas(width, height) {\n if (\"OffscreenCanvas\" in globalThis) {\n return new OffscreenCanvas(width, height);\n }\n return document.createElement(\"canvas\");\n}\n\n/**\n * Returns a byte representation as an hex.\n *\n * @param {number} byte\n * @returns {string}\n */\nexport function getByteAsHex(byte) {\n return byte.toString(16).padStart(2, \"0\");\n}\n\n/**\n * Returns a color definition from a fillStyle color.\n *\n * @param {string} fillStyle\n * @returns {[string, number]}\n */\nexport function getColor(fillStyle) {\n const context = getContext();\n context.fillStyle = fillStyle;\n context.fillRect(0, 0, 1, 1);\n const imageData = context.getImageData(0, 0, 1, 1);\n const [r, g, b, a] = imageData.data;\n return [`#${getByteAsHex(r)}${getByteAsHex(g)}${getByteAsHex(b)}`, a / 255.0];\n}\n\n/**\n * Returns a fill from a fillStyle color.\n *\n * @param {string} fillStyle\n * @returns {string}\n */\nexport function getFills(fillStyle) {\n const [color, opacity] = getColor(fillStyle);\n return `[[\"^ \",\"~:fill-color\",\"${color}\",\"~:fill-opacity\",${opacity}]]`;\n}\n", "/**\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at http://mozilla.org/MPL/2.0/.\n *\n * Copyright (c) KALEIDOS INC\n */\n\nimport { getFills } from \"./Color\";\n\n/**\n * Merges two style declarations. `source` -> `target`.\n *\n * @param {CSSStyleDeclaration} target\n * @param {CSSStyleDeclaration} source\n * @returns {CSSStyleDeclaration}\n */\nexport function mergeStyleDeclarations(target, source) {\n // This is better but it doesn't work in JSDOM\n // for (const styleName of source) {\n for (let index = 0; index < source.length; index++) {\n const styleName = source.item(index);\n target.setProperty(styleName, source.getPropertyValue(styleName));\n }\n return target\n}\n\n/**\n * Computes the styles of an element the same way `window.getComputedStyle` does.\n *\n * @param {Element} element\n * @returns {CSSStyleDeclaration}\n */\nexport function getComputedStyle(element) {\n const inertElement = document.createElement(\"div\");\n let currentElement = element;\n while (currentElement) {\n // This is better but it doesn't work in JSDOM.\n // for (const styleName of currentElement.style) {\n for (let index = 0; index < currentElement.style.length; index++) {\n const styleName = currentElement.style.item(index);\n const currentValue = inertElement.style.getPropertyValue(styleName);\n if (currentValue) {\n const priority = currentElement.style.getPropertyPriority(styleName);\n if (priority === \"important\") {\n const newValue = currentElement.style.getPropertyValue(styleName);\n inertElement.style.setProperty(styleName, newValue);\n }\n } else {\n inertElement.style.setProperty(\n styleName,\n currentElement.style.getPropertyValue(styleName)\n );\n }\n }\n currentElement = currentElement.parentElement;\n }\n return inertElement.style;\n}\n\n/**\n * Normalizes style declaration.\n *\n * TODO: I think that this also needs to remove some \"conflicting\"\n * CSS properties like `font-family` or some CSS variables.\n *\n * @param {CSSStyleDeclaration} styleDeclaration\n * @returns {CSSStyleDeclaration}\n */\nexport function normalizeStyles(styleDeclaration) {\n // If there's a color property, we should convert it to\n // a --fills CSS variable property.\n const color = styleDeclaration.getPropertyValue(\"color\");\n if (color) {\n styleDeclaration.removeProperty(\"color\");\n styleDeclaration.setProperty(\"--fills\", getFills(color));\n }\n // If there's a font-family property and not a --font-id, then\n // we remove the font-family because it will not work.\n const fontFamily = styleDeclaration.getPropertyValue(\"font-family\");\n const fontId = styleDeclaration.getPropertyPriority(\"--font-id\");\n if (fontFamily && !fontId) {\n styleDeclaration.removeProperty(\"font-family\");\n }\n return styleDeclaration\n}\n/**\n * Sets a single style property value of an element.\n *\n * @param {HTMLElement} element\n * @param {string} styleName\n * @param {*} styleValue\n * @param {string} [styleUnit]\n * @returns {HTMLElement}\n */\nexport function setStyle(element, styleName, styleValue, styleUnit) {\n if (\n styleName.startsWith(\"--\") &&\n typeof styleValue !== \"string\" &&\n typeof styleValue !== \"number\"\n ) {\n if (styleName === \"--fills\" && styleValue === null) debugger;\n element.style.setProperty(styleName, JSON.stringify(styleValue));\n } else {\n element.style.setProperty(styleName, styleValue + (styleUnit ?? \"\"));\n }\n return element;\n}\n\n/**\n * Returns the value of a style from a declaration.\n *\n * @param {CSSStyleDeclaration} style\n * @param {string} styleName\n * @param {string|undefined} [styleUnit]\n * @returns {*}\n */\nexport function getStyleFromDeclaration(style, styleName, styleUnit) {\n if (styleName.startsWith(\"--\")) {\n return style.getPropertyValue(styleName);\n }\n const styleValue = style.getPropertyValue(styleName);\n if (styleValue.endsWith(styleUnit)) {\n return styleValue.slice(0, -styleUnit.length);\n }\n return styleValue;\n}\n\n/**\n * Returns the value of a style.\n *\n * @param {HTMLElement} element\n * @param {string} styleName\n * @param {string|undefined} [styleUnit]\n * @returns {*}\n */\nexport function getStyle(element, styleName, styleUnit) {\n return getStyleFromDeclaration(element.style, styleName, styleUnit);\n}\n\n/**\n * Sets the styles of an element using an object and a list of\n * allowed styles.\n *\n * @param {HTMLElement} element\n * @param {Array<[string,?string]>} allowedStyles\n * @param {Object.} styleObject\n * @returns {HTMLElement}\n */\nexport function setStylesFromObject(element, allowedStyles, styleObject) {\n for (const [styleName, styleUnit] of allowedStyles) {\n if (!(styleName in styleObject)) {\n continue;\n }\n const styleValue = styleObject[styleName];\n if (styleValue) {\n setStyle(element, styleName, styleValue, styleUnit);\n }\n }\n return element;\n}\n\n/**\n * Sets the styles of an element using a CSS Style Declaration and a list\n * of allowed styles.\n *\n * @param {HTMLElement} element\n * @param {Array<[string,?string]>} allowedStyles\n * @param {CSSStyleDeclaration} styleDeclaration\n * @returns {HTMLElement}\n */\nexport function setStylesFromDeclaration(\n element,\n allowedStyles,\n styleDeclaration\n) {\n for (const [styleName, styleUnit] of allowedStyles) {\n const styleValue = getStyleFromDeclaration(styleDeclaration, styleName, styleUnit);\n if (styleValue) {\n setStyle(element, styleName, styleValue, styleUnit);\n }\n }\n return element;\n}\n\n/**\n * Sets the styles of an element using an Object or a CSS Style Declaration and\n * a list of allowed styles.\n *\n * @param {HTMLElement} element\n * @param {Array<[string,?string]} allowedStyles\n * @param {Object.|CSSStyleDeclaration} styleObjectOrDeclaration\n * @returns {HTMLElement}\n */\nexport function setStyles(element, allowedStyles, styleObjectOrDeclaration) {\n if (styleObjectOrDeclaration instanceof CSSStyleDeclaration) {\n return setStylesFromDeclaration(\n element,\n allowedStyles,\n styleObjectOrDeclaration\n );\n }\n return setStylesFromObject(element, allowedStyles, styleObjectOrDeclaration);\n}\n\n/**\n * Gets the styles of an element using a list of allowed styles.\n *\n * @param {HTMLElement} element\n * @param {Array<[string,?string]} allowedStyles\n * @returns {Object.}\n */\nexport function getStyles(element, allowedStyles) {\n const styleObject = {};\n for (const [styleName, styleUnit] of allowedStyles) {\n const styleValue = getStyle(element, styleName, styleUnit);\n if (styleValue) {\n styleObject[styleName] = styleValue;\n }\n }\n return styleObject;\n}\n\n/**\n * Returns a series of merged styles.\n *\n * @param {Array<[string,?string]} allowedStyles\n * @param {CSSStyleDeclaration} styleDeclaration\n * @param {Object.} newStyles\n * @returns {Object.}\n */\nexport function mergeStyles(allowedStyles, styleDeclaration, newStyles) {\n const mergedStyles = {};\n for (const [styleName, styleUnit] of allowedStyles) {\n if (styleName in newStyles) {\n mergedStyles[styleName] = newStyles[styleName];\n } else {\n mergedStyles[styleName] = getStyleFromDeclaration(styleDeclaration, styleName, styleUnit);\n }\n }\n return mergedStyles;\n}\n\n/**\n * Returns true if the specified style declaration has a display block.\n *\n * @param {CSSStyleDeclaration} style\n * @returns {boolean}\n */\nexport function isDisplayBlock(style) {\n return style.display === \"block\";\n}\n\n/**\n * Returns true if the specified style declaration has a display inline\n * or inline-block.\n *\n * @param {CSSStyleDeclaration} style\n * @returns {boolean}\n */\nexport function isDisplayInline(style) {\n return style.display === \"inline\" || style.display === \"inline-block\";\n}\n", "/**\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at http://mozilla.org/MPL/2.0/.\n *\n * Copyright (c) KALEIDOS INC\n */\n\nimport { setStyles } from \"./Style\";\n\n/**\n * @typedef {Object} CreateElementOptions\n * @property {Object.} [attributes]\n * @property {Object.} [data]\n * @property {Object.|CSSStyleDeclaration} [styles]\n * @property {Array<[string,?string]>} [allowedStyles]\n * @property {Array|Node} [children]\n */\n\n/**\n * Creates a new random id to identify content nodes.\n *\n * @returns {string}\n */\nexport function createRandomId() {\n return Math.floor(Math.random() * Number.MAX_SAFE_INTEGER).toString(36);\n}\n\n/**\n * Creates a new HTML element.\n *\n * @param {string} tag\n * @param {*} options\n * @returns {HTMLElement}\n */\nexport function createElement(tag, options) {\n const element = document.createElement(tag);\n if (options?.attributes) {\n Object.entries(options.attributes).forEach(([name, value]) =>\n element.setAttribute(name, value)\n );\n }\n if (options?.data) {\n Object.entries(options.data).forEach(\n ([name, value]) => (element.dataset[name] = value)\n );\n }\n if (options?.styles && options?.allowedStyles) {\n setStyles(element, options.allowedStyles, options.styles);\n }\n if (options?.children) {\n if (Array.isArray(options.children)) {\n element.append(...options.children);\n } else {\n element.appendChild(options.children);\n }\n }\n return element;\n}\n\n/**\n * Returns true if passed node is an element.\n *\n * @param {Node} element\n * @param {string} nodeName\n * @returns {boolean}\n */\nexport function isElement(element, nodeName) {\n return (\n element.nodeType === Node.ELEMENT_NODE &&\n element.nodeName === nodeName.toUpperCase()\n );\n}\n\n/**\n * Returns true if the specified offset is at the start of the element.\n *\n * @param {Node} node\n * @param {number} offset\n * @returns {boolean}\n */\nexport function isOffsetAtStart(node, offset) {\n return offset === 0;\n}\n\n/**\n * Returns true if the specified offset is at the end of the element.\n *\n * @param {Node} node\n * @param {number} offset\n * @returns {boolean}\n */\nexport function isOffsetAtEnd(node, offset) {\n if (node.nodeType === Node.TEXT_NODE) {\n return node.nodeValue.length === offset;\n }\n return true;\n}\n", "/**\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at http://mozilla.org/MPL/2.0/.\n *\n * Copyright (c) KALEIDOS INC\n */\n\nexport const TAG = \"BR\";\n\n/**\n * Creates a new line break.\n *\n * @returns {HTMLBRElement}\n */\nexport function createLineBreak() {\n return document.createElement(TAG);\n}\n\n/**\n * Returns true if the passed node is a line break.\n *\n * @param {Node} node\n * @returns {boolean}\n */\nexport function isLineBreak(node) {\n return node.nodeType === Node.ELEMENT_NODE && node.nodeName === TAG;\n}\n", "/**\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at http://mozilla.org/MPL/2.0/.\n *\n * Copyright (c) KALEIDOS INC\n */\n\nimport {\n createElement,\n isElement,\n isOffsetAtStart,\n isOffsetAtEnd,\n} from \"./Element\";\nimport { createLineBreak, isLineBreak } from \"./LineBreak\";\nimport { setStyles, mergeStyles } from \"./Style\";\nimport { createRandomId } from \"./Element\";\n\nexport const TAG = \"SPAN\";\nexport const TYPE = \"inline\";\nexport const QUERY = `[data-itype=\"${TYPE}\"]`;\nexport const STYLES = [\n [\"--typography-ref-id\"],\n [\"--typography-ref-file\"],\n [\"--font-id\"],\n [\"--font-variant-id\"],\n [\"--fills\"],\n [\"font-variant\"],\n [\"font-family\"],\n [\"font-size\", \"px\"],\n [\"font-weight\"],\n [\"font-style\"],\n [\"line-height\"],\n [\"letter-spacing\", \"px\"],\n [\"text-decoration\"],\n [\"text-transform\"],\n];\n\n/**\n * Returns true if passed node is an inline.\n *\n * @param {Node} node\n * @returns {boolean}\n */\nexport function isInline(node) {\n if (!node) return false;\n if (!isElement(node, TAG)) return false;\n if (node.dataset.itype !== TYPE) return false;\n return true;\n}\n\n/**\n * Returns true if the passed node \"behaves\" like an\n * inline.\n *\n * @param {Node} element\n * @returns {boolean}\n */\nexport function isLikeInline(element) {\n return element\n ? [\n \"A\",\n \"ABBR\",\n \"ACRONYM\",\n \"B\",\n \"BDO\",\n \"BIG\",\n \"BR\",\n \"BUTTON\",\n \"CITE\",\n \"CODE\",\n \"DFN\",\n \"EM\",\n \"I\",\n \"IMG\",\n \"INPUT\",\n \"KBD\",\n \"LABEL\",\n \"MAP\",\n \"OBJECT\",\n \"OUTPUT\",\n \"Q\",\n \"SAMP\",\n \"SCRIPT\",\n \"SELECT\",\n \"SMALL\",\n \"SPAN\",\n \"STRONG\",\n \"SUB\",\n \"SUP\",\n \"TEXTAREA\",\n \"TIME\",\n \"TT\",\n \"VAR\",\n ].includes(element.nodeName)\n : false;\n}\n\n/**\n * Creates a new Inline\n *\n * @param {Text|HTMLBRElement} text\n * @param {Object.|CSSStyleDeclaration} styles\n * @param {Object.} [attrs]\n * @returns {HTMLSpanElement}\n */\nexport function createInline(textOrLineBreak, styles, attrs) {\n if (\n !(textOrLineBreak instanceof HTMLBRElement) &&\n !(textOrLineBreak instanceof Text)\n ) {\n throw new TypeError(\"Invalid inline child\");\n }\n if (textOrLineBreak instanceof Text\n && textOrLineBreak.nodeValue.length === 0) {\n console.trace(\"nodeValue\", textOrLineBreak.nodeValue)\n throw new TypeError(\"Invalid inline child, cannot be an empty text\");\n }\n return createElement(TAG, {\n attributes: { id: createRandomId(), ...attrs },\n data: { itype: TYPE },\n styles: styles,\n allowedStyles: STYLES,\n children: textOrLineBreak,\n });\n}\n\n/**\n * Creates a new inline from an older inline. This only\n * merges styles from the older inline to the new inline.\n *\n * @param {HTMLSpanElement} inline\n * @param {Object.} textOrLineBreak\n * @param {Object.|CSSStyleDeclaration} styles\n * @param {Object.} [attrs]\n * @returns {HTMLSpanElement}\n */\nexport function createInlineFrom(inline, textOrLineBreak, styles, attrs) {\n return createInline(\n textOrLineBreak,\n mergeStyles(STYLES, inline.style, styles),\n attrs\n );\n}\n\n/**\n * Creates a new empty inline.\n *\n * @param {Object.|CSSStyleDeclaration} styles\n * @returns {HTMLSpanElement}\n */\nexport function createEmptyInline(styles) {\n return createInline(createLineBreak(), styles);\n}\n\n/**\n * Sets the inline styles.\n *\n * @param {HTMLSpanElement} element\n * @param {Object.|CSSStyleDeclaration} styles\n * @returns {HTMLSpanElement}\n */\nexport function setInlineStyles(element, styles) {\n return setStyles(element, STYLES, styles);\n}\n\n/**\n * Gets the closest inline from a node.\n *\n * @param {Node} node\n * @returns {HTMLElement|null}\n */\nexport function getInline(node) {\n if (!node) return null; // FIXME: Should throw?\n if (isInline(node)) return node;\n if (node.nodeType === Node.TEXT_NODE) {\n const inline = node?.parentElement;\n if (!inline) return null;\n if (!isInline(inline)) return null;\n return inline;\n }\n return node.closest(QUERY);\n}\n\n/**\n * Returns true if we are at the start offset\n * of an inline.\n *\n * NOTE: Only the first inline returns this as true\n *\n * @param {TextNode|HTMLBRElement} node\n * @param {number} offset\n * @returns {boolean}\n */\nexport function isInlineStart(node, offset) {\n const inline = getInline(node);\n if (!inline) return false;\n return isOffsetAtStart(inline, offset);\n}\n\n/**\n * Returns true if we are at the end offset\n * of an inline.\n *\n * @param {TextNode|HTMLBRElement} node\n * @param {number} offset\n * @returns {boolean}\n */\nexport function isInlineEnd(node, offset) {\n const inline = getInline(node);\n if (!inline) return false;\n return isOffsetAtEnd(inline.firstChild, offset);\n}\n\n/**\n * Splits an inline.\n *\n * @param {HTMLSpanElement} inline\n * @param {number} offset\n */\nexport function splitInline(inline, offset) {\n const textNode = inline.firstChild;\n const style = inline.style;\n const newTextNode = textNode.splitText(offset);\n return createInline(newTextNode, style);\n}\n\n/**\n * Returns all the inlines of a paragraph starting at\n * the specified inline.\n *\n * @param {HTMLSpanElement} startInline\n * @returns {Array}\n */\nexport function getInlinesFrom(startInline) {\n const inlines = [];\n let currentInline = startInline;\n let index = 0;\n while (currentInline) {\n if (index > 0) inlines.push(currentInline);\n currentInline = currentInline.nextElementSibling;\n index++;\n }\n return inlines;\n}\n\n/**\n * Returns the length of an inline.\n *\n * @param {HTMLElement} inline\n * @returns {number}\n */\nexport function getInlineLength(inline) {\n if (!isInline(inline)) throw new Error(\"Invalid inline\");\n if (isLineBreak(inline.firstChild)) return 0;\n return inline.firstChild.nodeValue.length;\n}\n\n/**\n * Merges two inlines.\n *\n * @param {HTMLSpanElement} a\n * @param {HTMLSpanElement} b\n * @returns {HTMLSpanElement}\n */\nexport function mergeInlines(a, b) {\n a.append(...b.childNodes);\n b.remove();\n // We need to normalize Text nodes.\n a.normalize();\n return a;\n}\n", "/**\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at http://mozilla.org/MPL/2.0/.\n *\n * Copyright (c) KALEIDOS INC\n */\n\nimport { createElement, isElement } from \"./Element\";\nimport { createEmptyParagraph, isParagraph } from \"./Paragraph\";\nimport { setStyles } from \"./Style\";\nimport { createRandomId } from \"./Element\";\n\nexport const TAG = \"DIV\";\nexport const TYPE = \"root\";\nexport const QUERY = `[data-itype=\"${TYPE}\"]`;\nexport const STYLES = [[\"--vertical-align\"]];\n\n/**\n * Returns true if passed node is a root.\n *\n * @param {Node} node\n * @returns {boolean}\n */\nexport function isRoot(node) {\n if (!node) return false;\n if (!isElement(node, TAG)) return false;\n if (node.dataset.itype !== TYPE) return false;\n return true;\n}\n\n/**\n * Create a new root element\n *\n * @param {Array} paragraphs\n * @param {Object.|CSSStyleDeclaration} styles,\n * @param {Object.} [attrs]\n * @returns {HTMLDivElement}\n */\nexport function createRoot(paragraphs, styles, attrs) {\n if (!Array.isArray(paragraphs) || !paragraphs.every(isParagraph))\n throw new TypeError(\"Invalid root children\");\n\n return createElement(TAG, {\n attributes: { id: createRandomId(), ...attrs },\n data: { itype: TYPE },\n styles: styles,\n allowedStyles: STYLES,\n children: paragraphs,\n });\n}\n\n/**\n * Creates a new empty root element\n *\n * @param {Object.|CSSStyleDeclaration} styles\n */\nexport function createEmptyRoot(styles) {\n return createRoot([createEmptyParagraph(styles)], styles);\n}\n\n/**\n * Sets the root styles.\n *\n * @param {HTMLDivElement} element\n * @param {Object.|CSSStyleDeclaration} styles\n * @returns {HTMLDivElement}\n */\nexport function setRootStyles(element, styles) {\n return setStyles(element, STYLES, styles);\n}\n", "/**\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at http://mozilla.org/MPL/2.0/.\n *\n * Copyright (c) KALEIDOS INC\n */\n\nimport { isInline } from \"./Inline\";\nimport { isLineBreak } from \"./LineBreak\";\nimport { isParagraph } from \"./Paragraph\";\nimport { isRoot } from \"./Root\";\n\n/**\n * Returns true if the node is \"like\"\n * text, this means that it is a Text\n * node or a
element.\n *\n * @param {Node} node\n * @returns {boolean}\n */\nexport function isTextNode(node) {\n if (!node) throw new TypeError(\"Invalid text node\");\n return node.nodeType === Node.TEXT_NODE\n || isLineBreak(node);\n}\n\n/**\n * Returns true if the text node is empty.\n *\n * @param {Node} node\n * @returns {boolean}\n */\nexport function isEmptyTextNode(node) {\n return node.nodeType === Node.TEXT_NODE\n && node.nodeValue === \"\";\n}\n\n/**\n * Returns the content length of the\n * node.\n *\n * @param {Node} node\n * @returns {number}\n */\nexport function getTextNodeLength(node) {\n if (!node) throw new TypeError(\"Invalid text node\");\n if (isLineBreak(node)) return 0;\n return node.nodeValue.length;\n}\n\n/**\n * Gets the closest text node.\n *\n * @param {Node} node\n * @returns {Node}\n */\nexport function getClosestTextNode(node) {\n if (isTextNode(node)) return node;\n if (isInline(node)) return node.firstChild;\n if (isParagraph(node)) return node.firstChild.firstChild;\n if (isRoot(node)) return node.firstChild.firstChild.firstChild;\n throw new Error(\"Cannot find a text node\");\n}\n", "/**\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at http://mozilla.org/MPL/2.0/.\n *\n * Copyright (c) KALEIDOS INC\n */\n\nimport {\n createElement,\n isElement,\n isOffsetAtStart,\n isOffsetAtEnd,\n} from \"./Element\";\nimport {\n isInline,\n isLikeInline,\n getInline,\n getInlinesFrom,\n createEmptyInline,\n isInlineEnd,\n splitInline,\n} from \"./Inline\";\nimport { isLineBreak } from \"./LineBreak\";\nimport { setStyles } from \"./Style\";\nimport { createRandomId } from \"./Element\";\nimport { isEmptyTextNode, isTextNode } from './TextNode';\n\nexport const TAG = \"DIV\";\nexport const TYPE = \"paragraph\";\nexport const QUERY = `[data-itype=\"${TYPE}\"]`;\nexport const STYLES = [\n [\"--typography-ref-id\"],\n [\"--typography-ref-file\"],\n [\"--font-id\"],\n [\"--font-variant-id\"],\n [\"--fills\"],\n [\"font-variant\"],\n [\"font-family\"],\n [\"font-size\", \"px\"],\n [\"font-weight\"],\n [\"font-style\"],\n [\"line-height\"],\n [\"letter-spacing\", \"px\"],\n [\"text-decoration\"],\n [\"text-transform\"],\n [\"text-align\"],\n [\"direction\"]\n];\n\n/**\n * Returns true if the passed node behaves like a paragraph.\n *\n * NOTE: This is mainly used in paste operations. Every element node\n * it's going to be treated as paragraph it\n *\n * @param {Node} element\n * @returns {boolean}\n */\nexport function isLikeParagraph(element) {\n return !isLikeInline(element);\n}\n\n/**\n * Returns true if we have an empty paragraph.\n *\n * @param {Node} element\n * @returns {boolean}\n */\nexport function isEmptyParagraph(element) {\n if (!isParagraph(element)) throw new TypeError(\"Invalid paragraph\");\n const inline = element.firstChild;\n if (!isInline(inline)) throw new TypeError(\"Invalid inline\");\n return isLineBreak(inline.firstChild);\n}\n\n/**\n * Returns true if passed node is a paragraph.\n *\n * @param {Node} node\n * @returns {boolean}\n */\nexport function isParagraph(node) {\n if (!node) return false;\n if (!isElement(node, TAG)) return false;\n if (node.dataset.itype !== TYPE) return false;\n return true;\n}\n\n/**\n * Creates a new paragraph.\n *\n * @param {Array} inlines\n * @param {Object.|CSSStyleDeclaration} styles\n * @param {Object.} [attrs]\n * @returns {HTMLDivElement}\n */\nexport function createParagraph(inlines, styles, attrs) {\n if (inlines && (!Array.isArray(inlines) || !inlines.every(isInline)))\n throw new TypeError(\"Invalid paragraph children\");\n return createElement(TAG, {\n attributes: { id: createRandomId(), ...attrs },\n data: { itype: TYPE },\n styles: styles,\n allowedStyles: STYLES,\n children: inlines,\n });\n}\n\n/**\n * Returns a new empty paragraph\n *\n * @param {Object.} styles\n * @returns {HTMLDivElement}\n */\nexport function createEmptyParagraph(styles) {\n return createParagraph([\n createEmptyInline(styles)\n ], styles);\n}\n\n/**\n * Sets the paragraph styles.\n *\n * @param {HTMLDivElement} element\n * @param {Object.|CSSStyleDeclaration} styles\n * @returns {HTMLDivElement}\n */\nexport function setParagraphStyles(element, styles) {\n return setStyles(element, STYLES, styles);\n}\n\n/**\n * Gets the closest paragraph from a node.\n *\n * @param {Text|HTMLBRElement} node\n * @returns {HTMLElement|null}\n */\nexport function getParagraph(node) {\n if (!node) return null;\n if (isParagraph(node)) return node;\n if (node.nodeType === Node.TEXT_NODE\n || isLineBreak(node)) {\n const paragraph = node?.parentElement?.parentElement;\n if (!paragraph) {\n return null;\n }\n if (!isParagraph(paragraph)) {\n return null;\n }\n return paragraph;\n }\n return node.closest(QUERY);\n}\n\n/**\n * Returns if the specified node and offset represents\n * the start of the paragraph.\n *\n * @param {Text|HTMLBRElement} node\n * @param {number} offset\n * @returns {boolean}\n */\nexport function isParagraphStart(node, offset) {\n const paragraph = getParagraph(node);\n if (!paragraph) throw new Error(\"Can't find the paragraph\");\n const inline = getInline(node);\n if (!inline) throw new Error(\"Can't find the inline\");\n return (\n paragraph.firstElementChild === inline &&\n isOffsetAtStart(inline.firstChild, offset)\n );\n}\n\n/**\n * Returns if the specified node and offset represents\n * the end of the paragraph.\n *\n * @param {Text|HTMLBRElement} node\n * @param {number} offset\n * @returns {boolean}\n */\nexport function isParagraphEnd(node, offset) {\n const paragraph = getParagraph(node);\n if (!paragraph) throw new Error(\"Cannot find the paragraph\");\n const inline = getInline(node);\n if (!inline) throw new Error(\"Cannot find the inline\");\n return (\n paragraph.lastElementChild === inline &&\n isOffsetAtEnd(inline.firstChild, offset)\n );\n}\n\n/**\n * Splits a paragraph.\n *\n * @param {HTMLDivElement} paragraph\n * @param {HTMLSpanElement} inline\n * @param {number} offset\n */\nexport function splitParagraph(paragraph, inline, offset) {\n const style = paragraph.style;\n if (isInlineEnd(inline, offset)) {\n const newParagraph = createParagraph(getInlinesFrom(inline), style);\n return newParagraph;\n }\n const newInline = splitInline(inline, offset);\n const newParagraph = createParagraph([newInline], style);\n return newParagraph;\n}\n\n/**\n * Splits a paragraph at a specified child node index\n *\n * @param {HTMLDivElement} paragraph\n * @param {number} startIndex\n */\nexport function splitParagraphAtNode(paragraph, startIndex) {\n const style = paragraph.style;\n const newParagraph = createParagraph(null, style);\n const newInlines = [];\n for (let index = startIndex; index < paragraph.children.length; index++) {\n newInlines.push(paragraph.children.item(index));\n }\n newParagraph.append(...newInlines);\n return newParagraph;\n}\n\n/**\n * Merges two paragraphs.\n *\n * @param {HTMLDivElement} a\n * @param {HTMLDivElement} b\n * @returns {HTMLDivElement}\n */\nexport function mergeParagraphs(a, b) {\n a.append(...b.children);\n b.remove();\n return a;\n}\n", "/**\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at http://mozilla.org/MPL/2.0/.\n *\n * Copyright (c) KALEIDOS INC\n */\n\nimport { createInline } from \"./Inline\";\nimport {\n createEmptyParagraph,\n createParagraph,\n isLikeParagraph,\n} from \"./Paragraph\";\nimport { isDisplayBlock, normalizeStyles, getComputedStyle, mergeStyleDeclarations } from \"./Style\";\n\n/**\n * Maps any HTML into a valid content DOM element.\n *\n * @param {Document} document\n * @param {HTMLElement} root\n * @param {CSSStyleDeclaration} [styleDefaults]\n * @returns {DocumentFragment}\n */\nexport function mapContentFragmentFromDocument(document, root, styleDefaults) {\n const nodeIterator = document.createNodeIterator(root, NodeFilter.SHOW_TEXT);\n const fragment = document.createDocumentFragment();\n\n let currentParagraph = null;\n let currentNode = nodeIterator.nextNode();\n while (currentNode) {\n // We cannot call document.defaultView because it is `null`.\n const parentStyle = normalizeStyles(mergeStyleDeclarations(styleDefaults, getComputedStyle(currentNode.parentElement)));\n if (\n isDisplayBlock(currentNode.parentElement.style) ||\n isDisplayBlock(parentStyle) ||\n isLikeParagraph(currentNode.parentElement)\n ) {\n if (currentParagraph) {\n fragment.appendChild(currentParagraph);\n }\n currentParagraph = createParagraph(undefined, parentStyle);\n } else {\n if (currentParagraph === null) {\n currentParagraph = createParagraph();\n }\n }\n\n currentParagraph.appendChild(\n createInline(new Text(currentNode.nodeValue), parentStyle)\n );\n\n currentNode = nodeIterator.nextNode();\n }\n\n fragment.appendChild(currentParagraph);\n return fragment;\n}\n\n/**\n * Maps any HTML into a valid content DOM element.\n *\n * @param {string} html\n * @param {CSSStyleDeclaration} [styleDefaults]\n * @returns {DocumentFragment}\n */\nexport function mapContentFragmentFromHTML(html, styleDefaults) {\n const parser = new DOMParser();\n const htmlDocument = parser.parseFromString(html, \"text/html\");\n return mapContentFragmentFromDocument(\n htmlDocument,\n htmlDocument.documentElement,\n styleDefaults\n );\n}\n\n/**\n * Maps a plain text into a valid content DOM element.\n *\n * @param {string} string\n * @param {CSSStyleDeclaration} [styleDefaults]\n * @returns {DocumentFragment}\n */\nexport function mapContentFragmentFromString(string, styleDefaults) {\n const lines = string.replace(/\\r/g, \"\").split(\"\\n\");\n const fragment = document.createDocumentFragment();\n for (const line of lines) {\n if (line === \"\") {\n fragment.appendChild(createEmptyParagraph(styleDefaults));\n } else {\n fragment.appendChild(createParagraph([createInline(new Text(line), styleDefaults)], styleDefaults));\n }\n }\n return fragment;\n}\n", "/**\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at http://mozilla.org/MPL/2.0/.\n *\n * Copyright (c) KALEIDOS INC\n */\n\nimport { mapContentFragmentFromHTML, mapContentFragmentFromString } from '~/editor/content/dom/Content';\n\n/**\n * When the user pastes some HTML, what we do is generate\n * a new DOM based on what the user pasted and then we\n * insert it in the appropiate part (see `insertFromPaste` command).\n *\n * @param {ClipboardEvent} event\n * @param {TextEditor} editor\n * @param {SelectionController} selectionController\n * @returns {void}\n */\nexport function paste(event, editor, selectionController) {\n // We need to prevent default behavior\n // because we don't allow any HTML to\n // be pasted.\n event.preventDefault();\n\n let fragment = null;\n if (event.clipboardData.types.includes(\"text/html\")) {\n const html = event.clipboardData.getData(\"text/html\");\n fragment = mapContentFragmentFromHTML(html, selectionController.currentStyle);\n } else if (event.clipboardData.types.includes(\"text/plain\")) {\n const plain = event.clipboardData.getData(\"text/plain\");\n fragment = mapContentFragmentFromString(plain, selectionController.currentStyle);\n }\n\n if (!fragment) {\n return;\n }\n\n if (selectionController.isCollapsed) {\n selectionController.insertPaste(fragment);\n } else {\n selectionController.replaceWithPaste(fragment);\n }\n}\n", "/**\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at http://mozilla.org/MPL/2.0/.\n *\n * Copyright (c) KALEIDOS INC\n */\n\nimport { copy } from \"./copy.js\";\nimport { cut } from \"./cut.js\";\nimport { paste } from \"./paste.js\";\n\nexport default {\n copy,\n cut,\n paste,\n};\n", "/**\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at http://mozilla.org/MPL/2.0/.\n *\n * Copyright (c) KALEIDOS INC\n */\n\n/**\n * Insert typed plain text\n *\n * @see https://w3c.github.io/input-events/#interface-InputEvent\n * @param {InputEvent} event\n * @param {TextEditor} editor\n * @param {SelectionController} selectionController\n */\nexport function insertText(event, editor, selectionController) {\n event.preventDefault();\n if (selectionController.isCollapsed) {\n if (selectionController.isTextFocus) {\n return selectionController.insertText(event.data);\n } else if (selectionController.isLineBreakFocus) {\n return selectionController.replaceLineBreak(event.data);\n }\n } else {\n if (selectionController.isMultiParagraph) {\n return selectionController.replaceParagraphs(event.data);\n } else if (selectionController.isMultiInline) {\n return selectionController.replaceInlines(event.data);\n } else if (selectionController.isTextSame) {\n return selectionController.replaceText(event.data);\n }\n }\n}\n", "/**\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at http://mozilla.org/MPL/2.0/.\n *\n * Copyright (c) KALEIDOS INC\n */\n\n/**\n * Insert a paragraph\n *\n * @see https://w3c.github.io/input-events/#interface-InputEvent\n * @param {InputEvent} event\n * @param {TextEditor} editor\n * @param {SelectionController} selectionController\n */\nexport function insertParagraph(event, editor, selectionController) {\n event.preventDefault();\n if (selectionController.isCollapsed) {\n return selectionController.insertParagraph();\n }\n return selectionController.replaceWithParagraph();\n}\n", "/**\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at http://mozilla.org/MPL/2.0/.\n *\n * Copyright (c) KALEIDOS INC\n */\n\n/**\n * Remove the current selection as part of a cut.\n *\n * @param {InputEvent} event\n * @param {TextEditor} editor\n * @param {SelectionController} selectionController\n */\nexport function deleteByCut(event, editor, selectionController) {\n event.preventDefault();\n if (selectionController.isCollapsed) {\n throw new Error(\"This should be impossible\");\n }\n return selectionController.removeSelected();\n}\n", "/**\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at http://mozilla.org/MPL/2.0/.\n *\n * Copyright (c) KALEIDOS INC\n */\n\n/**\n * delete the content directly before the caret position and this intention is\n * not covered by another `inputType` or delete the selection with the\n * selection collapsing to its start after the deletion.\n *\n * @param {InputEvent} event\n * @param {TextEditor} editor\n * @param {SelectionController} selectionController\n */\nexport function deleteContentBackward(event, editor, selectionController) {\n event.preventDefault();\n // If the editor is empty this is a no op.\n if (editor.isEmpty) return;\n\n // If not is collapsed AKA is a selection, then\n // we removeSelected.\n if (!selectionController.isCollapsed) {\n return selectionController.removeSelected({ direction: 'backward' });\n }\n\n // If we're in a text node and the offset is\n // greater than 0 (not at the start of the inline)\n // we simple remove a character from the text.\n if (selectionController.isTextFocus && selectionController.focusOffset > 0) {\n return selectionController.removeBackwardText();\n\n // If we're in a text node but we're at the end of the\n // paragraph, we should merge the current paragraph\n // with the following paragraph.\n } else if (\n selectionController.isTextFocus &&\n selectionController.focusAtStart\n ) {\n return selectionController.mergeBackwardParagraph();\n\n // If we're at an inline or a line break paragraph\n // and there's more than one paragraph, then we should\n // remove the next paragraph.\n } else if (\n selectionController.isInlineFocus ||\n selectionController.isLineBreakFocus\n ) {\n return selectionController.removeBackwardParagraph();\n }\n}\n", "/**\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at http://mozilla.org/MPL/2.0/.\n *\n * Copyright (c) KALEIDOS INC\n */\n\n/**\n * delete the content directly after the caret position and this intention is not covered by\n * another inputType or delete the selection with the selection collapsing to its end after the deletion\n *\n * @param {InputEvent} event\n * @param {TextEditor} editor\n * @param {SelectionController} selectionController\n */\nexport function deleteContentForward(event, editor, selectionController) {\n event.preventDefault();\n // If the editor is empty this is a no op.\n if (editor.isEmpty) return;\n\n // If not is collapsed AKA is a selection, then\n // we removeSelected.\n if (!selectionController.isCollapsed) {\n return selectionController.removeSelected({ direction: \"forward\" });\n }\n\n // If we're in a text node and the offset is\n // greater than 0 (not at the start of the inline)\n // we simple remove a character from the text.\n if (\n selectionController.isTextFocus &&\n selectionController.focusOffset >= 0\n ) {\n return selectionController.removeForwardText();\n\n // If we're in a text node but we're at the end of the\n // paragraph, we should merge the current paragraph\n // with the following paragraph.\n } else if (\n selectionController.isTextFocus &&\n selectionController.focusAtEnd\n ) {\n return selectionController.mergeForwardParagraph();\n\n // If we're at an inline or a line break paragraph\n // and there's more than one paragraph, then we should\n // remove the next paragraph.\n } else if (\n (selectionController.isInlineFocus ||\n selectionController.isLineBreakFocus) &&\n editor.numParagraphs > 1\n ) {\n return selectionController.removeForwardParagraph();\n }\n}\n", "/**\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at http://mozilla.org/MPL/2.0/.\n *\n * Copyright (c) KALEIDOS INC\n */\n\nimport { insertText } from \"./insertText.js\";\nimport { insertParagraph } from \"./insertParagraph.js\";\nimport { deleteByCut } from \"./deleteByCut.js\";\nimport { deleteContentBackward } from \"./deleteContentBackward.js\";\nimport { deleteContentForward } from \"./deleteContentForward.js\";\n\nexport default {\n insertText,\n insertParagraph,\n deleteByCut,\n deleteContentBackward,\n deleteContentForward,\n};\n", "/**\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at http://mozilla.org/MPL/2.0/.\n *\n * Copyright (c) KALEIDOS INC\n */\n\n/**\n * Change controller is responsible of notifying when a change happens.\n */\nexport class ChangeController extends EventTarget {\n /**\n * Keeps the timeout id.\n *\n * @type {number}\n */\n #timeout = null;\n\n /**\n * Keeps the time at which we're going to\n * call the debounced change calls.\n *\n * @type {number}\n */\n #time = 1000;\n\n /**\n * Keeps if we have some pending changes or not.\n *\n * @type {boolean}\n */\n #hasPendingChanges = false;\n\n /**\n * Constructor\n *\n * @param {number} [time=500]\n */\n constructor(time = 500) {\n super()\n if (typeof time === \"number\" && (!Number.isInteger(time) || time <= 0)) {\n throw new TypeError(\"Invalid time\");\n }\n this.#time = time ?? 500;\n }\n\n /**\n * Indicates that there are some pending changes.\n *\n * @type {boolean}\n */\n get hasPendingChanges() {\n return this.#hasPendingChanges;\n }\n\n #onTimeout = () => {\n this.dispatchEvent(new Event(\"change\"));\n };\n\n /**\n * Tells the ChangeController that a change has been made\n * but that you need to delay the notification (and debounce)\n * for sometime.\n */\n notifyDebounced() {\n this.#hasPendingChanges = true;\n clearTimeout(this.#timeout);\n this.#timeout = setTimeout(this.#onTimeout, this.#time);\n }\n\n /**\n * Tells the ChangeController that a change should be notified\n * immediately.\n */\n notifyImmediately() {\n clearTimeout(this.#timeout);\n this.#onTimeout();\n }\n\n /**\n * Disposes the referenced resources.\n */\n dispose() {\n if (this.hasPendingChanges) {\n this.notifyImmediately();\n }\n clearTimeout(this.#timeout);\n }\n}\n\nexport default ChangeController;\n", "/**\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at http://mozilla.org/MPL/2.0/.\n *\n * Copyright (c) KALEIDOS INC\n */\n\n/**\n * Throws if the passed value is not a valid offset value.\n *\n * @param {*} offset\n * @throws {TypeError}\n */\nfunction tryOffset(offset) {\n if (!Number.isInteger(offset) || offset < 0)\n throw new TypeError(\"Invalid offset\");\n}\n\n/**\n * Throws if the passed value is not a valid string.\n *\n * @param {*} str\n * @throws {TypeError}\n */\nfunction tryString(str) {\n if (typeof str !== \"string\") throw new TypeError(\"Invalid string\");\n}\n\n/**\n * Inserts string into a string.\n *\n * @param {string} str\n * @param {number} offset\n * @param {string} text\n * @returns {string}\n */\nexport function insertInto(str, offset, text) {\n tryString(str);\n tryOffset(offset);\n tryString(text);\n return str.slice(0, offset) + text + str.slice(offset);\n}\n\n/**\n * Replaces a part of a string with a string.\n *\n * @param {string} str\n * @param {number} startOffset\n * @param {number} endOffset\n * @param {string} text\n * @returns {string}\n */\nexport function replaceWith(str, startOffset, endOffset, text) {\n tryString(str);\n tryOffset(startOffset);\n tryOffset(endOffset);\n tryString(text);\n return str.slice(0, startOffset) + text + str.slice(endOffset);\n}\n\n/**\n * Removes text backward from specified offset.\n *\n * @param {string} str\n * @param {number} offset\n * @returns {string}\n */\nexport function removeBackward(str, offset) {\n tryString(str);\n tryOffset(offset);\n if (offset === 0) {\n return str;\n }\n return str.slice(0, offset - 1) + str.slice(offset);\n}\n\n/**\n * Removes text forward from specified offset.\n *\n * @param {string} str\n * @param {number} offset\n * @returns {string}\n */\nexport function removeForward(str, offset) {\n tryString(str);\n tryOffset(offset);\n return str.slice(0, offset) + str.slice(offset + 1);\n}\n\n/**\n * Removes a slice of text.\n *\n * @param {string} str\n * @param {number} start\n * @param {number} end\n * @returns {string}\n */\nexport function removeSlice(str, start, end) {\n tryString(str);\n tryOffset(start);\n tryOffset(end);\n return str.slice(0, start) + str.slice(end);\n}\n", "/**\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at http://mozilla.org/MPL/2.0/.\n *\n * Copyright (c) KALEIDOS INC\n */\n\n/**\n * Iterator direction.\n *\n * @enum {number}\n */\nexport const TextNodeIteratorDirection = {\n FORWARD: 1,\n BACKWARD: 0,\n};\n\n/**\n * TextNodeIterator\n */\nexport class TextNodeIterator {\n /**\n * Returns if a specific node is a text node.\n *\n * @param {Node} node\n * @returns {boolean}\n */\n static isTextNode(node) {\n return (\n node.nodeType === Node.TEXT_NODE ||\n (node.nodeType === Node.ELEMENT_NODE && node.nodeName === \"BR\")\n );\n }\n\n /**\n * Returns if a specific node is a container node.\n *\n * @param {Node} node\n * @returns {boolean}\n */\n static isContainerNode(node) {\n return node.nodeType === Node.ELEMENT_NODE && node.nodeName !== \"BR\";\n }\n\n /**\n * Finds a node from an initial node and down the tree.\n *\n * @param {Node} startNode\n * @param {Node} rootNode\n * @param {Set} skipNodes\n * @param {number} direction\n * @returns {Node}\n */\n static findDown(\n startNode,\n rootNode,\n skipNodes = new Set(),\n direction = TextNodeIteratorDirection.FORWARD\n ) {\n if (startNode === rootNode) {\n return TextNodeIterator.findDown(\n direction === TextNodeIteratorDirection.FORWARD\n ? startNode.firstChild\n : startNode.lastChild,\n rootNode,\n skipNodes,\n direction\n );\n }\n\n // NOTE: This should not use the SafeGuard\n // module.\n let safeGuard = Date.now();\n let currentNode = startNode;\n while (currentNode) {\n if (Date.now() - safeGuard >= 1000) {\n throw new Error(\"Iteration timeout\");\n }\n if (skipNodes.has(currentNode)) {\n currentNode =\n direction === TextNodeIteratorDirection.FORWARD\n ? currentNode.nextSibling\n : currentNode.previousSibling;\n continue;\n }\n if (TextNodeIterator.isTextNode(currentNode)) {\n return currentNode;\n } else if (TextNodeIterator.isContainerNode(currentNode)) {\n return TextNodeIterator.findDown(\n direction === TextNodeIteratorDirection.FORWARD\n ? currentNode.firstChild\n : currentNode.lastChild,\n rootNode,\n skipNodes,\n direction\n );\n }\n currentNode =\n direction === TextNodeIteratorDirection.FORWARD\n ? currentNode.nextSibling\n : currentNode.previousSibling;\n }\n return null;\n }\n\n /**\n * Finds a node from an initial node and up the tree.\n *\n * @param {Node} startNode\n * @param {Node} rootNode\n * @param {Set} backTrack\n * @param {number} direction\n * @returns {Node}\n */\n static findUp(\n startNode,\n rootNode,\n backTrack = new Set(),\n direction = TextNodeIteratorDirection.FORWARD\n ) {\n backTrack.add(startNode);\n if (TextNodeIterator.isTextNode(startNode)) {\n return TextNodeIterator.findUp(\n startNode.parentNode,\n rootNode,\n backTrack,\n direction\n );\n } else if (TextNodeIterator.isContainerNode(startNode)) {\n const found = TextNodeIterator.findDown(\n startNode,\n rootNode,\n backTrack,\n direction\n );\n if (found) {\n return found;\n }\n if (startNode !== rootNode) {\n return TextNodeIterator.findUp(\n startNode.parentNode,\n rootNode,\n backTrack,\n direction\n );\n }\n }\n return null;\n }\n\n /**\n * This is the root text node.\n *\n * @type {HTMLElement}\n */\n #rootNode = null;\n\n /**\n * This is the current text node.\n *\n * @type {Text|null}\n */\n #currentNode = null;\n\n /**\n * Constructor\n *\n * @param {HTMLElement} rootNode\n */\n constructor(rootNode) {\n if (!(rootNode instanceof HTMLElement)) {\n throw new TypeError(\"Invalid root node\");\n }\n this.#rootNode = rootNode;\n this.#currentNode = TextNodeIterator.findDown(rootNode, rootNode);\n }\n\n /**\n * Current node we're into.\n *\n * @type {TextNode|HTMLBRElement}\n */\n get currentNode() {\n return this.#currentNode;\n }\n\n set currentNode(newCurrentNode) {\n const isContained =\n (newCurrentNode.compareDocumentPosition(this.#rootNode) &\n Node.DOCUMENT_POSITION_CONTAINS) ===\n Node.DOCUMENT_POSITION_CONTAINS;\n if (\n !(newCurrentNode instanceof Node) ||\n !TextNodeIterator.isTextNode(newCurrentNode) ||\n !isContained\n ) {\n throw new TypeError(\"Invalid new current node\");\n }\n this.#currentNode = newCurrentNode;\n }\n\n /**\n * Returns the next Text node or
element or null if there are.\n *\n * @returns {Text|HTMLBRElement}\n */\n nextNode() {\n if (!this.#currentNode) return null;\n\n const nextNode = TextNodeIterator.findUp(\n this.#currentNode,\n this.#rootNode,\n new Set(),\n TextNodeIteratorDirection.FORWARD\n );\n\n if (!nextNode) {\n return null;\n }\n\n this.#currentNode = nextNode;\n return this.#currentNode;\n }\n\n /**\n * Returns the previous Text node or
element or null.\n *\n * @returns {Text|HTMLBRElement}\n */\n previousNode() {\n if (!this.#currentNode) return null;\n\n const previousNode = TextNodeIterator.findUp(\n this.#currentNode,\n this.#rootNode,\n new Set(),\n TextNodeIteratorDirection.BACKWARD\n );\n\n if (!previousNode) {\n return null;\n }\n\n this.#currentNode = previousNode;\n return this.#currentNode;\n }\n}\n\nexport default TextNodeIterator;\n", "/**\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at http://mozilla.org/MPL/2.0/.\n *\n * Copyright (c) KALEIDOS INC\n */\n\n/**\n * Command mutations\n */\nexport class CommandMutations {\n #added = new Set();\n #removed = new Set();\n #updated = new Set();\n\n constructor(added, updated, removed) {\n if (added && Array.isArray(added)) this.#added = new Set(added);\n if (updated && Array.isArray(updated)) this.#updated = new Set(updated);\n if (removed && Array.isArray(removed)) this.#removed = new Set(removed);\n }\n\n get added() {\n return this.#added;\n }\n\n get removed() {\n return this.#removed;\n }\n\n get updated() {\n return this.#updated;\n }\n\n clear() {\n this.#added.clear();\n this.#removed.clear();\n this.#updated.clear();\n }\n\n dispose() {\n this.#added.clear();\n this.#added = null;\n this.#removed.clear();\n this.#removed = null;\n this.#updated.clear();\n this.#updated = null;\n }\n\n add(node) {\n this.#added.add(node);\n return this;\n }\n\n remove(node) {\n this.#removed.add(node);\n return this;\n }\n\n update(node) {\n this.#updated.add(node);\n return this;\n }\n}\n\nexport default CommandMutations;\n", "/**\n * Indicates the direction of the selection.\n *\n * @readonly\n * @enum {number}\n */\nexport const SelectionDirection = {\n /** The anchorNode is behind the focusNode */\n FORWARD: 1,\n /** The focusNode and the anchorNode are collapsed */\n NONE: 0,\n /** The focusNode is behind the anchorNode */\n BACKWARD: -1,\n};\n\nexport default SelectionDirection;\n", "/**\n * Max. amount of time we should allow.\n *\n * @type {number}\n */\nconst SAFE_GUARD_TIME = 1000;\n\n/**\n * Time at which the safeguard started.\n *\n * @type {number}\n */\nlet startTime = Date.now();\n\n/**\n * Marks the start of the safeguard.\n */\nexport function start() {\n startTime = Date.now();\n}\n\n/**\n * Checks if the safeguard should throw.\n */\nexport function update() {\n if (Date.now - startTime >= SAFE_GUARD_TIME) {\n throw new Error('Safe guard timeout');\n }\n}\n\nexport default {\n start,\n update,\n}\n", "/**\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at http://mozilla.org/MPL/2.0/.\n *\n * Copyright (c) KALEIDOS INC\n */\n\nimport { createLineBreak, isLineBreak } from \"~/editor/content/dom/LineBreak\";\nimport {\n createInline,\n createInlineFrom,\n getInline,\n getInlineLength,\n isInline,\n isInlineStart,\n isInlineEnd,\n setInlineStyles,\n mergeInlines,\n splitInline,\n createEmptyInline,\n} from \"~/editor/content/dom/Inline\";\nimport {\n createEmptyParagraph,\n isEmptyParagraph,\n getParagraph,\n isParagraph,\n isParagraphStart,\n isParagraphEnd,\n setParagraphStyles,\n splitParagraph,\n splitParagraphAtNode,\n mergeParagraphs,\n} from \"~/editor/content/dom/Paragraph\";\nimport {\n removeBackward,\n removeForward,\n replaceWith,\n insertInto,\n removeSlice,\n} from \"~/editor/content/Text\";\nimport { getTextNodeLength, getClosestTextNode, isTextNode } from \"~/editor/content/dom/TextNode\";\nimport TextNodeIterator from \"~/editor/content/dom/TextNodeIterator\";\nimport TextEditor from \"~/editor/TextEditor\";\nimport CommandMutations from \"~/editor/commands/CommandMutations\";\nimport { setRootStyles } from \"~/editor/content/dom/Root\";\nimport { SelectionDirection } from \"./SelectionDirection\";\nimport SafeGuard from './SafeGuard';\n\nconst SAFE_GUARD = true;\nconst SAFE_GUARD_TIME = true;\n\n/**\n * Supported options for the SelectionController.\n *\n * @typedef {Object} SelectionControllerOptions\n * @property {Object} [debug] An object with references to DOM elements that will keep all the debugging values.\n */\n\n/**\n * SelectionController uses the same concepts used by the Selection API but extending it to support\n * our own internal model based on paragraphs (in drafconst textEditorMock = TextEditorMock.createTextEditorMockWithParagraphs([\n createParagraph([createInline(new Text(\"Hello, \"))]),\n createEmptyParagraph(),\n createParagraph([createInline(new Text(\"World!\"))]),\n ]);\n const root = textEditorMock.root;\n const selection = document.getSelection();\n const selectionController = new SelectionController(\n textEditorMock,\n selection\n );\n focus(\n selection,\n textEditorMock,\n root.childNodes.item(2).firstChild.firstChild,\n 0\n );\n selectionController.mergeBackwardParagraph();\n expect(textEditorMock.root).toBeInstanceOf(HTMLDivElement);\n expect(textEditorMock.root.children.length).toBe(2);\n expect(textEditorMock.root.dataset.itype).toBe(\"root\");\n expect(textEditorMock.root.firstChild).toBeInstanceOf(HTMLDivElement);\n expect(textEditorMock.root.firstChild.dataset.itype).toBe(\"paragraph\");\n expect(textEditorMock.root.firstChild.firstChild).toBeInstanceOf(\n HTMLSpanElement\n );\n expect(textEditorMock.root.firstChild.firstChild.dataset.itype).toBe(\n \"inline\"\n );\n expect(textEditorMock.root.textContent).toBe(\"Hello, World!\");\n expect(textEditorMock.root.firstChild.textContent).toBe(\"Hello, \");\n expect(textEditorMock.root.lastChild.textContent).toBe(\"World!\");\n t.js they were called blocks) and inlines.\n */\nexport class SelectionController extends EventTarget {\n /**\n * Reference to the text editor.\n *\n * @type {TextEditor}\n */\n #textEditor = null;\n\n /**\n * Selection.\n *\n * @type {Selection}\n */\n #selection = null;\n\n /**\n * Set of ranges (this should always have one)\n *\n * @type {Set}\n */\n #ranges = new Set();\n\n /**\n * Current range (.rangeAt 0)\n *\n * @type {Range}\n */\n #range = null;\n\n /**\n * @type {Node}\n */\n #focusNode = null;\n\n /**\n * @type {number}\n */\n #focusOffset = 0;\n\n /**\n * @type {Node}\n */\n #anchorNode = null;\n\n /**\n * @type {number}\n */\n #anchorOffset = 0;\n\n /**\n * Saved selection.\n *\n * @type {object}\n */\n #savedSelection = null;\n\n /**\n * TextNodeIterator that allows us to move\n * around the root element but only through\n *
and #text nodes.\n *\n * @type {TextNodeIterator}\n */\n #textNodeIterator = null;\n\n /**\n * CSSStyleDeclaration that we can mutate\n * to handle style changes.\n *\n * @type {CSSStyleDeclaration}\n */\n #currentStyle = null;\n\n /**\n * Element used to have a custom CSSStyleDeclaration\n * that we can modify to handle style changes when the\n * selection is changed.\n *\n * @type {HTMLDivElement}\n */\n #inertElement = null;\n\n /**\n * @type {SelectionControllerDebug}\n */\n #debug = null;\n\n /**\n * Command Mutations.\n *\n * @type {CommandMutations}\n */\n #mutations = new CommandMutations();\n\n /**\n * Style defaults.\n *\n * @type {Object.}\n */\n #styleDefaults = null;\n\n /**\n * Constructor\n *\n * @param {TextEditor} textEditor\n * @param {Selection} selection\n * @param {SelectionControllerOptions} [options]\n */\n constructor(textEditor, selection, options) {\n super();\n // FIXME: We can't check if it is an instanceof TextEditor\n // because tests use TextEditorMock.\n /*\n if (!(textEditor instanceof TextEditor)) {\n throw new TypeError(\"Invalid EventTarget\");\n }\n */\n this.#debug = options?.debug;\n this.#styleDefaults = options?.styleDefaults;\n this.#selection = selection;\n this.#textEditor = textEditor;\n this.#textNodeIterator = new TextNodeIterator(this.#textEditor.element);\n\n // Setups everything.\n this.#setup();\n }\n\n /**\n * Styles of the current inline.\n *\n * @type {CSSStyleDeclaration}\n */\n get currentStyle() {\n return this.#currentStyle;\n }\n\n /**\n * Applies the default styles to the currentStyle\n * CSSStyleDeclaration.\n */\n #applyDefaultStylesToCurrentStyle() {\n if (this.#styleDefaults) {\n for (const [name, value] of Object.entries(this.#styleDefaults)) {\n this.#currentStyle.setProperty(\n name,\n value + (name === \"font-size\" ? \"px\" : \"\")\n );\n }\n }\n }\n\n /**\n * Applies some styles to the currentStyle\n * CSSStyleDeclaration\n *\n * @param {HTMLElement} element\n */\n #applyStylesToCurrentStyle(element) {\n for (let index = 0; index < element.style.length; index++) {\n const styleName = element.style.item(index);\n const styleValue = element.style.getPropertyValue(styleName);\n this.#currentStyle.setProperty(styleName, styleValue);\n }\n }\n\n /**\n * Updates current styles based on the currently selected inline.\n *\n * @param {HTMLSpanElement} inline\n * @returns {SelectionController}\n */\n #updateCurrentStyle(inline) {\n this.#applyDefaultStylesToCurrentStyle();\n const root = inline.parentElement.parentElement;\n this.#applyStylesToCurrentStyle(root);\n const paragraph = inline.parentElement;\n this.#applyStylesToCurrentStyle(paragraph);\n this.#applyStylesToCurrentStyle(inline);\n return this;\n }\n\n /**\n * This is called on every `selectionchange` because it is dispatched\n * only by the `document` object.\n *\n * @param {Event} e\n */\n #onSelectionChange = (e) => {\n // If we're outside the contenteditable element, then\n // we return.\n if (!this.hasFocus) return;\n\n let focusNodeChanges = false;\n let anchorNodeChanges = false;\n\n if (this.#focusNode !== this.#selection.focusNode) {\n this.#focusNode = this.#selection.focusNode;\n focusNodeChanges = true;\n }\n this.#focusOffset = this.#selection.focusOffset;\n\n if (this.#anchorNode !== this.#selection.anchorNode) {\n this.#anchorNode = this.#selection.anchorNode;\n anchorNodeChanges = true;\n }\n this.#anchorOffset = this.#selection.anchorOffset;\n\n // We need to handle multi selection from firefox\n // and remove all the old ranges and just keep the\n // last one added.\n if (this.#selection.rangeCount > 1) {\n for (let index = 0; index < this.#selection.rangeCount; index++) {\n const range = this.#selection.getRangeAt(index);\n if (this.#ranges.has(range)) {\n this.#ranges.delete(range);\n this.#selection.removeRange(range);\n } else {\n this.#ranges.add(range);\n this.#range = range;\n }\n }\n } else if (this.#selection.rangeCount > 0) {\n const range = this.#selection.getRangeAt(0);\n this.#range = range;\n this.#ranges.clear();\n this.#ranges.add(range);\n } else {\n this.#range = null;\n this.#ranges.clear();\n }\n\n // If focus node changed, we need to retrieve all the\n // styles of the current inline and dispatch an event\n // to notify that the styles have changed.\n if (focusNodeChanges) {\n this.#notifyStyleChange();\n }\n\n if (this.#debug) {\n this.#debug.update(this);\n }\n };\n\n /**\n * Notifies that the styles have changed.\n */\n #notifyStyleChange() {\n const inline = this.focusInline;\n if (inline) {\n this.#updateCurrentStyle(inline);\n this.dispatchEvent(\n new CustomEvent(\"stylechange\", {\n detail: this.#currentStyle,\n })\n );\n }\n }\n\n /**\n * Setups\n */\n #setup() {\n // This element is not attached to the DOM\n // so it doesn't trigger style or layout calculations.\n // That's why it's called \"inertElement\".\n this.#inertElement = document.createElement(\"div\");\n this.#currentStyle = this.#inertElement.style;\n this.#applyDefaultStylesToCurrentStyle();\n\n if (this.#selection.rangeCount > 0) {\n const range = this.#selection.getRangeAt(0);\n this.#range = range;\n this.#ranges.add(range);\n }\n\n // If there are more than one range, we should remove\n // them because this is a feature not supported by browsers\n // like Safari and Chrome.\n if (this.#selection.rangeCount > 1) {\n for (let index = 1; index < this.#selection.rangeCount; index++) {\n this.#selection.removeRange(index);\n }\n }\n document.addEventListener(\"selectionchange\", this.#onSelectionChange);\n }\n\n /**\n * Returns a Range-like object.\n *\n * @returns {RangeLike}\n */\n #getSavedRange() {\n if (!this.#range) {\n return {\n collapsed: true,\n commonAncestorContainer: null,\n startContainer: null,\n startOffset: 0,\n endContainer: null,\n endOffset: 0,\n };\n }\n return {\n collapsed: this.#range.collapsed,\n commonAncestorContainer: this.#range.commonAncestorContainer,\n startContainer: this.#range.startContainer,\n startOffset: this.#range.startOffset,\n endContainer: this.#range.endContainer,\n endOffset: this.#range.endOffset,\n };\n }\n\n /**\n * Saves the current selection and returns the client rects.\n *\n * @returns {boolean}\n */\n saveSelection() {\n this.#savedSelection = {\n isCollapsed: this.#selection.isCollapsed,\n focusNode: this.#selection.focusNode,\n focusOffset: this.#selection.focusOffset,\n anchorNode: this.#selection.anchorNode,\n anchorOffset: this.#selection.anchorOffset,\n range: this.#getSavedRange(),\n };\n return true;\n }\n\n /**\n * Restores a saved selection if there's any.\n *\n * @returns {boolean}\n */\n restoreSelection() {\n if (!this.#savedSelection) return false;\n\n if (this.#savedSelection.anchorNode && this.#savedSelection.focusNode) {\n if (this.#savedSelection.anchorNode === this.#savedSelection.focusNode) {\n this.#selection.setPosition(this.#savedSelection.focusNode, this.#savedSelection.focusOffset);\n } else {\n this.#selection.setBaseAndExtent(\n this.#savedSelection.anchorNode,\n this.#savedSelection.anchorOffset,\n this.#savedSelection.focusNode,\n this.#savedSelection.focusOffset\n );\n }\n }\n this.#savedSelection = null;\n return true;\n }\n\n /**\n * Marks the start of a mutation.\n *\n * Clears all the mutations kept in CommandMutations.\n */\n startMutation() {\n this.#mutations.clear();\n if (!this.#focusNode) return false;\n return true;\n }\n\n /**\n * Marks the end of a mutation.\n *\n * @returns\n */\n endMutation() {\n return this.#mutations;\n }\n\n /**\n * Selects all content.\n */\n selectAll() {\n this.#selection.selectAllChildren(this.#textEditor.root);\n return this;\n }\n\n /**\n * Moves cursor to end.\n */\n cursorToEnd() {\n const range = document.createRange(); //Create a range (a range is a like the selection but invisible)\n range.selectNodeContents(this.#textEditor.element);\n range.collapse(false);\n this.#selection.removeAllRanges();\n this.#selection.addRange(range);\n return this;\n }\n\n /**\n * Collapses a selection.\n *\n * @param {Node} node\n * @param {number} offset\n */\n collapse(node, offset) {\n const nodeOffset = (node.nodeType === Node.TEXT_NODE && offset >= node.nodeValue.length)\n ? node.nodeValue.length\n : offset\n\n return this.setSelection(\n node,\n nodeOffset,\n node,\n nodeOffset\n );\n }\n\n /**\n * Sets base and extent.\n *\n * @param {Node} anchorNode\n * @param {number} anchorOffset\n * @param {Node} [focusNode=anchorNode]\n * @param {number} [focusOffset=anchorOffset]\n */\n setSelection(anchorNode, anchorOffset, focusNode = anchorNode, focusOffset = anchorOffset) {\n if (!anchorNode.isConnected) {\n throw new Error('Invalid anchorNode')\n }\n if (!focusNode.isConnected) {\n throw new Error('Invalid focusNode')\n }\n if (this.#savedSelection) {\n this.#savedSelection.isCollapsed =\n focusNode === anchorNode && anchorOffset === focusOffset;\n this.#savedSelection.focusNode = focusNode;\n this.#savedSelection.focusOffset = focusOffset;\n this.#savedSelection.anchorNode = anchorNode;\n this.#savedSelection.anchorOffset = anchorOffset;\n\n this.#savedSelection.range.collapsed = this.#savedSelection.isCollapsed;\n const position = focusNode.compareDocumentPosition(anchorNode);\n if (position & Node.DOCUMENT_POSITION_FOLLOWING) {\n this.#savedSelection.range.startContainer = focusNode;\n this.#savedSelection.range.startOffset = focusOffset;\n this.#savedSelection.range.endContainer = anchorNode;\n this.#savedSelection.range.endOffset = anchorOffset;\n } else {\n this.#savedSelection.range.startContainer = anchorNode;\n this.#savedSelection.range.startOffset = anchorOffset;\n this.#savedSelection.range.endContainer = focusNode;\n this.#savedSelection.range.endOffset = focusOffset;\n }\n } else {\n this.#anchorNode = anchorNode;\n this.#anchorOffset = anchorOffset;\n if (anchorNode === focusNode) {\n this.#focusNode = this.#anchorNode;\n this.#focusOffset = this.#anchorOffset;\n this.#selection.setPosition(anchorNode, anchorOffset);\n } else {\n this.#focusNode = focusNode;\n this.#focusOffset = focusOffset;\n this.#selection.setBaseAndExtent(\n anchorNode,\n anchorOffset,\n focusNode,\n focusOffset\n );\n }\n }\n }\n\n /**\n * Disposes the current resources.\n */\n dispose() {\n document.removeEventListener(\"selectionchange\", this.#onSelectionChange);\n this.#textEditor = null;\n this.#ranges.clear();\n this.#ranges = null;\n this.#range = null;\n this.#selection = null;\n this.#focusNode = null;\n this.#anchorNode = null;\n this.#mutations.dispose();\n this.#mutations = null;\n }\n\n /**\n * Returns the current selection.\n *\n * @type {Selection}\n */\n get selection() {\n return this.#selection;\n }\n\n /**\n * Returns the current range.\n *\n * @type {Range}\n */\n get range() {\n return this.#range;\n }\n\n /**\n * Indicates the direction of the selection\n *\n * @type {SelectionDirection}\n */\n get direction() {\n if (this.isCollapsed) {\n return SelectionDirection.NONE;\n }\n if (this.focusNode !== this.anchorNode) {\n return this.startContainer === this.focusNode\n ? SelectionDirection.BACKWARD\n : SelectionDirection.FORWARD;\n }\n return this.focusOffset < this.anchorOffset\n ? SelectionDirection.BACKWARD\n : SelectionDirection.FORWARD;\n }\n\n /**\n * Indicates that the editor element has the\n * focus.\n *\n * @type {boolean}\n */\n get hasFocus() {\n return document.activeElement === this.#textEditor.element;\n }\n\n /**\n * Returns true if the selection is collapsed (caret)\n * or false otherwise.\n *\n * @type {boolean}\n */\n get isCollapsed() {\n if (this.#savedSelection) {\n return this.#savedSelection.isCollapsed;\n }\n return this.#selection.isCollapsed;\n }\n\n /**\n * Current or saved anchor node.\n *\n * @type {Node}\n */\n get anchorNode() {\n if (this.#savedSelection) {\n return this.#savedSelection.anchorNode;\n }\n return this.#anchorNode;\n }\n\n /**\n * Current or saved anchor offset.\n *\n * @type {number}\n */\n get anchorOffset() {\n if (this.#savedSelection) {\n return this.#savedSelection.anchorOffset;\n }\n return this.#selection.anchorOffset;\n }\n\n /**\n * Indicates that the caret is at the start of the node.\n *\n * @type {boolean}\n */\n get anchorAtStart() {\n return this.anchorOffset === 0;\n }\n\n /**\n * Indicates that the caret is at the end of the node.\n *\n * @type {boolean}\n */\n get anchorAtEnd() {\n return this.anchorOffset === this.anchorNode.nodeValue.length;\n }\n\n /**\n * Current or saved focus node.\n *\n * @type {Node}\n */\n get focusNode() {\n if (this.#savedSelection) {\n return this.#savedSelection.focusNode;\n }\n if (!this.#focusNode)\n console.trace(\"focusNode\", this.#focusNode);\n return this.#focusNode;\n }\n\n /**\n * Current or saved focus offset.\n *\n * @type {number}\n */\n get focusOffset() {\n if (this.#savedSelection) {\n return this.#savedSelection.focusOffset;\n }\n return this.#focusOffset;\n }\n\n /**\n * Indicates that the caret is at the start of the node.\n *\n * @type {boolean}\n */\n get focusAtStart() {\n return this.focusOffset === 0;\n }\n\n /**\n * Indicates that the caret is at the end of the node.\n *\n * @type {boolean}\n */\n get focusAtEnd() {\n return this.focusOffset === this.focusNode.nodeValue.length;\n }\n\n /**\n * Returns the paragraph in the focus node\n * of the current selection.\n *\n * @type {HTMLElement|null}\n */\n get focusParagraph() {\n return getParagraph(this.focusNode);\n }\n\n /**\n * Returns the inline in the focus node\n * of the current selection.\n *\n * @type {HTMLElement|null}\n */\n get focusInline() {\n return getInline(this.focusNode);\n }\n\n /**\n * Returns the current paragraph in the anchor\n * node of the current selection.\n *\n * @type {HTMLElement|null}\n */\n get anchorParagraph() {\n return getParagraph(this.anchorNode);\n }\n\n /**\n * Returns the current inline in the anchor\n * node of the current selection.\n *\n * @type {HTMLElement|null}\n */\n get anchorInline() {\n return getInline(this.anchorNode);\n }\n\n /**\n * Start container of the current range.\n */\n get startContainer() {\n if (this.#savedSelection) {\n return this.#savedSelection?.range?.startContainer;\n }\n return this.#range?.startContainer;\n }\n\n /**\n * `startOffset` of the current range.\n *\n * @type {number|null}\n */\n get startOffset() {\n if (this.#savedSelection) {\n return this.#savedSelection?.range?.startOffset;\n }\n return this.#range?.startOffset;\n }\n\n /**\n * Start paragraph of the current range.\n *\n * @type {HTMLElement|null}\n */\n get startParagraph() {\n const startContainer = this.startContainer;\n if (!startContainer) return null;\n return getParagraph(startContainer);\n }\n\n /**\n * Start inline of the current page.\n *\n * @type {HTMLElement|null}\n */\n get startInline() {\n const startContainer = this.startContainer;\n if (!startContainer) return null;\n return getInline(startContainer);\n }\n\n /**\n * End container of the current range.\n *\n * @type {Node}\n */\n get endContainer() {\n if (this.#savedSelection) {\n return this.#savedSelection?.range?.endContainer;\n }\n return this.#range?.endContainer;\n }\n\n /**\n * `endOffset` of the current range\n *\n * @type {HTMLElement|null}\n */\n get endOffset() {\n if (this.#savedSelection) {\n return this.#savedSelection?.range?.endOffset;\n }\n return this.#range?.endOffset;\n }\n\n /**\n * Paragraph element of the `endContainer` of\n * the current range.\n *\n * @type {HTMLElement|null}\n */\n get endParagraph() {\n const endContainer = this.endContainer;\n if (!endContainer) return null;\n return getParagraph(endContainer);\n }\n\n /**\n * Inline element of the `endContainer` of\n * the current range.\n *\n * @type {HTMLElement|null}\n */\n get endInline() {\n const endContainer = this.endContainer;\n if (!endContainer) return null;\n return getInline(endContainer);\n }\n\n /**\n * Returns true if the anchor node and the focus\n * node are the same text nodes.\n *\n * @type {boolean}\n */\n get isTextSame() {\n return (\n this.isTextFocus === this.isTextAnchor &&\n this.focusNode === this.anchorNode\n );\n }\n\n /**\n * Indicates that focus node is a text node.\n *\n * @type {boolean}\n */\n get isTextFocus() {\n return this.focusNode.nodeType === Node.TEXT_NODE;\n }\n\n /**\n * Indicates that anchor node is a text node.\n *\n * @type {boolean}\n */\n get isTextAnchor() {\n return this.anchorNode.nodeType === Node.TEXT_NODE;\n }\n\n /**\n * Is true if the current focus node is a inline.\n *\n * @type {boolean}\n */\n get isInlineFocus() {\n return isInline(this.focusNode);\n }\n\n /**\n * Is true if the current anchor node is a inline.\n *\n * @type {boolean}\n */\n get isInlineAnchor() {\n return isInline(this.anchorNode);\n }\n\n /**\n * Is true if the current focus node is a paragraph.\n *\n * @type {boolean}\n */\n get isParagraphFocus() {\n return isParagraph(this.focusNode);\n }\n\n /**\n * Is true if the current anchor node is a paragraph.\n *\n * @type {boolean}\n */\n get isParagraphAnchor() {\n return isParagraph(this.anchorNode);\n }\n\n /**\n * Is true if the current focus node is a line break.\n *\n * @type {boolean}\n */\n get isLineBreakFocus() {\n return (\n isLineBreak(this.focusNode) ||\n (isInline(this.focusNode) && isLineBreak(this.focusNode.firstChild))\n );\n }\n\n /**\n * Indicates that we have multiple nodes selected.\n *\n * @type {boolean}\n */\n get isMulti() {\n return this.focusNode !== this.anchorNode;\n }\n\n /**\n * Indicates that we have selected multiple\n * paragraph elements.\n *\n * @type {boolean}\n */\n get isMultiParagraph() {\n return this.isMulti && this.focusParagraph !== this.anchorParagraph;\n }\n\n /**\n * Indicates that we have selected multiple\n * inline elements.\n *\n * @type {boolean}\n */\n get isMultiInline() {\n return this.isMulti && this.focusInline !== this.anchorInline;\n }\n\n /**\n * Indicates that the caret (only the caret)\n * is at the start of an inline.\n *\n * @type {boolean}\n */\n get isInlineStart() {\n if (!this.isCollapsed) return false;\n return isInlineStart(this.focusNode, this.focusOffset);\n }\n\n /**\n * Indicates that the caret (only the caret)\n * is at the end of an inline. This value doesn't\n * matter when dealing with selections.\n *\n * @type {boolean}\n */\n get isInlineEnd() {\n if (!this.isCollapsed) return false;\n return isInlineEnd(this.focusNode, this.focusOffset);\n }\n\n /**\n * Indicates that we're in the starting position of a paragraph.\n *\n * @type {boolean}\n */\n get isParagraphStart() {\n if (!this.isCollapsed) return false;\n return isParagraphStart(this.focusNode, this.focusOffset);\n }\n\n /**\n * Indicates that we're in the ending position of a paragraph.\n *\n * @type {boolean}\n */\n get isParagraphEnd() {\n if (!this.isCollapsed) return false;\n return isParagraphEnd(this.focusNode, this.focusOffset);\n }\n\n /**\n * Insert pasted fragment.\n *\n * @param {DocumentFragment} fragment\n */\n insertPaste(fragment) {\n const numParagraphs = fragment.children.length;\n if (this.isParagraphStart) {\n this.focusParagraph.before(fragment);\n } else if (this.isParagraphEnd) {\n this.focusParagraph.after(fragment);\n } else {\n const newParagraph = splitParagraph(\n this.focusParagraph,\n this.focusInline,\n this.focusOffset\n );\n this.focusParagraph.after(fragment, newParagraph);\n }\n }\n\n /**\n * Replaces data with pasted fragment\n *\n * @param {DocumentFragment} fragment\n */\n replaceWithPaste(fragment) {\n const numParagraphs = fragment.children.length;\n this.removeSelected();\n this.insertPaste(fragment);\n }\n\n /**\n * Replaces the current line break with text\n *\n * @param {string} text\n */\n replaceLineBreak(text) {\n const newText = new Text(text);\n this.focusInline.replaceChildren(newText);\n this.collapse(newText, text.length);\n }\n\n /**\n * Removes text forward from the current position.\n */\n removeForwardText() {\n this.#textNodeIterator.currentNode = this.focusNode;\n\n const removedData = removeForward(\n this.focusNode.nodeValue,\n this.focusOffset\n );\n\n if (this.focusNode.nodeValue !== removedData) {\n this.focusNode.nodeValue = removedData;\n }\n\n const paragraph = this.focusParagraph;\n if (!paragraph) throw new Error(\"Cannot find paragraph\");\n const inline = this.focusInline;\n if (!inline) throw new Error(\"Cannot find inline\");\n\n const nextTextNode = this.#textNodeIterator.nextNode();\n if (this.focusNode.nodeValue === \"\") {\n this.focusNode.remove();\n }\n\n if (paragraph.childNodes.length === 1 && inline.childNodes.length === 0) {\n const lineBreak = createLineBreak();\n inline.appendChild(lineBreak);\n return this.collapse(lineBreak, 0);\n } else if (\n paragraph.childNodes.length > 1 &&\n inline.childNodes.length === 0\n ) {\n inline.remove();\n return this.collapse(nextTextNode, 0);\n }\n return this.collapse(this.focusNode, this.focusOffset);\n }\n\n /**\n * Removes text backward from the current caret position.\n */\n removeBackwardText() {\n this.#textNodeIterator.currentNode = this.focusNode;\n\n // Remove the character from the string.\n const removedData = removeBackward(\n this.focusNode.nodeValue,\n this.focusOffset\n );\n\n if (this.focusNode.nodeValue !== removedData) {\n this.focusNode.nodeValue = removedData;\n }\n\n // If the focusNode has content we don't need to do\n // anything else.\n if (this.focusOffset - 1 > 0) {\n return this.collapse(this.focusNode, this.focusOffset - 1);\n }\n\n const paragraph = this.focusParagraph;\n if (!paragraph) throw new Error(\"Cannot find paragraph\");\n const inline = this.focusInline;\n if (!inline) throw new Error(\"Cannot find inline\");\n\n const previousTextNode = this.#textNodeIterator.previousNode();\n if (this.focusNode.nodeValue === \"\") {\n this.focusNode.remove();\n }\n\n if (paragraph.children.length === 1 && inline.childNodes.length === 0) {\n const lineBreak = createLineBreak();\n inline.appendChild(lineBreak);\n return this.collapse(lineBreak, 0);\n } else if (\n paragraph.children.length > 1 &&\n inline.childNodes.length === 0\n ) {\n inline.remove();\n return this.collapse(previousTextNode, getTextNodeLength(previousTextNode));\n }\n\n return this.collapse(this.focusNode, this.focusOffset - 1);\n }\n\n /**\n * Inserts some text in the caret position.\n *\n * @param {string} newText\n */\n insertText(newText) {\n this.focusNode.nodeValue = insertInto(\n this.focusNode.nodeValue,\n this.focusOffset,\n newText\n );\n this.#mutations.update(this.focusInline);\n return this.collapse(this.focusNode, this.focusOffset + newText.length);\n }\n\n /**\n * Replaces currently selected text.\n *\n * @param {string} newText\n */\n replaceText(newText) {\n const startOffset = Math.min(this.anchorOffset, this.focusOffset);\n const endOffset = Math.max(this.anchorOffset, this.focusOffset);\n this.focusNode.nodeValue = replaceWith(\n this.focusNode.nodeValue,\n startOffset,\n endOffset,\n newText\n );\n this.#mutations.update(this.focusInline);\n return this.collapse(this.focusNode, startOffset + newText.length);\n }\n\n /**\n * Replaces the selected inlines with new text.\n *\n * @param {string} newText\n */\n replaceInlines(newText) {\n const currentParagraph = this.focusParagraph;\n\n // This is the special (and fast) case where we're\n // removing everything inside a paragraph.\n if (\n this.startInline === currentParagraph.firstChild &&\n this.startOffset === 0 &&\n this.endInline === currentParagraph.lastChild &&\n this.endOffset === currentParagraph.lastChild.textContent.length\n ) {\n const newTextNode = new Text(newText);\n currentParagraph.replaceChildren(\n createInline(newTextNode, this.anchorInline.style)\n );\n return this.collapse(newTextNode, newTextNode.nodeValue.length);\n }\n\n this.removeSelected();\n\n this.focusNode.nodeValue = insertInto(\n this.focusNode.nodeValue,\n this.focusOffset,\n newText\n );\n\n // FIXME: I'm not sure if we should merge inlines when they share the same styles.\n // For example: if we have > 2 inlines and the start inline and the end inline\n // share the same styles, maybe we should merge them?\n // mergeInlines(startInline, endInline);\n return this.collapse(this.focusNode, this.focusOffset + newText.length);\n }\n\n /**\n * Replaces paragraphs with text.\n *\n * @param {string} newText\n */\n replaceParagraphs(newText) {\n const currentParagraph = this.focusParagraph;\n\n this.removeSelected();\n\n this.focusNode.nodeValue = insertInto(\n this.focusNode.nodeValue,\n this.focusOffset,\n newText\n );\n\n for (const child of currentParagraph.children) {\n if (child.textContent === \"\") {\n child.remove();\n }\n }\n }\n\n /**\n * Inserts a new paragraph after the current paragraph.\n */\n insertParagraphAfter() {\n const currentParagraph = this.focusParagraph;\n const newParagraph = createEmptyParagraph(this.#currentStyle);\n currentParagraph.after(newParagraph);\n this.#mutations.update(currentParagraph);\n this.#mutations.add(newParagraph);\n return this.collapse(newParagraph.firstChild.firstChild, 0);\n }\n\n /**\n * Inserts a new paragraph before the current paragraph.\n */\n insertParagraphBefore() {\n const currentParagraph = this.focusParagraph;\n const newParagraph = createEmptyParagraph(this.#currentStyle);\n currentParagraph.before(newParagraph);\n this.#mutations.update(currentParagraph);\n this.#mutations.add(newParagraph);\n return this.collapse(currentParagraph.firstChild.firstChild, 0);\n }\n\n /**\n * Splits the current paragraph.\n */\n splitParagraph() {\n const currentParagraph = this.focusParagraph;\n const newParagraph = splitParagraph(\n this.focusParagraph,\n this.focusInline,\n this.#focusOffset\n );\n this.focusParagraph.after(newParagraph);\n this.#mutations.update(currentParagraph);\n this.#mutations.add(newParagraph);\n return this.collapse(newParagraph.firstChild.firstChild, 0);\n }\n\n /**\n * Inserts a new paragraph.\n */\n insertParagraph() {\n if (this.isParagraphEnd) {\n return this.insertParagraphAfter();\n } else if (this.isParagraphStart) {\n return this.insertParagraphBefore();\n }\n return this.splitParagraph();\n }\n\n /**\n * Replaces the currently selected content with\n * a paragraph.\n */\n replaceWithParagraph() {\n const currentParagraph = this.focusParagraph;\n const currentInline = this.focusInline;\n\n this.removeSelected();\n\n const newParagraph = splitParagraph(\n currentParagraph,\n currentInline,\n this.focusOffset\n );\n currentParagraph.after(newParagraph);\n\n this.#mutations.update(currentParagraph);\n this.#mutations.add(newParagraph);\n\n // FIXME: Missing collapse?\n }\n\n /**\n * Removes a paragraph in backward direction.\n */\n removeBackwardParagraph() {\n const previousParagraph = this.focusParagraph.previousElementSibling;\n if (!previousParagraph) {\n return;\n }\n const paragraphToBeRemoved = this.focusParagraph;\n paragraphToBeRemoved.remove();\n const previousInline =\n previousParagraph.children.length > 1\n ? previousParagraph.lastElementChild\n : previousParagraph.firstChild;\n const previousOffset = isLineBreak(previousInline.firstChild)\n ? 0\n : previousInline.firstChild.nodeValue.length;\n this.#mutations.remove(paragraphToBeRemoved);\n return this.collapse(previousInline.firstChild, previousOffset);\n }\n\n /**\n * Merges the previous paragraph with the current paragraph.\n */\n mergeBackwardParagraph() {\n const currentParagraph = this.focusParagraph;\n const previousParagraph = this.focusParagraph.previousElementSibling;\n if (!previousParagraph) {\n return;\n }\n let previousInline = previousParagraph.lastChild;\n const previousOffset = getInlineLength(previousInline);\n if (isEmptyParagraph(previousParagraph)) {\n previousParagraph.replaceChildren(...currentParagraph.children);\n previousInline = previousParagraph.firstChild;\n currentParagraph.remove();\n } else {\n mergeParagraphs(previousParagraph, currentParagraph);\n }\n this.#mutations.remove(currentParagraph);\n this.#mutations.update(previousParagraph);\n return this.collapse(previousInline.firstChild, previousOffset);\n }\n\n /**\n * Merges the next paragraph with the current paragraph.\n */\n mergeForwardParagraph() {\n const currentParagraph = this.focusParagraph;\n const nextParagraph = this.focusParagraph.nextElementSibling;\n if (!nextParagraph) {\n return;\n }\n mergeParagraphs(this.focusParagraph, nextParagraph);\n this.#mutations.update(currentParagraph);\n this.#mutations.remove(nextParagraph);\n\n // FIXME: Missing collapse?\n }\n\n /**\n * Removes the forward paragraph.\n */\n removeForwardParagraph() {\n const nextParagraph = this.focusParagraph.nextSibling;\n if (!nextParagraph) {\n return;\n }\n const paragraphToBeRemoved = this.focusParagraph;\n paragraphToBeRemoved.remove();\n const nextInline = nextParagraph.firstChild;\n const nextOffset = this.focusOffset;\n this.#mutations.remove(paragraphToBeRemoved);\n return this.collapse(nextInline.firstChild, nextOffset);\n }\n\n /**\n * Cleans up all the affected paragraphs.\n *\n * @param {Set} affectedParagraphs\n * @param {Set} affectedInlines\n */\n cleanUp(affectedParagraphs, affectedInlines) {\n // Remove empty inlines\n for (const inline of affectedInlines) {\n if (inline.textContent === \"\") {\n inline.remove();\n this.#mutations.remove(inline);\n }\n }\n\n // Remove empty paragraphs.\n for (const paragraph of affectedParagraphs) {\n if (paragraph.children.length === 0) {\n paragraph.remove();\n this.#mutations.remove(paragraph);\n }\n }\n }\n\n /**\n * Removes the selected content.\n *\n * @param {RemoveSelectedOptions} [options]\n */\n removeSelected(options) {\n if (this.isCollapsed) return;\n\n const affectedInlines = new Set();\n const affectedParagraphs = new Set();\n\n const startNode = getClosestTextNode(this.#range.startContainer);\n const endNode = getClosestTextNode(this.#range.endContainer);\n const startOffset = this.#range.startOffset;\n const endOffset = this.#range.endOffset;\n\n let previousNode = null;\n let nextNode = null;\n\n // This is the simplest case, when the startNode and the endNode\n // are the same and they're a textNode.\n if (startNode === endNode) {\n this.#textNodeIterator.currentNode = startNode;\n previousNode = this.#textNodeIterator.previousNode();\n\n this.#textNodeIterator.currentNode = startNode;\n nextNode = this.#textNodeIterator.nextNode();\n\n const inline = getInline(startNode);\n const paragraph = getParagraph(startNode);\n affectedInlines.add(inline);\n affectedParagraphs.add(paragraph);\n\n const newNodeValue = removeSlice(\n startNode.nodeValue,\n startOffset,\n endOffset\n );\n if (newNodeValue === \"\") {\n const lineBreak = createLineBreak();\n inline.replaceChildren(lineBreak);\n return this.collapse(lineBreak, 0);\n }\n startNode.nodeValue = newNodeValue;\n return this.collapse(startNode, startOffset);\n }\n\n // If startNode and endNode are different,\n // then we should process every text node from\n // start to end.\n\n // Select initial node.\n this.#textNodeIterator.currentNode = startNode;\n\n const startInline = getInline(startNode);\n const startParagraph = getParagraph(startNode);\n const endInline = getInline(endNode);\n const endParagraph = getParagraph(endNode);\n\n SafeGuard.start();\n do {\n SafeGuard.update();\n\n const currentNode = this.#textNodeIterator.currentNode;\n\n // We retrieve the inline and paragraph of the\n // current node.\n const inline = getInline(this.#textNodeIterator.currentNode);\n const paragraph = getParagraph(this.#textNodeIterator.currentNode);\n\n let shouldRemoveNodeCompletely = false;\n if (this.#textNodeIterator.currentNode === startNode) {\n if (startOffset === 0) {\n // We should remove this node completely.\n shouldRemoveNodeCompletely = true;\n } else {\n // We should remove this node partially.\n currentNode.nodeValue = currentNode.nodeValue.slice(0, startOffset);\n }\n } else if (this.#textNodeIterator.currentNode === endNode) {\n if (isLineBreak(endNode)\n || (isTextNode(endNode)\n && endOffset === endNode.nodeValue.length)) {\n // We should remove this node completely.\n shouldRemoveNodeCompletely = true;\n } else {\n // We should remove this node partially.\n currentNode.nodeValue = currentNode.nodeValue.slice(endOffset);\n }\n } else {\n // We should remove this node completely.\n shouldRemoveNodeCompletely = true;\n }\n\n this.#textNodeIterator.nextNode();\n\n // Realizamos el borrado del nodo actual.\n if (shouldRemoveNodeCompletely) {\n currentNode.remove();\n if (currentNode === startNode) {\n continue;\n }\n if (currentNode === endNode) {\n break;\n }\n\n if (inline.childNodes.length === 0) {\n inline.remove();\n }\n if (paragraph !== startParagraph && paragraph.children.length === 0) {\n paragraph.remove();\n }\n }\n\n if (currentNode === endNode) {\n break;\n }\n\n } while (this.#textNodeIterator.currentNode);\n\n if (startParagraph !== endParagraph) {\n const mergedParagraph = mergeParagraphs(startParagraph, endParagraph);\n if (mergedParagraph.children.length === 0) {\n const newEmptyInline = createEmptyInline(this.#currentStyle);\n mergedParagraph.appendChild(newEmptyInline);\n return this.collapse(newEmptyInline.firstChild, 0);\n }\n }\n\n if (startInline.childNodes.length === 0 && endInline.childNodes.length > 0) {\n startInline.remove();\n return this.collapse(endNode, 0);\n } else if (startInline.childNodes.length > 0 && endInline.childNodes.length === 0) {\n endInline.remove();\n return this.collapse(startNode, startOffset);\n } else if (startInline.childNodes.length === 0 && endInline.childNodes.length === 0) {\n const previousInline = startInline.previousElementSibling;\n const nextInline = endInline.nextElementSibling;\n startInline.remove();\n endInline.remove();\n if (previousInline) {\n return this.collapse(previousInline.firstChild, previousInline.firstChild.nodeValue.length);\n }\n if (nextInline) {\n return this.collapse(nextInline.firstChild, 0);\n }\n const newEmptyInline = createEmptyInline(this.#currentStyle);\n startParagraph.appendChild(newEmptyInline);\n return this.collapse(newEmptyInline.firstChild, 0);\n }\n\n return this.collapse(startNode, startOffset);\n }\n\n /**\n * Applies styles from the startNode to the endNode.\n *\n * @param {Node} startNode\n * @param {number} startOffset\n * @param {Node} endNode\n * @param {number} endOffset\n * @param {Object.|CSSStyleDeclaration} newStyles\n * @returns {void}\n */\n #applyStylesTo(startNode, startOffset, endNode, endOffset, newStyles) {\n // Applies the necessary styles to the root element.\n const root = this.#textEditor.root;\n setRootStyles(root, newStyles);\n\n // If the startContainer and endContainer are the same\n // node, then we can apply styles directly to that\n // node.\n if (startNode === endNode && startNode.nodeType === Node.TEXT_NODE) {\n // The styles are applied to the node completelly.\n if (startOffset === 0 && endOffset === endNode.nodeValue.length) {\n const paragraph = this.startParagraph;\n const inline = this.startInline;\n setParagraphStyles(paragraph, newStyles);\n setInlineStyles(inline, newStyles);\n\n // The styles are applied to a part of the node.\n } else if (startOffset !== endOffset) {\n const paragraph = this.startParagraph;\n setParagraphStyles(paragraph, newStyles);\n const inline = this.startInline;\n const midText = startNode.splitText(startOffset);\n const endText = midText.splitText(endOffset - startOffset);\n const midInline = createInlineFrom(inline, midText, newStyles);\n inline.after(midInline);\n if (endText.length > 0) {\n const endInline = createInline(endText, inline.style);\n midInline.after(endInline);\n }\n\n // FIXME: This can change focus <-> anchor order.\n this.setSelection(midText, 0, midText, midText.nodeValue.length);\n\n // The styles are applied to the paragraph.\n } else {\n const paragraph = this.startParagraph;\n setParagraphStyles(paragraph, newStyles);\n }\n return this.#notifyStyleChange();\n\n // If the startContainer and endContainer are different\n // then we need to iterate through those nodes to apply\n // the styles.\n } else if (startNode !== endNode) {\n SafeGuard.start();\n const expectedEndNode = getClosestTextNode(endNode);\n this.#textNodeIterator.currentNode = getClosestTextNode(startNode);\n do {\n SafeGuard.update();\n\n const paragraph = getParagraph(this.#textNodeIterator.currentNode);\n setParagraphStyles(paragraph, newStyles);\n const inline = getInline(this.#textNodeIterator.currentNode);\n // If we're at the start node and offset is greater than 0\n // then we should split the inline and apply styles to that\n // new inline.\n if (\n this.#textNodeIterator.currentNode === startNode &&\n startOffset > 0\n ) {\n const newInline = splitInline(inline, startOffset);\n setInlineStyles(newInline, newStyles);\n inline.after(newInline);\n // If we're at the start node and offset is equal to 0\n // or current node is different to start node and\n // different to end node or we're at the end node\n // and the offset is equalto the node length\n } else if (\n (this.#textNodeIterator.currentNode === startNode &&\n startOffset === 0) ||\n (this.#textNodeIterator.currentNode !== startNode &&\n this.#textNodeIterator.currentNode !== endNode) ||\n (this.#textNodeIterator.currentNode === endNode &&\n endOffset === endNode.nodeValue.length)\n ) {\n setInlineStyles(inline, newStyles);\n\n // If we're at end node\n } else if (\n this.#textNodeIterator.currentNode === endNode &&\n endOffset < endNode.nodeValue.length\n ) {\n const newInline = splitInline(inline, endOffset);\n setInlineStyles(inline, newStyles);\n inline.after(newInline);\n }\n\n // We've reached the final node so we can return safely.\n if (this.#textNodeIterator.currentNode === expectedEndNode) return;\n\n this.#textNodeIterator.nextNode();\n } while (this.#textNodeIterator.currentNode);\n }\n\n return this.#notifyStyleChange();\n }\n\n /**\n * Applies styles to selection\n *\n * @param {Object.} newStyles\n * @returns {void}\n */\n applyStyles(newStyles) {\n return this.#applyStylesTo(\n this.startContainer,\n this.startOffset,\n this.endContainer,\n this.endOffset,\n newStyles\n );\n }\n}\n\nexport default SelectionController;\n", "/**\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at http://mozilla.org/MPL/2.0/.\n *\n * Copyright (c) KALEIDOS INC\n */\n\n/**\n * Creates a new selection imposter from a list of client rects.\n *\n * @param {DOMRect} referenceRect\n * @param {DOMRectList} clientRects\n * @returns {DocumentFragment}\n */\nexport function createSelectionImposterFromClientRects(\n referenceRect,\n clientRects\n) {\n const fragment = document.createDocumentFragment();\n for (const rect of clientRects) {\n const rectElement = document.createElement(\"div\");\n rectElement.className = \"selection-imposter-rect\";\n rectElement.style.left = `${rect.x - referenceRect.x}px`;\n rectElement.style.top = `${rect.y - referenceRect.y}px`;\n rectElement.style.width = `${rect.width}px`;\n rectElement.style.height = `${rect.height}px`;\n fragment.appendChild(rectElement);\n }\n return fragment;\n}\n", "/**\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at http://mozilla.org/MPL/2.0/.\n *\n * Copyright (c) KALEIDOS INC\n */\n\n/**\n * Adds a series of listeners.\n *\n * @param {EventTarget} target\n * @param {Object.} object\n * @param {EventListenerOptions} [options]\n */\nexport function addEventListeners(target, object, options) {\n Object.entries(object).forEach(([type, listener]) =>\n target.addEventListener(type, listener, options)\n );\n}\n\n/**\n * Removes a series of listeners.\n *\n * @param {EventTarget} target\n * @param {Object.} object\n */\nexport function removeEventListeners(target, object) {\n Object.entries(object).forEach(([type, listener]) =>\n target.removeEventListener(type, listener)\n );\n}\n", "/**\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at http://mozilla.org/MPL/2.0/.\n *\n * Copyright (c) KALEIDOS INC\n */\n\n/**\n * Enumeration of types of layout.\n *\n * @enum {string}\n */\nexport const LayoutType = {\n FULL: \"full\",\n PARTIAL: \"partial\",\n};\n\nexport default LayoutType;\n", "/**\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at http://mozilla.org/MPL/2.0/.\n *\n * Copyright (c) KALEIDOS INC\n */\n\nimport clipboard from \"./clipboard/index.js\";\nimport commands from \"./commands/index.js\";\nimport ChangeController from './controllers/ChangeController.js';\nimport SelectionController from './controllers/SelectionController.js';\nimport { createSelectionImposterFromClientRects } from './selection/Imposter.js';\nimport { addEventListeners, removeEventListeners } from \"./Event\";\nimport { createRoot, createEmptyRoot } from './content/dom/Root.js';\nimport { createParagraph } from './content/dom/Paragraph.js';\nimport { createEmptyInline, createInline } from './content/dom/Inline.js';\nimport { isLineBreak } from './content/dom/LineBreak.js';\nimport LayoutType from './layout/LayoutType.js';\n\n/**\n * Text Editor.\n */\nexport class TextEditor extends EventTarget {\n /**\n * Element content editable to be used by the TextEditor\n *\n * @type {HTMLElement}\n */\n #element = null;\n\n /**\n * Map/Dictionary of events.\n *\n * @type {Object.}\n */\n #events = null;\n\n /**\n * Root element that will contain the content.\n *\n * @type {HTMLElement}\n */\n #root = null;\n\n /**\n * Change controller controls when we should notify changes.\n *\n * @type {ChangeController}\n */\n #changeController = null;\n\n /**\n * Selection controller controls the current/saved selection.\n *\n * @type {SelectionController}\n */\n #selectionController = null;\n\n /**\n * Selection imposter keeps selection elements.\n *\n * @type {HTMLElement}\n */\n #selectionImposterElement = null;\n\n /**\n * Style defaults.\n *\n * @type {Object.}\n */\n #styleDefaults = null;\n\n /**\n * Constructor.\n *\n * @param {HTMLElement} element\n */\n constructor(element, options) {\n super();\n if (!(element instanceof HTMLElement))\n throw new TypeError(\"Invalid text editor element\");\n\n this.#element = element;\n this.#selectionImposterElement = options?.selectionImposterElement;\n this.#events = {\n blur: this.#onBlur,\n focus: this.#onFocus,\n\n paste: this.#onPaste,\n cut: this.#onCut,\n copy: this.#onCopy,\n\n beforeinput: this.#onBeforeInput,\n input: this.#onInput,\n };\n this.#styleDefaults = options?.styleDefaults;\n this.#setup(options);\n }\n\n /**\n * Setups editor properties.\n */\n #setupElementProperties() {\n if (!this.#element.isContentEditable) {\n this.#element.contentEditable = \"true\";\n // In `jsdom` it isn't enough to set the attribute 'contentEditable'\n // to `true` to work.\n // FIXME: Remove this when `jsdom` implements this interface.\n if (!this.#element.isContentEditable) {\n this.#element.setAttribute(\"contenteditable\", \"true\");\n }\n }\n if (this.#element.spellcheck) this.#element.spellcheck = false;\n if (this.#element.autocapitalize) this.#element.autocapitalize = false;\n if (!this.#element.autofocus) this.#element.autofocus = true;\n if (!this.#element.role || this.#element.role !== \"textbox\")\n this.#element.role = \"textbox\";\n if (this.#element.ariaAutoComplete) this.#element.ariaAutoComplete = false;\n if (!this.#element.ariaMultiLine) this.#element.ariaMultiLine = true;\n this.#element.dataset.itype = \"editor\";\n }\n\n /**\n * Setups the root element.\n */\n #setupRoot() {\n this.#root = createEmptyRoot(this.#styleDefaults);\n this.#element.appendChild(this.#root);\n }\n\n /**\n * Dispatchs a `change` event.\n *\n * @param {CustomEvent} e\n * @returns {void}\n */\n #onChange = (e) => this.dispatchEvent(new e.constructor(e.type, e));\n\n /**\n * Dispatchs a `stylechange` event.\n *\n * @param {CustomEvent} e\n * @returns {void}\n */\n #onStyleChange = (e) => {\n if (this.#selectionImposterElement.children.length > 0) {\n // We need to recreate the selection imposter when we've\n // already have one.\n this.#createSelectionImposter();\n }\n this.dispatchEvent(new e.constructor(e.type, e));\n };\n\n /**\n * Setups the elements, the properties and the\n * initial content.\n */\n #setup(options) {\n this.#setupElementProperties();\n this.#setupRoot();\n this.#changeController = new ChangeController(this);\n this.#changeController.addEventListener(\"change\", this.#onChange);\n this.#selectionController = new SelectionController(\n this,\n document.getSelection(),\n options\n );\n this.#selectionController.addEventListener(\n \"stylechange\",\n this.#onStyleChange\n );\n addEventListeners(this.#element, this.#events, {\n capture: true\n });\n }\n\n /**\n * Creates the selection imposter.\n */\n #createSelectionImposter() {\n // We only create a selection imposter if there's any selection\n // and if there is a selection imposter element to attach the\n // rects.\n if (\n this.#selectionImposterElement &&\n !this.#selectionController.isCollapsed\n ) {\n const rects = this.#selectionController.range?.getClientRects();\n if (rects) {\n const rect = this.#selectionImposterElement.getBoundingClientRect();\n this.#selectionImposterElement.replaceChildren(\n createSelectionImposterFromClientRects(rect, rects)\n );\n }\n }\n }\n\n /**\n * On blur we create a new FakeSelection if there's any.\n *\n * @param {FocusEvent} e\n */\n #onBlur = (e) => {\n this.#changeController.notifyImmediately();\n this.#selectionController.saveSelection();\n this.#createSelectionImposter();\n this.dispatchEvent(new FocusEvent(e.type, e));\n };\n\n /**\n * On focus we should restore the FakeSelection from the current\n * selection.\n *\n * @param {FocusEvent} e\n */\n #onFocus = (e) => {\n this.#selectionController.restoreSelection();\n if (this.#selectionImposterElement) {\n this.#selectionImposterElement.replaceChildren();\n }\n this.dispatchEvent(new FocusEvent(e.type, e));\n };\n\n /**\n * Event called when the user pastes some text into the\n * editor.\n *\n * @param {ClipboardEvent} e\n */\n #onPaste = (e) => clipboard.paste(e, this, this.#selectionController);\n\n /**\n * Event called when the user cuts some text from the\n * editor.\n *\n * @param {ClipboardEvent} e\n */\n #onCut = (e) => clipboard.cut(e, this, this.#selectionController);\n\n /**\n * Event called when the user copies some text from the\n * editor.\n *\n * @param {ClipboardEvent} e\n */\n #onCopy = (e) => clipboard.copy(e, this, this.#selectionController);\n\n /**\n * Event called before the DOM is modified.\n *\n * @param {InputEvent} e\n */\n #onBeforeInput = (e) => {\n if (e.inputType === \"historyUndo\"\n || e.inputType === \"historyRedo\") {\n return;\n }\n\n if (!(e.inputType in commands)) {\n if (e.inputType !== \"insertCompositionText\") {\n e.preventDefault();\n }\n return;\n }\n\n if (e.inputType in commands) {\n const command = commands[e.inputType];\n if (!this.#selectionController.startMutation())\n return;\n\n command(e, this, this.#selectionController);\n const mutations = this.#selectionController.endMutation();\n this.#notifyLayout(LayoutType.FULL, mutations);\n }\n };\n\n /**\n * Event called after the DOM is modified.\n *\n * @param {InputEvent} e\n */\n #onInput = (e) => {\n if (e.inputType === \"historyUndo\" || e.inputType === \"historyRedo\") {\n return;\n }\n\n if (e.inputType === \"insertCompositionText\") {\n this.#notifyLayout(LayoutType.FULL, null);\n }\n };\n\n /**\n * Notifies that the edited texts needs layout.\n *\n * @param {'full'|'partial'} type\n * @param {CommandMutations} mutations\n */\n #notifyLayout(type = LayoutType.FULL, mutations) {\n this.dispatchEvent(\n new CustomEvent(\"needslayout\", {\n detail: {\n type: type,\n mutations: mutations,\n },\n })\n );\n }\n\n /**\n * Root element that contains all the paragraphs.\n *\n * @type {HTMLDivElement}\n */\n get root() {\n return this.#root;\n }\n\n set root(newRoot) {\n const previousRoot = this.#root;\n this.#root = newRoot;\n previousRoot.replaceWith(newRoot);\n }\n\n /**\n * Element that contains the root and that has the\n * contenteditable attribute.\n *\n * @type {HTMLElement}\n */\n get element() {\n return this.#element;\n }\n\n /**\n * Returns true if the content is in an empty state.\n *\n * @type {boolean}\n */\n get isEmpty() {\n return (\n this.#root.children.length === 1 &&\n this.#root.firstElementChild.children.length === 1 &&\n isLineBreak(this.#root.firstElementChild.firstElementChild.firstChild)\n );\n }\n\n /**\n * Indicates the amount of paragraphs in the current content.\n *\n * @type {number}\n */\n get numParagraphs() {\n return this.#root.children.length;\n }\n\n /**\n * CSS Style declaration for the current inline. From here we\n * can infer root, paragraph and inline declarations.\n *\n * @type {CSSStyleDeclaration}\n */\n get currentStyle() {\n return this.#selectionController.currentStyle;\n }\n\n /**\n * Focus the element\n */\n focus() {\n return this.#element.focus();\n }\n\n /**\n * Blurs the element\n */\n blur() {\n return this.#element.blur();\n }\n\n /**\n * Creates a new root.\n *\n * @param {...any} args\n * @returns {HTMLDivElement}\n */\n createRoot(...args) {\n return createRoot(...args);\n }\n\n /**\n * Creates a new paragraph.\n *\n * @param {...any} args\n * @returns {HTMLDivElement}\n */\n createParagraph(...args) {\n return createParagraph(...args);\n }\n\n /**\n * Creates a new inline from a string.\n *\n * @param {string} text\n * @param {Object.|CSSStyleDeclaration} styles\n * @returns {HTMLSpanElement}\n */\n createInlineFromString(text, styles) {\n if (text === \"\") {\n return createEmptyInline(styles);\n }\n return createInline(new Text(text), styles);\n }\n\n /**\n * Creates a new inline.\n *\n * @param {...any} args\n * @returns {HTMLSpanElement}\n */\n createInline(...args) {\n return createInline(...args);\n }\n\n /**\n * Applies the current styles to the selection or\n * the current DOM node at the caret.\n *\n * @param {*} styles\n */\n applyStylesToSelection(styles) {\n this.#selectionController.startMutation();\n this.#selectionController.applyStyles(styles);\n const mutations = this.#selectionController.endMutation();\n this.#notifyLayout(LayoutType.FULL, mutations);\n this.#changeController.notifyImmediately();\n return this;\n }\n\n /**\n * Selects all content.\n */\n selectAll() {\n this.#selectionController.selectAll();\n return this;\n }\n\n /**\n * Moves cursor to end.\n *\n * @returns\n */\n cursorToEnd() {\n this.#selectionController.cursorToEnd();\n return this;\n }\n\n /**\n * Disposes everything.\n */\n dispose() {\n this.#changeController.removeEventListener(\"change\", this.#onChange);\n this.#changeController.dispose();\n this.#changeController = null;\n this.#selectionController.removeEventListener(\n \"stylechange\",\n this.#onStyleChange\n );\n this.#selectionController.dispose();\n this.#selectionController = null;\n removeEventListeners(this.#element, this.#events);\n this.#element = null;\n this.#root = null;\n }\n}\n\nexport function isEditor(instance) {\n return (instance instanceof TextEditor);\n}\n\n/* Convenience function based API for Text Editor */\nexport function getRoot(instance) {\n if (isEditor(instance)) {\n return instance.root;\n } else {\n return null;\n }\n}\n\nexport function setRoot(instance, root) {\n if (isEditor(instance)) {\n instance.root = root;\n }\n\n return instance;\n}\n\nexport function create(element, options) {\n return new TextEditor(element, {...options});\n}\n\nexport function getCurrentStyle(instance) {\n if (isEditor(instance)) {\n return instance.currentStyle;\n }\n}\n\nexport function applyStylesToSelection(instance, styles) {\n if (isEditor(instance)) {\n return instance.applyStylesToSelection(styles);\n }\n}\n\nexport function dispose(instance) {\n if (isEditor(instance)) {\n instance.dispose();\n }\n}\n\nexport default TextEditor;\n"], + "mappings": "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", + "names": ["copy", "event", "editor", "cut", "event", "editor", "canvas", "context", "getContext", "createCanvas", "width", "height", "getByteAsHex", "byte", "getColor", "fillStyle", "imageData", "r", "g", "b", "a", "getFills", "color", "opacity", "mergeStyleDeclarations", "target", "source", "index", "styleName", "getComputedStyle", "element", "inertElement", "currentElement", "newValue", "normalizeStyles", "styleDeclaration", "color", "getFills", "fontFamily", "fontId", "setStyle", "styleValue", "styleUnit", "getStyleFromDeclaration", "style", "setStylesFromObject", "element", "allowedStyles", "styleObject", "styleName", "styleUnit", "styleValue", "setStyle", "setStylesFromDeclaration", "styleDeclaration", "getStyleFromDeclaration", "setStyles", "styleObjectOrDeclaration", "mergeStyles", "allowedStyles", "styleDeclaration", "newStyles", "mergedStyles", "styleName", "styleUnit", "getStyleFromDeclaration", "isDisplayBlock", "style", "createRandomId", "createElement", "tag", "options", "element", "name", "value", "setStyles", "isElement", "nodeName", "isOffsetAtStart", "node", "offset", "isOffsetAtEnd", "TAG", "createLineBreak", "isLineBreak", "node", "TAG", "TYPE", "QUERY", "STYLES", "isInline", "node", "isElement", "isLikeInline", "element", "createInline", "textOrLineBreak", "styles", "attrs", "createElement", "createRandomId", "createInlineFrom", "inline", "mergeStyles", "createEmptyInline", "createLineBreak", "setInlineStyles", "setStyles", "getInline", "isInlineStart", "offset", "isOffsetAtStart", "isInlineEnd", "isOffsetAtEnd", "splitInline", "textNode", "style", "newTextNode", "getInlinesFrom", "startInline", "inlines", "currentInline", "index", "getInlineLength", "isLineBreak", "TAG", "TYPE", "QUERY", "STYLES", "isRoot", "node", "isElement", "createRoot", "paragraphs", "styles", "attrs", "isParagraph", "createElement", "createRandomId", "createEmptyRoot", "createEmptyParagraph", "setRootStyles", "element", "setStyles", "isTextNode", "node", "isLineBreak", "getTextNodeLength", "node", "isLineBreak", "getClosestTextNode", "isTextNode", "isInline", "isParagraph", "isRoot", "TAG", "TYPE", "QUERY", "STYLES", "isLikeParagraph", "element", "isLikeInline", "isEmptyParagraph", "isParagraph", "inline", "isInline", "isLineBreak", "node", "isElement", "createParagraph", "inlines", "styles", "attrs", "createElement", "createRandomId", "createEmptyParagraph", "createEmptyInline", "setParagraphStyles", "setStyles", "getParagraph", "paragraph", "isParagraphStart", "offset", "getInline", "isOffsetAtStart", "isParagraphEnd", "isOffsetAtEnd", "splitParagraph", "style", "isInlineEnd", "getInlinesFrom", "newInline", "splitInline", "mergeParagraphs", "a", "b", "mapContentFragmentFromDocument", "document", "root", "styleDefaults", "nodeIterator", "fragment", "currentParagraph", "currentNode", "parentStyle", "normalizeStyles", "mergeStyleDeclarations", "getComputedStyle", "isDisplayBlock", "isLikeParagraph", "createParagraph", "createInline", "mapContentFragmentFromHTML", "html", "htmlDocument", "mapContentFragmentFromString", "string", "lines", "line", "createEmptyParagraph", "paste", "event", "editor", "selectionController", "fragment", "html", "mapContentFragmentFromHTML", "plain", "mapContentFragmentFromString", "clipboard_default", "copy", "cut", "paste", "insertText", "event", "editor", "selectionController", "insertParagraph", "event", "editor", "selectionController", "deleteByCut", "event", "editor", "selectionController", "deleteContentBackward", "event", "editor", "selectionController", "deleteContentForward", "event", "editor", "selectionController", "commands_default", "insertText", "insertParagraph", "deleteByCut", "deleteContentBackward", "deleteContentForward", "_timeout", "_time", "_hasPendingChanges", "_onTimeout", "ChangeController", "time", "__privateAdd", "__privateSet", "__privateGet", "ChangeController_default", "tryOffset", "offset", "tryString", "str", "insertInto", "text", "replaceWith", "startOffset", "endOffset", "removeBackward", "removeForward", "removeSlice", "start", "end", "TextNodeIteratorDirection", "_rootNode", "_currentNode", "_TextNodeIterator", "rootNode", "__privateAdd", "__privateSet", "node", "startNode", "skipNodes", "direction", "safeGuard", "currentNode", "backTrack", "found", "__privateGet", "newCurrentNode", "isContained", "nextNode", "previousNode", "TextNodeIterator", "TextNodeIterator_default", "_added", "_removed", "_updated", "CommandMutations", "added", "updated", "removed", "__privateAdd", "__privateSet", "__privateGet", "node", "CommandMutations_default", "SelectionDirection", "startTime", "start", "update", "SafeGuard_default", "_textEditor", "_selection", "_ranges", "_range", "_focusNode", "_focusOffset", "_anchorNode", "_anchorOffset", "_savedSelection", "_textNodeIterator", "_currentStyle", "_inertElement", "_debug", "_mutations", "_styleDefaults", "_SelectionController_instances", "applyDefaultStylesToCurrentStyle_fn", "applyStylesToCurrentStyle_fn", "updateCurrentStyle_fn", "_onSelectionChange", "notifyStyleChange_fn", "setup_fn", "getSavedRange_fn", "applyStylesTo_fn", "SelectionController", "textEditor", "selection", "options", "__privateAdd", "CommandMutations_default", "e", "focusNodeChanges", "anchorNodeChanges", "__privateGet", "__privateSet", "index", "range", "__privateMethod", "TextNodeIterator_default", "node", "offset", "nodeOffset", "anchorNode", "anchorOffset", "focusNode", "focusOffset", "SelectionDirection", "getParagraph", "getInline", "startContainer", "endContainer", "isInline", "isParagraph", "isLineBreak", "isInlineStart", "isInlineEnd", "isParagraphStart", "isParagraphEnd", "fragment", "numParagraphs", "newParagraph", "splitParagraph", "text", "newText", "removedData", "removeForward", "paragraph", "inline", "nextTextNode", "lineBreak", "createLineBreak", "removeBackward", "previousTextNode", "getTextNodeLength", "insertInto", "startOffset", "endOffset", "replaceWith", "currentParagraph", "newTextNode", "createInline", "child", "createEmptyParagraph", "currentInline", "previousParagraph", "paragraphToBeRemoved", "previousInline", "previousOffset", "getInlineLength", "isEmptyParagraph", "mergeParagraphs", "nextParagraph", "nextInline", "nextOffset", "affectedParagraphs", "affectedInlines", "startNode", "getClosestTextNode", "endNode", "previousNode", "nextNode", "newNodeValue", "removeSlice", "startInline", "startParagraph", "endInline", "endParagraph", "SafeGuard_default", "currentNode", "shouldRemoveNodeCompletely", "isTextNode", "mergedParagraph", "newEmptyInline", "createEmptyInline", "newStyles", "name", "value", "element", "styleName", "styleValue", "root", "setRootStyles", "setParagraphStyles", "setInlineStyles", "midText", "endText", "midInline", "createInlineFrom", "expectedEndNode", "newInline", "splitInline", "SelectionController_default", "createSelectionImposterFromClientRects", "referenceRect", "clientRects", "fragment", "rect", "rectElement", "addEventListeners", "target", "object", "options", "type", "listener", "removeEventListeners", "LayoutType", "LayoutType_default", "_element", "_events", "_root", "_changeController", "_selectionController", "_selectionImposterElement", "_styleDefaults", "_TextEditor_instances", "setupElementProperties_fn", "setupRoot_fn", "_onChange", "_onStyleChange", "setup_fn", "createSelectionImposter_fn", "_onBlur", "_onFocus", "_onPaste", "_onCut", "_onCopy", "_onBeforeInput", "_onInput", "notifyLayout_fn", "TextEditor", "element", "options", "__privateAdd", "e", "__privateGet", "__privateMethod", "clipboard_default", "commands_default", "command", "mutations", "LayoutType_default", "__privateSet", "newRoot", "previousRoot", "isLineBreak", "args", "createRoot", "createParagraph", "text", "styles", "createEmptyInline", "createInline", "removeEventListeners", "createEmptyRoot", "ChangeController_default", "SelectionController_default", "addEventListeners", "rects", "rect", "createSelectionImposterFromClientRects", "type", "isEditor", "instance", "getRoot", "setRoot", "root", "create", "getCurrentStyle", "applyStylesToSelection", "dispose", "TextEditor_default"] +}