"use strict";
Object.defineProperty(exports, "__esModule", {
    value: true
});
function _export(target, all) {
    for(var name in all)Object.defineProperty(target, name, {
        enumerable: true,
        get: Object.getOwnPropertyDescriptor(all, name).get
    });
}
_export(exports, {
    get CopyPaste () {
        return CopyPaste;
    },
    get PLUGIN_KEY () {
        return PLUGIN_KEY;
    },
    get PLUGIN_PRIORITY () {
        return PLUGIN_PRIORITY;
    }
});
const _base = require("../base");
const _hooks = require("../../core/hooks");
const _SheetClip = require("../../3rdparty/SheetClip");
const _array = require("../../helpers/array");
const _string = require("../../helpers/string");
const _object = require("../../helpers/object");
const _console = require("../../helpers/console");
const _element = require("../../helpers/dom/element");
const _browser = require("../../helpers/browser");
const _copy = /*#__PURE__*/ _interop_require_default(require("./contextMenuItem/copy"));
const _copyColumnHeadersOnly = /*#__PURE__*/ _interop_require_default(require("./contextMenuItem/copyColumnHeadersOnly"));
const _copyWithColumnGroupHeaders = /*#__PURE__*/ _interop_require_default(require("./contextMenuItem/copyWithColumnGroupHeaders"));
const _copyWithColumnHeaders = /*#__PURE__*/ _interop_require_default(require("./contextMenuItem/copyWithColumnHeaders"));
const _cut = /*#__PURE__*/ _interop_require_default(require("./contextMenuItem/cut"));
const _pasteEvent = /*#__PURE__*/ _interop_require_default(require("./pasteEvent"));
const _copyableRanges = require("./copyableRanges");
const _parseTable = require("../../utils/parseTable");
function _check_private_redeclaration(obj, privateCollection) {
    if (privateCollection.has(obj)) {
        throw new TypeError("Cannot initialize the same private elements twice on an object");
    }
}
function _class_apply_descriptor_get(receiver, descriptor) {
    if (descriptor.get) {
        return descriptor.get.call(receiver);
    }
    return descriptor.value;
}
function _class_apply_descriptor_set(receiver, descriptor, value) {
    if (descriptor.set) {
        descriptor.set.call(receiver, value);
    } else {
        if (!descriptor.writable) {
            throw new TypeError("attempted to set read only private field");
        }
        descriptor.value = value;
    }
}
function _class_extract_field_descriptor(receiver, privateMap, action) {
    if (!privateMap.has(receiver)) {
        throw new TypeError("attempted to " + action + " private field on non-instance");
    }
    return privateMap.get(receiver);
}
function _class_private_field_get(receiver, privateMap) {
    var descriptor = _class_extract_field_descriptor(receiver, privateMap, "get");
    return _class_apply_descriptor_get(receiver, descriptor);
}
function _class_private_field_init(obj, privateMap, value) {
    _check_private_redeclaration(obj, privateMap);
    privateMap.set(obj, value);
}
function _class_private_field_set(receiver, privateMap, value) {
    var descriptor = _class_extract_field_descriptor(receiver, privateMap, "set");
    _class_apply_descriptor_set(receiver, descriptor, value);
    return value;
}
function _class_private_method_get(receiver, privateSet, fn) {
    if (!privateSet.has(receiver)) {
        throw new TypeError("attempted to get private field on non-instance");
    }
    return fn;
}
function _class_private_method_init(obj, privateSet) {
    _check_private_redeclaration(obj, privateSet);
    privateSet.add(obj);
}
function _interop_require_default(obj) {
    return obj && obj.__esModule ? obj : {
        default: obj
    };
}
_hooks.Hooks.getSingleton().register('afterCopyLimit');
_hooks.Hooks.getSingleton().register('modifyCopyableRange');
_hooks.Hooks.getSingleton().register('beforeCut');
_hooks.Hooks.getSingleton().register('afterCut');
_hooks.Hooks.getSingleton().register('beforePaste');
_hooks.Hooks.getSingleton().register('afterPaste');
_hooks.Hooks.getSingleton().register('beforeCopy');
_hooks.Hooks.getSingleton().register('afterCopy');
const PLUGIN_KEY = 'copyPaste';
const PLUGIN_PRIORITY = 80;
const SETTING_KEYS = [
    'fragmentSelection'
];
const SOURCE_DATA_HTML_MIME_TYPE = 'application/ht-source-data-json-html';
const META_HEAD = [
    '<meta name="generator" content="Handsontable"/>',
    '<style type="text/css">td{white-space:normal}br{mso-data-placement:same-cell}</style>'
].join('');
const sanitizeDeprecatedMessageShown = new WeakSet();
var /**
   * Shows the "Copy with headers" item in the context menu and extends the context menu with the
   * `'copy_with_column_headers'` option that can be used for creating custom menus arrangements.
   *
   * @type {boolean}
   * @default false
   */ _enableCopyColumnHeaders = /*#__PURE__*/ new WeakMap(), /**
   * Shows the "Copy with group headers" item in the context menu and extends the context menu with the
   * `'copy_with_column_group headers'` option that can be used for creating custom menus arrangements.
   *
   * @type {boolean}
   * @default false
   */ _enableCopyColumnGroupHeaders = /*#__PURE__*/ new WeakMap(), /**
   * Shows the "Copy headers only" item in the context menu and extends the context menu with the
   * `'copy_column_headers_only'` option that can be used for creating custom menus arrangements.
   *
   * @type {boolean}
   * @default false
   */ _enableCopyColumnHeadersOnly = /*#__PURE__*/ new WeakMap(), /**
   * Defines the data range to copy. Possible values:
   *  * `'cells-only'` Copy selected cells only;
   *  * `'column-headers-only'` Copy column headers only;
   *  * `'with-column-group-headers'` Copy cells with all column headers;
   *  * `'with-column-headers'` Copy cells with column headers;
   *
   * @type {'cells-only' | 'column-headers-only' | 'with-column-group-headers' | 'with-column-headers'}
   */ _copyMode = /*#__PURE__*/ new WeakMap(), /**
   * Flag that is used to prevent copying when the native shortcut was not pressed.
   *
   * @type {boolean}
   */ _isTriggeredByCopy = /*#__PURE__*/ new WeakMap(), /**
   * Flag that is used to prevent cutting when the native shortcut was not pressed.
   *
   * @type {boolean}
   */ _isTriggeredByCut = /*#__PURE__*/ new WeakMap(), /**
   * Class that helps generate copyable ranges based on the current selection for different copy mode
   * types.
   *
   * @type {CopyableRangesFactory}
   */ _copyableRangesFactory = /*#__PURE__*/ new WeakMap(), /**
   * Flag that indicates if the viewport scroll should be prevented after pasting the data.
   *
   * @type {boolean}
   */ _preventViewportScrollOnPaste = /*#__PURE__*/ new WeakMap(), /**
   * Ensure that the `copy`/`cut` events get triggered properly in Safari.
   *
   * @param {string} eventName Name of the event to get triggered.
   */ _ensureClipboardEventsGetTriggered = /*#__PURE__*/ new WeakSet(), /**
   * Counts how many column headers will be copied based on the passed range.
   *
   * @private
   * @param {Array<{startRow: number, startCol: number, endRow: number, endCol: number}>} ranges Array of objects with properties `startRow`, `startCol`, `endRow` and `endCol`.
   * @returns {{ columnHeadersCount: number }} Returns an object with keys that holds
   *                                           information with the number of copied headers.
   */ _countCopiedHeaders = /*#__PURE__*/ new WeakSet(), /**
   * Add the `contenteditable` attribute to the highlighted cell and select its content.
   */ _addContentEditableToHighlightedCell = /*#__PURE__*/ new WeakSet(), /**
   * Remove the `contenteditable` attribute from the highlighted cell and deselect its content.
   */ _removeContentEditableFromHighlightedCell = /*#__PURE__*/ new WeakSet(), /**
   * Sets the clipboard data.
   *
   * @param {ClipboardEvent} event The Clipboard event.
   * @param {Array} rangedData Ranged data to set to the clipboard.
   * @param {Array} rangedSourceData Ranged source data to set to the clipboard.
   */ _setClipboardData = /*#__PURE__*/ new WeakSet(), /**
   * Add copy and cut options to the Context Menu.
   *
   * @param {object} options Contains default added options of the Context Menu.
   */ _onAfterContextMenuDefaultOptions = /*#__PURE__*/ new WeakSet(), /**
   * Disables the viewport scroll after pasting the data.
   *
   * @param {number} fromRow Selection start row visual index.
   * @param {number} fromColumn Selection start column visual index.
   * @param {number} toRow Selection end row visual index.
   * @param {number} toColumn Selection end column visual index.
   * @param {object} preventScrolling Object with `value` property. If `true`, the viewport scroll will be prevented.
   */ _onAfterSelection = /*#__PURE__*/ new WeakSet(), /**
   * Force focus on focusableElement after end of the selection.
   */ _onAfterSelectionEnd = /*#__PURE__*/ new WeakSet(), /**
   * `document.body` `mouseenter` callback used to work around a Safari's problem with copying/cutting from the
   * browser's menu.
   */ _onSafariMouseEnter = /*#__PURE__*/ new WeakSet(), /**
   * `document.body` `mouseleave` callback used to work around a Safari's problem with copying/cutting from the
   * browser's menu.
   */ _onSafariMouseLeave = /*#__PURE__*/ new WeakSet(), /**
   * `afterSelection` hook callback triggered only on Safari.
   */ _onSafariAfterSelection = /*#__PURE__*/ new WeakSet();
class CopyPaste extends _base.BasePlugin {
    static get PLUGIN_KEY() {
        return PLUGIN_KEY;
    }
    static get SETTING_KEYS() {
        return [
            PLUGIN_KEY,
            ...SETTING_KEYS
        ];
    }
    static get PLUGIN_PRIORITY() {
        return PLUGIN_PRIORITY;
    }
    static get DEFAULT_SETTINGS() {
        return {
            pasteMode: 'overwrite',
            rowsLimit: Infinity,
            columnsLimit: Infinity,
            copyColumnHeaders: false,
            copyColumnGroupHeaders: false,
            copyColumnHeadersOnly: false
        };
    }
    /**
   * Checks if the [`CopyPaste`](#copypaste) plugin is enabled.
   *
   * This method gets called by Handsontable's [`beforeInit`](@/api/hooks.md#beforeinit) hook.
   * If it returns `true`, the [`enablePlugin()`](#enableplugin) method gets called.
   *
   * @returns {boolean}
   */ isEnabled() {
        return !!this.hot.getSettings()[PLUGIN_KEY];
    }
    /**
   * Enables the [`CopyPaste`](#copypaste) plugin for your Handsontable instance.
   */ enablePlugin() {
        if (this.enabled) {
            return;
        }
        this.pasteMode = this.getSetting('pasteMode') ?? this.pasteMode;
        this.rowsLimit = isNaN(this.getSetting('rowsLimit')) ? this.rowsLimit : this.getSetting('rowsLimit');
        this.columnsLimit = isNaN(this.getSetting('columnsLimit')) ? this.columnsLimit : this.getSetting('columnsLimit');
        _class_private_field_set(this, _enableCopyColumnHeaders, this.getSetting('copyColumnHeaders'));
        _class_private_field_set(this, _enableCopyColumnGroupHeaders, this.getSetting('copyColumnGroupHeaders'));
        _class_private_field_set(this, _enableCopyColumnHeadersOnly, this.getSetting('copyColumnHeadersOnly'));
        this.uiContainer = this.getSetting('uiContainer') ?? this.uiContainer;
        this.addHook('afterContextMenuDefaultOptions', (options)=>_class_private_method_get(this, _onAfterContextMenuDefaultOptions, onAfterContextMenuDefaultOptions).call(this, options));
        this.addHook('afterSelection', (...args)=>_class_private_method_get(this, _onAfterSelection, onAfterSelection).call(this, ...args));
        this.addHook('afterSelectionEnd', ()=>_class_private_method_get(this, _onAfterSelectionEnd, onAfterSelectionEnd).call(this));
        // Events are attached to the document, not the root table element - as it should,
        // for Chrome 133 and lower to copy/paste/cut work properly (#dev-2277).
        this.eventManager.addEventListener(this.hot.rootDocument, 'copy', (...args)=>this.onCopy(...args));
        this.eventManager.addEventListener(this.hot.rootDocument, 'cut', (...args)=>this.onCut(...args));
        this.eventManager.addEventListener(this.hot.rootDocument, 'paste', (...args)=>this.onPaste(...args));
        // Without this workaround Safari (tested on Safari@16.5.2) does allow copying/cutting from the browser menu.
        if ((0, _browser.isSafari)()) {
            this.eventManager.addEventListener(this.hot.rootDocument.body, 'mouseenter', (...args)=>_class_private_method_get(this, _onSafariMouseEnter, onSafariMouseEnter).call(this, ...args));
            this.eventManager.addEventListener(this.hot.rootDocument.body, 'mouseleave', (...args)=>_class_private_method_get(this, _onSafariMouseLeave, onSafariMouseLeave).call(this, ...args));
            this.addHook('afterSelection', ()=>_class_private_method_get(this, _onSafariAfterSelection, onSafariAfterSelection).call(this));
        }
        super.enablePlugin();
    }
    /**
   * Updates the state of the [`CopyPaste`](#copypaste) plugin.
   *
   * Gets called when [`updateSettings()`](@/api/core.md#updatesettings)
   * is invoked with any of the following configuration options:
   *  - [`copyPaste`](@/api/options.md#copypaste)
   *  - [`fragmentSelection`](@/api/options.md#fragmentselection)
   */ updatePlugin() {
        this.disablePlugin();
        this.enablePlugin();
        super.updatePlugin();
    }
    /**
   * Disables the [`CopyPaste`](#copypaste) plugin for your Handsontable instance.
   */ disablePlugin() {
        super.disablePlugin();
    }
    /**
   * Copies the contents of the selected cells (and/or their related column headers) to the system clipboard.
   *
   * Takes an optional parameter (`copyMode`) that defines the scope of copying:
   *
   * | `copyMode` value              | Description                                                     |
   * | ----------------------------- | --------------------------------------------------------------- |
   * | `'cells-only'` (default)      | Copy the selected cells                                         |
   * | `'with-column-headers'`       | - Copy the selected cells<br>- Copy the nearest column headers  |
   * | `'with-column-group-headers'` | - Copy the selected cells<br>- Copy all related columns headers |
   * | `'column-headers-only'`       | Copy the nearest column headers (without copying cells)         |
   *
   * @param {string} [copyMode='cells-only'] Copy mode.
   */ copy(copyMode = 'cells-only') {
        _class_private_field_set(this, _copyMode, copyMode);
        _class_private_field_set(this, _isTriggeredByCopy, true);
        _class_private_method_get(this, _ensureClipboardEventsGetTriggered, ensureClipboardEventsGetTriggered).call(this, 'copy');
    }
    /**
   * Copies the contents of the selected cells.
   */ copyCellsOnly() {
        this.copy('cells-only');
    }
    /**
   * Copies the contents of column headers that are nearest to the selected cells.
   */ copyColumnHeadersOnly() {
        this.copy('column-headers-only');
    }
    /**
   * Copies the contents of the selected cells and all their related column headers.
   */ copyWithAllColumnHeaders() {
        this.copy('with-column-group-headers');
    }
    /**
   * Copies the contents of the selected cells and their nearest column headers.
   */ copyWithColumnHeaders() {
        this.copy('with-column-headers');
    }
    /**
   * Cuts the contents of the selected cells to the system clipboard.
   */ cut() {
        _class_private_field_set(this, _isTriggeredByCut, true);
        _class_private_method_get(this, _ensureClipboardEventsGetTriggered, ensureClipboardEventsGetTriggered).call(this, 'cut');
    }
    /**
   * Converts the contents of multiple ranges (`ranges`) into a single string.
   *
   * @param {Array<{startRow: number, startCol: number, endRow: number, endCol: number}>} ranges Array of objects with properties `startRow`, `endRow`, `startCol` and `endCol`.
   * @returns {string} A string that will be copied to the clipboard.
   */ getRangedCopyableData(ranges) {
        return (0, _SheetClip.stringify)(this.getRangedData(ranges));
    }
    /**
   * Converts the contents of multiple ranges (`ranges`) into an array of arrays.
   *
   * @param {Array<{startRow: number, startCol: number, endRow: number, endCol: number}>} ranges Array of objects with properties `startRow`, `startCol`, `endRow` and `endCol`.
   * @param {boolean} [useSourceData=false] Whether to use the source data instead of the data. This will stringify objects as JSON.
   * @returns {Array[]} An array of arrays that will be copied to the clipboard.
   */ getRangedData(ranges, useSourceData = false) {
        const data = [];
        const { rows, columns } = (0, _copyableRanges.normalizeRanges)(ranges);
        // concatenate all rows and columns data defined in ranges into one copyable string
        (0, _array.arrayEach)(rows, (row)=>{
            const rowSet = [];
            (0, _array.arrayEach)(columns, (column)=>{
                if (row < 0) {
                    // `row` as the second argument acts here as the `headerLevel` argument
                    rowSet.push(this.hot.getColHeader(column, row));
                } else {
                    let copyableCellData = useSourceData ? this.hot.getCopyableSourceData(row, column) : this.hot.getCopyableData(row, column);
                    if (useSourceData && ((0, _object.isObject)(copyableCellData) || Array.isArray(copyableCellData))) {
                        copyableCellData = JSON.stringify(copyableCellData);
                    }
                    rowSet.push(copyableCellData);
                }
            });
            data.push(rowSet);
        });
        return data;
    }
    /**
   * Simulates the paste action.
   *
   * For security reasons, modern browsers don't allow reading from the system clipboard.
   *
   * @param {string} pastableText The value to paste, as a raw string.
   * @param {string} [pastableHtml=''] The value to paste, as HTML.
   */ paste(pastableText = '', pastableHtml = pastableText) {
        if (!pastableText && !pastableHtml) {
            return;
        }
        const pasteData = new _pasteEvent.default();
        if (pastableText) {
            pasteData.clipboardData.setData('text/plain', pastableText);
        }
        if (pastableHtml) {
            pasteData.clipboardData.setData('text/html', pastableHtml);
        }
        this.onPaste(pasteData);
    }
    /**
   * Prepares copyable text from the cells selection in the invisible textarea.
   */ setCopyableText() {
        const selectionRange = this.hot.getSelectedRangeActive();
        if (!selectionRange) {
            return;
        }
        if (selectionRange.isSingleHeader()) {
            this.copyableRanges = [];
            return;
        }
        _class_private_field_get(this, _copyableRangesFactory).setSelectedRange(selectionRange);
        const groupedRanges = new Map([
            [
                'headers',
                null
            ],
            [
                'cells',
                null
            ]
        ]);
        if (_class_private_field_get(this, _copyMode) === 'column-headers-only') {
            groupedRanges.set('headers', _class_private_field_get(this, _copyableRangesFactory).getMostBottomColumnHeadersRange());
        } else {
            if (_class_private_field_get(this, _copyMode) === 'with-column-headers') {
                groupedRanges.set('headers', _class_private_field_get(this, _copyableRangesFactory).getMostBottomColumnHeadersRange());
            } else if (_class_private_field_get(this, _copyMode) === 'with-column-group-headers') {
                groupedRanges.set('headers', _class_private_field_get(this, _copyableRangesFactory).getAllColumnHeadersRange());
            }
            groupedRanges.set('cells', _class_private_field_get(this, _copyableRangesFactory).getCellsRange());
        }
        this.copyableRanges = Array.from(groupedRanges.values()).filter((range)=>range !== null).map(({ startRow, startCol, endRow, endCol })=>({
                startRow,
                startCol,
                endRow,
                endCol
            }));
        this.copyableRanges = this.hot.runHooks('modifyCopyableRange', this.copyableRanges);
        const cellsRange = groupedRanges.get('cells');
        if (cellsRange !== null && cellsRange.isRangeTrimmed) {
            const { startRow, startCol, endRow, endCol } = cellsRange;
            this.hot.runHooks('afterCopyLimit', endRow - startRow + 1, endCol - startCol + 1, this.rowsLimit, this.columnsLimit);
        }
    }
    /**
   * Verifies if editor exists and is open.
   *
   * @private
   * @returns {boolean}
   */ isEditorOpened() {
        return this.hot.getActiveEditor()?.isOpened();
    }
    /**
   * Prepares new values to populate them into datasource.
   *
   * @private
   * @param {object} pasteData An object with the data to populate.
   * @param {object} pasteData.plainData The plain data to populate.
   * @param {object} pasteData.sourceData The source data to populate.
   * @param {object} pasteData.originalPlainData The original plain data before user modifications in beforePaste.
   * @returns {number[]} Range coordinates after populate data.
   */ populateValues({ plainData, originalPlainData, sourceData }) {
        if (!plainData.length) {
            return [
                null,
                null,
                null,
                null
            ];
        }
        const selection = this.hot.getSelectedRangeActive();
        const populatedRowsLength = plainData.length;
        const populatedColumnsLength = plainData[0].length;
        const newRows = [];
        const { row: startRow, col: startColumn } = selection.getTopStartCorner();
        const { row: endRowFromSelection, col: endColumnFromSelection } = selection.getBottomEndCorner();
        let visualRowForPopulatedData = startRow;
        let visualColumnForPopulatedData = startColumn;
        let lastVisualRow = startRow;
        let lastVisualColumn = startColumn;
        // We try to populate just all copied data or repeat copied data within a selection. Please keep in mind that we
        // don't know whether populated data is bigger than selection on start as there are some cells for which values
        // should be not inserted (it's known right after getting cell meta).
        while(newRows.length < populatedRowsLength || visualRowForPopulatedData <= endRowFromSelection){
            const { skipRowOnPaste, visualRow } = this.hot.getCellMeta(visualRowForPopulatedData, startColumn);
            visualRowForPopulatedData = visualRow + 1;
            if (skipRowOnPaste === true) {
                continue;
            }
            lastVisualRow = visualRow;
            visualColumnForPopulatedData = startColumn;
            const newRow = [];
            const insertedRow = newRows.length % populatedRowsLength;
            while(newRow.length < populatedColumnsLength || visualColumnForPopulatedData <= endColumnFromSelection){
                const { skipColumnOnPaste, visualCol, parsePastedValue } = this.hot.getCellMeta(visualRow, visualColumnForPopulatedData);
                const insertedColumn = newRow.length % populatedColumnsLength;
                visualColumnForPopulatedData = visualCol + 1;
                if (skipColumnOnPaste === true) {
                    continue;
                }
                lastVisualColumn = visualCol;
                const sourceCellValue = sourceData?.[insertedRow]?.[insertedColumn];
                const originalCellValue = originalPlainData?.[insertedRow]?.[insertedColumn];
                let cellValue = plainData[insertedRow][insertedColumn];
                if (parsePastedValue && sourceData && (0, _string.isJSON)(sourceCellValue) && cellValue === originalCellValue) {
                    const parsedCellValue = JSON.parse(sourceCellValue);
                    cellValue = parsedCellValue;
                }
                newRow.push(cellValue);
            }
            newRows.push(newRow);
        }
        _class_private_field_set(this, _preventViewportScrollOnPaste, true);
        this.hot.populateFromArray(startRow, startColumn, newRows, undefined, undefined, 'CopyPaste.paste', this.pasteMode);
        return [
            startRow,
            startColumn,
            lastVisualRow,
            lastVisualColumn
        ];
    }
    /**
   * `copy` event callback on textarea element.
   *
   * @param {Event} event ClipboardEvent.
   * @private
   */ onCopy(event) {
        const eventTarget = event.composedPath()[0];
        const focusedElement = this.hot.getFocusManager().getRefocusElement();
        const isHotInput = eventTarget?.hasAttribute('data-hot-input');
        if (!this.hot.isListening() && !_class_private_field_get(this, _isTriggeredByCopy) || this.isEditorOpened() || (0, _element.isHTMLElement)(eventTarget) && (isHotInput && eventTarget !== focusedElement || !isHotInput && eventTarget !== this.hot.rootDocument.body && !(0, _element.isInternalElement)(eventTarget, this.hot.rootElement))) {
            return;
        }
        event.preventDefault();
        this.setCopyableText();
        _class_private_field_set(this, _isTriggeredByCopy, false);
        const rangedData = this.getRangedData(this.copyableRanges);
        const rangedSourceData = this.getRangedData(this.copyableRanges, true);
        const copiedHeadersCount = _class_private_method_get(this, _countCopiedHeaders, countCopiedHeaders).call(this, this.copyableRanges);
        const allowCopying = !!this.hot.runHooks('beforeCopy', rangedData, this.copyableRanges, copiedHeadersCount);
        if (allowCopying) {
            _class_private_method_get(this, _setClipboardData, setClipboardData).call(this, event, rangedData, rangedSourceData);
            this.hot.runHooks('afterCopy', rangedData, this.copyableRanges, copiedHeadersCount);
        }
        _class_private_field_set(this, _copyMode, 'cells-only');
    }
    /**
   * `cut` event callback on textarea element.
   *
   * @param {Event} event ClipboardEvent.
   * @private
   */ onCut(event) {
        const eventTarget = event.composedPath()[0];
        const focusedElement = this.hot.getFocusManager().getRefocusElement();
        const isHotInput = eventTarget?.hasAttribute('data-hot-input');
        if (!this.hot.isListening() && !_class_private_field_get(this, _isTriggeredByCut) || this.isEditorOpened() || (0, _element.isHTMLElement)(eventTarget) && (isHotInput && eventTarget !== focusedElement || !isHotInput && eventTarget !== this.hot.rootDocument.body && !(0, _element.isInternalElement)(eventTarget, this.hot.rootElement))) {
            return;
        }
        event.preventDefault();
        this.setCopyableText();
        _class_private_field_set(this, _isTriggeredByCut, false);
        const rangedData = this.getRangedData(this.copyableRanges);
        const rangedSourceData = this.getRangedData(this.copyableRanges, true);
        const allowCuttingOut = !!this.hot.runHooks('beforeCut', rangedData, this.copyableRanges);
        if (allowCuttingOut) {
            _class_private_method_get(this, _setClipboardData, setClipboardData).call(this, event, rangedData, rangedSourceData);
            this.hot.emptySelectedCells('CopyPaste.cut');
            this.hot.runHooks('afterCut', rangedData, this.copyableRanges);
        }
    }
    /**
   * `paste` event callback on textarea element.
   *
   * @param {Event} event ClipboardEvent or pseudo ClipboardEvent, if paste was called manually.
   * @private
   */ onPaste(event) {
        const eventTarget = event.composedPath()[0];
        const focusedElement = this.hot.getFocusManager().getRefocusElement();
        const isHotInput = eventTarget?.hasAttribute('data-hot-input');
        if (!this.hot.isListening() || this.isEditorOpened() || !this.hot.getSelected() || (0, _element.isHTMLElement)(eventTarget) && (isHotInput && eventTarget !== focusedElement || !isHotInput && eventTarget !== this.hot.rootDocument.body && !(0, _element.isInternalElement)(eventTarget, this.hot.rootElement))) {
            return;
        }
        event.preventDefault();
        let pastedData;
        let pastedSourceData;
        if (event && typeof event.clipboardData !== 'undefined') {
            const sourceDataHTML = event.clipboardData.getData(SOURCE_DATA_HTML_MIME_TYPE);
            if (sourceDataHTML) {
                const parsedSourceConfig = (0, _parseTable.htmlToGridSettings)(sourceDataHTML, this.hot.rootDocument);
                pastedSourceData = parsedSourceConfig.data;
            }
            const rawTextHTML = event.clipboardData.getData('text/html');
            const { sanitizer } = this.hot.getSettings();
            let textHTML = rawTextHTML;
            if (typeof sanitizer === 'function') {
                textHTML = sanitizer(rawTextHTML, 'CopyPaste.paste');
            } else {
                if (!sanitizeDeprecatedMessageShown.has(this.hot)) {
                    sanitizeDeprecatedMessageShown.add(this.hot);
                    (0, _console.deprecatedWarn)('The HTML sanitization using DOMPurify library is deprecated and will be removed in ' + 'the next major release. Use the `sanitizer` option instead.\n\n' + 'Migration guide: https://handsontable.com/docs/migration-from-16.2-to-17.0/\n' + '`sanitizer` documentation: https://handsontable.com/docs/api/options/#sanitizer');
                }
                textHTML = (0, _string.sanitize)(rawTextHTML, {
                    ADD_TAGS: [
                        'meta'
                    ],
                    ADD_ATTR: [
                        'content'
                    ],
                    FORCE_BODY: true
                });
            }
            if (textHTML && /(<table)|(<TABLE)/g.test(textHTML)) {
                const parsedConfig = (0, _parseTable.htmlToGridSettings)(textHTML, this.hot.rootDocument);
                pastedData = parsedConfig.data;
            } else {
                pastedData = event.clipboardData.getData('text/plain');
            }
        } else if (typeof ClipboardEvent === 'undefined' && typeof this.hot.rootWindow.clipboardData !== 'undefined') {
            pastedData = this.hot.rootWindow.clipboardData.getData('Text');
        }
        if (typeof pastedData === 'string') {
            pastedData = (0, _SheetClip.parse)(pastedData);
        }
        if (pastedData === void 0 || pastedData && pastedData.length === 0) {
            return;
        }
        // Store a copy of the original pasted data before user can modify it in beforePaste hook.
        // This is needed to detect if user modified values and respect their modifications over source data.
        const originalPastedData = (0, _object.deepClone)(pastedData);
        if (this.hot.runHooks('beforePaste', pastedData, this.copyableRanges) === false) {
            return;
        }
        const [startRow, startColumn, endRow, endColumn] = this.populateValues({
            plainData: pastedData,
            sourceData: pastedSourceData,
            originalPlainData: originalPastedData
        });
        if (startRow !== null && startColumn !== null) {
            this.hot.selectCell(startRow, startColumn, Math.min(this.hot.countRows() - 1, endRow), Math.min(this.hot.countCols() - 1, endColumn));
        }
        this.hot.runHooks('afterPaste', pastedData, this.copyableRanges);
    }
    /**
   * Destroys the `CopyPaste` plugin instance.
   */ destroy() {
        super.destroy();
    }
    constructor(...args){
        super(...args), _class_private_method_init(this, _ensureClipboardEventsGetTriggered), _class_private_method_init(this, _countCopiedHeaders), _class_private_method_init(this, _addContentEditableToHighlightedCell), _class_private_method_init(this, _removeContentEditableFromHighlightedCell), _class_private_method_init(this, _setClipboardData), _class_private_method_init(this, _onAfterContextMenuDefaultOptions), _class_private_method_init(this, _onAfterSelection), _class_private_method_init(this, _onAfterSelectionEnd), _class_private_method_init(this, _onSafariMouseEnter), _class_private_method_init(this, _onSafariMouseLeave), _class_private_method_init(this, _onSafariAfterSelection), _class_private_field_init(this, _enableCopyColumnHeaders, {
            writable: true,
            value: void 0
        }), _class_private_field_init(this, _enableCopyColumnGroupHeaders, {
            writable: true,
            value: void 0
        }), _class_private_field_init(this, _enableCopyColumnHeadersOnly, {
            writable: true,
            value: void 0
        }), _class_private_field_init(this, _copyMode, {
            writable: true,
            value: void 0
        }), _class_private_field_init(this, _isTriggeredByCopy, {
            writable: true,
            value: void 0
        }), _class_private_field_init(this, _isTriggeredByCut, {
            writable: true,
            value: void 0
        }), _class_private_field_init(this, _copyableRangesFactory, {
            writable: true,
            value: void 0
        }), _class_private_field_init(this, _preventViewportScrollOnPaste, {
            writable: true,
            value: void 0
        }), /**
   * The maximum number of columns than can be copied to the clipboard.
   *
   * @type {number}
   * @default Infinity
   */ this.columnsLimit = Infinity, /**
   * The maximum number of rows than can be copied to the clipboard.
   *
   * @type {number}
   * @default Infinity
   */ this.rowsLimit = Infinity, /**
   * When pasting:
   * - `'overwrite'` - overwrite the currently-selected cells
   * - `'shift_down'` - move currently-selected cells down
   * - `'shift_right'` - move currently-selected cells to the right
   *
   * @type {string}
   * @default 'overwrite'
   */ this.pasteMode = 'overwrite', /**
   * The UI container for the secondary focusable element.
   *
   * @type {HTMLElement}
   */ this.uiContainer = this.hot.rootDocument.body, _class_private_field_set(this, _enableCopyColumnHeaders, false), _class_private_field_set(this, _enableCopyColumnGroupHeaders, false), _class_private_field_set(this, _enableCopyColumnHeadersOnly, false), _class_private_field_set(this, _copyMode, 'cells-only'), _class_private_field_set(this, _isTriggeredByCopy, false), _class_private_field_set(this, _isTriggeredByCut, false), _class_private_field_set(this, _copyableRangesFactory, new _copyableRanges.CopyableRangesFactory({
            countRows: ()=>this.hot.countRows(),
            countColumns: ()=>this.hot.countCols(),
            rowsLimit: ()=>this.rowsLimit,
            columnsLimit: ()=>this.columnsLimit,
            countColumnHeaders: ()=>this.hot.view.getColumnHeadersCount()
        })), _class_private_field_set(this, _preventViewportScrollOnPaste, false), /**
   * Ranges of the cells coordinates, which should be used to copy/cut/paste actions.
   *
   * @private
   * @type {Array<{startRow: number, startCol: number, endRow: number, endCol: number}>}
   */ this.copyableRanges = [];
    }
}
function ensureClipboardEventsGetTriggered(eventName) {
    // Without this workaround Safari (tested on Safari@16.5.2) does not trigger the 'copy' event.
    if ((0, _browser.isSafari)()) {
        const activeSelectedRange = this.hot.getSelectedRangeActive();
        if (activeSelectedRange) {
            const { row: highlightRow, col: highlightColumn } = activeSelectedRange.highlight;
            const currentlySelectedCell = this.hot.getCell(highlightRow, highlightColumn, true);
            if (currentlySelectedCell) {
                (0, _element.runWithSelectedContendEditableElement)(currentlySelectedCell, ()=>{
                    this.hot.rootDocument.execCommand(eventName);
                });
            }
        }
    } else {
        this.hot.rootDocument.execCommand(eventName);
    }
}
function countCopiedHeaders(ranges) {
    const { rows } = (0, _copyableRanges.normalizeRanges)(ranges);
    let columnHeadersCount = 0;
    for(let row = 0; row < rows.length; row++){
        if (rows[row] >= 0) {
            break;
        }
        columnHeadersCount += 1;
    }
    return {
        columnHeadersCount
    };
}
function addContentEditableToHighlightedCell() {
    if (this.hot.isListening()) {
        const activeSelectedRange = this.hot.getSelectedRangeActive();
        if (activeSelectedRange) {
            const { row: highlightRow, col: highlightColumn } = activeSelectedRange.highlight;
            const currentlySelectedCell = this.hot.getCell(highlightRow, highlightColumn, true);
            if (currentlySelectedCell) {
                (0, _element.makeElementContentEditableAndSelectItsContent)(currentlySelectedCell);
            }
        }
    }
}
function removeContentEditableFromHighlightedCell() {
    // If the instance is not listening, the workaround is not needed.
    if (this.hot.isListening()) {
        const activeSelectedRange = this.hot.getSelectedRangeActive();
        if (activeSelectedRange) {
            const { row: highlightRow, col: highlightColumn } = activeSelectedRange.highlight;
            const currentlySelectedCell = this.hot.getCell(highlightRow, highlightColumn, true);
            if (currentlySelectedCell?.hasAttribute('contenteditable')) {
                (0, _element.removeContentEditableFromElementAndDeselect)(currentlySelectedCell);
            }
        }
    }
}
function setClipboardData(event, rangedData, rangedSourceData) {
    const textPlain = (0, _SheetClip.stringify)(rangedData);
    if (event && event.clipboardData) {
        const textHTML = (0, _parseTable._dataToHTML)(rangedData);
        const textSourceDataHTML = (0, _parseTable._dataToHTML)(rangedSourceData);
        event.clipboardData.setData('text/plain', textPlain);
        event.clipboardData.setData('text/html', [
            META_HEAD,
            textHTML
        ].join(''));
        event.clipboardData.setData(SOURCE_DATA_HTML_MIME_TYPE, [
            META_HEAD,
            textSourceDataHTML
        ].join(''));
    } else if (typeof ClipboardEvent === 'undefined') {
        this.hot.rootWindow.clipboardData.setData('Text', textPlain);
    }
}
function onAfterContextMenuDefaultOptions(options) {
    options.items.push({
        name: '---------'
    }, (0, _copy.default)(this));
    if (_class_private_field_get(this, _enableCopyColumnHeaders)) {
        options.items.push((0, _copyWithColumnHeaders.default)(this));
    }
    if (_class_private_field_get(this, _enableCopyColumnGroupHeaders)) {
        options.items.push((0, _copyWithColumnGroupHeaders.default)(this));
    }
    if (_class_private_field_get(this, _enableCopyColumnHeadersOnly)) {
        options.items.push((0, _copyColumnHeadersOnly.default)(this));
    }
    options.items.push((0, _cut.default)(this));
}
function onAfterSelection(fromRow, fromColumn, toRow, toColumn, preventScrolling) {
    if (_class_private_field_get(this, _preventViewportScrollOnPaste)) {
        preventScrolling.value = true;
    }
    _class_private_field_set(this, _preventViewportScrollOnPaste, false);
}
function onAfterSelectionEnd() {
    if (this.isEditorOpened()) {
        return;
    }
    if (this.hot.getSettings().fragmentSelection) {
        return;
    }
    this.setCopyableText();
}
function onSafariMouseEnter() {
    _class_private_method_get(this, _removeContentEditableFromHighlightedCell, removeContentEditableFromHighlightedCell).call(this);
}
function onSafariMouseLeave() {
    _class_private_method_get(this, _addContentEditableToHighlightedCell, addContentEditableToHighlightedCell).call(this);
}
function onSafariAfterSelection() {
    _class_private_method_get(this, _removeContentEditableFromHighlightedCell, removeContentEditableFromHighlightedCell).call(this);
}
