var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __generator = (this && this.__generator) || function (thisArg, body) {
    var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
    return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
    function verb(n) { return function (v) { return step([n, v]); }; }
    function step(op) {
        if (f) throw new TypeError("Generator is already executing.");
        while (_) try {
            if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
            if (y = 0, t) op = [op[0] & 2, t.value];
            switch (op[0]) {
                case 0: case 1: t = op; break;
                case 4: _.label++; return { value: op[1], done: false };
                case 5: _.label++; y = op[1]; op = [0]; continue;
                case 7: op = _.ops.pop(); _.trys.pop(); continue;
                default:
                    if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
                    if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
                    if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
                    if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
                    if (t[2]) _.ops.pop();
                    _.trys.pop(); continue;
            }
            op = body.call(thisArg, _);
        } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
        if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
    }
};
import { p as pointerCoord } from './helpers-29d709d1.js';
var cloneMap = new WeakMap();
var relocateInput = function (componentEl, inputEl, shouldRelocate, inputRelativeY) {
    if (inputRelativeY === void 0) { inputRelativeY = 0; }
    if (cloneMap.has(componentEl) === shouldRelocate) {
        return;
    }
    if (shouldRelocate) {
        addClone(componentEl, inputEl, inputRelativeY);
    }
    else {
        removeClone(componentEl, inputEl);
    }
};
var isFocused = function (input) {
    return input === input.getRootNode().activeElement;
};
var addClone = function (componentEl, inputEl, inputRelativeY) {
    // this allows for the actual input to receive the focus from
    // the user's touch event, but before it receives focus, it
    // moves the actual input to a location that will not screw
    // up the app's layout, and does not allow the native browser
    // to attempt to scroll the input into place (messing up headers/footers)
    // the cloned input fills the area of where native input should be
    // while the native input fakes out the browser by relocating itself
    // before it receives the actual focus event
    // We hide the focused input (with the visible caret) invisible by making it scale(0),
    var parentEl = inputEl.parentNode;
    // DOM WRITES
    var clonedEl = inputEl.cloneNode(false);
    clonedEl.classList.add('cloned-input');
    clonedEl.tabIndex = -1;
    parentEl.appendChild(clonedEl);
    cloneMap.set(componentEl, clonedEl);
    var doc = componentEl.ownerDocument;
    var tx = doc.dir === 'rtl' ? 9999 : -9999;
    componentEl.style.pointerEvents = 'none';
    inputEl.style.transform = "translate3d(" + tx + "px," + inputRelativeY + "px,0) scale(0)";
};
var removeClone = function (componentEl, inputEl) {
    var clone = cloneMap.get(componentEl);
    if (clone) {
        cloneMap.delete(componentEl);
        clone.remove();
    }
    componentEl.style.pointerEvents = '';
    inputEl.style.transform = '';
};
var enableHideCaretOnScroll = function (componentEl, inputEl, scrollEl) {
    if (!scrollEl || !inputEl) {
        return function () { return; };
    }
    var scrollHideCaret = function (shouldHideCaret) {
        if (isFocused(inputEl)) {
            relocateInput(componentEl, inputEl, shouldHideCaret);
        }
    };
    var onBlur = function () { return relocateInput(componentEl, inputEl, false); };
    var hideCaret = function () { return scrollHideCaret(true); };
    var showCaret = function () { return scrollHideCaret(false); };
    scrollEl.addEventListener('ionScrollStart', hideCaret);
    scrollEl.addEventListener('ionScrollEnd', showCaret);
    inputEl.addEventListener('blur', onBlur);
    return function () {
        scrollEl.removeEventListener('ionScrollStart', hideCaret);
        scrollEl.removeEventListener('ionScrollEnd', showCaret);
        inputEl.addEventListener('ionBlur', onBlur);
    };
};
var SKIP_SELECTOR = 'input, textarea, [no-blur]';
var enableInputBlurring = function () {
    var focused = true;
    var didScroll = false;
    var doc = document;
    var onScroll = function () {
        didScroll = true;
    };
    var onFocusin = function () {
        focused = true;
    };
    var onTouchend = function (ev) {
        // if app did scroll return early
        if (didScroll) {
            didScroll = false;
            return;
        }
        var active = doc.activeElement;
        if (!active) {
            return;
        }
        // only blur if the active element is a text-input or a textarea
        if (active.matches(SKIP_SELECTOR)) {
            return;
        }
        // if the selected target is the active element, do not blur
        var tapped = ev.target;
        if (tapped === active) {
            return;
        }
        if (tapped.matches(SKIP_SELECTOR) || tapped.closest(SKIP_SELECTOR)) {
            return;
        }
        focused = false;
        // TODO: find a better way, why 50ms?
        setTimeout(function () {
            if (!focused) {
                active.blur();
            }
        }, 50);
    };
    doc.addEventListener('ionScrollStart', onScroll);
    doc.addEventListener('focusin', onFocusin, true);
    doc.addEventListener('touchend', onTouchend, false);
    return function () {
        doc.removeEventListener('ionScrollStart', onScroll, true);
        doc.removeEventListener('focusin', onFocusin, true);
        doc.removeEventListener('touchend', onTouchend, false);
    };
};
var SCROLL_ASSIST_SPEED = 0.3;
var getScrollData = function (componentEl, contentEl, keyboardHeight) {
    var itemEl = componentEl.closest('ion-item,[ion-item]') || componentEl;
    return calcScrollData(itemEl.getBoundingClientRect(), contentEl.getBoundingClientRect(), keyboardHeight, componentEl.ownerDocument.defaultView.innerHeight);
};
var calcScrollData = function (inputRect, contentRect, keyboardHeight, platformHeight) {
    // compute input's Y values relative to the body
    var inputTop = inputRect.top;
    var inputBottom = inputRect.bottom;
    // compute visible area
    var visibleAreaTop = contentRect.top;
    var visibleAreaBottom = Math.min(contentRect.bottom, platformHeight - keyboardHeight);
    // compute safe area
    var safeAreaTop = visibleAreaTop + 15;
    var safeAreaBottom = visibleAreaBottom * 0.75;
    // figure out if each edge of the input is within the safe area
    var distanceToBottom = safeAreaBottom - inputBottom;
    var distanceToTop = safeAreaTop - inputTop;
    // desiredScrollAmount is the negated distance to the safe area according to our calculations.
    var desiredScrollAmount = Math.round((distanceToBottom < 0)
        ? -distanceToBottom
        : (distanceToTop > 0)
            ? -distanceToTop
            : 0);
    // our calculations make some assumptions that aren't always true, like the keyboard being closed when an input
    // gets focus, so make sure we don't scroll the input above the visible area
    var scrollAmount = Math.min(desiredScrollAmount, inputTop - visibleAreaTop);
    var distance = Math.abs(scrollAmount);
    var duration = distance / SCROLL_ASSIST_SPEED;
    var scrollDuration = Math.min(400, Math.max(150, duration));
    return {
        scrollAmount: scrollAmount,
        scrollDuration: scrollDuration,
        scrollPadding: keyboardHeight,
        inputSafeY: -(inputTop - safeAreaTop) + 4
    };
};
var enableScrollAssist = function (componentEl, inputEl, contentEl, footerEl, keyboardHeight) {
    var coord;
    var touchStart = function (ev) {
        coord = pointerCoord(ev);
    };
    var touchEnd = function (ev) {
        // input cover touchend/mouseup
        if (!coord) {
            return;
        }
        // get where the touchend/mouseup ended
        var endCoord = pointerCoord(ev);
        // focus this input if the pointer hasn't moved XX pixels
        // and the input doesn't already have focus
        if (!hasPointerMoved(6, coord, endCoord) && !isFocused(inputEl)) {
            ev.preventDefault();
            ev.stopPropagation();
            // begin the input focus process
            jsSetFocus(componentEl, inputEl, contentEl, footerEl, keyboardHeight);
        }
    };
    componentEl.addEventListener('touchstart', touchStart, true);
    componentEl.addEventListener('touchend', touchEnd, true);
    return function () {
        componentEl.removeEventListener('touchstart', touchStart, true);
        componentEl.removeEventListener('touchend', touchEnd, true);
    };
};
var jsSetFocus = function (componentEl, inputEl, contentEl, footerEl, keyboardHeight) { return __awaiter(void 0, void 0, void 0, function () {
    var scrollData, scrollContentTimeout_1, scrollContent_1, doubleKeyboardEventListener_1, scrollEl, totalScrollAmount;
    return __generator(this, function (_a) {
        switch (_a.label) {
            case 0:
                if (!contentEl && !footerEl) {
                    return [2 /*return*/];
                }
                scrollData = getScrollData(componentEl, (contentEl || footerEl), keyboardHeight);
                if (contentEl && Math.abs(scrollData.scrollAmount) < 4) {
                    // the text input is in a safe position that doesn't
                    // require it to be scrolled into view, just set focus now
                    inputEl.focus();
                    return [2 /*return*/];
                }
                // temporarily move the focus to the focus holder so the browser
                // doesn't freak out while it's trying to get the input in place
                // at this point the native text input still does not have focus
                relocateInput(componentEl, inputEl, true, scrollData.inputSafeY);
                inputEl.focus();
                if (!(typeof window !== 'undefined')) return [3 /*break*/, 3];
                scrollContent_1 = function () { return __awaiter(void 0, void 0, void 0, function () {
                    return __generator(this, function (_a) {
                        switch (_a.label) {
                            case 0:
                                // clean up listeners and timeouts
                                if (scrollContentTimeout_1 !== undefined) {
                                    clearTimeout(scrollContentTimeout_1);
                                }
                                window.removeEventListener('ionKeyboardDidShow', doubleKeyboardEventListener_1);
                                window.removeEventListener('ionKeyboardDidShow', scrollContent_1);
                                if (!contentEl) return [3 /*break*/, 2];
                                return [4 /*yield*/, contentEl.scrollByPoint(0, scrollData.scrollAmount, scrollData.scrollDuration)];
                            case 1:
                                _a.sent();
                                _a.label = 2;
                            case 2:
                                // the scroll view is in the correct position now
                                // give the native text input focus
                                relocateInput(componentEl, inputEl, false, scrollData.inputSafeY);
                                // ensure this is the focused input
                                inputEl.focus();
                                return [2 /*return*/];
                        }
                    });
                }); };
                doubleKeyboardEventListener_1 = function () {
                    window.removeEventListener('ionKeyboardDidShow', doubleKeyboardEventListener_1);
                    window.addEventListener('ionKeyboardDidShow', scrollContent_1);
                };
                if (!contentEl) return [3 /*break*/, 2];
                return [4 /*yield*/, contentEl.getScrollElement()];
            case 1:
                scrollEl = _a.sent();
                totalScrollAmount = scrollEl.scrollHeight - scrollEl.clientHeight;
                if (scrollData.scrollAmount > (totalScrollAmount - scrollEl.scrollTop)) {
                    /**
                     * On iOS devices, the system will show a "Passwords" bar above the keyboard
                     * after the initial keyboard is shown. This prevents the webview from resizing
                     * until the "Passwords" bar is shown, so we need to wait for that to happen first.
                     */
                    if (inputEl.type === 'password') {
                        // Add 50px to account for the "Passwords" bar
                        scrollData.scrollAmount += 50;
                        window.addEventListener('ionKeyboardDidShow', doubleKeyboardEventListener_1);
                    }
                    else {
                        window.addEventListener('ionKeyboardDidShow', scrollContent_1);
                    }
                    /**
                     * This should only fire in 2 instances:
                     * 1. The app is very slow.
                     * 2. The app is running in a browser on an old OS
                     * that does not support Ionic Keyboard Events
                     */
                    scrollContentTimeout_1 = setTimeout(scrollContent_1, 1000);
                    return [2 /*return*/];
                }
                _a.label = 2;
            case 2:
                scrollContent_1();
                _a.label = 3;
            case 3: return [2 /*return*/];
        }
    });
}); };
var hasPointerMoved = function (threshold, startCoord, endCoord) {
    if (startCoord && endCoord) {
        var deltaX = (startCoord.x - endCoord.x);
        var deltaY = (startCoord.y - endCoord.y);
        var distance = deltaX * deltaX + deltaY * deltaY;
        return distance > (threshold * threshold);
    }
    return false;
};
var PADDING_TIMER_KEY = '$ionPaddingTimer';
var enableScrollPadding = function (keyboardHeight) {
    var doc = document;
    var onFocusin = function (ev) {
        setScrollPadding(ev.target, keyboardHeight);
    };
    var onFocusout = function (ev) {
        setScrollPadding(ev.target, 0);
    };
    doc.addEventListener('focusin', onFocusin);
    doc.addEventListener('focusout', onFocusout);
    return function () {
        doc.removeEventListener('focusin', onFocusin);
        doc.removeEventListener('focusout', onFocusout);
    };
};
var setScrollPadding = function (input, keyboardHeight) {
    if (input.tagName !== 'INPUT') {
        return;
    }
    if (input.parentElement && input.parentElement.tagName === 'ION-INPUT') {
        return;
    }
    if (input.parentElement &&
        input.parentElement.parentElement &&
        input.parentElement.parentElement.tagName === 'ION-SEARCHBAR') {
        return;
    }
    var el = input.closest('ion-content');
    if (el === null) {
        return;
    }
    var timer = el[PADDING_TIMER_KEY];
    if (timer) {
        clearTimeout(timer);
    }
    if (keyboardHeight > 0) {
        el.style.setProperty('--keyboard-offset', keyboardHeight + "px");
    }
    else {
        el[PADDING_TIMER_KEY] = setTimeout(function () {
            el.style.setProperty('--keyboard-offset', '0px');
        }, 120);
    }
};
var INPUT_BLURRING = true;
var SCROLL_PADDING = true;
var startInputShims = function (config) {
    var doc = document;
    var keyboardHeight = config.getNumber('keyboardHeight', 290);
    var scrollAssist = config.getBoolean('scrollAssist', true);
    var hideCaret = config.getBoolean('hideCaretOnScroll', true);
    var inputBlurring = config.getBoolean('inputBlurring', true);
    var scrollPadding = config.getBoolean('scrollPadding', true);
    var inputs = Array.from(doc.querySelectorAll('ion-input, ion-textarea'));
    var hideCaretMap = new WeakMap();
    var scrollAssistMap = new WeakMap();
    var registerInput = function (componentEl) { return __awaiter(void 0, void 0, void 0, function () {
        var inputRoot, inputEl, scrollEl, footerEl, rmFn, rmFn;
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0:
                    if (!componentEl.componentOnReady) return [3 /*break*/, 2];
                    return [4 /*yield*/, componentEl.componentOnReady()];
                case 1:
                    _a.sent();
                    _a.label = 2;
                case 2:
                    inputRoot = componentEl.shadowRoot || componentEl;
                    inputEl = inputRoot.querySelector('input') || inputRoot.querySelector('textarea');
                    scrollEl = componentEl.closest('ion-content');
                    footerEl = (!scrollEl) ? componentEl.closest('ion-footer') : null;
                    if (!inputEl) {
                        return [2 /*return*/];
                    }
                    if (!!scrollEl && hideCaret && !hideCaretMap.has(componentEl)) {
                        rmFn = enableHideCaretOnScroll(componentEl, inputEl, scrollEl);
                        hideCaretMap.set(componentEl, rmFn);
                    }
                    if ((!!scrollEl || !!footerEl) && scrollAssist && !scrollAssistMap.has(componentEl)) {
                        rmFn = enableScrollAssist(componentEl, inputEl, scrollEl, footerEl, keyboardHeight);
                        scrollAssistMap.set(componentEl, rmFn);
                    }
                    return [2 /*return*/];
            }
        });
    }); };
    var unregisterInput = function (componentEl) {
        if (hideCaret) {
            var fn = hideCaretMap.get(componentEl);
            if (fn) {
                fn();
            }
            hideCaretMap.delete(componentEl);
        }
        if (scrollAssist) {
            var fn = scrollAssistMap.get(componentEl);
            if (fn) {
                fn();
            }
            scrollAssistMap.delete(componentEl);
        }
    };
    if (inputBlurring && INPUT_BLURRING) {
        enableInputBlurring();
    }
    if (scrollPadding && SCROLL_PADDING) {
        enableScrollPadding(keyboardHeight);
    }
    // Input might be already loaded in the DOM before ion-device-hacks did.
    // At this point we need to look for all of the inputs not registered yet
    // and register them.
    for (var _i = 0, inputs_1 = inputs; _i < inputs_1.length; _i++) {
        var input = inputs_1[_i];
        registerInput(input);
    }
    doc.addEventListener('ionInputDidLoad', (function (ev) {
        registerInput(ev.detail);
    }));
    doc.addEventListener('ionInputDidUnload', (function (ev) {
        unregisterInput(ev.detail);
    }));
};
export { startInputShims };
