PageSourceSearch

https://www.beaumonde.ge/static/videojs/vast-client.js

js beaumonde.ge collected 2026-09-25 05:09:03 UTC 118,997 bytes, 3,551 lines download raw bytes

1var VAST = (function (exports) {
2    "use strict";
3  
4    var classCallCheck = function (instance, Constructor) {
5      if (!(instance instanceof Constructor)) {
6        throw new TypeError("Cannot call a class as a function");
7      }
8    };
9  
10    var createClass = (function () {
11      function defineProperties(target, props) {
12        for (var i = 0; i < props.length; i++) {
13          var descriptor = props[i];
14          descriptor.enumerable = descriptor.enumerable || false;
15          descriptor.configurable = true;
16          if ("value" in descriptor) descriptor.writable = true;
17          Object.defineProperty(target, descriptor.key, descriptor);
18        }
19      }
20  
21      return function (Constructor, protoProps, staticProps) {
22        if (protoProps) defineProperties(Constructor.prototype, protoProps);
23        if (staticProps) defineProperties(Constructor, staticProps);
24        return Constructor;
25      };
26    })();
27  
28    var inherits = function (subClass, superClass) {
29      if (typeof superClass !== "function" && superClass !== null) {
30        throw new TypeError(
31          "Super expression must either be null or a function, not " +
32            typeof superClass
33        );
34      }
35  
36      subClass.prototype = Object.create(superClass && superClass.prototype, {
37        constructor: {
38          value: subClass,
39          enumerable: false,
40          writable: true,
41          configurable: true,
42        },
43      });
44      if (superClass)
45        Object.setPrototypeOf
46          ? Object.setPrototypeOf(subClass, superClass)
47          : (subClass.__proto__ = superClass);
48    };
49  
50    var possibleConstructorReturn = function (self, call) {
51      if (!self) {
52        throw new ReferenceError(
53          "this hasn't been initialised - super() hasn't been called"
54        );
55      }
56  
57      return call && (typeof call === "object" || typeof call === "function")
58        ? call
59        : self;
60    };
61  
62    var Ad = function Ad() {
63      classCallCheck(this, Ad);
64  
65      this.id = null;
66      this.sequence = null;
67      this.system = null;
68      this.title = null;
69      this.description = null;
70      this.advertiser = null;
71      this.pricing = null;
72      this.survey = null;
73      this.errorURLTemplates = [];
74      this.impressionURLTemplates = [];
75      this.creatives = [];
76      this.extensions = [];
77    };
78  
79    var AdExtension = function AdExtension() {
80      classCallCheck(this, AdExtension);
81  
82      this.attributes = {};
83      this.children = [];
84    };
85  
86    var AdExtensionChild = function AdExtensionChild() {
87      classCallCheck(this, AdExtensionChild);
88  
89      this.name = null;
90      this.value = null;
91      this.attributes = {};
92    };
93  
94    var CompanionAd = function CompanionAd() {
95      classCallCheck(this, CompanionAd);
96  
97      this.id = null;
98      this.width = 0;
99      this.height = 0;
100      this.type = null;
101      this.staticResource = null;
102      this.htmlResource = null;
103      this.iframeResource = null;
104      this.altText = null;
105      this.companionClickThroughURLTemplate = null;
106      this.companionClickTrackingURLTemplates = [];
107      this.trackingEvents = {};
108    };
109  
110    var Creative = function Creative() {
111      var creativeAttributes =
112        arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
113      classCallCheck(this, Creative);
114  
115      this.id = creativeAttributes.id || null;
116      this.adId = creativeAttributes.adId || null;
117      this.sequence = creativeAttributes.sequence || null;
118      this.apiFramework = creativeAttributes.apiFramework || null;
119      this.trackingEvents = {};
120    };
121  
122    var CreativeCompanion = (function (_Creative) {
123      inherits(CreativeCompanion, _Creative);
124  
125      function CreativeCompanion() {
126        var creativeAttributes =
127          arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
128        classCallCheck(this, CreativeCompanion);
129  
130        var _this = possibleConstructorReturn(
131          this,
132          (
133            CreativeCompanion.__proto__ ||
134            Object.getPrototypeOf(CreativeCompanion)
135          ).call(this, creativeAttributes)
136        );
137  
138        _this.type = "companion";
139        _this.variations = [];
140        return _this;
141      }
142  
143      return CreativeCompanion;
144    })(Creative);
145  
146    function track(URLTemplates, variables, options) {
147      var URLs = resolveURLTemplates(URLTemplates, variables, options);
148  
149      URLs.forEach(function (URL) {
150        if (typeof window !== "undefined" && window !== null) {
151          var i = new Image();
152          i.src = URL;
153        }
154      });
155    }
156  
157    /**
158     * Replace the provided URLTemplates with the given values
159     *
160     * @param {Array} URLTemplates - An array of tracking url templates.
161     * @param {Object} [variables={}] - An optional Object of parameters to be used in the tracking calls.
162     * @param {Object} [options={}] - An optional Object of options to be used in the tracking calls.
163     */
164    function resolveURLTemplates(URLTemplates) {
165      var variables =
166        arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
167      var options =
168        arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
169  
170      var URLs = [];
171  
172      // Encode String variables, when given
173      if (variables["ASSETURI"]) {
174        variables["ASSETURI"] = encodeURIComponentRFC3986(variables["ASSETURI"]);
175      }
176      if (variables["CONTENTPLAYHEAD"]) {
177        variables["CONTENTPLAYHEAD"] = encodeURIComponentRFC3986(
178          variables["CONTENTPLAYHEAD"]
179        );
180      }
181  
182      // Set default value for invalid ERRORCODE
183      if (
184        variables["ERRORCODE"] &&
185        !options.isCustomCode &&
186        !/^[0-9]{3}$/.test(variables["ERRORCODE"])
187      ) {
188        variables["ERRORCODE"] = 900;
189      }
190  
191      // Calc random/time based macros
192      variables["CACHEBUSTING"] = leftpad(
193        Math.round(Math.random() * 1.0e8).toString()
194      );
195      variables["TIMESTAMP"] = encodeURIComponentRFC3986(
196        new Date().toISOString()
197      );
198  
199      // RANDOM/random is not defined in VAST 3/4 as a valid macro tho it's used by some adServer (Auditude)
200      variables["RANDOM"] = variables["random"] = variables["CACHEBUSTING"];
201  
202      for (var URLTemplateKey in URLTemplates) {
203        var resolveURL = URLTemplates[URLTemplateKey];
204  
205        if (typeof resolveURL !== "string") {
206          continue;
207        }
208  
209        for (var key in variables) {
210          var value = variables[key];
211          var macro1 = "[" + key + "]";
212          var macro2 = "%%" + key + "%%";
213          resolveURL = resolveURL.replace(macro1, value);
214          resolveURL = resolveURL.replace(macro2, value);
215        }
216        URLs.push(resolveURL);
217      }
218  
219      return URLs;
220    }
221  
222    // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent
223    function encodeURIComponentRFC3986(str) {
224      return encodeURIComponent(str).replace(/[!'()*]/g, function (c) {
225        return "%" + c.charCodeAt(0).toString(16);
226      });
227    }
228  
229    function leftpad(str) {
230      if (str.length < 8) {
231        return (
232          range(0, 8 - str.length, false)
233            .map(function () {
234              return "0";
235            })
236            .join("") + str
237        );
238      }
239      return str;
240    }
241  
242    function range(left, right, inclusive) {
243      var result = [];
244      var ascending = left < right;
245      var end = !inclusive ? right : ascending ? right + 1 : right - 1;
246  
247      for (var i = left; ascending ? i < end : i > end; ascending ? i++ : i--) {
248        result.push(i);
249      }
250      return result;
251    }
252  
253    function isNumeric(n) {
254      return !isNaN(parseFloat(n)) && isFinite(n);
255    }
256  
257    function flatten(arr) {
258      return arr.reduce(function (flat, toFlatten) {
259        return flat.concat(
260          Array.isArray(toFlatten) ? flatten(toFlatten) : toFlatten
261        );
262      }, []);
263    }
264  
265    var util = {
266      track: track,
267      resolveURLTemplates: resolveURLTemplates,
268      encodeURIComponentRFC3986: encodeURIComponentRFC3986,
269      leftpad: leftpad,
270      range: range,
271      isNumeric: isNumeric,
272      flatten: flatten,
273    };
274  
275    /**
276     * This module provides support methods to the parsing classes.
277     */
278  
279    /**
280     * Returns the first element of the given node which nodeName matches the given name.
281     * @param  {Object} node - The node to use to find a match.
282     * @param  {String} name - The name to look for.
283     * @return {Object}
284     */
285    function childByName(node, name) {
286      var childNodes = node.childNodes;
287  
288      for (var childKey in childNodes) {
289        var child = childNodes[childKey];
290  
291        if (child.nodeName === name) {
292          return child;
293        }
294      }
295    }
296  
297    /**
298     * Returns all the elements of the given node which nodeName match the given name.
299     * @param  {any} node - The node to use to find the matches.
300     * @param  {any} name - The name to look for.
301     * @return {Array}
302     */
303    function childrenByName(node, name) {
304      var children = [];
305      var childNodes = node.childNodes;
306  
307      for (var childKey in childNodes) {
308        var child = childNodes[childKey];
309  
310        if (child.nodeName === name) {
311          children.push(child);
312        }
313      }
314      return children;
315    }
316  
317    /**
318     * Converts relative vastAdTagUri.
319     * @param  {String} vastAdTagUrl - The url to resolve.
320     * @param  {String} originalUrl - The original url.
321     * @return {String}
322     */
323    function resolveVastAdTagURI(vastAdTagUrl, originalUrl) {
324      if (!originalUrl) {
325        return vastAdTagUrl;
326      }
327  
328      if (vastAdTagUrl.indexOf("//") === 0) {
329        var _location = location,
330          protocol = _location.protocol;
331  
332        return "" + protocol + vastAdTagUrl;
333      }
334  
335      if (vastAdTagUrl.indexOf("://") === -1) {
336        // Resolve relative URLs (mainly for unit testing)
337        var baseURL = originalUrl.slice(0, originalUrl.lastIndexOf("/"));
338        return baseURL + "/" + vastAdTagUrl;
339      }
340  
341      return vastAdTagUrl;
342    }
343  
344    /**
345     * Converts a boolean string into a Boolean.
346     * @param  {String} booleanString - The boolean string to convert.
347     * @return {Boolean}
348     */
349    function parseBoolean(booleanString) {
350      return ["true", "TRUE", "1"].indexOf(booleanString) !== -1;
351    }
352  
353    /**
354     * Parses a node text (for legacy support).
355     * @param  {Object} node - The node to parse the text from.
356     * @return {String}
357     */
358    function parseNodeText(node) {
359      return node && (node.textContent || node.text || "").trim();
360    }
361  
362    /**
363     * Copies an attribute from a node to another.
364     * @param  {String} attributeName - The name of the attribute to clone.
365     * @param  {Object} nodeSource - The source node to copy the attribute from.
366     * @param  {Object} nodeDestination - The destination node to copy the attribute at.
367     */
368    function copyNodeAttribute(attributeName, nodeSource, nodeDestination) {
369      var attributeValue = nodeSource.getAttribute(attributeName);
370      if (attributeValue) {
371        nodeDestination.setAttribute(attributeName, attributeValue);
372      }
373    }
374  
375    /**
376     * Parses a String duration into a Number.
377     * @param  {String} durationString - The dureation represented as a string.
378     * @return {Number}
379     */
380    function parseDuration(durationString) {
381      if (durationString === null || typeof durationString === "undefined") {
382        return -1;
383      }
384      // Some VAST doesn't have an HH:MM:SS duration format but instead jus the number of seconds
385      if (util.isNumeric(durationString)) {
386        return parseInt(durationString);
387      }
388  
389      var durationComponents = durationString.split(":");
390      if (durationComponents.length !== 3) {
391        return -1;
392      }
393  
394      var secondsAndMS = durationComponents[2].split(".");
395      var seconds = parseInt(secondsAndMS[0]);
396      if (secondsAndMS.length === 2) {
397        seconds += parseFloat("0." + secondsAndMS[1]);
398      }
399  
400      var minutes = parseInt(durationComponents[1] * 60);
401      var hours = parseInt(durationComponents[0] * 60 * 60);
402  
403      if (
404        isNaN(hours) ||
405        isNaN(minutes) ||
406        isNaN(seconds) ||
407        minutes > 60 * 60 ||
408        seconds > 60
409      ) {
410        return -1;
411      }
412      return hours + minutes + seconds;
413    }
414  
415    /**
416     * Splits an Array of ads into an Array of Arrays of ads.
417     * Each subarray contains either one ad or multiple ads (an AdPod)
418     * @param  {Array} ads - An Array of ads to split
419     * @return {Array}
420     */
421    function splitVAST(ads) {
422      var splittedVAST = [];
423      var lastAdPod = null;
424  
425      ads.forEach(function (ad, i) {
426        if (ad.sequence) {
427          ad.sequence = parseInt(ad.sequence, 10);
428        }
429        // The current Ad may be the next Ad of an AdPod
430        if (ad.sequence > 1) {
431          var lastAd = ads[i - 1];
432          // check if the current Ad is exactly the next one in the AdPod
433          if (lastAd && lastAd.sequence === ad.sequence - 1) {
434            lastAdPod && lastAdPod.push(ad);
435            return;
436          }
437          // If the ad had a sequence attribute but it was not part of a correctly formed
438          // AdPod, let's remove the sequence attribute
439          delete ad.sequence;
440        }
441  
442        lastAdPod = [ad];
443        splittedVAST.push(lastAdPod);
444      });
445  
446      return splittedVAST;
447    }
448  
449    /**
450     * Merges the data between an unwrapped ad and his wrapper.
451     * @param  {Ad} unwrappedAd - The 'unwrapped' Ad.
452     * @param  {Ad} wrapper - The wrapper Ad.
453     * @return {void}
454     */
455    function mergeWrapperAdData(unwrappedAd, wrapper) {
456      unwrappedAd.errorURLTemplates = wrapper.errorURLTemplates.concat(
457        unwrappedAd.errorURLTemplates
458      );
459      unwrappedAd.impressionURLTemplates = wrapper.impressionURLTemplates.concat(
460        unwrappedAd.impressionURLTemplates
461      );
462      unwrappedAd.extensions = wrapper.extensions.concat(unwrappedAd.extensions);
463  
464      unwrappedAd.creatives.forEach(function (creative) {
465        if (wrapper.trackingEvents && wrapper.trackingEvents[creative.type]) {
466          for (var eventName in wrapper.trackingEvents[creative.type]) {
467            var urls = wrapper.trackingEvents[creative.type][eventName];
468            if (!Array.isArray(creative.trackingEvents[eventName])) {
469              creative.trackingEvents[eventName] = [];
470            }
471            creative.trackingEvents[eventName] = creative.trackingEvents[
472              eventName
473            ].concat(urls);
474          }
475        }
476      });
477  
478      if (
479        wrapper.videoClickTrackingURLTemplates &&
480        wrapper.videoClickTrackingURLTemplates.length
481      ) {
482        unwrappedAd.creatives.forEach(function (creative) {
483          if (creative.type === "linear") {
484            creative.videoClickTrackingURLTemplates = creative.videoClickTrackingURLTemplates.concat(
485              wrapper.videoClickTrackingURLTemplates
486            );
487          }
488        });
489      }
490  
491      if (
492        wrapper.videoCustomClickURLTemplates &&
493        wrapper.videoCustomClickURLTemplates.length
494      ) {
495        unwrappedAd.creatives.forEach(function (creative) {
496          if (creative.type === "linear") {
497            creative.videoCustomClickURLTemplates = creative.videoCustomClickURLTemplates.concat(
498              wrapper.videoCustomClickURLTemplates
499            );
500          }
501        });
502      }
503  
504      // VAST 2.0 support - Use Wrapper/linear/clickThrough when Inline/Linear/clickThrough is null
505      if (wrapper.videoClickThroughURLTemplate) {
506        unwrappedAd.creatives.forEach(function (creative) {
507          if (
508            creative.type === "linear" &&
509            (creative.videoClickThroughURLTemplate === null ||
510              typeof creative.videoClickThroughURLTemplate === "undefined")
511          ) {
512            creative.videoClickThroughURLTemplate =
513              wrapper.videoClickThroughURLTemplate;
514          }
515        });
516      }
517    }
518  
519    var parserUtils = {
520      childByName: childByName,
521      childrenByName: childrenByName,
522      resolveVastAdTagURI: resolveVastAdTagURI,
523      parseBoolean: parseBoolean,
524      parseNodeText: parseNodeText,
525      copyNodeAttribute: copyNodeAttribute,
526      parseDuration: parseDuration,
527      splitVAST: splitVAST,
528      mergeWrapperAdData: mergeWrapperAdData,
529    };
530  
531    /**
532     * This module provides methods to parse a VAST CompanionAd Element.
533     */
534  
535    /**
536     * Parses a CompanionAd.
537     * @param  {Object} creativeElement - The VAST CompanionAd element to parse.
538     * @param  {Object} creativeAttributes - The attributes of the CompanionAd (optional).
539     * @return {CreativeCompanion}
540     */
541    function parseCreativeCompanion(creativeElement, creativeAttributes) {
542      var creative = new CreativeCompanion(creativeAttributes);
543  
544      parserUtils
545        .childrenByName(creativeElement, "Companion")
546        .forEach(function (companionResource) {
547          var companionAd = new CompanionAd();
548          companionAd.id = companionResource.getAttribute("id") || null;
549          companionAd.width = companionResource.getAttribute("width");
550          companionAd.height = companionResource.getAttribute("height");
551          companionAd.companionClickTrackingURLTemplates = [];
552  
553          parserUtils
554            .childrenByName(companionResource, "HTMLResource")
555            .forEach(function (htmlElement) {
556              companionAd.type =
557                htmlElement.getAttribute("creativeType") || "text/html";
558              companionAd.htmlResource = parserUtils.parseNodeText(htmlElement);
559            });
560  
561          parserUtils
562            .childrenByName(companionResource, "IFrameResource")
563            .forEach(function (iframeElement) {
564              companionAd.type = iframeElement.getAttribute("creativeType") || 0;
565              companionAd.iframeResource = parserUtils.parseNodeText(
566                iframeElement
567              );
568            });
569  
570          parserUtils
571            .childrenByName(companionResource, "StaticResource")
572            .forEach(function (staticElement) {
573              companionAd.type = staticElement.getAttribute("creativeType") || 0;
574  
575              parserUtils
576                .childrenByName(companionResource, "AltText")
577                .forEach(function (child) {
578                  companionAd.altText = parserUtils.parseNodeText(child);
579                });
580  
581              companionAd.staticResource = parserUtils.parseNodeText(
582                staticElement
583              );
584            });
585  
586          parserUtils
587            .childrenByName(companionResource, "TrackingEvents")
588            .forEach(function (trackingEventsElement) {
589              parserUtils
590                .childrenByName(trackingEventsElement, "Tracking")
591                .forEach(function (trackingElement) {
592                  var eventName = trackingElement.getAttribute("event");
593                  var trackingURLTemplate = parserUtils.parseNodeText(
594                    trackingElement
595                  );
596                  if (eventName && trackingURLTemplate) {
597                    if (!Array.isArray(companionAd.trackingEvents[eventName])) {
598                      companionAd.trackingEvents[eventName] = [];
599                    }
600                    companionAd.trackingEvents[eventName].push(
601                      trackingURLTemplate
602                    );
603                  }
604                });
605            });
606  
607          parserUtils
608            .childrenByName(companionResource, "CompanionClickTracking")
609            .forEach(function (clickTrackingElement) {
610              companionAd.companionClickTrackingURLTemplates.push(
611                parserUtils.parseNodeText(clickTrackingElement)
612              );
613            });
614  
615          companionAd.companionClickThroughURLTemplate = parserUtils.parseNodeText(
616            parserUtils.childByName(companionResource, "CompanionClickThrough")
617          );
618          companionAd.companionClickTrackingURLTemplate = parserUtils.parseNodeText(
619            parserUtils.childByName(companionResource, "CompanionClickTracking")
620          );
621          creative.variations.push(companionAd);
622        });
623  
624      return creative;
625    }
626  
627    var CreativeLinear = (function (_Creative) {
628      inherits(CreativeLinear, _Creative);
629  
630      function CreativeLinear() {
631        var creativeAttributes =
632          arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
633        classCallCheck(this, CreativeLinear);
634  
635        var _this = possibleConstructorReturn(
636          this,
637          (
638            CreativeLinear.__proto__ || Object.getPrototypeOf(CreativeLinear)
639          ).call(this, creativeAttributes)
640        );
641  
642        _this.type = "linear";
643        _this.duration = 0;
644        _this.skipDelay = null;
645        _this.mediaFiles = [];
646        _this.videoClickThroughURLTemplate = null;
647        _this.videoClickTrackingURLTemplates = [];
648        _this.videoCustomClickURLTemplates = [];
649        _this.adParameters = null;
650        _this.icons = [];
651        return _this;
652      }
653  
654      return CreativeLinear;
655    })(Creative);
656  
657    var Icon = function Icon() {
658      classCallCheck(this, Icon);
659  
660      this.program = null;
661      this.height = 0;
662      this.width = 0;
663      this.xPosition = 0;
664      this.yPosition = 0;
665      this.apiFramework = null;
666      this.offset = null;
667      this.duration = 0;
668      this.type = null;
669      this.staticResource = null;
670      this.htmlResource = null;
671      this.iframeResource = null;
672      this.iconClickThroughURLTemplate = null;
673      this.iconClickTrackingURLTemplates = [];
674      this.iconViewTrackingURLTemplate = null;
675    };
676  
677    var MediaFile = function MediaFile() {
678      classCallCheck(this, MediaFile);
679  
680      this.id = null;
681      this.fileURL = null;
682      this.deliveryType = "progressive";
683      this.mimeType = null;
684      this.codec = null;
685      this.bitrate = 0;
686      this.minBitrate = 0;
687      this.maxBitrate = 0;
688      this.width = 0;
689      this.height = 0;
690      this.apiFramework = null;
691      this.scalable = null;
692      this.maintainAspectRatio = null;
693    };
694  
695    /**
696     * This module provides methods to parse a VAST Linear Element.
697     */
698  
699    /**
700     * Parses a Linear element.
701     * @param  {Object} creativeElement - The VAST Linear element to parse.
702     * @param  {any} creativeAttributes - The attributes of the Linear (optional).
703     * @return {CreativeLinear}
704     */
705    function parseCreativeLinear(creativeElement, creativeAttributes) {
706      var offset = void 0;
707      var creative = new CreativeLinear(creativeAttributes);
708  
709      creative.duration = parserUtils.parseDuration(
710        parserUtils.parseNodeText(
711          parserUtils.childByName(creativeElement, "Duration")
712        )
713      );
714      var skipOffset = creativeElement.getAttribute("skipoffset");
715  
716      if (typeof skipOffset === "undefined" || skipOffset === null) {
717        creative.skipDelay = null;
718      } else if (
719        skipOffset.charAt(skipOffset.length - 1) === "%" &&
720        creative.duration !== -1
721      ) {
722        var percent = parseInt(skipOffset, 10);
723        creative.skipDelay = creative.duration * (percent / 100);
724      } else {
725        creative.skipDelay = parserUtils.parseDuration(skipOffset);
726      }
727  
728      var videoClicksElement = parserUtils.childByName(
729        creativeElement,
730        "VideoClicks"
731      );
732      if (videoClicksElement) {
733        creative.videoClickThroughURLTemplate = parserUtils.parseNodeText(
734          parserUtils.childByName(videoClicksElement, "ClickThrough")
735        );
736  
737        parserUtils
738          .childrenByName(videoClicksElement, "ClickTracking")
739          .forEach(function (clickTrackingElement) {
740            creative.videoClickTrackingURLTemplates.push(
741              parserUtils.parseNodeText(clickTrackingElement)
742            );
743          });
744  
745        parserUtils
746          .childrenByName(videoClicksElement, "CustomClick")
747          .forEach(function (customClickElement) {
748            creative.videoCustomClickURLTemplates.push(
749              parserUtils.parseNodeText(customClickElement)
750            );
751          });
752      }
753  
754      var adParamsElement = parserUtils.childByName(
755        creativeElement,
756        "AdParameters"
757      );
758      if (adParamsElement) {
759        creative.adParameters = parserUtils.parseNodeText(adParamsElement);
760      }
761  
762      parserUtils
763        .childrenByName(creativeElement, "TrackingEvents")
764        .forEach(function (trackingEventsElement) {
765          parserUtils
766            .childrenByName(trackingEventsElement, "Tracking")
767            .forEach(function (trackingElement) {
768              var eventName = trackingElement.getAttribute("event");
769              var trackingURLTemplate = parserUtils.parseNodeText(
770                trackingElement
771              );
772              if (eventName && trackingURLTemplate) {
773                if (eventName === "progress") {
774                  offset = trackingElement.getAttribute("offset");
775                  if (!offset) {
776                    return;
777                  }
778                  if (offset.charAt(offset.length - 1) === "%") {
779                    eventName = "progress-" + offset;
780                  } else {
781                    eventName =
782                      "progress-" + Math.round(parserUtils.parseDuration(offset));
783                  }
784                }
785  
786                if (!Array.isArray(creative.trackingEvents[eventName])) {
787                  creative.trackingEvents[eventName] = [];
788                }
789                creative.trackingEvents[eventName].push(trackingURLTemplate);
790              }
791            });
792        });
793  
794      parserUtils
795        .childrenByName(creativeElement, "MediaFiles")
796        .forEach(function (mediaFilesElement) {
797          parserUtils
798            .childrenByName(mediaFilesElement, "MediaFile")
799            .forEach(function (mediaFileElement) {
800              var mediaFile = new MediaFile();
801              mediaFile.id = mediaFileElement.getAttribute("id");
802              mediaFile.fileURL = parserUtils.parseNodeText(mediaFileElement);
803              mediaFile.deliveryType = mediaFileElement.getAttribute("delivery");
804              mediaFile.codec = mediaFileElement.getAttribute("codec");
805              mediaFile.mimeType = mediaFileElement.getAttribute("type");
806              mediaFile.apiFramework = mediaFileElement.getAttribute(
807                "apiFramework"
808              );
809              mediaFile.bitrate = parseInt(
810                mediaFileElement.getAttribute("bitrate") || 0
811              );
812              mediaFile.minBitrate = parseInt(
813                mediaFileElement.getAttribute("minBitrate") || 0
814              );
815              mediaFile.maxBitrate = parseInt(
816                mediaFileElement.getAttribute("maxBitrate") || 0
817              );
818              mediaFile.width = parseInt(
819                mediaFileElement.getAttribute("width") || 0
820              );
821              mediaFile.height = parseInt(
822                mediaFileElement.getAttribute("height") || 0
823              );
824  
825              var scalable = mediaFileElement.getAttribute("scalable");
826              if (scalable && typeof scalable === "string") {
827                scalable = scalable.toLowerCase();
828                if (scalable === "true") {
829                  mediaFile.scalable = true;
830                }
830 else if (scalable === "false") {
831                  mediaFile.scalable = false;
832                }
833              }
834  
835              var maintainAspectRatio = mediaFileElement.getAttribute(
836                "maintainAspectRatio"
837              );
838              if (
839                maintainAspectRatio &&
840                typeof maintainAspectRatio === "string"
841              ) {
842                maintainAspectRatio = maintainAspectRatio.toLowerCase();
843                if (maintainAspectRatio === "true") {
844                  mediaFile.maintainAspectRatio = true;
845                } else if (maintainAspectRatio === "false") {
846                  mediaFile.maintainAspectRatio = false;
847                }
848              }
849  
850              creative.mediaFiles.push(mediaFile);
851            });
852        });
853  
854      var iconsElement = parserUtils.childByName(creativeElement, "Icons");
855      if (iconsElement) {
856        parserUtils
857          .childrenByName(iconsElement, "Icon")
858          .forEach(function (iconElement) {
859            var icon = new Icon();
860            icon.program = iconElement.getAttribute("program");
861            icon.height = parseInt(iconElement.getAttribute("height") || 0);
862            icon.width = parseInt(iconElement.getAttribute("width") || 0);
863            icon.xPosition = parseXPosition(
864              iconElement.getAttribute("xPosition")
865            );
866            icon.yPosition = parseYPosition(
867              iconElement.getAttribute("yPosition")
868            );
869            icon.apiFramework = iconElement.getAttribute("apiFramework");
870            icon.offset = parserUtils.parseDuration(
871              iconElement.getAttribute("offset")
872            );
873            icon.duration = parserUtils.parseDuration(
874              iconElement.getAttribute("duration")
875            );
876  
877            parserUtils
878              .childrenByName(iconElement, "HTMLResource")
879              .forEach(function (htmlElement) {
880                icon.type =
881                  htmlElement.getAttribute("creativeType") || "text/html";
882                icon.htmlResource = parserUtils.parseNodeText(htmlElement);
883              });
884  
885            parserUtils
886              .childrenByName(iconElement, "IFrameResource")
887              .forEach(function (iframeElement) {
888                icon.type = iframeElement.getAttribute("creativeType") || 0;
889                icon.iframeResource = parserUtils.parseNodeText(iframeElement);
890              });
891  
892            parserUtils
893              .childrenByName(iconElement, "StaticResource")
894              .forEach(function (staticElement) {
895                icon.type = staticElement.getAttribute("creativeType") || 0;
896                icon.staticResource = parserUtils.parseNodeText(staticElement);
897              });
898  
899            var iconClicksElement = parserUtils.childByName(
900              iconElement,
901              "IconClicks"
902            );
903            if (iconClicksElement) {
904              icon.iconClickThroughURLTemplate = parserUtils.parseNodeText(
905                parserUtils.childByName(iconClicksElement, "IconClickThrough")
906              );
907              parserUtils
908                .childrenByName(iconClicksElement, "IconClickTracking")
909                .forEach(function (iconClickTrackingElement) {
910                  icon.iconClickTrackingURLTemplates.push(
911                    parserUtils.parseNodeText(iconClickTrackingElement)
912                  );
913                });
914            }
915  
916            icon.iconViewTrackingURLTemplate = parserUtils.parseNodeText(
917              parserUtils.childByName(iconElement, "IconViewTracking")
918            );
919  
920            creative.icons.push(icon);
921          });
922      }
923  
924      return creative;
925    }
926  
927    /**
928     * Parses an horizontal position into a String ('left' or 'right') or into a Number.
929     * @param  {String} xPosition - The x position to parse.
930     * @return {String|Number}
931     */
932    function parseXPosition(xPosition) {
933      if (["left", "right"].indexOf(xPosition) !== -1) {
934        return xPosition;
935      }
936  
937      return parseInt(xPosition || 0);
938    }
939  
940    /**
941     * Parses an vertical position into a String ('top' or 'bottom') or into a Number.
942     * @param  {String} yPosition - The x position to parse.
943     * @return {String|Number}
944     */
945    function parseYPosition(yPosition) {
946      if (["top", "bottom"].indexOf(yPosition) !== -1) {
947        return yPosition;
948      }
949  
950      return parseInt(yPosition || 0);
951    }
952  
953    var CreativeNonLinear = (function (_Creative) {
954      inherits(CreativeNonLinear, _Creative);
955  
956      function CreativeNonLinear() {
957        var creativeAttributes =
958          arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
959        classCallCheck(this, CreativeNonLinear);
960  
961        var _this = possibleConstructorReturn(
962          this,
963          (
964            CreativeNonLinear.__proto__ ||
965            Object.getPrototypeOf(CreativeNonLinear)
966          ).call(this, creativeAttributes)
967        );
968  
969        _this.type = "nonlinear";
970        _this.variations = [];
971        return _this;
972      }
973  
974      return CreativeNonLinear;
975    })(Creative);
976  
977    var NonLinearAd = function NonLinearAd() {
978      classCallCheck(this, NonLinearAd);
979  
980      this.id = null;
981      this.width = 0;
982      this.height = 0;
983      this.expandedWidth = 0;
984      this.expandedHeight = 0;
985      this.scalable = true;
986      this.maintainAspectRatio = true;
987      this.minSuggestedDuration = 0;
988      this.apiFramework = "static";
989      this.type = null;
990      this.staticResource = null;
991      this.htmlResource = null;
992      this.iframeResource = null;
993      this.nonlinearClickThroughURLTemplate = null;
994      this.nonlinearClickTrackingURLTemplates = [];
995      this.adParameters = null;
996    };
997  
998    /**
999     * This module provides methods to parse a VAST NonLinear Element.
1000     */
1001  
1002    /**
1003     * Parses a NonLinear element.
1004     * @param  {any} creativeElement - The VAST NonLinear element to parse.
1005     * @param  {any} creativeAttributes - The attributes of the NonLinear (optional).
1006     * @return {CreativeNonLinear}
1007     */
1008    function parseCreativeNonLinear(creativeElement, creativeAttributes) {
1009      var creative = new CreativeNonLinear(creativeAttributes);
1010  
1011      parserUtils
1012        .childrenByName(creativeElement, "TrackingEvents")
1013        .forEach(function (trackingEventsElement) {
1014          var eventName = void 0,
1015            trackingURLTemplate = void 0;
1016          parserUtils
1017            .childrenByName(trackingEventsElement, "Tracking")
1018            .forEach(function (trackingElement) {
1019              eventName = trackingElement.getAttribute("event");
1020              trackingURLTemplate = parserUtils.parseNodeText(trackingElement);
1021  
1022              if (eventName && trackingURLTemplate) {
1023                if (!Array.isArray(creative.trackingEvents[eventName])) {
1024                  creative.trackingEvents[eventName] = [];
1025                }
1026                creative.trackingEvents[eventName].push(trackingURLTemplate);
1027              }
1028            });
1029        });
1030  
1031      parserUtils
1032        .childrenByName(creativeElement, "NonLinear")
1033        .forEach(function (nonlinearResource) {
1034          var nonlinearAd = new NonLinearAd();
1035          nonlinearAd.id = nonlinearResource.getAttribute("id") || null;
1036          nonlinearAd.width = nonlinearResource.getAttribute("width");
1037          nonlinearAd.height = nonlinearResource.getAttribute("height");
1038          nonlinearAd.expandedWidth = nonlinearResource.getAttribute(
1039            "expandedWidth"
1040          );
1041          nonlinearAd.expandedHeight = nonlinearResource.getAttribute(
1042            "expandedHeight"
1043          );
1044          nonlinearAd.scalable = parserUtils.parseBoolean(
1045            nonlinearResource.getAttribute("scalable")
1046          );
1047          nonlinearAd.maintainAspectRatio = parserUtils.parseBoolean(
1048            nonlinearResource.getAttribute("maintainAspectRatio")
1049          );
1050          nonlinearAd.minSuggestedDuration = parserUtils.parseDuration(
1051            nonlinearResource.getAttribute("minSuggestedDuration")
1052          );
1053          nonlinearAd.apiFramework = nonlinearResource.getAttribute(
1054            "apiFramework"
1055          );
1056  
1057          parserUtils
1058            .childrenByName(nonlinearResource, "HTMLResource")
1059            .forEach(function (htmlElement) {
1060              nonlinearAd.type =
1061                htmlElement.getAttribute("creativeType") || "text/html";
1062              nonlinearAd.htmlResource = parserUtils.parseNodeText(htmlElement);
1063            });
1064  
1065          parserUtils
1066            .childrenByName(nonlinearResource, "IFrameResource")
1067            .forEach(function (iframeElement) {
1068              nonlinearAd.type = iframeElement.getAttribute("creativeType") || 0;
1069              nonlinearAd.iframeResource = parserUtils.parseNodeText(
1070                iframeElement
1071              );
1072            });
1073  
1074          parserUtils
1075            .childrenByName(nonlinearResource, "StaticResource")
1076            .forEach(function (staticElement) {
1077              nonlinearAd.type = staticElement.getAttribute("creativeType") || 0;
1078              nonlinearAd.staticResource = parserUtils.parseNodeText(
1079                staticElement
1080              );
1081            });
1082  
1083          var adParamsElement = parserUtils.childByName(
1084            nonlinearResource,
1085            "AdParameters"
1086          );
1087          if (adParamsElement) {
1088            nonlinearAd.adParameters = parserUtils.parseNodeText(adParamsElement);
1089          }
1090  
1091          nonlinearAd.nonlinearClickThroughURLTemplate = parserUtils.parseNodeText(
1092            parserUtils.childByName(nonlinearResource, "NonLinearClickThrough")
1093          );
1094          parserUtils
1095            .childrenByName(nonlinearResource, "NonLinearClickTracking")
1096            .forEach(function (clickTrackingElement) {
1097              nonlinearAd.nonlinearClickTrackingURLTemplates.push(
1098                parserUtils.parseNodeText(clickTrackingElement)
1099              );
1100            });
1101  
1102          creative.variations.push(nonlinearAd);
1103        });
1104  
1105      return creative;
1106    }
1107  
1108    /**
1109     * This module provides methods to parse a VAST Ad Element.
1110     */
1111  
1112    /**
1113     * Parses an Ad element (can either be a Wrapper or an InLine).
1114     * @param  {Object} adElement - The VAST Ad element to parse.
1115     * @return {Ad}
1116     */
1117    function parseAd(adElement) {
1118      var childNodes = adElement.childNodes;
1119  
1120      for (var adTypeElementKey in childNodes) {
1121        var adTypeElement = childNodes[adTypeElementKey];
1122  
1123        if (["Wrapper", "InLine"].indexOf(adTypeElement.nodeName) === -1) {
1124          continue;
1125        }
1126  
1127        parserUtils.copyNodeAttribute("id", adElement, adTypeElement);
1128        parserUtils.copyNodeAttribute("sequence", adElement, adTypeElement);
1129  
1130        if (adTypeElement.nodeName === "Wrapper") {
1131          return parseWrapper(adTypeElement);
1132        } else if (adTypeElement.nodeName === "InLine") {
1133          return parseInLine(adTypeElement);
1134        }
1135      }
1136    }
1137  
1138    /**
1139     * Parses an Inline element.
1140     * @param  {Object} inLineElement - The VAST Inline element to parse.
1141     * @return {Ad}
1142     */
1143    function parseInLine(inLineElement) {
1144      var childNodes = inLineElement.childNodes;
1145      var ad = new Ad();
1146      ad.id = inLineElement.getAttribute("id") || null;
1147      ad.sequence = inLineElement.getAttribute("sequence") || null;
1148  
1149      for (var nodeKey in childNodes) {
1150        var node = childNodes[nodeKey];
1151  
1152        switch (node.nodeName) {
1153          case "Error":
1154            ad.errorURLTemplates.push(parserUtils.parseNodeText(node));
1155            break;
1156  
1157          case "Impression":
1158            ad.impressionURLTemplates.push(parserUtils.parseNodeText(node));
1159            break;
1160  
1161          case "Creatives":
1162            parserUtils
1163              .childrenByName(node, "Creative")
1164              .forEach(function (creativeElement) {
1165                var creativeAttributes = {
1166                  id: creativeElement.getAttribute("id") || null,
1167                  adId: parseCreativeAdIdAttribute(creativeElement),
1168                  sequence: creativeElement.getAttribute("sequence") || null,
1169                  apiFramework:
1170                    creativeElement.getAttribute("apiFramework") || null,
1171                };
1172  
1173                for (var creativeTypeElementKey in creativeElement.childNodes) {
1174                  var creativeTypeElement =
1175                    creativeElement.childNodes[creativeTypeElementKey];
1176                  var parsedCreative = void 0;
1177  
1178                  switch (creativeTypeElement.nodeName) {
1179                    case "Linear":
1180                      parsedCreative = parseCreativeLinear(
1181                        creativeTypeElement,
1182                        creativeAttributes
1183                      );
1184                      if (parsedCreative) {
1185                        ad.creatives.push(parsedCreative);
1186                      }
1187                      break;
1188                    case "NonLinearAds":
1189                      parsedCreative = parseCreativeNonLinear(
1190                        creativeTypeElement,
1191                        creativeAttributes
1192                      );
1193                      if (parsedCreative) {
1194                        ad.creatives.push(parsedCreative);
1195                      }
1196                      break;
1197                    case "CompanionAds":
1198                      parsedCreative = parseCreativeCompanion(
1199                        creativeTypeElement,
1200                        creativeAttributes
1201                      );
1202                      if (parsedCreative) {
1203                        ad.creatives.push(parsedCreative);
1204                      }
1205                      break;
1206                  }
1207                }
1208              });
1209            break;
1210  
1211          case "Extensions":
1212            parseExtensions(
1213              ad.extensions,
1214              parserUtils.childrenByName(node, "Extension")
1215            );
1216            break;
1217  
1218          case "AdSystem":
1219            ad.system = {
1220              value: parserUtils.parseNodeText(node),
1221              version: node.getAttribute("version") || null,
1222            };
1223            break;
1224  
1225          case "AdTitle":
1226            ad.title = parserUtils.parseNodeText(node);
1227            break;
1228  
1229          case "Description":
1230            ad.description = parserUtils.parseNodeText(node);
1231            break;
1232  
1233          case "Advertiser":
1234            ad.advertiser = parserUtils.parseNodeText(node);
1235            break;
1236  
1237          case "Pricing":
1238            ad.pricing = {
1239              value: parserUtils.parseNodeText(node),
1240              model: node.getAttribute("model") || null,
1241              currency: node.getAttribute("currency") || null,
1242            };
1243            break;
1244  
1245          case "Survey":
1246            ad.survey = parserUtils.parseNodeText(node);
1247            break;
1248        }
1249      }
1250  
1251      return ad;
1252    }
1253  
1254    /**
1255     * Parses a Wrapper element without resolving the wrapped urls.
1256     * @param  {Object} wrapperElement - The VAST Wrapper element to be parsed.
1257     * @return {Ad}
1258     */
1259    function parseWrapper(wrapperElement) {
1260      var ad = parseInLine(wrapperElement);
1261      var wrapperURLElement = parserUtils.childByName(
1262        wrapperElement,
1263        "VASTAdTagURI"
1264      );
1265  
1266      if (wrapperURLElement) {
1267        ad.nextWrapperURL = parserUtils.parseNodeText(wrapperURLElement);
1268      } else {
1269        wrapperURLElement = parserUtils.childByName(
1270          wrapperElement,
1271          "VASTAdTagURL"
1272        );
1273  
1274        if (wrapperURLElement) {
1275          ad.nextWrapperURL = parserUtils.parseNodeText(
1276            parserUtils.childByName(wrapperURLElement, "URL")
1277          );
1278        }
1279      }
1280  
1281      ad.creatives.forEach(function (wrapperCreativeElement) {
1282        if (["linear", "nonlinear"].indexOf(wrapperCreativeElement.type) !== -1) {
1283          // TrackingEvents Linear / NonLinear
1284          if (wrapperCreativeElement.trackingEvents) {
1285            if (!ad.trackingEvents) {
1286              ad.trackingEvents = {};
1287            }
1288            if (!ad.trackingEvents[wrapperCreativeElement.type]) {
1289              ad.trackingEvents[wrapperCreativeElement.type] = {};
1290            }
1291  
1292            var _loop = function _loop(eventName) {
1293              var urls = wrapperCreativeElement.trackingEvents[eventName];
1294              if (
1295                !Array.isArray(
1296                  ad.trackingEvents[wrapperCreativeElement.type][eventName]
1297                )
1298              ) {
1299                ad.trackingEvents[wrapperCreativeElement.type][eventName] = [];
1300              }
1301              urls.forEach(function (url) {
1302                ad.trackingEvents[wrapperCreativeElement.type][eventName].push(
1303                  url
1304                );
1305              });
1306            };
1307  
1308            for (var eventName in wrapperCreativeElement.trackingEvents) {
1309              _loop(eventName);
1310            }
1311          }
1312          // ClickTracking
1313          if (wrapperCreativeElement.videoClickTrackingURLTemplates) {
1314            if (!Array.isArray(ad.videoClickTrackingURLTemplates)) {
1315              ad.videoClickTrackingURLTemplates = [];
1316            } // tmp property to save wrapper tracking URLs until they are merged
1317            wrapperCreativeElement.videoClickTrackingURLTemplates.forEach(
1318              function (item) {
1319                ad.videoClickTrackingURLTemplates.push(item);
1320              }
1321            );
1322          }
1323          // ClickThrough
1324          if (wrapperCreativeElement.videoClickThroughURLTemplate) {
1325            ad.videoClickThroughURLTemplate =
1326              wrapperCreativeElement.videoClickThroughURLTemplate;
1327          }
1328          // CustomClick
1329          if (wrapperCreativeElement.videoCustomClickURLTemplates) {
1330            if (!Array.isArray(ad.videoCustomClickURLTemplates)) {
1331              ad.videoCustomClickURLTemplates = [];
1332            } // tmp property to save wrapper tracking URLs until they are merged
1333            wrapperCreativeElement.videoCustomClickURLTemplates.forEac
1333h(function (
1334              item
1335            ) {
1336              ad.videoCustomClickURLTemplates.push(item);
1337            });
1338          }
1339        }
1340      });
1341  
1342      if (ad.nextWrapperURL) {
1343        return ad;
1344      }
1345    }
1346  
1347    /**
1348     * Parses an array of Extension elements.
1349     * @param  {Array} collection - The array used to store the parsed extensions.
1350     * @param  {Array} extensions - The array of extensions to parse.
1351     */
1352    function parseExtensions(collection, extensions) {
1353      extensions.forEach(function (extNode) {
1354        var ext = new AdExtension();
1355        var extNodeAttrs = extNode.attributes;
1356        var childNodes = extNode.childNodes;
1357  
1358        if (extNode.attributes) {
1359          for (var extNodeAttrKey in extNodeAttrs) {
1360            var extNodeAttr = extNodeAttrs[extNodeAttrKey];
1361  
1362            if (extNodeAttr.nodeName && extNodeAttr.nodeValue) {
1363              ext.attributes[extNodeAttr.nodeName] = extNodeAttr.nodeValue;
1364            }
1365          }
1366        }
1367  
1368        for (var childNodeKey in childNodes) {
1369          var childNode = childNodes[childNodeKey];
1370          var txt = parserUtils.parseNodeText(childNode);
1371  
1372          // ignore comments / empty value
1373          if (childNode.nodeName !== "#comment" && txt !== "") {
1374            var extChild = new AdExtensionChild();
1375            extChild.name = childNode.nodeName;
1376            extChild.value = txt;
1377  
1378            if (childNode.attributes) {
1379              var childNodeAttributes = childNode.attributes;
1380  
1381              for (var extChildNodeAttrKey in childNodeAttributes) {
1382                var extChildNodeAttr = childNodeAttributes[extChildNodeAttrKey];
1383  
1384                extChild.attributes[extChildNodeAttr.nodeName] =
1385                  extChildNodeAttr.nodeValue;
1386              }
1387            }
1388  
1389            ext.children.push(extChild);
1390          }
1391        }
1392  
1393        collection.push(ext);
1394      });
1395    }
1396  
1397    /**
1398     * Parses the creative adId Attribute.
1399     * @param  {any} creativeElement - The creative element to retrieve the adId from.
1400     * @return {String|null}
1401     */
1402    function parseCreativeAdIdAttribute(creativeElement) {
1403      return (
1404        creativeElement.getAttribute("AdID") || // VAST 2 spec
1405        creativeElement.getAttribute("adID") || // VAST 3 spec
1406        creativeElement.getAttribute("adId") || // VAST 4 spec
1407        null
1408      );
1409    }
1410  
1411    var domain;
1412  
1413    // This constructor is used to store event handlers. Instantiating this is
1414    // faster than explicitly calling `Object.create(null)` to get a "clean" empty
1415    // object (tested with v8 v4.9).
1416    function EventHandlers() {}
1417    EventHandlers.prototype = Object.create(null);
1418  
1419    function EventEmitter() {
1420      EventEmitter.init.call(this);
1421    }
1422  
1423    // nodejs oddity
1424    // require('events') === require('events').EventEmitter
1425    EventEmitter.EventEmitter = EventEmitter;
1426  
1427    EventEmitter.usingDomains = false;
1428  
1429    EventEmitter.prototype.domain = undefined;
1430    EventEmitter.prototype._events = undefined;
1431    EventEmitter.prototype._maxListeners = undefined;
1432  
1433    // By default EventEmitters will print a warning if more than 10 listeners are
1434    // added to it. This is a useful default which helps finding memory leaks.
1435    EventEmitter.defaultMaxListeners = 10;
1436  
1437    EventEmitter.init = function () {
1438      this.domain = null;
1439      if (EventEmitter.usingDomains) {
1440        // if there is an active domain, then attach to it.
1441        if (domain.active && !(this instanceof domain.Domain)) {
1442          this.domain = domain.active;
1443        }
1444      }
1445  
1446      if (!this._events || this._events === Object.getPrototypeOf(this)._events) {
1447        this._events = new EventHandlers();
1448        this._eventsCount = 0;
1449      }
1450  
1451      this._maxListeners = this._maxListeners || undefined;
1452    };
1453  
1454    // Obviously not all Emitters should be limited to 10. This function allows
1455    // that to be increased. Set to zero for unlimited.
1456    EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) {
1457      if (typeof n !== "number" || n < 0 || isNaN(n))
1458        throw new TypeError('"n" argument must be a positive number');
1459      this._maxListeners = n;
1460      return this;
1461    };
1462  
1463    function $getMaxListeners(that) {
1464      if (that._maxListeners === undefined)
1465        return EventEmitter.defaultMaxListeners;
1466      return that._maxListeners;
1467    }
1468  
1469    EventEmitter.prototype.getMaxListeners = function getMaxListeners() {
1470      return $getMaxListeners(this);
1471    };
1472  
1473    // These standalone emit* functions are used to optimize calling of event
1474    // handlers for fast cases because emit() itself often has a variable number of
1475    // arguments and can be deoptimized because of that. These functions always have
1476    // the same number of arguments and thus do not get deoptimized, so the code
1477    // inside them can execute faster.
1478    function emitNone(handler, isFn, self) {
1479      if (isFn) handler.call(self);
1480      else {
1481        var len = handler.length;
1482        var listeners = arrayClone(handler, len);
1483        for (var i = 0; i < len; ++i) listeners[i].call(self);
1484      }
1485    }
1486    function emitOne(handler, isFn, self, arg1) {
1487      if (isFn) handler.call(self, arg1);
1488      else {
1489        var len = handler.length;
1490        var listeners = arrayClone(handler, len);
1491        for (var i = 0; i < len; ++i) listeners[i].call(self, arg1);
1492      }
1493    }
1494    function emitTwo(handler, isFn, self, arg1, arg2) {
1495      if (isFn) handler.call(self, arg1, arg2);
1496      else {
1497        var len = handler.length;
1498        var listeners = arrayClone(handler, len);
1499        for (var i = 0; i < len; ++i) listeners[i].call(self, arg1, arg2);
1500      }
1501    }
1502    function emitThree(handler, isFn, self, arg1, arg2, arg3) {
1503      if (isFn) handler.call(self, arg1, arg2, arg3);
1504      else {
1505        var len = handler.length;
1506        var listeners = arrayClone(handler, len);
1507        for (var i = 0; i < len; ++i) listeners[i].call(self, arg1, arg2, arg3);
1508      }
1509    }
1510  
1511    function emitMany(handler, isFn, self, args) {
1512      if (isFn) handler.apply(self, args);
1513      else {
1514        var len = handler.length;
1515        var listeners = arrayClone(handler, len);
1516        for (var i = 0; i < len; ++i) listeners[i].apply(self, args);
1517      }
1518    }
1519  
1520    EventEmitter.prototype.emit = function emit(type) {
1521      var er, handler, len, args, i, events, domain;
1522      var needDomainExit = false;
1523      var doError = type === "error";
1524  
1525      events = this._events;
1526      if (events) doError = doError && events.error == null;
1527      else if (!doError) return false;
1528  
1529      domain = this.domain;
1530  
1531      // If there is no 'error' event listener then throw.
1532      if (doError) {
1533        er = arguments[1];
1534        if (domain) {
1535          if (!er) er = new Error('Uncaught, unspecified "error" event');
1536          er.domainEmitter = this;
1537          er.domain = domain;
1538          er.domainThrown = false;
1539          domain.emit("error", er);
1540        } else if (er instanceof Error) {
1541          throw er; // Unhandled 'error' event
1542        } else {
1543          // At least give some kind of context to the user
1544          var err = new Error(
1545            'Uncaught, unspecified "error" event. (' + er + ")"
1546          );
1547          err.context = er;
1548          throw err;
1549        }
1550        return false;
1551      }
1552  
1553      handler = events[type];
1554  
1555      if (!handler) return false;
1556  
1557      var isFn = typeof handler === "function";
1558      len = arguments.length;
1559      switch (len) {
1560        // fast cases
1561        case 1:
1562          emitNone(handler, isFn, this);
1563          break;
1564        case 2:
1565          emitOne(handler, isFn, this, arguments[1]);
1566          break;
1567        case 3:
1568          emitTwo(handler, isFn, this, arguments[1], arguments[2]);
1569          break;
1570        case 4:
1571          emitThree(
1572            handler,
1573            isFn,
1574            this,
1575            arguments[1],
1576            arguments[2],
1577            arguments[3]
1578          );
1579          break;
1580        // slower
1581        default:
1582          args = new Array(len - 1);
1583          for (i = 1; i < len; i++) args[i - 1] = arguments[i];
1584          emitMany(handler, isFn, this, args);
1585      }
1586  
1587      if (needDomainExit) domain.exit();
1588  
1589      return true;
1590    };
1591  
1592    function _addListener(target, type, listener, prepend) {
1593      var m;
1594      var events;
1595      var existing;
1596  
1597      if (typeof listener !== "function")
1598        throw new TypeError('"listener" argument must be a function');
1599  
1600      events = target._events;
1601      if (!events) {
1602        events = target._events = new EventHandlers();
1603        target._eventsCount = 0;
1604      } else {
1605        // To avoid recursion in the case that type === "newListener"! Before
1606        // adding it to the listeners, first emit "newListener".
1607        if (events.newListener) {
1608          target.emit(
1609            "newListener",
1610            type,
1611            listener.listener ? listener.listener : listener
1612          );
1613  
1614          // Re-assign `events` because a newListener handler could have caused the
1615          // this._events to be assigned to a new object
1616          events = target._events;
1617        }
1618        existing = events[type];
1619      }
1620  
1621      if (!existing) {
1622        // Optimize the case of one listener. Don't need the extra array object.
1623        existing = events[type] = listener;
1624        ++target._eventsCount;
1625      } else {
1626        if (typeof existing === "function") {
1627          // Adding the second element, need to change to array.
1628          existing = events[type] = prepend
1629            ? [listener, existing]
1630            : [existing, listener];
1631        } else {
1632          // If we've already got an array, just append.
1633          if (prepend) {
1634            existing.unshift(listener);
1635          } else {
1636            existing.push(listener);
1637          }
1638        }
1639  
1640        // Check for listener leak
1641        if (!existing.warned) {
1642          m = $getMaxListeners(target);
1643          if (m && m > 0 && existing.length > m) {
1644            existing.warned = true;
1645            var w = new Error(
1646              "Possible EventEmitter memory leak detected. " +
1647                existing.length +
1648                " " +
1649                type +
1650                " listeners added. " +
1651                "Use emitter.setMaxListeners() to increase limit"
1652            );
1653            w.name = "MaxListenersExceededWarning";
1654            w.emitter = target;
1655            w.type = type;
1656            w.count = existing.length;
1657            emitWarning(w);
1658          }
1659        }
1660      }
1661  
1662      return target;
1663    }
1664    function emitWarning(e) {
1665      typeof console.warn === "function" ? console.warn(e) : console.log(e);
1666    }
1667    EventEmitter.prototype.addListener = function addListener(type, listener) {
1668      return _addListener(this, type, listener, false);
1669    };
1670  
1671    EventEmitter.prototype.on = EventEmitter.prototype.addListener;
1672  
1673    EventEmitter.prototype.prependListener = function prependListener(
1674      type,
1675      listener
1676    ) {
1677      return _addListener(this, type, listener, true);
1678    };
1679  
1680    function _onceWrap(target, type, listener) {
1681      var fired = false;
1682      function g() {
1683        target.removeListener(type, g);
1684        if (!fired) {
1685          fired = true;
1686          listener.apply(target, arguments);
1687        }
1688      }
1689      g.listener = listener;
1690      return g;
1691    }
1692  
1693    EventEmitter.prototype.once = function once(type, listener) {
1694      if (typeof listener !== "function")
1695        throw new TypeError('"listener" argument must be a function');
1696      this.on(type, _onceWrap(this, type, listener));
1697      return this;
1698    };
1699  
1700    EventEmitter.prototype.prependOnceListener = function prependOnceListener(
1701      type,
1702      listener
1703    ) {
1704      if (typeof listener !== "function")
1705        throw new TypeError('"listener" argument must be a function');
1706      this.prependListener(type, _onceWrap(this, type, listener));
1707      return this;
1708    };
1709  
1710    // emits a 'removeListener' event iff the listener was removed
1711    EventEmitter.prototype.removeListener = function removeListener(
1712      type,
1713      listener
1714    ) {
1715      var list, events, position, i, originalListener;
1716  
1717      if (typeof listener !== "function")
1718        throw new TypeError('"listener" argument must be a function');
1719  
1720      events = this._events;
1721      if (!events) return this;
1722  
1723      list = events[type];
1724      if (!list) return this;
1725  
1726      if (list === listener || (list.listener && list.listener === listener)) {
1727        if (--this._eventsCount === 0) this._events = new EventHandlers();
1728        else {
1729          delete events[type];
1730          if (events.removeListener)
1731            this.emit("removeListener", type, list.listener || listener);
1732        }
1733      } else if (typeof list !== "function") {
1734        position = -1;
1735  
1736        for (i = list.length; i-- > 0; ) {
1737          if (
1738            list[i] === listener ||
1739            (list[i].listener && list[i].listener === listener)
1740          ) {
1741            originalListener = list[i].listener;
1742            position = i;
1743            break;
1744          }
1745        }
1746  
1747        if (position < 0) return this;
1748  
1749        if (list.length === 1) {
1750          list[0] = undefined;
1751          if (--this._eventsCount === 0) {
1752            this._events = new EventHandlers();
1753            return this;
1754          } else {
1755            delete events[type];
1756          }
1757        } else {
1758          spliceOne(list, position);
1759        }
1760  
1761        if (events.removeListener)
1762          this.emit("removeListener", type, originalListener || listener);
1763      }
1764  
1765      return this;
1766    };
1767  
1768    EventEmitter.prototype.removeAllListeners = function removeAllListeners(
1769      type
1770    ) {
1771      var listeners, events;
1772  
1773      events = this._events;
1774      if (!events) return this;
1775  
1776      // not listening for removeListener, no need to emit
1777      if (!events.removeListener) {
1778        if (arguments.length === 0) {
1779          this._events = new EventHandlers();
1780          this._eventsCount = 0;
1781        } else if (events[type]) {
1782          if (--this._eventsCount === 0) this._events = new EventHandlers();
1783          else delete events[type];
1784        }
1785        return this;
1786      }
1787  
1788      // emit removeListener for all listeners on all events
1789      if (arguments.length === 0) {
1790        var keys = Object.keys(events);
1791        for (var i = 0, key; i < keys.length; ++i) {
1792          key = keys[i];
1793          if (key === "removeListener") continue;
1794          this.removeAllListeners(key);
1795        }
1796        this.removeAllListeners("removeListener");
1797        this._events = new EventHandlers();
1798        this._eventsCount = 0;
1799        return this;
1800      }
1801  
1802      listeners = events[type];
1803  
1804      if (typeof listeners === "function") {
1805        this.removeListener(type, listeners);
1806      } else if (listeners) {
1807        // LIFO order
1808        do {
1809          this.removeListener(type, listeners[listeners.length - 1]);
1810        } while (listeners[0]);
1811      }
1812  
1813      return this;
1814    };
1815  
1816    EventEmitter.prototype.listeners = function listeners(type) {
1817      var evlistener;
1818      var ret;
1819      var events = this._events;
1820  
1821      if (!events) ret = [];
1822      else {
1823        evlistener = events[type];
1824        if (!evlistener) ret = [];
1825        else if (typeof evlistener === "function")
1826          ret = [evlistener.listener || evlistener];
1827        else ret = unwrapListeners(evlistener);
1828      }
1829  
1830      return ret;
1831    };
1832  
1833    EventEmitter.listenerCount = function (emitter, type) {
1834      if (typeof emitter.listenerCount === "function") {
1835        return emitter.listenerCount(type);
1836      } else {
1837        return listenerCount.call(emitter, type);
1838      }
1839    };
1840  
1841    EventEmitter.prototype.listenerCount = listenerCount;
1842    function listenerCount(type) {
1843      var events = this._events;
1844  
1845      if (events) {
1846        var evlistener = events[type];
1847  
1848        if (typeof evlistener === "function") {
1849          return 1;
1850        } else if (evlistener) {
1851          return evlistener.length;
1852        }
1853      }
1854  
1855      return 0;
1856    }
1857  
1858    EventEmitter.prototype.eventNames = function eventNames() {
1859      return this._eventsCount > 0 ? Reflect.ownKeys(this._events) : [];
1860    };
1861  
1862    // About 1.5x faster than the two-arg version of Array#splice().
1863    function spliceOne(list, index) {
1864      for (var i = index, k = i + 1, n = list.length; k < n; i += 1, k += 1)
1865        list[i] = list[k];
1866      list.pop();
1867    }
1868  
1869    function arrayClone(arr, i) {
1870      var copy = new Array(i);
1871      while (i--) copy[i] = arr[i];
1872      return copy;
1873    }
1874  
1875    function unwrapListeners(arr) {
1876      var ret = new Array(arr.length);
1877      for (var i = 0; i < ret.length; ++i) {
1878        ret[i] = arr[i].listener || arr[i];
1879      }
1880      return ret;
1881    }
1882  
1883    function xdr() {
1884      var request = void 0;
1885      if (window.XDomainRequest) {
1886        // eslint-disable-next-line no-undef
1887        request = new XDomainRequest();
1888      }
1889      return request;
1890    }
1891  
1892    function supported() {
1893      return !!xdr();
1894    }
1895  
1896    function get$1(url, options, cb) {
1897      var xmlDocument =
1898        typeof window.ActiveXObject === "function"
1899          ? new window.ActiveXObject("Microsoft.XMLDOM")
1900          : undefined;
1901  
1902      if (xmlDocument) {
1903        xmlDocument.async = false;
1904      } else {
1905        return cb(
1906          new Error("FlashURLHandler: Microsoft.XMLDOM format not supported")
1907        );
1908      }
1909  
1910      var request = xdr();
1911      request.open("GET", url);
1912      request.timeout = options.timeout || 0;
1913      request.withCredentials = options.withCredentials || false;
1914      request.send();
1915      request.onprogress = function () {};
1916  
1917      request.onload = function () {
1918        xmlDocument.loadXML(request.responseText);
1919        cb(null, xmlDocument);
1920      };
1921    }
1922  
1923    var flashURLHandler = {
1924      get: get$1,
1925      supported: supported,
1926    };
1927  
1928    // This mock module is loaded in stead of the original NodeURLHandler module
1929    // when bundling the library for environments which are not node.
1930    // This allows us to avoid bundling useless node components and have a smaller build.
1931    function get$2(url, options, cb) {
1932      cb(
1933        new Error("Please bundle the library for node to use the node urlHandler")
1934      );
1935    }
1936  
1937    var nodeURLHandler = {
1938      get: get$2,
1939    };
1940  
1941    function xhr() {
1942      try {
1943        var request = new window.XMLHttpRequest();
1944        if ("withCredentials" in request) {
1945          // check CORS support
1946          return request;
1947        }
1948        return null;
1949      } catch (err) {
1950        return null;
1951      }
1952    }
1953  
1954    function supported$1() {
1955      return !!xhr();
1956    }
1957  
1958    function get$3(url, options, cb) {
1959      if (window.location.protocol === "https:" && url.indexOf("http://") === 0) {
1960        return cb(new Error("XHRURLHandler: Cannot go from HTTPS to HTTP."));
1961      }
1962  
1963      try {
1964        var request = xhr();
1965  
1966        request.open("GET", url);
1967        request.timeout = options.timeout || 0;
1968        request.withCredentials = options.withCredentials || false;
1969        request.overrideMimeType && request.overrideMimeType("text/xml");
1970        request.onreadystatechange = function () {
1971          if (request.readyState === 4) {
1972            if (request.status === 200) {
1973              cb(null, request.responseXML);
1974            } else {
1975              cb(new Error("XHRURLHandler: " + request.statusText));
1976            }
1977          }
1978        };
1979        request.send();
1980      } catch (error) {
1981        cb(new Error("XHRURLHandler: Unexpected error"));
1982      }
1983    }
1984  
1985    var XHRURLHandler = {
1986      get: get$3,
1987      supported: supported$1,
1988    };
1989  
1990    function get$4(url, options, cb) {
1991      // Allow skip of the options param
1992      if (!cb) {
1993        if (typeof options === "function") {
1994          cb = options;
1995        }
1996        options = {};
1997      }
1998  
1999      if (typeof window === "undefined" || window === null) {
2000        return nodeURLHandler.get(url, options, cb);
2001      } else if (XHRURLHandler.supported()) {
2002        return XHRURLHandler.get(url, options, cb);
2003      } else if (flashURLHandler.supported()) {
2004        return flashURLHandler.get(url, options, cb);
2005      }
2006      return cb(
2007        new Error(
2008          "Current context is not supported by any of the default URLHandlers. Please provide a custom URLHandler"
2009        )
2010      );
2011    }
2012  
2013    var urlHandler = {
2014      get: get$4,
2015    };
2016  
2017    var VASTResponse = function VASTResponse() {
2018      classCallCheck(this, VASTResponse);
2019  
2020      this.ads = [];
2021      this.errorURLTemplates = [];
2022      this.version = null;
2023    };
2024  
2025    var DEFAULT_MAX_WRAPPER_DEPTH = 10;
2026    var DEFAULT_EVENT_DATA = {
2027      ERRORCODE: 900,
2028      extensions: [],
2029    };
2030  
2031    /**
2032     * This class provides methods to fetch and parse a VAST document.
2033     * @export
2034     * @class VASTParser
2035     * @extends EventEmitter
2036     */
2037    var VASTParser = (function (_EventEmitter) {
2038      inherits(VASTParser, _EventEmitter);
2039  
2040      /**
2041       * Creates an instance of VASTParser.
2042       * @constructor
2043       */
2044      function VASTParser() {
2045        classCallCheck(this, VASTParser);
2046  
2047        var _this = possibleConstructorReturn(
2048          this,
2049          (VASTParser.__proto__ || Object.getPrototypeOf(VASTParser)).call(this)
2050        );
2051  
2052        _this.remainingAds = [];
2053        _this.parentURLs = [];
2054        _this.errorURLTemplates = [];
2055        _this.rootErrorURLTemplates = [];
2056        _this.maxWrapperDepth = null;
2057        _this.URLTemplateFilters = [];
2058        _this.fetchingOptions = {};
2059        return _this;
2060      }
2061  
2062      /**
2063       * Adds a filter function to the array of filters which are called before fetching a VAST document.
2064       * @param  {function} filter - The filter function to be added at the end of the array.
2065       * @return {void}
2066       */
2067  
2068      createClass(VASTParser, [
2069        {
2070          key: "addURLTemplateFilter",
2071          value: function addURLTemplateFilter(filter) {
2072            if (typeof filter === "function") {
2073              this.URLTemplateFilters.push(filter);
2074            }
2075          },
2076  
2077          /**
2078           * Removes the last element of the url templates filters array.
2079           * @return {void}
2080           */
2081        },
2082        {
2083          key: "removeURLTemplateFilter",
2084          value: function removeURLTemplateFilter() {
2085            this.URLTemplateFilters.pop();
2086          },
2087  
2088          /**
2089           * Returns the number of filters of the url templates filters array.
2090           * @return {Number}
2091           */
2092        },
2093        {
2094          key: "countURLTemplateFilters",
2095          value: function countURLTemplateFilters() {
2096            return this.URLTemplateFilters.length;
2097          },
2098  
2099          /**
2100           * Removes all the filter functions from the url templates filters array.
2101           * @return {void}
2102           */
2103        },
2104        {
2105          key: "clearURLTemplateFilters",
2106          value: function clearURLTemplateFilters() {
2107            this.URLTemplateFilters = [];
2108          },
2109  
2110          /**
2111           * Tracks the error provided in the errorCode parameter and emits a VAST-error event for the given error.
2112           * @param  {Array} urlTemplates - An Array of url templates to use to make the tracking call.
2113           * @param  {Object} errorCode - An Object containing the error data.
2114           * @param  {Object} data - One (or more) Object containing additional data.
2115           * @emits  VASTParser#VAST-error
2116           * @return {void}
2117           */
2118        },
2119        {
2120          key: "trackVastError",
2121          value: function trackVastError(urlTemplates, errorCode) {
2122            for (
2123              var _len = arguments.length,
2124                data = Array(_len > 2 ? _len - 2 : 0),
2125                _key = 2;
2126              _key < _len;
2127              _key++
2128            ) {
2129              data[_key - 2] = arguments[_key];
2130            }
2131  
2132            this.emit(
2133              "VAST-error",
2134              Object.assign.apply(
2135                Object,
2136                [{}, DEFAULT_EVENT_DATA, errorCode].concat(data)
2137              )
2138            );
2139            util.track(urlTemplates, errorCode);
2140          },
2141  
2142          /**
2143           * Returns an array of errorURLTemplates for the VAST being parsed.
2144           * @return {Array}
2145           */
2146        },
2147        {
2148          key: "getErrorURLTemplates",
2149          value: function getErrorURLTemplates() {
2150            return this.rootErrorURLTemplates.concat(this.errorURLTemplates);
2151          },
2152  
2153          /**
2154           * Fetches a VAST document for the given url.
2155           * Returns a Promise which resolves,rejects according to the result of the request.
2156           * @param  {String} url - The url to request the VAST document.
2157           * @param {Number} wrapperDepth - how many times the current url has been wrapped
2158           * @param {String} originalUrl - url of original wrapper
2159           * @emits  VASTParser#VAST-resolving
2160           * @emits  VASTParser#VAST-resolved
2161           * @return {Promise}
2162           */
2163        },
2164        {
2165          key: "fetchVAST",
2166          value: function fetchVAST(url, wrapperDepth, originalUrl) {
2167            var _this2 = this;
2168  
2169            return new Promise(function (resolve, reject) {
2170              // Process url with defined filter
2171              _this2.URLTemplateFilters.forEach(function (filter) {
2172                url = filter(url);
2173              });
2174  
2175              _this2.parentURLs.push(url);
2176              _this2.emit("VAST-resolving", {
2177                url: url,
2178                wrapperDepth: wrapperDepth,
2179                originalUrl: originalUrl,
2180              });
2181  
2182              _this2.urlHandler.get(url, _this2.fetchingOptions, function (
2183                err,
2184                xml
2185              ) {
2186                _this2.emit("VAST-resolved", { url: url, error: err });
2187  
2188                if (err) {
2189                  reject(err);
2190                } else {
2191                  resolve(xml);
2192                }
2193              });
2194            });
2195          },
2196  
2197          /**
2198           * Inits the parsing properties of the class with the custom values provided as options.
2199           * @param {Object} options - The options to initialize a parsing sequence
2200           */
2201        },
2202        {
2203          key: "initParsingStatus",
2204          value: function initParsingStatus() {
2205            var options =
2206              arguments.length > 0 && arguments[0] !== undefined
2207                ? arguments[0]
2208                : {};
2209  
2210            this.rootURL = "";
2211            this.remainingAds = [];
2212            this.parentURLs = [];
2213            this.errorURLTemplates = [];
2214            this.rootErrorURLTemplates = [];
2215            this.maxWrapperDepth =
2216              options.wrapperLimit || DEFAULT_MAX_WRAPPER_DEPTH;
2217            this.fetchingOptions = {
2218              timeout: options.timeout,
2219              withCredentials: options.withCredentials,
2220            };
2221  
2222            this.urlHandler =
2223              options.urlHandler || options.urlhandler || urlHandler;
2224            this.vastVersion = null;
2225          },
2226  
2227          /**
2228           * Resolves the next group of ads. If all is true resolves all the remaining ads.
2229           * @param  {Boolean} all - If true all the remaining ads are resolved
2230           * @return {Promise}
2231           */
2232        },
2233        {
2234          key: "getRemainingAds",
2235          value: function getRemainingAds(all) {
2236            var _this3 = this;
2237  
2238            if (this.remainingAds.length === 0) {
2239              return Promise.reject(
2240                new Error("No more ads are available for the given VAST")
2241              );
2242            }
2243  
2244            var ads = all
2245              ? util.flatten(this.remainingAds)
2246              : this.remainingAds.shift();
2247            this.errorURLTemplates = [];
2248            this.parentURLs = [];
2249  
2250            return this.resolveAds(ads, {
2251              wrapperDepth: 0,
2252              originalUrl: this.rootURL,
2253            }).then(function (resolvedAds) {
2254              return _this3.buildVASTResponse(resolvedAds);
2255            });
2256          },
2257  
2258          /**
2259           * Fetches and parses a VAST for the given url.
2260           * Returns a Promise which resolves with a fully parsed VASTResponse or rejects with an Error.
2261           * @param  {String} url - The url to request the VAST document.
2262           * @param  {Object} options - An optional Object of parameters to be used in the parsing process.
2263           * @emits  VASTParser#VAST-resolving
2264           * @emits  VASTParser#VAST-resolved
2265           * @return {Promise}
2266           */
2267        },
2268        {
2269          key: "getAndParseVAST",
2270          value: function getAndParseVAST(url) {
2271            var _this4 = this;
2272  
2273            var options =
2274              arguments.length > 1 && arguments[1] !== undefined
2275                ? arguments[1]
2276                : {};
2277  
2278            this.initParsingStatus(options);
2279            this.rootURL = url;
2280  
2281            return this.fetchVAST(url).then(function (xml) {
2282              options.originalUrl = url;
2283              options.isRootVAST = true;
2284  
2285              return _this4.parse(xml, options).then(function (ads) {
2286                return _this4.buildVASTResponse(ads);
2287              });
2288            });
2289          },
2290  
2291          /**
2292           * Parses the given xml Object into a VASTResponse.
2293           * Returns a Promise which resolves with a fully parsed VASTResponse or rejects with an Error.
2294           * @param  {Object} vastXml - An object representing a vast xml document.
2295           * @param  {Object} options - An optional Object of parameters to be used in the parsing process.
2296           * @emits  VASTParser#VAST-resolving
2297           * @emits  VASTParser#VAST-resolved
2298           * @return {Promise}
2299           */
2300        },
2301        {
2302          key: "parseVAST",
2303          value: function parseVAST(vastXml) {
2304            var _this5 = this;
2305  
2306            var options =
2307              arguments.length > 1 && arguments[1] !== undefined
2308                ? arguments[1]
2309                : {};
2310  
2311            this.initParsingStatus(options);
2312  
2313            options.isRootVAST = true;
2314  
2315            return this.parse(vastXml, options).then(function (ads) {
2316              return _this5.buildVASTResponse(ads);
2317            });
2318          },
2319  
2320          /**
2321           * Builds a VASTResponse which can be returned.
2322           * @param  {Array} ads - An Array of unwrapped ads
2323           * @return {VASTResponse}
2324           */
2325        },
2326        {
2327          key: "buildVASTResponse",
2328          value: function buildVASTResponse(ads) {
2329            var response = new VASTResponse();
2330            response.ads = ads;
2331            response.errorURLTemplates = this.getErrorURLTemplates();
2332            response.version = this.vastVersion;
2333            this.completeWrapperResolving(response);
2334  
2335            return response;
2336          },
2337  
2338          /**
2339           * Parses the given xml Object into an array of ads
2340           * Returns the array or throws an `Error` if an invali
2340d VAST XML is provided
2341           * @param  {Object} vastXml - An object representing an xml document.
2342           * @param  {Object} options - An optional Object of parameters to be used in the parsing process.
2343           * @return {Array}
2344           * @throws {Error} `vastXml` must be a valid VAST XMLDocument
2345           */
2346        },
2347        {
2348          key: "parseVastXml",
2349          value: function parseVastXml(vastXml, _ref) {
2350            var _ref$isRootVAST = _ref.isRootVAST,
2351              isRootVAST =
2352                _ref$isRootVAST === undefined ? false : _ref$isRootVAST;
2353  
2354            // check if is a valid VAST document
2355            if (
2356              !vastXml ||
2357              !vastXml.documentElement ||
2358              vastXml.documentElement.nodeName !== "VAST"
2359            ) {
2360              throw new Error("Invalid VAST XMLDocument");
2361            }
2362  
2363            var ads = [];
2364            var childNodes = vastXml.documentElement.childNodes;
2365  
2366            /* Only parse the version of the Root VAST for now because we don't know yet how to
2367               handle some cases like multiple wrappers in the same vast
2368            */
2369            if (isRootVAST) {
2370              var vastVersion = vastXml.documentElement.getAttribute("version");
2371              if (vastVersion) this.vastVersion = vastVersion;
2372            }
2373  
2374            // Fill the VASTResponse object with ads and errorURLTemplates
2375            for (var nodeKey in childNodes) {
2376              var node = childNodes[nodeKey];
2377  
2378              if (node.nodeName === "Error") {
2379                var errorURLTemplate = parserUtils.parseNodeText(node);
2380  
2381                // Distinguish root VAST url templates from ad specific ones
2382                isRootVAST
2383                  ? this.rootErrorURLTemplates.push(errorURLTemplate)
2384                  : this.errorURLTemplates.push(errorURLTemplate);
2385              }
2386  
2387              if (node.nodeName === "Ad") {
2388                var ad = parseAd(node);
2389  
2390                if (ad) {
2391                  ads.push(ad);
2392                } else {
2393                  // VAST version of response not supported.
2394                  this.trackVastError(this.getErrorURLTemplates(), {
2395                    ERRORCODE: 101,
2396                  });
2397                }
2398              }
2399            }
2400  
2401            return ads;
2402          },
2403  
2404          /**
2405           * Parses the given xml Object into an array of unwrapped ads.
2406           * Returns a Promise which resolves with the array or rejects with an error according to the result of the parsing.
2407           * @param  {Object} vastXml - An object representing an xml document.
2408           * @param  {Object} options - An optional Object of parameters to be used in the parsing process.
2409           * @emits  VASTParser#VAST-resolving
2410           * @emits  VASTParser#VAST-resolved
2411           * @return {Promise}
2412           */
2413        },
2414        {
2415          key: "parse",
2416          value: function parse(vastXml, _ref2) {
2417            var _ref2$resolveAll = _ref2.resolveAll,
2418              resolveAll =
2419                _ref2$resolveAll === undefined ? true : _ref2$resolveAll,
2420              _ref2$wrapperSequence = _ref2.wrapperSequence,
2421              wrapperSequence =
2422                _ref2$wrapperSequence === undefined
2423                  ? null
2424                  : _ref2$wrapperSequence,
2425              _ref2$originalUrl = _ref2.originalUrl,
2426              originalUrl =
2427                _ref2$originalUrl === undefined ? null : _ref2$originalUrl,
2428              _ref2$wrapperDepth = _ref2.wrapperDepth,
2429              wrapperDepth =
2430                _ref2$wrapperDepth === undefined ? 0 : _ref2$wrapperDepth,
2431              _ref2$isRootVAST = _ref2.isRootVAST,
2432              isRootVAST =
2433                _ref2$isRootVAST === undefined ? false : _ref2$isRootVAST;
2434  
2435            var ads = [];
2436            try {
2437              ads = this.parseVastXml(vastXml, { isRootVAST: isRootVAST });
2438            } catch (e) {
2439              return Promise.reject(e);
2440            }
2441  
2442            var adsCount = ads.length;
2443            var lastAddedAd = ads[adsCount - 1];
2444            // if in child nodes we have only one ads
2445            // and wrapperSequence is defined
2446            // and this ads doesn't already have sequence
2447            if (
2448              adsCount === 1 &&
2449              wrapperSequence !== undefined &&
2450              wrapperSequence !== null &&
2451              lastAddedAd &&
2452              !lastAddedAd.sequence
2453            ) {
2454              lastAddedAd.sequence = wrapperSequence;
2455            }
2456  
2457            // Split the VAST in case we don't want to resolve everything at the first time
2458            if (resolveAll === false) {
2459              this.remainingAds = parserUtils.splitVAST(ads);
2460              // Remove the first element from the remaining ads array, since we're going to resolve that element
2461              ads = this.remainingAds.shift();
2462            }
2463  
2464            return this.resolveAds(ads, {
2465              wrapperDepth: wrapperDepth,
2466              originalUrl: originalUrl,
2467            });
2468          },
2469  
2470          /**
2471           * Resolves an Array of ads, recursively calling itself with the remaining ads if a no ad
2472           * response is returned for the given array.
2473           * @param {Array} ads - An array of ads to resolve
2474           * @param {Object} options - An options Object containing resolving parameters
2475           * @return {Promise}
2476           */
2477        },
2478        {
2479          key: "resolveAds",
2480          value: function resolveAds() {
2481            var _this6 = this;
2482  
2483            var ads =
2484              arguments.length > 0 && arguments[0] !== undefined
2485                ? arguments[0]
2486                : [];
2487            var _ref3 = arguments[1];
2488            var wrapperDepth = _ref3.wrapperDepth,
2489              originalUrl = _ref3.originalUrl;
2490  
2491            var resolveWrappersPromises = [];
2492  
2493            ads.forEach(function (ad) {
2494              var resolveWrappersPromise = _this6.resolveWrappers(
2495                ad,
2496                wrapperDepth,
2497                originalUrl
2498              );
2499  
2500              resolveWrappersPromises.push(resolveWrappersPromise);
2501            });
2502  
2503            return Promise.all(resolveWrappersPromises).then(function (
2504              unwrappedAds
2505            ) {
2506              var resolvedAds = util.flatten(unwrappedAds);
2507  
2508              if (!resolvedAds && _this6.remainingAds.length > 0) {
2509                var remainingAdsToResolve = _this6.remainingAds.shift();
2510  
2511                return _this6.resolveAds(remainingAdsToResolve, {
2512                  wrapperDepth: wrapperDepth,
2513                  originalUrl: originalUrl,
2514                });
2515              }
2516  
2517              return resolvedAds;
2518            });
2519          },
2520  
2521          /**
2522           * Resolves the wrappers for the given ad in a recursive way.
2523           * Returns a Promise which resolves with the unwrapped ad or rejects with an error.
2524           * @param  {Ad} ad - An ad to be unwrapped.
2525           * @param  {Number} wrapperDepth - The reached depth in the wrapper resolving chain.
2526           * @param  {String} originalUrl - The original vast url.
2527           * @return {Promise}
2528           */
2529        },
2530        {
2531          key: "resolveWrappers",
2532          value: function resolveWrappers(ad, wrapperDepth, originalUrl) {
2533            var _this7 = this;
2534  
2535            return new Promise(function (resolve) {
2536              // Going one level deeper in the wrapper chain
2537              wrapperDepth++;
2538              // We already have a resolved VAST ad, no need to resolve wrapper
2539              if (!ad.nextWrapperURL) {
2540                delete ad.nextWrapperURL;
2541                return resolve(ad);
2542              }
2543  
2544              if (
2545                wrapperDepth >= _this7.maxWrapperDepth ||
2546                _this7.parentURLs.indexOf(ad.nextWrapperURL) !== -1
2547              ) {
2548                // Wrapper limit reached, as defined by the video player.
2549                // Too many Wrapper responses have been received with no InLine response.
2550                ad.errorCode = 302;
2551                delete ad.nextWrapperURL;
2552                return resolve(ad);
2553              }
2554  
2555              // Get full URL
2556              ad.nextWrapperURL = parserUtils.resolveVastAdTagURI(
2557                ad.nextWrapperURL,
2558                originalUrl
2559              );
2560  
2561              // sequence doesn't carry over in wrapper element
2562              var wrapperSequence = ad.sequence;
2563              originalUrl = ad.nextWrapperURL;
2564  
2565              _this7
2566                .fetchVAST(ad.nextWrapperURL, wrapperDepth, originalUrl)
2567                .then(function (xml) {
2568                  return _this7
2569                    .parse(xml, {
2570                      originalUrl: originalUrl,
2571                      wrapperSequence: wrapperSequence,
2572                      wrapperDepth: wrapperDepth,
2573                    })
2574                    .then(function (unwrappedAds) {
2575                      delete ad.nextWrapperURL;
2576                      if (unwrappedAds.length === 0) {
2577                        // No ads returned by the wrappedResponse, discard current <Ad><Wrapper> creatives
2578                        ad.creatives = [];
2579                        return resolve(ad);
2580                      }
2581  
2582                      unwrappedAds.forEach(function (unwrappedAd) {
2583                        if (unwrappedAd) {
2584                          parserUtils.mergeWrapperAdData(unwrappedAd, ad);
2585                        }
2586                      });
2587  
2588                      resolve(unwrappedAds);
2589                    });
2590                })
2591                .catch(function (err) {
2592                  // Timeout of VAST URI provided in Wrapper element, or of VAST URI provided in a subsequent Wrapper element.
2593                  // (URI was either unavailable or reached a timeout as defined by the video player.)
2594                  ad.errorCode = 301;
2595                  ad.errorMessage = err.message;
2596  
2597                  resolve(ad);
2598                });
2599            });
2600          },
2601  
2602          /**
2603           * Takes care of handling errors when the wrappers are resolved.
2604           * @param {VASTResponse} vastResponse - A resolved VASTResponse.
2605           */
2606        },
2607        {
2608          key: "completeWrapperResolving",
2609          value: function completeWrapperResolving(vastResponse) {
2610            // We've to wait for all <Ad> elements to be parsed before handling error so we can:
2611            // - Send computed extensions data
2612            // - Ping all <Error> URIs defined across VAST files
2613  
2614            // No Ad case - The parser never bump into an <Ad> element
2615            if (vastResponse.ads.length === 0) {
2616              this.trackVastError(vastResponse.errorURLTemplates, {
2617                ERRORCODE: 303,
2618              });
2619            } else {
2620              for (var index = vastResponse.ads.length - 1; index >= 0; index--) {
2621                // - Error encountred while parsing
2622                // - No Creative case - The parser has dealt with soma <Ad><Wrapper> or/and an <Ad><Inline> elements
2623                // but no creative was found
2624                var ad = vastResponse.ads[index];
2625                if (ad.errorCode || ad.creatives.length === 0) {
2626                  this.trackVastError(
2627                    ad.errorURLTemplates.concat(vastResponse.errorURLTemplates),
2628                    { ERRORCODE: ad.errorCode || 303 },
2629                    { ERRORMESSAGE: ad.errorMessage || "" },
2630                    { extensions: ad.extensions },
2631                    { system: ad.system }
2632                  );
2633                  vastResponse.ads.splice(index, 1);
2634                }
2635              }
2636            }
2637          },
2638        },
2639      ]);
2640      return VASTParser;
2641    })(EventEmitter);
2642  
2643    var storage = null;
2644  
2645    /**
2646     * This Object represents a default storage to be used in case no other storage is available.
2647     * @constant
2648     * @type {Object}
2649     */
2650    var DEFAULT_STORAGE = {
2651      data: {},
2652      length: 0,
2653      getItem: function getItem(key) {
2654        return this.data[key];
2655      },
2656      setItem: function setItem(key, value) {
2657        this.data[key] = value;
2658        this.length = Object.keys(this.data).length;
2659      },
2660      removeItem: function removeItem(key) {
2661        delete this.data[key];
2662        this.length = Object.keys(this.data).length;
2663      },
2664      clear: function clear() {
2665        this.data = {};
2666        this.length = 0;
2667      },
2668    };
2669  
2670    /**
2671     * This class provides an wrapper interface to the a key-value storage.
2672     * It uses localStorage, sessionStorage or a custom storage if none of the two is available.
2673     * @export
2674     * @class Storage
2675     */
2676    var Storage = (function () {
2677      /**
2678       * Creates an instance of Storage.
2679       * @constructor
2680       */
2681      function Storage() {
2682        classCallCheck(this, Storage);
2683  
2684        this.storage = this.initStorage();
2685      }
2686  
2687      /**
2688       * Provides a singleton instance of the wrapped storage.
2689       * @return {Object}
2690       */
2691  
2692      createClass(Storage, [
2693        {
2694          key: "initStorage",
2695          value: function initStorage() {
2696            if (storage) {
2697              return storage;
2698            }
2699  
2700            try {
2701              storage =
2702                typeof window !== "undefined" && window !== null
2703                  ? window.localStorage || window.sessionStorage
2704                  : null;
2705            } catch (storageError) {
2706              storage = null;
2707            }
2708  
2709            if (!storage || this.isStorageDisabled(storage)) {
2710              storage = DEFAULT_STORAGE;
2711              storage.clear();
2712            }
2713  
2714            return storage;
2715          },
2716  
2717          /**
2718           * Check if storage is disabled (like in certain cases with private browsing).
2719           * In Safari (Mac + iOS) when private browsing is ON, localStorage is read only
2720           * http://spin.atomicobject.com/2013/01/23/ios-private-browsing-localstorage/
2721           * @param {Object} testStorage - The storage to check.
2722           * @return {Boolean}
2723           */
2724        },
2725        {
2726          key: "isStorageDisabled",
2727          value: function isStorageDisabled(testStorage) {
2728            var testValue = "__VASTStorage__";
2729  
2730            try {
2731              testStorage.setItem(testValue, testValue);
2732              if (testStorage.getItem(testValue) !== testValue) {
2733                testStorage.removeItem(testValue);
2734                return true;
2735              }
2736            } catch (e) {
2737              return true;
2738            }
2739  
2740            testStorage.removeItem(testValue);
2741            return false;
2742          },
2743  
2744          /**
2745           * Returns the value for the given key. If the key does not exist, null is returned.
2746           * @param  {String} key - The key to retrieve the value.
2747           * @return {any}
2748           */
2749        },
2750        {
2751          key: "getItem",
2752          value: function getItem(key) {
2753            return this.storage.getItem(key);
2754          },
2755  
2756          /**
2757           * Adds or updates the value for the given key.
2758           * @param  {String} key - The key to modify the value.
2759           * @param  {any} value - The value to be associated with the key.
2760           * @return {any}
2761           */
2762        },
2763        {
2764          key: "setItem",
2765          value: function setItem(key, value) {
2766            return this.storage.setItem(key, value);
2767          },
2768  
2769          /**
2770           * Removes an item for the given key.
2771           * @param  {String} key - The key to remove the value.
2772           * @return {any}
2773           */
2774        },
2775        {
2776          key: "removeItem",
2777          value: function removeItem(key) {
2778            return this.storage.removeItem(key);
2779          },
2780  
2781          /**
2782           * Removes all the items from the storage.
2783           */
2784        },
2785        {
2786          key: "clear",
2787          value: function clear() {
2788            return this.storage.clear();
2789          },
2790        },
2791      ]);
2792      return Storage;
2793    })();
2794  
2795    /**
2796     * This class provides methods to fetch and parse a VAST document using VASTParser.
2797     * In addition it provides options to skip consecutive calls based on constraints.
2798     * @export
2799     * @class VASTClient
2800     */
2801    var VASTClient = (function () {
2802      /**
2803       * Creates an instance of VASTClient.
2804       * @param  {Number} cappingFreeLunch - The number of first calls to skip.
2805       * @param  {Number} cappingMinimumTimeInterval - The minimum time interval between two consecutive calls.
2806       * @param  {Storage} customStorage - A custom storage to use instead of the default one.
2807       * @constructor
2808       */
2809      function VASTClient(
2810        cappingFreeLunch,
2811        cappingMinimumTimeInterval,
2812        customStorage
2813      ) {
2814        classCallCheck(this, VASTClient);
2815  
2816        this.cappingFreeLunch = cappingFreeLunch || 0;
2817        this.cappingMinimumTimeInterval = cappingMinimumTimeInterval || 0;
2818        this.defaultOptions = {
2819          withCredentials: false,
2820          timeout: 0,
2821        };
2822        this.vastParser = new VASTParser();
2823        this.storage = customStorage || new Storage();
2824  
2825        // Init values if not already set
2826        if (this.lastSuccessfulAd === undefined) {
2827          this.lastSuccessfulAd = 0;
2828        }
2829  
2830        if (this.totalCalls === undefined) {
2831          this.totalCalls = 0;
2832        }
2833        if (this.totalCallsTimeout === undefined) {
2834          this.totalCallsTimeout = 0;
2835        }
2836      }
2837  
2838      createClass(VASTClient, [
2839        {
2840          key: "getParser",
2841          value: function getParser() {
2842            return this.vastParser;
2843          },
2844        },
2845        {
2846          key: "hasRemainingAds",
2847  
2848          /**
2849           * Returns a boolean indicating if there are more ads to resolve for the current parsing.
2850           * @return {Boolean}
2851           */
2852          value: function hasRemainingAds() {
2853            return this.vastParser.remainingAds.length > 0;
2854          },
2855  
2856          /**
2857           * Resolves the next group of ads. If all is true resolves all the remaining ads.
2858           * @param  {Boolean} all - If true all the remaining ads are resolved
2859           * @return {Promise}
2860           */
2861        },
2862        {
2863          key: "getNextAds",
2864          value: function getNextAds(all) {
2865            return this.vastParser.getRemainingAds(all);
2866          },
2867  
2868          /**
2869           * Gets a parsed VAST document for the given url, applying the skipping rules defined.
2870           * Returns a Promise which resolves with a fully parsed VASTResponse or rejects with an Error.
2871           * @param  {String} url - The url to use to fecth the VAST document.
2872           * @param  {Object} options - An optional Object of parameters to be applied in the process.
2873           * @return {Promise}
2874           */
2875        },
2876        {
2877          key: "get",
2878          value: function get$$1(url) {
2879            var _this = this;
2880  
2881            var options =
2882              arguments.length > 1 && arguments[1] !== undefined
2883                ? arguments[1]
2884                : {};
2885  
2886            var now = Date.now();
2887            options = Object.assign({}, this.defaultOptions, options);
2888  
2889            // By default the client resolves only the first Ad or AdPod
2890            if (!options.hasOwnProperty("resolveAll")) {
2891              options.resolveAll = false;
2892            }
2893  
2894            // Check totalCallsTimeout (first call + 1 hour), if older than now,
2895            // reset totalCalls number, by this way the client will be eligible again
2896            // for freelunch capping
2897            if (this.totalCallsTimeout < now) {
2898              this.totalCalls = 1;
2899              this.totalCallsTimeout = now + 60 * 60 * 1000;
2900            } else {
2901              this.totalCalls++;
2902            }
2903  
2904            return new Promise(function (resolve, reject) {
2905              if (_this.cappingFreeLunch >= _this.totalCalls) {
2906                return reject(
2907                  new Error(
2908                    "VAST call canceled \u2013 FreeLunch capping not reached yet " +
2909                      _this.totalCalls +
2910                      "/" +
2911                      _this.cappingFreeLunch
2912                  )
2913                );
2914              }
2915  
2916              var timeSinceLastCall = now - _this.lastSuccessfulAd;
2917  
2918              // Check timeSinceLastCall to be a positive number. If not, this mean the
2919              // previous was made in the future. We reset lastSuccessfulAd value
2920              if (timeSinceLastCall < 0) {
2921                _this.lastSuccessfulAd = 0;
2922              } else if (timeSinceLastCall < _this.cappingMinimumTimeInterval) {
2923                return reject(
2924                  new Error(
2925                    "VAST call canceled \u2013 (" +
2926                      _this.cappingMinimumTimeInterval +
2927                      ")ms minimum interval reached"
2928                  )
2929                );
2930              }
2931  
2932              _this.vastParser
2933                .getAndParseVAST(url, options)
2934                .then(function (response) {
2935                  return resolve(response);
2936                })
2937                .catch(function (err) {
2938                  return reject(err);
2939                });
2940            });
2941          },
2942        },
2943        {
2944          key: "lastSuccessfulAd",
2945          get: function get$$1() {
2946            return this.storage.getItem("vast-client-last-successful-ad");
2947          },
2948          set: function set$$1(value) {
2949            this.storage.setItem("vast-client-last-successful-ad", value);
2950          },
2951        },
2952        {
2953          key: "totalCalls",
2954          get: function get$$1() {
2955            return this.storage.getItem("vast-client-total-calls");
2956          },
2957          set: function set$$1(value) {
2958            this.storage.setItem("vast-client-total-calls", value);
2959          },
2960        },
2961        {
2962          key: "totalCallsTimeout",
2963          get: function get$$1() {
2964            return this.storage.getItem("vast-client-total-calls-timeout");
2965          },
2966          set: function set$$1(value) {
2967            this.storage.setItem("vast-client-total-calls-timeout", value);
2968          },
2969        },
2970      ]);
2971      return VASTClient;
2972    })();
2973  
2974    /**
2975     * The default skip delay used in case a custom one is not provided
2976     * @constant
2977     * @type {Number}
2978     */
2979    var DEFAULT_SKIP_DELAY = -1;
2980  
2981    /**
2982     * This class provides methods to track an ad execution.
2983     *
2984     * @export
2985     * @class VASTTracker
2986     * @extends EventEmitter
2987     */
2988    var VASTTracker = (function (_EventEmitter) {
2989      inherits(VASTTracker, _EventEmitter);
2990  
2991      /**
2992       * Creates an instance of VASTTracker.
2993       *
2994       * @param {VASTClient} client - An instance of VASTClient that can be updated by the tracker. [optional]
2995       * @param {Ad} ad - The ad to track.
2996       * @param {Creative} creative - The creative to track.
2997       * @param {CompanionAd|NonLinearAd} [variation=null] - An optional variation of the creative.
2998       * @constructor
2999       */
3000      function VASTTracker(client, ad, creative) {
3001        var variation =
3002          arguments.length > 3 && arguments[3] !== undefined
3003            ? arguments[3]
3004            : null;
3005        classCallCheck(this, VASTTracker);
3006  
3007        var _this = possibleConstructorReturn(
3008          this,
3009          (VASTTracker.__proto__ || Object.getPrototypeOf(VASTTracker)).call(this)
3010        );
3011  
3012        _this.ad = ad;
3013        _this.creative = creative;
3014        _this.variation = variation;
3015        _this.muted = false;
3016        _this.impressed = false;
3017        _this.skippable = false;
3018        _this.trackingEvents = {};
3019        // We need to save the already triggered quartiles, in order to not trigger them again
3020        _this._alreadyTriggeredQuartiles = {};
3021        // Tracker listeners should be notified with some events
3022        // no matter if there is a tracking URL or not
3023        _this.emitAlwaysEvents = [
3024          "creativeView",
3025          "start",
3026          "firstQuartile",
3027          "midpoint",
3028          "thirdQuartile",
3029          "complete",
3030          "resume",
3031          "pause",
3032          "rewind",
3033          "skip",
3034          "closeLinear",
3035          "close",
3036        ];
3037  
3038        // Duplicate the creative's trackingEvents property so we can alter it
3039        for (var eventName in _this.creative.trackingEvents) {
3040          var events = _this.creative.trackingEvents[eventName];
3041          _this.trackingEvents[eventName] = events.slice(0);
3042        }
3043  
3044        // Nonlinear and companion creatives provide some tracking information at a variation level
3045        // While linear creatives provided that at a creative level. That's why we need to
3046        // differentiate how we retrieve some tracking information.
3047        if (_this.creative instanceof CreativeLinear) {
3048          _this._initLinearTracking();
3049        } else {
3050          _this._initVariationTracking();
3051        }
3052  
3053        // If the tracker is associated with a client we add a listener to the start event
3054        // to update the lastSuccessfulAd property.
3055        if (client) {
3056          _this.on("start", function () {
3057            client.lastSuccessfulAd = Date.now();
3058          });
3059        }
3060        return _this;
3061      }
3062  
3063      /**
3064       * Init the custom tracking options for linear creatives.
3065       *
3066       * @return {void}
3067       */
3068  
3069      createClass(VASTTracker, [
3070        {
3071          key: "_initLinearTracking",
3072          value: function _initLinearTracking() {
3073            this.linear = true;
3074            this.skipDelay = this.creative.skipDelay;
3075  
3076            this.setDuration(this.creative.duration);
3077  
3078            this.clickThroughURLTemplate = this.creative.videoClickThroughURLTemplate;
3079            this.clickTrackingURLTemplates = this.creative.videoClickTrackingURLTemplates;
3080          },
3081  
3082          /**
3083           * Init the custom tracking options for nonlinear and companion creatives.
3084           * These options are provided in the variation Object.
3085           *
3086           * @return {void}
3087           */
3088        },
3089        {
3090          key: "_initVariationTracking",
3091          value: function _initVariationTracking() {
3092            this.linear = false;
3093            this.skipDelay = DEFAULT_SKIP_DELAY;
3094  
3095            // If no variation has been provided there's nothing else to set
3096            if (!this.variation) {
3097              return;
3098            }
3099  
3100            // Duplicate the variation's trackingEvents property so we can alter it
3101            for (var eventName in this.variation.trackingEvents) {
3102              var events = this.variation.trackingEvents[eventName];
3103  
3104              // If for the given eventName we already had some trackingEvents provided by the creative
3105              // we want to keep both the creative trackingEvents and the variation ones
3106              if (this.trackingEvents[eventName]) {
3107                this.trackingEvents[eventName] = this.trackingEvents[
3108                  eventName
3109                ].concat(events.slice(0));
3110              } else {
3111                this.trackingEvents[eventName] = events.slice(0);
3112              }
3113            }
3114  
3115            if (this.variation instanceof NonLinearAd) {
3116              this.clickThroughURLTemplate = this.variation.nonlinearClickThroughURLTemplate;
3117              this.clickTrackingURLTemplates = this.variation.nonlinearClickTrackingURLTemplates;
3118              this.setDuration(this.variation.minSuggestedDuration);
3119            } else if (this.variation instanceof CompanionAd) {
3120              this.clickThroughURLTemplate = this.variation.companionClickThroughURLTemplate;
3121              this.clickTrackingURLTemplates = this.variation.companionCl
3121ickTrackingURLTemplates;
3122            }
3123          },
3124  
3125          /**
3126           * Sets the duration of the ad and updates the quartiles based on that.
3127           *
3128           * @param  {Number} duration - The duration of the ad.
3129           */
3130        },
3131        {
3132          key: "setDuration",
3133          value: function setDuration(duration) {
3134            this.assetDuration = duration;
3135            // beware of key names, theses are also used as event names
3136            this.quartiles = {
3137              firstQuartile: Math.round(25 * this.assetDuration) / 100,
3138              midpoint: Math.round(50 * this.assetDuration) / 100,
3139              thirdQuartile: Math.round(75 * this.assetDuration) / 100,
3140            };
3141          },
3142  
3143          /**
3144           * Sets the duration of the ad and updates the quartiles based on that.
3145           * This is required for tracking time related events.
3146           *
3147           * @param {Number} progress - Current playback time in seconds.
3148           * @emits VASTTracker#start
3149           * @emits VASTTracker#skip-countdown
3150           * @emits VASTTracker#progress-[0-100]%
3151           * @emits VASTTracker#progress-[currentTime]
3152           * @emits VASTTracker#rewind
3153           * @emits VASTTracker#firstQuartile
3154           * @emits VASTTracker#midpoint
3155           * @emits VASTTracker#thirdQuartile
3156           */
3157        },
3158        {
3159          key: "setProgress",
3160          value: function setProgress(progress) {
3161            var _this2 = this;
3162  
3163            var skipDelay = this.skipDelay || DEFAULT_SKIP_DELAY;
3164  
3165            if (skipDelay !== -1 && !this.skippable) {
3166              if (skipDelay > progress) {
3167                this.emit("skip-countdown", skipDelay - progress);
3168              } else {
3169                this.skippable = true;
3170                this.emit("skip-countdown", 0);
3171              }
3172            }
3173  
3174            if (this.assetDuration > 0) {
3175              var events = [];
3176  
3177              if (progress > 0) {
3178                var percent = Math.round((progress / this.assetDuration) * 100);
3179  
3180                events.push("start");
3181                events.push("progress-" + percent + "%");
3182                events.push("progress-" + Math.round(progress));
3183  
3184                for (var quartile in this.quartiles) {
3185                  if (
3186                    this.isQuartileReached(
3187                      quartile,
3188                      this.quartiles[quartile],
3189                      progress
3190                    )
3191                  ) {
3192                    events.push(quartile);
3193                    this._alreadyTriggeredQuartiles[quartile] = true;
3194                  }
3195                }
3196              }
3197  
3198              events.forEach(function (eventName) {
3199                _this2.track(eventName, true);
3200              });
3201  
3202              if (progress < this.progress) {
3203                this.track("rewind");
3204              }
3205            }
3206  
3207            this.progress = progress;
3208          },
3209  
3210          /**
3211           * Checks if a quartile has been reached without have being triggered already.
3212           *
3213           * @param {String} quartile - Quartile name
3214           * @param {Number} time - Time offset, when this quartile is reached in seconds.
3215           * @param {Number} progress - Current progress of the ads in seconds.
3216           *
3217           * @return {Boolean}
3218           */
3219        },
3220        {
3221          key: "isQuartileReached",
3222          value: function isQuartileReached(quartile, time, progress) {
3223            var quartileReached = false;
3224            // if quartile time already reached and never triggered
3225            if (time <= progress && !this._alreadyTriggeredQuartiles[quartile]) {
3226              quartileReached = true;
3227            }
3228            return quartileReached;
3229          },
3230  
3231          /**
3232           * Updates the mute state and calls the mute/unmute tracking URLs.
3233           *
3234           * @param {Boolean} muted - Indicates if the video is muted or not.
3235           * @emits VASTTracker#mute
3236           * @emits VASTTracker#unmute
3237           */
3238        },
3239        {
3240          key: "setMuted",
3241          value: function setMuted(muted) {
3242            if (this.muted !== muted) {
3243              this.track(muted ? "mute" : "unmute");
3244            }
3245            this.muted = muted;
3246          },
3247  
3248          /**
3249           * Update the pause state and call the resume/pause tracking URLs.
3250           *
3251           * @param {Boolean} paused - Indicates if the video is paused or not.
3252           * @emits VASTTracker#pause
3253           * @emits VASTTracker#resume
3254           */
3255        },
3256        {
3257          key: "setPaused",
3258          value: function setPaused(paused) {
3259            if (this.paused !== paused) {
3260              this.track(paused ? "pause" : "resume");
3261            }
3262            this.paused = paused;
3263          },
3264  
3265          /**
3266           * Updates the fullscreen state and calls the fullscreen tracking URLs.
3267           *
3268           * @param {Boolean} fullscreen - Indicates if the video is in fulscreen mode or not.
3269           * @emits VASTTracker#fullscreen
3270           * @emits VASTTracker#exitFullscreen
3271           */
3272        },
3273        {
3274          key: "setFullscreen",
3275          value: function setFullscreen(fullscreen) {
3276            if (this.fullscreen !== fullscreen) {
3277              this.track(fullscreen ? "fullscreen" : "exitFullscreen");
3278            }
3279            this.fullscreen = fullscreen;
3280          },
3281  
3282          /**
3283           * Updates the expand state and calls the expand/collapse tracking URLs.
3284           *
3285           * @param {Boolean} expanded - Indicates if the video is expanded or not.
3286           * @emits VASTTracker#expand
3287           * @emits VASTTracker#collapse
3288           */
3289        },
3290        {
3291          key: "setExpand",
3292          value: function setExpand(expanded) {
3293            if (this.expanded !== expanded) {
3294              this.track(expanded ? "expand" : "collapse");
3295            }
3296            this.expanded = expanded;
3297          },
3298  
3299          /**
3300           * Must be called if you want to overwrite the <Linear> Skipoffset value.
3301           * This will init the skip countdown duration. Then, every time setProgress() is called,
3302           * it will decrease the countdown and emit a skip-countdown event with the remaining time.
3303           * Do not call this method if you want to keep the original Skipoffset value.
3304           *
3305           * @param {Number} duration - The time in seconds until the skip button is displayed.
3306           */
3307        },
3308        {
3309          key: "setSkipDelay",
3310          value: function setSkipDelay(duration) {
3311            if (typeof duration === "number") {
3312              this.skipDelay = duration;
3313            }
3314          },
3315  
3316          /**
3317           * Tracks an impression (can be called only once).
3318           *
3319           * @emits VASTTracker#creativeView
3320           */
3321        },
3322        {
3323          key: "trackImpression",
3324          value: function trackImpression() {
3325            if (!this.impressed) {
3326              this.impressed = true;
3327              this.trackURLs(this.ad.impressionURLTemplates);
3328              this.track("creativeView");
3329            }
3330          },
3331  
3332          /**
3333           * Send a request to the URI provided by the VAST <Error> element.
3334           * If an [ERRORCODE] macro is included, it will be substitute with errorCode.
3335           *
3336           * @param {String} errorCode - Replaces [ERRORCODE] macro. [ERRORCODE] values are listed in the VAST specification.
3337           * @param {Boolean} [isCustomCode=false] - Flag to allow custom values on error code.
3338           */
3339        },
3340        {
3341          key: "errorWithCode",
3342          value: function errorWithCode(errorCode) {
3343            var isCustomCode =
3344              arguments.length > 1 && arguments[1] !== undefined
3345                ? arguments[1]
3346                : false;
3347  
3348            this.trackURLs(
3349              this.ad.errorURLTemplates,
3350              { ERRORCODE: errorCode },
3351              { isCustomCode: isCustomCode }
3352            );
3353          },
3354  
3355          /**
3356           * Must be called when the user watched the linear creative until its end.
3357           * Calls the complete tracking URLs.
3358           *
3359           * @emits VASTTracker#complete
3360           */
3361        },
3362        {
3363          key: "complete",
3364          value: function complete() {
3365            this.track("complete");
3366          },
3367  
3368          /**
3369           * Must be called when the player or the window is closed during the ad.
3370           * Calls the `closeLinear` (in VAST 3.0) and `close` tracking URLs.
3371           *
3372           * @emits VASTTracker#closeLinear
3373           * @emits VASTTracker#close
3374           */
3375        },
3376        {
3377          key: "close",
3378          value: function close() {
3379            this.track(this.linear ? "closeLinear" : "close");
3380          },
3381  
3382          /**
3383           * Must be called when the skip button is clicked. Calls the skip tracking URLs.
3384           *
3385           * @emits VASTTracker#skip
3386           */
3387        },
3388        {
3389          key: "skip",
3390          value: function skip() {
3391            this.track("skip");
3392          },
3393  
3394          /**
3395           * Must be called when the user clicks on the creative.
3396           * It calls the tracking URLs and emits a 'clickthrough' event with the resolved
3397           * clickthrough URL when done.
3398           *
3399           * @param {String} [fallbackClickThroughURL=null] - an optional clickThroughURL template that could be used as a fallback
3400           * @emits VASTTracker#clickthrough
3401           */
3402        },
3403        {
3404          key: "click",
3405          value: function click() {
3406            var fallbackClickThroughURL =
3407              arguments.length > 0 && arguments[0] !== undefined
3408                ? arguments[0]
3409                : null;
3410  
3411            if (
3412              this.clickTrackingURLTemplates &&
3413              this.clickTrackingURLTemplates.length
3414            ) {
3415              this.trackURLs(this.clickTrackingURLTemplates);
3416            }
3417  
3418            // Use the provided fallbackClickThroughURL as a fallback
3419            var clickThroughURLTemplate =
3420              this.clickThroughURLTemplate || fallbackClickThroughURL;
3421  
3422            if (clickThroughURLTemplate) {
3423              var variables = this.linear
3424                ? { CONTENTPLAYHEAD: this.progressFormatted() }
3425                : {};
3426              var clickThroughURL = util.resolveURLTemplates(
3427                [clickThroughURLTemplate],
3428                variables
3429              )[0];
3430  
3431              this.emit("clickthrough", clickThroughURL);
3432            }
3433          },
3434  
3435          /**
3436           * Calls the tracking URLs for the given eventName and emits the event.
3437           *
3438           * @param {String} eventName - The name of the event.
3439           * @param {Boolean} [once=false] - Boolean to define if the event has to be tracked only once.
3440           */
3441        },
3442        {
3443          key: "track",
3444          value: function track(eventName) {
3445            var once =
3446              arguments.length > 1 && arguments[1] !== undefined
3447                ? arguments[1]
3448                : false;
3449  
3450            // closeLinear event was introduced in VAST 3.0
3451            // Fallback to vast 2.0 close event if necessary
3452            if (
3453              eventName === "closeLinear" &&
3454              !this.trackingEvents[eventName] &&
3455              this.trackingEvents["close"]
3456            ) {
3457              eventName = "close";
3458            }
3459  
3460            var trackingURLTemplates = this.trackingEvents[eventName];
3461            var isAlwaysEmitEvent = this.emitAlwaysEvents.indexOf(eventName) > -1;
3462  
3463            if (trackingURLTemplates) {
3464              this.emit(eventName, "");
3465              this.trackURLs(trackingURLTemplates);
3466            } else if (isAlwaysEmitEvent) {
3467              this.emit(eventName, "");
3468            }
3469  
3470            if (once) {
3471              delete this.trackingEvents[eventName];
3472              if (isAlwaysEmitEvent) {
3473                this.emitAlwaysEvents.splice(
3474                  this.emitAlwaysEvents.indexOf(eventName),
3475                  1
3476                );
3477              }
3478            }
3479          },
3480  
3481          /**
3482           * Calls the tracking urls templates with the given variables.
3483           *
3484           * @param {Array} URLTemplates - An array of tracking url templates.
3485           * @param {Object} [variables={}] - An optional Object of parameters to be used in the tracking calls.
3486           * @param {Object} [options={}] - An optional Object of options to be used in the tracking calls.
3487           */
3488        },
3489        {
3490          key: "trackURLs",
3491          value: function trackURLs(URLTemplates) {
3492            var variables =
3493              arguments.length > 1 && arguments[1] !== undefined
3494                ? arguments[1]
3495                : {};
3496            var options =
3497              arguments.length > 2 && arguments[2] !== undefined
3498                ? arguments[2]
3499                : {};
3500  
3501            if (this.linear) {
3502              if (
3503                this.creative &&
3504                this.creative.mediaFiles &&
3505                this.creative.mediaFiles[0] &&
3506                this.creative.mediaFiles[0].fileURL
3507              ) {
3508                variables["ASSETURI"] = this.creative.mediaFiles[0].fileURL;
3509              }
3510              variables["CONTENTPLAYHEAD"] = this.progressFormatted();
3511            }
3512  
3513            util.track(URLTemplates, variables, options);
3514          },
3515  
3516          /**
3517           * Formats time progress in a readable string.
3518           *
3519           * @return {String}
3520           */
3521        },
3522        {
3523          key: "progressFormatted",
3524          value: function progressFormatted() {
3525            var seconds = parseInt(this.progress);
3526            var h = seconds / (60 * 60);
3527            if (h.length < 2) {
3528              h = "0" + h;
3529            }
3530            var m = (seconds / 60) % 60;
3531            if (m.length < 2) {
3532              m = "0" + m;
3533            }
3534            var s = seconds % 60;
3535            if (s.length < 2) {
3536              s = "0" + m;
3537            }
3538            var ms = parseInt((this.progress - seconds) * 100);
3539            return h + ":" + m + ":" + s + "." + ms;
3540          },
3541        },
3542      ]);
3543      return VASTTracker;
3544    })(EventEmitter);
3545  
3546    exports.VASTClient = VASTClient;
3547    exports.VASTParser = VASTParser;
3548    exports.VASTTracker = VASTTracker;
3549  
3550    return exports;
3551  })({});

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.