(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, }; })();