"use strict";

exports.__esModule = true;
require("core-js/modules/es.error.cause.js");
var _console = require("./helpers/console");
var _element = require("./helpers/dom/element");
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 _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; } }
/**
 * Possible focus modes.
 * - CELL - The browser's focus stays on the lastly selected cell element.
 * - MIXED - The browser's focus switches from the lastly selected cell element to the currently active editor's
 * `TEXTAREA` element after a delay defined in the manager.
 *
 * @type {{CELL: string, MIXED: string}}
 */
const FOCUS_MODES = Object.freeze({
  CELL: 'cell',
  MIXED: 'mixed'
});

/**
 * Manages the browser's focus in the table.
 */
var _hot = /*#__PURE__*/new WeakMap();
var _focusMode = /*#__PURE__*/new WeakMap();
var _refocusDelay = /*#__PURE__*/new WeakMap();
var _refocusElementGetter = /*#__PURE__*/new WeakMap();
var _getCurrentHighlightCoords = /*#__PURE__*/new WeakSet();
var _getSelectedCell = /*#__PURE__*/new WeakSet();
var _manageFocus = /*#__PURE__*/new WeakSet();
var _onUpdateSettings = /*#__PURE__*/new WeakSet();
class FocusManager {
  constructor(hotInstance) {
    var _this = this;
    /**
     * Update the manager configuration after calling `updateSettings`.
     *
     * @private
     * @param {object} newSettings The new settings passed to the `updateSettings` method.
     */
    _classPrivateMethodInitSpec(this, _onUpdateSettings);
    /**
     * Manage the browser's focus after cell selection.
     *
     * @private
     */
    _classPrivateMethodInitSpec(this, _manageFocus);
    /**
     * Get and return the currently selected and highlighted cell/header element.
     *
     * @private
     * @returns {HTMLTableCellElement}
     */
    _classPrivateMethodInitSpec(this, _getSelectedCell);
    /**
     * Get the coordinates of the highlight of the currently selected cell/header.
     *
     * @returns {CellCoords}
     */
    _classPrivateMethodInitSpec(this, _getCurrentHighlightCoords);
    /**
     * The Handsontable instance.
     */
    _classPrivateFieldInitSpec(this, _hot, {
      writable: true,
      value: void 0
    });
    /**
     * The currently enabled focus mode.
     * Can be either:
     *
     * - 'cell' - The browser's focus stays on the lastly selected cell element.
     * - 'mixed' - The browser's focus switches from the lastly selected cell element to the currently active editor's
     * `TEXTAREA` element after a delay defined in the manager.
     *
     * @type {string}
     */
    _classPrivateFieldInitSpec(this, _focusMode, {
      writable: true,
      value: void 0
    });
    /**
     * The delay after which the focus switches from the lastly selected cell to the active editor's `TEXTAREA`
     * element if the focus mode is set to 'mixed'.
     *
     * @type {number}
     */
    _classPrivateFieldInitSpec(this, _refocusDelay, {
      writable: true,
      value: 50
    });
    /**
     * Getter function for the element to be used when refocusing the browser after a delay. If `null`, the active
     * editor's `TEXTAREA` element will be used.
     *
     * @type {null|Function}
     */
    _classPrivateFieldInitSpec(this, _refocusElementGetter, {
      writable: true,
      value: null
    });
    const hotSettings = hotInstance.getSettings();
    _classPrivateFieldSet(this, _hot, hotInstance);
    _classPrivateFieldSet(this, _focusMode, hotSettings.imeFastEdit ? FOCUS_MODES.MIXED : FOCUS_MODES.CELL);
    _classPrivateFieldGet(this, _hot).addHook('afterUpdateSettings', function () {
      for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
        args[_key] = arguments[_key];
      }
      return _classPrivateMethodGet(_this, _onUpdateSettings, _onUpdateSettings2).call(_this, ...args);
    });
    _classPrivateFieldGet(this, _hot).addHook('afterSelection', function () {
      for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
        args[_key2] = arguments[_key2];
      }
      return _classPrivateMethodGet(_this, _manageFocus, _manageFocus2).call(_this, ...args);
    });
  }

  /**
   * Get the current focus mode.
   *
   * @returns {'cell' | 'mixed'}
   */
  getFocusMode() {
    return _classPrivateFieldGet(this, _focusMode);
  }

  /**
   * Set the focus mode.
   *
   * @param {'cell' | 'mixed'} focusMode The new focus mode.
   */
  setFocusMode(focusMode) {
    if (Object.values(FOCUS_MODES).includes(focusMode)) {
      _classPrivateFieldSet(this, _focusMode, focusMode);
    } else {
      (0, _console.warn)(`"${focusMode}" is not a valid focus mode.`);
    }
  }

  /**
   * Get the delay after which the focus will change from the cell elements to the active editor's `TEXTAREA`
   * element if the focus mode is set to 'mixed'.
   *
   * @returns {number} Delay in milliseconds.
   */
  getRefocusDelay() {
    return _classPrivateFieldGet(this, _refocusDelay);
  }

  /**
   * Set the delay after which the focus will change from the cell elements to the active editor's `TEXTAREA`
   * element if the focus mode is set to 'mixed'.
   *
   * @param {number} delay Delay in milliseconds.
   */
  setRefocusDelay(delay) {
    _classPrivateFieldSet(this, _refocusDelay, delay);
  }

  /**
   * Set the function to be used as the "refocus element" getter. It should return a focusable HTML element.
   *
   * @param {Function} getRefocusElementFunction The refocus element getter.
   */
  setRefocusElementGetter(getRefocusElementFunction) {
    _classPrivateFieldSet(this, _refocusElementGetter, getRefocusElementFunction);
  }

  /**
   * Get the element to be used when refocusing the browser after a delay in case of the focus mode being 'mixed'.
   *
   * @returns {HTMLTextAreaElement|HTMLElement|undefined}
   */
  getRefocusElement() {
    if (typeof _classPrivateFieldGet(this, _refocusElementGetter) === 'function') {
      return _classPrivateFieldGet(this, _refocusElementGetter).call(this);
    } else {
      var _classPrivateFieldGet2;
      return (_classPrivateFieldGet2 = _classPrivateFieldGet(this, _hot).getActiveEditor()) === null || _classPrivateFieldGet2 === void 0 ? void 0 : _classPrivateFieldGet2.TEXTAREA;
    }
  }

  /**
   * Set the browser's focus to the highlighted cell of the last selection.
   *
   * @param {HTMLTableCellElement} [selectedCell] The highlighted cell/header element.
   */
  focusOnHighlightedCell(selectedCell) {
    var _classPrivateFieldGet3;
    const currentHighlightCoords = _classPrivateMethodGet(this, _getCurrentHighlightCoords, _getCurrentHighlightCoords2).call(this);
    const currentlySelectedHighlight = selectedCell || _classPrivateMethodGet(this, _getSelectedCell, _getSelectedCell2).call(this);
    let elementToBeFocused = _classPrivateFieldGet(this, _hot).runHooks('modifyFocusedElement', currentHighlightCoords.row, currentHighlightCoords.col, currentlySelectedHighlight);
    if (!(elementToBeFocused instanceof HTMLElement)) {
      elementToBeFocused = currentlySelectedHighlight;
    }
    if (elementToBeFocused && !((_classPrivateFieldGet3 = _classPrivateFieldGet(this, _hot).getActiveEditor()) !== null && _classPrivateFieldGet3 !== void 0 && _classPrivateFieldGet3.isOpened())) {
      elementToBeFocused.focus({
        preventScroll: true
      });
    }
  }

  /**
   * Set the focus to the active editor's `TEXTAREA` element after the provided delay. If no delay is provided, it
   * will be taken from the manager's configuration.
   *
   * @param {number} delay Delay in milliseconds.
   */
  refocusToEditorTextarea() {
    let delay = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : _classPrivateFieldGet(this, _refocusDelay);
    const refocusElement = this.getRefocusElement();

    // Re-focus on the editor's `TEXTAREA` element (or a predefined element) if the `imeFastEdit` option is enabled.
    if (_classPrivateFieldGet(this, _hot).getSettings().imeFastEdit && !!refocusElement) {
      _classPrivateFieldGet(this, _hot)._registerTimeout(() => {
        refocusElement.select();
      }, delay);
    }
  }
}
exports.FocusManager = FocusManager;
function _getCurrentHighlightCoords2() {
  const lastSelectedRange = _classPrivateFieldGet(this, _hot).getSelectedRangeLast();
  return lastSelectedRange.highlight;
}
function _getSelectedCell2() {
  const selectedCellCoords = _classPrivateMethodGet(this, _getCurrentHighlightCoords, _getCurrentHighlightCoords2).call(this);
  return _classPrivateFieldGet(this, _hot).getCell(selectedCellCoords.row, selectedCellCoords.col, true);
}
function _manageFocus2() {
  const selectedCell = _classPrivateMethodGet(this, _getSelectedCell, _getSelectedCell2).call(this);
  const {
    activeElement
  } = _classPrivateFieldGet(this, _hot).rootDocument;

  // Blurring the `activeElement` removes the unwanted border around the focusable element (#6877)
  // and resets the `document.activeElement` property. The blurring should happen only when the
  // previously selected input element has not belonged to the Handsontable editor. If blurring is
  // triggered for all elements, there is a problem with the disappearing IME editor (#9672).
  if (activeElement && (0, _element.isOutsideInput)(activeElement)) {
    activeElement.blur();
  }
  this.focusOnHighlightedCell(selectedCell);
  if (this.getFocusMode() === FOCUS_MODES.MIXED && selectedCell.nodeName === 'TD') {
    _classPrivateFieldGet(this, _hot).addHookOnce('afterSelectionEnd', () => {
      this.refocusToEditorTextarea();
    });
  }
}
function _onUpdateSettings2(newSettings) {
  if (newSettings.imeFastEdit && this.getFocusMode() !== FOCUS_MODES.MIXED) {
    this.setFocusMode(FOCUS_MODES.MIXED);
  } else if (!newSettings.imeFastEdit && this.getFocusMode() !== FOCUS_MODES.CELL) {
    this.setFocusMode(FOCUS_MODES.CELL);
  }
}