{"version":3,"file":"index.cjs","sources":["../src/actions/Action.ts","../src/actions/CaretAction.ts","../node_modules/deepmerge/dist/cjs.js","../src/actions/toolbar/Toolbar.ts","../src/tooltip/TooltipContainPosition.ts","../src/blots/Image.ts","../src/actions/align/AlignFormats.ts","../src/blots/Video.ts","../src/actions/align/DefaultAligner.ts","../src/actions/toolbar/ToolbarButton.ts","../src/actions/align/AlignAction.ts","../src/actions/DeleteAction.ts","../src/actions/ResizeAction.ts","../src/specs/BlotSpec.ts","../src/specs/UnclickableBlotSpec.ts","../src/specs/IframeVideoSpec.ts","../src/actions/AttributeAction.ts","../src/actions/CompressAction.ts","../src/actions/LinkAction.ts","../src/specs/ImageSpec.ts","../src/DefaultOptions.ts","../src/BlotFormatter.ts"],"sourcesContent":["import BlotFormatter from '../BlotFormatter';\nimport ToolbarButton from './toolbar/ToolbarButton';\n\n/**\n * Represents a base class for actions used within the BlotFormatter.\n * \n * This class provides a structure for actions that can be performed by the formatter,\n * including lifecycle hooks for creation, destruction, and updates. Subclasses should\n * override the lifecycle methods as needed.\n * \n * @remarks\n * - Each action holds a reference to the parent `BlotFormatter` instance.\n * - Actions can define their own toolbar buttons by populating the `toolbarButtons` array.\n * - Debug logging is available if the formatter's options enable it.\n * \n * @example\n * ```typescript\n * class CustomAction extends Action {\n *   onCreate = (): void => {\n *     // Custom initialization logic\n *   }\n *   onDestroy = (): void => {\n *     // Custom destruction logic\n *   }\n *   onUpdate = (): void => {\n *     // Custom update logic\n *   }\n * }\n * ```\n * \n * @public\n */\nexport default class Action {\n  formatter: BlotFormatter;\n  toolbarButtons: ToolbarButton[] = [];\n  debug: boolean;\n\n  constructor(formatter: BlotFormatter) {\n    this.formatter = formatter;\n    this.debug = this.formatter.options.debug || false;\n    if (this.debug) console.debug('Action created:', this.constructor.name);\n  }\n\n  /**\n   * Called when the action is created.\n   * Override this method to implement custom initialization logic.\n   */\n  onCreate = (): void => {}\n\n  /**\n   * Called when the action is being destroyed.\n   * Override this method to implement custom cleanup logic.\n   */\n  onDestroy = (): void => {}\n\n  /**\n   * Called when the action should be updated.\n   * Override this method to implement custom update logic.\n   */\n  onUpdate = (): void => {}\n\n}\n","import Action from './Action';\r\nimport { Blot } from '../specs/BlotSpec';\r\n\r\n/**\r\n * Provides caret (text cursor) manipulation actions for the Quill editor, including moving the caret\r\n * backward, placing the caret before or after a specified blot, and handling keyboard navigation events.\r\n *\r\n * This class is designed to work with the Quill editor and the Blot Formatter overlay, enabling precise\r\n * caret placement and navigation around custom blots (such as images or embeds) within the editor.\r\n *\r\n * @remarks\r\n * - Integrates with the Quill editor instance and its formatting specifications.\r\n * - Handles keyboard events to facilitate intuitive caret movement for users.\r\n * - Ensures proper event listener management to prevent memory leaks.\r\n *\r\n * @public\r\n */\r\nexport default class CaretAction extends Action {\r\n\r\n  /**\r\n   * Moves the caret (text cursor) backward by a specified number of characters within the current selection.\r\n   *\r\n   * If the caret is at the beginning of a text node, it attempts to move to the end of the previous sibling text node.\r\n   * If there is no previous sibling or the selection is not valid, the caret position remains unchanged.\r\n   *\r\n   * @param n - The number of characters to move the caret back. Defaults to 1.\r\n   */\r\n  static sendCaretBack = (n = 1, debug = false): void => {\r\n    const selection = window.getSelection();\r\n    if (selection && selection.rangeCount > 0) {\r\n      const range = selection.getRangeAt(0);\r\n      const currentNode = range.startContainer;\r\n      const currentOffset = range.startOffset;\r\n\r\n      // Move the cursor back by n characters\r\n      if (currentOffset > 0) {\r\n        range.setStart(currentNode, currentOffset - n);\r\n      } else if (currentNode.previousSibling) {\r\n        // Move to end of previous text node\r\n        const prevNode = currentNode.previousSibling;\r\n        if (prevNode.nodeType === Node.TEXT_NODE) {\r\n          range.setStart(prevNode, prevNode.textContent?.length || 0);\r\n        }\r\n      }\r\n\r\n      range.collapse(true);\r\n      selection.removeAllRanges();\r\n      selection.addRange(range);\r\n      if (debug) {\r\n        console.debug('Caret moved back by', n, 'characters');\r\n      }\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Places the caret (text cursor) immediately before the specified blot in the Quill editor.\r\n   *\r\n   * @param quill - The Quill editor instance.\r\n   * @param targetBlot - The blot before which the caret should be placed.\r\n   */\r\n  static placeCaretBeforeBlot = (quill: any, targetBlot: Blot, debug = false): void => {\r\n    const index = quill.getIndex(targetBlot);\r\n    quill.setSelection(index, 0, \"user\");\r\n    if (debug) {\r\n      console.debug('Caret placed before blot at index:', index, targetBlot);\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Places the caret (text cursor) immediately after the specified blot in the Quill editor.\r\n   *\r\n   * This method first clears any existing selection and ensures the editor is focused.\r\n   * It then calculates the index of the target blot and determines whether it is the last blot in the document.\r\n   * - If the target blot is the last one, the caret is placed at the very end of the document.\r\n   * - Otherwise, the caret is positioned just after the target blot, using a combination of Quill's selection API\r\n   *   and a native browser adjustment to avoid placing the caret inside a formatting span wrapper.\r\n   *\r\n   * @param quill - The Quill editor instance.\r\n   * @param targetBlot - The blot after which the caret should be placed.\r\n   */\r\n  static placeCaretAfterBlot = (quill: any, targetBlot: Blot, debug = false): void => {\r\n    quill.setSelection(null); // Clear selection first\r\n    quill.root.focus(); // Ensure the editor is focused\r\n    const index = quill.getIndex(targetBlot);\r\n    const documentLength = quill.getLength();\r\n\r\n    // Check if this is the last blot in the document\r\n    if (index + 1 >= documentLength - 1) {\r\n      // For the last blot, place cursor at the very end\r\n      quill.setSelection(documentLength - 1, 0, \"user\");\r\n      if (debug) {\r\n        console.debug('Caret placed at the end of the document after blot:', targetBlot);\r\n      }\r\n    } else {\r\n      // overshoot by one then use native browser API to send caret back one\r\n      // without this, caret will be placed inside formatting span wrapper\r\n      if (debug) {\r\n        console.debug('Caret placed after character following blot at index:', index, targetBlot);\r\n      } \r\n      quill.setSelection(index + 2, 0, \"user\");\r\n      this.sendCaretBack(1, debug); // Move cursor back by 1 character\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Initializes event listeners for the CaretAction.\r\n   *\r\n   * Adds a 'keyup' event listener to the document and an 'input' event listener\r\n   * to the Quill editor's root element. Both listeners trigger the `onKeyUp` handler.\r\n   *\r\n   * @remarks\r\n   * This method should be called when the action is created to ensure proper\r\n   * caret handling and formatting updates in response to user input.\r\n   */\r\n  onCreate = (): void => {\r\n    document.addEventListener('keyup', this.onKeyUp);\r\n    this.formatter.quill.root.addEventListener('input', this.onKeyUp);\r\n  }\r\n\r\n  /**\r\n   * Cleans up event listeners attached by this action.\r\n   *\r\n   * Removes the 'keyup' event listener from the document and the 'input' event listener\r\n   * from the Quill editor's root element to prevent memory leaks and unintended behavior\r\n   * after the action is destroyed.\r\n   */\r\n  onDestroy = (): void => {\r\n    document.removeEventListener('keyup', this.onKeyUp);\r\n    this.formatter.quill.root.removeEventListener('input', this.onKeyUp);\r\n  }\r\n\r\n  /**\r\n   * Handles the keyup event for caret navigation around a target blot in the editor.\r\n   *\r\n   * - If a modal is open or there is no current formatting specification, the handler exits early.\r\n   * - If the left arrow key is pressed, places the caret before the target blot and hides the formatter UI.\r\n   * - If the right arrow key is pressed, places the caret after the target blot and hides the formatter UI.\r\n   *\r\n   * @param e - The keyboard event triggered by the user's keyup action.\r\n   */\r\n  onKeyUp = (e: KeyboardEvent) => {\r\n    const modalOpen: boolean = !!document.querySelector('[data-blot-formatter-modal]')\r\n    if (!this.formatter.currentSpec || modalOpen) {\r\n      return;\r\n    }\r\n    const targetBlot = this.formatter.currentSpec.getTargetBlot();\r\n    if (!targetBlot) {\r\n      return;\r\n    }\r\n\r\n    // if left arrow, place cursor before targetBlot\r\n    // if right arrow, place cursor after targetBlot\r\n    if (e.code === 'ArrowLeft') {\r\n      CaretAction.placeCaretBeforeBlot(this.formatter.quill, targetBlot, this.debug);\r\n      this.formatter.hide();\r\n    } else if (e.code === 'ArrowRight') {\r\n      CaretAction.placeCaretAfterBlot(this.formatter.quill, targetBlot, this.debug);\r\n      this.formatter.hide();\r\n    }\r\n  };\r\n}\r\n","'use strict';\n\nvar isMergeableObject = function isMergeableObject(value) {\n\treturn isNonNullObject(value)\n\t\t&& !isSpecial(value)\n};\n\nfunction isNonNullObject(value) {\n\treturn !!value && typeof value === 'object'\n}\n\nfunction isSpecial(value) {\n\tvar stringValue = Object.prototype.toString.call(value);\n\n\treturn stringValue === '[object RegExp]'\n\t\t|| stringValue === '[object Date]'\n\t\t|| isReactElement(value)\n}\n\n// see https://github.com/facebook/react/blob/b5ac963fb791d1298e7f396236383bc955f916c1/src/isomorphic/classic/element/ReactElement.js#L21-L25\nvar canUseSymbol = typeof Symbol === 'function' && Symbol.for;\nvar REACT_ELEMENT_TYPE = canUseSymbol ? Symbol.for('react.element') : 0xeac7;\n\nfunction isReactElement(value) {\n\treturn value.$$typeof === REACT_ELEMENT_TYPE\n}\n\nfunction emptyTarget(val) {\n\treturn Array.isArray(val) ? [] : {}\n}\n\nfunction cloneUnlessOtherwiseSpecified(value, options) {\n\treturn (options.clone !== false && options.isMergeableObject(value))\n\t\t? deepmerge(emptyTarget(value), value, options)\n\t\t: value\n}\n\nfunction defaultArrayMerge(target, source, options) {\n\treturn target.concat(source).map(function(element) {\n\t\treturn cloneUnlessOtherwiseSpecified(element, options)\n\t})\n}\n\nfunction getMergeFunction(key, options) {\n\tif (!options.customMerge) {\n\t\treturn deepmerge\n\t}\n\tvar customMerge = options.customMerge(key);\n\treturn typeof customMerge === 'function' ? customMerge : deepmerge\n}\n\nfunction getEnumerableOwnPropertySymbols(target) {\n\treturn Object.getOwnPropertySymbols\n\t\t? Object.getOwnPropertySymbols(target).filter(function(symbol) {\n\t\t\treturn Object.propertyIsEnumerable.call(target, symbol)\n\t\t})\n\t\t: []\n}\n\nfunction getKeys(target) {\n\treturn Object.keys(target).concat(getEnumerableOwnPropertySymbols(target))\n}\n\nfunction propertyIsOnObject(object, property) {\n\ttry {\n\t\treturn property in object\n\t} catch(_) {\n\t\treturn false\n\t}\n}\n\n// Protects from prototype poisoning and unexpected merging up the prototype chain.\nfunction propertyIsUnsafe(target, key) {\n\treturn propertyIsOnObject(target, key) // Properties are safe to merge if they don't exist in the target yet,\n\t\t&& !(Object.hasOwnProperty.call(target, key) // unsafe if they exist up the prototype chain,\n\t\t\t&& Object.propertyIsEnumerable.call(target, key)) // and also unsafe if they're nonenumerable.\n}\n\nfunction mergeObject(target, source, options) {\n\tvar destination = {};\n\tif (options.isMergeableObject(target)) {\n\t\tgetKeys(target).forEach(function(key) {\n\t\t\tdestination[key] = cloneUnlessOtherwiseSpecified(target[key], options);\n\t\t});\n\t}\n\tgetKeys(source).forEach(function(key) {\n\t\tif (propertyIsUnsafe(target, key)) {\n\t\t\treturn\n\t\t}\n\n\t\tif (propertyIsOnObject(target, key) && options.isMergeableObject(source[key])) {\n\t\t\tdestination[key] = getMergeFunction(key, options)(target[key], source[key], options);\n\t\t} else {\n\t\t\tdestination[key] = cloneUnlessOtherwiseSpecified(source[key], options);\n\t\t}\n\t});\n\treturn destination\n}\n\nfunction deepmerge(target, source, options) {\n\toptions = options || {};\n\toptions.arrayMerge = options.arrayMerge || defaultArrayMerge;\n\toptions.isMergeableObject = options.isMergeableObject || isMergeableObject;\n\t// cloneUnlessOtherwiseSpecified is added to `options` so that custom arrayMerge()\n\t// implementations can use it. The caller may not replace it.\n\toptions.cloneUnlessOtherwiseSpecified = cloneUnlessOtherwiseSpecified;\n\n\tvar sourceIsArray = Array.isArray(source);\n\tvar targetIsArray = Array.isArray(target);\n\tvar sourceAndTargetTypesMatch = sourceIsArray === targetIsArray;\n\n\tif (!sourceAndTargetTypesMatch) {\n\t\treturn cloneUnlessOtherwiseSpecified(source, options)\n\t} else if (sourceIsArray) {\n\t\treturn options.arrayMerge(target, source, options)\n\t} else {\n\t\treturn mergeObject(target, source, options)\n\t}\n}\n\ndeepmerge.all = function deepmergeAll(array, options) {\n\tif (!Array.isArray(array)) {\n\t\tthrow new Error('first argument should be an array')\n\t}\n\n\treturn array.reduce(function(prev, next) {\n\t\treturn deepmerge(prev, next, options)\n\t}, {})\n};\n\nvar deepmerge_1 = deepmerge;\n\nmodule.exports = deepmerge_1;\n","import BlotFormatter from '../../BlotFormatter';\r\nimport ToolbarButton from './ToolbarButton';\r\n\r\n/**\r\n * Manages the creation, display, and destruction of a toolbar for BlotFormatter actions.\r\n *\r\n * The `Toolbar` class is responsible for rendering toolbar buttons associated with registered actions,\r\n * handling their lifecycle, and appending/removing the toolbar element from the formatter's overlay.\r\n *\r\n * @remarks\r\n * - The toolbar is initialized and shown via the `create()` method, which collects all action buttons and appends them to the DOM.\r\n * - The `destroy()` method cleans up the toolbar, removes it from the DOM, and destroys all associated buttons to prevent memory leaks.\r\n *\r\n * @example\r\n * ```typescript\r\n * const toolbar = new Toolbar(formatter);\r\n * toolbar.create(); // Show toolbar\r\n * // ... later\r\n * toolbar.destroy(); // Hide and clean up toolbar\r\n * ```\r\n *\r\n * @public\r\n */\r\nexport default class Toolbar {\r\n    formatter: BlotFormatter;\r\n    element: HTMLElement;\r\n    buttons: Record<string, ToolbarButton> = {};\r\n\r\n    constructor(formatter: BlotFormatter) {\r\n        this.formatter = formatter;\r\n        this.element = document.createElement('div');\r\n        this.element.classList.add(this.formatter.options.toolbar.mainClassName);\r\n        this.element.addEventListener('mousedown', (event: MouseEvent) => {\r\n            event.stopPropagation();\r\n        });\r\n        if (this.formatter.options.toolbar.mainStyle) {\r\n            Object.assign(this.element.style, this.formatter.options.toolbar.mainStyle);\r\n        }\r\n    }\r\n\r\n    /**\r\n     * Creates and appends toolbar action buttons to the toolbar element. \r\n     * Called by BlotFormatter.show() to initialize the toolbar.\r\n     * \r\n     * Iterates through all actions registered in the formatter, collects their toolbar buttons,\r\n     * stores each button in the `buttons` map by its action name, and appends the created button elements\r\n     * to the toolbar's DOM element. Finally, appends the toolbar element to the formatter's overlay.\r\n     */\r\n    create = (): void => {\r\n        const actionButtons: HTMLElement[] = [];\r\n        this.formatter.actions.forEach(action => {\r\n            action.toolbarButtons.forEach(button => {\r\n                this.buttons[button.action] = button;\r\n                actionButtons.push(button.create());\r\n            });\r\n        });\r\n        this.element.append(...actionButtons);\r\n        this.formatter.overlay.append(this.element);\r\n        if (this.formatter.options.debug) {\r\n            console.debug('Toolbar created with buttons:', Object.keys(this.buttons), actionButtons);\r\n        }\r\n    }\r\n\r\n    /**\r\n     * Cleans up the toolbar by removing its element from the overlay,\r\n     * destroying all associated buttons, and clearing internal references.\r\n     * Called by BlotFormatter.hide() to remove the toolbar from the DOM.\r\n     * \r\n     * This should be called when the toolbar is no longer needed to prevent memory leaks.\r\n     */\r\n    destroy = (): void => {\r\n        if (this.element) {\r\n            this.formatter.overlay.removeChild(this.element);\r\n        }\r\n        for (const button of Object.values(this.buttons)) {\r\n            button.destroy();\r\n        }\r\n        this.buttons = {};\r\n        this.element.innerHTML = '';\r\n        if (this.formatter.options.debug) {\r\n            console.debug('Toolbar destroyed');\r\n        }\r\n    }\r\n}","import type Quill from 'quill';\r\n\r\nexport default class TooltipContainPosition {\r\n  /**\r\n   * Repositions a tooltip element within a given container to ensure it does not overflow\r\n   * the container's boundaries. Adjusts the tooltip's `top` and `left` CSS properties if\r\n   * necessary to keep it fully visible. Optionally logs debug information about the repositioning.\r\n   *\r\n   * @param tooltip - The tooltip HTMLDivElement to reposition.\r\n   * @param container - The container HTMLElement within which the tooltip should remain visible.\r\n   * @param debug - If true, logs debug information to the console. Defaults to false.\r\n   */\r\n  private static _repositionTooltip = (tooltip: HTMLDivElement, container: HTMLElement, debug = false) => {\r\n    const tooltipRect = tooltip.getBoundingClientRect();\r\n    const containerRect = container.getBoundingClientRect();\r\n\r\n    // Calculate position relative to the container\r\n    let left = tooltipRect.left - containerRect.left;\r\n    let top = tooltipRect.top - containerRect.top;\r\n    const width = tooltipRect.width;\r\n    const height = tooltipRect.height;\r\n    const maxWidth = container.clientWidth;\r\n    const maxHeight = container.clientHeight;\r\n\r\n    let changed = false;\r\n    const newStyles: { top?: string; left?: string } = {};\r\n\r\n    // 1) Top overflow \r\n    if (top < 0) {\r\n      newStyles.top = `${tooltipRect.height}px`;\r\n      changed = true;\r\n    }\r\n\r\n    // 2) Bottom overflow\r\n    if (top + height > maxHeight) {\r\n      newStyles.top = `${maxHeight - height}px`;\r\n      changed = true;\r\n    }\r\n\r\n    // 3) Left overflow\r\n    if (left < 0) {\r\n      newStyles.left = '0px';\r\n      changed = true;\r\n    }\r\n\r\n    // 4) Right overflow\r\n    if (left + width > maxWidth) {\r\n      newStyles.left = `${maxWidth - width}px`;\r\n      changed = true;\r\n    }\r\n\r\n    if (changed) {\r\n      if (debug) {\r\n        console.debug('Repositioning tooltip', newStyles);\r\n      }\r\n\r\n      // Apply all style changes at once to minimize mutations\r\n      if (newStyles.top !== undefined) {\r\n        tooltip.style.top = newStyles.top;\r\n      }\r\n      if (newStyles.left !== undefined) {\r\n        tooltip.style.left = newStyles.left;\r\n      }\r\n\r\n      if (tooltip.classList.contains('ql-flip')) {\r\n        tooltip.classList.remove('ql-flip');\r\n      }\r\n    } else {\r\n      if (debug) {\r\n        console.debug('Tooltip position is fine, no changes needed');\r\n      }\r\n    }\r\n  }\r\n  \r\n  // Static property to store observers\r\n  private static observers = new WeakMap<HTMLDivElement, MutationObserver>();\r\n\r\n  /**\r\n   * Observes changes to the tooltip's attributes and triggers repositioning when necessary.\r\n   *\r\n   * @param quill - The Quill editor instance containing the tooltip.\r\n   * @param debug - Optional flag to enable debug logging of attribute mutations.\r\n   *\r\n   * @remarks\r\n   * Uses a MutationObserver to monitor changes to the tooltip's `style` and `class` attributes.\r\n   * When a mutation is detected, the tooltip is repositioned within the container.\r\n   * If `debug` is true, mutation details are logged to the console.\r\n   */\r\n  static watchTooltip(quill: Quill, debug = false): void {\r\n    const tooltip = quill.container.querySelector('.ql-tooltip') as HTMLDivElement;\r\n    const container = quill.container;\r\n    if (!tooltip) {\r\n      console.warn('No tooltip found to watch for adjustments.');\r\n      return;\r\n    }\r\n    // Clean up any existing observer for this tooltip\r\n    this.removeTooltipWatcher(tooltip, debug);\r\n\r\n    let isRepositioning = false;\r\n\r\n    const observer = new MutationObserver((mutations: MutationRecord[]) => {\r\n      // Ignore mutations caused by our own repositioning\r\n      if (isRepositioning) return;\r\n\r\n      if (debug) {\r\n        for (const m of mutations) {\r\n          console.debug('Tooltip mutation:', m.attributeName, tooltip.getAttribute(m.attributeName!));\r\n        }\r\n      }\r\n\r\n      isRepositioning = true;\r\n      this._repositionTooltip(tooltip, container, debug);\r\n      // Use setTimeout to reset the flag after the current execution context\r\n      setTimeout(() => {\r\n        isRepositioning = false;\r\n      }, 0);\r\n    });\r\n\r\n    observer.observe(tooltip, {\r\n      attributes: true,\r\n      attributeFilter: ['style', 'class'],\r\n    });\r\n\r\n    // Store the observer for later cleanup\r\n    this.observers.set(tooltip, observer);\r\n  }\r\n\r\n  /**\r\n   * Removes the MutationObserver for the specified tooltip element.\r\n   *\r\n   * @param tooltip - The HTMLDivElement or Quill instance to stop watching.\r\n   *                  If a Quill instance is provided, finds the tooltip within its container.\r\n   */\r\n  static removeTooltipWatcher(tooltip: HTMLDivElement | Quill, debug = false): void {\r\n    let tooltipElement: HTMLDivElement | null = null;\r\n\r\n    if (tooltip instanceof HTMLDivElement) {\r\n      tooltipElement = tooltip;\r\n    } else {\r\n      // Assume it's a Quill instance\r\n      tooltipElement = tooltip.container.querySelector('.ql-tooltip') as HTMLDivElement;\r\n    }\r\n\r\n    if (tooltipElement && this.observers.has(tooltipElement)) {\r\n      const observer = this.observers.get(tooltipElement);\r\n      observer?.disconnect();\r\n      this.observers.delete(tooltipElement);\r\n      if (debug) {\r\n        console.debug('Tooltip watcher removed for:', tooltipElement);\r\n      }\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Initializes the tooltip adjustment watcher when the action is created.\r\n   * Searches for the tooltip element within the Quill container and, if found,\r\n   * sets up observation for tooltip adjustments. Logs a warning if the tooltip\r\n   * element is not present.\r\n   *\r\n   * @remarks\r\n   * This method should be called during the creation lifecycle of the action.\r\n   */\r\n  constructor(private readonly quill: Quill, private readonly debug = false) {\r\n    const tooltip = quill.container.querySelector('.ql-tooltip') as HTMLDivElement;\r\n    console.debug('tooltip:', tooltip);\r\n    if (tooltip) {\r\n      TooltipContainPosition.watchTooltip(quill, debug);\r\n      if (debug) {\r\n        console.debug('Tooltip watcher initialized for:', tooltip);\r\n      }\r\n    } else {\r\n      console.warn('No tooltip found to watch for adjustments.');\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Cleans up resources when the action is destroyed.\r\n   * Specifically, it finds the tooltip element within the Quill editor container\r\n   * and removes its associated watcher if the tooltip exists.\r\n   */\r\n  destroy = (): void => {\r\n    const tooltip = this.quill.container.querySelector('.ql-tooltip') as HTMLDivElement;\r\n    if (tooltip) {\r\n      TooltipContainPosition.removeTooltipWatcher(this.quill, this.debug);\r\n      if (this.debug) {\r\n        console.debug('Tooltip watcher removed on destroy');\r\n      }\r\n    }\r\n  }\r\n\r\n}\r\n","\r\n/**\r\n * Factory function to create a custom Quill Image blot class supporting additional attributes.\r\n *\r\n * This function returns a class extending Quill's native Image blot, adding support for the `title` attribute\r\n * (in addition to `alt`, `height`, and `width`). The returned class overrides the static `formats` method\r\n * to extract these attributes from the DOM node, and the instance `format` method to set or remove them.\r\n *\r\n * @param QuillConstructor - The Quill constructor or instance used to import the base Image blot.\r\n * @returns A custom Image blot class supporting `alt`, `height`, `width`, and `title` attributes.\r\n *\r\n * @remarks\r\n * - This is useful for enabling the `title` attribute on images in Quill editors, which is not supported by default.\r\n * - See https://github.com/slab/quill/pull/4350 for related discussion.\r\n *\r\n * @example\r\n * ```typescript\r\n * const CustomImageBlot = createAltTitleImageBlotClass(Quill);\r\n * Quill.register(CustomImageBlot);\r\n * ```\r\n */\r\nexport const createAltTitleImageBlotClass = (QuillConstructor: any): any => {\r\n    const ImageBlot = QuillConstructor.import('formats/image') as any;\r\n\r\n    const ATTRIBUTES = ['alt', 'height', 'width', 'title'];\r\n\r\n    /**\r\n     * Represents a custom image blot for Quill editor, extending the base `ImageBlot`.\r\n     * \r\n     * This class provides custom formatting logic to allow managing the `title` attribute missing from the native Quill image blot.\r\n     * See PR https://github.com/slab/quill/pull/4350 for more details.\r\n     * \r\n     * @remarks\r\n     * - The static `formats` method extracts supported attributes from a DOM node and returns them as a record.\r\n     * - The `format` method sets or removes supported attributes on the image DOM node, delegating to the superclass for unsupported attributes.\r\n     * \r\n     * @example\r\n     * ```typescript\r\n     * // Extract formats from an image DOM node\r\n     * const formats = Image.formats(domNode);\r\n     * \r\n     * // Format an image blot\r\n     * imageBlot.format('alt', 'A description');\r\n     * ```\r\n     */\r\n    return class Image extends ImageBlot {\r\n        static blotName = 'image';\r\n        static formats(domNode: Element) {\r\n            return ATTRIBUTES.reduce(\r\n                (formats: Record<string, string | null>, attribute) => {\r\n                    if (domNode.hasAttribute(attribute)) {\r\n                        formats[attribute] = domNode.getAttribute(attribute);\r\n                    }\r\n                    return formats;\r\n                },\r\n                {},\r\n            );\r\n        }\r\n\r\n        format(name: string, value: string) {\r\n            if (ATTRIBUTES.indexOf(name) > -1) {\r\n                if (value || name === 'alt') {\r\n                    this.domNode.setAttribute(name, value);\r\n                } else {\r\n                    this.domNode.removeAttribute(name);\r\n                }\r\n            } else {\r\n                super.format(name, value);\r\n            }\r\n        }\r\n    }\r\n}\r\n","interface IframeAlignValue {\r\n  align: string;\r\n  width: string;\r\n  relativeSize: string;\r\n}\r\n\r\ninterface ImageAlignInputValue {\r\n  align: string;\r\n  title: string;\r\n}\r\n\r\ninterface ImageAlignValue extends ImageAlignInputValue {\r\n  width: string;\r\n  contenteditable: string;\r\n  relativeSize: string;\r\n}\r\n\r\n/**\r\n * Represents a class type for Attributors, which are used to define and manage\r\n * custom attributes in Quill editors. This type describes a constructor signature\r\n * for Attributor classes, including their prototype and the attribute name they handle.\r\n *\r\n * @template T The instance type created by the constructor.\r\n * @property {string} attrName - The name of the attribute managed by the Attributor.\r\n */\r\nexport type AttributorClass = {\r\n  new (...args: any[]): any;\r\n  prototype: any;\r\n  attrName: string;\r\n}\r\n\r\n/**\r\n * Creates a custom Quill Attributor class for handling iframe alignment.\r\n *\r\n * This attributor allows alignment of iframe elements within the Quill editor by\r\n * applying a CSS class and managing related dataset properties. It also handles\r\n * width styling and tracks whether the width is relative (percentage-based).\r\n *\r\n * @param QuillConstructor - The Quill constructor or Quill instance used to import Parchment.\r\n * @returns A class extending Quill's ClassAttributor, customized for iframe alignment.\r\n *\r\n * @example\r\n *   this.Quill = this.quill.constructor\r\n *   const IframeAlignClass = createIframeAlignAttributor(this.Quill);\r\n *   this.IframeAlign = new IframeAlignClass();\r\n *   this.Quill.register({\r\n *     'formats/iframeAlign': this.IframeAlign,\r\n *     'attributors/class/iframeAlign': this.IframeAlign,\r\n *   }, true);\r\n * \r\n * @remarks\r\n * - Supported alignments: 'left', 'center', 'right'.\r\n * - Adds/removes the `ql-iframe-align` class and manages `data-blot-align` and `--resize-width` style.\r\n * - Handles both string and object values for alignment.\r\n */\r\nexport const createIframeAlignAttributor = (QuillConstructor: any): AttributorClass => {\r\n  const parchment = QuillConstructor.import('parchment') as any;\r\n  const { ClassAttributor, Scope } = parchment;\r\n\r\n  return class IframeAlignAttributor extends ClassAttributor {\r\n    static attrName = 'iframeAlign';\r\n\r\n    constructor(private debug = false) {\r\n      super('iframeAlign', 'ql-iframe-align', {\r\n        scope: Scope.BLOCK,\r\n        whitelist: ['left', 'center', 'right'],\r\n      });\r\n    }\r\n\r\n    /**\r\n     * Adds alignment and width-related formatting to the specified HTML element node.\r\n     *\r\n     * - Sets the alignment using the provided value, which can be either a string or an object containing an `align` property.\r\n     * - Stores the alignment value in the element's `data-blot-align` attribute.\r\n     * - Handles the element's `width` attribute:\r\n     *   - If present, ensures the width value includes units (appends 'px' if numeric only).\r\n     *   - Sets the CSS custom property `--resize-width` to the processed width value.\r\n     *   - Sets the `data-relative-size` attribute to `'true'` if the width ends with '%', otherwise `'false'`.\r\n     *   - If no width is specified, removes the `--resize-width` property and sets `data-relative-size` to `'false'`.\r\n     *\r\n     * @param node - The DOM element to which alignment and width formatting will be applied.\r\n     * @param value - The alignment value, either as a string or an object with an `align` property.\r\n     * @returns `true` if the formatting was successfully applied to an HTMLElement, otherwise `false`.\r\n     */\r\n    add(node: Element, value: IframeAlignValue): boolean {\r\n      if (this.debug) {\r\n        console.debug('IframeAlignAttributor.add', node, value);\r\n      }\r\n      if (node instanceof HTMLElement) {\r\n        if (typeof value === 'object') {\r\n          super.add(node, value.align);\r\n          node.dataset.blotAlign = value.align;\r\n        } else {\r\n          super.add(node, value);\r\n          node.dataset.blotAlign = value;\r\n        }\r\n        let width: string | null = node.getAttribute('width');\r\n        if (width) {\r\n          // width style value must include units, add 'px' if numeric only\r\n          if (!isNaN(Number(width.trim().slice(-1)))) {\r\n            width = `${width}px`\r\n          }\r\n          node.style.setProperty('--resize-width', width);\r\n          node.dataset.relativeSize = `${width.endsWith('%')}`;\r\n        } else {\r\n          node.style.removeProperty('--resize-width');\r\n          node.dataset.relativeSize = 'false';\r\n        }\r\n        if (this.debug) {\r\n          console.debug('IframeAlignAttributor.add - node:', node, 'aligned with:', value);\r\n        }\r\n        return true;\r\n      } else {\r\n        if (this.debug) {\r\n          console.debug('IframeAlignAttributor.add - node is not an HTMLElement, skipping alignment');\r\n        }\r\n        return false;\r\n      }\r\n    }\r\n\r\n    /**\r\n     * Removes the alignment formatting from the specified DOM element.\r\n     * \r\n     * If the provided node is an instance of HTMLElement, this method first calls the\r\n     * parent class's `remove` method to perform any additional removal logic, and then\r\n     * deletes the `data-blot-align` attribute from the element's dataset.\r\n     *\r\n     * @param node - The DOM element from which to remove the alignment formatting.\r\n     */\r\n    remove(node: Element): void {\r\n      if (this.debug) {\r\n        console.debug('IframeAlignAttributor.remove', node);\r\n      }\r\n      if (node instanceof HTMLElement) {\r\n        super.remove(node);\r\n        delete node.dataset.blotAlign;\r\n      }\r\n    }\r\n\r\n    /**\r\n     * Extracts alignment and width information from a given DOM element.\r\n     *\r\n     * @param node - The DOM element from which to extract alignment and width values.\r\n     * @returns An object containing:\r\n     *   - `align`: The alignment class name derived from the superclass's value method.\r\n     *   - `width`: The width value, determined from the element's CSS custom property '--resize-width', \r\n     *     its 'width' attribute, or an empty string if not present.\r\n     *   - `relativeSize`: A string indicating whether the width ends with a '%' character, representing a relative size.\r\n     */\r\n    value(node: Element): IframeAlignValue {\r\n      const className = super.value(node);\r\n      const width = (node instanceof HTMLElement) ?\r\n        node.style.getPropertyValue('--resize-width') || node.getAttribute('width') || '' :\r\n        '';\r\n      const value = {\r\n        align: className,\r\n        width: width,\r\n        relativeSize: `${width.endsWith('%')}`\r\n      };\r\n      if (this.debug) {\r\n        console.debug('IframeAlignAttributor.value', node, value);\r\n      }\r\n      return value;\r\n    }\r\n\r\n  }\r\n}\r\n\r\n/**\r\n * Creates a custom Quill Attributor class for handling image alignment within a span wrapper.\r\n * \r\n * This attributor enables alignment formatting (`left`, `center`, `right`) for images by wrapping them in a `<span>`\r\n * element with a specific class and managing related attributes such as `data-title` for captions and width handling.\r\n * It also ensures that the image's alignment and caption are properly reflected in the DOM and Quill's internal model.\r\n * \r\n * @param QuillConstructor - The Quill constructor or instance used to import Parchment and related classes.\r\n * @returns A custom AttributorClass for image alignment formatting.\r\n * \r\n * @example\r\n *   this.Quill = this.quill.constructor\r\n *   const ImageAlignClass = createImageAlignAttributor(this.Quill);\r\n *   this.ImageAlign = new ImageAlignClass();\r\n *   this.Quill.register({\r\n *     'formats/imageAlign': this.ImageAlign,\r\n *     'attributors/class/imageAlign': this.ImageAlign,\r\n *   }, true);\r\n *\r\n * @remarks\r\n * - The attributor manages both string and object values for alignment, supporting additional metadata like `title`.\r\n * - It ensures the wrapper span is not editable and synchronizes width information for correct CSS rendering.\r\n * - Handles edge cases where Quill merges inline styles, ensuring the image alignment format is reapplied as needed.\r\n * - The `remove` and `value` methods ensure proper cleanup and retrieval of alignment state.\r\n */\r\nexport const createImageAlignAttributor = (QuillConstructor: any): AttributorClass => {\r\n  const parchment = QuillConstructor.import('parchment') as any;\r\n  const { ClassAttributor, Scope } = parchment;\r\n\r\n  return class ImageAlignAttributor extends ClassAttributor {\r\n    static tagName = 'SPAN';\r\n    static attrName = 'imageAlign';\r\n\r\n    constructor(private debug = false) {\r\n      super('imageAlign', 'ql-image-align', {\r\n        scope: Scope.INLINE,\r\n        whitelist: ['left', 'center', 'right'],\r\n      });\r\n    }\r\n\r\n    /**\r\n     * Adds or updates alignment and related formatting for an image wrapper node.\r\n     *\r\n     * This method applies alignment, caption, and width formatting to a given node containing an image.\r\n     * It handles both object-based and string-based alignment values, updates relevant attributes,\r\n     * and ensures the wrapper is styled correctly for Quill's image formatting.\r\n     *\r\n     * - If the node is an HTMLSpanElement, it sets alignment, caption (data-title), and width attributes.\r\n     * - If the node is not a span, it attempts to find an image child and reapply the imageAlign format.\r\n     * - Handles Quill's inline style merging quirks to avoid redundant wrappers.\r\n     *\r\n     * @param node - The DOM element (typically a span or container) to apply formatting to.\r\n     * @param value - The alignment value, which can be a string or an object containing alignment and optional title.\r\n     * @returns `true` if formatting was applied or handled, `false` otherwise.\r\n     */\r\n    add(node: Element, value: ImageAlignInputValue | string): boolean {\r\n      if (this.debug) {\r\n        console.debug('ImageAlignAttributor.add', node, value);\r\n      }\r\n      if (node instanceof HTMLSpanElement && value) {\r\n        let imageElement = node.querySelector('img') as HTMLImageElement;\r\n        if (typeof value === 'object' && value.align) {\r\n          super.add(node, value.align);\r\n          node.setAttribute('contenteditable', 'false');\r\n          // data-title used to populate caption via ::after\r\n          if (!!value.title) {\r\n            node.setAttribute('data-title', value.title);\r\n          } else {\r\n            node.removeAttribute('data-title');\r\n          }\r\n          if (value.align) {\r\n            imageElement.dataset.blotAlign = value.align;\r\n          }\r\n          if (this.debug) {\r\n            console.debug('ImageAlignAttributor.add - imageElement:', imageElement, 'aligned with:', value.align);\r\n          }\r\n        } else if (typeof value === 'string') {\r\n          super.add(node, value);\r\n          imageElement.dataset.blotAlign = value;\r\n          if (this.debug) {\r\n            console.debug('ImageAlignAttributor.add - imageElement:', imageElement, 'aligned with:', value);\r\n          }\r\n        } else {\r\n          if (this.debug) {\r\n            console.debug('ImageAlignAttributor.add - no value provided, skipping alignment');\r\n          }\r\n          return false;\r\n        }\r\n        // set width style property on wrapper if image and has imageAlign format\r\n        // fallback to image natural width if width attribute missing (image not resized))\r\n        // width needed to size wrapper correctly via css\r\n        // width style value must include units, add 'px' if numeric only\r\n        let width: string | null = this.getImageWidth(imageElement);\r\n        node.setAttribute('data-relative-size', `${width?.endsWith('%')}`)\r\n        return true;\r\n      } else {\r\n        // bug fix - Quill's inline styles merge elements and remove span element if styles nested\r\n        // for the first outer style, reapply imageAlign format on image \r\n        // for subsequent outer styles, skip reformat and just return true - will nest multiple span wrappers otherwise\r\n        const imageElement = node instanceof HTMLImageElement ? node : node.querySelector('img');\r\n        if (this.debug)\r\n          console.debug(`ImageAlignAttributor.add - ${node.tagName} is not a span, checking for image:`, imageElement);\r\n        if (imageElement instanceof HTMLImageElement) {\r\n          // Use QuillConstructor.find to find the image blot, using global Quill static methods will always return null \r\n          //   in some environments such as vite, react, etc.\r\n          const imageBlot = QuillConstructor.find(imageElement) as any;\r\n          if (this.debug) {\r\n            console.debug('ImageAlignAttributor.add - found image blot:', imageBlot);\r\n          }\r\n          if (\r\n            imageBlot &&\r\n            (\r\n              node.firstChild instanceof HTMLSpanElement ||\r\n              !(imageElement.parentElement?.matches('span[class^=\"ql-image-align-\"]'))\r\n            )\r\n          ) {\r\n            imageBlot.format('imageAlign', value);\r\n            if (this.debug) {\r\n              console.debug('ImageAlignAttributor.add - reapplying imageAlign format to image blot:', value, imageBlot);\r\n            }\r\n          }\r\n          return true;\r\n        }\r\n        return false;\r\n      }\r\n    }\r\n\r\n    /**\r\n     * Removes alignment formatting from the given DOM node.\r\n     *\r\n     * If the node is an HTMLElement, it first calls the parent class's remove method.\r\n     * Then, if the node's first child is also an HTMLElement, it deletes the `blotAlign`\r\n     * data attribute from that child element.\r\n     *\r\n     * @param node - The DOM element from which to remove alignment formatting.\r\n     */\r\n    remove(node: Element): void {\r\n      if (this.debug) {\r\n        console.debug('ImageAlignAttributor.remove', node);\r\n      }\r\n      if (node instanceof HTMLElement) {\r\n        super.remove(node);\r\n        if (node.firstChild && (node.firstChild instanceof HTMLElement)) {\r\n          delete node.firstChild.dataset.blotAlign;\r\n        }\r\n      }\r\n    }\r\n\r\n    /**\r\n     * Retrieves alignment and metadata information for an image element within a given DOM node.\r\n     *\r\n     * This method attempts to find an `<img>` element within the provided node, then extracts its alignment,\r\n     * title, and width attributes. If the width attribute is missing or invalid, it tries to determine the width\r\n     * either immediately (if the image is loaded) or by setting an `onload` handler. The method returns an object\r\n     * containing the alignment, title, width, a `contenteditable` flag, and a boolean string indicating if the width\r\n     * is specified as a percentage.\r\n     *\r\n     * @param node - The DOM element to search for an image and extract alignment and metadata from.\r\n     * @returns An object containing the image's alignment, title, width, contenteditable status, and relative size flag.\r\n     */\r\n    value(node: Element): ImageAlignValue {\r\n      // in case nested style, find image element and span wrapper\r\n      const imageElement = node.querySelector('img') as HTMLImageElement;\r\n      if (!imageElement) return null as any; // this can happen during certain 'undo' operations\r\n      const parentElement = imageElement.parentElement as HTMLElement;\r\n      const align = super.value(parentElement);\r\n      const title: string = imageElement.getAttribute('title') || '';\r\n      let width: string = imageElement.getAttribute('width') || '';\r\n      // attempt to get width if missing or image not loaded\r\n      if (!parseFloat(width)) {\r\n        if (imageElement.complete) {\r\n          width = this.getImageWidth(imageElement);\r\n        } else {\r\n          imageElement.onload = (event) => {\r\n            width = this.getImageWidth(event.target as HTMLImageElement);\r\n          }\r\n        }\r\n      }\r\n      const value = {\r\n        align: align,\r\n        title: title,\r\n        width: width,\r\n        contenteditable: 'false',\r\n        relativeSize: `${width.endsWith('%')}`\r\n      };\r\n      if (this.debug) {\r\n        console.debug('ImageAlignAttributor.value', node, value);\r\n      }\r\n      return value;\r\n    }\r\n\r\n    /**\r\n     * Retrieves the width of the given HTMLImageElement, ensuring it is set as an attribute and formatted with 'px' units.\r\n     * \r\n     * - If the 'width' attribute is not present, it uses the image's natural width and sets it as the 'width' attribute in pixels.\r\n     * - If the 'width' attribute exists but does not end with a non-numeric character (i.e., is a number), it appends 'px' to the value.\r\n     * - Updates the parent element's CSS variable '--resize-width' with the computed width.\r\n     * \r\n     * @param imageElement - The HTMLImageElement whose width is to be retrieved and set.\r\n     * @returns The width of the image as a string with 'px' units.\r\n     */\r\n    getImageWidth(imageElement: HTMLImageElement) {\r\n      let width = imageElement.getAttribute('width');\r\n      if (!width) {\r\n        width = `${imageElement.naturalWidth}px`;\r\n        imageElement.setAttribute('width', width);\r\n      } else {\r\n        if (!isNaN(Number(width.trim().slice(-1)))) {\r\n          width = `${width}px`\r\n          imageElement.setAttribute('width', width);\r\n        }\r\n      }\r\n      (imageElement.parentElement as HTMLElement).style.setProperty('--resize-width', width);\r\n      return width;\r\n    }\r\n\r\n  }\r\n}\r\n","/**\r\n * Factory function to create a custom Quill video blot class with responsive styling.\r\n *\r\n * @param QuillConstructor - The Quill constructor or instance used to import the base video format.\r\n * @returns A class extending Quill's VideoEmbed, enforcing a 16:9 aspect ratio and full width for embedded videos.\r\n *\r\n * @remarks\r\n * The returned class, `VideoResponsive`, overrides the default video blot to ensure videos are displayed responsively.\r\n * The aspect ratio is controlled via the static `aspectRatio` property and applied to the video element's style.\r\n *\r\n * @example\r\n * ```typescript\r\n * const VideoResponsive = createResponsiveVideoBlotClass(Quill);\r\n * Quill.register(VideoResponsive);\r\n * ```\r\n */\r\nexport const createResponsiveVideoBlotClass = (QuillConstructor: any): any => {\r\n    const VideoEmbed = QuillConstructor.import(\"formats/video\") as any;\r\n\r\n    /**\r\n     * A custom Quill blot for embedding responsive videos.\r\n     * Extends `VideoEmbed` to ensure videos use a 16:9 aspect ratio and full width.\r\n     *\r\n     * @remarks\r\n     * The aspect ratio is set via the `aspectRatio` static property and applied to the video element's style.\r\n     *\r\n     * @example\r\n     * ```typescript\r\n     * const videoBlot = VideoResponsive.create('https://example.com/video.mp4');\r\n     * ```\r\n     *\r\n     * @extends VideoEmbed\r\n     */\r\n    return class VideoResponsive extends VideoEmbed {\r\n        static blotName = 'video';\r\n        static aspectRatio: string = \"16 / 9 auto\"\r\n        static create(value: string) {\r\n            const node = super.create(value);\r\n            node.setAttribute('width', '100%');\r\n            node.style.aspectRatio = this.aspectRatio;\r\n            return node;\r\n        }\r\n        html() {\r\n            return this.domNode.outerHTML;\r\n        }\r\n    }\r\n}\r\n","import BlotFormatter from '../../BlotFormatter';\r\nimport { Aligner } from './Aligner';\r\nimport type { Alignment } from './Alignment';\r\nimport type { Blot } from '../../specs/BlotSpec';\r\nimport type { Options } from '../../Options';\r\n\r\n/**\r\n * The `DefaultAligner` class provides alignment management for Quill editor blots (such as images and iframes).\r\n * It implements the `Aligner` interface and is responsible for applying, clearing, and querying alignment\r\n * formatting on supported blots within the editor.\r\n *\r\n * This class supports both inline and block-level blots, and can be configured with custom alignment options.\r\n * It interacts with Quill's Parchment module to determine blot types and scopes, and uses formatter options\r\n * to control alignment and resizing behaviors.\r\n *\r\n * Key features:\r\n * - Registers available alignments and exposes them via `getAlignments()`.\r\n * - Applies alignment to blots using `setAlignment()`, handling both inline (e.g., images) and block (e.g., iframes) elements.\r\n * - Clears alignment formatting from blots with `clear()`.\r\n * - Determines blot type and scope with utility methods (`isInlineBlot`, `isBlockBlot`, `hasInlineScope`, `hasBlockScope`).\r\n * - Checks and retrieves current alignment with `isAligned()` and `getAlignment()`.\r\n * - Optionally sets relative width for images if configured.\r\n * - Ensures editor usability by adding a new paragraph if the editor contains only an aligned image.\r\n *\r\n * @remarks\r\n * This class is intended for internal use by the BlotFormatter module and expects a properly configured\r\n * `BlotFormatter` instance with alignment and resize options.\r\n *\r\n * @example\r\n * ```typescript\r\n * const aligner = new DefaultAligner(formatter);\r\n * aligner.setAlignment(blot, 'center');\r\n * ```\r\n *\r\n * @see Aligner\r\n * @see BlotFormatter\r\n */\r\nexport default class DefaultAligner implements Aligner {\r\n  alignments: Record<string, Alignment> = {};\r\n  options: Options;\r\n  formatter: BlotFormatter;\r\n  private debug: boolean;\r\n  private Scope: any;\r\n\r\n  constructor(formatter: BlotFormatter) {\r\n    this.formatter = formatter;    // Get Scope from the formatter's Quill constructor\r\n    this.debug = formatter.options.debug ?? false;\r\n    const parchment = formatter.Quill.import('parchment') as any;\r\n    this.Scope = parchment.Scope;\r\n    this.options = formatter.options;\r\n    this.options.align.alignments.forEach(alignment => {\r\n      this.alignments[alignment] = {\r\n        name: alignment,\r\n        apply: (blot: Blot | null) => {\r\n          this.setAlignment(blot, alignment);\r\n        },\r\n      }\r\n    })\r\n    if (this.debug) {\r\n      console.debug('DefaultAligner created with alignments:', this.alignments);\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Retrieves all available alignment options.\r\n   *\r\n   * @returns {Alignment[]} An array of alignment objects defined in the `alignments` property.\r\n   */\r\n  getAlignments = (): Alignment[] => {\r\n    return Object.keys(this.alignments).map(k => this.alignments[k]);\r\n  }\r\n\r\n  /**\r\n   * Clears alignment formatting from the given blot if it is an image or iframe.\r\n   *\r\n   * - For image blots (`IMG`), if the parent is a `SPAN`, removes the alignment attribute from the parent.\r\n   * - For iframe blots (`IFRAME`), removes the alignment attribute directly from the blot.\r\n   *\r\n   * @param blot - The blot to clear alignment formatting from, or `null` if none.\r\n   */\r\n  clear = (blot: Blot | null): void => {\r\n    if (blot != null) {\r\n      if (blot.domNode.tagName === 'IMG') {\r\n        if (blot.parent !== null && blot.parent.domNode.tagName === 'SPAN') {\r\n          blot.parent.format(this.formatter.ImageAlign.attrName, false)\r\n          if (this.debug) {\r\n            console.debug('Cleared image alignment from parent span:', blot.parent);\r\n          }\r\n        }\r\n      } else if (blot.domNode.tagName === 'IFRAME') {\r\n        blot.format(this.formatter.IframeAlign.attrName, false)\r\n        if (this.debug) {\r\n          console.debug('Cleared iframe alignment:', blot);\r\n        }\r\n      }\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Determines whether the given blot is an inline blot.\r\n   *\r\n   * Checks if the provided `blot` has a scope that matches the inline blot scope.\r\n   *\r\n   * @param blot - The blot instance to check.\r\n   * @returns `true` if the blot is an inline blot; otherwise, `false`.\r\n   */\r\n  isInlineBlot = (blot: Blot): boolean => {\r\n    return (blot.statics?.scope & this.Scope.INLINE) === this.Scope.INLINE_BLOT;\r\n  }\r\n\r\n  /**\r\n   * Determines if the provided blot is a block-level blot.\r\n   *\r\n   * Checks the blot's static scope against the BLOCK scope constant,\r\n   * and returns true if it matches the BLOCK_BLOT type.\r\n   *\r\n   * @param blot - The blot instance to check.\r\n   * @returns True if the blot is a block blot; otherwise, false.\r\n   */\r\n  isBlockBlot = (blot: Blot): boolean => {\r\n    return (blot.statics?.scope & this.Scope.BLOCK) === this.Scope.BLOCK_BLOT;\r\n  }\r\n\r\n  /**\r\n   * Determines whether the given blot has an inline scope.\r\n   *\r\n   * @param blot - The blot instance to check.\r\n   * @returns `true` if the blot's scope includes the inline scope; otherwise, `false`.\r\n   */\r\n  hasInlineScope = (blot: Blot): boolean => {\r\n    return (blot.statics.scope & this.Scope.INLINE) === this.Scope.INLINE;\r\n  }\r\n\r\n  /**\r\n   * Determines whether the given blot has block-level scope.\r\n   *\r\n   * @param blot - The blot instance to check.\r\n   * @returns `true` if the blot's scope includes block-level formatting; otherwise, `false`.\r\n   */\r\n  hasBlockScope = (blot: Blot): boolean => {\r\n    return (blot.statics.scope & this.Scope.BLOCK) === this.Scope.BLOCK;\r\n  }\r\n\r\n  /**\r\n   * Determines whether the given blot is aligned.\r\n   *\r\n   * If an alignment is specified, returns `true` only if the blot's alignment matches the specified alignment.\r\n   * If no alignment is specified, returns `true` if the blot has any alignment.\r\n   *\r\n   * @param blot - The blot to check for alignment.\r\n   * @param alignment - The alignment to compare against, or `null` to check for any alignment.\r\n   * @returns `true` if the blot is aligned (and matches the specified alignment, if provided); otherwise, `false`.\r\n   */\r\n  isAligned = (blot: Blot, alignment: Alignment | null): boolean => {\r\n    // true if blot is aligned, if alignment specfied then true only if alignment matches\r\n    const existingAlignment = this.getAlignment(blot);\r\n    if (alignment) {\r\n      return existingAlignment === alignment.name;\r\n    } else {\r\n      return (!!existingAlignment);\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Retrieves the alignment value from the given blot's DOM node.\r\n   *\r\n   * @param blot - The blot instance whose alignment is to be determined.\r\n   * @returns The alignment value as a string if present, otherwise `undefined`.\r\n   */\r\n  getAlignment = (blot: Blot): string | undefined => {\r\n    return blot.domNode.dataset.blotAlign;\r\n  }\r\n\r\n  /**\r\n   * Sets the alignment for a given blot (content element) in the editor.\r\n   *\r\n   * This method checks if the blot is already aligned as requested. If not, it clears any existing alignment,\r\n   * and applies the new alignment based on the blot type (inline or block). For inline blots (such as images),\r\n   * it may also set a relative width attribute if required by the configuration. For block blots (such as iframes),\r\n   * it applies the alignment directly.\r\n   *\r\n   * Additionally, if the editor contains only an image, it ensures a new paragraph is added underneath to maintain\r\n   * editor usability.\r\n   *\r\n   * @param blot - The blot (content element) to align. Can be `null`, in which case no action is taken.\r\n   * @param alignment - The alignment to apply (e.g., 'left', 'center', 'right'). Must correspond to a key in `this.alignments`.\r\n   */\r\n  setAlignment = (blot: Blot | null, alignment: string): void => {\r\n    if (blot === null) {\r\n      if (this.debug) console.debug('DefaultAligner.setAlignment called with null blot, no action taken');\r\n      return;\r\n    }\r\n    const hasAlignment = this.isAligned(blot, this.alignments[alignment]);\r\n    if (this.debug) console.debug('hasAlignment', hasAlignment);\r\n    this.clear(blot);\r\n    if (!hasAlignment) {\r\n      if (this.isInlineBlot(blot) || this.hasInlineScope(blot)) {\r\n        if (this.debug) console.debug('setting alignment', (this.isInlineBlot(blot) || this.hasInlineScope(blot)));\r\n        // if no width attr and use relative mandatory, try to set relative width attr\r\n        if (\r\n          !blot.domNode.getAttribute('width') &&\r\n          this.options.resize.useRelativeSize &&\r\n          !this.options.resize.allowResizeModeChange\r\n        ) {\r\n          try {\r\n            const editorStyle = getComputedStyle(this.formatter.quill.root);\r\n            const editorWidth = this.formatter.quill.root.clientWidth -\r\n              parseFloat(editorStyle.paddingLeft) -\r\n              parseFloat(editorStyle.paddingRight);\r\n            blot.domNode.setAttribute(\r\n              'width',\r\n              `${Math.min(Math.round(100 * (blot.domNode as HTMLImageElement).naturalWidth / editorWidth), 100)}%`\r\n            )\r\n          } catch {\r\n            if (this.debug) console.debug('DefaultAligner.setAlignment Error setting image width:', blot);\r\n          }\r\n        }\r\n        if (this.debug) console.debug(\r\n          'DefaultAligner.setAlignment formatting image with',\r\n          this.formatter.ImageAlign.attrName,\r\n          {\r\n            align: this.alignments[alignment].name,\r\n            title: blot.domNode.getAttribute('title') || ''\r\n          }\r\n        )\r\n        blot.format(\r\n          this.formatter.ImageAlign.attrName,\r\n          {\r\n            align: this.alignments[alignment].name,\r\n            title: blot.domNode.getAttribute('title') || ''\r\n          }\r\n        );\r\n        // if image only item in editor, add new paragraph underneath otherwise no selectable item in editor\r\n        try {\r\n          const ops: any = this.formatter.quill.getContents().ops;\r\n          if (ops.length === 2 && ops[1].insert === '\\n') {\r\n            this.formatter.quill.insertText(this.formatter.quill.getLength(), '\\n', 'user');\r\n          }\r\n        } catch { }\r\n      } else if (this.isBlockBlot(blot) || this.hasBlockScope(blot)) {\r\n        if (this.debug) console.debug(\r\n          'DefaultAligner.setAlignment formatting iframe with',\r\n          this.formatter.IframeAlign.attrName,\r\n          {\r\n            align: this.alignments[alignment].name\r\n          }\r\n        )\r\n        blot.format(\r\n          this.formatter.IframeAlign.attrName,\r\n          this.alignments[alignment].name\r\n        );\r\n      }\r\n    }\r\n  }\r\n}\r\n\r\n","import { ToolbarOptions } from \"../../Options\";\r\n\r\nexport interface _ToolbarButton {\r\n    action: string;\r\n    element: HTMLElement | null;\r\n    icon: string;\r\n    selected: boolean;\r\n    visible: boolean;\r\n    onClick: EventListener;\r\n    options: ToolbarOptions;\r\n    create(action: string, icon: string): HTMLElement;\r\n}\r\n\r\n/**\r\n * Represents a toolbar button used in the Quill editor's formatting toolbar.\r\n * \r\n * The `ToolbarButton` class encapsulates the creation, management, and styling of a single toolbar button.\r\n * It provides methods for initializing the button element, handling selection and visibility states,\r\n * applying custom styles, and cleaning up resources when the button is destroyed.\r\n * Instances of this class should be added to the `toolbarButtons` array of an `Action` class.\r\n * \r\n * @implements {_ToolbarButton}\r\n * \r\n * @remarks\r\n * - The button's icon, action, and event handler are configured via the constructor.\r\n * - Customization options for styling, tooltips, and class names are provided through the `ToolbarOptions` object.\r\n * - Selection and visibility states are managed via getter/setter properties.\r\n * - The class is designed to be extended for custom selection logic via the `preselect` method.\r\n * \r\n * @example\r\n * ```typescript\r\n *     someAction.toolbarButtons = [\r\n *         new ToolbarButton(\r\n *             'someActionName',\r\n *             this.onClickHandler,\r\n *             this.formatter.options.toolbar,\r\n *         )\r\n *     ]\r\n * ```\r\n */\r\nexport default class ToolbarButton implements _ToolbarButton {\r\n    action: string;\r\n    icon: string;\r\n    onClick: EventListener;\r\n    options: ToolbarOptions;\r\n    element: HTMLElement | null = null;\r\n    initialVisibility: boolean = true; // preset visibility before button is created\r\n\r\n    constructor(\r\n        action: string,\r\n        onClickHandler: EventListener,\r\n        options: ToolbarOptions\r\n    ) {\r\n        this.action = action;\r\n        this.icon = options.icons[action as keyof typeof options.icons];\r\n        this.onClick = onClickHandler;\r\n        this.options = options;\r\n    }\r\n\r\n    /**\r\n     * Creates and initializes the toolbar button element.\r\n     * \r\n     * This method constructs a `span` element, sets its inner HTML to the provided icon,\r\n     * assigns the appropriate class name and action data attribute, and attaches the click handler.\r\n     * If tooltips are configured for the action, it sets the tooltip text.\r\n     * The button's selected and visible states are initialized, and custom styling is applied.\r\n     * \r\n     * @returns {HTMLElement} The created and configured toolbar button element.\r\n     */\r\n    create = (): HTMLElement => {\r\n        this.element = document.createElement('span');\r\n        this.element.innerHTML = this.icon;\r\n        this.element.className = this.options.buttonClassName;\r\n        this.element.dataset.action = this.action;\r\n        this.element.onclick = this.onClick;\r\n        if (this.options.tooltips && this.options.tooltips[this.action]) {\r\n            this.element.title = this.options.tooltips[this.action];\r\n        }\r\n        this.selected = this.preselect();\r\n        this.visible = this.initialVisibility;\r\n        this._styleButton();\r\n        return this.element;\r\n    }\r\n\r\n    /**\r\n     * Cleans up the toolbar button by removing its click event handler,\r\n     * detaching it from the DOM, and clearing the reference to the element.\r\n     * This method should be called when the button is no longer needed to\r\n     * prevent memory leaks.\r\n     */\r\n    destroy = (): void => {\r\n        if (this.element) {\r\n            this.element.onclick = null;\r\n            this.element.remove();\r\n            this.element = null;\r\n        }\r\n    }\r\n\r\n    /**\r\n     * Determines whether the toolbar button should appear as selected (active) when loaded.\r\n     * Override this method to provide custom logic for button selection state.\r\n     *\r\n     * @returns {boolean} `true` if the button should be preselected; otherwise, `false`.\r\n     */\r\n    preselect = (): boolean => {\r\n        // override this with logic to show if button is selected (active) when loaded\r\n        // someCriteria: boolean = true / false;\r\n        // return someCriteria;\r\n        return false;\r\n    }\r\n\r\n    /**\r\n     * Indicates whether the toolbar button is currently selected.\r\n     *\r\n     * Returns `true` if the underlying element's `data-selected` attribute is set to `'true'`, otherwise returns `false`.\r\n     */\r\n    get selected(): boolean {\r\n        return this.element?.dataset.selected === 'true';\r\n    }\r\n\r\n    /**\r\n     * Sets the selected state of the toolbar button.\r\n     * \r\n     * When set to `true`, applies the selected class and style to the button element.\r\n     * When set to `false`, removes the selected class and style, and reapplies the default button style if provided.\r\n     * Also updates the `data-selected` attribute on the element.\r\n     *\r\n     * @param value - Indicates whether the button should be in the selected state.\r\n     */\r\n    set selected(value: boolean) {\r\n        if (this.element) {\r\n            this.element.dataset.selected = value.toString();\r\n            // apply styles to indicate selected state\r\n            if (value) {\r\n                this.element.classList.add(this.options.buttonSelectedClassName);\r\n                if (this.options.buttonSelectedStyle) {\r\n                    Object.assign(this.element.style, this.options.buttonSelectedStyle);\r\n                }\r\n            } else {\r\n                this.element.classList.remove(this.options.buttonSelectedClassName);\r\n                if (this.options.buttonSelectedStyle) {\r\n                    this.element.removeAttribute('style');\r\n                    if (this.options.buttonStyle)\r\n                        Object.assign(this.element.style, this.options.buttonStyle);\r\n                }\r\n            }\r\n        }\r\n    }\r\n\r\n    /**\r\n     * Indicates whether the toolbar button is currently visible.\r\n     * Returns `true` if the button's element is not hidden (`display` is not set to `'none'`), otherwise returns `false`.\r\n     */\r\n    get visible(): boolean {\r\n        return this.element?.style.display !== 'none';\r\n    }\r\n\r\n    /**\r\n     * Sets the visibility of the toolbar button element.\r\n     * Accepts a CSS display value as a string or a boolean.\r\n     * If a boolean is provided, `true` sets the display to 'inline-block', and `false` sets it to 'none'.\r\n     * If a string is provided, it is used directly as the CSS display value.\r\n     *\r\n     * @param style - The desired visibility state, either as a CSS display string or a boolean.\r\n     */\r\n    set visible(style: string | boolean) {\r\n        if (this.element) {\r\n            if (typeof style === 'boolean') {\r\n                style = style ? 'inline-block' : 'none';\r\n            }\r\n            this.element.style.display = style\r\n        }\r\n    }\r\n\r\n    /**\r\n     * Applies custom styles to the toolbar button and its SVG child element, if provided in the options.\r\n     *\r\n     * - If `options.buttonStyle` is defined, it merges the style properties into the button's element.\r\n     * - If `options.svgStyle` is defined, it merges the style properties into the first child element (assumed to be an SVG).\r\n     *\r\n     * @private\r\n     */\r\n    private _styleButton = (): void => {\r\n        if (this.element) {\r\n            if (this.options.buttonStyle) {\r\n                Object.assign(this.element.style, this.options.buttonStyle);\r\n            }\r\n            if (this.options.svgStyle) {\r\n                const childElement = this.element.children[0] as HTMLElement; // Type assertion\r\n                if (childElement) {\r\n                    Object.assign(childElement.style, this.options.svgStyle);\r\n                }\r\n            }\r\n        }\r\n    }\r\n\r\n}","import Action from '../Action';\r\nimport BlotFormatter from '../../BlotFormatter';\r\nimport DefaultAligner from './DefaultAligner';\r\nimport ToolbarButton from '../toolbar/ToolbarButton';\r\nimport { Alignment } from './Alignment';\r\n\r\n/**\r\n * Provides alignment actions for Quill editor blots, including creating, managing,\r\n * and handling alignment toolbar buttons. Integrates with a `DefaultAligner` to\r\n * apply, clear, and detect alignment on selected blots. Handles UI state for\r\n * alignment buttons and supports debug logging.\r\n *\r\n * @remarks\r\n * This class is intended to be used as part of a Quill blot formatter extension,\r\n * enabling users to align embedded content (such as images or videos) via a toolbar.\r\n *\r\n * @extends Action\r\n *\r\n * @example\r\n * ```typescript\r\n * const alignAction = new AlignAction(formatter);\r\n * alignAction.onCreate();\r\n * // ... user interacts with toolbar ...\r\n * alignAction.onDestroy();\r\n * ```\r\n */\r\nexport default class AlignAction extends Action {\r\n  aligner: DefaultAligner;\r\n  alignButtons: Record<string, ToolbarButton> = {};\r\n\r\n  constructor(formatter: BlotFormatter) {\r\n    super(formatter);\r\n    this.aligner = new DefaultAligner(formatter);\r\n    if (formatter.options.debug) console.debug('AlignAction Aligner created:', this.aligner);\r\n  }\r\n\r\n  /**\r\n   * Creates alignment toolbar buttons for each available alignment option.\r\n   * \r\n   * Iterates over the alignments provided by the aligner and creates a `ToolbarButton`\r\n   * for each alignment, storing them in the `alignButtons` map. If there is a currently\r\n   * selected blot, it checks its alignment and preselects the corresponding button.\r\n   * Optionally logs debug information about the created buttons and the current alignment.\r\n   *\r\n   * @private\r\n   */\r\n  private _createAlignmentButtons = (): void => {\r\n    for (const alignment of Object.values(this.aligner.alignments)) {\r\n      this.alignButtons[alignment.name] = new ToolbarButton(\r\n        alignment.name,\r\n        this.onClickHandler,\r\n        this.formatter.options.toolbar\r\n      )\r\n    }\r\n    const targetBlot = this.formatter.currentSpec?.getTargetBlot();\r\n    if (targetBlot) {\r\n      const alignment: string | undefined = this.aligner.getAlignment(targetBlot);\r\n      if (alignment && this.alignButtons[alignment]) {\r\n        this.alignButtons[alignment].preselect = () => { return true };\r\n      }\r\n      if (this.debug) {\r\n        console.debug('AlignAction alignment buttons created:', this.alignButtons);\r\n        console.debug('Blot alignment on load:', alignment);\r\n      }\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Clears the selection state of all alignment buttons.\r\n   *\r\n   * Iterates through all buttons in the `alignButtons` collection and sets their\r\n   * `selected` property to `false`. If debugging is enabled, logs a message to the console.\r\n   *\r\n   * @private\r\n   */\r\n  private _clearButtons = (): void => {\r\n    for (const button of Object.values(this.alignButtons)) {\r\n      button.selected = false;\r\n    }\r\n    if (this.debug) console.debug('AlignAction alignment buttons cleared');\r\n  }\r\n\r\n  /**\r\n   * Handles click events on alignment toolbar buttons.\r\n   *\r\n   * This event handler determines which alignment action was triggered by the user,\r\n   * retrieves the corresponding alignment configuration, and applies or clears the alignment\r\n   * on the currently selected blot in the editor. It also updates the toolbar button states\r\n   * and logs debug information if enabled.\r\n   *\r\n   * @param event - The click event triggered by the user on a toolbar button.\r\n   */\r\n  onClickHandler: EventListener = (event: Event): void => {\r\n    const button: HTMLElement | null = (event.target as HTMLElement)\r\n      .closest(`span.${this.formatter.options.toolbar.buttonClassName}`);\r\n    if (!!button) {\r\n      const action:string = button.dataset.action || '';\r\n      const targetBlot = this.formatter.currentSpec?.getTargetBlot();\r\n      if (!!action && !!targetBlot) {\r\n        const alignment: Alignment = this.aligner.alignments[action];\r\n        this._clearButtons();\r\n        if (this.aligner.isAligned(targetBlot, alignment)) {\r\n          this.aligner.clear(targetBlot);\r\n          if (this.debug) {\r\n            console.debug('AlignAction clear alignment:', action, targetBlot);\r\n          }\r\n        } else {\r\n          this.aligner.setAlignment(targetBlot, action);\r\n          this.alignButtons[action].selected = true;\r\n          if (this.debug) {\r\n            console.debug('AlignAction set alignment:', action, targetBlot);\r\n          }\r\n        }\r\n      }\r\n    }\r\n    this.formatter.update();\r\n  }\r\n\r\n  /**\r\n   * Initializes the alignment action by creating alignment buttons and storing them in the toolbar.\r\n   * If debug mode is enabled in the formatter options, logs the created alignment buttons to the console.\r\n   *\r\n   * @returns {void}\r\n   */\r\n  onCreate = (): void => {\r\n    this._createAlignmentButtons();\r\n    this.toolbarButtons = Object.values(this.alignButtons);\r\n    if (this.formatter.options.debug) console.debug('AlignAction alignment buttons created:', this.toolbarButtons);\r\n  }\r\n\r\n  /**\r\n   * Cleans up resources used by the alignment action.\r\n   * \r\n   * This method resets the `alignButtons` object and clears the `toolbarButtons` array.\r\n   * If debug mode is enabled in the formatter options, a debug message is logged to the console.\r\n   *\r\n   * @returns {void}\r\n   */\r\n  onDestroy = (): void => {\r\n    this.alignButtons = {};\r\n    this.toolbarButtons = [];\r\n    if (this.formatter.options.debug) console.debug('AlignAction alignment buttons destroyed');\r\n  }\r\n}\r\n","import Action from './Action';\r\n\r\n/**\r\n * Represents an action that handles deletion of a selected blot in a Quill editor.\r\n * \r\n * The `DeleteAction` class listens for keyboard and input events to detect when the user\r\n * presses the 'Delete' or 'Backspace' keys. If a blot is selected and no modal is open,\r\n * it deletes the corresponding blot from the editor and hides the formatter UI.\r\n * \r\n * @remarks\r\n * - Event listeners are attached on creation and removed on destruction to prevent memory leaks.\r\n * - The action only triggers when a blot is selected and no modal dialog with attribute `data-blot-formatter-modal` is open.\r\n * \r\n * @example\r\n * ```typescript\r\n * const deleteAction = new DeleteAction(formatter);\r\n * deleteAction.onCreate();\r\n * // ... later\r\n * deleteAction.onDestroy();\r\n * ```\r\n */\r\nexport default class DeleteAction extends Action {\r\n  /**\r\n   * Initializes event listeners for the delete action.\r\n   * \r\n   * - Adds a 'keyup' event listener to the document that triggers `_onKeyUp`.\r\n   * - Adds an 'input' event listener to the Quill editor's root element that also triggers `_onKeyUp`.\r\n   * \r\n   * This method should be called when the delete action is created to ensure\r\n   * proper handling of keyboard and input events.\r\n   */\r\n  onCreate = (): void => {\r\n    document.addEventListener('keyup', this._onKeyUp);\r\n    this.formatter.quill.root.addEventListener('input', this._onKeyUp);\r\n  }\r\n\r\n  /**\r\n   * Cleans up event listeners associated with the action.\r\n   * \r\n   * Removes the 'keyup' event listener from the document and the 'input' event listener\r\n   * from the Quill editor's root element to prevent memory leaks and unintended behavior\r\n   * after the action is destroyed.\r\n   */\r\n  onDestroy = (): void => {\r\n    document.removeEventListener('keyup', this._onKeyUp);\r\n    this.formatter.quill.root.removeEventListener('input', this._onKeyUp);\r\n  }\r\n\r\n  /**\r\n   * Handles the keyup event for delete and backspace actions.\r\n   * \r\n   * If no modal is open and a current spec is selected, checks if the pressed key is\r\n   * 'Delete' or 'Backspace'. If so, finds the target blot element in the Quill editor,\r\n   * determines its index, and deletes one character at that index. Afterwards, hides the formatter UI.\r\n   * \r\n   * @param e - The keyboard event triggered by the user.\r\n   */\r\n  private _onKeyUp = (e: KeyboardEvent): void => {\r\n    const modalOpen: boolean = !!document.querySelector('[data-blot-formatter-modal]')\r\n    if (!this.formatter.currentSpec || modalOpen) {\r\n      return;\r\n    }\r\n    // delete or backspace\r\n    if (e.code === 'Delete' || e.code === 'Backspace') {\r\n      if (this.debug) {\r\n        console.debug('DeleteAction keyup detected:', e.code);\r\n      }\r\n      // Get the target element from the current spec\r\n      const targetElement = this.formatter.currentSpec.getTargetElement();\r\n      if (targetElement) {\r\n        const blot = this.formatter.Quill.find(targetElement);\r\n        if (blot) {\r\n          const index = this.formatter.quill.getIndex(blot);\r\n          this.formatter.quill.deleteText(index, 1, \"user\"); // Deletes 1 character from index position\r\n        }\r\n      }\r\n      this.formatter.hide();\r\n    }\r\n  };\r\n}\r\n","import Action from './Action';\r\nimport BlotFormatter from '../BlotFormatter';\r\nimport ToolbarButton from './toolbar/ToolbarButton';\r\n\r\ninterface HandleStyle {\r\n  width?: string;\r\n  height?: string;\r\n}\r\n\r\n/**\r\n * Provides interactive resizing functionality for elements within a Quill editor overlay.\r\n * \r\n * `ResizeAction` manages the creation, positioning, and behavior of resize handles for supported elements\r\n * (such as images, videos, and iframes), allowing users to adjust their size via mouse or touch gestures.\r\n * It supports both absolute (pixel-based) and relative (percentage-based) sizing modes, aspect ratio maintenance,\r\n * and optional oversize protection for images. The class also integrates with a toolbar for toggling resize modes,\r\n * displays live size information, and ensures proper cleanup of DOM elements and event listeners.\r\n * \r\n * @remarks\r\n * - Handles mouse and touch events for resizing, including pinch-to-resize gestures.\r\n * - Maintains aspect ratio and supports custom aspect ratios for unclickable elements.\r\n * - Integrates with a toolbar for resize mode switching.\r\n * - Displays live size info and manages fade-out transitions.\r\n * - Supports oversize protection for images and SVG detection.\r\n * - Ensures proper cleanup to prevent memory leaks.\r\n * \r\n * @example\r\n * ```typescript\r\n * const resizeAction = new ResizeAction(formatter);\r\n * resizeAction.onCreate();\r\n * // ... user interacts with overlay ...\r\n * resizeAction.onDestroy();\r\n * ```\r\n */\r\nexport default class ResizeAction extends Action {\r\n  private _topLeftHandle: HTMLElement;\r\n  private _topRightHandle: HTMLElement;\r\n  private _bottomRightHandle: HTMLElement;\r\n  private _bottomLeftHandle: HTMLElement;\r\n  private _dragHandle: HTMLElement | null | undefined = null;\r\n  private _dragStartX: number = 0;\r\n  private _dragCursorStyle: HTMLElement;\r\n  private _preDragWidth: number = 0;\r\n  private _pinchStartDistance: number = 0;\r\n  private _calculatedAspectRatio: number = 0;\r\n  private _computedAspectRatio: string | undefined = undefined;\r\n  private _target: HTMLElement | null | undefined;\r\n  private _editorStyle: CSSStyleDeclaration | undefined;\r\n  private _editorWidth: number = 0;\r\n  private _useRelativeSize: boolean;\r\n  private _resizeModeButton: ToolbarButton | null = null;\r\n  private _isUnclickable: boolean = false;\r\n  private _hasResized: boolean = false;\r\n  private _formattedWidth: string = '';\r\n  private _sizeInfoTimerId: ReturnType<typeof setTimeout> | null = null;\r\n  private _isImage: boolean = false;\r\n  private _isSVG: boolean = false;\r\n  private _naturalWidth: number | undefined = undefined;\r\n\r\n  constructor(formatter: BlotFormatter) {\r\n    super(formatter);\r\n    this._topLeftHandle = this._createHandle('top-left', 'nwse-resize');\r\n    this._topRightHandle = this._createHandle('top-right', 'nesw-resize');\r\n    this._bottomRightHandle = this._createHandle('bottom-right', 'nwse-resize');\r\n    this._bottomLeftHandle = this._createHandle('bottom-left', 'nesw-resize');\r\n    this._dragCursorStyle = document.createElement('style');\r\n    this._useRelativeSize = this.formatter.options.resize.useRelativeSize;\r\n    if (formatter.options.resize.allowResizeModeChange) {\r\n      this._resizeModeButton = this._createResizeModeButton();\r\n      this.toolbarButtons = [\r\n        this._resizeModeButton\r\n      ]\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Initializes the resize action by setting up the target element, determining its type,\r\n   * and appending resize handles to the overlay. Also attaches mouse and touch event listeners\r\n   * to the overlay for handling user interactions. Finally, positions the handles according to\r\n   * the specified style options.\r\n   *\r\n   * @remarks\r\n   * This method should be called when the resize action is created to ensure all necessary\r\n   * DOM elements and event listeners are properly initialized.\r\n   */\r\n  onCreate = (): void => {\r\n    this._target = this.formatter.currentSpec?.getTargetElement();\r\n    this._isUnclickable = this.formatter.currentSpec?.isUnclickable || false;\r\n    this._isImage = this._target instanceof HTMLImageElement;\r\n    if (this._isImage) {\r\n      this._isSVG = this._isSvgImage();\r\n    }\r\n\r\n    this.formatter.overlay.append(\r\n      this._topLeftHandle, this._topRightHandle,\r\n      this._bottomRightHandle, this._bottomLeftHandle\r\n    );\r\n    this.formatter.overlay.addEventListener('mousedown', this._onOverlayMouseDown)\r\n    this.formatter.overlay.addEventListener('mouseup', this._onOverlayMouseUp)\r\n\r\n    const passiveFalse = { passive: false } as EventListenerOptions;\r\n    this.formatter.overlay.addEventListener('touchstart', this._onOverlayTouchStart, passiveFalse);\r\n    this.formatter.overlay.addEventListener('touchmove', this._onOverlayTouchMove, passiveFalse);\r\n    this.formatter.overlay.addEventListener('touchend', this._onOverlayTouchEnd, passiveFalse);\r\n\r\n    const handleStyle: HandleStyle = this.formatter.options.resize.handleStyle || {};\r\n    this._repositionHandles(handleStyle);\r\n    if (this.debug) {\r\n      console.debug('ResizeAction created with target:', this._target, 'isUnclickable:', this._isUnclickable);\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Cleans up resources and event listeners associated with the resize action.\r\n   * \r\n   * This method resets internal state, removes resize handles from the overlay,\r\n   * detaches mouse and touch event listeners, and triggers an update on the formatter.\r\n   * \r\n   * Should be called when the resize action is no longer needed to prevent memory leaks\r\n   * and unintended behavior.\r\n   */\r\n  onDestroy = (): void => {\r\n    this._target = null;\r\n    this._isUnclickable = false;\r\n    this._isImage = false;\r\n    this._naturalWidth = undefined;\r\n    this._isSVG = false;\r\n    this._setCursor('');\r\n    [\r\n      this._topLeftHandle, this._topRightHandle,\r\n      this._bottomRightHandle, this._bottomLeftHandle\r\n    ].forEach(handle => { handle.remove(); });\r\n    this.formatter.overlay.removeEventListener('mousedown', this._onOverlayMouseDown);\r\n    this.formatter.overlay.removeEventListener('mouseup', this._onOverlayMouseUp);\r\n\r\n    const passiveFalse = { passive: false } as EventListenerOptions;\r\n    this.formatter.overlay.removeEventListener('touchstart', this._onOverlayTouchStart, passiveFalse);\r\n    this.formatter.overlay.removeEventListener('touchmove', this._onOverlayTouchMove, passiveFalse);\r\n    this.formatter.overlay.removeEventListener('touchend', this._onOverlayTouchEnd, passiveFalse);\r\n    this.formatter.update();\r\n  }\r\n\r\n  /**\r\n   * Creates a resize handle element for the specified position with the given cursor style.\r\n   *\r\n   * The handle is styled using the class name and optional style provided in the formatter's options.\r\n   * It also sets a `data-position` attribute and attaches a pointer down event listener.\r\n   *\r\n   * @param position - The position identifier for the handle (e.g., 'top-left', 'bottom-right').\r\n   * @param cursor - The CSS cursor style to apply when hovering over the handle.\r\n   * @returns The created HTMLElement representing the resize handle.\r\n   */\r\n  private _createHandle = (position: string, cursor: string): HTMLElement => {\r\n    // create resize handles\r\n    const box = document.createElement('div');\r\n    box.classList.add(this.formatter.options.resize.handleClassName);\r\n    box.setAttribute('data-position', position);\r\n    box.style.cursor = cursor;\r\n    if (this.formatter.options.resize.handleStyle) {\r\n      Object.assign(box.style, this.formatter.options.resize.handleStyle);\r\n    }\r\n    box.addEventListener('pointerdown', this._onHandlePointerDown);\r\n    return box;\r\n  }\r\n\r\n  /**\r\n   * Repositions the resize handles around an element based on the provided handle style.\r\n   *\r\n   * @param handleStyle - Optional style object containing width and height properties for the handles.\r\n   *                      If provided, the handles are offset by half their width and height to center them.\r\n   *                      If not provided, default offsets of '0px' are used.\r\n   *\r\n   * The method updates the `left`, `right`, `top`, and `bottom` CSS properties of the four handles\r\n   * (`_topLeftHandle`, `_topRightHandle`, `_bottomRightHandle`, `_bottomLeftHandle`) to ensure they are\r\n   * correctly positioned relative to the element being resized.\r\n   */\r\n  private _repositionHandles = (handleStyle?: HandleStyle): void => {\r\n    // Cache offset calculations\r\n    const handleXOffset = handleStyle?.width ? `${-parseFloat(handleStyle.width) / 2}px` : '0px';\r\n    const handleYOffset = handleStyle?.height ? `${-parseFloat(handleStyle.height) / 2}px` : '0px';\r\n\r\n    // Use direct property assignment instead of Object.assign for better performance\r\n    const { style: topLeftStyle } = this._topLeftHandle;\r\n    topLeftStyle.left = handleXOffset;\r\n    topLeftStyle.top = handleYOffset;\r\n\r\n    const { style: topRightStyle } = this._topRightHandle;\r\n    topRightStyle.right = handleXOffset;\r\n    topRightStyle.top = handleYOffset;\r\n\r\n    const { style: bottomRightStyle } = this._bottomRightHandle;\r\n    bottomRightStyle.right = handleXOffset;\r\n    bottomRightStyle.bottom = handleYOffset;\r\n\r\n    const { style: bottomLeftStyle } = this._bottomLeftHandle;\r\n    bottomLeftStyle.left = handleXOffset;\r\n    bottomLeftStyle.bottom = handleYOffset;\r\n  }\r\n\r\n  /**\r\n   * Sets the cursor style for the document body and all its children.\r\n   * When a non-empty value is provided, it applies the specified cursor style\r\n   * globally by injecting a style element into the document head.\r\n   * When an empty value is provided, it removes the previously injected style element,\r\n   * reverting the cursor to its default behavior.\r\n   *\r\n   * @param value - The CSS cursor value to apply (e.g., 'pointer', 'col-resize').\r\n   */\r\n  private _setCursor = (value: string): void => {\r\n    if (!document.body) {\r\n      console.warn('ResizeAction: Cannot set cursor - document.body is null');\r\n      return;\r\n    }\r\n\r\n    try {\r\n      if (value) {\r\n        this._dragCursorStyle.innerHTML = `body, body * { cursor: ${value} !important; }`;\r\n        if (!document.head.contains(this._dragCursorStyle)) {\r\n          document.head.appendChild(this._dragCursorStyle);\r\n        }\r\n      } else {\r\n        if (document.head.contains(this._dragCursorStyle)) {\r\n          document.head.removeChild(this._dragCursorStyle);\r\n        }\r\n      }\r\n    } catch (error) {\r\n      console.error('ResizeAction: Error setting cursor style:', error);\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Activates or deactivates the resize mode for the target element.\r\n   * \r\n   * When activated, prepares the target for resizing by determining the resize mode (absolute or relative),\r\n   * calculating editor and target dimensions, handling aspect ratio logic, and displaying size information.\r\n   * When deactivated, applies the finalized width to the _target, updates toolbar button states, sets style attributes,\r\n   * clears cached natural width, updates the formatter, and hides the size info box.\r\n   * \r\n   * @param activate - If `true`, activates resize mode; if `false`, finalizes and deactivates resize mode.\r\n   */\r\n  private _resizeMode = (activate: boolean): void => {\r\n    if (activate) {\r\n      // activate resize mode, show size info\r\n      this._hasResized = false;\r\n      this._formattedWidth = '';\r\n      if (!!this._target) {\r\n        // determine resize mode to use (absolute/relative)\r\n        this._useRelativeSize = this.formatter._useRelative(this._target);\r\n        // get inner editor width to calculate % values\r\n        this._editorStyle = getComputedStyle(this.formatter.quill.root);\r\n        this._editorWidth = this.formatter.quill.root.clientWidth -\r\n          parseFloat(this._editorStyle.paddingLeft) -\r\n          parseFloat(this._editorStyle.paddingRight);\r\n\r\n        const rect = this._target.getBoundingClientRect();\r\n        // prevent division by zero in the case that rect.height is 0 for some reason\r\n        if (rect.height === undefined || rect.height === 0) {\r\n          rect.height = this._target.clientHeight + 1;\r\n        }\r\n        this._preDragWidth = rect.width;\r\n        this._computedAspectRatio = getComputedStyle(this._target).aspectRatio || 'auto'\r\n        this._calculatedAspectRatio = rect.width / rect.height;\r\n\r\n        if (this._useRelativeSize) {\r\n          if (this._isUnclickable) {\r\n            // strip height for relative iframe sizing, rely on aspect-ratio instead\r\n            if (this._computedAspectRatio === 'auto') {\r\n              // relative size on iframe requires aspect-ratio to be set - use default from options\r\n              this._target.style.aspectRatio = this.formatter.options.video.defaultAspectRatio;\r\n              console.warn(\r\n                `No iframe aspect-ratio set. Set an aspect ratio either via custom blot or css.\\n` +\r\n                `Using temporary aspect ratio \"${this.formatter.options.video.defaultAspectRatio}\"`\r\n              );\r\n            }\r\n          }\r\n        } else {\r\n          if (this._isUnclickable && this._computedAspectRatio !== 'auto') {\r\n            // if aspect-ratio set via blot or css, try to use that ratio for new height instead\r\n            const ratio = this._computedAspectRatio.match(/(\\d+)\\s*\\/\\s*(\\d+)/);\r\n            if (ratio) {\r\n              try {\r\n                this._calculatedAspectRatio = parseFloat(ratio[1]) / parseFloat(ratio[2]);\r\n              } catch { }\r\n            }\r\n          }\r\n        }\r\n        // get natural width if oversize protection on and resize mode is absolute (not relative) - excludes SVG\r\n        if (this._isImage && !this._useRelativeSize && !this._isSVG && this.formatter.options.resize.imageOversizeProtection) {\r\n          this._naturalWidth = (this._target as HTMLImageElement).naturalWidth;\r\n        }\r\n        // show size info box\r\n        this._showSizeInfo(true, rect.width, rect.height);\r\n        if (this.debug) {\r\n          console.debug('ResizeAction resize mode activated:', {\r\n            target: this._target,\r\n            useRelativeSize: this._useRelativeSize,\r\n            editorWidth: this._editorWidth,\r\n            preDragWidth: this._preDragWidth,\r\n            aspectRatio: this._calculatedAspectRatio,\r\n            computedAspectRatio: this._computedAspectRatio\r\n          });\r\n        }\r\n      }\r\n    } else {\r\n      if (this._target && this._hasResized) {\r\n        // round dimensions to whole numbers\r\n        let width: string = this._roundDimension(this._formattedWidth);\r\n        this._target.setAttribute('width', width);\r\n        // set resize mode button selected status if inuded in toolbar\r\n        if (this.formatter.toolbar.buttons['resizeMode']) {\r\n          this.formatter.toolbar.buttons['resizeMode'].selected = this.isRelative;\r\n        }\r\n        // set --resize-width style attribute and data-relative-size attribute\r\n        if (this._isUnclickable) {\r\n          this._target.style.setProperty('--resize-width', `${width}`);\r\n          this._target.dataset.relativeSize = `${this.isRelative}`\r\n        } else {\r\n          if (this.isAligned && this._target.parentElement) {\r\n            this._target.parentElement.style.setProperty('--resize-width', `${width}`);\r\n            this._target.parentElement.dataset.relativeSize = `${this.isRelative}`\r\n          }\r\n        }\r\n        if (this.debug) {\r\n          console.debug('ResizeAction resize mode deactivated:', {\r\n            target: this._target,\r\n            width: width,\r\n            isRelative: this.isRelative,\r\n            isAligned: this.isAligned\r\n          });\r\n        }\r\n      }\r\n      // clear any cached image natural width\r\n      this._naturalWidth = undefined;\r\n\r\n      this.formatter.update();\r\n      // fade out size info box\r\n      this._showSizeInfo(false);\r\n    }\r\n  };\r\n\r\n  /**\r\n   * Handles the pointer down event on a resize handle.\r\n   * \r\n   * Initiates the resize mode, sets up the drag handle, and stores the starting X position.\r\n   * Adds event listeners for pointer move and pointer up to enable drag behavior.\r\n   * \r\n   * @param event - The pointer event triggered when the user presses down on a resize handle.\r\n   */\r\n  private _onHandlePointerDown = (event: PointerEvent): void => {\r\n    this._resizeMode(true);\r\n    if (event.target instanceof HTMLElement && !!this._target) {\r\n      this._dragHandle = event.target;\r\n      this._setCursor(this._dragHandle.style.cursor);\r\n      this._dragStartX = event.clientX;\r\n      // enable drag behaviour until pointer up event\r\n      document.addEventListener('pointermove', this._onHandleDrag);\r\n      document.addEventListener('pointerup', this._onHandlePointerUp);\r\n    }\r\n  };\r\n\r\n  /**\r\n   * Handles the drag event for a resize handle, updating the target element's width.\r\n   *\r\n   * Calculates the new width based on the pointer's movement and the initial drag position.\r\n   * Ensures the new width stays within the editor's bounds and does not shrink below the minimum allowed width.\r\n   * Applies the new width to both the target element and its overlay.\r\n   *\r\n   * @param event - The pointer event triggered during dragging.\r\n   */\r\n  private _onHandleDrag = (event: PointerEvent): void => {\r\n    // If no target element or drag handle is set, exit early\r\n    if (!this._target || !this._dragHandle) return;\r\n\r\n    // Mark that a resize has occurred\r\n    this._hasResized = true;\r\n\r\n    // Calculate horizontal movement since drag started\r\n    const deltaX = event.clientX - this._dragStartX;\r\n\r\n    // Determine if the dragged handle is on the left side\r\n    const isLeftHandle =\r\n      this._dragHandle === this._topLeftHandle ||\r\n      this._dragHandle === this._bottomLeftHandle;\r\n\r\n    // Calculate new width: subtract delta for left handles, add for right handles\r\n    const newWidth = Math.round(\r\n      isLeftHandle ? this._preDragWidth - deltaX : this._preDragWidth + deltaX\r\n    );\r\n\r\n    // Constrain the new width between minimum and editor width\r\n    const constrainedWidth = Math.max(\r\n      Math.min(newWidth, this._editorWidth),\r\n      this.formatter.options.resize.minimumWidthPx\r\n    );\r\n\r\n    // Resize the target element to the constrained width\r\n    this._resizeTarget(constrainedWidth);\r\n  };\r\n\r\n  /**\r\n   * Handles the pointer up event on the resize handle.\r\n   * \r\n   * This method disables resize mode, resets the cursor style,\r\n   * and removes the event listeners for pointer movement and pointer up events.\r\n   * It is typically called when the user releases the pointer after resizing.\r\n   */\r\n  private _onHandlePointerUp = (): void => {\r\n    // disable resize mode, reset cursor, tidy up\r\n    this._setCursor('');\r\n    this._resizeMode(false);\r\n    // remove resize event listeners\r\n    document.removeEventListener('pointermove', this._onHandleDrag);\r\n    document.removeEventListener('pointerup', this._onHandlePointerUp);\r\n  };\r\n\r\n  /**\r\n   * Handles the touch start event on the overlay element.\r\n   * If the overlay itself is the _target, enables resize mode.\r\n   * When two fingers touch the target element, prevents default scrolling,\r\n   * calculates the initial distance between the fingers for pinch-to-resize,\r\n   * and stores the initial width of the target element.\r\n   *\r\n   * @param event - The touch event triggered on the overlay.\r\n   */\r\n  private _onOverlayTouchStart = (event: TouchEvent): void => {\r\n    if (event.target === this.formatter.overlay) {\r\n      this._resizeMode(true);\r\n      if (!!this._target && event.touches.length === 2) {\r\n        event.preventDefault(); // Prevent default touch behavior like scrolling\r\n        // Calculate the initial distance between two fingers\r\n        this._pinchStartDistance = this._calculateDistance(event.touches[0], event.touches[1]);\r\n        // Get the initial width of the element\r\n        this._preDragWidth = this._target.clientWidth;\r\n      }\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Handles touch move events on the overlay for resizing the target element via pinch gestures.\r\n   *\r\n   * When two fingers are detected on the overlay, calculates the distance between them to determine\r\n   * the scale factor for resizing. The new width is constrained between a minimum of 10px and the\r\n   * maximum editor width. Prevents default touch behavior such as scrolling during the gesture.\r\n   *\r\n   * @param event - The touch event triggered by user interaction.\r\n   */\r\n  private _onOverlayTouchMove = (event: TouchEvent): void => {\r\n    if (event.target === this.formatter.overlay) {\r\n      if (!!this._target && event.touches.length === 2 && this._pinchStartDistance !== null && this._preDragWidth !== null) {\r\n        event.preventDefault(); // Prevent default touch behaviour like scrolling\r\n        if (this._target) {\r\n          this._hasResized = true;\r\n          // Calculate the current distance between two fingers\r\n          const currentDistance = this._calculateDistance(event.touches[0], event.touches[1]);\r\n          // Calculate the scale factor & new width\r\n          const scale = currentDistance / this._pinchStartDistance;\r\n          let newWidth: number = Math.round(this._preDragWidth * scale);\r\n          // ensure width does not grow beyond editor width or shrink below 10px\r\n          newWidth = Math.max(Math.min(newWidth, this._editorWidth), 10);\r\n          // resize target + overlay\r\n          this._resizeTarget(newWidth);\r\n        }\r\n      }\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Handles the touch end event on the overlay element.\r\n   * If the touch event's target is the formatter's overlay, it disables resize mode.\r\n   *\r\n   * @param event - The touch event triggered on the overlay.\r\n   */\r\n  private _onOverlayTouchEnd = (event: TouchEvent): void => {\r\n    if (event.target === this.formatter.overlay) {\r\n      this._resizeMode(false);\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Handles the mouse down event on the overlay element.\r\n   * If the event target is the formatter's overlay, enables resize mode.\r\n   *\r\n   * @param event - The mouse event triggered by the user interaction.\r\n   */\r\n  private _onOverlayMouseDown = (event: MouseEvent): void => {\r\n    if (event.target === this.formatter.overlay) {\r\n      this._resizeMode(true);\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Handles the mouse up event on the overlay element.\r\n   * If the event target is the formatter's overlay, it disables resize mode.\r\n   *\r\n   * @param event - The mouse event triggered when the user releases the mouse button.\r\n   */\r\n  private _onOverlayMouseUp = (event: MouseEvent): void => {\r\n    if (event.target === this.formatter.overlay) {\r\n      this._resizeMode(false);\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Resizes the target element to the specified width, maintaining aspect ratio and updating related UI elements.\r\n   *\r\n   * - Limits the new width if image oversize protection is enabled.\r\n   * - Calculates the new height based on the aspect ratio.\r\n   * - Updates the size information display.\r\n   * - Sets the new width and height attributes on the target element.\r\n   * - Applies the width style property to the wrapper if the image is aligned.\r\n   * - Handles special cases for unclickable elements and absolute sizing.\r\n   * - Triggers an update to the overlay position.\r\n   *\r\n   * @param newWidth - The desired new width for the target element.\r\n   */\r\n  private _resizeTarget = (newWidth: number): void => {\r\n    if (!this._target) {\r\n      console.warn('ResizeAction: Cannot resize - target element is null');\r\n      return;\r\n    }\r\n\r\n    try {\r\n      // if image oversize protection on, limit newWidth\r\n      // this._naturalWidth only has value when target is image and protextion is on (set in _resizeMode)\r\n      newWidth = Math.min(this._naturalWidth ?? Infinity, newWidth);\r\n      // update size info display\r\n      const newHeight: number = newWidth / this._calculatedAspectRatio;\r\n      this._updateSizeInfo(newWidth, newHeight);\r\n      // set new dimensions on _target\r\n      if (this.formatter._useRelative(this._target)) {\r\n        this._formattedWidth = `${100 * newWidth / this._editorWidth}%`;\r\n      } else {\r\n        this._formattedWidth = `${newWidth}px`;\r\n      }\r\n      this._target.setAttribute('width', this._formattedWidth);\r\n      this._target.setAttribute('height', 'auto');\r\n\r\n      // update width style property to wrapper if image and has imageAlign format\r\n      // width needed to size wrapper correctly via css\r\n      // set fixed height to unclickable if using absolute size mode and no aspect ratio given\r\n      if (this._isUnclickable) {\r\n        if (!this._useRelativeSize && this._computedAspectRatio === 'auto') {\r\n          this._target.setAttribute('height', `${newHeight | 0}px`);\r\n        }\r\n        this._target.style.setProperty('--resize-width', this._formattedWidth);\r\n      } else {\r\n        if (this.isAligned && !!this._target.parentElement) {\r\n          this._target.parentElement.style.setProperty('--resize-width', this._formattedWidth);\r\n        }\r\n        if (!this._useRelativeSize && !this.formatter.options.image.autoHeight) {\r\n          this._target.setAttribute('height', `${newHeight | 0}px`);\r\n        }\r\n      }\r\n      // updates overlay position\r\n      this.formatter.update();\r\n    } catch (error) {\r\n      console.error('ResizeAction: Error resizing target element:', error);\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Shows or hides the size information box for the formatter.\r\n   *\r\n   * When `show` is `true`, cancels any existing size info timer, updates the size info\r\n   * if `width` and `height` are provided, and makes the size info box visible.\r\n   * When `show` is `false`, fades out and closes the size info box.\r\n   *\r\n   * @param show - Whether to show (`true`) or hide (`false`) the size info box.\r\n   * @param width - The width to display in the size info box (optional).\r\n   * @param height - The height to display in the size info box (optional).\r\n   */\r\n  private _showSizeInfo = (show: boolean, width: number | null = null, height: number | null = null): void => {\r\n    if (show) {\r\n      this._cancelSizeInfoTimer();\r\n      if (width !== null && height !== null) {\r\n        this._updateSizeInfo(width, height);\r\n      }\r\n      this.formatter.sizeInfo.style.transition = '';\r\n      this.formatter.sizeInfo.style.opacity = '1';\r\n    }\r\n    else {\r\n      // fade out size info box\r\n      this._closeSizeInfo();\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Updates the size information display for the selected blot.\r\n   *\r\n   * - Rounds the provided width and height to the nearest integer.\r\n   * - Formats the size string as \"width x height px\".\r\n   * - If the size is relative, displays the percentage relative to the editor width,\r\n   *   with the actual pixel size in brackets.\r\n   * - If the size is absolute and the blot has not been resized:\r\n   *   - If the target element has a `width` attribute that differs from the displayed width,\r\n   *     shows the attribute value and its calculated height, with the displayed size in brackets.\r\n   *   - If the target is an image and its natural dimensions differ from the displayed size,\r\n   *     shows the natural dimensions with the displayed size in brackets.\r\n   * - Updates the `sizeInfo` element in the formatter with the computed size string.\r\n   *\r\n   * @param width - The displayed width of the blot.\r\n   * @param height - The displayed height of the blot.\r\n   */\r\n  private _updateSizeInfo = (width: number, height: number): void => {\r\n    // Round width and height to nearest integer for display\r\n    const roundedWidth = Math.round(width);\r\n    const roundedHeight = Math.round(height);\r\n    // Default size string in \"width x height px\" format\r\n    let size = `${roundedWidth} x ${roundedHeight}px`;\r\n\r\n    if (this.isRelative) {\r\n      // If using relative sizing, calculate percentage of editor width\r\n      const percentage = Math.round(100 * width / this._editorWidth);\r\n      // Show percentage and actual pixel size in brackets\r\n      size = `${percentage}% (${size})`;\r\n    } else if (!this._hasResized && this._target) {\r\n      // If not resized yet and target exists, check for set width attribute\r\n      const setWidth = this._target.getAttribute('width');\r\n      if (setWidth) {\r\n        // If set width differs from current width, show set width and calculated height\r\n        const setWidthNum = parseFloat(setWidth);\r\n        if (setWidthNum !== width) {\r\n          const aspectRatio = width / height;\r\n          const calculatedHeight = Math.round(setWidthNum / aspectRatio);\r\n          size = `${setWidth} x ${calculatedHeight}px (${size})`;\r\n        }\r\n      } else if (this._target instanceof HTMLImageElement) {\r\n        // For images, if natural width differs, show natural dimensions\r\n        const { naturalWidth, naturalHeight } = this._target;\r\n        if (naturalWidth !== width) {\r\n          size = `${naturalWidth} x ${naturalHeight}px (${size})`;\r\n        }\r\n      }\r\n    }\r\n\r\n    // Update the formatter's size info element with the computed size string\r\n    this.formatter.sizeInfo.innerText = size;\r\n  }\r\n\r\n  get isRelative(): boolean {\r\n    return this._target?.getAttribute('width')?.endsWith('%') || false;\r\n  }\r\n\r\n  get isAligned(): boolean {\r\n    if (this._target) {\r\n      return this._target.hasAttribute('data-blot-align');\r\n    } else {\r\n      return false;\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Creates a toolbar button for toggling the resize mode.\r\n   *\r\n   * The button is initialized with a unique identifier, a click handler, and toolbar options.\r\n   * The `preselect` property is set to indicate whether the resize mode is currently relative.\r\n   *\r\n   * @returns {ToolbarButton} The configured resize mode toolbar button.\r\n   */\r\n  private _createResizeModeButton = (): ToolbarButton => {\r\n    const button = new ToolbarButton(\r\n      'resizeMode',\r\n      this._onResizeModeClickHandler,\r\n      this.formatter.options.toolbar,\r\n    );\r\n    button.preselect = () => {\r\n      return this.isRelative;\r\n    }\r\n    return button\r\n  }\r\n\r\n  /**\r\n   * Handles the click event for the resize mode control.\r\n   * Stops the event from propagating further and swaps the resize mode.\r\n   *\r\n   * @param event - The event object triggered by the click.\r\n   */\r\n  private _onResizeModeClickHandler: EventListener = (event: Event): void => {\r\n    event.stopImmediatePropagation();\r\n    this._swapResizeMode(true);\r\n  }\r\n\r\n  /**\r\n   * Swaps the resize mode of the target element between relative (percentage-based) and absolute (pixel-based) sizing.\r\n   * Updates the _target's width and height attributes, as well as relevant CSS custom properties and data attributes,\r\n   * depending on the current resize mode and alignment. Also updates the toolbar button state and optionally displays\r\n   * size information.\r\n   *\r\n   * @param showInfo - If true, displays size information after resizing.\r\n   */\r\n  private _swapResizeMode = (showInfo: boolean = false): void => {\r\n    if (this._target) {\r\n      const rect: DOMRect = this._target.getBoundingClientRect();\r\n      this._editorStyle = getComputedStyle(this.formatter.quill.root);\r\n      this._editorWidth = this.formatter.quill.root.clientWidth -\r\n        parseFloat(this._editorStyle.paddingLeft) -\r\n        parseFloat(this._editorStyle.paddingRight);\r\n      let newWidth, newHeight: string;\r\n      if (this.isRelative) {\r\n        newWidth = `${Math.round(rect.width)}px`;\r\n        newHeight = this.formatter.options.image.autoHeight \r\n          ? 'auto' \r\n          : `${Math.round(rect.height)}px`;\r\n      } else {\r\n        newWidth = `${Math.round(100 * rect.width / this._editorWidth)}%`;\r\n        newHeight = 'auto';\r\n      }\r\n      this._target.setAttribute('width', `${newWidth}`);\r\n      this._target.setAttribute('height', `${newHeight}`);\r\n      if (this.formatter.currentSpec?.isUnclickable) {\r\n        this._target.style.setProperty('--resize-width', `${newWidth}`);\r\n        this._target.dataset.relativeSize = `${this.isRelative}`;\r\n      } else {\r\n        if (this.isAligned && this._target.parentElement) {\r\n          this._target.parentElement.style.setProperty('--resize-width', `${newWidth}`);\r\n          this._target.parentElement.dataset.relativeSize = `${this.isRelative}`;\r\n        }\r\n      }\r\n      this.formatter.toolbar.buttons['resizeMode'].selected = this.isRelative;\r\n      this.formatter.update();\r\n      if (showInfo) {\r\n        this._showSizeInfo(true, rect.width, rect.height);\r\n        this._showSizeInfo(false);\r\n      }\r\n      if (this.debug) {\r\n        console.debug('ResizeAction resize mode swapped:', {\r\n          target: this._target,\r\n          newWidth: newWidth,\r\n          isRelative: this.isRelative,\r\n          isAligned: this.isAligned\r\n        });\r\n      }\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Initiates a timer to fade out the size information element after a delay.\r\n   * Sets the opacity of the `sizeInfo` element to 0 with a transition effect after 1 second.\r\n   * Stores the timer ID in `_sizeInfoTimerId` for potential future reference or cancellation.\r\n   */\r\n  private _closeSizeInfo = (): void => {\r\n    this._sizeInfoTimerId = setTimeout(() => {\r\n      this.formatter.sizeInfo.style.transition = 'opacity 1s';\r\n      this.formatter.sizeInfo.style.opacity = '0';\r\n    }, 1000);\r\n  }\r\n\r\n  /**\r\n   * Cancels the active size info timer, if one exists.\r\n   * Clears the timeout associated with `_sizeInfoTimerId` and resets the timer ID to `null`.\r\n   */\r\n  private _cancelSizeInfoTimer = (): void => {\r\n    if (this._sizeInfoTimerId !== null) {\r\n      clearTimeout(this._sizeInfoTimerId);\r\n      this._sizeInfoTimerId = null;\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Calculates the Euclidean distance between two touch points.\r\n   *\r\n   * @param touch1 - The first touch point.\r\n   * @param touch2 - The second touch point.\r\n   * @returns The distance in pixels between the two touch points.\r\n   */\r\n  private _calculateDistance = (touch1: Touch, touch2: Touch): number => {\r\n    const dx = touch2.clientX - touch1.clientX;\r\n    const dy = touch2.clientY - touch1.clientY;\r\n    return Math.sqrt(dx * dx + dy * dy);\r\n  }\r\n\r\n  /**\r\n   * Rounds the numeric part of a dimension string to the nearest integer, preserving any prefix or suffix.\r\n   *\r\n   * Examples:\r\n   * - '-$34.565c' becomes '-$35c'\r\n   * - '21.244px' becomes '21px'\r\n   *\r\n   * @param dim - The dimension string containing a number and optional prefix/suffix.\r\n   * @returns The dimension string with the numeric part rounded to the nearest integer.\r\n   */\r\n  private _roundDimension = (dim: string): string => {\r\n    // round string number with units (prefix and/or suffix): '-$34.565c' -> '-$35c', '21.244px' -> '24px'\r\n    return dim.replace(/([^0-9.-]*)(-?[\\d.]+)(.*)/, (_, prefix, num, suffix) => `${prefix}${Math.round(Number(num))}${suffix}`);\r\n  }\r\n\r\n  /**\r\n   * Determines whether the target image is an SVG image.\r\n   *\r\n   * Checks if the target is an HTMLImageElement and then verifies:\r\n   * - If the image source is a data URL, it checks for the 'image/svg+xml' MIME type.\r\n   * - Otherwise, it checks if the image source URL ends with '.svg'.\r\n   *\r\n   * @returns {boolean} True if the target image is an SVG, otherwise false.\r\n   */\r\n  private _isSvgImage = (): boolean => {\r\n    if (this._target instanceof HTMLImageElement) {\r\n      if (this._target.src.startsWith('data:image/')) {\r\n        return this._target.src.includes('image/svg+xml');\r\n      }\r\n      return this._target.src.endsWith('.svg');\r\n    }\r\n    return false;\r\n  }\r\n}\r\n","import Action from '../actions/Action';\r\nimport AlignAction from '../actions/align/AlignAction';\r\nimport BlotFormatter from '../BlotFormatter';\r\nimport CaretAction from '../actions/CaretAction';\r\nimport DeleteAction from '../actions/DeleteAction';\r\nimport ResizeAction from '../actions/ResizeAction';\r\n\r\nexport interface Blot {\r\n  domNode: HTMLElement;\r\n  parent: Blot | null;\r\n  next: Blot | null;\r\n  prev: Blot | null;\r\n  statics: any | null;\r\n  format(name: string, value: any): void | undefined;\r\n  formats(): { [key: string]: any };\r\n  length(): number;\r\n}\r\n\r\n/**\r\n * Abstract base class representing a specification for a Quill blot.\r\n *\r\n * `BlotSpec` provides a framework for defining custom blot behaviors and actions\r\n * within the Quill editor. Subclasses can override methods to implement specific\r\n * initialization, action handling, and element targeting logic for different blot types.\r\n *\r\n * @remarks\r\n * - Designed to be extended by concrete blot specification classes.\r\n * - Manages formatter instance and provides utility methods for blot manipulation.\r\n *\r\n * @example\r\n * ```typescript\r\n * class ImageBlotSpec extends BlotSpec {\r\n *   // Custom implementation for image blots\r\n * }\r\n * ```\r\n *\r\n * @property formatter - The `BlotFormatter` instance associated with this spec.\r\n * @property isUnclickable - Indicates whether the blot is unclickable.\r\n *\r\n * @method init - Initializes the blot specification. Intended to be overridden.\r\n * @method getActions - Returns an array of enabled `Action` objects for the current formatter. Intended to be extended.\r\n * @method getTargetElement - Returns the target HTML element for the blot. Intended to be overridden.\r\n * @method getTargetBlot - Retrieves the target blot associated with the current selection.\r\n * @method getOverlayElement - Returns the overlay element associated with the blot.\r\n * @method setSelection - Clears the current selection in the Quill editor.\r\n * @method onHide - Callback invoked when the blot is hidden. Intended to be overridden.\r\n */\r\nexport default class BlotSpec {\r\n  // abstract class for Blot specifications\r\n\r\n  formatter: BlotFormatter;\r\n  isUnclickable: boolean = false;\r\n\r\n  constructor(formatter: BlotFormatter) {\r\n    this.formatter = formatter;\r\n  }\r\n\r\n  /**\r\n   * Initializes the blot specification.\r\n   *\r\n   * This method is intended to perform any setup required for the blot spec.\r\n   * It can be overridden by subclasses to provide specific initialization logic.\r\n   * \r\n   */\r\n  init = (): void => {}\r\n\r\n  /**\r\n   * Returns an array of `Action` instances based on the formatter's configuration options.\r\n   * \r\n   * The returned actions may include:\r\n   * - `AlignAction` if aligning is allowed (`options.align.allowAligning`)\r\n   * - `ResizeAction` if resizing is allowed (`options.resize.allowResizing`)\r\n   * - `DeleteAction` if keyboard deletion is allowed (`options.delete.allowKeyboardDelete`)\r\n   * - Always includes `CaretAction`\r\n   *\r\n   * It can be overridden by subclasses to provide additional actions specific to the blot type.\r\n   * \r\n   * @returns {Array<Action>} An array of enabled `Action` objects for the current formatter.\r\n   */\r\n  getActions(): Array<Action> {\r\n    const actions: Array<Action> = [];\r\n    if (this.formatter.options.align.allowAligning) {\r\n      actions.push(new AlignAction(this.formatter));\r\n    }\r\n    if (this.formatter.options.resize.allowResizing) {\r\n      actions.push(new ResizeAction(this.formatter));\r\n    }\r\n    if (this.formatter.options.delete.allowKeyboardDelete) {\r\n      actions.push(new DeleteAction(this.formatter));\r\n    }\r\n    actions.push(new CaretAction(this.formatter));\r\n\r\n    return actions;\r\n  }\r\n\r\n  /**\r\n   * Returns the target HTML element associated with this blot.\r\n   * \r\n   * This method is intended to be overridden by subclasses to provide the specific target element\r\n   * for the blot type.\r\n   *\r\n   * @returns {HTMLElement | null} The target element, or `null` if none exists.\r\n   */\r\n  getTargetElement = (): HTMLElement | null => {\r\n    return null;\r\n  }\r\n\r\n  /**\r\n   * Retrieves the target blot associated with the current selection.\r\n   *\r\n   * This method first obtains the target DOM element using `getTargetElement()`.\r\n   * If a target element exists, it uses the Quill instance to find and return the corresponding blot.\r\n   * If no target element is found, it returns `null`.\r\n   * \r\n   * @remarks\r\n   * This method uses the quill instance constructor to overcome issue encountered with `Quill.find()`\r\n   * with certain environments where the `Quill` global differs from the one used in the quill instance.\r\n   * In those cases, the `find()` method will always return `null`. These environments include: vite,\r\n   * react and angular.\r\n   *\r\n   * @returns {Blot | null} The blot corresponding to the target element, or `null` if not found.\r\n   */\r\n  getTargetBlot = (): Blot | null => {\r\n    const target = this.getTargetElement();\r\n    if (!!target) {\r\n      return this.formatter.Quill.find(target) as Blot | null;\r\n    } else {\r\n      return null;\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Returns the overlay element associated with the blot.\r\n   * \r\n   * @returns {HTMLElement | null} The overlay element, or `null` if none exists.\r\n   */\r\n  getOverlayElement = (): HTMLElement | null => {\r\n    return this.getTargetElement();\r\n  }\r\n\r\n  /**\r\n   * Clears the current selection in the Quill editor by setting it to `null`.\r\n   * This effectively removes any active text selection.\r\n   *\r\n   * @remarks\r\n   * Useful for resetting the editor's selection state, such as after formatting actions.\r\n   */\r\n  setSelection = (): void => {\r\n    this.formatter.quill.setSelection(null);\r\n  }\r\n\r\n  /**\r\n   * Callback invoked when the blot is hidden.\r\n   * Override this method to implement custom hide behavior.\r\n   */\r\n  onHide = (): void => {}\r\n}\r\n","import BlotFormatter from '../BlotFormatter';\r\nimport BlotSpec from './BlotSpec';\r\n\r\nconst PROXY_IMAGE_CLASS = 'blot-formatter__proxy-image';\r\n\r\ntype UnclickableProxy = {\r\n  unclickable: HTMLElement;\r\n  proxyImage: HTMLElement;\r\n};\r\ntype UnclickableProxies = Record<string, UnclickableProxy>;\r\n\r\n/**\r\n * Represents a Quill BlotSpec for managing \"unclickable\" elements within the editor.\r\n * \r\n * This class overlays transparent proxy images on unclickable HTML elements (such as videos)\r\n * to intercept user interactions and enable custom formatting behaviour. It tracks proxies,\r\n * synchronizes their positions with the underlying elements, and manages event listeners\r\n * for editor changes, scrolling, and resizing.\r\n * \r\n * Key Features:\r\n * - Automatically creates and removes proxy overlays for unclickable elements.\r\n * - Repositions proxies on editor scroll and resize events.\r\n * - Handles click events on proxies to trigger formatter overlays.\r\n * - Passes through wheel and touch events for smooth scrolling.\r\n * \r\n * @remarks\r\n * - Proxies are managed using a randomly generated ID stored in the element's dataset.\r\n * - The proxy container is appended to the Quill editor's container and holds all proxy images.\r\n * - Designed to work with Quill's BlotFormatter extension for custom video or media formatting.\r\n * \r\n * @extends BlotSpec\r\n */\r\nexport default class UnclickableBlotSpec extends BlotSpec {\r\n  selector: string;\r\n  unclickable: HTMLElement | null;\r\n  proxyContainer: HTMLElement;\r\n  unclickableProxies: UnclickableProxies;\r\n  isUnclickable: boolean = true;\r\n\r\n  constructor(formatter: BlotFormatter) {\r\n    super(formatter);\r\n    this.selector = formatter.options.video.selector;\r\n    this.unclickable = null;\r\n    this.proxyContainer = this._createProxyContainer();\r\n    this.unclickableProxies = {};\r\n  }\r\n\r\n  /**\r\n   * Initializes event listeners and observers for unclickable blot proxies.\r\n   * - Sets up a listener for Quill's 'text-change' event to handle updates.\r\n   * - Adds a scroll event listener to the Quill root to reposition proxy images when scrolling occurs.\r\n   * - Observes editor resize events to maintain correct proxy positioning.\r\n   */\r\n  init = (): void => {\r\n    // create unclickable proxies, position proxies over unclickables\r\n    this.formatter.quill.on('text-change', this._onTextChange);\r\n    // reposition proxy image if quill root scrolls (only if target is child of quill root)\r\n    this.formatter.quill.root.addEventListener('scroll', () => {\r\n      this._repositionProxyImages();\r\n    });\r\n    this._observeEditorResize();\r\n  }\r\n\r\n  /**\r\n   * Observes the Quill editor's root element for resize events and triggers repositioning\r\n   * of proxy images when the editor's dimensions change (e.g., due to screen resize or editor grow/shrink).\r\n   * Uses a debounced approach to avoid excessive repositioning by waiting 200ms after the last resize event.\r\n   *\r\n   * @remarks\r\n   * This method sets up a `ResizeObserver` on the editor's root element and calls\r\n   * `_repositionProxyImages` whenever a resize is detected, with debouncing to improve performance.\r\n   */\r\n  private _observeEditorResize = (): void => {\r\n    // reposition proxies if editor dimensions change (e.g. screen resize or editor grow/shrink)\r\n    let resizeTimeout: number | null = null;\r\n    const resizeObserver = new ResizeObserver((entries) => {\r\n      for (const entry of entries) {\r\n        // Clear the previous timeout if there was one\r\n        if (resizeTimeout) {\r\n          clearTimeout(resizeTimeout);\r\n        }\r\n        // Set a new timeout to run after 200ms\r\n        resizeTimeout = window.setTimeout(() => {\r\n          this._repositionProxyImages();\r\n        }, 200);\r\n      }\r\n    });\r\n    // Start observing the element for size changes\r\n    resizeObserver.observe(this.formatter.quill.root);\r\n  }\r\n\r\n  /**\r\n   * Returns the target HTML element associated with this instance.\r\n   * \r\n   * @returns {HTMLElement | null} The unclickable HTML element, or `null` if not set.\r\n   */\r\n  getTargetElement = (): HTMLElement | null => {\r\n    return this.unclickable;\r\n  }\r\n\r\n  /**\r\n   * Returns the overlay HTML element associated with the blot, or `null` if none exists.\r\n   *\r\n   * @returns {HTMLElement | null} The unclickable overlay element, or `null` if not set.\r\n   */\r\n  getOverlayElement = (): HTMLElement | null => {\r\n    return this.unclickable;\r\n  }\r\n\r\n  /**\r\n   * Handles changes to the text content within the Quill editor.\r\n   *\r\n   * This method performs the following actions:\r\n   * 1. Checks if any \"unclickable\" elements tracked by proxies have been deleted from the editor.\r\n   *    If so, it removes their corresponding proxy images and cleans up the tracking object.\r\n   * 2. Searches for new \"unclickable\" elements that do not yet have a proxy image and creates proxies for them.\r\n   * 3. Repositions all proxy images to ensure they are correctly aligned with their associated elements.\r\n   *\r\n   * This method is intended to be called whenever the editor's content changes to keep proxy images in sync.\r\n   */\r\n  private _onTextChange = (): void => {\r\n    // check if any unclickable has been deleted, remove proxy if so\r\n    Object.entries(this.unclickableProxies).forEach(([key, { unclickable, proxyImage }]) => {\r\n      try {\r\n        if (!this.formatter.quill.root.contains(unclickable)) {\r\n          proxyImage.remove();\r\n          delete this.unclickableProxies[key];\r\n        }\r\n      } catch { }\r\n    });\r\n    // add proxy for any new unclickables\r\n    this.formatter.quill.root.querySelectorAll(`${this.selector}:not([data-blot-formatter-id])`)\r\n      .forEach((unclickable: HTMLElement) => {\r\n        this._createUnclickableProxyImage(unclickable);\r\n      });\r\n    this._repositionProxyImages();\r\n  };\r\n\r\n  /**\r\n   * Creates a transparent proxy image overlay for an unclickable HTML element.\r\n   * The proxy image is linked to the unclickable element via a randomly generated ID,\r\n   * which is stored in the element's dataset and used as a key in the `unclickableProxies` record.\r\n   * The proxy image is styled to be absolutely positioned and unselectable, and is appended to the proxy container.\r\n   * Event listeners are attached to the proxy image to handle click, context menu, wheel, and touch events,\r\n   * allowing interaction to be managed or passed through as needed.\r\n   *\r\n   * @param unclickable - The target HTMLElement to overlay with a transparent proxy image.\r\n   */\r\n  private _createUnclickableProxyImage = (unclickable: HTMLElement): void => {\r\n    const id = Array.from(crypto.getRandomValues(new Uint8Array(5)), (n) =>\r\n      String.fromCharCode(97 + (n % 26))\r\n    ).join('');\r\n    unclickable.dataset.blotFormatterId = id;\r\n\r\n    const canvas = document.createElement('canvas');\r\n    const context = canvas.getContext('2d');\r\n    if (context) {\r\n      context.globalAlpha = 0;\r\n      context.fillRect(0, 0, 1, 1);\r\n    }\r\n\r\n    const proxyImage = document.createElement('img');\r\n    proxyImage.src = canvas.toDataURL('image/png');\r\n    proxyImage.classList.add(PROXY_IMAGE_CLASS);\r\n    proxyImage.dataset.blotFormatterId = id;\r\n\r\n    const mergedStyle: Record<string, any> = {\r\n      ...this.formatter.options.video.proxyStyle,\r\n      ...{\r\n        position: 'absolute',\r\n        margin: '0',\r\n        userSelect: 'none',\r\n      }\r\n    }\r\n\r\n    Object.assign(proxyImage.style, mergedStyle);\r\n\r\n    proxyImage.style.setProperty('-webkit-user-select', 'none');\r\n    proxyImage.style.setProperty('-moz-user-select', 'none');\r\n    proxyImage.style.setProperty('-ms-user-select', 'none');\r\n\r\n    if (this.formatter.options.debug) {\r\n      proxyImage.style.setProperty('border', '3px solid red');\r\n    }\r\n\r\n    this.proxyContainer.appendChild(proxyImage);\r\n\r\n    // on click, hide proxy, show overlay\r\n    proxyImage.addEventListener('click', this._onProxyImageClick);\r\n    // disable context menu on proxy\r\n    proxyImage.addEventListener('contextmenu', (event) => {\r\n      event.stopPropagation();\r\n      event.preventDefault();\r\n    });\r\n    // scroll the quill root on pointer wheel event & touch scroll\r\n    proxyImage.addEventListener('wheel', this.formatter._passWheelEventThrough);\r\n    proxyImage.addEventListener('touchstart', this.formatter._onTouchScrollStart, { passive: false });\r\n    proxyImage.addEventListener('touchmove', this.formatter._onTouchScrollMove, { passive: false });\r\n\r\n    // used to reposition proxy and identify target unclickable\r\n    this.unclickableProxies[id] = {\r\n      unclickable: unclickable,\r\n      proxyImage: proxyImage\r\n    }\r\n\r\n    if (this.formatter.options.debug) {\r\n      console.debug('UnclickableBlotSpec created proxy for unclickable:', unclickable, 'with ID:', id, 'and proxy image:', proxyImage);\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Repositions proxy images to overlay their corresponding \"unclickable\" elements\r\n   * within the Quill editor container. Calculates each unclickable element's position\r\n   * relative to the container, accounting for scroll offsets, and updates the proxy image's\r\n   * style properties (`left`, `top`, `width`, `height`) accordingly.\r\n   *\r\n   * Handles errors gracefully by logging any issues encountered during positioning.\r\n   *\r\n   * @private\r\n   */\r\n  private _repositionProxyImages = (): void => {\r\n    if (Object.keys(this.unclickableProxies).length > 0) {\r\n      const containerRect: DOMRect = this.formatter.quill.container.getBoundingClientRect();\r\n      const containerScrollLeft: number = this.formatter.quill.container.scrollLeft;\r\n      const containerScrollTop: number = this.formatter.quill.container.scrollTop;\r\n\r\n      Object.entries(this.unclickableProxies).forEach(([key, { unclickable, proxyImage }]) => {\r\n        try {\r\n          // Calculate the unclickable's position relative to the container\r\n          const unclickableRect: DOMRect = unclickable.getBoundingClientRect();\r\n          Object.assign(proxyImage.style, {\r\n              // display: 'block',\r\n              left: `${unclickableRect.left - containerRect.left - 1 + containerScrollLeft}px`,\r\n              top: `${unclickableRect.top - containerRect.top + containerScrollTop}px`,\r\n              width: `${unclickableRect.width}px`,\r\n              height: `${unclickableRect.height}px`,\r\n            },\r\n          );\r\n        } catch (error) {\r\n          const msg: string = `Error positioning proxy image with id ${key}: `\r\n          console.error(msg, `${error instanceof Error ? error.message : error}`);\r\n        }\r\n      });\r\n    }\r\n  }\r\n\r\n\r\n  /**\r\n   * Handles click events on proxy images representing unclickable blots.\r\n   * Retrieves the associated unclickable blot using the proxy's dataset ID,\r\n   * updates the `unclickable` property, and displays the formatter overlay.\r\n   *\r\n   * @param event - The mouse event triggered by clicking the proxy image.\r\n   */\r\n  private _onProxyImageClick = (event: MouseEvent): void => {\r\n    // get target unclickable (unclickable), show overlay\r\n    const targetElement = event.target as HTMLElement;\r\n    const id = targetElement.dataset.blotFormatterId;\r\n    this.unclickable = this.unclickableProxies[`${id}`].unclickable;\r\n    this.formatter.show(this);\r\n  };\r\n\r\n  /**\r\n   * Creates a proxy container element (`div`) with the class 'proxy-container' and appends it\r\n   * to the Quill editor's container. This container is used to hold all proxy images.\r\n   *\r\n   * @returns {HTMLElement} The newly created proxy container element.\r\n   * @private\r\n   */\r\n  private _createProxyContainer = (): HTMLElement => {\r\n    // create a child div on quill.container to hold all the proxy images\r\n    const proxyContainer = document.createElement('div');\r\n    proxyContainer.classList.add('proxy-container');\r\n    this.formatter.quill.container.appendChild(proxyContainer);\r\n    return proxyContainer;\r\n  }\r\n\r\n}\r\n","import BlotFormatter from '../BlotFormatter';\r\nimport UnclickableBlotSpec from './UnclickableBlotSpec';\r\n\r\n/**\r\n * Represents a specification for handling iframe-based video blots within the Quill editor.\r\n * Extends {@link UnclickableBlotSpec} to provide formatting capabilities for video iframes\r\n * that should not be clickable.\r\n *\r\n * @remarks\r\n * This class is intended to be used with the BlotFormatter to manage video embeds\r\n * that use iframes, such as YouTube or Vimeo videos.\r\n *\r\n * @param formatter - The {@link BlotFormatter} instance used to apply formatting logic.\r\n */\r\nexport default class IframeVideoSpec extends UnclickableBlotSpec {\r\n  constructor(formatter: BlotFormatter) {\r\n    super(formatter);\r\n  }\r\n}\r\n","import Action from './Action';\r\nimport BlotFormatter from '../BlotFormatter';\r\nimport ToolbarButton from './toolbar/ToolbarButton';\r\nimport type { Blot } from '../specs/BlotSpec';\r\n\r\ninterface AltTitleModal {\r\n    element: HTMLDivElement;\r\n    form: HTMLFormElement;\r\n    altInput: HTMLTextAreaElement;\r\n    titleInput: HTMLTextAreaElement;\r\n    cancelButton: HTMLButtonElement;\r\n}\r\n\r\nexport default class AttributeAction extends Action {\r\n    modal: AltTitleModal;\r\n    targetElement: HTMLElement | null | undefined = null;\r\n    currentBlot: Blot | null | undefined = null;\r\n\r\n    constructor(formatter: BlotFormatter) {\r\n        super(formatter);\r\n        this.toolbarButtons = [\r\n            new ToolbarButton(\r\n                'attribute',\r\n                this._onClickHandler,\r\n                this.formatter.options.toolbar,\r\n            )\r\n        ]\r\n        this.modal = this._createModal();\r\n    }\r\n\r\n    /**\r\n     * Initializes the target element and current blot for the action.\r\n     * Retrieves the target element and blot from the current formatter specification.\r\n     *\r\n     * @remarks\r\n     * This method should be called when the action is created to ensure\r\n     * that the necessary references are set up for further processing.\r\n     */\r\n    onCreate = (): void => {\r\n        this.targetElement = this.formatter.currentSpec?.getTargetElement();\r\n        this.currentBlot = this.formatter.currentSpec?.getTargetBlot();\r\n    }\r\n\r\n    /**\r\n     * Cleans up resources when the action is destroyed.\r\n     * Sets the target element to null and removes the modal element from the DOM.\r\n     */\r\n    onDestroy = (): void => {\r\n        this.targetElement = null;\r\n        this.modal.form.removeEventListener('submit', this._onSubmitHandler);\r\n        this.modal.form.removeEventListener('cancel', this._hideAltTitleModal);\r\n        this.modal.element.removeEventListener('pointerdown', this._onPointerDownHandler);\r\n        this.modal.cancelButton.removeEventListener('click', this._hideAltTitleModal);\r\n        this.modal.element.remove();\r\n    }\r\n\r\n    /**\r\n     * Event handler for click events that triggers the display of the Alt Title modal.\r\n     * \r\n     * @private\r\n     * @remarks\r\n     * This handler is assigned to UI elements to allow users to edit or view the Alt Title attribute.\r\n     */\r\n    private _onClickHandler: EventListener = (): void => {\r\n        this._showAltTitleModal();\r\n    }\r\n\r\n    /**\r\n     * Displays the modal for editing the 'alt' and 'title' attributes of the target element.\r\n     * \r\n     * If a target element is present, this method sets the modal's input fields to the current\r\n     * 'alt' and 'title' attribute values of the target element (or empty strings if not set),\r\n     * and appends the modal element to the document body.\r\n     *\r\n     * @private\r\n     */\r\n    private _showAltTitleModal = (): void => {\r\n        if (this.targetElement) {\r\n            this.modal.altInput.value = this.targetElement.getAttribute('alt') || '';\r\n            this.modal.titleInput.value = this.targetElement.getAttribute('title') || '';\r\n            document.body.append(this.modal.element);\r\n            if (this.formatter.options.debug) {\r\n                console.debug('Showing Alt Title modal for:', this.targetElement);\r\n            }\r\n        }\r\n    }\r\n\r\n    /**\r\n     * Hides and removes the alt/title modal from the DOM.\r\n     *\r\n     * This method removes the modal's element, effectively closing the modal UI.\r\n     * It is typically called when the modal should no longer be visible to the user.\r\n     *\r\n     * @private\r\n     */\r\n    private _hideAltTitleModal = (): void => {\r\n        this.modal.element.remove();\r\n    }\r\n\r\n    /**\r\n     * Updates the `alt` and `title` attributes of the target image element based on user input.\r\n     * If a title is provided, it sets the `title` attribute; otherwise, it removes it.\r\n     * Additionally, if an image alignment format is applied, it updates the alignment format\r\n     * to include the new title value.\r\n     *\r\n     * @private\r\n     */\r\n    private _setAltTitle = (): void => {\r\n        if (this.targetElement) {\r\n            const alt: string = typeof this.modal.altInput.value === \"string\" \r\n                ? this.modal.altInput.value \r\n                : \"\";\r\n            const title: string = this.modal.titleInput.value;\r\n            this.targetElement.setAttribute('alt', alt);\r\n            if (title) {\r\n                this.targetElement.setAttribute('title', title);\r\n            } else {\r\n                this.targetElement.removeAttribute('title');\r\n            }\r\n            if (this.formatter.options.debug) {\r\n                console.debug('Setting alt:', alt, 'title:', title, 'on target element:', this.targetElement);\r\n            }\r\n            // Update align format if applied\r\n            const imageAlignment = this.currentBlot?.parent?.formats()[this.formatter.ImageAlign.attrName]?.align;\r\n            if (this.currentBlot && imageAlignment) {\r\n                if (this.formatter.options.debug) {\r\n                    console.debug('Updating title of image with alignment:', imageAlignment);\r\n                }\r\n                // Reapply the existing alignment format if it exists\r\n                this.currentBlot.parent?.format(this.formatter.ImageAlign.attrName, false)\r\n                this.currentBlot.format(\r\n                    this.formatter.ImageAlign.attrName,\r\n                    {\r\n                        align: imageAlignment,\r\n                        title: title\r\n                    }\r\n                );\r\n            }\r\n        }\r\n    }\r\n\r\n    /**\r\n     * Creates and configures a modal dialog for editing image `alt` and `title` attributes.\r\n     *\r\n     * The modal includes:\r\n     * - A unique identifier for each instance.\r\n     * - A form with labeled textareas for `alt` and `title` values.\r\n     * - Submit and cancel buttons, with customizable icons and styles.\r\n     * - Event listeners for submitting, cancelling, and closing the modal by clicking outside.\r\n     *\r\n     * Styles and labels are sourced from `this.formatter.options.image.altTitleModalOptions` and `this.formatter.options.overlay.labels`.\r\n     *\r\n     * @returns {AltTitleModal} An object containing references to the modal element, form, alt and title inputs, and the cancel button.\r\n     */\r\n    private _createModal = (): AltTitleModal => {\r\n        const uuid: string = Array.from(crypto.getRandomValues(new Uint8Array(5)), (n) =>\r\n            String.fromCharCode(97 + (n % 26))\r\n        ).join('');\r\n\r\n        // Create modal background\r\n        const modal = document.createElement('div');\r\n        modal.id = `${uuid}-modal`;\r\n        modal.setAttribute('data-blot-formatter-modal', '');\r\n\r\n        // Create modal container\r\n        const modalContainer = document.createElement('div');\r\n\r\n        // Create form element\r\n        const form = document.createElement('form');\r\n        form.id = `${uuid}-form`;\r\n\r\n        // Create label for alt\r\n        const labelAlt = document.createElement('label');\r\n        labelAlt.setAttribute('for', 'alt');\r\n        labelAlt.textContent = this.formatter.options.overlay.labels?.alt || this.formatter.options.image.altTitleModalOptions.labels.alt;\r\n\r\n        // Create textarea for alt\r\n        const textareaAlt = document.createElement('textarea');\r\n        textareaAlt.name = 'alt';\r\n        textareaAlt.rows = 3;\r\n\r\n        // Create label for title\r\n        const labelTitle = document.createElement('label');\r\n        labelTitle.setAttribute('for', 'title');\r\n        labelTitle.textContent = this.formatter.options.overlay.labels?.title || this.formatter.options.image.altTitleModalOptions.labels.title;\r\n\r\n        // Create textarea for title\r\n        const textareaTitle = document.createElement('textarea');\r\n        textareaTitle.name = 'title';\r\n        textareaTitle.rows = 3;\r\n\r\n        // Create submit button\r\n        const buttonDiv = document.createElement('div');\r\n        const submitButton = document.createElement('button');\r\n        submitButton.type = 'submit';\r\n        submitButton.innerHTML = this.formatter.options.image.altTitleModalOptions.icons.submitButton;\r\n        buttonDiv.appendChild(submitButton);\r\n\r\n        // Append elements to the form\r\n        form.appendChild(labelAlt);\r\n        form.appendChild(textareaAlt);\r\n        form.appendChild(labelTitle);\r\n        form.appendChild(textareaTitle);\r\n        form.appendChild(buttonDiv);\r\n\r\n        // Create cancel button\r\n        const cancelButton = document.createElement('button');\r\n        cancelButton.id = `${uuid}-cancel`;\r\n        cancelButton.type = 'button';\r\n        cancelButton.innerHTML = this.formatter.options.image.altTitleModalOptions.icons.cancelButton;\r\n\r\n        // styles\r\n        if (this.formatter.options.image.altTitleModalOptions.styles) {\r\n            Object.assign(modal.style, this.formatter.options.image.altTitleModalOptions.styles.modalBackground);\r\n            Object.assign(modalContainer.style, this.formatter.options.image.altTitleModalOptions.styles.modalContainer);\r\n            Object.assign(labelAlt.style, this.formatter.options.image.altTitleModalOptions.styles.label);\r\n            Object.assign(textareaAlt.style, this.formatter.options.image.altTitleModalOptions.styles.textarea);\r\n            Object.assign(labelTitle.style, this.formatter.options.image.altTitleModalOptions.styles.label);\r\n            Object.assign(textareaTitle.style, this.formatter.options.image.altTitleModalOptions.styles.textarea);\r\n            Object.assign(submitButton.style, this.formatter.options.image.altTitleModalOptions.styles.submitButton);\r\n            Object.assign(cancelButton.style, this.formatter.options.image.altTitleModalOptions.styles.cancelButton);\r\n        }\r\n\r\n        // Append form and cancel button to the modal container\r\n        modalContainer.appendChild(form);\r\n        modalContainer.appendChild(cancelButton);\r\n\r\n        // Append modal container to the modal background\r\n        modal.appendChild(modalContainer);\r\n\r\n        // event listeners\r\n        form.addEventListener('submit', this._onSubmitHandler);\r\n        form.addEventListener('cancel', this._hideAltTitleModal);\r\n        modal.addEventListener('pointerdown', this._onPointerDownHandler);\r\n        cancelButton.addEventListener('click', this._hideAltTitleModal);\r\n\r\n        return {\r\n            element: modal,\r\n            form: form,\r\n            altInput: textareaAlt,\r\n            titleInput: textareaTitle,\r\n            cancelButton: cancelButton\r\n        };\r\n    }\r\n\r\n    private _onSubmitHandler = (event: SubmitEvent): void => {\r\n        event.preventDefault();\r\n        this._setAltTitle();\r\n        this._hideAltTitleModal();\r\n    }\r\n\r\n    private _onPointerDownHandler = (event: PointerEvent): void => {\r\n        if (event.target === this.modal.element) {\r\n            this._hideAltTitleModal();\r\n        }\r\n    }\r\n}\r\n","import Action from './Action';\r\nimport BlotFormatter from '../BlotFormatter';\r\nimport ToolbarButton from './toolbar/ToolbarButton';\r\nimport { CompressorOptions } from '../Options';\r\n\r\ntype CompressModal = {\r\n    element: HTMLDivElement;\r\n    moreInfoButton: HTMLButtonElement;\r\n    continueButton: HTMLButtonElement;\r\n    cancelButton: HTMLButtonElement;\r\n    moreInfoText: HTMLDivElement;\r\n}\r\n\r\ntype ImageDetails = {\r\n    naturalWidth: number;\r\n    naturalHeight: number;\r\n    targetWidth: number | null;\r\n    targetHeight: number | null;\r\n    size: number;\r\n    canCompress: boolean;\r\n}\r\n\r\n/**\r\n * CompressAction provides an interactive UI and logic for compressing images embedded in a Quill editor.\r\n * \r\n * This action enables users to reduce the file size of images by resizing and re-encoding them as JPEGs,\r\n * while preserving the aspect ratio and respecting user-defined options such as maximum width and JPEG quality.\r\n * \r\n * Key features:\r\n * - Determines image eligibility for compression (must be a data URL, not SVG or GIF).\r\n * - Parses image dimensions from HTML attributes, supporting various units (px, %, em, rem).\r\n * - Calculates image size from base64 data URLs.\r\n * - Presents a modal dialog for user interaction, including prompts, more info, and feedback.\r\n * - Handles image compression via canvas resizing and JPEG encoding.\r\n * - Updates the image in the editor if compression is successful and provides feedback on size reduction.\r\n * - Cleans up resources and UI elements when the action is destroyed.\r\n * \r\n * Usage:\r\n * - Instantiate with a BlotFormatter instance.\r\n * - Call `onCreate` to initialize and update toolbar button visibility.\r\n * - Call `onDestroy` to clean up when the action is no longer needed.\r\n * \r\n * @remarks\r\n * This class is intended for use within the Quill Blot Formatter extension and assumes integration with its toolbar and modal infrastructure.\r\n */\r\nexport default class CompressAction extends Action {\r\n    options: CompressorOptions;\r\n    modal: CompressModal;\r\n    targetElement: HTMLElement | null | undefined = null;\r\n    imageDetails: ImageDetails | null = null;\r\n\r\n    /**\r\n     * Determines whether the given HTML element is eligible for image compression.\r\n     *\r\n     * Eligibility criteria:\r\n     * - The element must be an `<img>` tag.\r\n     * - The image source must be a data URL (`data:image/`).\r\n     * - The image must not be an SVG (`svg+xml`) or GIF (`gif`).\r\n     *\r\n     * @param targetElement - The HTML element to check for compression eligibility.\r\n     * @returns `true` if the element is an eligible image for compression, otherwise `false`.\r\n     */\r\n    static isEligibleForCompression = (targetElement: HTMLElement | null | undefined, debug: boolean = false): boolean => {\r\n        // Must be image tag with data url and not gif or svg\r\n        let isEligible = false;\r\n        if (targetElement instanceof HTMLImageElement) {\r\n            if (targetElement.src.startsWith(\"data:image/\")) {\r\n                const mimeType = targetElement.src.substring(5, targetElement.src.indexOf(\";\"));\r\n                isEligible = mimeType !== \"svg+xml\" && mimeType !== \"gif\";\r\n            }\r\n        }\r\n        if (debug) {\r\n            console.debug('Image eligibility check:', {\r\n                element: targetElement,\r\n                isEligible\r\n            });\r\n        }\r\n        return isEligible;\r\n    }\r\n\r\n    constructor(formatter: BlotFormatter) {\r\n        super(formatter);\r\n        this.options = this.formatter.options.image.compressorOptions;\r\n        this.toolbarButtons = [\r\n            new ToolbarButton(\r\n                'compress',\r\n                this._onClickHandler,\r\n                this.formatter.options.toolbar,\r\n            )\r\n        ]\r\n        this.modal = this._createModal();\r\n    }\r\n\r\n    /**\r\n     * Initializes the CompressAction by setting the target element and updating the initial visibility\r\n     * of the first toolbar button based on whether the target element is eligible for compression.\r\n     *\r\n     * This method should be called when the action is created. It ensures that the toolbar button\r\n     * reflects the current eligibility state of the target element.\r\n     */\r\n    onCreate = (): void => {\r\n        this.targetElement = this.formatter.currentSpec?.getTargetElement();\r\n        // class should not be instantiated if not eligible - double check here\r\n        const isEligibleForCompression = CompressAction.isEligibleForCompression(this.targetElement, this.debug);\r\n        this.toolbarButtons[0].initialVisibility = isEligibleForCompression;\r\n        if (this.debug) {\r\n            console.debug('CompressAction initialized with target element:', this.targetElement, 'is eligible:', isEligibleForCompression);\r\n        }\r\n    }\r\n\r\n    /**\r\n     * Cleans up resources when the action is destroyed.\r\n     * Sets the target element to null and hides the modal dialog.\r\n     */\r\n    onDestroy = (): void => {\r\n        this.targetElement = null;\r\n        this._hideModal();\r\n\r\n        this.modal.continueButton.removeEventListener('click', this._onContinueClick);\r\n        this.modal.moreInfoButton.removeEventListener('click', this._onMoreInfoClick);\r\n        this.modal.cancelButton.removeEventListener('click', this._hideModal);\r\n        this.modal.element.removeEventListener('pointerdown', this._onBackgroundClick);\r\n    }\r\n\r\n    /**\r\n     * Handles the click event for the compress action.\r\n     * When triggered, it displays the modal dialog for compression options.\r\n     *\r\n     * @param event - The click event object.\r\n     */\r\n    private _onClickHandler: EventListener = () => {\r\n        this._showModal();\r\n    }\r\n\r\n    /**\r\n     * Displays a modal dialog for image compression if the target element is an image.\r\n     * If the image can be compressed, shows additional information and appends the modal to the document body.\r\n     * Otherwise, displays feedback indicating that no compression is possible.\r\n     *\r\n     * @private\r\n     */\r\n    private _showModal = (): void => {\r\n        if (this.targetElement instanceof HTMLImageElement) {\r\n            this.imageDetails = this._getImageDetails(this.targetElement);\r\n            if (this.imageDetails.canCompress) {\r\n                this.modal.moreInfoButton.style.visibility = 'visible';\r\n                this.modal.moreInfoText.style.display = 'none';\r\n                document.body.append(this.modal.element);\r\n            } else {\r\n                this._displayFeedback(this.options.text.nothingToDo);\r\n            }\r\n        }\r\n    }\r\n\r\n    /**\r\n     * Removes the modal element from the DOM, effectively hiding the modal.\r\n     *\r\n     * @private\r\n     */\r\n    private _hideModal = (): void => {\r\n        this.modal.element.remove();\r\n    }\r\n\r\n    /**\r\n     * Parses the `width` and `height` attributes of an HTMLImageElement and returns their numeric values.\r\n     * Handles values specified in pixels (`px`), percentages (`%`), em/rem units, or plain numbers.\r\n     * If the attribute is a percentage, uses the maximum width from options if available.\r\n     * For em/rem units, assumes 16px per unit.\r\n     * If the height is not specified or cannot be parsed, attempts to calculate it using the aspect ratio\r\n     * from the image's natural dimensions if width is available.\r\n     * \r\n     * @param img - The HTMLImageElement whose dimensions are to be parsed.\r\n     * @returns A tuple containing the parsed width and height as numbers, or `null` if parsing fails.\r\n     */\r\n    private _parseDimensions = (img: HTMLImageElement): [number | null, number | null] => {\r\n        let width: string | null = img.getAttribute('width');\r\n        let height: string | null = img.getAttribute('height');\r\n        let parsedWidth: number | null = null;\r\n        let parsedHeight: number | null = null;\r\n\r\n        // Parse width\r\n        if (width) {\r\n            if (width.toLowerCase().endsWith('px')) {\r\n                parsedWidth = parseFloat(width);\r\n            } else if (width.endsWith('%')) {\r\n                parsedWidth = this.options.maxWidth ?? null;\r\n            } else if (width.toLowerCase().endsWith('em') || width.toLowerCase().endsWith('rem')) {\r\n                parsedWidth = parseFloat(width) * 16; // assume 16px per unit\r\n            } else if (!isNaN(parseFloat(width))) {\r\n                parsedWidth = parseFloat(width); // Parse as number only if it's plain numeric string\r\n            } else {\r\n                // unknown width attribute, return null to skip resizing\r\n                return [null, null];\r\n            }\r\n        }\r\n\r\n        // Parse height\r\n        if (height) {\r\n            if (!isNaN(parseFloat(height))) {\r\n                parsedHeight = parseFloat(height);\r\n            } else if (height.toLowerCase().endsWith('px')) {\r\n                parsedHeight = parseFloat(height);\r\n            } else if (height.toLowerCase().endsWith('em') || height.toLowerCase().endsWith('rem')) {\r\n                parsedHeight = parseFloat(height) * 16; // assume 16px per unit\r\n            } else {\r\n                // use aspect ratio if width is available and natural dimensions are valid\r\n                if (parsedWidth && img.naturalWidth > 0 && img.naturalHeight > 0) {\r\n                    parsedHeight = parsedWidth / (img.naturalWidth / img.naturalHeight);\r\n                } else {\r\n                    return [null, null];\r\n                }\r\n            }\r\n        }\r\n\r\n        return [parsedWidth, parsedHeight];\r\n    }\r\n\r\n    /**\r\n     * Calculates the approximate byte size of an image from its data URL.\r\n     *\r\n     * @param img - The HTMLImageElement whose size is to be determined.\r\n     * @returns The size of the image in bytes if the `src` attribute is a valid base64-encoded data URL,\r\n     *          or `null` if the `src` is not a valid image data URL or does not contain base64 data.\r\n     */\r\n    private _getImageSize = (img: HTMLImageElement): number | null => {\r\n        const dataUrl = img.getAttribute('src');\r\n        if (!dataUrl || !dataUrl.startsWith('data:image/')) {\r\n            // Return null if the src is not a valid data URL or doesn't contain image data\r\n            return null;\r\n        }\r\n        // Extract the base64-encoded part of the data URL\r\n        const base64Data = dataUrl.split(',')[1]; // Ignore the data type metadata before the comma\r\n        if (!base64Data) {\r\n            return null;\r\n        }\r\n        // Return the byte size of the base64 string\r\n        return Math.ceil((base64Data.length * 3) / 4);\r\n    }\r\n\r\n    /**\r\n     * Displays a feedback message in the formatter's sizeInfo element.\r\n     * The message is shown with full opacity, then fades out after 2.5 seconds.\r\n     *\r\n     * @param msg - The feedback message to display.\r\n     */\r\n    private _displayFeedback = (msg: string): void => {\r\n        this.formatter.sizeInfo.innerHTML = msg;\r\n        this.formatter.sizeInfo.style.transition = '';\r\n        this.formatter.sizeInfo.style.opacity = '1';\r\n        setTimeout(() => {\r\n            this.formatter.sizeInfo.style.transition = 'opacity 1s';\r\n            this.formatter.sizeInfo.style.opacity = '0';\r\n        }, 2500);\r\n    }\r\n\r\n    /**\r\n     * Retrieves detailed information about an image element, including its natural and target dimensions,\r\n     * file size, and whether it is eligible for compression based on the provided options.\r\n     *\r\n     * @param img - The HTMLImageElement to extract details from.\r\n     * @returns An {@link ImageDetails} object containing the image's natural and target dimensions,\r\n     *          file size, and compression eligibility.\r\n     */\r\n    private _getImageDetails = (img: HTMLImageElement): ImageDetails => {\r\n        let [width, height] = this._parseDimensions(img);\r\n        if (!width && ((this.options.maxWidth ?? Infinity) < img.naturalWidth)) {\r\n            width = this.options.maxWidth as number;\r\n            height = width / (img.naturalWidth / img.naturalHeight);\r\n        }\r\n        const details = {\r\n            naturalWidth: img.naturalWidth,\r\n            naturalHeight: img.naturalHeight,\r\n            targetWidth: width,\r\n            targetHeight: height,\r\n            size: this._getImageSize(img) as number,\r\n            canCompress: !!(width && height && (width < img.naturalWidth) && CompressAction.isEligibleForCompression(img, this.debug))\r\n        }\r\n        if (this.debug) {\r\n            console.debug('Image details:', {\r\n                element: img,\r\n                ...details,\r\n            });\r\n        }\r\n        return details;\r\n    }\r\n\r\n    /**\r\n     * Compresses a given HTMLImageElement by resizing it to target dimensions and reducing its quality.\r\n     * If compression results in a smaller image, the image's `src` is updated with the compressed data URL.\r\n     * Displays feedback about the compression result, including size reduction and new dimensions.\r\n     *\r\n     * @param img - The HTMLImageElement to compress.\r\n     * @returns `true` if the compression process was initiated, `false` if image loading failed.\r\n     *\r\n     * @remarks\r\n     * - Compression only occurs if `imageDetails.canCompress` is `true`.\r\n     * - The image is resized to `imageDetails.targetWidth` and `imageDetails.targetHeight`.\r\n     * - JPEG quality is determined by `options.jpegQuality`.\r\n     * - Feedback is displayed using `_displayFeedback`.\r\n     * - If compression is not possible, a \"nothing to do\" message is shown.\r\n     */\r\n    private _compressImage = (img: HTMLImageElement): boolean => {\r\n        if (this.imageDetails?.canCompress) {\r\n            const newImg = new Image();\r\n            newImg.src = img.src;\r\n            // Once the image has loaded, resize it\r\n            newImg.onload = () => {\r\n                if (this.debug) {\r\n                    console.debug('Compressing Image Copy loaded:', newImg);\r\n                }\r\n                // Create a canvas element\r\n                const canvas = document.createElement('canvas');\r\n                canvas.width = this.imageDetails!.targetWidth as number;\r\n                canvas.height = this.imageDetails!.targetHeight as number;\r\n                // Draw the image onto the canvas\r\n                const ctx = canvas.getContext('2d') as CanvasRenderingContext2D;\r\n                ctx.drawImage(newImg, 0, 0, canvas.width, canvas.height);\r\n                // Get the resized image data URL in JPEG format with a quality of this.options.jpegQuality\r\n                const resizedDataUrl = canvas.toDataURL('image/jpeg', this.options.jpegQuality);\r\n                // Convert data URLs to byte length\r\n                const originalSize = new TextEncoder().encode(img.src).length;\r\n                const resizedSize = new TextEncoder().encode(resizedDataUrl).length;\r\n                // Check if the resized image is smaller than the original\r\n                if (resizedSize < originalSize) {\r\n                    // Set the resized image data URL to the original image\r\n                    img.src = resizedDataUrl;\r\n                }\r\n                const sizeDiff: string = `${Math.ceil((this.imageDetails!.size - (this._getImageSize(img) as number)) / 1024)}kB`\r\n                const msg: string = `${this.options.text.reducedLabel}: ${sizeDiff}<br>\r\n                            ${this.imageDetails!.naturalWidth} x ${this.imageDetails!.naturalHeight}px → ${canvas.width} x ${Math.round(canvas.height)}px\r\n                        `;\r\n                if (this.debug) {\r\n                    console.debug('Image compressed:', {\r\n                        'original size': originalSize,\r\n                        'resized size': resizedSize,\r\n                        'size diff': sizeDiff,\r\n                        'new dimensions': { width: canvas.width, height: Math.round(canvas.height) }\r\n                    });\r\n                }\r\n                this._displayFeedback(msg);\r\n                return true;\r\n            };\r\n            newImg.onerror = (error) => {\r\n                console.error('Image loading failed:', error);\r\n                this._displayFeedback(`Image loading failed: ${error}`);\r\n                return false;\r\n            };\r\n        } else {\r\n            this._displayFeedback(this.options.text.nothingToDo);\r\n        };\r\n        return true;\r\n    }\r\n\r\n    /**\r\n     * Creates and configures a modal dialog for the compress action.\r\n     *\r\n     * The modal includes a prompt, an optional \"more info\" section, and three buttons:\r\n     * Cancel, More Info, and Continue. Styles and content are applied based on the\r\n     * provided options. Event listeners are attached to handle user interactions:\r\n     * - Continue: triggers image compression and hides the modal.\r\n     * - More Info: displays additional information and hides the button.\r\n     * - Cancel: hides the modal.\r\n     * - Clicking the background: hides the modal if the background itself is clicked.\r\n     *\r\n     * @returns {CompressModal} An object containing the modal background element,\r\n     *          the \"More Info\" button, and the \"More Info\" text element.\r\n     */\r\n    private _createModal = (): CompressModal => {\r\n        const modalBackground: HTMLDivElement = document.createElement('div');\r\n        modalBackground.setAttribute('data-blot-formatter-compress-modal', '');\r\n        const modalContainer: HTMLDivElement = document.createElement('div');\r\n        const modalDefaultPrompt: HTMLDivElement = document.createElement('div');\r\n        const modalMoreInfo: HTMLDivElement = document.createElement('div');\r\n        const modalButtonContainer: HTMLDivElement = document.createElement('div');\r\n        const cancelButton: HTMLButtonElement = document.createElement('button');\r\n        const moreInfoButton: HTMLButtonElement = document.createElement('button');\r\n        const continueButton: HTMLButtonElement = document.createElement('button');\r\n        modalMoreInfo.style.display = 'none';\r\n        modalButtonContainer.append(cancelButton, moreInfoButton, continueButton);\r\n        modalContainer.append(modalDefaultPrompt, modalMoreInfo, modalButtonContainer);\r\n        modalBackground.appendChild(modalContainer);\r\n        modalDefaultPrompt.innerHTML = this.options.text.prompt;\r\n        modalMoreInfo.innerHTML = this.options.text.moreInfo || '';\r\n        if (this.options.styles) {\r\n            Object.assign(modalBackground.style, this.options.styles.modalBackground);\r\n            Object.assign(modalContainer.style, this.options.styles.modalContainer);\r\n            Object.assign(modalButtonContainer.style, this.options.styles.buttonContainer);\r\n            Object.assign(cancelButton.style, { ...this.options.styles.buttons, ...this.options.buttons.cancel.style });\r\n            if (this.options.text.moreInfo) {\r\n                Object.assign(moreInfoButton.style, { ...this.options.styles.buttons, ...this.options.buttons.moreInfo.style });\r\n            } else {\r\n                moreInfoButton.style.visibility = 'hidden';\r\n            }\r\n            Object.assign(continueButton.style, { ...this.options.styles.buttons, ...this.options.buttons.continue.style });\r\n        }\r\n        cancelButton.innerHTML = this.options.icons.cancel;\r\n        moreInfoButton.innerHTML = this.options.icons.moreInfo;\r\n        continueButton.innerHTML = this.options.icons.continue;\r\n\r\n        // event listeners\r\n        continueButton.addEventListener('click', this._onContinueClick);\r\n        moreInfoButton.addEventListener('click', this._onMoreInfoClick);\r\n        cancelButton.addEventListener('click', this._hideModal);\r\n        modalBackground.addEventListener('pointerdown', this._onBackgroundClick);\r\n        return {\r\n            element: modalBackground,\r\n            moreInfoButton: moreInfoButton,\r\n            cancelButton: cancelButton,\r\n            continueButton: continueButton,\r\n            moreInfoText: modalMoreInfo,\r\n        }\r\n    }\r\n\r\n    private _onContinueClick = (): void => {\r\n        if (this.targetElement instanceof HTMLImageElement) {\r\n            this._compressImage(this.targetElement);\r\n        }\r\n        this._hideModal();\r\n    }\r\n\r\n    private _onMoreInfoClick = (): void => {\r\n        this.modal.moreInfoText.innerHTML = this.options.text.moreInfo || '';\r\n        this.modal.moreInfoText.style.display = 'block';\r\n        this.modal.moreInfoButton.style.visibility = 'hidden';\r\n    }\r\n\r\n    private _onBackgroundClick = (event: PointerEvent) => {\r\n        event.stopImmediatePropagation();\r\n        if (event.target === this.modal.element) {\r\n            if (this.debug) {\r\n                console.debug('Modal background clicked, hiding modal');\r\n            }\r\n            this._hideModal();\r\n        }\r\n    }\r\n\r\n}","import Action from './Action';\r\nimport BlotFormatter from '../BlotFormatter';\r\nimport type Quill from 'quill';\r\nimport ToolbarButton from './toolbar/ToolbarButton';\r\nimport type { Blot } from '../specs/BlotSpec';\r\nimport type { LinkOptions } from '../Options';\r\n\r\n/**\r\n * Handles the link editing action for image blots within the Quill editor.\r\n * \r\n * `LinkAction` provides UI and logic for adding, editing, and removing links on image blots.\r\n * It manages the display and positioning of a modal dialog for link input, attaches and removes\r\n * event listeners for modal interaction, and applies or removes link formats on the target blot.\r\n * \r\n * @remarks\r\n * - Integrates with the BlotFormatter overlay and toolbar system.\r\n * - Ensures modal dialog is accessible and correctly positioned relative to the editor.\r\n * - Cleans up all resources and event listeners on destruction.\r\n * \r\n * @extends Action\r\n * \r\n * @public\r\n */\r\nexport default class LinkAction extends Action {\r\n    targetElement: HTMLElement | null | undefined = null;\r\n    currentBlot: Blot | null | undefined = null;\r\n    toolbarButton: ToolbarButton;\r\n    linkOptions: LinkOptions;\r\n    modal?: {\r\n        dialog: HTMLDialogElement;\r\n        background: HTMLDivElement;\r\n        form: HTMLFormElement;\r\n        label: HTMLLabelElement;\r\n        input: HTMLInputElement;\r\n        okButton: HTMLButtonElement;\r\n        cancelButton: HTMLButtonElement;\r\n        removeButton: HTMLButtonElement;\r\n    }\r\n\r\n    constructor(formatter: BlotFormatter) {\r\n        super(formatter);\r\n        this.linkOptions = this.formatter.options.image.linkOptions;\r\n        this.toolbarButton = new ToolbarButton(\r\n            'link',\r\n            this._onClickHandler,\r\n            this.formatter.options.toolbar\r\n        );\r\n        // override preselect method to show button active if img has link on load\r\n        this.toolbarButton.preselect = () => {\r\n            return !!this.getLink();\r\n        };\r\n        this.toolbarButtons = [this.toolbarButton];\r\n        (window as any).LinkAction = this; // For debugging purposes\r\n    }\r\n\r\n    /**\r\n     * Initializes the action by setting the `targetElement` property.\r\n     * Retrieves the target element from the current formatter specification, if available.\r\n     * This method is typically called when the action is created.\r\n     */\r\n    onCreate = (): void => {\r\n        this.targetElement = this.formatter.currentSpec?.getTargetElement();\r\n        this.currentBlot = this.formatter.currentSpec?.getTargetBlot();\r\n    }\r\n\r\n    /**\r\n     * Cleans up resources when the action is destroyed.\r\n     * - Sets the target element to null.\r\n     * - Removes any attached event listeners.\r\n     * - Hides the link modal if it is visible.\r\n     */\r\n    onDestroy = (): void => {\r\n        this.targetElement = null;\r\n        this._removeEventListeners();\r\n        this.hideLinkModal();\r\n    }\r\n\r\n    /**\r\n     * Attaches all necessary event listeners to the modal elements for handling\r\n     * link-related actions such as submitting the form, blocking certain key events,\r\n     * handling input changes, canceling, removing links, and managing background/context menu interactions.\r\n     *\r\n     * This method should be called after the modal elements are initialized to ensure\r\n     * proper event handling within the link modal dialog.\r\n     * \r\n     * @private\r\n     */\r\n    private _addEventListeners = (): void => {\r\n        if (this.modal) {\r\n            this.modal.form.addEventListener('submit', this._formSubmitHandler);\r\n            this.modal.cancelButton.addEventListener('click', this.hideLinkModal);\r\n            this.modal.removeButton.addEventListener('click', this.removeLink);\r\n            this.modal.background.addEventListener('click', this._onBackgroundClick);\r\n            this.modal.input.addEventListener('contextmenu', this._trapContextEvent);\r\n        }\r\n    }\r\n\r\n    /**\r\n     * Removes all event listeners attached to the modal elements.\r\n     * \r\n     * This method detaches event handlers from the modal's dialog, form, input,\r\n     * cancel button, remove button, background, and input context menu to prevent\r\n     * memory leaks and unintended behavior when the modal is no longer in use.\r\n     * \r\n     * @private\r\n     */\r\n    private _removeEventListeners = (): void => {\r\n        if (this.modal) {\r\n            this.modal.form.removeEventListener('submit', this._formSubmitHandler);\r\n            this.modal.cancelButton.removeEventListener('click', this.hideLinkModal);\r\n            this.modal.removeButton.removeEventListener('click', this.removeLink);\r\n            this.modal.background.removeEventListener('click', this._onBackgroundClick);\r\n            this.modal.input.removeEventListener('contextmenu', this._trapContextEvent);\r\n        }\r\n    }\r\n\r\n    /**\r\n     * Prevents the event from propagating further in the event chain.\r\n     * This method is typically used to trap context menu or similar events,\r\n     * ensuring that no other event listeners are triggered for the same event.\r\n     *\r\n     * @param e - The event to be stopped.\r\n     */\r\n    private _trapContextEvent = (e: Event): void => {\r\n        e.stopImmediatePropagation();\r\n    }\r\n\r\n    /**\r\n     * Event handler that is triggered when the associated element is clicked.\r\n     * Invokes the `showLinkModal` method to display the link editing modal.\r\n     *\r\n     * @private\r\n     * @remarks\r\n     * This handler is typically bound to a UI element to allow users to edit or add links.\r\n     */\r\n    private _onClickHandler: EventListener = (): void => {\r\n        this.showLinkModal();\r\n    }\r\n\r\n    /**\r\n     * Handles click events on the modal background.\r\n     * \r\n     * If the click event's target is the modal background, this method prevents the default behavior,\r\n     * stops the event from propagating further, and hides the link modal.\r\n     *\r\n     * @param e - The mouse event triggered by the user's click.\r\n     */\r\n    private _onBackgroundClick = (e: MouseEvent): void => {\r\n        if (e.target === this.modal?.background) {\r\n            e.stopImmediatePropagation();\r\n            e.preventDefault();\r\n            this.hideLinkModal();\r\n            if (this.debug) {\r\n                console.debug('LinkAction modal background clicked, hiding modal');\r\n            }   \r\n        }\r\n    }\r\n\r\n    /**\r\n     * Displays the link modal dialog for editing or inserting a link.\r\n     * \r\n     * If a target element is present, this method constructs the modal,\r\n     * appends it to the formatter's overlay, and sets up necessary event listeners.\r\n     * The modal is initially hidden to prevent flicker, then shown after being\r\n     * positioned correctly relative to the target element.\r\n     *\r\n     * @returns {void}\r\n     */\r\n    showLinkModal = (): void => {\r\n        if (this.targetElement) {\r\n            this.modal = this._buildModal();\r\n            if (!this.modal) return;\r\n\r\n            this.formatter.overlay.append(this.modal.dialog, this.modal.background);\r\n            this._addEventListeners();\r\n\r\n            // Hide to prevent flicker and force layout\r\n            this.modal.dialog.style.visibility = 'hidden';\r\n            this.modal.dialog.show();\r\n            this._positionModal(this.modal.dialog);\r\n            // Show after positioning\r\n            this.modal.dialog.style.visibility = \"visible\";\r\n            // Focus the input field\r\n            this.modal.input.focus();\r\n            this.modal.input.select();\r\n        }\r\n    }\r\n\r\n    /**\r\n     * Builds and returns the modal elements used for editing or inserting a link.\r\n     *\r\n     * This method creates a dialog element containing a form with a label, input field for the URL,\r\n     * and three buttons: OK (submit), Remove, and Cancel. It also creates a background mask element.\r\n     * All elements are styled and configured according to the `linkOptions` provided to the class.\r\n     *\r\n     * @returns An object containing references to the created modal elements:\r\n     * - `dialog`: The dialog element that serves as the modal container.\r\n     * - `background`: The background mask element for the modal.\r\n     * - `form`: The form element inside the dialog.\r\n     * - `label`: The label element for the input.\r\n     * - `input`: The input element for entering the link URL.\r\n     * - `okButton`: The submit button for confirming the link.\r\n     * - `cancelButton`: The button for cancelling the operation.\r\n     * - `removeButton`: The button for removing the link.\r\n     */\r\n    private _buildModal = (): typeof this.modal => {\r\n        const dialog = document.createElement('dialog');\r\n        dialog.className = this.linkOptions.modal.dialog.className;\r\n        dialog.dataset.blotFormatterModal = '';\r\n        Object.assign(dialog.style, this.linkOptions.modal.dialog.style);\r\n\r\n        // Create form\r\n        const form = document.createElement('form');\r\n        form.method = 'dialog';\r\n        form.className = this.linkOptions.modal.form.className;\r\n        Object.assign(form.style, this.linkOptions.modal.form.style);\r\n\r\n        // Label\r\n        const label = document.createElement('label');\r\n        label.htmlFor = 'link-url';\r\n        label.textContent = this.linkOptions.modal.label.text;\r\n        label.className = this.linkOptions.modal.label.className;\r\n        Object.assign(label.style, this.linkOptions.modal.label.style);\r\n\r\n        // Input\r\n        const input = document.createElement('input');\r\n        input.type = 'url';\r\n        input.id = 'link-url';\r\n        input.name = 'url';\r\n        input.value = this.getLink() || '';\r\n        input.select();\r\n        input.autofocus = true;\r\n        input.className = this.linkOptions.modal.input.className;\r\n        Object.assign(input.style, this.linkOptions.modal.input.style);\r\n        input.placeholder = this.linkOptions.modal.input.placeholder || '';\r\n\r\n        // OK button\r\n        const okButton = document.createElement('button');\r\n        okButton.type = 'submit';\r\n        okButton.innerHTML = this.linkOptions.modal.buttons.submit.icon;\r\n        okButton.className = this.linkOptions.modal.buttons.submit.className;\r\n        Object.assign(okButton.style, this.linkOptions.modal.buttons.submit.style);\r\n\r\n        // Cancel button\r\n        const cancelButton = document.createElement('button');\r\n        cancelButton.type = 'button';\r\n        cancelButton.innerHTML = this.linkOptions.modal.buttons.cancel.icon;\r\n        cancelButton.className = this.linkOptions.modal.buttons.cancel.className;\r\n        Object.assign(cancelButton.style, this.linkOptions.modal.buttons.cancel.style);\r\n\r\n        // Remove button\r\n        const removeButton = document.createElement('button');\r\n        removeButton.type = 'button';\r\n        removeButton.innerHTML = this.linkOptions.modal.buttons.remove.icon;\r\n        removeButton.className = this.linkOptions.modal.buttons.remove.className;\r\n        Object.assign(removeButton.style, this.linkOptions.modal.buttons.remove.style);\r\n\r\n        // Append modal\r\n        form.appendChild(label);\r\n        form.appendChild(input);\r\n        form.appendChild(okButton);\r\n        form.appendChild(removeButton);\r\n        form.appendChild(cancelButton);\r\n        dialog.appendChild(form);\r\n\r\n        // Background mask\r\n        const background = document.createElement('div');\r\n        background.className = this.linkOptions.modal.background.className || '';\r\n        Object.assign(background.style, this.linkOptions.modal.background.style);\r\n\r\n        return {\r\n            dialog,\r\n            background,\r\n            form,\r\n            label,\r\n            input,\r\n            okButton,\r\n            cancelButton,\r\n            removeButton\r\n        };\r\n    }\r\n\r\n    /**\r\n     * Positions the given dialog element centered over the formatter's overlay,\r\n     * ensuring it stays within the bounds of the Quill editor root element.\r\n     *\r\n     * The method calculates the overlay and Quill root bounding rectangles,\r\n     * determines the dialog's dimensions, and computes the appropriate\r\n     * `left` and `top` CSS properties to center the dialog over the overlay.\r\n     * The horizontal & vertical position is clamped so the dialog does not overflow\r\n     * the Quill root element.\r\n     *\r\n     * @param dialog - The HTMLDialogElement to position.\r\n     */\r\n    private _positionModal = (dialog: HTMLDialogElement): void => {\r\n        const overlayRect = this.formatter.overlay.getBoundingClientRect();\r\n        const quillRect = this.formatter.quill.root.getBoundingClientRect();\r\n        const offsetParentRect = dialog.offsetParent?.getBoundingClientRect() ?? { top: 0, left: 0 };\r\n\r\n        const dialogWidth = dialog.offsetWidth;\r\n        const dialogHeight = dialog.offsetHeight;\r\n\r\n        // Center horizontally relative to overlay\r\n        let left = overlayRect.left + overlayRect.width / 2 - dialogWidth / 2 - offsetParentRect.left;\r\n        let top = overlayRect.top + overlayRect.height / 2 - dialogHeight / 2 - offsetParentRect.top;\r\n\r\n        // Clamp left to stay within quill root, in same coordinate space\r\n        const minLeft = quillRect.left - offsetParentRect.left;\r\n        const maxLeft = quillRect.right - dialogWidth - offsetParentRect.left;\r\n        left = Math.min(Math.max(left, minLeft), maxLeft);\r\n\r\n        const minTop = quillRect.top - offsetParentRect.top;\r\n        const maxTop = quillRect.bottom - dialogHeight - offsetParentRect.top;\r\n        top = Math.min(Math.max(top, minTop), maxTop);\r\n\r\n        // Apply positioning\r\n        dialog.style.position = \"absolute\";\r\n        dialog.style.left = `${left}px`;\r\n        dialog.style.top = `${top}px`;\r\n    }\r\n\r\n    /**\r\n     * Hides and cleans up the link modal dialog.\r\n     *\r\n     * This method closes and removes the modal dialog and its background overlay if they exist,\r\n     * removes any associated event listeners, and resets the modal reference to undefined.\r\n     */\r\n    hideLinkModal = (): void => {\r\n        if (this.modal?.dialog?.open) this.modal.dialog.close();\r\n        this.modal?.dialog?.remove();\r\n        if (this.modal?.background)\r\n            this.modal.background.remove();\r\n        this._removeEventListeners();\r\n        this.modal = undefined;\r\n    }\r\n\r\n    /**\r\n     * Handles the form submission event for the link action.\r\n     * \r\n     * Prevents the default form submission behavior, extracts the URL from the form data,\r\n     * and applies or removes a link on the current blot based on the URL's presence.\r\n     *\r\n     * @param event - The form submission event.\r\n     */\r\n    private _formSubmitHandler = (event: Event): void => {\r\n        event.preventDefault();\r\n        const form = event.target as HTMLFormElement;\r\n        const formData = new FormData(form);\r\n        const url = (formData.get('url') as string).trim();\r\n        if (this.debug) {\r\n            console.debug('LinkAction form submitted with URL:', url);\r\n        }\r\n        if (this.currentBlot) {\r\n            if (url) {\r\n                this.applyLink(url);\r\n            } else {\r\n                this.removeLink();\r\n            }\r\n        }\r\n    }\r\n\r\n    /**\r\n     * Retrieves the link format associated with the current blot, if any.\r\n     *\r\n     * @returns {any | null} The link URL if the current blot has a link format, otherwise `null`.\r\n     *\r\n     * @remarks\r\n     * This method checks if the current blot exists and has a DOM node. It then retrieves the index of the blot\r\n     * in the Quill editor and fetches its formats. If a link format is present, it returns the link value; otherwise, it returns `null`.\r\n     */\r\n    getLink = (): any => {\r\n        const blot = this.currentBlot;\r\n        if (!blot || !blot.domNode) return null;\r\n        const index = this.formatter.quill.getIndex(blot);\r\n        const formats = this.formatter.quill.getFormat(index, 1, this.formatter.Quill.sources.SILENT);\r\n        if (this.debug) {\r\n            console.debug('LinkAction getLink called, formats:', formats);\r\n        }\r\n        return formats.link || null;\r\n    }\r\n\r\n    /**\r\n     * Removes the link format from the current image blot's parent wrapper, if present.\r\n     * \r\n     * Traverses up the blot hierarchy from the current image blot to find a parent blot\r\n     * with a 'link' format. If found, it removes the link format from that wrapper.\r\n     * After removing the link, it hides the link modal and deselects the toolbar button.\r\n     *\r\n     * @returns {void}\r\n     */\r\n    removeLink = (): void => {\r\n        const imageBlot = this.currentBlot;\r\n        if (!imageBlot || !imageBlot.domNode) return;\r\n        let wrapperBlot = imageBlot.parent;\r\n        // Traverse upward until we find the inline wrapper with the link format\r\n        while (wrapperBlot && typeof wrapperBlot.formats === 'function') {\r\n            const formats = wrapperBlot.formats();\r\n            if (formats.link) {\r\n                wrapperBlot.format('link', null);\r\n                break;\r\n            }\r\n            wrapperBlot = wrapperBlot.parent;\r\n        }\r\n        if (this.debug) {\r\n            console.debug('LinkAction removeLink called, removed link from blot:', wrapperBlot);\r\n        }\r\n        this.hideLinkModal();\r\n        this.toolbarButton.selected = false;\r\n    }\r\n\r\n    /**\r\n     * Applies a link to the current blot if the provided URL is different from the existing link.\r\n     * Removes any existing link, formats the current blot with the new link, and updates the toolbar button state.\r\n     * Hides the link modal after applying the link.\r\n     *\r\n     * @param url - The URL to apply as a link to the current blot.\r\n     */\r\n    applyLink = (url: string): void => {\r\n        if (url !== this.getLink()) {\r\n            this.removeLink();\r\n            this.currentBlot?.format('link', url);\r\n            this.toolbarButton.selected = (!!url);\r\n        }\r\n        this.hideLinkModal();\r\n    }\r\n}","import Action from '../actions/Action';\r\nimport AttributeAction from '../actions/AttributeAction';\r\nimport BlotFormatter from '../BlotFormatter';\r\nimport BlotSpec from './BlotSpec';\r\nimport CompressAction from '../actions/CompressAction';\r\nimport LinkAction from '../actions/LinkAction';\r\n\r\n/**\r\n * Represents a specification for handling image elements within a Quill editor instance.\r\n * \r\n * `ImageSpec` extends `BlotSpec` to provide image-specific functionality, including:\r\n * - Initializing event listeners for image selection.\r\n * - Determining available actions (link editing, alt/title editing, compression) based on formatter options.\r\n * - Managing the currently selected image element.\r\n * - Handling UI display and cleanup when the image overlay is shown or hidden.\r\n * \r\n * @remarks\r\n * This class is intended to be used as part of the Quill Blot Formatter extension for rich image editing.\r\n * \r\n * @extends BlotSpec\r\n */\r\nexport default class ImageSpec extends BlotSpec {\r\n  img: HTMLElement | null;\r\n\r\n  constructor(formatter: BlotFormatter) {\r\n    super(formatter);\r\n    this.img = null;\r\n  }\r\n\r\n  /**\r\n   * Initializes the image spec by attaching a click event listener to the Quill editor's root element.\r\n   * The event listener triggers the `onClick` handler when the root element is clicked.\r\n   */\r\n  init = (): void => {\r\n    this.formatter.quill.root.addEventListener('click', this.onClick);\r\n  }\r\n\r\n  /**\r\n   * Returns an array of available actions for the image spec, based on the current formatter options and image eligibility.\r\n   *\r\n   * The returned actions may include:\r\n   * - `LinkAction`: If link editing is allowed (`image.linkOptions.allowLinkEdit`).\r\n   * - `AttributeAction`: If alt/title editing is allowed (`image.allowAltTitleEdit`).\r\n   * - `CompressAction`: If compression is allowed (`image.allowCompressor`) and the image is eligible for compression.\r\n   *\r\n   * @returns {Array<Action>} The list of actions applicable to the current image spec.\r\n   */\r\n  getActions = (): Array<Action> => {\r\n    const actions = super.getActions();\r\n    if (this.formatter.options.image.linkOptions.allowLinkEdit) {\r\n      actions.push(new LinkAction(this.formatter));\r\n    }\r\n    if (this.formatter.options.image.allowAltTitleEdit) {\r\n      actions.push(new AttributeAction(this.formatter));\r\n    }\r\n    if (this.formatter.options.image.allowCompressor && CompressAction.isEligibleForCompression(this.img)) {\r\n      actions.push(new CompressAction(this.formatter));\r\n    }\r\n    return actions;\r\n  }\r\n\r\n  /**\r\n   * Returns the target HTML element associated with this instance.\r\n   *\r\n   * @returns {HTMLElement | null} The image element if available, otherwise `null`.\r\n   */\r\n  getTargetElement = (): HTMLElement | null => {\r\n    return this.img;\r\n  }\r\n\r\n  /**\r\n   * Handles the hide event by resetting the image reference to null.\r\n   * This is typically called when the overlay should no longer be displayed or interacted with.\r\n   */\r\n  onHide = (): void => {\r\n    this.img = null;\r\n  }\r\n\r\n  /**\r\n   * Handles click events on image elements.\r\n   * \r\n   * If the clicked element is an HTMLImageElement, prevents the default behaviour\r\n   * (such as opening links), stores a reference to the image, and displays the formatter UI.\r\n   * \r\n   * @param event - The mouse event triggered by the click.\r\n   */\r\n  onClick = (event: MouseEvent): void => {\r\n    const el = event.target;\r\n    if (this.formatter.enabled && el instanceof HTMLImageElement) {\r\n      // prevent <a> links from opening\r\n      event.stopImmediatePropagation();\r\n      event.preventDefault();\r\n      this.img = el;\r\n      this.formatter.show(this);\r\n    }\r\n  };\r\n}\r\n","import IframeVideoSpec from './specs/IframeVideoSpec';\r\nimport ImageSpec from './specs/ImageSpec';\r\nimport type { Options } from './Options';\r\n\r\nconst closeButtonIcon = `<svg viewBox=\"0 0 16 16\" fill=\"currentColor\" style=\"height:100%;width:auto\"><path d=\"M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2zm3.354 4.646L8 7.293l2.646-2.647a.5.5 0 0 1 .708.708L8.707 8l2.647 2.646a.5.5 0 0 1-.708.708L8 8.707l-2.646 2.647a.5.5 0 0 1-.708-.708L7.293 8 4.646 5.354a.5.5 0 1 1 .708-.708\"/></svg>`;\r\nconst okButtonIcon = `<svg viewBox=\"0 0 24 24\" fill=\"currentcolor\" style=\"height:100%;width:auto\"><path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M 12,24 C 6.34314,24 3.514716,24 1.757364,22.2426 0,20.48532 0,17.6568 0,12 0,6.34314 0,3.514716 1.757364,1.757364 3.514716,0 6.34314,0 12,0 17.6568,0 20.48532,0 22.2426,1.757364 24,3.514716 24,6.34314 24,12 24,17.6568 24,20.48532 22.2426,22.2426 20.48532,24 17.6568,24 12,24 Z M 16.83636,8.363604 c 0.35148,0.351468 0.35148,0.921324 0,1.272756 l -6,6 c -0.35148,0.35148 -0.92124,0.35148 -1.272756,0 l -2.4,-2.4 c -0.351468,-0.35148 -0.351468,-0.92124 0,-1.27272 0.351468,-0.35148 0.921324,-0.35148 1.272792,0 L 10.2,13.72716 15.56364,8.363604 c 0.35148,-0.351468 0.92124,-0.351468 1.27272,0 z\" style=\"stroke-width:1.2\" /></svg>`;\r\nconst infoButtonIcon = `<svg viewBox=\"0 0 512 512\" fill=\"currentcolor\" style=\"height:100%;width:auto\"><path d=\"M464 256A208 208 0 1 0 48 256a208 208 0 1 0 416 0zM0 256a256 256 0 1 1 512 0A256 256 0 1 1 0 256zm169.8-90.7c7.9-22.3 29.1-37.3 52.8-37.3l58.3 0c34.9 0 63.1 28.3 63.1 63.1c0 22.6-12.1 43.5-31.7 54.8L280 264.4c-.2 13-10.9 23.6-24 23.6c-13.3 0-24-10.7-24-24l0-13.5c0-8.6 4.6-16.5 12.1-20.8l44.3-25.4c4.7-2.7 7.6-7.7 7.6-13.1c0-8.4-6.8-15.1-15.1-15.1l-58.3 0c-3.4 0-6.4 2.1-7.5 5.3l-.4 1.2c-4.4 12.5-18.2 19-30.6 14.6s-19-18.2-14.6-30.6l.4-1.2zM224 352a32 32 0 1 1 64 0 32 32 0 1 1 -64 0z\" /></svg>`;\r\n\r\nexport const DefaultOptions: Options = {\r\n  specs: [\r\n    ImageSpec,\r\n    IframeVideoSpec,\r\n  ],\r\n  overlay: {\r\n    className: 'blot-formatter__overlay',\r\n    style: {\r\n      position: 'absolute',\r\n      boxSizing: 'border-box',\r\n      border: '1px dashed #444',\r\n      backgroundColor: 'rgba(255, 255, 255, 0.35)',\r\n      maxWidth: \"100%\",\r\n      zIndex: 9999\r\n    },\r\n    sizeInfoStyle: {\r\n      position: 'absolute',\r\n      color: 'rgba(255, 255, 255, 0.7)',\r\n      backgroundColor: 'rgba(0, 0, 0, 0.7)',\r\n      top: '50%',\r\n      left: '50%',\r\n      transform: 'translate(-50%, -50%)',\r\n      padding: '1em',\r\n      textWrap: 'nowrap',\r\n      fontSize: '1rem',\r\n      opacity: 0,\r\n      lineHeight: 1.2,\r\n    },\r\n  },\r\n  align: {\r\n    allowAligning: true,\r\n    alignments: ['left', 'center', 'right']\r\n  },\r\n  resize: {\r\n    allowResizing: true,\r\n    allowResizeModeChange: false,\r\n    imageOversizeProtection: false,\r\n    handleClassName: 'blot-formatter__resize-handle',\r\n    handleStyle: {\r\n      position: 'absolute',\r\n      height: '12px',\r\n      width: '12px',\r\n      backgroundColor: 'white',\r\n      border: '1px solid #777',\r\n      boxSizing: 'border-box',\r\n      opacity: '0.80',\r\n      zIndex: 999\r\n    },\r\n    useRelativeSize: false,\r\n    minimumWidthPx: 25,\r\n  },\r\n  delete: {\r\n    allowKeyboardDelete: true,\r\n  },\r\n  toolbar: {\r\n    icons: {\r\n      left: `<svg viewbox=\"0 0 18 18\"><line class=\"ql-stroke\" x1=\"3\" x2=\"15\" y1=\"9\" y2=\"9\"></line><line class=\"ql-stroke\" x1=\"3\" x2=\"13\" y1=\"14\" y2=\"14\"></line><line class=\"ql-stroke\" x1=\"3\" x2=\"9\" y1=\"4\" y2=\"4\"></line></svg>`,\r\n      center: `<svg viewbox=\"0 0 18 18\"><line class=\"ql-stroke\" x1=\"15\" x2=\"3\" y1=\"9\" y2=\"9\"></line><line class=\"ql-stroke\" x1=\"14\" x2=\"4\" y1=\"14\" y2=\"14\"></line><line class=\"ql-stroke\" x1=\"12\" x2=\"6\" y1=\"4\" y2=\"4\"></line></svg>`,\r\n      right: `<svg viewbox=\"0 0 18 18\"><line class=\"ql-stroke\" x1=\"15\" x2=\"3\" y1=\"9\" y2=\"9\"></line><line class=\"ql-stroke\" x1=\"15\" x2=\"5\" y1=\"14\" y2=\"14\"></line><line class=\"ql-stroke\" x1=\"15\" x2=\"9\" y1=\"4\" y2=\"4\"></line></svg>`,\r\n      attribute: `<svg viewBox=\"0 0 24 24\" fill=\"none\" class=\"ql-stroke\"><path d=\"M10 19H12M12 19H14M12 19V5M12 5H6V6M12 5H18V6\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>`,\r\n      resizeMode: `<svg viewBox=\"0 0 24 24\" class=\"ql-stroke\"><path d=\"m 7.7056591,11.853515 q -1.515179,0 -2.4160962,-0.993056 -0.9009172,-1.0032944 -0.9009172,-2.6720388 0,-1.8223098 0.9521057,-2.8665548 0.9521057,-1.0544826 2.5696616,-1.0544826 1.5663674,0 2.426334,0.9725811 0.870204,0.972581 0.870204,2.7334647 0,1.7608836 -0.972581,2.8256044 -0.9623435,1.054482 -2.5287109,1.054482 z M 7.8489868,5.3935293 q -0.9725811,0 -1.5356544,0.7268764 -0.5630732,0.7166387 -0.5630732,1.9758752 0,1.2387612 0.5528356,1.9349241 0.5528355,0.685926 1.5049412,0.685926 0.9623434,0 1.5049413,-0.716639 0.5425978,-0.7166384 0.5425978,-1.9861126 0,-1.2387612 -0.5425978,-1.9246868 Q 8.7806171,5.3935293 7.8489868,5.3935293 Z M 17.533847,4.4926121 8.1151669,19.275845 H 6.6511764 L 16.059619,4.4926121 Z M 16.448651,19.398697 q -1.515179,0 -2.416096,-1.003294 -0.900917,-1.003294 -0.900917,-2.661801 0,-1.82231 0.962343,-2.876793 0.962344,-1.06472 2.559424,-1.06472 1.55613,0 2.426334,0.982819 0.870204,0.982819 0.870204,2.75394 0,1.750646 -0.972581,2.815366 -0.962343,1.054483 -2.528711,1.054483 z m 0.143328,-6.449748 q -0.982819,0 -1.545892,0.716638 -0.552836,0.716639 -0.552836,1.986113 0,1.218286 0.552836,1.914449 0.552835,0.685926 1.504941,0.685926 0.962343,0 1.504941,-0.716639 0.542598,-0.726876 0.542598,-1.986113 0,-1.248998 -0.542598,-1.924686 -0.53236,-0.675688 -1.46399,-0.675688 z\" style=\"fill:currentColor;stroke:currentColor;stroke-width:0.3\"/></svg>`,\r\n      compress: `<svg viewBox=\"0 0 28 28\"><path d=\"m 19.250001,9.3125004 c 0.240623,0 0.437498,0.1968749 0.437498,0.4374991 V 18.49453 l -0.136717,-0.177734 -3.718751,-4.812498 c -0.123046,-0.161329 -0.317188,-0.254297 -0.51953,-0.254297 -0.202345,0 -0.39375,0.09297 -0.519532,0.254297 l -2.269532,2.936715 -0.833984,-1.167577 c -0.123047,-0.172265 -0.319922,-0.273437 -0.533204,-0.273437 -0.213281,0 -0.410156,0.101172 -0.533202,0.276172 l -2.1875003,3.0625 -0.1230462,0.169532 v -0.0082 -8.7500002 c 0,-0.2406242 0.1968749,-0.4374991 0.4374991,-0.4374991 z M 8.7499996,8 C 7.7847663,8 7,8.7847662 7,9.7499995 V 18.5 c 0,0.965233 0.7847663,1.75 1.7499996,1.75 H 19.250001 C 20.215235,20.25 21,19.465233 21,18.5 V 9.7499995 C 21,8.7847662 20.215235,8 19.250001,8 Z M 10.9375,13.250001 a 1.3125025,1.312501 0 1 0 0,-2.625002 1.3125025,1.312501 0 1 0 0,2.625002 z\" /><path d=\"m 25.298508,20 h -3.58209 C 21.286567,20 21,20.286571 21,20.716427 v 3.582131 c 0,0.429856 0.286567,0.716426 0.716418,0.716426 v 0 c 0.429851,0 0.716418,-0.28657 0.716418,-0.716426 v -2.865705 h 2.865672 c 0.429851,0 0.716418,-0.28657 0.716418,-0.716426 C 26.014926,20.286571 25.728359,20 25.298508,20 Z\" /><path d=\"M 6.298508,20 H 2.716418 C 2.2865673,20 2,20.286571 2,20.716427 c 0,0.429856 0.2865673,0.716426 0.716418,0.716426 H 5.58209 v 2.865705 c 0,0.429856 0.286567,0.716426 0.716418,0.716426 v 0 c 0.429851,0 0.716418,-0.28657 0.716418,-0.716426 V 20.716427 C 7.014926,20.286571 6.728359,20 6.298508,20 Z\" /><path d=\"M 6.298507,3 C 5.868656,3 5.582089,3.28657 5.582089,3.716426 V 6.582131 H 2.716417 C 2.286567,6.582131 2,6.868702 2,7.298557 2,7.728413 2.286567,8.014984 2.716417,8.014984 h 3.58209 c 0.429845,0 0.716412,-0.286571 0.716412,-0.716427 V 3.716426 C 7.014919,3.28657 6.728352,3 6.298507,3 Z\" /><path d=\"m 21.716418,8.014984 h 3.582089 c 0.429851,0 0.716418,-0.286571 0.716418,-0.716427 0,-0.429855 -0.286567,-0.716426 -0.716418,-0.716426 H 22.432836 V 3.716426 C 22.432836,3.28657 22.146269,3 21.716418,3 21.286567,3 21,3.28657 21,3.716426 v 3.582131 c 0,0.429856 0.286567,0.716427 0.716418,0.716427 z\" /></svg>`,\r\n      link: `<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 18 18\" style=\"fill: none;stroke: #444;stroke-linecap: round;stroke-linejoin: round;stroke-width: 1.5;\"><line x1=\"7\" x2=\"11\" y1=\"7\" y2=\"11\"/><path class=\"ql-even\" d=\"M8.9,4.577a3.476,3.476,0,0,1,.36,4.679A3.476,3.476,0,0,1,4.577,8.9C3.185,7.5,2.035,6.4,4.217,4.217S7.5,3.185,8.9,4.577Z\"/><path class=\"ql-even\" d=\"M13.423,9.1a3.476,3.476,0,0,0-4.679-.36,3.476,3.476,0,0,0,.36,4.679c1.392,1.392,2.5,2.542,4.679.36S14.815,10.5,13.423,9.1Z\"/></svg>`,\r\n    },\r\n    tooltips: {\r\n      left: 'Align Left',\r\n      center: 'Align Center',\r\n      right: 'Align Right',\r\n      attribute: 'Set Alt/Title',\r\n      resizeMode: 'Use Relative (%)/Absolute (px) Sizing',\r\n      compress: 'Compress Image',\r\n      link: 'Add/Edit Link',\r\n    },\r\n    mainClassName: 'blot-formatter__toolbar',\r\n    mainStyle: {\r\n      position: 'absolute',\r\n      display: 'flex',\r\n      top: '0',\r\n      right: '0',\r\n      left: '0',\r\n      transform: 'translateY(-50%)',\r\n      justifyContent: 'center',\r\n      flexWrap: 'wrap',\r\n      color: '#333',\r\n      zIndex: '1',\r\n    },\r\n    buttonClassName: 'blot-formatter__toolbar-button',\r\n    buttonStyle: {\r\n      display: 'inline-block',\r\n      width: '27px',\r\n      height: '26px',\r\n      background: 'white',\r\n      border: '1px solid #999',\r\n      cursor: 'pointer',\r\n      margin: '0 -1px 0 0'\r\n    },\r\n    buttonSelectedClassName: 'blot-formatter__toolbar-button--selected',\r\n    buttonSelectedStyle: {\r\n      filter: 'invert(20%)',\r\n    },\r\n    svgStyle: {\r\n      display: 'inline-block',\r\n      width: '100%',\r\n      height: '100%',\r\n      background: 'white',\r\n      verticalAlign: 'top',\r\n    },\r\n  },\r\n  image: {\r\n    allowAltTitleEdit: true,\r\n    registerImageTitleBlot: false,\r\n    registerArrowRightFix: true,\r\n    altTitleModalOptions: {\r\n      styles: {\r\n        modalBackground: {\r\n          position: 'fixed',\r\n          top: 0,\r\n          left: 0,\r\n          width: '100%',\r\n          height: '100%',\r\n          backgroundColor: 'rgba(0,0,0,0.5)',\r\n          display: 'flex',\r\n          alignItems: 'center',\r\n          justifyContent: 'center',\r\n          zIndex: 9999\r\n        },\r\n        modalContainer: {\r\n          backgroundColor: '#f2f2f2',\r\n          padding: '5px 10px 10px 10px',\r\n          borderRadius: '5px',\r\n          position: 'relative',\r\n          width: '90%',\r\n          maxWidth: '500px',\r\n          boxShadow: '6px 6px 5px #00000075'\r\n        },\r\n        label: {\r\n          display: 'block',\r\n          color: 'black',\r\n          margin: '7px 0 5px 0',\r\n          fontSize: '14px'\r\n        },\r\n        textarea: {\r\n          backgroundColor: 'white',\r\n          fontSize: '13px',\r\n          display: 'block',\r\n          resize: 'none',\r\n          width: '100%',\r\n          padding: '5px',\r\n          border: '1px solid lightgray',\r\n          borderRadius: '4px',\r\n          boxSizing: 'border-box'\r\n        },\r\n        submitButton: {\r\n          display: 'block',\r\n          marginLeft: 'auto',\r\n          marginTop: '5px',\r\n          cursor: 'pointer',\r\n          border: 0,\r\n          padding: 0,\r\n          width: '2rem',\r\n          height: '2rem',\r\n          color: '#198754'\r\n        },\r\n        cancelButton: {\r\n          display: 'flex',\r\n          width: '1.5rem',\r\n          height: '1.5rem',\r\n          position: 'absolute',\r\n          padding: 0,\r\n          top: '-0.7rem',\r\n          right: '-0.7rem',\r\n          background: 'white',\r\n          border: 'none',\r\n          borderRadius: '5px',\r\n          cursor: 'pointer',\r\n          alignItems: 'center',\r\n          color: 'rgb(197, 74, 71)'\r\n        },\r\n      },\r\n      icons: {\r\n        submitButton: okButtonIcon,\r\n        cancelButton: closeButtonIcon,\r\n      },\r\n      labels: {\r\n        alt: 'Alt Text',\r\n        title: 'Image Title'\r\n      }\r\n    },\r\n    allowCompressor: false,\r\n    compressorOptions: {\r\n      jpegQuality: 0.8,\r\n      styles: {\r\n        modalBackground: {\r\n          position: 'fixed',\r\n          top: 0,\r\n          left: 0,\r\n          width: '100%',\r\n          height: '100%',\r\n          backgroundColor: 'rgba(0,0,0,0.5)',\r\n          display: 'flex',\r\n          alignItems: 'center',\r\n          justifyContent: 'center',\r\n          zIndex: 9999\r\n        },\r\n        modalContainer: {\r\n          backgroundColor: 'rgb(253, 253, 253)',\r\n          border: '1px solid #ccc',\r\n          boxShadow: '6px 6px 5px #00000075',\r\n          padding: '15px',\r\n          borderRadius: '8px',\r\n          position: 'relative',\r\n          maxWidth: 'min(90%, 400px)',\r\n          textAlign: 'justify',\r\n          userSelect: 'none',\r\n        },\r\n        buttonContainer: {\r\n          gridTemplateColumns: 'auto 1fr auto',\r\n          display: 'grid',\r\n          gap: '1em',\r\n          justifyItems: 'center',\r\n          borderTop: '1px solid lightgray',\r\n          paddingTop: '12px'\r\n        },\r\n        buttons: {\r\n          width: '1.5rem',\r\n          height: '1.5rem',\r\n          padding: 0,\r\n          backgroundColor: 'transparent',\r\n          border: 0,\r\n          cursor: 'pointer',\r\n        }\r\n      },\r\n      buttons: {\r\n        continue: {\r\n          className: 'blot-formatter__compress-continue',\r\n          style: {\r\n            color: '#198754'\r\n          }\r\n        },\r\n        cancel: {\r\n          className: 'blot-formatter__compress-cancel',\r\n          style: {\r\n            color: 'rgb(197, 74, 71)'\r\n          }\r\n        },\r\n        moreInfo: {\r\n          className: 'blot-formatter__compress-more-info',\r\n          style: {\r\n            color: 'royalblue'\r\n          }\r\n        }\r\n      },\r\n      text: {\r\n        prompt: `<p style=\"font-style: large;margin: 0 0 0.5em;\">Compress image to its resized width?</p>`,\r\n        moreInfo: `<p style=\"font-size: smaller; line-height: 1.2;\">You can reduce the file size and save disk space by compressing pictures. The compression reduces both the file size and picture dimensions based on the width setting.</p><p style=\"font-size: smaller;\"><strong>NOTE:</strong> This process cannot be undone.</p>`,\r\n        reducedLabel: 'Reduced',\r\n        nothingToDo: 'Image already optimised.'\r\n      },\r\n      icons: {\r\n        cancel: `<span style=\"color: rgb(197, 74, 71);\">${closeButtonIcon}</span>`,\r\n        moreInfo: infoButtonIcon,\r\n        continue: okButtonIcon\r\n      }\r\n    },\r\n    linkOptions: {\r\n      allowLinkEdit: true,\r\n      modal: {\r\n        dialog: {\r\n          className: 'blot-formatter__link-modal',\r\n          style: { // NOTE: positioning handled programatically\r\n            margin: 0,\r\n            backgroundColor: '#fdfdfd',\r\n            border: '1px solid #ccc',\r\n            boxShadow: '6px 6px 5px #00000075',\r\n            color: '#444',\r\n            padding: '6px 13px 6px 10px',\r\n            whiteSpace: 'nowrap',\r\n            borderRadius: '5px',\r\n            minWidth: '300px',\r\n            maxWidth: '90%',\r\n            overflow: 'visible',\r\n            zIndex: 101, // Ensure it is above the background\r\n          }\r\n        },\r\n        background: {\r\n          className: 'blot-formatter__link-modal-background',\r\n          style: {\r\n            position: 'fixed',\r\n            inset: 0,\r\n            background: 'rgba(0, 0, 0, 0.5)',\r\n            zIndex: 100,\r\n          }\r\n        },\r\n        form: {\r\n          className: 'blot-formatter__link-form',\r\n          style: {\r\n            display: 'flex',\r\n            flexWrap: 'nowrap',\r\n            columnGap: '5px',\r\n            alignItems: 'center',\r\n            margin: 0\r\n          }\r\n        },\r\n        label: {\r\n          className: 'blot-formatter__link-label',\r\n          style: {\r\n            paddingRight: '5px',\r\n            fontSize: '13px',\r\n          },\r\n          text: 'URL:',\r\n        },\r\n        input: {\r\n          className: 'blot-formatter__link-input',\r\n          style: {\r\n            border: '1px solid #ccc',\r\n            borderRadius: '4px',\r\n            boxSizing: 'border-box',\r\n            fontSize: '13px',\r\n            height: '26px',\r\n            margin: '0 0.2rem 0 0',\r\n            padding: '3px 5px',\r\n            width: '100%',\r\n            outline: '1px auto #df9001c2',\r\n          },\r\n          placeholder: 'https://example.com',\r\n        },\r\n        buttons: {\r\n          submit: {\r\n            className: 'blot-formatter__link-submit',\r\n            style: {\r\n              border: 'none',\r\n              borderRadius: '3px',\r\n              padding: '0',\r\n              cursor: 'pointer',\r\n              background: 'transparent',\r\n              width: '26px',\r\n              height: '26px',\r\n              color: '#198754',\r\n              display: 'flex',\r\n              alignContent: 'center',\r\n              justifyContent: 'center',\r\n            },\r\n            icon: `<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"currentColor\" viewBox=\"0 0 16 16\" style=\"height:100%;width:auto\"><path d=\"M14 1a1 1 0 0 1 1 1v12a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1zM2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2z\"/><path d=\"M10.97 4.97a.75.75 0 0 1 1.071 1.05l-3.992 4.99a.75.75 0 0 1-1.08.02L4.324 8.384a.75.75 0 1 1 1.06-1.06l2.094 2.093 3.473-4.425z\"/></svg>`,\r\n            tooltip: 'Create Link'\r\n          },\r\n          cancel: {\r\n            className: 'blot-formatter__link-cancel',\r\n            style: {\r\n              display: 'flex',\r\n              width: '18px',\r\n              height: '18px',\r\n              position: 'absolute',\r\n              top: '-8px',\r\n              right: '-10px',\r\n              padding: '0',\r\n              background: 'white',\r\n              border: '1px solid rgb(157, 58, 56)',\r\n              borderRadius: '5px',\r\n              cursor: 'pointer',\r\n              alignItems: 'center',\r\n              color: 'rgb(197, 74, 71)',\r\n            },\r\n            icon: closeButtonIcon,\r\n            tooltip: 'Cancel'\r\n          },\r\n          remove: {\r\n            className: 'blot-formatter__link-remove',\r\n            style: {\r\n              border: 'none',\r\n              padding: '0',\r\n              cursor: 'pointer',\r\n              background: 'transparent',\r\n              width: '26px',\r\n              height: '26px',\r\n              fill: '#c54a47',\r\n              display: 'flex',\r\n              placeContent: 'center',\r\n              justifyContent: 'center',\r\n            },\r\n            icon: `<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:svg=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\" style=\"height:100%;width:auto\"><path d=\"M 6.265625,3.3457031 6.125,3.625 H 4.25 c -0.3457031,0 -0.625,0.2792969 -0.625,0.625 0,0.3457031 0.2792969,0.625 0.625,0.625 h 7.5 c 0.345703,0 0.625,-0.2792969 0.625,-0.625 0,-0.3457031 -0.279297,-0.625 -0.625,-0.625 H 9.875 L 9.734375,3.3457031 C 9.6289062,3.1328125 9.4121094,3 9.1757812,3 H 6.8242188 C 6.5878906,3 6.3710938,3.1328125 6.265625,3.3457031 Z M 11.75,5.5 h -7.5 l 0.4140625,6.621094 C 4.6953125,12.615234 5.1054688,13 5.5996094,13 h 4.8007816 c 0.49414,0 0.904297,-0.384766 0.935547,-0.878906 z\" style=\"display:inline;stroke-width:0.0195312\"/><path d=\"m 14,1 a 1,1 0 0 1 1,1 v 12 a 1,1 0 0 1 -1,1 H 2 A 1,1 0 0 1 1,14 V 2 A 1,1 0 0 1 2,1 Z M 2,0 A 2,2 0 0 0 0,2 v 12 a 2,2 0 0 0 2,2 h 12 a 2,2 0 0 0 2,-2 V 2 A 2,2 0 0 0 14,0 Z\" style=\"display:inline\"/></svg>`,\r\n            tooltip: 'Remove Link'\r\n          }\r\n        }\r\n      }\r\n    },\r\n    autoHeight: true\r\n  },\r\n  video: {\r\n    selector: 'iframe.ql-video',\r\n    registerCustomVideoBlot: false,\r\n    registerBackspaceFix: true,\r\n    defaultAspectRatio: '16/9 auto',\r\n    proxyStyle: {}\r\n  }\r\n};\r\n","import Action from './actions/Action';\r\nimport BlotSpec from './specs/BlotSpec';\r\nimport CaretAction from './actions/CaretAction';\r\nimport deepmerge from 'deepmerge';\r\nimport type Quill from 'quill';\r\nimport Toolbar from './actions/toolbar/Toolbar';\r\nimport TooltipContainPosition from './tooltip/TooltipContainPosition';\r\nimport type { AttributorClass } from './actions/align/AlignFormats';\r\nimport { createAltTitleImageBlotClass } from './blots/Image';\r\nimport { createIframeAlignAttributor, createImageAlignAttributor } from './actions/align/AlignFormats';\r\nimport { createResponsiveVideoBlotClass } from './blots/Video';\r\nimport { DefaultOptions } from './DefaultOptions';\r\nimport type { Options } from './Options';\r\n\r\nconst dontMerge = (destination: Array<any>, source: Array<any>) => source;\r\n\r\n/**\r\n * Represents the possible positions of a pointer relative to the formatter overlay.\r\n *\r\n * - `LEFT`: The pointer is positioned to the left of the overlay.\r\n * - `RIGHT`: The pointer is positioned to the right of the overlay.\r\n * - `ABOVE`: The pointer is positioned above the overlay.\r\n * - `BELOW`: The pointer is positioned below the overlay.\r\n * - `INSIDE`: The pointer is positioned inside the overlay.\r\n */\r\nenum PointerPosition {\r\n  LEFT = 'left',\r\n  RIGHT = 'right',\r\n  ABOVE = 'above',\r\n  BELOW = 'below',\r\n  INSIDE = 'inside',\r\n}\r\n\r\ntype PxString = `${number}px`;\r\ntype CssRectPx = {\r\n  left: PxString;\r\n  top: PxString;\r\n  width: PxString;\r\n  height: PxString;\r\n};\r\n\r\n/**\r\n * BlotFormatter is a Quill module that provides an interactive overlay for formatting embedded blots\r\n * (such as images, iframes, and videos) within the Quill rich text editor. It enables resizing, alignment,\r\n * and other custom actions on supported blots via a floating UI, and manages all related event handling,\r\n * DOM manipulation, and integration with Quill's API.\r\n *\r\n * Features:\r\n * - Displays an overlay with handles and a toolbar for formatting selected blots.\r\n * - Supports custom actions, alignment, and resizing (with both relative and absolute sizing).\r\n * - Integrates with Quill's keyboard bindings to fix known issues with embedded content.\r\n * - Handles touch and mouse interactions, including scrolling and context menu suppression.\r\n * - Registers and manages custom blots and attributors for advanced formatting.\r\n * - Provides robust cleanup and destruction of all event listeners and DOM elements.\r\n * - Exposes debugging hooks and logs when enabled via options.\r\n *\r\n * Usage:\r\n * Instantiate with a Quill editor instance and optional configuration options.\r\n * The formatter automatically attaches to the editor and manages its own lifecycle.\r\n *\r\n * Example:\r\n * ```typescript\r\n * const quill = new Quill('#editor', { ... });\r\n * const blotFormatter = new BlotFormatter(quill, { debug: true });\r\n * ```\r\n *\r\n * @template Options - The configuration options type for the formatter.\r\n * @template BlotSpec - The specification interface for supported blots.\r\n * @template Action - The interface for custom actions available in the overlay.\r\n *\r\n * @public\r\n */\r\nexport default class BlotFormatter {\r\n  Quill: typeof Quill;\r\n  quill: any;\r\n  options: Options;\r\n  currentSpec: BlotSpec | null;\r\n  specs: BlotSpec[];\r\n  overlay: HTMLElement;\r\n  toolbar: Toolbar;\r\n  sizeInfo: HTMLElement;\r\n  actions: Action[];\r\n  private _enabled: boolean = true;\r\n  private _startX: number = 0; // touch scroll tracking\r\n  private _startY: number = 0;\r\n  private _abortController?: AbortController;\r\n  private _resizeObserver?: ResizeObserver;\r\n  private _tooltipContainPosition?: TooltipContainPosition;\r\n  ImageAlign: AttributorClass;\r\n  IframeAlign: AttributorClass;\r\n\r\n  constructor(quill: any, options: Partial<Options> = {}) {\r\n    this.Quill = quill.constructor;\r\n    this.quill = quill;\r\n    this.currentSpec = null;\r\n    this.actions = [];\r\n    if (options.debug) {\r\n      (window as any).blotFormatter = this;\r\n    }\r\n\r\n    // Register the custom align formats with Quill\r\n    const ImageAlignClass = createImageAlignAttributor(this.Quill);\r\n    const IframeAlignClass = createIframeAlignAttributor(this.Quill);\r\n\r\n    // Create instances of the classes\r\n    this.ImageAlign = new ImageAlignClass(options.debug);\r\n    this.IframeAlign = new IframeAlignClass(options.debug);\r\n\r\n    // Register the align formats with Quill\r\n    if (options.debug) console.debug('Registering custom align formats', this.ImageAlign, this.IframeAlign);\r\n    this.Quill.register({\r\n      'formats/imageAlign': this.ImageAlign,\r\n      'attributors/class/imageAlign': this.ImageAlign,\r\n      'formats/iframeAlign': this.IframeAlign,\r\n      'attributors/class/iframeAlign': this.IframeAlign,\r\n    }, true);\r\n\r\n    // merge custom options with default\r\n    this.options = deepmerge(DefaultOptions, options, { arrayMerge: dontMerge });\r\n    if (options.debug) console.debug('BlotFormatter options', this.options);\r\n\r\n    // set enabled, add css to hide proxy images when editor is disabled\r\n    this._enabled = !(this.quill.options.readOnly || this.quill.container.classList.contains('ql-disabled'));\r\n    const style = document.createElement('style');\r\n    style.innerHTML = `.ql-disabled .blot-formatter__proxy-image {display: none;}`;\r\n    document.head.appendChild(style);\r\n\r\n    // create overlay & size info plus associated event listeners \r\n    [this.overlay, this.sizeInfo] = this._createOverlay();\r\n    this._addEventListeners();\r\n    // create overlay toolbar\r\n    this.toolbar = new Toolbar(this);\r\n    if (options.debug) console.debug('BlotFormatter toolbar', this.toolbar);\r\n    // define which specs to be formatted, initialise each\r\n    this.specs = this.options.specs.map(\r\n      (SpecClass: new (formatter: BlotFormatter) => BlotSpec) => new SpecClass(this)\r\n    );\r\n    this.specs.forEach(spec => spec.init());\r\n    if (options.debug) console.debug('BlotFormatter specs', this.specs);\r\n    // set position relative on quill container for absolute positioning of overlay & proxies \r\n    this.quill.container.style.position = this.quill.container.style.position || 'relative';\r\n    // register custom blots as per options\r\n    this._registerCustomBlots();\r\n    // register keyboard bindings as per options\r\n    this._keyboardBindings();\r\n    // add tooltip position fix if enabled\r\n    if (this.options.debug) console.debug('tooltip option', this.options.tooltip?.containTooltipPosition)\r\n    if (this.options.tooltip?.containTooltipPosition) {\r\n      this._tooltipContainPosition = new TooltipContainPosition(this.quill, this.options.debug);\r\n    }\r\n\r\n  }\r\n\r\n  /**\r\n   * Destroys the BlotFormatter instance, cleaning up event listeners, actions, toolbar,\r\n   * and DOM references. Also removes any global references and clears internal state.\r\n   * Logs a debug message if the `debug` option is enabled.\r\n   * Catches and logs any errors that occur during the destruction process.\r\n   */\r\n  destroy = (): void => {\r\n    try {\r\n      this.hide();\r\n      this._removeEventListeners();\r\n      this._destroyActions();\r\n      this.toolbar?.destroy();\r\n\r\n      // Clean up DOM references\r\n      if (this.overlay?.parentNode) {\r\n        this.overlay.parentNode.removeChild(this.overlay);\r\n      }\r\n\r\n      // Clear references\r\n      this.currentSpec = null;\r\n      this.specs = [];\r\n      this.actions = [];\r\n\r\n      if (this.options.tooltip?.containTooltipPosition && this._tooltipContainPosition) {\r\n        this._tooltipContainPosition?.destroy();\r\n      }\r\n\r\n      // Remove global reference\r\n      if ((window as any).bf === this) {\r\n        delete (window as any).bf;\r\n      }\r\n\r\n      this.quill = null;\r\n      if (this.options.debug) console.debug('BlotFormatter destroyed');\r\n    } catch (error) {\r\n      console.error('BlotFormatter.destroy error:', error);\r\n    }\r\n  };\r\n\r\n  /**\r\n   * Indicates whether the blot formatter is currently active.\r\n   *\r\n   * When `true`, formatting controls and interactions are available.\r\n   * When `false`, the formatter is disabled and will not react to user input. Proxy images are hidden by css.\r\n   *\r\n   * @returns True if the formatter is enabled; otherwise false.\r\n   */\r\n  get enabled(): boolean {\r\n    return this._enabled;\r\n  }\r\n\r\n  /**\r\n   * Enables or disables the blot formatter UI.\r\n   *\r\n   * When set to false, any currently visible formatter interface (such as\r\n   * overlays, resize handles, or toolbars) is immediately hidden via `hide()`.\r\n   * While disabled, user interactions that would normally trigger the formatter\r\n   * are ignored until re-enabled.\r\n   *\r\n   * @param value True to enable the formatter; false to disable it and hide all active UI.\r\n   */\r\n  set enabled(value: boolean) {\r\n    if (!value) this.hide();\r\n    this._enabled = value;\r\n  }\r\n\r\n  /**\r\n   * MutationObserver monitoring the Quill editor container's class attribute to track disabled state.\r\n   *\r\n   * When the container gains or loses the 'ql-disabled' CSS class, this observer updates the\r\n   * formatter's `enabled` property accordingly, ensuring the formatter UI is automatically\r\n   * disabled/enabled in sync with the editor.\r\n   *\r\n   * Behavior:\r\n   * - Listens only for attribute mutations on the 'class' attribute.\r\n   * - Sets `this.enabled` to false if 'ql-disabled' is present; true otherwise.\r\n   *\r\n   * Rationale:\r\n   * Centralizes Quill's disabled state propagation without requiring explicit event hooks or\r\n   * modifications to Quill's core. This avoids polling and keeps formatter state consistent.\r\n   *\r\n   * Lifecycle:\r\n   * - Should be started after the editor container is available.\r\n   * - Must be disconnected during teardown (e.g., in a destroy/dispose method) to prevent memory leaks.\r\n   *\r\n   * Caveats:\r\n   * - Assumes the presence and semantic meaning of the 'ql-disabled' class as used by Quill.\r\n   * - If external code mutates classes frequently, this may fire often; the handler is intentionally lightweight.\r\n   */\r\n  private _qlDisabledObserver = new MutationObserver(mutations => {\r\n    mutations.forEach(mutation => {\r\n      if (mutation.type === 'attributes' && mutation.attributeName === 'class') {\r\n        this.enabled = !this.quill.container.classList.contains('ql-disabled');\r\n      }\r\n    });\r\n  });\r\n\r\n  /**\r\n   * Displays the blot formatter overlay for the specified blot.\r\n   *\r\n   * This method performs the following actions:\r\n   * - Hides any open Quill tooltips (such as hyperlink dialogs).\r\n   * - Optionally exposes the formatter instance for debugging.\r\n   * - Clears any existing overlay if active on another blot.\r\n   * - Sets the current blot specification and selection.\r\n   * - Disables user selection to prevent unwanted interactions.\r\n   * - Appends the overlay to the Quill editor container.\r\n   * - Repositions the overlay to match the blot's position.\r\n   * - Creates action buttons or controls for the current blot.\r\n   * - Initializes the toolbar for the formatter.\r\n   * - Adds a document-level pointerdown event listener to handle outside clicks.\r\n   * - Logs debug information if enabled in options.\r\n   *\r\n   * @param spec - The specification of the blot (*BlotSpec*) to be formatted.\r\n   * @returns void\r\n   */\r\n  show = (spec: BlotSpec): void => {\r\n    try {\r\n      // exit early if editor is readOnly or disabled\r\n      if (!this.enabled) return;\r\n      // hide any open tooltips (closes open hyperlink dialog and more)\r\n      this.quill.container.querySelectorAll('.ql-tooltip:not(.ql-hidden)').forEach(\r\n        (tooltip: HTMLElement) => {\r\n          tooltip.classList.add('ql-hidden');\r\n        }\r\n      );\r\n      // expose formatter for debugging\r\n      if (this.options.debug) (window as any).blotFormatter = this;\r\n      // clear overlay in case show called while overlay active on other blot\r\n      this.hide();\r\n      this.currentSpec = spec;\r\n      this.currentSpec.setSelection();\r\n      this._setUserSelect('none');\r\n      this.quill.container.appendChild(this.overlay);\r\n      this._repositionOverlay();\r\n      this._createActions(spec);\r\n      this.toolbar.create();\r\n      this._scrollToolbarIntoView(this.toolbar.element);\r\n      document.addEventListener('pointerdown', this._onDocumentPointerDown);\r\n      if (this.options.debug) console.debug('BlotFormatter show', spec);\r\n    } catch (error) {\r\n      console.error('Error showing BlotFormatter:', error);\r\n      this.hide();\r\n      // Re-throw the error to ensure it can be handled by the caller if needed\r\n      throw error;\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Hides the blot formatter overlay and performs necessary cleanup.\r\n   *\r\n   * If a pointer event is provided, determines the click position relative to the target blot\r\n   * and places the caret before or after the blot accordingly. Calls the `onHide` method of the\r\n   * current spec, removes the overlay from the DOM, removes event listeners, resets user selection,\r\n   * destroys toolbar and actions, and emits a `TEXT_CHANGE` event to ensure the editor state is updated.\r\n   *\r\n   * @param event - Optional pointer event that triggered the hide action. Used to determine caret placement.\r\n   */\r\n  hide = (event: PointerEvent | null = null): void => {\r\n    if (this.currentSpec) {\r\n      if (event) {\r\n        const targetBlot = this.currentSpec!.getTargetBlot();\r\n        if (targetBlot) {\r\n          const position = this._getClickPosition(event);\r\n          if (position === PointerPosition.LEFT) {\r\n            if (this.options.debug) console.debug('Click position: LEFT');\r\n            CaretAction.placeCaretBeforeBlot(this.quill, targetBlot);\r\n          } else if (position === PointerPosition.RIGHT) {\r\n            if (this.options.debug) console.debug('Click position: RIGHT');\r\n            CaretAction.placeCaretAfterBlot(this.quill, targetBlot);\r\n          }\r\n        }\r\n      }\r\n      this.currentSpec.onHide();\r\n      this.currentSpec = null;\r\n      this.quill.container.removeChild(this.overlay);\r\n      document.removeEventListener('pointerdown', this._onDocumentPointerDown);\r\n      this.overlay.style.setProperty('display', 'none');\r\n      this._setUserSelect('');\r\n      this._destroyActions();\r\n      this.toolbar.destroy();\r\n      // TEXT_CHANGE event clears resize cursor from image when form is saved while overlay still active\r\n      this.quill.emitter.emit(\r\n        this.quill.constructor.events.TEXT_CHANGE, 0, this.quill.getLength(), 'api'\r\n      );\r\n    }\r\n    if (this.options.debug) console.debug('BlotFormatter hide');\r\n  }\r\n\r\n  /**\r\n   * Updates the state of the BlotFormatter overlay and its associated actions.\r\n   *\r\n   * This method repositions the overlay to match the current selection or formatting context,\r\n   * triggers the `onUpdate` method for each registered action, and logs a debug message if\r\n   * debugging is enabled in the options.\r\n   *\r\n   * @returns {void}\r\n   */\r\n  update = (): void => {\r\n    this._repositionOverlay();\r\n    this.actions.forEach(action => action.onUpdate());\r\n    if (this.options.debug) console.debug('BlotFormatter update');\r\n  }\r\n\r\n  /**\r\n   * Initializes the actions for the given blot specification.\r\n   * \r\n   * This method retrieves the list of actions from the provided `spec` using `getActions()`,\r\n   * calls the `onCreate()` lifecycle method on each action, and assigns the resulting array\r\n   * to the `actions` property. If debugging is enabled in the options, it logs each created action.\r\n   *\r\n   * @param spec - The blot specification containing the actions to initialize.\r\n   */\r\n  private _createActions = (spec: BlotSpec): void => {\r\n    this.actions = spec.getActions().map((action: Action) => {\r\n      action.onCreate();\r\n      if (this.options.debug) console.debug('BlotFormatter action created', action);\r\n      return action;\r\n    });\r\n  }\r\n\r\n  /**\r\n   * Destroys all registered actions by calling their `onDestroy` method and clearing the actions array.\r\n   * If debugging is enabled in the options, logs a debug message to the console.\r\n   *\r\n   * @private\r\n   */\r\n  private _destroyActions = (): void => {\r\n    this.actions.forEach((action: Action) => action.onDestroy());\r\n    this.actions = [];\r\n    if (this.options.debug) console.debug('BlotFormatter actions destroyed');\r\n  }\r\n\r\n  /**\r\n   * Creates and configures the overlay and size info HTML elements used for formatting blots.\r\n   *\r\n   * The overlay element is styled and configured to be non-selectable, and the size info element\r\n   * is appended to the overlay. Both elements can be customized via the `options.overlay` property.\r\n   * \r\n   * @returns A tuple containing the overlay HTMLElement and the size info HTMLElement.\r\n   */\r\n  private _createOverlay = (): [HTMLElement, HTMLElement] => {\r\n    const overlay = document.createElement('div');\r\n    // set up overlay element\r\n    overlay.classList.add(this.options.overlay.className);\r\n    if (this.options.overlay.style) {\r\n      Object.assign(overlay.style, this.options.overlay.style);\r\n    }\r\n    // prevent overlay being selectable\r\n    overlay.style.userSelect = 'none';\r\n    overlay.style.setProperty('-webkit-user-select', 'none');\r\n    overlay.style.setProperty('-moz-user-select', 'none');\r\n    overlay.style.setProperty('-ms-user-select', 'none');\r\n\r\n    const sizeInfo = document.createElement('div');\r\n    if (this.options.overlay.sizeInfoStyle) {\r\n      Object.assign(sizeInfo.style, this.options.overlay.sizeInfoStyle);\r\n    }\r\n    overlay.appendChild(sizeInfo);\r\n    if (this.options.debug) console.debug('BlotFormatter overlay created', overlay, sizeInfo);\r\n    return [overlay, sizeInfo]\r\n  }\r\n\r\n  /**\r\n   * Ensures that the toolbar element is visible within the viewport of the Quill editor.\r\n   * If the toolbar is positioned above the visible area of the editor, it scrolls the target element into view\r\n   * with an offset equal to the toolbar's height, then recalculates the toolbar's position.\r\n   * If the toolbar is still above the viewport, it scrolls the window to bring the toolbar into view smoothly.\r\n   *\r\n   * @param toolbarElement - The HTML element representing the toolbar to be scrolled into view.\r\n   * @returns A promise that resolves when any necessary scrolling has completed.\r\n   */\r\n  private _scrollToolbarIntoView = async (toolbarElement: HTMLElement): Promise<void> => {\r\n    let toolbarRect = toolbarElement.getBoundingClientRect();\r\n    const quillRect = this.quill.container.getBoundingClientRect();\r\n\r\n    const targetElement = this.currentSpec?.getTargetElement();\r\n    if (toolbarRect.top - quillRect.top < 0) {\r\n      if (targetElement) {\r\n        // Wait for the smooth scroll to complete\r\n        await this._scrollIntoViewWithOffset(targetElement, toolbarRect.height);\r\n        // Now recalculate the toolbar position\r\n        toolbarRect = toolbarElement.getBoundingClientRect();\r\n      }\r\n    }\r\n    // If toolbar is still above the viewport, scroll the window\r\n    if (toolbarRect.top < 0) {\r\n      if (this.options.debug) {\r\n        console.debug(`Scrolling window ${toolbarRect.top - toolbarRect.height}px to bring toolbar into view`);\r\n      }\r\n      window.scrollBy({ top: toolbarRect.top - toolbarRect.height, behavior: 'smooth' });\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Scrolls the first scrollable ancestor of the given element into view with a specified offset.\r\n   * If the element is outside the visible bounds of its scrollable ancestor, the ancestor is scrolled\r\n   * so that the element is visible with the given offset from the top. Returns a promise that resolves\r\n   * when scrolling has completed (or immediately if no scrolling was necessary).\r\n   *\r\n   * @param el - The target HTMLElement to scroll into view.\r\n   * @param offset - The number of pixels to offset from the top of the scrollable ancestor (default: 10).\r\n   * @returns A promise that resolves when scrolling is finished.\r\n   */\r\n  private _scrollIntoViewWithOffset = (el: HTMLElement, offset = 10): Promise<void> => {\r\n    return new Promise((resolve) => {\r\n      let scrollingElement: Element | null = null;\r\n\r\n      for (let ancestor = el.parentElement; ancestor; ancestor = ancestor.parentElement) {\r\n        const { overflowY } = getComputedStyle(ancestor);\r\n        if (!['auto', 'scroll'].includes(overflowY) || ancestor.scrollHeight <= ancestor.clientHeight) continue;\r\n\r\n        const containerRect = ancestor.getBoundingClientRect();\r\n        const elRect = el.getBoundingClientRect();\r\n\r\n        if (elRect.top < containerRect.top + offset) {\r\n          scrollingElement = ancestor;\r\n          ancestor.scrollTo({\r\n            top: ancestor.scrollTop + elRect.top - containerRect.top - offset\r\n          });\r\n          if (this.options.debug) {\r\n            console.debug(`Scrolling ancestor ${ancestor.tagName} to bring element into view with offset ${offset}px`);\r\n          }\r\n          break; // Only scroll the first scrollable ancestor\r\n        }\r\n      }\r\n\r\n      if (scrollingElement) {\r\n        // Wait for smooth scroll to complete\r\n        const checkScrollEnd = () => {\r\n          let lastScrollTop = scrollingElement!.scrollTop;\r\n          const checkInterval = setInterval(() => {\r\n            if (scrollingElement!.scrollTop === lastScrollTop) {\r\n              // Scroll has stopped\r\n              clearInterval(checkInterval);\r\n              resolve();\r\n            } else {\r\n              lastScrollTop = scrollingElement!.scrollTop;\r\n            }\r\n          }, 50); // Check every 50ms\r\n        };\r\n\r\n        // Start checking after a brief delay\r\n        setTimeout(checkScrollEnd, 100);\r\n      } else {\r\n        // No scrolling occurred\r\n        resolve();\r\n      }\r\n    });\r\n  };\r\n\r\n  /**\r\n   * Adds all necessary event listeners to the overlay and Quill root elements.\r\n   *\r\n   * - For the overlay:\r\n   *   - Forwards mouse wheel and touch move events to allow scrolling.\r\n   *   - Disables the context menu to prevent default browser actions.\r\n   * - For the Quill root:\r\n   *   - Repositions the overlay on scroll and resize events.\r\n   *   - Dismisses the overlay when clicking on the Quill root.\r\n   * - For the quill container\r\n   *   - Observes class attribute changes to detect disabled state.\r\n   *\r\n   * This method ensures proper interaction and synchronization between the overlay\r\n   * and the Quill editor, handling user input and UI updates.\r\n   *\r\n   * @private\r\n   */\r\n  private _addEventListeners = (): void => {\r\n    this._abortController = new AbortController();\r\n    const { signal } = this._abortController;\r\n\r\n    // overlay event listeners\r\n    // scroll the quill root on mouse wheel & touch move event - do not use default 'passive' on these\r\n    this.overlay.addEventListener('wheel', this._passWheelEventThrough, { passive: false, signal });\r\n    this.overlay.addEventListener('touchstart', this._onTouchScrollStart, { passive: false, signal });\r\n    this.overlay.addEventListener('touchmove', this._onTouchScrollMove, { passive: false, signal });\r\n    // disable context menu on overlay\r\n    this.overlay.addEventListener('contextmenu', this._preventContextMenu, { signal });\r\n    // dismiss overlay if active and click on quill root\r\n    this.quill.root.addEventListener('click', this._onClick, { signal });\r\n\r\n    // quill root event listeners\r\n    // scroll visible overlay if editor is scrollable\r\n    this.quill.root.addEventListener('scroll', this._repositionOverlay, { signal });\r\n\r\n    this._resizeObserver = new ResizeObserver(this._repositionOverlay);\r\n    this._resizeObserver.observe(this.quill.root);\r\n\r\n    // observe quill container for disabled state changes\r\n    this._qlDisabledObserver.observe(this.quill.container, {\r\n      attributes: true,\r\n      attributeFilter: ['class']\r\n    });\r\n  }\r\n\r\n  /**\r\n   * Removes event listeners and observers associated with the instance.\r\n   * \r\n   * Aborts any ongoing operations managed by the internal AbortController,\r\n   * and disconnects the internal ResizeObserver to stop observing changes.\r\n   *\r\n   * @private\r\n   */\r\n  private _removeEventListeners = (): void => {\r\n    this._abortController?.abort();\r\n    this._resizeObserver?.disconnect();\r\n    this._qlDisabledObserver.disconnect();\r\n  };\r\n\r\n  /**\r\n   * Prevents the default context menu from appearing and stops the event from propagating further.\r\n   *\r\n   * @param event - The event object associated with the context menu action.\r\n   */\r\n  private _preventContextMenu = (event: Event): void => {\r\n    event.stopPropagation();\r\n    event.preventDefault();\r\n  };\r\n\r\n  /**\r\n   * Repositions the overlay element to align with the currently selected blot's overlay target.\r\n   *\r\n   * Calculates the position and size of the overlay based on the bounding rectangles of the\r\n   * Quill container and the overlay target element. Updates the overlay's style to match\r\n   * the target's position and dimensions, ensuring it is correctly displayed over the selected blot.\r\n   * Optionally logs debug information if the `debug` option is enabled.\r\n   *\r\n   * @private\r\n   */\r\n  private _repositionOverlay = (): void => {\r\n    if (this.currentSpec) {\r\n      const overlayTarget = this.currentSpec.getOverlayElement();\r\n      if (overlayTarget) {\r\n        const containerRect: DOMRect = this.quill.container.getBoundingClientRect();\r\n        const specRect: DOMRect = overlayTarget.getBoundingClientRect();\r\n        const overlayRect: CssRectPx = {\r\n          left: `${specRect.left - containerRect.left - 1 + this.quill.container.scrollLeft}px`,\r\n          top: `${specRect.top - containerRect.top + this.quill.container.scrollTop}px`,\r\n          width: `${specRect.width}px`,\r\n          height: `${specRect.height}px`,\r\n        };\r\n        Object.assign(this.overlay.style, {\r\n          display: 'block',\r\n          ...overlayRect\r\n        });\r\n        if (this.options.debug)\r\n          console.debug('Blotformatter _repositionOverlay', 'specRect:', specRect, 'overlayRect:', overlayRect);\r\n      }\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Sets the CSS `user-select` property (and its vendor-prefixed variants) to the specified value\r\n   * on both the Quill editor root element and the document's root element.\r\n   *\r\n   * This method is typically used to enable or disable text selection within the editor and the page,\r\n   * which can be useful during formatting operations to prevent unwanted user interactions.\r\n   *\r\n   * @param value - The value to set for the `user-select` property (e.g., `'none'`, `'auto'`).\r\n   */\r\n  private _setUserSelect = (value: string): void => {\r\n    const props: string[] = [\r\n      'userSelect',\r\n      'mozUserSelect',\r\n      'webkitUserSelect',\r\n      'msUserSelect',\r\n    ];\r\n\r\n    props.forEach((prop: string) => {\r\n      // set on contenteditable document element and quill root\r\n      this.quill.root.style.setProperty(prop, value);\r\n      if (document.documentElement) {\r\n        document.documentElement.style.setProperty(prop, value);\r\n      }\r\n    });\r\n    if (this.options.debug) console.debug('BlotFormatter _setUserSelect', value);\r\n  }\r\n\r\n  /**\r\n   * Handles the `pointerdown` event on the document to determine whether the blot formatter overlay should be dismissed.\r\n   *\r\n   * If the pointer event target is outside the Quill editor, not within a blot formatter modal,\r\n   * and not a proxy image used by the blot formatter, the overlay is hidden.\r\n   *\r\n   * @param event - The pointer event triggered by user interaction.\r\n   */\r\n  private _onDocumentPointerDown = (event: PointerEvent): void => {\r\n    // if clicked outside of quill editor and not a blot formatter modal or iframe proxy image, dismiss overlay \r\n    const target = event.target as HTMLElement;\r\n    if (!(\r\n      this.quill.root.parentNode.contains(target) ||\r\n      target.closest('[data-blot-formatter-modal]') ||\r\n      target.classList.contains('blot-formatter__proxy-image')\r\n    )) {\r\n      this.hide(event);\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Handles pointer click events on the editor.\r\n   * \r\n   * If debugging is enabled in the options, logs the click event to the console.\r\n   * Then, hides the formatter UI in response to the click event.\r\n   *\r\n   * @param event - The pointer event triggered by the user's click.\r\n   */\r\n  private _onClick = (event: PointerEvent): void => {\r\n    if (this.options.debug) console.debug('BlotFormatter _onClick', event);\r\n    if (this.enabled) this.hide(event);\r\n  }\r\n\r\n  /**\r\n   * Handles the wheel event by scrolling the Quill editor's root element.\r\n   * This method is intended to be used when the overlay or proxy receives a wheel event,\r\n   * ensuring that the scroll action is passed through to the underlying Quill editor.\r\n   *\r\n   * @param event - The wheel event containing scroll delta values.\r\n   *\r\n   * @remarks\r\n   * If the `debug` option is enabled, this method logs the scroll delta values to the console.\r\n   */\r\n  _passWheelEventThrough = (event: WheelEvent): void => {\r\n    // scroll the quill root element when overlay or proxy wheel scrolled\r\n    this.quill.root.scrollLeft += event.deltaX;\r\n    this.quill.root.scrollTop += event.deltaY;\r\n    if (this.options.debug)\r\n      console.debug(`BlotFormatter scrolling Quill root x: ${event.deltaX}, y: ${event.deltaY}`);\r\n  };\r\n\r\n  /**\r\n   * Handles the touch start event for scrolling interactions.\r\n   * Records the initial X and Y positions of the first touch point.\r\n   * Optionally logs debug information if enabled in options.\r\n   *\r\n   * @param event - The touch event triggered when the user starts touching the screen.\r\n   */\r\n  _onTouchScrollStart = (event: TouchEvent): void => {\r\n    // Record the initial touch positions\r\n    if (event.touches.length === 1) {\r\n      const touch = event.touches[0];\r\n      this._startX = touch.clientX;\r\n      this._startY = touch.clientY;\r\n      if (this.options.debug)\r\n        console.debug('BlotFormatter _onTouchScrollStart', `X: ${this._startX}, Y: ${this._startY}`);\r\n    }\r\n  };\r\n\r\n  /**\r\n   * Handles touch move events to enable custom scrolling behavior within the Quill editor root element.\r\n   * \r\n   * This method allows for both vertical and horizontal scrolling using touch gestures,\r\n   * and prevents default browser scrolling when appropriate to provide a smoother, controlled experience.\r\n   * It updates the scroll position of the editor root based on the movement of the touch point,\r\n   * and ensures scrolling does not exceed the bounds of the content.\r\n   * \r\n   * @param event - The touch event triggered by the user's finger movement.\r\n   * \r\n   * @remarks\r\n   * - Only processes single-touch events.\r\n   * - Prevents default scrolling if the editor can be scrolled further in the direction of the gesture.\r\n   * - Updates the starting touch coordinates after each move to track incremental movement.\r\n   * - Logs debug information if the `debug` option is enabled.\r\n   */\r\n  _onTouchScrollMove = (event: TouchEvent): void => {\r\n    if (event.touches.length === 1) {\r\n      const touch = event.touches[0];\r\n      const deltaX = this._startX - touch.clientX;\r\n      const deltaY = this._startY - touch.clientY;\r\n\r\n      // Early return if minimal movement\r\n      if (Math.abs(deltaX) < 2 && Math.abs(deltaY) < 2) return;\r\n\r\n      const root = this.quill.root;\r\n\r\n      // Check if we can scroll further vertically and horizontally\r\n      const atTop = root.scrollTop === 0;\r\n      const atBottom = root.scrollTop + root.clientHeight === root.scrollHeight;\r\n      const atLeft = root.scrollLeft === 0;\r\n      const atRight = root.scrollLeft + root.clientWidth === root.scrollWidth;\r\n\r\n      // Determine if we're scrolling vertically or horizontally\r\n      const isScrollingVertically = Math.abs(deltaY) > Math.abs(deltaX);\r\n      const isScrollingHorizontally = Math.abs(deltaX) > Math.abs(deltaY);\r\n\r\n      let preventDefault = false;\r\n\r\n      // If scrolling vertically\r\n      if (isScrollingVertically) {\r\n        if (!(atTop && deltaY < 0) && !(atBottom && deltaY > 0)) {\r\n          preventDefault = true; // Prevent default only if we can scroll further\r\n          root.scrollTop += deltaY;\r\n        }\r\n      }\r\n\r\n      // If scrolling horizontally\r\n      if (isScrollingHorizontally) {\r\n        if (!(atLeft && deltaX < 0) && !(atRight && deltaX > 0)) {\r\n          preventDefault = true; // Prevent default only if we can scroll further\r\n          root.scrollLeft += deltaX;\r\n        }\r\n      }\r\n\r\n      if (preventDefault) {\r\n        event.preventDefault(); // Prevent default scrolling if necessary\r\n      }\r\n\r\n      // Update start positions for the next move event\r\n      this._startX = touch.clientX;\r\n      this._startY = touch.clientY;\r\n      if (this.options.debug)\r\n        console.debug('BlotFormatter touch scroll end', `X: ${this._startX}, Y: ${this._startY}`);\r\n    }\r\n  };\r\n\r\n  /**\r\n   * Registers custom Quill blots based on the provided options.\r\n   *\r\n   * - If `options.image.registerImageTitleBlot` is enabled, registers a custom Image blot\r\n   *   that supports a title attribute.\r\n   * - If `options.video.registerCustomVideoBlot` is enabled, registers a custom Video blot\r\n   *   with responsive behavior and sets its default aspect ratio from the options.\r\n   *\r\n   * Debug information is logged to the console if `options.debug` is true.\r\n   *\r\n   * @private\r\n   */\r\n  private _registerCustomBlots = (): void => {\r\n    // register image bot with title attribute support\r\n    if (this.options.image.registerImageTitleBlot) {\r\n      const ImageAltTitleBlot = createAltTitleImageBlotClass(this.Quill);\r\n      if (this.options.debug) console.debug('Registering custom Image blot', ImageAltTitleBlot);\r\n      this.Quill.register({ 'formats/image': ImageAltTitleBlot }, true);\r\n      if (this.options.debug) {\r\n        console.debug('formats/image after register:', this.Quill.import('formats/image'));\r\n      }\r\n    }\r\n    // register custom video blot with initial width 100% & aspect ratio from options\r\n    if (this.options.video.registerCustomVideoBlot) {\r\n      const VideoResponsive = createResponsiveVideoBlotClass(this.Quill);\r\n      if (this.options.debug) {\r\n        console.debug('Registering custom Video blot', VideoResponsive);\r\n        console.debug('Setting default aspect ratio for Video blot', this.options.video.defaultAspectRatio);\r\n      }\r\n      // set default aspect ratio for video responsive blot\r\n      VideoResponsive.aspectRatio = this.options.video.defaultAspectRatio;\r\n      this.Quill.register({ 'formats/video': VideoResponsive }, true);\r\n      if (this.options.debug) {\r\n        console.debug('formats/video after register:', this.Quill.import('formats/video'));\r\n      }\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Registers custom keyboard bindings to address specific Quill editor issues and enhance user experience.\r\n   *\r\n   * - Adds a Backspace key binding to fix Quill bug #4364, ensuring proper deletion behavior for embedded videos (e.g., iframes).\r\n   *   This is enabled if `options.video.registerBackspaceFix` is true.\r\n   * - Adds an ArrowRight key binding to fix cursor navigation issues when moving past images,\r\n   *   ensuring the cursor does not get stuck or hidden at the image location.\r\n   *   This is enabled if `options.image.registerArrowRightFix` is true.\r\n   *\r\n   * Both bindings are conditionally registered based on the provided options.\r\n   * Debug information is logged to the console if `options.debug` is enabled.\r\n   *\r\n   * @private\r\n   */\r\n  private _keyboardBindings = (): void => {\r\n    // add backspace keyboard bindings\r\n    // patch that fixes Quill bug #4364 (https://github.com/slab/quill/issues/4364)\r\n    if (this.options.video.registerBackspaceFix) {\r\n      if (!this.quill.keyboard.bindings.Backspace) {\r\n        this.quill.keyboard.bindings.Backspace = []\r\n      }\r\n      const backspaceRule = {\r\n        key: 'Backspace',\r\n        empty: true,\r\n        line: {\r\n          domNode: {\r\n            tagName: \"IFRAME\"\r\n          }\r\n        },\r\n        handler: (range: any) => {\r\n          // Get the blot before the cursor - only fire for iframes\r\n          const [blot] = this.quill.getLeaf(range.index - 1)\r\n          if (blot?.domNode?.tagName !== 'IFRAME')\r\n            return true\r\n          this.quill.deleteText(range.index - 1, 1, 'user')\r\n          if (this.options.debug)\r\n            console.debug(\r\n              'BlotFormatter Backspace binding triggered, deleting iframe at index',\r\n              range.index - 1\r\n            )\r\n        }\r\n      }\r\n      this.quill.keyboard.bindings.Backspace.unshift(backspaceRule)\r\n      if (this.options.debug)\r\n        console.debug(\r\n          'BlotFormatter added Backspace keyboard binding',\r\n          backspaceRule\r\n        )\r\n    }\r\n\r\n    // handles moving the cursor past the image when format set\r\n    // without this, cursor stops and stays hidden at the image location\r\n    if (this.options.image.registerArrowRightFix) {\r\n      if (!this.quill.keyboard.bindings.ArrowRight) {\r\n        this.quill.keyboard.bindings.ArrowRight = []\r\n      }\r\n      const arrowRightFixRule = {\r\n        key: 'ArrowRight',\r\n        collapsed: true,\r\n        empty: false,\r\n        suffix: /^$/,\r\n        handler: (range: any) => {\r\n          const index = range.index + range.length;\r\n          // detect formatted image wrapper with contenteditable=false span - only fire for these\r\n          const [nextLeaf] = this.quill.getLeaf(index + 1);\r\n          if (!nextLeaf?.domNode) {\r\n            return true;\r\n          }\r\n          const node = nextLeaf.domNode;\r\n          if (node?.tagName !== 'IMG' || !node.parentElement?.matches('span[contenteditable=\"false\"]')) {\r\n            return true;\r\n          }\r\n          const documentLength = this.quill.getLength();\r\n          if (index + 1 >= documentLength - 1) {\r\n            // For the last blot, place cursor at the very end\r\n            this.quill.setSelection(documentLength - 1, 0, \"user\");\r\n          } else {\r\n            // overshoot by one then use native browser API to send caret back one\r\n            // without this, caret will be placed inside formatting span wrapper\r\n            this.quill.setSelection(index + 2, 0, \"user\");\r\n            CaretAction.sendCaretBack(1); // Move cursor back by 1 character\r\n          }\r\n          if (this.options.debug)\r\n            console.debug(\r\n              'BlotFormatter ArrowRight binding triggered, moving cursor past image at index',\r\n              index\r\n            );\r\n        }\r\n      };\r\n      this.quill.keyboard.bindings.ArrowRight.unshift(arrowRightFixRule);\r\n      if (this.options.debug)\r\n        console.debug('BlotFormatter added ArrowRightFix keyboard binding', arrowRightFixRule);\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Determines whether the resizing of the target element should use relative sizing (percentages)\r\n   * or absolute sizing (pixels), based on the current configuration and the element's width attribute.\r\n   *\r\n   * @param targetElement - The HTML element whose sizing mode is being determined.\r\n   * @returns `true` if relative sizing should be used, `false` otherwise.\r\n   *\r\n   * The method checks the `useRelativeSize` option and, if `allowResizeModeChange` is enabled,\r\n   * inspects the element's `width` attribute to decide whether to use relative or absolute sizing.\r\n   * If debugging is enabled, logs the decision to the console.\r\n   */\r\n  _useRelative = (targetElement: HTMLElement): boolean => {\r\n    let _useRelative: boolean = this.options.resize.useRelativeSize;\r\n    if (this.options.resize.allowResizeModeChange) {\r\n      // if no width set, use useRelativeSize by default else respect existing type\r\n      const width: string | null = targetElement.getAttribute('width');\r\n      if (!width) {\r\n        _useRelative = this.options.resize.useRelativeSize;\r\n      } else {\r\n        _useRelative = width.endsWith('%');\r\n      }\r\n    }\r\n    if (this.options.debug) {\r\n      console.debug('BlotFormatter _useRelative', _useRelative, 'for element', targetElement);\r\n    }\r\n    return _useRelative;\r\n  }\r\n\r\n  /**\r\n   * Determines the relative position of a pointer event with respect to the overlay element.\r\n   *\r\n   * @param event - The pointer event to evaluate.\r\n   * @returns The position of the pointer relative to the overlay, as a `PointerPosition` enum value.\r\n   *\r\n   * The possible return values are:\r\n   * - `PointerPosition.ABOVE` if the pointer is above the overlay.\r\n   * - `PointerPosition.BELOW` if the pointer is below the overlay.\r\n   * - `PointerPosition.LEFT` if the pointer is to the left of the overlay.\r\n   * - `PointerPosition.RIGHT` if the pointer is to the right of the overlay.\r\n   * - `PointerPosition.INSIDE` if the pointer is inside the overlay.\r\n   *\r\n   * If the `debug` option is enabled, logs the determined position and event to the console.\r\n   */\r\n  private _getClickPosition = (event: PointerEvent): PointerPosition => {\r\n    const target = this.overlay;\r\n    const rect = target.getBoundingClientRect();\r\n    let position: PointerPosition;\r\n    if (event.clientY < rect.top) {\r\n      position = PointerPosition.ABOVE;\r\n    } else if (event.clientY > rect.bottom) {\r\n      position = PointerPosition.BELOW;\r\n    } else if (event.clientX < rect.left) {\r\n      position = PointerPosition.LEFT;\r\n    } else if (event.clientX > rect.right) {\r\n      position = PointerPosition.RIGHT;\r\n    } else {\r\n      position = PointerPosition.INSIDE;\r\n    }\r\n    if (this.options.debug) {\r\n      console.debug('BlotFormatter _getClickPosition', position, 'for event', event);\r\n    }\r\n    return position;\r\n  }\r\n}\r\n"],"names":["Action","formatter","CaretAction","n","debug","selection","range","currentNode","currentOffset","prevNode","quill","targetBlot","index","documentLength","e","modalOpen","isMergeableObject","value","isNonNullObject","isSpecial","stringValue","isReactElement","canUseSymbol","REACT_ELEMENT_TYPE","emptyTarget","val","cloneUnlessOtherwiseSpecified","options","deepmerge","defaultArrayMerge","target","source","element","getMergeFunction","key","customMerge","getEnumerableOwnPropertySymbols","symbol","getKeys","propertyIsOnObject","object","property","propertyIsUnsafe","mergeObject","destination","sourceIsArray","targetIsArray","sourceAndTargetTypesMatch","array","prev","next","deepmerge_1","cjs","Toolbar","event","actionButtons","action","button","TooltipContainPosition","tooltip","container","tooltipRect","containerRect","left","top","width","height","maxWidth","maxHeight","changed","newStyles","isRepositioning","observer","mutations","m","tooltipElement","createAltTitleImageBlotClass","QuillConstructor","ImageBlot","ATTRIBUTES","domNode","formats","attribute","name","createIframeAlignAttributor","parchment","ClassAttributor","Scope","node","className","createImageAlignAttributor","imageElement","imageBlot","parentElement","align","title","createResponsiveVideoBlotClass","VideoEmbed","DefaultAligner","alignment","blot","k","existingAlignment","hasAlignment","editorStyle","editorWidth","ops","ToolbarButton","onClickHandler","style","childElement","AlignAction","DeleteAction","targetElement","ResizeAction","passiveFalse","handleStyle","handle","position","cursor","box","handleXOffset","handleYOffset","topLeftStyle","topRightStyle","bottomRightStyle","bottomLeftStyle","error","activate","rect","ratio","deltaX","isLeftHandle","newWidth","constrainedWidth","scale","newHeight","show","roundedWidth","roundedHeight","size","setWidth","setWidthNum","aspectRatio","calculatedHeight","naturalWidth","naturalHeight","showInfo","touch1","touch2","dx","dy","dim","_","prefix","num","suffix","BlotSpec","actions","PROXY_IMAGE_CLASS","UnclickableBlotSpec","resizeTimeout","entries","entry","unclickable","proxyImage","id","canvas","context","mergedStyle","containerScrollLeft","containerScrollTop","unclickableRect","msg","proxyContainer","IframeVideoSpec","AttributeAction","alt","imageAlignment","uuid","modal","modalContainer","form","labelAlt","textareaAlt","labelTitle","textareaTitle","buttonDiv","submitButton","cancelButton","CompressAction","isEligible","mimeType","isEligibleForCompression","img","parsedWidth","parsedHeight","dataUrl","base64Data","details","newImg","resizedDataUrl","originalSize","resizedSize","sizeDiff","modalBackground","modalDefaultPrompt","modalMoreInfo","modalButtonContainer","moreInfoButton","continueButton","LinkAction","dialog","label","input","okButton","removeButton","background","overlayRect","quillRect","offsetParentRect","dialogWidth","dialogHeight","minLeft","maxLeft","minTop","maxTop","url","wrapperBlot","ImageSpec","el","closeButtonIcon","okButtonIcon","infoButtonIcon","DefaultOptions","dontMerge","BlotFormatter","ImageAlignClass","IframeAlignClass","SpecClass","spec","mutation","overlay","sizeInfo","toolbarElement","toolbarRect","offset","resolve","scrollingElement","ancestor","overflowY","elRect","lastScrollTop","checkInterval","signal","overlayTarget","specRect","prop","touch","deltaY","root","atTop","atBottom","atLeft","atRight","isScrollingVertically","isScrollingHorizontally","preventDefault","ImageAltTitleBlot","VideoResponsive","backspaceRule","arrowRightFixRule","nextLeaf","_useRelative"],"mappings":"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","x_google_ignoreList":[2]}