UNPKG

video-ad-sdk

Version:

VAST/VPAID SDK that allows video ads to be played on top of any player

100 lines (99 loc) 4.02 kB
import debounce from 'lodash.debounce'; import { IntersectionObserver } from './helpers/IntersectionObserver'; const validate = (target, callback) => { if (!(target instanceof Element)) { throw new TypeError('Target is not an Element node'); } if (!(callback instanceof Function)) { throw new TypeError('Callback is not a function'); } }; const noop = () => { }; const intersectionHandlers = Symbol('intersectionHandlers'); const observerKey = Symbol('intersectionObserver'); const THRESHOLDS_COUNT = 11; const onIntersection = (target, callback) => { var _a, _b; if (!target[intersectionHandlers]) { target[intersectionHandlers] = []; const execHandlers = (entries, observer) => { var _a; (_a = target[intersectionHandlers]) === null || _a === void 0 ? void 0 : _a.forEach((handler) => handler(entries, observer)); }; const options = { root: null, rootMargin: '0px', threshold: [...new Array(THRESHOLDS_COUNT)].map((_item, index) => index / (THRESHOLDS_COUNT - 1)) }; target[observerKey] = new IntersectionObserver(execHandlers, options); (_a = target[observerKey]) === null || _a === void 0 ? void 0 : _a.observe(target); } (_b = target[intersectionHandlers]) === null || _b === void 0 ? void 0 : _b.push(callback); return () => { var _a, _b, _c; target[intersectionHandlers] = (_a = target[intersectionHandlers]) === null || _a === void 0 ? void 0 : _a.filter((handler) => handler !== callback); if (((_b = target[intersectionHandlers]) === null || _b === void 0 ? void 0 : _b.length) === 0) { (_c = target[observerKey]) === null || _c === void 0 ? void 0 : _c.disconnect(); delete target[intersectionHandlers]; delete target[observerKey]; } }; }; let visibilityHandlers = []; const onVisibilityChange = (_target, callback) => { const execHandlers = () => { if (visibilityHandlers) { visibilityHandlers.forEach((handler) => handler()); } }; visibilityHandlers.push(callback); if (visibilityHandlers.length === 1) { document.addEventListener('visibilitychange', execHandlers); } return () => { visibilityHandlers = visibilityHandlers.filter((handler) => handler !== callback); if (visibilityHandlers.length === 0) { document.removeEventListener('visibilitychange', execHandlers); } }; }; let lastIntersectionEntries = []; /** * Helper function to know if the visibility of an element has changed. * * @ignore * * @param target The element that we want to observe. * @param callback The callback that handles the visibility change. * @param options Options Map. * * @returns Unsubscribe function. */ export const onElementVisibilityChange = (target, callback, { threshold = 100, viewabilityOffset = 0.4 } = {}) => { validate(target, callback); if (!IntersectionObserver) { // NOTE: visibility is not determined callback(undefined); return noop; } let lastIsInViewport = false; const checkVisibility = (entries) => { entries.forEach((entry) => { if (entry.target === target) { const isInViewport = !document.hidden && entry.intersectionRatio > viewabilityOffset; if (isInViewport !== lastIsInViewport) { lastIsInViewport = isInViewport; callback(isInViewport); } } }); lastIntersectionEntries = entries; }; const visibilityHandler = debounce(checkVisibility, threshold); const stopObservingIntersection = onIntersection(target, visibilityHandler); const stopListeningToVisibilityChange = onVisibilityChange(target, () => visibilityHandler(lastIntersectionEntries)); return () => { stopObservingIntersection(); stopListeningToVisibilityChange(); }; };