UNPKG

video-ad-sdk

Version:

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

228 lines (227 loc) 8.03 kB
var _a; import { linearEvents, ErrorCode, isVastErrorCode } from '../tracker'; import { getSkipOffset } from '../vastSelectors'; import { findBestMedia } from './helpers/media/findBestMedia'; import { once } from './helpers/dom/once'; import { setupMetricHandlers } from './helpers/metrics/setupMetricHandlers'; import { updateMedia } from './helpers/media/updateMedia'; import { AdUnitError } from './helpers/adUnitError'; import { VideoAdUnit, _protected } from './VideoAdUnit'; const { complete, error: errorEvent, skip } = linearEvents; const _private = Symbol('_private'); /** * This class provides everything necessary to run a Vast ad. */ export class VastAdUnit extends VideoAdUnit { /** * Creates a {@link VastAdUnit}. * * @param vastChain The {@link VastChain} with all the {@link VastResponse} * @param videoAdContainer - container instance to place the ad * @param options Options Map */ constructor(vastChain, videoAdContainer, options = {}) { super(vastChain, videoAdContainer, options); this[_a] = { handleMetric: (event, data) => { var _b; switch (event) { case complete: { this[_protected].finish(); break; } case errorEvent: { if (data instanceof Error) { this.error = data; this.errorCode = ((_b = this.error) === null || _b === void 0 ? void 0 : _b.code) && isVastErrorCode(this.error.code) ? this.error.code : ErrorCode.VAST_PROBLEM_DISPLAYING_MEDIA_FILE; } this[_protected].onErrorCallbacks.forEach((callback) => callback(this.error, { adUnit: this, vastChain: this.vastChain })); this[_protected].finish(); break; } case skip: { this.cancel(); break; } } this.emit(event, { adUnit: this, data, type: event }); }, drawIcons: async () => { var _b, _c, _d, _e; if (this.isFinished()) { return; } await ((_c = (_b = this[_protected]).drawIcons) === null || _c === void 0 ? void 0 : _c.call(_b)); if (((_e = (_d = this[_protected]).hasPendingIconRedraws) === null || _e === void 0 ? void 0 : _e.call(_d)) && !this.isFinished()) { const { videoElement } = this.videoAdContainer; once(videoElement, 'timeupdate', this[_private].drawIcons); } } }; /** Ad unit type. Will be `VAST` for VastAdUnit */ this.type = 'VAST'; const { onFinishCallbacks } = this[_protected]; const { handleMetric } = this[_private]; this.hooks = options.hooks || {}; const removeMetricHandlers = setupMetricHandlers({ hooks: this.hooks, pauseOnAdClick: this.pauseOnAdClick, vastChain: this.vastChain, videoAdContainer: this.videoAdContainer }, handleMetric); onFinishCallbacks.push(removeMetricHandlers); } /** * Starts the ad unit. * * @throws if called twice. * @throws if ad unit is finished. */ async start() { this[_protected].throwIfFinished(); if (this.isStarted()) { throw new AdUnitError('VastAdUnit already started'); } const inlineAd = this.vastChain[0].ad; const { videoElement } = this.videoAdContainer; const media = inlineAd && findBestMedia(inlineAd, this.videoAdContainer, this.hooks); if (media) { if (this.icons) { await this[_private].drawIcons(); } if (media === null || media === void 0 ? void 0 : media.src) { videoElement.src = media.src; this.assetUri = media.src; } videoElement.play(); } else { const adUnitError = new AdUnitError("Can't find a suitable media to play"); adUnitError.code = ErrorCode.VAST_LINEAR_ASSET_MISMATCH; this[_private].handleMetric(errorEvent, adUnitError); } this[_protected].started = true; } /** * Resumes a previously paused ad unit. * * @throws if ad unit is not started. * @throws if ad unit is finished. */ resume() { this.videoAdContainer.videoElement.play(); } /** * Pauses the ad unit. * * @throws if ad unit is not started. * @throws if ad unit is finished. */ pause() { this.videoAdContainer.videoElement.pause(); } /** * Skips the ad unit. * * @throws if ad unit is not started. * @throws if ad unit is finished. */ skip() { const inlineAd = this.vastChain[0].ad; const skipoffset = inlineAd && getSkipOffset(inlineAd); const currentTimeMs = this.currentTime() * 1000; if (typeof skipoffset === 'number' && currentTimeMs >= skipoffset) { this[_private].handleMetric(skip); } } /** * Returns true if the ad is paused and false otherwise */ paused() { return this.videoAdContainer.videoElement.paused; } /** * Sets the volume of the ad unit. * * @throws if ad unit is not started. * @throws if ad unit is finished. * * @param volume must be a value between 0 and 1; */ setVolume(volume) { this.videoAdContainer.videoElement.volume = volume; } /** * Gets the volume of the ad unit. * * @throws if ad unit is not started. * @throws if ad unit is finished. * * @returns the volume of the ad unit. */ getVolume() { return this.videoAdContainer.videoElement.volume; } /** * Cancels the ad unit. * * @throws if ad unit is finished. */ cancel() { this[_protected].throwIfFinished(); this.videoAdContainer.videoElement.pause(); this[_protected].finish(); } /** * Returns the duration of the ad Creative or 0 if there is no creative. * * @returns the duration of the ad unit. */ duration() { if (!this.isStarted()) { return 0; } return this.videoAdContainer.videoElement.duration; } /** * Returns the current time of the ad Creative or 0 if there is no creative. * * @returns the current time of the ad unit. */ currentTime() { if (!this.isStarted()) { return 0; } return this.videoAdContainer.videoElement.currentTime; } /** * This method resizes the ad unit to fit the available space in the passed {@link VideoAdContainer} * * @param width the new width of the ad container. * @param height the new height of the ad container. * @param viewmode fullscreen | normal | thumbnail * @returns Promise that resolves once the unit was resized */ async resize(width, height, viewmode) { await super.resize(width, height, viewmode); if (this.isStarted() && !this.isFinished()) { const inlineAd = this.vastChain[0].ad; const { videoElement } = this.videoAdContainer; const media = inlineAd && findBestMedia(inlineAd, this.videoAdContainer, this.hooks); if (media && videoElement.src !== media.src) { updateMedia(videoElement, media); } } } } _a = _private;