/*!
 * (C) Ionic http://ionicframework.com - MIT License
 */
import { j as printIonError } from './index-C8IsBmNU.js';
import { w as win } from './ionic-global-2R0iPB51.js';

let animationPrefix;
const getAnimationPrefix = (el) => {
    if (animationPrefix === undefined) {
        const supportsUnprefixed = el.style.animationName !== undefined;
        const supportsWebkitPrefix = el.style.webkitAnimationName !== undefined;
        animationPrefix = !supportsUnprefixed && supportsWebkitPrefix ? '-webkit-' : '';
    }
    return animationPrefix;
};
const setStyleProperty = (element, propertyName, value) => {
    const prefix = propertyName.startsWith('animation') ? getAnimationPrefix(element) : '';
    element.style.setProperty(prefix + propertyName, value);
};
const addClassToArray = (classes = [], className) => {
    if (className !== undefined) {
        const classNameToAppend = Array.isArray(className) ? className : [className];
        return [...classes, ...classNameToAppend];
    }
    return classes;
};

const createAnimation = (animationId) => {
    let _delay;
    let _duration;
    let _easing;
    let _iterations;
    let _fill;
    let _direction;
    let _keyframes = [];
    let beforeAddClasses = [];
    let beforeRemoveClasses = [];
    let initialized = false;
    let parentAnimation;
    let beforeStylesValue = {};
    let afterAddClasses = [];
    let afterRemoveClasses = [];
    let afterStylesValue = {};
    let numAnimationsRunning = 0;
    let shouldForceLinearEasing = false;
    let shouldForceSyncPlayback = false;
    let forceDirectionValue;
    let forceDurationValue;
    let forceDelayValue;
    let willComplete = true;
    let finished = false;
    let shouldCalculateNumAnimations = true;
    let ani;
    let paused = false;
    const id = animationId;
    const onFinishCallbacks = [];
    const onFinishOneTimeCallbacks = [];
    const onStopOneTimeCallbacks = [];
    const elements = [];
    const childAnimations = [];
    const stylesheets = [];
    const _beforeAddReadFunctions = [];
    const _beforeAddWriteFunctions = [];
    const _afterAddReadFunctions = [];
    const _afterAddWriteFunctions = [];
    const webAnimations = [];
    const supportsAnimationEffect = typeof AnimationEffect === 'function' ||
        (win !== undefined && typeof win.AnimationEffect === 'function');
    /**
     * This is a feature detection for Web Animations.
     *
     * Certain environments such as emulated browser environments for testing,
     * do not support Web Animations. As a result, we need to check for support
     * and provide a fallback to test certain functionality related to Web Animations.
     */
    const supportsWebAnimations = typeof Element === 'function' &&
        typeof Element.prototype.animate === 'function' &&
        supportsAnimationEffect;
    const getWebAnimations = () => {
        return webAnimations;
    };
    const destroy = (clearStyleSheets) => {
        childAnimations.forEach((childAnimation) => {
            childAnimation.destroy(clearStyleSheets);
        });
        cleanUp(clearStyleSheets);
        elements.length = 0;
        childAnimations.length = 0;
        _keyframes.length = 0;
        clearOnFinish();
        initialized = false;
        shouldCalculateNumAnimations = true;
        return ani;
    };
    /**
     * Cancels any Web Animations, removes
     * any animation properties from the
     * animation's elements, and removes the
     * animation's stylesheets from the DOM.
     */
    const cleanUp = (clearStyleSheets) => {
        cleanUpElements();
        if (clearStyleSheets) {
            cleanUpStyleSheets();
        }
    };
    const resetFlags = () => {
        shouldForceLinearEasing = false;
        shouldForceSyncPlayback = false;
        shouldCalculateNumAnimations = true;
        forceDirectionValue = undefined;
        forceDurationValue = undefined;
        forceDelayValue = undefined;
        numAnimationsRunning = 0;
        finished = false;
        willComplete = true;
        paused = false;
    };
    const isRunning = () => {
        return numAnimationsRunning !== 0 && !paused;
    };
    /**
     * @internal
     * Remove a callback from a chosen callback array
     * @param callbackToRemove: A reference to the callback that should be removed
     * @param callbackObjects: An array of callbacks that callbackToRemove should be removed from.
     */
    const clearCallback = (callbackToRemove, callbackObjects) => {
        const index = callbackObjects.findIndex((callbackObject) => callbackObject.c === callbackToRemove);
        if (index > -1) {
            callbackObjects.splice(index, 1);
        }
    };
    /**
     * @internal
     * Add a callback to be fired when an animation is stopped/cancelled.
     * @param callback: A reference to the callback that should be fired
     * @param opts: Any options associated with this particular callback
     */
    const onStop = (callback, opts) => {
        onStopOneTimeCallbacks.push({ c: callback, o: opts });
        return ani;
    };
    const onFinish = (callback, opts) => {
        const callbacks = (opts === null || opts === void 0 ? void 0 : opts.oneTimeCallback) ? onFinishOneTimeCallbacks : onFinishCallbacks;
        callbacks.push({ c: callback, o: opts });
        return ani;
    };
    const clearOnFinish = () => {
        onFinishCallbacks.length = 0;
        onFinishOneTimeCallbacks.length = 0;
        return ani;
    };
    /**
     * Cancels any Web Animations and removes
     * any animation properties from the
     * the animation's elements.
     */
    const cleanUpElements = () => {
        if (supportsWebAnimations) {
            webAnimations.forEach((animation) => {
                animation.cancel();
            });
            webAnimations.length = 0;
        }
    };
    /**
     * Removes the animation's stylesheets
     * from the DOM.
     */
    const cleanUpStyleSheets = () => {
        stylesheets.forEach((stylesheet) => {
            /**
             * When sharing stylesheets, it's possible
             * for another animation to have already
             * cleaned up a particular stylesheet
             */
            if (stylesheet === null || stylesheet === void 0 ? void 0 : stylesheet.parentNode) {
                stylesheet.parentNode.removeChild(stylesheet);
            }
        });
        stylesheets.length = 0;
    };
    const beforeAddRead = (readFn) => {
        _beforeAddReadFunctions.push(readFn);
        return ani;
    };
    const beforeAddWrite = (writeFn) => {
        _beforeAddWriteFunctions.push(writeFn);
        return ani;
    };
    const afterAddRead = (readFn) => {
        _afterAddReadFunctions.push(readFn);
        return ani;
    };
    const afterAddWrite = (writeFn) => {
        _afterAddWriteFunctions.push(writeFn);
        return ani;
    };
    const beforeAddClass = (className) => {
        beforeAddClasses = addClassToArray(beforeAddClasses, className);
        return ani;
    };
    const beforeRemoveClass = (className) => {
        beforeRemoveClasses = addClassToArray(beforeRemoveClasses, className);
        return ani;
    };
    /**
     * Set CSS inline styles to the animation's
     * elements before the animation begins.
     */
    const beforeStyles = (styles = {}) => {
        beforeStylesValue = styles;
        return ani;
    };
    /**
     * Clear CSS inline styles from the animation's
     * elements before the animation begins.
     */
    const beforeClearStyles = (propertyNames = []) => {
        for (const property of propertyNames) {
            beforeStylesValue[property] = '';
        }
        return ani;
    };
    const afterAddClass = (className) => {
        afterAddClasses = addClassToArray(afterAddClasses, className);
        return ani;
    };
    const afterRemoveClass = (className) => {
        afterRemoveClasses = addClassToArray(afterRemoveClasses, className);
        return ani;
    };
    const afterStyles = (styles = {}) => {
        afterStylesValue = styles;
        return ani;
    };
    const afterClearStyles = (propertyNames = []) => {
        for (const property of propertyNames) {
            afterStylesValue[property] = '';
        }
        return ani;
    };
    const getFill = () => {
        if (_fill !== undefined) {
            return _fill;
        }
        if (parentAnimation) {
            return parentAnimation.getFill();
        }
        return 'both';
    };
    const getDirection = () => {
        if (forceDirectionValue !== undefined) {
            return forceDirectionValue;
        }
        if (_direction !== undefined) {
            return _direction;
        }
        if (parentAnimation) {
            return parentAnimation.getDirection();
        }
        return 'normal';
    };
    const getEasing = () => {
        if (shouldForceLinearEasing) {
            return 'linear';
        }
        if (_easing !== undefined) {
            return _easing;
        }
        if (parentAnimation) {
            return parentAnimation.getEasing();
        }
        return 'linear';
    };
    const getDuration = () => {
        if (shouldForceSyncPlayback) {
            return 0;
        }
        if (forceDurationValue !== undefined) {
            return forceDurationValue;
        }
        if (_duration !== undefined) {
            return _duration;
        }
        if (parentAnimation) {
            return parentAnimation.getDuration();
        }
        return 0;
    };
    const getIterations = () => {
        if (_iterations !== undefined) {
            return _iterations;
        }
        if (parentAnimation) {
            return parentAnimation.getIterations();
        }
        return 1;
    };
    const getDelay = () => {
        if (forceDelayValue !== undefined) {
            return forceDelayValue;
        }
        if (_delay !== undefined) {
            return _delay;
        }
        if (parentAnimation) {
            return parentAnimation.getDelay();
        }
        return 0;
    };
    const getKeyframes = () => {
        return _keyframes;
    };
    const direction = (animationDirection) => {
        _direction = animationDirection;
        update(true);
        return ani;
    };
    const fill = (animationFill) => {
        _fill = animationFill;
        update(true);
        return ani;
    };
    const delay = (animationDelay) => {
        _delay = animationDelay;
        update(true);
        return ani;
    };
    const easing = (animationEasing) => {
        _easing = animationEasing;
        update(true);
        return ani;
    };
    const duration = (animationDuration) => {
        /**
         * CSS Animation Durations of 0ms work fine on Chrome
         * but do not run on Safari, so force it to 1ms to
         * get it to run on both platforms.
         */
        if (!supportsWebAnimations && animationDuration === 0) {
            animationDuration = 1;
        }
        _duration = animationDuration;
        update(true);
        return ani;
    };
    const iterations = (animationIterations) => {
        _iterations = animationIterations;
        update(true);
        return ani;
    };
    const parent = (animation) => {
        parentAnimation = animation;
        return ani;
    };
    const addElement = (el) => {
        if (el != null) {
            if (el.nodeType === 1) {
                elements.push(el);
            }
            else if (el.length >= 0) {
                for (let i = 0; i < el.length; i++) {
                    elements.push(el[i]);
                }
            }
            else {
                printIonError('createAnimation - Invalid addElement value.');
            }
        }
        return ani;
    };
    const addAnimation = (animationToAdd) => {
        if (animationToAdd != null) {
            if (Array.isArray(animationToAdd)) {
                for (const animation of animationToAdd) {
                    animation.parent(ani);
                    childAnimations.push(animation);
                }
            }
            else {
                animationToAdd.parent(ani);
                childAnimations.push(animationToAdd);
            }
        }
        return ani;
    };
    const keyframes = (keyframeValues) => {
        const different = _keyframes !== keyframeValues;
        _keyframes = keyframeValues;
        if (different) {
            updateKeyframes(_keyframes);
        }
        return ani;
    };
    const updateKeyframes = (keyframeValues) => {
        if (supportsWebAnimations) {
            getWebAnimations().forEach((animation) => {
                /**
                 * animation.effect's type is AnimationEffect.
                 * However, in this case we have a more specific
                 * type of AnimationEffect called KeyframeEffect which
                 * inherits from AnimationEffect. As a result,
                 * we cast animation.effect to KeyframeEffect.
                 */
                const keyframeEffect = animation.effect;
                /**
                 * setKeyframes is not supported in all browser
                 * versions that Ionic supports, so we need to
                 * check for support before using it.
                 */
                // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
                if (keyframeEffect.setKeyframes) {
                    keyframeEffect.setKeyframes(keyframeValues);
                }
                else {
                    const newEffect = new KeyframeEffect(keyframeEffect.target, keyframeValues, keyframeEffect.getTiming());
                    animation.effect = newEffect;
                }
            });
        }
    };
    /**
     * Run all "before" animation hooks.
     */
    const beforeAnimation = () => {
        // Runs all before read callbacks
        _beforeAddReadFunctions.forEach((callback) => callback());
        // Runs all before write callbacks
        _beforeAddWriteFunctions.forEach((callback) => callback());
        // Updates styles and classes before animation runs
        const addClasses = beforeAddClasses;
        const removeClasses = beforeRemoveClasses;
        const styles = beforeStylesValue;
        elements.forEach((el) => {
            const elementClassList = el.classList;
            addClasses.forEach((c) => elementClassList.add(c));
            removeClasses.forEach((c) => elementClassList.remove(c));
            for (const property in styles) {
                // eslint-disable-next-line no-prototype-builtins
                if (styles.hasOwnProperty(property)) {
                    setStyleProperty(el, property, styles[property]);
                }
            }
        });
    };
    /**
     * Run all "after" animation hooks.
     */
    const afterAnimation = () => {
        // Runs all after read callbacks
        _afterAddReadFunctions.forEach((callback) => callback());
        // Runs all after write callbacks
        _afterAddWriteFunctions.forEach((callback) => callback());
        // Updates styles and classes before animation ends
        const currentStep = willComplete ? 1 : 0;
        const addClasses = afterAddClasses;
        const removeClasses = afterRemoveClasses;
        const styles = afterStylesValue;
        elements.forEach((el) => {
            const elementClassList = el.classList;
            addClasses.forEach((c) => elementClassList.add(c));
            removeClasses.forEach((c) => elementClassList.remove(c));
            for (const property in styles) {
                // eslint-disable-next-line no-prototype-builtins
                if (styles.hasOwnProperty(property)) {
                    setStyleProperty(el, property, styles[property]);
                }
            }
        });
        /**
         * Clean up any value coercion before
         * the user callbacks fire otherwise
         * they may get stale values. For example,
         * if someone calls progressStart(0) the
         * animation may still be reversed.
         */
        forceDurationValue = undefined;
        forceDirectionValue = undefined;
        forceDelayValue = undefined;
        onFinishCallbacks.forEach((onFinishCallback) => {
            return onFinishCallback.c(currentStep, ani);
        });
        onFinishOneTimeCallbacks.forEach((onFinishCallback) => {
            return onFinishCallback.c(currentStep, ani);
        });
        onFinishOneTimeCallbacks.length = 0;
        shouldCalculateNumAnimations = true;
        if (willComplete) {
            finished = true;
        }
        willComplete = true;
    };
    const animationFinish = () => {
        if (numAnimationsRunning === 0) {
            return;
        }
        numAnimationsRunning--;
        if (numAnimationsRunning === 0) {
            afterAnimation();
            if (parentAnimation) {
                parentAnimation.animationFinish();
            }
        }
    };
    const initializeWebAnimation = () => {
        elements.forEach((element) => {
            const animation = element.animate(_keyframes, {
                id,
                delay: getDelay(),
                duration: getDuration(),
                easing: getEasing(),
                iterations: getIterations(),
                fill: getFill(),
                direction: getDirection(),
            });
            animation.pause();
            webAnimations.push(animation);
        });
        if (webAnimations.length > 0) {
            webAnimations[0].onfinish = () => {
                animationFinish();
            };
        }
    };
    const initializeAnimation = () => {
        beforeAnimation();
        if (_keyframes.length > 0) {
            if (supportsWebAnimations) {
                initializeWebAnimation();
            }
        }
        initialized = true;
    };
    const setAnimationStep = (step) => {
        step = Math.min(Math.max(step, 0), 0.9999);
        if (supportsWebAnimations) {
            webAnimations.forEach((animation) => {
                // When creating the animation the delay is guaranteed to be set to a number.
                animation.currentTime = animation.effect.getComputedTiming().delay + getDuration() * step;
                animation.pause();
            });
        }
    };
    const updateWebAnimation = (step) => {
        webAnimations.forEach((animation) => {
            animation.effect.updateTiming({
                delay: getDelay(),
                duration: getDuration(),
                easing: getEasing(),
                iterations: getIterations(),
                fill: getFill(),
                direction: getDirection(),
            });
        });
        if (step !== undefined) {
            setAnimationStep(step);
        }
    };
    const update = (deep = false, toggleAnimationName = true, step) => {
        if (deep) {
            childAnimations.forEach((animation) => {
                animation.update(deep, toggleAnimationName, step);
            });
        }
        if (supportsWebAnimations) {
            updateWebAnimation(step);
        }
        return ani;
    };
    const progressStart = (forceLinearEasing = false, step) => {
        childAnimations.forEach((animation) => {
            animation.progressStart(forceLinearEasing, step);
        });
        pauseAnimation();
        shouldForceLinearEasing = forceLinearEasing;
        if (!initialized) {
            initializeAnimation();
        }
        update(false, true, step);
        return ani;
    };
    const progressStep = (step) => {
        childAnimations.forEach((animation) => {
            animation.progressStep(step);
        });
        setAnimationStep(step);
        return ani;
    };
    const progressEnd = (playTo, step, dur) => {
        shouldForceLinearEasing = false;
        childAnimations.forEach((animation) => {
            animation.progressEnd(playTo, step, dur);
        });
        if (dur !== undefined) {
            forceDurationValue = dur;
        }
        finished = false;
        willComplete = true;
        if (playTo === 0) {
            forceDirectionValue = getDirection() === 'reverse' ? 'normal' : 'reverse';
            if (forceDirectionValue === 'reverse') {
                willComplete = false;
            }
            if (supportsWebAnimations) {
                update();
                setAnimationStep(1 - step);
            }
            else {
                forceDelayValue = (1 - step) * getDuration() * -1;
                update(false, false);
            }
        }
        else if (playTo === 1) {
            if (supportsWebAnimations) {
                update();
                setAnimationStep(step);
            }
            else {
                forceDelayValue = step * getDuration() * -1;
                update(false, false);
            }
        }
        if (playTo !== undefined && !parentAnimation) {
            play();
        }
        return ani;
    };
    const pauseAnimation = () => {
        if (initialized) {
            if (supportsWebAnimations) {
                webAnimations.forEach((animation) => {
                    animation.pause();
                });
            }
            else {
                elements.forEach((element) => {
                    setStyleProperty(element, 'animation-play-state', 'paused');
                });
            }
            paused = true;
        }
    };
    const pause = () => {
        childAnimations.forEach((animation) => {
            animation.pause();
        });
        pauseAnimation();
        return ani;
    };
    const playCSSAnimations = () => {
        animationFinish();
    };
    const playWebAnimations = () => {
        webAnimations.forEach((animation) => {
            animation.play();
        });
        if (_keyframes.length === 0 || elements.length === 0) {
            animationFinish();
        }
    };
    const resetAnimation = () => {
        if (supportsWebAnimations) {
            setAnimationStep(0);
            updateWebAnimation();
        }
    };
    const play = (opts) => {
        return new Promise((resolve) => {
            if (opts === null || opts === void 0 ? void 0 : opts.sync) {
                shouldForceSyncPlayback = true;
                onFinish(() => (shouldForceSyncPlayback = false), { oneTimeCallback: true });
            }
            if (!initialized) {
                initializeAnimation();
            }
            if (finished) {
                resetAnimation();
                finished = false;
            }
            if (shouldCalculateNumAnimations) {
                numAnimationsRunning = childAnimations.length + 1;
                shouldCalculateNumAnimations = false;
            }
            /**
             * When one of these callbacks fires we
             * need to clear the other's callback otherwise
             * you can potentially get these callbacks
             * firing multiple times if the play method
             * is subsequently called.
             * Example:
             * animation.play() (onStop and onFinish callbacks are registered)
             * animation.stop() (onStop callback is fired, onFinish is not)
             * animation.play() (onStop and onFinish callbacks are registered)
             * Total onStop callbacks: 1
             * Total onFinish callbacks: 2
             */
            const onStopCallback = () => {
                clearCallback(onFinishCallback, onFinishOneTimeCallbacks);
                resolve();
            };
            const onFinishCallback = () => {
                clearCallback(onStopCallback, onStopOneTimeCallbacks);
                resolve();
            };
            /**
             * The play method resolves when an animation
             * run either finishes or is cancelled.
             */
            onFinish(onFinishCallback, { oneTimeCallback: true });
            onStop(onStopCallback, { oneTimeCallback: true });
            childAnimations.forEach((animation) => {
                animation.play();
            });
            if (supportsWebAnimations) {
                playWebAnimations();
            }
            else {
                playCSSAnimations();
            }
            paused = false;
        });
    };
    /**
     * Stops an animation and resets it state to the
     * beginning. This does not fire any onFinish
     * callbacks because the animation did not finish.
     * However, since the animation was not destroyed
     * (i.e. the animation could run again) we do not
     * clear the onFinish callbacks.
     */
    const stop = () => {
        childAnimations.forEach((animation) => {
            animation.stop();
        });
        if (initialized) {
            cleanUpElements();
            initialized = false;
        }
        resetFlags();
        onStopOneTimeCallbacks.forEach((onStopCallback) => onStopCallback.c(0, ani));
        onStopOneTimeCallbacks.length = 0;
    };
    const from = (property, value) => {
        const firstFrame = _keyframes[0];
        if (firstFrame !== undefined && (firstFrame.offset === undefined || firstFrame.offset === 0)) {
            firstFrame[property] = value;
        }
        else {
            _keyframes = [{ offset: 0, [property]: value }, ..._keyframes];
        }
        return ani;
    };
    const to = (property, value) => {
        const lastFrame = _keyframes[_keyframes.length - 1];
        if (lastFrame !== undefined && (lastFrame.offset === undefined || lastFrame.offset === 1)) {
            lastFrame[property] = value;
        }
        else {
            _keyframes = [..._keyframes, { offset: 1, [property]: value }];
        }
        return ani;
    };
    const fromTo = (property, fromValue, toValue) => {
        return from(property, fromValue).to(property, toValue);
    };
    return (ani = {
        parentAnimation,
        elements,
        childAnimations,
        id,
        animationFinish,
        from,
        to,
        fromTo,
        parent,
        play,
        pause,
        stop,
        destroy,
        keyframes,
        addAnimation,
        addElement,
        update,
        fill,
        direction,
        iterations,
        duration,
        easing,
        delay,
        getWebAnimations,
        getKeyframes,
        getFill,
        getDirection,
        getDelay,
        getIterations,
        getEasing,
        getDuration,
        afterAddRead,
        afterAddWrite,
        afterClearStyles,
        afterStyles,
        afterRemoveClass,
        afterAddClass,
        beforeAddRead,
        beforeAddWrite,
        beforeClearStyles,
        beforeStyles,
        beforeRemoveClass,
        beforeAddClass,
        onFinish,
        isRunning,
        progressStart,
        progressStep,
        progressEnd,
    });
};

export { createAnimation as c };
