UNPKG

video-ad-sdk

Version:

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

405 lines (404 loc) 15.2 kB
import { get, getAll, getFirstChild, getText, getAttributes, getAttribute } from '../xml'; import { parseOffset } from './helpers/parseOffset'; import { getLinearCreative } from './helpers/getLinearCreative'; import { getLinearTrackingEvents } from './getLinearTrackingEvents'; import { getNonLinearTrackingEvents } from './getNonLinearTrackingEvents'; import { getIcons } from './getIcons'; const getBooleanValue = (value) => { if (typeof value === 'string') { return value === 'true'; } return Boolean(value); }; const compareBySequence = (itemA, itemB) => { const itemASequenceString = getAttribute(itemA, 'sequence'); const itemBSequenceString = getAttribute(itemB, 'sequence'); const itemASequence = itemASequenceString && parseInt(itemASequenceString, 10); const itemBSequence = itemBSequenceString && parseInt(itemBSequenceString, 10); if (typeof itemASequence !== 'string' || typeof itemBSequence !== 'string') { return 0; } if (itemASequence < itemBSequence) { return -1; } if (itemASequence > itemBSequence) { return 1; } return 0; }; /** * Selects the ads of the passed VAST. * * @param parsedVast Parsed VAST xml. * @returns Array of ads or empty array. */ export const getAds = (parsedVast) => { const vastElement = parsedVast && get(parsedVast, 'VAST'); const ads = vastElement && getAll(vastElement, 'Ad'); if (ads && ads.length > 0) { return ads; } return []; }; /** * Gets the Error URI of the passed parsed VAST xml. * * @param parsedVast Parsed VAST xml. * @returns array of the Vast Error URI */ export const getVastErrorURI = (parsedVast) => { const vastElement = parsedVast && get(parsedVast, 'VAST'); const errors = vastElement && getAll(vastElement, 'Error'); if (errors && errors.length > 0) { return errors.map((error) => { var _a; return (_a = getText(error)) !== null && _a !== void 0 ? _a : ''; }).filter(Boolean); } }; /** * Gets the sequence of the pod ad. * * @param ad Parsed ad definition object. * @returns The pod ad sequence number. */ export const getPodAdSequence = (ad) => { const sequenceString = getAttribute(ad, 'sequence'); const sequence = sequenceString && parseInt(sequenceString, 10); if (typeof sequence === 'number' && !isNaN(sequence)) { return sequence; } }; /** * Checks if the passed ad definition is a pod ad. * * @param ad Parsed ad definition object. * @returns Returns true if there the ad is a pod ad and false otherwise. */ export const isPodAd = (ad) => Boolean(getPodAdSequence(ad)); /** * Checks if the passed array of ads have an ad pod. * * @param parsedVast Parsed VAST xml. * @returns Returns true if there is an ad pod in the array and false otherwise. */ export const hasAdPod = (parsedVast) => { const ads = getAds(parsedVast); return Array.isArray(ads) && ads.filter(isPodAd).length > 1; }; /** * Returns true if the passed vastChain has an ad pod or false otherwise. * * @param vastChain Array of VAST responses. See `load` or `requestAd` for more info. * @returns True if the vastChain contains an ad pod and false otherwise. */ export const isAdPod = (vastChain = []) => vastChain.map(({ parsedXML }) => parsedXML).some(hasAdPod); /** * Selects the first ad of the passed VAST. If the passed VAST response contains an ad pod it will return the first ad in the ad pod sequence. * * @param parsedVast Parsed VAST xml. * @returns First ad of the VAST xml. */ export const getFirstAd = (parsedVast) => { const ads = getAds(parsedVast); if (Array.isArray(ads) && ads.length > 0) { if (hasAdPod(parsedVast)) { return ads.filter(isPodAd).sort(compareBySequence)[0]; } return ads[0]; } }; /** * Checks if the passed ad is a Wrapper. * * @param ad VAST ad object. * @returns `true` if the ad contains a wrapper and `false` otherwise. */ export const isWrapper = (ad) => Boolean(get(ad || {}, 'Wrapper')); /** * Checks if the passed ad is an Inline. * * @param ad VAST ad object. * @returns Returns `true` if the ad contains an Inline or `false` otherwise. */ export const isInline = (ad) => Boolean(get(ad || {}, 'Inline')); /** * Returns the VASTAdTagURI from the wrapper ad. * * @param ad VAST ad object. * @returns Returns the VASTAdTagURI from the wrapper ad. */ export const getVASTAdTagURI = (ad) => { const wrapperElement = get(ad, 'Wrapper'); const vastAdTagURIElement = wrapperElement && get(wrapperElement, 'VastAdTagUri'); return vastAdTagURIElement && getText(vastAdTagURIElement); }; /** * Returns the options from the wrapper ad. * * @param ad VAST ad object. * @returns Returns the options from the wrapper ad. */ export const getWrapperOptions = (ad) => { const wrapperElement = get(ad, 'Wrapper'); const options = {}; if (wrapperElement) { const { allowMultipleAds, fallbackOnNoAd, followAdditionalWrappers } = getAttributes(wrapperElement); if (allowMultipleAds) { options.allowMultipleAds = getBooleanValue(allowMultipleAds); } if (fallbackOnNoAd) { options.fallbackOnNoAd = getBooleanValue(fallbackOnNoAd); } if (followAdditionalWrappers) { options.followAdditionalWrappers = getBooleanValue(followAdditionalWrappers); } } return options; }; /** * Gets the Error URI of the passed ad. * * @param ad VAST ad object. * @returns array of the Vast ad Error URI. */ export const getAdErrorURI = (ad) => { const adTypeElement = ad && getFirstChild(ad); const errors = adTypeElement && getAll(adTypeElement, 'Error'); if (errors && errors.length > 0) { return errors.map((error) => { var _a; return (_a = getText(error)) !== null && _a !== void 0 ? _a : ''; }).filter(Boolean); } }; /** * Gets array of the Impression URI of the passed ad. * * @param ad VAST ad object. * @returns array of the Vast ad Impression URI. */ export const getImpression = (ad) => { const adTypeElement = ad && getFirstChild(ad); const impressions = adTypeElement && getAll(adTypeElement, 'Impression'); if (impressions && impressions.length > 0) { return impressions .map((impression) => { var _a; return (_a = getText(impression)) !== null && _a !== void 0 ? _a : ''; }) .filter(Boolean); } }; /** * Gets array of the Viewable URI of the passed ad. * * @param ad VAST ad object. * @returns array of the Vast ad Viewable URI. */ export const getViewable = (ad) => { const adTypeElement = ad && getFirstChild(ad); const viewableImpression = adTypeElement && get(adTypeElement, 'ViewableImpression'); const viewableElements = viewableImpression && getAll(viewableImpression, 'Viewable'); if (viewableElements && viewableElements.length > 0) { return viewableElements .map((element) => { var _a; return (_a = getText(element)) !== null && _a !== void 0 ? _a : ''; }) .filter(Boolean); } }; /** * Gets array of the NotViewable URI of the passed ad. * * @param ad VAST ad object. * @returns array of the Vast ad NotViewable URI. */ export const getNotViewable = (ad) => { const adTypeElement = ad && getFirstChild(ad); const viewableImpression = adTypeElement && get(adTypeElement, 'ViewableImpression'); const notViewableElements = viewableImpression && getAll(viewableImpression, 'NotViewable'); if (notViewableElements && notViewableElements.length > 0) { return notViewableElements .map((element) => { var _a; return (_a = getText(element)) !== null && _a !== void 0 ? _a : ''; }) .filter(Boolean); } }; /** * Gets array of the ViewUndetermined URI of the passed ad. * * @param ad VAST ad object. * @returns array of the Vast ad ViewUndetermined URI. */ export const getViewUndetermined = (ad) => { const adTypeElement = ad && getFirstChild(ad); const viewableImpression = adTypeElement && get(adTypeElement, 'ViewableImpression'); const viewUndeterminedElements = viewableImpression && getAll(viewableImpression, 'ViewUndetermined'); if (viewUndeterminedElements && viewUndeterminedElements.length > 0) { return viewUndeterminedElements .map((element) => { var _a; return (_a = getText(element)) !== null && _a !== void 0 ? _a : ''; }) .filter(Boolean); } }; /** * Gets the ad's MediaFiles. * * @param ad VAST ad object. * @returns array of media files */ export const getMediaFiles = (ad) => { const creativeElement = ad && getLinearCreative(ad); const universalAdIdElement = creativeElement && get(creativeElement, 'UniversalAdId'); const universalAdId = universalAdIdElement && getText(universalAdIdElement); const linearElement = creativeElement && get(creativeElement, 'Linear'); const mediaFilesElement = linearElement && get(linearElement, 'MediaFiles'); const mediaFileElements = mediaFilesElement && getAll(mediaFilesElement, 'MediaFile'); if (mediaFileElements && mediaFileElements.length > 0) { return mediaFileElements.map((mediaFileElement) => { const source = getText(mediaFileElement); const { apiFramework, bitrate, codec, delivery, height, id, maintainAspectRatio, maxBitrate, minBitrate, scalable, type, width } = getAttributes(mediaFileElement); return { apiFramework, bitrate, codec, delivery, height, id, maintainAspectRatio, maxBitrate, minBitrate, scalable, src: source, type, universalAdId, width }; }); } }; /** * Gets the ad's InteractiveFiles. That were added with the `InteractiveCreativeFile` tag. * * @param ad VAST ad object. * @returns array of media files. */ export const getInteractiveCreativeFiles = (ad) => { const creativeElement = ad && getLinearCreative(ad); const linearElement = creativeElement && get(creativeElement, 'Linear'); const mediaFilesElement = linearElement && get(linearElement, 'MediaFiles'); const interactiveElements = mediaFilesElement && getAll(mediaFilesElement, 'InteractiveCreativeFile'); if (interactiveElements && interactiveElements.length > 0) { return interactiveElements.map((interactiveElement) => { const { apiFramework, type } = getAttributes(interactiveElement); const source = getText(interactiveElement); return { apiFramework, src: source, type }; }); } }; /** * Gets all the ad's InteractiveFiles. * * @param ad VAST ad object. * @returns array of media files */ export const getInteractiveFiles = (ad) => { let interactiveFiles = getInteractiveCreativeFiles(ad); if (interactiveFiles) { return interactiveFiles; } const mediaFiles = getMediaFiles(ad); if (mediaFiles) { interactiveFiles = mediaFiles .filter(({ apiFramework = '' }) => (apiFramework === null || apiFramework === void 0 ? void 0 : apiFramework.toLowerCase()) === 'vpaid') .map(({ apiFramework, src: source, type }) => ({ apiFramework, src: source, type })); if (interactiveFiles.length > 0) { return interactiveFiles; } } }; const getVideoClicksElement = (ad) => { const creativeElement = ad && getLinearCreative(ad); const linearElement = creativeElement && get(creativeElement, 'Linear'); return linearElement && get(linearElement, 'VideoClicks'); }; /** * Gets the click through {@link VastMacro}. * * @param ad VAST ad object. * @returns clickthrough macro */ export const getClickThrough = (ad) => { const videoClicksElement = getVideoClicksElement(ad); const clickThroughElement = videoClicksElement && get(videoClicksElement, 'ClickThrough'); return clickThroughElement && getText(clickThroughElement); }; /** * Gets the click through {@link VastMacro}. * * @param ad VAST ad object. * @returns click tracking macro */ export const getClickTracking = (ad) => { const videoClicksElement = ad && getVideoClicksElement(ad); const clickTrackingElements = videoClicksElement && getAll(videoClicksElement, 'ClickTracking'); if (clickTrackingElements && clickTrackingElements.length > 0) { return clickTrackingElements .map((element) => { var _a; return (_a = getText(element)) !== null && _a !== void 0 ? _a : ''; }) .filter(Boolean); } }; /** * Gets the custom click {@link VastMacro}. * * @param ad VAST ad object. * @returns click tracking macro */ export const getCustomClick = (ad) => { const videoClicksElement = getVideoClicksElement(ad); const customClickElements = videoClicksElement && getAll(videoClicksElement, 'CustomClick'); if (customClickElements && customClickElements.length > 0) { return customClickElements .map((element) => { var _a; return (_a = getText(element)) !== null && _a !== void 0 ? _a : ''; }) .filter(Boolean); } }; /** * Gets the skipoffset. * * @param ad VAST ad object. * @returns the time offset in milliseconds or a string with the percentage */ export const getSkipOffset = (ad) => { const creativeElement = ad && getLinearCreative(ad); const linearElement = creativeElement && get(creativeElement, 'Linear'); const skipoffset = linearElement && getAttribute(linearElement, 'skipoffset'); return skipoffset && parseOffset(skipoffset); }; const getLinearContent = (xml) => { const linearRegex = /<Linear([\s\S]*)<\/Linear/gm; const result = linearRegex.exec(xml); return result === null || result === void 0 ? void 0 : result[1]; }; const getAdParametersContent = (xml) => { const paramsRegex = /<AdParameters[\s\w="]*>([\s\S]*)<\/AdParameters>/gm; const result = paramsRegex.exec(xml); return (result === null || result === void 0 ? void 0 : result[1].replace(/[\n\s]*<!\[CDATA\[[\n\s]*/, '').replace(/[\n\s]*\]\]>[\n\s]*$/, '').replace(/\]\]\]\]><!\[CDATA\[>/, ']]>').trim()); }; const getXmlEncodedValue = (xml) => { const xmlEncodedRegex = /<AdParameters[\s]*xmlEncoded="(.*?)">/gim; const result = xmlEncodedRegex.exec(xml); return (result === null || result === void 0 ? void 0 : result[1]) === 'true'; }; /** * Gets the creative data. * * @param xml VAST XML text. * @returns with `AdParameters` as they come in the XML and a flag `xmlEncoded` to indicate if the ad parameters are xml encoded. */ export const getCreativeData = (xml) => { const linearContent = getLinearContent(xml); const AdParameters = linearContent && getAdParametersContent(linearContent); const xmlEncoded = linearContent ? getXmlEncodedValue(linearContent) : false; return { AdParameters, xmlEncoded }; }; export { getIcons, getLinearTrackingEvents, getNonLinearTrackingEvents };