(function () {
    var DEFAULT_SVGA_SCRIPT =
        'https://cdn.jsdelivr.net/npm/svgaplayerweb@2.3.2/build/svga.min.js';
    var DEFAULT_PAG_BASE = 'https://cdn.jsdelivr.net/npm/libpag@4.5.68/lib/';
    var scriptCache = {};
    var pagInitCache = {};
    var videoLayoutCache = {};
    var svgaVideoItemCache = {};
    var MIN_SPLIT_MATCH_FRAMES = 3;
    var MIN_USEFUL_FRAME_BRIGHTNESS = 0.035;
    var MIN_SPLIT_SATURATION_GAP = 0.08;
    var MAX_SPLIT_MASK_SATURATION = 0.28;
    var MAX_SPLIT_MASK_GRAY_ERROR = 0.06;

    function extend(target, source) {
        var output = target || {};
        source = source || {};
        Object.keys(source).forEach(function (key) {
            if (source[key] !== undefined) output[key] = source[key];
        });
        return output;
    }

    function resolveElement(target) {
        if (!target) return null;
        if (typeof target === 'string') return document.querySelector(target);
        return target;
    }

    function loadScriptOnce(url) {
        if (scriptCache[url]) return scriptCache[url];
        scriptCache[url] = new Promise(function (resolve, reject) {
            var script = document.createElement('script');
            script.src = url;
            script.async = true;
            script.onload = resolve;
            script.onerror = function () {
                reject(new Error('script_load_failed'));
            };
            document.head.appendChild(script);
        });
        return scriptCache[url];
    }

    function loadSVGAItemOnce(sourceURL, canvas, options) {
        if (svgaVideoItemCache[sourceURL]) return svgaVideoItemCache[sourceURL];
        svgaVideoItemCache[sourceURL] = loadScriptOnce(options.svgaScript)
            .then(function () {
                if (!window.SVGA) throw new Error('svga_unavailable');
                return new Promise(function (resolve, reject) {
                    var parser = new window.SVGA.Parser(canvas);
                    parser.load(sourceURL, resolve, reject);
                });
            })
            .catch(function (error) {
                delete svgaVideoItemCache[sourceURL];
                throw error;
            });
        return svgaVideoItemCache[sourceURL];
    }

    function waitForWindowLoad() {
        if (document.readyState === 'complete') return Promise.resolve();
        return new Promise(function (resolve) {
            window.addEventListener('load', resolve, { once: true });
        });
    }

    function extensionFromURL(url) {
        var clean = String(url || '')
            .split('?')[0]
            .split('#')[0];
        var match = clean.match(/\.([a-z0-9]+)$/i);
        return match ? match[1].toLowerCase() : '';
    }

    function inferType(url, explicitType) {
        var type = String(explicitType || '').toLowerCase();
        if (type) return type;
        var ext = extensionFromURL(url);
        if (ext === 'mp4' || ext === 'webm' || ext === 'mov') return 'video';
        if (ext === 'svga') return 'svga';
        if (ext === 'pag') return 'pag';
        return '';
    }

    function sameOrigin(url) {
        try {
            return (
                new URL(url, window.location.href).origin ===
                window.location.origin
            );
        } catch (_) {
            return true;
        }
    }

    function proxyBinaryURL(url, type, options) {
        if (options.proxy === false) return url;
        if (type !== 'svga' && type !== 'pag') return url;
        if (sameOrigin(url)) return url;
        if (typeof options.proxyURL === 'function') {
            return options.proxyURL(url, type) || url;
        }
        if (typeof options.proxyURL === 'string' && options.proxyURL) {
            var base = options.proxyURL;
            var separator = base.indexOf('?') >= 0 ? '&' : '?';
            return base + separator + 'url=' + encodeURIComponent(url);
        }
        if (window.HyAppAPI && typeof window.HyAppAPI.buildURL === 'function') {
            return window.HyAppAPI.buildURL('/api/v1/media/effects/proxy', {
                url: url,
            });
        }
        return url;
    }

    function mediaSize(container, options) {
        return {
            width:
                Number(options.width) ||
                (container && container.clientWidth) ||
                300,
            height:
                Number(options.height) ||
                (container && container.clientHeight) ||
                300,
        };
    }

    function styleNode(node, options) {
        node.style.display = 'block';
        node.style.width = '100%';
        node.style.height = '100%';
        if (node.tagName === 'VIDEO' || node.tagName === 'CANVAS') {
            node.style.objectFit = options.fit || 'contain';
        }
    }

    function clamp(value, min, max) {
        return Math.max(min, Math.min(max, value));
    }

    function median(values) {
        if (!values.length) return 0;
        var sorted = values.slice().sort(function (a, b) {
            return a - b;
        });
        return sorted[Math.floor(sorted.length / 2)];
    }

    function fullFrameLayout(video, kind, hasNativeAlpha) {
        var width = Math.max(1, video.videoWidth || 1);
        var height = Math.max(1, video.videoHeight || 1);
        var rect = { x: 0, y: 0, width: 1, height: 1 };
        return {
            kind: kind || 'normal',
            hasAlphaMask: false,
            hasNativeAlpha: !!hasNativeAlpha,
            contentAspectRatio: width / height,
            colorRect: rect,
            alphaRect: rect,
        };
    }

    function waitForMediaEvent(target, eventName, timeoutMS) {
        return new Promise(function (resolve, reject) {
            var timer = 0;
            function cleanup() {
                window.clearTimeout(timer);
                target.removeEventListener(eventName, handleEvent);
                target.removeEventListener('error', handleError);
            }
            function handleEvent() {
                cleanup();
                resolve();
            }
            function handleError() {
                cleanup();
                reject(new Error('video_' + eventName + '_failed'));
            }
            target.addEventListener(eventName, handleEvent, { once: true });
            target.addEventListener('error', handleError, { once: true });
            timer = window.setTimeout(function () {
                cleanup();
                reject(new Error('video_' + eventName + '_timeout'));
            }, timeoutMS || 4000);
        });
    }

    function loadVideoMetadata(video) {
        if (video.readyState >= 1 && video.videoWidth && video.videoHeight) {
            return Promise.resolve();
        }
        return waitForMediaEvent(video, 'loadedmetadata', 8000);
    }

    function seekVideo(video, time) {
        var duration = Number(video.duration || 0);
        var targetTime = duration
            ? clamp(time, 0, Math.max(0, duration - 0.05))
            : Math.max(0, time);
        if (Math.abs(video.currentTime - targetTime) < 0.04) {
            return Promise.resolve();
        }
        video.currentTime = targetTime;
        return waitForMediaEvent(video, 'seeked', 2500).catch(function () {});
    }

    function videoSampleTimes(duration, mode) {
        var base =
            mode === 'packed'
                ? [0, 0.5, 1, 2, 3]
                : [0.3, 0.6, 0.9, 1.2, 1.5, 1.8, 2.1, 2.4, 2.7, 3];
        var filtered = base.filter(function (time) {
            return !duration || !Number.isFinite(duration) || time <= duration;
        });
        return filtered.length ? filtered : [duration ? duration / 2 : 0];
    }

    function uniqueTimes(times) {
        var seen = {};
        return times
            .slice()
            .sort(function (a, b) {
                return a - b;
            })
            .filter(function (time) {
                var key = time.toFixed(2);
                if (seen[key]) return false;
                seen[key] = true;
                return true;
            });
    }

    function readVideoFrame(video, maxWidth) {
        var sourceWidth = Math.max(1, video.videoWidth || 1);
        var sourceHeight = Math.max(1, video.videoHeight || 1);
        var sampleWidth = Math.min(sourceWidth, Number(maxWidth) || 480);
        var sampleHeight = Math.max(
            1,
            Math.round((sampleWidth * sourceHeight) / sourceWidth)
        );
        var canvas = document.createElement('canvas');
        var context = canvas.getContext('2d', { willReadFrequently: true });
        canvas.width = sampleWidth;
        canvas.height = sampleHeight;
        context.clearRect(0, 0, sampleWidth, sampleHeight);
        context.drawImage(video, 0, 0, sampleWidth, sampleHeight);
        return {
            width: sampleWidth,
            height: sampleHeight,
            data: context.getImageData(0, 0, sampleWidth, sampleHeight).data,
        };
    }

    function pixelAt(frame, x, y) {
        var safeX = clamp(Math.round(x), 0, frame.width - 1);
        var safeY = clamp(Math.round(y), 0, frame.height - 1);
        var index = (safeY * frame.width + safeX) * 4;
        return {
            r: frame.data[index],
            g: frame.data[index + 1],
            b: frame.data[index + 2],
            a: frame.data[index + 3],
        };
    }

    function hasNativeAlpha(frames) {
        for (var index = 0; index < frames.length; index += 1) {
            var data = frames[index].data;
            var transparent = 0;
            var sampled = 0;
            var step = Math.max(4, Math.floor(data.length / (4 * 6000)) * 4);
            for (var offset = 3; offset < data.length; offset += step) {
                sampled += 1;
                if (data[offset] < 245) transparent += 1;
            }
            if (sampled && transparent / sampled > 0.003) return true;
        }
        return false;
    }

    function isLikelyAlphaMaskPixel(pixel) {
        var maxChannel = Math.max(pixel.r, pixel.g, pixel.b);
        if (maxChannel < 22) return false;
        var minChannel = Math.min(pixel.r, pixel.g, pixel.b);
        var saturation = maxChannel
            ? (maxChannel - minChannel) / maxChannel
            : 0;
        var grayError =
            (Math.abs(pixel.r - pixel.g) +
                Math.abs(pixel.g - pixel.b) +
                Math.abs(pixel.b - pixel.r)) /
            (3 * 255);
        return saturation < 0.16 && grayError < 0.055;
    }

    function detectPackedAlphaTopRightInFrame(frame) {
        var width = frame.width;
        var height = frame.height;
        if (width < 12 || height < 12) return null;

        var searchStart = clamp(Math.floor(width * 0.54), 0, width - 1);
        var sampleHeight = clamp(Math.floor(height * 0.58), 1, height);
        var stepY = Math.max(1, Math.floor(sampleHeight / 220));
        var rowSamples = Math.max(1, Math.ceil(sampleHeight / stepY));
        var threshold = Math.max(8, Math.floor(rowSamples * 0.32));
        var grayColumns = [];

        for (var x = searchStart; x < width; x += 1) {
            var grayCount = 0;
            for (var y = 0; y < sampleHeight; y += stepY) {
                if (isLikelyAlphaMaskPixel(pixelAt(frame, x, y))) {
                    grayCount += 1;
                }
            }
            grayColumns[x] = grayCount >= threshold;
        }

        var minRangeWidth = Math.max(24, Math.floor(width * 0.12));
        var bestStart = -1;
        var bestEnd = -1;
        var cursor = searchStart;
        while (cursor < width) {
            while (cursor < width && !grayColumns[cursor]) cursor += 1;
            if (cursor >= width) break;
            var start = cursor;
            while (cursor < width && grayColumns[cursor]) cursor += 1;
            var end = cursor - 1;
            if (end - start + 1 >= minRangeWidth && end > bestEnd) {
                bestStart = start;
                bestEnd = end;
            }
        }
        if (bestStart < 0 || bestEnd < 0) return null;
        return {
            frameWidth: width,
            frameHeight: height,
            alphaStartX: bestStart,
            alphaEndX: bestEnd,
        };
    }

    function detectPackedAlphaTopRight(frames) {
        var candidates = frames
            .map(detectPackedAlphaTopRightInFrame)
            .filter(Boolean);
        if (!candidates.length) return null;
        var frameWidth = median(
            candidates.map(function (item) {
                return item.frameWidth;
            })
        );
        var frameHeight = median(
            candidates.map(function (item) {
                return item.frameHeight;
            })
        );
        var alphaStartX = median(
            candidates.map(function (item) {
                return item.alphaStartX;
            })
        );
        var alphaEndX = median(
            candidates.map(function (item) {
                return item.alphaEndX;
            })
        );
        var alphaWidth = alphaEndX - alphaStartX + 1;
        var alphaStartRatio = alphaStartX / frameWidth;
        var alphaWidthRatio = alphaWidth / frameWidth;
        var colorToAlphaRatio = alphaStartX / alphaWidth;
        if (
            alphaStartRatio < 0.58 ||
            alphaStartRatio > 0.78 ||
            alphaWidthRatio < 0.18 ||
            alphaWidthRatio > 0.42 ||
            colorToAlphaRatio < 1.45 ||
            colorToAlphaRatio > 2.65 ||
            alphaEndX < frameWidth * 0.82
        ) {
            return null;
        }

        var colorWidth = clamp(alphaStartX, 1, frameWidth - 1);
        var safeAlphaEndX = clamp(alphaEndX, colorWidth, frameWidth - 1);
        var safeAlphaWidth = Math.max(1, safeAlphaEndX - colorWidth + 1);
        var alphaHeight = clamp(
            Math.round((frameHeight * safeAlphaWidth) / colorWidth),
            1,
            frameHeight
        );
        return {
            kind: 'packedAlphaTopRight',
            hasAlphaMask: true,
            hasNativeAlpha: false,
            contentAspectRatio: colorWidth / frameHeight,
            colorRect: {
                x: 0,
                y: 0,
                width: colorWidth / frameWidth,
                height: 1,
            },
            alphaRect: {
                x: colorWidth / frameWidth,
                y: 0,
                width: safeAlphaWidth / frameWidth,
                height: alphaHeight / frameHeight,
            },
        };
    }

    function statsForRect(frame, startX, startY, endX, endY) {
        var width = Math.max(1, endX - startX);
        var height = Math.max(1, endY - startY);
        var stepX = Math.max(1, Math.floor(width / 80));
        var stepY = Math.max(1, Math.floor(height / 120));
        var saturationSum = 0;
        var grayErrorSum = 0;
        var brightnessSum = 0;
        var count = 0;
        for (var y = startY; y < endY; y += stepY) {
            for (var x = startX; x < endX; x += stepX) {
                var pixel = pixelAt(frame, x, y);
                var red = pixel.r / 255;
                var green = pixel.g / 255;
                var blue = pixel.b / 255;
                var maxChannel = Math.max(red, green, blue);
                var minChannel = Math.min(red, green, blue);
                saturationSum += maxChannel
                    ? (maxChannel - minChannel) / maxChannel
                    : 0;
                grayErrorSum +=
                    (Math.abs(red - green) +
                        Math.abs(green - blue) +
                        Math.abs(blue - red)) /
                    3;
                brightnessSum += red * 0.299 + green * 0.587 + blue * 0.114;
                count += 1;
            }
        }
        if (!count) {
            return { saturation: 0, grayError: 0, brightness: 0 };
        }
        return {
            saturation: saturationSum / count,
            grayError: grayErrorSum / count,
            brightness: brightnessSum / count,
        };
    }

    function statsByHorizontalHalves(frame) {
        var half = Math.floor(frame.width / 2);
        return [
            statsForRect(frame, 0, 0, half, frame.height),
            statsForRect(frame, half, 0, frame.width, frame.height),
        ];
    }

    function statsByVerticalHalves(frame) {
        var half = Math.floor(frame.height / 2);
        return [
            statsForRect(frame, 0, 0, frame.width, half),
            statsForRect(frame, 0, half, frame.width, frame.height),
        ];
    }

    function addSplitScore(score, first, second) {
        if (
            Math.max(first.brightness, second.brightness) <
            MIN_USEFUL_FRAME_BRIGHTNESS
        ) {
            return;
        }
        var alphaStats = first.saturation < second.saturation ? first : second;
        var saturationGap = Math.abs(second.saturation - first.saturation);
        if (
            saturationGap < MIN_SPLIT_SATURATION_GAP ||
            alphaStats.saturation > MAX_SPLIT_MASK_SATURATION ||
            alphaStats.grayError > MAX_SPLIT_MASK_GRAY_ERROR
        ) {
            return;
        }
        score.direction += second.saturation - first.saturation;
        score.saturationGap += saturationGap;
        score.maskSaturation += alphaStats.saturation;
        score.maskGrayError += alphaStats.grayError;
        score.frames += 1;
    }

    function splitCandidateFromScore(orientation, score) {
        if (score.frames < MIN_SPLIT_MATCH_FRAMES) return null;
        var averageSaturationGap = score.saturationGap / score.frames;
        var averageMaskSaturation = score.maskSaturation / score.frames;
        var averageMaskGrayError = score.maskGrayError / score.frames;
        if (
            averageSaturationGap < MIN_SPLIT_SATURATION_GAP ||
            averageMaskSaturation > MAX_SPLIT_MASK_SATURATION ||
            averageMaskGrayError > MAX_SPLIT_MASK_GRAY_ERROR
        ) {
            return null;
        }
        return {
            candidate: {
                orientation: orientation,
                alphaFirst: score.direction > 0,
            },
            score: averageSaturationGap,
        };
    }

    function splitSourceLayout(candidate, video) {
        var left = { x: 0, y: 0, width: 0.5, height: 1 };
        var right = { x: 0.5, y: 0, width: 0.5, height: 1 };
        var top = { x: 0, y: 0, width: 1, height: 0.5 };
        var bottom = { x: 0, y: 0.5, width: 1, height: 0.5 };
        var isHorizontal = candidate.orientation === 'horizontal';
        var first = isHorizontal ? left : top;
        var second = isHorizontal ? right : bottom;
        var width = Math.max(1, video.videoWidth || 1);
        var height = Math.max(1, video.videoHeight || 1);
        return {
            kind: isHorizontal ? 'splitAlphaHorizontal' : 'splitAlphaVertical',
            hasAlphaMask: true,
            hasNativeAlpha: false,
            contentAspectRatio: isHorizontal
                ? width / 2 / height
                : width / (height / 2),
            colorRect: candidate.alphaFirst ? second : first,
            alphaRect: candidate.alphaFirst ? first : second,
        };
    }

    function detectSplitAlpha(frames, video) {
        var horizontalScore = {
            direction: 0,
            saturationGap: 0,
            maskSaturation: 0,
            maskGrayError: 0,
            frames: 0,
        };
        var verticalScore = {
            direction: 0,
            saturationGap: 0,
            maskSaturation: 0,
            maskGrayError: 0,
            frames: 0,
        };
        frames.forEach(function (frame) {
            var horizontal = statsByHorizontalHalves(frame);
            var vertical = statsByVerticalHalves(frame);
            addSplitScore(horizontalScore, horizontal[0], horizontal[1]);
            addSplitScore(verticalScore, vertical[0], vertical[1]);
        });
        var candidates = [
            splitCandidateFromScore('horizontal', horizontalScore),
            splitCandidateFromScore('vertical', verticalScore),
        ].filter(Boolean);
        if (!candidates.length) return null;
        candidates.sort(function (a, b) {
            return b.score - a.score;
        });
        return splitSourceLayout(candidates[0].candidate, video);
    }

    function sampleVideoFrames(video, mode, maxWidth) {
        var times = uniqueTimes(videoSampleTimes(video.duration || 0, mode));
        var frames = [];
        var chain = Promise.resolve();
        times.forEach(function (time) {
            chain = chain
                .then(function () {
                    return seekVideo(video, time);
                })
                .then(function () {
                    frames.push(readVideoFrame(video, maxWidth));
                });
        });
        return chain.then(function () {
            return frames;
        });
    }

    function detectVideoLayout(video, url, options) {
        var cacheKey = String(url || '');
        if (videoLayoutCache[cacheKey]) {
            return Promise.resolve(videoLayoutCache[cacheKey]);
        }
        var maxWidth = Number(options.videoDetectionSampleWidth) || 480;
        return loadVideoMetadata(video)
            .then(function () {
                return sampleVideoFrames(video, 'packed', maxWidth);
            })
            .then(function (packedFrames) {
                if (hasNativeAlpha(packedFrames)) {
                    return fullFrameLayout(video, 'nativeAlpha', true);
                }
                return (
                    detectPackedAlphaTopRight(packedFrames) ||
                    sampleVideoFrames(video, 'split', maxWidth).then(
                        function (splitFrames) {
                            return (
                                detectSplitAlpha(splitFrames, video) ||
                                fullFrameLayout(video, 'normal', false)
                            );
                        }
                    )
                );
            })
            .then(function (layout) {
                videoLayoutCache[cacheKey] = layout;
                return layout;
            });
    }

    function rectToPixels(rect, width, height) {
        var x = clamp(Math.round(rect.x * width), 0, width - 1);
        var y = clamp(Math.round(rect.y * height), 0, height - 1);
        var right = clamp(
            Math.round((rect.x + rect.width) * width),
            x + 1,
            width
        );
        var bottom = clamp(
            Math.round((rect.y + rect.height) * height),
            y + 1,
            height
        );
        return {
            x: x,
            y: y,
            width: Math.max(1, right - x),
            height: Math.max(1, bottom - y),
        };
    }

    function layoutCanvasSize(video, layout, options) {
        var videoWidth = Math.max(1, video.videoWidth || 1);
        var videoHeight = Math.max(1, video.videoHeight || 1);
        var rect = layout.hasAlphaMask
            ? rectToPixels(layout.colorRect, videoWidth, videoHeight)
            : { width: videoWidth, height: videoHeight };
        var maxDimension = Number(options.maxVideoCanvasDimension) || 1200;
        var scale = Math.min(
            1,
            maxDimension / Math.max(rect.width, rect.height)
        );
        return {
            width: Math.max(1, Math.round(rect.width * scale)),
            height: Math.max(1, Math.round(rect.height * scale)),
        };
    }

    function renderMaskedVideoFrame(video, canvas, layout, scratch) {
        var videoWidth = Math.max(1, video.videoWidth || 1);
        var videoHeight = Math.max(1, video.videoHeight || 1);
        var outWidth = canvas.width;
        var outHeight = canvas.height;
        var context = scratch.outputContext;
        context.clearRect(0, 0, outWidth, outHeight);

        if (!layout.hasAlphaMask) {
            context.drawImage(video, 0, 0, outWidth, outHeight);
            return;
        }

        var colorRect = rectToPixels(layout.colorRect, videoWidth, videoHeight);
        var alphaRect = rectToPixels(layout.alphaRect, videoWidth, videoHeight);
        scratch.colorCanvas.width = outWidth;
        scratch.colorCanvas.height = outHeight;
        scratch.alphaCanvas.width = outWidth;
        scratch.alphaCanvas.height = outHeight;
        scratch.colorContext.clearRect(0, 0, outWidth, outHeight);
        scratch.alphaContext.clearRect(0, 0, outWidth, outHeight);
        scratch.colorContext.drawImage(
            video,
            colorRect.x,
            colorRect.y,
            colorRect.width,
            colorRect.height,
            0,
            0,
            outWidth,
            outHeight
        );
        scratch.alphaContext.drawImage(
            video,
            alphaRect.x,
            alphaRect.y,
            alphaRect.width,
            alphaRect.height,
            0,
            0,
            outWidth,
            outHeight
        );
        var colorData = scratch.colorContext.getImageData(
            0,
            0,
            outWidth,
            outHeight
        );
        var alphaData = scratch.alphaContext.getImageData(
            0,
            0,
            outWidth,
            outHeight
        );
        for (var offset = 0; offset < colorData.data.length; offset += 4) {
            colorData.data[offset + 3] =
                alphaData.data[offset] * 0.299 +
                alphaData.data[offset + 1] * 0.587 +
                alphaData.data[offset + 2] * 0.114;
        }
        context.putImageData(colorData, 0, 0);
    }

    function initPAG(libBase) {
        var base = libBase || DEFAULT_PAG_BASE;
        if (pagInitCache[base]) return pagInitCache[base];
        pagInitCache[base] = loadScriptOnce(base + 'libpag.umd.js').then(
            function () {
                if (!window.libpag) throw new Error('pag_unavailable');
                return window.libpag.PAGInit({
                    locateFile: function (file) {
                        return base + file;
                    },
                });
            }
        );
        return pagInitCache[base];
    }

    function EffectPlayer(target, options) {
        this.container = resolveElement(target);
        this.options = extend(
            {
                activeClass: 'is-active',
                fit: 'contain',
                loop: true,
                muted: true,
                waitForWindowLoad: true,
                svgaScript: DEFAULT_SVGA_SCRIPT,
                pagBase: DEFAULT_PAG_BASE,
            },
            options || {}
        );
        this.url = '';
        this.active = false;
        this.cleanup = null;
        this.current = null;
        this.sequence = 0;
        this.destroyed = false;
    }

    EffectPlayer.prototype._teardown = function () {
        if (this.cleanup) {
            try {
                this.cleanup();
            } catch (_) {}
        }
        this.cleanup = null;
        this.active = false;
        this.current = null;
        if (this.container) {
            this.container.classList.remove(this.options.activeClass);
            this.container.innerHTML = '';
        }
    };

    EffectPlayer.prototype._mount = function (node) {
        if (!this.container) throw new Error('container_unavailable');
        this.container.innerHTML = '';
        if (node) this.container.appendChild(node);
        this.container.classList.add(this.options.activeClass);
        this.active = true;
    };

    EffectPlayer.prototype._isCurrent = function (url) {
        return !this.destroyed && this.url === url;
    };

    EffectPlayer.prototype._createVideoElement = function (
        url,
        options,
        useCORS
    ) {
        var video = document.createElement('video');
        if (useCORS) video.crossOrigin = 'anonymous';
        video.src = url;
        video.autoplay = true;
        video.loop = options.loop !== false;
        video.muted = options.muted !== false;
        video.playsInline = true;
        video.setAttribute('webkit-playsinline', 'true');
        video.preload = options.preload || 'auto';
        return video;
    };

    EffectPlayer.prototype._playPlainVideo = function (url, options, video) {
        if (!this._isCurrent(url)) return Promise.resolve();
        video = video || this._createVideoElement(url, options, false);
        styleNode(video, options);
        this._mount(video);
        this.cleanup = function () {
            video.pause();
            video.removeAttribute('src');
            video.load();
        };
        return video.play().catch(function () {});
    };

    EffectPlayer.prototype._playCanvasVideo = function (
        url,
        options,
        video,
        layout
    ) {
        var self = this;
        if (!this._isCurrent(url)) return Promise.resolve();
        var size = layoutCanvasSize(video, layout, options);
        var canvas = document.createElement('canvas');
        canvas.width = size.width;
        canvas.height = size.height;
        styleNode(canvas, options);
        var scratch = {
            outputContext: canvas.getContext('2d', {
                alpha: true,
                willReadFrequently: !!layout.hasAlphaMask,
            }),
            colorCanvas: document.createElement('canvas'),
            alphaCanvas: document.createElement('canvas'),
        };
        scratch.colorContext = scratch.colorCanvas.getContext('2d', {
            willReadFrequently: true,
        });
        scratch.alphaContext = scratch.alphaCanvas.getContext('2d', {
            willReadFrequently: true,
        });
        var raf = 0;
        function draw() {
            if (!self._isCurrent(url)) return;
            if (video.readyState >= 2) {
                renderMaskedVideoFrame(video, canvas, layout, scratch);
            }
            raf = window.requestAnimationFrame(draw);
        }
        this._mount(canvas);
        this.cleanup = function () {
            window.cancelAnimationFrame(raf);
            video.pause();
            video.removeAttribute('src');
            video.load();
        };
        draw();
        return seekVideo(video, 0).then(function () {
            return video.play().catch(function () {});
        });
    };

    EffectPlayer.prototype._playVideo = function (url, options) {
        var self = this;
        if (!this._isCurrent(url)) return Promise.resolve();
        if (options.detectTransparentVideo === false) {
            return this._playPlainVideo(url, options);
        }
        var video = this._createVideoElement(url, options, true);
        return detectVideoLayout(video, url, options)
            .then(function (layout) {
                if (!self._isCurrent(url)) return undefined;
                if (!layout.hasAlphaMask && !layout.hasNativeAlpha) {
                    return self._playPlainVideo(url, options, video);
                }
                return self._playCanvasVideo(url, options, video, layout);
            })
            .catch(function () {
                if (!self._isCurrent(url)) return undefined;
                return self._playPlainVideo(url, options);
            });
    };

    EffectPlayer.prototype._playSVGA = function (url, options) {
        var self = this;
        var sourceURL = proxyBinaryURL(url, 'svga', options);
        var size = mediaSize(self.container, options);
        var canvas = document.createElement('canvas');
        canvas.width = size.width;
        canvas.height = size.height;
        styleNode(canvas, options);
        self._mount(canvas);
        return loadSVGAItemOnce(sourceURL, canvas, options).then(
            function (videoItem) {
                if (!self._isCurrent(url)) return undefined;
                if (!window.SVGA) throw new Error('svga_unavailable');
                var player = new window.SVGA.Player(canvas);
                self.cleanup = function () {
                    if (player.stopAnimation) player.stopAnimation();
                    if (player.clear) player.clear();
                };
                player.setVideoItem(videoItem);
                player.startAnimation();
                return undefined;
            }
        );
    };

    EffectPlayer.prototype._playPAG = function (url, options) {
        var self = this;
        var sourceURL = proxyBinaryURL(url, 'pag', options);
        return initPAG(options.pagBase)
            .then(function (PAG) {
                if (!self._isCurrent(url)) return undefined;
                return fetch(sourceURL).then(function (response) {
                    if (!response.ok) throw new Error('pag_fetch_failed');
                    return response.arrayBuffer().then(function (buffer) {
                        return { PAG: PAG, buffer: buffer };
                    });
                });
            })
            .then(function (payload) {
                if (!payload || !self._isCurrent(url)) return undefined;
                var size = mediaSize(self.container, options);
                var canvas = document.createElement('canvas');
                canvas.width = size.width;
                canvas.height = size.height;
                styleNode(canvas, options);
                self._mount(canvas);
                return payload.PAG.PAGFile.load(payload.buffer).then(
                    function (pagFile) {
                        return payload.PAG.PAGView.init(pagFile, canvas, {
                            useScale: false,
                        });
                    }
                );
            })
            .then(function (pagView) {
                if (!self._isCurrent(url)) return undefined;
                if (!pagView) throw new Error('pag_view_unavailable');
                self.cleanup = function () {
                    if (pagView.destroy) pagView.destroy();
                };
                return pagView.play();
            });
    };

    EffectPlayer.prototype._playNow = function (url, options) {
        var type = inferType(url, options.type);
        if (type === 'video' || type === 'mp4') {
            return this._playVideo(url, options);
        }
        if (type === 'svga') return this._playSVGA(url, options);
        if (type === 'pag') return this._playPAG(url, options);
        return Promise.reject(new Error('unsupported_effect_type'));
    };

    EffectPlayer.prototype.play = function (url, options) {
        var self = this;
        var nextURL = String(url || '').trim();
        var nextOptions = extend(extend({}, this.options), options || {});
        if (!nextURL) {
            this.stop();
            return Promise.resolve();
        }
        if (this.url === nextURL && this.current) return this.current;
        this.stop();
        this.url = nextURL;
        var sequence = this.sequence;
        var run = function () {
            if (self.destroyed || sequence !== self.sequence) {
                return Promise.resolve();
            }
            self.current = Promise.resolve()
                .then(function () {
                    return self._playNow(nextURL, nextOptions);
                })
                .catch(function (error) {
                    if (sequence === self.sequence) self.stop();
                    throw error;
                });
            return self.current;
        };
        if (nextOptions.waitForWindowLoad === false) return run();
        this.current = waitForWindowLoad().then(run);
        return this.current;
    };

    EffectPlayer.prototype.stop = function () {
        this.sequence += 1;
        this.url = '';
        this._teardown();
    };

    EffectPlayer.prototype.destroy = function () {
        this.destroyed = true;
        this.stop();
        this.container = null;
    };

    window.HyAppEffectPlayer = {
        create: function (target, options) {
            return new EffectPlayer(target, options);
        },
        preload: function (url, options) {
            var normalized = extend(
                {
                    svgaScript: DEFAULT_SVGA_SCRIPT,
                    proxy: true,
                },
                options || {}
            );
            var type = inferType(url, normalized.type);
            if (type !== 'svga') return Promise.resolve();
            var canvas = document.createElement('canvas');
            var sourceURL = proxyBinaryURL(url, 'svga', normalized);
            return loadSVGAItemOnce(sourceURL, canvas, normalized);
        },
        play: function (target, url, options) {
            var player = new EffectPlayer(target, options);
            return player.play(url).then(function () {
                return player;
            });
        },
        inferType: inferType,
        loadScriptOnce: loadScriptOnce,
    };
})();
