"use strict";
Object.defineProperty(exports, "__esModule", {
    value: true
});
function _export(target, all) {
    for(var name in all)Object.defineProperty(target, name, {
        enumerable: true,
        get: Object.getOwnPropertyDescriptor(all, name).get
    });
}
_export(exports, {
    get HTML_CHARACTERS () {
        return HTML_CHARACTERS;
    },
    get addClass () {
        return addClass;
    },
    get addEvent () {
        return addEvent;
    },
    get clearTextSelection () {
        return clearTextSelection;
    },
    get closest () {
        return closest;
    },
    get closestDown () {
        return closestDown;
    },
    get empty () {
        return empty;
    },
    get fastInnerHTML () {
        return fastInnerHTML;
    },
    get fastInnerText () {
        return fastInnerText;
    },
    get findFirstParentWithClass () {
        return findFirstParentWithClass;
    },
    get getCaretPosition () {
        return getCaretPosition;
    },
    get getCssTransform () {
        return getCssTransform;
    },
    get getFractionalScalingCompensation () {
        return getFractionalScalingCompensation;
    },
    get getFrameElement () {
        return getFrameElement;
    },
    get getMaximumScrollLeft () {
        return getMaximumScrollLeft;
    },
    get getMaximumScrollTop () {
        return getMaximumScrollTop;
    },
    get getParent () {
        return getParent;
    },
    get getParentWindow () {
        return getParentWindow;
    },
    get getScrollLeft () {
        return getScrollLeft;
    },
    get getScrollTop () {
        return getScrollTop;
    },
    get getScrollableElement () {
        return getScrollableElement;
    },
    get getScrollbarWidth () {
        return getScrollbarWidth;
    },
    get getSelectionEndPosition () {
        return getSelectionEndPosition;
    },
    get getSelectionText () {
        return getSelectionText;
    },
    get getStyle () {
        return getStyle;
    },
    get getTrimmingContainer () {
        return getTrimmingContainer;
    },
    get getWindowScrollLeft () {
        return getWindowScrollLeft;
    },
    get getWindowScrollTop () {
        return getWindowScrollTop;
    },
    get hasAccessToParentWindow () {
        return hasAccessToParentWindow;
    },
    get hasClass () {
        return hasClass;
    },
    get hasHorizontalScrollbar () {
        return hasHorizontalScrollbar;
    },
    get hasVerticalScrollbar () {
        return hasVerticalScrollbar;
    },
    get hasZeroHeight () {
        return hasZeroHeight;
    },
    get index () {
        return index;
    },
    get innerHeight () {
        return innerHeight;
    },
    get innerWidth () {
        return innerWidth;
    },
    get isBottomMostColumnHeader () {
        return isBottomMostColumnHeader;
    },
    get isChildOf () {
        return isChildOf;
    },
    get isDetached () {
        return isDetached;
    },
    get isHTMLElement () {
        return isHTMLElement;
    },
    get isInput () {
        return isInput;
    },
    get isInternalElement () {
        return isInternalElement;
    },
    get isOutsideInput () {
        return isOutsideInput;
    },
    get isVisible () {
        return isVisible;
    },
    get makeElementContentEditableAndSelectItsContent () {
        return makeElementContentEditableAndSelectItsContent;
    },
    get matchesCSSRules () {
        return matchesCSSRules;
    },
    get observeVisibilityChangeOnce () {
        return observeVisibilityChangeOnce;
    },
    get offset () {
        return offset;
    },
    get outerHeight () {
        return outerHeight;
    },
    get outerWidth () {
        return outerWidth;
    },
    get overlayContainsElement () {
        return overlayContainsElement;
    },
    get removeAttribute () {
        return removeAttribute;
    },
    get removeClass () {
        return removeClass;
    },
    get removeContentEditableFromElementAndDeselect () {
        return removeContentEditableFromElementAndDeselect;
    },
    get removeEvent () {
        return removeEvent;
    },
    get removeTextNodes () {
        return removeTextNodes;
    },
    get resetCssTransform () {
        return resetCssTransform;
    },
    get runWithSelectedContendEditableElement () {
        return runWithSelectedContendEditableElement;
    },
    get selectElementIfAllowed () {
        return selectElementIfAllowed;
    },
    get setAttribute () {
        return setAttribute;
    },
    get setCaretPosition () {
        return setCaretPosition;
    },
    get setOverlayPosition () {
        return setOverlayPosition;
    }
});
const _string = require("../string");
const _a11y = require("../a11y");
const _browser = require("../browser");
const _console = require("../console");
const _errors = require("../../helpers/errors");
function getParent(element, level = 0) {
    let iteration = -1;
    let parent = null;
    let elementToCheck = element;
    while(elementToCheck !== null){
        if (iteration === level) {
            parent = elementToCheck;
            break;
        }
        if (elementToCheck.host && elementToCheck.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
            elementToCheck = elementToCheck.host;
        } else {
            iteration += 1;
            elementToCheck = elementToCheck.parentNode;
        }
    }
    return parent;
}
function isInternalElement(element, thisHotContainer) {
    const closestHandsontableContainer = element.closest('.handsontable');
    return !!closestHandsontableContainer && (closestHandsontableContainer.parentNode === thisHotContainer || closestHandsontableContainer === thisHotContainer);
}
function getFrameElement(frame) {
    return Object.getPrototypeOf(frame.parent) && frame.frameElement;
}
function getParentWindow(frame) {
    return getFrameElement(frame) && frame.parent;
}
function hasAccessToParentWindow(frame) {
    return !!Object.getPrototypeOf(frame.parent);
}
function closest(element, nodes = [], until) {
    const { ELEMENT_NODE, DOCUMENT_FRAGMENT_NODE } = Node;
    let elementToCheck = element;
    while(elementToCheck !== null && elementToCheck !== undefined && elementToCheck !== until){
        const { nodeType, nodeName } = elementToCheck;
        if (nodeType === ELEMENT_NODE && (nodes.includes(nodeName) || nodes.includes(elementToCheck))) {
            return elementToCheck;
        }
        const { host } = elementToCheck;
        if (host && nodeType === DOCUMENT_FRAGMENT_NODE) {
            elementToCheck = host;
        } else {
            elementToCheck = elementToCheck.parentNode;
        }
    }
    return null;
}
function closestDown(element, nodes, until) {
    const matched = [];
    let elementToCheck = element;
    while(elementToCheck){
        elementToCheck = closest(elementToCheck, nodes, until);
        if (!elementToCheck || until && !until.contains(elementToCheck)) {
            break;
        }
        matched.push(elementToCheck);
        if (elementToCheck.host && elementToCheck.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
            elementToCheck = elementToCheck.host;
        } else {
            elementToCheck = elementToCheck.parentNode;
        }
    }
    const length = matched.length;
    return length ? matched[length - 1] : null;
}
function findFirstParentWithClass(element, className) {
    const matched = {
        element: undefined,
        classNames: []
    };
    let elementToCheck = element;
    while(elementToCheck !== null && elementToCheck !== element.ownerDocument.documentElement && !matched.element){
        if (typeof className === 'string' && elementToCheck.classList.contains(className)) {
            matched.element = elementToCheck;
            matched.classNames.push(className);
        } else if (className instanceof RegExp) {
            const matchingClasses = Array.from(elementToCheck.classList).filter((cls)=>className.test(cls));
            if (matchingClasses.length) {
                matched.element = elementToCheck;
                matched.classNames.push(...matchingClasses);
            }
        }
        elementToCheck = elementToCheck.parentElement;
    }
    return matched;
}
function isChildOf(child, parent) {
    let node = child.parentNode;
    let queriedParents = [];
    if (typeof parent === 'string') {
        if (child.defaultView) {
            queriedParents = Array.prototype.slice.call(child.querySelectorAll(parent), 0);
        } else {
            queriedParents = Array.prototype.slice.call(child.ownerDocument.querySelectorAll(parent), 0);
        }
    } else {
        queriedParents.push(parent);
    }
    while(node !== null){
        if (queriedParents.indexOf(node) > -1) {
            return true;
        }
        node = node.parentNode;
    }
    return false;
}
function index(element) {
    let i = 0;
    let elementToCheck = element;
    if (elementToCheck.previousSibling) {
        /* eslint-disable no-cond-assign */ while(elementToCheck = elementToCheck.previousSibling){
            i += 1;
        }
    }
    return i;
}
function overlayContainsElement(overlayType, element, root) {
    const overlayElement = root.parentElement.querySelector(`.ht_clone_${overlayType}`);
    return overlayElement ? overlayElement.contains(element) : null;
}
function isBottomMostColumnHeader(TH) {
    if (!TH || !TH.classList || !TH.parentNode || !TH.parentNode.parentNode) {
        return false;
    }
    if (TH.classList.contains('hiddenHeader') || TH.style.display === 'none') {
        return false;
    }
    const headerRow = TH.parentNode;
    const headerRows = Array.from(headerRow.parentNode.childNodes);
    const headerLevel = headerRows.indexOf(headerRow);
    const rowspan = Number.parseInt(TH.getAttribute('rowspan'), 10) || 1;
    return headerLevel + rowspan >= headerRows.length;
}
/**
 * @param {string[]} classNames The element "class" attribute string.
 * @returns {string[]}
 */ function filterEmptyClassNames(classNames) {
    if (!classNames || !classNames.length) {
        return [];
    }
    return classNames.filter((x)=>!!x);
}
/**
 * Filter out the RegExp entries from an array.
 *
 * @param {(string|RegExp)[]} list Array of either strings, Regexes or a mix of both.
 * @param {boolean} [returnBoth] If `true`, both the array without regexes and an array of regexes will be returned.
 * @returns {string[]|{regexFree: string[], regexes: RegExp[]}}
 */ function filterRegexes(list, returnBoth) {
    if (!list || !list.length) {
        return returnBoth ? {
            regexFree: [],
            regexes: []
        } : [];
    }
    const regexes = [];
    const regexFree = [];
    regexFree.push(...list.filter((entry)=>{
        const isRegex = entry instanceof RegExp;
        if (isRegex && returnBoth) {
            regexes.push(entry);
        }
        return !isRegex;
    }));
    return returnBoth ? {
        regexFree,
        regexes
    } : regexFree;
}
function hasClass(element, className) {
    if (element.classList === undefined || typeof className !== 'string' || className === '') {
        return false;
    }
    return element.classList.contains(className);
}
function addClass(element, className) {
    if (typeof className === 'string') {
        className = className.split(' ');
    }
    className = filterEmptyClassNames(className);
    if (className.length > 0) {
        element.classList.add(...className);
    }
}
function removeClass(element, className) {
    if (typeof className === 'string') {
        className = className.split(' ');
    } else if (className instanceof RegExp) {
        className = [
            className
        ];
    }
    let { regexFree: stringClasses, // eslint-disable-next-line prefer-const
    regexes: regexClasses } = filterRegexes(className, true);
    stringClasses = filterEmptyClassNames(stringClasses);
    if (stringClasses.length > 0) {
        element.classList.remove(...stringClasses);
    }
    regexClasses.forEach((regexClassName)=>{
        element.classList.forEach((currentClassName)=>{
            if (regexClassName.test(currentClassName)) {
                element.classList.remove(currentClassName);
            }
        });
    });
}
function setAttribute(domElement, attributes = [], attributeValue) {
    if (!Array.isArray(attributes)) {
        attributes = [
            [
                attributes,
                attributeValue
            ]
        ];
    }
    attributes.forEach((attributeInfo)=>{
        if (Array.isArray(attributeInfo) && attributeInfo[0] !== '') {
            domElement.setAttribute(...attributeInfo);
        }
    });
}
function removeAttribute(domElement, attributesToRemove = []) {
    if (typeof attributesToRemove === 'string') {
        attributesToRemove = attributesToRemove.split(' ');
    } else if (attributesToRemove instanceof RegExp) {
        attributesToRemove = [
            attributesToRemove
        ];
    }
    const { regexFree: stringAttributes, regexes: regexAttributes } = filterRegexes(attributesToRemove, true);
    stringAttributes.forEach((attributeNameToRemove)=>{
        if (attributeNameToRemove !== '') {
            domElement.removeAttribute(attributeNameToRemove);
        }
    });
    regexAttributes.forEach((attributeRegex)=>{
        domElement.getAttributeNames().forEach((attributeName)=>{
            if (attributeRegex.test(attributeName)) {
                domElement.removeAttribute(attributeName);
            }
        });
    });
}
function removeTextNodes(element) {
    if (element.nodeType === 3) {
        element.parentNode.removeChild(element); // bye text nodes!
    } else if ([
        'TABLE',
        'THEAD',
        'TBODY',
        'TFOOT',
        'TR'
    ].indexOf(element.nodeName) > -1) {
        const childs = element.childNodes;
        for(let i = childs.length - 1; i >= 0; i--){
            removeTextNodes(childs[i]);
        }
    }
}
function empty(element) {
    let child;
    /* eslint-disable no-cond-assign */ while(child = element.lastChild){
        element.removeChild(child);
    }
}
const HTML_CHARACTERS = /(<(.*)>|&(.*);)/;
let dompurifyDeprecatedMessageShown = false;
function fastInnerHTML(element, content, sanitizer = _string.sanitize) {
    if (HTML_CHARACTERS.test(content)) {
        if (!dompurifyDeprecatedMessageShown && sanitizer === _string.sanitize) {
            dompurifyDeprecatedMessageShown = true;
            (0, _console.deprecatedWarn)('The HTML sanitization using DOMPurify library is deprecated and will be removed in the next major release. ' + 'Use the `sanitizer` option instead.\n\n' + 'Migration guide: https://handsontable.com/docs/migration-from-16.2-to-17.0/\n' + '`sanitizer` documentation: https://handsontable.com/docs/api/options/#sanitizer');
        }
        element.innerHTML = typeof sanitizer === 'function' ? sanitizer(content, 'innerHTML') : content;
    } else {
        fastInnerText(element, content);
    }
}
function fastInnerText(element, content) {
    const child = element.firstChild;
    if (child && child.nodeType === 3 && child.nextSibling === null) {
        // fast lane - replace existing text node
        child.textContent = content;
    } else {
        // slow lane - empty element and insert a text node
        empty(element);
        element.appendChild(element.ownerDocument.createTextNode(content));
    }
}
function isVisible(element) {
    const documentElement = element.ownerDocument.documentElement;
    const windowElement = element.ownerDocument.defaultView;
    let next = element;
    while(next !== documentElement){
        if (next === null) {
            return false;
        } else if (next.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
            if (next.host) {
                // see: http://w3c.github.io/webcomponents/spec/shadow/#encapsulation
                // according to spec, should be if (next.ownerDocument !== window.document), but that doesn't work yet
                if (next.host.impl) {
                    return isVisible(next.host.impl);
                } else if (next.host) {
                    return isVisible(next.host);
                }
                (0, _errors.throwWithCause)('Lost in Web Components world');
            } else {
                return false; // this is a node detached from document in IE8
            }
        } else if (windowElement.getComputedStyle(next).display === 'none') {
            return false;
        }
        next = next.parentNode;
    }
    return true;
}
function hasZeroHeight(element) {
    const rootDocument = element.ownerDocument;
    const rootWindow = rootDocument.defaultView;
    let currentElement = element;
    while(currentElement.parentNode){
        if (currentElement.style.height === '0px' || currentElement.style.height === '0') {
            const computedOverflow = rootWindow.getComputedStyle(currentElement).getPropertyValue('overflow');
            return computedOverflow === 'hidden' || computedOverflow === 'clip';
        }
        currentElement = currentElement.parentNode;
    }
    return false;
}
function offset(element) {
    const rootDocument = element.ownerDocument;
    const rootWindow = rootDocument.defaultView;
    const documentElement = rootDocument.documentElement;
    let elementToCheck = element;
    let offsetLeft;
    let offsetTop;
    let lastElem;
    offsetLeft = elementToCheck.offsetLeft;
    offsetTop = elementToCheck.offsetTop;
    lastElem = elementToCheck;
    /* eslint-disable no-cond-assign */ while(elementToCheck = elementToCheck.offsetParent){
        // from my observation, document.body always has scrollLeft/scrollTop == 0
        if (elementToCheck === rootDocument.body) {
            break;
        }
        // If the element is inside an SVG context, the `offsetParent` can be
        // a <foreignObject> that does not have properties `offsetLeft` and `offsetTop` defined.
        if (!('offsetLeft' in elementToCheck)) {
            break;
        }
        offsetLeft += elementToCheck.offsetLeft;
        offsetTop += elementToCheck.offsetTop;
        lastElem = elementToCheck;
    }
    // slow - http://jsperf.com/offset-vs-getboundingclientrect/6
    if (lastElem && lastElem.style.position === 'fixed') {
        // if(lastElem !== document.body) { //faster but does gives false positive in Firefox
        offsetLeft += rootWindow.pageXOffset || documentElement.scrollLeft;
        offsetTop += rootWindow.pageYOffset || documentElement.scrollTop;
    }
    return {
        left: offsetLeft,
        top: offsetTop
    };
}
function getWindowScrollTop(rootWindow = window) {
    return rootWindow.scrollY;
}
function getWindowScrollLeft(rootWindow = window) {
    return rootWindow.scrollX;
}
function getScrollTop(element, rootWindow = window) {
    if (element === rootWindow) {
        return getWindowScrollTop(rootWindow);
    }
    return element.scrollTop;
}
function getScrollLeft(element, rootWindow = window) {
    if (element === rootWindow) {
        return getWindowScrollLeft(rootWindow);
    }
    return element.scrollLeft;
}
function getScrollableElement(element) {
    let rootDocument = element.ownerDocument;
    let rootWindow = rootDocument ? rootDocument.defaultView : undefined;
    if (!rootDocument) {
        rootDocument = element.document ? element.document : element;
        rootWindow = rootDocument.defaultView;
    }
    const props = [
        'auto',
        'scroll'
    ];
    let el = element.parentNode;
    while(el && el.style && rootDocument.body !== el){
        let { overflow, overflowX, overflowY } = el.style;
        if ([
            overflow,
            overflowX,
            overflowY
        ].includes('scroll')) {
            return el;
        } else {
            ({ overflow, overflowX, overflowY } = rootWindow.getComputedStyle(el));
            if (props.includes(overflow) || props.includes(overflowX) || props.includes(overflowY)) {
                return el;
            }
        }
        // The '+ 1' after the scrollHeight/scrollWidth is to prevent problems with zoomed out Chrome.
        if (el.clientHeight <= el.scrollHeight + 1 && (props.includes(overflowY) || props.includes(overflow))) {
            return el;
        }
        if (el.clientWidth <= el.scrollWidth + 1 && (props.includes(overflowX) || props.includes(overflow))) {
            return el;
        }
        el = el.parentNode;
    }
    return rootWindow;
}
function getMaximumScrollTop(element) {
    return element.scrollHeight - element.clientHeight;
}
function getMaximumScrollLeft(element) {
    return element.scrollWidth - element.clientWidth;
}
function getTrimmingContainer(base) {
    const rootDocument = base.ownerDocument;
    const rootWindow = rootDocument.defaultView;
    let el = base.parentNode;
    while(el && el.style && rootDocument.body !== el){
        if (el.style.overflow !== 'visible' && el.style.overflow !== '') {
            return el;
        }
        const computedStyle = rootWindow.getComputedStyle(el);
        const allowedProperties = [
            'scroll',
            'hidden',
            'auto',
            'clip'
        ];
        const property = computedStyle.getPropertyValue('overflow');
        const propertyY = computedStyle.getPropertyValue('overflow-y');
        const propertyX = computedStyle.getPropertyValue('overflow-x');
        if (allowedProperties.includes(property) || allowedProperties.includes(propertyY) || allowedProperties.includes(propertyX)) {
            return el;
        }
        el = el.parentNode;
    }
    return rootWindow;
}
function getStyle(element, prop, rootWindow = window) {
    if (!element) {
        return;
    } else if (element === rootWindow) {
        if (prop === 'width') {
            return `${rootWindow.innerWidth}px`;
        } else if (prop === 'height') {
            return `${rootWindow.innerHeight}px`;
        }
        return;
    }
    const styleProp = element.style[prop];
    if (styleProp !== '' && styleProp !== undefined) {
        return styleProp;
    }
    const computedStyle = rootWindow.getComputedStyle(element);
    if (computedStyle[prop] !== '' && computedStyle[prop] !== undefined) {
        return computedStyle[prop];
    }
}
function matchesCSSRules(element, rule) {
    const { selectorText } = rule;
    let result = false;
    if (rule.type === CSSRule.STYLE_RULE && selectorText) {
        if (element.msMatchesSelector) {
            result = element.msMatchesSelector(selectorText);
        } else if (element.matches) {
            result = element.matches(selectorText);
        }
    }
    return result;
}
function outerWidth(element) {
    return element.offsetWidth;
}
function outerHeight(element) {
    return element.offsetHeight;
}
function innerHeight(element) {
    return element.clientHeight || element.innerHeight;
}
function innerWidth(element) {
    return element.clientWidth || element.innerWidth;
}
function addEvent(element, event, callback) {
    element.addEventListener(event, callback, false);
}
function removeEvent(element, event, callback) {
    element.removeEventListener(event, callback, false);
}
function getCaretPosition(el) {
    if (el.selectionStart) {
        return el.selectionStart;
    }
    return 0;
}
function getSelectionEndPosition(el) {
    if (el.selectionEnd) {
        return el.selectionEnd;
    }
    return 0;
}
function getSelectionText(rootWindow = window) {
    const rootDocument = rootWindow.document;
    let text = '';
    if (rootWindow.getSelection) {
        text = rootWindow.getSelection().toString();
    } else if (rootDocument.selection && rootDocument.selection.type !== 'Control') {
        text = rootDocument.selection.createRange().text;
    }
    return text;
}
function clearTextSelection(rootWindow = window) {
    // http://stackoverflow.com/questions/3169786/clear-text-selection-with-javascript
    if (rootWindow.getSelection) {
        if (rootWindow.getSelection().empty) {
            rootWindow.getSelection().empty();
        } else if (rootWindow.getSelection().removeAllRanges) {
            rootWindow.getSelection().removeAllRanges();
        }
    }
}
function setCaretPosition(element, pos, endPos) {
    if (endPos === undefined) {
        endPos = pos;
    }
    if (element.setSelectionRange) {
        element.focus();
        try {
            element.setSelectionRange(pos, endPos);
        } catch (err) {
            const elementParent = element.parentNode;
            const parentDisplayValue = elementParent.style.display;
            elementParent.style.display = 'block';
            element.setSelectionRange(pos, endPos);
            elementParent.style.display = parentDisplayValue;
        }
    }
}
function getFractionalScalingCompensation(rootDocument = document) {
    if (!(0, _browser.isWindowsOS)()) {
        return 0;
    }
    // On Windows, fractional scaling makes the scrollbar wider to compensate for the anti-aliasing.
    // This is a workaround to calculate the correct scrollbar width.
    return Number.isInteger(rootDocument.defaultView.devicePixelRatio || 1) ? 0 : 2;
}
/**
 * Helper to calculate scrollbar width.
 * Source: https://stackoverflow.com/questions/986937/how-can-i-get-the-browsers-scrollbar-sizes.
 *
 * @private
 * @param {Document} rootDocument The owner of the document.
 * @returns {number}
 */ // eslint-disable-next-line no-restricted-globals
function walkontableCalculateScrollbarWidth(rootDocument = document) {
    const calculateScrollbarWidth = (shouldForceWebkitScrollbarStyles = false)=>{
        const inner = rootDocument.createElement('div');
        inner.style.height = '200px';
        inner.style.width = '100%';
        const outer = rootDocument.createElement('div');
        outer.classList.add('htScrollbarTest');
        if (shouldForceWebkitScrollbarStyles) {
            outer.classList.add('htScrollbarSafariTest');
        }
        outer.style.boxSizing = 'content-box';
        outer.style.height = '150px';
        outer.style.left = '0px';
        outer.style.overflow = 'hidden';
        outer.style.position = 'absolute';
        outer.style.top = '0px';
        outer.style.width = '200px';
        outer.style.visibility = 'hidden';
        outer.appendChild(inner);
        rootDocument.body.appendChild(outer);
        const w1 = inner.offsetWidth;
        outer.style.overflow = 'scroll';
        let w2 = inner.offsetWidth;
        if (w1 === w2) {
            w2 = outer.clientWidth;
        }
        rootDocument.body.removeChild(outer);
        return w1 - w2;
    };
    const defaultScrollbarWidth = calculateScrollbarWidth();
    // Safari around 26.x (e.g. 26.2/26.3) changed how scrollbars are rendered: overlay scrollbars
    // and the standard scrollbar-color/scrollbar-width properties are preferred. When those are set
    // (e.g. on .wtHolder via theme), they override the non-standard ::-webkit-scrollbar per spec,
    // so the real scrollbar can be 0-width. Older Safari (before 26.1) sometimes reports 0 because
    // it needs explicit ::-webkit-scrollbar size to lay out a classic scrollbar; the fallback below
    // forces that via htScrollbarSafariTest so we get a correct non-zero width. We must only run
    // this fallback when isSafariBefore261(), otherwise Safari 26.1+ with overlay scrollbars would
    // be given 9px from the probe (which has no theme) while .wtHolder actually has 0-width overlay.
    if (defaultScrollbarWidth === 0 && (0, _browser.isSafariBefore261)() && !(0, _browser.isMobileBrowser)() && !(0, _browser.isIpadOS)()) {
        return calculateScrollbarWidth(true);
    }
    return defaultScrollbarWidth;
}
let cachedScrollbarWidth;
function getScrollbarWidth(rootDocument = document) {
    if (cachedScrollbarWidth === undefined) {
        cachedScrollbarWidth = walkontableCalculateScrollbarWidth(rootDocument);
    }
    return cachedScrollbarWidth;
}
function hasVerticalScrollbar(element) {
    if (element instanceof Window) {
        return element.document.body.scrollHeight > element.innerHeight;
    }
    return element.offsetWidth !== element.clientWidth;
}
function hasHorizontalScrollbar(element) {
    if (element instanceof Window) {
        return element.document.body.scrollWidth > element.innerWidth;
    }
    return element.offsetHeight !== element.clientHeight;
}
function setOverlayPosition(overlayElem, left, top) {
    overlayElem.style.transform = `translate3d(${left},${top},0)`;
}
function getCssTransform(element) {
    let transform;
    if (element.style.transform && (transform = element.style.transform) !== '') {
        return [
            'transform',
            transform
        ];
    }
    return -1;
}
function resetCssTransform(element) {
    if (element.style.transform && element.style.transform !== '') {
        element.style.transform = '';
    }
}
function isInput(element) {
    const inputs = [
        'INPUT',
        'SELECT',
        'TEXTAREA'
    ];
    return element && (inputs.indexOf(element.nodeName) > -1 || element.contentEditable === 'true');
}
function isOutsideInput(element) {
    return isInput(element) && element.hasAttribute('data-hot-input') === false;
}
function selectElementIfAllowed(element) {
    const activeElement = element.ownerDocument.activeElement;
    if (!isOutsideInput(activeElement)) {
        element.select();
    }
}
function isDetached(element) {
    return !element.parentNode;
}
function observeVisibilityChangeOnce(elementToBeObserved, callback) {
    const visibilityObserver = new IntersectionObserver((entries, observer)=>{
        entries.forEach((entry)=>{
            if (entry.isIntersecting) {
                callback();
                observer.disconnect();
            }
        });
    });
    visibilityObserver.observe(elementToBeObserved);
}
function makeElementContentEditableAndSelectItsContent(element, invisibleSelection = true, ariaHidden = true) {
    const ownerDocument = element.ownerDocument;
    const range = ownerDocument.createRange();
    const sel = ownerDocument.defaultView.getSelection();
    setAttribute(element, 'contenteditable', true);
    if (ariaHidden) {
        setAttribute(element, ...(0, _a11y.A11Y_HIDDEN)());
    }
    if (invisibleSelection) {
        addClass(element, 'invisibleSelection');
    }
    range.selectNodeContents(element);
    sel.removeAllRanges();
    sel.addRange(range);
}
function removeContentEditableFromElementAndDeselect(selectedElement, removeInvisibleSelectionClass = true) {
    const sel = selectedElement.ownerDocument.defaultView.getSelection();
    if (selectedElement.hasAttribute('aria-hidden')) {
        selectedElement.removeAttribute('aria-hidden');
    }
    sel.removeAllRanges();
    if (removeInvisibleSelectionClass) {
        removeClass(selectedElement, 'invisibleSelection');
    }
    selectedElement.removeAttribute('contenteditable');
}
function runWithSelectedContendEditableElement(element, callback, invisibleSelection = true) {
    makeElementContentEditableAndSelectItsContent(element, invisibleSelection);
    callback();
    removeContentEditableFromElementAndDeselect(element, invisibleSelection);
}
function isHTMLElement(element) {
    const OwnElement = element?.ownerDocument?.defaultView.Element;
    return !!(OwnElement && OwnElement !== null && element instanceof OwnElement);
}
