"use strict";
/**
 * The original package is https://www.npmjs.com/package/textarea-caret-ts
 * The original file is https://github.com/TheRealSyler/textarea-caret-position/blob/master/index.ts
 *
 * Here I modified it to make it works when input is scrolled.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.getAbsolutePosition = getAbsolutePosition;
exports.getRelativePosition = getRelativePosition;
exports.setElementPositionBasedOnCaret = setElementPositionBasedOnCaret;
const _utils_1 = require("../../_utils");
/**
 * Returns the Absolute (relative to the inner window size) position of the caret in the given element.
 * @param element Input (has to be type='text') or Text Area.
 */
function getAbsolutePosition(element) {
    const caretRelPost = getRelativePosition(element);
    return {
        left: window.scrollX + element.getBoundingClientRect().left + caretRelPost.left,
        top: window.scrollY + element.getBoundingClientRect().top + caretRelPost.top,
        absolute: true,
        height: caretRelPost.height
    };
}
/**
 * Returns the relative position of the caret in the given element.
 * @param element Input (has to be type='text') or Text Area.
 */
function getRelativePosition(element, options = {
    debug: false,
    useSelectionEnd: false,
    checkWidthOverflow: true
}) {
    const selectionStart = element.selectionStart !== null ? element.selectionStart : 0;
    const selectionEnd = element.selectionEnd !== null ? element.selectionEnd : 0;
    const position = options.useSelectionEnd ? selectionEnd : selectionStart;
    // We'll copy the properties below into the mirror div.
    // Note that some browsers, such as Firefox, do not concatenate properties
    // into their shorthand (e.g. padding-top, padding-bottom etc. -> padding),
    // so we have to list every single property explicitly.
    const properties = [
        'direction', // RTL support
        'boxSizing',
        'width', // on Chrome and IE, exclude the scrollbar, so the mirror div wraps exactly as the textarea does
        'height',
        'overflowX',
        'overflowY', // copy the scrollbar for IE
        'borderTopWidth',
        'borderRightWidth',
        'borderBottomWidth',
        'borderLeftWidth',
        'borderStyle',
        'paddingTop',
        'paddingRight',
        'paddingBottom',
        'paddingLeft',
        // https://developer.mozilla.org/en-US/docs/Web/CSS/font
        'fontStyle',
        'fontVariant',
        'fontWeight',
        'fontStretch',
        'fontSize',
        'fontSizeAdjust',
        'lineHeight',
        'fontFamily',
        'textAlign',
        'textTransform',
        'textIndent',
        'textDecoration', // might not make a difference, but better be safe
        'letterSpacing',
        'wordSpacing',
        'tabSize',
        'MozTabSize'
    ];
    // Firefox 1.0+
    const isFirefox = navigator.userAgent.toLowerCase().includes('firefox');
    if (!_utils_1.isBrowser) {
        throw new Error('textarea-caret-position#getCaretPosition should only be called in a browser');
    }
    const debug = options === null || options === void 0 ? void 0 : options.debug;
    if (debug) {
        const el = document.querySelector('#input-textarea-caret-position-mirror-div');
        if (el === null || el === void 0 ? void 0 : el.parentNode)
            el.parentNode.removeChild(el);
    }
    // The mirror div will replicate the textareas style
    const div = document.createElement('div');
    div.id = 'input-textarea-caret-position-mirror-div';
    document.body.appendChild(div);
    const style = div.style;
    const computed = window.getComputedStyle
        ? window.getComputedStyle(element)
        : element.currentStyle; // currentStyle for IE < 9
    const isInput = element.nodeName === 'INPUT';
    // Default textarea styles
    style.whiteSpace = isInput ? 'nowrap' : 'pre-wrap';
    if (!isInput)
        style.wordWrap = 'break-word'; // only for textarea-s
    // Position off-screen
    style.position = 'absolute'; // required to return coordinates properly
    if (!debug)
        style.visibility = 'hidden'; // not 'display: none' because we want rendering
    // Transfer the element's properties to the div
    properties.forEach((prop) => {
        if (isInput && prop === 'lineHeight') {
            // Special case for <input>s because text is rendered centered and line height may be != height
            if (computed.boxSizing === 'border-box') {
                const height = Number.parseInt(computed.height);
                const outerHeight = Number.parseInt(computed.paddingTop)
                    + Number.parseInt(computed.paddingBottom)
                    + Number.parseInt(computed.borderTopWidth)
                    + Number.parseInt(computed.borderBottomWidth);
                const targetHeight = outerHeight + Number.parseInt(computed.lineHeight);
                if (height > targetHeight) {
                    style.lineHeight = `${height - outerHeight}px`;
                }
                else if (height === targetHeight) {
                    style.lineHeight = computed.lineHeight;
                }
                else {
                    style.lineHeight = '0';
                }
            }
            else {
                style.lineHeight = computed.height;
            }
        }
        else {
            style[prop] = computed[prop];
        }
    });
    if (isFirefox) {
        // Firefox lies about the overflow property for textareas: https://bugzilla.mozilla.org/show_bug.cgi?id=984275
        if (element.scrollHeight > Number.parseInt(computed.height)) {
            style.overflowY = 'scroll';
        }
    }
    else {
        style.overflow = 'hidden'; // for Chrome to not render a scrollbar; IE keeps overflowY = 'scroll'
    }
    div.textContent = element.value.substring(0, position);
    // The second special handling for input type="text" vs textarea:
    // spaces need to be replaced with non-breaking spaces - http://stackoverflow.com/a/13402035/1269037
    if (isInput && div.textContent) {
        div.textContent = div.textContent.replace(/\s/g, '\u00A0');
    }
    const span = document.createElement('span');
    // Wrapping must be replicated *exactly*, including when a long word gets
    // onto the next line, with whitespace at the end of the line before (#7).
    // The  *only* reliable way to do that is to copy the *entire* rest of the
    // textareas content into the <span> created at the caret position.
    // For inputs, just '.' would be enough, but no need to bother.
    span.textContent = element.value.substring(position) || '.'; // || because a completely empty faux span doesn't render at all
    span.style.position = 'relative';
    span.style.left = `${-element.scrollLeft}px`;
    span.style.top = `${-element.scrollTop}px`;
    div.appendChild(span);
    const relativePosition = {
        top: span.offsetTop + Number.parseInt(computed.borderTopWidth),
        left: span.offsetLeft + Number.parseInt(computed.borderLeftWidth),
        absolute: false,
        // We don't use line-height since it may be too large for position. Eg. 34px
        // for input
        height: Number.parseInt(computed.fontSize) * 1.5
    };
    if (debug) {
        span.style.backgroundColor = '#aaa';
    }
    else {
        document.body.removeChild(div);
    }
    if (relativePosition.left >= element.clientWidth
        && options.checkWidthOverflow) {
        relativePosition.left = element.clientWidth;
    }
    return relativePosition;
}
/**
 * sets the top and left css style of the element based on the absolute position of the caretElements caret,
 * @param offset offsets the position.
 * @param detectBoundary offsets the position if the position would be outside the window.
 * @param returnOnly if true the element position wont be set.
 */
function setElementPositionBasedOnCaret(element, caretElement, offset = { top: 0, left: 0 }, margin = 2, detectBoundary = true, returnOnly = false) {
    const pos = getAbsolutePosition(caretElement);
    if (detectBoundary) {
        pos.left
            = pos.left + (element.clientWidth + margin) + offset.left
                > window.scrollX + window.innerWidth
                ? (pos.left
                    = window.scrollX + window.innerWidth - (element.clientWidth + margin))
                : (pos.left += offset.left);
        pos.top
            = pos.top + (element.clientWidth + margin) + offset.top
                > window.scrollY + window.innerHeight
                ? (pos.top -= element.clientWidth + margin)
                : (pos.top += offset.top);
    }
    else {
        pos.top += offset.top;
        pos.left += offset.left;
    }
    if (!returnOnly) {
        element.style.top = `${pos.top}px`;
        element.style.left = `${pos.left}px`;
    }
    return pos;
}
