"use strict";

require("core-js/modules/es.symbol.to-primitive.js");
require("core-js/modules/es.date.to-primitive.js");
require("core-js/modules/es.symbol.js");
require("core-js/modules/es.symbol.description.js");
require("core-js/modules/es.number.constructor.js");
require("core-js/modules/es.object.set-prototype-of.js");
require("core-js/modules/es.object.get-prototype-of.js");
require("core-js/modules/es.reflect.construct.js");
require("core-js/modules/es.reflect.get.js");
require("core-js/modules/es.object.get-own-property-descriptor.js");
require("core-js/modules/es.symbol.iterator.js");
require("core-js/modules/es.array.slice.js");
require("core-js/modules/es.regexp.to-string.js");
require("core-js/modules/es.function.name.js");
exports.__esModule = true;
exports.PLUGIN_PRIORITY = exports.PLUGIN_KEY = exports.CopyPaste = void 0;
require("core-js/modules/es.array.join.js");
require("core-js/modules/es.array.concat.js");
require("core-js/modules/es.array.iterator.js");
require("core-js/modules/es.map.js");
require("core-js/modules/es.object.to-string.js");
require("core-js/modules/es.string.iterator.js");
require("core-js/modules/web.dom-collections.iterator.js");
require("core-js/modules/es.array.map.js");
require("core-js/modules/es.array.filter.js");
require("core-js/modules/es.array.from.js");
require("core-js/modules/es.regexp.exec.js");
require("core-js/modules/es.weak-map.js");
require("core-js/modules/es.weak-set.js");
var _base = require("../base");
var _pluginHooks = _interopRequireDefault(require("../../pluginHooks"));
var _SheetClip = require("../../3rdparty/SheetClip");
var _array = require("../../helpers/array");
var _string = require("../../helpers/string");
var _element = require("../../helpers/dom/element");
var _copy = _interopRequireDefault(require("./contextMenuItem/copy"));
var _copyColumnHeadersOnly = _interopRequireDefault(require("./contextMenuItem/copyColumnHeadersOnly"));
var _copyWithColumnGroupHeaders = _interopRequireDefault(require("./contextMenuItem/copyWithColumnGroupHeaders"));
var _copyWithColumnHeaders = _interopRequireDefault(require("./contextMenuItem/copyWithColumnHeaders"));
var _cut = _interopRequireDefault(require("./contextMenuItem/cut"));
var _pasteEvent = _interopRequireDefault(require("./pasteEvent"));
var _focusableElement = require("./focusableElement");
var _copyableRanges = require("./copyableRanges");
var _parseTable = require("../../utils/parseTable");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
function _iterableToArrayLimit(arr, i) { var _i = null == arr ? null : "undefined" != typeof Symbol && arr[Symbol.iterator] || arr["@@iterator"]; if (null != _i) { var _s, _e, _x, _r, _arr = [], _n = !0, _d = !1; try { if (_x = (_i = _i.call(arr)).next, 0 === i) { if (Object(_i) !== _i) return; _n = !1; } else for (; !(_n = (_s = _x.call(_i)).done) && (_arr.push(_s.value), _arr.length !== i); _n = !0); } catch (err) { _d = !0, _e = err; } finally { try { if (!_n && null != _i.return && (_r = _i.return(), Object(_r) !== _r)) return; } finally { if (_d) throw _e; } } return _arr; } }
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } }
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
function _get() { if (typeof Reflect !== "undefined" && Reflect.get) { _get = Reflect.get.bind(); } else { _get = function _get(target, property, receiver) { var base = _superPropBase(target, property); if (!base) return; var desc = Object.getOwnPropertyDescriptor(base, property); if (desc.get) { return desc.get.call(arguments.length < 3 ? target : receiver); } return desc.value; }; } return _get.apply(this, arguments); }
function _superPropBase(object, property) { while (!Object.prototype.hasOwnProperty.call(object, property)) { object = _getPrototypeOf(object); if (object === null) break; } return object; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); Object.defineProperty(subClass, "prototype", { writable: false }); if (superClass) _setPrototypeOf(subClass, superClass); }
function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }
function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } else if (call !== void 0) { throw new TypeError("Derived constructors may only return object or undefined"); } return _assertThisInitialized(self); }
function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } }
function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
function _classPrivateMethodInitSpec(obj, privateSet) { _checkPrivateRedeclaration(obj, privateSet); privateSet.add(obj); }
function _classPrivateFieldInitSpec(obj, privateMap, value) { _checkPrivateRedeclaration(obj, privateMap); privateMap.set(obj, value); }
function _checkPrivateRedeclaration(obj, privateCollection) { if (privateCollection.has(obj)) { throw new TypeError("Cannot initialize the same private elements twice on an object"); } }
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return _typeof(key) === "symbol" ? key : String(key); }
function _toPrimitive(input, hint) { if (_typeof(input) !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (_typeof(res) !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
function _classPrivateMethodGet(receiver, privateSet, fn) { if (!privateSet.has(receiver)) { throw new TypeError("attempted to get private field on non-instance"); } return fn; }
function _classPrivateFieldGet(receiver, privateMap) { var descriptor = _classExtractFieldDescriptor(receiver, privateMap, "get"); return _classApplyDescriptorGet(receiver, descriptor); }
function _classApplyDescriptorGet(receiver, descriptor) { if (descriptor.get) { return descriptor.get.call(receiver); } return descriptor.value; }
function _classPrivateFieldSet(receiver, privateMap, value) { var descriptor = _classExtractFieldDescriptor(receiver, privateMap, "set"); _classApplyDescriptorSet(receiver, descriptor, value); return value; }
function _classExtractFieldDescriptor(receiver, privateMap, action) { if (!privateMap.has(receiver)) { throw new TypeError("attempted to " + action + " private field on non-instance"); } return privateMap.get(receiver); }
function _classApplyDescriptorSet(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; } }
_pluginHooks.default.getSingleton().register('afterCopyLimit');
_pluginHooks.default.getSingleton().register('modifyCopyableRange');
_pluginHooks.default.getSingleton().register('beforeCut');
_pluginHooks.default.getSingleton().register('afterCut');
_pluginHooks.default.getSingleton().register('beforePaste');
_pluginHooks.default.getSingleton().register('afterPaste');
_pluginHooks.default.getSingleton().register('beforeCopy');
_pluginHooks.default.getSingleton().register('afterCopy');
var PLUGIN_KEY = 'copyPaste';
exports.PLUGIN_KEY = PLUGIN_KEY;
var PLUGIN_PRIORITY = 80;
exports.PLUGIN_PRIORITY = PLUGIN_PRIORITY;
var SETTING_KEYS = ['fragmentSelection'];
var META_HEAD = ['<meta name="generator" content="Handsontable"/>', '<style type="text/css">td{white-space:normal}br{mso-data-placement:same-cell}</style>'].join('');

/* eslint-disable jsdoc/require-description-complete-sentence */
/**
 * @description
 * Copy, cut, and paste data by using the `CopyPaste` plugin.
 *
 * Control the `CopyPaste` plugin programmatically through its [API methods](#methods).
 *
 * The user can access the copy-paste features through:
 * - The [context menu](@/guides/cell-features/clipboard.md#context-menu).
 * - The [keyboard shortcuts](@/guides/cell-features/clipboard.md#related-keyboard-shortcuts).
 * - The browser's menu bar.
 *
 * Read more:
 * - [Guides: Clipboard](@/guides/cell-features/clipboard.md)
 * - [Configuration options: `copyPaste`](@/api/options.md#copypaste)
 *
 * @example
 * ```js
 * // enable the plugin with the default configuration
 * copyPaste: true,
 *
 * // or, enable the plugin with a custom configuration
 * copyPaste: {
 *   columnsLimit: 25,
 *   rowsLimit: 50,
 *   pasteMode: 'shift_down',
 *   copyColumnHeaders: true,
 *   copyColumnGroupHeaders: true,
 *   copyColumnHeadersOnly: true,
 *   uiContainer: document.body,
 * },
 * ```
 * @class CopyPaste
 * @plugin CopyPaste
 */
var _enableCopyColumnHeaders = /*#__PURE__*/new WeakMap();
var _enableCopyColumnGroupHeaders = /*#__PURE__*/new WeakMap();
var _enableCopyColumnHeadersOnly = /*#__PURE__*/new WeakMap();
var _copyMode = /*#__PURE__*/new WeakMap();
var _isTriggeredByCopy = /*#__PURE__*/new WeakMap();
var _isTriggeredByCut = /*#__PURE__*/new WeakMap();
var _copyableRangesFactory = /*#__PURE__*/new WeakMap();
var _countCopiedHeaders = /*#__PURE__*/new WeakSet();
var CopyPaste = /*#__PURE__*/function (_BasePlugin) {
  _inherits(CopyPaste, _BasePlugin);
  var _super = _createSuper(CopyPaste);
  function CopyPaste() {
    var _this;
    _classCallCheck(this, CopyPaste);
    for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
      args[_key] = arguments[_key];
    }
    _this = _super.call.apply(_super, [this].concat(args));
    /**
     * 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.
     */
    _classPrivateMethodInitSpec(_assertThisInitialized(_this), _countCopiedHeaders);
    /**
     * The maximum number of columns than can be copied to the clipboard.
     *
     * @type {number}
     * @default Infinity
     */
    _defineProperty(_assertThisInitialized(_this), "columnsLimit", Infinity);
    /**
     * The maximum number of rows than can be copied to the clipboard.
     *
     * @type {number}
     * @default Infinity
     */
    _defineProperty(_assertThisInitialized(_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'
     */
    _defineProperty(_assertThisInitialized(_this), "pasteMode", 'overwrite');
    /**
     * The UI container for the secondary focusable element.
     *
     * @type {HTMLElement}
     */
    _defineProperty(_assertThisInitialized(_this), "uiContainer", _this.hot.rootDocument.body);
    /**
     * 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
     */
    _classPrivateFieldInitSpec(_assertThisInitialized(_this), _enableCopyColumnHeaders, {
      writable: true,
      value: false
    });
    /**
     * 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
     */
    _classPrivateFieldInitSpec(_assertThisInitialized(_this), _enableCopyColumnGroupHeaders, {
      writable: true,
      value: false
    });
    /**
     * 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
     */
    _classPrivateFieldInitSpec(_assertThisInitialized(_this), _enableCopyColumnHeadersOnly, {
      writable: true,
      value: false
    });
    /**
     * Defines the data range to copy. Possible values:
     *  * `'cells-only'` Copy selected cells only;
     *  * `'column-headers-only'` Copy column headers only;
     *  * `'with-all-column-headers'` Copy cells with all column headers;
     *  * `'with-column-headers'` Copy cells with column headers;
     *
     * @type {'cells-only' | 'column-headers-only' | 'with-all-column-headers' | 'with-column-headers'}
     */
    _classPrivateFieldInitSpec(_assertThisInitialized(_this), _copyMode, {
      writable: true,
      value: 'cells-only'
    });
    /**
     * Flag that is used to prevent copying when the native shortcut was not pressed.
     *
     * @type {boolean}
     */
    _classPrivateFieldInitSpec(_assertThisInitialized(_this), _isTriggeredByCopy, {
      writable: true,
      value: false
    });
    /**
     * Flag that is used to prevent cutting when the native shortcut was not pressed.
     *
     * @type {boolean}
     */
    _classPrivateFieldInitSpec(_assertThisInitialized(_this), _isTriggeredByCut, {
      writable: true,
      value: false
    });
    /**
     * Class that helps generate copyable ranges based on the current selection for different copy mode
     * types.
     *
     * @type {CopyableRangesFactory}
     */
    _classPrivateFieldInitSpec(_assertThisInitialized(_this), _copyableRangesFactory, {
      writable: true,
      value: new _copyableRanges.CopyableRangesFactory({
        countRows: function countRows() {
          return _this.hot.countRows();
        },
        countColumns: function countColumns() {
          return _this.hot.countCols();
        },
        rowsLimit: function rowsLimit() {
          return _this.rowsLimit;
        },
        columnsLimit: function columnsLimit() {
          return _this.columnsLimit;
        },
        countColumnHeaders: function countColumnHeaders() {
          return _this.hot.view.getColumnHeadersCount();
        }
      })
    });
    /**
     * 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}>}
     */
    _defineProperty(_assertThisInitialized(_this), "copyableRanges", []);
    /**
     * Provides focusable element to support IME and copy/paste/cut actions.
     *
     * @private
     * @type {FocusableWrapper}
     */
    _defineProperty(_assertThisInitialized(_this), "focusableElement", void 0);
    return _this;
  }
  _createClass(CopyPaste, [{
    key: "isEnabled",
    value:
    /**
     * 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}
     */
    function isEnabled() {
      return !!this.hot.getSettings()[PLUGIN_KEY];
    }

    /**
     * Enables the [`CopyPaste`](#copypaste) plugin for your Handsontable instance.
     */
  }, {
    key: "enablePlugin",
    value: function enablePlugin() {
      var _this2 = this;
      if (this.enabled) {
        return;
      }
      var _this$hot$getSettings = this.hot.getSettings(),
        settings = _this$hot$getSettings[PLUGIN_KEY];
      if (_typeof(settings) === 'object') {
        var _settings$pasteMode, _settings$uiContainer;
        this.pasteMode = (_settings$pasteMode = settings.pasteMode) !== null && _settings$pasteMode !== void 0 ? _settings$pasteMode : this.pasteMode;
        this.rowsLimit = isNaN(settings.rowsLimit) ? this.rowsLimit : settings.rowsLimit;
        this.columnsLimit = isNaN(settings.columnsLimit) ? this.columnsLimit : settings.columnsLimit;
        _classPrivateFieldSet(this, _enableCopyColumnHeaders, !!settings.copyColumnHeaders);
        _classPrivateFieldSet(this, _enableCopyColumnGroupHeaders, !!settings.copyColumnGroupHeaders);
        _classPrivateFieldSet(this, _enableCopyColumnHeadersOnly, !!settings.copyColumnHeadersOnly);
        this.uiContainer = (_settings$uiContainer = settings.uiContainer) !== null && _settings$uiContainer !== void 0 ? _settings$uiContainer : this.uiContainer;
      }
      this.addHook('afterContextMenuDefaultOptions', function (options) {
        return _this2.onAfterContextMenuDefaultOptions(options);
      });
      this.addHook('afterOnCellMouseUp', function () {
        return _this2.onAfterOnCellMouseUp();
      });
      this.addHook('afterSelectionEnd', function () {
        return _this2.onAfterSelectionEnd();
      });
      this.addHook('beforeKeyDown', function () {
        return _this2.onBeforeKeyDown();
      });
      this.focusableElement = (0, _focusableElement.createElement)(this.uiContainer);
      this.focusableElement.addLocalHook('copy', function (event) {
        return _this2.onCopy(event);
      }).addLocalHook('cut', function (event) {
        return _this2.onCut(event);
      }).addLocalHook('paste', function (event) {
        return _this2.onPaste(event);
      });
      _get(_getPrototypeOf(CopyPaste.prototype), "enablePlugin", this).call(this);
    }

    /**
     * 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)
     */
  }, {
    key: "updatePlugin",
    value: function updatePlugin() {
      this.disablePlugin();
      this.enablePlugin();
      this.getOrCreateFocusableElement();
      _get(_getPrototypeOf(CopyPaste.prototype), "updatePlugin", this).call(this);
    }

    /**
     * Disables the [`CopyPaste`](#copypaste) plugin for your Handsontable instance.
     */
  }, {
    key: "disablePlugin",
    value: function disablePlugin() {
      if (this.focusableElement) {
        (0, _focusableElement.destroyElement)(this.focusableElement);
      }
      _get(_getPrototypeOf(CopyPaste.prototype), "disablePlugin", this).call(this);
    }

    /**
     * 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-all-column-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.
     */
  }, {
    key: "copy",
    value: function copy() {
      var copyMode = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'cells-only';
      _classPrivateFieldSet(this, _copyMode, copyMode);
      _classPrivateFieldSet(this, _isTriggeredByCopy, true);
      this.getOrCreateFocusableElement();
      this.focusableElement.focus();
      this.hot.rootDocument.execCommand('copy');
    }

    /**
     * Copies the contents of the selected cells.
     */
  }, {
    key: "copyCellsOnly",
    value: function copyCellsOnly() {
      this.copy('cells-only');
    }
    /**
     * Copies the contents of column headers that are nearest to the selected cells.
     */
  }, {
    key: "copyColumnHeadersOnly",
    value: function copyColumnHeadersOnly() {
      this.copy('column-headers-only');
    }
    /**
     * Copies the contents of the selected cells and all their related column headers.
     */
  }, {
    key: "copyWithAllColumnHeaders",
    value: function copyWithAllColumnHeaders() {
      this.copy('with-column-group-headers');
    }
    /**
     * Copies the contents of the selected cells and their nearest column headers.
     */
  }, {
    key: "copyWithColumnHeaders",
    value: function copyWithColumnHeaders() {
      this.copy('with-column-headers');
    }

    /**
     * Cuts the contents of the selected cells to the system clipboard.
     */
  }, {
    key: "cut",
    value: function cut() {
      _classPrivateFieldSet(this, _isTriggeredByCut, true);
      this.getOrCreateFocusableElement();
      this.focusableElement.focus();
      this.hot.rootDocument.execCommand('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.
     */
  }, {
    key: "getRangedCopyableData",
    value: function 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`.
     * @returns {Array[]} An array of arrays that will be copied to the clipboard.
     */
  }, {
    key: "getRangedData",
    value: function getRangedData(ranges) {
      var _this3 = this;
      var data = [];
      var _normalizeRanges = (0, _copyableRanges.normalizeRanges)(ranges),
        rows = _normalizeRanges.rows,
        columns = _normalizeRanges.columns;

      // concatenate all rows and columns data defined in ranges into one copyable string
      (0, _array.arrayEach)(rows, function (row) {
        var rowSet = [];
        (0, _array.arrayEach)(columns, function (column) {
          if (row < 0) {
            // `row` as the second argument acts here as the `headerLevel` argument
            rowSet.push(_this3.hot.getColHeader(column, row));
          } else {
            rowSet.push(_this3.hot.getCopyableData(row, column));
          }
        });
        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.
     */
  }, {
    key: "paste",
    value: function paste() {
      var pastableText = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
      var pastableHtml = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : pastableText;
      if (!pastableText && !pastableHtml) {
        return;
      }
      var pasteData = new _pasteEvent.default();
      if (pastableText) {
        pasteData.clipboardData.setData('text/plain', pastableText);
      }
      if (pastableHtml) {
        pasteData.clipboardData.setData('text/html', pastableHtml);
      }
      this.getOrCreateFocusableElement();
      this.onPaste(pasteData);
    }

    /**
     * Prepares copyable text from the cells selection in the invisible textarea.
     */
  }, {
    key: "setCopyableText",
    value: function setCopyableText() {
      var selectionRange = this.hot.getSelectedRangeLast();
      if (!selectionRange) {
        return;
      }
      if (selectionRange.isSingleHeader()) {
        this.copyableRanges = [];
        return;
      }
      _classPrivateFieldGet(this, _copyableRangesFactory).setSelectedRange(selectionRange);
      var groupedRanges = new Map([['headers', null], ['cells', null]]);
      if (_classPrivateFieldGet(this, _copyMode) === 'column-headers-only') {
        groupedRanges.set('headers', _classPrivateFieldGet(this, _copyableRangesFactory).getMostBottomColumnHeadersRange());
      } else {
        if (_classPrivateFieldGet(this, _copyMode) === 'with-column-headers') {
          groupedRanges.set('headers', _classPrivateFieldGet(this, _copyableRangesFactory).getMostBottomColumnHeadersRange());
        } else if (_classPrivateFieldGet(this, _copyMode) === 'with-column-group-headers') {
          groupedRanges.set('headers', _classPrivateFieldGet(this, _copyableRangesFactory).getAllColumnHeadersRange());
        }
        groupedRanges.set('cells', _classPrivateFieldGet(this, _copyableRangesFactory).getCellsRange());
      }
      this.copyableRanges = Array.from(groupedRanges.values()).filter(function (range) {
        return range !== null;
      }).map(function (_ref) {
        var startRow = _ref.startRow,
          startCol = _ref.startCol,
          endRow = _ref.endRow,
          endCol = _ref.endCol;
        return {
          startRow: startRow,
          startCol: startCol,
          endRow: endRow,
          endCol: endCol
        };
      });
      this.copyableRanges = this.hot.runHooks('modifyCopyableRange', this.copyableRanges);
      var cellsRange = groupedRanges.get('cells');
      if (cellsRange !== null && cellsRange.isRangeTrimmed) {
        var startRow = cellsRange.startRow,
          startCol = cellsRange.startCol,
          endRow = cellsRange.endRow,
          endCol = cellsRange.endCol;
        this.hot.runHooks('afterCopyLimit', endRow - startRow + 1, endCol - startCol + 1, this.rowsLimit, this.columnsLimit);
      }
    }

    /**
     * Force focus on editable element.
     *
     * @private
     */
  }, {
    key: "getOrCreateFocusableElement",
    value: function getOrCreateFocusableElement() {
      var _this$hot$getActiveEd;
      var editableElement = (_this$hot$getActiveEd = this.hot.getActiveEditor()) === null || _this$hot$getActiveEd === void 0 ? void 0 : _this$hot$getActiveEd.TEXTAREA;
      if (editableElement) {
        this.focusableElement.setFocusableElement(editableElement);
      } else {
        this.focusableElement.useSecondaryElement();
      }
    }

    /**
     * Verifies if editor exists and is open.
     *
     * @private
     * @returns {boolean}
     */
  }, {
    key: "isEditorOpened",
    value: function isEditorOpened() {
      var _this$hot$getActiveEd2;
      return (_this$hot$getActiveEd2 = this.hot.getActiveEditor()) === null || _this$hot$getActiveEd2 === void 0 ? void 0 : _this$hot$getActiveEd2.isOpened();
    }
  }, {
    key: "populateValues",
    value:
    /**
     * Prepares new values to populate them into datasource.
     *
     * @private
     * @param {Array} inputArray An array of the data to populate.
     * @param {Array} [selection] The selection which indicates from what position the data will be populated.
     * @returns {Array} Range coordinates after populate data.
     */
    function populateValues(inputArray) {
      var selection = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.hot.getSelectedRangeLast();
      if (!inputArray.length) {
        return;
      }
      var populatedRowsLength = inputArray.length;
      var populatedColumnsLength = inputArray[0].length;
      var newRows = [];
      var _selection$getTopStar = selection.getTopStartCorner(),
        startRow = _selection$getTopStar.row,
        startColumn = _selection$getTopStar.col;
      var _selection$getBottomE = selection.getBottomEndCorner(),
        endRowFromSelection = _selection$getBottomE.row,
        endColumnFromSelection = _selection$getBottomE.col;
      var visualRowForPopulatedData = startRow;
      var visualColumnForPopulatedData = startColumn;
      var lastVisualRow = startRow;
      var 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) {
        var _this$hot$getCellMeta = this.hot.getCellMeta(visualRowForPopulatedData, startColumn),
          skipRowOnPaste = _this$hot$getCellMeta.skipRowOnPaste,
          visualRow = _this$hot$getCellMeta.visualRow;
        visualRowForPopulatedData = visualRow + 1;
        if (skipRowOnPaste === true) {
          /* eslint-disable no-continue */
          continue;
        }
        lastVisualRow = visualRow;
        visualColumnForPopulatedData = startColumn;
        var newRow = [];
        var insertedRow = newRows.length % populatedRowsLength;
        while (newRow.length < populatedColumnsLength || visualColumnForPopulatedData <= endColumnFromSelection) {
          var _this$hot$getCellMeta2 = this.hot.getCellMeta(startRow, visualColumnForPopulatedData),
            skipColumnOnPaste = _this$hot$getCellMeta2.skipColumnOnPaste,
            visualCol = _this$hot$getCellMeta2.visualCol;
          visualColumnForPopulatedData = visualCol + 1;
          if (skipColumnOnPaste === true) {
            /* eslint-disable no-continue */
            continue;
          }
          lastVisualColumn = visualCol;
          var insertedColumn = newRow.length % populatedColumnsLength;
          newRow.push(inputArray[insertedRow][insertedColumn]);
        }
        newRows.push(newRow);
      }
      this.hot.populateFromArray(startRow, startColumn, newRows, void 0, void 0, 'CopyPaste.paste', this.pasteMode);
      return [startRow, startColumn, lastVisualRow, lastVisualColumn];
    }

    /**
     * `copy` event callback on textarea element.
     *
     * @param {Event} event ClipboardEvent.
     * @private
     */
  }, {
    key: "onCopy",
    value: function onCopy(event) {
      if (!this.hot.isListening() && !_classPrivateFieldGet(this, _isTriggeredByCopy) || this.isEditorOpened()) {
        return;
      }
      this.setCopyableText();
      _classPrivateFieldSet(this, _isTriggeredByCopy, false);
      var data = this.getRangedData(this.copyableRanges);
      var copiedHeadersCount = _classPrivateMethodGet(this, _countCopiedHeaders, _countCopiedHeaders2).call(this, this.copyableRanges);
      var allowCopying = !!this.hot.runHooks('beforeCopy', data, this.copyableRanges, copiedHeadersCount);
      if (allowCopying) {
        var textPlain = (0, _SheetClip.stringify)(data);
        if (event && event.clipboardData) {
          var textHTML = (0, _parseTable._dataToHTML)(data, this.hot.rootDocument);
          event.clipboardData.setData('text/plain', textPlain);
          event.clipboardData.setData('text/html', [META_HEAD, textHTML].join(''));
        } else if (typeof ClipboardEvent === 'undefined') {
          this.hot.rootWindow.clipboardData.setData('Text', textPlain);
        }
        this.hot.runHooks('afterCopy', data, this.copyableRanges, copiedHeadersCount);
      }
      _classPrivateFieldSet(this, _copyMode, 'cells-only');
      event.preventDefault();
    }

    /**
     * `cut` event callback on textarea element.
     *
     * @param {Event} event ClipboardEvent.
     * @private
     */
  }, {
    key: "onCut",
    value: function onCut(event) {
      if (!this.hot.isListening() && !_classPrivateFieldGet(this, _isTriggeredByCut) || this.isEditorOpened()) {
        return;
      }
      this.setCopyableText();
      _classPrivateFieldSet(this, _isTriggeredByCut, false);
      var rangedData = this.getRangedData(this.copyableRanges);
      var allowCuttingOut = !!this.hot.runHooks('beforeCut', rangedData, this.copyableRanges);
      if (allowCuttingOut) {
        var textPlain = (0, _SheetClip.stringify)(rangedData);
        if (event && event.clipboardData) {
          var textHTML = (0, _parseTable._dataToHTML)(rangedData, this.hot.rootDocument);
          event.clipboardData.setData('text/plain', textPlain);
          event.clipboardData.setData('text/html', [META_HEAD, textHTML].join(''));
        } else if (typeof ClipboardEvent === 'undefined') {
          this.hot.rootWindow.clipboardData.setData('Text', textPlain);
        }
        this.hot.emptySelectedCells('CopyPaste.cut');
        this.hot.runHooks('afterCut', rangedData, this.copyableRanges);
      }
      event.preventDefault();
    }

    /**
     * `paste` event callback on textarea element.
     *
     * @param {Event} event ClipboardEvent or pseudo ClipboardEvent, if paste was called manually.
     * @private
     */
  }, {
    key: "onPaste",
    value: function onPaste(event) {
      if (!this.hot.isListening() || this.isEditorOpened() || !this.hot.getSelected()) {
        return;
      }
      if (event && event.preventDefault) {
        event.preventDefault();
      }
      var pastedData;
      if (event && typeof event.clipboardData !== 'undefined') {
        var textHTML = (0, _string.sanitize)(event.clipboardData.getData('text/html'), {
          ADD_TAGS: ['meta'],
          ADD_ATTR: ['content'],
          FORCE_BODY: true
        });
        if (textHTML && /(<table)|(<TABLE)/g.test(textHTML)) {
          var 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;
      }
      if (this.hot.runHooks('beforePaste', pastedData, this.copyableRanges) === false) {
        return;
      }
      var _this$populateValues = this.populateValues(pastedData),
        _this$populateValues2 = _slicedToArray(_this$populateValues, 4),
        startRow = _this$populateValues2[0],
        startColumn = _this$populateValues2[1],
        endRow = _this$populateValues2[2],
        endColumn = _this$populateValues2[3];
      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);
    }

    /**
     * Add copy and cut options to the Context Menu.
     *
     * @private
     * @param {object} options Contains default added options of the Context Menu.
     */
  }, {
    key: "onAfterContextMenuDefaultOptions",
    value: function onAfterContextMenuDefaultOptions(options) {
      options.items.push({
        name: '---------'
      }, (0, _copy.default)(this));
      if (_classPrivateFieldGet(this, _enableCopyColumnHeaders)) {
        options.items.push((0, _copyWithColumnHeaders.default)(this));
      }
      if (_classPrivateFieldGet(this, _enableCopyColumnGroupHeaders)) {
        options.items.push((0, _copyWithColumnGroupHeaders.default)(this));
      }
      if (_classPrivateFieldGet(this, _enableCopyColumnHeadersOnly)) {
        options.items.push((0, _copyColumnHeadersOnly.default)(this));
      }
      options.items.push((0, _cut.default)(this));
    }

    /**
     * Force focus on focusableElement.
     *
     * @private
     */
  }, {
    key: "onAfterOnCellMouseUp",
    value: function onAfterOnCellMouseUp() {
      // Changing focused element will remove current selection. It's unnecessary in case when we give possibility
      // for fragment selection
      if (!this.hot.isListening() || this.isEditorOpened() || this.hot.getSettings().fragmentSelection) {
        return;
      }
      this.getOrCreateFocusableElement();
      this.focusableElement.focus();
    }

    /**
     * Force focus on focusableElement after end of the selection.
     *
     * @private
     */
  }, {
    key: "onAfterSelectionEnd",
    value: function onAfterSelectionEnd() {
      if (this.isEditorOpened()) {
        return;
      }
      this.getOrCreateFocusableElement();
      if (this.hot.getSettings().fragmentSelection && this.focusableElement.getFocusableElement() !== this.hot.rootDocument.activeElement && (0, _element.getSelectionText)()) {
        return;
      }
      this.setCopyableText();
      this.focusableElement.focus();
    }

    /**
     * `beforeKeyDown` listener to force focus of focusableElement.
     *
     * @private
     */
  }, {
    key: "onBeforeKeyDown",
    value: function onBeforeKeyDown() {
      if (!this.hot.isListening() || this.isEditorOpened()) {
        return;
      }
      var activeElement = this.hot.rootDocument.activeElement;
      var activeEditor = this.hot.getActiveEditor();
      if (!activeEditor || activeElement !== this.focusableElement.getFocusableElement() && activeElement !== activeEditor.select) {
        return;
      }
      this.getOrCreateFocusableElement();
      this.focusableElement.focus();
    }

    /**
     * Destroys the `CopyPaste` plugin instance.
     */
  }, {
    key: "destroy",
    value: function destroy() {
      if (this.focusableElement) {
        (0, _focusableElement.destroyElement)(this.focusableElement);
        this.focusableElement = null;
      }
      _get(_getPrototypeOf(CopyPaste.prototype), "destroy", this).call(this);
    }
  }], [{
    key: "PLUGIN_KEY",
    get: function get() {
      return PLUGIN_KEY;
    }
  }, {
    key: "SETTING_KEYS",
    get: function get() {
      return [PLUGIN_KEY].concat(SETTING_KEYS);
    }
  }, {
    key: "PLUGIN_PRIORITY",
    get: function get() {
      return PLUGIN_PRIORITY;
    }
  }]);
  return CopyPaste;
}(_base.BasePlugin);
exports.CopyPaste = CopyPaste;
function _countCopiedHeaders2(ranges) {
  var _normalizeRanges2 = (0, _copyableRanges.normalizeRanges)(ranges),
    rows = _normalizeRanges2.rows;
  var columnHeadersCount = 0;
  for (var row = 0; row < rows.length; row++) {
    if (rows[row] >= 0) {
      break;
    }
    columnHeadersCount += 1;
  }
  return {
    columnHeadersCount: columnHeadersCount
  };
}