From 97e52560139b01fe5d4a764e19053b4386d3e272 Mon Sep 17 00:00:00 2001 From: Zacharie Taifour Date: Mon, 1 Jul 2024 17:24:45 +0200 Subject: [PATCH] Bundle release 6.0.0 --- dist/chunks/xmldom-623c1b6b.min.js | 1 + dist/chunks/xmldom-ac2d7d04.js | 6 + dist/vast-client-node.js | 4265 ++++++++-------- dist/vast-client-node.min.js | 2 +- dist/vast-client.js | 7261 ++++++++++++++-------------- dist/vast-client.min.js | 2 +- 6 files changed, 5529 insertions(+), 6008 deletions(-) create mode 100644 dist/chunks/xmldom-623c1b6b.min.js create mode 100644 dist/chunks/xmldom-ac2d7d04.js diff --git a/dist/chunks/xmldom-623c1b6b.min.js b/dist/chunks/xmldom-623c1b6b.min.js new file mode 100644 index 00000000..0683a6fb --- /dev/null +++ b/dist/chunks/xmldom-623c1b6b.min.js @@ -0,0 +1 @@ +"use strict";var e=require("./dom");exports.DOMImplementation=e.DOMImplementation,exports.XMLSerializer=e.XMLSerializer,exports.DOMParser=require("./dom-parser").DOMParser; diff --git a/dist/chunks/xmldom-ac2d7d04.js b/dist/chunks/xmldom-ac2d7d04.js new file mode 100644 index 00000000..a4a18e4d --- /dev/null +++ b/dist/chunks/xmldom-ac2d7d04.js @@ -0,0 +1,6 @@ +'use strict'; + +var dom = require('./dom'); +exports.DOMImplementation = dom.DOMImplementation; +exports.XMLSerializer = dom.XMLSerializer; +exports.DOMParser = require('./dom-parser').DOMParser; diff --git a/dist/vast-client-node.js b/dist/vast-client-node.js index 614e1558..6f5033ed 100644 --- a/dist/vast-client-node.js +++ b/dist/vast-client-node.js @@ -2,182 +2,8 @@ Object.defineProperty(exports, '__esModule', { value: true }); -function ownKeys(object, enumerableOnly) { - var keys = Object.keys(object); - if (Object.getOwnPropertySymbols) { - var symbols = Object.getOwnPropertySymbols(object); - enumerableOnly && (symbols = symbols.filter(function (sym) { - return Object.getOwnPropertyDescriptor(object, sym).enumerable; - })), keys.push.apply(keys, symbols); - } - return keys; -} -function _objectSpread2(target) { - for (var i = 1; i < arguments.length; i++) { - var source = null != arguments[i] ? arguments[i] : {}; - i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { - _defineProperty(target, key, source[key]); - }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { - Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); - }); - } - return target; -} -function _typeof(obj) { - "@babel/helpers - typeof"; - - return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { - return typeof obj; - } : function (obj) { - return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; - }, _typeof(obj); -} -function _classCallCheck(instance, Constructor) { - if (!(instance instanceof Constructor)) { - throw new TypeError("Cannot call a class as a function"); - } -} -function _defineProperties(target, props) { - for (var i = 0; i < props.length; i++) { - var descriptor = props[i]; - descriptor.enumerable = descriptor.enumerable || false; - descriptor.configurable = true; - if ("value" in descriptor) descriptor.writable = true; - Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); - } -} -function _createClass(Constructor, protoProps, staticProps) { - if (protoProps) _defineProperties(Constructor.prototype, protoProps); - if (staticProps) _defineProperties(Constructor, staticProps); - Object.defineProperty(Constructor, "prototype", { - writable: false - }); - return Constructor; -} -function _defineProperty(obj, key, value) { - key = _toPropertyKey(key); - if (key in obj) { - Object.defineProperty(obj, key, { - value: value, - enumerable: true, - configurable: true, - writable: true - }); - } else { - obj[key] = value; - } - return obj; -} -function _inherits(subClass, superClass) { - if (typeof superClass !== "function" && superClass !== null) { - throw new TypeError("Super expression must either be null or a function"); - } - subClass.prototype = Object.create(superClass && superClass.prototype, { - constructor: { - value: subClass, - writable: true, - configurable: true - } - }); - Object.defineProperty(subClass, "prototype", { - writable: false - }); - if (superClass) _setPrototypeOf(subClass, superClass); -} -function _getPrototypeOf(o) { - _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function _getPrototypeOf(o) { - return o.__proto__ || Object.getPrototypeOf(o); - }; - return _getPrototypeOf(o); -} -function _setPrototypeOf(o, p) { - _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) { - o.__proto__ = p; - return o; - }; - return _setPrototypeOf(o, p); -} -function _isNativeReflectConstruct() { - if (typeof Reflect === "undefined" || !Reflect.construct) return false; - if (Reflect.construct.sham) return false; - if (typeof Proxy === "function") return true; - try { - Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); - return true; - } catch (e) { - return false; - } -} -function _assertThisInitialized(self) { - if (self === void 0) { - throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); - } - return self; -} -function _possibleConstructorReturn(self, call) { - if (call && (typeof call === "object" || typeof call === "function")) { - return call; - } else if (call !== void 0) { - throw new TypeError("Derived constructors may only return object or undefined"); - } - return _assertThisInitialized(self); -} -function _createSuper(Derived) { - var hasNativeReflectConstruct = _isNativeReflectConstruct(); - return function _createSuperInternal() { - var Super = _getPrototypeOf(Derived), - result; - if (hasNativeReflectConstruct) { - var NewTarget = _getPrototypeOf(this).constructor; - result = Reflect.construct(Super, arguments, NewTarget); - } else { - result = Super.apply(this, arguments); - } - return _possibleConstructorReturn(this, result); - }; -} -function _toConsumableArray(arr) { - return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); -} -function _arrayWithoutHoles(arr) { - if (Array.isArray(arr)) return _arrayLikeToArray(arr); -} -function _iterableToArray(iter) { - if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); -} -function _unsupportedIterableToArray(o, minLen) { - if (!o) return; - if (typeof o === "string") return _arrayLikeToArray(o, minLen); - var n = Object.prototype.toString.call(o).slice(8, -1); - if (n === "Object" && o.constructor) n = o.constructor.name; - if (n === "Map" || n === "Set") return Array.from(o); - if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); -} -function _arrayLikeToArray(arr, len) { - if (len == null || len > arr.length) len = arr.length; - for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; - return arr2; -} -function _nonIterableSpread() { - throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); -} -function _toPrimitive(input, hint) { - if (typeof input !== "object" || input === null) return input; - var prim = input[Symbol.toPrimitive]; - if (prim !== undefined) { - var res = prim.call(input, hint || "default"); - if (typeof res !== "object") return res; - throw new TypeError("@@toPrimitive must return a primitive value."); - } - return (hint === "string" ? String : Number)(input); -} -function _toPropertyKey(arg) { - var key = _toPrimitive(arg, "string"); - return typeof key === "symbol" ? key : String(key); -} - function createAd() { - var adAttributes = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + let adAttributes = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; return { id: adAttributes.id || null, sequence: adAttributes.sequence || null, @@ -218,7 +44,7 @@ function createAdVerification() { } function createCompanionAd() { - var creativeAttributes = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + let creativeAttributes = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; return { id: creativeAttributes.id || null, adType: 'companionAd', @@ -247,7 +73,7 @@ function isCompanionAd(ad) { } function createCreative() { - var creativeAttributes = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + let creativeAttributes = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; return { id: creativeAttributes.id || null, adId: creativeAttributes.adId || null, @@ -259,32 +85,33 @@ function createCreative() { } function createCreativeCompanion() { - var creativeAttributes = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; - var _createCreative = createCreative(creativeAttributes), - id = _createCreative.id, - adId = _createCreative.adId, - sequence = _createCreative.sequence, - apiFramework = _createCreative.apiFramework; + let creativeAttributes = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + const { + id, + adId, + sequence, + apiFramework + } = createCreative(creativeAttributes); return { - id: id, - adId: adId, - sequence: sequence, - apiFramework: apiFramework, + id, + adId, + sequence, + apiFramework, type: 'companion', required: null, variations: [] }; } -var supportedMacros = ['ADCATEGORIES', 'ADCOUNT', 'ADPLAYHEAD', 'ADSERVINGID', 'ADTYPE', 'APIFRAMEWORKS', 'APPBUNDLE', 'ASSETURI', 'BLOCKEDADCATEGORIES', 'BREAKMAXADLENGTH', 'BREAKMAXADS', 'BREAKMAXDURATION', 'BREAKMINADLENGTH', 'BREAKMINDURATION', 'BREAKPOSITION', 'CLICKPOS', 'CLICKTYPE', 'CLIENTUA', 'CONTENTID', 'CONTENTPLAYHEAD', +const supportedMacros = ['ADCATEGORIES', 'ADCOUNT', 'ADPLAYHEAD', 'ADSERVINGID', 'ADTYPE', 'APIFRAMEWORKS', 'APPBUNDLE', 'ASSETURI', 'BLOCKEDADCATEGORIES', 'BREAKMAXADLENGTH', 'BREAKMAXADS', 'BREAKMAXDURATION', 'BREAKMINADLENGTH', 'BREAKMINDURATION', 'BREAKPOSITION', 'CLICKPOS', 'CLICKTYPE', 'CLIENTUA', 'CONTENTID', 'CONTENTPLAYHEAD', // @deprecated VAST 4.1 'CONTENTURI', 'DEVICEIP', 'DEVICEUA', 'DOMAIN', 'EXTENSIONS', 'GDPRCONSENT', 'IFA', 'IFATYPE', 'INVENTORYSTATE', 'LATLONG', 'LIMITADTRACKING', 'MEDIAMIME', 'MEDIAPLAYHEAD', 'OMIDPARTNER', 'PAGEURL', 'PLACEMENTTYPE', 'PLAYERCAPABILITIES', 'PLAYERSIZE', 'PLAYERSTATE', 'PODSEQUENCE', 'REGULATIONS', 'SERVERSIDE', 'SERVERUA', 'TRANSACTIONID', 'UNIVERSALADID', 'VASTVERSIONS', 'VERIFICATIONVENDORS']; function track(URLTemplates, macros, options) { - var URLs = resolveURLTemplates(URLTemplates, macros, options); - URLs.forEach(function (URL) { + const URLs = resolveURLTemplates(URLTemplates, macros, options); + URLs.forEach(URL => { if (typeof window !== 'undefined' && window !== null) { - var i = new Image(); + const i = new Image(); i.src = URL; } }); @@ -298,10 +125,10 @@ function track(URLTemplates, macros, options) { * @param {Object} [options={}] - An optional Object of options to be used in the tracking calls. */ function resolveURLTemplates(URLTemplates) { - var macros = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; - var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; - var resolvedURLs = []; - var URLArray = extractURLsFromTemplates(URLTemplates); + let macros = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; + const resolvedURLs = []; + const URLArray = extractURLsFromTemplates(URLTemplates); // Set default value for invalid ERRORCODE if (macros['ERRORCODE'] && !options.isCustomCode && !/^[0-9]{3}$/.test(macros['ERRORCODE'])) { @@ -314,11 +141,11 @@ function resolveURLTemplates(URLTemplates) { // RANDOM/random is not defined in VAST 3/4 as a valid macro tho it's used by some adServer (Auditude) macros['RANDOM'] = macros['random'] = macros['CACHEBUSTING']; - for (var macro in macros) { + for (const macro in macros) { macros[macro] = encodeURIComponentRFC3986(macros[macro]); } - for (var URLTemplateKey in URLArray) { - var resolveURL = URLArray[URLTemplateKey]; + for (const URLTemplateKey in URLArray) { + const resolveURL = URLArray[URLTemplateKey]; if (typeof resolveURL !== 'string') { continue; } @@ -338,17 +165,15 @@ function resolveURLTemplates(URLTemplates) { function replaceUrlMacros(url, macros) { url = replaceMacrosValues(url, macros); // match any macros from the url that was not replaced - var remainingMacros = url.match(/[^[\]]+(?=])/g); + const remainingMacros = url.match(/[^[\]]+(?=])/g); if (!remainingMacros) { return url; } - var supportedRemainingMacros = remainingMacros.filter(function (macro) { - return supportedMacros.indexOf(macro) > -1; - }); + let supportedRemainingMacros = remainingMacros.filter(macro => supportedMacros.indexOf(macro) > -1); if (supportedRemainingMacros.length === 0) { return url; } - supportedRemainingMacros = supportedRemainingMacros.reduce(function (accumulator, macro) { + supportedRemainingMacros = supportedRemainingMacros.reduce((accumulator, macro) => { accumulator[macro] = -1; return accumulator; }, {}); @@ -362,9 +187,9 @@ function replaceUrlMacros(url, macros) { * @param {Object} macros - Object of macros to be replaced in the tracking calls */ function replaceMacrosValues(url, macros) { - var replacedMacrosUrl = url; - for (var key in macros) { - var value = macros[key]; + let replacedMacrosUrl = url; + for (const key in macros) { + const value = macros[key]; // this will match [${key}] and %%${key}%% and replace it replacedMacrosUrl = replacedMacrosUrl.replace(new RegExp("(?:\\[|%%)(".concat(key, ")(?:\\]|%%)"), 'g'), value); } @@ -381,7 +206,7 @@ function replaceMacrosValues(url, macros) { */ function extractURLsFromTemplates(URLTemplates) { if (Array.isArray(URLTemplates)) { - return URLTemplates.map(function (URLTemplate) { + return URLTemplates.map(URLTemplate => { return URLTemplate && URLTemplate.hasOwnProperty('url') ? URLTemplate.url : URLTemplate; }); } @@ -389,25 +214,32 @@ function extractURLsFromTemplates(URLTemplates) { } /** - * Filter URLTemplates elements to keep only valid and safe URL templates. + * Filter URLTemplates elements . * To be valid, urls should: * - have the same protocol as the client * or * - be protocol-relative urls * + * Returns an object with two arrays + * - validUrls : An array of valid URLs + * - invalidUrls: An array of invalid URLs + * * @param {Array} URLTemplates - A Array of string/object containing urls templates. + * @returns {Object} + * */ -function filterValidUrlTemplates(URLTemplates) { - if (Array.isArray(URLTemplates)) { - return URLTemplates.filter(function (urlTemplate) { - var url = urlTemplate.hasOwnProperty('url') ? urlTemplate.url : urlTemplate; - return isValidUrl(url); - }); - } - return isValidUrl(URLTemplates); +function filterUrlTemplates(URLTemplates) { + return URLTemplates.reduce((acc, urlTemplate) => { + const url = urlTemplate.url || urlTemplate; + isValidUrl(url) ? acc.validUrls.push(url) : acc.invalidUrls.push(url); + return acc; + }, { + validUrls: [], + invalidUrls: [] + }); } function isValidUrl(url) { - var regex = /^(https?:\/\/|\/\/)/; + const regex = /^(https?:\/\/|\/\/)/; return regex.test(url); } @@ -419,7 +251,7 @@ function isValidUrl(url) { * @param {Array} list - List of objects. */ function containsTemplateObject(obj, list) { - for (var i = 0; i < list.length; i++) { + for (let i = 0; i < list.length; i++) { if (isTemplateObjectEqual(list[i], obj)) { return true; } @@ -436,8 +268,8 @@ function containsTemplateObject(obj, list) { */ function isTemplateObjectEqual(obj1, obj2) { if (obj1 && obj2) { - var obj1Properties = Object.getOwnPropertyNames(obj1); - var obj2Properties = Object.getOwnPropertyNames(obj2); + const obj1Properties = Object.getOwnPropertyNames(obj1); + const obj2Properties = Object.getOwnPropertyNames(obj2); // If number of properties is different, objects are not equivalent if (obj1Properties.length !== obj2Properties.length) { @@ -453,9 +285,7 @@ function isTemplateObjectEqual(obj1, obj2) { // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent function encodeURIComponentRFC3986(str) { - return encodeURIComponent(str).replace(/[!'()*]/g, function (c) { - return "%".concat(c.charCodeAt(0).toString(16)); - }); + return encodeURIComponent(str).replace(/[!'()*]/g, c => "%".concat(c.charCodeAt(0).toString(16))); } /** @@ -467,14 +297,14 @@ function encodeURIComponentRFC3986(str) { * @return {String} */ function addLeadingZeros(input) { - var length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 8; + let length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 8; return input.toString().padStart(length, '0'); } function isNumeric(n) { return !isNaN(parseFloat(n)) && isFinite(n); } function flatten(arr) { - return arr.reduce(function (flat, toFlatten) { + return arr.reduce((flat, toFlatten) => { return flat.concat(Array.isArray(toFlatten) ? flatten(toFlatten) : toFlatten); }, []); } @@ -488,12 +318,12 @@ function flatten(arr) { * @return {Array} */ function joinArrayOfUniqueTemplateObjs() { - var arr1 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; - var arr2 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : []; - var firstArr = Array.isArray(arr1) ? arr1 : []; - var secondArr = Array.isArray(arr2) ? arr2 : []; - var arr = firstArr.concat(secondArr); - return arr.reduce(function (res, val) { + let arr1 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; + let arr2 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : []; + const firstArr = Array.isArray(arr1) ? arr1 : []; + const secondArr = Array.isArray(arr2) ? arr2 : []; + const arr = firstArr.concat(secondArr); + return arr.reduce((res, val) => { if (!containsTemplateObject(val, res)) { res.push(val); } @@ -512,20 +342,34 @@ function joinArrayOfUniqueTemplateObjs() { function isValidTimeValue(time) { return Number.isFinite(time) && time >= -2; } -var util = { - track: track, - resolveURLTemplates: resolveURLTemplates, - extractURLsFromTemplates: extractURLsFromTemplates, - filterValidUrlTemplates: filterValidUrlTemplates, - containsTemplateObject: containsTemplateObject, - isTemplateObjectEqual: isTemplateObjectEqual, - encodeURIComponentRFC3986: encodeURIComponentRFC3986, - replaceUrlMacros: replaceUrlMacros, - isNumeric: isNumeric, - flatten: flatten, - joinArrayOfUniqueTemplateObjs: joinArrayOfUniqueTemplateObjs, - isValidTimeValue: isValidTimeValue, - addLeadingZeros: addLeadingZeros + +/** + * Check if we are in a browser environment + * @returns {Boolean} + */ +function isBrowserEnvironment() { + return typeof window !== 'undefined'; +} +function formatMacrosValues(macros) { + return typeof macros !== 'object' ? macros : JSON.stringify(macros); +} +const util = { + track, + resolveURLTemplates, + extractURLsFromTemplates, + filterUrlTemplates, + containsTemplateObject, + isTemplateObjectEqual, + encodeURIComponentRFC3986, + replaceUrlMacros, + isNumeric, + flatten, + joinArrayOfUniqueTemplateObjs, + isValidTimeValue, + addLeadingZeros, + isValidUrl, + isBrowserEnvironment, + formatMacrosValues }; /** @@ -539,13 +383,8 @@ var util = { * @return {Object|undefined} */ function childByName(node, name) { - var childNodes = node.childNodes; - for (var childKey in childNodes) { - var child = childNodes[childKey]; - if (child.nodeName === name) { - return child; - } - } + const childNodes = Array.from(node.childNodes); + return childNodes.find(childNode => childNode.nodeName === name); } /** @@ -555,15 +394,8 @@ function childByName(node, name) { * @return {Array} */ function childrenByName(node, name) { - var children = []; - var childNodes = node.childNodes; - for (var childKey in childNodes) { - var child = childNodes[childKey]; - if (child.nodeName === name) { - children.push(child); - } - } - return children; + const childNodes = Array.from(node.childNodes); + return childNodes.filter(childNode => childNode.nodeName === name); } /** @@ -576,14 +408,15 @@ function resolveVastAdTagURI(vastAdTagUrl, originalUrl) { if (!originalUrl) { return vastAdTagUrl; } - if (vastAdTagUrl.indexOf('//') === 0) { - var _location = location, - protocol = _location.protocol; + if (vastAdTagUrl.startsWith('//')) { + const { + protocol + } = location; return "".concat(protocol).concat(vastAdTagUrl); } - if (vastAdTagUrl.indexOf('://') === -1) { + if (!vastAdTagUrl.includes('://')) { // Resolve relative URLs (mainly for unit testing) - var baseURL = originalUrl.slice(0, originalUrl.lastIndexOf('/')); + const baseURL = originalUrl.slice(0, originalUrl.lastIndexOf('/')); return "".concat(baseURL, "/").concat(vastAdTagUrl); } return vastAdTagUrl; @@ -595,7 +428,7 @@ function resolveVastAdTagURI(vastAdTagUrl, originalUrl) { * @return {Boolean} */ function parseBoolean(booleanString) { - return ['true', 'TRUE', 'True', '1'].indexOf(booleanString) !== -1; + return ['true', 'TRUE', 'True', '1'].includes(booleanString); } /** @@ -614,7 +447,7 @@ function parseNodeText(node) { * @param {Object} nodeDestination - The destination node to copy the attribute at. */ function copyNodeAttribute(attributeName, nodeSource, nodeDestination) { - var attributeValue = nodeSource.getAttribute(attributeName); + const attributeValue = nodeSource.getAttribute(attributeName); if (attributeValue) { nodeDestination.setAttribute(attributeName, attributeValue); } @@ -627,12 +460,11 @@ function copyNodeAttribute(attributeName, nodeSource, nodeDestination) { * @returns {Object} */ function parseAttributes(element) { - var nodeAttributes = element.attributes; - var attributes = {}; - for (var i = 0; i < nodeAttributes.length; i++) { - attributes[nodeAttributes[i].nodeName] = nodeAttributes[i].nodeValue; - } - return attributes; + const nodeAttributes = Array.from(element.attributes); + return nodeAttributes.reduce((acc, nodeAttribute) => { + acc[nodeAttribute.nodeName] = nodeAttribute.nodeValue; + return acc; + }, {}); } /** @@ -648,17 +480,17 @@ function parseDuration(durationString) { if (util.isNumeric(durationString)) { return parseInt(durationString); } - var durationComponents = durationString.split(':'); + const durationComponents = durationString.split(':'); if (durationComponents.length !== 3) { return -1; } - var secondsAndMS = durationComponents[2].split('.'); - var seconds = parseInt(secondsAndMS[0]); + const secondsAndMS = durationComponents[2].split('.'); + let seconds = parseInt(secondsAndMS[0]); if (secondsAndMS.length === 2) { seconds += parseFloat("0.".concat(secondsAndMS[1])); } - var minutes = parseInt(durationComponents[1] * 60); - var hours = parseInt(durationComponents[0] * 60 * 60); + const minutes = parseInt(durationComponents[1] * 60); + const hours = parseInt(durationComponents[0] * 60 * 60); if (isNaN(hours) || isNaN(minutes) || isNaN(seconds) || minutes > 60 * 60 || seconds > 60) { return -1; } @@ -672,15 +504,15 @@ function parseDuration(durationString) { * @return {Array} */ function splitVAST(ads) { - var splittedVAST = []; - var lastAdPod = null; - ads.forEach(function (ad, i) { + const splittedVAST = []; + let lastAdPod = null; + ads.forEach((ad, i) => { if (ad.sequence) { ad.sequence = parseInt(ad.sequence, 10); } // The current Ad may be the next Ad of an AdPod if (ad.sequence > 1) { - var lastAd = ads[i - 1]; + const lastAd = ads[i - 1]; // check if the current Ad is exactly the next one in the AdPod if (lastAd && lastAd.sequence === ad.sequence - 1) { lastAdPod && lastAdPod.push(ad); @@ -703,16 +535,19 @@ function splitVAST(ads) { */ function assignAttributes(attributes, verificationObject) { if (attributes) { - for (var attrKey in attributes) { - var attribute = attributes[attrKey]; - if (attribute.nodeName && attribute.nodeValue && verificationObject.hasOwnProperty(attribute.nodeName)) { - var value = attribute.nodeValue; - if (typeof verificationObject[attribute.nodeName] === 'boolean') { + Array.from(attributes).forEach(_ref => { + let { + nodeName, + nodeValue + } = _ref; + if (nodeName && nodeValue && verificationObject.hasOwnProperty(nodeName)) { + let value = nodeValue; + if (typeof verificationObject[nodeName] === 'boolean') { value = parseBoolean(value); } - verificationObject[attribute.nodeName] = value; + verificationObject[nodeName] = value; } - } + }); } } @@ -728,19 +563,17 @@ function mergeWrapperAdData(unwrappedAd, wrapper) { unwrappedAd.impressionURLTemplates = wrapper.impressionURLTemplates.concat(unwrappedAd.impressionURLTemplates); unwrappedAd.extensions = wrapper.extensions.concat(unwrappedAd.extensions); if (wrapper.viewableImpression.length > 0) { - unwrappedAd.viewableImpression = [].concat(_toConsumableArray(unwrappedAd.viewableImpression), _toConsumableArray(wrapper.viewableImpression)); + unwrappedAd.viewableImpression = [...unwrappedAd.viewableImpression, ...wrapper.viewableImpression]; } // values from the child wrapper will be overridden unwrappedAd.followAdditionalWrappers = wrapper.followAdditionalWrappers; unwrappedAd.allowMultipleAds = wrapper.allowMultipleAds; unwrappedAd.fallbackOnNoAd = wrapper.fallbackOnNoAd; - var wrapperCompanions = (wrapper.creatives || []).filter(function (creative) { - return creative && creative.type === 'companion'; - }); - var wrapperCompanionClickTracking = wrapperCompanions.reduce(function (result, creative) { - (creative.variations || []).forEach(function (variation) { - (variation.companionClickTrackingURLTemplates || []).forEach(function (companionClickTrackingURLTemplate) { + const wrapperCompanions = (wrapper.creatives || []).filter(creative => creative && creative.type === 'companion'); + const wrapperCompanionClickTracking = wrapperCompanions.reduce((result, creative) => { + (creative.variations || []).forEach(variation => { + (variation.companionClickTrackingURLTemplates || []).forEach(companionClickTrackingURLTemplate => { if (!util.containsTemplateObject(companionClickTrackingURLTemplate, result)) { result.push(companionClickTrackingURLTemplate); } @@ -749,13 +582,13 @@ function mergeWrapperAdData(unwrappedAd, wrapper) { return result; }, []); unwrappedAd.creatives = wrapperCompanions.concat(unwrappedAd.creatives); - var wrapperHasVideoClickTracking = wrapper.videoClickTrackingURLTemplates && wrapper.videoClickTrackingURLTemplates.length; - var wrapperHasVideoCustomClick = wrapper.videoCustomClickURLTemplates && wrapper.videoCustomClickURLTemplates.length; - unwrappedAd.creatives.forEach(function (creative) { + const wrapperHasVideoClickTracking = wrapper.videoClickTrackingURLTemplates && wrapper.videoClickTrackingURLTemplates.length; + const wrapperHasVideoCustomClick = wrapper.videoCustomClickURLTemplates && wrapper.videoCustomClickURLTemplates.length; + unwrappedAd.creatives.forEach(creative => { // merge tracking events if (wrapper.trackingEvents && wrapper.trackingEvents[creative.type]) { - for (var eventName in wrapper.trackingEvents[creative.type]) { - var urls = wrapper.trackingEvents[creative.type][eventName]; + for (const eventName in wrapper.trackingEvents[creative.type]) { + const urls = wrapper.trackingEvents[creative.type][eventName]; if (!Array.isArray(creative.trackingEvents[eventName])) { creative.trackingEvents[eventName] = []; } @@ -781,7 +614,7 @@ function mergeWrapperAdData(unwrappedAd, wrapper) { // pass wrapper companion trackers to all companions if (creative.type === 'companion' && wrapperCompanionClickTracking.length) { - (creative.variations || []).forEach(function (variation) { + (creative.variations || []).forEach(variation => { variation.companionClickTrackingURLTemplates = util.joinArrayOfUniqueTemplateObjs(variation.companionClickTrackingURLTemplates, wrapperCompanionClickTracking); }); } @@ -797,7 +630,7 @@ function mergeWrapperAdData(unwrappedAd, wrapper) { // Merge Wrapper's creatives containing icon elements if ((_wrapper$creatives = wrapper.creatives) !== null && _wrapper$creatives !== void 0 && _wrapper$creatives.length) { // As specified by VAST specs, wrapper should not contain any mediafiles - var wrapperCreativesWithIconsNode = wrapper.creatives.filter(function (creative) { + const wrapperCreativesWithIconsNode = wrapper.creatives.filter(creative => { var _creative$icons; return ((_creative$icons = creative.icons) === null || _creative$icons === void 0 ? void 0 : _creative$icons.length) && !creative.mediaFiles.length; }); @@ -806,18 +639,18 @@ function mergeWrapperAdData(unwrappedAd, wrapper) { } } } -var parserUtils = { - childByName: childByName, - childrenByName: childrenByName, - resolveVastAdTagURI: resolveVastAdTagURI, - parseBoolean: parseBoolean, - parseNodeText: parseNodeText, - copyNodeAttribute: copyNodeAttribute, - parseAttributes: parseAttributes, - parseDuration: parseDuration, - splitVAST: splitVAST, - assignAttributes: assignAttributes, - mergeWrapperAdData: mergeWrapperAdData +const parserUtils = { + childByName, + childrenByName, + resolveVastAdTagURI, + parseBoolean, + parseNodeText, + copyNodeAttribute, + parseAttributes, + parseDuration, + splitVAST, + assignAttributes, + mergeWrapperAdData }; /** @@ -831,31 +664,31 @@ var parserUtils = { * @return {Object} creative - The creative object. */ function parseCreativeCompanion(creativeElement, creativeAttributes) { - var creative = createCreativeCompanion(creativeAttributes); + const creative = createCreativeCompanion(creativeAttributes); creative.required = creativeElement.getAttribute('required') || null; - creative.variations = parserUtils.childrenByName(creativeElement, 'Companion').map(function (companionResource) { - var companionAd = createCompanionAd(parserUtils.parseAttributes(companionResource)); - companionAd.htmlResources = parserUtils.childrenByName(companionResource, 'HTMLResource').reduce(function (urls, resource) { - var url = parserUtils.parseNodeText(resource); + creative.variations = parserUtils.childrenByName(creativeElement, 'Companion').map(companionResource => { + const companionAd = createCompanionAd(parserUtils.parseAttributes(companionResource)); + companionAd.htmlResources = parserUtils.childrenByName(companionResource, 'HTMLResource').reduce((urls, resource) => { + const url = parserUtils.parseNodeText(resource); return url ? urls.concat(url) : urls; }, []); - companionAd.iframeResources = parserUtils.childrenByName(companionResource, 'IFrameResource').reduce(function (urls, resource) { - var url = parserUtils.parseNodeText(resource); + companionAd.iframeResources = parserUtils.childrenByName(companionResource, 'IFrameResource').reduce((urls, resource) => { + const url = parserUtils.parseNodeText(resource); return url ? urls.concat(url) : urls; }, []); - companionAd.staticResources = parserUtils.childrenByName(companionResource, 'StaticResource').reduce(function (urls, resource) { - var url = parserUtils.parseNodeText(resource); + companionAd.staticResources = parserUtils.childrenByName(companionResource, 'StaticResource').reduce((urls, resource) => { + const url = parserUtils.parseNodeText(resource); return url ? urls.concat({ - url: url, + url, creativeType: resource.getAttribute('creativeType') || null }) : urls; }, []); companionAd.altText = parserUtils.parseNodeText(parserUtils.childByName(companionResource, 'AltText')) || null; - var trackingEventsElement = parserUtils.childByName(companionResource, 'TrackingEvents'); + const trackingEventsElement = parserUtils.childByName(companionResource, 'TrackingEvents'); if (trackingEventsElement) { - parserUtils.childrenByName(trackingEventsElement, 'Tracking').forEach(function (trackingElement) { - var eventName = trackingElement.getAttribute('event'); - var trackingURLTemplate = parserUtils.parseNodeText(trackingElement); + parserUtils.childrenByName(trackingEventsElement, 'Tracking').forEach(trackingElement => { + const eventName = trackingElement.getAttribute('event'); + const trackingURLTemplate = parserUtils.parseNodeText(trackingElement); if (eventName && trackingURLTemplate) { if (!Array.isArray(companionAd.trackingEvents[eventName])) { companionAd.trackingEvents[eventName] = []; @@ -864,14 +697,14 @@ function parseCreativeCompanion(creativeElement, creativeAttributes) { } }); } - companionAd.companionClickTrackingURLTemplates = parserUtils.childrenByName(companionResource, 'CompanionClickTracking').map(function (clickTrackingElement) { + companionAd.companionClickTrackingURLTemplates = parserUtils.childrenByName(companionResource, 'CompanionClickTracking').map(clickTrackingElement => { return { id: clickTrackingElement.getAttribute('id') || null, url: parserUtils.parseNodeText(clickTrackingElement) }; }); companionAd.companionClickThroughURLTemplate = parserUtils.parseNodeText(parserUtils.childByName(companionResource, 'CompanionClickThrough')) || null; - var adParametersElement = parserUtils.childByName(companionResource, 'AdParameters'); + const adParametersElement = parserUtils.childByName(companionResource, 'AdParameters'); if (adParametersElement) { companionAd.adParameters = { value: parserUtils.parseNodeText(adParametersElement), @@ -884,17 +717,18 @@ function parseCreativeCompanion(creativeElement, creativeAttributes) { } function createCreativeLinear() { - var creativeAttributes = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; - var _createCreative = createCreative(creativeAttributes), - id = _createCreative.id, - adId = _createCreative.adId, - sequence = _createCreative.sequence, - apiFramework = _createCreative.apiFramework; + let creativeAttributes = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + const { + id, + adId, + sequence, + apiFramework + } = createCreative(creativeAttributes); return { - id: id, - adId: adId, - sequence: sequence, - apiFramework: apiFramework, + id, + adId, + sequence, + apiFramework, type: 'linear', duration: 0, skipDelay: null, @@ -915,7 +749,7 @@ function isCreativeLinear(ad) { } function createClosedCaptionFile() { - var closedCaptionAttributes = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + let closedCaptionAttributes = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; return { type: closedCaptionAttributes.type || null, language: closedCaptionAttributes.language || null, @@ -946,7 +780,7 @@ function createIcon() { } function createInteractiveCreativeFile() { - var interactiveCreativeAttributes = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + let interactiveCreativeAttributes = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; return { type: interactiveCreativeAttributes.type || null, apiFramework: interactiveCreativeAttributes.apiFramework || null, @@ -1001,21 +835,21 @@ function createMezzanine() { * @return {Object} creative - The creativeLinear object. */ function parseCreativeLinear(creativeElement, creativeAttributes) { - var offset; - var creative = createCreativeLinear(creativeAttributes); + let offset; + const creative = createCreativeLinear(creativeAttributes); creative.duration = parserUtils.parseDuration(parserUtils.parseNodeText(parserUtils.childByName(creativeElement, 'Duration'))); - var skipOffset = creativeElement.getAttribute('skipoffset'); + const skipOffset = creativeElement.getAttribute('skipoffset'); if (typeof skipOffset === 'undefined' || skipOffset === null) { creative.skipDelay = null; } else if (skipOffset.charAt(skipOffset.length - 1) === '%' && creative.duration !== -1) { - var percent = parseInt(skipOffset, 10); + const percent = parseInt(skipOffset, 10); creative.skipDelay = creative.duration * (percent / 100); } else { creative.skipDelay = parserUtils.parseDuration(skipOffset); } - var videoClicksElement = parserUtils.childByName(creativeElement, 'VideoClicks'); + const videoClicksElement = parserUtils.childByName(creativeElement, 'VideoClicks'); if (videoClicksElement) { - var videoClickThroughElement = parserUtils.childByName(videoClicksElement, 'ClickThrough'); + const videoClickThroughElement = parserUtils.childByName(videoClicksElement, 'ClickThrough'); if (videoClickThroughElement) { creative.videoClickThroughURLTemplate = { id: videoClickThroughElement.getAttribute('id') || null, @@ -1024,30 +858,30 @@ function parseCreativeLinear(creativeElement, creativeAttributes) { } else { creative.videoClickThroughURLTemplate = null; } - parserUtils.childrenByName(videoClicksElement, 'ClickTracking').forEach(function (clickTrackingElement) { + parserUtils.childrenByName(videoClicksElement, 'ClickTracking').forEach(clickTrackingElement => { creative.videoClickTrackingURLTemplates.push({ id: clickTrackingElement.getAttribute('id') || null, url: parserUtils.parseNodeText(clickTrackingElement) }); }); - parserUtils.childrenByName(videoClicksElement, 'CustomClick').forEach(function (customClickElement) { + parserUtils.childrenByName(videoClicksElement, 'CustomClick').forEach(customClickElement => { creative.videoCustomClickURLTemplates.push({ id: customClickElement.getAttribute('id') || null, url: parserUtils.parseNodeText(customClickElement) }); }); } - var adParamsElement = parserUtils.childByName(creativeElement, 'AdParameters'); + const adParamsElement = parserUtils.childByName(creativeElement, 'AdParameters'); if (adParamsElement) { creative.adParameters = { value: parserUtils.parseNodeText(adParamsElement), xmlEncoded: adParamsElement.getAttribute('xmlEncoded') || null }; } - parserUtils.childrenByName(creativeElement, 'TrackingEvents').forEach(function (trackingEventsElement) { - parserUtils.childrenByName(trackingEventsElement, 'Tracking').forEach(function (trackingElement) { - var eventName = trackingElement.getAttribute('event'); - var trackingURLTemplate = parserUtils.parseNodeText(trackingElement); + parserUtils.childrenByName(creativeElement, 'TrackingEvents').forEach(trackingEventsElement => { + parserUtils.childrenByName(trackingEventsElement, 'Tracking').forEach(trackingElement => { + let eventName = trackingElement.getAttribute('event'); + const trackingURLTemplate = parserUtils.parseNodeText(trackingElement); if (eventName && trackingURLTemplate) { if (eventName === 'progress') { offset = trackingElement.getAttribute('offset'); @@ -1067,26 +901,26 @@ function parseCreativeLinear(creativeElement, creativeAttributes) { } }); }); - parserUtils.childrenByName(creativeElement, 'MediaFiles').forEach(function (mediaFilesElement) { - parserUtils.childrenByName(mediaFilesElement, 'MediaFile').forEach(function (mediaFileElement) { + parserUtils.childrenByName(creativeElement, 'MediaFiles').forEach(mediaFilesElement => { + parserUtils.childrenByName(mediaFilesElement, 'MediaFile').forEach(mediaFileElement => { creative.mediaFiles.push(parseMediaFile(mediaFileElement)); }); - var interactiveCreativeElement = parserUtils.childByName(mediaFilesElement, 'InteractiveCreativeFile'); + const interactiveCreativeElement = parserUtils.childByName(mediaFilesElement, 'InteractiveCreativeFile'); if (interactiveCreativeElement) { creative.interactiveCreativeFile = parseInteractiveCreativeFile(interactiveCreativeElement); } - var closedCaptionElements = parserUtils.childByName(mediaFilesElement, 'ClosedCaptionFiles'); + const closedCaptionElements = parserUtils.childByName(mediaFilesElement, 'ClosedCaptionFiles'); if (closedCaptionElements) { - parserUtils.childrenByName(closedCaptionElements, 'ClosedCaptionFile').forEach(function (closedCaptionElement) { - var closedCaptionFile = createClosedCaptionFile(parserUtils.parseAttributes(closedCaptionElement)); + parserUtils.childrenByName(closedCaptionElements, 'ClosedCaptionFile').forEach(closedCaptionElement => { + const closedCaptionFile = createClosedCaptionFile(parserUtils.parseAttributes(closedCaptionElement)); closedCaptionFile.fileURL = parserUtils.parseNodeText(closedCaptionElement); creative.closedCaptionFiles.push(closedCaptionFile); }); } - var mezzanineElement = parserUtils.childByName(mediaFilesElement, 'Mezzanine'); - var requiredAttributes = getRequiredAttributes(mezzanineElement, ['delivery', 'type', 'width', 'height']); + const mezzanineElement = parserUtils.childByName(mediaFilesElement, 'Mezzanine'); + const requiredAttributes = getRequiredAttributes(mezzanineElement, ['delivery', 'type', 'width', 'height']); if (requiredAttributes) { - var mezzanine = createMezzanine(); + const mezzanine = createMezzanine(); mezzanine.id = mezzanineElement.getAttribute('id'); mezzanine.fileURL = parserUtils.parseNodeText(mezzanineElement); mezzanine.delivery = requiredAttributes.delivery; @@ -1099,9 +933,9 @@ function parseCreativeLinear(creativeElement, creativeAttributes) { creative.mezzanine = mezzanine; } }); - var iconsElement = parserUtils.childByName(creativeElement, 'Icons'); + const iconsElement = parserUtils.childByName(creativeElement, 'Icons'); if (iconsElement) { - parserUtils.childrenByName(iconsElement, 'Icon').forEach(function (iconElement) { + parserUtils.childrenByName(iconsElement, 'Icon').forEach(iconElement => { creative.icons.push(parseIcon(iconElement)); }); } @@ -1114,7 +948,7 @@ function parseCreativeLinear(creativeElement, creativeAttributes) { * @return {Object} - Parsed mediaFile object. */ function parseMediaFile(mediaFileElement) { - var mediaFile = createMediaFile(); + const mediaFile = createMediaFile(); mediaFile.id = mediaFileElement.getAttribute('id'); mediaFile.fileURL = parserUtils.parseNodeText(mediaFileElement); mediaFile.deliveryType = mediaFileElement.getAttribute('delivery'); @@ -1128,11 +962,11 @@ function parseMediaFile(mediaFileElement) { mediaFile.maxBitrate = parseInt(mediaFileElement.getAttribute('maxBitrate') || 0); mediaFile.width = parseInt(mediaFileElement.getAttribute('width') || 0); mediaFile.height = parseInt(mediaFileElement.getAttribute('height') || 0); - var scalable = mediaFileElement.getAttribute('scalable'); + const scalable = mediaFileElement.getAttribute('scalable'); if (scalable && typeof scalable === 'string') { mediaFile.scalable = parserUtils.parseBoolean(scalable); } - var maintainAspectRatio = mediaFileElement.getAttribute('maintainAspectRatio'); + const maintainAspectRatio = mediaFileElement.getAttribute('maintainAspectRatio'); if (maintainAspectRatio && typeof maintainAspectRatio === 'string') { mediaFile.maintainAspectRatio = parserUtils.parseBoolean(maintainAspectRatio); } @@ -1145,7 +979,7 @@ function parseMediaFile(mediaFileElement) { * @return {Object} - Parsed interactiveCreativeFile object. */ function parseInteractiveCreativeFile(interactiveCreativeElement) { - var interactiveCreativeFile = createInteractiveCreativeFile(parserUtils.parseAttributes(interactiveCreativeElement)); + const interactiveCreativeFile = createInteractiveCreativeFile(parserUtils.parseAttributes(interactiveCreativeElement)); interactiveCreativeFile.fileURL = parserUtils.parseNodeText(interactiveCreativeElement); return interactiveCreativeFile; } @@ -1156,7 +990,7 @@ function parseInteractiveCreativeFile(interactiveCreativeElement) { * @return {Object} - Parsed icon object. */ function parseIcon(iconElement) { - var icon = createIcon(); + const icon = createIcon(); icon.program = iconElement.getAttribute('program'); icon.height = parseInt(iconElement.getAttribute('height') || 0); icon.width = parseInt(iconElement.getAttribute('width') || 0); @@ -1166,30 +1000,30 @@ function parseIcon(iconElement) { icon.pxratio = iconElement.getAttribute('pxratio') || '1'; icon.offset = parserUtils.parseDuration(iconElement.getAttribute('offset')); icon.duration = parserUtils.parseDuration(iconElement.getAttribute('duration')); - parserUtils.childrenByName(iconElement, 'HTMLResource').forEach(function (htmlElement) { + parserUtils.childrenByName(iconElement, 'HTMLResource').forEach(htmlElement => { icon.type = htmlElement.getAttribute('creativeType') || 'text/html'; icon.htmlResource = parserUtils.parseNodeText(htmlElement); }); - parserUtils.childrenByName(iconElement, 'IFrameResource').forEach(function (iframeElement) { + parserUtils.childrenByName(iconElement, 'IFrameResource').forEach(iframeElement => { icon.type = iframeElement.getAttribute('creativeType') || 0; icon.iframeResource = parserUtils.parseNodeText(iframeElement); }); - parserUtils.childrenByName(iconElement, 'StaticResource').forEach(function (staticElement) { + parserUtils.childrenByName(iconElement, 'StaticResource').forEach(staticElement => { icon.type = staticElement.getAttribute('creativeType') || 0; icon.staticResource = parserUtils.parseNodeText(staticElement); }); - var iconClicksElement = parserUtils.childByName(iconElement, 'IconClicks'); + const iconClicksElement = parserUtils.childByName(iconElement, 'IconClicks'); if (iconClicksElement) { icon.iconClickThroughURLTemplate = parserUtils.parseNodeText(parserUtils.childByName(iconClicksElement, 'IconClickThrough')); - parserUtils.childrenByName(iconClicksElement, 'IconClickTracking').forEach(function (iconClickTrackingElement) { + parserUtils.childrenByName(iconClicksElement, 'IconClickTracking').forEach(iconClickTrackingElement => { icon.iconClickTrackingURLTemplates.push({ id: iconClickTrackingElement.getAttribute('id') || null, url: parserUtils.parseNodeText(iconClickTrackingElement) }); }); - var iconClickFallbackImagesElement = parserUtils.childByName(iconClicksElement, 'IconClickFallbackImages'); + const iconClickFallbackImagesElement = parserUtils.childByName(iconClicksElement, 'IconClickFallbackImages'); if (iconClickFallbackImagesElement) { - parserUtils.childrenByName(iconClickFallbackImagesElement, 'IconClickFallbackImage').forEach(function (iconClickFallbackImageElement) { + parserUtils.childrenByName(iconClickFallbackImagesElement, 'IconClickFallbackImage').forEach(iconClickFallbackImageElement => { icon.iconClickFallbackImages.push({ url: parserUtils.parseNodeText(iconClickFallbackImageElement) || null, width: iconClickFallbackImageElement.getAttribute('width') || null, @@ -1233,9 +1067,9 @@ function parseYPosition(yPosition) { * @return {Object|null} null if a least one element not present */ function getRequiredAttributes(element, attributes) { - var values = {}; - var error = false; - attributes.forEach(function (name) { + const values = {}; + let error = false; + attributes.forEach(name => { if (!element || !element.getAttribute(name)) { error = true; } else { @@ -1246,17 +1080,18 @@ function getRequiredAttributes(element, attributes) { } function createCreativeNonLinear() { - var creativeAttributes = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; - var _createCreative = createCreative(creativeAttributes), - id = _createCreative.id, - adId = _createCreative.adId, - sequence = _createCreative.sequence, - apiFramework = _createCreative.apiFramework; + let creativeAttributes = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + const { + id, + adId, + sequence, + apiFramework + } = createCreative(creativeAttributes); return { - id: id, - adId: adId, - sequence: sequence, - apiFramework: apiFramework, + id, + adId, + sequence, + apiFramework, type: 'nonlinear', variations: [], trackingEvents: {} @@ -1299,10 +1134,10 @@ function isNonLinearAd(ad) { * @return {Object} creative - The CreativeNonLinear object. */ function parseCreativeNonLinear(creativeElement, creativeAttributes) { - var creative = createCreativeNonLinear(creativeAttributes); - parserUtils.childrenByName(creativeElement, 'TrackingEvents').forEach(function (trackingEventsElement) { - var eventName, trackingURLTemplate; - parserUtils.childrenByName(trackingEventsElement, 'Tracking').forEach(function (trackingElement) { + const creative = createCreativeNonLinear(creativeAttributes); + parserUtils.childrenByName(creativeElement, 'TrackingEvents').forEach(trackingEventsElement => { + let eventName, trackingURLTemplate; + parserUtils.childrenByName(trackingEventsElement, 'Tracking').forEach(trackingElement => { eventName = trackingElement.getAttribute('event'); trackingURLTemplate = parserUtils.parseNodeText(trackingElement); if (eventName && trackingURLTemplate) { @@ -1313,8 +1148,8 @@ function parseCreativeNonLinear(creativeElement, creativeAttributes) { } }); }); - parserUtils.childrenByName(creativeElement, 'NonLinear').forEach(function (nonlinearResource) { - var nonlinearAd = createNonLinearAd(); + parserUtils.childrenByName(creativeElement, 'NonLinear').forEach(nonlinearResource => { + const nonlinearAd = createNonLinearAd(); nonlinearAd.id = nonlinearResource.getAttribute('id') || null; nonlinearAd.width = nonlinearResource.getAttribute('width'); nonlinearAd.height = nonlinearResource.getAttribute('height'); @@ -1324,19 +1159,19 @@ function parseCreativeNonLinear(creativeElement, creativeAttributes) { nonlinearAd.maintainAspectRatio = parserUtils.parseBoolean(nonlinearResource.getAttribute('maintainAspectRatio')); nonlinearAd.minSuggestedDuration = parserUtils.parseDuration(nonlinearResource.getAttribute('minSuggestedDuration')); nonlinearAd.apiFramework = nonlinearResource.getAttribute('apiFramework'); - parserUtils.childrenByName(nonlinearResource, 'HTMLResource').forEach(function (htmlElement) { + parserUtils.childrenByName(nonlinearResource, 'HTMLResource').forEach(htmlElement => { nonlinearAd.type = htmlElement.getAttribute('creativeType') || 'text/html'; nonlinearAd.htmlResource = parserUtils.parseNodeText(htmlElement); }); - parserUtils.childrenByName(nonlinearResource, 'IFrameResource').forEach(function (iframeElement) { + parserUtils.childrenByName(nonlinearResource, 'IFrameResource').forEach(iframeElement => { nonlinearAd.type = iframeElement.getAttribute('creativeType') || 0; nonlinearAd.iframeResource = parserUtils.parseNodeText(iframeElement); }); - parserUtils.childrenByName(nonlinearResource, 'StaticResource').forEach(function (staticElement) { + parserUtils.childrenByName(nonlinearResource, 'StaticResource').forEach(staticElement => { nonlinearAd.type = staticElement.getAttribute('creativeType') || 0; nonlinearAd.staticResource = parserUtils.parseNodeText(staticElement); }); - var adParamsElement = parserUtils.childByName(nonlinearResource, 'AdParameters'); + const adParamsElement = parserUtils.childByName(nonlinearResource, 'AdParameters'); if (adParamsElement) { nonlinearAd.adParameters = { value: parserUtils.parseNodeText(adParamsElement), @@ -1344,7 +1179,7 @@ function parseCreativeNonLinear(creativeElement, creativeAttributes) { }; } nonlinearAd.nonlinearClickThroughURLTemplate = parserUtils.parseNodeText(parserUtils.childByName(nonlinearResource, 'NonLinearClickThrough')); - parserUtils.childrenByName(nonlinearResource, 'NonLinearClickTracking').forEach(function (clickTrackingElement) { + parserUtils.childrenByName(nonlinearResource, 'NonLinearClickTracking').forEach(clickTrackingElement => { nonlinearAd.nonlinearClickTrackingURLTemplates.push({ id: clickTrackingElement.getAttribute('id') || null, url: parserUtils.parseNodeText(clickTrackingElement) @@ -1374,9 +1209,9 @@ function isEmptyExtension(extension) { * @return {AdExtension[]|CreativeExtension[]} - The nodes parsed to extensions */ function parseExtensions(extensions) { - var exts = []; - extensions.forEach(function (extNode) { - var ext = _parseExtension(extNode); + const exts = []; + extensions.forEach(extNode => { + const ext = _parseExtension(extNode); if (ext) { exts.push(ext); } @@ -1392,16 +1227,16 @@ function parseExtensions(extensions) { function _parseExtension(extNode) { // Ignore comments if (extNode.nodeName === '#comment') return null; - var ext = createExtension(); - var extNodeAttrs = extNode.attributes; - var childNodes = extNode.childNodes; + const ext = createExtension(); + const extNodeAttrs = extNode.attributes; + const childNodes = extNode.childNodes; ext.name = extNode.nodeName; // Parse attributes if (extNode.attributes) { - for (var extNodeAttrKey in extNodeAttrs) { + for (const extNodeAttrKey in extNodeAttrs) { if (extNodeAttrs.hasOwnProperty(extNodeAttrKey)) { - var extNodeAttr = extNodeAttrs[extNodeAttrKey]; + const extNodeAttr = extNodeAttrs[extNodeAttrKey]; if (extNodeAttr.nodeName && extNodeAttr.nodeValue) { ext.attributes[extNodeAttr.nodeName] = extNodeAttr.nodeValue; } @@ -1410,9 +1245,9 @@ function _parseExtension(extNode) { } // Parse all children - for (var childNodeKey in childNodes) { + for (const childNodeKey in childNodes) { if (childNodes.hasOwnProperty(childNodeKey)) { - var parsedChild = _parseExtension(childNodes[childNodeKey]); + const parsedChild = _parseExtension(childNodes[childNodeKey]); if (parsedChild) { ext.children.push(parsedChild); } @@ -1424,7 +1259,7 @@ function _parseExtension(extNode) { to avoid useless parsing that would result to a concatenation of all children */ if (ext.children.length === 0 || ext.children.length === 1 && ['#cdata-section', '#text'].indexOf(ext.children[0].name) >= 0) { - var txt = parserUtils.parseNodeText(extNode); + const txt = parserUtils.parseNodeText(extNode); if (txt !== '') { ext.value = txt; } @@ -1443,31 +1278,31 @@ function _parseExtension(extNode) { * @return {Array} - An array of Creative objects. */ function parseCreatives(creativeNodes) { - var creatives = []; - creativeNodes.forEach(function (creativeElement) { - var creativeAttributes = { + const creatives = []; + creativeNodes.forEach(creativeElement => { + const creativeAttributes = { id: creativeElement.getAttribute('id') || null, adId: parseCreativeAdIdAttribute(creativeElement), sequence: creativeElement.getAttribute('sequence') || null, apiFramework: creativeElement.getAttribute('apiFramework') || null }; - var universalAdIds = []; - var universalAdIdElements = parserUtils.childrenByName(creativeElement, 'UniversalAdId'); - universalAdIdElements.forEach(function (universalAdIdElement) { - var universalAdId = { + const universalAdIds = []; + const universalAdIdElements = parserUtils.childrenByName(creativeElement, 'UniversalAdId'); + universalAdIdElements.forEach(universalAdIdElement => { + const universalAdId = { idRegistry: universalAdIdElement.getAttribute('idRegistry') || 'unknown', value: parserUtils.parseNodeText(universalAdIdElement) }; universalAdIds.push(universalAdId); }); - var creativeExtensions; - var creativeExtensionsElement = parserUtils.childByName(creativeElement, 'CreativeExtensions'); + let creativeExtensions; + const creativeExtensionsElement = parserUtils.childByName(creativeElement, 'CreativeExtensions'); if (creativeExtensionsElement) { creativeExtensions = parseExtensions(parserUtils.childrenByName(creativeExtensionsElement, 'CreativeExtension')); } - for (var creativeTypeElementKey in creativeElement.childNodes) { - var creativeTypeElement = creativeElement.childNodes[creativeTypeElementKey]; - var parsedCreative = void 0; + for (const creativeTypeElementKey in creativeElement.childNodes) { + const creativeTypeElement = creativeElement.childNodes[creativeTypeElementKey]; + let parsedCreative; switch (creativeTypeElement.nodeName) { case 'Linear': parsedCreative = parseCreativeLinear(creativeTypeElement, creativeAttributes); @@ -1508,7 +1343,7 @@ function parseCreativeAdIdAttribute(creativeElement) { null; } -var requiredValues = { +const requiredValues = { Wrapper: { subElements: ['VASTAdTagURI', 'Impression'] }, @@ -1595,7 +1430,7 @@ function verifyRequiredValues(node, emit, isAdInline) { verifyRequiredAttributes(node, emit); if (hasSubElements(node)) { verifyRequiredSubElements(node, emit, isAdInline); - for (var i = 0; i < node.children.length; i++) { + for (let i = 0; i < node.children.length; i++) { verifyRequiredValues(node.children[i], emit, isAdInline); } } else if (parserUtils.parseNodeText(node).length === 0) { @@ -1616,10 +1451,8 @@ function verifyRequiredAttributes(node, emit) { if (!requiredValues[node.nodeName] || !requiredValues[node.nodeName].attributes) { return; } - var requiredAttributes = requiredValues[node.nodeName].attributes; - var missingAttributes = requiredAttributes.filter(function (attributeName) { - return !node.getAttribute(attributeName); - }); + const requiredAttributes = requiredValues[node.nodeName].attributes; + const missingAttributes = requiredAttributes.filter(attributeName => !node.getAttribute(attributeName)); if (missingAttributes.length > 0) { emitMissingValueWarning({ name: node.nodeName, @@ -1637,18 +1470,16 @@ function verifyRequiredAttributes(node, emit) { * @emits VASTParser#VAST-warning */ function verifyRequiredSubElements(node, emit, isAdInline) { - var required = requiredValues[node.nodeName]; + const required = requiredValues[node.nodeName]; // Do not verify subelement if node is a child of wrapper, but verify it if node is the Wrapper itself // Wrapper child have no required subElement. (Only InLine does) - var isInWrapperButNotWrapperItself = !isAdInline && node.nodeName !== 'Wrapper'; + const isInWrapperButNotWrapperItself = !isAdInline && node.nodeName !== 'Wrapper'; if (!required || isInWrapperButNotWrapperItself) { return; } if (required.subElements) { - var requiredSubElements = required.subElements; - var missingSubElements = requiredSubElements.filter(function (subElementName) { - return !parserUtils.childByName(node, subElementName); - }); + const requiredSubElements = required.subElements; + const missingSubElements = requiredSubElements.filter(subElementName => !parserUtils.childByName(node, subElementName)); if (missingSubElements.length > 0) { emitMissingValueWarning({ name: node.nodeName, @@ -1663,7 +1494,7 @@ function verifyRequiredSubElements(node, emit, isAdInline) { if (!isAdInline || !required.oneOfinLineResources) { return; } - var resourceFound = required.oneOfinLineResources.some(function (resource) { + const resourceFound = required.oneOfinLineResources.some(resource => { return parserUtils.childByName(node, resource); }); if (!resourceFound) { @@ -1696,12 +1527,14 @@ function hasSubElements(node) { * @emits VastParser#VAST-warning */ function emitMissingValueWarning(_ref, emit) { - var name = _ref.name, - parentName = _ref.parentName, - attributes = _ref.attributes, - subElements = _ref.subElements, - oneOfResources = _ref.oneOfResources; - var message = "Element '".concat(name, "'"); + let { + name, + parentName, + attributes, + subElements, + oneOfResources + } = _ref; + let message = "Element '".concat(name, "'"); if (attributes) { message += " missing required attribute(s) '".concat(attributes.join(', '), "' "); } else if (subElements) { @@ -1712,17 +1545,17 @@ function emitMissingValueWarning(_ref, emit) { message += " is empty"; } emit('VAST-warning', { - message: message, + message, parentElement: parentName, specVersion: 4.1 }); } -var parserVerification = { - verifyRequiredValues: verifyRequiredValues, - hasSubElements: hasSubElements, - emitMissingValueWarning: emitMissingValueWarning, - verifyRequiredAttributes: verifyRequiredAttributes, - verifyRequiredSubElements: verifyRequiredSubElements +const parserVerification = { + verifyRequiredValues, + hasSubElements, + emitMissingValueWarning, + verifyRequiredAttributes, + verifyRequiredSubElements }; /** @@ -1738,34 +1571,38 @@ var parserVerification = { * @return {Object|undefined} - Object containing the ad and if it is wrapper/inline */ function parseAd(adElement, emit) { - var _ref = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}, - allowMultipleAds = _ref.allowMultipleAds, - followAdditionalWrappers = _ref.followAdditionalWrappers; - var childNodes = adElement.childNodes; - for (var adTypeElementKey in childNodes) { - var adTypeElement = childNodes[adTypeElementKey]; - if (['Wrapper', 'InLine'].indexOf(adTypeElement.nodeName) === -1) { - continue; - } - if (adTypeElement.nodeName === 'Wrapper' && followAdditionalWrappers === false) { - continue; - } - parserUtils.copyNodeAttribute('id', adElement, adTypeElement); - parserUtils.copyNodeAttribute('sequence', adElement, adTypeElement); - parserUtils.copyNodeAttribute('adType', adElement, adTypeElement); - if (adTypeElement.nodeName === 'Wrapper') { + let { + allowMultipleAds, + followAdditionalWrappers + } = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; + const childNodes = Array.from(adElement.childNodes); + const filteredChildNodes = childNodes.filter(childNode => { + const childNodeToLowerCase = childNode.nodeName.toLowerCase(); + return childNodeToLowerCase === 'inline' || followAdditionalWrappers !== false && childNodeToLowerCase === 'wrapper'; + }); + for (const node of filteredChildNodes) { + parserUtils.copyNodeAttribute('id', adElement, node); + parserUtils.copyNodeAttribute('sequence', adElement, node); + parserUtils.copyNodeAttribute('adType', adElement, node); + if (node.nodeName === 'Wrapper') { return { - ad: parseWrapper(adTypeElement, emit), + ad: parseWrapper(node, emit), type: 'WRAPPER' }; - } else if (adTypeElement.nodeName === 'InLine') { + } else if (node.nodeName === 'InLine') { return { - ad: parseInLine(adTypeElement, emit, { - allowMultipleAds: allowMultipleAds + ad: parseInLine(node, emit, { + allowMultipleAds }), type: 'INLINE' }; } + const wrongNode = node.nodeName.toLowerCase(); + const message = wrongNode === 'inline' ? "<".concat(node.nodeName, "> must be written ") : "<".concat(node.nodeName, "> must be written "); + emit('VAST-warning', { + message, + wrongNode: node + }); } } @@ -1778,8 +1615,9 @@ function parseAd(adElement, emit) { * @return {Object} ad - The ad object. */ function parseInLine(adElement, emit) { - var _ref2 = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}, - allowMultipleAds = _ref2.allowMultipleAds; + let { + allowMultipleAds + } = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; // if allowMultipleAds is set to false by wrapper attribute // only the first stand-alone Ad (with no sequence values) in the // requested VAST response is allowed so we won't parse ads with sequence @@ -1797,14 +1635,13 @@ function parseInLine(adElement, emit) { * @return {Object} ad - The ad object. */ function parseAdElement(adTypeElement, emit) { - var adVerificationsFromExtensions = []; + let adVerificationsFromExtensions = []; if (emit) { parserVerification.verifyRequiredValues(adTypeElement, emit); } - var childNodes = adTypeElement.childNodes; - var ad = createAd(parserUtils.parseAttributes(adTypeElement)); - for (var nodeKey in childNodes) { - var node = childNodes[nodeKey]; + const childNodes = Array.from(adTypeElement.childNodes); + const ad = createAd(parserUtils.parseAttributes(adTypeElement)); + childNodes.forEach(node => { switch (node.nodeName) { case 'Error': ad.errorURLTemplates.push(parserUtils.parseNodeText(node)); @@ -1820,7 +1657,7 @@ function parseAdElement(adTypeElement, emit) { break; case 'Extensions': { - var extNodes = parserUtils.childrenByName(node, 'Extension'); + const extNodes = parserUtils.childrenByName(node, 'Extension'); ad.extensions = parseExtensions(extNodes); /* @@ -1889,7 +1726,7 @@ function parseAdElement(adTypeElement, emit) { }); break; } - } + }); if (adVerificationsFromExtensions.length) { ad.adVerifications = ad.adVerifications.concat(adVerificationsFromExtensions); } @@ -1904,14 +1741,14 @@ function parseAdElement(adTypeElement, emit) { * @return {Ad} */ function parseWrapper(wrapperElement, emit) { - var ad = parseAdElement(wrapperElement, emit); - var followAdditionalWrappersValue = wrapperElement.getAttribute('followAdditionalWrappers'); - var allowMultipleAdsValue = wrapperElement.getAttribute('allowMultipleAds'); - var fallbackOnNoAdValue = wrapperElement.getAttribute('fallbackOnNoAd'); + const ad = parseAdElement(wrapperElement, emit); + const followAdditionalWrappersValue = wrapperElement.getAttribute('followAdditionalWrappers'); + const allowMultipleAdsValue = wrapperElement.getAttribute('allowMultipleAds'); + const fallbackOnNoAdValue = wrapperElement.getAttribute('fallbackOnNoAd'); ad.followAdditionalWrappers = followAdditionalWrappersValue ? parserUtils.parseBoolean(followAdditionalWrappersValue) : true; ad.allowMultipleAds = allowMultipleAdsValue ? parserUtils.parseBoolean(allowMultipleAdsValue) : false; ad.fallbackOnNoAd = fallbackOnNoAdValue ? parserUtils.parseBoolean(fallbackOnNoAdValue) : null; - var wrapperURLElement = parserUtils.childByName(wrapperElement, 'VASTAdTagURI'); + let wrapperURLElement = parserUtils.childByName(wrapperElement, 'VASTAdTagURI'); if (wrapperURLElement) { ad.nextWrapperURL = parserUtils.parseNodeText(wrapperURLElement); } else { @@ -1920,8 +1757,8 @@ function parseWrapper(wrapperElement, emit) { ad.nextWrapperURL = parserUtils.parseNodeText(parserUtils.childByName(wrapperURLElement, 'URL')); } } - ad.creatives.forEach(function (wrapperCreativeElement) { - if (['linear', 'nonlinear'].indexOf(wrapperCreativeElement.type) !== -1) { + ad.creatives.forEach(wrapperCreativeElement => { + if (['linear', 'nonlinear'].includes(wrapperCreativeElement.type)) { // TrackingEvents Linear / NonLinear if (wrapperCreativeElement.trackingEvents) { if (!ad.trackingEvents) { @@ -1930,17 +1767,14 @@ function parseWrapper(wrapperElement, emit) { if (!ad.trackingEvents[wrapperCreativeElement.type]) { ad.trackingEvents[wrapperCreativeElement.type] = {}; } - var _loop = function _loop(eventName) { - var urls = wrapperCreativeElement.trackingEvents[eventName]; + for (const eventName in wrapperCreativeElement.trackingEvents) { + const urls = wrapperCreativeElement.trackingEvents[eventName]; if (!Array.isArray(ad.trackingEvents[wrapperCreativeElement.type][eventName])) { ad.trackingEvents[wrapperCreativeElement.type][eventName] = []; } - urls.forEach(function (url) { + urls.forEach(url => { ad.trackingEvents[wrapperCreativeElement.type][eventName].push(url); }); - }; - for (var eventName in wrapperCreativeElement.trackingEvents) { - _loop(eventName); } } // ClickTracking @@ -1948,7 +1782,7 @@ function parseWrapper(wrapperElement, emit) { if (!Array.isArray(ad.videoClickTrackingURLTemplates)) { ad.videoClickTrackingURLTemplates = []; } // tmp property to save wrapper tracking URLs until they are merged - wrapperCreativeElement.videoClickTrackingURLTemplates.forEach(function (item) { + wrapperCreativeElement.videoClickTrackingURLTemplates.forEach(item => { ad.videoClickTrackingURLTemplates.push(item); }); } @@ -1961,7 +1795,7 @@ function parseWrapper(wrapperElement, emit) { if (!Array.isArray(ad.videoCustomClickURLTemplates)) { ad.videoCustomClickURLTemplates = []; } // tmp property to save wrapper tracking URLs until they are merged - wrapperCreativeElement.videoCustomClickURLTemplates.forEach(function (item) { + wrapperCreativeElement.videoCustomClickURLTemplates.forEach(item => { ad.videoCustomClickURLTemplates.push(item); }); } @@ -1978,29 +1812,33 @@ function parseWrapper(wrapperElement, emit) { * @return {Array} */ function _parseAdVerifications(verifications) { - var ver = []; - verifications.forEach(function (verificationNode) { - var verification = createAdVerification(); - var childNodes = verificationNode.childNodes; + const ver = []; + verifications.forEach(verificationNode => { + const verification = createAdVerification(); + const childNodes = Array.from(verificationNode.childNodes); parserUtils.assignAttributes(verificationNode.attributes, verification); - for (var nodeKey in childNodes) { - var node = childNodes[nodeKey]; - switch (node.nodeName) { + childNodes.forEach(_ref => { + let { + nodeName, + textContent, + attributes + } = _ref; + switch (nodeName) { case 'JavaScriptResource': case 'ExecutableResource': - verification.resource = parserUtils.parseNodeText(node); - parserUtils.assignAttributes(node.attributes, verification); + verification.resource = textContent.trim(); + parserUtils.assignAttributes(attributes, verification); break; case 'VerificationParameters': - verification.parameters = parserUtils.parseNodeText(node); + verification.parameters = textContent.trim(); break; } - } - var trackingEventsElement = parserUtils.childByName(verificationNode, 'TrackingEvents'); + }); + const trackingEventsElement = parserUtils.childByName(verificationNode, 'TrackingEvents'); if (trackingEventsElement) { - parserUtils.childrenByName(trackingEventsElement, 'Tracking').forEach(function (trackingElement) { - var eventName = trackingElement.getAttribute('event'); - var trackingURLTemplate = parserUtils.parseNodeText(trackingElement); + parserUtils.childrenByName(trackingEventsElement, 'Tracking').forEach(trackingElement => { + const eventName = trackingElement.getAttribute('event'); + const trackingURLTemplate = parserUtils.parseNodeText(trackingElement); if (eventName && trackingURLTemplate) { if (!Array.isArray(verification.trackingEvents[eventName])) { verification.trackingEvents[eventName] = []; @@ -2020,11 +1858,11 @@ function _parseAdVerifications(verifications) { * @return {Array} */ function _parseAdVerificationsFromExtensions(extensions) { - var adVerificationsNode = null, + let adVerificationsNode = null, adVerifications = []; // Find the first (and only) AdVerifications node from extensions - extensions.some(function (extension) { + extensions.some(extension => { return adVerificationsNode = parserUtils.childByName(extension, 'AdVerifications'); }); @@ -2041,8 +1879,8 @@ function _parseAdVerificationsFromExtensions(extensions) { * @return {Object} viewableImpression - The viewableImpression object */ function _parseViewableImpression(viewableImpressionNode) { - var regroupNodesUrl = function regroupNodesUrl(urls, node) { - var url = parserUtils.parseNodeText(node); + const regroupNodesUrl = (urls, node) => { + const url = parserUtils.parseNodeText(node); url && urls.push(url); return urls; }; @@ -2054,9 +1892,8 @@ function _parseViewableImpression(viewableImpressionNode) { }; } -var EventEmitter = /*#__PURE__*/function () { - function EventEmitter() { - _classCallCheck(this, EventEmitter); +class EventEmitter { + constructor() { this._handlers = []; } @@ -2069,140 +1906,116 @@ var EventEmitter = /*#__PURE__*/function () { * @param {Function} handler * @returns {EventEmitter} */ - _createClass(EventEmitter, [{ - key: "on", - value: function on(event, handler) { - if (typeof handler !== 'function') { - throw new TypeError("The handler argument must be of type Function. Received type ".concat(_typeof(handler))); - } - if (!event) { - throw new TypeError("The event argument must be of type String. Received type ".concat(_typeof(event))); - } - this._handlers.push({ - event: event, - handler: handler - }); - return this; + on(event, handler) { + if (typeof handler !== 'function') { + throw new TypeError("The handler argument must be of type Function. Received type ".concat(typeof handler)); } - - /** - * Adds a one-time handler function for the named event. - * The next time event is triggered, this handler is removed and then invoked. - * @param {String} event - * @param {Function} handler - * @returns {EventEmitter} - */ - }, { - key: "once", - value: function once(event, handler) { - return this.on(event, onceWrap(this, event, handler)); + if (!event) { + throw new TypeError("The event argument must be of type String. Received type ".concat(typeof event)); } + this._handlers.push({ + event, + handler + }); + return this; + } - /** - * Removes all instances for the specified handler from the handler array for the named event. - * @param {String} event - * @param {Function} handler - * @returns {EventEmitter} - */ - }, { - key: "off", - value: function off(event, handler) { - this._handlers = this._handlers.filter(function (item) { - return item.event !== event || item.handler !== handler; - }); - return this; - } + /** + * Adds a one-time handler function for the named event. + * The next time event is triggered, this handler is removed and then invoked. + * @param {String} event + * @param {Function} handler + * @returns {EventEmitter} + */ + once(event, handler) { + return this.on(event, onceWrap(this, event, handler)); + } - /** - * Synchronously calls each of the handlers registered for the named event, - * in the order they were registered, passing the supplied arguments to each. - * @param {String} event - * @param {...any} args list of arguments that will be used by the event handler - * @returns {Boolean} true if the event had handlers, false otherwise. - */ - }, { - key: "emit", - value: function emit(event) { - for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { - args[_key - 1] = arguments[_key]; - } - var called = false; - this._handlers.forEach(function (item) { - if (item.event === '*') { - called = true; - item.handler.apply(item, [event].concat(args)); - } - if (item.event === event) { - called = true; - item.handler.apply(item, args); - } - }); - return called; - } + /** + * Removes all instances for the specified handler from the handler array for the named event. + * @param {String} event + * @param {Function} handler + * @returns {EventEmitter} + */ + off(event, handler) { + this._handlers = this._handlers.filter(item => { + return item.event !== event || item.handler !== handler; + }); + return this; + } - /** - * Removes all listeners, or those of the specified named event. - * @param {String} event - * @returns {EventEmitter} - */ - }, { - key: "removeAllListeners", - value: function removeAllListeners(event) { - if (!event) { - this._handlers = []; - return this; + /** + * Synchronously calls each of the handlers registered for the named event, + * in the order they were registered, passing the supplied arguments to each. + * @param {String} event + * @param {...any} args list of arguments that will be used by the event handler + * @returns {Boolean} true if the event had handlers, false otherwise. + */ + emit(event) { + for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { + args[_key - 1] = arguments[_key]; + } + let called = false; + this._handlers.forEach(item => { + if (item.event === '*') { + called = true; + item.handler(event, ...args); } - this._handlers = this._handlers.filter(function (item) { - return item.event !== event; - }); + if (item.event === event) { + called = true; + item.handler(...args); + } + }); + return called; + } + + /** + * Removes all listeners, or those of the specified named event. + * @param {String} event + * @returns {EventEmitter} + */ + removeAllListeners(event) { + if (!event) { + this._handlers = []; return this; } + this._handlers = this._handlers.filter(item => item.event !== event); + return this; + } - /** - * Returns the number of listeners listening to the named event. - * @param {String} event - * @returns {Number} - */ - }, { - key: "listenerCount", - value: function listenerCount(event) { - return this._handlers.filter(function (item) { - return item.event === event; - }).length; - } + /** + * Returns the number of listeners listening to the named event. + * @param {String} event + * @returns {Number} + */ + listenerCount(event) { + return this._handlers.filter(item => item.event === event).length; + } - /** - * Returns a copy of the array of listeners for the named event including those created by .once(). - * @param {String} event - * @returns {Function[]} - */ - }, { - key: "listeners", - value: function listeners(event) { - return this._handlers.reduce(function (listeners, item) { - if (item.event === event) { - listeners.push(item.handler); - } - return listeners; - }, []); - } + /** + * Returns a copy of the array of listeners for the named event including those created by .once(). + * @param {String} event + * @returns {Function[]} + */ + listeners(event) { + return this._handlers.reduce((listeners, item) => { + if (item.event === event) { + listeners.push(item.handler); + } + return listeners; + }, []); + } - /** - * Returns an array listing the events for which the emitter has registered handlers. - * @returns {String[]} - */ - }, { - key: "eventNames", - value: function eventNames() { - return this._handlers.map(function (item) { - return item.event; - }); - } - }]); - return EventEmitter; -}(); + /** + * Returns an array listing the events for which the emitter has registered handlers. + * @returns {String[]} + */ + eventNames() { + return this._handlers.map(item => item.event); + } +} function onceWrap(target, event, handler) { - var state = { + const state = { fired: false, wrapFn: undefined }; @@ -2210,161 +2023,19 @@ function onceWrap(target, event, handler) { if (!state.fired) { target.off(event, state.wrapFn); state.fired = true; - handler.bind(target).apply(void 0, arguments); + handler.bind(target)(...arguments); } } state.wrapFn = onceWrapper; return onceWrapper; } -var DEFAULT_TIMEOUT = 120000; - -var uri = require('url'); -var fs = require('fs'); -var http = require('http'); -var https = require('https'); -var DOMParser = require('@xmldom/xmldom').DOMParser; -function get$2(url, options, cb) { - url = uri.parse(url); - var httpModule = url.protocol === 'https:' ? https : http; - if (url.protocol === 'file:') { - fs.readFile(uri.fileURLToPath(url.href), 'utf8', function (err, data) { - if (err) { - return cb(err); - } - var xml = new DOMParser().parseFromString(data); - cb(null, xml, { - byteLength: Buffer.from(data).byteLength - }); - }); - } else { - var timeoutId; - var data = ''; - var timeout = options.timeout || DEFAULT_TIMEOUT; - var req = httpModule.get(url.href, function (res) { - res.on('data', function (chunk) { - data += chunk; - clearTimeout(timeoutId); - timeoutId = startTimeout(); - }); - res.on('end', function () { - clearTimeout(timeoutId); - var xml = new DOMParser().parseFromString(data); - cb(null, xml, { - byteLength: Buffer.from(data).byteLength, - statusCode: res.statusCode - }); - }); - }); - req.on('error', function (err) { - clearTimeout(timeoutId); - if (req.aborted) { - cb(new Error("NodeURLHandler: Request timed out after ".concat(timeout, " ms.")), null, { - statusCode: 408 // Request timeout - }); - } else { - cb(err); - } - }); - var startTimeout = function startTimeout() { - return setTimeout(function () { - return req.abort(); - }, timeout); - }; - timeoutId = startTimeout(); - } -} -var nodeURLHandler = { - get: get$2 -}; - -function xhr() { - try { - var request = new window.XMLHttpRequest(); - if ('withCredentials' in request) { - // check CORS support - return request; - } - return null; - } catch (err) { - return null; - } -} -function supported() { - return !!xhr(); -} -function handleLoad(request, cb) { - if (request.status === 200) { - cb(null, request.responseXML, { - byteLength: request.response.length, - statusCode: request.status - }); - } else { - handleFail(request, cb, false); - } -} -function handleFail(request, cb, isTimeout) { - var statusCode = !isTimeout ? request.status : 408; // Request timeout - var msg = isTimeout ? "XHRURLHandler: Request timed out after ".concat(request.timeout, " ms (").concat(statusCode, ")") : "XHRURLHandler: ".concat(request.statusText, " (").concat(statusCode, ")"); - cb(new Error(msg), null, { - statusCode: statusCode - }); -} -function get$1(url, options, cb) { - if (window.location.protocol === 'https:' && url.indexOf('http://') === 0) { - return cb(new Error('XHRURLHandler: Cannot go from HTTPS to HTTP.')); - } - try { - var request = xhr(); - request.open('GET', url); - request.timeout = options.timeout || DEFAULT_TIMEOUT; - request.withCredentials = options.withCredentials || false; - request.overrideMimeType && request.overrideMimeType('text/xml'); - request.onload = function () { - return handleLoad(request, cb); - }; - request.onerror = function () { - return handleFail(request, cb, false); - }; - request.onabort = function () { - return handleFail(request, cb, false); - }; - request.ontimeout = function () { - return handleFail(request, cb, true); - }; - request.send(); - } catch (error) { - cb(new Error('XHRURLHandler: Unexpected error')); - } -} -var XHRURLHandler = { - get: get$1, - supported: supported -}; - -function get(url, options, cb) { - // Allow skip of the options param - if (!cb) { - if (typeof options === 'function') { - cb = options; - } - options = {}; - } - if (typeof window === 'undefined' || window === null) { - return nodeURLHandler.get(url, options, cb); - } else if (XHRURLHandler.supported()) { - return XHRURLHandler.get(url, options, cb); - } - return cb(new Error('Current context is not supported by any of the default URLHandlers. Please provide a custom URLHandler')); -} -var urlHandler = { - get: get -}; - function createVASTResponse(_ref) { - var ads = _ref.ads, - errorURLTemplates = _ref.errorURLTemplates, - version = _ref.version; + let { + ads, + errorURLTemplates, + version + } = _ref; return { ads: ads || [], errorURLTemplates: errorURLTemplates || [], @@ -2376,26 +2047,26 @@ function createVASTResponse(_ref) { We decided to put the estimated bitrate separated from classes to persist it between different instances of vast client/parser */ -var estimatedBitrateCount = 0; -var estimatedBitrate = 0; +let estimatedBitrateCount = 0; +let estimatedBitrate = 0; /** * Calculate average estimated bitrate from the previous values and new entries * @param {Number} byteLength - The length of the response in bytes. * @param {Number} duration - The duration of the request in ms. */ -var updateEstimatedBitrate = function updateEstimatedBitrate(byteLength, duration) { +const updateEstimatedBitrate = (byteLength, duration) => { if (!byteLength || !duration || byteLength <= 0 || duration <= 0) { return; } // We want the bitrate in kb/s, byteLength are in bytes and duration in ms, just need to convert the byteLength because kb/s = b/ms - var bitrate = byteLength * 8 / duration; + const bitrate = byteLength * 8 / duration; estimatedBitrate = (estimatedBitrate * estimatedBitrateCount + bitrate) / ++estimatedBitrateCount; }; -var DEFAULT_MAX_WRAPPER_DEPTH = 10; -var DEFAULT_EVENT_DATA = { +const DEFAULT_MAX_WRAPPER_DEPTH = 10; +const DEFAULT_EVENT_DATA = { ERRORCODE: 900, extensions: [] }; @@ -2406,604 +2077,448 @@ var DEFAULT_EVENT_DATA = { * @class VASTParser * @extends EventEmitter */ -var VASTParser = /*#__PURE__*/function (_EventEmitter) { - _inherits(VASTParser, _EventEmitter); - var _super = _createSuper(VASTParser); +class VASTParser extends EventEmitter { /** * Creates an instance of VASTParser. * @constructor */ - function VASTParser() { - var _this; - _classCallCheck(this, VASTParser); - _this = _super.call(this); - _this.remainingAds = []; - _this.errorURLTemplates = []; - _this.rootErrorURLTemplates = []; - _this.maxWrapperDepth = null; - _this.URLTemplateFilters = []; - _this.fetchingOptions = {}; - _this.parsingOptions = {}; - return _this; + constructor() { + let { + fetcher + } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + super(); + this.maxWrapperDepth = null; + this.remainingAds = []; + this.fetcher = fetcher || null; } /** - * Adds a filter function to the array of filters which are called before fetching a VAST document. - * @param {function} filter - The filter function to be added at the end of the array. + * Tracks the error provided in the errorCode parameter and emits a VAST-error event for the given error. + * @param {Array} urlTemplates - An Array of url templates to use to make the tracking call. + * @param {Object} errorCode - An Object containing the error data. + * @param {Object} data - One (or more) Object containing additional data. + * @emits VASTParser#VAST-error * @return {void} */ - _createClass(VASTParser, [{ - key: "addURLTemplateFilter", - value: function addURLTemplateFilter(filter) { - if (typeof filter === 'function') { - this.URLTemplateFilters.push(filter); - } + trackVastError(urlTemplates, errorCode) { + for (var _len = arguments.length, data = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) { + data[_key - 2] = arguments[_key]; } + this.emit('VAST-error', Object.assign({}, DEFAULT_EVENT_DATA, errorCode, ...data)); + util.track(urlTemplates, errorCode); + } - /** - * Removes the last element of the url templates filters array. - * @return {void} - */ - }, { - key: "removeURLTemplateFilter", - value: function removeURLTemplateFilter() { - this.URLTemplateFilters.pop(); - } + /** + * Returns an array of errorURLTemplates for the VAST being parsed. + * @return {Array} + */ + getErrorURLTemplates() { + return this.rootErrorURLTemplates.concat(this.errorURLTemplates); + } - /** - * Returns the number of filters of the url templates filters array. - * @return {Number} - */ - }, { - key: "countURLTemplateFilters", - value: function countURLTemplateFilters() { - return this.URLTemplateFilters.length; - } + /** + * Returns the estimated bitrate calculated from all previous requests + * @returns The average of all estimated bitrates in kb/s. + */ + getEstimatedBitrate() { + return estimatedBitrate; + } - /** - * Removes all the filter functions from the url templates filters array. - * @return {void} - */ - }, { - key: "clearURLTemplateFilters", - value: function clearURLTemplateFilters() { - this.URLTemplateFilters = []; - } + /** + * Inits the parsing properties of the class with the custom values provided as options. + * @param {Object} options - The options to initialize a parsing sequence + */ + initParsingStatus() { + let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + this.maxWrapperDepth = options.wrapperLimit || DEFAULT_MAX_WRAPPER_DEPTH; + this.parsingOptions = { + allowMultipleAds: options.allowMultipleAds + }; + this.rootURL = ''; + this.resetParsingStatus(); + updateEstimatedBitrate(options.byteLength, options.requestDuration); + } - /** - * Tracks the error provided in the errorCode parameter and emits a VAST-error event for the given error. - * @param {Array} urlTemplates - An Array of url templates to use to make the tracking call. - * @param {Object} errorCode - An Object containing the error data. - * @param {Object} data - One (or more) Object containing additional data. - * @emits VASTParser#VAST-error - * @return {void} - */ - }, { - key: "trackVastError", - value: function trackVastError(urlTemplates, errorCode) { - for (var _len = arguments.length, data = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) { - data[_key - 2] = arguments[_key]; - } - this.emit('VAST-error', Object.assign.apply(Object, [{}, DEFAULT_EVENT_DATA, errorCode].concat(data))); - util.track(urlTemplates, errorCode); - } + /** + * Reset the parsing property of the class everytime a VAST is parsed + */ + resetParsingStatus() { + this.errorURLTemplates = []; + this.rootErrorURLTemplates = []; + this.vastVersion = null; + } + /** + * Resolves the next group of ads. If all is true resolves all the remaining ads. + * @param {Boolean} all - If true all the remaining ads are resolved + * @return {Promise} + */ + getRemainingAds(all) { + if (this.remainingAds.length === 0) { + return Promise.reject(new Error('No more ads are available for the given VAST')); + } + const ads = all ? util.flatten(this.remainingAds) : this.remainingAds.shift(); + this.errorURLTemplates = []; + return this.resolveAds(ads, { + wrapperDepth: 0, + url: this.rootURL + }).then(resolvedAds => { + return this.buildVASTResponse(resolvedAds); + }); + } - /** - * Returns an array of errorURLTemplates for the VAST being parsed. - * @return {Array} - */ - }, { - key: "getErrorURLTemplates", - value: function getErrorURLTemplates() { - return this.rootErrorURLTemplates.concat(this.errorURLTemplates); - } + /** + * Parses the given xml Object into a VASTResponse. + * Returns a Promise which resolves with a fully parsed VASTResponse or rejects with an Error. + * @param {Object} vastXml - An object representing a vast xml document. + * @param {Object} options - An optional Object of parameters to be used in the parsing process. + * @emits VASTParser#VAST-resolving + * @emits VASTParser#VAST-resolved + * @emits VASTParser#VAST-warning + * @return {Promise} + */ + parseVAST(vastXml) { + let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + this.initParsingStatus(options); + options.isRootVAST = true; + return this.parse(vastXml, options).then(ads => { + return this.buildVASTResponse(ads); + }); + } - /** - * Returns the estimated bitrate calculated from all previous requests - * @returns The average of all estimated bitrates in kb/s. - */ - }, { - key: "getEstimatedBitrate", - value: function getEstimatedBitrate() { - return estimatedBitrate; - } + /** + * Builds a VASTResponse which can be returned. + * @param {Array} ads - An Array of unwrapped ads + * @return {Object} + */ + buildVASTResponse(ads) { + const response = createVASTResponse({ + ads, + errorURLTemplates: this.getErrorURLTemplates(), + version: this.vastVersion + }); + this.completeWrapperResolving(response); + return response; + } - /** - * Fetches a VAST document for the given url. - * Returns a Promise which resolves,rejects according to the result of the request. - * @param {String} url - The url to request the VAST document. - * @param {Number} wrapperDepth - How many times the current url has been wrapped. - * @param {String} previousUrl - Url of the previous VAST. - * @param {Object} wrapperAd - Previously parsed ad node (Wrapper) related to this fetching. - * @emits VASTParser#VAST-resolving - * @emits VASTParser#VAST-resolved - * @return {Promise} - */ - }, { - key: "fetchVAST", - value: function fetchVAST(url) { - var _this2 = this; - var wrapperDepth = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; - var previousUrl = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null; - var wrapperAd = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null; - return new Promise(function (resolve, reject) { - // Process url with defined filter - _this2.URLTemplateFilters.forEach(function (filter) { - url = filter(url); - }); - var timeBeforeGet = Date.now(); - _this2.emit('VAST-resolving', { - url: url, - previousUrl: previousUrl, - wrapperDepth: wrapperDepth, - maxWrapperDepth: _this2.maxWrapperDepth, - timeout: _this2.fetchingOptions.timeout, - wrapperAd: wrapperAd - }); - _this2.urlHandler.get(url, _this2.fetchingOptions, function (error, xml) { - var details = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; - var deltaTime = Math.round(Date.now() - timeBeforeGet); - var info = Object.assign({ - url: url, - previousUrl: previousUrl, - wrapperDepth: wrapperDepth, - error: error, - duration: deltaTime - }, details); - _this2.emit('VAST-resolved', info); - updateEstimatedBitrate(details.byteLength, deltaTime); - if (error) { - reject(error); - } else { - resolve(xml); - } - }); - }); - } - - /** - * Inits the parsing properties of the class with the custom values provided as options. - * @param {Object} options - The options to initialize a parsing sequence - */ - }, { - key: "initParsingStatus", - value: function initParsingStatus() { - var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; - this.errorURLTemplates = []; - this.fetchingOptions = { - timeout: options.timeout || DEFAULT_TIMEOUT, - withCredentials: options.withCredentials - }; - this.maxWrapperDepth = options.wrapperLimit || DEFAULT_MAX_WRAPPER_DEPTH; - this.parsingOptions = { - allowMultipleAds: options.allowMultipleAds - }; - this.remainingAds = []; - this.rootErrorURLTemplates = []; - this.rootURL = ''; - this.urlHandler = options.urlHandler || options.urlhandler || urlHandler; - this.vastVersion = null; - updateEstimatedBitrate(options.byteLength, options.requestDuration); - } - - /** - * Resolves the next group of ads. If all is true resolves all the remaining ads. - * @param {Boolean} all - If true all the remaining ads are resolved - * @return {Promise} - */ - }, { - key: "getRemainingAds", - value: function getRemainingAds(all) { - var _this3 = this; - if (this.remainingAds.length === 0) { - return Promise.reject(new Error('No more ads are available for the given VAST')); - } - var ads = all ? util.flatten(this.remainingAds) : this.remainingAds.shift(); - this.errorURLTemplates = []; - return this.resolveAds(ads, { - wrapperDepth: 0, - url: this.rootURL - }).then(function (resolvedAds) { - return _this3.buildVASTResponse(resolvedAds); - }); - } - - /** - * Fetches and parses a VAST for the given url. - * Returns a Promise which resolves with a fully parsed VASTResponse or rejects with an Error. - * @param {String} url - The url to request the VAST document. - * @param {Object} options - An optional Object of parameters to be used in the parsing process. - * @emits VASTParser#VAST-resolving - * @emits VASTParser#VAST-resolved - * @emits VASTParser#VAST-warning - * @return {Promise} - */ - }, { - key: "getAndParseVAST", - value: function getAndParseVAST(url) { - var _this4 = this; - var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; - this.initParsingStatus(options); - this.URLTemplateFilters.forEach(function (filter) { - url = filter(url); - }); - this.rootURL = url; - return this.fetchVAST(url).then(function (xml) { - options.previousUrl = url; - options.isRootVAST = true; - options.url = url; - return _this4.parse(xml, options).then(function (ads) { - return _this4.buildVASTResponse(ads); - }); - }); - } - - /** - * Parses the given xml Object into a VASTResponse. - * Returns a Promise which resolves with a fully parsed VASTResponse or rejects with an Error. - * @param {Object} vastXml - An object representing a vast xml document. - * @param {Object} options - An optional Object of parameters to be used in the parsing process. - * @emits VASTParser#VAST-resolving - * @emits VASTParser#VAST-resolved - * @emits VASTParser#VAST-warning - * @return {Promise} - */ - }, { - key: "parseVAST", - value: function parseVAST(vastXml) { - var _this5 = this; - var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; - this.initParsingStatus(options); - options.isRootVAST = true; - return this.parse(vastXml, options).then(function (ads) { - return _this5.buildVASTResponse(ads); - }); - } - - /** - * Builds a VASTResponse which can be returned. - * @param {Array} ads - An Array of unwrapped ads - * @return {Object} - */ - }, { - key: "buildVASTResponse", - value: function buildVASTResponse(ads) { - var response = createVASTResponse({ - ads: ads, - errorURLTemplates: this.getErrorURLTemplates(), - version: this.vastVersion + /** + * Parses the given xml Object into an array of ads + * Returns the array or throws an `Error` if an invalid VAST XML is provided + * @param {Object} vastXml - An object representing an xml document. + * @param {Object} options - An optional Object of parameters to be used in the parsing process. + * @emits VASTParser#VAST-warning + * @emits VASTParser#VAST-ad-parsed + * @return {Array} + * @throws {Error} `vastXml` must be a valid VAST XMLDocument + */ + parseVastXml(vastXml, _ref) { + let { + isRootVAST = false, + url = null, + wrapperDepth = 0, + allowMultipleAds, + followAdditionalWrappers + } = _ref; + // check if is a valid VAST document + if (!vastXml || !vastXml.documentElement || vastXml.documentElement.nodeName !== 'VAST') { + this.emit('VAST-ad-parsed', { + type: 'ERROR', + url, + wrapperDepth }); - this.completeWrapperResolving(response); - return response; - } - - /** - * Parses the given xml Object into an array of ads - * Returns the array or throws an `Error` if an invalid VAST XML is provided - * @param {Object} vastXml - An object representing an xml document. - * @param {Object} options - An optional Object of parameters to be used in the parsing process. - * @emits VASTParser#VAST-warning - * @emits VASTParser#VAST-ad-parsed - * @return {Array} - * @throws {Error} `vastXml` must be a valid VAST XMLDocument - */ - }, { - key: "parseVastXml", - value: function parseVastXml(vastXml, _ref) { - var _ref$isRootVAST = _ref.isRootVAST, - isRootVAST = _ref$isRootVAST === void 0 ? false : _ref$isRootVAST, - _ref$url = _ref.url, - url = _ref$url === void 0 ? null : _ref$url, - _ref$wrapperDepth = _ref.wrapperDepth, - wrapperDepth = _ref$wrapperDepth === void 0 ? 0 : _ref$wrapperDepth, - allowMultipleAds = _ref.allowMultipleAds, - followAdditionalWrappers = _ref.followAdditionalWrappers; - // check if is a valid VAST document - if (!vastXml || !vastXml.documentElement || vastXml.documentElement.nodeName !== 'VAST') { - this.emit('VAST-ad-parsed', { - type: 'ERROR', - url: url, - wrapperDepth: wrapperDepth + throw new Error('Invalid VAST XMLDocument'); + } + const ads = []; + const childNodes = vastXml.documentElement.childNodes; + + /* Only parse the version of the Root VAST for now because we don't know yet how to + * handle some cases like multiple wrappers in the same vast + */ + const vastVersion = vastXml.documentElement.getAttribute('version'); + if (isRootVAST) { + if (vastVersion) this.vastVersion = vastVersion; + } + // Fill the VASTResponse object with ads and errorURLTemplates + for (const nodeKey in childNodes) { + const node = childNodes[nodeKey]; + if (node.nodeName === 'Error') { + const errorURLTemplate = parserUtils.parseNodeText(node); + + // Distinguish root VAST url templates from ad specific ones + isRootVAST ? this.rootErrorURLTemplates.push(errorURLTemplate) : this.errorURLTemplates.push(errorURLTemplate); + } else if (node.nodeName === 'Ad') { + // allowMultipleAds was introduced in VAST 3 + // for retrocompatibility set it to true + if (this.vastVersion && parseFloat(this.vastVersion) < 3) { + allowMultipleAds = true; + } else if (allowMultipleAds === false && ads.length > 1) { + // if wrapper allowMultipleAds is set to false only the first stand-alone Ad + // (with no sequence values) in the requested VAST response is allowed + break; + } + const result = parseAd(node, this.emit.bind(this), { + allowMultipleAds, + followAdditionalWrappers }); - throw new Error('Invalid VAST XMLDocument'); - } - var ads = []; - var childNodes = vastXml.documentElement.childNodes; - - /* Only parse the version of the Root VAST for now because we don't know yet how to - * handle some cases like multiple wrappers in the same vast - */ - var vastVersion = vastXml.documentElement.getAttribute('version'); - if (isRootVAST) { - if (vastVersion) this.vastVersion = vastVersion; - } - - // Fill the VASTResponse object with ads and errorURLTemplates - for (var nodeKey in childNodes) { - var node = childNodes[nodeKey]; - if (node.nodeName === 'Error') { - var errorURLTemplate = parserUtils.parseNodeText(node); - - // Distinguish root VAST url templates from ad specific ones - isRootVAST ? this.rootErrorURLTemplates.push(errorURLTemplate) : this.errorURLTemplates.push(errorURLTemplate); - } else if (node.nodeName === 'Ad') { - // allowMultipleAds was introduced in VAST 3 - // for retrocompatibility set it to true - if (this.vastVersion && parseFloat(this.vastVersion) < 3) { - allowMultipleAds = true; - } else if (allowMultipleAds === false && ads.length > 1) { - // if wrapper allowMultipleAds is set to false only the first stand-alone Ad - // (with no sequence values) in the requested VAST response is allowed - break; - } - var result = parseAd(node, this.emit.bind(this), { - allowMultipleAds: allowMultipleAds, - followAdditionalWrappers: followAdditionalWrappers + if (result.ad) { + ads.push(result.ad); + this.emit('VAST-ad-parsed', { + type: result.type, + url, + wrapperDepth, + adIndex: ads.length - 1, + vastVersion + }); + } else { + // VAST version of response not supported. + this.trackVastError(this.getErrorURLTemplates(), { + ERRORCODE: 101 }); - if (result.ad) { - ads.push(result.ad); - this.emit('VAST-ad-parsed', { - type: result.type, - url: url, - wrapperDepth: wrapperDepth, - adIndex: ads.length - 1, - vastVersion: vastVersion - }); - } else { - // VAST version of response not supported. - this.trackVastError(this.getErrorURLTemplates(), { - ERRORCODE: 101 - }); - } } } - return ads; - } - - /** - * Parses the given xml Object into an array of unwrapped ads. - * Returns a Promise which resolves with the array or rejects with an error according to the result of the parsing. - * @param {Object} vastXml - An object representing an xml document. - * @param {Object} options - An optional Object of parameters to be used in the parsing process. - * @emits VASTParser#VAST-resolving - * @emits VASTParser#VAST-resolved - * @emits VASTParser#VAST-warning - * @return {Promise} - */ - }, { - key: "parse", - value: function parse(vastXml) { - var _ref2 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, - _ref2$url = _ref2.url, - url = _ref2$url === void 0 ? null : _ref2$url, - _ref2$resolveAll = _ref2.resolveAll, - resolveAll = _ref2$resolveAll === void 0 ? true : _ref2$resolveAll, - _ref2$wrapperSequence = _ref2.wrapperSequence, - wrapperSequence = _ref2$wrapperSequence === void 0 ? null : _ref2$wrapperSequence, - _ref2$previousUrl = _ref2.previousUrl, - previousUrl = _ref2$previousUrl === void 0 ? null : _ref2$previousUrl, - _ref2$wrapperDepth = _ref2.wrapperDepth, - wrapperDepth = _ref2$wrapperDepth === void 0 ? 0 : _ref2$wrapperDepth, - _ref2$isRootVAST = _ref2.isRootVAST, - isRootVAST = _ref2$isRootVAST === void 0 ? false : _ref2$isRootVAST, - followAdditionalWrappers = _ref2.followAdditionalWrappers, - allowMultipleAds = _ref2.allowMultipleAds; - var ads = []; - // allowMultipleAds was introduced in VAST 3 as wrapper attribute - // for retrocompatibility set it to true for vast pre-version 3 - if (this.vastVersion && parseFloat(this.vastVersion) < 3 && isRootVAST) { - allowMultipleAds = true; - } - try { - ads = this.parseVastXml(vastXml, { - isRootVAST: isRootVAST, - url: url, - wrapperDepth: wrapperDepth, - allowMultipleAds: allowMultipleAds, - followAdditionalWrappers: followAdditionalWrappers - }); - } catch (e) { - return Promise.reject(e); - } - - /* Keep wrapper sequence value to not break AdPod when wrapper contain only one Ad. - e.g,for a AdPod containing : - - Inline with sequence=1 - - Inline with sequence=2 - - Wrapper with sequence=3 wrapping a Inline with sequence=1 - once parsed we will obtain : - - Inline sequence 1, - - Inline sequence 2, - - Inline sequence 3 - */ - if (ads.length === 1 && wrapperSequence !== undefined && wrapperSequence !== null) { - ads[0].sequence = wrapperSequence; - } - - // Split the VAST in case we don't want to resolve everything at the first time - if (resolveAll === false) { - this.remainingAds = parserUtils.splitVAST(ads); - // Remove the first element from the remaining ads array, since we're going to resolve that element - ads = this.remainingAds.shift(); - } - return this.resolveAds(ads, { - wrapperDepth: wrapperDepth, - previousUrl: previousUrl, - url: url - }); } + return ads; + } - /** - * Resolves an Array of ads, recursively calling itself with the remaining ads if a no ad - * response is returned for the given array. - * @param {Array} ads - An array of ads to resolve - * @param {Object} options - An options Object containing resolving parameters - * @return {Promise} - */ - }, { - key: "resolveAds", - value: function resolveAds() { - var _this6 = this; - var ads = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; - var _ref3 = arguments.length > 1 ? arguments[1] : undefined, - wrapperDepth = _ref3.wrapperDepth, - previousUrl = _ref3.previousUrl, - url = _ref3.url; - var resolveWrappersPromises = []; - previousUrl = url; - ads.forEach(function (ad) { - var resolveWrappersPromise = _this6.resolveWrappers(ad, wrapperDepth, previousUrl); - resolveWrappersPromises.push(resolveWrappersPromise); - }); - return Promise.all(resolveWrappersPromises).then(function (unwrappedAds) { - var resolvedAds = util.flatten(unwrappedAds); - if (!resolvedAds && _this6.remainingAds.length > 0) { - var remainingAdsToResolve = _this6.remainingAds.shift(); - return _this6.resolveAds(remainingAdsToResolve, { - wrapperDepth: wrapperDepth, - previousUrl: previousUrl, - url: url - }); - } - return resolvedAds; + /** + * Parses the given xml Object into an array of unwrapped ads. + * Returns a Promise which resolves with the array or rejects with an error according to the result of the parsing. + * @param {Object} vastXml - An object representing an xml document. + * @param {Object} options - An optional Object of parameters to be used in the parsing process. + * @emits VASTParser#VAST-resolving + * @emits VASTParser#VAST-resolved + * @emits VASTParser#VAST-warning + * @return {Promise} + */ + parse(vastXml) { + let { + url = null, + resolveAll = true, + wrapperSequence = null, + previousUrl = null, + wrapperDepth = 0, + isRootVAST = false, + followAdditionalWrappers, + allowMultipleAds + } = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + let ads = []; + // allowMultipleAds was introduced in VAST 3 as wrapper attribute + // for retrocompatibility set it to true for vast pre-version 3 + if (this.vastVersion && parseFloat(this.vastVersion) < 3 && isRootVAST) { + allowMultipleAds = true; + } + try { + ads = this.parseVastXml(vastXml, { + isRootVAST, + url, + wrapperDepth, + allowMultipleAds, + followAdditionalWrappers }); - } - - /** - * Resolves the wrappers for the given ad in a recursive way. - * Returns a Promise which resolves with the unwrapped ad or rejects with an error. - * @param {Object} ad - An ad object to be unwrapped. - * @param {Number} wrapperDepth - The reached depth in the wrapper resolving chain. - * @param {String} previousUrl - The previous vast url. - * @return {Promise} - */ - }, { - key: "resolveWrappers", - value: function resolveWrappers(ad, wrapperDepth, previousUrl) { - var _this7 = this; - return new Promise(function (resolve) { - var _this7$parsingOptions; - // Going one level deeper in the wrapper chain - wrapperDepth++; - // We already have a resolved VAST ad, no need to resolve wrapper - if (!ad.nextWrapperURL) { - delete ad.nextWrapperURL; - return resolve(ad); - } - if (wrapperDepth >= _this7.maxWrapperDepth) { - // Wrapper limit reached, as defined by the video player. - // Too many Wrapper responses have been received with no InLine response. - ad.errorCode = 302; - delete ad.nextWrapperURL; - return resolve(ad); - } + } catch (e) { + return Promise.reject(e); + } + + /* Keep wrapper sequence value to not break AdPod when wrapper contain only one Ad. + e.g,for a AdPod containing : + - Inline with sequence=1 + - Inline with sequence=2 + - Wrapper with sequence=3 wrapping a Inline with sequence=1 + once parsed we will obtain : + - Inline sequence 1, + - Inline sequence 2, + - Inline sequence 3 + */ + if (ads.length === 1 && wrapperSequence !== undefined && wrapperSequence !== null) { + ads[0].sequence = wrapperSequence; + } + + // Split the VAST in case we don't want to resolve everything at the first time + if (resolveAll === false) { + this.remainingAds = parserUtils.splitVAST(ads); + // Remove the first element from the remaining ads array, since we're going to resolve that element + ads = this.remainingAds.shift(); + } + return this.resolveAds(ads, { + wrapperDepth, + previousUrl, + url + }); + } - // Get full URL - ad.nextWrapperURL = parserUtils.resolveVastAdTagURI(ad.nextWrapperURL, previousUrl); - _this7.URLTemplateFilters.forEach(function (filter) { - ad.nextWrapperURL = filter(ad.nextWrapperURL); + /** + * Resolves an Array of ads, recursively calling itself with the remaining ads if a no ad + * response is returned for the given array. + * @param {Array} ads - An array of ads to resolve + * @param {Object} options - An options Object containing resolving parameters + * @return {Promise} + */ + resolveAds() { + let ads = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; + let { + wrapperDepth, + previousUrl, + url + } = arguments.length > 1 ? arguments[1] : undefined; + const resolveWrappersPromises = []; + previousUrl = url; + ads.forEach(ad => { + const resolveWrappersPromise = this.resolveWrappers(ad, wrapperDepth, previousUrl); + resolveWrappersPromises.push(resolveWrappersPromise); + }); + return Promise.all(resolveWrappersPromises).then(unwrappedAds => { + const resolvedAds = util.flatten(unwrappedAds); + if (!resolvedAds.length && this.remainingAds.length > 0) { + const remainingAdsToResolve = this.remainingAds.shift(); + return this.resolveAds(remainingAdsToResolve, { + wrapperDepth, + previousUrl, + url }); + } + return resolvedAds; + }); + } - // If allowMultipleAds is set inside the parameter 'option' of public method - // override the vast value by the one provided - var allowMultipleAds = (_this7$parsingOptions = _this7.parsingOptions.allowMultipleAds) !== null && _this7$parsingOptions !== void 0 ? _this7$parsingOptions : ad.allowMultipleAds; - // sequence doesn't carry over in wrapper element - var wrapperSequence = ad.sequence; - _this7.fetchVAST(ad.nextWrapperURL, wrapperDepth, previousUrl, ad).then(function (xml) { - return _this7.parse(xml, { - url: ad.nextWrapperURL, - previousUrl: previousUrl, - wrapperSequence: wrapperSequence, - wrapperDepth: wrapperDepth, - followAdditionalWrappers: ad.followAdditionalWrappers, - allowMultipleAds: allowMultipleAds - }).then(function (unwrappedAds) { - delete ad.nextWrapperURL; - if (unwrappedAds.length === 0) { - // No ads returned by the wrappedResponse, discard current creatives - ad.creatives = []; - return resolve(ad); + /** + * Resolves the wrappers for the given ad in a recursive way. + * Returns a Promise which resolves with the unwrapped ad or rejects with an error. + * @param {Object} adToUnWrap - An ad object to be unwrapped. + * @param {Number} wrapperDepth - The reached depth in the wrapper resolving chain. + * @param {String} previousUrl - The previous vast url. + * @return {Promise} + */ + resolveWrappers(adToUnWrap, wrapperDepth, previousUrl) { + // Copy ad from parameters to prevent altering given object outside of function scope + const ad = { + ...adToUnWrap + }; + return new Promise(resolve => { + var _this$parsingOptions$; + // Going one level deeper in the wrapper chain + wrapperDepth++; + // We already have a resolved VAST ad, no need to resolve wrapper + if (!ad.nextWrapperURL) { + delete ad.nextWrapperURL; + return resolve(ad); + } + if (!this.fetcher) { + ad.VASTAdTagURI = ad.nextWrapperURL; + delete ad.nextWrapperURL; + return resolve(ad); + } + if (wrapperDepth >= this.maxWrapperDepth) { + // Wrapper limit reached, as defined by the video player. + // Too many Wrapper responses have been received with no InLine response. + ad.errorCode = 302; + delete ad.nextWrapperURL; + return resolve(ad); + } + + // Get full URL + ad.nextWrapperURL = parserUtils.resolveVastAdTagURI(ad.nextWrapperURL, previousUrl); + + // If allowMultipleAds is set inside the parameter 'option' of public method + // override the vast value by the one provided + const allowMultipleAds = (_this$parsingOptions$ = this.parsingOptions.allowMultipleAds) !== null && _this$parsingOptions$ !== void 0 ? _this$parsingOptions$ : ad.allowMultipleAds; + // sequence doesn't carry over in wrapper element + const wrapperSequence = ad.sequence; + this.fetcher.fetchVAST({ + url: ad.nextWrapperURL, + emitter: this.emit.bind(this), + maxWrapperDepth: this.maxWrapperDepth + }).then(xml => { + return this.parse(xml, { + url: ad.nextWrapperURL, + previousUrl, + wrapperSequence, + wrapperDepth, + followAdditionalWrappers: ad.followAdditionalWrappers, + allowMultipleAds + }).then(unwrappedAds => { + delete ad.nextWrapperURL; + if (unwrappedAds.length === 0) { + // No ads returned by the wrappedResponse, discard current creatives + ad.creatives = []; + return resolve(ad); + } + unwrappedAds.forEach(unwrappedAd => { + if (unwrappedAd) { + parserUtils.mergeWrapperAdData(unwrappedAd, ad); } - unwrappedAds.forEach(function (unwrappedAd) { - if (unwrappedAd) { - parserUtils.mergeWrapperAdData(unwrappedAd, ad); - } - }); - resolve(unwrappedAds); }); - })["catch"](function (err) { - // Timeout of VAST URI provided in Wrapper element, or of VAST URI provided in a subsequent Wrapper element. - // (URI was either unavailable or reached a timeout as defined by the video player.) - ad.errorCode = 301; - ad.errorMessage = err.message; - resolve(ad); + resolve(unwrappedAds); }); + }).catch(err => { + // Timeout of VAST URI provided in Wrapper element, or of VAST URI provided in a subsequent Wrapper element. + // (URI was either unavailable or reached a timeout as defined by the video player.) + ad.errorCode = 301; + ad.errorMessage = err.message; + resolve(ad); }); - } + }); + } - /** - * Takes care of handling errors when the wrappers are resolved. - * @param {Object} vastResponse - A resolved VASTResponse. - */ - }, { - key: "completeWrapperResolving", - value: function completeWrapperResolving(vastResponse) { - // We've to wait for all elements to be parsed before handling error so we can: - // - Send computed extensions data - // - Ping all URIs defined across VAST files - - // No Ad case - The parser never bump into an element - if (vastResponse.ads.length === 0) { - this.trackVastError(vastResponse.errorURLTemplates, { - ERRORCODE: 303 - }); - } else { - for (var index = vastResponse.ads.length - 1; index >= 0; index--) { - // - Error encountered while parsing - // - No Creative case - The parser has dealt with soma or/and an elements - // but no creative was found - var ad = vastResponse.ads[index]; - if (ad.errorCode || ad.creatives.length === 0) { - this.trackVastError(ad.errorURLTemplates.concat(vastResponse.errorURLTemplates), { - ERRORCODE: ad.errorCode || 303 - }, { - ERRORMESSAGE: ad.errorMessage || '' - }, { - extensions: ad.extensions - }, { - system: ad.system - }); - vastResponse.ads.splice(index, 1); - } + /** + * Takes care of handling errors when the wrappers are resolved. + * @param {Object} vastResponse - A resolved VASTResponse. + */ + completeWrapperResolving(vastResponse) { + // We've to wait for all elements to be parsed before handling error so we can: + // - Send computed extensions data + // - Ping all URIs defined across VAST files + + // No Ad case - The parser never bump into an element + if (vastResponse.ads.length === 0) { + this.trackVastError(vastResponse.errorURLTemplates, { + ERRORCODE: 303 + }); + } else { + for (let index = vastResponse.ads.length - 1; index >= 0; index--) { + // - Error encountered while parsing + // - No Creative case - The parser has dealt with soma or/and an elements + // but no creative was found + const ad = vastResponse.ads[index]; + if ((ad.errorCode || ad.creatives.length === 0) && !ad.VASTAdTagURI) { + // If VASTAdTagURI is in the vastResponse, it means we are dealing with a Wrapper when using parseVAST from the VASTParser. + // In that case, we dont want to modify the vastResponse since the creatives node is not required in a wrapper. + this.trackVastError(ad.errorURLTemplates.concat(vastResponse.errorURLTemplates), { + ERRORCODE: ad.errorCode || 303 + }, { + ERRORMESSAGE: ad.errorMessage || '' + }, { + extensions: ad.extensions + }, { + system: ad.system + }); + vastResponse.ads.splice(index, 1); } } } - }]); - return VASTParser; -}(EventEmitter); + } +} -var storage = null; +let storage = null; /** * This Object represents a default storage to be used in case no other storage is available. * @constant * @type {Object} */ -var DEFAULT_STORAGE = { +const DEFAULT_STORAGE = { data: {}, length: 0, - getItem: function getItem(key) { + getItem(key) { return this.data[key]; }, - setItem: function setItem(key, value) { + setItem(key, value) { this.data[key] = value; this.length = Object.keys(this.data).length; }, - removeItem: function removeItem(key) { + removeItem(key) { delete this.data[key]; this.length = Object.keys(this.data).length; }, - clear: function clear() { + clear() { this.data = {}; this.length = 0; } @@ -3015,13 +2530,12 @@ var DEFAULT_STORAGE = { * @export * @class Storage */ -var Storage = /*#__PURE__*/function () { +class Storage { /** * Creates an instance of Storage. * @constructor */ - function Storage() { - _classCallCheck(this, Storage); + constructor() { this.storage = this.initStorage(); } @@ -3029,93 +2543,268 @@ var Storage = /*#__PURE__*/function () { * Provides a singleton instance of the wrapped storage. * @return {Object} */ - _createClass(Storage, [{ - key: "initStorage", - value: function initStorage() { - if (storage) { - return storage; - } - try { - storage = typeof window !== 'undefined' && window !== null ? window.localStorage || window.sessionStorage : null; - } catch (storageError) { - storage = null; - } - if (!storage || this.isStorageDisabled(storage)) { - storage = DEFAULT_STORAGE; - storage.clear(); - } + initStorage() { + if (storage) { return storage; } + try { + storage = typeof window !== 'undefined' && window !== null ? window.localStorage || window.sessionStorage : null; + } catch (storageError) { + storage = null; + } + if (!storage || this.isStorageDisabled(storage)) { + storage = DEFAULT_STORAGE; + storage.clear(); + } + return storage; + } - /** - * Check if storage is disabled (like in certain cases with private browsing). - * In Safari (Mac + iOS) when private browsing is ON, localStorage is read only - * http://spin.atomicobject.com/2013/01/23/ios-private-browsing-localstorage/ - * @param {Object} testStorage - The storage to check. - * @return {Boolean} - */ - }, { - key: "isStorageDisabled", - value: function isStorageDisabled(testStorage) { - var testValue = '__VASTStorage__'; - try { - testStorage.setItem(testValue, testValue); - if (testStorage.getItem(testValue) !== testValue) { - testStorage.removeItem(testValue); - return true; - } - } catch (e) { + /** + * Check if storage is disabled (like in certain cases with private browsing). + * In Safari (Mac + iOS) when private browsing is ON, localStorage is read only + * http://spin.atomicobject.com/2013/01/23/ios-private-browsing-localstorage/ + * @param {Object} testStorage - The storage to check. + * @return {Boolean} + */ + isStorageDisabled(testStorage) { + const testValue = '__VASTStorage__'; + try { + testStorage.setItem(testValue, testValue); + if (testStorage.getItem(testValue) !== testValue) { + testStorage.removeItem(testValue); return true; } - testStorage.removeItem(testValue); - return false; + } catch (e) { + return true; } + testStorage.removeItem(testValue); + return false; + } - /** - * Returns the value for the given key. If the key does not exist, null is returned. - * @param {String} key - The key to retrieve the value. - * @return {any} - */ - }, { - key: "getItem", - value: function getItem(key) { - return this.storage.getItem(key); + /** + * Returns the value for the given key. If the key does not exist, null is returned. + * @param {String} key - The key to retrieve the value. + * @return {any} + */ + getItem(key) { + return this.storage.getItem(key); + } + + /** + * Adds or updates the value for the given key. + * @param {String} key - The key to modify the value. + * @param {any} value - The value to be associated with the key. + * @return {any} + */ + setItem(key, value) { + return this.storage.setItem(key, value); + } + + /** + * Removes an item for the given key. + * @param {String} key - The key to remove the value. + * @return {any} + */ + removeItem(key) { + return this.storage.removeItem(key); + } + + /** + * Removes all the items from the storage. + */ + clear() { + return this.storage.clear(); + } +} + +const DEFAULT_TIMEOUT = 120000; + +/** + * Return an object containing an XML document. + * in addition to the byteLength and the statusCode of the response. + * @param {Object} response the response of the fetch request. + * @returns {Object} + */ +async function handleResponse(response) { + const textXml = await response.text(); + let parser; + if (!util.isBrowserEnvironment()) { + const xmlDom = await Promise.resolve().then(function () { return require('./chunks/xmldom-ac2d7d04.js'); }); + parser = new xmlDom.DOMParser(); + } else { + parser = new DOMParser(); + } + const xml = parser.parseFromString(textXml, 'text/xml'); + return { + xml, + details: { + byteLength: textXml.length, + statusCode: response.status, + rawXml: textXml } + }; +} - /** - * Adds or updates the value for the given key. - * @param {String} key - The key to modify the value. - * @param {any} value - The value to be associated with the key. - * @return {any} - */ - }, { - key: "setItem", - value: function setItem(key, value) { - return this.storage.setItem(key, value); +/** + * Return a custom message if an error occured + * @param {Object} response The response of fetch request + * @returns {String | null} + */ +function handleError(response) { + if (window.location.protocol === 'https:' && response.url.includes('http://')) { + return 'URLHandler: Cannot go from HTTPS to HTTP.'; + } + if (response.status !== 200 || !response.ok) { + return "URLHandler: ".concat(response.statusText, " (").concat(response.status, ")"); + } + return null; +} +async function get(url, options) { + try { + // fetch does not have "timeout" option, we are using AbortController + // to abort the request if it reach the timeout. + const controller = new AbortController(); + const timer = setTimeout(() => { + controller.abort(); + throw new Error("URLHandler: Request timed out after ".concat(options.timeout || DEFAULT_TIMEOUT, " ms (408)")); + }, options.timeout || DEFAULT_TIMEOUT); + const response = await fetch(url, { + ...options, + signal: controller.signal, + credentials: options.withCredentials ? 'include' : 'omit' + }); + clearTimeout(timer); + const error = handleError(response); + if (error) { + return { + error: new Error(error), + statusCode: response.status + }; } + return handleResponse(response); + } catch (error) { + return { + error, + statusCode: error.name === 'AbortError' ? 408 : null + }; + } +} +const urlHandler = { + get +}; - /** - * Removes an item for the given key. - * @param {String} key - The key to remove the value. - * @return {any} - */ - }, { - key: "removeItem", - value: function removeItem(key) { - return this.storage.removeItem(key); +/** + * This class provides a method to fetch a VAST document + * @exports + * @class Fetcher + */ + +class Fetcher { + constructor() { + this.URLTemplateFilters = []; + } + + /** + * Inits the fetching properties of the class with the custom values provided as options + * @param {Object} options - The options to initialize before fetching + */ + setOptions() { + let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + this.urlHandler = options.urlHandler || options.urlhandler || urlHandler; + this.fetchingOptions = { + timeout: options.timeout || DEFAULT_TIMEOUT, + withCredentials: Boolean(options.withCredentials) + }; + } + + /** + * Adds a filter function to the array of filters which are called before fetching a VAST document. + * @param {function} filter - The filter function to be added at the end of the array. + */ + addURLTemplateFilter(filter) { + if (typeof filter === 'function') { + this.URLTemplateFilters.push(filter); } + } - /** - * Removes all the items from the storage. - */ - }, { - key: "clear", - value: function clear() { - return this.storage.clear(); + /** + * Removes the latest URL template filter added. + */ + removeLastURLTemplateFilter() { + this.URLTemplateFilters.pop(); + } + + /** + * Returns the number of URL template filters added. + * @return {Number} + */ + countURLTemplateFilters() { + return this.URLTemplateFilters.length; + } + + /** + * Removes all the URL template filters added. + */ + clearURLTemplateFilters() { + this.URLTemplateFilters = []; + } + + /** + * Fetches a VAST document for the given url. + * @param {Object} params + * @param {String} params.url - The url to request the VAST document. + * @param {Number} params.wrapperDepth - How many times the current url has been wrapped. + * @param {(String | null)} params.previousUrl - Url of the previous VAST. + * @param {Object} params.wrapperAd - Previously parsed ad node (Wrapper) related to this fetching. + * @param {Number} params.maxWrapperDepth - The maximum number of Wrapper that can be fetch + * @param {Function} params.emitter - The function used to Emit event + * @emits VASTParser#VAST-resolving + * @emits VASTParser#VAST-resolved + * @return {Promise} + */ + async fetchVAST(_ref) { + var _data$details; + let { + url, + maxWrapperDepth, + emitter, + wrapperDepth = 0, + previousUrl = null, + wrapperAd = null + } = _ref; + const timeBeforeGet = Date.now(); + + // Process url with defined filter + this.URLTemplateFilters.forEach(filter => { + url = filter(url); + }); + emitter('VAST-resolving', { + url, + previousUrl, + wrapperDepth, + maxWrapperDepth, + timeout: this.fetchingOptions.timeout, + wrapperAd + }); + const data = await this.urlHandler.get(url, this.fetchingOptions); + const requestDuration = Math.round(Date.now() - timeBeforeGet); + emitter('VAST-resolved', { + url, + previousUrl, + wrapperDepth, + error: (data === null || data === void 0 ? void 0 : data.error) || null, + duration: requestDuration, + statusCode: (data === null || data === void 0 ? void 0 : data.statusCode) || null, + ...(data === null || data === void 0 ? void 0 : data.details) + }); + updateEstimatedBitrate(data === null || data === void 0 || (_data$details = data.details) === null || _data$details === void 0 ? void 0 : _data$details.byteLength, requestDuration); + if (data.error) { + throw new Error(data.error); + } else { + return data.xml; } - }]); - return Storage; -}(); + } +} /** * This class provides methods to fetch and parse a VAST document using VASTParser. @@ -3123,7 +2812,7 @@ var Storage = /*#__PURE__*/function () { * @export * @class VASTClient */ -var VASTClient = /*#__PURE__*/function () { +class VASTClient { /** * Creates an instance of VASTClient. * @param {Number} cappingFreeLunch - The number of first calls to skip. @@ -3131,17 +2820,17 @@ var VASTClient = /*#__PURE__*/function () { * @param {Storage} customStorage - A custom storage to use instead of the default one. * @constructor */ - function VASTClient(cappingFreeLunch, cappingMinimumTimeInterval, customStorage) { - _classCallCheck(this, VASTClient); - this.cappingFreeLunch = cappingFreeLunch || 0; - this.cappingMinimumTimeInterval = cappingMinimumTimeInterval || 0; - this.defaultOptions = { - withCredentials: false, - timeout: 0 - }; - this.vastParser = new VASTParser(); - this.storage = customStorage || new Storage(); - + constructor() { + let cappingFreeLunch = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; + let cappingMinimumTimeInterval = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; + let customStorage = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : new Storage(); + this.cappingFreeLunch = cappingFreeLunch; + this.cappingMinimumTimeInterval = cappingMinimumTimeInterval; + this.fetcher = new Fetcher(); + this.vastParser = new VASTParser({ + fetcher: this.fetcher + }); + this.storage = customStorage; // Init values if not already set if (this.lastSuccessfulAd === undefined) { this.lastSuccessfulAd = 0; @@ -3153,116 +2842,154 @@ var VASTClient = /*#__PURE__*/function () { this.totalCallsTimeout = 0; } } - _createClass(VASTClient, [{ - key: "getParser", - value: function getParser() { - return this.vastParser; - } - }, { - key: "lastSuccessfulAd", - get: function get() { - return this.storage.getItem('vast-client-last-successful-ad'); - }, - set: function set(value) { - this.storage.setItem('vast-client-last-successful-ad', value); - } - }, { - key: "totalCalls", - get: function get() { - return this.storage.getItem('vast-client-total-calls'); - }, - set: function set(value) { - this.storage.setItem('vast-client-total-calls', value); - } - }, { - key: "totalCallsTimeout", - get: function get() { - return this.storage.getItem('vast-client-total-calls-timeout'); - }, - set: function set(value) { - this.storage.setItem('vast-client-total-calls-timeout', value); - } - /** - * Returns a boolean indicating if there are more ads to resolve for the current parsing. - * @return {Boolean} - */ - }, { - key: "hasRemainingAds", - value: function hasRemainingAds() { - return this.vastParser.remainingAds.length > 0; - } + /** + * Adds a filter function to the array of filters which are called before fetching a VAST document. + * @param {function} filter - The filter function to be added at the end of the array. + * @return {void} + */ + addURLTemplateFilter(filter) { + this.fetcher.addURLTemplateFilter(filter); + } - /** - * Resolves the next group of ads. If all is true resolves all the remaining ads. - * @param {Boolean} all - If true all the remaining ads are resolved - * @return {Promise} - */ - }, { - key: "getNextAds", - value: function getNextAds(all) { - return this.vastParser.getRemainingAds(all); - } + /** + * Removes the last element of the url templates filters array. + * @return {void} + */ + removeLastURLTemplateFilter() { + this.fetcher.removeLastURLTemplateFilter(); + } - /** - * Gets a parsed VAST document for the given url, applying the skipping rules defined. - * Returns a Promise which resolves with a fully parsed VASTResponse or rejects with an Error. - * @param {String} url - The url to use to fecth the VAST document. - * @param {Object} options - An optional Object of parameters to be applied in the process. - * @return {Promise} - */ - }, { - key: "get", - value: function get(url) { - var _this = this; - var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; - var now = Date.now(); - options = Object.assign({}, this.defaultOptions, options); - - // By default the client resolves only the first Ad or AdPod - if (!options.hasOwnProperty('resolveAll')) { - options.resolveAll = false; - } + /** + * Returns the number of filters of the url templates filters array. + * @return {Number} + */ + countURLTemplateFilters() { + return this.fetcher.countURLTemplateFilters(); + } - // Check totalCallsTimeout (first call + 1 hour), if older than now, - // reset totalCalls number, by this way the client will be eligible again - // for freelunch capping - if (this.totalCallsTimeout < now) { - this.totalCalls = 1; - this.totalCallsTimeout = now + 60 * 60 * 1000; - } else { - this.totalCalls++; - } - return new Promise(function (resolve, reject) { - if (_this.cappingFreeLunch >= _this.totalCalls) { - return reject(new Error("VAST call canceled \u2013 FreeLunch capping not reached yet ".concat(_this.totalCalls, "/").concat(_this.cappingFreeLunch))); - } - var timeSinceLastCall = now - _this.lastSuccessfulAd; - - // Check timeSinceLastCall to be a positive number. If not, this mean the - // previous was made in the future. We reset lastSuccessfulAd value - if (timeSinceLastCall < 0) { - _this.lastSuccessfulAd = 0; - } else if (timeSinceLastCall < _this.cappingMinimumTimeInterval) { - return reject(new Error("VAST call canceled \u2013 (".concat(_this.cappingMinimumTimeInterval, ")ms minimum interval reached"))); - } - _this.vastParser.getAndParseVAST(url, options).then(function (response) { - return resolve(response); - })["catch"](function (err) { - return reject(err); - }); - }); - } - }]); - return VASTClient; -}(); + /** + * Removes all the filter functions from the url templates filters array. + * @return {void} + */ + clearURLTemplateFilters() { + this.fetcher.clearURLTemplateFilters(); + } + getParser() { + return this.vastParser; + } + get lastSuccessfulAd() { + return this.storage.getItem('vast-client-last-successful-ad'); + } + set lastSuccessfulAd(value) { + this.storage.setItem('vast-client-last-successful-ad', value); + } + get totalCalls() { + return this.storage.getItem('vast-client-total-calls'); + } + set totalCalls(value) { + this.storage.setItem('vast-client-total-calls', value); + } + get totalCallsTimeout() { + return this.storage.getItem('vast-client-total-calls-timeout'); + } + set totalCallsTimeout(value) { + this.storage.setItem('vast-client-total-calls-timeout', value); + } -/** - * The default skip delay used in case a custom one is not provided + /** + * Returns a boolean indicating if there are more ads to resolve for the current parsing. + * @return {Boolean} + */ + hasRemainingAds() { + return this.vastParser.remainingAds.length > 0; + } + + /** + * Resolves the next group of ads. If all is true resolves all the remaining ads. + * @param {Boolean} all - If true all the remaining ads are resolved + * @return {Promise} + */ + getNextAds(all) { + return this.vastParser.getRemainingAds(all); + } + + /** + * Parses the given xml Object into a VASTResponse. + * Returns a Promise which resolves with a fully parsed VASTResponse or rejects with an Error. + * @param {Object} xml - An object representing a vast xml document. + * @param {Object} options - An optional Object of parameters to be used in the parsing and fetching process. + * @returns {Promise} + */ + parseVAST(xml) { + let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + this.fetcher.setOptions(options); + return this.vastParser.parseVAST(xml, options); + } + /** + * Gets a parsed VAST document for the given url, applying the skipping rules defined. + * Returns a Promise which resolves with a fully parsed VASTResponse or rejects with an Error. + * @param {String} url - The url to use to fecth the VAST document. + * @param {Object} options - An optional Object of parameters to be applied in the process. + * @return {Promise} + */ + get(url) { + let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + const now = Date.now(); + + // By default the client resolves only the first Ad or AdPod + if (!options.hasOwnProperty('resolveAll')) { + options.resolveAll = false; + } + + // Check totalCallsTimeout (first call + 1 hour), if older than now, + // reset totalCalls number, by this way the client will be eligible again + // for freelunch capping + if (this.totalCallsTimeout < now) { + this.totalCalls = 1; + this.totalCallsTimeout = now + 60 * 60 * 1000; + } else { + this.totalCalls++; + } + return new Promise((resolve, reject) => { + if (this.cappingFreeLunch >= this.totalCalls) { + return reject(new Error("VAST call canceled \u2013 FreeLunch capping not reached yet ".concat(this.totalCalls, "/").concat(this.cappingFreeLunch))); + } + const timeSinceLastCall = now - this.lastSuccessfulAd; + + // Check timeSinceLastCall to be a positive number. If not, this mean the + // previous was made in the future. We reset lastSuccessfulAd value + if (timeSinceLastCall < 0) { + this.lastSuccessfulAd = 0; + } else if (timeSinceLastCall < this.cappingMinimumTimeInterval) { + return reject(new Error("VAST call canceled \u2013 (".concat(this.cappingMinimumTimeInterval, ")ms minimum interval reached"))); + } + this.vastParser.initParsingStatus(options); + this.fetcher.setOptions(options); + this.vastParser.rootURL = url; + this.fetcher.fetchVAST({ + url, + emitter: this.vastParser.emit.bind(this.vastParser), + maxWrapperDepth: this.vastParser.maxWrapperDepth + }).then(xml => { + options.previousUrl = url; + options.isRootVAST = true; + options.url = url; + return this.vastParser.parse(xml, options).then(resolvedAd => { + const vastResponse = this.vastParser.buildVASTResponse(resolvedAd); + resolve(vastResponse); + }); + }).catch(err => reject(err)); + }); + } +} + +/** + * The default skip delay used in case a custom one is not provided * @constant * @type {Number} */ -var DEFAULT_SKIP_DELAY = -1; +const DEFAULT_SKIP_DELAY = -1; /** * This class provides methods to track an ad execution. @@ -3271,9 +2998,7 @@ var DEFAULT_SKIP_DELAY = -1; * @class VASTTracker * @extends EventEmitter */ -var VASTTracker = /*#__PURE__*/function (_EventEmitter) { - _inherits(VASTTracker, _EventEmitter); - var _super = _createSuper(VASTTracker); +class VASTTracker extends EventEmitter { /** * Creates an instance of VASTTracker. * @@ -3284,50 +3009,47 @@ var VASTTracker = /*#__PURE__*/function (_EventEmitter) { * @param {Boolean} [muted=false] - The initial muted state of the video. * @constructor */ - function VASTTracker(client, ad, creative) { - var _this; - var variation = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null; - var muted = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false; - _classCallCheck(this, VASTTracker); - _this = _super.call(this); - _this.ad = ad; - _this.creative = creative; - _this.variation = variation; - _this.muted = muted; - _this.impressed = false; - _this.skippable = false; - _this.trackingEvents = {}; + constructor(client, ad, creative) { + let variation = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null; + let muted = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false; + super(); + this.ad = ad; + this.creative = creative; + this.variation = variation; + this.muted = muted; + this.impressed = false; + this.skippable = false; + this.trackingEvents = {}; // We need to keep the last percentage of the tracker in order to // calculate to trigger the events when the VAST duration is short - _this.lastPercentage = 0; - _this._alreadyTriggeredQuartiles = {}; + this.lastPercentage = 0; + this._alreadyTriggeredQuartiles = {}; // Tracker listeners should be notified with some events // no matter if there is a tracking URL or not - _this.emitAlwaysEvents = ['creativeView', 'start', 'firstQuartile', 'midpoint', 'thirdQuartile', 'complete', 'resume', 'pause', 'rewind', 'skip', 'closeLinear', 'close']; + this.emitAlwaysEvents = ['creativeView', 'start', 'firstQuartile', 'midpoint', 'thirdQuartile', 'complete', 'resume', 'pause', 'rewind', 'skip', 'closeLinear', 'close']; // Duplicate the creative's trackingEvents property so we can alter it - for (var eventName in _this.creative.trackingEvents) { - var events = _this.creative.trackingEvents[eventName]; - _this.trackingEvents[eventName] = events.slice(0); + for (const eventName in this.creative.trackingEvents) { + const events = this.creative.trackingEvents[eventName]; + this.trackingEvents[eventName] = events.slice(0); } // Nonlinear and companion creatives provide some tracking information at a variation level // While linear creatives provided that at a creative level. That's why we need to // differentiate how we retrieve some tracking information. - if (isCreativeLinear(_this.creative)) { - _this._initLinearTracking(); + if (isCreativeLinear(this.creative)) { + this._initLinearTracking(); } else { - _this._initVariationTracking(); + this._initVariationTracking(); } // If the tracker is associated with a client we add a listener to the start event // to update the lastSuccessfulAd property. if (client) { - _this.on('start', function () { + this.on('start', () => { client.lastSuccessfulAd = Date.now(); }); } - return _this; } /** @@ -3335,808 +3057,825 @@ var VASTTracker = /*#__PURE__*/function (_EventEmitter) { * * @return {void} */ - _createClass(VASTTracker, [{ - key: "_initLinearTracking", - value: function _initLinearTracking() { - this.linear = true; - this.skipDelay = this.creative.skipDelay; - this.setDuration(this.creative.duration); - this.clickThroughURLTemplate = this.creative.videoClickThroughURLTemplate; - this.clickTrackingURLTemplates = this.creative.videoClickTrackingURLTemplates; - } + _initLinearTracking() { + this.linear = true; + this.skipDelay = this.creative.skipDelay; + this.setDuration(this.creative.duration); + this.clickThroughURLTemplate = this.creative.videoClickThroughURLTemplate; + this.clickTrackingURLTemplates = this.creative.videoClickTrackingURLTemplates; + } - /** - * Init the custom tracking options for nonlinear and companion creatives. - * These options are provided in the variation Object. - * - * @return {void} - */ - }, { - key: "_initVariationTracking", - value: function _initVariationTracking() { - this.linear = false; - this.skipDelay = DEFAULT_SKIP_DELAY; + /** + * Init the custom tracking options for nonlinear and companion creatives. + * These options are provided in the variation Object. + * + * @return {void} + */ + _initVariationTracking() { + this.linear = false; + this.skipDelay = DEFAULT_SKIP_DELAY; - // If no variation has been provided there's nothing else to set - if (!this.variation) { - return; - } + // If no variation has been provided there's nothing else to set + if (!this.variation) { + return; + } - // Duplicate the variation's trackingEvents property so we can alter it - for (var eventName in this.variation.trackingEvents) { - var events = this.variation.trackingEvents[eventName]; + // Duplicate the variation's trackingEvents property so we can alter it + for (const eventName in this.variation.trackingEvents) { + const events = this.variation.trackingEvents[eventName]; - // If for the given eventName we already had some trackingEvents provided by the creative - // we want to keep both the creative trackingEvents and the variation ones - if (this.trackingEvents[eventName]) { - this.trackingEvents[eventName] = this.trackingEvents[eventName].concat(events.slice(0)); - } else { - this.trackingEvents[eventName] = events.slice(0); - } - } - if (isNonLinearAd(this.variation)) { - this.clickThroughURLTemplate = this.variation.nonlinearClickThroughURLTemplate; - this.clickTrackingURLTemplates = this.variation.nonlinearClickTrackingURLTemplates; - this.setDuration(this.variation.minSuggestedDuration); - } else if (isCompanionAd(this.variation)) { - this.clickThroughURLTemplate = this.variation.companionClickThroughURLTemplate; - this.clickTrackingURLTemplates = this.variation.companionClickTrackingURLTemplates; + // If for the given eventName we already had some trackingEvents provided by the creative + // we want to keep both the creative trackingEvents and the variation ones + if (this.trackingEvents[eventName]) { + this.trackingEvents[eventName] = this.trackingEvents[eventName].concat(events.slice(0)); + } else { + this.trackingEvents[eventName] = events.slice(0); } } - - /** - * Sets the duration of the ad and updates the quartiles based on that. - * - * @param {Number} duration - The duration of the ad. - */ - }, { - key: "setDuration", - value: function setDuration(duration) { - // check if duration is a valid time input - if (!util.isValidTimeValue(duration)) { - return; - } - this.assetDuration = duration; - // beware of key names, theses are also used as event names - this.quartiles = { - firstQuartile: Math.round(25 * this.assetDuration) / 100, - midpoint: Math.round(50 * this.assetDuration) / 100, - thirdQuartile: Math.round(75 * this.assetDuration) / 100 - }; + if (isNonLinearAd(this.variation)) { + this.clickThroughURLTemplate = this.variation.nonlinearClickThroughURLTemplate; + this.clickTrackingURLTemplates = this.variation.nonlinearClickTrackingURLTemplates; + this.setDuration(this.variation.minSuggestedDuration); + } else if (isCompanionAd(this.variation)) { + this.clickThroughURLTemplate = this.variation.companionClickThroughURLTemplate; + this.clickTrackingURLTemplates = this.variation.companionClickTrackingURLTemplates; } + } - /** - * Sets the duration of the ad and updates the quartiles based on that. - * This is required for tracking time related events. - * - * @param {Number} progress - Current playback time in seconds. - * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. - * @emits VASTTracker#start - * @emits VASTTracker#skip-countdown - * @emits VASTTracker#progress-[0-100]% - * @emits VASTTracker#progress-[currentTime] - * @emits VASTTracker#rewind - * @emits VASTTracker#firstQuartile - * @emits VASTTracker#midpoint - * @emits VASTTracker#thirdQuartile - */ - }, { - key: "setProgress", - value: function setProgress(progress) { - var _this2 = this; - var macros = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; - // check if progress is a valid time input - if (!util.isValidTimeValue(progress) || _typeof(macros) !== 'object') { - return; + /** + * Sets the duration of the ad and updates the quartiles based on that. + * + * @param {Number} duration - The duration of the ad. + */ + setDuration(duration) { + // check if duration is a valid time input + if (!util.isValidTimeValue(duration)) { + this.emit('TRACKER-error', { + message: "the duration provided is not valid. duration: ".concat(duration) + }); + return; + } + this.assetDuration = duration; + // beware of key names, theses are also used as event names + this.quartiles = { + firstQuartile: Math.round(25 * this.assetDuration) / 100, + midpoint: Math.round(50 * this.assetDuration) / 100, + thirdQuartile: Math.round(75 * this.assetDuration) / 100 + }; + } + + /** + * Sets the duration of the ad and updates the quartiles based on that. + * This is required for tracking time related events. + * + * @param {Number} progress - Current playback time in seconds. + * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. + * @emits VASTTracker#start + * @emits VASTTracker#skip-countdown + * @emits VASTTracker#progress-[0-100]% + * @emits VASTTracker#progress-[currentTime] + * @emits VASTTracker#rewind + * @emits VASTTracker#firstQuartile + * @emits VASTTracker#midpoint + * @emits VASTTracker#thirdQuartile + */ + setProgress(progress) { + let macros = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + let trackOnce = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true; + // check if progress is a valid time input + if (!util.isValidTimeValue(progress) || typeof macros !== 'object') { + this.emit('TRACKER-error', { + message: "One given setProgress parameter has the wrong type. progress: ".concat(progress, ", macros: ").concat(util.formatMacrosValues(macros)) + }); + return; + } + const skipDelay = this.skipDelay || DEFAULT_SKIP_DELAY; + if (skipDelay !== -1 && !this.skippable) { + if (skipDelay > progress) { + this.emit('skip-countdown', skipDelay - progress); + } else { + this.skippable = true; + this.emit('skip-countdown', 0); } - var skipDelay = this.skipDelay || DEFAULT_SKIP_DELAY; - if (skipDelay !== -1 && !this.skippable) { - if (skipDelay > progress) { - this.emit('skip-countdown', skipDelay - progress); - } else { - this.skippable = true; - this.emit('skip-countdown', 0); + } + if (this.assetDuration > 0) { + const percent = Math.round(progress / this.assetDuration * 100); + const events = []; + if (progress > 0) { + events.push('start'); + for (let i = this.lastPercentage; i < percent; i++) { + events.push("progress-".concat(i + 1, "%")); } - } - if (this.assetDuration > 0) { - var percent = Math.round(progress / this.assetDuration * 100); - var events = []; - if (progress > 0) { - events.push('start'); - for (var i = this.lastPercentage; i < percent; i++) { - events.push("progress-".concat(i + 1, "%")); - } - events.push("progress-".concat(Math.round(progress))); - for (var quartile in this.quartiles) { - if (this.isQuartileReached(quartile, this.quartiles[quartile], progress)) { - events.push(quartile); - this._alreadyTriggeredQuartiles[quartile] = true; - } + events.push("progress-".concat(Math.round(progress))); + for (const quartile in this.quartiles) { + if (this.isQuartileReached(quartile, this.quartiles[quartile], progress)) { + events.push(quartile); + this._alreadyTriggeredQuartiles[quartile] = true; } - this.lastPercentage = percent; } - events.forEach(function (eventName) { - _this2.track(eventName, { - macros: macros, - once: true - }); + this.lastPercentage = percent; + } + events.forEach(eventName => { + this.track(eventName, { + macros, + once: trackOnce + }); + }); + if (progress < this.progress) { + this.track('rewind', { + macros }); - if (progress < this.progress) { - this.track('rewind', { - macros: macros - }); - } } - this.progress = progress; } + this.progress = progress; + } - /** - * Checks if a quartile has been reached without have being triggered already. - * - * @param {String} quartile - Quartile name - * @param {Number} time - Time offset of the quartile, when this quartile is reached in seconds. - * @param {Number} progress - Current progress of the ads in seconds. - * - * @return {Boolean} - */ - }, { - key: "isQuartileReached", - value: function isQuartileReached(quartile, time, progress) { - var quartileReached = false; - // if quartile time already reached and never triggered - if (time <= progress && !this._alreadyTriggeredQuartiles[quartile]) { - quartileReached = true; - } - return quartileReached; + /** + * Checks if a quartile has been reached without have being triggered already. + * + * @param {String} quartile - Quartile name + * @param {Number} time - Time offset of the quartile, when this quartile is reached in seconds. + * @param {Number} progress - Current progress of the ads in seconds. + * + * @return {Boolean} + */ + isQuartileReached(quartile, time, progress) { + let quartileReached = false; + // if quartile time already reached and never triggered + if (time <= progress && !this._alreadyTriggeredQuartiles[quartile]) { + quartileReached = true; } + return quartileReached; + } - /** - * Updates the mute state and calls the mute/unmute tracking URLs. - * - * @param {Boolean} muted - Indicates if the video is muted or not. - * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. - * @emits VASTTracker#mute - * @emits VASTTracker#unmute - */ - }, { - key: "setMuted", - value: function setMuted(muted) { - var macros = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; - if (typeof muted !== 'boolean' || _typeof(macros) !== 'object') { - return; - } - if (this.muted !== muted) { - this.track(muted ? 'mute' : 'unmute', { - macros: macros - }); - } - this.muted = muted; + /** + * Updates the mute state and calls the mute/unmute tracking URLs. + * + * @param {Boolean} muted - Indicates if the video is muted or not. + * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. + * @emits VASTTracker#mute + * @emits VASTTracker#unmute + */ + setMuted(muted) { + let macros = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + if (typeof muted !== 'boolean' || typeof macros !== 'object') { + this.emit('TRACKER-error', { + message: "One given setMuted parameter has the wrong type. muted: ".concat(muted, ", macros: ").concat(util.formatMacrosValues(macros)) + }); + return; } + if (this.muted !== muted) { + this.track(muted ? 'mute' : 'unmute', { + macros + }); + } + this.muted = muted; + } - /** - * Update the pause state and call the resume/pause tracking URLs. - * - * @param {Boolean} paused - Indicates if the video is paused or not. - * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. - * @emits VASTTracker#pause - * @emits VASTTracker#resume - */ - }, { - key: "setPaused", - value: function setPaused(paused) { - var macros = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; - if (typeof paused !== 'boolean' || _typeof(macros) !== 'object') { - return; - } - if (this.paused !== paused) { - this.track(paused ? 'pause' : 'resume', { - macros: macros - }); - } - this.paused = paused; + /** + * Update the pause state and call the resume/pause tracking URLs. + * + * @param {Boolean} paused - Indicates if the video is paused or not. + * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. + * @emits VASTTracker#pause + * @emits VASTTracker#resume + */ + setPaused(paused) { + let macros = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + if (typeof paused !== 'boolean' || typeof macros !== 'object') { + this.emit('TRACKER-error', { + message: "One given setPaused parameter has the wrong type. paused: ".concat(paused, ", macros: ").concat(util.formatMacrosValues(macros)) + }); + return; } + if (this.paused !== paused) { + this.track(paused ? 'pause' : 'resume', { + macros + }); + } + this.paused = paused; + } - /** - * Updates the fullscreen state and calls the fullscreen tracking URLs. - * - * @param {Boolean} fullscreen - Indicates if the video is in fulscreen mode or not. - * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. - * @emits VASTTracker#fullscreen - * @emits VASTTracker#exitFullscreen - */ - }, { - key: "setFullscreen", - value: function setFullscreen(fullscreen) { - var macros = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; - if (typeof fullscreen !== 'boolean' || _typeof(macros) !== 'object') { - return; - } - if (this.fullscreen !== fullscreen) { - this.track(fullscreen ? 'fullscreen' : 'exitFullscreen', { - macros: macros - }); - } - this.fullscreen = fullscreen; + /** + * Updates the fullscreen state and calls the fullscreen tracking URLs. + * + * @param {Boolean} fullscreen - Indicates if the video is in fulscreen mode or not. + * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. + * @emits VASTTracker#fullscreen + * @emits VASTTracker#exitFullscreen + */ + setFullscreen(fullscreen) { + let macros = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + if (typeof fullscreen !== 'boolean' || typeof macros !== 'object') { + this.emit('TRACKER-error', { + message: "One given setFullScreen parameter has the wrong type. fullscreen: ".concat(fullscreen, ", macros: ").concat(util.formatMacrosValues(macros)) + }); + return; + } + if (this.fullscreen !== fullscreen) { + this.track(fullscreen ? 'fullscreen' : 'exitFullscreen', { + macros + }); } + this.fullscreen = fullscreen; + } - /** - * Updates the expand state and calls the expand/collapse tracking URLs. - * - * @param {Boolean} expanded - Indicates if the video is expanded or not. - * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. - * @emits VASTTracker#expand - * @emits VASTTracker#playerExpand - * @emits VASTTracker#collapse - * @emits VASTTracker#playerCollapse - */ - }, { - key: "setExpand", - value: function setExpand(expanded) { - var macros = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; - if (typeof expanded !== 'boolean' || _typeof(macros) !== 'object') { - return; - } - if (this.expanded !== expanded) { - this.track(expanded ? 'expand' : 'collapse', { - macros: macros - }); - this.track(expanded ? 'playerExpand' : 'playerCollapse', { - macros: macros - }); - } - this.expanded = expanded; + /** + * Updates the expand state and calls the expand/collapse tracking URLs. + * + * @param {Boolean} expanded - Indicates if the video is expanded or not. + * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. + * @emits VASTTracker#expand + * @emits VASTTracker#playerExpand + * @emits VASTTracker#collapse + * @emits VASTTracker#playerCollapse + */ + setExpand(expanded) { + let macros = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + if (typeof expanded !== 'boolean' || typeof macros !== 'object') { + this.emit('TRACKER-error', { + message: "One given setExpand parameter has the wrong type. expanded: ".concat(expanded, ", macros: ").concat(util.formatMacrosValues(macros)) + }); + return; + } + if (this.expanded !== expanded) { + this.track(expanded ? 'expand' : 'collapse', { + macros + }); + this.track(expanded ? 'playerExpand' : 'playerCollapse', { + macros + }); } + this.expanded = expanded; + } - /** - * Must be called if you want to overwrite the Skipoffset value. - * This will init the skip countdown duration. Then, every time setProgress() is called, - * it will decrease the countdown and emit a skip-countdown event with the remaining time. - * Do not call this method if you want to keep the original Skipoffset value. - * - * @param {Number} duration - The time in seconds until the skip button is displayed. - */ - }, { - key: "setSkipDelay", - value: function setSkipDelay(duration) { - if (!util.isValidTimeValue(duration)) { - return; - } - this.skipDelay = duration; + /** + * Must be called if you want to overwrite the Skipoffset value. + * This will init the skip countdown duration. Then, every time setProgress() is called, + * it will decrease the countdown and emit a skip-countdown event with the remaining time. + * Do not call this method if you want to keep the original Skipoffset value. + * + * @param {Number} duration - The time in seconds until the skip button is displayed. + */ + setSkipDelay(duration) { + if (!util.isValidTimeValue(duration)) { + this.emit('TRACKER-error', { + message: "setSkipDelay parameter does not have a valid value. duration: ".concat(duration) + }); + return; } + this.skipDelay = duration; + } - /** - * Tracks an impression (can be called only once). - * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. - * @emits VASTTracker#creativeView - */ - }, { - key: "trackImpression", - value: function trackImpression() { - var macros = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; - if (_typeof(macros) !== 'object') { - return; - } - if (!this.impressed) { - this.impressed = true; - this.trackURLs(this.ad.impressionURLTemplates, macros); - this.track('creativeView', { - macros: macros - }); - } + /** + * Tracks an impression (can be called only once). + * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. + * @emits VASTTracker#creativeView + */ + trackImpression() { + let macros = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + if (typeof macros !== 'object') { + this.emit('TRACKER-error', { + message: "trackImpression parameter has the wrong type. macros: ".concat(macros) + }); + return; + } + if (!this.impressed) { + this.impressed = true; + this.trackURLs(this.ad.impressionURLTemplates, macros); + this.track('creativeView', { + macros + }); } + } - /** - * Tracks Viewable impression - * @param {Object} [macros = {}] An optional Object containing macros and their values to be used and replaced in the tracking calls. - */ - }, { - key: "trackViewableImpression", - value: function trackViewableImpression() { - var _this3 = this; - var macros = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; - if (_typeof(macros) !== 'object') { - return; - } - this.ad.viewableImpression.forEach(function (impression) { - _this3.trackURLs(impression.viewable, macros); + /** + * Tracks Viewable impression + * @param {Object} [macros = {}] An optional Object containing macros and their values to be used and replaced in the tracking calls. + */ + trackViewableImpression() { + let macros = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + if (typeof macros !== 'object') { + this.emit('TRACKER-error', { + message: "trackViewableImpression given macros has the wrong type. macros: ".concat(macros) }); + return; } + this.ad.viewableImpression.forEach(impression => { + this.trackURLs(impression.viewable, macros); + }); + } - /** - * Tracks NotViewable impression - * @param {Object} [macros = {}] An optional Object containing macros and their values to be used and replaced in the tracking calls. - */ - }, { - key: "trackNotViewableImpression", - value: function trackNotViewableImpression() { - var _this4 = this; - var macros = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; - if (_typeof(macros) !== 'object') { - return; - } - this.ad.viewableImpression.forEach(function (impression) { - _this4.trackURLs(impression.notViewable, macros); + /** + * Tracks NotViewable impression + * @param {Object} [macros = {}] An optional Object containing macros and their values to be used and replaced in the tracking calls. + */ + + trackNotViewableImpression() { + let macros = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + if (typeof macros !== 'object') { + this.emit('TRACKER-error', { + message: "trackNotViewableImpression given macros has the wrong type. macros: ".concat(macros) }); + return; } + this.ad.viewableImpression.forEach(impression => { + this.trackURLs(impression.notViewable, macros); + }); + } - /** - * Tracks ViewUndetermined impression - * @param {Object} [macros = {}] An optional Object containing macros and their values to be used and replaced in the tracking calls. - */ - }, { - key: "trackUndeterminedImpression", - value: function trackUndeterminedImpression() { - var _this5 = this; - var macros = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; - if (_typeof(macros) !== 'object') { - return; - } - this.ad.viewableImpression.forEach(function (impression) { - _this5.trackURLs(impression.viewUndetermined, macros); + /** + * Tracks ViewUndetermined impression + * @param {Object} [macros = {}] An optional Object containing macros and their values to be used and replaced in the tracking calls. + */ + trackUndeterminedImpression() { + let macros = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + if (typeof macros !== 'object') { + this.emit('TRACKER-error', { + message: "trackUndeterminedImpression given macros has the wrong type. macros: ".concat(macros) }); + return; } + this.ad.viewableImpression.forEach(impression => { + this.trackURLs(impression.viewUndetermined, macros); + }); + } - /** - * Send a request to the URI provided by the VAST element. - * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. - * @param {Boolean} [isCustomCode=false] - Flag to allow custom values on error code. - */ - }, { - key: "error", - value: function error() { - var macros = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; - var isCustomCode = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; - if (_typeof(macros) !== 'object' || typeof isCustomCode !== 'boolean') { - return; - } - this.trackURLs(this.ad.errorURLTemplates, macros, { - isCustomCode: isCustomCode + /** + * Send a request to the URI provided by the VAST element. + * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. + * @param {Boolean} [isCustomCode=false] - Flag to allow custom values on error code. + */ + error() { + let macros = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + let isCustomCode = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; + if (typeof macros !== 'object' || typeof isCustomCode !== 'boolean') { + this.emit('TRACKER-error', { + message: "One given error parameter has the wrong type. macros: ".concat(util.formatMacrosValues(macros), ", isCustomCode: ").concat(isCustomCode) }); + return; } + this.trackURLs(this.ad.errorURLTemplates, macros, { + isCustomCode + }); + } - /** - * Send a request to the URI provided by the VAST element. - * If an [ERRORCODE] macro is included, it will be substitute with errorCode. - * @deprecated - * @param {String} errorCode - Replaces [ERRORCODE] macro. [ERRORCODE] values are listed in the VAST specification. - * @param {Boolean} [isCustomCode=false] - Flag to allow custom values on error code. - */ - }, { - key: "errorWithCode", - value: function errorWithCode(errorCode) { - var isCustomCode = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; - if (typeof errorCode !== 'string' || typeof isCustomCode !== 'boolean') { - return; - } - this.error({ - ERRORCODE: errorCode - }, isCustomCode); - //eslint-disable-next-line - console.log('The method errorWithCode is deprecated, please use vast tracker error method instead'); + /** + * Send a request to the URI provided by the VAST element. + * If an [ERRORCODE] macro is included, it will be substitute with errorCode. + * @deprecated + * @param {String} errorCode - Replaces [ERRORCODE] macro. [ERRORCODE] values are listed in the VAST specification. + * @param {Boolean} [isCustomCode=false] - Flag to allow custom values on error code. + */ + errorWithCode(errorCode) { + let isCustomCode = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; + if (typeof errorCode !== 'string' || typeof isCustomCode !== 'boolean') { + this.emit('TRACKER-error', { + message: "One given errorWithCode parameter has the wrong type. errorCode: ".concat(errorCode, ", isCustomCode: ").concat(isCustomCode) + }); + return; } + this.error({ + ERRORCODE: errorCode + }, isCustomCode); + //eslint-disable-next-line + console.log('The method errorWithCode is deprecated, please use vast tracker error method instead'); + } - /** - * Must be called when the user watched the linear creative until its end. - * Calls the complete tracking URLs. - * - * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. - * @emits VASTTracker#complete - */ - }, { - key: "complete", - value: function complete() { - var macros = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; - if (_typeof(macros) !== 'object') { - return; - } - this.track('complete', { - macros: macros + /** + * Must be called when the user watched the linear creative until its end. + * Calls the complete tracking URLs. + * + * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. + * @emits VASTTracker#complete + */ + complete() { + let macros = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + if (typeof macros !== 'object') { + this.emit('TRACKER-error', { + message: "complete given macros has the wrong type. macros: ".concat(macros) }); + return; } + this.track('complete', { + macros + }); + } - /** - * Must be called if the ad was not and will not be played - * This is a terminal event; no other tracking events should be sent when this is used. - * Calls the notUsed tracking URLs. - * - * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. - * @emits VASTTracker#notUsed - */ - }, { - key: "notUsed", - value: function notUsed() { - var macros = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; - if (_typeof(macros) !== 'object') { - return; - } - this.track('notUsed', { - macros: macros + /** + * Must be called if the ad was not and will not be played + * This is a terminal event; no other tracking events should be sent when this is used. + * Calls the notUsed tracking URLs. + * + * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. + * @emits VASTTracker#notUsed + */ + notUsed() { + let macros = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + if (typeof macros !== 'object') { + this.emit('TRACKER-error', { + message: "notUsed given macros has the wrong type. macros: ".concat(macros) }); - this.trackingEvents = []; + return; } + this.track('notUsed', { + macros + }); + this.trackingEvents = []; + } - /** - * An optional metric that can capture all other user interactions - * under one metric such as hover-overs, or custom clicks. It should NOT replace - * clickthrough events or other existing events like mute, unmute, pause, etc. - * Calls the otherAdInteraction tracking URLs. - * - * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. - * @emits VASTTracker#otherAdInteraction - */ - }, { - key: "otherAdInteraction", - value: function otherAdInteraction() { - var macros = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; - if (_typeof(macros) !== 'object') { - return; - } - this.track('otherAdInteraction', { - macros: macros + /** + * An optional metric that can capture all other user interactions + * under one metric such as hover-overs, or custom clicks. It should NOT replace + * clickthrough events or other existing events like mute, unmute, pause, etc. + * Calls the otherAdInteraction tracking URLs. + * + * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. + * @emits VASTTracker#otherAdInteraction + */ + otherAdInteraction() { + let macros = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + if (typeof macros !== 'object') { + this.emit('TRACKER-error', { + message: "otherAdInteraction given macros has the wrong type. macros: ".concat(macros) }); + return; } + this.track('otherAdInteraction', { + macros + }); + } - /** - * Must be called if the user clicked or otherwise activated a control used to - * pause streaming content,* which either expands the ad within the player’s - * viewable area or “takes-over” the streaming content area by launching - * additional portion of the ad. - * Calls the acceptInvitation tracking URLs. - * - * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. - * @emits VASTTracker#acceptInvitation - */ - }, { - key: "acceptInvitation", - value: function acceptInvitation() { - var macros = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; - if (_typeof(macros) !== 'object') { - return; - } - this.track('acceptInvitation', { - macros: macros + /** + * Must be called if the user clicked or otherwise activated a control used to + * pause streaming content,* which either expands the ad within the player’s + * viewable area or “takes-over” the streaming content area by launching + * additional portion of the ad. + * Calls the acceptInvitation tracking URLs. + * + * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. + * @emits VASTTracker#acceptInvitation + */ + acceptInvitation() { + let macros = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + if (typeof macros !== 'object') { + this.emit('TRACKER-error', { + message: "acceptInvitation given macros has the wrong type. macros: ".concat(macros) }); + return; } + this.track('acceptInvitation', { + macros + }); + } - /** - * Must be called if user activated a control to expand the creative. - * Calls the adExpand tracking URLs. - * - * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. - * @emits VASTTracker#adExpand - */ - }, { - key: "adExpand", - value: function adExpand() { - var macros = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; - if (_typeof(macros) !== 'object') { - return; - } - this.track('adExpand', { - macros: macros + /** + * Must be called if user activated a control to expand the creative. + * Calls the adExpand tracking URLs. + * + * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. + * @emits VASTTracker#adExpand + */ + adExpand() { + let macros = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + if (typeof macros !== 'object') { + this.emit('TRACKER-error', { + message: "adExpand given macros has the wrong type. macros: ".concat(macros) }); + return; } + this.track('adExpand', { + macros + }); + } - /** - * Must be called when the user activated a control to reduce the creative to its original dimensions. - * Calls the adCollapse tracking URLs. - * - * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. - * @emits VASTTracker#adCollapse - */ - }, { - key: "adCollapse", - value: function adCollapse() { - var macros = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; - if (_typeof(macros) !== 'object') { - return; - } - this.track('adCollapse', { - macros: macros + /** + * Must be called when the user activated a control to reduce the creative to its original dimensions. + * Calls the adCollapse tracking URLs. + * + * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. + * @emits VASTTracker#adCollapse + */ + adCollapse() { + let macros = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + if (typeof macros !== 'object') { + this.emit('TRACKER-error', { + message: "adCollapse given macros has the wrong type. macros: ".concat(macros) }); + return; } + this.track('adCollapse', { + macros + }); + } - /** - * Must be called if the user clicked or otherwise activated a control used to minimize the ad. - * Calls the minimize tracking URLs. - * - * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. - * @emits VASTTracker#minimize - */ - }, { - key: "minimize", - value: function minimize() { - var macros = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; - if (_typeof(macros) !== 'object') { - return; - } - this.track('minimize', { - macros: macros + /** + * Must be called if the user clicked or otherwise activated a control used to minimize the ad. + * Calls the minimize tracking URLs. + * + * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. + * @emits VASTTracker#minimize + */ + minimize() { + let macros = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + if (typeof macros !== 'object') { + this.emit('TRACKER-error', { + message: "minimize given macros has the wrong type. macros: ".concat(macros) }); + return; } + this.track('minimize', { + macros + }); + } - /** - * Must be called if the player did not or was not able to execute the provided - * verification code.The [REASON] macro must be filled with reason code - * Calls the verificationNotExecuted tracking URL of associated verification vendor. - * - * @param {String} vendor - An identifier for the verification vendor. The recommended format is [domain]-[useCase], to avoid name collisions. For example, "company.com-omid". - * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. - * @emits VASTTracker#verificationNotExecuted - */ - }, { - key: "verificationNotExecuted", - value: function verificationNotExecuted(vendor) { - var macros = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; - if (typeof vendor !== 'string' || _typeof(macros) !== 'object') { - return; - } - if (!this.ad || !this.ad.adVerifications || !this.ad.adVerifications.length) { - throw new Error('No adVerifications provided'); - } - if (!vendor) { - throw new Error('No vendor provided, unable to find associated verificationNotExecuted'); - } - var vendorVerification = this.ad.adVerifications.find(function (verifications) { - return verifications.vendor === vendor; + /** + * Must be called if the player did not or was not able to execute the provided + * verification code.The [REASON] macro must be filled with reason code + * Calls the verificationNotExecuted tracking URL of associated verification vendor. + * + * @param {String} vendor - An identifier for the verification vendor. The recommended format is [domain]-[useCase], to avoid name collisions. For example, "company.com-omid". + * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. + * @emits VASTTracker#verificationNotExecuted + */ + verificationNotExecuted(vendor) { + let macros = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + if (typeof vendor !== 'string' || typeof macros !== 'object') { + this.emit('TRACKER-error', { + message: "One given verificationNotExecuted parameter has to wrong type. vendor: ".concat(vendor, ", macros: ").concat(util.formatMacrosValues(macros)) + }); + return; + } + if (!this.ad || !this.ad.adVerifications || !this.ad.adVerifications.length) { + throw new Error('No adVerifications provided'); + } + if (!vendor) { + throw new Error('No vendor provided, unable to find associated verificationNotExecuted'); + } + const vendorVerification = this.ad.adVerifications.find(verifications => verifications.vendor === vendor); + if (!vendorVerification) { + throw new Error("No associated verification element found for vendor: ".concat(vendor)); + } + const vendorTracking = vendorVerification.trackingEvents; + if (vendorTracking && vendorTracking.verificationNotExecuted) { + const verifsNotExecuted = vendorTracking.verificationNotExecuted; + this.trackURLs(verifsNotExecuted, macros); + this.emit('verificationNotExecuted', { + trackingURLTemplates: verifsNotExecuted }); - if (!vendorVerification) { - throw new Error("No associated verification element found for vendor: ".concat(vendor)); - } - var vendorTracking = vendorVerification.trackingEvents; - if (vendorTracking && vendorTracking.verificationNotExecuted) { - var verifsNotExecuted = vendorTracking.verificationNotExecuted; - this.trackURLs(verifsNotExecuted, macros); - this.emit('verificationNotExecuted', { - trackingURLTemplates: verifsNotExecuted - }); - } } + } - /** - * The time that the initial ad is displayed. This time is based on - * the time between the impression and either the completed length of display based - * on the agreement between transactional parties or a close, minimize, or accept - * invitation event. - * The time will be passed using [ADPLAYHEAD] macros for VAST 4.1 - * Calls the overlayViewDuration tracking URLs. - * - * @param {String} formattedDuration - The time that the initial ad is displayed. - * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. - * @emits VASTTracker#overlayViewDuration - */ - }, { - key: "overlayViewDuration", - value: function overlayViewDuration(formattedDuration) { - var macros = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; - if (typeof formattedDuration !== 'string' || _typeof(macros) !== 'object') { - return; - } - macros['ADPLAYHEAD'] = formattedDuration; - this.track('overlayViewDuration', { - macros: macros + /** + * The time that the initial ad is displayed. This time is based on + * the time between the impression and either the completed length of display based + * on the agreement between transactional parties or a close, minimize, or accept + * invitation event. + * The time will be passed using [ADPLAYHEAD] macros for VAST 4.1 + * Calls the overlayViewDuration tracking URLs. + * + * @param {String} formattedDuration - The time that the initial ad is displayed. + * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. + * @emits VASTTracker#overlayViewDuration + */ + overlayViewDuration(formattedDuration) { + let macros = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + if (typeof formattedDuration !== 'string' || typeof macros !== 'object') { + this.emit('TRACKER-error', { + message: "One given overlayViewDuration parameters has the wrong type. formattedDuration: ".concat(formattedDuration, ", macros: ").concat(util.formatMacrosValues(macros)) }); + return; } + macros['ADPLAYHEAD'] = formattedDuration; + this.track('overlayViewDuration', { + macros + }); + } - /** - * Must be called when the player or the window is closed during the ad. - * Calls the `closeLinear` (in VAST 3.0 and 4.1) and `close` tracking URLs. - * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. - * - * @emits VASTTracker#closeLinear - * @emits VASTTracker#close - */ - }, { - key: "close", - value: function close() { - var macros = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; - if (_typeof(macros) !== 'object') { - return; - } - this.track(this.linear ? 'closeLinear' : 'close', { - macros: macros + /** + * Must be called when the player or the window is closed during the ad. + * Calls the `closeLinear` (in VAST 3.0 and 4.1) and `close` tracking URLs. + * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. + * + * @emits VASTTracker#closeLinear + * @emits VASTTracker#close + */ + close() { + let macros = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + if (typeof macros !== 'object') { + this.emit('TRACKER-error', { + message: "close given macros has the wrong type. macros: ".concat(macros) }); + return; } + this.track(this.linear ? 'closeLinear' : 'close', { + macros + }); + } - /** - * Must be called when the skip button is clicked. Calls the skip tracking URLs. - * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. - * - * @emits VASTTracker#skip - */ - }, { - key: "skip", - value: function skip() { - var macros = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; - if (_typeof(macros) !== 'object') { - return; - } - this.track('skip', { - macros: macros + /** + * Must be called when the skip button is clicked. Calls the skip tracking URLs. + * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. + * + * @emits VASTTracker#skip + */ + skip() { + let macros = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + if (typeof macros !== 'object') { + this.emit('TRACKER-error', { + message: "skip given macros has the wrong type. macros: ".concat(macros) }); + return; } + this.track('skip', { + macros + }); + } - /** - * Must be called then loaded and buffered the creative’s media and assets either fully - * or to the extent that it is ready to play the media - * Calls the loaded tracking URLs. - * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. - * - * @emits VASTTracker#loaded - */ - }, { - key: "load", - value: function load() { - var macros = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; - if (_typeof(macros) !== 'object') { - return; - } - this.track('loaded', { - macros: macros + /** + * Must be called then loaded and buffered the creative’s media and assets either fully + * or to the extent that it is ready to play the media + * Calls the loaded tracking URLs. + * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. + * + * @emits VASTTracker#loaded + */ + load() { + let macros = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + if (typeof macros !== 'object') { + this.emit('TRACKER-error', { + message: "load given macros has the wrong type. macros: ".concat(macros) }); + return; } + this.track('loaded', { + macros + }); + } - /** - * Must be called when the user clicks on the creative. - * It calls the tracking URLs and emits a 'clickthrough' event with the resolved - * clickthrough URL when done. - * - * @param {?String} [fallbackClickThroughURL=null] - an optional clickThroughURL template that could be used as a fallback - * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. - * @emits VASTTracker#clickthrough - */ - }, { - key: "click", - value: function click() { - var fallbackClickThroughURL = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null; - var macros = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; - if (fallbackClickThroughURL !== null && typeof fallbackClickThroughURL !== 'string' || _typeof(macros) !== 'object') { - return; - } - if (this.clickTrackingURLTemplates && this.clickTrackingURLTemplates.length) { - this.trackURLs(this.clickTrackingURLTemplates, macros); + /** + * Must be called when the user clicks on the creative. + * It calls the tracking URLs and emits a 'clickthrough' event with the resolved + * clickthrough URL when done. + * + * @param {?String} [fallbackClickThroughURL=null] - an optional clickThroughURL template that could be used as a fallback + * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. + * @emits VASTTracker#clickthrough + */ + click() { + let fallbackClickThroughURL = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null; + let macros = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + if (fallbackClickThroughURL !== null && typeof fallbackClickThroughURL !== 'string' || typeof macros !== 'object') { + this.emit('TRACKER-error', { + message: "One given click parameter has the wrong type. fallbackClickThroughURL: ".concat(fallbackClickThroughURL, ", macros: ").concat(util.formatMacrosValues(macros)) + }); + return; + } + if (this.clickTrackingURLTemplates && this.clickTrackingURLTemplates.length) { + this.trackURLs(this.clickTrackingURLTemplates, macros); + } + + // Use the provided fallbackClickThroughURL as a fallback + const clickThroughURLTemplate = this.clickThroughURLTemplate || fallbackClickThroughURL; + // clone second usage of macros, which get mutated inside resolveURLTemplates + const clonedMacros = { + ...macros + }; + if (clickThroughURLTemplate) { + if (this.progress) { + clonedMacros['ADPLAYHEAD'] = this.progressFormatted(); } + const clickThroughURL = util.resolveURLTemplates([clickThroughURLTemplate], clonedMacros)[0]; + this.emit('clickthrough', clickThroughURL); + } + } - // Use the provided fallbackClickThroughURL as a fallback - var clickThroughURLTemplate = this.clickThroughURLTemplate || fallbackClickThroughURL; - // clone second usage of macros, which get mutated inside resolveURLTemplates - var clonedMacros = _objectSpread2({}, macros); - if (clickThroughURLTemplate) { - if (this.progress) { - clonedMacros['ADPLAYHEAD'] = this.progressFormatted(); - } - var clickThroughURL = util.resolveURLTemplates([clickThroughURLTemplate], clonedMacros)[0]; - this.emit('clickthrough', clickThroughURL); + /** + * Calls the tracking URLs for the given eventName and emits the event. + * + * @param {String} eventName - The name of the event. + * @param {Object} options + * @param {Object} [options.macros={}] - An optional Object of parameters(vast macros) to be used in the tracking calls. + * @param {Boolean} [options.once=false] - Boolean to define if the event has to be tracked only once. + * + */ + track(eventName) { + let { + macros = {}, + once = false + } = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + if (typeof macros !== 'object') { + this.emit('TRACKER-error', { + message: "track given macros has the wrong type. macros: ".concat(macros) + }); + return; + } + // closeLinear event was introduced in VAST 3.0 + // Fallback to vast 2.0 close event if necessary + if (eventName === 'closeLinear' && !this.trackingEvents[eventName] && this.trackingEvents['close']) { + eventName = 'close'; + } + const trackingURLTemplates = this.trackingEvents[eventName]; + const isAlwaysEmitEvent = this.emitAlwaysEvents.indexOf(eventName) > -1; + if (trackingURLTemplates) { + this.emit(eventName, { + trackingURLTemplates + }); + this.trackURLs(trackingURLTemplates, macros); + } else if (isAlwaysEmitEvent) { + this.emit(eventName, null); + } + if (once) { + delete this.trackingEvents[eventName]; + if (isAlwaysEmitEvent) { + this.emitAlwaysEvents.splice(this.emitAlwaysEvents.indexOf(eventName), 1); } } + } - /** - * Calls the tracking URLs for the given eventName and emits the event. - * - * @param {String} eventName - The name of the event. - * @param {Object} options - * @param {Object} [options.macros={}] - An optional Object of parameters(vast macros) to be used in the tracking calls. - * @param {Boolean} [options.once=false] - Boolean to define if the event has to be tracked only once. - * - */ - }, { - key: "track", - value: function track(eventName) { - var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, - _ref$macros = _ref.macros, - macros = _ref$macros === void 0 ? {} : _ref$macros, - _ref$once = _ref.once, - once = _ref$once === void 0 ? false : _ref$once; - if (_typeof(macros) !== 'object') { - return; + /** + * Calls the tracking urls templates with the given macros . + * + * @param {Array} URLTemplates - An array of tracking url templates. + * @param {Object} [macros ={}] - An optional Object of parameters to be used in the tracking calls. + * @param {Object} [options={}] - An optional Object of options to be used in the tracking calls. + */ + trackURLs(URLTemplates) { + var _this$creative; + let macros = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; + const { + validUrls, + invalidUrls + } = util.filterUrlTemplates(URLTemplates); + if (invalidUrls.length) { + this.emit('TRACKER-error', { + message: "Provided urls are malformed. url: ".concat(invalidUrls) + }); + } + + //Avoid mutating the object received in parameters. + const givenMacros = { + ...macros + }; + if (this.linear) { + if (this.creative && this.creative.mediaFiles && this.creative.mediaFiles[0] && this.creative.mediaFiles[0].fileURL) { + givenMacros['ASSETURI'] = this.creative.mediaFiles[0].fileURL; } - // closeLinear event was introduced in VAST 3.0 - // Fallback to vast 2.0 close event if necessary - if (eventName === 'closeLinear' && !this.trackingEvents[eventName] && this.trackingEvents['close']) { - eventName = 'close'; + if (this.progress) { + givenMacros['ADPLAYHEAD'] = this.progressFormatted(); } - var trackingURLTemplates = this.trackingEvents[eventName]; - var isAlwaysEmitEvent = this.emitAlwaysEvents.indexOf(eventName) > -1; - if (trackingURLTemplates) { - this.emit(eventName, { - trackingURLTemplates: trackingURLTemplates - }); - this.trackURLs(trackingURLTemplates, macros); - } else if (isAlwaysEmitEvent) { - this.emit(eventName, null); + } + if ((_this$creative = this.creative) !== null && _this$creative !== void 0 && (_this$creative = _this$creative.universalAdIds) !== null && _this$creative !== void 0 && _this$creative.length) { + givenMacros['UNIVERSALADID'] = this.creative.universalAdIds.map(universalAdId => universalAdId.idRegistry.concat(' ', universalAdId.value)).join(','); + } + if (this.ad) { + if (this.ad.sequence) { + givenMacros['PODSEQUENCE'] = this.ad.sequence; } - if (once) { - delete this.trackingEvents[eventName]; - if (isAlwaysEmitEvent) { - this.emitAlwaysEvents.splice(this.emitAlwaysEvents.indexOf(eventName), 1); - } + if (this.ad.adType) { + givenMacros['ADTYPE'] = this.ad.adType; } - } - - /** - * Calls the tracking urls templates with the given macros . - * - * @param {Array} URLTemplates - An array of tracking url templates. - * @param {Object} [macros ={}] - An optional Object of parameters to be used in the tracking calls. - * @param {Object} [options={}] - An optional Object of options to be used in the tracking calls. - */ - }, { - key: "trackURLs", - value: function trackURLs(URLTemplates) { - var _this$creative; - var macros = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; - var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; - var validUrlTemplates = util.filterValidUrlTemplates(URLTemplates); - //Avoid mutating the object received in parameters. - var givenMacros = _objectSpread2({}, macros); - if (this.linear) { - if (this.creative && this.creative.mediaFiles && this.creative.mediaFiles[0] && this.creative.mediaFiles[0].fileURL) { - givenMacros['ASSETURI'] = this.creative.mediaFiles[0].fileURL; - } - if (this.progress) { - givenMacros['ADPLAYHEAD'] = this.progressFormatted(); - } + if (this.ad.adServingId) { + givenMacros['ADSERVINGID'] = this.ad.adServingId; } - if ((_this$creative = this.creative) !== null && _this$creative !== void 0 && (_this$creative = _this$creative.universalAdIds) !== null && _this$creative !== void 0 && _this$creative.length) { - givenMacros['UNIVERSALADID'] = this.creative.universalAdIds.map(function (universalAdId) { - return universalAdId.idRegistry.concat(' ', universalAdId.value); - }).join(','); + if (this.ad.categories && this.ad.categories.length) { + givenMacros['ADCATEGORIES'] = this.ad.categories.map(category => category.value).join(','); } - if (this.ad) { - if (this.ad.sequence) { - givenMacros['PODSEQUENCE'] = this.ad.sequence; - } - if (this.ad.adType) { - givenMacros['ADTYPE'] = this.ad.adType; - } - if (this.ad.adServingId) { - givenMacros['ADSERVINGID'] = this.ad.adServingId; - } - if (this.ad.categories && this.ad.categories.length) { - givenMacros['ADCATEGORIES'] = this.ad.categories.map(function (category) { - return category.value; - }).join(','); - } - if (this.ad.blockedAdCategories && this.ad.blockedAdCategories.length) { - givenMacros['BLOCKEDADCATEGORIES'] = this.ad.blockedAdCategories; - } + if (this.ad.blockedAdCategories && this.ad.blockedAdCategories.length) { + givenMacros['BLOCKEDADCATEGORIES'] = this.ad.blockedAdCategories.map(blockedCategorie => blockedCategorie.value).join(','); } - util.track(validUrlTemplates, givenMacros, options); } + util.track(validUrls, givenMacros, options); + } - /** - * Formats time in seconds to VAST timecode (e.g. 00:00:10.000) - * - * @param {Number} timeInSeconds - Number in seconds - * @return {String} - */ - }, { - key: "convertToTimecode", - value: function convertToTimecode(timeInSeconds) { - if (!util.isValidTimeValue(timeInSeconds)) { - return ''; - } - var progress = timeInSeconds * 1000; - var hours = Math.floor(progress / (60 * 60 * 1000)); - var minutes = Math.floor(progress / (60 * 1000) % 60); - var seconds = Math.floor(progress / 1000 % 60); - var milliseconds = Math.floor(progress % 1000); - return "".concat(util.addLeadingZeros(hours, 2), ":").concat(util.addLeadingZeros(minutes, 2), ":").concat(util.addLeadingZeros(seconds, 2), ".").concat(util.addLeadingZeros(milliseconds, 3)); + /** + * Formats time in seconds to VAST timecode (e.g. 00:00:10.000) + * + * @param {Number} timeInSeconds - Number in seconds + * @return {String} + */ + convertToTimecode(timeInSeconds) { + if (!util.isValidTimeValue(timeInSeconds)) { + return ''; } + const progress = timeInSeconds * 1000; + const hours = Math.floor(progress / (60 * 60 * 1000)); + const minutes = Math.floor(progress / (60 * 1000) % 60); + const seconds = Math.floor(progress / 1000 % 60); + const milliseconds = Math.floor(progress % 1000); + return "".concat(util.addLeadingZeros(hours, 2), ":").concat(util.addLeadingZeros(minutes, 2), ":").concat(util.addLeadingZeros(seconds, 2), ".").concat(util.addLeadingZeros(milliseconds, 3)); + } - /** - * Formats time progress in a readable string. - * - * @return {String} - */ - }, { - key: "progressFormatted", - value: function progressFormatted() { - return this.convertToTimecode(this.progress); - } - }]); - return VASTTracker; -}(EventEmitter); + /** + * Formats time progress in a readable string. + * + * @return {String} + */ + progressFormatted() { + return this.convertToTimecode(this.progress); + } +} exports.VASTClient = VASTClient; exports.VASTParser = VASTParser; diff --git a/dist/vast-client-node.min.js b/dist/vast-client-node.min.js index 7a878e00..e0facc1a 100644 --- a/dist/vast-client-node.min.js +++ b/dist/vast-client-node.min.js @@ -1 +1 @@ -"use strict";function e(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,i)}return r}function t(t){for(var r=1;re.length)&&(t=e.length);for(var r=0,i=new Array(t);r0&&void 0!==arguments[0]?arguments[0]:{};return{id:e.id||null,adId:e.adId||null,sequence:e.sequence||null,apiFramework:e.apiFramework||null,universalAdIds:[],creativeExtensions:[]}}Object.defineProperty(exports,"__esModule",{value:!0});var m=["ADCATEGORIES","ADCOUNT","ADPLAYHEAD","ADSERVINGID","ADTYPE","APIFRAMEWORKS","APPBUNDLE","ASSETURI","BLOCKEDADCATEGORIES","BREAKMAXADLENGTH","BREAKMAXADS","BREAKMAXDURATION","BREAKMINADLENGTH","BREAKMINDURATION","BREAKPOSITION","CLICKPOS","CLICKTYPE","CLIENTUA","CONTENTID","CONTENTPLAYHEAD","CONTENTURI","DEVICEIP","DEVICEUA","DOMAIN","EXTENSIONS","GDPRCONSENT","IFA","IFATYPE","INVENTORYSTATE","LATLONG","LIMITADTRACKING","MEDIAMIME","MEDIAPLAYHEAD","OMIDPARTNER","PAGEURL","PLACEMENTTYPE","PLAYERCAPABILITIES","PLAYERSIZE","PLAYERSTATE","PODSEQUENCE","REGULATIONS","SERVERSIDE","SERVERUA","TRANSACTIONID","UNIVERSALADID","VASTVERSIONS","VERIFICATIONVENDORS"];function g(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},i=[],n=k(e);for(var a in!t.ERRORCODE||r.isCustomCode||/^[0-9]{3}$/.test(t.ERRORCODE)||(t.ERRORCODE=900),t.CACHEBUSTING=N(Math.round(1e8*Math.random())),t.TIMESTAMP=(new Date).toISOString(),t.RANDOM=t.random=t.CACHEBUSTING,t)t[a]=E(t[a]);for(var o in n){var s=n[o];"string"==typeof s&&i.push(y(s,t))}return i}function y(e,t){var r=(e=T(e,t)).match(/[^[\]]+(?=])/g);if(!r)return e;var i=r.filter((function(e){return m.indexOf(e)>-1}));return 0===i.length?e:T(e,i=i.reduce((function(e,t){return e[t]=-1,e}),{}))}function T(e,t){var r=e;for(var i in t){var n=t[i];r=r.replace(new RegExp("(?:\\[|%%)(".concat(i,")(?:\\]|%%)"),"g"),n)}return r}function k(e){return Array.isArray(e)?e.map((function(e){return e&&e.hasOwnProperty("url")?e.url:e})):e}function A(e){return/^(https?:\/\/|\/\/)/.test(e)}function b(e,t){for(var r=0;r1&&void 0!==arguments[1]?arguments[1]:8;return e.toString().padStart(t,"0")}var L={track:function(e,t,r){g(e,t,r).forEach((function(e){"undefined"!=typeof window&&null!==window&&((new Image).src=e)}))},resolveURLTemplates:g,extractURLsFromTemplates:k,filterValidUrlTemplates:function(e){return Array.isArray(e)?e.filter((function(e){return A(e.hasOwnProperty("url")?e.url:e)})):A(e)},containsTemplateObject:b,isTemplateObjectEqual:R,encodeURIComponentRFC3986:E,replaceUrlMacros:y,isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},flatten:function e(t){return t.reduce((function(t,r){return t.concat(Array.isArray(r)?e(r):r)}),[])},joinArrayOfUniqueTemplateObjs:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[],t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:[],r=Array.isArray(e)?e:[],i=Array.isArray(t)?t:[];return r.concat(i).reduce((function(e,t){return b(t,e)||e.push(t),e}),[])},isValidTimeValue:function(e){return Number.isFinite(e)&&e>=-2},addLeadingZeros:N};function w(e){return-1!==["true","TRUE","True","1"].indexOf(e)}function U(e){if(null==e)return-1;if(L.isNumeric(e))return parseInt(e);var t=e.split(":");if(3!==t.length)return-1;var r=t[2].split("."),i=parseInt(r[0]);2===r.length&&(i+=parseFloat("0.".concat(r[1])));var n=parseInt(60*t[1]),a=parseInt(60*t[0]*60);return isNaN(a)||isNaN(n)||isNaN(i)||n>3600||i>60?-1:a+n+i}var C={childByName:function(e,t){var r=e.childNodes;for(var i in r){var n=r[i];if(n.nodeName===t)return n}},childrenByName:function(e,t){var r=[],i=e.childNodes;for(var n in i){var a=i[n];a.nodeName===t&&r.push(a)}return r},resolveVastAdTagURI:function(e,t){if(!t)return e;if(0===e.indexOf("//")){var r=location.protocol;return"".concat(r).concat(e)}if(-1===e.indexOf("://")){var i=t.slice(0,t.lastIndexOf("/"));return"".concat(i,"/").concat(e)}return e},parseBoolean:w,parseNodeText:function(e){return e&&(e.textContent||e.text||"").trim()},copyNodeAttribute:function(e,t,r){var i=t.getAttribute(e);i&&r.setAttribute(e,i)},parseAttributes:function(e){for(var t=e.attributes,r={},i=0;i1){var a=e[n-1];if(a&&a.sequence===i.sequence-1)return void(r&&r.push(i));delete i.sequence}r=[i],t.push(r)})),t},assignAttributes:function(e,t){if(e)for(var r in e){var i=e[r];if(i.nodeName&&i.nodeValue&&t.hasOwnProperty(i.nodeName)){var n=i.nodeValue;"boolean"==typeof t[i.nodeName]&&(n=w(n)),t[i.nodeName]=n}}},mergeWrapperAdData:function(e,t){var r;e.errorURLTemplates=t.errorURLTemplates.concat(e.errorURLTemplates),e.impressionURLTemplates=t.impressionURLTemplates.concat(e.impressionURLTemplates),e.extensions=t.extensions.concat(e.extensions),t.viewableImpression.length>0&&(e.viewableImpression=[].concat(p(e.viewableImpression),p(t.viewableImpression))),e.followAdditionalWrappers=t.followAdditionalWrappers,e.allowMultipleAds=t.allowMultipleAds,e.fallbackOnNoAd=t.fallbackOnNoAd;var i=(t.creatives||[]).filter((function(e){return e&&"companion"===e.type})),n=i.reduce((function(e,t){return(t.variations||[]).forEach((function(t){(t.companionClickTrackingURLTemplates||[]).forEach((function(t){L.containsTemplateObject(t,e)||e.push(t)}))})),e}),[]);e.creatives=i.concat(e.creatives);var a=t.videoClickTrackingURLTemplates&&t.videoClickTrackingURLTemplates.length,o=t.videoCustomClickURLTemplates&&t.videoCustomClickURLTemplates.length;if(e.creatives.forEach((function(e){if(t.trackingEvents&&t.trackingEvents[e.type])for(var r in t.trackingEvents[e.type]){var i=t.trackingEvents[e.type][r];Array.isArray(e.trackingEvents[r])||(e.trackingEvents[r]=[]),e.trackingEvents[r]=e.trackingEvents[r].concat(i)}"linear"===e.type&&(a&&(e.videoClickTrackingURLTemplates=e.videoClickTrackingURLTemplates.concat(t.videoClickTrackingURLTemplates)),o&&(e.videoCustomClickURLTemplates=e.videoCustomClickURLTemplates.concat(t.videoCustomClickURLTemplates)),!t.videoClickThroughURLTemplate||null!==e.videoClickThroughURLTemplate&&void 0!==e.videoClickThroughURLTemplate||(e.videoClickThroughURLTemplate=t.videoClickThroughURLTemplate)),"companion"===e.type&&n.length&&(e.variations||[]).forEach((function(e){e.companionClickTrackingURLTemplates=L.joinArrayOfUniqueTemplateObjs(e.companionClickTrackingURLTemplates,n)}))})),t.adVerifications&&(e.adVerifications=e.adVerifications.concat(t.adVerifications)),t.blockedAdCategories&&(e.blockedAdCategories=e.blockedAdCategories.concat(t.blockedAdCategories)),null!==(r=t.creatives)&&void 0!==r&&r.length){var s=t.creatives.filter((function(e){var t;return(null===(t=e.icons)||void 0===t?void 0:t.length)&&!e.mediaFiles.length}));s.length&&(e.creatives=e.creatives.concat(s))}}};function I(e,t){var r=function(){var e=f(arguments.length>0&&void 0!==arguments[0]?arguments[0]:{});return{id:e.id,adId:e.adId,sequence:e.sequence,apiFramework:e.apiFramework,type:"companion",required:null,variations:[]}}(t);return r.required=e.getAttribute("required")||null,r.variations=C.childrenByName(e,"Companion").map((function(e){var t=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return{id:e.id||null,adType:"companionAd",width:e.width||0,height:e.height||0,assetWidth:e.assetWidth||null,assetHeight:e.assetHeight||null,expandedWidth:e.expandedWidth||null,expandedHeight:e.expandedHeight||null,apiFramework:e.apiFramework||null,adSlotId:e.adSlotId||null,pxratio:e.pxratio||"1",renderingMode:e.renderingMode||"default",staticResources:[],htmlResources:[],iframeResources:[],adParameters:null,altText:null,companionClickThroughURLTemplate:null,companionClickTrackingURLTemplates:[],trackingEvents:{}}}(C.parseAttributes(e));t.htmlResources=C.childrenByName(e,"HTMLResource").reduce((function(e,t){var r=C.parseNodeText(t);return r?e.concat(r):e}),[]),t.iframeResources=C.childrenByName(e,"IFrameResource").reduce((function(e,t){var r=C.parseNodeText(t);return r?e.concat(r):e}),[]),t.staticResources=C.childrenByName(e,"StaticResource").reduce((function(e,t){var r=C.parseNodeText(t);return r?e.concat({url:r,creativeType:t.getAttribute("creativeType")||null}):e}),[]),t.altText=C.parseNodeText(C.childByName(e,"AltText"))||null;var r=C.childByName(e,"TrackingEvents");r&&C.childrenByName(r,"Tracking").forEach((function(e){var r=e.getAttribute("event"),i=C.parseNodeText(e);r&&i&&(Array.isArray(t.trackingEvents[r])||(t.trackingEvents[r]=[]),t.trackingEvents[r].push(i))})),t.companionClickTrackingURLTemplates=C.childrenByName(e,"CompanionClickTracking").map((function(e){return{id:e.getAttribute("id")||null,url:C.parseNodeText(e)}})),t.companionClickThroughURLTemplate=C.parseNodeText(C.childByName(e,"CompanionClickThrough"))||null;var i=C.childByName(e,"AdParameters");return i&&(t.adParameters={value:C.parseNodeText(i),xmlEncoded:i.getAttribute("xmlEncoded")||null}),t})),r}function x(e,t){var r,i=function(){var e=f(arguments.length>0&&void 0!==arguments[0]?arguments[0]:{});return{id:e.id,adId:e.adId,sequence:e.sequence,apiFramework:e.apiFramework,type:"linear",duration:0,skipDelay:null,mediaFiles:[],mezzanine:null,interactiveCreativeFile:null,closedCaptionFiles:[],videoClickThroughURLTemplate:null,videoClickTrackingURLTemplates:[],videoCustomClickURLTemplates:[],adParameters:null,icons:[],trackingEvents:{}}}(t);i.duration=C.parseDuration(C.parseNodeText(C.childByName(e,"Duration")));var n=e.getAttribute("skipoffset");if(null==n)i.skipDelay=null;else if("%"===n.charAt(n.length-1)&&-1!==i.duration){var a=parseInt(n,10);i.skipDelay=i.duration*(a/100)}else i.skipDelay=C.parseDuration(n);var o=C.childByName(e,"VideoClicks");if(o){var s=C.childByName(o,"ClickThrough");i.videoClickThroughURLTemplate=s?{id:s.getAttribute("id")||null,url:C.parseNodeText(s)}:null,C.childrenByName(o,"ClickTracking").forEach((function(e){i.videoClickTrackingURLTemplates.push({id:e.getAttribute("id")||null,url:C.parseNodeText(e)})})),C.childrenByName(o,"CustomClick").forEach((function(e){i.videoCustomClickURLTemplates.push({id:e.getAttribute("id")||null,url:C.parseNodeText(e)})}))}var l=C.childByName(e,"AdParameters");l&&(i.adParameters={value:C.parseNodeText(l),xmlEncoded:l.getAttribute("xmlEncoded")||null}),C.childrenByName(e,"TrackingEvents").forEach((function(e){C.childrenByName(e,"Tracking").forEach((function(e){var t=e.getAttribute("event"),n=C.parseNodeText(e);if(t&&n){if("progress"===t){if(!(r=e.getAttribute("offset")))return;t="%"===r.charAt(r.length-1)?"progress-".concat(r):"progress-".concat(Math.round(C.parseDuration(r)))}Array.isArray(i.trackingEvents[t])||(i.trackingEvents[t]=[]),i.trackingEvents[t].push(n)}}))})),C.childrenByName(e,"MediaFiles").forEach((function(e){C.childrenByName(e,"MediaFile").forEach((function(e){i.mediaFiles.push(function(e){var t={id:null,fileURL:null,fileSize:0,deliveryType:"progressive",mimeType:null,mediaType:null,codec:null,bitrate:0,minBitrate:0,maxBitrate:0,width:0,height:0,apiFramework:null,scalable:null,maintainAspectRatio:null};t.id=e.getAttribute("id"),t.fileURL=C.parseNodeText(e),t.deliveryType=e.getAttribute("delivery"),t.codec=e.getAttribute("codec"),t.mimeType=e.getAttribute("type"),t.mediaType=e.getAttribute("mediaType")||"2D",t.apiFramework=e.getAttribute("apiFramework"),t.fileSize=parseInt(e.getAttribute("fileSize")||0),t.bitrate=parseInt(e.getAttribute("bitrate")||0),t.minBitrate=parseInt(e.getAttribute("minBitrate")||0),t.maxBitrate=parseInt(e.getAttribute("maxBitrate")||0),t.width=parseInt(e.getAttribute("width")||0),t.height=parseInt(e.getAttribute("height")||0);var r=e.getAttribute("scalable");r&&"string"==typeof r&&(t.scalable=C.parseBoolean(r));var i=e.getAttribute("maintainAspectRatio");i&&"string"==typeof i&&(t.maintainAspectRatio=C.parseBoolean(i));return t}(e))}));var t=C.childByName(e,"InteractiveCreativeFile");t&&(i.interactiveCreativeFile=function(e){var t=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return{type:e.type||null,apiFramework:e.apiFramework||null,variableDuration:C.parseBoolean(e.variableDuration),fileURL:null}}(C.parseAttributes(e));return t.fileURL=C.parseNodeText(e),t}(t));var r=C.childByName(e,"ClosedCaptionFiles");r&&C.childrenByName(r,"ClosedCaptionFile").forEach((function(e){var t=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return{type:e.type||null,language:e.language||null,fileURL:null}}(C.parseAttributes(e));t.fileURL=C.parseNodeText(e),i.closedCaptionFiles.push(t)}));var n,a,o,s=C.childByName(e,"Mezzanine"),l=(n=s,a={},o=!1,["delivery","type","width","height"].forEach((function(e){n&&n.getAttribute(e)?a[e]=n.getAttribute(e):o=!0})),o?null:a);if(l){var u={id:null,fileURL:null,delivery:null,codec:null,type:null,width:0,height:0,fileSize:0,mediaType:"2D"};u.id=s.getAttribute("id"),u.fileURL=C.parseNodeText(s),u.delivery=l.delivery,u.codec=s.getAttribute("codec"),u.type=l.type,u.width=parseInt(l.width,10),u.height=parseInt(l.height,10),u.fileSize=parseInt(s.getAttribute("fileSize"),10),u.mediaType=s.getAttribute("mediaType")||"2D",i.mezzanine=u}}));var u=C.childByName(e,"Icons");return u&&C.childrenByName(u,"Icon").forEach((function(e){i.icons.push(function(e){var t={program:null,height:0,width:0,xPosition:0,yPosition:0,apiFramework:null,offset:null,duration:0,type:null,staticResource:null,htmlResource:null,iframeResource:null,pxratio:"1",iconClickThroughURLTemplate:null,iconClickTrackingURLTemplates:[],iconViewTrackingURLTemplate:null,iconClickFallbackImages:[]};t.program=e.getAttribute("program"),t.height=parseInt(e.getAttribute("height")||0),t.width=parseInt(e.getAttribute("width")||0),t.xPosition=function(e){if(-1!==["left","right"].indexOf(e))return e;return parseInt(e||0)}(e.getAttribute("xPosition")),t.yPosition=function(e){if(-1!==["top","bottom"].indexOf(e))return e;return parseInt(e||0)}(e.getAttribute("yPosition")),t.apiFramework=e.getAttribute("apiFramework"),t.pxratio=e.getAttribute("pxratio")||"1",t.offset=C.parseDuration(e.getAttribute("offset")),t.duration=C.parseDuration(e.getAttribute("duration")),C.childrenByName(e,"HTMLResource").forEach((function(e){t.type=e.getAttribute("creativeType")||"text/html",t.htmlResource=C.parseNodeText(e)})),C.childrenByName(e,"IFrameResource").forEach((function(e){t.type=e.getAttribute("creativeType")||0,t.iframeResource=C.parseNodeText(e)})),C.childrenByName(e,"StaticResource").forEach((function(e){t.type=e.getAttribute("creativeType")||0,t.staticResource=C.parseNodeText(e)}));var r=C.childByName(e,"IconClicks");if(r){t.iconClickThroughURLTemplate=C.parseNodeText(C.childByName(r,"IconClickThrough")),C.childrenByName(r,"IconClickTracking").forEach((function(e){t.iconClickTrackingURLTemplates.push({id:e.getAttribute("id")||null,url:C.parseNodeText(e)})}));var i=C.childByName(r,"IconClickFallbackImages");i&&C.childrenByName(i,"IconClickFallbackImage").forEach((function(e){t.iconClickFallbackImages.push({url:C.parseNodeText(e)||null,width:e.getAttribute("width")||null,height:e.getAttribute("height")||null})}))}return t.iconViewTrackingURLTemplate=C.parseNodeText(C.childByName(e,"IconViewTracking")),t}(e))})),i}function S(e,t){var r=function(){var e=f(arguments.length>0&&void 0!==arguments[0]?arguments[0]:{});return{id:e.id,adId:e.adId,sequence:e.sequence,apiFramework:e.apiFramework,type:"nonlinear",variations:[],trackingEvents:{}}}(t);return C.childrenByName(e,"TrackingEvents").forEach((function(e){var t,i;C.childrenByName(e,"Tracking").forEach((function(e){t=e.getAttribute("event"),i=C.parseNodeText(e),t&&i&&(Array.isArray(r.trackingEvents[t])||(r.trackingEvents[t]=[]),r.trackingEvents[t].push(i))}))})),C.childrenByName(e,"NonLinear").forEach((function(e){var t={id:null,width:0,height:0,expandedWidth:0,expandedHeight:0,scalable:!0,maintainAspectRatio:!0,minSuggestedDuration:0,apiFramework:"static",adType:"nonLinearAd",type:null,staticResource:null,htmlResource:null,iframeResource:null,nonlinearClickThroughURLTemplate:null,nonlinearClickTrackingURLTemplates:[],adParameters:null};t.id=e.getAttribute("id")||null,t.width=e.getAttribute("width"),t.height=e.getAttribute("height"),t.expandedWidth=e.getAttribute("expandedWidth"),t.expandedHeight=e.getAttribute("expandedHeight"),t.scalable=C.parseBoolean(e.getAttribute("scalable")),t.maintainAspectRatio=C.parseBoolean(e.getAttribute("maintainAspectRatio")),t.minSuggestedDuration=C.parseDuration(e.getAttribute("minSuggestedDuration")),t.apiFramework=e.getAttribute("apiFramework"),C.childrenByName(e,"HTMLResource").forEach((function(e){t.type=e.getAttribute("creativeType")||"text/html",t.htmlResource=C.parseNodeText(e)})),C.childrenByName(e,"IFrameResource").forEach((function(e){t.type=e.getAttribute("creativeType")||0,t.iframeResource=C.parseNodeText(e)})),C.childrenByName(e,"StaticResource").forEach((function(e){t.type=e.getAttribute("creativeType")||0,t.staticResource=C.parseNodeText(e)}));var i=C.childByName(e,"AdParameters");i&&(t.adParameters={value:C.parseNodeText(i),xmlEncoded:i.getAttribute("xmlEncoded")||null}),t.nonlinearClickThroughURLTemplate=C.parseNodeText(C.childByName(e,"NonLinearClickThrough")),C.childrenByName(e,"NonLinearClickTracking").forEach((function(e){t.nonlinearClickTrackingURLTemplates.push({id:e.getAttribute("id")||null,url:C.parseNodeText(e)})})),r.variations.push(t)})),r}function O(e){var t=[];return e.forEach((function(e){var r=D(e);r&&t.push(r)})),t}function D(e){if("#comment"===e.nodeName)return null;var t,r={name:null,value:null,attributes:{},children:[]},i=e.attributes,n=e.childNodes;if(r.name=e.nodeName,e.attributes)for(var a in i)if(i.hasOwnProperty(a)){var o=i[a];o.nodeName&&o.nodeValue&&(r.attributes[o.nodeName]=o.nodeValue)}for(var s in n)if(n.hasOwnProperty(s)){var l=D(n[s]);l&&r.children.push(l)}if(0===r.children.length||1===r.children.length&&["#cdata-section","#text"].indexOf(r.children[0].name)>=0){var u=C.parseNodeText(e);""!==u&&(r.value=u),r.children=[]}return null===(t=r).value&&0===Object.keys(t.attributes).length&&0===t.children.length?null:r}function V(e){var t=[];return e.forEach((function(e){var r,i={id:e.getAttribute("id")||null,adId:P(e),sequence:e.getAttribute("sequence")||null,apiFramework:e.getAttribute("apiFramework")||null},n=[];C.childrenByName(e,"UniversalAdId").forEach((function(e){var t={idRegistry:e.getAttribute("idRegistry")||"unknown",value:C.parseNodeText(e)};n.push(t)}));var a=C.childByName(e,"CreativeExtensions");for(var o in a&&(r=O(C.childrenByName(a,"CreativeExtension"))),e.childNodes){var s=e.childNodes[o],l=void 0;switch(s.nodeName){case"Linear":l=x(s,i);break;case"NonLinearAds":l=S(s,i);break;case"CompanionAds":l=I(s,i)}l&&(n&&(l.universalAdIds=n),r&&(l.creativeExtensions=r),t.push(l))}})),t}function P(e){return e.getAttribute("AdID")||e.getAttribute("adID")||e.getAttribute("adId")||null}var B={Wrapper:{subElements:["VASTAdTagURI","Impression"]},BlockedAdCategories:{attributes:["authority"]},InLine:{subElements:["AdSystem","AdTitle","Impression","AdServingId","Creatives"]},Category:{attributes:["authority"]},Pricing:{attributes:["model","currency"]},Verification:{oneOfinLineResources:["JavaScriptResource","ExecutableResource"],attributes:["vendor"]},UniversalAdId:{attributes:["idRegistry"]},JavaScriptResource:{attributes:["apiFramework","browserOptional"]},ExecutableResource:{attributes:["apiFramework","type"]},Tracking:{attributes:["event"]},Creatives:{subElements:["Creative"]},Creative:{subElements:["UniversalAdId"]},Linear:{subElements:["MediaFiles","Duration"]},MediaFiles:{subElements:["MediaFile"]},MediaFile:{attributes:["delivery","type","width","height"]},Mezzanine:{attributes:["delivery","type","width","height"]},NonLinear:{oneOfinLineResources:["StaticResource","IFrameResource","HTMLResource"],attributes:["width","height"]},Companion:{oneOfinLineResources:["StaticResource","IFrameResource","HTMLResource"],attributes:["width","height"]},StaticResource:{attributes:["creativeType"]},Icons:{subElements:["Icon"]},Icon:{oneOfinLineResources:["StaticResource","IFrameResource","HTMLResource"]}};function F(e,t){if(B[e.nodeName]&&B[e.nodeName].attributes){var r=B[e.nodeName].attributes.filter((function(t){return!e.getAttribute(t)}));r.length>0&&W({name:e.nodeName,parentName:e.parentNode.nodeName,attributes:r},t)}}function M(e,t,r){var i=B[e.nodeName],n=!r&&"Wrapper"!==e.nodeName;if(i&&!n){if(i.subElements){var a=i.subElements.filter((function(t){return!C.childByName(e,t)}));a.length>0&&W({name:e.nodeName,parentName:e.parentNode.nodeName,subElements:a},t)}if(r&&i.oneOfinLineResources)i.oneOfinLineResources.some((function(t){return C.childByName(e,t)}))||W({name:e.nodeName,parentName:e.parentNode.nodeName,oneOfResources:i.oneOfinLineResources},t)}}function j(e){return e.children&&0!==e.children.length}function W(e,t){var r=e.name,i=e.parentName,n=e.attributes,a=e.subElements,o=e.oneOfResources,s="Element '".concat(r,"'");t("VAST-warning",{message:s+=n?" missing required attribute(s) '".concat(n.join(", "),"' "):a?" missing required sub element(s) '".concat(a.join(", "),"' "):o?" must provide one of the following '".concat(o.join(", "),"' "):" is empty",parentElement:i,specVersion:4.1})}var q={verifyRequiredValues:function e(t,r,i){if(t&&t.nodeName)if("InLine"===t.nodeName&&(i=!0),F(t,r),j(t)){M(t,r,i);for(var n=0;n2&&void 0!==arguments[2]?arguments[2]:{},i=r.allowMultipleAds,n=r.followAdditionalWrappers,a=e.childNodes;for(var o in a){var s=a[o];if(-1!==["Wrapper","InLine"].indexOf(s.nodeName)&&("Wrapper"!==s.nodeName||!1!==n)){if(C.copyNodeAttribute("id",e,s),C.copyNodeAttribute("sequence",e,s),C.copyNodeAttribute("adType",e,s),"Wrapper"===s.nodeName)return{ad:z(s,t),type:"WRAPPER"};if("InLine"===s.nodeName)return{ad:_(s,t,{allowMultipleAds:i}),type:"INLINE"}}}}function _(e,t){return!1===(arguments.length>2&&void 0!==arguments[2]?arguments[2]:{}).allowMultipleAds&&e.getAttribute("sequence")?null:G(e,t)}function G(e,t){var r=[];t&&q.verifyRequiredValues(e,t);var i,n,a=e.childNodes,o=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return{id:e.id||null,sequence:e.sequence||null,adType:e.adType||null,adServingId:null,categories:[],expires:null,viewableImpression:[],system:null,title:null,description:null,advertiser:null,pricing:null,survey:null,errorURLTemplates:[],impressionURLTemplates:[],creatives:[],extensions:[],adVerifications:[],blockedAdCategories:[],followAdditionalWrappers:!0,allowMultipleAds:!1,fallbackOnNoAd:null}}(C.parseAttributes(e));for(var s in a){var l=a[s];switch(l.nodeName){case"Error":o.errorURLTemplates.push(C.parseNodeText(l));break;case"Impression":o.impressionURLTemplates.push({id:l.getAttribute("id")||null,url:C.parseNodeText(l)});break;case"Creatives":o.creatives=V(C.childrenByName(l,"Creative"));break;case"Extensions":var u=C.childrenByName(l,"Extension");o.extensions=O(u),o.adVerifications.length||(r=X(u));break;case"AdVerifications":o.adVerifications=Y(C.childrenByName(l,"Verification"));break;case"AdSystem":o.system={value:C.parseNodeText(l),version:l.getAttribute("version")||null};break;case"AdTitle":o.title=C.parseNodeText(l);break;case"AdServingId":o.adServingId=C.parseNodeText(l);break;case"Category":o.categories.push({authority:l.getAttribute("authority")||null,value:C.parseNodeText(l)});break;case"Expires":o.expires=parseInt(C.parseNodeText(l),10);break;case"ViewableImpression":o.viewableImpression.push((n=void 0,n=function(e,t){var r=C.parseNodeText(t);return r&&e.push(r),e},{id:(i=l).getAttribute("id")||null,viewable:C.childrenByName(i,"Viewable").reduce(n,[]),notViewable:C.childrenByName(i,"NotViewable").reduce(n,[]),viewUndetermined:C.childrenByName(i,"ViewUndetermined").reduce(n,[])}));break;case"Description":o.description=C.parseNodeText(l);break;case"Advertiser":o.advertiser={id:l.getAttribute("id")||null,value:C.parseNodeText(l)};break;case"Pricing":o.pricing={value:C.parseNodeText(l),model:l.getAttribute("model")||null,currency:l.getAttribute("currency")||null};break;case"Survey":o.survey={value:C.parseNodeText(l),type:l.getAttribute("type")||null};break;case"BlockedAdCategories":o.blockedAdCategories.push({authority:l.getAttribute("authority")||null,value:C.parseNodeText(l)})}}return r.length&&(o.adVerifications=o.adVerifications.concat(r)),o}function z(e,t){var r=G(e,t),i=e.getAttribute("followAdditionalWrappers"),n=e.getAttribute("allowMultipleAds"),a=e.getAttribute("fallbackOnNoAd");r.followAdditionalWrappers=!i||C.parseBoolean(i),r.allowMultipleAds=!!n&&C.parseBoolean(n),r.fallbackOnNoAd=a?C.parseBoolean(a):null;var o=C.childByName(e,"VASTAdTagURI");if(o?r.nextWrapperURL=C.parseNodeText(o):(o=C.childByName(e,"VASTAdTagURL"))&&(r.nextWrapperURL=C.parseNodeText(C.childByName(o,"URL"))),r.creatives.forEach((function(e){if(-1!==["linear","nonlinear"].indexOf(e.type)){if(e.trackingEvents){r.trackingEvents||(r.trackingEvents={}),r.trackingEvents[e.type]||(r.trackingEvents[e.type]={});var t=function(t){var i=e.trackingEvents[t];Array.isArray(r.trackingEvents[e.type][t])||(r.trackingEvents[e.type][t]=[]),i.forEach((function(i){r.trackingEvents[e.type][t].push(i)}))};for(var i in e.trackingEvents)t(i)}e.videoClickTrackingURLTemplates&&(Array.isArray(r.videoClickTrackingURLTemplates)||(r.videoClickTrackingURLTemplates=[]),e.videoClickTrackingURLTemplates.forEach((function(e){r.videoClickTrackingURLTemplates.push(e)}))),e.videoClickThroughURLTemplate&&(r.videoClickThroughURLTemplate=e.videoClickThroughURLTemplate),e.videoCustomClickURLTemplates&&(Array.isArray(r.videoCustomClickURLTemplates)||(r.videoCustomClickURLTemplates=[]),e.videoCustomClickURLTemplates.forEach((function(e){r.videoCustomClickURLTemplates.push(e)})))}})),r.nextWrapperURL)return r}function Y(e){var t=[];return e.forEach((function(e){var r={resource:null,vendor:null,browserOptional:!1,apiFramework:null,type:null,parameters:null,trackingEvents:{}},i=e.childNodes;for(var n in C.assignAttributes(e.attributes,r),i){var a=i[n];switch(a.nodeName){case"JavaScriptResource":case"ExecutableResource":r.resource=C.parseNodeText(a),C.assignAttributes(a.attributes,r);break;case"VerificationParameters":r.parameters=C.parseNodeText(a)}}var o=C.childByName(e,"TrackingEvents");o&&C.childrenByName(o,"Tracking").forEach((function(e){var t=e.getAttribute("event"),i=C.parseNodeText(e);t&&i&&(Array.isArray(r.trackingEvents[t])||(r.trackingEvents[t]=[]),r.trackingEvents[t].push(i))})),t.push(r)})),t}function X(e){var t=null,r=[];return e.some((function(e){return t=C.childByName(e,"AdVerifications")})),t&&(r=Y(C.childrenByName(t,"Verification"))),r}var K=function(){function e(){i(this,e),this._handlers=[]}return a(e,[{key:"on",value:function(e,t){if("function"!=typeof t)throw new TypeError("The handler argument must be of type Function. Received type ".concat(r(t)));if(!e)throw new TypeError("The event argument must be of type String. Received type ".concat(r(e)));return this._handlers.push({event:e,handler:t}),this}},{key:"once",value:function(e,t){return this.on(e,function(e,t,r){var i={fired:!1,wrapFn:void 0};function n(){i.fired||(e.off(t,i.wrapFn),i.fired=!0,r.bind(e).apply(void 0,arguments))}return i.wrapFn=n,n}(this,e,t))}},{key:"off",value:function(e,t){return this._handlers=this._handlers.filter((function(r){return r.event!==e||r.handler!==t})),this}},{key:"emit",value:function(e){for(var t=arguments.length,r=new Array(t>1?t-1:0),i=1;i2?r-2:0),n=2;n1&&void 0!==arguments[1]?arguments[1]:0,i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null,n=arguments.length>3&&void 0!==arguments[3]?arguments[3]:null;return new Promise((function(a,o){t.URLTemplateFilters.forEach((function(t){e=t(e)}));var s=Date.now();t.emit("VAST-resolving",{url:e,previousUrl:i,wrapperDepth:r,maxWrapperDepth:t.maxWrapperDepth,timeout:t.fetchingOptions.timeout,wrapperAd:n}),t.urlHandler.get(e,t.fetchingOptions,(function(n,l){var u=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},c=Math.round(Date.now()-s),d=Object.assign({url:e,previousUrl:i,wrapperDepth:r,error:n,duration:c},u);t.emit("VAST-resolved",d),ue(u.byteLength,c),n?o(n):a(l)}))}))}},{key:"initParsingStatus",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};this.errorURLTemplates=[],this.fetchingOptions={timeout:e.timeout||Q,withCredentials:e.withCredentials},this.maxWrapperDepth=e.wrapperLimit||10,this.parsingOptions={allowMultipleAds:e.allowMultipleAds},this.remainingAds=[],this.rootErrorURLTemplates=[],this.rootURL="",this.urlHandler=e.urlHandler||e.urlhandler||oe,this.vastVersion=null,ue(e.byteLength,e.requestDuration)}},{key:"getRemainingAds",value:function(e){var t=this;if(0===this.remainingAds.length)return Promise.reject(new Error("No more ads are available for the given VAST"));var r=e?L.flatten(this.remainingAds):this.remainingAds.shift();return this.errorURLTemplates=[],this.resolveAds(r,{wrapperDepth:0,url:this.rootURL}).then((function(e){return t.buildVASTResponse(e)}))}},{key:"getAndParseVAST",value:function(e){var t=this,r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return this.initParsingStatus(r),this.URLTemplateFilters.forEach((function(t){e=t(e)})),this.rootURL=e,this.fetchVAST(e).then((function(i){return r.previousUrl=e,r.isRootVAST=!0,r.url=e,t.parse(i,r).then((function(e){return t.buildVASTResponse(e)}))}))}},{key:"parseVAST",value:function(e){var t=this,r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return this.initParsingStatus(r),r.isRootVAST=!0,this.parse(e,r).then((function(e){return t.buildVASTResponse(e)}))}},{key:"buildVASTResponse",value:function(e){var t,r={ads:(t={ads:e,errorURLTemplates:this.getErrorURLTemplates(),version:this.vastVersion}).ads||[],errorURLTemplates:t.errorURLTemplates||[],version:t.version||null};return this.completeWrapperResolving(r),r}},{key:"parseVastXml",value:function(e,t){var r=t.isRootVAST,i=void 0!==r&&r,n=t.url,a=void 0===n?null:n,o=t.wrapperDepth,s=void 0===o?0:o,l=t.allowMultipleAds,u=t.followAdditionalWrappers;if(!e||!e.documentElement||"VAST"!==e.documentElement.nodeName)throw this.emit("VAST-ad-parsed",{type:"ERROR",url:a,wrapperDepth:s}),new Error("Invalid VAST XMLDocument");var c=[],d=e.documentElement.childNodes,p=e.documentElement.getAttribute("version");for(var h in i&&p&&(this.vastVersion=p),d){var v=d[h];if("Error"===v.nodeName){var f=C.parseNodeText(v);i?this.rootErrorURLTemplates.push(f):this.errorURLTemplates.push(f)}else if("Ad"===v.nodeName){if(this.vastVersion&&parseFloat(this.vastVersion)<3)l=!0;else if(!1===l&&c.length>1)break;var m=H(v,this.emit.bind(this),{allowMultipleAds:l,followAdditionalWrappers:u});m.ad?(c.push(m.ad),this.emit("VAST-ad-parsed",{type:m.type,url:a,wrapperDepth:s,adIndex:c.length-1,vastVersion:p})):this.trackVastError(this.getErrorURLTemplates(),{ERRORCODE:101})}}return c}},{key:"parse",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},r=t.url,i=void 0===r?null:r,n=t.resolveAll,a=void 0===n||n,o=t.wrapperSequence,s=void 0===o?null:o,l=t.previousUrl,u=void 0===l?null:l,c=t.wrapperDepth,d=void 0===c?0:c,p=t.isRootVAST,h=void 0!==p&&p,v=t.followAdditionalWrappers,f=t.allowMultipleAds,m=[];this.vastVersion&&parseFloat(this.vastVersion)<3&&h&&(f=!0);try{m=this.parseVastXml(e,{isRootVAST:h,url:i,wrapperDepth:d,allowMultipleAds:f,followAdditionalWrappers:v})}catch(e){return Promise.reject(e)}return 1===m.length&&null!=s&&(m[0].sequence=s),!1===a&&(this.remainingAds=C.splitVAST(m),m=this.remainingAds.shift()),this.resolveAds(m,{wrapperDepth:d,previousUrl:u,url:i})}},{key:"resolveAds",value:function(){var e=this,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[],r=arguments.length>1?arguments[1]:void 0,i=r.wrapperDepth,n=r.previousUrl,a=r.url,o=[];return n=a,t.forEach((function(t){var r=e.resolveWrappers(t,i,n);o.push(r)})),Promise.all(o).then((function(t){var r=L.flatten(t);if(!r&&e.remainingAds.length>0){var o=e.remainingAds.shift();return e.resolveAds(o,{wrapperDepth:i,previousUrl:n,url:a})}return r}))}},{key:"resolveWrappers",value:function(e,t,r){var i=this;return new Promise((function(n){var a;if(t++,!e.nextWrapperURL)return delete e.nextWrapperURL,n(e);if(t>=i.maxWrapperDepth)return e.errorCode=302,delete e.nextWrapperURL,n(e);e.nextWrapperURL=C.resolveVastAdTagURI(e.nextWrapperURL,r),i.URLTemplateFilters.forEach((function(t){e.nextWrapperURL=t(e.nextWrapperURL)}));var o=null!==(a=i.parsingOptions.allowMultipleAds)&&void 0!==a?a:e.allowMultipleAds,s=e.sequence;i.fetchVAST(e.nextWrapperURL,t,r,e).then((function(a){return i.parse(a,{url:e.nextWrapperURL,previousUrl:r,wrapperSequence:s,wrapperDepth:t,followAdditionalWrappers:e.followAdditionalWrappers,allowMultipleAds:o}).then((function(t){if(delete e.nextWrapperURL,0===t.length)return e.creatives=[],n(e);t.forEach((function(t){t&&C.mergeWrapperAdData(t,e)})),n(t)}))})).catch((function(t){e.errorCode=301,e.errorMessage=t.message,n(e)}))}))}},{key:"completeWrapperResolving",value:function(e){if(0===e.ads.length)this.trackVastError(e.errorURLTemplates,{ERRORCODE:303});else for(var t=e.ads.length-1;t>=0;t--){var r=e.ads[t];(r.errorCode||0===r.creatives.length)&&(this.trackVastError(r.errorURLTemplates.concat(e.errorURLTemplates),{ERRORCODE:r.errorCode||303},{ERRORMESSAGE:r.errorMessage||""},{extensions:r.extensions},{system:r.system}),e.ads.splice(t,1))}}}]),r}(),pe=null,he={data:{},length:0,getItem:function(e){return this.data[e]},setItem:function(e,t){this.data[e]=t,this.length=Object.keys(this.data).length},removeItem:function(e){delete this.data[e],this.length=Object.keys(this.data).length},clear:function(){this.data={},this.length=0}},ve=function(){function e(){i(this,e),this.storage=this.initStorage()}return a(e,[{key:"initStorage",value:function(){if(pe)return pe;try{pe="undefined"!=typeof window&&null!==window?window.localStorage||window.sessionStorage:null}catch(e){pe=null}return pe&&!this.isStorageDisabled(pe)||(pe=he).clear(),pe}},{key:"isStorageDisabled",value:function(e){var t="__VASTStorage__";try{if(e.setItem(t,t),e.getItem(t)!==t)return e.removeItem(t),!0}catch(e){return!0}return e.removeItem(t),!1}},{key:"getItem",value:function(e){return this.storage.getItem(e)}},{key:"setItem",value:function(e,t){return this.storage.setItem(e,t)}},{key:"removeItem",value:function(e){return this.storage.removeItem(e)}},{key:"clear",value:function(){return this.storage.clear()}}]),e}(),fe=function(){function e(t,r,n){i(this,e),this.cappingFreeLunch=t||0,this.cappingMinimumTimeInterval=r||0,this.defaultOptions={withCredentials:!1,timeout:0},this.vastParser=new de,this.storage=n||new ve,void 0===this.lastSuccessfulAd&&(this.lastSuccessfulAd=0),void 0===this.totalCalls&&(this.totalCalls=0),void 0===this.totalCallsTimeout&&(this.totalCallsTimeout=0)}return a(e,[{key:"getParser",value:function(){return this.vastParser}},{key:"lastSuccessfulAd",get:function(){return this.storage.getItem("vast-client-last-successful-ad")},set:function(e){this.storage.setItem("vast-client-last-successful-ad",e)}},{key:"totalCalls",get:function(){return this.storage.getItem("vast-client-total-calls")},set:function(e){this.storage.setItem("vast-client-total-calls",e)}},{key:"totalCallsTimeout",get:function(){return this.storage.getItem("vast-client-total-calls-timeout")},set:function(e){this.storage.setItem("vast-client-total-calls-timeout",e)}},{key:"hasRemainingAds",value:function(){return this.vastParser.remainingAds.length>0}},{key:"getNextAds",value:function(e){return this.vastParser.getRemainingAds(e)}},{key:"get",value:function(e){var t=this,r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},i=Date.now();return(r=Object.assign({},this.defaultOptions,r)).hasOwnProperty("resolveAll")||(r.resolveAll=!1),this.totalCallsTimeout=t.totalCalls)return a(new Error("VAST call canceled – FreeLunch capping not reached yet ".concat(t.totalCalls,"/").concat(t.cappingFreeLunch)));var o=i-t.lastSuccessfulAd;if(o<0)t.lastSuccessfulAd=0;else if(o3&&void 0!==arguments[3]?arguments[3]:null,l=arguments.length>4&&void 0!==arguments[4]&&arguments[4];for(var u in i(this,o),(a=n.call(this)).ad=t,a.creative=r,a.variation=s,a.muted=l,a.impressed=!1,a.skippable=!1,a.trackingEvents={},a.lastPercentage=0,a._alreadyTriggeredQuartiles={},a.emitAlwaysEvents=["creativeView","start","firstQuartile","midpoint","thirdQuartile","complete","resume","pause","rewind","skip","closeLinear","close"],a.creative.trackingEvents){var c=a.creative.trackingEvents[u];a.trackingEvents[u]=c.slice(0)}return!function(e){return"linear"===e.type}(a.creative)?a._initVariationTracking():a._initLinearTracking(),e&&a.on("start",(function(){e.lastSuccessfulAd=Date.now()})),a}return a(o,[{key:"_initLinearTracking",value:function(){this.linear=!0,this.skipDelay=this.creative.skipDelay,this.setDuration(this.creative.duration),this.clickThroughURLTemplate=this.creative.videoClickThroughURLTemplate,this.clickTrackingURLTemplates=this.creative.videoClickTrackingURLTemplates}},{key:"_initVariationTracking",value:function(){if(this.linear=!1,this.skipDelay=-1,this.variation){for(var e in this.variation.trackingEvents){var t=this.variation.trackingEvents[e];this.trackingEvents[e]?this.trackingEvents[e]=this.trackingEvents[e].concat(t.slice(0)):this.trackingEvents[e]=t.slice(0)}"nonLinearAd"===this.variation.adType?(this.clickThroughURLTemplate=this.variation.nonlinearClickThroughURLTemplate,this.clickTrackingURLTemplates=this.variation.nonlinearClickTrackingURLTemplates,this.setDuration(this.variation.minSuggestedDuration)):function(e){return"companionAd"===e.adType}(this.variation)&&(this.clickThroughURLTemplate=this.variation.companionClickThroughURLTemplate,this.clickTrackingURLTemplates=this.variation.companionClickTrackingURLTemplates)}}},{key:"setDuration",value:function(e){L.isValidTimeValue(e)&&(this.assetDuration=e,this.quartiles={firstQuartile:Math.round(25*this.assetDuration)/100,midpoint:Math.round(50*this.assetDuration)/100,thirdQuartile:Math.round(75*this.assetDuration)/100})}},{key:"setProgress",value:function(e){var t=this,i=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if(L.isValidTimeValue(e)&&"object"===r(i)){var n=this.skipDelay||-1;if(-1===n||this.skippable||(n>e?this.emit("skip-countdown",n-e):(this.skippable=!0,this.emit("skip-countdown",0))),this.assetDuration>0){var a=Math.round(e/this.assetDuration*100),o=[];if(e>0){o.push("start");for(var s=this.lastPercentage;s1&&void 0!==arguments[1]?arguments[1]:{};"boolean"==typeof e&&"object"===r(t)&&(this.muted!==e&&this.track(e?"mute":"unmute",{macros:t}),this.muted=e)}},{key:"setPaused",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};"boolean"==typeof e&&"object"===r(t)&&(this.paused!==e&&this.track(e?"pause":"resume",{macros:t}),this.paused=e)}},{key:"setFullscreen",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};"boolean"==typeof e&&"object"===r(t)&&(this.fullscreen!==e&&this.track(e?"fullscreen":"exitFullscreen",{macros:t}),this.fullscreen=e)}},{key:"setExpand",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};"boolean"==typeof e&&"object"===r(t)&&(this.expanded!==e&&(this.track(e?"expand":"collapse",{macros:t}),this.track(e?"playerExpand":"playerCollapse",{macros:t})),this.expanded=e)}},{key:"setSkipDelay",value:function(e){L.isValidTimeValue(e)&&(this.skipDelay=e)}},{key:"trackImpression",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};"object"===r(e)&&(this.impressed||(this.impressed=!0,this.trackURLs(this.ad.impressionURLTemplates,e),this.track("creativeView",{macros:e})))}},{key:"trackViewableImpression",value:function(){var e=this,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};"object"===r(t)&&this.ad.viewableImpression.forEach((function(r){e.trackURLs(r.viewable,t)}))}},{key:"trackNotViewableImpression",value:function(){var e=this,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};"object"===r(t)&&this.ad.viewableImpression.forEach((function(r){e.trackURLs(r.notViewable,t)}))}},{key:"trackUndeterminedImpression",value:function(){var e=this,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};"object"===r(t)&&this.ad.viewableImpression.forEach((function(r){e.trackURLs(r.viewUndetermined,t)}))}},{key:"error",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];"object"===r(e)&&"boolean"==typeof t&&this.trackURLs(this.ad.errorURLTemplates,e,{isCustomCode:t})}},{key:"errorWithCode",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];"string"==typeof e&&"boolean"==typeof t&&(this.error({ERRORCODE:e},t),console.log("The method errorWithCode is deprecated, please use vast tracker error method instead"))}},{key:"complete",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};"object"===r(e)&&this.track("complete",{macros:e})}},{key:"notUsed",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};"object"===r(e)&&(this.track("notUsed",{macros:e}),this.trackingEvents=[])}},{key:"otherAdInteraction",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};"object"===r(e)&&this.track("otherAdInteraction",{macros:e})}},{key:"acceptInvitation",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};"object"===r(e)&&this.track("acceptInvitation",{macros:e})}},{key:"adExpand",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};"object"===r(e)&&this.track("adExpand",{macros:e})}},{key:"adCollapse",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};"object"===r(e)&&this.track("adCollapse",{macros:e})}},{key:"minimize",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};"object"===r(e)&&this.track("minimize",{macros:e})}},{key:"verificationNotExecuted",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if("string"==typeof e&&"object"===r(t)){if(!this.ad||!this.ad.adVerifications||!this.ad.adVerifications.length)throw new Error("No adVerifications provided");if(!e)throw new Error("No vendor provided, unable to find associated verificationNotExecuted");var i=this.ad.adVerifications.find((function(t){return t.vendor===e}));if(!i)throw new Error("No associated verification element found for vendor: ".concat(e));var n=i.trackingEvents;if(n&&n.verificationNotExecuted){var a=n.verificationNotExecuted;this.trackURLs(a,t),this.emit("verificationNotExecuted",{trackingURLTemplates:a})}}}},{key:"overlayViewDuration",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};"string"==typeof e&&"object"===r(t)&&(t.ADPLAYHEAD=e,this.track("overlayViewDuration",{macros:t}))}},{key:"close",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};"object"===r(e)&&this.track(this.linear?"closeLinear":"close",{macros:e})}},{key:"skip",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};"object"===r(e)&&this.track("skip",{macros:e})}},{key:"load",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};"object"===r(e)&&this.track("loaded",{macros:e})}},{key:"click",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null,i=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if((null===e||"string"==typeof e)&&"object"===r(i)){this.clickTrackingURLTemplates&&this.clickTrackingURLTemplates.length&&this.trackURLs(this.clickTrackingURLTemplates,i);var n=this.clickThroughURLTemplate||e,a=t({},i);if(n){this.progress&&(a.ADPLAYHEAD=this.progressFormatted());var o=L.resolveURLTemplates([n],a)[0];this.emit("clickthrough",o)}}}},{key:"track",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},i=t.macros,n=void 0===i?{}:i,a=t.once,o=void 0!==a&&a;if("object"===r(n)){"closeLinear"===e&&!this.trackingEvents[e]&&this.trackingEvents.close&&(e="close");var s=this.trackingEvents[e],l=this.emitAlwaysEvents.indexOf(e)>-1;s?(this.emit(e,{trackingURLTemplates:s}),this.trackURLs(s,n)):l&&this.emit(e,null),o&&(delete this.trackingEvents[e],l&&this.emitAlwaysEvents.splice(this.emitAlwaysEvents.indexOf(e),1))}}},{key:"trackURLs",value:function(e){var r,i=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},a=L.filterValidUrlTemplates(e),o=t({},i);this.linear&&(this.creative&&this.creative.mediaFiles&&this.creative.mediaFiles[0]&&this.creative.mediaFiles[0].fileURL&&(o.ASSETURI=this.creative.mediaFiles[0].fileURL),this.progress&&(o.ADPLAYHEAD=this.progressFormatted())),null!==(r=this.creative)&&void 0!==r&&null!==(r=r.universalAdIds)&&void 0!==r&&r.length&&(o.UNIVERSALADID=this.creative.universalAdIds.map((function(e){return e.idRegistry.concat(" ",e.value)})).join(",")),this.ad&&(this.ad.sequence&&(o.PODSEQUENCE=this.ad.sequence),this.ad.adType&&(o.ADTYPE=this.ad.adType),this.ad.adServingId&&(o.ADSERVINGID=this.ad.adServingId),this.ad.categories&&this.ad.categories.length&&(o.ADCATEGORIES=this.ad.categories.map((function(e){return e.value})).join(",")),this.ad.blockedAdCategories&&this.ad.blockedAdCategories.length&&(o.BLOCKEDADCATEGORIES=this.ad.blockedAdCategories)),L.track(a,o,n)}},{key:"convertToTimecode",value:function(e){if(!L.isValidTimeValue(e))return"";var t=1e3*e,r=Math.floor(t/36e5),i=Math.floor(t/6e4%60),n=Math.floor(t/1e3%60),a=Math.floor(t%1e3);return"".concat(L.addLeadingZeros(r,2),":").concat(L.addLeadingZeros(i,2),":").concat(L.addLeadingZeros(n,2),".").concat(L.addLeadingZeros(a,3))}},{key:"progressFormatted",value:function(){return this.convertToTimecode(this.progress)}}]),o}();exports.VASTClient=fe,exports.VASTParser=de,exports.VASTTracker=me,exports.parseDuration=U; +"use strict";function e(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return{id:e.id||null,adId:e.adId||null,sequence:e.sequence||null,apiFramework:e.apiFramework||null,universalAdIds:[],creativeExtensions:[]}}Object.defineProperty(exports,"__esModule",{value:!0});const t=["ADCATEGORIES","ADCOUNT","ADPLAYHEAD","ADSERVINGID","ADTYPE","APIFRAMEWORKS","APPBUNDLE","ASSETURI","BLOCKEDADCATEGORIES","BREAKMAXADLENGTH","BREAKMAXADS","BREAKMAXDURATION","BREAKMINADLENGTH","BREAKMINDURATION","BREAKPOSITION","CLICKPOS","CLICKTYPE","CLIENTUA","CONTENTID","CONTENTPLAYHEAD","CONTENTURI","DEVICEIP","DEVICEUA","DOMAIN","EXTENSIONS","GDPRCONSENT","IFA","IFATYPE","INVENTORYSTATE","LATLONG","LIMITADTRACKING","MEDIAMIME","MEDIAPLAYHEAD","OMIDPARTNER","PAGEURL","PLACEMENTTYPE","PLAYERCAPABILITIES","PLAYERSIZE","PLAYERSTATE","PODSEQUENCE","REGULATIONS","SERVERSIDE","SERVERUA","TRANSACTIONID","UNIVERSALADID","VASTVERSIONS","VERIFICATIONVENDORS"];function r(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};const a=[],n=s(e);!t.ERRORCODE||r.isCustomCode||/^[0-9]{3}$/.test(t.ERRORCODE)||(t.ERRORCODE=900),t.CACHEBUSTING=u(Math.round(1e8*Math.random())),t.TIMESTAMP=(new Date).toISOString(),t.RANDOM=t.random=t.CACHEBUSTING;for(const e in t)t[e]=c(t[e]);for(const e in n){const r=n[e];"string"==typeof r&&a.push(i(r,t))}return a}function i(e,r){const i=(e=a(e,r)).match(/[^[\]]+(?=])/g);if(!i)return e;let s=i.filter((e=>t.indexOf(e)>-1));return 0===s.length?e:(s=s.reduce(((e,t)=>(e[t]=-1,e)),{}),a(e,s))}function a(e,t){let r=e;for(const e in t){const i=t[e];r=r.replace(new RegExp("(?:\\[|%%)(".concat(e,")(?:\\]|%%)"),"g"),i)}return r}function s(e){return Array.isArray(e)?e.map((e=>e&&e.hasOwnProperty("url")?e.url:e)):e}function n(e){return/^(https?:\/\/|\/\/)/.test(e)}function o(e,t){for(let r=0;r"%".concat(e.charCodeAt(0).toString(16))))}function u(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:8;return e.toString().padStart(t,"0")}const d={track:function(e,t,i){r(e,t,i).forEach((e=>{if("undefined"!=typeof window&&null!==window){(new Image).src=e}}))},resolveURLTemplates:r,extractURLsFromTemplates:s,filterUrlTemplates:function(e){return e.reduce(((e,t)=>{const r=t.url||t;return n(r)?e.validUrls.push(r):e.invalidUrls.push(r),e}),{validUrls:[],invalidUrls:[]})},containsTemplateObject:o,isTemplateObjectEqual:l,encodeURIComponentRFC3986:c,replaceUrlMacros:i,isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},flatten:function e(t){return t.reduce(((t,r)=>t.concat(Array.isArray(r)?e(r):r)),[])},joinArrayOfUniqueTemplateObjs:function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[],t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:[];const r=Array.isArray(e)?e:[],i=Array.isArray(t)?t:[];return r.concat(i).reduce(((e,t)=>(o(t,e)||e.push(t),e)),[])},isValidTimeValue:function(e){return Number.isFinite(e)&&e>=-2},addLeadingZeros:u,isValidUrl:n,isBrowserEnvironment:function(){return"undefined"!=typeof window},formatMacrosValues:function(e){return"object"!=typeof e?e:JSON.stringify(e)}};function h(e){return["true","TRUE","True","1"].includes(e)}function p(e){if(null==e)return-1;if(d.isNumeric(e))return parseInt(e);const t=e.split(":");if(3!==t.length)return-1;const r=t[2].split(".");let i=parseInt(r[0]);2===r.length&&(i+=parseFloat("0.".concat(r[1])));const a=parseInt(60*t[1]),s=parseInt(60*t[0]*60);return isNaN(s)||isNaN(a)||isNaN(i)||a>3600||i>60?-1:s+a+i}const m={childByName:function(e,t){return Array.from(e.childNodes).find((e=>e.nodeName===t))},childrenByName:function(e,t){return Array.from(e.childNodes).filter((e=>e.nodeName===t))},resolveVastAdTagURI:function(e,t){if(!t)return e;if(e.startsWith("//")){const{protocol:t}=location;return"".concat(t).concat(e)}if(!e.includes("://")){const r=t.slice(0,t.lastIndexOf("/"));return"".concat(r,"/").concat(e)}return e},parseBoolean:h,parseNodeText:function(e){return e&&(e.textContent||e.text||"").trim()},copyNodeAttribute:function(e,t,r){const i=t.getAttribute(e);i&&r.setAttribute(e,i)},parseAttributes:function(e){return Array.from(e.attributes).reduce(((e,t)=>(e[t.nodeName]=t.nodeValue,e)),{})},parseDuration:p,splitVAST:function(e){const t=[];let r=null;return e.forEach(((i,a)=>{if(i.sequence&&(i.sequence=parseInt(i.sequence,10)),i.sequence>1){const t=e[a-1];if(t&&t.sequence===i.sequence-1)return void(r&&r.push(i));delete i.sequence}r=[i],t.push(r)})),t},assignAttributes:function(e,t){e&&Array.from(e).forEach((e=>{let{nodeName:r,nodeValue:i}=e;if(r&&i&&t.hasOwnProperty(r)){let e=i;"boolean"==typeof t[r]&&(e=h(e)),t[r]=e}}))},mergeWrapperAdData:function(e,t){var r;e.errorURLTemplates=t.errorURLTemplates.concat(e.errorURLTemplates),e.impressionURLTemplates=t.impressionURLTemplates.concat(e.impressionURLTemplates),e.extensions=t.extensions.concat(e.extensions),t.viewableImpression.length>0&&(e.viewableImpression=[...e.viewableImpression,...t.viewableImpression]),e.followAdditionalWrappers=t.followAdditionalWrappers,e.allowMultipleAds=t.allowMultipleAds,e.fallbackOnNoAd=t.fallbackOnNoAd;const i=(t.creatives||[]).filter((e=>e&&"companion"===e.type)),a=i.reduce(((e,t)=>((t.variations||[]).forEach((t=>{(t.companionClickTrackingURLTemplates||[]).forEach((t=>{d.containsTemplateObject(t,e)||e.push(t)}))})),e)),[]);e.creatives=i.concat(e.creatives);const s=t.videoClickTrackingURLTemplates&&t.videoClickTrackingURLTemplates.length,n=t.videoCustomClickURLTemplates&&t.videoCustomClickURLTemplates.length;if(e.creatives.forEach((e=>{if(t.trackingEvents&&t.trackingEvents[e.type])for(const r in t.trackingEvents[e.type]){const i=t.trackingEvents[e.type][r];Array.isArray(e.trackingEvents[r])||(e.trackingEvents[r]=[]),e.trackingEvents[r]=e.trackingEvents[r].concat(i)}"linear"===e.type&&(s&&(e.videoClickTrackingURLTemplates=e.videoClickTrackingURLTemplates.concat(t.videoClickTrackingURLTemplates)),n&&(e.videoCustomClickURLTemplates=e.videoCustomClickURLTemplates.concat(t.videoCustomClickURLTemplates)),!t.videoClickThroughURLTemplate||null!==e.videoClickThroughURLTemplate&&void 0!==e.videoClickThroughURLTemplate||(e.videoClickThroughURLTemplate=t.videoClickThroughURLTemplate)),"companion"===e.type&&a.length&&(e.variations||[]).forEach((e=>{e.companionClickTrackingURLTemplates=d.joinArrayOfUniqueTemplateObjs(e.companionClickTrackingURLTemplates,a)}))})),t.adVerifications&&(e.adVerifications=e.adVerifications.concat(t.adVerifications)),t.blockedAdCategories&&(e.blockedAdCategories=e.blockedAdCategories.concat(t.blockedAdCategories)),null!==(r=t.creatives)&&void 0!==r&&r.length){const r=t.creatives.filter((e=>{var t;return(null===(t=e.icons)||void 0===t?void 0:t.length)&&!e.mediaFiles.length}));r.length&&(e.creatives=e.creatives.concat(r))}}};function g(t,r){const i=function(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};const{id:r,adId:i,sequence:a,apiFramework:s}=e(t);return{id:r,adId:i,sequence:a,apiFramework:s,type:"companion",required:null,variations:[]}}(r);return i.required=t.getAttribute("required")||null,i.variations=m.childrenByName(t,"Companion").map((e=>{const t=function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return{id:e.id||null,adType:"companionAd",width:e.width||0,height:e.height||0,assetWidth:e.assetWidth||null,assetHeight:e.assetHeight||null,expandedWidth:e.expandedWidth||null,expandedHeight:e.expandedHeight||null,apiFramework:e.apiFramework||null,adSlotId:e.adSlotId||null,pxratio:e.pxratio||"1",renderingMode:e.renderingMode||"default",staticResources:[],htmlResources:[],iframeResources:[],adParameters:null,altText:null,companionClickThroughURLTemplate:null,companionClickTrackingURLTemplates:[],trackingEvents:{}}}(m.parseAttributes(e));t.htmlResources=m.childrenByName(e,"HTMLResource").reduce(((e,t)=>{const r=m.parseNodeText(t);return r?e.concat(r):e}),[]),t.iframeResources=m.childrenByName(e,"IFrameResource").reduce(((e,t)=>{const r=m.parseNodeText(t);return r?e.concat(r):e}),[]),t.staticResources=m.childrenByName(e,"StaticResource").reduce(((e,t)=>{const r=m.parseNodeText(t);return r?e.concat({url:r,creativeType:t.getAttribute("creativeType")||null}):e}),[]),t.altText=m.parseNodeText(m.childByName(e,"AltText"))||null;const r=m.childByName(e,"TrackingEvents");r&&m.childrenByName(r,"Tracking").forEach((e=>{const r=e.getAttribute("event"),i=m.parseNodeText(e);r&&i&&(Array.isArray(t.trackingEvents[r])||(t.trackingEvents[r]=[]),t.trackingEvents[r].push(i))})),t.companionClickTrackingURLTemplates=m.childrenByName(e,"CompanionClickTracking").map((e=>({id:e.getAttribute("id")||null,url:m.parseNodeText(e)}))),t.companionClickThroughURLTemplate=m.parseNodeText(m.childByName(e,"CompanionClickThrough"))||null;const i=m.childByName(e,"AdParameters");return i&&(t.adParameters={value:m.parseNodeText(i),xmlEncoded:i.getAttribute("xmlEncoded")||null}),t})),i}function v(t,r){let i;const a=function(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};const{id:r,adId:i,sequence:a,apiFramework:s}=e(t);return{id:r,adId:i,sequence:a,apiFramework:s,type:"linear",duration:0,skipDelay:null,mediaFiles:[],mezzanine:null,interactiveCreativeFile:null,closedCaptionFiles:[],videoClickThroughURLTemplate:null,videoClickTrackingURLTemplates:[],videoCustomClickURLTemplates:[],adParameters:null,icons:[],trackingEvents:{}}}(r);a.duration=m.parseDuration(m.parseNodeText(m.childByName(t,"Duration")));const s=t.getAttribute("skipoffset");if(null==s)a.skipDelay=null;else if("%"===s.charAt(s.length-1)&&-1!==a.duration){const e=parseInt(s,10);a.skipDelay=a.duration*(e/100)}else a.skipDelay=m.parseDuration(s);const n=m.childByName(t,"VideoClicks");if(n){const e=m.childByName(n,"ClickThrough");a.videoClickThroughURLTemplate=e?{id:e.getAttribute("id")||null,url:m.parseNodeText(e)}:null,m.childrenByName(n,"ClickTracking").forEach((e=>{a.videoClickTrackingURLTemplates.push({id:e.getAttribute("id")||null,url:m.parseNodeText(e)})})),m.childrenByName(n,"CustomClick").forEach((e=>{a.videoCustomClickURLTemplates.push({id:e.getAttribute("id")||null,url:m.parseNodeText(e)})}))}const o=m.childByName(t,"AdParameters");o&&(a.adParameters={value:m.parseNodeText(o),xmlEncoded:o.getAttribute("xmlEncoded")||null}),m.childrenByName(t,"TrackingEvents").forEach((e=>{m.childrenByName(e,"Tracking").forEach((e=>{let t=e.getAttribute("event");const r=m.parseNodeText(e);if(t&&r){if("progress"===t){if(i=e.getAttribute("offset"),!i)return;t="%"===i.charAt(i.length-1)?"progress-".concat(i):"progress-".concat(Math.round(m.parseDuration(i)))}Array.isArray(a.trackingEvents[t])||(a.trackingEvents[t]=[]),a.trackingEvents[t].push(r)}}))})),m.childrenByName(t,"MediaFiles").forEach((e=>{m.childrenByName(e,"MediaFile").forEach((e=>{a.mediaFiles.push(function(e){const t={id:null,fileURL:null,fileSize:0,deliveryType:"progressive",mimeType:null,mediaType:null,codec:null,bitrate:0,minBitrate:0,maxBitrate:0,width:0,height:0,apiFramework:null,scalable:null,maintainAspectRatio:null};t.id=e.getAttribute("id"),t.fileURL=m.parseNodeText(e),t.deliveryType=e.getAttribute("delivery"),t.codec=e.getAttribute("codec"),t.mimeType=e.getAttribute("type"),t.mediaType=e.getAttribute("mediaType")||"2D",t.apiFramework=e.getAttribute("apiFramework"),t.fileSize=parseInt(e.getAttribute("fileSize")||0),t.bitrate=parseInt(e.getAttribute("bitrate")||0),t.minBitrate=parseInt(e.getAttribute("minBitrate")||0),t.maxBitrate=parseInt(e.getAttribute("maxBitrate")||0),t.width=parseInt(e.getAttribute("width")||0),t.height=parseInt(e.getAttribute("height")||0);const r=e.getAttribute("scalable");r&&"string"==typeof r&&(t.scalable=m.parseBoolean(r));const i=e.getAttribute("maintainAspectRatio");i&&"string"==typeof i&&(t.maintainAspectRatio=m.parseBoolean(i));return t}(e))}));const t=m.childByName(e,"InteractiveCreativeFile");t&&(a.interactiveCreativeFile=function(e){const t=function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return{type:e.type||null,apiFramework:e.apiFramework||null,variableDuration:m.parseBoolean(e.variableDuration),fileURL:null}}(m.parseAttributes(e));return t.fileURL=m.parseNodeText(e),t}(t));const r=m.childByName(e,"ClosedCaptionFiles");r&&m.childrenByName(r,"ClosedCaptionFile").forEach((e=>{const t=function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return{type:e.type||null,language:e.language||null,fileURL:null}}(m.parseAttributes(e));t.fileURL=m.parseNodeText(e),a.closedCaptionFiles.push(t)}));const i=m.childByName(e,"Mezzanine"),s=function(e,t){const r={};let i=!1;return t.forEach((t=>{e&&e.getAttribute(t)?r[t]=e.getAttribute(t):i=!0})),i?null:r}(i,["delivery","type","width","height"]);if(s){const e={id:null,fileURL:null,delivery:null,codec:null,type:null,width:0,height:0,fileSize:0,mediaType:"2D"};e.id=i.getAttribute("id"),e.fileURL=m.parseNodeText(i),e.delivery=s.delivery,e.codec=i.getAttribute("codec"),e.type=s.type,e.width=parseInt(s.width,10),e.height=parseInt(s.height,10),e.fileSize=parseInt(i.getAttribute("fileSize"),10),e.mediaType=i.getAttribute("mediaType")||"2D",a.mezzanine=e}}));const l=m.childByName(t,"Icons");return l&&m.childrenByName(l,"Icon").forEach((e=>{a.icons.push(function(e){const t={program:null,height:0,width:0,xPosition:0,yPosition:0,apiFramework:null,offset:null,duration:0,type:null,staticResource:null,htmlResource:null,iframeResource:null,pxratio:"1",iconClickThroughURLTemplate:null,iconClickTrackingURLTemplates:[],iconViewTrackingURLTemplate:null,iconClickFallbackImages:[]};t.program=e.getAttribute("program"),t.height=parseInt(e.getAttribute("height")||0),t.width=parseInt(e.getAttribute("width")||0),t.xPosition=function(e){if(-1!==["left","right"].indexOf(e))return e;return parseInt(e||0)}(e.getAttribute("xPosition")),t.yPosition=function(e){if(-1!==["top","bottom"].indexOf(e))return e;return parseInt(e||0)}(e.getAttribute("yPosition")),t.apiFramework=e.getAttribute("apiFramework"),t.pxratio=e.getAttribute("pxratio")||"1",t.offset=m.parseDuration(e.getAttribute("offset")),t.duration=m.parseDuration(e.getAttribute("duration")),m.childrenByName(e,"HTMLResource").forEach((e=>{t.type=e.getAttribute("creativeType")||"text/html",t.htmlResource=m.parseNodeText(e)})),m.childrenByName(e,"IFrameResource").forEach((e=>{t.type=e.getAttribute("creativeType")||0,t.iframeResource=m.parseNodeText(e)})),m.childrenByName(e,"StaticResource").forEach((e=>{t.type=e.getAttribute("creativeType")||0,t.staticResource=m.parseNodeText(e)}));const r=m.childByName(e,"IconClicks");if(r){t.iconClickThroughURLTemplate=m.parseNodeText(m.childByName(r,"IconClickThrough")),m.childrenByName(r,"IconClickTracking").forEach((e=>{t.iconClickTrackingURLTemplates.push({id:e.getAttribute("id")||null,url:m.parseNodeText(e)})}));const e=m.childByName(r,"IconClickFallbackImages");e&&m.childrenByName(e,"IconClickFallbackImage").forEach((e=>{t.iconClickFallbackImages.push({url:m.parseNodeText(e)||null,width:e.getAttribute("width")||null,height:e.getAttribute("height")||null})}))}return t.iconViewTrackingURLTemplate=m.parseNodeText(m.childByName(e,"IconViewTracking")),t}(e))})),a}function f(t,r){const i=function(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};const{id:r,adId:i,sequence:a,apiFramework:s}=e(t);return{id:r,adId:i,sequence:a,apiFramework:s,type:"nonlinear",variations:[],trackingEvents:{}}}(r);return m.childrenByName(t,"TrackingEvents").forEach((e=>{let t,r;m.childrenByName(e,"Tracking").forEach((e=>{t=e.getAttribute("event"),r=m.parseNodeText(e),t&&r&&(Array.isArray(i.trackingEvents[t])||(i.trackingEvents[t]=[]),i.trackingEvents[t].push(r))}))})),m.childrenByName(t,"NonLinear").forEach((e=>{const t={id:null,width:0,height:0,expandedWidth:0,expandedHeight:0,scalable:!0,maintainAspectRatio:!0,minSuggestedDuration:0,apiFramework:"static",adType:"nonLinearAd",type:null,staticResource:null,htmlResource:null,iframeResource:null,nonlinearClickThroughURLTemplate:null,nonlinearClickTrackingURLTemplates:[],adParameters:null};t.id=e.getAttribute("id")||null,t.width=e.getAttribute("width"),t.height=e.getAttribute("height"),t.expandedWidth=e.getAttribute("expandedWidth"),t.expandedHeight=e.getAttribute("expandedHeight"),t.scalable=m.parseBoolean(e.getAttribute("scalable")),t.maintainAspectRatio=m.parseBoolean(e.getAttribute("maintainAspectRatio")),t.minSuggestedDuration=m.parseDuration(e.getAttribute("minSuggestedDuration")),t.apiFramework=e.getAttribute("apiFramework"),m.childrenByName(e,"HTMLResource").forEach((e=>{t.type=e.getAttribute("creativeType")||"text/html",t.htmlResource=m.parseNodeText(e)})),m.childrenByName(e,"IFrameResource").forEach((e=>{t.type=e.getAttribute("creativeType")||0,t.iframeResource=m.parseNodeText(e)})),m.childrenByName(e,"StaticResource").forEach((e=>{t.type=e.getAttribute("creativeType")||0,t.staticResource=m.parseNodeText(e)}));const r=m.childByName(e,"AdParameters");r&&(t.adParameters={value:m.parseNodeText(r),xmlEncoded:r.getAttribute("xmlEncoded")||null}),t.nonlinearClickThroughURLTemplate=m.parseNodeText(m.childByName(e,"NonLinearClickThrough")),m.childrenByName(e,"NonLinearClickTracking").forEach((e=>{t.nonlinearClickTrackingURLTemplates.push({id:e.getAttribute("id")||null,url:m.parseNodeText(e)})})),i.variations.push(t)})),i}function T(e){const t=[];return e.forEach((e=>{const r=A(e);r&&t.push(r)})),t}function A(e){if("#comment"===e.nodeName)return null;const t={name:null,value:null,attributes:{},children:[]},r=e.attributes,i=e.childNodes;if(t.name=e.nodeName,e.attributes)for(const e in r)if(r.hasOwnProperty(e)){const i=r[e];i.nodeName&&i.nodeValue&&(t.attributes[i.nodeName]=i.nodeValue)}for(const e in i)if(i.hasOwnProperty(e)){const r=A(i[e]);r&&t.children.push(r)}if(0===t.children.length||1===t.children.length&&["#cdata-section","#text"].indexOf(t.children[0].name)>=0){const r=m.parseNodeText(e);""!==r&&(t.value=r),t.children=[]}return null===(a=t).value&&0===Object.keys(a.attributes).length&&0===a.children.length?null:t;var a}function R(e){return e.getAttribute("AdID")||e.getAttribute("adID")||e.getAttribute("adId")||null}const y={Wrapper:{subElements:["VASTAdTagURI","Impression"]},BlockedAdCategories:{attributes:["authority"]},InLine:{subElements:["AdSystem","AdTitle","Impression","AdServingId","Creatives"]},Category:{attributes:["authority"]},Pricing:{attributes:["model","currency"]},Verification:{oneOfinLineResources:["JavaScriptResource","ExecutableResource"],attributes:["vendor"]},UniversalAdId:{attributes:["idRegistry"]},JavaScriptResource:{attributes:["apiFramework","browserOptional"]},ExecutableResource:{attributes:["apiFramework","type"]},Tracking:{attributes:["event"]},Creatives:{subElements:["Creative"]},Creative:{subElements:["UniversalAdId"]},Linear:{subElements:["MediaFiles","Duration"]},MediaFiles:{subElements:["MediaFile"]},MediaFile:{attributes:["delivery","type","width","height"]},Mezzanine:{attributes:["delivery","type","width","height"]},NonLinear:{oneOfinLineResources:["StaticResource","IFrameResource","HTMLResource"],attributes:["width","height"]},Companion:{oneOfinLineResources:["StaticResource","IFrameResource","HTMLResource"],attributes:["width","height"]},StaticResource:{attributes:["creativeType"]},Icons:{subElements:["Icon"]},Icon:{oneOfinLineResources:["StaticResource","IFrameResource","HTMLResource"]}};function k(e,t){if(!y[e.nodeName]||!y[e.nodeName].attributes)return;const r=y[e.nodeName].attributes.filter((t=>!e.getAttribute(t)));r.length>0&&N({name:e.nodeName,parentName:e.parentNode.nodeName,attributes:r},t)}function E(e,t,r){const i=y[e.nodeName],a=!r&&"Wrapper"!==e.nodeName;if(!i||a)return;if(i.subElements){const r=i.subElements.filter((t=>!m.childByName(e,t)));r.length>0&&N({name:e.nodeName,parentName:e.parentNode.nodeName,subElements:r},t)}if(!r||!i.oneOfinLineResources)return;i.oneOfinLineResources.some((t=>m.childByName(e,t)))||N({name:e.nodeName,parentName:e.parentNode.nodeName,oneOfResources:i.oneOfinLineResources},t)}function b(e){return e.children&&0!==e.children.length}function N(e,t){let{name:r,parentName:i,attributes:a,subElements:s,oneOfResources:n}=e,o="Element '".concat(r,"'");o+=a?" missing required attribute(s) '".concat(a.join(", "),"' "):s?" missing required sub element(s) '".concat(s.join(", "),"' "):n?" must provide one of the following '".concat(n.join(", "),"' "):" is empty",t("VAST-warning",{message:o,parentElement:i,specVersion:4.1})}const w={verifyRequiredValues:function e(t,r,i){if(t&&t.nodeName)if("InLine"===t.nodeName&&(i=!0),k(t,r),b(t)){E(t,r,i);for(let a=0;a2&&void 0!==arguments[2]?arguments[2]:{};const a=Array.from(e.childNodes).filter((e=>{const t=e.nodeName.toLowerCase();return"inline"===t||!1!==i&&"wrapper"===t}));for(const i of a){if(m.copyNodeAttribute("id",e,i),m.copyNodeAttribute("sequence",e,i),m.copyNodeAttribute("adType",e,i),"Wrapper"===i.nodeName)return{ad:I(i,t),type:"WRAPPER"};if("InLine"===i.nodeName)return{ad:L(i,t,{allowMultipleAds:r}),type:"INLINE"};const a=i.nodeName.toLowerCase();t("VAST-warning",{message:"<".concat(i.nodeName,"inline"===a?"> must be written ":"> must be written "),wrongNode:i})}}function L(e,t){let{allowMultipleAds:r}=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};return!1===r&&e.getAttribute("sequence")?null:U(e,t)}function U(e,t){let r=[];t&&w.verifyRequiredValues(e,t);const i=Array.from(e.childNodes),a=function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return{id:e.id||null,sequence:e.sequence||null,adType:e.adType||null,adServingId:null,categories:[],expires:null,viewableImpression:[],system:null,title:null,description:null,advertiser:null,pricing:null,survey:null,errorURLTemplates:[],impressionURLTemplates:[],creatives:[],extensions:[],adVerifications:[],blockedAdCategories:[],followAdditionalWrappers:!0,allowMultipleAds:!1,fallbackOnNoAd:null}}(m.parseAttributes(e));return i.forEach((e=>{switch(e.nodeName){case"Error":a.errorURLTemplates.push(m.parseNodeText(e));break;case"Impression":a.impressionURLTemplates.push({id:e.getAttribute("id")||null,url:m.parseNodeText(e)});break;case"Creatives":a.creatives=function(e){const t=[];return e.forEach((e=>{const r={id:e.getAttribute("id")||null,adId:R(e),sequence:e.getAttribute("sequence")||null,apiFramework:e.getAttribute("apiFramework")||null},i=[];let a;m.childrenByName(e,"UniversalAdId").forEach((e=>{const t={idRegistry:e.getAttribute("idRegistry")||"unknown",value:m.parseNodeText(e)};i.push(t)}));const s=m.childByName(e,"CreativeExtensions");s&&(a=T(m.childrenByName(s,"CreativeExtension")));for(const s in e.childNodes){const n=e.childNodes[s];let o;switch(n.nodeName){case"Linear":o=v(n,r);break;case"NonLinearAds":o=f(n,r);break;case"CompanionAds":o=g(n,r)}o&&(i&&(o.universalAdIds=i),a&&(o.creativeExtensions=a),t.push(o))}})),t}(m.childrenByName(e,"Creative"));break;case"Extensions":{const t=m.childrenByName(e,"Extension");a.extensions=T(t),a.adVerifications.length||(r=function(e){let t=null,r=[];e.some((e=>t=m.childByName(e,"AdVerifications"))),t&&(r=x(m.childrenByName(t,"Verification")));return r}(t));break}case"AdVerifications":a.adVerifications=x(m.childrenByName(e,"Verification"));break;case"AdSystem":a.system={value:m.parseNodeText(e),version:e.getAttribute("version")||null};break;case"AdTitle":a.title=m.parseNodeText(e);break;case"AdServingId":a.adServingId=m.parseNodeText(e);break;case"Category":a.categories.push({authority:e.getAttribute("authority")||null,value:m.parseNodeText(e)});break;case"Expires":a.expires=parseInt(m.parseNodeText(e),10);break;case"ViewableImpression":a.viewableImpression.push(function(e){const t=(e,t)=>{const r=m.parseNodeText(t);return r&&e.push(r),e};return{id:e.getAttribute("id")||null,viewable:m.childrenByName(e,"Viewable").reduce(t,[]),notViewable:m.childrenByName(e,"NotViewable").reduce(t,[]),viewUndetermined:m.childrenByName(e,"ViewUndetermined").reduce(t,[])}}(e));break;case"Description":a.description=m.parseNodeText(e);break;case"Advertiser":a.advertiser={id:e.getAttribute("id")||null,value:m.parseNodeText(e)};break;case"Pricing":a.pricing={value:m.parseNodeText(e),model:e.getAttribute("model")||null,currency:e.getAttribute("currency")||null};break;case"Survey":a.survey={value:m.parseNodeText(e),type:e.getAttribute("type")||null};break;case"BlockedAdCategories":a.blockedAdCategories.push({authority:e.getAttribute("authority")||null,value:m.parseNodeText(e)})}})),r.length&&(a.adVerifications=a.adVerifications.concat(r)),a}function I(e,t){const r=U(e,t),i=e.getAttribute("followAdditionalWrappers"),a=e.getAttribute("allowMultipleAds"),s=e.getAttribute("fallbackOnNoAd");r.followAdditionalWrappers=!i||m.parseBoolean(i),r.allowMultipleAds=!!a&&m.parseBoolean(a),r.fallbackOnNoAd=s?m.parseBoolean(s):null;let n=m.childByName(e,"VASTAdTagURI");if(n?r.nextWrapperURL=m.parseNodeText(n):(n=m.childByName(e,"VASTAdTagURL"),n&&(r.nextWrapperURL=m.parseNodeText(m.childByName(n,"URL")))),r.creatives.forEach((e=>{if(["linear","nonlinear"].includes(e.type)){if(e.trackingEvents){r.trackingEvents||(r.trackingEvents={}),r.trackingEvents[e.type]||(r.trackingEvents[e.type]={});for(const t in e.trackingEvents){const i=e.trackingEvents[t];Array.isArray(r.trackingEvents[e.type][t])||(r.trackingEvents[e.type][t]=[]),i.forEach((i=>{r.trackingEvents[e.type][t].push(i)}))}}e.videoClickTrackingURLTemplates&&(Array.isArray(r.videoClickTrackingURLTemplates)||(r.videoClickTrackingURLTemplates=[]),e.videoClickTrackingURLTemplates.forEach((e=>{r.videoClickTrackingURLTemplates.push(e)}))),e.videoClickThroughURLTemplate&&(r.videoClickThroughURLTemplate=e.videoClickThroughURLTemplate),e.videoCustomClickURLTemplates&&(Array.isArray(r.videoCustomClickURLTemplates)||(r.videoCustomClickURLTemplates=[]),e.videoCustomClickURLTemplates.forEach((e=>{r.videoCustomClickURLTemplates.push(e)})))}})),r.nextWrapperURL)return r}function x(e){const t=[];return e.forEach((e=>{const r={resource:null,vendor:null,browserOptional:!1,apiFramework:null,type:null,parameters:null,trackingEvents:{}},i=Array.from(e.childNodes);m.assignAttributes(e.attributes,r),i.forEach((e=>{let{nodeName:t,textContent:i,attributes:a}=e;switch(t){case"JavaScriptResource":case"ExecutableResource":r.resource=i.trim(),m.assignAttributes(a,r);break;case"VerificationParameters":r.parameters=i.trim()}}));const a=m.childByName(e,"TrackingEvents");a&&m.childrenByName(a,"Tracking").forEach((e=>{const t=e.getAttribute("event"),i=m.parseNodeText(e);t&&i&&(Array.isArray(r.trackingEvents[t])||(r.trackingEvents[t]=[]),r.trackingEvents[t].push(i))})),t.push(r)})),t}class S{constructor(){this._handlers=[]}on(e,t){if("function"!=typeof t)throw new TypeError("The handler argument must be of type Function. Received type ".concat(typeof t));if(!e)throw new TypeError("The event argument must be of type String. Received type ".concat(typeof e));return this._handlers.push({event:e,handler:t}),this}once(e,t){return this.on(e,function(e,t,r){const i={fired:!1,wrapFn:void 0};function a(){i.fired||(e.off(t,i.wrapFn),i.fired=!0,r.bind(e)(...arguments))}return i.wrapFn=a,a}(this,e,t))}off(e,t){return this._handlers=this._handlers.filter((r=>r.event!==e||r.handler!==t)),this}emit(e){for(var t=arguments.length,r=new Array(t>1?t-1:0),i=1;i{"*"===t.event&&(a=!0,t.handler(e,...r)),t.event===e&&(a=!0,t.handler(...r))})),a}removeAllListeners(e){return e?(this._handlers=this._handlers.filter((t=>t.event!==e)),this):(this._handlers=[],this)}listenerCount(e){return this._handlers.filter((t=>t.event===e)).length}listeners(e){return this._handlers.reduce(((t,r)=>(r.event===e&&t.push(r.handler),t)),[])}eventNames(){return this._handlers.map((e=>e.event))}}let D=0,V=0;const O=(e,t)=>{if(!e||!t||e<=0||t<=0)return;V=(V*D+8*e/t)/++D},F={ERRORCODE:900,extensions:[]};class B extends S{constructor(){let{fetcher:e}=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};super(),this.maxWrapperDepth=null,this.remainingAds=[],this.fetcher=e||null}trackVastError(e,t){for(var r=arguments.length,i=new Array(r>2?r-2:0),a=2;a0&&void 0!==arguments[0]?arguments[0]:{};this.maxWrapperDepth=e.wrapperLimit||10,this.parsingOptions={allowMultipleAds:e.allowMultipleAds},this.rootURL="",this.resetParsingStatus(),O(e.byteLength,e.requestDuration)}resetParsingStatus(){this.errorURLTemplates=[],this.rootErrorURLTemplates=[],this.vastVersion=null}getRemainingAds(e){if(0===this.remainingAds.length)return Promise.reject(new Error("No more ads are available for the given VAST"));const t=e?d.flatten(this.remainingAds):this.remainingAds.shift();return this.errorURLTemplates=[],this.resolveAds(t,{wrapperDepth:0,url:this.rootURL}).then((e=>this.buildVASTResponse(e)))}parseVAST(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return this.initParsingStatus(t),t.isRootVAST=!0,this.parse(e,t).then((e=>this.buildVASTResponse(e)))}buildVASTResponse(e){const t=function(e){let{ads:t,errorURLTemplates:r,version:i}=e;return{ads:t||[],errorURLTemplates:r||[],version:i||null}}({ads:e,errorURLTemplates:this.getErrorURLTemplates(),version:this.vastVersion});return this.completeWrapperResolving(t),t}parseVastXml(e,t){let{isRootVAST:r=!1,url:i=null,wrapperDepth:a=0,allowMultipleAds:s,followAdditionalWrappers:n}=t;if(!e||!e.documentElement||"VAST"!==e.documentElement.nodeName)throw this.emit("VAST-ad-parsed",{type:"ERROR",url:i,wrapperDepth:a}),new Error("Invalid VAST XMLDocument");const o=[],l=e.documentElement.childNodes,c=e.documentElement.getAttribute("version");r&&c&&(this.vastVersion=c);for(const e in l){const t=l[e];if("Error"===t.nodeName){const e=m.parseNodeText(t);r?this.rootErrorURLTemplates.push(e):this.errorURLTemplates.push(e)}else if("Ad"===t.nodeName){if(this.vastVersion&&parseFloat(this.vastVersion)<3)s=!0;else if(!1===s&&o.length>1)break;const e=C(t,this.emit.bind(this),{allowMultipleAds:s,followAdditionalWrappers:n});e.ad?(o.push(e.ad),this.emit("VAST-ad-parsed",{type:e.type,url:i,wrapperDepth:a,adIndex:o.length-1,vastVersion:c})):this.trackVastError(this.getErrorURLTemplates(),{ERRORCODE:101})}}return o}parse(e){let{url:t=null,resolveAll:r=!0,wrapperSequence:i=null,previousUrl:a=null,wrapperDepth:s=0,isRootVAST:n=!1,followAdditionalWrappers:o,allowMultipleAds:l}=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},c=[];this.vastVersion&&parseFloat(this.vastVersion)<3&&n&&(l=!0);try{c=this.parseVastXml(e,{isRootVAST:n,url:t,wrapperDepth:s,allowMultipleAds:l,followAdditionalWrappers:o})}catch(e){return Promise.reject(e)}return 1===c.length&&null!=i&&(c[0].sequence=i),!1===r&&(this.remainingAds=m.splitVAST(c),c=this.remainingAds.shift()),this.resolveAds(c,{wrapperDepth:s,previousUrl:a,url:t})}resolveAds(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[],{wrapperDepth:t,previousUrl:r,url:i}=arguments.length>1?arguments[1]:void 0;const a=[];return r=i,e.forEach((e=>{const i=this.resolveWrappers(e,t,r);a.push(i)})),Promise.all(a).then((e=>{const a=d.flatten(e);if(!a.length&&this.remainingAds.length>0){const e=this.remainingAds.shift();return this.resolveAds(e,{wrapperDepth:t,previousUrl:r,url:i})}return a}))}resolveWrappers(e,t,r){const i={...e};return new Promise((e=>{var a;if(t++,!i.nextWrapperURL)return delete i.nextWrapperURL,e(i);if(!this.fetcher)return i.VASTAdTagURI=i.nextWrapperURL,delete i.nextWrapperURL,e(i);if(t>=this.maxWrapperDepth)return i.errorCode=302,delete i.nextWrapperURL,e(i);i.nextWrapperURL=m.resolveVastAdTagURI(i.nextWrapperURL,r);const s=null!==(a=this.parsingOptions.allowMultipleAds)&&void 0!==a?a:i.allowMultipleAds,n=i.sequence;this.fetcher.fetchVAST({url:i.nextWrapperURL,emitter:this.emit.bind(this),maxWrapperDepth:this.maxWrapperDepth}).then((a=>this.parse(a,{url:i.nextWrapperURL,previousUrl:r,wrapperSequence:n,wrapperDepth:t,followAdditionalWrappers:i.followAdditionalWrappers,allowMultipleAds:s}).then((t=>{if(delete i.nextWrapperURL,0===t.length)return i.creatives=[],e(i);t.forEach((e=>{e&&m.mergeWrapperAdData(e,i)})),e(t)})))).catch((t=>{i.errorCode=301,i.errorMessage=t.message,e(i)}))}))}completeWrapperResolving(e){if(0===e.ads.length)this.trackVastError(e.errorURLTemplates,{ERRORCODE:303});else for(let t=e.ads.length-1;t>=0;t--){const r=e.ads[t];!r.errorCode&&0!==r.creatives.length||r.VASTAdTagURI||(this.trackVastError(r.errorURLTemplates.concat(e.errorURLTemplates),{ERRORCODE:r.errorCode||303},{ERRORMESSAGE:r.errorMessage||""},{extensions:r.extensions},{system:r.system}),e.ads.splice(t,1))}}}let P=null;const M={data:{},length:0,getItem(e){return this.data[e]},setItem(e,t){this.data[e]=t,this.length=Object.keys(this.data).length},removeItem(e){delete this.data[e],this.length=Object.keys(this.data).length},clear(){this.data={},this.length=0}};class W{constructor(){this.storage=this.initStorage()}initStorage(){if(P)return P;try{P="undefined"!=typeof window&&null!==window?window.localStorage||window.sessionStorage:null}catch(e){P=null}return P&&!this.isStorageDisabled(P)||(P=M,P.clear()),P}isStorageDisabled(e){const t="__VASTStorage__";try{if(e.setItem(t,t),e.getItem(t)!==t)return e.removeItem(t),!0}catch(e){return!0}return e.removeItem(t),!1}getItem(e){return this.storage.getItem(e)}setItem(e,t){return this.storage.setItem(e,t)}removeItem(e){return this.storage.removeItem(e)}clear(){return this.storage.clear()}}const j=12e4;const q={get:async function(e,t){try{const r=new AbortController,i=setTimeout((()=>{throw r.abort(),new Error("URLHandler: Request timed out after ".concat(t.timeout||j," ms (408)"))}),t.timeout||j),a=await fetch(e,{...t,signal:r.signal,credentials:t.withCredentials?"include":"omit"});clearTimeout(i);const s=function(e){return"https:"===window.location.protocol&&e.url.includes("http://")?"URLHandler: Cannot go from HTTPS to HTTP.":200===e.status&&e.ok?null:"URLHandler: ".concat(e.statusText," (").concat(e.status,")")}(a);return s?{error:new Error(s),statusCode:a.status}:async function(e){const t=await e.text();let r;r=d.isBrowserEnvironment()?new DOMParser:new((await Promise.resolve().then((function(){return require("./chunks/xmldom-623c1b6b.min.js")}))).DOMParser);return{xml:r.parseFromString(t,"text/xml"),details:{byteLength:t.length,statusCode:e.status,rawXml:t}}}(a)}catch(e){return{error:e,statusCode:"AbortError"===e.name?408:null}}}};class K{constructor(){this.URLTemplateFilters=[]}setOptions(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};this.urlHandler=e.urlHandler||e.urlhandler||q,this.fetchingOptions={timeout:e.timeout||j,withCredentials:Boolean(e.withCredentials)}}addURLTemplateFilter(e){"function"==typeof e&&this.URLTemplateFilters.push(e)}removeLastURLTemplateFilter(){this.URLTemplateFilters.pop()}countURLTemplateFilters(){return this.URLTemplateFilters.length}clearURLTemplateFilters(){this.URLTemplateFilters=[]}async fetchVAST(e){var t;let{url:r,maxWrapperDepth:i,emitter:a,wrapperDepth:s=0,previousUrl:n=null,wrapperAd:o=null}=e;const l=Date.now();this.URLTemplateFilters.forEach((e=>{r=e(r)})),a("VAST-resolving",{url:r,previousUrl:n,wrapperDepth:s,maxWrapperDepth:i,timeout:this.fetchingOptions.timeout,wrapperAd:o});const c=await this.urlHandler.get(r,this.fetchingOptions),u=Math.round(Date.now()-l);if(a("VAST-resolved",{url:r,previousUrl:n,wrapperDepth:s,error:(null==c?void 0:c.error)||null,duration:u,statusCode:(null==c?void 0:c.statusCode)||null,...null==c?void 0:c.details}),O(null==c||null===(t=c.details)||void 0===t?void 0:t.byteLength,u),c.error)throw new Error(c.error);return c.xml}}exports.VASTClient=class{constructor(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0,t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:new W;this.cappingFreeLunch=e,this.cappingMinimumTimeInterval=t,this.fetcher=new K,this.vastParser=new B({fetcher:this.fetcher}),this.storage=r,void 0===this.lastSuccessfulAd&&(this.lastSuccessfulAd=0),void 0===this.totalCalls&&(this.totalCalls=0),void 0===this.totalCallsTimeout&&(this.totalCallsTimeout=0)}addURLTemplateFilter(e){this.fetcher.addURLTemplateFilter(e)}removeLastURLTemplateFilter(){this.fetcher.removeLastURLTemplateFilter()}countURLTemplateFilters(){return this.fetcher.countURLTemplateFilters()}clearURLTemplateFilters(){this.fetcher.clearURLTemplateFilters()}getParser(){return this.vastParser}get lastSuccessfulAd(){return this.storage.getItem("vast-client-last-successful-ad")}set lastSuccessfulAd(e){this.storage.setItem("vast-client-last-successful-ad",e)}get totalCalls(){return this.storage.getItem("vast-client-total-calls")}set totalCalls(e){this.storage.setItem("vast-client-total-calls",e)}get totalCallsTimeout(){return this.storage.getItem("vast-client-total-calls-timeout")}set totalCallsTimeout(e){this.storage.setItem("vast-client-total-calls-timeout",e)}hasRemainingAds(){return this.vastParser.remainingAds.length>0}getNextAds(e){return this.vastParser.getRemainingAds(e)}parseVAST(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return this.fetcher.setOptions(t),this.vastParser.parseVAST(e,t)}get(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};const r=Date.now();return t.hasOwnProperty("resolveAll")||(t.resolveAll=!1),this.totalCallsTimeout{if(this.cappingFreeLunch>=this.totalCalls)return a(new Error("VAST call canceled – FreeLunch capping not reached yet ".concat(this.totalCalls,"/").concat(this.cappingFreeLunch)));const s=r-this.lastSuccessfulAd;if(s<0)this.lastSuccessfulAd=0;else if(s(t.previousUrl=e,t.isRootVAST=!0,t.url=e,this.vastParser.parse(r,t).then((e=>{const t=this.vastParser.buildVASTResponse(e);i(t)}))))).catch((e=>a(e)))}))}},exports.VASTParser=B,exports.VASTTracker=class extends S{constructor(e,t,r){let i=arguments.length>3&&void 0!==arguments[3]?arguments[3]:null,a=arguments.length>4&&void 0!==arguments[4]&&arguments[4];super(),this.ad=t,this.creative=r,this.variation=i,this.muted=a,this.impressed=!1,this.skippable=!1,this.trackingEvents={},this.lastPercentage=0,this._alreadyTriggeredQuartiles={},this.emitAlwaysEvents=["creativeView","start","firstQuartile","midpoint","thirdQuartile","complete","resume","pause","rewind","skip","closeLinear","close"];for(const e in this.creative.trackingEvents){const t=this.creative.trackingEvents[e];this.trackingEvents[e]=t.slice(0)}!function(e){return"linear"===e.type}(this.creative)?this._initVariationTracking():this._initLinearTracking(),e&&this.on("start",(()=>{e.lastSuccessfulAd=Date.now()}))}_initLinearTracking(){this.linear=!0,this.skipDelay=this.creative.skipDelay,this.setDuration(this.creative.duration),this.clickThroughURLTemplate=this.creative.videoClickThroughURLTemplate,this.clickTrackingURLTemplates=this.creative.videoClickTrackingURLTemplates}_initVariationTracking(){if(this.linear=!1,this.skipDelay=-1,this.variation){for(const e in this.variation.trackingEvents){const t=this.variation.trackingEvents[e];this.trackingEvents[e]?this.trackingEvents[e]=this.trackingEvents[e].concat(t.slice(0)):this.trackingEvents[e]=t.slice(0)}"nonLinearAd"===this.variation.adType?(this.clickThroughURLTemplate=this.variation.nonlinearClickThroughURLTemplate,this.clickTrackingURLTemplates=this.variation.nonlinearClickTrackingURLTemplates,this.setDuration(this.variation.minSuggestedDuration)):function(e){return"companionAd"===e.adType}(this.variation)&&(this.clickThroughURLTemplate=this.variation.companionClickThroughURLTemplate,this.clickTrackingURLTemplates=this.variation.companionClickTrackingURLTemplates)}}setDuration(e){d.isValidTimeValue(e)?(this.assetDuration=e,this.quartiles={firstQuartile:Math.round(25*this.assetDuration)/100,midpoint:Math.round(50*this.assetDuration)/100,thirdQuartile:Math.round(75*this.assetDuration)/100}):this.emit("TRACKER-error",{message:"the duration provided is not valid. duration: ".concat(e)})}setProgress(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},r=!(arguments.length>2&&void 0!==arguments[2])||arguments[2];if(!d.isValidTimeValue(e)||"object"!=typeof t)return void this.emit("TRACKER-error",{message:"One given setProgress parameter has the wrong type. progress: ".concat(e,", macros: ").concat(d.formatMacrosValues(t))});const i=this.skipDelay||-1;if(-1===i||this.skippable||(i>e?this.emit("skip-countdown",i-e):(this.skippable=!0,this.emit("skip-countdown",0))),this.assetDuration>0){const i=Math.round(e/this.assetDuration*100),a=[];if(e>0){a.push("start");for(let e=this.lastPercentage;e{this.track(e,{macros:t,once:r})})),e1&&void 0!==arguments[1]?arguments[1]:{};"boolean"==typeof e&&"object"==typeof t?(this.muted!==e&&this.track(e?"mute":"unmute",{macros:t}),this.muted=e):this.emit("TRACKER-error",{message:"One given setMuted parameter has the wrong type. muted: ".concat(e,", macros: ").concat(d.formatMacrosValues(t))})}setPaused(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};"boolean"==typeof e&&"object"==typeof t?(this.paused!==e&&this.track(e?"pause":"resume",{macros:t}),this.paused=e):this.emit("TRACKER-error",{message:"One given setPaused parameter has the wrong type. paused: ".concat(e,", macros: ").concat(d.formatMacrosValues(t))})}setFullscreen(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};"boolean"==typeof e&&"object"==typeof t?(this.fullscreen!==e&&this.track(e?"fullscreen":"exitFullscreen",{macros:t}),this.fullscreen=e):this.emit("TRACKER-error",{message:"One given setFullScreen parameter has the wrong type. fullscreen: ".concat(e,", macros: ").concat(d.formatMacrosValues(t))})}setExpand(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};"boolean"==typeof e&&"object"==typeof t?(this.expanded!==e&&(this.track(e?"expand":"collapse",{macros:t}),this.track(e?"playerExpand":"playerCollapse",{macros:t})),this.expanded=e):this.emit("TRACKER-error",{message:"One given setExpand parameter has the wrong type. expanded: ".concat(e,", macros: ").concat(d.formatMacrosValues(t))})}setSkipDelay(e){d.isValidTimeValue(e)?this.skipDelay=e:this.emit("TRACKER-error",{message:"setSkipDelay parameter does not have a valid value. duration: ".concat(e)})}trackImpression(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};"object"==typeof e?this.impressed||(this.impressed=!0,this.trackURLs(this.ad.impressionURLTemplates,e),this.track("creativeView",{macros:e})):this.emit("TRACKER-error",{message:"trackImpression parameter has the wrong type. macros: ".concat(e)})}trackViewableImpression(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};"object"==typeof e?this.ad.viewableImpression.forEach((t=>{this.trackURLs(t.viewable,e)})):this.emit("TRACKER-error",{message:"trackViewableImpression given macros has the wrong type. macros: ".concat(e)})}trackNotViewableImpression(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};"object"==typeof e?this.ad.viewableImpression.forEach((t=>{this.trackURLs(t.notViewable,e)})):this.emit("TRACKER-error",{message:"trackNotViewableImpression given macros has the wrong type. macros: ".concat(e)})}trackUndeterminedImpression(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};"object"==typeof e?this.ad.viewableImpression.forEach((t=>{this.trackURLs(t.viewUndetermined,e)})):this.emit("TRACKER-error",{message:"trackUndeterminedImpression given macros has the wrong type. macros: ".concat(e)})}error(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];"object"==typeof e&&"boolean"==typeof t?this.trackURLs(this.ad.errorURLTemplates,e,{isCustomCode:t}):this.emit("TRACKER-error",{message:"One given error parameter has the wrong type. macros: ".concat(d.formatMacrosValues(e),", isCustomCode: ").concat(t)})}errorWithCode(e){let t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];"string"==typeof e&&"boolean"==typeof t?(this.error({ERRORCODE:e},t),console.log("The method errorWithCode is deprecated, please use vast tracker error method instead")):this.emit("TRACKER-error",{message:"One given errorWithCode parameter has the wrong type. errorCode: ".concat(e,", isCustomCode: ").concat(t)})}complete(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};"object"==typeof e?this.track("complete",{macros:e}):this.emit("TRACKER-error",{message:"complete given macros has the wrong type. macros: ".concat(e)})}notUsed(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};"object"==typeof e?(this.track("notUsed",{macros:e}),this.trackingEvents=[]):this.emit("TRACKER-error",{message:"notUsed given macros has the wrong type. macros: ".concat(e)})}otherAdInteraction(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};"object"==typeof e?this.track("otherAdInteraction",{macros:e}):this.emit("TRACKER-error",{message:"otherAdInteraction given macros has the wrong type. macros: ".concat(e)})}acceptInvitation(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};"object"==typeof e?this.track("acceptInvitation",{macros:e}):this.emit("TRACKER-error",{message:"acceptInvitation given macros has the wrong type. macros: ".concat(e)})}adExpand(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};"object"==typeof e?this.track("adExpand",{macros:e}):this.emit("TRACKER-error",{message:"adExpand given macros has the wrong type. macros: ".concat(e)})}adCollapse(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};"object"==typeof e?this.track("adCollapse",{macros:e}):this.emit("TRACKER-error",{message:"adCollapse given macros has the wrong type. macros: ".concat(e)})}minimize(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};"object"==typeof e?this.track("minimize",{macros:e}):this.emit("TRACKER-error",{message:"minimize given macros has the wrong type. macros: ".concat(e)})}verificationNotExecuted(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if("string"!=typeof e||"object"!=typeof t)return void this.emit("TRACKER-error",{message:"One given verificationNotExecuted parameter has to wrong type. vendor: ".concat(e,", macros: ").concat(d.formatMacrosValues(t))});if(!this.ad||!this.ad.adVerifications||!this.ad.adVerifications.length)throw new Error("No adVerifications provided");if(!e)throw new Error("No vendor provided, unable to find associated verificationNotExecuted");const r=this.ad.adVerifications.find((t=>t.vendor===e));if(!r)throw new Error("No associated verification element found for vendor: ".concat(e));const i=r.trackingEvents;if(i&&i.verificationNotExecuted){const e=i.verificationNotExecuted;this.trackURLs(e,t),this.emit("verificationNotExecuted",{trackingURLTemplates:e})}}overlayViewDuration(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};"string"==typeof e&&"object"==typeof t?(t.ADPLAYHEAD=e,this.track("overlayViewDuration",{macros:t})):this.emit("TRACKER-error",{message:"One given overlayViewDuration parameters has the wrong type. formattedDuration: ".concat(e,", macros: ").concat(d.formatMacrosValues(t))})}close(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};"object"==typeof e?this.track(this.linear?"closeLinear":"close",{macros:e}):this.emit("TRACKER-error",{message:"close given macros has the wrong type. macros: ".concat(e)})}skip(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};"object"==typeof e?this.track("skip",{macros:e}):this.emit("TRACKER-error",{message:"skip given macros has the wrong type. macros: ".concat(e)})}load(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};"object"==typeof e?this.track("loaded",{macros:e}):this.emit("TRACKER-error",{message:"load given macros has the wrong type. macros: ".concat(e)})}click(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null,t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if(null!==e&&"string"!=typeof e||"object"!=typeof t)return void this.emit("TRACKER-error",{message:"One given click parameter has the wrong type. fallbackClickThroughURL: ".concat(e,", macros: ").concat(d.formatMacrosValues(t))});this.clickTrackingURLTemplates&&this.clickTrackingURLTemplates.length&&this.trackURLs(this.clickTrackingURLTemplates,t);const r=this.clickThroughURLTemplate||e,i={...t};if(r){this.progress&&(i.ADPLAYHEAD=this.progressFormatted());const e=d.resolveURLTemplates([r],i)[0];this.emit("clickthrough",e)}}track(e){let{macros:t={},once:r=!1}=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if("object"!=typeof t)return void this.emit("TRACKER-error",{message:"track given macros has the wrong type. macros: ".concat(t)});"closeLinear"===e&&!this.trackingEvents[e]&&this.trackingEvents.close&&(e="close");const i=this.trackingEvents[e],a=this.emitAlwaysEvents.indexOf(e)>-1;i?(this.emit(e,{trackingURLTemplates:i}),this.trackURLs(i,t)):a&&this.emit(e,null),r&&(delete this.trackingEvents[e],a&&this.emitAlwaysEvents.splice(this.emitAlwaysEvents.indexOf(e),1))}trackURLs(e){var t;let r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};const{validUrls:a,invalidUrls:s}=d.filterUrlTemplates(e);s.length&&this.emit("TRACKER-error",{message:"Provided urls are malformed. url: ".concat(s)});const n={...r};this.linear&&(this.creative&&this.creative.mediaFiles&&this.creative.mediaFiles[0]&&this.creative.mediaFiles[0].fileURL&&(n.ASSETURI=this.creative.mediaFiles[0].fileURL),this.progress&&(n.ADPLAYHEAD=this.progressFormatted())),null!==(t=this.creative)&&void 0!==t&&null!==(t=t.universalAdIds)&&void 0!==t&&t.length&&(n.UNIVERSALADID=this.creative.universalAdIds.map((e=>e.idRegistry.concat(" ",e.value))).join(",")),this.ad&&(this.ad.sequence&&(n.PODSEQUENCE=this.ad.sequence),this.ad.adType&&(n.ADTYPE=this.ad.adType),this.ad.adServingId&&(n.ADSERVINGID=this.ad.adServingId),this.ad.categories&&this.ad.categories.length&&(n.ADCATEGORIES=this.ad.categories.map((e=>e.value)).join(",")),this.ad.blockedAdCategories&&this.ad.blockedAdCategories.length&&(n.BLOCKEDADCATEGORIES=this.ad.blockedAdCategories.map((e=>e.value)).join(","))),d.track(a,n,i)}convertToTimecode(e){if(!d.isValidTimeValue(e))return"";const t=1e3*e,r=Math.floor(t/36e5),i=Math.floor(t/6e4%60),a=Math.floor(t/1e3%60),s=Math.floor(t%1e3);return"".concat(d.addLeadingZeros(r,2),":").concat(d.addLeadingZeros(i,2),":").concat(d.addLeadingZeros(a,2),".").concat(d.addLeadingZeros(s,3))}progressFormatted(){return this.convertToTimecode(this.progress)}},exports.parseDuration=p; diff --git a/dist/vast-client.js b/dist/vast-client.js index 2a572fc2..595bfabe 100644 --- a/dist/vast-client.js +++ b/dist/vast-client.js @@ -1,4101 +1,3876 @@ -(function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : - typeof define === 'function' && define.amd ? define(['exports'], factory) : - (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.VAST = {})); -})(this, (function (exports) { 'use strict'; - - function ownKeys(object, enumerableOnly) { - var keys = Object.keys(object); - if (Object.getOwnPropertySymbols) { - var symbols = Object.getOwnPropertySymbols(object); - enumerableOnly && (symbols = symbols.filter(function (sym) { - return Object.getOwnPropertyDescriptor(object, sym).enumerable; - })), keys.push.apply(keys, symbols); - } - return keys; - } - function _objectSpread2(target) { - for (var i = 1; i < arguments.length; i++) { - var source = null != arguments[i] ? arguments[i] : {}; - i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { - _defineProperty(target, key, source[key]); - }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { - Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); - }); +function createAd() { + let adAttributes = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + return { + id: adAttributes.id || null, + sequence: adAttributes.sequence || null, + adType: adAttributes.adType || null, + adServingId: null, + categories: [], + expires: null, + viewableImpression: [], + system: null, + title: null, + description: null, + advertiser: null, + pricing: null, + survey: null, + // @deprecated in VAST 4.1 + errorURLTemplates: [], + impressionURLTemplates: [], + creatives: [], + extensions: [], + adVerifications: [], + blockedAdCategories: [], + followAdditionalWrappers: true, + allowMultipleAds: false, + fallbackOnNoAd: null + }; +} + +function createAdVerification() { + return { + resource: null, + vendor: null, + browserOptional: false, + apiFramework: null, + type: null, + parameters: null, + trackingEvents: {} + }; +} + +function createCompanionAd() { + let creativeAttributes = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + return { + id: creativeAttributes.id || null, + adType: 'companionAd', + width: creativeAttributes.width || 0, + height: creativeAttributes.height || 0, + assetWidth: creativeAttributes.assetWidth || null, + assetHeight: creativeAttributes.assetHeight || null, + expandedWidth: creativeAttributes.expandedWidth || null, + expandedHeight: creativeAttributes.expandedHeight || null, + apiFramework: creativeAttributes.apiFramework || null, + adSlotId: creativeAttributes.adSlotId || null, + pxratio: creativeAttributes.pxratio || '1', + renderingMode: creativeAttributes.renderingMode || 'default', + staticResources: [], + htmlResources: [], + iframeResources: [], + adParameters: null, + altText: null, + companionClickThroughURLTemplate: null, + companionClickTrackingURLTemplates: [], + trackingEvents: {} + }; +} +function isCompanionAd(ad) { + return ad.adType === 'companionAd'; +} + +function createCreative() { + let creativeAttributes = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + return { + id: creativeAttributes.id || null, + adId: creativeAttributes.adId || null, + sequence: creativeAttributes.sequence || null, + apiFramework: creativeAttributes.apiFramework || null, + universalAdIds: [], + creativeExtensions: [] + }; +} + +function createCreativeCompanion() { + let creativeAttributes = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + const { + id, + adId, + sequence, + apiFramework + } = createCreative(creativeAttributes); + return { + id, + adId, + sequence, + apiFramework, + type: 'companion', + required: null, + variations: [] + }; +} + +const supportedMacros = ['ADCATEGORIES', 'ADCOUNT', 'ADPLAYHEAD', 'ADSERVINGID', 'ADTYPE', 'APIFRAMEWORKS', 'APPBUNDLE', 'ASSETURI', 'BLOCKEDADCATEGORIES', 'BREAKMAXADLENGTH', 'BREAKMAXADS', 'BREAKMAXDURATION', 'BREAKMINADLENGTH', 'BREAKMINDURATION', 'BREAKPOSITION', 'CLICKPOS', 'CLICKTYPE', 'CLIENTUA', 'CONTENTID', 'CONTENTPLAYHEAD', +// @deprecated VAST 4.1 +'CONTENTURI', 'DEVICEIP', 'DEVICEUA', 'DOMAIN', 'EXTENSIONS', 'GDPRCONSENT', 'IFA', 'IFATYPE', 'INVENTORYSTATE', 'LATLONG', 'LIMITADTRACKING', 'MEDIAMIME', 'MEDIAPLAYHEAD', 'OMIDPARTNER', 'PAGEURL', 'PLACEMENTTYPE', 'PLAYERCAPABILITIES', 'PLAYERSIZE', 'PLAYERSTATE', 'PODSEQUENCE', 'REGULATIONS', 'SERVERSIDE', 'SERVERUA', 'TRANSACTIONID', 'UNIVERSALADID', 'VASTVERSIONS', 'VERIFICATIONVENDORS']; + +function track(URLTemplates, macros, options) { + const URLs = resolveURLTemplates(URLTemplates, macros, options); + URLs.forEach(URL => { + if (typeof window !== 'undefined' && window !== null) { + const i = new Image(); + i.src = URL; + } + }); +} + +/** + * Replace the provided URLTemplates with the given values + * + * @param {Array} URLTemplates - An array of tracking url templates. + * @param {Object} [macros={}] - An optional Object of parameters to be used in the tracking calls. + * @param {Object} [options={}] - An optional Object of options to be used in the tracking calls. + */ +function resolveURLTemplates(URLTemplates) { + let macros = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; + const resolvedURLs = []; + const URLArray = extractURLsFromTemplates(URLTemplates); + + // Set default value for invalid ERRORCODE + if (macros['ERRORCODE'] && !options.isCustomCode && !/^[0-9]{3}$/.test(macros['ERRORCODE'])) { + macros['ERRORCODE'] = 900; + } + + // Calc random/time based macros + macros['CACHEBUSTING'] = addLeadingZeros(Math.round(Math.random() * 1.0e8)); + macros['TIMESTAMP'] = new Date().toISOString(); + + // RANDOM/random is not defined in VAST 3/4 as a valid macro tho it's used by some adServer (Auditude) + macros['RANDOM'] = macros['random'] = macros['CACHEBUSTING']; + for (const macro in macros) { + macros[macro] = encodeURIComponentRFC3986(macros[macro]); + } + for (const URLTemplateKey in URLArray) { + const resolveURL = URLArray[URLTemplateKey]; + if (typeof resolveURL !== 'string') { + continue; + } + resolvedURLs.push(replaceUrlMacros(resolveURL, macros)); + } + return resolvedURLs; +} + +/** + * Replace the macros tracking url with their value. + * If no value is provided for a supported macro and it exists in the url, + * it will be replaced by -1 as described by the VAST 4.1 iab specifications + * + * @param {String} url - Tracking url. + * @param {Object} macros - Object of macros to be replaced in the tracking calls + */ +function replaceUrlMacros(url, macros) { + url = replaceMacrosValues(url, macros); + // match any macros from the url that was not replaced + const remainingMacros = url.match(/[^[\]]+(?=])/g); + if (!remainingMacros) { + return url; + } + let supportedRemainingMacros = remainingMacros.filter(macro => supportedMacros.indexOf(macro) > -1); + if (supportedRemainingMacros.length === 0) { + return url; + } + supportedRemainingMacros = supportedRemainingMacros.reduce((accumulator, macro) => { + accumulator[macro] = -1; + return accumulator; + }, {}); + return replaceMacrosValues(url, supportedRemainingMacros); +} + +/** + * Replace the macros tracking url with their value. + * + * @param {String} url - Tracking url. + * @param {Object} macros - Object of macros to be replaced in the tracking calls + */ +function replaceMacrosValues(url, macros) { + let replacedMacrosUrl = url; + for (const key in macros) { + const value = macros[key]; + // this will match [${key}] and %%${key}%% and replace it + replacedMacrosUrl = replacedMacrosUrl.replace(new RegExp("(?:\\[|%%)(".concat(key, ")(?:\\]|%%)"), 'g'), value); + } + return replacedMacrosUrl; +} + +/** + * Extract the url/s from the URLTemplates. + * If the URLTemplates is an array of urls + * If the URLTemplates object has a url property + * If the URLTemplates is a single string + * + * @param {Array|String} URLTemplates - An array|string of url templates. + */ +function extractURLsFromTemplates(URLTemplates) { + if (Array.isArray(URLTemplates)) { + return URLTemplates.map(URLTemplate => { + return URLTemplate && URLTemplate.hasOwnProperty('url') ? URLTemplate.url : URLTemplate; + }); + } + return URLTemplates; +} + +/** + * Filter URLTemplates elements . + * To be valid, urls should: + * - have the same protocol as the client + * or + * - be protocol-relative urls + * + * Returns an object with two arrays + * - validUrls : An array of valid URLs + * - invalidUrls: An array of invalid URLs + * + * @param {Array} URLTemplates - A Array of string/object containing urls templates. + * @returns {Object} + * + */ +function filterUrlTemplates(URLTemplates) { + return URLTemplates.reduce((acc, urlTemplate) => { + const url = urlTemplate.url || urlTemplate; + isValidUrl(url) ? acc.validUrls.push(url) : acc.invalidUrls.push(url); + return acc; + }, { + validUrls: [], + invalidUrls: [] + }); +} +function isValidUrl(url) { + const regex = /^(https?:\/\/|\/\/)/; + return regex.test(url); +} + +/** + * Returns a boolean after checking if the object exists in the array. + * true - if the object exists, false otherwise + * + * @param {Object} obj - The object who existence is to be checked. + * @param {Array} list - List of objects. + */ +function containsTemplateObject(obj, list) { + for (let i = 0; i < list.length; i++) { + if (isTemplateObjectEqual(list[i], obj)) { + return true; } - return target; } - function _typeof(obj) { - "@babel/helpers - typeof"; + return false; +} - return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { - return typeof obj; - } : function (obj) { - return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; - }, _typeof(obj); - } - function _classCallCheck(instance, Constructor) { - if (!(instance instanceof Constructor)) { - throw new TypeError("Cannot call a class as a function"); +/** + * Returns a boolean after comparing two Template objects. + * true - if the objects are equivalent, false otherwise + * + * @param {Object} obj1 + * @param {Object} obj2 + */ +function isTemplateObjectEqual(obj1, obj2) { + if (obj1 && obj2) { + const obj1Properties = Object.getOwnPropertyNames(obj1); + const obj2Properties = Object.getOwnPropertyNames(obj2); + + // If number of properties is different, objects are not equivalent + if (obj1Properties.length !== obj2Properties.length) { + return false; } - } - function _defineProperties(target, props) { - for (var i = 0; i < props.length; i++) { - var descriptor = props[i]; - descriptor.enumerable = descriptor.enumerable || false; - descriptor.configurable = true; - if ("value" in descriptor) descriptor.writable = true; - Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); + if (obj1.id !== obj2.id || obj1.url !== obj2.url) { + return false; } + return true; + } + return false; +} + +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent +function encodeURIComponentRFC3986(str) { + return encodeURIComponent(str).replace(/[!'()*]/g, c => "%".concat(c.charCodeAt(0).toString(16))); +} + +/** + * Return a string of the input number with leading zeros defined by the length param + * + * @param {Number} input - number to convert + * @param {Number} length - length of the desired string + * + * @return {String} + */ +function addLeadingZeros(input) { + let length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 8; + return input.toString().padStart(length, '0'); +} +function isNumeric(n) { + return !isNaN(parseFloat(n)) && isFinite(n); +} +function flatten(arr) { + return arr.reduce((flat, toFlatten) => { + return flat.concat(Array.isArray(toFlatten) ? flatten(toFlatten) : toFlatten); + }, []); +} + +/** + * Joins two arrays of objects without duplicates + * + * @param {Array} arr1 + * @param {Array} arr2 + * + * @return {Array} + */ +function joinArrayOfUniqueTemplateObjs() { + let arr1 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; + let arr2 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : []; + const firstArr = Array.isArray(arr1) ? arr1 : []; + const secondArr = Array.isArray(arr2) ? arr2 : []; + const arr = firstArr.concat(secondArr); + return arr.reduce((res, val) => { + if (!containsTemplateObject(val, res)) { + res.push(val); + } + return res; + }, []); +} + +/** + * Check if a provided value is a valid time value according to the IAB definition + * Check if a provided value is a valid time value according to the IAB definition: Must be a positive number or -1. + * if not implemented by ad unit or -2 if value is unknown. + * @param {Number} time + * + * @return {Boolean} + */ +function isValidTimeValue(time) { + return Number.isFinite(time) && time >= -2; +} + +/** + * Check if we are in a browser environment + * @returns {Boolean} + */ +function isBrowserEnvironment() { + return typeof window !== 'undefined'; +} +function formatMacrosValues(macros) { + return typeof macros !== 'object' ? macros : JSON.stringify(macros); +} +const util = { + track, + resolveURLTemplates, + extractURLsFromTemplates, + filterUrlTemplates, + containsTemplateObject, + isTemplateObjectEqual, + encodeURIComponentRFC3986, + replaceUrlMacros, + isNumeric, + flatten, + joinArrayOfUniqueTemplateObjs, + isValidTimeValue, + addLeadingZeros, + isValidUrl, + isBrowserEnvironment, + formatMacrosValues +}; + +/** + * This module provides support methods to the parsing classes. + */ + +/** + * Returns the first element of the given node which nodeName matches the given name. + * @param {Node} node - The node to use to find a match. + * @param {String} name - The name to look for. + * @return {Object|undefined} + */ +function childByName(node, name) { + const childNodes = Array.from(node.childNodes); + return childNodes.find(childNode => childNode.nodeName === name); +} + +/** + * Returns all the elements of the given node which nodeName match the given name. + * @param {Node} node - The node to use to find the matches. + * @param {String} name - The name to look for. + * @return {Array} + */ +function childrenByName(node, name) { + const childNodes = Array.from(node.childNodes); + return childNodes.filter(childNode => childNode.nodeName === name); +} + +/** + * Converts relative vastAdTagUri. + * @param {String} vastAdTagUrl - The url to resolve. + * @param {String} originalUrl - The original url. + * @return {String} + */ +function resolveVastAdTagURI(vastAdTagUrl, originalUrl) { + if (!originalUrl) { + return vastAdTagUrl; } - function _createClass(Constructor, protoProps, staticProps) { - if (protoProps) _defineProperties(Constructor.prototype, protoProps); - if (staticProps) _defineProperties(Constructor, staticProps); - Object.defineProperty(Constructor, "prototype", { - writable: false + if (vastAdTagUrl.startsWith('//')) { + const { + protocol + } = location; + return "".concat(protocol).concat(vastAdTagUrl); + } + if (!vastAdTagUrl.includes('://')) { + // Resolve relative URLs (mainly for unit testing) + const baseURL = originalUrl.slice(0, originalUrl.lastIndexOf('/')); + return "".concat(baseURL, "/").concat(vastAdTagUrl); + } + return vastAdTagUrl; +} + +/** + * Converts a boolean string into a Boolean. + * @param {String} booleanString - The boolean string to convert. + * @return {Boolean} + */ +function parseBoolean(booleanString) { + return ['true', 'TRUE', 'True', '1'].includes(booleanString); +} + +/** + * Parses a node text (for legacy support). + * @param {Object} node - The node to parse the text from. + * @return {String} + */ +function parseNodeText(node) { + return node && (node.textContent || node.text || '').trim(); +} + +/** + * Copies an attribute from a node to another. + * @param {String} attributeName - The name of the attribute to clone. + * @param {Object} nodeSource - The source node to copy the attribute from. + * @param {Object} nodeDestination - The destination node to copy the attribute at. + */ +function copyNodeAttribute(attributeName, nodeSource, nodeDestination) { + const attributeValue = nodeSource.getAttribute(attributeName); + if (attributeValue) { + nodeDestination.setAttribute(attributeName, attributeValue); + } +} + +/** + * Converts element attributes into an object, where object key is attribute name + * and object value is attribute value + * @param {Element} element + * @returns {Object} + */ +function parseAttributes(element) { + const nodeAttributes = Array.from(element.attributes); + return nodeAttributes.reduce((acc, nodeAttribute) => { + acc[nodeAttribute.nodeName] = nodeAttribute.nodeValue; + return acc; + }, {}); +} + +/** + * Parses a String duration into a Number. + * @param {String} durationString - The dureation represented as a string. + * @return {Number} + */ +function parseDuration(durationString) { + if (durationString === null || typeof durationString === 'undefined') { + return -1; + } + // Some VAST doesn't have an HH:MM:SS duration format but instead jus the number of seconds + if (util.isNumeric(durationString)) { + return parseInt(durationString); + } + const durationComponents = durationString.split(':'); + if (durationComponents.length !== 3) { + return -1; + } + const secondsAndMS = durationComponents[2].split('.'); + let seconds = parseInt(secondsAndMS[0]); + if (secondsAndMS.length === 2) { + seconds += parseFloat("0.".concat(secondsAndMS[1])); + } + const minutes = parseInt(durationComponents[1] * 60); + const hours = parseInt(durationComponents[0] * 60 * 60); + if (isNaN(hours) || isNaN(minutes) || isNaN(seconds) || minutes > 60 * 60 || seconds > 60) { + return -1; + } + return hours + minutes + seconds; +} + +/** + * Splits an Array of ads into an Array of Arrays of ads. + * Each subarray contains either one ad or multiple ads (an AdPod) + * @param {Array} ads - An Array of ads to split + * @return {Array} + */ +function splitVAST(ads) { + const splittedVAST = []; + let lastAdPod = null; + ads.forEach((ad, i) => { + if (ad.sequence) { + ad.sequence = parseInt(ad.sequence, 10); + } + // The current Ad may be the next Ad of an AdPod + if (ad.sequence > 1) { + const lastAd = ads[i - 1]; + // check if the current Ad is exactly the next one in the AdPod + if (lastAd && lastAd.sequence === ad.sequence - 1) { + lastAdPod && lastAdPod.push(ad); + return; + } + // If the ad had a sequence attribute but it was not part of a correctly formed + // AdPod, let's remove the sequence attribute + delete ad.sequence; + } + lastAdPod = [ad]; + splittedVAST.push(lastAdPod); + }); + return splittedVAST; +} + +/** + * Parses the attributes and assign them to object + * @param {Object} attributes attribute + * @param {Object} verificationObject with properties which can be assigned + */ +function assignAttributes(attributes, verificationObject) { + if (attributes) { + Array.from(attributes).forEach(_ref => { + let { + nodeName, + nodeValue + } = _ref; + if (nodeName && nodeValue && verificationObject.hasOwnProperty(nodeName)) { + let value = nodeValue; + if (typeof verificationObject[nodeName] === 'boolean') { + value = parseBoolean(value); + } + verificationObject[nodeName] = value; + } }); - return Constructor; - } - function _defineProperty(obj, key, value) { - key = _toPropertyKey(key); - if (key in obj) { - Object.defineProperty(obj, key, { - value: value, - enumerable: true, - configurable: true, - writable: true + } +} + +/** + * Merges the data between an unwrapped ad and his wrapper. + * @param {Ad} unwrappedAd - The 'unwrapped' Ad. + * @param {Ad} wrapper - The wrapper Ad. + * @return {void} + */ +function mergeWrapperAdData(unwrappedAd, wrapper) { + var _wrapper$creatives; + unwrappedAd.errorURLTemplates = wrapper.errorURLTemplates.concat(unwrappedAd.errorURLTemplates); + unwrappedAd.impressionURLTemplates = wrapper.impressionURLTemplates.concat(unwrappedAd.impressionURLTemplates); + unwrappedAd.extensions = wrapper.extensions.concat(unwrappedAd.extensions); + if (wrapper.viewableImpression.length > 0) { + unwrappedAd.viewableImpression = [...unwrappedAd.viewableImpression, ...wrapper.viewableImpression]; + } + + // values from the child wrapper will be overridden + unwrappedAd.followAdditionalWrappers = wrapper.followAdditionalWrappers; + unwrappedAd.allowMultipleAds = wrapper.allowMultipleAds; + unwrappedAd.fallbackOnNoAd = wrapper.fallbackOnNoAd; + const wrapperCompanions = (wrapper.creatives || []).filter(creative => creative && creative.type === 'companion'); + const wrapperCompanionClickTracking = wrapperCompanions.reduce((result, creative) => { + (creative.variations || []).forEach(variation => { + (variation.companionClickTrackingURLTemplates || []).forEach(companionClickTrackingURLTemplate => { + if (!util.containsTemplateObject(companionClickTrackingURLTemplate, result)) { + result.push(companionClickTrackingURLTemplate); + } + }); + }); + return result; + }, []); + unwrappedAd.creatives = wrapperCompanions.concat(unwrappedAd.creatives); + const wrapperHasVideoClickTracking = wrapper.videoClickTrackingURLTemplates && wrapper.videoClickTrackingURLTemplates.length; + const wrapperHasVideoCustomClick = wrapper.videoCustomClickURLTemplates && wrapper.videoCustomClickURLTemplates.length; + unwrappedAd.creatives.forEach(creative => { + // merge tracking events + if (wrapper.trackingEvents && wrapper.trackingEvents[creative.type]) { + for (const eventName in wrapper.trackingEvents[creative.type]) { + const urls = wrapper.trackingEvents[creative.type][eventName]; + if (!Array.isArray(creative.trackingEvents[eventName])) { + creative.trackingEvents[eventName] = []; + } + creative.trackingEvents[eventName] = creative.trackingEvents[eventName].concat(urls); + } + } + if (creative.type === 'linear') { + // merge video click tracking url + if (wrapperHasVideoClickTracking) { + creative.videoClickTrackingURLTemplates = creative.videoClickTrackingURLTemplates.concat(wrapper.videoClickTrackingURLTemplates); + } + + // merge video custom click url + if (wrapperHasVideoCustomClick) { + creative.videoCustomClickURLTemplates = creative.videoCustomClickURLTemplates.concat(wrapper.videoCustomClickURLTemplates); + } + + // VAST 2.0 support - Use Wrapper/linear/clickThrough when Inline/Linear/clickThrough is null + if (wrapper.videoClickThroughURLTemplate && (creative.videoClickThroughURLTemplate === null || typeof creative.videoClickThroughURLTemplate === 'undefined')) { + creative.videoClickThroughURLTemplate = wrapper.videoClickThroughURLTemplate; + } + } + + // pass wrapper companion trackers to all companions + if (creative.type === 'companion' && wrapperCompanionClickTracking.length) { + (creative.variations || []).forEach(variation => { + variation.companionClickTrackingURLTemplates = util.joinArrayOfUniqueTemplateObjs(variation.companionClickTrackingURLTemplates, wrapperCompanionClickTracking); }); - } else { - obj[key] = value; } - return obj; + }); + if (wrapper.adVerifications) { + // As specified by VAST specs unwrapped ads should contains wrapper adVerification script + unwrappedAd.adVerifications = unwrappedAd.adVerifications.concat(wrapper.adVerifications); } - function _inherits(subClass, superClass) { - if (typeof superClass !== "function" && superClass !== null) { - throw new TypeError("Super expression must either be null or a function"); + if (wrapper.blockedAdCategories) { + unwrappedAd.blockedAdCategories = unwrappedAd.blockedAdCategories.concat(wrapper.blockedAdCategories); + } + + // Merge Wrapper's creatives containing icon elements + if ((_wrapper$creatives = wrapper.creatives) !== null && _wrapper$creatives !== void 0 && _wrapper$creatives.length) { + // As specified by VAST specs, wrapper should not contain any mediafiles + const wrapperCreativesWithIconsNode = wrapper.creatives.filter(creative => { + var _creative$icons; + return ((_creative$icons = creative.icons) === null || _creative$icons === void 0 ? void 0 : _creative$icons.length) && !creative.mediaFiles.length; + }); + if (wrapperCreativesWithIconsNode.length) { + unwrappedAd.creatives = unwrappedAd.creatives.concat(wrapperCreativesWithIconsNode); + } + } +} +const parserUtils = { + childByName, + childrenByName, + resolveVastAdTagURI, + parseBoolean, + parseNodeText, + copyNodeAttribute, + parseAttributes, + parseDuration, + splitVAST, + assignAttributes, + mergeWrapperAdData +}; + +/** + * This module provides methods to parse a VAST CompanionAd Element. + */ + +/** + * Parses a CompanionAd. + * @param {Object} creativeElement - The VAST CompanionAd element to parse. + * @param {Object} creativeAttributes - The attributes of the CompanionAd (optional). + * @return {Object} creative - The creative object. + */ +function parseCreativeCompanion(creativeElement, creativeAttributes) { + const creative = createCreativeCompanion(creativeAttributes); + creative.required = creativeElement.getAttribute('required') || null; + creative.variations = parserUtils.childrenByName(creativeElement, 'Companion').map(companionResource => { + const companionAd = createCompanionAd(parserUtils.parseAttributes(companionResource)); + companionAd.htmlResources = parserUtils.childrenByName(companionResource, 'HTMLResource').reduce((urls, resource) => { + const url = parserUtils.parseNodeText(resource); + return url ? urls.concat(url) : urls; + }, []); + companionAd.iframeResources = parserUtils.childrenByName(companionResource, 'IFrameResource').reduce((urls, resource) => { + const url = parserUtils.parseNodeText(resource); + return url ? urls.concat(url) : urls; + }, []); + companionAd.staticResources = parserUtils.childrenByName(companionResource, 'StaticResource').reduce((urls, resource) => { + const url = parserUtils.parseNodeText(resource); + return url ? urls.concat({ + url, + creativeType: resource.getAttribute('creativeType') || null + }) : urls; + }, []); + companionAd.altText = parserUtils.parseNodeText(parserUtils.childByName(companionResource, 'AltText')) || null; + const trackingEventsElement = parserUtils.childByName(companionResource, 'TrackingEvents'); + if (trackingEventsElement) { + parserUtils.childrenByName(trackingEventsElement, 'Tracking').forEach(trackingElement => { + const eventName = trackingElement.getAttribute('event'); + const trackingURLTemplate = parserUtils.parseNodeText(trackingElement); + if (eventName && trackingURLTemplate) { + if (!Array.isArray(companionAd.trackingEvents[eventName])) { + companionAd.trackingEvents[eventName] = []; + } + companionAd.trackingEvents[eventName].push(trackingURLTemplate); + } + }); } - subClass.prototype = Object.create(superClass && superClass.prototype, { - constructor: { - value: subClass, - writable: true, - configurable: true - } + companionAd.companionClickTrackingURLTemplates = parserUtils.childrenByName(companionResource, 'CompanionClickTracking').map(clickTrackingElement => { + return { + id: clickTrackingElement.getAttribute('id') || null, + url: parserUtils.parseNodeText(clickTrackingElement) + }; + }); + companionAd.companionClickThroughURLTemplate = parserUtils.parseNodeText(parserUtils.childByName(companionResource, 'CompanionClickThrough')) || null; + const adParametersElement = parserUtils.childByName(companionResource, 'AdParameters'); + if (adParametersElement) { + companionAd.adParameters = { + value: parserUtils.parseNodeText(adParametersElement), + xmlEncoded: adParametersElement.getAttribute('xmlEncoded') || null + }; + } + return companionAd; + }); + return creative; +} + +function createCreativeLinear() { + let creativeAttributes = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + const { + id, + adId, + sequence, + apiFramework + } = createCreative(creativeAttributes); + return { + id, + adId, + sequence, + apiFramework, + type: 'linear', + duration: 0, + skipDelay: null, + mediaFiles: [], + mezzanine: null, + interactiveCreativeFile: null, + closedCaptionFiles: [], + videoClickThroughURLTemplate: null, + videoClickTrackingURLTemplates: [], + videoCustomClickURLTemplates: [], + adParameters: null, + icons: [], + trackingEvents: {} + }; +} +function isCreativeLinear(ad) { + return ad.type === 'linear'; +} + +function createClosedCaptionFile() { + let closedCaptionAttributes = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + return { + type: closedCaptionAttributes.type || null, + language: closedCaptionAttributes.language || null, + fileURL: null + }; +} + +function createIcon() { + return { + program: null, + height: 0, + width: 0, + xPosition: 0, + yPosition: 0, + apiFramework: null, + offset: null, + duration: 0, + type: null, + staticResource: null, + htmlResource: null, + iframeResource: null, + pxratio: '1', + iconClickThroughURLTemplate: null, + iconClickTrackingURLTemplates: [], + iconViewTrackingURLTemplate: null, + iconClickFallbackImages: [] + }; +} + +function createInteractiveCreativeFile() { + let interactiveCreativeAttributes = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + return { + type: interactiveCreativeAttributes.type || null, + apiFramework: interactiveCreativeAttributes.apiFramework || null, + variableDuration: parserUtils.parseBoolean(interactiveCreativeAttributes.variableDuration), + fileURL: null + }; +} + +function createMediaFile() { + return { + id: null, + fileURL: null, + fileSize: 0, + deliveryType: 'progressive', + mimeType: null, + mediaType: null, + codec: null, + bitrate: 0, + minBitrate: 0, + maxBitrate: 0, + width: 0, + height: 0, + apiFramework: null, + // @deprecated in VAST 4.1. should be used instead. + scalable: null, + maintainAspectRatio: null + }; +} + +function createMezzanine() { + return { + id: null, + fileURL: null, + delivery: null, + codec: null, + type: null, + width: 0, + height: 0, + fileSize: 0, + mediaType: '2D' + }; +} + +/** + * This module provides methods to parse a VAST Linear Element. + */ + +/** + * Parses a Linear element. + * @param {Object} creativeElement - The VAST Linear element to parse. + * @param {any} creativeAttributes - The attributes of the Linear (optional). + * @return {Object} creative - The creativeLinear object. + */ +function parseCreativeLinear(creativeElement, creativeAttributes) { + let offset; + const creative = createCreativeLinear(creativeAttributes); + creative.duration = parserUtils.parseDuration(parserUtils.parseNodeText(parserUtils.childByName(creativeElement, 'Duration'))); + const skipOffset = creativeElement.getAttribute('skipoffset'); + if (typeof skipOffset === 'undefined' || skipOffset === null) { + creative.skipDelay = null; + } else if (skipOffset.charAt(skipOffset.length - 1) === '%' && creative.duration !== -1) { + const percent = parseInt(skipOffset, 10); + creative.skipDelay = creative.duration * (percent / 100); + } else { + creative.skipDelay = parserUtils.parseDuration(skipOffset); + } + const videoClicksElement = parserUtils.childByName(creativeElement, 'VideoClicks'); + if (videoClicksElement) { + const videoClickThroughElement = parserUtils.childByName(videoClicksElement, 'ClickThrough'); + if (videoClickThroughElement) { + creative.videoClickThroughURLTemplate = { + id: videoClickThroughElement.getAttribute('id') || null, + url: parserUtils.parseNodeText(videoClickThroughElement) + }; + } else { + creative.videoClickThroughURLTemplate = null; + } + parserUtils.childrenByName(videoClicksElement, 'ClickTracking').forEach(clickTrackingElement => { + creative.videoClickTrackingURLTemplates.push({ + id: clickTrackingElement.getAttribute('id') || null, + url: parserUtils.parseNodeText(clickTrackingElement) + }); }); - Object.defineProperty(subClass, "prototype", { - writable: false + parserUtils.childrenByName(videoClicksElement, 'CustomClick').forEach(customClickElement => { + creative.videoCustomClickURLTemplates.push({ + id: customClickElement.getAttribute('id') || null, + url: parserUtils.parseNodeText(customClickElement) + }); }); - if (superClass) _setPrototypeOf(subClass, superClass); - } - function _getPrototypeOf(o) { - _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function _getPrototypeOf(o) { - return o.__proto__ || Object.getPrototypeOf(o); - }; - return _getPrototypeOf(o); } - function _setPrototypeOf(o, p) { - _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) { - o.__proto__ = p; - return o; + const adParamsElement = parserUtils.childByName(creativeElement, 'AdParameters'); + if (adParamsElement) { + creative.adParameters = { + value: parserUtils.parseNodeText(adParamsElement), + xmlEncoded: adParamsElement.getAttribute('xmlEncoded') || null }; - return _setPrototypeOf(o, p); } - function _isNativeReflectConstruct() { - if (typeof Reflect === "undefined" || !Reflect.construct) return false; - if (Reflect.construct.sham) return false; - if (typeof Proxy === "function") return true; - try { - Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); - return true; - } catch (e) { - return false; + parserUtils.childrenByName(creativeElement, 'TrackingEvents').forEach(trackingEventsElement => { + parserUtils.childrenByName(trackingEventsElement, 'Tracking').forEach(trackingElement => { + let eventName = trackingElement.getAttribute('event'); + const trackingURLTemplate = parserUtils.parseNodeText(trackingElement); + if (eventName && trackingURLTemplate) { + if (eventName === 'progress') { + offset = trackingElement.getAttribute('offset'); + if (!offset) { + return; + } + if (offset.charAt(offset.length - 1) === '%') { + eventName = "progress-".concat(offset); + } else { + eventName = "progress-".concat(Math.round(parserUtils.parseDuration(offset))); + } + } + if (!Array.isArray(creative.trackingEvents[eventName])) { + creative.trackingEvents[eventName] = []; + } + creative.trackingEvents[eventName].push(trackingURLTemplate); + } + }); + }); + parserUtils.childrenByName(creativeElement, 'MediaFiles').forEach(mediaFilesElement => { + parserUtils.childrenByName(mediaFilesElement, 'MediaFile').forEach(mediaFileElement => { + creative.mediaFiles.push(parseMediaFile(mediaFileElement)); + }); + const interactiveCreativeElement = parserUtils.childByName(mediaFilesElement, 'InteractiveCreativeFile'); + if (interactiveCreativeElement) { + creative.interactiveCreativeFile = parseInteractiveCreativeFile(interactiveCreativeElement); + } + const closedCaptionElements = parserUtils.childByName(mediaFilesElement, 'ClosedCaptionFiles'); + if (closedCaptionElements) { + parserUtils.childrenByName(closedCaptionElements, 'ClosedCaptionFile').forEach(closedCaptionElement => { + const closedCaptionFile = createClosedCaptionFile(parserUtils.parseAttributes(closedCaptionElement)); + closedCaptionFile.fileURL = parserUtils.parseNodeText(closedCaptionElement); + creative.closedCaptionFiles.push(closedCaptionFile); + }); } + const mezzanineElement = parserUtils.childByName(mediaFilesElement, 'Mezzanine'); + const requiredAttributes = getRequiredAttributes(mezzanineElement, ['delivery', 'type', 'width', 'height']); + if (requiredAttributes) { + const mezzanine = createMezzanine(); + mezzanine.id = mezzanineElement.getAttribute('id'); + mezzanine.fileURL = parserUtils.parseNodeText(mezzanineElement); + mezzanine.delivery = requiredAttributes.delivery; + mezzanine.codec = mezzanineElement.getAttribute('codec'); + mezzanine.type = requiredAttributes.type; + mezzanine.width = parseInt(requiredAttributes.width, 10); + mezzanine.height = parseInt(requiredAttributes.height, 10); + mezzanine.fileSize = parseInt(mezzanineElement.getAttribute('fileSize'), 10); + mezzanine.mediaType = mezzanineElement.getAttribute('mediaType') || '2D'; + creative.mezzanine = mezzanine; + } + }); + const iconsElement = parserUtils.childByName(creativeElement, 'Icons'); + if (iconsElement) { + parserUtils.childrenByName(iconsElement, 'Icon').forEach(iconElement => { + creative.icons.push(parseIcon(iconElement)); + }); } - function _assertThisInitialized(self) { - if (self === void 0) { - throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); + return creative; +} + +/** + * Parses the MediaFile element from VAST. + * @param {Object} mediaFileElement - The VAST MediaFile element. + * @return {Object} - Parsed mediaFile object. + */ +function parseMediaFile(mediaFileElement) { + const mediaFile = createMediaFile(); + mediaFile.id = mediaFileElement.getAttribute('id'); + mediaFile.fileURL = parserUtils.parseNodeText(mediaFileElement); + mediaFile.deliveryType = mediaFileElement.getAttribute('delivery'); + mediaFile.codec = mediaFileElement.getAttribute('codec'); + mediaFile.mimeType = mediaFileElement.getAttribute('type'); + mediaFile.mediaType = mediaFileElement.getAttribute('mediaType') || '2D'; + mediaFile.apiFramework = mediaFileElement.getAttribute('apiFramework'); + mediaFile.fileSize = parseInt(mediaFileElement.getAttribute('fileSize') || 0); + mediaFile.bitrate = parseInt(mediaFileElement.getAttribute('bitrate') || 0); + mediaFile.minBitrate = parseInt(mediaFileElement.getAttribute('minBitrate') || 0); + mediaFile.maxBitrate = parseInt(mediaFileElement.getAttribute('maxBitrate') || 0); + mediaFile.width = parseInt(mediaFileElement.getAttribute('width') || 0); + mediaFile.height = parseInt(mediaFileElement.getAttribute('height') || 0); + const scalable = mediaFileElement.getAttribute('scalable'); + if (scalable && typeof scalable === 'string') { + mediaFile.scalable = parserUtils.parseBoolean(scalable); + } + const maintainAspectRatio = mediaFileElement.getAttribute('maintainAspectRatio'); + if (maintainAspectRatio && typeof maintainAspectRatio === 'string') { + mediaFile.maintainAspectRatio = parserUtils.parseBoolean(maintainAspectRatio); + } + return mediaFile; +} + +/** + * Parses the InteractiveCreativeFile element from VAST MediaFiles node. + * @param {Object} interactiveCreativeElement - The VAST InteractiveCreativeFile element. + * @return {Object} - Parsed interactiveCreativeFile object. + */ +function parseInteractiveCreativeFile(interactiveCreativeElement) { + const interactiveCreativeFile = createInteractiveCreativeFile(parserUtils.parseAttributes(interactiveCreativeElement)); + interactiveCreativeFile.fileURL = parserUtils.parseNodeText(interactiveCreativeElement); + return interactiveCreativeFile; +} + +/** + * Parses the Icon element from VAST. + * @param {Object} iconElement - The VAST Icon element. + * @return {Object} - Parsed icon object. + */ +function parseIcon(iconElement) { + const icon = createIcon(); + icon.program = iconElement.getAttribute('program'); + icon.height = parseInt(iconElement.getAttribute('height') || 0); + icon.width = parseInt(iconElement.getAttribute('width') || 0); + icon.xPosition = parseXPosition(iconElement.getAttribute('xPosition')); + icon.yPosition = parseYPosition(iconElement.getAttribute('yPosition')); + icon.apiFramework = iconElement.getAttribute('apiFramework'); + icon.pxratio = iconElement.getAttribute('pxratio') || '1'; + icon.offset = parserUtils.parseDuration(iconElement.getAttribute('offset')); + icon.duration = parserUtils.parseDuration(iconElement.getAttribute('duration')); + parserUtils.childrenByName(iconElement, 'HTMLResource').forEach(htmlElement => { + icon.type = htmlElement.getAttribute('creativeType') || 'text/html'; + icon.htmlResource = parserUtils.parseNodeText(htmlElement); + }); + parserUtils.childrenByName(iconElement, 'IFrameResource').forEach(iframeElement => { + icon.type = iframeElement.getAttribute('creativeType') || 0; + icon.iframeResource = parserUtils.parseNodeText(iframeElement); + }); + parserUtils.childrenByName(iconElement, 'StaticResource').forEach(staticElement => { + icon.type = staticElement.getAttribute('creativeType') || 0; + icon.staticResource = parserUtils.parseNodeText(staticElement); + }); + const iconClicksElement = parserUtils.childByName(iconElement, 'IconClicks'); + if (iconClicksElement) { + icon.iconClickThroughURLTemplate = parserUtils.parseNodeText(parserUtils.childByName(iconClicksElement, 'IconClickThrough')); + parserUtils.childrenByName(iconClicksElement, 'IconClickTracking').forEach(iconClickTrackingElement => { + icon.iconClickTrackingURLTemplates.push({ + id: iconClickTrackingElement.getAttribute('id') || null, + url: parserUtils.parseNodeText(iconClickTrackingElement) + }); + }); + const iconClickFallbackImagesElement = parserUtils.childByName(iconClicksElement, 'IconClickFallbackImages'); + if (iconClickFallbackImagesElement) { + parserUtils.childrenByName(iconClickFallbackImagesElement, 'IconClickFallbackImage').forEach(iconClickFallbackImageElement => { + icon.iconClickFallbackImages.push({ + url: parserUtils.parseNodeText(iconClickFallbackImageElement) || null, + width: iconClickFallbackImageElement.getAttribute('width') || null, + height: iconClickFallbackImageElement.getAttribute('height') || null + }); + }); } - return self; } - function _possibleConstructorReturn(self, call) { - if (call && (typeof call === "object" || typeof call === "function")) { - return call; - } else if (call !== void 0) { - throw new TypeError("Derived constructors may only return object or undefined"); + icon.iconViewTrackingURLTemplate = parserUtils.parseNodeText(parserUtils.childByName(iconElement, 'IconViewTracking')); + return icon; +} + +/** + * Parses an horizontal position into a String ('left' or 'right') or into a Number. + * @param {String} xPosition - The x position to parse. + * @return {String|Number} + */ +function parseXPosition(xPosition) { + if (['left', 'right'].indexOf(xPosition) !== -1) { + return xPosition; + } + return parseInt(xPosition || 0); +} + +/** + * Parses an vertical position into a String ('top' or 'bottom') or into a Number. + * @param {String} yPosition - The x position to parse. + * @return {String|Number} + */ +function parseYPosition(yPosition) { + if (['top', 'bottom'].indexOf(yPosition) !== -1) { + return yPosition; + } + return parseInt(yPosition || 0); +} + +/** + * Getting required attributes from element + * @param {Object} element - DOM element + * @param {Array} attributes - list of attributes + * @return {Object|null} null if a least one element not present + */ +function getRequiredAttributes(element, attributes) { + const values = {}; + let error = false; + attributes.forEach(name => { + if (!element || !element.getAttribute(name)) { + error = true; + } else { + values[name] = element.getAttribute(name); + } + }); + return error ? null : values; +} + +function createCreativeNonLinear() { + let creativeAttributes = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + const { + id, + adId, + sequence, + apiFramework + } = createCreative(creativeAttributes); + return { + id, + adId, + sequence, + apiFramework, + type: 'nonlinear', + variations: [], + trackingEvents: {} + }; +} + +function createNonLinearAd() { + return { + id: null, + width: 0, + height: 0, + expandedWidth: 0, + expandedHeight: 0, + scalable: true, + maintainAspectRatio: true, + minSuggestedDuration: 0, + apiFramework: 'static', + adType: 'nonLinearAd', + type: null, + staticResource: null, + htmlResource: null, + iframeResource: null, + nonlinearClickThroughURLTemplate: null, + nonlinearClickTrackingURLTemplates: [], + adParameters: null + }; +} +function isNonLinearAd(ad) { + return ad.adType === 'nonLinearAd'; +} + +/** + * This module provides methods to parse a VAST NonLinear Element. + */ + +/** + * Parses a NonLinear element. + * @param {any} creativeElement - The VAST NonLinear element to parse. + * @param {any} creativeAttributes - The attributes of the NonLinear (optional). + * @return {Object} creative - The CreativeNonLinear object. + */ +function parseCreativeNonLinear(creativeElement, creativeAttributes) { + const creative = createCreativeNonLinear(creativeAttributes); + parserUtils.childrenByName(creativeElement, 'TrackingEvents').forEach(trackingEventsElement => { + let eventName, trackingURLTemplate; + parserUtils.childrenByName(trackingEventsElement, 'Tracking').forEach(trackingElement => { + eventName = trackingElement.getAttribute('event'); + trackingURLTemplate = parserUtils.parseNodeText(trackingElement); + if (eventName && trackingURLTemplate) { + if (!Array.isArray(creative.trackingEvents[eventName])) { + creative.trackingEvents[eventName] = []; + } + creative.trackingEvents[eventName].push(trackingURLTemplate); + } + }); + }); + parserUtils.childrenByName(creativeElement, 'NonLinear').forEach(nonlinearResource => { + const nonlinearAd = createNonLinearAd(); + nonlinearAd.id = nonlinearResource.getAttribute('id') || null; + nonlinearAd.width = nonlinearResource.getAttribute('width'); + nonlinearAd.height = nonlinearResource.getAttribute('height'); + nonlinearAd.expandedWidth = nonlinearResource.getAttribute('expandedWidth'); + nonlinearAd.expandedHeight = nonlinearResource.getAttribute('expandedHeight'); + nonlinearAd.scalable = parserUtils.parseBoolean(nonlinearResource.getAttribute('scalable')); + nonlinearAd.maintainAspectRatio = parserUtils.parseBoolean(nonlinearResource.getAttribute('maintainAspectRatio')); + nonlinearAd.minSuggestedDuration = parserUtils.parseDuration(nonlinearResource.getAttribute('minSuggestedDuration')); + nonlinearAd.apiFramework = nonlinearResource.getAttribute('apiFramework'); + parserUtils.childrenByName(nonlinearResource, 'HTMLResource').forEach(htmlElement => { + nonlinearAd.type = htmlElement.getAttribute('creativeType') || 'text/html'; + nonlinearAd.htmlResource = parserUtils.parseNodeText(htmlElement); + }); + parserUtils.childrenByName(nonlinearResource, 'IFrameResource').forEach(iframeElement => { + nonlinearAd.type = iframeElement.getAttribute('creativeType') || 0; + nonlinearAd.iframeResource = parserUtils.parseNodeText(iframeElement); + }); + parserUtils.childrenByName(nonlinearResource, 'StaticResource').forEach(staticElement => { + nonlinearAd.type = staticElement.getAttribute('creativeType') || 0; + nonlinearAd.staticResource = parserUtils.parseNodeText(staticElement); + }); + const adParamsElement = parserUtils.childByName(nonlinearResource, 'AdParameters'); + if (adParamsElement) { + nonlinearAd.adParameters = { + value: parserUtils.parseNodeText(adParamsElement), + xmlEncoded: adParamsElement.getAttribute('xmlEncoded') || null + }; } - return _assertThisInitialized(self); - } - function _createSuper(Derived) { - var hasNativeReflectConstruct = _isNativeReflectConstruct(); - return function _createSuperInternal() { - var Super = _getPrototypeOf(Derived), - result; - if (hasNativeReflectConstruct) { - var NewTarget = _getPrototypeOf(this).constructor; - result = Reflect.construct(Super, arguments, NewTarget); - } else { - result = Super.apply(this, arguments); + nonlinearAd.nonlinearClickThroughURLTemplate = parserUtils.parseNodeText(parserUtils.childByName(nonlinearResource, 'NonLinearClickThrough')); + parserUtils.childrenByName(nonlinearResource, 'NonLinearClickTracking').forEach(clickTrackingElement => { + nonlinearAd.nonlinearClickTrackingURLTemplates.push({ + id: clickTrackingElement.getAttribute('id') || null, + url: parserUtils.parseNodeText(clickTrackingElement) + }); + }); + creative.variations.push(nonlinearAd); + }); + return creative; +} + +function createExtension() { + return { + name: null, + value: null, + attributes: {}, + children: [] + }; +} +function isEmptyExtension(extension) { + return extension.value === null && Object.keys(extension.attributes).length === 0 && extension.children.length === 0; +} + +/** + * Parses an array of Extension elements. + * @param {Node[]} extensions - The array of extensions to parse. + * @param {String} type - The type of extensions to parse.(Ad|Creative) + * @return {AdExtension[]|CreativeExtension[]} - The nodes parsed to extensions + */ +function parseExtensions(extensions) { + const exts = []; + extensions.forEach(extNode => { + const ext = _parseExtension(extNode); + if (ext) { + exts.push(ext); + } + }); + return exts; +} + +/** + * Parses an extension child node + * @param {Node} extNode - The extension node to parse + * @return {AdExtension|CreativeExtension|null} - The node parsed to extension + */ +function _parseExtension(extNode) { + // Ignore comments + if (extNode.nodeName === '#comment') return null; + const ext = createExtension(); + const extNodeAttrs = extNode.attributes; + const childNodes = extNode.childNodes; + ext.name = extNode.nodeName; + + // Parse attributes + if (extNode.attributes) { + for (const extNodeAttrKey in extNodeAttrs) { + if (extNodeAttrs.hasOwnProperty(extNodeAttrKey)) { + const extNodeAttr = extNodeAttrs[extNodeAttrKey]; + if (extNodeAttr.nodeName && extNodeAttr.nodeValue) { + ext.attributes[extNodeAttr.nodeName] = extNodeAttr.nodeValue; + } + } + } + } + + // Parse all children + for (const childNodeKey in childNodes) { + if (childNodes.hasOwnProperty(childNodeKey)) { + const parsedChild = _parseExtension(childNodes[childNodeKey]); + if (parsedChild) { + ext.children.push(parsedChild); } - return _possibleConstructorReturn(this, result); - }; - } - function _toConsumableArray(arr) { - return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); - } - function _arrayWithoutHoles(arr) { - if (Array.isArray(arr)) return _arrayLikeToArray(arr); - } - function _iterableToArray(iter) { - if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); - } - function _unsupportedIterableToArray(o, minLen) { - if (!o) return; - if (typeof o === "string") return _arrayLikeToArray(o, minLen); - var n = Object.prototype.toString.call(o).slice(8, -1); - if (n === "Object" && o.constructor) n = o.constructor.name; - if (n === "Map" || n === "Set") return Array.from(o); - if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); - } - function _arrayLikeToArray(arr, len) { - if (len == null || len > arr.length) len = arr.length; - for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; - return arr2; - } - function _nonIterableSpread() { - throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); - } - function _toPrimitive(input, hint) { - if (typeof input !== "object" || input === null) return input; - var prim = input[Symbol.toPrimitive]; - if (prim !== undefined) { - var res = prim.call(input, hint || "default"); - if (typeof res !== "object") return res; - throw new TypeError("@@toPrimitive must return a primitive value."); } - return (hint === "string" ? String : Number)(input); - } - function _toPropertyKey(arg) { - var key = _toPrimitive(arg, "string"); - return typeof key === "symbol" ? key : String(key); - } - - function createAd() { - var adAttributes = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; - return { - id: adAttributes.id || null, - sequence: adAttributes.sequence || null, - adType: adAttributes.adType || null, - adServingId: null, - categories: [], - expires: null, - viewableImpression: [], - system: null, - title: null, - description: null, - advertiser: null, - pricing: null, - survey: null, - // @deprecated in VAST 4.1 - errorURLTemplates: [], - impressionURLTemplates: [], - creatives: [], - extensions: [], - adVerifications: [], - blockedAdCategories: [], - followAdditionalWrappers: true, - allowMultipleAds: false, - fallbackOnNoAd: null - }; } - function createAdVerification() { - return { - resource: null, - vendor: null, - browserOptional: false, - apiFramework: null, - type: null, - parameters: null, - trackingEvents: {} + /* + Only parse value of Nodes with only eather no children or only a cdata or text + to avoid useless parsing that would result to a concatenation of all children + */ + if (ext.children.length === 0 || ext.children.length === 1 && ['#cdata-section', '#text'].indexOf(ext.children[0].name) >= 0) { + const txt = parserUtils.parseNodeText(extNode); + if (txt !== '') { + ext.value = txt; + } + + // Remove the children if it's a cdata or simply text to avoid useless children + ext.children = []; + } + + // Only return not empty objects to not pollute extentions + return isEmptyExtension(ext) ? null : ext; +} + +/** + * Parses the creatives from the Creatives Node. + * @param {any} creativeNodes - The creative nodes to parse. + * @return {Array} - An array of Creative objects. + */ +function parseCreatives(creativeNodes) { + const creatives = []; + creativeNodes.forEach(creativeElement => { + const creativeAttributes = { + id: creativeElement.getAttribute('id') || null, + adId: parseCreativeAdIdAttribute(creativeElement), + sequence: creativeElement.getAttribute('sequence') || null, + apiFramework: creativeElement.getAttribute('apiFramework') || null }; + const universalAdIds = []; + const universalAdIdElements = parserUtils.childrenByName(creativeElement, 'UniversalAdId'); + universalAdIdElements.forEach(universalAdIdElement => { + const universalAdId = { + idRegistry: universalAdIdElement.getAttribute('idRegistry') || 'unknown', + value: parserUtils.parseNodeText(universalAdIdElement) + }; + universalAdIds.push(universalAdId); + }); + let creativeExtensions; + const creativeExtensionsElement = parserUtils.childByName(creativeElement, 'CreativeExtensions'); + if (creativeExtensionsElement) { + creativeExtensions = parseExtensions(parserUtils.childrenByName(creativeExtensionsElement, 'CreativeExtension')); + } + for (const creativeTypeElementKey in creativeElement.childNodes) { + const creativeTypeElement = creativeElement.childNodes[creativeTypeElementKey]; + let parsedCreative; + switch (creativeTypeElement.nodeName) { + case 'Linear': + parsedCreative = parseCreativeLinear(creativeTypeElement, creativeAttributes); + break; + case 'NonLinearAds': + parsedCreative = parseCreativeNonLinear(creativeTypeElement, creativeAttributes); + break; + case 'CompanionAds': + parsedCreative = parseCreativeCompanion(creativeTypeElement, creativeAttributes); + break; + } + if (parsedCreative) { + if (universalAdIds) { + parsedCreative.universalAdIds = universalAdIds; + } + if (creativeExtensions) { + parsedCreative.creativeExtensions = creativeExtensions; + } + creatives.push(parsedCreative); + } + } + }); + return creatives; +} + +/** + * Parses the creative adId Attribute. + * @param {any} creativeElement - The creative element to retrieve the adId from. + * @return {String|null} + */ +function parseCreativeAdIdAttribute(creativeElement) { + return creativeElement.getAttribute('AdID') || + // VAST 2 spec + creativeElement.getAttribute('adID') || + // VAST 3 spec + creativeElement.getAttribute('adId') || + // VAST 4 spec + null; +} + +const requiredValues = { + Wrapper: { + subElements: ['VASTAdTagURI', 'Impression'] + }, + BlockedAdCategories: { + attributes: ['authority'] + }, + InLine: { + subElements: ['AdSystem', 'AdTitle', 'Impression', 'AdServingId', 'Creatives'] + }, + Category: { + attributes: ['authority'] + }, + Pricing: { + attributes: ['model', 'currency'] + }, + Verification: { + oneOfinLineResources: ['JavaScriptResource', 'ExecutableResource'], + attributes: ['vendor'] + }, + UniversalAdId: { + attributes: ['idRegistry'] + }, + JavaScriptResource: { + attributes: ['apiFramework', 'browserOptional'] + }, + ExecutableResource: { + attributes: ['apiFramework', 'type'] + }, + Tracking: { + attributes: ['event'] + }, + Creatives: { + subElements: ['Creative'] + }, + Creative: { + subElements: ['UniversalAdId'] + }, + Linear: { + subElements: ['MediaFiles', 'Duration'] + }, + MediaFiles: { + subElements: ['MediaFile'] + }, + MediaFile: { + attributes: ['delivery', 'type', 'width', 'height'] + }, + Mezzanine: { + attributes: ['delivery', 'type', 'width', 'height'] + }, + NonLinear: { + oneOfinLineResources: ['StaticResource', 'IFrameResource', 'HTMLResource'], + attributes: ['width', 'height'] + }, + Companion: { + oneOfinLineResources: ['StaticResource', 'IFrameResource', 'HTMLResource'], + attributes: ['width', 'height'] + }, + StaticResource: { + attributes: ['creativeType'] + }, + Icons: { + subElements: ['Icon'] + }, + Icon: { + oneOfinLineResources: ['StaticResource', 'IFrameResource', 'HTMLResource'] + } +}; + +/** + * Verify node required values and also verify recursively all his child nodes. + * Trigger warnings if a node required value is missing. + * @param {Node} node - The node element. + * @param {Function} emit - Emit function used to trigger Warning event. + * @emits VASTParser#VAST-warning + * @param {undefined|Boolean} [isAdInline] - Passed recursively to itself. True if the node is contained inside a inLine tag. + */ +function verifyRequiredValues(node, emit, isAdInline) { + if (!node || !node.nodeName) { + return; + } + if (node.nodeName === 'InLine') { + isAdInline = true; + } + verifyRequiredAttributes(node, emit); + if (hasSubElements(node)) { + verifyRequiredSubElements(node, emit, isAdInline); + for (let i = 0; i < node.children.length; i++) { + verifyRequiredValues(node.children[i], emit, isAdInline); + } + } else if (parserUtils.parseNodeText(node).length === 0) { + emitMissingValueWarning({ + name: node.nodeName, + parentName: node.parentNode.nodeName + }, emit); + } +} + +/** + * Verify and trigger warnings if node required attributes are not set. + * @param {Node} node - The node element. + * @param {Function} emit - Emit function used to trigger Warning event. + * @emits VASTParser#VAST-warning + */ +function verifyRequiredAttributes(node, emit) { + if (!requiredValues[node.nodeName] || !requiredValues[node.nodeName].attributes) { + return; + } + const requiredAttributes = requiredValues[node.nodeName].attributes; + const missingAttributes = requiredAttributes.filter(attributeName => !node.getAttribute(attributeName)); + if (missingAttributes.length > 0) { + emitMissingValueWarning({ + name: node.nodeName, + parentName: node.parentNode.nodeName, + attributes: missingAttributes + }, emit); + } +} + +/** + * Verify and trigger warnings if node required sub element are not set. + * @param {Node} node - The node element + * @param {Boolean} isAdInline - True if node is contained in a inline + * @param {Function} emit - Emit function used to trigger Warning event. + * @emits VASTParser#VAST-warning + */ +function verifyRequiredSubElements(node, emit, isAdInline) { + const required = requiredValues[node.nodeName]; + // Do not verify subelement if node is a child of wrapper, but verify it if node is the Wrapper itself + // Wrapper child have no required subElement. (Only InLine does) + const isInWrapperButNotWrapperItself = !isAdInline && node.nodeName !== 'Wrapper'; + if (!required || isInWrapperButNotWrapperItself) { + return; + } + if (required.subElements) { + const requiredSubElements = required.subElements; + const missingSubElements = requiredSubElements.filter(subElementName => !parserUtils.childByName(node, subElementName)); + if (missingSubElements.length > 0) { + emitMissingValueWarning({ + name: node.nodeName, + parentName: node.parentNode.nodeName, + subElements: missingSubElements + }, emit); + } } - function createCompanionAd() { - var creativeAttributes = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; - return { - id: creativeAttributes.id || null, - adType: 'companionAd', - width: creativeAttributes.width || 0, - height: creativeAttributes.height || 0, - assetWidth: creativeAttributes.assetWidth || null, - assetHeight: creativeAttributes.assetHeight || null, - expandedWidth: creativeAttributes.expandedWidth || null, - expandedHeight: creativeAttributes.expandedHeight || null, - apiFramework: creativeAttributes.apiFramework || null, - adSlotId: creativeAttributes.adSlotId || null, - pxratio: creativeAttributes.pxratio || '1', - renderingMode: creativeAttributes.renderingMode || 'default', - staticResources: [], - htmlResources: [], - iframeResources: [], - adParameters: null, - altText: null, - companionClickThroughURLTemplate: null, - companionClickTrackingURLTemplates: [], - trackingEvents: {} - }; - } - function isCompanionAd(ad) { - return ad.adType === 'companionAd'; + // When InLine format is used some nodes (i.e , , or ) + // require at least one of the following resources: StaticResource, IFrameResource, HTMLResource + if (!isAdInline || !required.oneOfinLineResources) { + return; + } + const resourceFound = required.oneOfinLineResources.some(resource => { + return parserUtils.childByName(node, resource); + }); + if (!resourceFound) { + emitMissingValueWarning({ + name: node.nodeName, + parentName: node.parentNode.nodeName, + oneOfResources: required.oneOfinLineResources + }, emit); + } +} + +/** + * Check if a node has sub elements. + * @param {Node} node - The node element. + * @returns {Boolean} + */ +function hasSubElements(node) { + return node.children && node.children.length !== 0; +} + +/** + * Trigger Warning if a element is empty or has missing attributes/subelements/resources + * @param {Object} missingElement - Object containing missing elements and values + * @param {String} missingElement.name - The name of element containing missing values + * @param {String} missingElement.parentName - The parent name of element containing missing values + * @param {Array} missingElement.attributes - The array of missing attributes + * @param {Array} missingElement.subElements - The array of missing sub elements + * @param {Array} missingElement.oneOfResources - The array of resources in which at least one must be provided by the element + * @param {Function} emit - Emit function used to trigger Warning event. + * @emits VastParser#VAST-warning + */ +function emitMissingValueWarning(_ref, emit) { + let { + name, + parentName, + attributes, + subElements, + oneOfResources + } = _ref; + let message = "Element '".concat(name, "'"); + if (attributes) { + message += " missing required attribute(s) '".concat(attributes.join(', '), "' "); + } else if (subElements) { + message += " missing required sub element(s) '".concat(subElements.join(', '), "' "); + } else if (oneOfResources) { + message += " must provide one of the following '".concat(oneOfResources.join(', '), "' "); + } else { + message += " is empty"; + } + emit('VAST-warning', { + message, + parentElement: parentName, + specVersion: 4.1 + }); +} +const parserVerification = { + verifyRequiredValues, + hasSubElements, + emitMissingValueWarning, + verifyRequiredAttributes, + verifyRequiredSubElements +}; + +/** + * This module provides methods to parse a VAST Ad Element. + */ + +/** + * Parses an Ad element (can either be a Wrapper or an InLine). + * @param {Object} adElement - The VAST Ad element to parse. + * @param {Function} emit - Emit function used to trigger Warning event + * @param {Object} options - An optional Object of parameters to be used in the parsing process. + * @emits VASTParser#VAST-warning + * @return {Object|undefined} - Object containing the ad and if it is wrapper/inline + */ +function parseAd(adElement, emit) { + let { + allowMultipleAds, + followAdditionalWrappers + } = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; + const childNodes = Array.from(adElement.childNodes); + const filteredChildNodes = childNodes.filter(childNode => { + const childNodeToLowerCase = childNode.nodeName.toLowerCase(); + return childNodeToLowerCase === 'inline' || followAdditionalWrappers !== false && childNodeToLowerCase === 'wrapper'; + }); + for (const node of filteredChildNodes) { + parserUtils.copyNodeAttribute('id', adElement, node); + parserUtils.copyNodeAttribute('sequence', adElement, node); + parserUtils.copyNodeAttribute('adType', adElement, node); + if (node.nodeName === 'Wrapper') { + return { + ad: parseWrapper(node, emit), + type: 'WRAPPER' + }; + } else if (node.nodeName === 'InLine') { + return { + ad: parseInLine(node, emit, { + allowMultipleAds + }), + type: 'INLINE' + }; + } + const wrongNode = node.nodeName.toLowerCase(); + const message = wrongNode === 'inline' ? "<".concat(node.nodeName, "> must be written ") : "<".concat(node.nodeName, "> must be written "); + emit('VAST-warning', { + message, + wrongNode: node + }); } - - function createCreative() { - var creativeAttributes = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; - return { - id: creativeAttributes.id || null, - adId: creativeAttributes.adId || null, - sequence: creativeAttributes.sequence || null, - apiFramework: creativeAttributes.apiFramework || null, - universalAdIds: [], - creativeExtensions: [] - }; +} + +/** + * Parses an Inline + * @param {Object} adElement Element - The VAST Inline element to parse. + * @param {Function} emit - Emit function used to trigger Warning event. + * @param {Object} options - An optional Object of parameters to be used in the parsing process. + * @emits VASTParser#VAST-warning + * @return {Object} ad - The ad object. + */ +function parseInLine(adElement, emit) { + let { + allowMultipleAds + } = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; + // if allowMultipleAds is set to false by wrapper attribute + // only the first stand-alone Ad (with no sequence values) in the + // requested VAST response is allowed so we won't parse ads with sequence + if (allowMultipleAds === false && adElement.getAttribute('sequence')) { + return null; + } + return parseAdElement(adElement, emit); +} + +/** + * Parses an ad type (Inline or Wrapper) + * @param {Object} adTypeElement - The VAST Inline or Wrapper element to parse. + * @param {Function} emit - Emit function used to trigger Warning event. + * @emits VASTParser#VAST-warning + * @return {Object} ad - The ad object. + */ +function parseAdElement(adTypeElement, emit) { + let adVerificationsFromExtensions = []; + if (emit) { + parserVerification.verifyRequiredValues(adTypeElement, emit); + } + const childNodes = Array.from(adTypeElement.childNodes); + const ad = createAd(parserUtils.parseAttributes(adTypeElement)); + childNodes.forEach(node => { + switch (node.nodeName) { + case 'Error': + ad.errorURLTemplates.push(parserUtils.parseNodeText(node)); + break; + case 'Impression': + ad.impressionURLTemplates.push({ + id: node.getAttribute('id') || null, + url: parserUtils.parseNodeText(node) + }); + break; + case 'Creatives': + ad.creatives = parseCreatives(parserUtils.childrenByName(node, 'Creative')); + break; + case 'Extensions': + { + const extNodes = parserUtils.childrenByName(node, 'Extension'); + ad.extensions = parseExtensions(extNodes); + + /* + OMID specify adVerifications should be in extensions for VAST < 4.0 + To avoid to put them on two different places in two different format we reparse it + from extensions the same way than for an AdVerifications node. + */ + if (!ad.adVerifications.length) { + adVerificationsFromExtensions = _parseAdVerificationsFromExtensions(extNodes); + } + break; + } + case 'AdVerifications': + ad.adVerifications = _parseAdVerifications(parserUtils.childrenByName(node, 'Verification')); + break; + case 'AdSystem': + ad.system = { + value: parserUtils.parseNodeText(node), + version: node.getAttribute('version') || null + }; + break; + case 'AdTitle': + ad.title = parserUtils.parseNodeText(node); + break; + case 'AdServingId': + ad.adServingId = parserUtils.parseNodeText(node); + break; + case 'Category': + ad.categories.push({ + authority: node.getAttribute('authority') || null, + value: parserUtils.parseNodeText(node) + }); + break; + case 'Expires': + ad.expires = parseInt(parserUtils.parseNodeText(node), 10); + break; + case 'ViewableImpression': + ad.viewableImpression.push(_parseViewableImpression(node)); + break; + case 'Description': + ad.description = parserUtils.parseNodeText(node); + break; + case 'Advertiser': + ad.advertiser = { + id: node.getAttribute('id') || null, + value: parserUtils.parseNodeText(node) + }; + break; + case 'Pricing': + ad.pricing = { + value: parserUtils.parseNodeText(node), + model: node.getAttribute('model') || null, + currency: node.getAttribute('currency') || null + }; + break; + case 'Survey': + ad.survey = { + value: parserUtils.parseNodeText(node), + type: node.getAttribute('type') || null + }; + break; + case 'BlockedAdCategories': + ad.blockedAdCategories.push({ + authority: node.getAttribute('authority') || null, + value: parserUtils.parseNodeText(node) + }); + break; + } + }); + if (adVerificationsFromExtensions.length) { + ad.adVerifications = ad.adVerifications.concat(adVerificationsFromExtensions); + } + return ad; +} + +/** + * Parses a Wrapper element without resolving the wrapped urls. + * @param {Object} wrapperElement - The VAST Wrapper element to be parsed. + * @param {Function} emit - Emit function used to trigger Warning event. + * @emits VASTParser#VAST-warning + * @return {Ad} + */ +function parseWrapper(wrapperElement, emit) { + const ad = parseAdElement(wrapperElement, emit); + const followAdditionalWrappersValue = wrapperElement.getAttribute('followAdditionalWrappers'); + const allowMultipleAdsValue = wrapperElement.getAttribute('allowMultipleAds'); + const fallbackOnNoAdValue = wrapperElement.getAttribute('fallbackOnNoAd'); + ad.followAdditionalWrappers = followAdditionalWrappersValue ? parserUtils.parseBoolean(followAdditionalWrappersValue) : true; + ad.allowMultipleAds = allowMultipleAdsValue ? parserUtils.parseBoolean(allowMultipleAdsValue) : false; + ad.fallbackOnNoAd = fallbackOnNoAdValue ? parserUtils.parseBoolean(fallbackOnNoAdValue) : null; + let wrapperURLElement = parserUtils.childByName(wrapperElement, 'VASTAdTagURI'); + if (wrapperURLElement) { + ad.nextWrapperURL = parserUtils.parseNodeText(wrapperURLElement); + } else { + wrapperURLElement = parserUtils.childByName(wrapperElement, 'VASTAdTagURL'); + if (wrapperURLElement) { + ad.nextWrapperURL = parserUtils.parseNodeText(parserUtils.childByName(wrapperURLElement, 'URL')); + } } - - function createCreativeCompanion() { - var creativeAttributes = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; - var _createCreative = createCreative(creativeAttributes), - id = _createCreative.id, - adId = _createCreative.adId, - sequence = _createCreative.sequence, - apiFramework = _createCreative.apiFramework; - return { - id: id, - adId: adId, - sequence: sequence, - apiFramework: apiFramework, - type: 'companion', - required: null, - variations: [] - }; + ad.creatives.forEach(wrapperCreativeElement => { + if (['linear', 'nonlinear'].includes(wrapperCreativeElement.type)) { + // TrackingEvents Linear / NonLinear + if (wrapperCreativeElement.trackingEvents) { + if (!ad.trackingEvents) { + ad.trackingEvents = {}; + } + if (!ad.trackingEvents[wrapperCreativeElement.type]) { + ad.trackingEvents[wrapperCreativeElement.type] = {}; + } + for (const eventName in wrapperCreativeElement.trackingEvents) { + const urls = wrapperCreativeElement.trackingEvents[eventName]; + if (!Array.isArray(ad.trackingEvents[wrapperCreativeElement.type][eventName])) { + ad.trackingEvents[wrapperCreativeElement.type][eventName] = []; + } + urls.forEach(url => { + ad.trackingEvents[wrapperCreativeElement.type][eventName].push(url); + }); + } + } + // ClickTracking + if (wrapperCreativeElement.videoClickTrackingURLTemplates) { + if (!Array.isArray(ad.videoClickTrackingURLTemplates)) { + ad.videoClickTrackingURLTemplates = []; + } // tmp property to save wrapper tracking URLs until they are merged + wrapperCreativeElement.videoClickTrackingURLTemplates.forEach(item => { + ad.videoClickTrackingURLTemplates.push(item); + }); + } + // ClickThrough + if (wrapperCreativeElement.videoClickThroughURLTemplate) { + ad.videoClickThroughURLTemplate = wrapperCreativeElement.videoClickThroughURLTemplate; + } + // CustomClick + if (wrapperCreativeElement.videoCustomClickURLTemplates) { + if (!Array.isArray(ad.videoCustomClickURLTemplates)) { + ad.videoCustomClickURLTemplates = []; + } // tmp property to save wrapper tracking URLs until they are merged + wrapperCreativeElement.videoCustomClickURLTemplates.forEach(item => { + ad.videoCustomClickURLTemplates.push(item); + }); + } + } + }); + if (ad.nextWrapperURL) { + return ad; } - - var supportedMacros = ['ADCATEGORIES', 'ADCOUNT', 'ADPLAYHEAD', 'ADSERVINGID', 'ADTYPE', 'APIFRAMEWORKS', 'APPBUNDLE', 'ASSETURI', 'BLOCKEDADCATEGORIES', 'BREAKMAXADLENGTH', 'BREAKMAXADS', 'BREAKMAXDURATION', 'BREAKMINADLENGTH', 'BREAKMINDURATION', 'BREAKPOSITION', 'CLICKPOS', 'CLICKTYPE', 'CLIENTUA', 'CONTENTID', 'CONTENTPLAYHEAD', - // @deprecated VAST 4.1 - 'CONTENTURI', 'DEVICEIP', 'DEVICEUA', 'DOMAIN', 'EXTENSIONS', 'GDPRCONSENT', 'IFA', 'IFATYPE', 'INVENTORYSTATE', 'LATLONG', 'LIMITADTRACKING', 'MEDIAMIME', 'MEDIAPLAYHEAD', 'OMIDPARTNER', 'PAGEURL', 'PLACEMENTTYPE', 'PLAYERCAPABILITIES', 'PLAYERSIZE', 'PLAYERSTATE', 'PODSEQUENCE', 'REGULATIONS', 'SERVERSIDE', 'SERVERUA', 'TRANSACTIONID', 'UNIVERSALADID', 'VASTVERSIONS', 'VERIFICATIONVENDORS']; - - function track(URLTemplates, macros, options) { - var URLs = resolveURLTemplates(URLTemplates, macros, options); - URLs.forEach(function (URL) { - if (typeof window !== 'undefined' && window !== null) { - var i = new Image(); - i.src = URL; +} + +/** + * Parses the AdVerifications Element. + * @param {Array} verifications - The array of verifications to parse. + * @return {Array} + */ +function _parseAdVerifications(verifications) { + const ver = []; + verifications.forEach(verificationNode => { + const verification = createAdVerification(); + const childNodes = Array.from(verificationNode.childNodes); + parserUtils.assignAttributes(verificationNode.attributes, verification); + childNodes.forEach(_ref => { + let { + nodeName, + textContent, + attributes + } = _ref; + switch (nodeName) { + case 'JavaScriptResource': + case 'ExecutableResource': + verification.resource = textContent.trim(); + parserUtils.assignAttributes(attributes, verification); + break; + case 'VerificationParameters': + verification.parameters = textContent.trim(); + break; } }); - } - - /** - * Replace the provided URLTemplates with the given values - * - * @param {Array} URLTemplates - An array of tracking url templates. - * @param {Object} [macros={}] - An optional Object of parameters to be used in the tracking calls. - * @param {Object} [options={}] - An optional Object of options to be used in the tracking calls. - */ - function resolveURLTemplates(URLTemplates) { - var macros = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; - var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; - var resolvedURLs = []; - var URLArray = extractURLsFromTemplates(URLTemplates); - - // Set default value for invalid ERRORCODE - if (macros['ERRORCODE'] && !options.isCustomCode && !/^[0-9]{3}$/.test(macros['ERRORCODE'])) { - macros['ERRORCODE'] = 900; + const trackingEventsElement = parserUtils.childByName(verificationNode, 'TrackingEvents'); + if (trackingEventsElement) { + parserUtils.childrenByName(trackingEventsElement, 'Tracking').forEach(trackingElement => { + const eventName = trackingElement.getAttribute('event'); + const trackingURLTemplate = parserUtils.parseNodeText(trackingElement); + if (eventName && trackingURLTemplate) { + if (!Array.isArray(verification.trackingEvents[eventName])) { + verification.trackingEvents[eventName] = []; + } + verification.trackingEvents[eventName].push(trackingURLTemplate); + } + }); } + ver.push(verification); + }); + return ver; +} + +/** + * Parses the AdVerifications Element from extension for versions < 4.0 + * @param {Array} extensions - The array of extensions to parse. + * @return {Array} + */ +function _parseAdVerificationsFromExtensions(extensions) { + let adVerificationsNode = null, + adVerifications = []; + + // Find the first (and only) AdVerifications node from extensions + extensions.some(extension => { + return adVerificationsNode = parserUtils.childByName(extension, 'AdVerifications'); + }); + + // Parse it if we get it + if (adVerificationsNode) { + adVerifications = _parseAdVerifications(parserUtils.childrenByName(adVerificationsNode, 'Verification')); + } + return adVerifications; +} + +/** + * Parses the ViewableImpression Element. + * @param {Object} viewableImpressionNode - The ViewableImpression node element. + * @return {Object} viewableImpression - The viewableImpression object + */ +function _parseViewableImpression(viewableImpressionNode) { + const regroupNodesUrl = (urls, node) => { + const url = parserUtils.parseNodeText(node); + url && urls.push(url); + return urls; + }; + return { + id: viewableImpressionNode.getAttribute('id') || null, + viewable: parserUtils.childrenByName(viewableImpressionNode, 'Viewable').reduce(regroupNodesUrl, []), + notViewable: parserUtils.childrenByName(viewableImpressionNode, 'NotViewable').reduce(regroupNodesUrl, []), + viewUndetermined: parserUtils.childrenByName(viewableImpressionNode, 'ViewUndetermined').reduce(regroupNodesUrl, []) + }; +} - // Calc random/time based macros - macros['CACHEBUSTING'] = addLeadingZeros(Math.round(Math.random() * 1.0e8)); - macros['TIMESTAMP'] = new Date().toISOString(); - - // RANDOM/random is not defined in VAST 3/4 as a valid macro tho it's used by some adServer (Auditude) - macros['RANDOM'] = macros['random'] = macros['CACHEBUSTING']; - for (var macro in macros) { - macros[macro] = encodeURIComponentRFC3986(macros[macro]); - } - for (var URLTemplateKey in URLArray) { - var resolveURL = URLArray[URLTemplateKey]; - if (typeof resolveURL !== 'string') { - continue; - } - resolvedURLs.push(replaceUrlMacros(resolveURL, macros)); - } - return resolvedURLs; +class EventEmitter { + constructor() { + this._handlers = []; } /** - * Replace the macros tracking url with their value. - * If no value is provided for a supported macro and it exists in the url, - * it will be replaced by -1 as described by the VAST 4.1 iab specifications - * - * @param {String} url - Tracking url. - * @param {Object} macros - Object of macros to be replaced in the tracking calls + * Adds the event name and handler function to the end of the handlers array. + * No checks are made to see if the handler has already been added. + * Multiple calls passing the same combination of event name and handler will result in the handler being added, + * and called, multiple times. + * @param {String} event + * @param {Function} handler + * @returns {EventEmitter} */ - function replaceUrlMacros(url, macros) { - url = replaceMacrosValues(url, macros); - // match any macros from the url that was not replaced - var remainingMacros = url.match(/[^[\]]+(?=])/g); - if (!remainingMacros) { - return url; + on(event, handler) { + if (typeof handler !== 'function') { + throw new TypeError("The handler argument must be of type Function. Received type ".concat(typeof handler)); } - var supportedRemainingMacros = remainingMacros.filter(function (macro) { - return supportedMacros.indexOf(macro) > -1; - }); - if (supportedRemainingMacros.length === 0) { - return url; + if (!event) { + throw new TypeError("The event argument must be of type String. Received type ".concat(typeof event)); } - supportedRemainingMacros = supportedRemainingMacros.reduce(function (accumulator, macro) { - accumulator[macro] = -1; - return accumulator; - }, {}); - return replaceMacrosValues(url, supportedRemainingMacros); + this._handlers.push({ + event, + handler + }); + return this; } /** - * Replace the macros tracking url with their value. - * - * @param {String} url - Tracking url. - * @param {Object} macros - Object of macros to be replaced in the tracking calls + * Adds a one-time handler function for the named event. + * The next time event is triggered, this handler is removed and then invoked. + * @param {String} event + * @param {Function} handler + * @returns {EventEmitter} */ - function replaceMacrosValues(url, macros) { - var replacedMacrosUrl = url; - for (var key in macros) { - var value = macros[key]; - // this will match [${key}] and %%${key}%% and replace it - replacedMacrosUrl = replacedMacrosUrl.replace(new RegExp("(?:\\[|%%)(".concat(key, ")(?:\\]|%%)"), 'g'), value); - } - return replacedMacrosUrl; + once(event, handler) { + return this.on(event, onceWrap(this, event, handler)); } /** - * Extract the url/s from the URLTemplates. - * If the URLTemplates is an array of urls - * If the URLTemplates object has a url property - * If the URLTemplates is a single string - * - * @param {Array|String} URLTemplates - An array|string of url templates. + * Removes all instances for the specified handler from the handler array for the named event. + * @param {String} event + * @param {Function} handler + * @returns {EventEmitter} */ - function extractURLsFromTemplates(URLTemplates) { - if (Array.isArray(URLTemplates)) { - return URLTemplates.map(function (URLTemplate) { - return URLTemplate && URLTemplate.hasOwnProperty('url') ? URLTemplate.url : URLTemplate; - }); - } - return URLTemplates; + off(event, handler) { + this._handlers = this._handlers.filter(item => { + return item.event !== event || item.handler !== handler; + }); + return this; } /** - * Filter URLTemplates elements to keep only valid and safe URL templates. - * To be valid, urls should: - * - have the same protocol as the client - * or - * - be protocol-relative urls - * - * @param {Array} URLTemplates - A Array of string/object containing urls templates. + * Synchronously calls each of the handlers registered for the named event, + * in the order they were registered, passing the supplied arguments to each. + * @param {String} event + * @param {...any} args list of arguments that will be used by the event handler + * @returns {Boolean} true if the event had handlers, false otherwise. */ - function filterValidUrlTemplates(URLTemplates) { - if (Array.isArray(URLTemplates)) { - return URLTemplates.filter(function (urlTemplate) { - var url = urlTemplate.hasOwnProperty('url') ? urlTemplate.url : urlTemplate; - return isValidUrl(url); - }); + emit(event) { + for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { + args[_key - 1] = arguments[_key]; } - return isValidUrl(URLTemplates); - } - function isValidUrl(url) { - var regex = /^(https?:\/\/|\/\/)/; - return regex.test(url); - } - - /** - * Returns a boolean after checking if the object exists in the array. - * true - if the object exists, false otherwise - * - * @param {Object} obj - The object who existence is to be checked. - * @param {Array} list - List of objects. - */ - function containsTemplateObject(obj, list) { - for (var i = 0; i < list.length; i++) { - if (isTemplateObjectEqual(list[i], obj)) { - return true; + let called = false; + this._handlers.forEach(item => { + if (item.event === '*') { + called = true; + item.handler(event, ...args); } - } - return false; + if (item.event === event) { + called = true; + item.handler(...args); + } + }); + return called; } /** - * Returns a boolean after comparing two Template objects. - * true - if the objects are equivalent, false otherwise - * - * @param {Object} obj1 - * @param {Object} obj2 + * Removes all listeners, or those of the specified named event. + * @param {String} event + * @returns {EventEmitter} */ - function isTemplateObjectEqual(obj1, obj2) { - if (obj1 && obj2) { - var obj1Properties = Object.getOwnPropertyNames(obj1); - var obj2Properties = Object.getOwnPropertyNames(obj2); - - // If number of properties is different, objects are not equivalent - if (obj1Properties.length !== obj2Properties.length) { - return false; - } - if (obj1.id !== obj2.id || obj1.url !== obj2.url) { - return false; - } - return true; + removeAllListeners(event) { + if (!event) { + this._handlers = []; + return this; } - return false; - } - - // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent - function encodeURIComponentRFC3986(str) { - return encodeURIComponent(str).replace(/[!'()*]/g, function (c) { - return "%".concat(c.charCodeAt(0).toString(16)); - }); + this._handlers = this._handlers.filter(item => item.event !== event); + return this; } /** - * Return a string of the input number with leading zeros defined by the length param - * - * @param {Number} input - number to convert - * @param {Number} length - length of the desired string - * - * @return {String} + * Returns the number of listeners listening to the named event. + * @param {String} event + * @returns {Number} */ - function addLeadingZeros(input) { - var length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 8; - return input.toString().padStart(length, '0'); - } - function isNumeric(n) { - return !isNaN(parseFloat(n)) && isFinite(n); - } - function flatten(arr) { - return arr.reduce(function (flat, toFlatten) { - return flat.concat(Array.isArray(toFlatten) ? flatten(toFlatten) : toFlatten); - }, []); + listenerCount(event) { + return this._handlers.filter(item => item.event === event).length; } /** - * Joins two arrays of objects without duplicates - * - * @param {Array} arr1 - * @param {Array} arr2 - * - * @return {Array} + * Returns a copy of the array of listeners for the named event including those created by .once(). + * @param {String} event + * @returns {Function[]} */ - function joinArrayOfUniqueTemplateObjs() { - var arr1 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; - var arr2 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : []; - var firstArr = Array.isArray(arr1) ? arr1 : []; - var secondArr = Array.isArray(arr2) ? arr2 : []; - var arr = firstArr.concat(secondArr); - return arr.reduce(function (res, val) { - if (!containsTemplateObject(val, res)) { - res.push(val); + listeners(event) { + return this._handlers.reduce((listeners, item) => { + if (item.event === event) { + listeners.push(item.handler); } - return res; + return listeners; }, []); } /** - * Check if a provided value is a valid time value according to the IAB definition - * Check if a provided value is a valid time value according to the IAB definition: Must be a positive number or -1. - * if not implemented by ad unit or -2 if value is unknown. - * @param {Number} time - * - * @return {Boolean} + * Returns an array listing the events for which the emitter has registered handlers. + * @returns {String[]} */ - function isValidTimeValue(time) { - return Number.isFinite(time) && time >= -2; - } - var util = { - track: track, - resolveURLTemplates: resolveURLTemplates, - extractURLsFromTemplates: extractURLsFromTemplates, - filterValidUrlTemplates: filterValidUrlTemplates, - containsTemplateObject: containsTemplateObject, - isTemplateObjectEqual: isTemplateObjectEqual, - encodeURIComponentRFC3986: encodeURIComponentRFC3986, - replaceUrlMacros: replaceUrlMacros, - isNumeric: isNumeric, - flatten: flatten, - joinArrayOfUniqueTemplateObjs: joinArrayOfUniqueTemplateObjs, - isValidTimeValue: isValidTimeValue, - addLeadingZeros: addLeadingZeros + eventNames() { + return this._handlers.map(item => item.event); + } +} +function onceWrap(target, event, handler) { + const state = { + fired: false, + wrapFn: undefined }; - + function onceWrapper() { + if (!state.fired) { + target.off(event, state.wrapFn); + state.fired = true; + handler.bind(target)(...arguments); + } + } + state.wrapFn = onceWrapper; + return onceWrapper; +} + +function createVASTResponse(_ref) { + let { + ads, + errorURLTemplates, + version + } = _ref; + return { + ads: ads || [], + errorURLTemplates: errorURLTemplates || [], + version: version || null + }; +} + +/* + We decided to put the estimated bitrate separated from classes to persist it between different instances of vast client/parser +*/ + +let estimatedBitrateCount = 0; +let estimatedBitrate = 0; + +/** + * Calculate average estimated bitrate from the previous values and new entries + * @param {Number} byteLength - The length of the response in bytes. + * @param {Number} duration - The duration of the request in ms. + */ +const updateEstimatedBitrate = (byteLength, duration) => { + if (!byteLength || !duration || byteLength <= 0 || duration <= 0) { + return; + } + + // We want the bitrate in kb/s, byteLength are in bytes and duration in ms, just need to convert the byteLength because kb/s = b/ms + const bitrate = byteLength * 8 / duration; + estimatedBitrate = (estimatedBitrate * estimatedBitrateCount + bitrate) / ++estimatedBitrateCount; +}; + +const DEFAULT_MAX_WRAPPER_DEPTH = 10; +const DEFAULT_EVENT_DATA = { + ERRORCODE: 900, + extensions: [] +}; + +/** + * This class provides methods to fetch and parse a VAST document. + * @export + * @class VASTParser + * @extends EventEmitter + */ +class VASTParser extends EventEmitter { /** - * This module provides support methods to the parsing classes. + * Creates an instance of VASTParser. + * @constructor */ + constructor() { + let { + fetcher + } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + super(); + this.maxWrapperDepth = null; + this.remainingAds = []; + this.fetcher = fetcher || null; + } /** - * Returns the first element of the given node which nodeName matches the given name. - * @param {Node} node - The node to use to find a match. - * @param {String} name - The name to look for. - * @return {Object|undefined} + * Tracks the error provided in the errorCode parameter and emits a VAST-error event for the given error. + * @param {Array} urlTemplates - An Array of url templates to use to make the tracking call. + * @param {Object} errorCode - An Object containing the error data. + * @param {Object} data - One (or more) Object containing additional data. + * @emits VASTParser#VAST-error + * @return {void} */ - function childByName(node, name) { - var childNodes = node.childNodes; - for (var childKey in childNodes) { - var child = childNodes[childKey]; - if (child.nodeName === name) { - return child; - } + trackVastError(urlTemplates, errorCode) { + for (var _len = arguments.length, data = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) { + data[_key - 2] = arguments[_key]; } + this.emit('VAST-error', Object.assign({}, DEFAULT_EVENT_DATA, errorCode, ...data)); + util.track(urlTemplates, errorCode); } /** - * Returns all the elements of the given node which nodeName match the given name. - * @param {Node} node - The node to use to find the matches. - * @param {String} name - The name to look for. + * Returns an array of errorURLTemplates for the VAST being parsed. * @return {Array} */ - function childrenByName(node, name) { - var children = []; - var childNodes = node.childNodes; - for (var childKey in childNodes) { - var child = childNodes[childKey]; - if (child.nodeName === name) { - children.push(child); - } - } - return children; + getErrorURLTemplates() { + return this.rootErrorURLTemplates.concat(this.errorURLTemplates); } /** - * Converts relative vastAdTagUri. - * @param {String} vastAdTagUrl - The url to resolve. - * @param {String} originalUrl - The original url. - * @return {String} + * Returns the estimated bitrate calculated from all previous requests + * @returns The average of all estimated bitrates in kb/s. */ - function resolveVastAdTagURI(vastAdTagUrl, originalUrl) { - if (!originalUrl) { - return vastAdTagUrl; - } - if (vastAdTagUrl.indexOf('//') === 0) { - var _location = location, - protocol = _location.protocol; - return "".concat(protocol).concat(vastAdTagUrl); - } - if (vastAdTagUrl.indexOf('://') === -1) { - // Resolve relative URLs (mainly for unit testing) - var baseURL = originalUrl.slice(0, originalUrl.lastIndexOf('/')); - return "".concat(baseURL, "/").concat(vastAdTagUrl); - } - return vastAdTagUrl; + getEstimatedBitrate() { + return estimatedBitrate; } /** - * Converts a boolean string into a Boolean. - * @param {String} booleanString - The boolean string to convert. - * @return {Boolean} + * Inits the parsing properties of the class with the custom values provided as options. + * @param {Object} options - The options to initialize a parsing sequence */ - function parseBoolean(booleanString) { - return ['true', 'TRUE', 'True', '1'].indexOf(booleanString) !== -1; + initParsingStatus() { + let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + this.maxWrapperDepth = options.wrapperLimit || DEFAULT_MAX_WRAPPER_DEPTH; + this.parsingOptions = { + allowMultipleAds: options.allowMultipleAds + }; + this.rootURL = ''; + this.resetParsingStatus(); + updateEstimatedBitrate(options.byteLength, options.requestDuration); } /** - * Parses a node text (for legacy support). - * @param {Object} node - The node to parse the text from. - * @return {String} + * Reset the parsing property of the class everytime a VAST is parsed */ - function parseNodeText(node) { - return node && (node.textContent || node.text || '').trim(); + resetParsingStatus() { + this.errorURLTemplates = []; + this.rootErrorURLTemplates = []; + this.vastVersion = null; } - /** - * Copies an attribute from a node to another. - * @param {String} attributeName - The name of the attribute to clone. - * @param {Object} nodeSource - The source node to copy the attribute from. - * @param {Object} nodeDestination - The destination node to copy the attribute at. + * Resolves the next group of ads. If all is true resolves all the remaining ads. + * @param {Boolean} all - If true all the remaining ads are resolved + * @return {Promise} */ - function copyNodeAttribute(attributeName, nodeSource, nodeDestination) { - var attributeValue = nodeSource.getAttribute(attributeName); - if (attributeValue) { - nodeDestination.setAttribute(attributeName, attributeValue); - } + getRemainingAds(all) { + if (this.remainingAds.length === 0) { + return Promise.reject(new Error('No more ads are available for the given VAST')); + } + const ads = all ? util.flatten(this.remainingAds) : this.remainingAds.shift(); + this.errorURLTemplates = []; + return this.resolveAds(ads, { + wrapperDepth: 0, + url: this.rootURL + }).then(resolvedAds => { + return this.buildVASTResponse(resolvedAds); + }); } /** - * Converts element attributes into an object, where object key is attribute name - * and object value is attribute value - * @param {Element} element - * @returns {Object} + * Parses the given xml Object into a VASTResponse. + * Returns a Promise which resolves with a fully parsed VASTResponse or rejects with an Error. + * @param {Object} vastXml - An object representing a vast xml document. + * @param {Object} options - An optional Object of parameters to be used in the parsing process. + * @emits VASTParser#VAST-resolving + * @emits VASTParser#VAST-resolved + * @emits VASTParser#VAST-warning + * @return {Promise} */ - function parseAttributes(element) { - var nodeAttributes = element.attributes; - var attributes = {}; - for (var i = 0; i < nodeAttributes.length; i++) { - attributes[nodeAttributes[i].nodeName] = nodeAttributes[i].nodeValue; - } - return attributes; + parseVAST(vastXml) { + let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + this.initParsingStatus(options); + options.isRootVAST = true; + return this.parse(vastXml, options).then(ads => { + return this.buildVASTResponse(ads); + }); } /** - * Parses a String duration into a Number. - * @param {String} durationString - The dureation represented as a string. - * @return {Number} + * Builds a VASTResponse which can be returned. + * @param {Array} ads - An Array of unwrapped ads + * @return {Object} */ - function parseDuration(durationString) { - if (durationString === null || typeof durationString === 'undefined') { - return -1; - } - // Some VAST doesn't have an HH:MM:SS duration format but instead jus the number of seconds - if (util.isNumeric(durationString)) { - return parseInt(durationString); - } - var durationComponents = durationString.split(':'); - if (durationComponents.length !== 3) { - return -1; - } - var secondsAndMS = durationComponents[2].split('.'); - var seconds = parseInt(secondsAndMS[0]); - if (secondsAndMS.length === 2) { - seconds += parseFloat("0.".concat(secondsAndMS[1])); - } - var minutes = parseInt(durationComponents[1] * 60); - var hours = parseInt(durationComponents[0] * 60 * 60); - if (isNaN(hours) || isNaN(minutes) || isNaN(seconds) || minutes > 60 * 60 || seconds > 60) { - return -1; - } - return hours + minutes + seconds; + buildVASTResponse(ads) { + const response = createVASTResponse({ + ads, + errorURLTemplates: this.getErrorURLTemplates(), + version: this.vastVersion + }); + this.completeWrapperResolving(response); + return response; } /** - * Splits an Array of ads into an Array of Arrays of ads. - * Each subarray contains either one ad or multiple ads (an AdPod) - * @param {Array} ads - An Array of ads to split + * Parses the given xml Object into an array of ads + * Returns the array or throws an `Error` if an invalid VAST XML is provided + * @param {Object} vastXml - An object representing an xml document. + * @param {Object} options - An optional Object of parameters to be used in the parsing process. + * @emits VASTParser#VAST-warning + * @emits VASTParser#VAST-ad-parsed * @return {Array} + * @throws {Error} `vastXml` must be a valid VAST XMLDocument */ - function splitVAST(ads) { - var splittedVAST = []; - var lastAdPod = null; - ads.forEach(function (ad, i) { - if (ad.sequence) { - ad.sequence = parseInt(ad.sequence, 10); - } - // The current Ad may be the next Ad of an AdPod - if (ad.sequence > 1) { - var lastAd = ads[i - 1]; - // check if the current Ad is exactly the next one in the AdPod - if (lastAd && lastAd.sequence === ad.sequence - 1) { - lastAdPod && lastAdPod.push(ad); - return; - } - // If the ad had a sequence attribute but it was not part of a correctly formed - // AdPod, let's remove the sequence attribute - delete ad.sequence; - } - lastAdPod = [ad]; - splittedVAST.push(lastAdPod); - }); - return splittedVAST; - } + parseVastXml(vastXml, _ref) { + let { + isRootVAST = false, + url = null, + wrapperDepth = 0, + allowMultipleAds, + followAdditionalWrappers + } = _ref; + // check if is a valid VAST document + if (!vastXml || !vastXml.documentElement || vastXml.documentElement.nodeName !== 'VAST') { + this.emit('VAST-ad-parsed', { + type: 'ERROR', + url, + wrapperDepth + }); + throw new Error('Invalid VAST XMLDocument'); + } + const ads = []; + const childNodes = vastXml.documentElement.childNodes; - /** - * Parses the attributes and assign them to object - * @param {Object} attributes attribute - * @param {Object} verificationObject with properties which can be assigned - */ - function assignAttributes(attributes, verificationObject) { - if (attributes) { - for (var attrKey in attributes) { - var attribute = attributes[attrKey]; - if (attribute.nodeName && attribute.nodeValue && verificationObject.hasOwnProperty(attribute.nodeName)) { - var value = attribute.nodeValue; - if (typeof verificationObject[attribute.nodeName] === 'boolean') { - value = parseBoolean(value); - } - verificationObject[attribute.nodeName] = value; + /* Only parse the version of the Root VAST for now because we don't know yet how to + * handle some cases like multiple wrappers in the same vast + */ + const vastVersion = vastXml.documentElement.getAttribute('version'); + if (isRootVAST) { + if (vastVersion) this.vastVersion = vastVersion; + } + // Fill the VASTResponse object with ads and errorURLTemplates + for (const nodeKey in childNodes) { + const node = childNodes[nodeKey]; + if (node.nodeName === 'Error') { + const errorURLTemplate = parserUtils.parseNodeText(node); + + // Distinguish root VAST url templates from ad specific ones + isRootVAST ? this.rootErrorURLTemplates.push(errorURLTemplate) : this.errorURLTemplates.push(errorURLTemplate); + } else if (node.nodeName === 'Ad') { + // allowMultipleAds was introduced in VAST 3 + // for retrocompatibility set it to true + if (this.vastVersion && parseFloat(this.vastVersion) < 3) { + allowMultipleAds = true; + } else if (allowMultipleAds === false && ads.length > 1) { + // if wrapper allowMultipleAds is set to false only the first stand-alone Ad + // (with no sequence values) in the requested VAST response is allowed + break; + } + const result = parseAd(node, this.emit.bind(this), { + allowMultipleAds, + followAdditionalWrappers + }); + if (result.ad) { + ads.push(result.ad); + this.emit('VAST-ad-parsed', { + type: result.type, + url, + wrapperDepth, + adIndex: ads.length - 1, + vastVersion + }); + } else { + // VAST version of response not supported. + this.trackVastError(this.getErrorURLTemplates(), { + ERRORCODE: 101 + }); } } } + return ads; } /** - * Merges the data between an unwrapped ad and his wrapper. - * @param {Ad} unwrappedAd - The 'unwrapped' Ad. - * @param {Ad} wrapper - The wrapper Ad. - * @return {void} + * Parses the given xml Object into an array of unwrapped ads. + * Returns a Promise which resolves with the array or rejects with an error according to the result of the parsing. + * @param {Object} vastXml - An object representing an xml document. + * @param {Object} options - An optional Object of parameters to be used in the parsing process. + * @emits VASTParser#VAST-resolving + * @emits VASTParser#VAST-resolved + * @emits VASTParser#VAST-warning + * @return {Promise} */ - function mergeWrapperAdData(unwrappedAd, wrapper) { - var _wrapper$creatives; - unwrappedAd.errorURLTemplates = wrapper.errorURLTemplates.concat(unwrappedAd.errorURLTemplates); - unwrappedAd.impressionURLTemplates = wrapper.impressionURLTemplates.concat(unwrappedAd.impressionURLTemplates); - unwrappedAd.extensions = wrapper.extensions.concat(unwrappedAd.extensions); - if (wrapper.viewableImpression.length > 0) { - unwrappedAd.viewableImpression = [].concat(_toConsumableArray(unwrappedAd.viewableImpression), _toConsumableArray(wrapper.viewableImpression)); + parse(vastXml) { + let { + url = null, + resolveAll = true, + wrapperSequence = null, + previousUrl = null, + wrapperDepth = 0, + isRootVAST = false, + followAdditionalWrappers, + allowMultipleAds + } = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + let ads = []; + // allowMultipleAds was introduced in VAST 3 as wrapper attribute + // for retrocompatibility set it to true for vast pre-version 3 + if (this.vastVersion && parseFloat(this.vastVersion) < 3 && isRootVAST) { + allowMultipleAds = true; } - - // values from the child wrapper will be overridden - unwrappedAd.followAdditionalWrappers = wrapper.followAdditionalWrappers; - unwrappedAd.allowMultipleAds = wrapper.allowMultipleAds; - unwrappedAd.fallbackOnNoAd = wrapper.fallbackOnNoAd; - var wrapperCompanions = (wrapper.creatives || []).filter(function (creative) { - return creative && creative.type === 'companion'; - }); - var wrapperCompanionClickTracking = wrapperCompanions.reduce(function (result, creative) { - (creative.variations || []).forEach(function (variation) { - (variation.companionClickTrackingURLTemplates || []).forEach(function (companionClickTrackingURLTemplate) { - if (!util.containsTemplateObject(companionClickTrackingURLTemplate, result)) { - result.push(companionClickTrackingURLTemplate); - } - }); + try { + ads = this.parseVastXml(vastXml, { + isRootVAST, + url, + wrapperDepth, + allowMultipleAds, + followAdditionalWrappers }); - return result; - }, []); - unwrappedAd.creatives = wrapperCompanions.concat(unwrappedAd.creatives); - var wrapperHasVideoClickTracking = wrapper.videoClickTrackingURLTemplates && wrapper.videoClickTrackingURLTemplates.length; - var wrapperHasVideoCustomClick = wrapper.videoCustomClickURLTemplates && wrapper.videoCustomClickURLTemplates.length; - unwrappedAd.creatives.forEach(function (creative) { - // merge tracking events - if (wrapper.trackingEvents && wrapper.trackingEvents[creative.type]) { - for (var eventName in wrapper.trackingEvents[creative.type]) { - var urls = wrapper.trackingEvents[creative.type][eventName]; - if (!Array.isArray(creative.trackingEvents[eventName])) { - creative.trackingEvents[eventName] = []; - } - creative.trackingEvents[eventName] = creative.trackingEvents[eventName].concat(urls); - } - } - if (creative.type === 'linear') { - // merge video click tracking url - if (wrapperHasVideoClickTracking) { - creative.videoClickTrackingURLTemplates = creative.videoClickTrackingURLTemplates.concat(wrapper.videoClickTrackingURLTemplates); - } + } catch (e) { + return Promise.reject(e); + } + + /* Keep wrapper sequence value to not break AdPod when wrapper contain only one Ad. + e.g,for a AdPod containing : + - Inline with sequence=1 + - Inline with sequence=2 + - Wrapper with sequence=3 wrapping a Inline with sequence=1 + once parsed we will obtain : + - Inline sequence 1, + - Inline sequence 2, + - Inline sequence 3 + */ + if (ads.length === 1 && wrapperSequence !== undefined && wrapperSequence !== null) { + ads[0].sequence = wrapperSequence; + } - // merge video custom click url - if (wrapperHasVideoCustomClick) { - creative.videoCustomClickURLTemplates = creative.videoCustomClickURLTemplates.concat(wrapper.videoCustomClickURLTemplates); - } + // Split the VAST in case we don't want to resolve everything at the first time + if (resolveAll === false) { + this.remainingAds = parserUtils.splitVAST(ads); + // Remove the first element from the remaining ads array, since we're going to resolve that element + ads = this.remainingAds.shift(); + } + return this.resolveAds(ads, { + wrapperDepth, + previousUrl, + url + }); + } - // VAST 2.0 support - Use Wrapper/linear/clickThrough when Inline/Linear/clickThrough is null - if (wrapper.videoClickThroughURLTemplate && (creative.videoClickThroughURLTemplate === null || typeof creative.videoClickThroughURLTemplate === 'undefined')) { - creative.videoClickThroughURLTemplate = wrapper.videoClickThroughURLTemplate; - } + /** + * Resolves an Array of ads, recursively calling itself with the remaining ads if a no ad + * response is returned for the given array. + * @param {Array} ads - An array of ads to resolve + * @param {Object} options - An options Object containing resolving parameters + * @return {Promise} + */ + resolveAds() { + let ads = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; + let { + wrapperDepth, + previousUrl, + url + } = arguments.length > 1 ? arguments[1] : undefined; + const resolveWrappersPromises = []; + previousUrl = url; + ads.forEach(ad => { + const resolveWrappersPromise = this.resolveWrappers(ad, wrapperDepth, previousUrl); + resolveWrappersPromises.push(resolveWrappersPromise); + }); + return Promise.all(resolveWrappersPromises).then(unwrappedAds => { + const resolvedAds = util.flatten(unwrappedAds); + if (!resolvedAds.length && this.remainingAds.length > 0) { + const remainingAdsToResolve = this.remainingAds.shift(); + return this.resolveAds(remainingAdsToResolve, { + wrapperDepth, + previousUrl, + url + }); } + return resolvedAds; + }); + } - // pass wrapper companion trackers to all companions - if (creative.type === 'companion' && wrapperCompanionClickTracking.length) { - (creative.variations || []).forEach(function (variation) { - variation.companionClickTrackingURLTemplates = util.joinArrayOfUniqueTemplateObjs(variation.companionClickTrackingURLTemplates, wrapperCompanionClickTracking); + /** + * Resolves the wrappers for the given ad in a recursive way. + * Returns a Promise which resolves with the unwrapped ad or rejects with an error. + * @param {Object} adToUnWrap - An ad object to be unwrapped. + * @param {Number} wrapperDepth - The reached depth in the wrapper resolving chain. + * @param {String} previousUrl - The previous vast url. + * @return {Promise} + */ + resolveWrappers(adToUnWrap, wrapperDepth, previousUrl) { + // Copy ad from parameters to prevent altering given object outside of function scope + const ad = { + ...adToUnWrap + }; + return new Promise(resolve => { + var _this$parsingOptions$; + // Going one level deeper in the wrapper chain + wrapperDepth++; + // We already have a resolved VAST ad, no need to resolve wrapper + if (!ad.nextWrapperURL) { + delete ad.nextWrapperURL; + return resolve(ad); + } + if (!this.fetcher) { + ad.VASTAdTagURI = ad.nextWrapperURL; + delete ad.nextWrapperURL; + return resolve(ad); + } + if (wrapperDepth >= this.maxWrapperDepth) { + // Wrapper limit reached, as defined by the video player. + // Too many Wrapper responses have been received with no InLine response. + ad.errorCode = 302; + delete ad.nextWrapperURL; + return resolve(ad); + } + + // Get full URL + ad.nextWrapperURL = parserUtils.resolveVastAdTagURI(ad.nextWrapperURL, previousUrl); + + // If allowMultipleAds is set inside the parameter 'option' of public method + // override the vast value by the one provided + const allowMultipleAds = (_this$parsingOptions$ = this.parsingOptions.allowMultipleAds) !== null && _this$parsingOptions$ !== void 0 ? _this$parsingOptions$ : ad.allowMultipleAds; + // sequence doesn't carry over in wrapper element + const wrapperSequence = ad.sequence; + this.fetcher.fetchVAST({ + url: ad.nextWrapperURL, + emitter: this.emit.bind(this), + maxWrapperDepth: this.maxWrapperDepth + }).then(xml => { + return this.parse(xml, { + url: ad.nextWrapperURL, + previousUrl, + wrapperSequence, + wrapperDepth, + followAdditionalWrappers: ad.followAdditionalWrappers, + allowMultipleAds + }).then(unwrappedAds => { + delete ad.nextWrapperURL; + if (unwrappedAds.length === 0) { + // No ads returned by the wrappedResponse, discard current creatives + ad.creatives = []; + return resolve(ad); + } + unwrappedAds.forEach(unwrappedAd => { + if (unwrappedAd) { + parserUtils.mergeWrapperAdData(unwrappedAd, ad); + } + }); + resolve(unwrappedAds); }); - } + }).catch(err => { + // Timeout of VAST URI provided in Wrapper element, or of VAST URI provided in a subsequent Wrapper element. + // (URI was either unavailable or reached a timeout as defined by the video player.) + ad.errorCode = 301; + ad.errorMessage = err.message; + resolve(ad); + }); }); - if (wrapper.adVerifications) { - // As specified by VAST specs unwrapped ads should contains wrapper adVerification script - unwrappedAd.adVerifications = unwrappedAd.adVerifications.concat(wrapper.adVerifications); - } - if (wrapper.blockedAdCategories) { - unwrappedAd.blockedAdCategories = unwrappedAd.blockedAdCategories.concat(wrapper.blockedAdCategories); - } + } - // Merge Wrapper's creatives containing icon elements - if ((_wrapper$creatives = wrapper.creatives) !== null && _wrapper$creatives !== void 0 && _wrapper$creatives.length) { - // As specified by VAST specs, wrapper should not contain any mediafiles - var wrapperCreativesWithIconsNode = wrapper.creatives.filter(function (creative) { - var _creative$icons; - return ((_creative$icons = creative.icons) === null || _creative$icons === void 0 ? void 0 : _creative$icons.length) && !creative.mediaFiles.length; + /** + * Takes care of handling errors when the wrappers are resolved. + * @param {Object} vastResponse - A resolved VASTResponse. + */ + completeWrapperResolving(vastResponse) { + // We've to wait for all elements to be parsed before handling error so we can: + // - Send computed extensions data + // - Ping all URIs defined across VAST files + + // No Ad case - The parser never bump into an element + if (vastResponse.ads.length === 0) { + this.trackVastError(vastResponse.errorURLTemplates, { + ERRORCODE: 303 }); - if (wrapperCreativesWithIconsNode.length) { - unwrappedAd.creatives = unwrappedAd.creatives.concat(wrapperCreativesWithIconsNode); - } - } + } else { + for (let index = vastResponse.ads.length - 1; index >= 0; index--) { + // - Error encountered while parsing + // - No Creative case - The parser has dealt with soma or/and an elements + // but no creative was found + const ad = vastResponse.ads[index]; + if ((ad.errorCode || ad.creatives.length === 0) && !ad.VASTAdTagURI) { + // If VASTAdTagURI is in the vastResponse, it means we are dealing with a Wrapper when using parseVAST from the VASTParser. + // In that case, we dont want to modify the vastResponse since the creatives node is not required in a wrapper. + this.trackVastError(ad.errorURLTemplates.concat(vastResponse.errorURLTemplates), { + ERRORCODE: ad.errorCode || 303 + }, { + ERRORMESSAGE: ad.errorMessage || '' + }, { + extensions: ad.extensions + }, { + system: ad.system + }); + vastResponse.ads.splice(index, 1); + } + } + } + } +} + +let storage = null; + +/** + * This Object represents a default storage to be used in case no other storage is available. + * @constant + * @type {Object} + */ +const DEFAULT_STORAGE = { + data: {}, + length: 0, + getItem(key) { + return this.data[key]; + }, + setItem(key, value) { + this.data[key] = value; + this.length = Object.keys(this.data).length; + }, + removeItem(key) { + delete this.data[key]; + this.length = Object.keys(this.data).length; + }, + clear() { + this.data = {}; + this.length = 0; + } +}; + +/** + * This class provides an wrapper interface to the a key-value storage. + * It uses localStorage, sessionStorage or a custom storage if none of the two is available. + * @export + * @class Storage + */ +class Storage { + /** + * Creates an instance of Storage. + * @constructor + */ + constructor() { + this.storage = this.initStorage(); } - var parserUtils = { - childByName: childByName, - childrenByName: childrenByName, - resolveVastAdTagURI: resolveVastAdTagURI, - parseBoolean: parseBoolean, - parseNodeText: parseNodeText, - copyNodeAttribute: copyNodeAttribute, - parseAttributes: parseAttributes, - parseDuration: parseDuration, - splitVAST: splitVAST, - assignAttributes: assignAttributes, - mergeWrapperAdData: mergeWrapperAdData - }; /** - * This module provides methods to parse a VAST CompanionAd Element. + * Provides a singleton instance of the wrapped storage. + * @return {Object} */ + initStorage() { + if (storage) { + return storage; + } + try { + storage = typeof window !== 'undefined' && window !== null ? window.localStorage || window.sessionStorage : null; + } catch (storageError) { + storage = null; + } + if (!storage || this.isStorageDisabled(storage)) { + storage = DEFAULT_STORAGE; + storage.clear(); + } + return storage; + } /** - * Parses a CompanionAd. - * @param {Object} creativeElement - The VAST CompanionAd element to parse. - * @param {Object} creativeAttributes - The attributes of the CompanionAd (optional). - * @return {Object} creative - The creative object. + * Check if storage is disabled (like in certain cases with private browsing). + * In Safari (Mac + iOS) when private browsing is ON, localStorage is read only + * http://spin.atomicobject.com/2013/01/23/ios-private-browsing-localstorage/ + * @param {Object} testStorage - The storage to check. + * @return {Boolean} */ - function parseCreativeCompanion(creativeElement, creativeAttributes) { - var creative = createCreativeCompanion(creativeAttributes); - creative.required = creativeElement.getAttribute('required') || null; - creative.variations = parserUtils.childrenByName(creativeElement, 'Companion').map(function (companionResource) { - var companionAd = createCompanionAd(parserUtils.parseAttributes(companionResource)); - companionAd.htmlResources = parserUtils.childrenByName(companionResource, 'HTMLResource').reduce(function (urls, resource) { - var url = parserUtils.parseNodeText(resource); - return url ? urls.concat(url) : urls; - }, []); - companionAd.iframeResources = parserUtils.childrenByName(companionResource, 'IFrameResource').reduce(function (urls, resource) { - var url = parserUtils.parseNodeText(resource); - return url ? urls.concat(url) : urls; - }, []); - companionAd.staticResources = parserUtils.childrenByName(companionResource, 'StaticResource').reduce(function (urls, resource) { - var url = parserUtils.parseNodeText(resource); - return url ? urls.concat({ - url: url, - creativeType: resource.getAttribute('creativeType') || null - }) : urls; - }, []); - companionAd.altText = parserUtils.parseNodeText(parserUtils.childByName(companionResource, 'AltText')) || null; - var trackingEventsElement = parserUtils.childByName(companionResource, 'TrackingEvents'); - if (trackingEventsElement) { - parserUtils.childrenByName(trackingEventsElement, 'Tracking').forEach(function (trackingElement) { - var eventName = trackingElement.getAttribute('event'); - var trackingURLTemplate = parserUtils.parseNodeText(trackingElement); - if (eventName && trackingURLTemplate) { - if (!Array.isArray(companionAd.trackingEvents[eventName])) { - companionAd.trackingEvents[eventName] = []; - } - companionAd.trackingEvents[eventName].push(trackingURLTemplate); - } - }); - } - companionAd.companionClickTrackingURLTemplates = parserUtils.childrenByName(companionResource, 'CompanionClickTracking').map(function (clickTrackingElement) { - return { - id: clickTrackingElement.getAttribute('id') || null, - url: parserUtils.parseNodeText(clickTrackingElement) - }; - }); - companionAd.companionClickThroughURLTemplate = parserUtils.parseNodeText(parserUtils.childByName(companionResource, 'CompanionClickThrough')) || null; - var adParametersElement = parserUtils.childByName(companionResource, 'AdParameters'); - if (adParametersElement) { - companionAd.adParameters = { - value: parserUtils.parseNodeText(adParametersElement), - xmlEncoded: adParametersElement.getAttribute('xmlEncoded') || null - }; + isStorageDisabled(testStorage) { + const testValue = '__VASTStorage__'; + try { + testStorage.setItem(testValue, testValue); + if (testStorage.getItem(testValue) !== testValue) { + testStorage.removeItem(testValue); + return true; } - return companionAd; - }); - return creative; + } catch (e) { + return true; + } + testStorage.removeItem(testValue); + return false; } - function createCreativeLinear() { - var creativeAttributes = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; - var _createCreative = createCreative(creativeAttributes), - id = _createCreative.id, - adId = _createCreative.adId, - sequence = _createCreative.sequence, - apiFramework = _createCreative.apiFramework; - return { - id: id, - adId: adId, - sequence: sequence, - apiFramework: apiFramework, - type: 'linear', - duration: 0, - skipDelay: null, - mediaFiles: [], - mezzanine: null, - interactiveCreativeFile: null, - closedCaptionFiles: [], - videoClickThroughURLTemplate: null, - videoClickTrackingURLTemplates: [], - videoCustomClickURLTemplates: [], - adParameters: null, - icons: [], - trackingEvents: {} - }; - } - function isCreativeLinear(ad) { - return ad.type === 'linear'; + /** + * Returns the value for the given key. If the key does not exist, null is returned. + * @param {String} key - The key to retrieve the value. + * @return {any} + */ + getItem(key) { + return this.storage.getItem(key); } - function createClosedCaptionFile() { - var closedCaptionAttributes = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; - return { - type: closedCaptionAttributes.type || null, - language: closedCaptionAttributes.language || null, - fileURL: null - }; + /** + * Adds or updates the value for the given key. + * @param {String} key - The key to modify the value. + * @param {any} value - The value to be associated with the key. + * @return {any} + */ + setItem(key, value) { + return this.storage.setItem(key, value); } - function createIcon() { - return { - program: null, - height: 0, - width: 0, - xPosition: 0, - yPosition: 0, - apiFramework: null, - offset: null, - duration: 0, - type: null, - staticResource: null, - htmlResource: null, - iframeResource: null, - pxratio: '1', - iconClickThroughURLTemplate: null, - iconClickTrackingURLTemplates: [], - iconViewTrackingURLTemplate: null, - iconClickFallbackImages: [] - }; + /** + * Removes an item for the given key. + * @param {String} key - The key to remove the value. + * @return {any} + */ + removeItem(key) { + return this.storage.removeItem(key); } - function createInteractiveCreativeFile() { - var interactiveCreativeAttributes = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + /** + * Removes all the items from the storage. + */ + clear() { + return this.storage.clear(); + } +} + +const DEFAULT_TIMEOUT = 120000; + +/** + * Return an object containing an XML document. + * in addition to the byteLength and the statusCode of the response. + * @param {Object} response the response of the fetch request. + * @returns {Object} + */ +async function handleResponse(response) { + const textXml = await response.text(); + let parser; + if (!util.isBrowserEnvironment()) { + const xmlDom = await import('@xmldom/xmldom'); + parser = new xmlDom.DOMParser(); + } else { + parser = new DOMParser(); + } + const xml = parser.parseFromString(textXml, 'text/xml'); + return { + xml, + details: { + byteLength: textXml.length, + statusCode: response.status, + rawXml: textXml + } + }; +} + +/** + * Return a custom message if an error occured + * @param {Object} response The response of fetch request + * @returns {String | null} + */ +function handleError(response) { + if (window.location.protocol === 'https:' && response.url.includes('http://')) { + return 'URLHandler: Cannot go from HTTPS to HTTP.'; + } + if (response.status !== 200 || !response.ok) { + return "URLHandler: ".concat(response.statusText, " (").concat(response.status, ")"); + } + return null; +} +async function get(url, options) { + try { + // fetch does not have "timeout" option, we are using AbortController + // to abort the request if it reach the timeout. + const controller = new AbortController(); + const timer = setTimeout(() => { + controller.abort(); + throw new Error("URLHandler: Request timed out after ".concat(options.timeout || DEFAULT_TIMEOUT, " ms (408)")); + }, options.timeout || DEFAULT_TIMEOUT); + const response = await fetch(url, { + ...options, + signal: controller.signal, + credentials: options.withCredentials ? 'include' : 'omit' + }); + clearTimeout(timer); + const error = handleError(response); + if (error) { + return { + error: new Error(error), + statusCode: response.status + }; + } + return handleResponse(response); + } catch (error) { return { - type: interactiveCreativeAttributes.type || null, - apiFramework: interactiveCreativeAttributes.apiFramework || null, - variableDuration: parserUtils.parseBoolean(interactiveCreativeAttributes.variableDuration), - fileURL: null + error, + statusCode: error.name === 'AbortError' ? 408 : null }; } +} +const urlHandler = { + get +}; - function createMediaFile() { - return { - id: null, - fileURL: null, - fileSize: 0, - deliveryType: 'progressive', - mimeType: null, - mediaType: null, - codec: null, - bitrate: 0, - minBitrate: 0, - maxBitrate: 0, - width: 0, - height: 0, - apiFramework: null, - // @deprecated in VAST 4.1. should be used instead. - scalable: null, - maintainAspectRatio: null - }; +/** + * This class provides a method to fetch a VAST document + * @exports + * @class Fetcher + */ + +class Fetcher { + constructor() { + this.URLTemplateFilters = []; } - function createMezzanine() { - return { - id: null, - fileURL: null, - delivery: null, - codec: null, - type: null, - width: 0, - height: 0, - fileSize: 0, - mediaType: '2D' + /** + * Inits the fetching properties of the class with the custom values provided as options + * @param {Object} options - The options to initialize before fetching + */ + setOptions() { + let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + this.urlHandler = options.urlHandler || options.urlhandler || urlHandler; + this.fetchingOptions = { + timeout: options.timeout || DEFAULT_TIMEOUT, + withCredentials: Boolean(options.withCredentials) }; } /** - * This module provides methods to parse a VAST Linear Element. + * Adds a filter function to the array of filters which are called before fetching a VAST document. + * @param {function} filter - The filter function to be added at the end of the array. */ + addURLTemplateFilter(filter) { + if (typeof filter === 'function') { + this.URLTemplateFilters.push(filter); + } + } /** - * Parses a Linear element. - * @param {Object} creativeElement - The VAST Linear element to parse. - * @param {any} creativeAttributes - The attributes of the Linear (optional). - * @return {Object} creative - The creativeLinear object. + * Removes the latest URL template filter added. */ - function parseCreativeLinear(creativeElement, creativeAttributes) { - var offset; - var creative = createCreativeLinear(creativeAttributes); - creative.duration = parserUtils.parseDuration(parserUtils.parseNodeText(parserUtils.childByName(creativeElement, 'Duration'))); - var skipOffset = creativeElement.getAttribute('skipoffset'); - if (typeof skipOffset === 'undefined' || skipOffset === null) { - creative.skipDelay = null; - } else if (skipOffset.charAt(skipOffset.length - 1) === '%' && creative.duration !== -1) { - var percent = parseInt(skipOffset, 10); - creative.skipDelay = creative.duration * (percent / 100); - } else { - creative.skipDelay = parserUtils.parseDuration(skipOffset); - } - var videoClicksElement = parserUtils.childByName(creativeElement, 'VideoClicks'); - if (videoClicksElement) { - var videoClickThroughElement = parserUtils.childByName(videoClicksElement, 'ClickThrough'); - if (videoClickThroughElement) { - creative.videoClickThroughURLTemplate = { - id: videoClickThroughElement.getAttribute('id') || null, - url: parserUtils.parseNodeText(videoClickThroughElement) - }; - } else { - creative.videoClickThroughURLTemplate = null; - } - parserUtils.childrenByName(videoClicksElement, 'ClickTracking').forEach(function (clickTrackingElement) { - creative.videoClickTrackingURLTemplates.push({ - id: clickTrackingElement.getAttribute('id') || null, - url: parserUtils.parseNodeText(clickTrackingElement) - }); - }); - parserUtils.childrenByName(videoClicksElement, 'CustomClick').forEach(function (customClickElement) { - creative.videoCustomClickURLTemplates.push({ - id: customClickElement.getAttribute('id') || null, - url: parserUtils.parseNodeText(customClickElement) - }); - }); - } - var adParamsElement = parserUtils.childByName(creativeElement, 'AdParameters'); - if (adParamsElement) { - creative.adParameters = { - value: parserUtils.parseNodeText(adParamsElement), - xmlEncoded: adParamsElement.getAttribute('xmlEncoded') || null - }; - } - parserUtils.childrenByName(creativeElement, 'TrackingEvents').forEach(function (trackingEventsElement) { - parserUtils.childrenByName(trackingEventsElement, 'Tracking').forEach(function (trackingElement) { - var eventName = trackingElement.getAttribute('event'); - var trackingURLTemplate = parserUtils.parseNodeText(trackingElement); - if (eventName && trackingURLTemplate) { - if (eventName === 'progress') { - offset = trackingElement.getAttribute('offset'); - if (!offset) { - return; - } - if (offset.charAt(offset.length - 1) === '%') { - eventName = "progress-".concat(offset); - } else { - eventName = "progress-".concat(Math.round(parserUtils.parseDuration(offset))); - } - } - if (!Array.isArray(creative.trackingEvents[eventName])) { - creative.trackingEvents[eventName] = []; - } - creative.trackingEvents[eventName].push(trackingURLTemplate); - } - }); - }); - parserUtils.childrenByName(creativeElement, 'MediaFiles').forEach(function (mediaFilesElement) { - parserUtils.childrenByName(mediaFilesElement, 'MediaFile').forEach(function (mediaFileElement) { - creative.mediaFiles.push(parseMediaFile(mediaFileElement)); - }); - var interactiveCreativeElement = parserUtils.childByName(mediaFilesElement, 'InteractiveCreativeFile'); - if (interactiveCreativeElement) { - creative.interactiveCreativeFile = parseInteractiveCreativeFile(interactiveCreativeElement); - } - var closedCaptionElements = parserUtils.childByName(mediaFilesElement, 'ClosedCaptionFiles'); - if (closedCaptionElements) { - parserUtils.childrenByName(closedCaptionElements, 'ClosedCaptionFile').forEach(function (closedCaptionElement) { - var closedCaptionFile = createClosedCaptionFile(parserUtils.parseAttributes(closedCaptionElement)); - closedCaptionFile.fileURL = parserUtils.parseNodeText(closedCaptionElement); - creative.closedCaptionFiles.push(closedCaptionFile); - }); - } - var mezzanineElement = parserUtils.childByName(mediaFilesElement, 'Mezzanine'); - var requiredAttributes = getRequiredAttributes(mezzanineElement, ['delivery', 'type', 'width', 'height']); - if (requiredAttributes) { - var mezzanine = createMezzanine(); - mezzanine.id = mezzanineElement.getAttribute('id'); - mezzanine.fileURL = parserUtils.parseNodeText(mezzanineElement); - mezzanine.delivery = requiredAttributes.delivery; - mezzanine.codec = mezzanineElement.getAttribute('codec'); - mezzanine.type = requiredAttributes.type; - mezzanine.width = parseInt(requiredAttributes.width, 10); - mezzanine.height = parseInt(requiredAttributes.height, 10); - mezzanine.fileSize = parseInt(mezzanineElement.getAttribute('fileSize'), 10); - mezzanine.mediaType = mezzanineElement.getAttribute('mediaType') || '2D'; - creative.mezzanine = mezzanine; - } - }); - var iconsElement = parserUtils.childByName(creativeElement, 'Icons'); - if (iconsElement) { - parserUtils.childrenByName(iconsElement, 'Icon').forEach(function (iconElement) { - creative.icons.push(parseIcon(iconElement)); - }); - } - return creative; + removeLastURLTemplateFilter() { + this.URLTemplateFilters.pop(); } /** - * Parses the MediaFile element from VAST. - * @param {Object} mediaFileElement - The VAST MediaFile element. - * @return {Object} - Parsed mediaFile object. + * Returns the number of URL template filters added. + * @return {Number} */ - function parseMediaFile(mediaFileElement) { - var mediaFile = createMediaFile(); - mediaFile.id = mediaFileElement.getAttribute('id'); - mediaFile.fileURL = parserUtils.parseNodeText(mediaFileElement); - mediaFile.deliveryType = mediaFileElement.getAttribute('delivery'); - mediaFile.codec = mediaFileElement.getAttribute('codec'); - mediaFile.mimeType = mediaFileElement.getAttribute('type'); - mediaFile.mediaType = mediaFileElement.getAttribute('mediaType') || '2D'; - mediaFile.apiFramework = mediaFileElement.getAttribute('apiFramework'); - mediaFile.fileSize = parseInt(mediaFileElement.getAttribute('fileSize') || 0); - mediaFile.bitrate = parseInt(mediaFileElement.getAttribute('bitrate') || 0); - mediaFile.minBitrate = parseInt(mediaFileElement.getAttribute('minBitrate') || 0); - mediaFile.maxBitrate = parseInt(mediaFileElement.getAttribute('maxBitrate') || 0); - mediaFile.width = parseInt(mediaFileElement.getAttribute('width') || 0); - mediaFile.height = parseInt(mediaFileElement.getAttribute('height') || 0); - var scalable = mediaFileElement.getAttribute('scalable'); - if (scalable && typeof scalable === 'string') { - mediaFile.scalable = parserUtils.parseBoolean(scalable); - } - var maintainAspectRatio = mediaFileElement.getAttribute('maintainAspectRatio'); - if (maintainAspectRatio && typeof maintainAspectRatio === 'string') { - mediaFile.maintainAspectRatio = parserUtils.parseBoolean(maintainAspectRatio); - } - return mediaFile; + countURLTemplateFilters() { + return this.URLTemplateFilters.length; } /** - * Parses the InteractiveCreativeFile element from VAST MediaFiles node. - * @param {Object} interactiveCreativeElement - The VAST InteractiveCreativeFile element. - * @return {Object} - Parsed interactiveCreativeFile object. + * Removes all the URL template filters added. */ - function parseInteractiveCreativeFile(interactiveCreativeElement) { - var interactiveCreativeFile = createInteractiveCreativeFile(parserUtils.parseAttributes(interactiveCreativeElement)); - interactiveCreativeFile.fileURL = parserUtils.parseNodeText(interactiveCreativeElement); - return interactiveCreativeFile; + clearURLTemplateFilters() { + this.URLTemplateFilters = []; } /** - * Parses the Icon element from VAST. - * @param {Object} iconElement - The VAST Icon element. - * @return {Object} - Parsed icon object. + * Fetches a VAST document for the given url. + * @param {Object} params + * @param {String} params.url - The url to request the VAST document. + * @param {Number} params.wrapperDepth - How many times the current url has been wrapped. + * @param {(String | null)} params.previousUrl - Url of the previous VAST. + * @param {Object} params.wrapperAd - Previously parsed ad node (Wrapper) related to this fetching. + * @param {Number} params.maxWrapperDepth - The maximum number of Wrapper that can be fetch + * @param {Function} params.emitter - The function used to Emit event + * @emits VASTParser#VAST-resolving + * @emits VASTParser#VAST-resolved + * @return {Promise} */ - function parseIcon(iconElement) { - var icon = createIcon(); - icon.program = iconElement.getAttribute('program'); - icon.height = parseInt(iconElement.getAttribute('height') || 0); - icon.width = parseInt(iconElement.getAttribute('width') || 0); - icon.xPosition = parseXPosition(iconElement.getAttribute('xPosition')); - icon.yPosition = parseYPosition(iconElement.getAttribute('yPosition')); - icon.apiFramework = iconElement.getAttribute('apiFramework'); - icon.pxratio = iconElement.getAttribute('pxratio') || '1'; - icon.offset = parserUtils.parseDuration(iconElement.getAttribute('offset')); - icon.duration = parserUtils.parseDuration(iconElement.getAttribute('duration')); - parserUtils.childrenByName(iconElement, 'HTMLResource').forEach(function (htmlElement) { - icon.type = htmlElement.getAttribute('creativeType') || 'text/html'; - icon.htmlResource = parserUtils.parseNodeText(htmlElement); + async fetchVAST(_ref) { + var _data$details; + let { + url, + maxWrapperDepth, + emitter, + wrapperDepth = 0, + previousUrl = null, + wrapperAd = null + } = _ref; + const timeBeforeGet = Date.now(); + + // Process url with defined filter + this.URLTemplateFilters.forEach(filter => { + url = filter(url); }); - parserUtils.childrenByName(iconElement, 'IFrameResource').forEach(function (iframeElement) { - icon.type = iframeElement.getAttribute('creativeType') || 0; - icon.iframeResource = parserUtils.parseNodeText(iframeElement); + emitter('VAST-resolving', { + url, + previousUrl, + wrapperDepth, + maxWrapperDepth, + timeout: this.fetchingOptions.timeout, + wrapperAd }); - parserUtils.childrenByName(iconElement, 'StaticResource').forEach(function (staticElement) { - icon.type = staticElement.getAttribute('creativeType') || 0; - icon.staticResource = parserUtils.parseNodeText(staticElement); + const data = await this.urlHandler.get(url, this.fetchingOptions); + const requestDuration = Math.round(Date.now() - timeBeforeGet); + emitter('VAST-resolved', { + url, + previousUrl, + wrapperDepth, + error: (data === null || data === void 0 ? void 0 : data.error) || null, + duration: requestDuration, + statusCode: (data === null || data === void 0 ? void 0 : data.statusCode) || null, + ...(data === null || data === void 0 ? void 0 : data.details) }); - var iconClicksElement = parserUtils.childByName(iconElement, 'IconClicks'); - if (iconClicksElement) { - icon.iconClickThroughURLTemplate = parserUtils.parseNodeText(parserUtils.childByName(iconClicksElement, 'IconClickThrough')); - parserUtils.childrenByName(iconClicksElement, 'IconClickTracking').forEach(function (iconClickTrackingElement) { - icon.iconClickTrackingURLTemplates.push({ - id: iconClickTrackingElement.getAttribute('id') || null, - url: parserUtils.parseNodeText(iconClickTrackingElement) - }); - }); - var iconClickFallbackImagesElement = parserUtils.childByName(iconClicksElement, 'IconClickFallbackImages'); - if (iconClickFallbackImagesElement) { - parserUtils.childrenByName(iconClickFallbackImagesElement, 'IconClickFallbackImage').forEach(function (iconClickFallbackImageElement) { - icon.iconClickFallbackImages.push({ - url: parserUtils.parseNodeText(iconClickFallbackImageElement) || null, - width: iconClickFallbackImageElement.getAttribute('width') || null, - height: iconClickFallbackImageElement.getAttribute('height') || null - }); - }); - } + updateEstimatedBitrate(data === null || data === void 0 || (_data$details = data.details) === null || _data$details === void 0 ? void 0 : _data$details.byteLength, requestDuration); + if (data.error) { + throw new Error(data.error); + } else { + return data.xml; } - icon.iconViewTrackingURLTemplate = parserUtils.parseNodeText(parserUtils.childByName(iconElement, 'IconViewTracking')); - return icon; } +} +/** + * This class provides methods to fetch and parse a VAST document using VASTParser. + * In addition it provides options to skip consecutive calls based on constraints. + * @export + * @class VASTClient + */ +class VASTClient { /** - * Parses an horizontal position into a String ('left' or 'right') or into a Number. - * @param {String} xPosition - The x position to parse. - * @return {String|Number} + * Creates an instance of VASTClient. + * @param {Number} cappingFreeLunch - The number of first calls to skip. + * @param {Number} cappingMinimumTimeInterval - The minimum time interval between two consecutive calls. + * @param {Storage} customStorage - A custom storage to use instead of the default one. + * @constructor */ - function parseXPosition(xPosition) { - if (['left', 'right'].indexOf(xPosition) !== -1) { - return xPosition; + constructor() { + let cappingFreeLunch = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; + let cappingMinimumTimeInterval = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; + let customStorage = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : new Storage(); + this.cappingFreeLunch = cappingFreeLunch; + this.cappingMinimumTimeInterval = cappingMinimumTimeInterval; + this.fetcher = new Fetcher(); + this.vastParser = new VASTParser({ + fetcher: this.fetcher + }); + this.storage = customStorage; + // Init values if not already set + if (this.lastSuccessfulAd === undefined) { + this.lastSuccessfulAd = 0; + } + if (this.totalCalls === undefined) { + this.totalCalls = 0; + } + if (this.totalCallsTimeout === undefined) { + this.totalCallsTimeout = 0; } - return parseInt(xPosition || 0); } /** - * Parses an vertical position into a String ('top' or 'bottom') or into a Number. - * @param {String} yPosition - The x position to parse. - * @return {String|Number} + * Adds a filter function to the array of filters which are called before fetching a VAST document. + * @param {function} filter - The filter function to be added at the end of the array. + * @return {void} */ - function parseYPosition(yPosition) { - if (['top', 'bottom'].indexOf(yPosition) !== -1) { - return yPosition; - } - return parseInt(yPosition || 0); + addURLTemplateFilter(filter) { + this.fetcher.addURLTemplateFilter(filter); } /** - * Getting required attributes from element - * @param {Object} element - DOM element - * @param {Array} attributes - list of attributes - * @return {Object|null} null if a least one element not present + * Removes the last element of the url templates filters array. + * @return {void} */ - function getRequiredAttributes(element, attributes) { - var values = {}; - var error = false; - attributes.forEach(function (name) { - if (!element || !element.getAttribute(name)) { - error = true; - } else { - values[name] = element.getAttribute(name); - } - }); - return error ? null : values; + removeLastURLTemplateFilter() { + this.fetcher.removeLastURLTemplateFilter(); } - function createCreativeNonLinear() { - var creativeAttributes = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; - var _createCreative = createCreative(creativeAttributes), - id = _createCreative.id, - adId = _createCreative.adId, - sequence = _createCreative.sequence, - apiFramework = _createCreative.apiFramework; - return { - id: id, - adId: adId, - sequence: sequence, - apiFramework: apiFramework, - type: 'nonlinear', - variations: [], - trackingEvents: {} - }; + /** + * Returns the number of filters of the url templates filters array. + * @return {Number} + */ + countURLTemplateFilters() { + return this.fetcher.countURLTemplateFilters(); } - function createNonLinearAd() { - return { - id: null, - width: 0, - height: 0, - expandedWidth: 0, - expandedHeight: 0, - scalable: true, - maintainAspectRatio: true, - minSuggestedDuration: 0, - apiFramework: 'static', - adType: 'nonLinearAd', - type: null, - staticResource: null, - htmlResource: null, - iframeResource: null, - nonlinearClickThroughURLTemplate: null, - nonlinearClickTrackingURLTemplates: [], - adParameters: null - }; + /** + * Removes all the filter functions from the url templates filters array. + * @return {void} + */ + clearURLTemplateFilters() { + this.fetcher.clearURLTemplateFilters(); + } + getParser() { + return this.vastParser; + } + get lastSuccessfulAd() { + return this.storage.getItem('vast-client-last-successful-ad'); + } + set lastSuccessfulAd(value) { + this.storage.setItem('vast-client-last-successful-ad', value); + } + get totalCalls() { + return this.storage.getItem('vast-client-total-calls'); } - function isNonLinearAd(ad) { - return ad.adType === 'nonLinearAd'; + set totalCalls(value) { + this.storage.setItem('vast-client-total-calls', value); + } + get totalCallsTimeout() { + return this.storage.getItem('vast-client-total-calls-timeout'); + } + set totalCallsTimeout(value) { + this.storage.setItem('vast-client-total-calls-timeout', value); } /** - * This module provides methods to parse a VAST NonLinear Element. + * Returns a boolean indicating if there are more ads to resolve for the current parsing. + * @return {Boolean} */ + hasRemainingAds() { + return this.vastParser.remainingAds.length > 0; + } /** - * Parses a NonLinear element. - * @param {any} creativeElement - The VAST NonLinear element to parse. - * @param {any} creativeAttributes - The attributes of the NonLinear (optional). - * @return {Object} creative - The CreativeNonLinear object. + * Resolves the next group of ads. If all is true resolves all the remaining ads. + * @param {Boolean} all - If true all the remaining ads are resolved + * @return {Promise} */ - function parseCreativeNonLinear(creativeElement, creativeAttributes) { - var creative = createCreativeNonLinear(creativeAttributes); - parserUtils.childrenByName(creativeElement, 'TrackingEvents').forEach(function (trackingEventsElement) { - var eventName, trackingURLTemplate; - parserUtils.childrenByName(trackingEventsElement, 'Tracking').forEach(function (trackingElement) { - eventName = trackingElement.getAttribute('event'); - trackingURLTemplate = parserUtils.parseNodeText(trackingElement); - if (eventName && trackingURLTemplate) { - if (!Array.isArray(creative.trackingEvents[eventName])) { - creative.trackingEvents[eventName] = []; - } - creative.trackingEvents[eventName].push(trackingURLTemplate); - } - }); - }); - parserUtils.childrenByName(creativeElement, 'NonLinear').forEach(function (nonlinearResource) { - var nonlinearAd = createNonLinearAd(); - nonlinearAd.id = nonlinearResource.getAttribute('id') || null; - nonlinearAd.width = nonlinearResource.getAttribute('width'); - nonlinearAd.height = nonlinearResource.getAttribute('height'); - nonlinearAd.expandedWidth = nonlinearResource.getAttribute('expandedWidth'); - nonlinearAd.expandedHeight = nonlinearResource.getAttribute('expandedHeight'); - nonlinearAd.scalable = parserUtils.parseBoolean(nonlinearResource.getAttribute('scalable')); - nonlinearAd.maintainAspectRatio = parserUtils.parseBoolean(nonlinearResource.getAttribute('maintainAspectRatio')); - nonlinearAd.minSuggestedDuration = parserUtils.parseDuration(nonlinearResource.getAttribute('minSuggestedDuration')); - nonlinearAd.apiFramework = nonlinearResource.getAttribute('apiFramework'); - parserUtils.childrenByName(nonlinearResource, 'HTMLResource').forEach(function (htmlElement) { - nonlinearAd.type = htmlElement.getAttribute('creativeType') || 'text/html'; - nonlinearAd.htmlResource = parserUtils.parseNodeText(htmlElement); - }); - parserUtils.childrenByName(nonlinearResource, 'IFrameResource').forEach(function (iframeElement) { - nonlinearAd.type = iframeElement.getAttribute('creativeType') || 0; - nonlinearAd.iframeResource = parserUtils.parseNodeText(iframeElement); - }); - parserUtils.childrenByName(nonlinearResource, 'StaticResource').forEach(function (staticElement) { - nonlinearAd.type = staticElement.getAttribute('creativeType') || 0; - nonlinearAd.staticResource = parserUtils.parseNodeText(staticElement); - }); - var adParamsElement = parserUtils.childByName(nonlinearResource, 'AdParameters'); - if (adParamsElement) { - nonlinearAd.adParameters = { - value: parserUtils.parseNodeText(adParamsElement), - xmlEncoded: adParamsElement.getAttribute('xmlEncoded') || null - }; - } - nonlinearAd.nonlinearClickThroughURLTemplate = parserUtils.parseNodeText(parserUtils.childByName(nonlinearResource, 'NonLinearClickThrough')); - parserUtils.childrenByName(nonlinearResource, 'NonLinearClickTracking').forEach(function (clickTrackingElement) { - nonlinearAd.nonlinearClickTrackingURLTemplates.push({ - id: clickTrackingElement.getAttribute('id') || null, - url: parserUtils.parseNodeText(clickTrackingElement) - }); - }); - creative.variations.push(nonlinearAd); - }); - return creative; - } - - function createExtension() { - return { - name: null, - value: null, - attributes: {}, - children: [] - }; - } - function isEmptyExtension(extension) { - return extension.value === null && Object.keys(extension.attributes).length === 0 && extension.children.length === 0; + getNextAds(all) { + return this.vastParser.getRemainingAds(all); } /** - * Parses an array of Extension elements. - * @param {Node[]} extensions - The array of extensions to parse. - * @param {String} type - The type of extensions to parse.(Ad|Creative) - * @return {AdExtension[]|CreativeExtension[]} - The nodes parsed to extensions + * Parses the given xml Object into a VASTResponse. + * Returns a Promise which resolves with a fully parsed VASTResponse or rejects with an Error. + * @param {Object} xml - An object representing a vast xml document. + * @param {Object} options - An optional Object of parameters to be used in the parsing and fetching process. + * @returns {Promise} */ - function parseExtensions(extensions) { - var exts = []; - extensions.forEach(function (extNode) { - var ext = _parseExtension(extNode); - if (ext) { - exts.push(ext); - } - }); - return exts; + parseVAST(xml) { + let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + this.fetcher.setOptions(options); + return this.vastParser.parseVAST(xml, options); } - /** - * Parses an extension child node - * @param {Node} extNode - The extension node to parse - * @return {AdExtension|CreativeExtension|null} - The node parsed to extension + * Gets a parsed VAST document for the given url, applying the skipping rules defined. + * Returns a Promise which resolves with a fully parsed VASTResponse or rejects with an Error. + * @param {String} url - The url to use to fecth the VAST document. + * @param {Object} options - An optional Object of parameters to be applied in the process. + * @return {Promise} */ - function _parseExtension(extNode) { - // Ignore comments - if (extNode.nodeName === '#comment') return null; - var ext = createExtension(); - var extNodeAttrs = extNode.attributes; - var childNodes = extNode.childNodes; - ext.name = extNode.nodeName; - - // Parse attributes - if (extNode.attributes) { - for (var extNodeAttrKey in extNodeAttrs) { - if (extNodeAttrs.hasOwnProperty(extNodeAttrKey)) { - var extNodeAttr = extNodeAttrs[extNodeAttrKey]; - if (extNodeAttr.nodeName && extNodeAttr.nodeValue) { - ext.attributes[extNodeAttr.nodeName] = extNodeAttr.nodeValue; - } - } - } - } + get(url) { + let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + const now = Date.now(); - // Parse all children - for (var childNodeKey in childNodes) { - if (childNodes.hasOwnProperty(childNodeKey)) { - var parsedChild = _parseExtension(childNodes[childNodeKey]); - if (parsedChild) { - ext.children.push(parsedChild); - } - } + // By default the client resolves only the first Ad or AdPod + if (!options.hasOwnProperty('resolveAll')) { + options.resolveAll = false; } - /* - Only parse value of Nodes with only eather no children or only a cdata or text - to avoid useless parsing that would result to a concatenation of all children - */ - if (ext.children.length === 0 || ext.children.length === 1 && ['#cdata-section', '#text'].indexOf(ext.children[0].name) >= 0) { - var txt = parserUtils.parseNodeText(extNode); - if (txt !== '') { - ext.value = txt; - } - - // Remove the children if it's a cdata or simply text to avoid useless children - ext.children = []; + // Check totalCallsTimeout (first call + 1 hour), if older than now, + // reset totalCalls number, by this way the client will be eligible again + // for freelunch capping + if (this.totalCallsTimeout < now) { + this.totalCalls = 1; + this.totalCallsTimeout = now + 60 * 60 * 1000; + } else { + this.totalCalls++; } + return new Promise((resolve, reject) => { + if (this.cappingFreeLunch >= this.totalCalls) { + return reject(new Error("VAST call canceled \u2013 FreeLunch capping not reached yet ".concat(this.totalCalls, "/").concat(this.cappingFreeLunch))); + } + const timeSinceLastCall = now - this.lastSuccessfulAd; - // Only return not empty objects to not pollute extentions - return isEmptyExtension(ext) ? null : ext; + // Check timeSinceLastCall to be a positive number. If not, this mean the + // previous was made in the future. We reset lastSuccessfulAd value + if (timeSinceLastCall < 0) { + this.lastSuccessfulAd = 0; + } else if (timeSinceLastCall < this.cappingMinimumTimeInterval) { + return reject(new Error("VAST call canceled \u2013 (".concat(this.cappingMinimumTimeInterval, ")ms minimum interval reached"))); + } + this.vastParser.initParsingStatus(options); + this.fetcher.setOptions(options); + this.vastParser.rootURL = url; + this.fetcher.fetchVAST({ + url, + emitter: this.vastParser.emit.bind(this.vastParser), + maxWrapperDepth: this.vastParser.maxWrapperDepth + }).then(xml => { + options.previousUrl = url; + options.isRootVAST = true; + options.url = url; + return this.vastParser.parse(xml, options).then(resolvedAd => { + const vastResponse = this.vastParser.buildVASTResponse(resolvedAd); + resolve(vastResponse); + }); + }).catch(err => reject(err)); + }); } - +} + +/** + * The default skip delay used in case a custom one is not provided + * @constant + * @type {Number} + */ +const DEFAULT_SKIP_DELAY = -1; + +/** + * This class provides methods to track an ad execution. + * + * @export + * @class VASTTracker + * @extends EventEmitter + */ +class VASTTracker extends EventEmitter { /** - * Parses the creatives from the Creatives Node. - * @param {any} creativeNodes - The creative nodes to parse. - * @return {Array} - An array of Creative objects. + * Creates an instance of VASTTracker. + * + * @param {VASTClient} client - An instance of VASTClient that can be updated by the tracker. [optional] + * @param {Ad} ad - The ad to track. + * @param {Creative} creative - The creative to track. + * @param {Object} [variation=null] - An optional variation of the creative. + * @param {Boolean} [muted=false] - The initial muted state of the video. + * @constructor */ - function parseCreatives(creativeNodes) { - var creatives = []; - creativeNodes.forEach(function (creativeElement) { - var creativeAttributes = { - id: creativeElement.getAttribute('id') || null, - adId: parseCreativeAdIdAttribute(creativeElement), - sequence: creativeElement.getAttribute('sequence') || null, - apiFramework: creativeElement.getAttribute('apiFramework') || null - }; - var universalAdIds = []; - var universalAdIdElements = parserUtils.childrenByName(creativeElement, 'UniversalAdId'); - universalAdIdElements.forEach(function (universalAdIdElement) { - var universalAdId = { - idRegistry: universalAdIdElement.getAttribute('idRegistry') || 'unknown', - value: parserUtils.parseNodeText(universalAdIdElement) - }; - universalAdIds.push(universalAdId); + constructor(client, ad, creative) { + let variation = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null; + let muted = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false; + super(); + this.ad = ad; + this.creative = creative; + this.variation = variation; + this.muted = muted; + this.impressed = false; + this.skippable = false; + this.trackingEvents = {}; + // We need to keep the last percentage of the tracker in order to + // calculate to trigger the events when the VAST duration is short + this.lastPercentage = 0; + this._alreadyTriggeredQuartiles = {}; + // Tracker listeners should be notified with some events + // no matter if there is a tracking URL or not + this.emitAlwaysEvents = ['creativeView', 'start', 'firstQuartile', 'midpoint', 'thirdQuartile', 'complete', 'resume', 'pause', 'rewind', 'skip', 'closeLinear', 'close']; + + // Duplicate the creative's trackingEvents property so we can alter it + for (const eventName in this.creative.trackingEvents) { + const events = this.creative.trackingEvents[eventName]; + this.trackingEvents[eventName] = events.slice(0); + } + + // Nonlinear and companion creatives provide some tracking information at a variation level + // While linear creatives provided that at a creative level. That's why we need to + // differentiate how we retrieve some tracking information. + if (isCreativeLinear(this.creative)) { + this._initLinearTracking(); + } else { + this._initVariationTracking(); + } + + // If the tracker is associated with a client we add a listener to the start event + // to update the lastSuccessfulAd property. + if (client) { + this.on('start', () => { + client.lastSuccessfulAd = Date.now(); }); - var creativeExtensions; - var creativeExtensionsElement = parserUtils.childByName(creativeElement, 'CreativeExtensions'); - if (creativeExtensionsElement) { - creativeExtensions = parseExtensions(parserUtils.childrenByName(creativeExtensionsElement, 'CreativeExtension')); - } - for (var creativeTypeElementKey in creativeElement.childNodes) { - var creativeTypeElement = creativeElement.childNodes[creativeTypeElementKey]; - var parsedCreative = void 0; - switch (creativeTypeElement.nodeName) { - case 'Linear': - parsedCreative = parseCreativeLinear(creativeTypeElement, creativeAttributes); - break; - case 'NonLinearAds': - parsedCreative = parseCreativeNonLinear(creativeTypeElement, creativeAttributes); - break; - case 'CompanionAds': - parsedCreative = parseCreativeCompanion(creativeTypeElement, creativeAttributes); - break; - } - if (parsedCreative) { - if (universalAdIds) { - parsedCreative.universalAdIds = universalAdIds; - } - if (creativeExtensions) { - parsedCreative.creativeExtensions = creativeExtensions; - } - creatives.push(parsedCreative); - } - } - }); - return creatives; + } } /** - * Parses the creative adId Attribute. - * @param {any} creativeElement - The creative element to retrieve the adId from. - * @return {String|null} + * Init the custom tracking options for linear creatives. + * + * @return {void} */ - function parseCreativeAdIdAttribute(creativeElement) { - return creativeElement.getAttribute('AdID') || - // VAST 2 spec - creativeElement.getAttribute('adID') || - // VAST 3 spec - creativeElement.getAttribute('adId') || - // VAST 4 spec - null; - } - - var requiredValues = { - Wrapper: { - subElements: ['VASTAdTagURI', 'Impression'] - }, - BlockedAdCategories: { - attributes: ['authority'] - }, - InLine: { - subElements: ['AdSystem', 'AdTitle', 'Impression', 'AdServingId', 'Creatives'] - }, - Category: { - attributes: ['authority'] - }, - Pricing: { - attributes: ['model', 'currency'] - }, - Verification: { - oneOfinLineResources: ['JavaScriptResource', 'ExecutableResource'], - attributes: ['vendor'] - }, - UniversalAdId: { - attributes: ['idRegistry'] - }, - JavaScriptResource: { - attributes: ['apiFramework', 'browserOptional'] - }, - ExecutableResource: { - attributes: ['apiFramework', 'type'] - }, - Tracking: { - attributes: ['event'] - }, - Creatives: { - subElements: ['Creative'] - }, - Creative: { - subElements: ['UniversalAdId'] - }, - Linear: { - subElements: ['MediaFiles', 'Duration'] - }, - MediaFiles: { - subElements: ['MediaFile'] - }, - MediaFile: { - attributes: ['delivery', 'type', 'width', 'height'] - }, - Mezzanine: { - attributes: ['delivery', 'type', 'width', 'height'] - }, - NonLinear: { - oneOfinLineResources: ['StaticResource', 'IFrameResource', 'HTMLResource'], - attributes: ['width', 'height'] - }, - Companion: { - oneOfinLineResources: ['StaticResource', 'IFrameResource', 'HTMLResource'], - attributes: ['width', 'height'] - }, - StaticResource: { - attributes: ['creativeType'] - }, - Icons: { - subElements: ['Icon'] - }, - Icon: { - oneOfinLineResources: ['StaticResource', 'IFrameResource', 'HTMLResource'] - } - }; + _initLinearTracking() { + this.linear = true; + this.skipDelay = this.creative.skipDelay; + this.setDuration(this.creative.duration); + this.clickThroughURLTemplate = this.creative.videoClickThroughURLTemplate; + this.clickTrackingURLTemplates = this.creative.videoClickTrackingURLTemplates; + } /** - * Verify node required values and also verify recursively all his child nodes. - * Trigger warnings if a node required value is missing. - * @param {Node} node - The node element. - * @param {Function} emit - Emit function used to trigger Warning event. - * @emits VASTParser#VAST-warning - * @param {undefined|Boolean} [isAdInline] - Passed recursively to itself. True if the node is contained inside a inLine tag. + * Init the custom tracking options for nonlinear and companion creatives. + * These options are provided in the variation Object. + * + * @return {void} */ - function verifyRequiredValues(node, emit, isAdInline) { - if (!node || !node.nodeName) { + _initVariationTracking() { + this.linear = false; + this.skipDelay = DEFAULT_SKIP_DELAY; + + // If no variation has been provided there's nothing else to set + if (!this.variation) { return; } - if (node.nodeName === 'InLine') { - isAdInline = true; - } - verifyRequiredAttributes(node, emit); - if (hasSubElements(node)) { - verifyRequiredSubElements(node, emit, isAdInline); - for (var i = 0; i < node.children.length; i++) { - verifyRequiredValues(node.children[i], emit, isAdInline); + + // Duplicate the variation's trackingEvents property so we can alter it + for (const eventName in this.variation.trackingEvents) { + const events = this.variation.trackingEvents[eventName]; + + // If for the given eventName we already had some trackingEvents provided by the creative + // we want to keep both the creative trackingEvents and the variation ones + if (this.trackingEvents[eventName]) { + this.trackingEvents[eventName] = this.trackingEvents[eventName].concat(events.slice(0)); + } else { + this.trackingEvents[eventName] = events.slice(0); } - } else if (parserUtils.parseNodeText(node).length === 0) { - emitMissingValueWarning({ - name: node.nodeName, - parentName: node.parentNode.nodeName - }, emit); + } + if (isNonLinearAd(this.variation)) { + this.clickThroughURLTemplate = this.variation.nonlinearClickThroughURLTemplate; + this.clickTrackingURLTemplates = this.variation.nonlinearClickTrackingURLTemplates; + this.setDuration(this.variation.minSuggestedDuration); + } else if (isCompanionAd(this.variation)) { + this.clickThroughURLTemplate = this.variation.companionClickThroughURLTemplate; + this.clickTrackingURLTemplates = this.variation.companionClickTrackingURLTemplates; } } /** - * Verify and trigger warnings if node required attributes are not set. - * @param {Node} node - The node element. - * @param {Function} emit - Emit function used to trigger Warning event. - * @emits VASTParser#VAST-warning + * Sets the duration of the ad and updates the quartiles based on that. + * + * @param {Number} duration - The duration of the ad. */ - function verifyRequiredAttributes(node, emit) { - if (!requiredValues[node.nodeName] || !requiredValues[node.nodeName].attributes) { + setDuration(duration) { + // check if duration is a valid time input + if (!util.isValidTimeValue(duration)) { + this.emit('TRACKER-error', { + message: "the duration provided is not valid. duration: ".concat(duration) + }); return; } - var requiredAttributes = requiredValues[node.nodeName].attributes; - var missingAttributes = requiredAttributes.filter(function (attributeName) { - return !node.getAttribute(attributeName); - }); - if (missingAttributes.length > 0) { - emitMissingValueWarning({ - name: node.nodeName, - parentName: node.parentNode.nodeName, - attributes: missingAttributes - }, emit); - } + this.assetDuration = duration; + // beware of key names, theses are also used as event names + this.quartiles = { + firstQuartile: Math.round(25 * this.assetDuration) / 100, + midpoint: Math.round(50 * this.assetDuration) / 100, + thirdQuartile: Math.round(75 * this.assetDuration) / 100 + }; } /** - * Verify and trigger warnings if node required sub element are not set. - * @param {Node} node - The node element - * @param {Boolean} isAdInline - True if node is contained in a inline - * @param {Function} emit - Emit function used to trigger Warning event. - * @emits VASTParser#VAST-warning + * Sets the duration of the ad and updates the quartiles based on that. + * This is required for tracking time related events. + * + * @param {Number} progress - Current playback time in seconds. + * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. + * @emits VASTTracker#start + * @emits VASTTracker#skip-countdown + * @emits VASTTracker#progress-[0-100]% + * @emits VASTTracker#progress-[currentTime] + * @emits VASTTracker#rewind + * @emits VASTTracker#firstQuartile + * @emits VASTTracker#midpoint + * @emits VASTTracker#thirdQuartile */ - function verifyRequiredSubElements(node, emit, isAdInline) { - var required = requiredValues[node.nodeName]; - // Do not verify subelement if node is a child of wrapper, but verify it if node is the Wrapper itself - // Wrapper child have no required subElement. (Only InLine does) - var isInWrapperButNotWrapperItself = !isAdInline && node.nodeName !== 'Wrapper'; - if (!required || isInWrapperButNotWrapperItself) { - return; - } - if (required.subElements) { - var requiredSubElements = required.subElements; - var missingSubElements = requiredSubElements.filter(function (subElementName) { - return !parserUtils.childByName(node, subElementName); + setProgress(progress) { + let macros = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + let trackOnce = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true; + // check if progress is a valid time input + if (!util.isValidTimeValue(progress) || typeof macros !== 'object') { + this.emit('TRACKER-error', { + message: "One given setProgress parameter has the wrong type. progress: ".concat(progress, ", macros: ").concat(util.formatMacrosValues(macros)) }); - if (missingSubElements.length > 0) { - emitMissingValueWarning({ - name: node.nodeName, - parentName: node.parentNode.nodeName, - subElements: missingSubElements - }, emit); - } + return; } - - // When InLine format is used some nodes (i.e , , or ) - // require at least one of the following resources: StaticResource, IFrameResource, HTMLResource - if (!isAdInline || !required.oneOfinLineResources) { - return; - } - var resourceFound = required.oneOfinLineResources.some(function (resource) { - return parserUtils.childByName(node, resource); - }); - if (!resourceFound) { - emitMissingValueWarning({ - name: node.nodeName, - parentName: node.parentNode.nodeName, - oneOfResources: required.oneOfinLineResources - }, emit); + const skipDelay = this.skipDelay || DEFAULT_SKIP_DELAY; + if (skipDelay !== -1 && !this.skippable) { + if (skipDelay > progress) { + this.emit('skip-countdown', skipDelay - progress); + } else { + this.skippable = true; + this.emit('skip-countdown', 0); + } + } + if (this.assetDuration > 0) { + const percent = Math.round(progress / this.assetDuration * 100); + const events = []; + if (progress > 0) { + events.push('start'); + for (let i = this.lastPercentage; i < percent; i++) { + events.push("progress-".concat(i + 1, "%")); + } + events.push("progress-".concat(Math.round(progress))); + for (const quartile in this.quartiles) { + if (this.isQuartileReached(quartile, this.quartiles[quartile], progress)) { + events.push(quartile); + this._alreadyTriggeredQuartiles[quartile] = true; + } + } + this.lastPercentage = percent; + } + events.forEach(eventName => { + this.track(eventName, { + macros, + once: trackOnce + }); + }); + if (progress < this.progress) { + this.track('rewind', { + macros + }); + } } + this.progress = progress; } /** - * Check if a node has sub elements. - * @param {Node} node - The node element. - * @returns {Boolean} + * Checks if a quartile has been reached without have being triggered already. + * + * @param {String} quartile - Quartile name + * @param {Number} time - Time offset of the quartile, when this quartile is reached in seconds. + * @param {Number} progress - Current progress of the ads in seconds. + * + * @return {Boolean} */ - function hasSubElements(node) { - return node.children && node.children.length !== 0; + isQuartileReached(quartile, time, progress) { + let quartileReached = false; + // if quartile time already reached and never triggered + if (time <= progress && !this._alreadyTriggeredQuartiles[quartile]) { + quartileReached = true; + } + return quartileReached; } /** - * Trigger Warning if a element is empty or has missing attributes/subelements/resources - * @param {Object} missingElement - Object containing missing elements and values - * @param {String} missingElement.name - The name of element containing missing values - * @param {String} missingElement.parentName - The parent name of element containing missing values - * @param {Array} missingElement.attributes - The array of missing attributes - * @param {Array} missingElement.subElements - The array of missing sub elements - * @param {Array} missingElement.oneOfResources - The array of resources in which at least one must be provided by the element - * @param {Function} emit - Emit function used to trigger Warning event. - * @emits VastParser#VAST-warning + * Updates the mute state and calls the mute/unmute tracking URLs. + * + * @param {Boolean} muted - Indicates if the video is muted or not. + * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. + * @emits VASTTracker#mute + * @emits VASTTracker#unmute */ - function emitMissingValueWarning(_ref, emit) { - var name = _ref.name, - parentName = _ref.parentName, - attributes = _ref.attributes, - subElements = _ref.subElements, - oneOfResources = _ref.oneOfResources; - var message = "Element '".concat(name, "'"); - if (attributes) { - message += " missing required attribute(s) '".concat(attributes.join(', '), "' "); - } else if (subElements) { - message += " missing required sub element(s) '".concat(subElements.join(', '), "' "); - } else if (oneOfResources) { - message += " must provide one of the following '".concat(oneOfResources.join(', '), "' "); - } else { - message += " is empty"; + setMuted(muted) { + let macros = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + if (typeof muted !== 'boolean' || typeof macros !== 'object') { + this.emit('TRACKER-error', { + message: "One given setMuted parameter has the wrong type. muted: ".concat(muted, ", macros: ").concat(util.formatMacrosValues(macros)) + }); + return; } - emit('VAST-warning', { - message: message, - parentElement: parentName, - specVersion: 4.1 - }); + if (this.muted !== muted) { + this.track(muted ? 'mute' : 'unmute', { + macros + }); + } + this.muted = muted; } - var parserVerification = { - verifyRequiredValues: verifyRequiredValues, - hasSubElements: hasSubElements, - emitMissingValueWarning: emitMissingValueWarning, - verifyRequiredAttributes: verifyRequiredAttributes, - verifyRequiredSubElements: verifyRequiredSubElements - }; /** - * This module provides methods to parse a VAST Ad Element. + * Update the pause state and call the resume/pause tracking URLs. + * + * @param {Boolean} paused - Indicates if the video is paused or not. + * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. + * @emits VASTTracker#pause + * @emits VASTTracker#resume */ + setPaused(paused) { + let macros = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + if (typeof paused !== 'boolean' || typeof macros !== 'object') { + this.emit('TRACKER-error', { + message: "One given setPaused parameter has the wrong type. paused: ".concat(paused, ", macros: ").concat(util.formatMacrosValues(macros)) + }); + return; + } + if (this.paused !== paused) { + this.track(paused ? 'pause' : 'resume', { + macros + }); + } + this.paused = paused; + } /** - * Parses an Ad element (can either be a Wrapper or an InLine). - * @param {Object} adElement - The VAST Ad element to parse. - * @param {Function} emit - Emit function used to trigger Warning event - * @param {Object} options - An optional Object of parameters to be used in the parsing process. - * @emits VASTParser#VAST-warning - * @return {Object|undefined} - Object containing the ad and if it is wrapper/inline + * Updates the fullscreen state and calls the fullscreen tracking URLs. + * + * @param {Boolean} fullscreen - Indicates if the video is in fulscreen mode or not. + * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. + * @emits VASTTracker#fullscreen + * @emits VASTTracker#exitFullscreen */ - function parseAd(adElement, emit) { - var _ref = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}, - allowMultipleAds = _ref.allowMultipleAds, - followAdditionalWrappers = _ref.followAdditionalWrappers; - var childNodes = adElement.childNodes; - for (var adTypeElementKey in childNodes) { - var adTypeElement = childNodes[adTypeElementKey]; - if (['Wrapper', 'InLine'].indexOf(adTypeElement.nodeName) === -1) { - continue; - } - if (adTypeElement.nodeName === 'Wrapper' && followAdditionalWrappers === false) { - continue; - } - parserUtils.copyNodeAttribute('id', adElement, adTypeElement); - parserUtils.copyNodeAttribute('sequence', adElement, adTypeElement); - parserUtils.copyNodeAttribute('adType', adElement, adTypeElement); - if (adTypeElement.nodeName === 'Wrapper') { - return { - ad: parseWrapper(adTypeElement, emit), - type: 'WRAPPER' - }; - } else if (adTypeElement.nodeName === 'InLine') { - return { - ad: parseInLine(adTypeElement, emit, { - allowMultipleAds: allowMultipleAds - }), - type: 'INLINE' - }; - } + setFullscreen(fullscreen) { + let macros = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + if (typeof fullscreen !== 'boolean' || typeof macros !== 'object') { + this.emit('TRACKER-error', { + message: "One given setFullScreen parameter has the wrong type. fullscreen: ".concat(fullscreen, ", macros: ").concat(util.formatMacrosValues(macros)) + }); + return; + } + if (this.fullscreen !== fullscreen) { + this.track(fullscreen ? 'fullscreen' : 'exitFullscreen', { + macros + }); } + this.fullscreen = fullscreen; } /** - * Parses an Inline - * @param {Object} adElement Element - The VAST Inline element to parse. - * @param {Function} emit - Emit function used to trigger Warning event. - * @param {Object} options - An optional Object of parameters to be used in the parsing process. - * @emits VASTParser#VAST-warning - * @return {Object} ad - The ad object. + * Updates the expand state and calls the expand/collapse tracking URLs. + * + * @param {Boolean} expanded - Indicates if the video is expanded or not. + * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. + * @emits VASTTracker#expand + * @emits VASTTracker#playerExpand + * @emits VASTTracker#collapse + * @emits VASTTracker#playerCollapse */ - function parseInLine(adElement, emit) { - var _ref2 = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}, - allowMultipleAds = _ref2.allowMultipleAds; - // if allowMultipleAds is set to false by wrapper attribute - // only the first stand-alone Ad (with no sequence values) in the - // requested VAST response is allowed so we won't parse ads with sequence - if (allowMultipleAds === false && adElement.getAttribute('sequence')) { - return null; + setExpand(expanded) { + let macros = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + if (typeof expanded !== 'boolean' || typeof macros !== 'object') { + this.emit('TRACKER-error', { + message: "One given setExpand parameter has the wrong type. expanded: ".concat(expanded, ", macros: ").concat(util.formatMacrosValues(macros)) + }); + return; + } + if (this.expanded !== expanded) { + this.track(expanded ? 'expand' : 'collapse', { + macros + }); + this.track(expanded ? 'playerExpand' : 'playerCollapse', { + macros + }); } - return parseAdElement(adElement, emit); + this.expanded = expanded; } /** - * Parses an ad type (Inline or Wrapper) - * @param {Object} adTypeElement - The VAST Inline or Wrapper element to parse. - * @param {Function} emit - Emit function used to trigger Warning event. - * @emits VASTParser#VAST-warning - * @return {Object} ad - The ad object. + * Must be called if you want to overwrite the Skipoffset value. + * This will init the skip countdown duration. Then, every time setProgress() is called, + * it will decrease the countdown and emit a skip-countdown event with the remaining time. + * Do not call this method if you want to keep the original Skipoffset value. + * + * @param {Number} duration - The time in seconds until the skip button is displayed. */ - function parseAdElement(adTypeElement, emit) { - var adVerificationsFromExtensions = []; - if (emit) { - parserVerification.verifyRequiredValues(adTypeElement, emit); - } - var childNodes = adTypeElement.childNodes; - var ad = createAd(parserUtils.parseAttributes(adTypeElement)); - for (var nodeKey in childNodes) { - var node = childNodes[nodeKey]; - switch (node.nodeName) { - case 'Error': - ad.errorURLTemplates.push(parserUtils.parseNodeText(node)); - break; - case 'Impression': - ad.impressionURLTemplates.push({ - id: node.getAttribute('id') || null, - url: parserUtils.parseNodeText(node) - }); - break; - case 'Creatives': - ad.creatives = parseCreatives(parserUtils.childrenByName(node, 'Creative')); - break; - case 'Extensions': - { - var extNodes = parserUtils.childrenByName(node, 'Extension'); - ad.extensions = parseExtensions(extNodes); - - /* - OMID specify adVerifications should be in extensions for VAST < 4.0 - To avoid to put them on two different places in two different format we reparse it - from extensions the same way than for an AdVerifications node. - */ - if (!ad.adVerifications.length) { - adVerificationsFromExtensions = _parseAdVerificationsFromExtensions(extNodes); - } - break; - } - case 'AdVerifications': - ad.adVerifications = _parseAdVerifications(parserUtils.childrenByName(node, 'Verification')); - break; - case 'AdSystem': - ad.system = { - value: parserUtils.parseNodeText(node), - version: node.getAttribute('version') || null - }; - break; - case 'AdTitle': - ad.title = parserUtils.parseNodeText(node); - break; - case 'AdServingId': - ad.adServingId = parserUtils.parseNodeText(node); - break; - case 'Category': - ad.categories.push({ - authority: node.getAttribute('authority') || null, - value: parserUtils.parseNodeText(node) - }); - break; - case 'Expires': - ad.expires = parseInt(parserUtils.parseNodeText(node), 10); - break; - case 'ViewableImpression': - ad.viewableImpression.push(_parseViewableImpression(node)); - break; - case 'Description': - ad.description = parserUtils.parseNodeText(node); - break; - case 'Advertiser': - ad.advertiser = { - id: node.getAttribute('id') || null, - value: parserUtils.parseNodeText(node) - }; - break; - case 'Pricing': - ad.pricing = { - value: parserUtils.parseNodeText(node), - model: node.getAttribute('model') || null, - currency: node.getAttribute('currency') || null - }; - break; - case 'Survey': - ad.survey = { - value: parserUtils.parseNodeText(node), - type: node.getAttribute('type') || null - }; - break; - case 'BlockedAdCategories': - ad.blockedAdCategories.push({ - authority: node.getAttribute('authority') || null, - value: parserUtils.parseNodeText(node) - }); - break; - } - } - if (adVerificationsFromExtensions.length) { - ad.adVerifications = ad.adVerifications.concat(adVerificationsFromExtensions); + setSkipDelay(duration) { + if (!util.isValidTimeValue(duration)) { + this.emit('TRACKER-error', { + message: "setSkipDelay parameter does not have a valid value. duration: ".concat(duration) + }); + return; } - return ad; + this.skipDelay = duration; } /** - * Parses a Wrapper element without resolving the wrapped urls. - * @param {Object} wrapperElement - The VAST Wrapper element to be parsed. - * @param {Function} emit - Emit function used to trigger Warning event. - * @emits VASTParser#VAST-warning - * @return {Ad} + * Tracks an impression (can be called only once). + * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. + * @emits VASTTracker#creativeView */ - function parseWrapper(wrapperElement, emit) { - var ad = parseAdElement(wrapperElement, emit); - var followAdditionalWrappersValue = wrapperElement.getAttribute('followAdditionalWrappers'); - var allowMultipleAdsValue = wrapperElement.getAttribute('allowMultipleAds'); - var fallbackOnNoAdValue = wrapperElement.getAttribute('fallbackOnNoAd'); - ad.followAdditionalWrappers = followAdditionalWrappersValue ? parserUtils.parseBoolean(followAdditionalWrappersValue) : true; - ad.allowMultipleAds = allowMultipleAdsValue ? parserUtils.parseBoolean(allowMultipleAdsValue) : false; - ad.fallbackOnNoAd = fallbackOnNoAdValue ? parserUtils.parseBoolean(fallbackOnNoAdValue) : null; - var wrapperURLElement = parserUtils.childByName(wrapperElement, 'VASTAdTagURI'); - if (wrapperURLElement) { - ad.nextWrapperURL = parserUtils.parseNodeText(wrapperURLElement); - } else { - wrapperURLElement = parserUtils.childByName(wrapperElement, 'VASTAdTagURL'); - if (wrapperURLElement) { - ad.nextWrapperURL = parserUtils.parseNodeText(parserUtils.childByName(wrapperURLElement, 'URL')); - } + trackImpression() { + let macros = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + if (typeof macros !== 'object') { + this.emit('TRACKER-error', { + message: "trackImpression parameter has the wrong type. macros: ".concat(macros) + }); + return; } - ad.creatives.forEach(function (wrapperCreativeElement) { - if (['linear', 'nonlinear'].indexOf(wrapperCreativeElement.type) !== -1) { - // TrackingEvents Linear / NonLinear - if (wrapperCreativeElement.trackingEvents) { - if (!ad.trackingEvents) { - ad.trackingEvents = {}; - } - if (!ad.trackingEvents[wrapperCreativeElement.type]) { - ad.trackingEvents[wrapperCreativeElement.type] = {}; - } - var _loop = function _loop(eventName) { - var urls = wrapperCreativeElement.trackingEvents[eventName]; - if (!Array.isArray(ad.trackingEvents[wrapperCreativeElement.type][eventName])) { - ad.trackingEvents[wrapperCreativeElement.type][eventName] = []; - } - urls.forEach(function (url) { - ad.trackingEvents[wrapperCreativeElement.type][eventName].push(url); - }); - }; - for (var eventName in wrapperCreativeElement.trackingEvents) { - _loop(eventName); - } - } - // ClickTracking - if (wrapperCreativeElement.videoClickTrackingURLTemplates) { - if (!Array.isArray(ad.videoClickTrackingURLTemplates)) { - ad.videoClickTrackingURLTemplates = []; - } // tmp property to save wrapper tracking URLs until they are merged - wrapperCreativeElement.videoClickTrackingURLTemplates.forEach(function (item) { - ad.videoClickTrackingURLTemplates.push(item); - }); - } - // ClickThrough - if (wrapperCreativeElement.videoClickThroughURLTemplate) { - ad.videoClickThroughURLTemplate = wrapperCreativeElement.videoClickThroughURLTemplate; - } - // CustomClick - if (wrapperCreativeElement.videoCustomClickURLTemplates) { - if (!Array.isArray(ad.videoCustomClickURLTemplates)) { - ad.videoCustomClickURLTemplates = []; - } // tmp property to save wrapper tracking URLs until they are merged - wrapperCreativeElement.videoCustomClickURLTemplates.forEach(function (item) { - ad.videoCustomClickURLTemplates.push(item); - }); - } - } - }); - if (ad.nextWrapperURL) { - return ad; + if (!this.impressed) { + this.impressed = true; + this.trackURLs(this.ad.impressionURLTemplates, macros); + this.track('creativeView', { + macros + }); } } /** - * Parses the AdVerifications Element. - * @param {Array} verifications - The array of verifications to parse. - * @return {Array} + * Tracks Viewable impression + * @param {Object} [macros = {}] An optional Object containing macros and their values to be used and replaced in the tracking calls. */ - function _parseAdVerifications(verifications) { - var ver = []; - verifications.forEach(function (verificationNode) { - var verification = createAdVerification(); - var childNodes = verificationNode.childNodes; - parserUtils.assignAttributes(verificationNode.attributes, verification); - for (var nodeKey in childNodes) { - var node = childNodes[nodeKey]; - switch (node.nodeName) { - case 'JavaScriptResource': - case 'ExecutableResource': - verification.resource = parserUtils.parseNodeText(node); - parserUtils.assignAttributes(node.attributes, verification); - break; - case 'VerificationParameters': - verification.parameters = parserUtils.parseNodeText(node); - break; - } - } - var trackingEventsElement = parserUtils.childByName(verificationNode, 'TrackingEvents'); - if (trackingEventsElement) { - parserUtils.childrenByName(trackingEventsElement, 'Tracking').forEach(function (trackingElement) { - var eventName = trackingElement.getAttribute('event'); - var trackingURLTemplate = parserUtils.parseNodeText(trackingElement); - if (eventName && trackingURLTemplate) { - if (!Array.isArray(verification.trackingEvents[eventName])) { - verification.trackingEvents[eventName] = []; - } - verification.trackingEvents[eventName].push(trackingURLTemplate); - } - }); - } - ver.push(verification); + trackViewableImpression() { + let macros = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + if (typeof macros !== 'object') { + this.emit('TRACKER-error', { + message: "trackViewableImpression given macros has the wrong type. macros: ".concat(macros) + }); + return; + } + this.ad.viewableImpression.forEach(impression => { + this.trackURLs(impression.viewable, macros); }); - return ver; } /** - * Parses the AdVerifications Element from extension for versions < 4.0 - * @param {Array} extensions - The array of extensions to parse. - * @return {Array} + * Tracks NotViewable impression + * @param {Object} [macros = {}] An optional Object containing macros and their values to be used and replaced in the tracking calls. */ - function _parseAdVerificationsFromExtensions(extensions) { - var adVerificationsNode = null, - adVerifications = []; - // Find the first (and only) AdVerifications node from extensions - extensions.some(function (extension) { - return adVerificationsNode = parserUtils.childByName(extension, 'AdVerifications'); + trackNotViewableImpression() { + let macros = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + if (typeof macros !== 'object') { + this.emit('TRACKER-error', { + message: "trackNotViewableImpression given macros has the wrong type. macros: ".concat(macros) + }); + return; + } + this.ad.viewableImpression.forEach(impression => { + this.trackURLs(impression.notViewable, macros); }); + } - // Parse it if we get it - if (adVerificationsNode) { - adVerifications = _parseAdVerifications(parserUtils.childrenByName(adVerificationsNode, 'Verification')); + /** + * Tracks ViewUndetermined impression + * @param {Object} [macros = {}] An optional Object containing macros and their values to be used and replaced in the tracking calls. + */ + trackUndeterminedImpression() { + let macros = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + if (typeof macros !== 'object') { + this.emit('TRACKER-error', { + message: "trackUndeterminedImpression given macros has the wrong type. macros: ".concat(macros) + }); + return; } - return adVerifications; + this.ad.viewableImpression.forEach(impression => { + this.trackURLs(impression.viewUndetermined, macros); + }); } /** - * Parses the ViewableImpression Element. - * @param {Object} viewableImpressionNode - The ViewableImpression node element. - * @return {Object} viewableImpression - The viewableImpression object + * Send a request to the URI provided by the VAST element. + * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. + * @param {Boolean} [isCustomCode=false] - Flag to allow custom values on error code. */ - function _parseViewableImpression(viewableImpressionNode) { - var regroupNodesUrl = function regroupNodesUrl(urls, node) { - var url = parserUtils.parseNodeText(node); - url && urls.push(url); - return urls; - }; - return { - id: viewableImpressionNode.getAttribute('id') || null, - viewable: parserUtils.childrenByName(viewableImpressionNode, 'Viewable').reduce(regroupNodesUrl, []), - notViewable: parserUtils.childrenByName(viewableImpressionNode, 'NotViewable').reduce(regroupNodesUrl, []), - viewUndetermined: parserUtils.childrenByName(viewableImpressionNode, 'ViewUndetermined').reduce(regroupNodesUrl, []) - }; + error() { + let macros = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + let isCustomCode = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; + if (typeof macros !== 'object' || typeof isCustomCode !== 'boolean') { + this.emit('TRACKER-error', { + message: "One given error parameter has the wrong type. macros: ".concat(util.formatMacrosValues(macros), ", isCustomCode: ").concat(isCustomCode) + }); + return; + } + this.trackURLs(this.ad.errorURLTemplates, macros, { + isCustomCode + }); } - var EventEmitter = /*#__PURE__*/function () { - function EventEmitter() { - _classCallCheck(this, EventEmitter); - this._handlers = []; + /** + * Send a request to the URI provided by the VAST element. + * If an [ERRORCODE] macro is included, it will be substitute with errorCode. + * @deprecated + * @param {String} errorCode - Replaces [ERRORCODE] macro. [ERRORCODE] values are listed in the VAST specification. + * @param {Boolean} [isCustomCode=false] - Flag to allow custom values on error code. + */ + errorWithCode(errorCode) { + let isCustomCode = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; + if (typeof errorCode !== 'string' || typeof isCustomCode !== 'boolean') { + this.emit('TRACKER-error', { + message: "One given errorWithCode parameter has the wrong type. errorCode: ".concat(errorCode, ", isCustomCode: ").concat(isCustomCode) + }); + return; } + this.error({ + ERRORCODE: errorCode + }, isCustomCode); + //eslint-disable-next-line + console.log('The method errorWithCode is deprecated, please use vast tracker error method instead'); + } - /** - * Adds the event name and handler function to the end of the handlers array. - * No checks are made to see if the handler has already been added. - * Multiple calls passing the same combination of event name and handler will result in the handler being added, - * and called, multiple times. - * @param {String} event - * @param {Function} handler - * @returns {EventEmitter} - */ - _createClass(EventEmitter, [{ - key: "on", - value: function on(event, handler) { - if (typeof handler !== 'function') { - throw new TypeError("The handler argument must be of type Function. Received type ".concat(_typeof(handler))); - } - if (!event) { - throw new TypeError("The event argument must be of type String. Received type ".concat(_typeof(event))); - } - this._handlers.push({ - event: event, - handler: handler - }); - return this; - } - - /** - * Adds a one-time handler function for the named event. - * The next time event is triggered, this handler is removed and then invoked. - * @param {String} event - * @param {Function} handler - * @returns {EventEmitter} - */ - }, { - key: "once", - value: function once(event, handler) { - return this.on(event, onceWrap(this, event, handler)); - } - - /** - * Removes all instances for the specified handler from the handler array for the named event. - * @param {String} event - * @param {Function} handler - * @returns {EventEmitter} - */ - }, { - key: "off", - value: function off(event, handler) { - this._handlers = this._handlers.filter(function (item) { - return item.event !== event || item.handler !== handler; - }); - return this; - } - - /** - * Synchronously calls each of the handlers registered for the named event, - * in the order they were registered, passing the supplied arguments to each. - * @param {String} event - * @param {...any} args list of arguments that will be used by the event handler - * @returns {Boolean} true if the event had handlers, false otherwise. - */ - }, { - key: "emit", - value: function emit(event) { - for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { - args[_key - 1] = arguments[_key]; - } - var called = false; - this._handlers.forEach(function (item) { - if (item.event === '*') { - called = true; - item.handler.apply(item, [event].concat(args)); - } - if (item.event === event) { - called = true; - item.handler.apply(item, args); - } - }); - return called; - } - - /** - * Removes all listeners, or those of the specified named event. - * @param {String} event - * @returns {EventEmitter} - */ - }, { - key: "removeAllListeners", - value: function removeAllListeners(event) { - if (!event) { - this._handlers = []; - return this; - } - this._handlers = this._handlers.filter(function (item) { - return item.event !== event; - }); - return this; - } - - /** - * Returns the number of listeners listening to the named event. - * @param {String} event - * @returns {Number} - */ - }, { - key: "listenerCount", - value: function listenerCount(event) { - return this._handlers.filter(function (item) { - return item.event === event; - }).length; - } - - /** - * Returns a copy of the array of listeners for the named event including those created by .once(). - * @param {String} event - * @returns {Function[]} - */ - }, { - key: "listeners", - value: function listeners(event) { - return this._handlers.reduce(function (listeners, item) { - if (item.event === event) { - listeners.push(item.handler); - } - return listeners; - }, []); - } - - /** - * Returns an array listing the events for which the emitter has registered handlers. - * @returns {String[]} - */ - }, { - key: "eventNames", - value: function eventNames() { - return this._handlers.map(function (item) { - return item.event; - }); - } - }]); - return EventEmitter; - }(); - function onceWrap(target, event, handler) { - var state = { - fired: false, - wrapFn: undefined - }; - function onceWrapper() { - if (!state.fired) { - target.off(event, state.wrapFn); - state.fired = true; - handler.bind(target).apply(void 0, arguments); - } + /** + * Must be called when the user watched the linear creative until its end. + * Calls the complete tracking URLs. + * + * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. + * @emits VASTTracker#complete + */ + complete() { + let macros = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + if (typeof macros !== 'object') { + this.emit('TRACKER-error', { + message: "complete given macros has the wrong type. macros: ".concat(macros) + }); + return; } - state.wrapFn = onceWrapper; - return onceWrapper; + this.track('complete', { + macros + }); } - // This mock module is loaded in stead of the original NodeURLHandler module - // when bundling the library for environments which are not node. - // This allows us to avoid bundling useless node components and have a smaller build. - function get$2(url, options, cb) { - cb(new Error('Please bundle the library for node to use the node urlHandler')); + /** + * Must be called if the ad was not and will not be played + * This is a terminal event; no other tracking events should be sent when this is used. + * Calls the notUsed tracking URLs. + * + * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. + * @emits VASTTracker#notUsed + */ + notUsed() { + let macros = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + if (typeof macros !== 'object') { + this.emit('TRACKER-error', { + message: "notUsed given macros has the wrong type. macros: ".concat(macros) + }); + return; + } + this.track('notUsed', { + macros + }); + this.trackingEvents = []; } - var nodeURLHandler = { - get: get$2 - }; - var DEFAULT_TIMEOUT = 120000; - - function xhr() { - try { - var request = new window.XMLHttpRequest(); - if ('withCredentials' in request) { - // check CORS support - return request; - } - return null; - } catch (err) { - return null; + /** + * An optional metric that can capture all other user interactions + * under one metric such as hover-overs, or custom clicks. It should NOT replace + * clickthrough events or other existing events like mute, unmute, pause, etc. + * Calls the otherAdInteraction tracking URLs. + * + * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. + * @emits VASTTracker#otherAdInteraction + */ + otherAdInteraction() { + let macros = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + if (typeof macros !== 'object') { + this.emit('TRACKER-error', { + message: "otherAdInteraction given macros has the wrong type. macros: ".concat(macros) + }); + return; } + this.track('otherAdInteraction', { + macros + }); } - function supported() { - return !!xhr(); - } - function handleLoad(request, cb) { - if (request.status === 200) { - cb(null, request.responseXML, { - byteLength: request.response.length, - statusCode: request.status + + /** + * Must be called if the user clicked or otherwise activated a control used to + * pause streaming content,* which either expands the ad within the player’s + * viewable area or “takes-over” the streaming content area by launching + * additional portion of the ad. + * Calls the acceptInvitation tracking URLs. + * + * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. + * @emits VASTTracker#acceptInvitation + */ + acceptInvitation() { + let macros = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + if (typeof macros !== 'object') { + this.emit('TRACKER-error', { + message: "acceptInvitation given macros has the wrong type. macros: ".concat(macros) }); - } else { - handleFail(request, cb, false); + return; } - } - function handleFail(request, cb, isTimeout) { - var statusCode = !isTimeout ? request.status : 408; // Request timeout - var msg = isTimeout ? "XHRURLHandler: Request timed out after ".concat(request.timeout, " ms (").concat(statusCode, ")") : "XHRURLHandler: ".concat(request.statusText, " (").concat(statusCode, ")"); - cb(new Error(msg), null, { - statusCode: statusCode + this.track('acceptInvitation', { + macros }); } - function get$1(url, options, cb) { - if (window.location.protocol === 'https:' && url.indexOf('http://') === 0) { - return cb(new Error('XHRURLHandler: Cannot go from HTTPS to HTTP.')); - } - try { - var request = xhr(); - request.open('GET', url); - request.timeout = options.timeout || DEFAULT_TIMEOUT; - request.withCredentials = options.withCredentials || false; - request.overrideMimeType && request.overrideMimeType('text/xml'); - request.onload = function () { - return handleLoad(request, cb); - }; - request.onerror = function () { - return handleFail(request, cb, false); - }; - request.onabort = function () { - return handleFail(request, cb, false); - }; - request.ontimeout = function () { - return handleFail(request, cb, true); - }; - request.send(); - } catch (error) { - cb(new Error('XHRURLHandler: Unexpected error')); + + /** + * Must be called if user activated a control to expand the creative. + * Calls the adExpand tracking URLs. + * + * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. + * @emits VASTTracker#adExpand + */ + adExpand() { + let macros = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + if (typeof macros !== 'object') { + this.emit('TRACKER-error', { + message: "adExpand given macros has the wrong type. macros: ".concat(macros) + }); + return; } + this.track('adExpand', { + macros + }); } - var XHRURLHandler = { - get: get$1, - supported: supported - }; - function get(url, options, cb) { - // Allow skip of the options param - if (!cb) { - if (typeof options === 'function') { - cb = options; - } - options = {}; - } - if (typeof window === 'undefined' || window === null) { - return nodeURLHandler.get(url, options, cb); - } else if (XHRURLHandler.supported()) { - return XHRURLHandler.get(url, options, cb); + /** + * Must be called when the user activated a control to reduce the creative to its original dimensions. + * Calls the adCollapse tracking URLs. + * + * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. + * @emits VASTTracker#adCollapse + */ + adCollapse() { + let macros = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + if (typeof macros !== 'object') { + this.emit('TRACKER-error', { + message: "adCollapse given macros has the wrong type. macros: ".concat(macros) + }); + return; } - return cb(new Error('Current context is not supported by any of the default URLHandlers. Please provide a custom URLHandler')); + this.track('adCollapse', { + macros + }); } - var urlHandler = { - get: get - }; - function createVASTResponse(_ref) { - var ads = _ref.ads, - errorURLTemplates = _ref.errorURLTemplates, - version = _ref.version; - return { - ads: ads || [], - errorURLTemplates: errorURLTemplates || [], - version: version || null - }; + /** + * Must be called if the user clicked or otherwise activated a control used to minimize the ad. + * Calls the minimize tracking URLs. + * + * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. + * @emits VASTTracker#minimize + */ + minimize() { + let macros = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + if (typeof macros !== 'object') { + this.emit('TRACKER-error', { + message: "minimize given macros has the wrong type. macros: ".concat(macros) + }); + return; + } + this.track('minimize', { + macros + }); } - /* - We decided to put the estimated bitrate separated from classes to persist it between different instances of vast client/parser - */ - - var estimatedBitrateCount = 0; - var estimatedBitrate = 0; - /** - * Calculate average estimated bitrate from the previous values and new entries - * @param {Number} byteLength - The length of the response in bytes. - * @param {Number} duration - The duration of the request in ms. + * Must be called if the player did not or was not able to execute the provided + * verification code.The [REASON] macro must be filled with reason code + * Calls the verificationNotExecuted tracking URL of associated verification vendor. + * + * @param {String} vendor - An identifier for the verification vendor. The recommended format is [domain]-[useCase], to avoid name collisions. For example, "company.com-omid". + * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. + * @emits VASTTracker#verificationNotExecuted */ - var updateEstimatedBitrate = function updateEstimatedBitrate(byteLength, duration) { - if (!byteLength || !duration || byteLength <= 0 || duration <= 0) { + verificationNotExecuted(vendor) { + let macros = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + if (typeof vendor !== 'string' || typeof macros !== 'object') { + this.emit('TRACKER-error', { + message: "One given verificationNotExecuted parameter has to wrong type. vendor: ".concat(vendor, ", macros: ").concat(util.formatMacrosValues(macros)) + }); return; } + if (!this.ad || !this.ad.adVerifications || !this.ad.adVerifications.length) { + throw new Error('No adVerifications provided'); + } + if (!vendor) { + throw new Error('No vendor provided, unable to find associated verificationNotExecuted'); + } + const vendorVerification = this.ad.adVerifications.find(verifications => verifications.vendor === vendor); + if (!vendorVerification) { + throw new Error("No associated verification element found for vendor: ".concat(vendor)); + } + const vendorTracking = vendorVerification.trackingEvents; + if (vendorTracking && vendorTracking.verificationNotExecuted) { + const verifsNotExecuted = vendorTracking.verificationNotExecuted; + this.trackURLs(verifsNotExecuted, macros); + this.emit('verificationNotExecuted', { + trackingURLTemplates: verifsNotExecuted + }); + } + } - // We want the bitrate in kb/s, byteLength are in bytes and duration in ms, just need to convert the byteLength because kb/s = b/ms - var bitrate = byteLength * 8 / duration; - estimatedBitrate = (estimatedBitrate * estimatedBitrateCount + bitrate) / ++estimatedBitrateCount; - }; - - var DEFAULT_MAX_WRAPPER_DEPTH = 10; - var DEFAULT_EVENT_DATA = { - ERRORCODE: 900, - extensions: [] - }; + /** + * The time that the initial ad is displayed. This time is based on + * the time between the impression and either the completed length of display based + * on the agreement between transactional parties or a close, minimize, or accept + * invitation event. + * The time will be passed using [ADPLAYHEAD] macros for VAST 4.1 + * Calls the overlayViewDuration tracking URLs. + * + * @param {String} formattedDuration - The time that the initial ad is displayed. + * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. + * @emits VASTTracker#overlayViewDuration + */ + overlayViewDuration(formattedDuration) { + let macros = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + if (typeof formattedDuration !== 'string' || typeof macros !== 'object') { + this.emit('TRACKER-error', { + message: "One given overlayViewDuration parameters has the wrong type. formattedDuration: ".concat(formattedDuration, ", macros: ").concat(util.formatMacrosValues(macros)) + }); + return; + } + macros['ADPLAYHEAD'] = formattedDuration; + this.track('overlayViewDuration', { + macros + }); + } /** - * This class provides methods to fetch and parse a VAST document. - * @export - * @class VASTParser - * @extends EventEmitter + * Must be called when the player or the window is closed during the ad. + * Calls the `closeLinear` (in VAST 3.0 and 4.1) and `close` tracking URLs. + * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. + * + * @emits VASTTracker#closeLinear + * @emits VASTTracker#close */ - var VASTParser = /*#__PURE__*/function (_EventEmitter) { - _inherits(VASTParser, _EventEmitter); - var _super = _createSuper(VASTParser); - /** - * Creates an instance of VASTParser. - * @constructor - */ - function VASTParser() { - var _this; - _classCallCheck(this, VASTParser); - _this = _super.call(this); - _this.remainingAds = []; - _this.errorURLTemplates = []; - _this.rootErrorURLTemplates = []; - _this.maxWrapperDepth = null; - _this.URLTemplateFilters = []; - _this.fetchingOptions = {}; - _this.parsingOptions = {}; - return _this; + close() { + let macros = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + if (typeof macros !== 'object') { + this.emit('TRACKER-error', { + message: "close given macros has the wrong type. macros: ".concat(macros) + }); + return; } - - /** - * Adds a filter function to the array of filters which are called before fetching a VAST document. - * @param {function} filter - The filter function to be added at the end of the array. - * @return {void} - */ - _createClass(VASTParser, [{ - key: "addURLTemplateFilter", - value: function addURLTemplateFilter(filter) { - if (typeof filter === 'function') { - this.URLTemplateFilters.push(filter); - } - } - - /** - * Removes the last element of the url templates filters array. - * @return {void} - */ - }, { - key: "removeURLTemplateFilter", - value: function removeURLTemplateFilter() { - this.URLTemplateFilters.pop(); - } - - /** - * Returns the number of filters of the url templates filters array. - * @return {Number} - */ - }, { - key: "countURLTemplateFilters", - value: function countURLTemplateFilters() { - return this.URLTemplateFilters.length; - } - - /** - * Removes all the filter functions from the url templates filters array. - * @return {void} - */ - }, { - key: "clearURLTemplateFilters", - value: function clearURLTemplateFilters() { - this.URLTemplateFilters = []; - } - - /** - * Tracks the error provided in the errorCode parameter and emits a VAST-error event for the given error. - * @param {Array} urlTemplates - An Array of url templates to use to make the tracking call. - * @param {Object} errorCode - An Object containing the error data. - * @param {Object} data - One (or more) Object containing additional data. - * @emits VASTParser#VAST-error - * @return {void} - */ - }, { - key: "trackVastError", - value: function trackVastError(urlTemplates, errorCode) { - for (var _len = arguments.length, data = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) { - data[_key - 2] = arguments[_key]; - } - this.emit('VAST-error', Object.assign.apply(Object, [{}, DEFAULT_EVENT_DATA, errorCode].concat(data))); - util.track(urlTemplates, errorCode); - } - - /** - * Returns an array of errorURLTemplates for the VAST being parsed. - * @return {Array} - */ - }, { - key: "getErrorURLTemplates", - value: function getErrorURLTemplates() { - return this.rootErrorURLTemplates.concat(this.errorURLTemplates); - } - - /** - * Returns the estimated bitrate calculated from all previous requests - * @returns The average of all estimated bitrates in kb/s. - */ - }, { - key: "getEstimatedBitrate", - value: function getEstimatedBitrate() { - return estimatedBitrate; - } - - /** - * Fetches a VAST document for the given url. - * Returns a Promise which resolves,rejects according to the result of the request. - * @param {String} url - The url to request the VAST document. - * @param {Number} wrapperDepth - How many times the current url has been wrapped. - * @param {String} previousUrl - Url of the previous VAST. - * @param {Object} wrapperAd - Previously parsed ad node (Wrapper) related to this fetching. - * @emits VASTParser#VAST-resolving - * @emits VASTParser#VAST-resolved - * @return {Promise} - */ - }, { - key: "fetchVAST", - value: function fetchVAST(url) { - var _this2 = this; - var wrapperDepth = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; - var previousUrl = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null; - var wrapperAd = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null; - return new Promise(function (resolve, reject) { - // Process url with defined filter - _this2.URLTemplateFilters.forEach(function (filter) { - url = filter(url); - }); - var timeBeforeGet = Date.now(); - _this2.emit('VAST-resolving', { - url: url, - previousUrl: previousUrl, - wrapperDepth: wrapperDepth, - maxWrapperDepth: _this2.maxWrapperDepth, - timeout: _this2.fetchingOptions.timeout, - wrapperAd: wrapperAd - }); - _this2.urlHandler.get(url, _this2.fetchingOptions, function (error, xml) { - var details = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; - var deltaTime = Math.round(Date.now() - timeBeforeGet); - var info = Object.assign({ - url: url, - previousUrl: previousUrl, - wrapperDepth: wrapperDepth, - error: error, - duration: deltaTime - }, details); - _this2.emit('VAST-resolved', info); - updateEstimatedBitrate(details.byteLength, deltaTime); - if (error) { - reject(error); - } else { - resolve(xml); - } - }); - }); - } - - /** - * Inits the parsing properties of the class with the custom values provided as options. - * @param {Object} options - The options to initialize a parsing sequence - */ - }, { - key: "initParsingStatus", - value: function initParsingStatus() { - var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; - this.errorURLTemplates = []; - this.fetchingOptions = { - timeout: options.timeout || DEFAULT_TIMEOUT, - withCredentials: options.withCredentials - }; - this.maxWrapperDepth = options.wrapperLimit || DEFAULT_MAX_WRAPPER_DEPTH; - this.parsingOptions = { - allowMultipleAds: options.allowMultipleAds - }; - this.remainingAds = []; - this.rootErrorURLTemplates = []; - this.rootURL = ''; - this.urlHandler = options.urlHandler || options.urlhandler || urlHandler; - this.vastVersion = null; - updateEstimatedBitrate(options.byteLength, options.requestDuration); - } - - /** - * Resolves the next group of ads. If all is true resolves all the remaining ads. - * @param {Boolean} all - If true all the remaining ads are resolved - * @return {Promise} - */ - }, { - key: "getRemainingAds", - value: function getRemainingAds(all) { - var _this3 = this; - if (this.remainingAds.length === 0) { - return Promise.reject(new Error('No more ads are available for the given VAST')); - } - var ads = all ? util.flatten(this.remainingAds) : this.remainingAds.shift(); - this.errorURLTemplates = []; - return this.resolveAds(ads, { - wrapperDepth: 0, - url: this.rootURL - }).then(function (resolvedAds) { - return _this3.buildVASTResponse(resolvedAds); - }); - } - - /** - * Fetches and parses a VAST for the given url. - * Returns a Promise which resolves with a fully parsed VASTResponse or rejects with an Error. - * @param {String} url - The url to request the VAST document. - * @param {Object} options - An optional Object of parameters to be used in the parsing process. - * @emits VASTParser#VAST-resolving - * @emits VASTParser#VAST-resolved - * @emits VASTParser#VAST-warning - * @return {Promise} - */ - }, { - key: "getAndParseVAST", - value: function getAndParseVAST(url) { - var _this4 = this; - var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; - this.initParsingStatus(options); - this.URLTemplateFilters.forEach(function (filter) { - url = filter(url); - }); - this.rootURL = url; - return this.fetchVAST(url).then(function (xml) { - options.previousUrl = url; - options.isRootVAST = true; - options.url = url; - return _this4.parse(xml, options).then(function (ads) { - return _this4.buildVASTResponse(ads); - }); - }); - } - - /** - * Parses the given xml Object into a VASTResponse. - * Returns a Promise which resolves with a fully parsed VASTResponse or rejects with an Error. - * @param {Object} vastXml - An object representing a vast xml document. - * @param {Object} options - An optional Object of parameters to be used in the parsing process. - * @emits VASTParser#VAST-resolving - * @emits VASTParser#VAST-resolved - * @emits VASTParser#VAST-warning - * @return {Promise} - */ - }, { - key: "parseVAST", - value: function parseVAST(vastXml) { - var _this5 = this; - var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; - this.initParsingStatus(options); - options.isRootVAST = true; - return this.parse(vastXml, options).then(function (ads) { - return _this5.buildVASTResponse(ads); - }); - } - - /** - * Builds a VASTResponse which can be returned. - * @param {Array} ads - An Array of unwrapped ads - * @return {Object} - */ - }, { - key: "buildVASTResponse", - value: function buildVASTResponse(ads) { - var response = createVASTResponse({ - ads: ads, - errorURLTemplates: this.getErrorURLTemplates(), - version: this.vastVersion - }); - this.completeWrapperResolving(response); - return response; - } - - /** - * Parses the given xml Object into an array of ads - * Returns the array or throws an `Error` if an invalid VAST XML is provided - * @param {Object} vastXml - An object representing an xml document. - * @param {Object} options - An optional Object of parameters to be used in the parsing process. - * @emits VASTParser#VAST-warning - * @emits VASTParser#VAST-ad-parsed - * @return {Array} - * @throws {Error} `vastXml` must be a valid VAST XMLDocument - */ - }, { - key: "parseVastXml", - value: function parseVastXml(vastXml, _ref) { - var _ref$isRootVAST = _ref.isRootVAST, - isRootVAST = _ref$isRootVAST === void 0 ? false : _ref$isRootVAST, - _ref$url = _ref.url, - url = _ref$url === void 0 ? null : _ref$url, - _ref$wrapperDepth = _ref.wrapperDepth, - wrapperDepth = _ref$wrapperDepth === void 0 ? 0 : _ref$wrapperDepth, - allowMultipleAds = _ref.allowMultipleAds, - followAdditionalWrappers = _ref.followAdditionalWrappers; - // check if is a valid VAST document - if (!vastXml || !vastXml.documentElement || vastXml.documentElement.nodeName !== 'VAST') { - this.emit('VAST-ad-parsed', { - type: 'ERROR', - url: url, - wrapperDepth: wrapperDepth - }); - throw new Error('Invalid VAST XMLDocument'); - } - var ads = []; - var childNodes = vastXml.documentElement.childNodes; - - /* Only parse the version of the Root VAST for now because we don't know yet how to - * handle some cases like multiple wrappers in the same vast - */ - var vastVersion = vastXml.documentElement.getAttribute('version'); - if (isRootVAST) { - if (vastVersion) this.vastVersion = vastVersion; - } - - // Fill the VASTResponse object with ads and errorURLTemplates - for (var nodeKey in childNodes) { - var node = childNodes[nodeKey]; - if (node.nodeName === 'Error') { - var errorURLTemplate = parserUtils.parseNodeText(node); - - // Distinguish root VAST url templates from ad specific ones - isRootVAST ? this.rootErrorURLTemplates.push(errorURLTemplate) : this.errorURLTemplates.push(errorURLTemplate); - } else if (node.nodeName === 'Ad') { - // allowMultipleAds was introduced in VAST 3 - // for retrocompatibility set it to true - if (this.vastVersion && parseFloat(this.vastVersion) < 3) { - allowMultipleAds = true; - } else if (allowMultipleAds === false && ads.length > 1) { - // if wrapper allowMultipleAds is set to false only the first stand-alone Ad - // (with no sequence values) in the requested VAST response is allowed - break; - } - var result = parseAd(node, this.emit.bind(this), { - allowMultipleAds: allowMultipleAds, - followAdditionalWrappers: followAdditionalWrappers - }); - if (result.ad) { - ads.push(result.ad); - this.emit('VAST-ad-parsed', { - type: result.type, - url: url, - wrapperDepth: wrapperDepth, - adIndex: ads.length - 1, - vastVersion: vastVersion - }); - } else { - // VAST version of response not supported. - this.trackVastError(this.getErrorURLTemplates(), { - ERRORCODE: 101 - }); - } - } - } - return ads; - } - - /** - * Parses the given xml Object into an array of unwrapped ads. - * Returns a Promise which resolves with the array or rejects with an error according to the result of the parsing. - * @param {Object} vastXml - An object representing an xml document. - * @param {Object} options - An optional Object of parameters to be used in the parsing process. - * @emits VASTParser#VAST-resolving - * @emits VASTParser#VAST-resolved - * @emits VASTParser#VAST-warning - * @return {Promise} - */ - }, { - key: "parse", - value: function parse(vastXml) { - var _ref2 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, - _ref2$url = _ref2.url, - url = _ref2$url === void 0 ? null : _ref2$url, - _ref2$resolveAll = _ref2.resolveAll, - resolveAll = _ref2$resolveAll === void 0 ? true : _ref2$resolveAll, - _ref2$wrapperSequence = _ref2.wrapperSequence, - wrapperSequence = _ref2$wrapperSequence === void 0 ? null : _ref2$wrapperSequence, - _ref2$previousUrl = _ref2.previousUrl, - previousUrl = _ref2$previousUrl === void 0 ? null : _ref2$previousUrl, - _ref2$wrapperDepth = _ref2.wrapperDepth, - wrapperDepth = _ref2$wrapperDepth === void 0 ? 0 : _ref2$wrapperDepth, - _ref2$isRootVAST = _ref2.isRootVAST, - isRootVAST = _ref2$isRootVAST === void 0 ? false : _ref2$isRootVAST, - followAdditionalWrappers = _ref2.followAdditionalWrappers, - allowMultipleAds = _ref2.allowMultipleAds; - var ads = []; - // allowMultipleAds was introduced in VAST 3 as wrapper attribute - // for retrocompatibility set it to true for vast pre-version 3 - if (this.vastVersion && parseFloat(this.vastVersion) < 3 && isRootVAST) { - allowMultipleAds = true; - } - try { - ads = this.parseVastXml(vastXml, { - isRootVAST: isRootVAST, - url: url, - wrapperDepth: wrapperDepth, - allowMultipleAds: allowMultipleAds, - followAdditionalWrappers: followAdditionalWrappers - }); - } catch (e) { - return Promise.reject(e); - } - - /* Keep wrapper sequence value to not break AdPod when wrapper contain only one Ad. - e.g,for a AdPod containing : - - Inline with sequence=1 - - Inline with sequence=2 - - Wrapper with sequence=3 wrapping a Inline with sequence=1 - once parsed we will obtain : - - Inline sequence 1, - - Inline sequence 2, - - Inline sequence 3 - */ - if (ads.length === 1 && wrapperSequence !== undefined && wrapperSequence !== null) { - ads[0].sequence = wrapperSequence; - } - - // Split the VAST in case we don't want to resolve everything at the first time - if (resolveAll === false) { - this.remainingAds = parserUtils.splitVAST(ads); - // Remove the first element from the remaining ads array, since we're going to resolve that element - ads = this.remainingAds.shift(); - } - return this.resolveAds(ads, { - wrapperDepth: wrapperDepth, - previousUrl: previousUrl, - url: url - }); - } - - /** - * Resolves an Array of ads, recursively calling itself with the remaining ads if a no ad - * response is returned for the given array. - * @param {Array} ads - An array of ads to resolve - * @param {Object} options - An options Object containing resolving parameters - * @return {Promise} - */ - }, { - key: "resolveAds", - value: function resolveAds() { - var _this6 = this; - var ads = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; - var _ref3 = arguments.length > 1 ? arguments[1] : undefined, - wrapperDepth = _ref3.wrapperDepth, - previousUrl = _ref3.previousUrl, - url = _ref3.url; - var resolveWrappersPromises = []; - previousUrl = url; - ads.forEach(function (ad) { - var resolveWrappersPromise = _this6.resolveWrappers(ad, wrapperDepth, previousUrl); - resolveWrappersPromises.push(resolveWrappersPromise); - }); - return Promise.all(resolveWrappersPromises).then(function (unwrappedAds) { - var resolvedAds = util.flatten(unwrappedAds); - if (!resolvedAds && _this6.remainingAds.length > 0) { - var remainingAdsToResolve = _this6.remainingAds.shift(); - return _this6.resolveAds(remainingAdsToResolve, { - wrapperDepth: wrapperDepth, - previousUrl: previousUrl, - url: url - }); - } - return resolvedAds; - }); - } - - /** - * Resolves the wrappers for the given ad in a recursive way. - * Returns a Promise which resolves with the unwrapped ad or rejects with an error. - * @param {Object} ad - An ad object to be unwrapped. - * @param {Number} wrapperDepth - The reached depth in the wrapper resolving chain. - * @param {String} previousUrl - The previous vast url. - * @return {Promise} - */ - }, { - key: "resolveWrappers", - value: function resolveWrappers(ad, wrapperDepth, previousUrl) { - var _this7 = this; - return new Promise(function (resolve) { - var _this7$parsingOptions; - // Going one level deeper in the wrapper chain - wrapperDepth++; - // We already have a resolved VAST ad, no need to resolve wrapper - if (!ad.nextWrapperURL) { - delete ad.nextWrapperURL; - return resolve(ad); - } - if (wrapperDepth >= _this7.maxWrapperDepth) { - // Wrapper limit reached, as defined by the video player. - // Too many Wrapper responses have been received with no InLine response. - ad.errorCode = 302; - delete ad.nextWrapperURL; - return resolve(ad); - } - - // Get full URL - ad.nextWrapperURL = parserUtils.resolveVastAdTagURI(ad.nextWrapperURL, previousUrl); - _this7.URLTemplateFilters.forEach(function (filter) { - ad.nextWrapperURL = filter(ad.nextWrapperURL); - }); - - // If allowMultipleAds is set inside the parameter 'option' of public method - // override the vast value by the one provided - var allowMultipleAds = (_this7$parsingOptions = _this7.parsingOptions.allowMultipleAds) !== null && _this7$parsingOptions !== void 0 ? _this7$parsingOptions : ad.allowMultipleAds; - // sequence doesn't carry over in wrapper element - var wrapperSequence = ad.sequence; - _this7.fetchVAST(ad.nextWrapperURL, wrapperDepth, previousUrl, ad).then(function (xml) { - return _this7.parse(xml, { - url: ad.nextWrapperURL, - previousUrl: previousUrl, - wrapperSequence: wrapperSequence, - wrapperDepth: wrapperDepth, - followAdditionalWrappers: ad.followAdditionalWrappers, - allowMultipleAds: allowMultipleAds - }).then(function (unwrappedAds) { - delete ad.nextWrapperURL; - if (unwrappedAds.length === 0) { - // No ads returned by the wrappedResponse, discard current creatives - ad.creatives = []; - return resolve(ad); - } - unwrappedAds.forEach(function (unwrappedAd) { - if (unwrappedAd) { - parserUtils.mergeWrapperAdData(unwrappedAd, ad); - } - }); - resolve(unwrappedAds); - }); - })["catch"](function (err) { - // Timeout of VAST URI provided in Wrapper element, or of VAST URI provided in a subsequent Wrapper element. - // (URI was either unavailable or reached a timeout as defined by the video player.) - ad.errorCode = 301; - ad.errorMessage = err.message; - resolve(ad); - }); - }); - } - - /** - * Takes care of handling errors when the wrappers are resolved. - * @param {Object} vastResponse - A resolved VASTResponse. - */ - }, { - key: "completeWrapperResolving", - value: function completeWrapperResolving(vastResponse) { - // We've to wait for all elements to be parsed before handling error so we can: - // - Send computed extensions data - // - Ping all URIs defined across VAST files - - // No Ad case - The parser never bump into an element - if (vastResponse.ads.length === 0) { - this.trackVastError(vastResponse.errorURLTemplates, { - ERRORCODE: 303 - }); - } else { - for (var index = vastResponse.ads.length - 1; index >= 0; index--) { - // - Error encountered while parsing - // - No Creative case - The parser has dealt with soma or/and an elements - // but no creative was found - var ad = vastResponse.ads[index]; - if (ad.errorCode || ad.creatives.length === 0) { - this.trackVastError(ad.errorURLTemplates.concat(vastResponse.errorURLTemplates), { - ERRORCODE: ad.errorCode || 303 - }, { - ERRORMESSAGE: ad.errorMessage || '' - }, { - extensions: ad.extensions - }, { - system: ad.system - }); - vastResponse.ads.splice(index, 1); - } - } - } - } - }]); - return VASTParser; - }(EventEmitter); - - var storage = null; + this.track(this.linear ? 'closeLinear' : 'close', { + macros + }); + } /** - * This Object represents a default storage to be used in case no other storage is available. - * @constant - * @type {Object} + * Must be called when the skip button is clicked. Calls the skip tracking URLs. + * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. + * + * @emits VASTTracker#skip */ - var DEFAULT_STORAGE = { - data: {}, - length: 0, - getItem: function getItem(key) { - return this.data[key]; - }, - setItem: function setItem(key, value) { - this.data[key] = value; - this.length = Object.keys(this.data).length; - }, - removeItem: function removeItem(key) { - delete this.data[key]; - this.length = Object.keys(this.data).length; - }, - clear: function clear() { - this.data = {}; - this.length = 0; + skip() { + let macros = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + if (typeof macros !== 'object') { + this.emit('TRACKER-error', { + message: "skip given macros has the wrong type. macros: ".concat(macros) + }); + return; } - }; + this.track('skip', { + macros + }); + } /** - * This class provides an wrapper interface to the a key-value storage. - * It uses localStorage, sessionStorage or a custom storage if none of the two is available. - * @export - * @class Storage + * Must be called then loaded and buffered the creative’s media and assets either fully + * or to the extent that it is ready to play the media + * Calls the loaded tracking URLs. + * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. + * + * @emits VASTTracker#loaded */ - var Storage = /*#__PURE__*/function () { - /** - * Creates an instance of Storage. - * @constructor - */ - function Storage() { - _classCallCheck(this, Storage); - this.storage = this.initStorage(); + load() { + let macros = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + if (typeof macros !== 'object') { + this.emit('TRACKER-error', { + message: "load given macros has the wrong type. macros: ".concat(macros) + }); + return; } - - /** - * Provides a singleton instance of the wrapped storage. - * @return {Object} - */ - _createClass(Storage, [{ - key: "initStorage", - value: function initStorage() { - if (storage) { - return storage; - } - try { - storage = typeof window !== 'undefined' && window !== null ? window.localStorage || window.sessionStorage : null; - } catch (storageError) { - storage = null; - } - if (!storage || this.isStorageDisabled(storage)) { - storage = DEFAULT_STORAGE; - storage.clear(); - } - return storage; - } - - /** - * Check if storage is disabled (like in certain cases with private browsing). - * In Safari (Mac + iOS) when private browsing is ON, localStorage is read only - * http://spin.atomicobject.com/2013/01/23/ios-private-browsing-localstorage/ - * @param {Object} testStorage - The storage to check. - * @return {Boolean} - */ - }, { - key: "isStorageDisabled", - value: function isStorageDisabled(testStorage) { - var testValue = '__VASTStorage__'; - try { - testStorage.setItem(testValue, testValue); - if (testStorage.getItem(testValue) !== testValue) { - testStorage.removeItem(testValue); - return true; - } - } catch (e) { - return true; - } - testStorage.removeItem(testValue); - return false; - } - - /** - * Returns the value for the given key. If the key does not exist, null is returned. - * @param {String} key - The key to retrieve the value. - * @return {any} - */ - }, { - key: "getItem", - value: function getItem(key) { - return this.storage.getItem(key); - } - - /** - * Adds or updates the value for the given key. - * @param {String} key - The key to modify the value. - * @param {any} value - The value to be associated with the key. - * @return {any} - */ - }, { - key: "setItem", - value: function setItem(key, value) { - return this.storage.setItem(key, value); - } - - /** - * Removes an item for the given key. - * @param {String} key - The key to remove the value. - * @return {any} - */ - }, { - key: "removeItem", - value: function removeItem(key) { - return this.storage.removeItem(key); - } - - /** - * Removes all the items from the storage. - */ - }, { - key: "clear", - value: function clear() { - return this.storage.clear(); - } - }]); - return Storage; - }(); + this.track('loaded', { + macros + }); + } /** - * This class provides methods to fetch and parse a VAST document using VASTParser. - * In addition it provides options to skip consecutive calls based on constraints. - * @export - * @class VASTClient + * Must be called when the user clicks on the creative. + * It calls the tracking URLs and emits a 'clickthrough' event with the resolved + * clickthrough URL when done. + * + * @param {?String} [fallbackClickThroughURL=null] - an optional clickThroughURL template that could be used as a fallback + * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. + * @emits VASTTracker#clickthrough */ - var VASTClient = /*#__PURE__*/function () { - /** - * Creates an instance of VASTClient. - * @param {Number} cappingFreeLunch - The number of first calls to skip. - * @param {Number} cappingMinimumTimeInterval - The minimum time interval between two consecutive calls. - * @param {Storage} customStorage - A custom storage to use instead of the default one. - * @constructor - */ - function VASTClient(cappingFreeLunch, cappingMinimumTimeInterval, customStorage) { - _classCallCheck(this, VASTClient); - this.cappingFreeLunch = cappingFreeLunch || 0; - this.cappingMinimumTimeInterval = cappingMinimumTimeInterval || 0; - this.defaultOptions = { - withCredentials: false, - timeout: 0 - }; - this.vastParser = new VASTParser(); - this.storage = customStorage || new Storage(); - - // Init values if not already set - if (this.lastSuccessfulAd === undefined) { - this.lastSuccessfulAd = 0; - } - if (this.totalCalls === undefined) { - this.totalCalls = 0; - } - if (this.totalCallsTimeout === undefined) { - this.totalCallsTimeout = 0; - } + click() { + let fallbackClickThroughURL = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null; + let macros = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + if (fallbackClickThroughURL !== null && typeof fallbackClickThroughURL !== 'string' || typeof macros !== 'object') { + this.emit('TRACKER-error', { + message: "One given click parameter has the wrong type. fallbackClickThroughURL: ".concat(fallbackClickThroughURL, ", macros: ").concat(util.formatMacrosValues(macros)) + }); + return; + } + if (this.clickTrackingURLTemplates && this.clickTrackingURLTemplates.length) { + this.trackURLs(this.clickTrackingURLTemplates, macros); } - _createClass(VASTClient, [{ - key: "getParser", - value: function getParser() { - return this.vastParser; - } - }, { - key: "lastSuccessfulAd", - get: function get() { - return this.storage.getItem('vast-client-last-successful-ad'); - }, - set: function set(value) { - this.storage.setItem('vast-client-last-successful-ad', value); - } - }, { - key: "totalCalls", - get: function get() { - return this.storage.getItem('vast-client-total-calls'); - }, - set: function set(value) { - this.storage.setItem('vast-client-total-calls', value); - } - }, { - key: "totalCallsTimeout", - get: function get() { - return this.storage.getItem('vast-client-total-calls-timeout'); - }, - set: function set(value) { - this.storage.setItem('vast-client-total-calls-timeout', value); - } - - /** - * Returns a boolean indicating if there are more ads to resolve for the current parsing. - * @return {Boolean} - */ - }, { - key: "hasRemainingAds", - value: function hasRemainingAds() { - return this.vastParser.remainingAds.length > 0; - } - - /** - * Resolves the next group of ads. If all is true resolves all the remaining ads. - * @param {Boolean} all - If true all the remaining ads are resolved - * @return {Promise} - */ - }, { - key: "getNextAds", - value: function getNextAds(all) { - return this.vastParser.getRemainingAds(all); - } - - /** - * Gets a parsed VAST document for the given url, applying the skipping rules defined. - * Returns a Promise which resolves with a fully parsed VASTResponse or rejects with an Error. - * @param {String} url - The url to use to fecth the VAST document. - * @param {Object} options - An optional Object of parameters to be applied in the process. - * @return {Promise} - */ - }, { - key: "get", - value: function get(url) { - var _this = this; - var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; - var now = Date.now(); - options = Object.assign({}, this.defaultOptions, options); - - // By default the client resolves only the first Ad or AdPod - if (!options.hasOwnProperty('resolveAll')) { - options.resolveAll = false; - } - // Check totalCallsTimeout (first call + 1 hour), if older than now, - // reset totalCalls number, by this way the client will be eligible again - // for freelunch capping - if (this.totalCallsTimeout < now) { - this.totalCalls = 1; - this.totalCallsTimeout = now + 60 * 60 * 1000; - } else { - this.totalCalls++; - } - return new Promise(function (resolve, reject) { - if (_this.cappingFreeLunch >= _this.totalCalls) { - return reject(new Error("VAST call canceled \u2013 FreeLunch capping not reached yet ".concat(_this.totalCalls, "/").concat(_this.cappingFreeLunch))); - } - var timeSinceLastCall = now - _this.lastSuccessfulAd; - - // Check timeSinceLastCall to be a positive number. If not, this mean the - // previous was made in the future. We reset lastSuccessfulAd value - if (timeSinceLastCall < 0) { - _this.lastSuccessfulAd = 0; - } else if (timeSinceLastCall < _this.cappingMinimumTimeInterval) { - return reject(new Error("VAST call canceled \u2013 (".concat(_this.cappingMinimumTimeInterval, ")ms minimum interval reached"))); - } - _this.vastParser.getAndParseVAST(url, options).then(function (response) { - return resolve(response); - })["catch"](function (err) { - return reject(err); - }); - }); + // Use the provided fallbackClickThroughURL as a fallback + const clickThroughURLTemplate = this.clickThroughURLTemplate || fallbackClickThroughURL; + // clone second usage of macros, which get mutated inside resolveURLTemplates + const clonedMacros = { + ...macros + }; + if (clickThroughURLTemplate) { + if (this.progress) { + clonedMacros['ADPLAYHEAD'] = this.progressFormatted(); } - }]); - return VASTClient; - }(); + const clickThroughURL = util.resolveURLTemplates([clickThroughURLTemplate], clonedMacros)[0]; + this.emit('clickthrough', clickThroughURL); + } + } /** - * The default skip delay used in case a custom one is not provided - * @constant - * @type {Number} + * Calls the tracking URLs for the given eventName and emits the event. + * + * @param {String} eventName - The name of the event. + * @param {Object} options + * @param {Object} [options.macros={}] - An optional Object of parameters(vast macros) to be used in the tracking calls. + * @param {Boolean} [options.once=false] - Boolean to define if the event has to be tracked only once. + * */ - var DEFAULT_SKIP_DELAY = -1; + track(eventName) { + let { + macros = {}, + once = false + } = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + if (typeof macros !== 'object') { + this.emit('TRACKER-error', { + message: "track given macros has the wrong type. macros: ".concat(macros) + }); + return; + } + // closeLinear event was introduced in VAST 3.0 + // Fallback to vast 2.0 close event if necessary + if (eventName === 'closeLinear' && !this.trackingEvents[eventName] && this.trackingEvents['close']) { + eventName = 'close'; + } + const trackingURLTemplates = this.trackingEvents[eventName]; + const isAlwaysEmitEvent = this.emitAlwaysEvents.indexOf(eventName) > -1; + if (trackingURLTemplates) { + this.emit(eventName, { + trackingURLTemplates + }); + this.trackURLs(trackingURLTemplates, macros); + } else if (isAlwaysEmitEvent) { + this.emit(eventName, null); + } + if (once) { + delete this.trackingEvents[eventName]; + if (isAlwaysEmitEvent) { + this.emitAlwaysEvents.splice(this.emitAlwaysEvents.indexOf(eventName), 1); + } + } + } /** - * This class provides methods to track an ad execution. + * Calls the tracking urls templates with the given macros . * - * @export - * @class VASTTracker - * @extends EventEmitter + * @param {Array} URLTemplates - An array of tracking url templates. + * @param {Object} [macros ={}] - An optional Object of parameters to be used in the tracking calls. + * @param {Object} [options={}] - An optional Object of options to be used in the tracking calls. */ - var VASTTracker = /*#__PURE__*/function (_EventEmitter) { - _inherits(VASTTracker, _EventEmitter); - var _super = _createSuper(VASTTracker); - /** - * Creates an instance of VASTTracker. - * - * @param {VASTClient} client - An instance of VASTClient that can be updated by the tracker. [optional] - * @param {Ad} ad - The ad to track. - * @param {Creative} creative - The creative to track. - * @param {Object} [variation=null] - An optional variation of the creative. - * @param {Boolean} [muted=false] - The initial muted state of the video. - * @constructor - */ - function VASTTracker(client, ad, creative) { - var _this; - var variation = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null; - var muted = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false; - _classCallCheck(this, VASTTracker); - _this = _super.call(this); - _this.ad = ad; - _this.creative = creative; - _this.variation = variation; - _this.muted = muted; - _this.impressed = false; - _this.skippable = false; - _this.trackingEvents = {}; - // We need to keep the last percentage of the tracker in order to - // calculate to trigger the events when the VAST duration is short - _this.lastPercentage = 0; - _this._alreadyTriggeredQuartiles = {}; - // Tracker listeners should be notified with some events - // no matter if there is a tracking URL or not - _this.emitAlwaysEvents = ['creativeView', 'start', 'firstQuartile', 'midpoint', 'thirdQuartile', 'complete', 'resume', 'pause', 'rewind', 'skip', 'closeLinear', 'close']; - - // Duplicate the creative's trackingEvents property so we can alter it - for (var eventName in _this.creative.trackingEvents) { - var events = _this.creative.trackingEvents[eventName]; - _this.trackingEvents[eventName] = events.slice(0); - } - - // Nonlinear and companion creatives provide some tracking information at a variation level - // While linear creatives provided that at a creative level. That's why we need to - // differentiate how we retrieve some tracking information. - if (isCreativeLinear(_this.creative)) { - _this._initLinearTracking(); - } else { - _this._initVariationTracking(); - } - - // If the tracker is associated with a client we add a listener to the start event - // to update the lastSuccessfulAd property. - if (client) { - _this.on('start', function () { - client.lastSuccessfulAd = Date.now(); - }); - } - return _this; + trackURLs(URLTemplates) { + var _this$creative; + let macros = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; + const { + validUrls, + invalidUrls + } = util.filterUrlTemplates(URLTemplates); + if (invalidUrls.length) { + this.emit('TRACKER-error', { + message: "Provided urls are malformed. url: ".concat(invalidUrls) + }); } - /** - * Init the custom tracking options for linear creatives. - * - * @return {void} - */ - _createClass(VASTTracker, [{ - key: "_initLinearTracking", - value: function _initLinearTracking() { - this.linear = true; - this.skipDelay = this.creative.skipDelay; - this.setDuration(this.creative.duration); - this.clickThroughURLTemplate = this.creative.videoClickThroughURLTemplate; - this.clickTrackingURLTemplates = this.creative.videoClickTrackingURLTemplates; - } - - /** - * Init the custom tracking options for nonlinear and companion creatives. - * These options are provided in the variation Object. - * - * @return {void} - */ - }, { - key: "_initVariationTracking", - value: function _initVariationTracking() { - this.linear = false; - this.skipDelay = DEFAULT_SKIP_DELAY; - - // If no variation has been provided there's nothing else to set - if (!this.variation) { - return; - } - - // Duplicate the variation's trackingEvents property so we can alter it - for (var eventName in this.variation.trackingEvents) { - var events = this.variation.trackingEvents[eventName]; - - // If for the given eventName we already had some trackingEvents provided by the creative - // we want to keep both the creative trackingEvents and the variation ones - if (this.trackingEvents[eventName]) { - this.trackingEvents[eventName] = this.trackingEvents[eventName].concat(events.slice(0)); - } else { - this.trackingEvents[eventName] = events.slice(0); - } - } - if (isNonLinearAd(this.variation)) { - this.clickThroughURLTemplate = this.variation.nonlinearClickThroughURLTemplate; - this.clickTrackingURLTemplates = this.variation.nonlinearClickTrackingURLTemplates; - this.setDuration(this.variation.minSuggestedDuration); - } else if (isCompanionAd(this.variation)) { - this.clickThroughURLTemplate = this.variation.companionClickThroughURLTemplate; - this.clickTrackingURLTemplates = this.variation.companionClickTrackingURLTemplates; - } - } - - /** - * Sets the duration of the ad and updates the quartiles based on that. - * - * @param {Number} duration - The duration of the ad. - */ - }, { - key: "setDuration", - value: function setDuration(duration) { - // check if duration is a valid time input - if (!util.isValidTimeValue(duration)) { - return; - } - this.assetDuration = duration; - // beware of key names, theses are also used as event names - this.quartiles = { - firstQuartile: Math.round(25 * this.assetDuration) / 100, - midpoint: Math.round(50 * this.assetDuration) / 100, - thirdQuartile: Math.round(75 * this.assetDuration) / 100 - }; - } - - /** - * Sets the duration of the ad and updates the quartiles based on that. - * This is required for tracking time related events. - * - * @param {Number} progress - Current playback time in seconds. - * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. - * @emits VASTTracker#start - * @emits VASTTracker#skip-countdown - * @emits VASTTracker#progress-[0-100]% - * @emits VASTTracker#progress-[currentTime] - * @emits VASTTracker#rewind - * @emits VASTTracker#firstQuartile - * @emits VASTTracker#midpoint - * @emits VASTTracker#thirdQuartile - */ - }, { - key: "setProgress", - value: function setProgress(progress) { - var _this2 = this; - var macros = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; - // check if progress is a valid time input - if (!util.isValidTimeValue(progress) || _typeof(macros) !== 'object') { - return; - } - var skipDelay = this.skipDelay || DEFAULT_SKIP_DELAY; - if (skipDelay !== -1 && !this.skippable) { - if (skipDelay > progress) { - this.emit('skip-countdown', skipDelay - progress); - } else { - this.skippable = true; - this.emit('skip-countdown', 0); - } - } - if (this.assetDuration > 0) { - var percent = Math.round(progress / this.assetDuration * 100); - var events = []; - if (progress > 0) { - events.push('start'); - for (var i = this.lastPercentage; i < percent; i++) { - events.push("progress-".concat(i + 1, "%")); - } - events.push("progress-".concat(Math.round(progress))); - for (var quartile in this.quartiles) { - if (this.isQuartileReached(quartile, this.quartiles[quartile], progress)) { - events.push(quartile); - this._alreadyTriggeredQuartiles[quartile] = true; - } - } - this.lastPercentage = percent; - } - events.forEach(function (eventName) { - _this2.track(eventName, { - macros: macros, - once: true - }); - }); - if (progress < this.progress) { - this.track('rewind', { - macros: macros - }); - } - } - this.progress = progress; - } - - /** - * Checks if a quartile has been reached without have being triggered already. - * - * @param {String} quartile - Quartile name - * @param {Number} time - Time offset of the quartile, when this quartile is reached in seconds. - * @param {Number} progress - Current progress of the ads in seconds. - * - * @return {Boolean} - */ - }, { - key: "isQuartileReached", - value: function isQuartileReached(quartile, time, progress) { - var quartileReached = false; - // if quartile time already reached and never triggered - if (time <= progress && !this._alreadyTriggeredQuartiles[quartile]) { - quartileReached = true; - } - return quartileReached; - } - - /** - * Updates the mute state and calls the mute/unmute tracking URLs. - * - * @param {Boolean} muted - Indicates if the video is muted or not. - * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. - * @emits VASTTracker#mute - * @emits VASTTracker#unmute - */ - }, { - key: "setMuted", - value: function setMuted(muted) { - var macros = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; - if (typeof muted !== 'boolean' || _typeof(macros) !== 'object') { - return; - } - if (this.muted !== muted) { - this.track(muted ? 'mute' : 'unmute', { - macros: macros - }); - } - this.muted = muted; - } - - /** - * Update the pause state and call the resume/pause tracking URLs. - * - * @param {Boolean} paused - Indicates if the video is paused or not. - * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. - * @emits VASTTracker#pause - * @emits VASTTracker#resume - */ - }, { - key: "setPaused", - value: function setPaused(paused) { - var macros = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; - if (typeof paused !== 'boolean' || _typeof(macros) !== 'object') { - return; - } - if (this.paused !== paused) { - this.track(paused ? 'pause' : 'resume', { - macros: macros - }); - } - this.paused = paused; - } - - /** - * Updates the fullscreen state and calls the fullscreen tracking URLs. - * - * @param {Boolean} fullscreen - Indicates if the video is in fulscreen mode or not. - * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. - * @emits VASTTracker#fullscreen - * @emits VASTTracker#exitFullscreen - */ - }, { - key: "setFullscreen", - value: function setFullscreen(fullscreen) { - var macros = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; - if (typeof fullscreen !== 'boolean' || _typeof(macros) !== 'object') { - return; - } - if (this.fullscreen !== fullscreen) { - this.track(fullscreen ? 'fullscreen' : 'exitFullscreen', { - macros: macros - }); - } - this.fullscreen = fullscreen; - } - - /** - * Updates the expand state and calls the expand/collapse tracking URLs. - * - * @param {Boolean} expanded - Indicates if the video is expanded or not. - * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. - * @emits VASTTracker#expand - * @emits VASTTracker#playerExpand - * @emits VASTTracker#collapse - * @emits VASTTracker#playerCollapse - */ - }, { - key: "setExpand", - value: function setExpand(expanded) { - var macros = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; - if (typeof expanded !== 'boolean' || _typeof(macros) !== 'object') { - return; - } - if (this.expanded !== expanded) { - this.track(expanded ? 'expand' : 'collapse', { - macros: macros - }); - this.track(expanded ? 'playerExpand' : 'playerCollapse', { - macros: macros - }); - } - this.expanded = expanded; - } - - /** - * Must be called if you want to overwrite the Skipoffset value. - * This will init the skip countdown duration. Then, every time setProgress() is called, - * it will decrease the countdown and emit a skip-countdown event with the remaining time. - * Do not call this method if you want to keep the original Skipoffset value. - * - * @param {Number} duration - The time in seconds until the skip button is displayed. - */ - }, { - key: "setSkipDelay", - value: function setSkipDelay(duration) { - if (!util.isValidTimeValue(duration)) { - return; - } - this.skipDelay = duration; - } - - /** - * Tracks an impression (can be called only once). - * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. - * @emits VASTTracker#creativeView - */ - }, { - key: "trackImpression", - value: function trackImpression() { - var macros = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; - if (_typeof(macros) !== 'object') { - return; - } - if (!this.impressed) { - this.impressed = true; - this.trackURLs(this.ad.impressionURLTemplates, macros); - this.track('creativeView', { - macros: macros - }); - } - } - - /** - * Tracks Viewable impression - * @param {Object} [macros = {}] An optional Object containing macros and their values to be used and replaced in the tracking calls. - */ - }, { - key: "trackViewableImpression", - value: function trackViewableImpression() { - var _this3 = this; - var macros = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; - if (_typeof(macros) !== 'object') { - return; - } - this.ad.viewableImpression.forEach(function (impression) { - _this3.trackURLs(impression.viewable, macros); - }); - } - - /** - * Tracks NotViewable impression - * @param {Object} [macros = {}] An optional Object containing macros and their values to be used and replaced in the tracking calls. - */ - }, { - key: "trackNotViewableImpression", - value: function trackNotViewableImpression() { - var _this4 = this; - var macros = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; - if (_typeof(macros) !== 'object') { - return; - } - this.ad.viewableImpression.forEach(function (impression) { - _this4.trackURLs(impression.notViewable, macros); - }); - } - - /** - * Tracks ViewUndetermined impression - * @param {Object} [macros = {}] An optional Object containing macros and their values to be used and replaced in the tracking calls. - */ - }, { - key: "trackUndeterminedImpression", - value: function trackUndeterminedImpression() { - var _this5 = this; - var macros = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; - if (_typeof(macros) !== 'object') { - return; - } - this.ad.viewableImpression.forEach(function (impression) { - _this5.trackURLs(impression.viewUndetermined, macros); - }); - } - - /** - * Send a request to the URI provided by the VAST element. - * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. - * @param {Boolean} [isCustomCode=false] - Flag to allow custom values on error code. - */ - }, { - key: "error", - value: function error() { - var macros = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; - var isCustomCode = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; - if (_typeof(macros) !== 'object' || typeof isCustomCode !== 'boolean') { - return; - } - this.trackURLs(this.ad.errorURLTemplates, macros, { - isCustomCode: isCustomCode - }); - } - - /** - * Send a request to the URI provided by the VAST element. - * If an [ERRORCODE] macro is included, it will be substitute with errorCode. - * @deprecated - * @param {String} errorCode - Replaces [ERRORCODE] macro. [ERRORCODE] values are listed in the VAST specification. - * @param {Boolean} [isCustomCode=false] - Flag to allow custom values on error code. - */ - }, { - key: "errorWithCode", - value: function errorWithCode(errorCode) { - var isCustomCode = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; - if (typeof errorCode !== 'string' || typeof isCustomCode !== 'boolean') { - return; - } - this.error({ - ERRORCODE: errorCode - }, isCustomCode); - //eslint-disable-next-line - console.log('The method errorWithCode is deprecated, please use vast tracker error method instead'); - } - - /** - * Must be called when the user watched the linear creative until its end. - * Calls the complete tracking URLs. - * - * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. - * @emits VASTTracker#complete - */ - }, { - key: "complete", - value: function complete() { - var macros = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; - if (_typeof(macros) !== 'object') { - return; - } - this.track('complete', { - macros: macros - }); - } - - /** - * Must be called if the ad was not and will not be played - * This is a terminal event; no other tracking events should be sent when this is used. - * Calls the notUsed tracking URLs. - * - * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. - * @emits VASTTracker#notUsed - */ - }, { - key: "notUsed", - value: function notUsed() { - var macros = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; - if (_typeof(macros) !== 'object') { - return; - } - this.track('notUsed', { - macros: macros - }); - this.trackingEvents = []; - } - - /** - * An optional metric that can capture all other user interactions - * under one metric such as hover-overs, or custom clicks. It should NOT replace - * clickthrough events or other existing events like mute, unmute, pause, etc. - * Calls the otherAdInteraction tracking URLs. - * - * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. - * @emits VASTTracker#otherAdInteraction - */ - }, { - key: "otherAdInteraction", - value: function otherAdInteraction() { - var macros = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; - if (_typeof(macros) !== 'object') { - return; - } - this.track('otherAdInteraction', { - macros: macros - }); - } - - /** - * Must be called if the user clicked or otherwise activated a control used to - * pause streaming content,* which either expands the ad within the player’s - * viewable area or “takes-over” the streaming content area by launching - * additional portion of the ad. - * Calls the acceptInvitation tracking URLs. - * - * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. - * @emits VASTTracker#acceptInvitation - */ - }, { - key: "acceptInvitation", - value: function acceptInvitation() { - var macros = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; - if (_typeof(macros) !== 'object') { - return; - } - this.track('acceptInvitation', { - macros: macros - }); - } - - /** - * Must be called if user activated a control to expand the creative. - * Calls the adExpand tracking URLs. - * - * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. - * @emits VASTTracker#adExpand - */ - }, { - key: "adExpand", - value: function adExpand() { - var macros = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; - if (_typeof(macros) !== 'object') { - return; - } - this.track('adExpand', { - macros: macros - }); - } - - /** - * Must be called when the user activated a control to reduce the creative to its original dimensions. - * Calls the adCollapse tracking URLs. - * - * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. - * @emits VASTTracker#adCollapse - */ - }, { - key: "adCollapse", - value: function adCollapse() { - var macros = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; - if (_typeof(macros) !== 'object') { - return; - } - this.track('adCollapse', { - macros: macros - }); - } - - /** - * Must be called if the user clicked or otherwise activated a control used to minimize the ad. - * Calls the minimize tracking URLs. - * - * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. - * @emits VASTTracker#minimize - */ - }, { - key: "minimize", - value: function minimize() { - var macros = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; - if (_typeof(macros) !== 'object') { - return; - } - this.track('minimize', { - macros: macros - }); - } - - /** - * Must be called if the player did not or was not able to execute the provided - * verification code.The [REASON] macro must be filled with reason code - * Calls the verificationNotExecuted tracking URL of associated verification vendor. - * - * @param {String} vendor - An identifier for the verification vendor. The recommended format is [domain]-[useCase], to avoid name collisions. For example, "company.com-omid". - * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. - * @emits VASTTracker#verificationNotExecuted - */ - }, { - key: "verificationNotExecuted", - value: function verificationNotExecuted(vendor) { - var macros = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; - if (typeof vendor !== 'string' || _typeof(macros) !== 'object') { - return; - } - if (!this.ad || !this.ad.adVerifications || !this.ad.adVerifications.length) { - throw new Error('No adVerifications provided'); - } - if (!vendor) { - throw new Error('No vendor provided, unable to find associated verificationNotExecuted'); - } - var vendorVerification = this.ad.adVerifications.find(function (verifications) { - return verifications.vendor === vendor; - }); - if (!vendorVerification) { - throw new Error("No associated verification element found for vendor: ".concat(vendor)); - } - var vendorTracking = vendorVerification.trackingEvents; - if (vendorTracking && vendorTracking.verificationNotExecuted) { - var verifsNotExecuted = vendorTracking.verificationNotExecuted; - this.trackURLs(verifsNotExecuted, macros); - this.emit('verificationNotExecuted', { - trackingURLTemplates: verifsNotExecuted - }); - } - } - - /** - * The time that the initial ad is displayed. This time is based on - * the time between the impression and either the completed length of display based - * on the agreement between transactional parties or a close, minimize, or accept - * invitation event. - * The time will be passed using [ADPLAYHEAD] macros for VAST 4.1 - * Calls the overlayViewDuration tracking URLs. - * - * @param {String} formattedDuration - The time that the initial ad is displayed. - * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. - * @emits VASTTracker#overlayViewDuration - */ - }, { - key: "overlayViewDuration", - value: function overlayViewDuration(formattedDuration) { - var macros = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; - if (typeof formattedDuration !== 'string' || _typeof(macros) !== 'object') { - return; - } - macros['ADPLAYHEAD'] = formattedDuration; - this.track('overlayViewDuration', { - macros: macros - }); - } - - /** - * Must be called when the player or the window is closed during the ad. - * Calls the `closeLinear` (in VAST 3.0 and 4.1) and `close` tracking URLs. - * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. - * - * @emits VASTTracker#closeLinear - * @emits VASTTracker#close - */ - }, { - key: "close", - value: function close() { - var macros = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; - if (_typeof(macros) !== 'object') { - return; - } - this.track(this.linear ? 'closeLinear' : 'close', { - macros: macros - }); - } - - /** - * Must be called when the skip button is clicked. Calls the skip tracking URLs. - * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. - * - * @emits VASTTracker#skip - */ - }, { - key: "skip", - value: function skip() { - var macros = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; - if (_typeof(macros) !== 'object') { - return; - } - this.track('skip', { - macros: macros - }); + //Avoid mutating the object received in parameters. + const givenMacros = { + ...macros + }; + if (this.linear) { + if (this.creative && this.creative.mediaFiles && this.creative.mediaFiles[0] && this.creative.mediaFiles[0].fileURL) { + givenMacros['ASSETURI'] = this.creative.mediaFiles[0].fileURL; } - - /** - * Must be called then loaded and buffered the creative’s media and assets either fully - * or to the extent that it is ready to play the media - * Calls the loaded tracking URLs. - * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. - * - * @emits VASTTracker#loaded - */ - }, { - key: "load", - value: function load() { - var macros = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; - if (_typeof(macros) !== 'object') { - return; - } - this.track('loaded', { - macros: macros - }); + if (this.progress) { + givenMacros['ADPLAYHEAD'] = this.progressFormatted(); } - - /** - * Must be called when the user clicks on the creative. - * It calls the tracking URLs and emits a 'clickthrough' event with the resolved - * clickthrough URL when done. - * - * @param {?String} [fallbackClickThroughURL=null] - an optional clickThroughURL template that could be used as a fallback - * @param {Object} [macros={}] - An optional Object containing macros and their values to be used and replaced in the tracking calls. - * @emits VASTTracker#clickthrough - */ - }, { - key: "click", - value: function click() { - var fallbackClickThroughURL = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null; - var macros = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; - if (fallbackClickThroughURL !== null && typeof fallbackClickThroughURL !== 'string' || _typeof(macros) !== 'object') { - return; - } - if (this.clickTrackingURLTemplates && this.clickTrackingURLTemplates.length) { - this.trackURLs(this.clickTrackingURLTemplates, macros); - } - - // Use the provided fallbackClickThroughURL as a fallback - var clickThroughURLTemplate = this.clickThroughURLTemplate || fallbackClickThroughURL; - // clone second usage of macros, which get mutated inside resolveURLTemplates - var clonedMacros = _objectSpread2({}, macros); - if (clickThroughURLTemplate) { - if (this.progress) { - clonedMacros['ADPLAYHEAD'] = this.progressFormatted(); - } - var clickThroughURL = util.resolveURLTemplates([clickThroughURLTemplate], clonedMacros)[0]; - this.emit('clickthrough', clickThroughURL); - } + } + if ((_this$creative = this.creative) !== null && _this$creative !== void 0 && (_this$creative = _this$creative.universalAdIds) !== null && _this$creative !== void 0 && _this$creative.length) { + givenMacros['UNIVERSALADID'] = this.creative.universalAdIds.map(universalAdId => universalAdId.idRegistry.concat(' ', universalAdId.value)).join(','); + } + if (this.ad) { + if (this.ad.sequence) { + givenMacros['PODSEQUENCE'] = this.ad.sequence; } - - /** - * Calls the tracking URLs for the given eventName and emits the event. - * - * @param {String} eventName - The name of the event. - * @param {Object} options - * @param {Object} [options.macros={}] - An optional Object of parameters(vast macros) to be used in the tracking calls. - * @param {Boolean} [options.once=false] - Boolean to define if the event has to be tracked only once. - * - */ - }, { - key: "track", - value: function track(eventName) { - var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, - _ref$macros = _ref.macros, - macros = _ref$macros === void 0 ? {} : _ref$macros, - _ref$once = _ref.once, - once = _ref$once === void 0 ? false : _ref$once; - if (_typeof(macros) !== 'object') { - return; - } - // closeLinear event was introduced in VAST 3.0 - // Fallback to vast 2.0 close event if necessary - if (eventName === 'closeLinear' && !this.trackingEvents[eventName] && this.trackingEvents['close']) { - eventName = 'close'; - } - var trackingURLTemplates = this.trackingEvents[eventName]; - var isAlwaysEmitEvent = this.emitAlwaysEvents.indexOf(eventName) > -1; - if (trackingURLTemplates) { - this.emit(eventName, { - trackingURLTemplates: trackingURLTemplates - }); - this.trackURLs(trackingURLTemplates, macros); - } else if (isAlwaysEmitEvent) { - this.emit(eventName, null); - } - if (once) { - delete this.trackingEvents[eventName]; - if (isAlwaysEmitEvent) { - this.emitAlwaysEvents.splice(this.emitAlwaysEvents.indexOf(eventName), 1); - } - } + if (this.ad.adType) { + givenMacros['ADTYPE'] = this.ad.adType; } - - /** - * Calls the tracking urls templates with the given macros . - * - * @param {Array} URLTemplates - An array of tracking url templates. - * @param {Object} [macros ={}] - An optional Object of parameters to be used in the tracking calls. - * @param {Object} [options={}] - An optional Object of options to be used in the tracking calls. - */ - }, { - key: "trackURLs", - value: function trackURLs(URLTemplates) { - var _this$creative; - var macros = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; - var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; - var validUrlTemplates = util.filterValidUrlTemplates(URLTemplates); - //Avoid mutating the object received in parameters. - var givenMacros = _objectSpread2({}, macros); - if (this.linear) { - if (this.creative && this.creative.mediaFiles && this.creative.mediaFiles[0] && this.creative.mediaFiles[0].fileURL) { - givenMacros['ASSETURI'] = this.creative.mediaFiles[0].fileURL; - } - if (this.progress) { - givenMacros['ADPLAYHEAD'] = this.progressFormatted(); - } - } - if ((_this$creative = this.creative) !== null && _this$creative !== void 0 && (_this$creative = _this$creative.universalAdIds) !== null && _this$creative !== void 0 && _this$creative.length) { - givenMacros['UNIVERSALADID'] = this.creative.universalAdIds.map(function (universalAdId) { - return universalAdId.idRegistry.concat(' ', universalAdId.value); - }).join(','); - } - if (this.ad) { - if (this.ad.sequence) { - givenMacros['PODSEQUENCE'] = this.ad.sequence; - } - if (this.ad.adType) { - givenMacros['ADTYPE'] = this.ad.adType; - } - if (this.ad.adServingId) { - givenMacros['ADSERVINGID'] = this.ad.adServingId; - } - if (this.ad.categories && this.ad.categories.length) { - givenMacros['ADCATEGORIES'] = this.ad.categories.map(function (category) { - return category.value; - }).join(','); - } - if (this.ad.blockedAdCategories && this.ad.blockedAdCategories.length) { - givenMacros['BLOCKEDADCATEGORIES'] = this.ad.blockedAdCategories; - } - } - util.track(validUrlTemplates, givenMacros, options); + if (this.ad.adServingId) { + givenMacros['ADSERVINGID'] = this.ad.adServingId; } - - /** - * Formats time in seconds to VAST timecode (e.g. 00:00:10.000) - * - * @param {Number} timeInSeconds - Number in seconds - * @return {String} - */ - }, { - key: "convertToTimecode", - value: function convertToTimecode(timeInSeconds) { - if (!util.isValidTimeValue(timeInSeconds)) { - return ''; - } - var progress = timeInSeconds * 1000; - var hours = Math.floor(progress / (60 * 60 * 1000)); - var minutes = Math.floor(progress / (60 * 1000) % 60); - var seconds = Math.floor(progress / 1000 % 60); - var milliseconds = Math.floor(progress % 1000); - return "".concat(util.addLeadingZeros(hours, 2), ":").concat(util.addLeadingZeros(minutes, 2), ":").concat(util.addLeadingZeros(seconds, 2), ".").concat(util.addLeadingZeros(milliseconds, 3)); + if (this.ad.categories && this.ad.categories.length) { + givenMacros['ADCATEGORIES'] = this.ad.categories.map(category => category.value).join(','); } - - /** - * Formats time progress in a readable string. - * - * @return {String} - */ - }, { - key: "progressFormatted", - value: function progressFormatted() { - return this.convertToTimecode(this.progress); + if (this.ad.blockedAdCategories && this.ad.blockedAdCategories.length) { + givenMacros['BLOCKEDADCATEGORIES'] = this.ad.blockedAdCategories.map(blockedCategorie => blockedCategorie.value).join(','); } - }]); - return VASTTracker; - }(EventEmitter); + } + util.track(validUrls, givenMacros, options); + } - exports.VASTClient = VASTClient; - exports.VASTParser = VASTParser; - exports.VASTTracker = VASTTracker; - exports.parseDuration = parseDuration; + /** + * Formats time in seconds to VAST timecode (e.g. 00:00:10.000) + * + * @param {Number} timeInSeconds - Number in seconds + * @return {String} + */ + convertToTimecode(timeInSeconds) { + if (!util.isValidTimeValue(timeInSeconds)) { + return ''; + } + const progress = timeInSeconds * 1000; + const hours = Math.floor(progress / (60 * 60 * 1000)); + const minutes = Math.floor(progress / (60 * 1000) % 60); + const seconds = Math.floor(progress / 1000 % 60); + const milliseconds = Math.floor(progress % 1000); + return "".concat(util.addLeadingZeros(hours, 2), ":").concat(util.addLeadingZeros(minutes, 2), ":").concat(util.addLeadingZeros(seconds, 2), ".").concat(util.addLeadingZeros(milliseconds, 3)); + } - Object.defineProperty(exports, '__esModule', { value: true }); + /** + * Formats time progress in a readable string. + * + * @return {String} + */ + progressFormatted() { + return this.convertToTimecode(this.progress); + } +} -})); +export { VASTClient, VASTParser, VASTTracker, parseDuration }; diff --git a/dist/vast-client.min.js b/dist/vast-client.min.js index 6b0c570f..b88a6371 100644 --- a/dist/vast-client.min.js +++ b/dist/vast-client.min.js @@ -1 +1 @@ -!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).VAST={})}(this,(function(e){"use strict";function t(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,i)}return r}function r(e){for(var r=1;re.length)&&(t=e.length);for(var r=0,i=new Array(t);r0&&void 0!==arguments[0]?arguments[0]:{};return{id:e.id||null,adId:e.adId||null,sequence:e.sequence||null,apiFramework:e.apiFramework||null,universalAdIds:[],creativeExtensions:[]}}var g=["ADCATEGORIES","ADCOUNT","ADPLAYHEAD","ADSERVINGID","ADTYPE","APIFRAMEWORKS","APPBUNDLE","ASSETURI","BLOCKEDADCATEGORIES","BREAKMAXADLENGTH","BREAKMAXADS","BREAKMAXDURATION","BREAKMINADLENGTH","BREAKMINDURATION","BREAKPOSITION","CLICKPOS","CLICKTYPE","CLIENTUA","CONTENTID","CONTENTPLAYHEAD","CONTENTURI","DEVICEIP","DEVICEUA","DOMAIN","EXTENSIONS","GDPRCONSENT","IFA","IFATYPE","INVENTORYSTATE","LATLONG","LIMITADTRACKING","MEDIAMIME","MEDIAPLAYHEAD","OMIDPARTNER","PAGEURL","PLACEMENTTYPE","PLAYERCAPABILITIES","PLAYERSIZE","PLAYERSTATE","PODSEQUENCE","REGULATIONS","SERVERSIDE","SERVERUA","TRANSACTIONID","UNIVERSALADID","VASTVERSIONS","VERIFICATIONVENDORS"];function y(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},i=[],n=A(e);for(var a in!t.ERRORCODE||r.isCustomCode||/^[0-9]{3}$/.test(t.ERRORCODE)||(t.ERRORCODE=900),t.CACHEBUSTING=w(Math.round(1e8*Math.random())),t.TIMESTAMP=(new Date).toISOString(),t.RANDOM=t.random=t.CACHEBUSTING,t)t[a]=N(t[a]);for(var o in n){var s=n[o];"string"==typeof s&&i.push(T(s,t))}return i}function T(e,t){var r=(e=k(e,t)).match(/[^[\]]+(?=])/g);if(!r)return e;var i=r.filter((function(e){return g.indexOf(e)>-1}));return 0===i.length?e:k(e,i=i.reduce((function(e,t){return e[t]=-1,e}),{}))}function k(e,t){var r=e;for(var i in t){var n=t[i];r=r.replace(new RegExp("(?:\\[|%%)(".concat(i,")(?:\\]|%%)"),"g"),n)}return r}function A(e){return Array.isArray(e)?e.map((function(e){return e&&e.hasOwnProperty("url")?e.url:e})):e}function b(e){return/^(https?:\/\/|\/\/)/.test(e)}function R(e,t){for(var r=0;r1&&void 0!==arguments[1]?arguments[1]:8;return e.toString().padStart(t,"0")}var L={track:function(e,t,r){y(e,t,r).forEach((function(e){"undefined"!=typeof window&&null!==window&&((new Image).src=e)}))},resolveURLTemplates:y,extractURLsFromTemplates:A,filterValidUrlTemplates:function(e){return Array.isArray(e)?e.filter((function(e){return b(e.hasOwnProperty("url")?e.url:e)})):b(e)},containsTemplateObject:R,isTemplateObjectEqual:E,encodeURIComponentRFC3986:N,replaceUrlMacros:T,isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},flatten:function e(t){return t.reduce((function(t,r){return t.concat(Array.isArray(r)?e(r):r)}),[])},joinArrayOfUniqueTemplateObjs:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[],t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:[],r=Array.isArray(e)?e:[],i=Array.isArray(t)?t:[];return r.concat(i).reduce((function(e,t){return R(t,e)||e.push(t),e}),[])},isValidTimeValue:function(e){return Number.isFinite(e)&&e>=-2},addLeadingZeros:w};function U(e){return-1!==["true","TRUE","True","1"].indexOf(e)}function C(e){if(null==e)return-1;if(L.isNumeric(e))return parseInt(e);var t=e.split(":");if(3!==t.length)return-1;var r=t[2].split("."),i=parseInt(r[0]);2===r.length&&(i+=parseFloat("0.".concat(r[1])));var n=parseInt(60*t[1]),a=parseInt(60*t[0]*60);return isNaN(a)||isNaN(n)||isNaN(i)||n>3600||i>60?-1:a+n+i}var I={childByName:function(e,t){var r=e.childNodes;for(var i in r){var n=r[i];if(n.nodeName===t)return n}},childrenByName:function(e,t){var r=[],i=e.childNodes;for(var n in i){var a=i[n];a.nodeName===t&&r.push(a)}return r},resolveVastAdTagURI:function(e,t){if(!t)return e;if(0===e.indexOf("//")){var r=location.protocol;return"".concat(r).concat(e)}if(-1===e.indexOf("://")){var i=t.slice(0,t.lastIndexOf("/"));return"".concat(i,"/").concat(e)}return e},parseBoolean:U,parseNodeText:function(e){return e&&(e.textContent||e.text||"").trim()},copyNodeAttribute:function(e,t,r){var i=t.getAttribute(e);i&&r.setAttribute(e,i)},parseAttributes:function(e){for(var t=e.attributes,r={},i=0;i1){var a=e[n-1];if(a&&a.sequence===i.sequence-1)return void(r&&r.push(i));delete i.sequence}r=[i],t.push(r)})),t},assignAttributes:function(e,t){if(e)for(var r in e){var i=e[r];if(i.nodeName&&i.nodeValue&&t.hasOwnProperty(i.nodeName)){var n=i.nodeValue;"boolean"==typeof t[i.nodeName]&&(n=U(n)),t[i.nodeName]=n}}},mergeWrapperAdData:function(e,t){var r;e.errorURLTemplates=t.errorURLTemplates.concat(e.errorURLTemplates),e.impressionURLTemplates=t.impressionURLTemplates.concat(e.impressionURLTemplates),e.extensions=t.extensions.concat(e.extensions),t.viewableImpression.length>0&&(e.viewableImpression=[].concat(h(e.viewableImpression),h(t.viewableImpression))),e.followAdditionalWrappers=t.followAdditionalWrappers,e.allowMultipleAds=t.allowMultipleAds,e.fallbackOnNoAd=t.fallbackOnNoAd;var i=(t.creatives||[]).filter((function(e){return e&&"companion"===e.type})),n=i.reduce((function(e,t){return(t.variations||[]).forEach((function(t){(t.companionClickTrackingURLTemplates||[]).forEach((function(t){L.containsTemplateObject(t,e)||e.push(t)}))})),e}),[]);e.creatives=i.concat(e.creatives);var a=t.videoClickTrackingURLTemplates&&t.videoClickTrackingURLTemplates.length,o=t.videoCustomClickURLTemplates&&t.videoCustomClickURLTemplates.length;if(e.creatives.forEach((function(e){if(t.trackingEvents&&t.trackingEvents[e.type])for(var r in t.trackingEvents[e.type]){var i=t.trackingEvents[e.type][r];Array.isArray(e.trackingEvents[r])||(e.trackingEvents[r]=[]),e.trackingEvents[r]=e.trackingEvents[r].concat(i)}"linear"===e.type&&(a&&(e.videoClickTrackingURLTemplates=e.videoClickTrackingURLTemplates.concat(t.videoClickTrackingURLTemplates)),o&&(e.videoCustomClickURLTemplates=e.videoCustomClickURLTemplates.concat(t.videoCustomClickURLTemplates)),!t.videoClickThroughURLTemplate||null!==e.videoClickThroughURLTemplate&&void 0!==e.videoClickThroughURLTemplate||(e.videoClickThroughURLTemplate=t.videoClickThroughURLTemplate)),"companion"===e.type&&n.length&&(e.variations||[]).forEach((function(e){e.companionClickTrackingURLTemplates=L.joinArrayOfUniqueTemplateObjs(e.companionClickTrackingURLTemplates,n)}))})),t.adVerifications&&(e.adVerifications=e.adVerifications.concat(t.adVerifications)),t.blockedAdCategories&&(e.blockedAdCategories=e.blockedAdCategories.concat(t.blockedAdCategories)),null!==(r=t.creatives)&&void 0!==r&&r.length){var s=t.creatives.filter((function(e){var t;return(null===(t=e.icons)||void 0===t?void 0:t.length)&&!e.mediaFiles.length}));s.length&&(e.creatives=e.creatives.concat(s))}}};function x(e,t){var r=function(){var e=m(arguments.length>0&&void 0!==arguments[0]?arguments[0]:{});return{id:e.id,adId:e.adId,sequence:e.sequence,apiFramework:e.apiFramework,type:"companion",required:null,variations:[]}}(t);return r.required=e.getAttribute("required")||null,r.variations=I.childrenByName(e,"Companion").map((function(e){var t=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return{id:e.id||null,adType:"companionAd",width:e.width||0,height:e.height||0,assetWidth:e.assetWidth||null,assetHeight:e.assetHeight||null,expandedWidth:e.expandedWidth||null,expandedHeight:e.expandedHeight||null,apiFramework:e.apiFramework||null,adSlotId:e.adSlotId||null,pxratio:e.pxratio||"1",renderingMode:e.renderingMode||"default",staticResources:[],htmlResources:[],iframeResources:[],adParameters:null,altText:null,companionClickThroughURLTemplate:null,companionClickTrackingURLTemplates:[],trackingEvents:{}}}(I.parseAttributes(e));t.htmlResources=I.childrenByName(e,"HTMLResource").reduce((function(e,t){var r=I.parseNodeText(t);return r?e.concat(r):e}),[]),t.iframeResources=I.childrenByName(e,"IFrameResource").reduce((function(e,t){var r=I.parseNodeText(t);return r?e.concat(r):e}),[]),t.staticResources=I.childrenByName(e,"StaticResource").reduce((function(e,t){var r=I.parseNodeText(t);return r?e.concat({url:r,creativeType:t.getAttribute("creativeType")||null}):e}),[]),t.altText=I.parseNodeText(I.childByName(e,"AltText"))||null;var r=I.childByName(e,"TrackingEvents");r&&I.childrenByName(r,"Tracking").forEach((function(e){var r=e.getAttribute("event"),i=I.parseNodeText(e);r&&i&&(Array.isArray(t.trackingEvents[r])||(t.trackingEvents[r]=[]),t.trackingEvents[r].push(i))})),t.companionClickTrackingURLTemplates=I.childrenByName(e,"CompanionClickTracking").map((function(e){return{id:e.getAttribute("id")||null,url:I.parseNodeText(e)}})),t.companionClickThroughURLTemplate=I.parseNodeText(I.childByName(e,"CompanionClickThrough"))||null;var i=I.childByName(e,"AdParameters");return i&&(t.adParameters={value:I.parseNodeText(i),xmlEncoded:i.getAttribute("xmlEncoded")||null}),t})),r}function S(e,t){var r,i=function(){var e=m(arguments.length>0&&void 0!==arguments[0]?arguments[0]:{});return{id:e.id,adId:e.adId,sequence:e.sequence,apiFramework:e.apiFramework,type:"linear",duration:0,skipDelay:null,mediaFiles:[],mezzanine:null,interactiveCreativeFile:null,closedCaptionFiles:[],videoClickThroughURLTemplate:null,videoClickTrackingURLTemplates:[],videoCustomClickURLTemplates:[],adParameters:null,icons:[],trackingEvents:{}}}(t);i.duration=I.parseDuration(I.parseNodeText(I.childByName(e,"Duration")));var n=e.getAttribute("skipoffset");if(null==n)i.skipDelay=null;else if("%"===n.charAt(n.length-1)&&-1!==i.duration){var a=parseInt(n,10);i.skipDelay=i.duration*(a/100)}else i.skipDelay=I.parseDuration(n);var o=I.childByName(e,"VideoClicks");if(o){var s=I.childByName(o,"ClickThrough");i.videoClickThroughURLTemplate=s?{id:s.getAttribute("id")||null,url:I.parseNodeText(s)}:null,I.childrenByName(o,"ClickTracking").forEach((function(e){i.videoClickTrackingURLTemplates.push({id:e.getAttribute("id")||null,url:I.parseNodeText(e)})})),I.childrenByName(o,"CustomClick").forEach((function(e){i.videoCustomClickURLTemplates.push({id:e.getAttribute("id")||null,url:I.parseNodeText(e)})}))}var l=I.childByName(e,"AdParameters");l&&(i.adParameters={value:I.parseNodeText(l),xmlEncoded:l.getAttribute("xmlEncoded")||null}),I.childrenByName(e,"TrackingEvents").forEach((function(e){I.childrenByName(e,"Tracking").forEach((function(e){var t=e.getAttribute("event"),n=I.parseNodeText(e);if(t&&n){if("progress"===t){if(!(r=e.getAttribute("offset")))return;t="%"===r.charAt(r.length-1)?"progress-".concat(r):"progress-".concat(Math.round(I.parseDuration(r)))}Array.isArray(i.trackingEvents[t])||(i.trackingEvents[t]=[]),i.trackingEvents[t].push(n)}}))})),I.childrenByName(e,"MediaFiles").forEach((function(e){I.childrenByName(e,"MediaFile").forEach((function(e){i.mediaFiles.push(function(e){var t={id:null,fileURL:null,fileSize:0,deliveryType:"progressive",mimeType:null,mediaType:null,codec:null,bitrate:0,minBitrate:0,maxBitrate:0,width:0,height:0,apiFramework:null,scalable:null,maintainAspectRatio:null};t.id=e.getAttribute("id"),t.fileURL=I.parseNodeText(e),t.deliveryType=e.getAttribute("delivery"),t.codec=e.getAttribute("codec"),t.mimeType=e.getAttribute("type"),t.mediaType=e.getAttribute("mediaType")||"2D",t.apiFramework=e.getAttribute("apiFramework"),t.fileSize=parseInt(e.getAttribute("fileSize")||0),t.bitrate=parseInt(e.getAttribute("bitrate")||0),t.minBitrate=parseInt(e.getAttribute("minBitrate")||0),t.maxBitrate=parseInt(e.getAttribute("maxBitrate")||0),t.width=parseInt(e.getAttribute("width")||0),t.height=parseInt(e.getAttribute("height")||0);var r=e.getAttribute("scalable");r&&"string"==typeof r&&(t.scalable=I.parseBoolean(r));var i=e.getAttribute("maintainAspectRatio");i&&"string"==typeof i&&(t.maintainAspectRatio=I.parseBoolean(i));return t}(e))}));var t=I.childByName(e,"InteractiveCreativeFile");t&&(i.interactiveCreativeFile=function(e){var t=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return{type:e.type||null,apiFramework:e.apiFramework||null,variableDuration:I.parseBoolean(e.variableDuration),fileURL:null}}(I.parseAttributes(e));return t.fileURL=I.parseNodeText(e),t}(t));var r=I.childByName(e,"ClosedCaptionFiles");r&&I.childrenByName(r,"ClosedCaptionFile").forEach((function(e){var t=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return{type:e.type||null,language:e.language||null,fileURL:null}}(I.parseAttributes(e));t.fileURL=I.parseNodeText(e),i.closedCaptionFiles.push(t)}));var n,a,o,s=I.childByName(e,"Mezzanine"),l=(n=s,a={},o=!1,["delivery","type","width","height"].forEach((function(e){n&&n.getAttribute(e)?a[e]=n.getAttribute(e):o=!0})),o?null:a);if(l){var c={id:null,fileURL:null,delivery:null,codec:null,type:null,width:0,height:0,fileSize:0,mediaType:"2D"};c.id=s.getAttribute("id"),c.fileURL=I.parseNodeText(s),c.delivery=l.delivery,c.codec=s.getAttribute("codec"),c.type=l.type,c.width=parseInt(l.width,10),c.height=parseInt(l.height,10),c.fileSize=parseInt(s.getAttribute("fileSize"),10),c.mediaType=s.getAttribute("mediaType")||"2D",i.mezzanine=c}}));var c=I.childByName(e,"Icons");return c&&I.childrenByName(c,"Icon").forEach((function(e){i.icons.push(function(e){var t={program:null,height:0,width:0,xPosition:0,yPosition:0,apiFramework:null,offset:null,duration:0,type:null,staticResource:null,htmlResource:null,iframeResource:null,pxratio:"1",iconClickThroughURLTemplate:null,iconClickTrackingURLTemplates:[],iconViewTrackingURLTemplate:null,iconClickFallbackImages:[]};t.program=e.getAttribute("program"),t.height=parseInt(e.getAttribute("height")||0),t.width=parseInt(e.getAttribute("width")||0),t.xPosition=function(e){if(-1!==["left","right"].indexOf(e))return e;return parseInt(e||0)}(e.getAttribute("xPosition")),t.yPosition=function(e){if(-1!==["top","bottom"].indexOf(e))return e;return parseInt(e||0)}(e.getAttribute("yPosition")),t.apiFramework=e.getAttribute("apiFramework"),t.pxratio=e.getAttribute("pxratio")||"1",t.offset=I.parseDuration(e.getAttribute("offset")),t.duration=I.parseDuration(e.getAttribute("duration")),I.childrenByName(e,"HTMLResource").forEach((function(e){t.type=e.getAttribute("creativeType")||"text/html",t.htmlResource=I.parseNodeText(e)})),I.childrenByName(e,"IFrameResource").forEach((function(e){t.type=e.getAttribute("creativeType")||0,t.iframeResource=I.parseNodeText(e)})),I.childrenByName(e,"StaticResource").forEach((function(e){t.type=e.getAttribute("creativeType")||0,t.staticResource=I.parseNodeText(e)}));var r=I.childByName(e,"IconClicks");if(r){t.iconClickThroughURLTemplate=I.parseNodeText(I.childByName(r,"IconClickThrough")),I.childrenByName(r,"IconClickTracking").forEach((function(e){t.iconClickTrackingURLTemplates.push({id:e.getAttribute("id")||null,url:I.parseNodeText(e)})}));var i=I.childByName(r,"IconClickFallbackImages");i&&I.childrenByName(i,"IconClickFallbackImage").forEach((function(e){t.iconClickFallbackImages.push({url:I.parseNodeText(e)||null,width:e.getAttribute("width")||null,height:e.getAttribute("height")||null})}))}return t.iconViewTrackingURLTemplate=I.parseNodeText(I.childByName(e,"IconViewTracking")),t}(e))})),i}function O(e,t){var r=function(){var e=m(arguments.length>0&&void 0!==arguments[0]?arguments[0]:{});return{id:e.id,adId:e.adId,sequence:e.sequence,apiFramework:e.apiFramework,type:"nonlinear",variations:[],trackingEvents:{}}}(t);return I.childrenByName(e,"TrackingEvents").forEach((function(e){var t,i;I.childrenByName(e,"Tracking").forEach((function(e){t=e.getAttribute("event"),i=I.parseNodeText(e),t&&i&&(Array.isArray(r.trackingEvents[t])||(r.trackingEvents[t]=[]),r.trackingEvents[t].push(i))}))})),I.childrenByName(e,"NonLinear").forEach((function(e){var t={id:null,width:0,height:0,expandedWidth:0,expandedHeight:0,scalable:!0,maintainAspectRatio:!0,minSuggestedDuration:0,apiFramework:"static",adType:"nonLinearAd",type:null,staticResource:null,htmlResource:null,iframeResource:null,nonlinearClickThroughURLTemplate:null,nonlinearClickTrackingURLTemplates:[],adParameters:null};t.id=e.getAttribute("id")||null,t.width=e.getAttribute("width"),t.height=e.getAttribute("height"),t.expandedWidth=e.getAttribute("expandedWidth"),t.expandedHeight=e.getAttribute("expandedHeight"),t.scalable=I.parseBoolean(e.getAttribute("scalable")),t.maintainAspectRatio=I.parseBoolean(e.getAttribute("maintainAspectRatio")),t.minSuggestedDuration=I.parseDuration(e.getAttribute("minSuggestedDuration")),t.apiFramework=e.getAttribute("apiFramework"),I.childrenByName(e,"HTMLResource").forEach((function(e){t.type=e.getAttribute("creativeType")||"text/html",t.htmlResource=I.parseNodeText(e)})),I.childrenByName(e,"IFrameResource").forEach((function(e){t.type=e.getAttribute("creativeType")||0,t.iframeResource=I.parseNodeText(e)})),I.childrenByName(e,"StaticResource").forEach((function(e){t.type=e.getAttribute("creativeType")||0,t.staticResource=I.parseNodeText(e)}));var i=I.childByName(e,"AdParameters");i&&(t.adParameters={value:I.parseNodeText(i),xmlEncoded:i.getAttribute("xmlEncoded")||null}),t.nonlinearClickThroughURLTemplate=I.parseNodeText(I.childByName(e,"NonLinearClickThrough")),I.childrenByName(e,"NonLinearClickTracking").forEach((function(e){t.nonlinearClickTrackingURLTemplates.push({id:e.getAttribute("id")||null,url:I.parseNodeText(e)})})),r.variations.push(t)})),r}function D(e){var t=[];return e.forEach((function(e){var r=V(e);r&&t.push(r)})),t}function V(e){if("#comment"===e.nodeName)return null;var t,r={name:null,value:null,attributes:{},children:[]},i=e.attributes,n=e.childNodes;if(r.name=e.nodeName,e.attributes)for(var a in i)if(i.hasOwnProperty(a)){var o=i[a];o.nodeName&&o.nodeValue&&(r.attributes[o.nodeName]=o.nodeValue)}for(var s in n)if(n.hasOwnProperty(s)){var l=V(n[s]);l&&r.children.push(l)}if(0===r.children.length||1===r.children.length&&["#cdata-section","#text"].indexOf(r.children[0].name)>=0){var c=I.parseNodeText(e);""!==c&&(r.value=c),r.children=[]}return null===(t=r).value&&0===Object.keys(t.attributes).length&&0===t.children.length?null:r}function P(e){var t=[];return e.forEach((function(e){var r,i={id:e.getAttribute("id")||null,adId:B(e),sequence:e.getAttribute("sequence")||null,apiFramework:e.getAttribute("apiFramework")||null},n=[];I.childrenByName(e,"UniversalAdId").forEach((function(e){var t={idRegistry:e.getAttribute("idRegistry")||"unknown",value:I.parseNodeText(e)};n.push(t)}));var a=I.childByName(e,"CreativeExtensions");for(var o in a&&(r=D(I.childrenByName(a,"CreativeExtension"))),e.childNodes){var s=e.childNodes[o],l=void 0;switch(s.nodeName){case"Linear":l=S(s,i);break;case"NonLinearAds":l=O(s,i);break;case"CompanionAds":l=x(s,i)}l&&(n&&(l.universalAdIds=n),r&&(l.creativeExtensions=r),t.push(l))}})),t}function B(e){return e.getAttribute("AdID")||e.getAttribute("adID")||e.getAttribute("adId")||null}var F={Wrapper:{subElements:["VASTAdTagURI","Impression"]},BlockedAdCategories:{attributes:["authority"]},InLine:{subElements:["AdSystem","AdTitle","Impression","AdServingId","Creatives"]},Category:{attributes:["authority"]},Pricing:{attributes:["model","currency"]},Verification:{oneOfinLineResources:["JavaScriptResource","ExecutableResource"],attributes:["vendor"]},UniversalAdId:{attributes:["idRegistry"]},JavaScriptResource:{attributes:["apiFramework","browserOptional"]},ExecutableResource:{attributes:["apiFramework","type"]},Tracking:{attributes:["event"]},Creatives:{subElements:["Creative"]},Creative:{subElements:["UniversalAdId"]},Linear:{subElements:["MediaFiles","Duration"]},MediaFiles:{subElements:["MediaFile"]},MediaFile:{attributes:["delivery","type","width","height"]},Mezzanine:{attributes:["delivery","type","width","height"]},NonLinear:{oneOfinLineResources:["StaticResource","IFrameResource","HTMLResource"],attributes:["width","height"]},Companion:{oneOfinLineResources:["StaticResource","IFrameResource","HTMLResource"],attributes:["width","height"]},StaticResource:{attributes:["creativeType"]},Icons:{subElements:["Icon"]},Icon:{oneOfinLineResources:["StaticResource","IFrameResource","HTMLResource"]}};function M(e,t){if(F[e.nodeName]&&F[e.nodeName].attributes){var r=F[e.nodeName].attributes.filter((function(t){return!e.getAttribute(t)}));r.length>0&&q({name:e.nodeName,parentName:e.parentNode.nodeName,attributes:r},t)}}function j(e,t,r){var i=F[e.nodeName],n=!r&&"Wrapper"!==e.nodeName;if(i&&!n){if(i.subElements){var a=i.subElements.filter((function(t){return!I.childByName(e,t)}));a.length>0&&q({name:e.nodeName,parentName:e.parentNode.nodeName,subElements:a},t)}if(r&&i.oneOfinLineResources)i.oneOfinLineResources.some((function(t){return I.childByName(e,t)}))||q({name:e.nodeName,parentName:e.parentNode.nodeName,oneOfResources:i.oneOfinLineResources},t)}}function W(e){return e.children&&0!==e.children.length}function q(e,t){var r=e.name,i=e.parentName,n=e.attributes,a=e.subElements,o=e.oneOfResources,s="Element '".concat(r,"'");t("VAST-warning",{message:s+=n?" missing required attribute(s) '".concat(n.join(", "),"' "):a?" missing required sub element(s) '".concat(a.join(", "),"' "):o?" must provide one of the following '".concat(o.join(", "),"' "):" is empty",parentElement:i,specVersion:4.1})}var H={verifyRequiredValues:function e(t,r,i){if(t&&t.nodeName)if("InLine"===t.nodeName&&(i=!0),M(t,r),W(t)){j(t,r,i);for(var n=0;n2&&void 0!==arguments[2]?arguments[2]:{},i=r.allowMultipleAds,n=r.followAdditionalWrappers,a=e.childNodes;for(var o in a){var s=a[o];if(-1!==["Wrapper","InLine"].indexOf(s.nodeName)&&("Wrapper"!==s.nodeName||!1!==n)){if(I.copyNodeAttribute("id",e,s),I.copyNodeAttribute("sequence",e,s),I.copyNodeAttribute("adType",e,s),"Wrapper"===s.nodeName)return{ad:Y(s,t),type:"WRAPPER"};if("InLine"===s.nodeName)return{ad:G(s,t,{allowMultipleAds:i}),type:"INLINE"}}}}function G(e,t){return!1===(arguments.length>2&&void 0!==arguments[2]?arguments[2]:{}).allowMultipleAds&&e.getAttribute("sequence")?null:z(e,t)}function z(e,t){var r=[];t&&H.verifyRequiredValues(e,t);var i,n,a=e.childNodes,o=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return{id:e.id||null,sequence:e.sequence||null,adType:e.adType||null,adServingId:null,categories:[],expires:null,viewableImpression:[],system:null,title:null,description:null,advertiser:null,pricing:null,survey:null,errorURLTemplates:[],impressionURLTemplates:[],creatives:[],extensions:[],adVerifications:[],blockedAdCategories:[],followAdditionalWrappers:!0,allowMultipleAds:!1,fallbackOnNoAd:null}}(I.parseAttributes(e));for(var s in a){var l=a[s];switch(l.nodeName){case"Error":o.errorURLTemplates.push(I.parseNodeText(l));break;case"Impression":o.impressionURLTemplates.push({id:l.getAttribute("id")||null,url:I.parseNodeText(l)});break;case"Creatives":o.creatives=P(I.childrenByName(l,"Creative"));break;case"Extensions":var c=I.childrenByName(l,"Extension");o.extensions=D(c),o.adVerifications.length||(r=K(c));break;case"AdVerifications":o.adVerifications=X(I.childrenByName(l,"Verification"));break;case"AdSystem":o.system={value:I.parseNodeText(l),version:l.getAttribute("version")||null};break;case"AdTitle":o.title=I.parseNodeText(l);break;case"AdServingId":o.adServingId=I.parseNodeText(l);break;case"Category":o.categories.push({authority:l.getAttribute("authority")||null,value:I.parseNodeText(l)});break;case"Expires":o.expires=parseInt(I.parseNodeText(l),10);break;case"ViewableImpression":o.viewableImpression.push((n=void 0,n=function(e,t){var r=I.parseNodeText(t);return r&&e.push(r),e},{id:(i=l).getAttribute("id")||null,viewable:I.childrenByName(i,"Viewable").reduce(n,[]),notViewable:I.childrenByName(i,"NotViewable").reduce(n,[]),viewUndetermined:I.childrenByName(i,"ViewUndetermined").reduce(n,[])}));break;case"Description":o.description=I.parseNodeText(l);break;case"Advertiser":o.advertiser={id:l.getAttribute("id")||null,value:I.parseNodeText(l)};break;case"Pricing":o.pricing={value:I.parseNodeText(l),model:l.getAttribute("model")||null,currency:l.getAttribute("currency")||null};break;case"Survey":o.survey={value:I.parseNodeText(l),type:l.getAttribute("type")||null};break;case"BlockedAdCategories":o.blockedAdCategories.push({authority:l.getAttribute("authority")||null,value:I.parseNodeText(l)})}}return r.length&&(o.adVerifications=o.adVerifications.concat(r)),o}function Y(e,t){var r=z(e,t),i=e.getAttribute("followAdditionalWrappers"),n=e.getAttribute("allowMultipleAds"),a=e.getAttribute("fallbackOnNoAd");r.followAdditionalWrappers=!i||I.parseBoolean(i),r.allowMultipleAds=!!n&&I.parseBoolean(n),r.fallbackOnNoAd=a?I.parseBoolean(a):null;var o=I.childByName(e,"VASTAdTagURI");if(o?r.nextWrapperURL=I.parseNodeText(o):(o=I.childByName(e,"VASTAdTagURL"))&&(r.nextWrapperURL=I.parseNodeText(I.childByName(o,"URL"))),r.creatives.forEach((function(e){if(-1!==["linear","nonlinear"].indexOf(e.type)){if(e.trackingEvents){r.trackingEvents||(r.trackingEvents={}),r.trackingEvents[e.type]||(r.trackingEvents[e.type]={});var t=function(t){var i=e.trackingEvents[t];Array.isArray(r.trackingEvents[e.type][t])||(r.trackingEvents[e.type][t]=[]),i.forEach((function(i){r.trackingEvents[e.type][t].push(i)}))};for(var i in e.trackingEvents)t(i)}e.videoClickTrackingURLTemplates&&(Array.isArray(r.videoClickTrackingURLTemplates)||(r.videoClickTrackingURLTemplates=[]),e.videoClickTrackingURLTemplates.forEach((function(e){r.videoClickTrackingURLTemplates.push(e)}))),e.videoClickThroughURLTemplate&&(r.videoClickThroughURLTemplate=e.videoClickThroughURLTemplate),e.videoCustomClickURLTemplates&&(Array.isArray(r.videoCustomClickURLTemplates)||(r.videoCustomClickURLTemplates=[]),e.videoCustomClickURLTemplates.forEach((function(e){r.videoCustomClickURLTemplates.push(e)})))}})),r.nextWrapperURL)return r}function X(e){var t=[];return e.forEach((function(e){var r={resource:null,vendor:null,browserOptional:!1,apiFramework:null,type:null,parameters:null,trackingEvents:{}},i=e.childNodes;for(var n in I.assignAttributes(e.attributes,r),i){var a=i[n];switch(a.nodeName){case"JavaScriptResource":case"ExecutableResource":r.resource=I.parseNodeText(a),I.assignAttributes(a.attributes,r);break;case"VerificationParameters":r.parameters=I.parseNodeText(a)}}var o=I.childByName(e,"TrackingEvents");o&&I.childrenByName(o,"Tracking").forEach((function(e){var t=e.getAttribute("event"),i=I.parseNodeText(e);t&&i&&(Array.isArray(r.trackingEvents[t])||(r.trackingEvents[t]=[]),r.trackingEvents[t].push(i))})),t.push(r)})),t}function K(e){var t=null,r=[];return e.some((function(e){return t=I.childByName(e,"AdVerifications")})),t&&(r=X(I.childrenByName(t,"Verification"))),r}var Q=function(){function e(){n(this,e),this._handlers=[]}return o(e,[{key:"on",value:function(e,t){if("function"!=typeof t)throw new TypeError("The handler argument must be of type Function. Received type ".concat(i(t)));if(!e)throw new TypeError("The event argument must be of type String. Received type ".concat(i(e)));return this._handlers.push({event:e,handler:t}),this}},{key:"once",value:function(e,t){return this.on(e,function(e,t,r){var i={fired:!1,wrapFn:void 0};function n(){i.fired||(e.off(t,i.wrapFn),i.fired=!0,r.bind(e).apply(void 0,arguments))}return i.wrapFn=n,n}(this,e,t))}},{key:"off",value:function(e,t){return this._handlers=this._handlers.filter((function(r){return r.event!==e||r.handler!==t})),this}},{key:"emit",value:function(e){for(var t=arguments.length,r=new Array(t>1?t-1:0),i=1;i2?r-2:0),n=2;n1&&void 0!==arguments[1]?arguments[1]:0,i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null,n=arguments.length>3&&void 0!==arguments[3]?arguments[3]:null;return new Promise((function(a,o){t.URLTemplateFilters.forEach((function(t){e=t(e)}));var s=Date.now();t.emit("VAST-resolving",{url:e,previousUrl:i,wrapperDepth:r,maxWrapperDepth:t.maxWrapperDepth,timeout:t.fetchingOptions.timeout,wrapperAd:n}),t.urlHandler.get(e,t.fetchingOptions,(function(n,l){var c=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},u=Math.round(Date.now()-s),d=Object.assign({url:e,previousUrl:i,wrapperDepth:r,error:n,duration:u},c);t.emit("VAST-resolved",d),ae(c.byteLength,u),n?o(n):a(l)}))}))}},{key:"initParsingStatus",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};this.errorURLTemplates=[],this.fetchingOptions={timeout:e.timeout||J,withCredentials:e.withCredentials},this.maxWrapperDepth=e.wrapperLimit||10,this.parsingOptions={allowMultipleAds:e.allowMultipleAds},this.remainingAds=[],this.rootErrorURLTemplates=[],this.rootURL="",this.urlHandler=e.urlHandler||e.urlhandler||re,this.vastVersion=null,ae(e.byteLength,e.requestDuration)}},{key:"getRemainingAds",value:function(e){var t=this;if(0===this.remainingAds.length)return Promise.reject(new Error("No more ads are available for the given VAST"));var r=e?L.flatten(this.remainingAds):this.remainingAds.shift();return this.errorURLTemplates=[],this.resolveAds(r,{wrapperDepth:0,url:this.rootURL}).then((function(e){return t.buildVASTResponse(e)}))}},{key:"getAndParseVAST",value:function(e){var t=this,r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return this.initParsingStatus(r),this.URLTemplateFilters.forEach((function(t){e=t(e)})),this.rootURL=e,this.fetchVAST(e).then((function(i){return r.previousUrl=e,r.isRootVAST=!0,r.url=e,t.parse(i,r).then((function(e){return t.buildVASTResponse(e)}))}))}},{key:"parseVAST",value:function(e){var t=this,r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return this.initParsingStatus(r),r.isRootVAST=!0,this.parse(e,r).then((function(e){return t.buildVASTResponse(e)}))}},{key:"buildVASTResponse",value:function(e){var t,r={ads:(t={ads:e,errorURLTemplates:this.getErrorURLTemplates(),version:this.vastVersion}).ads||[],errorURLTemplates:t.errorURLTemplates||[],version:t.version||null};return this.completeWrapperResolving(r),r}},{key:"parseVastXml",value:function(e,t){var r=t.isRootVAST,i=void 0!==r&&r,n=t.url,a=void 0===n?null:n,o=t.wrapperDepth,s=void 0===o?0:o,l=t.allowMultipleAds,c=t.followAdditionalWrappers;if(!e||!e.documentElement||"VAST"!==e.documentElement.nodeName)throw this.emit("VAST-ad-parsed",{type:"ERROR",url:a,wrapperDepth:s}),new Error("Invalid VAST XMLDocument");var u=[],d=e.documentElement.childNodes,p=e.documentElement.getAttribute("version");for(var h in i&&p&&(this.vastVersion=p),d){var v=d[h];if("Error"===v.nodeName){var f=I.parseNodeText(v);i?this.rootErrorURLTemplates.push(f):this.errorURLTemplates.push(f)}else if("Ad"===v.nodeName){if(this.vastVersion&&parseFloat(this.vastVersion)<3)l=!0;else if(!1===l&&u.length>1)break;var m=_(v,this.emit.bind(this),{allowMultipleAds:l,followAdditionalWrappers:c});m.ad?(u.push(m.ad),this.emit("VAST-ad-parsed",{type:m.type,url:a,wrapperDepth:s,adIndex:u.length-1,vastVersion:p})):this.trackVastError(this.getErrorURLTemplates(),{ERRORCODE:101})}}return u}},{key:"parse",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},r=t.url,i=void 0===r?null:r,n=t.resolveAll,a=void 0===n||n,o=t.wrapperSequence,s=void 0===o?null:o,l=t.previousUrl,c=void 0===l?null:l,u=t.wrapperDepth,d=void 0===u?0:u,p=t.isRootVAST,h=void 0!==p&&p,v=t.followAdditionalWrappers,f=t.allowMultipleAds,m=[];this.vastVersion&&parseFloat(this.vastVersion)<3&&h&&(f=!0);try{m=this.parseVastXml(e,{isRootVAST:h,url:i,wrapperDepth:d,allowMultipleAds:f,followAdditionalWrappers:v})}catch(e){return Promise.reject(e)}return 1===m.length&&null!=s&&(m[0].sequence=s),!1===a&&(this.remainingAds=I.splitVAST(m),m=this.remainingAds.shift()),this.resolveAds(m,{wrapperDepth:d,previousUrl:c,url:i})}},{key:"resolveAds",value:function(){var e=this,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[],r=arguments.length>1?arguments[1]:void 0,i=r.wrapperDepth,n=r.previousUrl,a=r.url,o=[];return n=a,t.forEach((function(t){var r=e.resolveWrappers(t,i,n);o.push(r)})),Promise.all(o).then((function(t){var r=L.flatten(t);if(!r&&e.remainingAds.length>0){var o=e.remainingAds.shift();return e.resolveAds(o,{wrapperDepth:i,previousUrl:n,url:a})}return r}))}},{key:"resolveWrappers",value:function(e,t,r){var i=this;return new Promise((function(n){var a;if(t++,!e.nextWrapperURL)return delete e.nextWrapperURL,n(e);if(t>=i.maxWrapperDepth)return e.errorCode=302,delete e.nextWrapperURL,n(e);e.nextWrapperURL=I.resolveVastAdTagURI(e.nextWrapperURL,r),i.URLTemplateFilters.forEach((function(t){e.nextWrapperURL=t(e.nextWrapperURL)}));var o=null!==(a=i.parsingOptions.allowMultipleAds)&&void 0!==a?a:e.allowMultipleAds,s=e.sequence;i.fetchVAST(e.nextWrapperURL,t,r,e).then((function(a){return i.parse(a,{url:e.nextWrapperURL,previousUrl:r,wrapperSequence:s,wrapperDepth:t,followAdditionalWrappers:e.followAdditionalWrappers,allowMultipleAds:o}).then((function(t){if(delete e.nextWrapperURL,0===t.length)return e.creatives=[],n(e);t.forEach((function(t){t&&I.mergeWrapperAdData(t,e)})),n(t)}))})).catch((function(t){e.errorCode=301,e.errorMessage=t.message,n(e)}))}))}},{key:"completeWrapperResolving",value:function(e){if(0===e.ads.length)this.trackVastError(e.errorURLTemplates,{ERRORCODE:303});else for(var t=e.ads.length-1;t>=0;t--){var r=e.ads[t];(r.errorCode||0===r.creatives.length)&&(this.trackVastError(r.errorURLTemplates.concat(e.errorURLTemplates),{ERRORCODE:r.errorCode||303},{ERRORMESSAGE:r.errorMessage||""},{extensions:r.extensions},{system:r.system}),e.ads.splice(t,1))}}}]),r}(Q),le=null,ce={data:{},length:0,getItem:function(e){return this.data[e]},setItem:function(e,t){this.data[e]=t,this.length=Object.keys(this.data).length},removeItem:function(e){delete this.data[e],this.length=Object.keys(this.data).length},clear:function(){this.data={},this.length=0}},ue=function(){function e(){n(this,e),this.storage=this.initStorage()}return o(e,[{key:"initStorage",value:function(){if(le)return le;try{le="undefined"!=typeof window&&null!==window?window.localStorage||window.sessionStorage:null}catch(e){le=null}return le&&!this.isStorageDisabled(le)||(le=ce).clear(),le}},{key:"isStorageDisabled",value:function(e){var t="__VASTStorage__";try{if(e.setItem(t,t),e.getItem(t)!==t)return e.removeItem(t),!0}catch(e){return!0}return e.removeItem(t),!1}},{key:"getItem",value:function(e){return this.storage.getItem(e)}},{key:"setItem",value:function(e,t){return this.storage.setItem(e,t)}},{key:"removeItem",value:function(e){return this.storage.removeItem(e)}},{key:"clear",value:function(){return this.storage.clear()}}]),e}(),de=function(){function e(t,r,i){n(this,e),this.cappingFreeLunch=t||0,this.cappingMinimumTimeInterval=r||0,this.defaultOptions={withCredentials:!1,timeout:0},this.vastParser=new se,this.storage=i||new ue,void 0===this.lastSuccessfulAd&&(this.lastSuccessfulAd=0),void 0===this.totalCalls&&(this.totalCalls=0),void 0===this.totalCallsTimeout&&(this.totalCallsTimeout=0)}return o(e,[{key:"getParser",value:function(){return this.vastParser}},{key:"lastSuccessfulAd",get:function(){return this.storage.getItem("vast-client-last-successful-ad")},set:function(e){this.storage.setItem("vast-client-last-successful-ad",e)}},{key:"totalCalls",get:function(){return this.storage.getItem("vast-client-total-calls")},set:function(e){this.storage.setItem("vast-client-total-calls",e)}},{key:"totalCallsTimeout",get:function(){return this.storage.getItem("vast-client-total-calls-timeout")},set:function(e){this.storage.setItem("vast-client-total-calls-timeout",e)}},{key:"hasRemainingAds",value:function(){return this.vastParser.remainingAds.length>0}},{key:"getNextAds",value:function(e){return this.vastParser.getRemainingAds(e)}},{key:"get",value:function(e){var t=this,r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},i=Date.now();return(r=Object.assign({},this.defaultOptions,r)).hasOwnProperty("resolveAll")||(r.resolveAll=!1),this.totalCallsTimeout=t.totalCalls)return a(new Error("VAST call canceled – FreeLunch capping not reached yet ".concat(t.totalCalls,"/").concat(t.cappingFreeLunch)));var o=i-t.lastSuccessfulAd;if(o<0)t.lastSuccessfulAd=0;else if(o3&&void 0!==arguments[3]?arguments[3]:null,l=arguments.length>4&&void 0!==arguments[4]&&arguments[4];for(var c in n(this,a),(o=t.call(this)).ad=r,o.creative=i,o.variation=s,o.muted=l,o.impressed=!1,o.skippable=!1,o.trackingEvents={},o.lastPercentage=0,o._alreadyTriggeredQuartiles={},o.emitAlwaysEvents=["creativeView","start","firstQuartile","midpoint","thirdQuartile","complete","resume","pause","rewind","skip","closeLinear","close"],o.creative.trackingEvents){var u=o.creative.trackingEvents[c];o.trackingEvents[c]=u.slice(0)}return!function(e){return"linear"===e.type}(o.creative)?o._initVariationTracking():o._initLinearTracking(),e&&o.on("start",(function(){e.lastSuccessfulAd=Date.now()})),o}return o(a,[{key:"_initLinearTracking",value:function(){this.linear=!0,this.skipDelay=this.creative.skipDelay,this.setDuration(this.creative.duration),this.clickThroughURLTemplate=this.creative.videoClickThroughURLTemplate,this.clickTrackingURLTemplates=this.creative.videoClickTrackingURLTemplates}},{key:"_initVariationTracking",value:function(){if(this.linear=!1,this.skipDelay=-1,this.variation){for(var e in this.variation.trackingEvents){var t=this.variation.trackingEvents[e];this.trackingEvents[e]?this.trackingEvents[e]=this.trackingEvents[e].concat(t.slice(0)):this.trackingEvents[e]=t.slice(0)}"nonLinearAd"===this.variation.adType?(this.clickThroughURLTemplate=this.variation.nonlinearClickThroughURLTemplate,this.clickTrackingURLTemplates=this.variation.nonlinearClickTrackingURLTemplates,this.setDuration(this.variation.minSuggestedDuration)):function(e){return"companionAd"===e.adType}(this.variation)&&(this.clickThroughURLTemplate=this.variation.companionClickThroughURLTemplate,this.clickTrackingURLTemplates=this.variation.companionClickTrackingURLTemplates)}}},{key:"setDuration",value:function(e){L.isValidTimeValue(e)&&(this.assetDuration=e,this.quartiles={firstQuartile:Math.round(25*this.assetDuration)/100,midpoint:Math.round(50*this.assetDuration)/100,thirdQuartile:Math.round(75*this.assetDuration)/100})}},{key:"setProgress",value:function(e){var t=this,r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if(L.isValidTimeValue(e)&&"object"===i(r)){var n=this.skipDelay||-1;if(-1===n||this.skippable||(n>e?this.emit("skip-countdown",n-e):(this.skippable=!0,this.emit("skip-countdown",0))),this.assetDuration>0){var a=Math.round(e/this.assetDuration*100),o=[];if(e>0){o.push("start");for(var s=this.lastPercentage;s1&&void 0!==arguments[1]?arguments[1]:{};"boolean"==typeof e&&"object"===i(t)&&(this.muted!==e&&this.track(e?"mute":"unmute",{macros:t}),this.muted=e)}},{key:"setPaused",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};"boolean"==typeof e&&"object"===i(t)&&(this.paused!==e&&this.track(e?"pause":"resume",{macros:t}),this.paused=e)}},{key:"setFullscreen",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};"boolean"==typeof e&&"object"===i(t)&&(this.fullscreen!==e&&this.track(e?"fullscreen":"exitFullscreen",{macros:t}),this.fullscreen=e)}},{key:"setExpand",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};"boolean"==typeof e&&"object"===i(t)&&(this.expanded!==e&&(this.track(e?"expand":"collapse",{macros:t}),this.track(e?"playerExpand":"playerCollapse",{macros:t})),this.expanded=e)}},{key:"setSkipDelay",value:function(e){L.isValidTimeValue(e)&&(this.skipDelay=e)}},{key:"trackImpression",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};"object"===i(e)&&(this.impressed||(this.impressed=!0,this.trackURLs(this.ad.impressionURLTemplates,e),this.track("creativeView",{macros:e})))}},{key:"trackViewableImpression",value:function(){var e=this,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};"object"===i(t)&&this.ad.viewableImpression.forEach((function(r){e.trackURLs(r.viewable,t)}))}},{key:"trackNotViewableImpression",value:function(){var e=this,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};"object"===i(t)&&this.ad.viewableImpression.forEach((function(r){e.trackURLs(r.notViewable,t)}))}},{key:"trackUndeterminedImpression",value:function(){var e=this,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};"object"===i(t)&&this.ad.viewableImpression.forEach((function(r){e.trackURLs(r.viewUndetermined,t)}))}},{key:"error",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];"object"===i(e)&&"boolean"==typeof t&&this.trackURLs(this.ad.errorURLTemplates,e,{isCustomCode:t})}},{key:"errorWithCode",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];"string"==typeof e&&"boolean"==typeof t&&(this.error({ERRORCODE:e},t),console.log("The method errorWithCode is deprecated, please use vast tracker error method instead"))}},{key:"complete",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};"object"===i(e)&&this.track("complete",{macros:e})}},{key:"notUsed",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};"object"===i(e)&&(this.track("notUsed",{macros:e}),this.trackingEvents=[])}},{key:"otherAdInteraction",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};"object"===i(e)&&this.track("otherAdInteraction",{macros:e})}},{key:"acceptInvitation",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};"object"===i(e)&&this.track("acceptInvitation",{macros:e})}},{key:"adExpand",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};"object"===i(e)&&this.track("adExpand",{macros:e})}},{key:"adCollapse",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};"object"===i(e)&&this.track("adCollapse",{macros:e})}},{key:"minimize",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};"object"===i(e)&&this.track("minimize",{macros:e})}},{key:"verificationNotExecuted",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if("string"==typeof e&&"object"===i(t)){if(!this.ad||!this.ad.adVerifications||!this.ad.adVerifications.length)throw new Error("No adVerifications provided");if(!e)throw new Error("No vendor provided, unable to find associated verificationNotExecuted");var r=this.ad.adVerifications.find((function(t){return t.vendor===e}));if(!r)throw new Error("No associated verification element found for vendor: ".concat(e));var n=r.trackingEvents;if(n&&n.verificationNotExecuted){var a=n.verificationNotExecuted;this.trackURLs(a,t),this.emit("verificationNotExecuted",{trackingURLTemplates:a})}}}},{key:"overlayViewDuration",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};"string"==typeof e&&"object"===i(t)&&(t.ADPLAYHEAD=e,this.track("overlayViewDuration",{macros:t}))}},{key:"close",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};"object"===i(e)&&this.track(this.linear?"closeLinear":"close",{macros:e})}},{key:"skip",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};"object"===i(e)&&this.track("skip",{macros:e})}},{key:"load",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};"object"===i(e)&&this.track("loaded",{macros:e})}},{key:"click",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null,t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if((null===e||"string"==typeof e)&&"object"===i(t)){this.clickTrackingURLTemplates&&this.clickTrackingURLTemplates.length&&this.trackURLs(this.clickTrackingURLTemplates,t);var n=this.clickThroughURLTemplate||e,a=r({},t);if(n){this.progress&&(a.ADPLAYHEAD=this.progressFormatted());var o=L.resolveURLTemplates([n],a)[0];this.emit("clickthrough",o)}}}},{key:"track",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},r=t.macros,n=void 0===r?{}:r,a=t.once,o=void 0!==a&&a;if("object"===i(n)){"closeLinear"===e&&!this.trackingEvents[e]&&this.trackingEvents.close&&(e="close");var s=this.trackingEvents[e],l=this.emitAlwaysEvents.indexOf(e)>-1;s?(this.emit(e,{trackingURLTemplates:s}),this.trackURLs(s,n)):l&&this.emit(e,null),o&&(delete this.trackingEvents[e],l&&this.emitAlwaysEvents.splice(this.emitAlwaysEvents.indexOf(e),1))}}},{key:"trackURLs",value:function(e){var t,i=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},a=L.filterValidUrlTemplates(e),o=r({},i);this.linear&&(this.creative&&this.creative.mediaFiles&&this.creative.mediaFiles[0]&&this.creative.mediaFiles[0].fileURL&&(o.ASSETURI=this.creative.mediaFiles[0].fileURL),this.progress&&(o.ADPLAYHEAD=this.progressFormatted())),null!==(t=this.creative)&&void 0!==t&&null!==(t=t.universalAdIds)&&void 0!==t&&t.length&&(o.UNIVERSALADID=this.creative.universalAdIds.map((function(e){return e.idRegistry.concat(" ",e.value)})).join(",")),this.ad&&(this.ad.sequence&&(o.PODSEQUENCE=this.ad.sequence),this.ad.adType&&(o.ADTYPE=this.ad.adType),this.ad.adServingId&&(o.ADSERVINGID=this.ad.adServingId),this.ad.categories&&this.ad.categories.length&&(o.ADCATEGORIES=this.ad.categories.map((function(e){return e.value})).join(",")),this.ad.blockedAdCategories&&this.ad.blockedAdCategories.length&&(o.BLOCKEDADCATEGORIES=this.ad.blockedAdCategories)),L.track(a,o,n)}},{key:"convertToTimecode",value:function(e){if(!L.isValidTimeValue(e))return"";var t=1e3*e,r=Math.floor(t/36e5),i=Math.floor(t/6e4%60),n=Math.floor(t/1e3%60),a=Math.floor(t%1e3);return"".concat(L.addLeadingZeros(r,2),":").concat(L.addLeadingZeros(i,2),":").concat(L.addLeadingZeros(n,2),".").concat(L.addLeadingZeros(a,3))}},{key:"progressFormatted",value:function(){return this.convertToTimecode(this.progress)}}]),a}(Q);e.VASTClient=de,e.VASTParser=se,e.VASTTracker=pe,e.parseDuration=C,Object.defineProperty(e,"__esModule",{value:!0})})); +function e(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return{id:e.id||null,adId:e.adId||null,sequence:e.sequence||null,apiFramework:e.apiFramework||null,universalAdIds:[],creativeExtensions:[]}}const t=["ADCATEGORIES","ADCOUNT","ADPLAYHEAD","ADSERVINGID","ADTYPE","APIFRAMEWORKS","APPBUNDLE","ASSETURI","BLOCKEDADCATEGORIES","BREAKMAXADLENGTH","BREAKMAXADS","BREAKMAXDURATION","BREAKMINADLENGTH","BREAKMINDURATION","BREAKPOSITION","CLICKPOS","CLICKTYPE","CLIENTUA","CONTENTID","CONTENTPLAYHEAD","CONTENTURI","DEVICEIP","DEVICEUA","DOMAIN","EXTENSIONS","GDPRCONSENT","IFA","IFATYPE","INVENTORYSTATE","LATLONG","LIMITADTRACKING","MEDIAMIME","MEDIAPLAYHEAD","OMIDPARTNER","PAGEURL","PLACEMENTTYPE","PLAYERCAPABILITIES","PLAYERSIZE","PLAYERSTATE","PODSEQUENCE","REGULATIONS","SERVERSIDE","SERVERUA","TRANSACTIONID","UNIVERSALADID","VASTVERSIONS","VERIFICATIONVENDORS"];function r(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};const a=[],n=s(e);!t.ERRORCODE||r.isCustomCode||/^[0-9]{3}$/.test(t.ERRORCODE)||(t.ERRORCODE=900),t.CACHEBUSTING=u(Math.round(1e8*Math.random())),t.TIMESTAMP=(new Date).toISOString(),t.RANDOM=t.random=t.CACHEBUSTING;for(const e in t)t[e]=c(t[e]);for(const e in n){const r=n[e];"string"==typeof r&&a.push(i(r,t))}return a}function i(e,r){const i=(e=a(e,r)).match(/[^[\]]+(?=])/g);if(!i)return e;let s=i.filter((e=>t.indexOf(e)>-1));return 0===s.length?e:(s=s.reduce(((e,t)=>(e[t]=-1,e)),{}),a(e,s))}function a(e,t){let r=e;for(const e in t){const i=t[e];r=r.replace(new RegExp("(?:\\[|%%)(".concat(e,")(?:\\]|%%)"),"g"),i)}return r}function s(e){return Array.isArray(e)?e.map((e=>e&&e.hasOwnProperty("url")?e.url:e)):e}function n(e){return/^(https?:\/\/|\/\/)/.test(e)}function o(e,t){for(let r=0;r"%".concat(e.charCodeAt(0).toString(16))))}function u(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:8;return e.toString().padStart(t,"0")}const d={track:function(e,t,i){r(e,t,i).forEach((e=>{if("undefined"!=typeof window&&null!==window){(new Image).src=e}}))},resolveURLTemplates:r,extractURLsFromTemplates:s,filterUrlTemplates:function(e){return e.reduce(((e,t)=>{const r=t.url||t;return n(r)?e.validUrls.push(r):e.invalidUrls.push(r),e}),{validUrls:[],invalidUrls:[]})},containsTemplateObject:o,isTemplateObjectEqual:l,encodeURIComponentRFC3986:c,replaceUrlMacros:i,isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},flatten:function e(t){return t.reduce(((t,r)=>t.concat(Array.isArray(r)?e(r):r)),[])},joinArrayOfUniqueTemplateObjs:function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[],t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:[];const r=Array.isArray(e)?e:[],i=Array.isArray(t)?t:[];return r.concat(i).reduce(((e,t)=>(o(t,e)||e.push(t),e)),[])},isValidTimeValue:function(e){return Number.isFinite(e)&&e>=-2},addLeadingZeros:u,isValidUrl:n,isBrowserEnvironment:function(){return"undefined"!=typeof window},formatMacrosValues:function(e){return"object"!=typeof e?e:JSON.stringify(e)}};function h(e){return["true","TRUE","True","1"].includes(e)}function p(e){if(null==e)return-1;if(d.isNumeric(e))return parseInt(e);const t=e.split(":");if(3!==t.length)return-1;const r=t[2].split(".");let i=parseInt(r[0]);2===r.length&&(i+=parseFloat("0.".concat(r[1])));const a=parseInt(60*t[1]),s=parseInt(60*t[0]*60);return isNaN(s)||isNaN(a)||isNaN(i)||a>3600||i>60?-1:s+a+i}const m={childByName:function(e,t){return Array.from(e.childNodes).find((e=>e.nodeName===t))},childrenByName:function(e,t){return Array.from(e.childNodes).filter((e=>e.nodeName===t))},resolveVastAdTagURI:function(e,t){if(!t)return e;if(e.startsWith("//")){const{protocol:t}=location;return"".concat(t).concat(e)}if(!e.includes("://")){const r=t.slice(0,t.lastIndexOf("/"));return"".concat(r,"/").concat(e)}return e},parseBoolean:h,parseNodeText:function(e){return e&&(e.textContent||e.text||"").trim()},copyNodeAttribute:function(e,t,r){const i=t.getAttribute(e);i&&r.setAttribute(e,i)},parseAttributes:function(e){return Array.from(e.attributes).reduce(((e,t)=>(e[t.nodeName]=t.nodeValue,e)),{})},parseDuration:p,splitVAST:function(e){const t=[];let r=null;return e.forEach(((i,a)=>{if(i.sequence&&(i.sequence=parseInt(i.sequence,10)),i.sequence>1){const t=e[a-1];if(t&&t.sequence===i.sequence-1)return void(r&&r.push(i));delete i.sequence}r=[i],t.push(r)})),t},assignAttributes:function(e,t){e&&Array.from(e).forEach((e=>{let{nodeName:r,nodeValue:i}=e;if(r&&i&&t.hasOwnProperty(r)){let e=i;"boolean"==typeof t[r]&&(e=h(e)),t[r]=e}}))},mergeWrapperAdData:function(e,t){var r;e.errorURLTemplates=t.errorURLTemplates.concat(e.errorURLTemplates),e.impressionURLTemplates=t.impressionURLTemplates.concat(e.impressionURLTemplates),e.extensions=t.extensions.concat(e.extensions),t.viewableImpression.length>0&&(e.viewableImpression=[...e.viewableImpression,...t.viewableImpression]),e.followAdditionalWrappers=t.followAdditionalWrappers,e.allowMultipleAds=t.allowMultipleAds,e.fallbackOnNoAd=t.fallbackOnNoAd;const i=(t.creatives||[]).filter((e=>e&&"companion"===e.type)),a=i.reduce(((e,t)=>((t.variations||[]).forEach((t=>{(t.companionClickTrackingURLTemplates||[]).forEach((t=>{d.containsTemplateObject(t,e)||e.push(t)}))})),e)),[]);e.creatives=i.concat(e.creatives);const s=t.videoClickTrackingURLTemplates&&t.videoClickTrackingURLTemplates.length,n=t.videoCustomClickURLTemplates&&t.videoCustomClickURLTemplates.length;if(e.creatives.forEach((e=>{if(t.trackingEvents&&t.trackingEvents[e.type])for(const r in t.trackingEvents[e.type]){const i=t.trackingEvents[e.type][r];Array.isArray(e.trackingEvents[r])||(e.trackingEvents[r]=[]),e.trackingEvents[r]=e.trackingEvents[r].concat(i)}"linear"===e.type&&(s&&(e.videoClickTrackingURLTemplates=e.videoClickTrackingURLTemplates.concat(t.videoClickTrackingURLTemplates)),n&&(e.videoCustomClickURLTemplates=e.videoCustomClickURLTemplates.concat(t.videoCustomClickURLTemplates)),!t.videoClickThroughURLTemplate||null!==e.videoClickThroughURLTemplate&&void 0!==e.videoClickThroughURLTemplate||(e.videoClickThroughURLTemplate=t.videoClickThroughURLTemplate)),"companion"===e.type&&a.length&&(e.variations||[]).forEach((e=>{e.companionClickTrackingURLTemplates=d.joinArrayOfUniqueTemplateObjs(e.companionClickTrackingURLTemplates,a)}))})),t.adVerifications&&(e.adVerifications=e.adVerifications.concat(t.adVerifications)),t.blockedAdCategories&&(e.blockedAdCategories=e.blockedAdCategories.concat(t.blockedAdCategories)),null!==(r=t.creatives)&&void 0!==r&&r.length){const r=t.creatives.filter((e=>{var t;return(null===(t=e.icons)||void 0===t?void 0:t.length)&&!e.mediaFiles.length}));r.length&&(e.creatives=e.creatives.concat(r))}}};function g(t,r){const i=function(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};const{id:r,adId:i,sequence:a,apiFramework:s}=e(t);return{id:r,adId:i,sequence:a,apiFramework:s,type:"companion",required:null,variations:[]}}(r);return i.required=t.getAttribute("required")||null,i.variations=m.childrenByName(t,"Companion").map((e=>{const t=function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return{id:e.id||null,adType:"companionAd",width:e.width||0,height:e.height||0,assetWidth:e.assetWidth||null,assetHeight:e.assetHeight||null,expandedWidth:e.expandedWidth||null,expandedHeight:e.expandedHeight||null,apiFramework:e.apiFramework||null,adSlotId:e.adSlotId||null,pxratio:e.pxratio||"1",renderingMode:e.renderingMode||"default",staticResources:[],htmlResources:[],iframeResources:[],adParameters:null,altText:null,companionClickThroughURLTemplate:null,companionClickTrackingURLTemplates:[],trackingEvents:{}}}(m.parseAttributes(e));t.htmlResources=m.childrenByName(e,"HTMLResource").reduce(((e,t)=>{const r=m.parseNodeText(t);return r?e.concat(r):e}),[]),t.iframeResources=m.childrenByName(e,"IFrameResource").reduce(((e,t)=>{const r=m.parseNodeText(t);return r?e.concat(r):e}),[]),t.staticResources=m.childrenByName(e,"StaticResource").reduce(((e,t)=>{const r=m.parseNodeText(t);return r?e.concat({url:r,creativeType:t.getAttribute("creativeType")||null}):e}),[]),t.altText=m.parseNodeText(m.childByName(e,"AltText"))||null;const r=m.childByName(e,"TrackingEvents");r&&m.childrenByName(r,"Tracking").forEach((e=>{const r=e.getAttribute("event"),i=m.parseNodeText(e);r&&i&&(Array.isArray(t.trackingEvents[r])||(t.trackingEvents[r]=[]),t.trackingEvents[r].push(i))})),t.companionClickTrackingURLTemplates=m.childrenByName(e,"CompanionClickTracking").map((e=>({id:e.getAttribute("id")||null,url:m.parseNodeText(e)}))),t.companionClickThroughURLTemplate=m.parseNodeText(m.childByName(e,"CompanionClickThrough"))||null;const i=m.childByName(e,"AdParameters");return i&&(t.adParameters={value:m.parseNodeText(i),xmlEncoded:i.getAttribute("xmlEncoded")||null}),t})),i}function v(t,r){let i;const a=function(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};const{id:r,adId:i,sequence:a,apiFramework:s}=e(t);return{id:r,adId:i,sequence:a,apiFramework:s,type:"linear",duration:0,skipDelay:null,mediaFiles:[],mezzanine:null,interactiveCreativeFile:null,closedCaptionFiles:[],videoClickThroughURLTemplate:null,videoClickTrackingURLTemplates:[],videoCustomClickURLTemplates:[],adParameters:null,icons:[],trackingEvents:{}}}(r);a.duration=m.parseDuration(m.parseNodeText(m.childByName(t,"Duration")));const s=t.getAttribute("skipoffset");if(null==s)a.skipDelay=null;else if("%"===s.charAt(s.length-1)&&-1!==a.duration){const e=parseInt(s,10);a.skipDelay=a.duration*(e/100)}else a.skipDelay=m.parseDuration(s);const n=m.childByName(t,"VideoClicks");if(n){const e=m.childByName(n,"ClickThrough");a.videoClickThroughURLTemplate=e?{id:e.getAttribute("id")||null,url:m.parseNodeText(e)}:null,m.childrenByName(n,"ClickTracking").forEach((e=>{a.videoClickTrackingURLTemplates.push({id:e.getAttribute("id")||null,url:m.parseNodeText(e)})})),m.childrenByName(n,"CustomClick").forEach((e=>{a.videoCustomClickURLTemplates.push({id:e.getAttribute("id")||null,url:m.parseNodeText(e)})}))}const o=m.childByName(t,"AdParameters");o&&(a.adParameters={value:m.parseNodeText(o),xmlEncoded:o.getAttribute("xmlEncoded")||null}),m.childrenByName(t,"TrackingEvents").forEach((e=>{m.childrenByName(e,"Tracking").forEach((e=>{let t=e.getAttribute("event");const r=m.parseNodeText(e);if(t&&r){if("progress"===t){if(i=e.getAttribute("offset"),!i)return;t="%"===i.charAt(i.length-1)?"progress-".concat(i):"progress-".concat(Math.round(m.parseDuration(i)))}Array.isArray(a.trackingEvents[t])||(a.trackingEvents[t]=[]),a.trackingEvents[t].push(r)}}))})),m.childrenByName(t,"MediaFiles").forEach((e=>{m.childrenByName(e,"MediaFile").forEach((e=>{a.mediaFiles.push(function(e){const t={id:null,fileURL:null,fileSize:0,deliveryType:"progressive",mimeType:null,mediaType:null,codec:null,bitrate:0,minBitrate:0,maxBitrate:0,width:0,height:0,apiFramework:null,scalable:null,maintainAspectRatio:null};t.id=e.getAttribute("id"),t.fileURL=m.parseNodeText(e),t.deliveryType=e.getAttribute("delivery"),t.codec=e.getAttribute("codec"),t.mimeType=e.getAttribute("type"),t.mediaType=e.getAttribute("mediaType")||"2D",t.apiFramework=e.getAttribute("apiFramework"),t.fileSize=parseInt(e.getAttribute("fileSize")||0),t.bitrate=parseInt(e.getAttribute("bitrate")||0),t.minBitrate=parseInt(e.getAttribute("minBitrate")||0),t.maxBitrate=parseInt(e.getAttribute("maxBitrate")||0),t.width=parseInt(e.getAttribute("width")||0),t.height=parseInt(e.getAttribute("height")||0);const r=e.getAttribute("scalable");r&&"string"==typeof r&&(t.scalable=m.parseBoolean(r));const i=e.getAttribute("maintainAspectRatio");i&&"string"==typeof i&&(t.maintainAspectRatio=m.parseBoolean(i));return t}(e))}));const t=m.childByName(e,"InteractiveCreativeFile");t&&(a.interactiveCreativeFile=function(e){const t=function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return{type:e.type||null,apiFramework:e.apiFramework||null,variableDuration:m.parseBoolean(e.variableDuration),fileURL:null}}(m.parseAttributes(e));return t.fileURL=m.parseNodeText(e),t}(t));const r=m.childByName(e,"ClosedCaptionFiles");r&&m.childrenByName(r,"ClosedCaptionFile").forEach((e=>{const t=function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return{type:e.type||null,language:e.language||null,fileURL:null}}(m.parseAttributes(e));t.fileURL=m.parseNodeText(e),a.closedCaptionFiles.push(t)}));const i=m.childByName(e,"Mezzanine"),s=function(e,t){const r={};let i=!1;return t.forEach((t=>{e&&e.getAttribute(t)?r[t]=e.getAttribute(t):i=!0})),i?null:r}(i,["delivery","type","width","height"]);if(s){const e={id:null,fileURL:null,delivery:null,codec:null,type:null,width:0,height:0,fileSize:0,mediaType:"2D"};e.id=i.getAttribute("id"),e.fileURL=m.parseNodeText(i),e.delivery=s.delivery,e.codec=i.getAttribute("codec"),e.type=s.type,e.width=parseInt(s.width,10),e.height=parseInt(s.height,10),e.fileSize=parseInt(i.getAttribute("fileSize"),10),e.mediaType=i.getAttribute("mediaType")||"2D",a.mezzanine=e}}));const l=m.childByName(t,"Icons");return l&&m.childrenByName(l,"Icon").forEach((e=>{a.icons.push(function(e){const t={program:null,height:0,width:0,xPosition:0,yPosition:0,apiFramework:null,offset:null,duration:0,type:null,staticResource:null,htmlResource:null,iframeResource:null,pxratio:"1",iconClickThroughURLTemplate:null,iconClickTrackingURLTemplates:[],iconViewTrackingURLTemplate:null,iconClickFallbackImages:[]};t.program=e.getAttribute("program"),t.height=parseInt(e.getAttribute("height")||0),t.width=parseInt(e.getAttribute("width")||0),t.xPosition=function(e){if(-1!==["left","right"].indexOf(e))return e;return parseInt(e||0)}(e.getAttribute("xPosition")),t.yPosition=function(e){if(-1!==["top","bottom"].indexOf(e))return e;return parseInt(e||0)}(e.getAttribute("yPosition")),t.apiFramework=e.getAttribute("apiFramework"),t.pxratio=e.getAttribute("pxratio")||"1",t.offset=m.parseDuration(e.getAttribute("offset")),t.duration=m.parseDuration(e.getAttribute("duration")),m.childrenByName(e,"HTMLResource").forEach((e=>{t.type=e.getAttribute("creativeType")||"text/html",t.htmlResource=m.parseNodeText(e)})),m.childrenByName(e,"IFrameResource").forEach((e=>{t.type=e.getAttribute("creativeType")||0,t.iframeResource=m.parseNodeText(e)})),m.childrenByName(e,"StaticResource").forEach((e=>{t.type=e.getAttribute("creativeType")||0,t.staticResource=m.parseNodeText(e)}));const r=m.childByName(e,"IconClicks");if(r){t.iconClickThroughURLTemplate=m.parseNodeText(m.childByName(r,"IconClickThrough")),m.childrenByName(r,"IconClickTracking").forEach((e=>{t.iconClickTrackingURLTemplates.push({id:e.getAttribute("id")||null,url:m.parseNodeText(e)})}));const e=m.childByName(r,"IconClickFallbackImages");e&&m.childrenByName(e,"IconClickFallbackImage").forEach((e=>{t.iconClickFallbackImages.push({url:m.parseNodeText(e)||null,width:e.getAttribute("width")||null,height:e.getAttribute("height")||null})}))}return t.iconViewTrackingURLTemplate=m.parseNodeText(m.childByName(e,"IconViewTracking")),t}(e))})),a}function f(t,r){const i=function(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};const{id:r,adId:i,sequence:a,apiFramework:s}=e(t);return{id:r,adId:i,sequence:a,apiFramework:s,type:"nonlinear",variations:[],trackingEvents:{}}}(r);return m.childrenByName(t,"TrackingEvents").forEach((e=>{let t,r;m.childrenByName(e,"Tracking").forEach((e=>{t=e.getAttribute("event"),r=m.parseNodeText(e),t&&r&&(Array.isArray(i.trackingEvents[t])||(i.trackingEvents[t]=[]),i.trackingEvents[t].push(r))}))})),m.childrenByName(t,"NonLinear").forEach((e=>{const t={id:null,width:0,height:0,expandedWidth:0,expandedHeight:0,scalable:!0,maintainAspectRatio:!0,minSuggestedDuration:0,apiFramework:"static",adType:"nonLinearAd",type:null,staticResource:null,htmlResource:null,iframeResource:null,nonlinearClickThroughURLTemplate:null,nonlinearClickTrackingURLTemplates:[],adParameters:null};t.id=e.getAttribute("id")||null,t.width=e.getAttribute("width"),t.height=e.getAttribute("height"),t.expandedWidth=e.getAttribute("expandedWidth"),t.expandedHeight=e.getAttribute("expandedHeight"),t.scalable=m.parseBoolean(e.getAttribute("scalable")),t.maintainAspectRatio=m.parseBoolean(e.getAttribute("maintainAspectRatio")),t.minSuggestedDuration=m.parseDuration(e.getAttribute("minSuggestedDuration")),t.apiFramework=e.getAttribute("apiFramework"),m.childrenByName(e,"HTMLResource").forEach((e=>{t.type=e.getAttribute("creativeType")||"text/html",t.htmlResource=m.parseNodeText(e)})),m.childrenByName(e,"IFrameResource").forEach((e=>{t.type=e.getAttribute("creativeType")||0,t.iframeResource=m.parseNodeText(e)})),m.childrenByName(e,"StaticResource").forEach((e=>{t.type=e.getAttribute("creativeType")||0,t.staticResource=m.parseNodeText(e)}));const r=m.childByName(e,"AdParameters");r&&(t.adParameters={value:m.parseNodeText(r),xmlEncoded:r.getAttribute("xmlEncoded")||null}),t.nonlinearClickThroughURLTemplate=m.parseNodeText(m.childByName(e,"NonLinearClickThrough")),m.childrenByName(e,"NonLinearClickTracking").forEach((e=>{t.nonlinearClickTrackingURLTemplates.push({id:e.getAttribute("id")||null,url:m.parseNodeText(e)})})),i.variations.push(t)})),i}function T(e){const t=[];return e.forEach((e=>{const r=A(e);r&&t.push(r)})),t}function A(e){if("#comment"===e.nodeName)return null;const t={name:null,value:null,attributes:{},children:[]},r=e.attributes,i=e.childNodes;if(t.name=e.nodeName,e.attributes)for(const e in r)if(r.hasOwnProperty(e)){const i=r[e];i.nodeName&&i.nodeValue&&(t.attributes[i.nodeName]=i.nodeValue)}for(const e in i)if(i.hasOwnProperty(e)){const r=A(i[e]);r&&t.children.push(r)}if(0===t.children.length||1===t.children.length&&["#cdata-section","#text"].indexOf(t.children[0].name)>=0){const r=m.parseNodeText(e);""!==r&&(t.value=r),t.children=[]}return null===(a=t).value&&0===Object.keys(a.attributes).length&&0===a.children.length?null:t;var a}function R(e){return e.getAttribute("AdID")||e.getAttribute("adID")||e.getAttribute("adId")||null}const y={Wrapper:{subElements:["VASTAdTagURI","Impression"]},BlockedAdCategories:{attributes:["authority"]},InLine:{subElements:["AdSystem","AdTitle","Impression","AdServingId","Creatives"]},Category:{attributes:["authority"]},Pricing:{attributes:["model","currency"]},Verification:{oneOfinLineResources:["JavaScriptResource","ExecutableResource"],attributes:["vendor"]},UniversalAdId:{attributes:["idRegistry"]},JavaScriptResource:{attributes:["apiFramework","browserOptional"]},ExecutableResource:{attributes:["apiFramework","type"]},Tracking:{attributes:["event"]},Creatives:{subElements:["Creative"]},Creative:{subElements:["UniversalAdId"]},Linear:{subElements:["MediaFiles","Duration"]},MediaFiles:{subElements:["MediaFile"]},MediaFile:{attributes:["delivery","type","width","height"]},Mezzanine:{attributes:["delivery","type","width","height"]},NonLinear:{oneOfinLineResources:["StaticResource","IFrameResource","HTMLResource"],attributes:["width","height"]},Companion:{oneOfinLineResources:["StaticResource","IFrameResource","HTMLResource"],attributes:["width","height"]},StaticResource:{attributes:["creativeType"]},Icons:{subElements:["Icon"]},Icon:{oneOfinLineResources:["StaticResource","IFrameResource","HTMLResource"]}};function k(e,t){if(!y[e.nodeName]||!y[e.nodeName].attributes)return;const r=y[e.nodeName].attributes.filter((t=>!e.getAttribute(t)));r.length>0&&N({name:e.nodeName,parentName:e.parentNode.nodeName,attributes:r},t)}function E(e,t,r){const i=y[e.nodeName],a=!r&&"Wrapper"!==e.nodeName;if(!i||a)return;if(i.subElements){const r=i.subElements.filter((t=>!m.childByName(e,t)));r.length>0&&N({name:e.nodeName,parentName:e.parentNode.nodeName,subElements:r},t)}if(!r||!i.oneOfinLineResources)return;i.oneOfinLineResources.some((t=>m.childByName(e,t)))||N({name:e.nodeName,parentName:e.parentNode.nodeName,oneOfResources:i.oneOfinLineResources},t)}function b(e){return e.children&&0!==e.children.length}function N(e,t){let{name:r,parentName:i,attributes:a,subElements:s,oneOfResources:n}=e,o="Element '".concat(r,"'");o+=a?" missing required attribute(s) '".concat(a.join(", "),"' "):s?" missing required sub element(s) '".concat(s.join(", "),"' "):n?" must provide one of the following '".concat(n.join(", "),"' "):" is empty",t("VAST-warning",{message:o,parentElement:i,specVersion:4.1})}const w={verifyRequiredValues:function e(t,r,i){if(t&&t.nodeName)if("InLine"===t.nodeName&&(i=!0),k(t,r),b(t)){E(t,r,i);for(let a=0;a2&&void 0!==arguments[2]?arguments[2]:{};const a=Array.from(e.childNodes).filter((e=>{const t=e.nodeName.toLowerCase();return"inline"===t||!1!==i&&"wrapper"===t}));for(const i of a){if(m.copyNodeAttribute("id",e,i),m.copyNodeAttribute("sequence",e,i),m.copyNodeAttribute("adType",e,i),"Wrapper"===i.nodeName)return{ad:I(i,t),type:"WRAPPER"};if("InLine"===i.nodeName)return{ad:L(i,t,{allowMultipleAds:r}),type:"INLINE"};const a=i.nodeName.toLowerCase();t("VAST-warning",{message:"<".concat(i.nodeName,"inline"===a?"> must be written ":"> must be written "),wrongNode:i})}}function L(e,t){let{allowMultipleAds:r}=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};return!1===r&&e.getAttribute("sequence")?null:U(e,t)}function U(e,t){let r=[];t&&w.verifyRequiredValues(e,t);const i=Array.from(e.childNodes),a=function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return{id:e.id||null,sequence:e.sequence||null,adType:e.adType||null,adServingId:null,categories:[],expires:null,viewableImpression:[],system:null,title:null,description:null,advertiser:null,pricing:null,survey:null,errorURLTemplates:[],impressionURLTemplates:[],creatives:[],extensions:[],adVerifications:[],blockedAdCategories:[],followAdditionalWrappers:!0,allowMultipleAds:!1,fallbackOnNoAd:null}}(m.parseAttributes(e));return i.forEach((e=>{switch(e.nodeName){case"Error":a.errorURLTemplates.push(m.parseNodeText(e));break;case"Impression":a.impressionURLTemplates.push({id:e.getAttribute("id")||null,url:m.parseNodeText(e)});break;case"Creatives":a.creatives=function(e){const t=[];return e.forEach((e=>{const r={id:e.getAttribute("id")||null,adId:R(e),sequence:e.getAttribute("sequence")||null,apiFramework:e.getAttribute("apiFramework")||null},i=[];let a;m.childrenByName(e,"UniversalAdId").forEach((e=>{const t={idRegistry:e.getAttribute("idRegistry")||"unknown",value:m.parseNodeText(e)};i.push(t)}));const s=m.childByName(e,"CreativeExtensions");s&&(a=T(m.childrenByName(s,"CreativeExtension")));for(const s in e.childNodes){const n=e.childNodes[s];let o;switch(n.nodeName){case"Linear":o=v(n,r);break;case"NonLinearAds":o=f(n,r);break;case"CompanionAds":o=g(n,r)}o&&(i&&(o.universalAdIds=i),a&&(o.creativeExtensions=a),t.push(o))}})),t}(m.childrenByName(e,"Creative"));break;case"Extensions":{const t=m.childrenByName(e,"Extension");a.extensions=T(t),a.adVerifications.length||(r=function(e){let t=null,r=[];e.some((e=>t=m.childByName(e,"AdVerifications"))),t&&(r=x(m.childrenByName(t,"Verification")));return r}(t));break}case"AdVerifications":a.adVerifications=x(m.childrenByName(e,"Verification"));break;case"AdSystem":a.system={value:m.parseNodeText(e),version:e.getAttribute("version")||null};break;case"AdTitle":a.title=m.parseNodeText(e);break;case"AdServingId":a.adServingId=m.parseNodeText(e);break;case"Category":a.categories.push({authority:e.getAttribute("authority")||null,value:m.parseNodeText(e)});break;case"Expires":a.expires=parseInt(m.parseNodeText(e),10);break;case"ViewableImpression":a.viewableImpression.push(function(e){const t=(e,t)=>{const r=m.parseNodeText(t);return r&&e.push(r),e};return{id:e.getAttribute("id")||null,viewable:m.childrenByName(e,"Viewable").reduce(t,[]),notViewable:m.childrenByName(e,"NotViewable").reduce(t,[]),viewUndetermined:m.childrenByName(e,"ViewUndetermined").reduce(t,[])}}(e));break;case"Description":a.description=m.parseNodeText(e);break;case"Advertiser":a.advertiser={id:e.getAttribute("id")||null,value:m.parseNodeText(e)};break;case"Pricing":a.pricing={value:m.parseNodeText(e),model:e.getAttribute("model")||null,currency:e.getAttribute("currency")||null};break;case"Survey":a.survey={value:m.parseNodeText(e),type:e.getAttribute("type")||null};break;case"BlockedAdCategories":a.blockedAdCategories.push({authority:e.getAttribute("authority")||null,value:m.parseNodeText(e)})}})),r.length&&(a.adVerifications=a.adVerifications.concat(r)),a}function I(e,t){const r=U(e,t),i=e.getAttribute("followAdditionalWrappers"),a=e.getAttribute("allowMultipleAds"),s=e.getAttribute("fallbackOnNoAd");r.followAdditionalWrappers=!i||m.parseBoolean(i),r.allowMultipleAds=!!a&&m.parseBoolean(a),r.fallbackOnNoAd=s?m.parseBoolean(s):null;let n=m.childByName(e,"VASTAdTagURI");if(n?r.nextWrapperURL=m.parseNodeText(n):(n=m.childByName(e,"VASTAdTagURL"),n&&(r.nextWrapperURL=m.parseNodeText(m.childByName(n,"URL")))),r.creatives.forEach((e=>{if(["linear","nonlinear"].includes(e.type)){if(e.trackingEvents){r.trackingEvents||(r.trackingEvents={}),r.trackingEvents[e.type]||(r.trackingEvents[e.type]={});for(const t in e.trackingEvents){const i=e.trackingEvents[t];Array.isArray(r.trackingEvents[e.type][t])||(r.trackingEvents[e.type][t]=[]),i.forEach((i=>{r.trackingEvents[e.type][t].push(i)}))}}e.videoClickTrackingURLTemplates&&(Array.isArray(r.videoClickTrackingURLTemplates)||(r.videoClickTrackingURLTemplates=[]),e.videoClickTrackingURLTemplates.forEach((e=>{r.videoClickTrackingURLTemplates.push(e)}))),e.videoClickThroughURLTemplate&&(r.videoClickThroughURLTemplate=e.videoClickThroughURLTemplate),e.videoCustomClickURLTemplates&&(Array.isArray(r.videoCustomClickURLTemplates)||(r.videoCustomClickURLTemplates=[]),e.videoCustomClickURLTemplates.forEach((e=>{r.videoCustomClickURLTemplates.push(e)})))}})),r.nextWrapperURL)return r}function x(e){const t=[];return e.forEach((e=>{const r={resource:null,vendor:null,browserOptional:!1,apiFramework:null,type:null,parameters:null,trackingEvents:{}},i=Array.from(e.childNodes);m.assignAttributes(e.attributes,r),i.forEach((e=>{let{nodeName:t,textContent:i,attributes:a}=e;switch(t){case"JavaScriptResource":case"ExecutableResource":r.resource=i.trim(),m.assignAttributes(a,r);break;case"VerificationParameters":r.parameters=i.trim()}}));const a=m.childByName(e,"TrackingEvents");a&&m.childrenByName(a,"Tracking").forEach((e=>{const t=e.getAttribute("event"),i=m.parseNodeText(e);t&&i&&(Array.isArray(r.trackingEvents[t])||(r.trackingEvents[t]=[]),r.trackingEvents[t].push(i))})),t.push(r)})),t}class S{constructor(){this._handlers=[]}on(e,t){if("function"!=typeof t)throw new TypeError("The handler argument must be of type Function. Received type ".concat(typeof t));if(!e)throw new TypeError("The event argument must be of type String. Received type ".concat(typeof e));return this._handlers.push({event:e,handler:t}),this}once(e,t){return this.on(e,function(e,t,r){const i={fired:!1,wrapFn:void 0};function a(){i.fired||(e.off(t,i.wrapFn),i.fired=!0,r.bind(e)(...arguments))}return i.wrapFn=a,a}(this,e,t))}off(e,t){return this._handlers=this._handlers.filter((r=>r.event!==e||r.handler!==t)),this}emit(e){for(var t=arguments.length,r=new Array(t>1?t-1:0),i=1;i{"*"===t.event&&(a=!0,t.handler(e,...r)),t.event===e&&(a=!0,t.handler(...r))})),a}removeAllListeners(e){return e?(this._handlers=this._handlers.filter((t=>t.event!==e)),this):(this._handlers=[],this)}listenerCount(e){return this._handlers.filter((t=>t.event===e)).length}listeners(e){return this._handlers.reduce(((t,r)=>(r.event===e&&t.push(r.handler),t)),[])}eventNames(){return this._handlers.map((e=>e.event))}}let D=0,V=0;const O=(e,t)=>{if(!e||!t||e<=0||t<=0)return;V=(V*D+8*e/t)/++D},F={ERRORCODE:900,extensions:[]};class B extends S{constructor(){let{fetcher:e}=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};super(),this.maxWrapperDepth=null,this.remainingAds=[],this.fetcher=e||null}trackVastError(e,t){for(var r=arguments.length,i=new Array(r>2?r-2:0),a=2;a0&&void 0!==arguments[0]?arguments[0]:{};this.maxWrapperDepth=e.wrapperLimit||10,this.parsingOptions={allowMultipleAds:e.allowMultipleAds},this.rootURL="",this.resetParsingStatus(),O(e.byteLength,e.requestDuration)}resetParsingStatus(){this.errorURLTemplates=[],this.rootErrorURLTemplates=[],this.vastVersion=null}getRemainingAds(e){if(0===this.remainingAds.length)return Promise.reject(new Error("No more ads are available for the given VAST"));const t=e?d.flatten(this.remainingAds):this.remainingAds.shift();return this.errorURLTemplates=[],this.resolveAds(t,{wrapperDepth:0,url:this.rootURL}).then((e=>this.buildVASTResponse(e)))}parseVAST(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return this.initParsingStatus(t),t.isRootVAST=!0,this.parse(e,t).then((e=>this.buildVASTResponse(e)))}buildVASTResponse(e){const t=function(e){let{ads:t,errorURLTemplates:r,version:i}=e;return{ads:t||[],errorURLTemplates:r||[],version:i||null}}({ads:e,errorURLTemplates:this.getErrorURLTemplates(),version:this.vastVersion});return this.completeWrapperResolving(t),t}parseVastXml(e,t){let{isRootVAST:r=!1,url:i=null,wrapperDepth:a=0,allowMultipleAds:s,followAdditionalWrappers:n}=t;if(!e||!e.documentElement||"VAST"!==e.documentElement.nodeName)throw this.emit("VAST-ad-parsed",{type:"ERROR",url:i,wrapperDepth:a}),new Error("Invalid VAST XMLDocument");const o=[],l=e.documentElement.childNodes,c=e.documentElement.getAttribute("version");r&&c&&(this.vastVersion=c);for(const e in l){const t=l[e];if("Error"===t.nodeName){const e=m.parseNodeText(t);r?this.rootErrorURLTemplates.push(e):this.errorURLTemplates.push(e)}else if("Ad"===t.nodeName){if(this.vastVersion&&parseFloat(this.vastVersion)<3)s=!0;else if(!1===s&&o.length>1)break;const e=C(t,this.emit.bind(this),{allowMultipleAds:s,followAdditionalWrappers:n});e.ad?(o.push(e.ad),this.emit("VAST-ad-parsed",{type:e.type,url:i,wrapperDepth:a,adIndex:o.length-1,vastVersion:c})):this.trackVastError(this.getErrorURLTemplates(),{ERRORCODE:101})}}return o}parse(e){let{url:t=null,resolveAll:r=!0,wrapperSequence:i=null,previousUrl:a=null,wrapperDepth:s=0,isRootVAST:n=!1,followAdditionalWrappers:o,allowMultipleAds:l}=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},c=[];this.vastVersion&&parseFloat(this.vastVersion)<3&&n&&(l=!0);try{c=this.parseVastXml(e,{isRootVAST:n,url:t,wrapperDepth:s,allowMultipleAds:l,followAdditionalWrappers:o})}catch(e){return Promise.reject(e)}return 1===c.length&&null!=i&&(c[0].sequence=i),!1===r&&(this.remainingAds=m.splitVAST(c),c=this.remainingAds.shift()),this.resolveAds(c,{wrapperDepth:s,previousUrl:a,url:t})}resolveAds(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[],{wrapperDepth:t,previousUrl:r,url:i}=arguments.length>1?arguments[1]:void 0;const a=[];return r=i,e.forEach((e=>{const i=this.resolveWrappers(e,t,r);a.push(i)})),Promise.all(a).then((e=>{const a=d.flatten(e);if(!a.length&&this.remainingAds.length>0){const e=this.remainingAds.shift();return this.resolveAds(e,{wrapperDepth:t,previousUrl:r,url:i})}return a}))}resolveWrappers(e,t,r){const i={...e};return new Promise((e=>{var a;if(t++,!i.nextWrapperURL)return delete i.nextWrapperURL,e(i);if(!this.fetcher)return i.VASTAdTagURI=i.nextWrapperURL,delete i.nextWrapperURL,e(i);if(t>=this.maxWrapperDepth)return i.errorCode=302,delete i.nextWrapperURL,e(i);i.nextWrapperURL=m.resolveVastAdTagURI(i.nextWrapperURL,r);const s=null!==(a=this.parsingOptions.allowMultipleAds)&&void 0!==a?a:i.allowMultipleAds,n=i.sequence;this.fetcher.fetchVAST({url:i.nextWrapperURL,emitter:this.emit.bind(this),maxWrapperDepth:this.maxWrapperDepth}).then((a=>this.parse(a,{url:i.nextWrapperURL,previousUrl:r,wrapperSequence:n,wrapperDepth:t,followAdditionalWrappers:i.followAdditionalWrappers,allowMultipleAds:s}).then((t=>{if(delete i.nextWrapperURL,0===t.length)return i.creatives=[],e(i);t.forEach((e=>{e&&m.mergeWrapperAdData(e,i)})),e(t)})))).catch((t=>{i.errorCode=301,i.errorMessage=t.message,e(i)}))}))}completeWrapperResolving(e){if(0===e.ads.length)this.trackVastError(e.errorURLTemplates,{ERRORCODE:303});else for(let t=e.ads.length-1;t>=0;t--){const r=e.ads[t];!r.errorCode&&0!==r.creatives.length||r.VASTAdTagURI||(this.trackVastError(r.errorURLTemplates.concat(e.errorURLTemplates),{ERRORCODE:r.errorCode||303},{ERRORMESSAGE:r.errorMessage||""},{extensions:r.extensions},{system:r.system}),e.ads.splice(t,1))}}}let P=null;const M={data:{},length:0,getItem(e){return this.data[e]},setItem(e,t){this.data[e]=t,this.length=Object.keys(this.data).length},removeItem(e){delete this.data[e],this.length=Object.keys(this.data).length},clear(){this.data={},this.length=0}};class W{constructor(){this.storage=this.initStorage()}initStorage(){if(P)return P;try{P="undefined"!=typeof window&&null!==window?window.localStorage||window.sessionStorage:null}catch(e){P=null}return P&&!this.isStorageDisabled(P)||(P=M,P.clear()),P}isStorageDisabled(e){const t="__VASTStorage__";try{if(e.setItem(t,t),e.getItem(t)!==t)return e.removeItem(t),!0}catch(e){return!0}return e.removeItem(t),!1}getItem(e){return this.storage.getItem(e)}setItem(e,t){return this.storage.setItem(e,t)}removeItem(e){return this.storage.removeItem(e)}clear(){return this.storage.clear()}}const j=12e4;const q={get:async function(e,t){try{const r=new AbortController,i=setTimeout((()=>{throw r.abort(),new Error("URLHandler: Request timed out after ".concat(t.timeout||j," ms (408)"))}),t.timeout||j),a=await fetch(e,{...t,signal:r.signal,credentials:t.withCredentials?"include":"omit"});clearTimeout(i);const s=function(e){return"https:"===window.location.protocol&&e.url.includes("http://")?"URLHandler: Cannot go from HTTPS to HTTP.":200===e.status&&e.ok?null:"URLHandler: ".concat(e.statusText," (").concat(e.status,")")}(a);return s?{error:new Error(s),statusCode:a.status}:async function(e){const t=await e.text();let r;r=d.isBrowserEnvironment()?new DOMParser:new((await import("@xmldom/xmldom")).DOMParser);return{xml:r.parseFromString(t,"text/xml"),details:{byteLength:t.length,statusCode:e.status,rawXml:t}}}(a)}catch(e){return{error:e,statusCode:"AbortError"===e.name?408:null}}}};class K{constructor(){this.URLTemplateFilters=[]}setOptions(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};this.urlHandler=e.urlHandler||e.urlhandler||q,this.fetchingOptions={timeout:e.timeout||j,withCredentials:Boolean(e.withCredentials)}}addURLTemplateFilter(e){"function"==typeof e&&this.URLTemplateFilters.push(e)}removeLastURLTemplateFilter(){this.URLTemplateFilters.pop()}countURLTemplateFilters(){return this.URLTemplateFilters.length}clearURLTemplateFilters(){this.URLTemplateFilters=[]}async fetchVAST(e){var t;let{url:r,maxWrapperDepth:i,emitter:a,wrapperDepth:s=0,previousUrl:n=null,wrapperAd:o=null}=e;const l=Date.now();this.URLTemplateFilters.forEach((e=>{r=e(r)})),a("VAST-resolving",{url:r,previousUrl:n,wrapperDepth:s,maxWrapperDepth:i,timeout:this.fetchingOptions.timeout,wrapperAd:o});const c=await this.urlHandler.get(r,this.fetchingOptions),u=Math.round(Date.now()-l);if(a("VAST-resolved",{url:r,previousUrl:n,wrapperDepth:s,error:(null==c?void 0:c.error)||null,duration:u,statusCode:(null==c?void 0:c.statusCode)||null,...null==c?void 0:c.details}),O(null==c||null===(t=c.details)||void 0===t?void 0:t.byteLength,u),c.error)throw new Error(c.error);return c.xml}}class H{constructor(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0,t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:new W;this.cappingFreeLunch=e,this.cappingMinimumTimeInterval=t,this.fetcher=new K,this.vastParser=new B({fetcher:this.fetcher}),this.storage=r,void 0===this.lastSuccessfulAd&&(this.lastSuccessfulAd=0),void 0===this.totalCalls&&(this.totalCalls=0),void 0===this.totalCallsTimeout&&(this.totalCallsTimeout=0)}addURLTemplateFilter(e){this.fetcher.addURLTemplateFilter(e)}removeLastURLTemplateFilter(){this.fetcher.removeLastURLTemplateFilter()}countURLTemplateFilters(){return this.fetcher.countURLTemplateFilters()}clearURLTemplateFilters(){this.fetcher.clearURLTemplateFilters()}getParser(){return this.vastParser}get lastSuccessfulAd(){return this.storage.getItem("vast-client-last-successful-ad")}set lastSuccessfulAd(e){this.storage.setItem("vast-client-last-successful-ad",e)}get totalCalls(){return this.storage.getItem("vast-client-total-calls")}set totalCalls(e){this.storage.setItem("vast-client-total-calls",e)}get totalCallsTimeout(){return this.storage.getItem("vast-client-total-calls-timeout")}set totalCallsTimeout(e){this.storage.setItem("vast-client-total-calls-timeout",e)}hasRemainingAds(){return this.vastParser.remainingAds.length>0}getNextAds(e){return this.vastParser.getRemainingAds(e)}parseVAST(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return this.fetcher.setOptions(t),this.vastParser.parseVAST(e,t)}get(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};const r=Date.now();return t.hasOwnProperty("resolveAll")||(t.resolveAll=!1),this.totalCallsTimeout{if(this.cappingFreeLunch>=this.totalCalls)return a(new Error("VAST call canceled – FreeLunch capping not reached yet ".concat(this.totalCalls,"/").concat(this.cappingFreeLunch)));const s=r-this.lastSuccessfulAd;if(s<0)this.lastSuccessfulAd=0;else if(s(t.previousUrl=e,t.isRootVAST=!0,t.url=e,this.vastParser.parse(r,t).then((e=>{const t=this.vastParser.buildVASTResponse(e);i(t)}))))).catch((e=>a(e)))}))}}class _ extends S{constructor(e,t,r){let i=arguments.length>3&&void 0!==arguments[3]?arguments[3]:null,a=arguments.length>4&&void 0!==arguments[4]&&arguments[4];super(),this.ad=t,this.creative=r,this.variation=i,this.muted=a,this.impressed=!1,this.skippable=!1,this.trackingEvents={},this.lastPercentage=0,this._alreadyTriggeredQuartiles={},this.emitAlwaysEvents=["creativeView","start","firstQuartile","midpoint","thirdQuartile","complete","resume","pause","rewind","skip","closeLinear","close"];for(const e in this.creative.trackingEvents){const t=this.creative.trackingEvents[e];this.trackingEvents[e]=t.slice(0)}!function(e){return"linear"===e.type}(this.creative)?this._initVariationTracking():this._initLinearTracking(),e&&this.on("start",(()=>{e.lastSuccessfulAd=Date.now()}))}_initLinearTracking(){this.linear=!0,this.skipDelay=this.creative.skipDelay,this.setDuration(this.creative.duration),this.clickThroughURLTemplate=this.creative.videoClickThroughURLTemplate,this.clickTrackingURLTemplates=this.creative.videoClickTrackingURLTemplates}_initVariationTracking(){if(this.linear=!1,this.skipDelay=-1,this.variation){for(const e in this.variation.trackingEvents){const t=this.variation.trackingEvents[e];this.trackingEvents[e]?this.trackingEvents[e]=this.trackingEvents[e].concat(t.slice(0)):this.trackingEvents[e]=t.slice(0)}"nonLinearAd"===this.variation.adType?(this.clickThroughURLTemplate=this.variation.nonlinearClickThroughURLTemplate,this.clickTrackingURLTemplates=this.variation.nonlinearClickTrackingURLTemplates,this.setDuration(this.variation.minSuggestedDuration)):function(e){return"companionAd"===e.adType}(this.variation)&&(this.clickThroughURLTemplate=this.variation.companionClickThroughURLTemplate,this.clickTrackingURLTemplates=this.variation.companionClickTrackingURLTemplates)}}setDuration(e){d.isValidTimeValue(e)?(this.assetDuration=e,this.quartiles={firstQuartile:Math.round(25*this.assetDuration)/100,midpoint:Math.round(50*this.assetDuration)/100,thirdQuartile:Math.round(75*this.assetDuration)/100}):this.emit("TRACKER-error",{message:"the duration provided is not valid. duration: ".concat(e)})}setProgress(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},r=!(arguments.length>2&&void 0!==arguments[2])||arguments[2];if(!d.isValidTimeValue(e)||"object"!=typeof t)return void this.emit("TRACKER-error",{message:"One given setProgress parameter has the wrong type. progress: ".concat(e,", macros: ").concat(d.formatMacrosValues(t))});const i=this.skipDelay||-1;if(-1===i||this.skippable||(i>e?this.emit("skip-countdown",i-e):(this.skippable=!0,this.emit("skip-countdown",0))),this.assetDuration>0){const i=Math.round(e/this.assetDuration*100),a=[];if(e>0){a.push("start");for(let e=this.lastPercentage;e{this.track(e,{macros:t,once:r})})),e1&&void 0!==arguments[1]?arguments[1]:{};"boolean"==typeof e&&"object"==typeof t?(this.muted!==e&&this.track(e?"mute":"unmute",{macros:t}),this.muted=e):this.emit("TRACKER-error",{message:"One given setMuted parameter has the wrong type. muted: ".concat(e,", macros: ").concat(d.formatMacrosValues(t))})}setPaused(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};"boolean"==typeof e&&"object"==typeof t?(this.paused!==e&&this.track(e?"pause":"resume",{macros:t}),this.paused=e):this.emit("TRACKER-error",{message:"One given setPaused parameter has the wrong type. paused: ".concat(e,", macros: ").concat(d.formatMacrosValues(t))})}setFullscreen(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};"boolean"==typeof e&&"object"==typeof t?(this.fullscreen!==e&&this.track(e?"fullscreen":"exitFullscreen",{macros:t}),this.fullscreen=e):this.emit("TRACKER-error",{message:"One given setFullScreen parameter has the wrong type. fullscreen: ".concat(e,", macros: ").concat(d.formatMacrosValues(t))})}setExpand(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};"boolean"==typeof e&&"object"==typeof t?(this.expanded!==e&&(this.track(e?"expand":"collapse",{macros:t}),this.track(e?"playerExpand":"playerCollapse",{macros:t})),this.expanded=e):this.emit("TRACKER-error",{message:"One given setExpand parameter has the wrong type. expanded: ".concat(e,", macros: ").concat(d.formatMacrosValues(t))})}setSkipDelay(e){d.isValidTimeValue(e)?this.skipDelay=e:this.emit("TRACKER-error",{message:"setSkipDelay parameter does not have a valid value. duration: ".concat(e)})}trackImpression(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};"object"==typeof e?this.impressed||(this.impressed=!0,this.trackURLs(this.ad.impressionURLTemplates,e),this.track("creativeView",{macros:e})):this.emit("TRACKER-error",{message:"trackImpression parameter has the wrong type. macros: ".concat(e)})}trackViewableImpression(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};"object"==typeof e?this.ad.viewableImpression.forEach((t=>{this.trackURLs(t.viewable,e)})):this.emit("TRACKER-error",{message:"trackViewableImpression given macros has the wrong type. macros: ".concat(e)})}trackNotViewableImpression(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};"object"==typeof e?this.ad.viewableImpression.forEach((t=>{this.trackURLs(t.notViewable,e)})):this.emit("TRACKER-error",{message:"trackNotViewableImpression given macros has the wrong type. macros: ".concat(e)})}trackUndeterminedImpression(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};"object"==typeof e?this.ad.viewableImpression.forEach((t=>{this.trackURLs(t.viewUndetermined,e)})):this.emit("TRACKER-error",{message:"trackUndeterminedImpression given macros has the wrong type. macros: ".concat(e)})}error(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];"object"==typeof e&&"boolean"==typeof t?this.trackURLs(this.ad.errorURLTemplates,e,{isCustomCode:t}):this.emit("TRACKER-error",{message:"One given error parameter has the wrong type. macros: ".concat(d.formatMacrosValues(e),", isCustomCode: ").concat(t)})}errorWithCode(e){let t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];"string"==typeof e&&"boolean"==typeof t?(this.error({ERRORCODE:e},t),console.log("The method errorWithCode is deprecated, please use vast tracker error method instead")):this.emit("TRACKER-error",{message:"One given errorWithCode parameter has the wrong type. errorCode: ".concat(e,", isCustomCode: ").concat(t)})}complete(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};"object"==typeof e?this.track("complete",{macros:e}):this.emit("TRACKER-error",{message:"complete given macros has the wrong type. macros: ".concat(e)})}notUsed(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};"object"==typeof e?(this.track("notUsed",{macros:e}),this.trackingEvents=[]):this.emit("TRACKER-error",{message:"notUsed given macros has the wrong type. macros: ".concat(e)})}otherAdInteraction(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};"object"==typeof e?this.track("otherAdInteraction",{macros:e}):this.emit("TRACKER-error",{message:"otherAdInteraction given macros has the wrong type. macros: ".concat(e)})}acceptInvitation(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};"object"==typeof e?this.track("acceptInvitation",{macros:e}):this.emit("TRACKER-error",{message:"acceptInvitation given macros has the wrong type. macros: ".concat(e)})}adExpand(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};"object"==typeof e?this.track("adExpand",{macros:e}):this.emit("TRACKER-error",{message:"adExpand given macros has the wrong type. macros: ".concat(e)})}adCollapse(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};"object"==typeof e?this.track("adCollapse",{macros:e}):this.emit("TRACKER-error",{message:"adCollapse given macros has the wrong type. macros: ".concat(e)})}minimize(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};"object"==typeof e?this.track("minimize",{macros:e}):this.emit("TRACKER-error",{message:"minimize given macros has the wrong type. macros: ".concat(e)})}verificationNotExecuted(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if("string"!=typeof e||"object"!=typeof t)return void this.emit("TRACKER-error",{message:"One given verificationNotExecuted parameter has to wrong type. vendor: ".concat(e,", macros: ").concat(d.formatMacrosValues(t))});if(!this.ad||!this.ad.adVerifications||!this.ad.adVerifications.length)throw new Error("No adVerifications provided");if(!e)throw new Error("No vendor provided, unable to find associated verificationNotExecuted");const r=this.ad.adVerifications.find((t=>t.vendor===e));if(!r)throw new Error("No associated verification element found for vendor: ".concat(e));const i=r.trackingEvents;if(i&&i.verificationNotExecuted){const e=i.verificationNotExecuted;this.trackURLs(e,t),this.emit("verificationNotExecuted",{trackingURLTemplates:e})}}overlayViewDuration(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};"string"==typeof e&&"object"==typeof t?(t.ADPLAYHEAD=e,this.track("overlayViewDuration",{macros:t})):this.emit("TRACKER-error",{message:"One given overlayViewDuration parameters has the wrong type. formattedDuration: ".concat(e,", macros: ").concat(d.formatMacrosValues(t))})}close(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};"object"==typeof e?this.track(this.linear?"closeLinear":"close",{macros:e}):this.emit("TRACKER-error",{message:"close given macros has the wrong type. macros: ".concat(e)})}skip(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};"object"==typeof e?this.track("skip",{macros:e}):this.emit("TRACKER-error",{message:"skip given macros has the wrong type. macros: ".concat(e)})}load(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};"object"==typeof e?this.track("loaded",{macros:e}):this.emit("TRACKER-error",{message:"load given macros has the wrong type. macros: ".concat(e)})}click(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null,t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if(null!==e&&"string"!=typeof e||"object"!=typeof t)return void this.emit("TRACKER-error",{message:"One given click parameter has the wrong type. fallbackClickThroughURL: ".concat(e,", macros: ").concat(d.formatMacrosValues(t))});this.clickTrackingURLTemplates&&this.clickTrackingURLTemplates.length&&this.trackURLs(this.clickTrackingURLTemplates,t);const r=this.clickThroughURLTemplate||e,i={...t};if(r){this.progress&&(i.ADPLAYHEAD=this.progressFormatted());const e=d.resolveURLTemplates([r],i)[0];this.emit("clickthrough",e)}}track(e){let{macros:t={},once:r=!1}=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if("object"!=typeof t)return void this.emit("TRACKER-error",{message:"track given macros has the wrong type. macros: ".concat(t)});"closeLinear"===e&&!this.trackingEvents[e]&&this.trackingEvents.close&&(e="close");const i=this.trackingEvents[e],a=this.emitAlwaysEvents.indexOf(e)>-1;i?(this.emit(e,{trackingURLTemplates:i}),this.trackURLs(i,t)):a&&this.emit(e,null),r&&(delete this.trackingEvents[e],a&&this.emitAlwaysEvents.splice(this.emitAlwaysEvents.indexOf(e),1))}trackURLs(e){var t;let r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};const{validUrls:a,invalidUrls:s}=d.filterUrlTemplates(e);s.length&&this.emit("TRACKER-error",{message:"Provided urls are malformed. url: ".concat(s)});const n={...r};this.linear&&(this.creative&&this.creative.mediaFiles&&this.creative.mediaFiles[0]&&this.creative.mediaFiles[0].fileURL&&(n.ASSETURI=this.creative.mediaFiles[0].fileURL),this.progress&&(n.ADPLAYHEAD=this.progressFormatted())),null!==(t=this.creative)&&void 0!==t&&null!==(t=t.universalAdIds)&&void 0!==t&&t.length&&(n.UNIVERSALADID=this.creative.universalAdIds.map((e=>e.idRegistry.concat(" ",e.value))).join(",")),this.ad&&(this.ad.sequence&&(n.PODSEQUENCE=this.ad.sequence),this.ad.adType&&(n.ADTYPE=this.ad.adType),this.ad.adServingId&&(n.ADSERVINGID=this.ad.adServingId),this.ad.categories&&this.ad.categories.length&&(n.ADCATEGORIES=this.ad.categories.map((e=>e.value)).join(",")),this.ad.blockedAdCategories&&this.ad.blockedAdCategories.length&&(n.BLOCKEDADCATEGORIES=this.ad.blockedAdCategories.map((e=>e.value)).join(","))),d.track(a,n,i)}convertToTimecode(e){if(!d.isValidTimeValue(e))return"";const t=1e3*e,r=Math.floor(t/36e5),i=Math.floor(t/6e4%60),a=Math.floor(t/1e3%60),s=Math.floor(t%1e3);return"".concat(d.addLeadingZeros(r,2),":").concat(d.addLeadingZeros(i,2),":").concat(d.addLeadingZeros(a,2),".").concat(d.addLeadingZeros(s,3))}progressFormatted(){return this.convertToTimecode(this.progress)}}export{H as VASTClient,B as VASTParser,_ as VASTTracker,p as parseDuration};