"use strict";

require("core-js/modules/es.array.slice.js");
require("core-js/modules/es.object.freeze.js");
require("core-js/modules/es.symbol.iterator.js");
require("core-js/modules/es.weak-map.js");
require("core-js/modules/es.object.get-own-property-descriptor.js");
exports.__esModule = true;
exports.default = void 0;
require("core-js/modules/es.array.iterator.js");
require("core-js/modules/es.object.to-string.js");
require("core-js/modules/es.set.js");
require("core-js/modules/es.string.iterator.js");
require("core-js/modules/web.dom-collections.iterator.js");
require("core-js/modules/es.array.concat.js");
require("core-js/modules/es.array.from.js");
require("core-js/modules/es.array.includes.js");
require("core-js/modules/es.string.includes.js");
require("core-js/modules/es.number.is-integer.js");
require("core-js/modules/es.number.constructor.js");
require("core-js/modules/web.dom-collections.for-each.js");
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");
var _src = require("./../3rdparty/walkontable/src");
var _highlight = _interopRequireWildcard(require("./highlight/highlight"));
var _range = _interopRequireDefault(require("./range"));
var _object = require("./../helpers/object");
var _mixed = require("./../helpers/mixed");
var _number = require("./../helpers/number");
var _array = require("./../helpers/array");
var _localHooks = _interopRequireDefault(require("./../mixins/localHooks"));
var _transformation = _interopRequireDefault(require("./transformation"));
var _utils = require("./utils");
var _templateLiteralTag = require("./../helpers/templateLiteralTag");
var _templateObject;
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
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 _taggedTemplateLiteral(strings, raw) { if (!raw) { raw = strings.slice(0); } return Object.freeze(Object.defineProperties(strings, { raw: { value: Object.freeze(raw) } })); }
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 _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); }
/**
 * @class Selection
 * @util
 */
var Selection = /*#__PURE__*/function () {
  function Selection(settings, tableProps) {
    var _this = this;
    _classCallCheck(this, Selection);
    /**
     * Handsontable settings instance.
     *
     * @type {GridSettings}
     */
    _defineProperty(this, "settings", void 0);
    /**
     * An additional object with dynamically defined properties which describes table state.
     *
     * @type {object}
     */
    _defineProperty(this, "tableProps", void 0);
    /**
     * The flag which determines if the selection is in progress.
     *
     * @type {boolean}
     */
    _defineProperty(this, "inProgress", false);
    /**
     * Selection data layer (handle visual coordinates).
     *
     * @type {SelectionRange}
     */
    _defineProperty(this, "selectedRange", new _range.default(function (highlight, from, to) {
      return _this.tableProps.createCellRange(highlight, from, to);
    }));
    /**
     * Visualization layer.
     *
     * @type {Highlight}
     */
    _defineProperty(this, "highlight", void 0);
    /**
     * The module for modifying coordinates.
     *
     * @type {Transformation}
     */
    _defineProperty(this, "transformation", void 0);
    /**
     * The collection of the selection layer levels where the whole row was selected using the row header or
     * the corner header.
     *
     * @type {Set<number>}
     */
    _defineProperty(this, "selectedByRowHeader", new Set());
    /**
     * The collection of the selection layer levels where the whole column was selected using the column header or
     * the corner header.
     *
     * @type {Set<number>}
     */
    _defineProperty(this, "selectedByColumnHeader", new Set());
    this.settings = settings;
    this.tableProps = tableProps;
    this.highlight = new _highlight.default({
      headerClassName: settings.currentHeaderClassName,
      activeHeaderClassName: settings.activeHeaderClassName,
      rowClassName: settings.currentRowClassName,
      columnClassName: settings.currentColClassName,
      rowIndexMapper: this.tableProps.rowIndexMapper,
      columnIndexMapper: this.tableProps.columnIndexMapper,
      disabledCellSelection: function disabledCellSelection(row, column) {
        return _this.tableProps.isDisabledCellSelection(row, column);
      },
      cellCornerVisible: function cellCornerVisible() {
        return _this.isCellCornerVisible.apply(_this, arguments);
      },
      areaCornerVisible: function areaCornerVisible() {
        return _this.isAreaCornerVisible.apply(_this, arguments);
      },
      visualToRenderableCoords: function visualToRenderableCoords(coords) {
        return _this.tableProps.visualToRenderableCoords(coords);
      },
      renderableToVisualCoords: function renderableToVisualCoords(coords) {
        return _this.tableProps.renderableToVisualCoords(coords);
      },
      createCellCoords: function createCellCoords(row, column) {
        return _this.tableProps.createCellCoords(row, column);
      },
      createCellRange: function createCellRange(highlight, from, to) {
        return _this.tableProps.createCellRange(highlight, from, to);
      }
    });
    this.transformation = new _transformation.default(this.selectedRange, {
      rowIndexMapper: this.tableProps.rowIndexMapper,
      columnIndexMapper: this.tableProps.columnIndexMapper,
      countRenderableRows: function countRenderableRows() {
        return _this.tableProps.countRenderableRows();
      },
      countRenderableColumns: function countRenderableColumns() {
        return _this.tableProps.countRenderableColumns();
      },
      countRowHeaders: function countRowHeaders() {
        return _this.tableProps.countRowHeaders();
      },
      countColHeaders: function countColHeaders() {
        return _this.tableProps.countColHeaders();
      },
      visualToRenderableCoords: function visualToRenderableCoords(coords) {
        return _this.tableProps.visualToRenderableCoords(coords);
      },
      renderableToVisualCoords: function renderableToVisualCoords(coords) {
        return _this.tableProps.renderableToVisualCoords(coords);
      },
      createCellCoords: function createCellCoords(row, column) {
        return _this.tableProps.createCellCoords(row, column);
      },
      navigableHeaders: function navigableHeaders() {
        return settings.navigableHeaders;
      },
      fixedRowsBottom: function fixedRowsBottom() {
        return settings.fixedRowsBottom;
      },
      minSpareRows: function minSpareRows() {
        return settings.minSpareRows;
      },
      minSpareCols: function minSpareCols() {
        return settings.minSpareCols;
      },
      autoWrapRow: function autoWrapRow() {
        return settings.autoWrapRow;
      },
      autoWrapCol: function autoWrapCol() {
        return settings.autoWrapCol;
      }
    });
    this.transformation.addLocalHook('beforeTransformStart', function () {
      for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
        args[_key] = arguments[_key];
      }
      return _this.runLocalHooks.apply(_this, ['beforeModifyTransformStart'].concat(args));
    });
    this.transformation.addLocalHook('afterTransformStart', function () {
      for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
        args[_key2] = arguments[_key2];
      }
      return _this.runLocalHooks.apply(_this, ['afterModifyTransformStart'].concat(args));
    });
    this.transformation.addLocalHook('beforeTransformEnd', function () {
      for (var _len3 = arguments.length, args = new Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
        args[_key3] = arguments[_key3];
      }
      return _this.runLocalHooks.apply(_this, ['beforeModifyTransformEnd'].concat(args));
    });
    this.transformation.addLocalHook('afterTransformEnd', function () {
      for (var _len4 = arguments.length, args = new Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {
        args[_key4] = arguments[_key4];
      }
      return _this.runLocalHooks.apply(_this, ['afterModifyTransformEnd'].concat(args));
    });
    this.transformation.addLocalHook('insertRowRequire', function () {
      for (var _len5 = arguments.length, args = new Array(_len5), _key5 = 0; _key5 < _len5; _key5++) {
        args[_key5] = arguments[_key5];
      }
      return _this.runLocalHooks.apply(_this, ['insertRowRequire'].concat(args));
    });
    this.transformation.addLocalHook('insertColRequire', function () {
      for (var _len6 = arguments.length, args = new Array(_len6), _key6 = 0; _key6 < _len6; _key6++) {
        args[_key6] = arguments[_key6];
      }
      return _this.runLocalHooks.apply(_this, ['insertColRequire'].concat(args));
    });
  }

  /**
   * Get data layer for current selection.
   *
   * @returns {SelectionRange}
   */
  _createClass(Selection, [{
    key: "getSelectedRange",
    value: function getSelectedRange() {
      return this.selectedRange;
    }

    /**
     * Indicate that selection process began. It sets internally `.inProgress` property to `true`.
     */
  }, {
    key: "begin",
    value: function begin() {
      this.inProgress = true;
    }

    /**
     * Indicate that selection process finished. It sets internally `.inProgress` property to `false`.
     */
  }, {
    key: "finish",
    value: function finish() {
      this.runLocalHooks('afterSelectionFinished', Array.from(this.selectedRange));
      this.inProgress = false;
    }

    /**
     * Check if the process of selecting the cell/cells is in progress.
     *
     * @returns {boolean}
     */
  }, {
    key: "isInProgress",
    value: function isInProgress() {
      return this.inProgress;
    }

    /**
     * Starts selection range on given coordinate object.
     *
     * @param {CellCoords} coords Visual coords.
     * @param {boolean} [multipleSelection] If `true`, selection will be worked in 'multiple' mode. This option works
     *                                      only when 'selectionMode' is set as 'multiple'. If the argument is not defined
     *                                      the default trigger will be used.
     * @param {boolean} [fragment=false] If `true`, the selection will be treated as a partial selection where the
     *                                   `setRangeEnd` method won't be called on every `setRangeStart` call.
     * @param {CellCoords} [highlightCoords] If set, allows changing the coordinates of the highlight/focus cell.
     */
  }, {
    key: "setRangeStart",
    value: function setRangeStart(coords, multipleSelection) {
      var fragment = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
      var highlightCoords = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : coords;
      var isMultipleMode = this.settings.selectionMode === 'multiple';
      var isMultipleSelection = (0, _mixed.isUndefined)(multipleSelection) ? this.tableProps.getShortcutManager().isCtrlPressed() : multipleSelection;
      // We are creating copy. We would like to modify just the start of the selection by below hook. Then original coords
      // should be handled by next methods.
      var coordsClone = coords.clone();
      this.runLocalHooks("beforeSetRangeStart".concat(fragment ? 'Only' : ''), coordsClone);
      if (!isMultipleMode || isMultipleMode && !isMultipleSelection && (0, _mixed.isUndefined)(multipleSelection)) {
        this.selectedRange.clear();
      }
      this.selectedRange.add(coordsClone).current().setHighlight(highlightCoords.clone());
      if (this.getLayerLevel() === 0) {
        this.selectedByRowHeader.clear();
        this.selectedByColumnHeader.clear();
      }
      if (!fragment) {
        this.setRangeEnd(coords);
      }
    }

    /**
     * Starts selection range on given coordinate object.
     *
     * @param {CellCoords} coords Visual coords.
     * @param {boolean} [multipleSelection] If `true`, selection will be worked in 'multiple' mode. This option works
     *                                      only when 'selectionMode' is set as 'multiple'. If the argument is not defined
     *                                      the default trigger will be used.
     * @param {CellCoords} [highlightCoords] If set, allows changing the coordinates of the highlight/focus cell.
     */
  }, {
    key: "setRangeStartOnly",
    value: function setRangeStartOnly(coords, multipleSelection) {
      var highlightCoords = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : coords;
      this.setRangeStart(coords, multipleSelection, true, highlightCoords);
    }

    /**
     * Ends selection range on given coordinate object.
     *
     * @param {CellCoords} coords Visual coords.
     */
  }, {
    key: "setRangeEnd",
    value: function setRangeEnd(coords) {
      if (this.selectedRange.isEmpty()) {
        return;
      }
      var coordsClone = coords.clone();
      var countRows = this.tableProps.countRows();
      var countCols = this.tableProps.countCols();
      var isSingle = this.selectedRange.current().clone().setTo(coords).isSingleHeader();

      // Ignore processing the end range when the header selection starts overlapping the corner and
      // the selection is not a single header highlight.
      if ((countRows > 0 || countCols > 0) && (countRows === 0 && coordsClone.col < 0 && !isSingle || countCols === 0 && coordsClone.row < 0 && !isSingle)) {
        return;
      }
      this.runLocalHooks('beforeSetRangeEnd', coordsClone);
      this.begin();
      var cellRange = this.selectedRange.current();
      if (!this.settings.navigableHeaders) {
        cellRange.highlight.normalize();
      }
      if (this.settings.selectionMode === 'single') {
        cellRange.setFrom(cellRange.highlight);
        cellRange.setTo(cellRange.highlight);
      } else {
        cellRange.setTo(coordsClone);
      }

      // Prevent creating "area" selection that overlaps headers.
      if (countRows > 0 && countCols > 0) {
        if (!this.settings.navigableHeaders || this.settings.navigableHeaders && !cellRange.isSingleHeader()) {
          cellRange.to.normalize();
        }
      }
      var focusHighlight = this.highlight.getFocus();
      focusHighlight.clear();
      if (this.highlight.isEnabledFor(_highlight.FOCUS_TYPE, cellRange.highlight)) {
        focusHighlight.add(this.selectedRange.current().highlight).commit().syncWith(cellRange);
      }
      var layerLevel = this.getLayerLevel();

      // If the next layer level is lower than previous then clear all area and header highlights. This is the
      // indication that the new selection is performing.
      if (layerLevel < this.highlight.layerLevel) {
        (0, _array.arrayEach)(this.highlight.getAreas(), function (highlight) {
          return void highlight.clear();
        });
        (0, _array.arrayEach)(this.highlight.getLayeredAreas(), function (highlight) {
          return void highlight.clear();
        });
        (0, _array.arrayEach)(this.highlight.getRowHeaders(), function (highlight) {
          return void highlight.clear();
        });
        (0, _array.arrayEach)(this.highlight.getColumnHeaders(), function (highlight) {
          return void highlight.clear();
        });
        (0, _array.arrayEach)(this.highlight.getActiveRowHeaders(), function (highlight) {
          return void highlight.clear();
        });
        (0, _array.arrayEach)(this.highlight.getActiveColumnHeaders(), function (highlight) {
          return void highlight.clear();
        });
        (0, _array.arrayEach)(this.highlight.getActiveCornerHeaders(), function (highlight) {
          return void highlight.clear();
        });
        (0, _array.arrayEach)(this.highlight.getRowHighlights(), function (highlight) {
          return void highlight.clear();
        });
        (0, _array.arrayEach)(this.highlight.getColumnHighlights(), function (highlight) {
          return void highlight.clear();
        });
      }
      this.highlight.useLayerLevel(layerLevel);
      var areaHighlight = this.highlight.createArea();
      var layeredAreaHighlight = this.highlight.createLayeredArea();
      var rowHeaderHighlight = this.highlight.createRowHeader();
      var columnHeaderHighlight = this.highlight.createColumnHeader();
      var activeRowHeaderHighlight = this.highlight.createActiveRowHeader();
      var activeColumnHeaderHighlight = this.highlight.createActiveColumnHeader();
      var activeCornerHeaderHighlight = this.highlight.createActiveCornerHeader();
      var rowHighlight = this.highlight.createRowHighlight();
      var columnHighlight = this.highlight.createColumnHighlight();
      areaHighlight.clear();
      layeredAreaHighlight.clear();
      rowHeaderHighlight.clear();
      columnHeaderHighlight.clear();
      activeRowHeaderHighlight.clear();
      activeColumnHeaderHighlight.clear();
      activeCornerHeaderHighlight.clear();
      rowHighlight.clear();
      columnHighlight.clear();
      if (this.highlight.isEnabledFor(_highlight.AREA_TYPE, cellRange.highlight) && (this.isMultiple() || layerLevel >= 1)) {
        areaHighlight.add(cellRange.from).add(cellRange.to).commit();
        layeredAreaHighlight.add(cellRange.from).add(cellRange.to).commit();
        if (layerLevel === 1) {
          // For single cell selection in the same layer, we do not create area selection to prevent blue background.
          // When non-consecutive selection is performed we have to add that missing area selection to the previous layer
          // based on previous coordinates. It only occurs when the previous selection wasn't select multiple cells.
          var previousRange = this.selectedRange.previous();
          this.highlight.useLayerLevel(layerLevel - 1);
          this.highlight.createArea().add(previousRange.from).commit()
          // Range may start with hidden indexes. Commit would not found start point (as we add just the `from` coords).
          .syncWith(previousRange);
          this.highlight.createLayeredArea().add(previousRange.from).commit()
          // Range may start with hidden indexes. Commit would not found start point (as we add just the `from` coords).
          .syncWith(previousRange);
          this.highlight.useLayerLevel(layerLevel);
        }
      }
      if (this.highlight.isEnabledFor(_highlight.HEADER_TYPE, cellRange.highlight)) {
        if (!cellRange.isSingleHeader()) {
          var rowCoordsFrom = this.tableProps.createCellCoords(Math.max(cellRange.from.row, 0), -1);
          var rowCoordsTo = this.tableProps.createCellCoords(cellRange.to.row, -1);
          var columnCoordsFrom = this.tableProps.createCellCoords(-1, Math.max(cellRange.from.col, 0));
          var columnCoordsTo = this.tableProps.createCellCoords(-1, cellRange.to.col);
          if (this.settings.selectionMode === 'single') {
            rowHeaderHighlight.add(rowCoordsFrom).commit();
            columnHeaderHighlight.add(columnCoordsFrom).commit();
            rowHighlight.add(rowCoordsFrom).commit();
            columnHighlight.add(columnCoordsFrom).commit();
          } else {
            rowHeaderHighlight.add(rowCoordsFrom).add(rowCoordsTo).commit();
            columnHeaderHighlight.add(columnCoordsFrom).add(columnCoordsTo).commit();
            rowHighlight.add(rowCoordsFrom).add(rowCoordsTo).commit();
            columnHighlight.add(columnCoordsFrom).add(columnCoordsTo).commit();
          }
        }
        var highlightRowHeaders = this.isEntireRowSelected() && (countCols > 0 && countCols === cellRange.getWidth() || countCols === 0 && (this.isSelectedByRowHeader() || this.isSelectedByCorner()));
        var highlightColumnHeaders = this.isEntireColumnSelected() && (countRows > 0 && countRows === cellRange.getHeight() || countRows === 0 && (this.isSelectedByColumnHeader() || this.isSelectedByCorner()));
        if (highlightRowHeaders) {
          activeRowHeaderHighlight.add(this.tableProps.createCellCoords(Math.max(cellRange.from.row, 0), Math.min(-this.tableProps.countRowHeaders(), -1))).add(this.tableProps.createCellCoords(Math.max(cellRange.to.row, 0), -1)).commit();
        }
        if (highlightColumnHeaders) {
          activeColumnHeaderHighlight.add(this.tableProps.createCellCoords(Math.min(-this.tableProps.countColHeaders(), -1), Math.max(cellRange.from.col, 0))).add(this.tableProps.createCellCoords(-1, Math.max(cellRange.to.col, 0))).commit();
        }
        if (highlightRowHeaders && highlightColumnHeaders) {
          activeCornerHeaderHighlight.add(this.tableProps.createCellCoords(-this.tableProps.countColHeaders(), -this.tableProps.countRowHeaders())).add(this.tableProps.createCellCoords(-1, -1)).commit();
        }
      }
      this.runLocalHooks('afterSetRangeEnd', coords);
    }

    /**
     * Returns information if we have a multiselection. This method check multiselection only on the latest layer of
     * the selection.
     *
     * @returns {boolean}
     */
  }, {
    key: "isMultiple",
    value: function isMultiple() {
      var isMultipleListener = (0, _object.createObjectPropListener)(!this.selectedRange.current().isSingle());
      this.runLocalHooks('afterIsMultipleSelection', isMultipleListener);
      return isMultipleListener.value;
    }

    /**
     * Selects cell relative to the current cell (if possible).
     *
     * @param {number} rowDelta Rows number to move, value can be passed as negative number.
     * @param {number} colDelta Columns number to move, value can be passed as negative number.
     * @param {boolean} [createMissingRecords=false] If `true` the new rows/columns will be created if necessary.
     * Otherwise, row/column will be created according to `minSpareRows/minSpareCols` settings of Handsontable.
     */
  }, {
    key: "transformStart",
    value: function transformStart(rowDelta, colDelta) {
      var createMissingRecords = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
      this.setRangeStart(this.transformation.transformStart(rowDelta, colDelta, createMissingRecords));
    }

    /**
     * Sets selection end cell relative to the current selection end cell (if possible).
     *
     * @param {number} rowDelta Rows number to move, value can be passed as negative number.
     * @param {number} colDelta Columns number to move, value can be passed as negative number.
     */
  }, {
    key: "transformEnd",
    value: function transformEnd(rowDelta, colDelta) {
      this.setRangeEnd(this.transformation.transformEnd(rowDelta, colDelta));
    }

    /**
     * Returns currently used layer level.
     *
     * @returns {number} Returns layer level starting from 0. If no selection was added to the table -1 is returned.
     */
  }, {
    key: "getLayerLevel",
    value: function getLayerLevel() {
      return this.selectedRange.size() - 1;
    }

    /**
     * Returns `true` if currently there is a selection on the screen, `false` otherwise.
     *
     * @returns {boolean}
     */
  }, {
    key: "isSelected",
    value: function isSelected() {
      return !this.selectedRange.isEmpty();
    }

    /**
     * Returns `true` if the selection was applied by clicking to the row header. If the `layerLevel`
     * argument is passed then only that layer will be checked. Otherwise, it checks if any row header
     * was clicked on any selection layer level.
     *
     * @param {number} [layerLevel=this.getLayerLevel()] Selection layer level to check.
     * @returns {boolean}
     */
  }, {
    key: "isSelectedByRowHeader",
    value: function isSelectedByRowHeader() {
      var layerLevel = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.getLayerLevel();
      return !this.isSelectedByCorner(layerLevel) && (layerLevel === -1 ? this.selectedByRowHeader.size > 0 : this.selectedByRowHeader.has(layerLevel));
    }

    /**
     * Returns `true` if the selection consists of entire rows (including their headers). If the `layerLevel`
     * argument is passed then only that layer will be checked. Otherwise, it checks the selection for all layers.
     *
     * @param {number} [layerLevel=this.getLayerLevel()] Selection layer level to check.
     * @returns {boolean}
     */
  }, {
    key: "isEntireRowSelected",
    value: function isEntireRowSelected() {
      var _this2 = this;
      var layerLevel = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.getLayerLevel();
      var tester = function tester(range) {
        var _range$getOuterTopSta = range.getOuterTopStartCorner(),
          col = _range$getOuterTopSta.col;
        var rowHeaders = _this2.tableProps.countRowHeaders();
        var countCols = _this2.tableProps.countCols();
        return (rowHeaders > 0 && col < 0 || rowHeaders === 0) && range.getWidth() === countCols;
      };
      if (layerLevel === -1) {
        return Array.from(this.selectedRange).some(function (range) {
          return tester(range);
        });
      }
      var range = this.selectedRange.peekByIndex(layerLevel);
      return range ? tester(range) : false;
    }

    /**
     * Returns `true` if the selection was applied by clicking to the column header. If the `layerLevel`
     * argument is passed then only that layer will be checked. Otherwise, it checks if any column header
     * was clicked on any selection layer level.
     *
     * @param {number} [layerLevel=this.getLayerLevel()] Selection layer level to check.
     * @returns {boolean}
     */
  }, {
    key: "isSelectedByColumnHeader",
    value: function isSelectedByColumnHeader() {
      var layerLevel = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.getLayerLevel();
      return !this.isSelectedByCorner() && (layerLevel === -1 ? this.selectedByColumnHeader.size > 0 : this.selectedByColumnHeader.has(layerLevel));
    }

    /**
     * Returns `true` if the selection consists of entire columns (including their headers). If the `layerLevel`
     * argument is passed then only that layer will be checked. Otherwise, it checks the selection for all layers.
     *
     * @param {number} [layerLevel=this.getLayerLevel()] Selection layer level to check.
     * @returns {boolean}
     */
  }, {
    key: "isEntireColumnSelected",
    value: function isEntireColumnSelected() {
      var _this3 = this;
      var layerLevel = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.getLayerLevel();
      var tester = function tester(range) {
        var _range$getOuterTopSta2 = range.getOuterTopStartCorner(),
          row = _range$getOuterTopSta2.row;
        var colHeaders = _this3.tableProps.countColHeaders();
        var countRows = _this3.tableProps.countRows();
        return (colHeaders > 0 && row < 0 || colHeaders === 0) && range.getHeight() === countRows;
      };
      if (layerLevel === -1) {
        return Array.from(this.selectedRange).some(function (range) {
          return tester(range);
        });
      }
      var range = this.selectedRange.peekByIndex(layerLevel);
      return range ? tester(range) : false;
    }

    /**
     * Returns `true` if the selection was applied by clicking on the row or column header on any layer level.
     *
     * @returns {boolean}
     */
  }, {
    key: "isSelectedByAnyHeader",
    value: function isSelectedByAnyHeader() {
      return this.isSelectedByRowHeader(-1) || this.isSelectedByColumnHeader(-1) || this.isSelectedByCorner();
    }

    /**
     * Returns `true` if the selection was applied by clicking on the left-top corner overlay.
     *
     * @returns {boolean}
     */
  }, {
    key: "isSelectedByCorner",
    value: function isSelectedByCorner() {
      return this.selectedByColumnHeader.has(this.getLayerLevel()) && this.selectedByRowHeader.has(this.getLayerLevel());
    }

    /**
     * Returns `true` if coords is within selection coords. This method iterates through all selection layers to check if
     * the coords object is within selection range.
     *
     * @param {CellCoords} coords The CellCoords instance with defined visual coordinates.
     * @returns {boolean}
     */
  }, {
    key: "inInSelection",
    value: function inInSelection(coords) {
      return this.selectedRange.includes(coords);
    }

    /**
     * Returns `true` if the cell corner should be visible.
     *
     * @private
     * @returns {boolean} `true` if the corner element has to be visible, `false` otherwise.
     */
  }, {
    key: "isCellCornerVisible",
    value: function isCellCornerVisible() {
      return this.settings.fillHandle && !this.tableProps.isEditorOpened() && !this.isMultiple();
    }

    /**
     * Returns `true` if the area corner should be visible.
     *
     * @param {number} layerLevel The layer level.
     * @returns {boolean} `true` if the corner element has to be visible, `false` otherwise.
     */
  }, {
    key: "isAreaCornerVisible",
    value: function isAreaCornerVisible(layerLevel) {
      if (Number.isInteger(layerLevel) && layerLevel !== this.getLayerLevel()) {
        return false;
      }
      return this.settings.fillHandle && !this.tableProps.isEditorOpened() && this.isMultiple();
    }

    /**
     * Clear the selection by resetting the collected ranges and highlights.
     */
  }, {
    key: "clear",
    value: function clear() {
      // TODO: collections selectedByColumnHeader and selectedByRowHeader should be clear too.
      this.selectedRange.clear();
      this.highlight.clear();
    }

    /**
     * Deselects all selected cells.
     */
  }, {
    key: "deselect",
    value: function deselect() {
      if (!this.isSelected()) {
        return;
      }
      this.inProgress = false;
      this.clear();
      this.runLocalHooks('afterDeselect');
    }

    /**
     * Selects all cells and headers.
     *
     * @param {boolean} [includeRowHeaders=false] `true` If the selection should include the row headers,
     * `false` otherwise.
     * @param {boolean} [includeColumnHeaders=false] `true` If the selection should include the column
     * headers, `false` otherwise.
     * @param {{row: number, col: number}} [focusPosition] The argument allows changing the cell/header
     * focus position. The value takes an object with a `row` and `col` properties from -N to N, where
     * negative values point to the headers and positive values point to the cell range.
     */
  }, {
    key: "selectAll",
    value: function selectAll() {
      var includeRowHeaders = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
      var includeColumnHeaders = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
      var focusPosition = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {
        row: this.tableProps.countColHeaders() > 0 ? -this.tableProps.countColHeaders() : 0,
        col: this.tableProps.countRowHeaders() > 0 ? -this.tableProps.countRowHeaders() : 0
      };
      var nrOfRows = this.tableProps.countRows();
      var nrOfColumns = this.tableProps.countCols();
      var countRowHeaders = this.tableProps.countRowHeaders();
      var countColHeaders = this.tableProps.countColHeaders();
      var rowFrom = includeRowHeaders ? -countColHeaders : 0;
      var columnFrom = includeColumnHeaders ? -countRowHeaders : 0;

      // We can't select cells when there is no data.
      if (rowFrom === 0 && columnFrom === 0 && (nrOfRows === 0 || nrOfColumns === 0)) {
        return;
      }
      var highlightRow = Number.isInteger(focusPosition.row) ? focusPosition.row : 0;
      var highlightColumn = Number.isInteger(focusPosition.col) ? focusPosition.col : 0;
      var startCoords = this.tableProps.createCellCoords(rowFrom, columnFrom);
      var highlight = this.tableProps.createCellCoords((0, _number.clamp)(highlightRow, rowFrom, nrOfRows - 1), (0, _number.clamp)(highlightColumn, columnFrom, nrOfColumns - 1));
      var endCoords = this.tableProps.createCellCoords(nrOfRows - 1, nrOfColumns - 1);
      this.clear();
      this.setRangeStartOnly(startCoords, void 0, highlight);
      if (columnFrom < 0) {
        this.selectedByRowHeader.add(this.getLayerLevel());
      }
      if (rowFrom < 0) {
        this.selectedByColumnHeader.add(this.getLayerLevel());
      }
      this.setRangeEnd(endCoords);
      this.finish();
    }

    /**
     * Make multiple, non-contiguous selection specified by `row` and `column` values or a range of cells
     * finishing at `endRow`, `endColumn`. The method supports two input formats, first as an array of arrays such
     * as `[[rowStart, columnStart, rowEnd, columnEnd]]` and second format as an array of CellRange objects.
     * If the passed ranges have another format the exception will be thrown.
     *
     * @param {Array[]|CellRange[]} selectionRanges The coordinates which define what the cells should be selected.
     * @returns {boolean} Returns `true` if selection was successful, `false` otherwise.
     */
  }, {
    key: "selectCells",
    value: function selectCells(selectionRanges) {
      var _this4 = this;
      var selectionType = (0, _utils.detectSelectionType)(selectionRanges);
      if (selectionType === _utils.SELECTION_TYPE_EMPTY) {
        return false;
      } else if (selectionType === _utils.SELECTION_TYPE_UNRECOGNIZED) {
        throw new Error((0, _templateLiteralTag.toSingleLine)(_templateObject || (_templateObject = _taggedTemplateLiteral(["Unsupported format of the selection ranges was passed. To select cells pass \n        the coordinates as an array of arrays ([[rowStart, columnStart/columnPropStart, rowEnd, \n        columnEnd/columnPropEnd]]) or as an array of CellRange objects."], ["Unsupported format of the selection ranges was passed. To select cells pass\\x20\n        the coordinates as an array of arrays ([[rowStart, columnStart/columnPropStart, rowEnd,\\x20\n        columnEnd/columnPropEnd]]) or as an array of CellRange objects."]))));
      }
      var selectionSchemaNormalizer = (0, _utils.normalizeSelectionFactory)(selectionType, {
        propToCol: function propToCol(prop) {
          return _this4.tableProps.propToCol(prop);
        },
        keepDirection: true
      });
      var navigableHeaders = this.settings.navigableHeaders;
      var tableParams = {
        countRows: this.tableProps.countRows(),
        countCols: this.tableProps.countCols(),
        countRowHeaders: navigableHeaders ? this.tableProps.countRowHeaders() : 0,
        countColHeaders: navigableHeaders ? this.tableProps.countColHeaders() : 0
      };

      // Check if every layer of the coordinates are valid.
      var isValid = !selectionRanges.some(function (selection) {
        var cellRange = selectionSchemaNormalizer(selection);
        var rangeValidity = cellRange.isValid(tableParams);
        return !(rangeValidity && !cellRange.containsHeaders() || rangeValidity && cellRange.containsHeaders() && cellRange.isSingleHeader());
      });
      if (isValid) {
        this.clear();
        (0, _array.arrayEach)(selectionRanges, function (selection) {
          var _selectionSchemaNorma = selectionSchemaNormalizer(selection),
            from = _selectionSchemaNorma.from,
            to = _selectionSchemaNorma.to;
          _this4.setRangeStartOnly(from.clone(), false);
          _this4.setRangeEnd(to.clone());
          _this4.finish();
        });
      }
      return isValid;
    }

    /**
     * Select column specified by `startColumn` visual index or column property or a range of columns finishing at
     * `endColumn`.
     *
     * @param {number|string} startColumn Visual column index or column property from which the selection starts.
     * @param {number|string} [endColumn] Visual column index or column property from to the selection finishes.
     * @param {number} [focusPosition=0] The argument allows changing the cell/header focus position.
     *                                   The value can take visual row index from -N to N, where negative values
     *                                   point to the headers and positive values point to the cell range.
     * @returns {boolean} Returns `true` if selection was successful, `false` otherwise.
     */
  }, {
    key: "selectColumns",
    value: function selectColumns(startColumn) {
      var endColumn = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : startColumn;
      var focusPosition = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
      var start = typeof startColumn === 'string' ? this.tableProps.propToCol(startColumn) : startColumn;
      var end = typeof endColumn === 'string' ? this.tableProps.propToCol(endColumn) : endColumn;
      var countRows = this.tableProps.countRows();
      var countCols = this.tableProps.countCols();
      var countColHeaders = this.tableProps.countColHeaders();
      var columnHeaderLastIndex = countColHeaders === 0 ? 0 : -countColHeaders;
      var fromCoords = new _src.CellCoords(columnHeaderLastIndex, start);
      var toCoords = new _src.CellCoords(countRows - 1, end);
      var isValid = new _src.CellRange(fromCoords, fromCoords, toCoords).isValid({
        countRows: countRows,
        countCols: countCols,
        countRowHeaders: 0,
        countColHeaders: countColHeaders
      });
      if (isValid) {
        var fromRow = countColHeaders === 0 ? 0 : (0, _number.clamp)(focusPosition, columnHeaderLastIndex, -1);
        var toRow = countRows - 1;
        var from = this.tableProps.createCellCoords(fromRow, start);
        var to = this.tableProps.createCellCoords(toRow, end);
        var highlight = this.tableProps.createCellCoords((0, _number.clamp)(focusPosition, columnHeaderLastIndex, countRows - 1), start);
        this.runLocalHooks('beforeSelectColumns', from, to, highlight);

        // disallow modifying row axis for that hooks
        from.row = fromRow;
        to.row = toRow;
        this.setRangeStartOnly(from, void 0, highlight);
        this.selectedByColumnHeader.add(this.getLayerLevel());
        this.setRangeEnd(to);
        this.runLocalHooks('afterSelectColumns', from, to, highlight);
        this.finish();
      }
      return isValid;
    }

    /**
     * Select row specified by `startRow` visual index or a range of rows finishing at `endRow`.
     *
     * @param {number} startRow Visual row index from which the selection starts.
     * @param {number} [endRow] Visual row index from to the selection finishes.
     * @param {number} [focusPosition=0] The argument allows changing the cell/header focus position.
     *                                   The value can take visual column index from -N to N, where negative values
     *                                   point to the headers and positive values point to the cell range.
     * @returns {boolean} Returns `true` if selection was successful, `false` otherwise.
     */
  }, {
    key: "selectRows",
    value: function selectRows(startRow) {
      var endRow = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : startRow;
      var focusPosition = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
      var countRows = this.tableProps.countRows();
      var countCols = this.tableProps.countCols();
      var countRowHeaders = this.tableProps.countRowHeaders();
      var rowHeaderLastIndex = countRowHeaders === 0 ? 0 : -countRowHeaders;
      var fromCoords = new _src.CellCoords(startRow, rowHeaderLastIndex);
      var toCoords = new _src.CellCoords(endRow, countCols - 1);
      var isValid = new _src.CellRange(fromCoords, fromCoords, toCoords).isValid({
        countRows: countRows,
        countCols: countCols,
        countRowHeaders: countRowHeaders,
        countColHeaders: 0
      });
      if (isValid) {
        var fromColumn = countRowHeaders === 0 ? 0 : (0, _number.clamp)(focusPosition, rowHeaderLastIndex, -1);
        var toColumn = countCols - 1;
        var from = this.tableProps.createCellCoords(startRow, fromColumn);
        var to = this.tableProps.createCellCoords(endRow, toColumn);
        var highlight = this.tableProps.createCellCoords(startRow, (0, _number.clamp)(focusPosition, rowHeaderLastIndex, countCols - 1));
        this.runLocalHooks('beforeSelectRows', from, to, highlight);

        // disallow modifying column axis for that hooks
        from.col = fromColumn;
        to.col = toColumn;
        this.setRangeStartOnly(from, void 0, highlight);
        this.selectedByRowHeader.add(this.getLayerLevel());
        this.setRangeEnd(to);
        this.runLocalHooks('afterSelectRows', from, to, highlight);
        this.finish();
      }
      return isValid;
    }

    /**
     * Rewrite the rendered state of the selection as visual selection may have a new representation in the DOM.
     */
  }, {
    key: "refresh",
    value: function refresh() {
      var customSelections = this.highlight.getCustomSelections();
      customSelections.forEach(function (customSelection) {
        customSelection.commit();
      });
      if (!this.isSelected()) {
        return;
      }
      var focusHighlight = this.highlight.getFocus();
      var currentLayer = this.getLayerLevel();
      focusHighlight.commit().syncWith(this.selectedRange.current());

      // Rewriting rendered ranges going through all layers.
      for (var layerLevel = 0; layerLevel < this.selectedRange.size(); layerLevel += 1) {
        this.highlight.useLayerLevel(layerLevel);
        var areaHighlight = this.highlight.createArea();
        var areaLayeredHighlight = this.highlight.createLayeredArea();
        var rowHeaderHighlight = this.highlight.createRowHeader();
        var columnHeaderHighlight = this.highlight.createColumnHeader();
        var activeRowHeaderHighlight = this.highlight.createActiveRowHeader();
        var activeColumnHeaderHighlight = this.highlight.createActiveColumnHeader();
        var activeCornerHeaderHighlight = this.highlight.createActiveCornerHeader();
        var rowHighlight = this.highlight.createRowHighlight();
        var columnHighlight = this.highlight.createColumnHighlight();
        areaHighlight.commit();
        areaLayeredHighlight.commit();
        rowHeaderHighlight.commit();
        columnHeaderHighlight.commit();
        activeRowHeaderHighlight.commit();
        activeColumnHeaderHighlight.commit();
        activeCornerHeaderHighlight.commit();
        rowHighlight.commit();
        columnHighlight.commit();
      }

      // Reverting starting layer for the Highlight.
      this.highlight.useLayerLevel(currentLayer);
    }
  }]);
  return Selection;
}();
(0, _object.mixin)(Selection, _localHooks.default);
var _default = Selection;
exports.default = _default;