"use strict";
Object.defineProperty(exports, "__esModule", {
    value: true
});
Object.defineProperty(exports, "default", {
    enumerable: true,
    get: function() {
        return _default;
    }
});
const _element = require("../../../helpers/dom/element");
const _function = require("../../../helpers/function");
const _feature = require("../../../helpers/feature");
const _browser = require("../../../helpers/browser");
const _mixed = require("../../../helpers/mixed");
function _check_private_redeclaration(obj, privateCollection) {
    if (privateCollection.has(obj)) {
        throw new TypeError("Cannot initialize the same private elements twice on an object");
    }
}
function _class_apply_descriptor_get(receiver, descriptor) {
    if (descriptor.get) {
        return descriptor.get.call(receiver);
    }
    return descriptor.value;
}
function _class_apply_descriptor_set(receiver, descriptor, value) {
    if (descriptor.set) {
        descriptor.set.call(receiver, value);
    } else {
        if (!descriptor.writable) {
            throw new TypeError("attempted to set read only private field");
        }
        descriptor.value = value;
    }
}
function _class_extract_field_descriptor(receiver, privateMap, action) {
    if (!privateMap.has(receiver)) {
        throw new TypeError("attempted to " + action + " private field on non-instance");
    }
    return privateMap.get(receiver);
}
function _class_private_field_get(receiver, privateMap) {
    var descriptor = _class_extract_field_descriptor(receiver, privateMap, "get");
    return _class_apply_descriptor_get(receiver, descriptor);
}
function _class_private_field_init(obj, privateMap, value) {
    _check_private_redeclaration(obj, privateMap);
    privateMap.set(obj, value);
}
function _class_private_field_set(receiver, privateMap, value) {
    var descriptor = _class_extract_field_descriptor(receiver, privateMap, "set");
    _class_apply_descriptor_set(receiver, descriptor, value);
    return value;
}
function _class_private_method_get(receiver, privateSet, fn) {
    if (!privateSet.has(receiver)) {
        throw new TypeError("attempted to get private field on non-instance");
    }
    return fn;
}
function _class_private_method_init(obj, privateSet) {
    _check_private_redeclaration(obj, privateSet);
    privateSet.add(obj);
}
const LONG_PRESS_DELAY = 500;
const LONG_PRESS_MOVE_THRESHOLD = 10;
var _wtSettings = /*#__PURE__*/ new WeakMap(), _domBindings = /*#__PURE__*/ new WeakMap(), _wtTable = /*#__PURE__*/ new WeakMap(), _selectionManager = /*#__PURE__*/ new WeakMap(), _parent = /*#__PURE__*/ new WeakMap(), /**
   * Instance of {@link EventManager}.
   *
   * @type {EventManager}
   */ _eventManager = /*#__PURE__*/ new WeakMap(), /**
   * Should be use only for passing face called external origin methods, like registered event listeners.
   * It provides backward compatibility by getting instance facade.
   *
   * @todo Consider about removing this from Event class, because it make relationship into facade (implicit circular
   *   dependency).
   * @todo Con. Maybe passing listener caller as an ioc from faced resolves this issue. To rethink later.
   *
   * @type {FacadeGetter}
   */ _facadeGetter = /*#__PURE__*/ new WeakMap(), /**
   * @type {boolean}
   */ _selectedCellBeforeTouchEnd = /*#__PURE__*/ new WeakMap(), /**
   * @type {number[]}
   */ _dblClickTimeout = /*#__PURE__*/ new WeakMap(), /**
   * @type {number[]}
   */ _dblClickOrigin = /*#__PURE__*/ new WeakMap(), /**
   * Timer ID for the long-press gesture detection.
   *
   * @type {number|null}
   */ _longPressTimeout = /*#__PURE__*/ new WeakMap(), /**
   * Starting coordinates of a touch gesture (used to detect movement that cancels long-press).
   *
   * @type {{ x: number, y: number }|null}
   */ _touchStartCoords = /*#__PURE__*/ new WeakMap(), /**
   * Starts the long-press timer. When the timer fires, a synthetic `contextmenu` event is
   * dispatched on the original touch target so that the existing contextmenu hook chain
   * (and ContextMenu plugin) work without changes.
   *
   * @private
   * @param {TouchEvent} event The original `touchstart` event.
   */ _startLongPressTimer = /*#__PURE__*/ new WeakSet(), /**
   * Cancels the pending long-press timer.
   *
   * @private
   */ _cancelLongPressTimer = /*#__PURE__*/ new WeakSet(), /**
   * OnTouchMove callback. Cancels the long-press timer if the finger moves beyond the threshold.
   *
   * @private
   * @param {TouchEvent} event The touch event object.
   */ _onTouchMove = /*#__PURE__*/ new WeakSet();
/**
 * @class Event
 */ class Event {
    /**
   * Adds listeners for mouse and touch events.
   *
   * @private
   */ registerEvents() {
        _class_private_field_get(this, _eventManager).addEventListener(_class_private_field_get(this, _wtTable).holder, 'contextmenu', (event)=>this.onContextMenu(event));
        _class_private_field_get(this, _eventManager).addEventListener(_class_private_field_get(this, _wtTable).TABLE, 'mouseover', (event)=>this.onMouseOver(event));
        _class_private_field_get(this, _eventManager).addEventListener(_class_private_field_get(this, _wtTable).TABLE, 'mouseout', (event)=>this.onMouseOut(event));
        const initTouchEvents = ()=>{
            _class_private_field_get(this, _eventManager).addEventListener(_class_private_field_get(this, _wtTable).holder, 'touchstart', (event)=>this.onTouchStart(event));
            _class_private_field_get(this, _eventManager).addEventListener(_class_private_field_get(this, _wtTable).holder, 'touchend', (event)=>this.onTouchEnd(event));
            _class_private_field_get(this, _eventManager).addEventListener(_class_private_field_get(this, _wtTable).holder, 'touchmove', (event)=>_class_private_method_get(this, _onTouchMove, onTouchMove).call(this, event));
            if (!this.momentumScrolling) {
                this.momentumScrolling = {};
            }
            _class_private_field_get(this, _eventManager).addEventListener(_class_private_field_get(this, _wtTable).holder, 'scroll', ()=>{
                _class_private_method_get(this, _cancelLongPressTimer, cancelLongPressTimer).call(this);
                clearTimeout(this.momentumScrolling._timeout);
                if (!this.momentumScrolling.ongoing) {
                    _class_private_field_get(this, _wtSettings).getSetting('onBeforeTouchScroll');
                }
                this.momentumScrolling.ongoing = true;
                this.momentumScrolling._timeout = setTimeout(()=>{
                    if (!this.touchApplied) {
                        this.momentumScrolling.ongoing = false;
                        _class_private_field_get(this, _wtSettings).getSetting('onAfterMomentumScroll');
                    }
                }, 200);
            });
        };
        const initMouseEvents = ()=>{
            _class_private_field_get(this, _eventManager).addEventListener(_class_private_field_get(this, _wtTable).holder, 'mouseup', (event)=>this.onMouseUp(event));
            _class_private_field_get(this, _eventManager).addEventListener(_class_private_field_get(this, _wtTable).holder, 'mousedown', (event)=>this.onMouseDown(event));
        };
        if ((0, _browser.isMobileBrowser)()) {
            initTouchEvents();
        } else {
            // PC like devices which support both methods (touchscreen and ability to plug-in mouse).
            if ((0, _feature.isTouchSupported)()) {
                initTouchEvents();
            }
            initMouseEvents();
        }
    }
    /**
   * Checks if an element is already selected.
   *
   * @private
   * @param {Element} touchTarget An element to check.
   * @returns {boolean}
   */ selectedCellWasTouched(touchTarget) {
        const cellUnderFinger = this.parentCell(touchTarget);
        const coordsOfCellUnderFinger = cellUnderFinger.coords;
        if (_class_private_field_get(this, _selectedCellBeforeTouchEnd) && coordsOfCellUnderFinger) {
            const [rowTouched, rowSelected] = [
                coordsOfCellUnderFinger.row,
                _class_private_field_get(this, _selectedCellBeforeTouchEnd).from.row
            ];
            const [colTouched, colSelected] = [
                coordsOfCellUnderFinger.col,
                _class_private_field_get(this, _selectedCellBeforeTouchEnd).from.col
            ];
            return rowTouched === rowSelected && colTouched === colSelected;
        }
        return false;
    }
    /**
   * Gets closest TD or TH element.
   *
   * @private
   * @param {Element} elem An element from the traversing starts.
   * @returns {object} Contains coordinates and reference to TD or TH if it exists. Otherwise it's empty object.
   */ parentCell(elem) {
        const cell = {};
        const TABLE = _class_private_field_get(this, _wtTable).TABLE;
        const TD = (0, _element.closestDown)(elem, [
            'TD',
            'TH'
        ], TABLE);
        if (TD) {
            cell.coords = _class_private_field_get(this, _wtTable).getCoords(TD);
            cell.TD = TD;
        } else if ((0, _element.hasClass)(elem, 'wtBorder') && (0, _element.hasClass)(elem, 'current')) {
            cell.coords = _class_private_field_get(this, _selectionManager).getFocusSelection().cellRange.highlight;
            cell.TD = _class_private_field_get(this, _wtTable).getCell(cell.coords);
        } else if ((0, _element.hasClass)(elem, 'wtBorder') && (0, _element.hasClass)(elem, 'area')) {
            if (_class_private_field_get(this, _selectionManager).getAreaSelection().cellRange) {
                cell.coords = _class_private_field_get(this, _selectionManager).getAreaSelection().cellRange.to;
                cell.TD = _class_private_field_get(this, _wtTable).getCell(cell.coords);
            }
        }
        return cell;
    }
    /**
   * OnMouseDown callback.
   *
   * @private
   * @param {MouseEvent} event The mouse event object.
   */ onMouseDown(event) {
        const activeElement = _class_private_field_get(this, _domBindings).rootDocument.activeElement;
        const getParentNode = (0, _function.partial)(_element.getParent, event.target);
        const realTarget = event.target;
        // ignore non-TD focusable elements from mouse down processing
        // (https://github.com/handsontable/handsontable/issues/3555)
        if (![
            'TD',
            'TH'
        ].includes(activeElement.nodeName) && (realTarget === activeElement || getParentNode(0) === activeElement || getParentNode(1) === activeElement)) {
            return;
        }
        const cell = this.parentCell(realTarget);
        if ((0, _element.hasClass)(realTarget, 'corner')) {
            _class_private_field_get(this, _wtSettings).getSetting('onCellCornerMouseDown', event, realTarget);
        } else if (cell.TD && _class_private_field_get(this, _wtSettings).has('onCellMouseDown')) {
            this.callListener('onCellMouseDown', event, cell.coords, cell.TD);
        }
        // doubleclick reacts only for left mouse button or from touch events
        if ((event.button === 0 || this.touchApplied) && cell.TD) {
            _class_private_field_get(this, _dblClickOrigin)[0] = cell.TD;
            clearTimeout(_class_private_field_get(this, _dblClickTimeout)[0]);
            _class_private_field_get(this, _dblClickTimeout)[0] = setTimeout(()=>{
                _class_private_field_get(this, _dblClickOrigin)[0] = null;
            }, 1000);
        }
    }
    /**
   * OnContextMenu callback.
   *
   * @private
   * @param {MouseEvent} event The mouse event object.
   */ onContextMenu(event) {
        _class_private_method_get(this, _cancelLongPressTimer, cancelLongPressTimer).call(this);
        if (_class_private_field_get(this, _wtSettings).has('onCellContextMenu')) {
            const cell = this.parentCell(event.target);
            if (cell.TD) {
                this.callListener('onCellContextMenu', event, cell.coords, cell.TD);
            }
        }
    }
    /**
   * OnMouseOver callback.
   *
   * @private
   * @param {MouseEvent} event The mouse event object.
   */ onMouseOver(event) {
        if (!_class_private_field_get(this, _wtSettings).has('onCellMouseOver')) {
            return;
        }
        const table = _class_private_field_get(this, _wtTable).TABLE;
        const td = (0, _element.closestDown)(event.target, [
            'TD',
            'TH'
        ], table);
        const parent = _class_private_field_get(this, _parent) || this;
        if (td && td !== parent.lastMouseOver && (0, _element.isChildOf)(td, table)) {
            parent.lastMouseOver = td;
            this.callListener('onCellMouseOver', event, _class_private_field_get(this, _wtTable).getCoords(td), td);
        }
    }
    /**
   * OnMouseOut callback.
   *
   * @private
   * @param {MouseEvent} event The mouse event object.
   */ onMouseOut(event) {
        if (!_class_private_field_get(this, _wtSettings).has('onCellMouseOut')) {
            return;
        }
        const table = _class_private_field_get(this, _wtTable).TABLE;
        const lastTD = (0, _element.closestDown)(event.target, [
            'TD',
            'TH'
        ], table);
        const nextTD = (0, _element.closestDown)(event.relatedTarget, [
            'TD',
            'TH'
        ], table);
        const parent = _class_private_field_get(this, _parent) || this;
        if (lastTD && lastTD !== nextTD && (0, _element.isChildOf)(lastTD, table)) {
            this.callListener('onCellMouseOut', event, _class_private_field_get(this, _wtTable).getCoords(lastTD), lastTD);
            if (nextTD === null) {
                parent.lastMouseOver = null;
            }
        }
    }
    /**
   * OnMouseUp callback.
   *
   * @private
   * @param {MouseEvent} event The mouse event object.
   */ onMouseUp(event) {
        const cell = this.parentCell(event.target);
        if (cell.TD && _class_private_field_get(this, _wtSettings).has('onCellMouseUp')) {
            this.callListener('onCellMouseUp', event, cell.coords, cell.TD);
        }
        // if not left mouse button, and the origin event is not comes from touch
        if (event.button !== 0 && !this.touchApplied) {
            return;
        }
        if (cell.TD === _class_private_field_get(this, _dblClickOrigin)[0] && cell.TD === _class_private_field_get(this, _dblClickOrigin)[1]) {
            if ((0, _element.hasClass)(event.target, 'corner')) {
                this.callListener('onCellCornerDblClick', event, cell.coords, cell.TD);
            } else {
                this.callListener('onCellDblClick', event, cell.coords, cell.TD);
            }
            _class_private_field_get(this, _dblClickOrigin)[0] = null;
            _class_private_field_get(this, _dblClickOrigin)[1] = null;
        } else if (cell.TD === _class_private_field_get(this, _dblClickOrigin)[0]) {
            _class_private_field_get(this, _dblClickOrigin)[1] = cell.TD;
            clearTimeout(_class_private_field_get(this, _dblClickTimeout)[1]);
            _class_private_field_get(this, _dblClickTimeout)[1] = setTimeout(()=>{
                _class_private_field_get(this, _dblClickOrigin)[1] = null;
            }, 500);
        }
    }
    /**
   * OnTouchStart callback. Simulates mousedown event.
   *
   * @private
   * @param {TouchEvent} event The touch event object.
   */ onTouchStart(event) {
        _class_private_field_set(this, _selectedCellBeforeTouchEnd, _class_private_field_get(this, _selectionManager).getFocusSelection().cellRange);
        this.touchApplied = true;
        this.onMouseDown(event);
        _class_private_method_get(this, _startLongPressTimer, startLongPressTimer).call(this, event);
    }
    /**
   * OnTouchEnd callback. Simulates mouseup event.
   *
   * @private
   * @param {TouchEvent} event The touch event object.
   */ onTouchEnd(event) {
        _class_private_method_get(this, _cancelLongPressTimer, cancelLongPressTimer).call(this);
        const target = event.target;
        const parentCellCoords = this.parentCell(target)?.coords;
        const isCellsRange = (0, _mixed.isDefined)(parentCellCoords) && parentCellCoords.row >= 0 && parentCellCoords.col >= 0;
        const isEventCancelable = event.cancelable && isCellsRange && _class_private_field_get(this, _wtSettings).getSetting('isDataViewInstance');
        // To prevent accidental redirects or other actions that the interactive elements (e.q "A" link) do
        // while the cell is highlighted, all touch events that are triggered on different cells are
        // "preventDefault"'ed. The user can interact with the element (e.q. click on the link that opens
        // a new page) only when the same cell was previously selected (see related PR #7980).
        if (isEventCancelable) {
            const interactiveElements = [
                'A',
                'BUTTON',
                'INPUT'
            ];
            // For browsers that use the WebKit as an engine (excluding Safari), there is a bug. The prevent
            // default has to be called all the time. Otherwise, the second tap won't be triggered (probably
            // caused by the native ~300ms delay - https://webkit.org/blog/5610/more-responsive-tapping-on-ios/).
            // To make the interactive elements work, the event target element has to be check. If the element
            // matches the allow-list, the event is not prevented.
            if ((0, _browser.isIOS)() && ((0, _browser.isChromeWebKit)() || (0, _browser.isFirefoxWebKit)()) && this.selectedCellWasTouched(target) && !interactiveElements.includes(target.tagName)) {
                event.preventDefault();
            } else if (!this.selectedCellWasTouched(target)) {
                // For other browsers, prevent default is fired only for the first tap and only when the previous
                // highlighted cell was different.
                event.preventDefault();
            }
        }
        this.onMouseUp(event);
        this.touchApplied = false;
    }
    /**
   * Call listener with backward compatibility.
   *
   * @private
   * @param {string} name Name of listener.
   * @param {MouseEvent} event The event object.
   * @param {CellCoords} coords Coordinates.
   * @param {HTMLElement} target Event target.
   */ callListener(name, event, coords, target) {
        const listener = _class_private_field_get(this, _wtSettings).getSettingPure(name);
        if (listener) {
            listener(event, coords, target, _class_private_field_get(this, _facadeGetter).call(this));
        }
    }
    /**
   * Clears double-click timeouts and destroys the internal eventManager instance.
   */ destroy() {
        clearTimeout(_class_private_field_get(this, _dblClickTimeout)[0]);
        clearTimeout(_class_private_field_get(this, _dblClickTimeout)[1]);
        _class_private_method_get(this, _cancelLongPressTimer, cancelLongPressTimer).call(this);
        _class_private_field_get(this, _eventManager).destroy();
    }
    /**
   * @param {FacadeGetter} facadeGetter Gets an instance facade.
   * @param {DomBindings} domBindings Bindings into dom.
   * @param {Settings} wtSettings The walkontable settings.
   * @param {EventManager} eventManager The walkontable event manager.
   * @param {Table} wtTable The table.
   * @param {SelectionManager} selectionManager Selections.
   * @param {Event} [parent=null] The main Event instance.
   */ constructor(facadeGetter, domBindings, wtSettings, eventManager, wtTable, selectionManager, parent = null){
        _class_private_method_init(this, _startLongPressTimer);
        _class_private_method_init(this, _cancelLongPressTimer);
        _class_private_method_init(this, _onTouchMove);
        _class_private_field_init(this, _wtSettings, {
            writable: true,
            value: void 0
        });
        _class_private_field_init(this, _domBindings, {
            writable: true,
            value: void 0
        });
        _class_private_field_init(this, _wtTable, {
            writable: true,
            value: void 0
        });
        _class_private_field_init(this, _selectionManager, {
            writable: true,
            value: void 0
        });
        _class_private_field_init(this, _parent, {
            writable: true,
            value: void 0
        });
        _class_private_field_init(this, _eventManager, {
            writable: true,
            value: void 0
        });
        _class_private_field_init(this, _facadeGetter, {
            writable: true,
            value: void 0
        });
        _class_private_field_init(this, _selectedCellBeforeTouchEnd, {
            writable: true,
            value: void 0
        });
        _class_private_field_init(this, _dblClickTimeout, {
            writable: true,
            value: void 0
        });
        _class_private_field_init(this, _dblClickOrigin, {
            writable: true,
            value: void 0
        });
        _class_private_field_init(this, _longPressTimeout, {
            writable: true,
            value: void 0
        });
        _class_private_field_init(this, _touchStartCoords, {
            writable: true,
            value: void 0
        });
        _class_private_field_set(this, _dblClickTimeout, [
            null,
            null
        ]);
        _class_private_field_set(this, _dblClickOrigin, [
            null,
            null
        ]);
        _class_private_field_set(this, _longPressTimeout, null);
        _class_private_field_set(this, _touchStartCoords, null);
        _class_private_field_set(this, _wtSettings, wtSettings);
        _class_private_field_set(this, _domBindings, domBindings);
        _class_private_field_set(this, _wtTable, wtTable);
        _class_private_field_set(this, _selectionManager, selectionManager);
        _class_private_field_set(this, _parent, parent);
        _class_private_field_set(this, _eventManager, eventManager);
        _class_private_field_set(this, _facadeGetter, facadeGetter);
        this.registerEvents();
    }
}
function startLongPressTimer(event) {
    _class_private_method_get(this, _cancelLongPressTimer, cancelLongPressTimer).call(this);
    const touch = event.touches[0];
    if (!touch) {
        return;
    }
    _class_private_field_set(this, _touchStartCoords, {
        x: touch.clientX,
        y: touch.clientY
    });
    _class_private_field_set(this, _longPressTimeout, setTimeout(()=>{
        _class_private_field_set(this, _longPressTimeout, null);
        _class_private_field_set(this, _touchStartCoords, null);
        _class_private_field_get(this, _dblClickOrigin)[0] = null;
        _class_private_field_get(this, _dblClickOrigin)[1] = null;
        clearTimeout(_class_private_field_get(this, _dblClickTimeout)[0]);
        clearTimeout(_class_private_field_get(this, _dblClickTimeout)[1]);
        const target = event.target;
        const contextMenuEvent = new MouseEvent('contextmenu', {
            bubbles: true,
            cancelable: true,
            clientX: touch.clientX,
            clientY: touch.clientY,
            screenX: touch.screenX,
            screenY: touch.screenY
        });
        target.dispatchEvent(contextMenuEvent);
    }, LONG_PRESS_DELAY));
}
function cancelLongPressTimer() {
    if (_class_private_field_get(this, _longPressTimeout) !== null) {
        clearTimeout(_class_private_field_get(this, _longPressTimeout));
        _class_private_field_set(this, _longPressTimeout, null);
    }
    _class_private_field_set(this, _touchStartCoords, null);
}
function onTouchMove(event) {
    if (_class_private_field_get(this, _longPressTimeout) === null || _class_private_field_get(this, _touchStartCoords) === null) {
        return;
    }
    const touch = event.touches[0];
    if (!touch) {
        _class_private_method_get(this, _cancelLongPressTimer, cancelLongPressTimer).call(this);
        return;
    }
    const dx = Math.abs(touch.clientX - _class_private_field_get(this, _touchStartCoords).x);
    const dy = Math.abs(touch.clientY - _class_private_field_get(this, _touchStartCoords).y);
    if (dx > LONG_PRESS_MOVE_THRESHOLD || dy > LONG_PRESS_MOVE_THRESHOLD) {
        _class_private_method_get(this, _cancelLongPressTimer, cancelLongPressTimer).call(this);
    }
}
const _default = Event;
