UNPKG

cloudinary-video-player

Version:
472 lines (462 loc) 17.6 kB
import { _ as _vjs } from './_videojs-proxy.js'; import { a as get, P as PLAYER_EVENT } from './index2.js'; import { t as throttle } from './throttle.js'; import { n as noop } from './noop.js'; import { I as INTERACTION_AREA_HAND_ICON, a as INTERACTION_AREAS_PREFIX, b as INTERACTION_AREA_LAYOUT_LOCAL_STORAGE_NAME, c as INTERACTION_AREAS_CONTAINER_CLASS_NAME, d as INTERACTION_AREAS_THEME, C as CLOSE_INTERACTION_AREA_LAYOUT_DELAY, T as TEMPLATE_INTERACTION_AREAS_VTT, D as DEFAULT_INTERACTION_ARE_TRANSITION } from './interaction-areas.const.js'; import { e as elementsCreator, s as styleElement, b as addEventListener, d as addMetadataTrack, f as createElement } from './video-player.js'; import { getDefaultPlayerColor } from './colors.js'; import './_commonjsHelpers.js'; import './video-player.const.js'; import './cloudinary-config-from-options.js'; import './cloudinary-url-prefix.js'; import './toNumber.js'; import './validators-functions.js'; const themedButton = _ref => { let { text, onClick, theme = '', loadingDelay = 0 } = _ref; return elementsCreator({ tag: 'button', attr: { class: `vp-theme-button theme-${theme}` }, onClick, children: [{ tag: 'div', attr: { class: 'vp-loading-bar' }, style: { 'animation-duration': `${loadingDelay}ms` } }, { tag: 'div', attr: { class: 'content' }, children: text }] }); }; const BUTTON_THEME = { TRANSPARENT_WHITE: 'transparent-white' }; const getInteractionAreaItemId = (item, index) => item.id || item.type || `id_${index}`; const getInteractionAreaItem = (_ref, item, index, durationTime, onClick) => { let { playerOptions, videojsOptions } = _ref; const defaultColor = getDefaultPlayerColor(videojsOptions); const accentColor = playerOptions && playerOptions.colors ? playerOptions.colors.accent : defaultColor.accent; // theme = 'pulsing' / 'shadowed' const theme = get(videojsOptions, 'interactionDisplay.theme.template', INTERACTION_AREAS_THEME.PULSING); return elementsCreator({ tag: 'div', attr: { class: `${INTERACTION_AREAS_PREFIX}-item theme-${theme}`, 'data-id': getInteractionAreaItemId(item, index) }, style: { left: `${item.left}%`, top: `${item.top}%`, width: `${item.width}%`, height: `${item.height}%`, transitionDuration: `${durationTime}ms` }, event: { name: 'click', callback: onClick }, children: [{ tag: 'div', attr: { class: `${INTERACTION_AREAS_PREFIX}-area-marker` }, children: [{ tag: 'div', attr: { class: `${INTERACTION_AREAS_PREFIX}-marker-shadow` }, style: { [theme === INTERACTION_AREAS_THEME.SHADOWED ? 'backgroundColor' : 'borderColor']: accentColor } }, { tag: 'div', attr: { class: `${INTERACTION_AREAS_PREFIX}-marker-main` }, style: { borderColor: accentColor } }] }] }); }; const percentageToFixedValue = (outOf, value) => outOf / (100 / +value); const getZoomTransformation = (videoElement, interactionAreaItem) => { const { videoHeight, videoWidth } = videoElement; const itemX = percentageToFixedValue(videoWidth, interactionAreaItem.left); const itemY = percentageToFixedValue(videoHeight, interactionAreaItem.top); const itemWidth = percentageToFixedValue(videoWidth, interactionAreaItem.width); const itemHeight = percentageToFixedValue(videoHeight, interactionAreaItem.height); const videoAspectRatio = videoWidth / videoHeight; const itemAspectRatio = itemWidth / itemHeight; const width = Math.round(itemAspectRatio > 1 || videoAspectRatio > 1 ? itemHeight * itemAspectRatio : itemWidth); const height = Math.round(width / videoAspectRatio); const x = Math.round(itemX - (width - itemWidth) / 2); const y = Math.round(itemY - (height - itemHeight) / 2); return { width, height, x: Math.min(Math.max(x, 0), videoWidth - width), y: Math.min(Math.max(y, 0), videoHeight - height), crop: 'crop' }; }; const setInteractionAreasContainer = (videojs, newInteractionAreasContainer) => { const currentInteractionAreasContainer = getInteractionAreasContainer(videojs); if (currentInteractionAreasContainer) { currentInteractionAreasContainer.replaceWith(newInteractionAreasContainer); } else { // do not use element.append for ie11 support videojs.el().appendChild(newInteractionAreasContainer); } }; const getInteractionAreaElementById = (interactionAreasContainer, item, index) => interactionAreasContainer.querySelector(`[data-id=${getInteractionAreaItemId(item, index)}]`); const updateInteractionAreasItem = (videojs, configs, interactionAreasData, previousInteractionAreasData, durationTime, onClick) => { const interactionAreasContainer = getInteractionAreasContainer(videojs); interactionAreasData.forEach((item, index) => { const itemElement = getInteractionAreaElementById(interactionAreasContainer, item, index); const itemId = getInteractionAreaItemId(item); const isExistItem = previousInteractionAreasData.some(i => getInteractionAreaItemId(i) === itemId); // in case the element of the item is in the dom and exist in the previous data , it update the element position if (isExistItem && itemElement) { styleElement(itemElement, { left: `${item.left}%`, top: `${item.top}%`, width: `${item.width}%`, height: `${item.height}%`, transitionDuration: `${durationTime}ms` }); // if the element did not exist before , not in the dom and not in the previous data , it add a new element } else if (!isExistItem && !itemElement) { // do not use element.append for ie11 support interactionAreasContainer.appendChild(getInteractionAreaItem(configs, item, index, durationTime, event => { onClick({ event, item, index }); })); } }); // checking the previous data for element that should be removed if not exist in the new data object. previousInteractionAreasData.forEach((item, index) => { const itemElement = getInteractionAreaElementById(interactionAreasContainer, item, index); const itemId = getInteractionAreaItemId(item); const shouldBeRemoved = !interactionAreasData.some(i => getInteractionAreaItemId(i) === itemId); if (itemElement && shouldBeRemoved) { // do not use element.remove for ie11 support itemElement.parentNode.removeChild(itemElement); } }); }; const shouldShowAreaLayoutMessage = interactionAreasConfig => { const isLayoutEnabled = get(interactionAreasConfig, 'layout.enable', true); return isLayoutEnabled && localStorage.getItem(INTERACTION_AREA_LAYOUT_LOCAL_STORAGE_NAME) !== 'true'; }; const onClickInteractionAreaLayoutClick = function (checked) { let onClick = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : noop; localStorage.setItem(INTERACTION_AREA_LAYOUT_LOCAL_STORAGE_NAME, JSON.parse(checked)); onClick(); }; const createInteractionAreaLayoutMessage = function (videojs, onClick) { let showItAgainCheckbox = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; let checked = false; const id = `checkbox_${Math.round(Math.random() * 10000)}`; const tracksContainer = elementsCreator({ tag: 'div', attr: { class: `${INTERACTION_AREAS_CONTAINER_CLASS_NAME} ${INTERACTION_AREAS_PREFIX}-layout-message ${showItAgainCheckbox ? '' : 'clickable'}` }, onClick: !showItAgainCheckbox ? () => onClickInteractionAreaLayoutClick(checked, onClick) : null, children: [{ tag: 'img', attr: { class: `${INTERACTION_AREAS_PREFIX}-layout-icon`, src: INTERACTION_AREA_HAND_ICON } }, { tag: 'h3', attr: { class: `${INTERACTION_AREAS_PREFIX}-layout-message-title` }, children: 'Tap on dots to zoom for a product.' }, themedButton({ text: 'Got it', theme: BUTTON_THEME.TRANSPARENT_WHITE, loadingDelay: showItAgainCheckbox ? 0 : CLOSE_INTERACTION_AREA_LAYOUT_DELAY, onClick: showItAgainCheckbox ? () => onClickInteractionAreaLayoutClick(checked, onClick) : null }), showItAgainCheckbox && { tag: 'div', attr: { class: `${INTERACTION_AREAS_PREFIX}-layout-message-do-not-show` }, children: [{ tag: 'input', attr: { type: 'checkbox', class: `${INTERACTION_AREAS_PREFIX}-layout-message-checkbox`, id }, event: { name: 'input', callback: event => { checked = event.target.checked; } } }, { tag: 'label', attr: { class: `${INTERACTION_AREAS_PREFIX}-layout-message-checkbox-title`, for: id }, children: 'Don׳t show it again' }] }].filter(i => i) }); setInteractionAreasContainer(videojs, tracksContainer); }; const getInteractionAreasContainer = videojs => videojs.el().querySelector(`.${INTERACTION_AREAS_CONTAINER_CLASS_NAME}`); const removeInteractionAreasContainer = videojs => { const interactionAreasContainer = getInteractionAreasContainer(videojs); // do not use element.remove for ie11 support interactionAreasContainer && interactionAreasContainer.parentNode.removeChild(interactionAreasContainer); }; const setInteractionAreasContainerSize = (videojs, videoElement) => { const interactionAreasContainer = getInteractionAreasContainer(videojs); if (!interactionAreasContainer) { return; } const { videoHeight, videoWidth } = videoElement; const videoAspectRatio = videoWidth / videoHeight; const width = videoAspectRatio * videoElement.clientHeight; interactionAreasContainer.style.width = `${videoElement.clientWidth < width ? '100%' : width}px`; interactionAreasContainer.style.height = videoElement.clientWidth < width ? `${videoElement.clientWidth / videoAspectRatio}px` : '100%'; }; const interactionAreasService = (player, playerOptions, videojsOptions) => { let isZoomed = false; let currentSource = null; let currentTrack = null; let unZoom = noop; const shouldLayoutMessage = () => shouldShowAreaLayoutMessage(videojsOptions.interactionDisplay); const getIsSyncOffsetTime = () => { const interactionAreasConfig = getInteractionAreasConfig(); return interactionAreasConfig && interactionAreasConfig.syncOffsetTime !== undefined ? interactionAreasConfig.syncOffsetTime : false; }; function isInteractionAreasEnabled() { let enabled = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; const interactionAreasConfig = getInteractionAreasConfig(); return enabled || interactionAreasConfig && interactionAreasConfig.enable; } function setAreasPositionListener() { currentTrack && player.videojs.removeRemoteTextTrack(currentTrack); const isEnabled = isInteractionAreasEnabled(); const interactionAreasConfig = getInteractionAreasConfig(); if (!isEnabled || isZoomed) { return null; } if (Array.isArray(interactionAreasConfig.template)) { addInteractionAreasItems(interactionAreasConfig.template); setContainerSize(); } else { const vttUrl = interactionAreasConfig.vttUrl || TEMPLATE_INTERACTION_AREAS_VTT[interactionAreasConfig.template]; if (vttUrl) { currentTrack = addMetadataTrack(player.videojs, vttUrl); addCueListener(currentTrack); } } } function setGoBackButton() { const button = createElement('div', { 'class': 'go-back-button' }); button.addEventListener('click', () => { unZoom(); }, false); const tracksContainer = createElement('div', { 'class': INTERACTION_AREAS_CONTAINER_CLASS_NAME }, button); setInteractionAreasContainer(player.videojs, tracksContainer); } function getInteractionAreasConfig() { const { cldSrc } = currentSource; return cldSrc && cldSrc.interactionAreas(); } function removeLayoutMessage() { removeInteractionAreasContainer(player.videojs); setAreasPositionListener(); player.play(); } function setLayoutMessage() { if (!isInteractionAreasEnabled()) { return; } if (shouldLayoutMessage()) { let layoutMessageTimout = null; const showItAgainCheckbox = get(videojsOptions, 'interactionDisplay.layout.showAgain', false); player.pause(); createInteractionAreaLayoutMessage(player.videojs, () => { clearTimeout(layoutMessageTimout); removeLayoutMessage(); }, showItAgainCheckbox); if (!showItAgainCheckbox) { layoutMessageTimout = setTimeout(removeLayoutMessage, CLOSE_INTERACTION_AREA_LAYOUT_DELAY); } } else { removeLayoutMessage(); } } function handleAds() { player.on('adsready', () => { player.videojs.ima.addEventListener(window.google.ima.AdEvent.Type.ALL_ADS_COMPLETED, setLayoutMessage); }); } function init() { currentSource = currentSource || player.videojs.currentSource(); if (isInteractionAreasEnabled()) { player.videojs.el().classList.add('interaction-areas'); player.videojs.one(PLAYER_EVENT.PLAY, () => { if (typeof player.videojs.ima === 'object') { handleAds(); } else { setLayoutMessage(); } }); const setInteractionAreasContainerSize = throttle(setContainerSize, 100); player.videojs.on(PLAYER_EVENT.FULL_SCREEN_CHANGE, () => { // waiting for fullscreen will end setTimeout(setInteractionAreasContainerSize, 100); }); const resizeDestroy = addEventListener(window, 'resize', setContainerSize); player.videojs.on(PLAYER_EVENT.DISPOSE, resizeDestroy); } player.videojs.on(PLAYER_EVENT.ENDED, () => { unZoom(); }); player.videojs.on(PLAYER_EVENT.ERROR, () => { player.pause(); }); } function onZoom(src, newOption, item) { const originalCurrentTime = player.currentTime(); const isSyncOffsetTime = getIsSyncOffsetTime(); const { cldSrc } = currentSource; const currentSrcOptions = cldSrc.getInitOptions(); const option = newOption || { transformation: currentSrcOptions.transformation }; const transformation = !src && getZoomTransformation(player.videoElement, item); const sourceOptions = transformation ? _vjs.obj.merge({ transformation }, option) : option; const newSource = cldSrc.isRawUrl ? currentSource.src : { publicId: cldSrc.publicId() }; player.source(transformation ? { publicId: cldSrc.publicId() } : src, sourceOptions).play(); isSyncOffsetTime && player.currentTime(originalCurrentTime); isZoomed = true; setGoBackButton(); unZoom = () => { if (isZoomed) { isZoomed = false; const currentZoomedTime = player.currentTime(); const duration = player.duration(); player.source(newSource, currentSrcOptions).play(); isSyncOffsetTime && currentZoomedTime < duration && player.currentTime(currentZoomedTime); setAreasPositionListener(); } }; } function onInteractionAreasClick(_ref) { let { event, item, index } = _ref; const interactionAreasConfig = getInteractionAreasConfig(); interactionAreasConfig.onClick && interactionAreasConfig.onClick({ item, index, event, zoom: (source, option) => { onZoom(source, option, item); } }); } function addInteractionAreasItems(interactionAreasData, previousInteractionAreasData) { let durationTime = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0; const configs = { playerOptions: playerOptions, videojsOptions: videojsOptions }; if (previousInteractionAreasData) { updateInteractionAreasItem(player.videojs, configs, interactionAreasData, previousInteractionAreasData, durationTime, onInteractionAreasClick); } else { const interactionAreasItems = interactionAreasData.map((item, index) => { return getInteractionAreaItem(configs, item, index, durationTime, event => { onInteractionAreasClick({ event, item, index }); }); }); setInteractionAreasContainer(player.videojs, createElement('div', { 'class': INTERACTION_AREAS_CONTAINER_CLASS_NAME }, interactionAreasItems)); } } function setContainerSize() { if (isInteractionAreasEnabled()) { setInteractionAreasContainerSize(player.videojs, player.videoElement); } } function addCueListener(track) { if (!track) { return; } let previousTracksData = null; track.addEventListener('cuechange', () => { const activeCue = track.activeCues && track.activeCues[0]; if (activeCue) { const durationTime = Math.max(Math.floor((activeCue.endTime - activeCue.startTime) * 1000), DEFAULT_INTERACTION_ARE_TRANSITION); const tracksData = JSON.parse(activeCue.text); addInteractionAreasItems(tracksData, previousTracksData, durationTime); !previousTracksData && setContainerSize(); previousTracksData = tracksData; } else { removeInteractionAreasContainer(player.videojs); previousTracksData = null; } }); } init(); }; export { interactionAreasService };