1/** 2 * Patched by USH Team for lazy-loading feature. 3 * Check <filename>.original.js for original source code. 4*/ 5 6(function (global, factory) { 7 global = global || self; 8 global.videojsIma = factory(); 9}(window, (function () { 10 'use strict'; 11 12 var videojs = window.videojs; 13 14 var version = "6.7.0"; 15 16 /* 17 * Implements the public API available in `player.ads` as well as application state. 18 */ 19 function getAds(player) { 20 return { 21 disableNextSnapshotRestore: false, 22 // This is true if we have finished actual content playback but haven't 23 // dealt with postrolls and officially ended yet 24 _contentEnding: false, 25 // This is set to true if the content has officially ended at least once. 26 // After that, the user can seek backwards and replay content, but _contentHasEnded 27 // remains true. 28 _contentHasEnded: false, 29 // Tracks if loadstart has happened yet for the initial source. It is not reset 30 // on source changes because loadstart is the event that signals to the ad plugin 31 // that the source has changed. Therefore, no special signaling is needed to know 32 // that there has been one for subsequent sources. 33 _hasThereBeenALoadStartDuringPlayerLife: false, 34 // Tracks if loadeddata has happened yet for the current source. 35 _hasThereBeenALoadedData: false, 36 // Tracks if loadedmetadata has happened yet for the current source. 37 _hasThereBeenALoadedMetaData: false, 38 // Are we after startLinearAdMode and before endLinearAdMode? 39 _inLinearAdMode: false, 40 // Should we block calls to play on the content player? 41 _shouldBlockPlay: false, 42 // Was play blocked by the plugin's playMiddleware feature? 43 _playBlocked: false, 44 // Tracks whether play has been requested for this source, 45 // either by the play method or user interaction 46 _playRequested: false, 47 // This is an estimation of the current ad type being played 48 // This is experimental currently. Do not rely on its presence or behavior! 49 adType: null, 50 VERSION: version, 51 reset: function reset() { 52 player.ads.disableNextSnapshotRestore = false; 53 player.ads._contentEnding = false; 54 player.ads._contentHasEnded = false; 55 player.ads.snapshot = null; 56 player.ads.adType = null; 57 player.ads._hasThereBeenALoadedData = false; 58 player.ads._hasThereBeenALoadedMetaData = false; 59 player.ads._cancelledPlay = false; 60 player.ads._shouldBlockPlay = false; 61 player.ads._playBlocked = false; 62 player.ads.nopreroll_ = false; 63 player.ads.nopostroll_ = false; 64 player.ads._playRequested = false; 65 }, 66 // Call this when an ad response has been received and there are 67 // linear ads ready to be played. 68 startLinearAdMode: function startLinearAdMode() { 69 player.ads._state.startLinearAdMode(); 70 }, 71 // Call this when a linear ad pod has finished playing. 72 endLinearAdMode: function endLinearAdMode() { 73 player.ads._state.endLinearAdMode(); 74 }, 75 // Call this when an ad response has been received but there are no 76 // linear ads to be played (i.e. no ads available, or overlays). 77 // This has no effect if we are already in an ad break. Always 78 // use endLinearAdMode() to exit from linear ad-playback state. 79 skipLinearAdMode: function skipLinearAdMode() { 80 player.ads._state.skipLinearAdMode(); 81 },
vendor: 5,848 bytes, lines 82-198
82 // With no arguments, returns a boolean value indicating whether or not 83 // contrib-ads is set to treat ads as stitched with content in a single 84 // stream. With arguments, treated as a setter, but this behavior is 85 // deprecated. 86 stitchedAds: function stitchedAds(arg) { 87 if (arg !== undefined) { 88 videojs.log.warn('Using player.ads.stitchedAds() as a setter is deprecated, ' + 'it should be set as an option upon initialization of contrib-ads.'); // Keep the private property and the settings in sync. When this 89 // setter is removed, we can probably stop using the private property. 90 91 this.settings.stitchedAds = !!arg; 92 } 93 94 return this.settings.stitchedAds; 95 }, 96 // Returns whether the video element has been modified since the 97 // snapshot was taken. 98 // We test both src and currentSrc because changing the src attribute to a URL that 99 // AdBlocker is intercepting doesn't update currentSrc. 100 videoElementRecycled: function videoElementRecycled() { 101 if (player.ads.shouldPlayContentBehindAd(player)) { 102 return false; 103 } 104 105 if (!this.snapshot) { 106 throw new Error('You cannot use videoElementRecycled while there is no snapshot.'); 107 } 108 109 var srcChanged = player.tech_.src() !== this.snapshot.src; 110 var currentSrcChanged = player.currentSrc() !== this.snapshot.currentSrc; 111 return srcChanged || currentSrcChanged; 112 }, 113 // Returns a boolean indicating if given player is in live mode. 114 // One reason for this: https://github.com/videojs/video.js/issues/3262 115 // Also, some live content can have a duration. 116 isLive: function isLive(somePlayer) { 117 if (somePlayer === void 0) { 118 somePlayer = player; 119 } 120 121 if (typeof somePlayer.ads.settings.contentIsLive === 'boolean') { 122 return somePlayer.ads.settings.contentIsLive; 123 } else if (somePlayer.duration() === Infinity) { 124 return true; 125 } else if (videojs.browser.IOS_VERSION === '8' && somePlayer.duration() === 0) { 126 return true; 127 } 128 129 return false; 130 }, 131 // Return true if content playback should mute and continue during ad breaks. 132 // This is only done during live streams on platforms where it's supported. 133 // This improves speed and accuracy when returning from an ad break. 134 shouldPlayContentBehindAd: function shouldPlayContentBehindAd(somePlayer) { 135 if (somePlayer === void 0) { 136 somePlayer = player; 137 } 138 139 if (!somePlayer) { 140 throw new Error('shouldPlayContentBehindAd requires a player as a param'); 141 } else if (!somePlayer.ads.settings.liveCuePoints) { 142 return false; 143 } else { 144 return !videojs.browser.IS_IOS && !videojs.browser.IS_ANDROID && somePlayer.duration() === Infinity; 145 } 146 }, 147 // Return true if the ads plugin should save and restore snapshots of the 148 // player state when moving into and out of ad mode. 149 shouldTakeSnapshots: function shouldTakeSnapshots(somePlayer) { 150 if (somePlayer === void 0) { 151 somePlayer = player; 152 } 153 154 return !this.shouldPlayContentBehindAd(somePlayer) && !this.stitchedAds(); 155 }, 156 // Returns true if player is in ad mode. 157 // 158 // Ad mode definition: 159 // If content playback is blocked by the ad plugin. 160 // 161 // Examples of ad mode: 162 // 163 // * Waiting to find out if an ad is going to play while content would normally be 164 // playing. 165 // * Waiting for an ad to start playing while content would normally be playing. 166 // * An ad is playing (even if content is also playing) 167 // * An ad has completed and content is about to resume, but content has not resumed 168 // yet. 169 // 170 // Examples of not ad mode: 171 // 172 // * Content playback has not been requested 173 // * Content playback is paused 174 // * An asynchronous ad request is ongoing while content is playing 175 // * A non-linear ad is active 176 isInAdMode: function isInAdMode() { 177 return this._state.isAdState(); 178 }, 179 // Returns true if in ad mode but an ad break hasn't started yet. 180 isWaitingForAdBreak: function isWaitingForAdBreak() { 181 return this._state.isWaitingForAdBreak(); 182 }, 183 // Returns true if content is resuming after an ad. This is part of ad mode. 184 isContentResuming: function isContentResuming() { 185 return this._state.isContentResuming(); 186 }, 187 // Deprecated because the name was misleading. Use inAdBreak instead. 188 isAdPlaying: function isAdPlaying() { 189 return this._state.inAdBreak(); 190 }, 191 // Returns true if an ad break is ongoing. This is part of ad mode. 192 // An ad break is the time between startLinearAdMode and endLinearAdMode. 193 inAdBreak: function inAdBreak() { 194 return this._state.inAdBreak(); 195 }, 196 197 /* 198 * Remove the poster attribute from the video element te
vendor: 15,835 bytes, lines 198-560
198ch, if present. When 199 * reusing a video element for multiple videos, the poster image will briefly 200 * reappear while the new source loads. Removing the attribute ahead of time 201 * prevents the poster from showing up between videos. 202 * 203 * @param {Object} player The videojs player object 204 */ 205 removeNativePoster: function removeNativePoster() { 206 var tech = player.$('.vjs-tech'); 207 208 if (tech) { 209 tech.removeAttribute('poster'); 210 } 211 }, 212 debug: function debug() { 213 if (this.settings.debug) { 214 for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { 215 args[_key] = arguments[_key]; 216 } 217 218 if (args.length === 1 && typeof args[0] === 'string') { 219 videojs.log('ADS: ' + args[0]); 220 } else { 221 videojs.log.apply(videojs, ['ADS:'].concat(args)); 222 } 223 } 224 } 225 }; 226 } 227 228 /* 229 The goal of this feature is to make player events work as an integrator would 230 expect despite the presense of ads. For example, an integrator would expect 231 an `ended` event to happen once the content is ended. If an `ended` event is sent 232 as a result of a preroll ending, that is a bug. The `redispatch` method should recognize 233 such `ended` events and prefix them so they are sent as `adended`, and so on with 234 all other player events. 235 */ 236 // Cancel an event. 237 // Video.js wraps native events. This technique stops propagation for the Video.js event 238 // (AKA player event or wrapper event) while native events continue propagating. 239 var cancelEvent = function cancelEvent(player, event) { 240 event.isImmediatePropagationStopped = function () { 241 return true; 242 }; 243 244 event.cancelBubble = true; 245 246 event.isPropagationStopped = function () { 247 return true; 248 }; 249 }; // Redispatch an event with a prefix. 250 // Cancels the event, then sends a new event with the type of the original 251 // event with the given prefix added. 252 // The inclusion of the "state" property should be removed in a future 253 // major version update with instructions to migrate any code that relies on it. 254 // It is an implementation detail and relying on it creates fragility. 255 256 257 var prefixEvent = function prefixEvent(player, prefix, event) { 258 cancelEvent(player, event); 259 player.trigger({ 260 type: prefix + event.type, 261 originalEvent: event 262 }); 263 }; // Playing event 264 // Requirements: 265 // * Normal playing event when there is no preroll 266 // * No playing event before preroll 267 // * At least one playing event after preroll 268 269 270 var handlePlaying = function handlePlaying(player, event) { 271 if (player.ads.isInAdMode()) { 272 if (player.ads.isContentResuming()) { 273 // Prefix playing event when switching back to content after postroll. 274 if (player.ads._contentEnding) { 275 prefixEvent(player, 'content', event); 276 } // Prefix all other playing events during ads. 277 278 } else { 279 prefixEvent(player, 'ad', event); 280 } 281 } 282 }; // Ended event 283 // Requirements: 284 // * A single ended event when there is no postroll 285 // * No ended event before postroll 286 // * A single ended event after postroll 287 288 289 var handleEnded = function handleEnded(player, event) { 290 if (player.ads.isInAdMode()) { 291 // Cancel ended events during content resuming. Normally we would 292 // prefix them, but `contentended` has a special meaning. In the 293 // future we'd like to rename the existing `contentended` to 294 // `readyforpostroll`, then we could remove the special `resumeended` 295 // and do a conventional content prefix here. 296 if (player.ads.isContentResuming()) { 297 cancelEvent(player, event); // Important: do not use this event outside of videojs-contrib-ads. 298 // It will be removed and your code will break. 299 // Ideally this would simply be `contentended`, but until 300 // `contentended` no longer has a special meaning it cannot be 301 // changed. 302 303 player.trigger('resumeended'); // Ad prefix in ad mode 304 } else { 305 prefixEvent(player, 'ad', event); 306 } // Prefix ended due to content ending before postroll check 307 308 } else if (!player.ads._contentHasEnded && !player.ads.stitchedAds()) { 309 // This will change to cancelEvent after the contentended deprecation 310 // period (contrib-ads 7) 311 prefixEvent(player, 'content', event); // Content ended for the first time, time to check for postrolls 312 313 player.trigger('readyforpostroll'); 314 } 315 }; // handleLoadEvent is used for loadstart, loadeddata, and loadedmetadata 316 // Requirements: 317 // * Initial event is not prefixed 318 // * Event due to ad loading is prefixed 319 // * Event due to content source change is not prefixed 320 // * Event due to content resuming is prefixed 321 322 323 var handleLoadEvent = function handleLoadEvent(player, event) { 324 // Initial event 325 if (event.type === 'loadstart' && !player.ads._hasThereBeenALoadStartDuringPlayerLife || event.type === 'loadeddata' && !player.ads._hasThereBeenALoadedData || event.type === 'loadedmetadata' && !player.ads._hasThereBeenALoadedMetaData) { 326 return; // Ad playing 327 } else if (player.ads.inAdBreak()) { 328 prefixEvent(player, 'ad', event); // Source change 329 } else if (player.currentSrc() !== player.ads.contentSrc) { 330 return; // Content resuming 331 } else { 332 prefixEvent(player, 'content', event); 333 } 334 }; // Play event 335 // Requirements: 336 // * Play events have the "ad" prefix when an ad is playing 337 // * Play events have the "content" prefix when content is resuming 338 // Play requests are unique because they represent user intention to play. They happen 339 // because the user clicked play, or someone called player.play(), etc. It could happen 340 // multiple times during ad loading, regardless of where we are in the process. With our 341 // current architecture, this could cause the content to start playing. 342 // Therefore, contrib-ads must always either: 343 // - cancelContentPlay if there is any possible chance the play caused the 344 // content to start playing, even if we are technically in ad mode. In order for 345 // that to happen, play events need to be unprefixed until the last possible moment. 346 // - use playMiddleware to stop the play from reaching the Tech so there is no risk 347 // of the content starting to play. 348 // Currently, playMiddleware is only supported on desktop browsers with 349 // video.js after version 6.7.1. 350 351 352 var handlePlay = function handlePlay(player, event) { 353 if (player.ads.inAdBreak()) { 354 prefixEvent(player, 'ad', event); // Content resuming 355 } else if (player.ads.isContentResuming()) { 356 prefixEvent(player, 'content', event); 357 } 358 }; // Handle a player event, either by redispatching it with a prefix, or by 359 // letting it go on its way without any meddling. 360 361 362 function redispatch(event) { 363 // Events with special treatment 364 if (event.type === 'playing') { 365 handlePlaying(this, event); 366 } else if (event.type === 'ended') { 367 handleEnded(this, event); 368 } else if (event.type === 'loadstart' || event.type === 'loadeddata' || event.type === 'loadedmetadata') { 369 handleLoadEvent(this, event); 370 } else if (event.type === 'play') { 371 handlePlay(this, event); // Standard handling for all other events 372 } else if (this.ads.isInAdMode()) { 373 if (this.ads.isContentResuming()) { 374 // Event came from snapshot restore after an ad, use "content" prefix 375 prefixEvent(this, 'content', event); 376 } else { 377 // Event came from ad playback, use "ad" prefix 378 prefixEvent(this, 'ad', event); 379 } 380 } 381 } 382 383 /* 384 This feature sends a `contentupdate` event when the player source changes. 385 */ 386 // Start sending contentupdate events 387 function initializeContentupdate(player) { 388 // Keep track of the current content source 389 // If you want to change the src of the video without triggering 390 // the ad workflow to restart, you can update this variable before 391 // modifying the player's source 392 player.ads.contentSrc = player.currentSrc(); 393 player.ads._seenInitialLoadstart = false; // Check if a new src has been set, if so, trigger contentupdate 394 395 var checkSrc = function checkSrc() { 396 if (!player.ads.inAdBreak()) { 397 var src = player.currentSrc(); 398 399 if (src !== player.ads.contentSrc) { 400 if (player.ads._seenInitialLoadstart) { 401 player.trigger({ 402 type: 'contentchanged' 403 }); 404 } 405 406 player.trigger({ 407 type: 'contentupdate', 408 oldValue: player.ads.contentSrc, 409 newValue: src 410 }); 411 player.ads.contentSrc = src; 412 } 413 414 player.ads._seenInitialLoadstart = true; 415 } 416 }; // loadstart reliably indicates a new src has been set 417 418 419 player.on('loadstart', checkSrc); 420 } 421 422 /* 423 This feature provides an optional method for ad plugins to insert run-time values 424 into an ad server URL or configuration. 425 */ 426 427 var uriEncodeIfNeeded = function uriEncodeIfNeeded(value, uriEncode) { 428 if (uriEncode) { 429 return encodeURIComponent(value); 430 } 431 432 return value; 433 }; // Add custom field macros to macros object 434 // based on given name for custom fields property of mediainfo object. 435 436 437 var customFields = function customFields(mediainfo, macros, customFieldsName) { 438 if (mediainfo && mediainfo[customFieldsName]) { 439 var fields = mediainfo[customFieldsName]; 440 var fieldNames = Object.keys(fields); 441 442 for (var i = 0; i < fieldNames.length; i++) { 443 var tag = '{mediainfo.' + customFieldsName + '.' + fieldNames[i] + '}'; 444 macros[tag] = fields[fieldNames[i]]; 445 } 446 } 447 }; // Public method that ad plugins use for ad macros. 448 // "string" is any string with macros to be replaced 449 // "uriEncode" if true will uri encode macro values when replaced 450 // "customMacros" is a object with custom macros and values to map them to 451 // - For example: {'{five}': 5} 452 // Return value is is "string" with macros replaced 453 // - For example: adMacroReplacement('{player.id}') returns a string of the player id 454 455 456 function adMacroReplacement(string, uriEncode, customMacros) { 457 var _this = this; 458 459 var defaults = {}; // Get macros with defaults e.g. {x=y}, store values and replace with standard macros 460 461 string = string.replace(/{([^}=]+)=([^}]+)}/g, function (match, name, defaultVal) { 462 defaults["{" + name + "}"] = defaultVal; 463 return "{" + name + "}"; 464 }); 465 466 if (uriEncode === undefined) { 467 uriEncode = false; 468 } 469 470 var macros = {}; 471 472 if (customMacros !== undefined) { 473 macros = customMacros; 474 } // Static macros 475 476 477 macros['{player.id}'] = this.options_['data-player']; 478 macros['{player.height}'] = this.currentHeight(); 479 macros['{player.width}'] = this.currentWidth(); 480 macros['{mediainfo.id}'] = this.mediainfo ? this.mediainfo.id : ''; 481 macros['{mediainfo.name}'] = this.mediainfo ? this.mediainfo.name : ''; 482 macros['{mediainfo.duration}'] = this.mediainfo ? this.mediainfo.duration : ''; 483 macros['{player.duration}'] = this.duration(); 484 macros['{player.pageUrl}'] = videojs.dom.isInFrame() ? document.referrer : window.location.href; 485 macros['{playlistinfo.id}'] = this.playlistinfo ? this.playlistinfo.id : ''; 486 macros['{playlistinfo.name}'] = this.playlistinfo ? this.playlistinfo.name : ''; 487 macros['{timestamp}'] = new Date().getTime(); 488 macros['{document.referrer}'] = document.referrer; 489 macros['{window.location.href}'] = window.location.href; 490 macros['{random}'] = Math.floor(Math.random() * 1000000000000); 491 ['description', 'tags', 'reference_id', 'ad_keys'].forEach(function (prop) { 492 if (_this.mediainfo && _this.mediainfo[prop]) { 493 macros["{mediainfo." + prop + "}"] = _this.mediainfo[prop]; 494 } else if (defaults["{mediainfo." + prop + "}"]) { 495 macros["{mediainfo." + prop + "}"] = defaults["{mediainfo." + prop + "}"]; 496 } else { 497 macros["{mediainfo." + prop + "}"] = ''; 498 } 499 }); // Custom fields in mediainfo 500 501 customFields(this.mediainfo, macros, 'custom_fields'); 502 customFields(this.mediainfo, macros, 'customFields'); // Go through all the replacement macros and apply them to the string. 503 // This will replace all occurrences of the replacement macros. 504 505 for (var i in macros) { 506 string = string.split(i).join(uriEncodeIfNeeded(macros[i], uriEncode)); 507 } // Page variables 508 509 510 string = string.replace(/{pageVariable\.([^}]+)}/g, function (match, name) { 511 var value; 512 var context = window; 513 var names = name.split('.'); // Iterate down multiple levels of selector without using eval 514 // This makes things like pageVariable.foo.bar work 515 516 for (var _i = 0; _i < names.length; _i++) { 517 if (_i === names.length - 1) { 518 value = context[names[_i]]; 519 } else { 520 context = context[names[_i]]; 521 } 522 } 523 524 var type = typeof value; // Only allow certain types of values. Anything else is probably a mistake. 525 526 if (value === null) { 527 return 'null'; 528 } else if (value === undefined) { 529 if (defaults["{pageVariable." + name + "}"]) { 530 return defaults["{pageVariable." + name + "}"]; 531 } 532 533 videojs.log.warn("Page variable \"" + name + "\" not found"); 534 return ''; 535 } else if (type !== 'string' && type !== 'number' && type !== 'boolean') { 536 videojs.log.warn("Page variable \"" + name + "\" is not a supported type"); 537 return ''; 538 } 539 540 return uriEncodeIfNeeded(String(value), uriEncode); 541 }); // Replace defaults 542 543 for (var defaultVal in defaults) { 544 string = string.replace(defaultVal, defaults[defaultVal]); 545 } 546 547 return string; 548 } 549 550 /* 551 * This feature allows metadata text tracks to be manipulated once available 552 * @see processMetadataTracks. 553 * It also allows ad implementations to leverage ad cues coming through 554 * text tracks, @see processAdTrack 555 **/ 556 var cueTextTracks = {}; 557 /* 558 * This feature allows metadata text tracks to be manipulated once they are available, 559 * usually after the 'loadstart' event is observed on the player 560 * @param player A reference to a player
vendor: 9,417 bytes, lines 561-816
561 * @param processMetadataTrack A callback that performs some operations on a 562 * metadata text track 563 **/ 564 565 cueTextTracks.processMetadataTracks = function (player, processMetadataTrack) { 566 var tracks = player.textTracks(); 567 568 var setModeAndProcess = function setModeAndProcess(track) { 569 if (track.kind === 'metadata') { 570 player.ads.cueTextTracks.setMetadataTrackMode(track); 571 processMetadataTrack(player, track); 572 } 573 }; // Text tracks are available 574 575 576 for (var i = 0; i < tracks.length; i++) { 577 setModeAndProcess(tracks[i]); 578 } // Wait until text tracks are added 579 580 581 tracks.addEventListener('addtrack', function (event) { 582 setModeAndProcess(event.track); 583 }); 584 }; 585 /* 586 * Sets the track mode to one of 'disabled', 'hidden' or 'showing' 587 * @see https://github.com/videojs/video.js/blob/master/docs/guides/text-tracks.md 588 * Default behavior is to do nothing, @override if this is not desired 589 * @param track The text track to set the mode on 590 */ 591 592 593 cueTextTracks.setMetadataTrackMode = function (track) { 594 return; 595 }; 596 /* 597 * Determines whether cue is an ad cue and returns the cue data. 598 * @param player A reference to the player 599 * @param cue The full cue object 600 * Returns the given cue by default @override if futher processing is required 601 * @return {Object} a useable ad cue or null if not supported 602 **/ 603 604 605 cueTextTracks.getSupportedAdCue = function (player, cue) { 606 return cue; 607 }; 608 /* 609 * Defines whether a cue is supported or not, potentially 610 * based on the player settings 611 * @param player A reference to the player 612 * @param cue The cue to be checked 613 * Default behavior is to return true, @override if this is not desired 614 * @return {Boolean} 615 */ 616 617 618 cueTextTracks.isSupportedAdCue = function (player, cue) { 619 return true; 620 }; 621 /* 622 * Gets the id associated with a cue. 623 * @param cue The cue to extract an ID from 624 * @returns The first occurance of 'id' in the object, 625 * @override if this is not the desired cue id 626 **/ 627 628 629 cueTextTracks.getCueId = function (player, cue) { 630 return cue.id; 631 }; 632 /* 633 * Checks whether a cue has already been used 634 * @param cueId The Id associated with a cue 635 **/ 636 637 638 var cueAlreadySeen = function cueAlreadySeen(player, cueId) { 639 return cueId !== undefined && player.ads.includedCues[cueId]; 640 }; 641 /* 642 * Indicates that a cue has been used 643 * @param cueId The Id associated with a cue 644 **/ 645 646 647 var setCueAlreadySeen = function setCueAlreadySeen(player, cueId) { 648 if (cueId !== undefined && cueId !== '') { 649 player.ads.includedCues[cueId] = true; 650 } 651 }; 652 /* 653 * This feature allows ad metadata tracks to be manipulated in ad implementations 654 * @param player A reference to the player 655 * @param cues The set of cues to work with 656 * @param processCue A method that uses a cue to make some 657 * ad request in the ad implementation 658 * @param [cancelAdsHandler] A method that dynamically cancels ads in the ad implementation 659 **/ 660 661 662 cueTextTracks.processAdTrack = function (player, cues, processCue, cancelAdsHandler) { 663 player.ads.includedCues = {}; // loop over set of cues 664 665 for (var i = 0; i < cues.length; i++) { 666 var cue = cues[i]; 667 var cueData = this.getSupportedAdCue(player, cue); // Exit if this is not a supported cue 668 669 if (!this.isSupportedAdCue(player, cue)) { 670 videojs.log.warn('Skipping as this is not a supported ad cue.', cue); 671 return; 672 } // Continue processing supported cue 673 674 675 var cueId = this.getCueId(player, cue); 676 var startTime = cue.startTime; // Skip ad if cue was already used 677 678 if (cueAlreadySeen(player, cueId)) { 679 videojs.log('Skipping ad already seen with ID ' + cueId); 680 return; 681 } // Optional dynamic ad cancellation 682 683 684 if (cancelAdsHandler) { 685 cancelAdsHandler(player, cueData, cueId, startTime); 686 } // Process cue as an ad cue 687 688 689 processCue(player, cueData, cueId, startTime); // Indicate that this cue has been used 690 691 setCueAlreadySeen(player, cueId); 692 } 693 }; 694 695 function initCancelContentPlay(player, debug) { 696 if (debug) { 697 videojs.log('Using cancelContentPlay to block content playback'); 698 } // Listen to play events to "cancel" them afterward 699 700 701 player.on('play', cancelContentPlay); 702 } 703 /* 704 This feature makes sure the player is paused during ad loading. 705 706 It does this by pausing the player immediately after a "play" where ads will be requested, 707 then signalling that we should play after the ad is done. 708 */ 709 710 function cancelContentPlay() { 711 // this function is in the player's context 712 if (this.ads._shouldBlockPlay === false) { 713 // Only block play if the ad plugin is in a state when content 714 // playback should be blocked. This currently means during 715 // BeforePrerollState and PrerollState. 716 return; 717 } // pause playback so ads can be handled. 718 719 720 if (!this.paused()) { 721 this.ads.debug('Playback was canceled by cancelContentPlay'); 722 this.pause(); 723 } // When the 'content-playback' state is entered, this will let us know to play. 724 // This is needed if there is no preroll or if it errors, times out, etc. 725 726 727 this.ads._cancelledPlay = true; 728 } 729 730 var obj = {}; // This reference allows videojs to be mocked in unit tests 731 // while still using the available videojs import in the source code 732 // @see obj.testHook 733 734 var videojsReference = videojs; 735 /** 736 * Checks if middleware mediators are available and 737 * can be used on this platform. 738 * Currently we can only use mediators on desktop platforms. 739 */ 740 741 obj.isMiddlewareMediatorSupported = function () { 742 if (videojsReference.browser.IS_IOS || videojsReference.browser.IS_ANDROID) { 743 return false; 744 } else if ( // added when middleware was introduced in video.js 745 videojsReference.use && // added when mediators were introduced in video.js 746 videojsReference.middleware && videojsReference.middleware.TERMINATOR) { 747 return true; 748 } 749 750 return false; 751 }; 752 753 obj.playMiddleware = function (player) { 754 return { 755 setSource: function setSource(srcObj, next) { 756 next(null, srcObj); 757 }, 758 callPlay: function callPlay() { 759 // Block play calls while waiting for an ad, only if this is an 760 // ad supported player 761 if (player.ads && player.ads._shouldBlockPlay === true) { 762 player.ads.debug('Using playMiddleware to block content playback'); 763 player.ads._playBlocked = true; 764 return videojsReference.middleware.TERMINATOR; 765 } 766 }, 767 play: function play(terminated, playPromise) { 768 if (player.ads && player.ads._playBlocked && terminated) { 769 player.ads.debug('Play call to Tech was terminated.'); // Trigger play event to match the user's intent to play. 770 // The call to play on the Tech has been blocked, so triggering 771 // the event on the Player will not affect the Tech's playback state. 772 773 player.trigger('play'); // At this point the player has technically started 774 775 player.addClass('vjs-has-started'); // Reset playBlocked 776 777 player.ads._playBlocked = false; // Safari issues a pause event when autoplay is blocked but other browsers 778 // do not, so we send a pause for consistency in those cases. This keeps the 779 // play button in the correct state if play is rejected. 780 } else if (playPromise && playPromise.catch) { 781 playPromise.catch(function (e) { 782 if (e.name === 'NotAllowedError' && !videojs.browser.IS_SAFARI) { 783 player.trigger('pause'); 784 } 785 }); 786 } 787 } 788 }; 789 }; 790 791 obj.testHook = function (testVjs) { 792 videojsReference = testVjs; 793 }; 794 795 var playMiddleware = obj.playMiddleware, 796 isMiddlewareMediatorSupported = obj.isMiddlewareMediatorSupported; 797 /** 798 * Whether or not this copy of Video.js has the ads plugin. 799 * 800 * @return {boolean} 801 * If `true`, has the plugin. `false` otherwise. 802 */ 803 804 var hasAdsPlugin = function hasAdsPlugin() { 805 // Video.js 6 and 7 have a getPlugin method. 806 if (videojs.getPlugin) { 807 return Boolean(videojs.getPlugin('ads')); 808 } // Video.js 5 does not have a getPlugin method, so check the player prototype. 809 810 811 var Player = videojs.getComponent('Player'); 812 return Boolean(Player && Player.prototype.ads); 813 }; 814 /** 815 * Register contrib-ads with Video.js, but provide protection for duplicate 816 * copies of the plugin. This could happen if, for example, a stitched ads
817 * plugin and a client-side ads plugin are included simultaneously with their 818 * own copies of contrib-ads. 819 * 820 * If contrib-ads detects a pre-existing duplicate, it will not register 821 * itself. 822 * 823 * Ad plugins using contrib-ads and anticipating that this could come into 824 * effect should verify that the contrib-ads they are using is of a compatible 825 * version. 826 * 827 * @param {Function} contribAdsPlugin 828 * The plugin function. 829 * 830 * @return {boolean} 831 * When `true`, the plugin was registered. When `false`, the plugin 832 * was not registered. 833 */ 834 835 836 function register(contribAdsPlugin) { 837 // If the ads plugin already exists, do not overwrite it. 838 if (hasAdsPlugin(videojs)) { 839 return false; 840 } // Cross-compatibility with Video.js 6/7 and 5. 841 842 843 var registerPlugin = videojs.registerPlugin || videojs.plugin; // Register this plugin with Video.js. 844 845 registerPlugin('ads', contribAdsPlugin); // Register the play middleware with Video.js on script execution, 846 // to avoid a new playMiddleware factory being added for each player. 847 // The `usingContribAdsMiddleware_` flag is used to ensure that we only ever 848 // register the middleware once - despite the ability to de-register and 849 // re-register the plugin itself. 850 851 if (isMiddlewareMediatorSupported() && !videojs.usingContribAdsMiddleware_) { 852 // Register the play middleware 853 videojs.use('*', playMiddleware); 854 videojs.usingContribAdsMiddleware_ = true; 855 videojs.log.debug('Play middleware has been registered with videojs'); 856 } 857 858 return true; 859 } 860 861 var States = 862 /*#__PURE__*/ 863 function () { 864 function States() {} 865 866 States.getState = function getState(name) { 867 if (!name) { 868 return; 869 } 870 871 if (States.states_ && States.states_[name]) { 872 return States.states_[name]; 873 } 874 }; 875 876 States.registerState = function registerState(name, StateToRegister) { 877 if (typeof name !== 'string' || !name) { 878 throw new Error("Illegal state name, \"" + name + "\"; must be a non-empty string."); 879 } 880 881 if (!States.states_) { 882 States.states_ = {}; 883 } 884 885 States.states_[name] = StateToRegister; 886 return StateToRegister; 887 }; 888 889 return States; 890 }(); 891 892 var State = 893 /*#__PURE__*/ 894 function () { 895 State._getName = function _getName() { 896 return 'Anonymous State'; 897 }; 898 899 function State(player) { 900 this.player = player; 901 } 902 /* 903 * This is the only allowed way to perform state transitions. State transitions usually 904 * happen in player event handlers. They can also happen recursively in `init`. They 905 * should _not_ happen in `cleanup`. 906 */ 907 908 909 var _proto = State.prototype; 910 911 _proto.transitionTo = function transitionTo(NewState) { 912 var player = this.player; // Since State is an abstract class, this will refer to 913 // the state that is extending this class 914 915 this.cleanup(player); 916 var newState = new NewState(player); 917 player.ads._state = newState; 918 player.ads.debug(this.constructor._getName() + ' -> ' + newState.constructor._getName()); 919 920 for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { 921 args[_key - 1] = arguments[_key]; 922 } 923 924 newState.init.apply(newState, [player].concat(args)); 925 } 926 /* 927 * Implemented by subclasses to provide initialization logic when transitioning 928 * to a new state. 929 */ 930 ; 931
vendor: 35,463 bytes, lines 932-1904
932 _proto.init = function init() {} 933 /* 934 * Implemented by subclasses to provide cleanup logic when transitioning 935 * to a new state. 936 */ 937 ; 938 939 _proto.cleanup = function cleanup() {} 940 /* 941 * Default event handlers. Different states can override these to provide behaviors. 942 */ 943 ; 944 945 _proto.onPlay = function onPlay() {}; 946 947 _proto.onPlaying = function onPlaying() {}; 948 949 _proto.onEnded = function onEnded() {}; 950 951 _proto.onAdEnded = function onAdEnded() {}; 952 953 _proto.onAdsReady = function onAdsReady() { 954 videojs.log.warn('Unexpected adsready event'); 955 }; 956 957 _proto.onAdsError = function onAdsError() {}; 958 959 _proto.onAdsCanceled = function onAdsCanceled() {}; 960 961 _proto.onAdTimeout = function onAdTimeout() {}; 962 963 _proto.onAdStarted = function onAdStarted() {}; 964 965 _proto.onContentChanged = function onContentChanged() {}; 966 967 _proto.onContentResumed = function onContentResumed() {}; 968 969 _proto.onReadyForPostroll = function onReadyForPostroll() { 970 videojs.log.warn('Unexpected readyforpostroll event'); 971 }; 972 973 _proto.onNoPreroll = function onNoPreroll() {}; 974 975 _proto.onNoPostroll = function onNoPostroll() {} 976 /* 977 * Method handlers. Different states can override these to provide behaviors. 978 */ 979 ; 980 981 _proto.startLinearAdMode = function startLinearAdMode() { 982 videojs.log.warn('Unexpected startLinearAdMode invocation ' + '(State via ' + this.constructor._getName() + ')'); 983 }; 984 985 _proto.endLinearAdMode = function endLinearAdMode() { 986 videojs.log.warn('Unexpected endLinearAdMode invocation ' + '(State via ' + this.constructor._getName() + ')'); 987 }; 988 989 _proto.skipLinearAdMode = function skipLinearAdMode() { 990 videojs.log.warn('Unexpected skipLinearAdMode invocation ' + '(State via ' + this.constructor._getName() + ')'); 991 } 992 /* 993 * Overridden by ContentState and AdState. Should not be overriden elsewhere. 994 */ 995 ; 996 997 _proto.isAdState = function isAdState() { 998 throw new Error('isAdState unimplemented for ' + this.constructor._getName()); 999 } 1000 /* 1001 * Overridden by Preroll and Postroll. Midrolls jump right into the ad break 1002 * so there is no "waiting" state for them. 1003 */ 1004 ; 1005 1006 _proto.isWaitingForAdBreak = function isWaitingForAdBreak() { 1007 return false; 1008 } 1009 /* 1010 * Overridden by Preroll, Midroll, and Postroll. 1011 */ 1012 ; 1013 1014 _proto.isContentResuming = function isContentResuming() { 1015 return false; 1016 }; 1017 1018 _proto.inAdBreak = function inAdBreak() { 1019 return false; 1020 } 1021 /* 1022 * Invoke event handler methods when events come in. 1023 */ 1024 ; 1025 1026 _proto.handleEvent = function handleEvent(type) { 1027 var player = this.player; 1028 1029 if (type === 'play') { 1030 this.onPlay(player); 1031 } else if (type === 'adsready') { 1032 this.onAdsReady(player); 1033 } else if (type === 'adserror') { 1034 this.onAdsError(player); 1035 } else if (type === 'adscanceled') { 1036 this.onAdsCanceled(player); 1037 } else if (type === 'adtimeout') { 1038 this.onAdTimeout(player); 1039 } else if (type === 'ads-ad-started') { 1040 this.onAdStarted(player); 1041 } else if (type === 'contentchanged') { 1042 this.onContentChanged(player); 1043 } else if (type === 'contentresumed') { 1044 this.onContentResumed(player); 1045 } else if (type === 'readyforpostroll') { 1046 this.onReadyForPostroll(player); 1047 } else if (type === 'playing') { 1048 this.onPlaying(player); 1049 } else if (type === 'ended') { 1050 this.onEnded(player); 1051 } else if (type === 'nopreroll') { 1052 this.onNoPreroll(player); 1053 } else if (type === 'nopostroll') { 1054 this.onNoPostroll(player); 1055 } else if (type === 'adended') { 1056 this.onAdEnded(player); 1057 } 1058 }; 1059 1060 return State; 1061 }(); 1062 1063 States.registerState('State', State); 1064 1065 function _inheritsLoose(subClass, superClass) { 1066 subClass.prototype = Object.create(superClass.prototype); 1067 subClass.prototype.constructor = subClass; 1068 subClass.__proto__ = superClass; 1069 } 1070 1071 /* 1072 * This class contains logic for all ads, be they prerolls, midrolls, or postrolls. 1073 * Primarily, this involves handling startLinearAdMode and endLinearAdMode. 1074 * It also handles content resuming. 1075 */ 1076 1077 var AdState = 1078 /*#__PURE__*/ 1079 function (_State) { 1080 _inheritsLoose(AdState, _State); 1081 1082 function AdState(player) { 1083 var _this; 1084 1085 _this = _State.call(this, player) || this; 1086 _this.contentResuming = false; 1087 _this.waitingForAdBreak = false; 1088 return _this; 1089 } 1090 /* 1091 * Overrides State.isAdState 1092 */ 1093 1094 1095 var _proto = AdState.prototype; 1096 1097 _proto.isAdState = function isAdState() { 1098 return true; 1099 } 1100 /* 1101 * We end the content-resuming process on the playing event because this is the exact 1102 * moment that content playback is no longer blocked by ads. 1103 */ 1104 ; 1105 1106 _proto.onPlaying = function onPlaying() { 1107 var ContentPlayback = States.getState('ContentPlayback'); 1108 1109 if (this.contentResuming) { 1110 this.transitionTo(ContentPlayback); 1111 } 1112 } 1113 /* 1114 * If the ad plugin does not result in a playing event when resuming content after an 1115 * ad, they should instead trigger a contentresumed event to signal that content should 1116 * resume. The main use case for this is when ads are stitched into the content video. 1117 */ 1118 ; 1119 1120 _proto.onContentResumed = function onContentResumed() { 1121 var ContentPlayback = States.getState('ContentPlayback'); 1122 1123 if (this.contentResuming) { 1124 this.transitionTo(ContentPlayback); 1125 } 1126 } 1127 /* 1128 * Check if we are in an ad state waiting for the ad plugin to start 1129 * an ad break. 1130 */ 1131 ; 1132 1133 _proto.isWaitingForAdBreak = function isWaitingForAdBreak() { 1134 return this.waitingForAdBreak; 1135 } 1136 /* 1137 * Allows you to check if content is currently resuming after an ad break. 1138 */ 1139 ; 1140 1141 _proto.isContentResuming = function isContentResuming() { 1142 return this.contentResuming; 1143 } 1144 /* 1145 * Allows you to check if an ad break is in progress. 1146 */ 1147 ; 1148 1149 _proto.inAdBreak = function inAdBreak() { 1150 return this.player.ads._inLinearAdMode === true; 1151 }; 1152 1153 return AdState; 1154 }(State); 1155 1156 States.registerState('AdState', AdState); 1157 1158 var ContentState = 1159 /*#__PURE__*/ 1160 function (_State) { 1161 _inheritsLoose(ContentState, _State); 1162 1163 function ContentState() { 1164 return _State.apply(this, arguments) || this; 1165 } 1166 1167 var _proto = ContentState.prototype; 1168 1169 /* 1170 * Overrides State.isAdState 1171 */ 1172 _proto.isAdState = function isAdState() { 1173 return false; 1174 } 1175 /* 1176 * Source change sends you back to preroll checks. contentchanged does not 1177 * fire during ad breaks, so we don't need to worry about that. 1178 */ 1179 ; 1180 1181 _proto.onContentChanged = function onContentChanged(player) { 1182 var BeforePreroll = States.getState('BeforePreroll'); 1183 var Preroll = States.getState('Preroll'); 1184 player.ads.debug('Received contentchanged event (ContentState)'); 1185 1186 if (player.paused()) { 1187 this.transitionTo(BeforePreroll); 1188 } else { 1189 this.transitionTo(Preroll, false); 1190 player.pause(); 1191 player.ads._pausedOnContentupdate = true; 1192 } 1193 }; 1194 1195 return ContentState; 1196 }(State); 1197 1198 States.registerState('ContentState', ContentState); 1199 1200 var ContentState$1 = States.getState('ContentState'); 1201 1202 var AdsDone = 1203 /*#__PURE__*/ 1204 function (_ContentState) { 1205 _inheritsLoose(AdsDone, _ContentState); 1206 1207 function AdsDone() { 1208 return _ContentState.apply(this, arguments) || this; 1209 } 1210 1211 /* 1212 * Allows state name to be logged even after minification. 1213 */ 1214 AdsDone._getName = function _getName() { 1215 return 'AdsDone'; 1216 } 1217 /* 1218 * For state transitions to work correctly, initialization should 1219 * happen here, not in a constructor. 1220 */ 1221 ; 1222 1223 var _proto = AdsDone.prototype; 1224 1225 _proto.init = function init(player) { 1226 // From now on, `ended` events won't be redispatched 1227 player.ads._contentHasEnded = true; 1228 // Do not trigger ended event as it's causing issues on next recommended flow 1229 // and for now we do not have a post roll ad 1230 // player.trigger('ended'); 1231 } 1232 /* 1233 * Midrolls do not play after ads are done. 1234 */ 1235 ; 1236 1237 _proto.startLinearAdMode = function startLinearAdMode() { 1238 videojs.log.warn('Unexpected startLinearAdMode invocation (AdsDone)'); 1239 }; 1240 1241 return AdsDone; 1242 }(ContentState$1); 1243 1244 States.registerState('AdsDone', AdsDone); 1245 1246 /* 1247 The snapshot feature is responsible for saving the player state before an ad, then 1248 restoring the player state after an ad. 1249 */ 1250 var tryToResumeTimeout_; 1251 /* 1252 * Returns an object that captures the portions of player state relevant to 1253 * video playback. The result of this function can be passed to 1254 * restorePlayerSnapshot with a player to return the player to the state it 1255 * was in when this function was invoked. 1256 * @param {Object} player The videojs player object 1257 */ 1258 1259 function getPlayerSnapshot(player) { 1260 var currentTime; 1261 1262 if (videojs.browser.IS_IOS && player.ads.isLive(player)) { 1263 // Record how far behind live we are 1264 if (player.seekable().length > 0) { 1265 currentTime = player.currentTime() - player.seekable().end(0); 1266 } else { 1267 currentTime = player.currentTime(); 1268 } 1269 } else { 1270 currentTime = player.currentTime(); 1271 } 1272 1273 var tech = player.$('.vjs-tech'); 1274 var tracks = player.textTracks ? player.textTracks() : []; 1275 var suppressedTracks = []; 1276 var snapshotObject = { 1277 ended: player.ended(), 1278 currentSrc: player.currentSrc(), 1279 sources: player.currentSources(), 1280 src: player.tech_.src(), 1281 currentTime: currentTime, 1282 type: player.currentType() 1283 }; 1284 1285 if (tech) { 1286 snapshotObject.style = tech.getAttribute('style'); 1287 } 1288 1289 for (var i = 0; i < tracks.length; i++) { 1290 var track = tracks[i]; 1291 suppressedTracks.push({ 1292 track: track, 1293 mode: track.mode 1294 }); 1295 track.mode = 'disabled'; 1296 } 1297 1298 snapshotObject.suppressedTracks = suppressedTracks; 1299 return snapshotObject; 1300 } 1301 /* 1302 * Attempts to modify the specified player so that its state is equivalent to 1303 * the state of the snapshot. 1304 * @param {Object} player - the videojs player object 1305 * @param {Object} snapshotObject - the player state to apply 1306 */ 1307 1308 function restorePlayerSnapshot(player, callback) { 1309 var snapshotObject = player.ads.snapshot; 1310 1311 if (callback === undefined) { 1312 callback = function callback() {}; 1313 } 1314 1315 if (player.ads.disableNextSnapshotRestore === true) { 1316 player.ads.disableNextSnapshotRestore = false; 1317 delete player.ads.snapshot; 1318 callback(); 1319 return; 1320 } // The playback tech 1321 1322 1323 var tech = player.$('.vjs-tech'); // the number of[ remaining attempts to restore the snapshot 1324 1325 var attempts = 20; 1326 var suppressedTracks = snapshotObject.suppressedTracks; 1327 var trackSnapshot; 1328 1329 var restoreTracks = function restoreTracks() { 1330 for (var i = 0; i < suppressedTracks.length; i++) { 1331 trackSnapshot = suppressedTracks[i]; 1332 trackSnapshot.track.mode = trackSnapshot.mode; 1333 } 1334 }; // Finish restoring the playback state. 1335 // This only happens if the content video element was reused for ad playback. 1336 1337 1338 var resume = function resume() { 1339 var currentTime; // Live video on iOS has special logic to try to seek to the right place after 1340 // an ad. 1341 1342 if (videojs.browser.IS_IOS && player.ads.isLive(player)) { 1343 if (snapshotObject.currentTime < 0) { 1344 // Playback was behind real time, so seek backwards to match 1345 if (player.seekable().length > 0) { 1346 currentTime = player.seekable().end(0) + snapshotObject.currentTime; 1347 } else { 1348 currentTime = player.currentTime(); 1349 } 1350 1351 player.currentTime(currentTime); 1352 } // iOS live play after restore if player was paused (would not be paused if 1353 // ad played muted behind ad) 1354 1355 1356 if (player.paused()) { 1357 var playPromise = player.play(); 1358 1359 if (playPromise && playPromise.catch) { 1360 playPromise.catch(function (error) { 1361 videojs.log.warn('Play promise rejected in IOS snapshot resume', error); 1362 }); 1363 } 1364 } // Restore the video position after an ad. 1365 // We check snapshotObject.ended because the content starts at the beginning again 1366 // after being restored. 1367 1368 } else if (snapshotObject.ended) { 1369 // For postrolls, seek to the player's current duration. 1370 // It could be different from the snapshot's currentTime due to 1371 // inaccuracy in HLS. 1372 player.currentTime(player.duration()); 1373 } else { 1374 // Prerolls and midrolls, just seek to the player time before the ad. 1375 player.currentTime(snapshotObject.currentTime); 1376 1377 var _playPromise = player.play(); 1378 1379 if (_playPromise && _playPromise.catch) { 1380 _playPromise.catch(function (error) { 1381 videojs.log.warn('Play promise rejected in snapshot resume', error); 1382 }); 1383 } 1384 } // if we added autoplay to force content loading on iOS, remove it now 1385 // that it has served its purpose 1386 1387 1388 if (player.ads.shouldRemoveAutoplay_) { 1389 player.autoplay(false); 1390 player.ads.shouldRemoveAutoplay_ = false; 1391 } 1392 }; // Determine if the video element has loaded enough of the snapshot source 1393 // to be ready to apply the rest of the state. 1394 // This only happens if the content video element was reused for ad playback. 1395 1396 1397 var tryToResume = function tryToResume() { 1398 // tryToResume can either have been called through the `contentcanplay` 1399 // event or fired through setTimeout. 1400 // When tryToResume is called, we should make sure to clear out the other 1401 // way it could've been called by removing the listener and clearing out 1402 // the timeout. 1403 player.off('contentcanplay', tryToResume); 1404 1405 if (tryToResumeTimeout_) { 1406 player.clearTimeout(tryToResumeTimeout_); 1407 } // Tech may have changed depending on the differences in sources of the 1408 // original video and that of the ad 1409 1410 1411 tech = player.el().querySelector('.vjs-tech'); 1412 1413 if (tech.readyState > 1) { 1414 // some browsers and media aren't "seekable". 1415 // readyState greater than 1 allows for seeking without exceptions 1416 return resume(); 1417 } 1418 1419 if (tech.seekable === undefined) { 1420 // if the tech doesn't expose the seekable time ranges, try to 1421 // resume playback immediately 1422 return resume(); 1423 } 1424 1425 if (tech.seekable.length > 0) { 1426 // if some period of the video is seekable, resume playback 1427 return resume(); 1428 } // delay a bit and then check again unless we're out of attempts 1429 1430 1431 if (attempts--) { 1432 player.setTimeout(tryToResume, 50); 1433 } else { 1434 try { 1435 resume(); 1436 } catch (e) { 1437 videojs.log.warn('Failed to resume the content after an advertisement', e); 1438 } 1439 } 1440 }; 1441 1442 if ('style' in snapshotObject) { 1443 // overwrite all css style properties to restore state precisely 1444 tech.setAttribute('style', snapshotObject.style || ''); 1445 } // Determine whether the player needs to be restored to its state 1446 // before ad playback began. With a custom ad display or burned-in 1447 // ads, the content player state hasn't been modified and so no 1448 // restoration is required 1449 1450 1451 if (player.ads.videoElementRecycled()) { 1452 // Snapshot restore is done, so now we're really finished. 1453 player.one('resumeended', function () { 1454 delete player.ads.snapshot; 1455 callback(); 1456 }); // on ios7, fiddling with textTracks too early will cause safari to crash 1457 1458 player.one('contentloadedmetadata', restoreTracks); // adding autoplay guarantees that Safari will load the content so we can 1459 // seek back to the correct time after ads 1460 1461 if (videojs.browser.IS_IOS && !player.autoplay()) { 1462 player.autoplay(true); // if we get here, the player was not originally configured to autoplay, 1463 // so we should remove it after it has served its purpose 1464 1465 player.ads.shouldRemoveAutoplay_ = true; 1466 } // if the src changed for ad playback, reset it 1467 1468 1469 player.src(snapshotObject.sources); // and then resume from the snapshots time once the original src has loaded 1470 // in some browsers (firefox) `canplay` may not fire correctly. 1471 // Reace the `canplay` event with a timeout. 1472 1473 player.one('contentcanplay', tryToResume); 1474 tryToResumeTimeout_ = player.setTimeout(tryToResume, 2000); 1475 } else { 1476 // if we didn't change the src, just restore the tracks 1477 restoreTracks(); // we don't need to check snapshotObject.ended here because the content video 1478 // element wasn't recycled 1479 1480 if (!player.ended()) { 1481 // the src didn't change and this wasn't a postroll 1482 // just resume playback at the current time. 1483 var playPromise = player.play(); 1484 1485 if (playPromise && playPromise.catch) { 1486 playPromise.catch(function (error) { 1487 videojs.log.warn('Play promise rejected in snapshot restore', error); 1488 }); 1489 } 1490 } // snapshot restore is complete 1491 1492 1493 delete player.ads.snapshot; 1494 callback(); 1495 } 1496 } 1497 1498 /* 1499 * Encapsulates logic for starting and ending ad breaks. An ad break 1500 * is the time between startLinearAdMode and endLinearAdMode. The ad 1501 * plugin may play 0 or more ads during this time. 1502 */ 1503 1504 function start(player) { 1505 player.ads.debug('Starting ad break'); 1506 player.ads._inLinearAdMode = true; // No longer does anything, used to move us to ad-playback 1507 1508 player.trigger('adstart'); // Capture current player state snapshot 1509 1510 if (player.ads.shouldTakeSnapshots()) { 1511 player.ads.snapshot = getPlayerSnapshot(player); 1512 } // Mute the player behind the ad 1513 1514 1515 if (player.ads.shouldPlayContentBehindAd(player)) { 1516 player.ads.preAdVolume_ = player.volume(); 1517 player.volume(0); 1518 } // Add css to the element to indicate and ad is playing. 1519 1520 1521 player.addClass('vjs-ad-playing'); // We should remove the vjs-live class if it has been added in order to 1522 // show the adprogress control bar on Android devices for falsely 1523 // determined LIVE videos due to the duration incorrectly reported as Infinity 1524 1525 if (player.hasClass('vjs-live')) { 1526 player.removeClass('vjs-live'); 1527 } // This removes the native poster so the ads don't show the content 1528 // poster if content element is reused for ad playback. 1529 1530 1531 player.ads.removeNativePoster(); 1532 } 1533 1534 function end(player, callback) { 1535 player.ads.debug('Ending ad break'); 1536 1537 if (callback === undefined) { 1538 callback = function callback() {}; 1539 } 1540 1541 player.ads.adType = null; 1542 player.ads._inLinearAdMode = false; // Signals the end of the ad break to anyone listening. 1543 1544 player.trigger('adend'); 1545 player.removeClass('vjs-ad-playing'); // We should add the vjs-live class back if the video is a LIVE video 1546 // If we dont do this, then for a LIVE Video, we will get an incorrect 1547 // styled control, which displays the time for the video 1548 1549 if (player.ads.isLive(player)) { 1550 player.addClass('vjs-live'); 1551 } // Restore snapshot 1552 1553 1554 if (player.ads.shouldTakeSnapshots()) { 1555 restorePlayerSnapshot(player, callback); // Reset the volume to pre-ad levels 1556 } else { 1557 player.volume(player.ads.preAdVolume_); 1558 callback(); 1559 } 1560 } 1561 1562 var obj$1 = { 1563 start: start, 1564 end: end 1565 }; 1566 1567 var AdState$1 = States.getState('AdState'); 1568 /* 1569 * This state encapsulates waiting for prerolls, preroll playback, and 1570 * content restoration after a preroll. 1571 */ 1572 1573 var Preroll = 1574 /*#__PURE__*/ 1575 function (_AdState) { 1576 _inheritsLoose(Preroll, _AdState); 1577 1578 function Preroll() { 1579 return _AdState.apply(this, arguments) || this; 1580 } 1581 1582 /* 1583 * Allows state name to be logged even after minification. 1584 */ 1585 Preroll._getName = function _getName() { 1586 return 'Preroll'; 1587 } 1588 /* 1589 * For state transitions to work correctly, initialization should 1590 * happen here, not in a constructor. 1591 */ 1592 ; 1593 1594 var _proto = Preroll.prototype; 1595 1596 _proto.init = function init(player, adsReady, shouldResumeToContent) { 1597 this.waitingForAdBreak = true; // Loading spinner from now until ad start or end of ad break. 1598 1599 player.addClass('vjs-ad-loading'); // If adserror, adscanceled, nopreroll or skipLinearAdMode already 1600 // ocurred, resume to content immediately 1601 1602 if (shouldResumeToContent || player.ads.nopreroll_) { 1603 return this.resumeAfterNoPreroll(player); 1604 } // Determine preroll timeout based on plugin settings 1605 1606 1607 var timeout = player.ads.settings.timeout; 1608 1609 if (typeof player.ads.settings.prerollTimeout === 'number') { 1610 timeout = player.ads.settings.prerollTimeout; 1611 } // Start the clock ticking for ad timeout 1612 1613 1614 this._timeout = player.setTimeout(function () { 1615 player.trigger('adtimeout'); 1616 }, timeout); // If adsready already happened, lets get started. Otherwise, 1617 // wait until onAdsReady. 1618 1619 if (adsReady) { 1620 this.handleAdsReady(); 1621 } else { 1622 this.adsReady = false; 1623 } 1624 } 1625 /* 1626 * Adsready event after play event. 1627 */ 1628 ; 1629 1630 _proto.onAdsReady = function onAdsReady(player) { 1631 if (!player.ads.inAdBreak()) { 1632 player.ads.debug('Received adsready event (Preroll)'); 1633 this.handleAdsReady(); 1634 } else { 1635 videojs.log.warn('Unexpected adsready event (Preroll)'); 1636 } 1637 } 1638 /* 1639 * Ad plugin is ready. Let's get started on this preroll. 1640 */ 1641 ; 1642 1643 _proto.handleAdsReady = function handleAdsReady() { 1644 this.adsReady = true; 1645 this.readyForPreroll(); 1646 } 1647 /* 1648 * Helper to call a callback only after a loadstart event. 1649 * If we start content or ads before loadstart, loadstart 1650 * will not be prefixed correctly. 1651 */ 1652 ; 1653 1654 _proto.afterLoadStart = function afterLoadStart(callback) { 1655 var player = this.player; 1656 1657 if (player.ads._hasThereBeenALoadStartDuringPlayerLife) { 1658 callback(); 1659 } else { 1660 player.ads.debug('Waiting for loadstart...'); 1661 player.one('loadstart', function () { 1662 player.ads.debug('Received loadstart event'); 1663 callback(); 1664 }); 1665 } 1666 } 1667 /* 1668 * If there is no preroll, play content instead. 1669 */ 1670 ; 1671 1672 _proto.noPreroll = function noPreroll() { 1673 var _this = this; 1674 1675 this.afterLoadStart(function () { 1676 _this.player.ads.debug('Skipping prerolls due to nopreroll event (Preroll)'); 1677 1678 _this.resumeAfterNoPreroll(_this.player); 1679 }); 1680 } 1681 /* 1682 * Fire the readyforpreroll event. If loadstart hasn't happened yet, 1683 * wait until loadstart first. 1684 */ 1685 ; 1686 1687 _proto.readyForPreroll = function readyForPreroll() { 1688 var player = this.player; 1689 this.afterLoadStart(function () { 1690 player.ads.debug('Triggered readyforpreroll event (Preroll)'); 1691 player.trigger('readyforpreroll'); 1692 }); 1693 } 1694 /* 1695 * adscanceled cancels all ads for the source. Play content now. 1696 */ 1697 ; 1698 1699 _proto.onAdsCanceled = function onAdsCanceled(player) { 1700 var _this2 = this; 1701 1702 player.ads.debug('adscanceled (Preroll)'); 1703 this.afterLoadStart(function () { 1704 _this2.resumeAfterNoPreroll(player); 1705 }); 1706 } 1707 /* 1708 * An ad error occured. Play content instead. 1709 */ 1710 ; 1711 1712 _proto.onAdsError = function onAdsError(player) { 1713 var _this3 = this; 1714 1715 videojs.log('adserror (Preroll)'); // In the future, we may not want to do this automatically. 1716 // Ad plugins should be able to choose to continue the ad break 1717 // if there was an error. 1718 1719 if (this.inAdBreak()) { 1720 player.ads.endLinearAdMode(); 1721 } else { 1722 this.afterLoadStart(function () { 1723 _this3.resumeAfterNoPreroll(player); 1724 }); 1725 } 1726 } 1727 /* 1728 * Ad plugin invoked startLinearAdMode, the ad break starts now. 1729 */ 1730 ; 1731 1732 _proto.startLinearAdMode = function startLinearAdMode() { 1733 var player = this.player; 1734 1735 if (this.adsReady && !player.ads.inAdBreak() && !this.isContentResuming()) { 1736 this.clearTimeout(player); 1737 player.ads.adType = 'preroll'; 1738 this.waitingForAdBreak = false; 1739 obj$1.start(player); // We don't need to block play calls anymore 1740 1741 player.ads._shouldBlockPlay = false; 1742 } else { 1743 videojs.log.warn('Unexpected startLinearAdMode invocation (Preroll)'); 1744 } 1745 } 1746 /* 1747 * An ad has actually started playing. 1748 * Remove the loading spinner. 1749 */ 1750 ; 1751 1752 _proto.onAdStarted = function onAdStarted(player) { 1753 player.removeClass('vjs-ad-loading'); 1754 } 1755 /* 1756 * Ad plugin invoked endLinearAdMode, the ad break ends now. 1757 */ 1758 ; 1759 1760 _proto.endLinearAdMode = function endLinearAdMode() { 1761 var player = this.player; 1762 1763 if (this.inAdBreak()) { 1764 player.removeClass('vjs-ad-loading'); 1765 player.addClass('vjs-ad-content-resuming'); 1766 this.contentResuming = true; 1767 obj$1.end(player); 1768 } 1769 } 1770 /* 1771 * Ad skipped by ad plugin. Play content instead. 1772 */ 1773 ; 1774 1775 _proto.skipLinearAdMode = function skipLinearAdMode() { 1776 var _this4 = this; 1777 1778 var player = this.player; 1779 1780 if (player.ads.inAdBreak() || this.isContentResuming()) { 1781 videojs.log.warn('Unexpected skipLinearAdMode invocation'); 1782 } else { 1783 this.afterLoadStart(function () { 1784 player.trigger('adskip'); 1785 player.ads.debug('skipLinearAdMode (Preroll)'); 1786 1787 _this4.resumeAfterNoPreroll(player); 1788 }); 1789 } 1790 } 1791 /* 1792 * Prerolls took too long! Play content instead. 1793 */ 1794 ; 1795 1796 _proto.onAdTimeout = function onAdTimeout(player) { 1797 var _this5 = this; 1798 1799 this.afterLoadStart(function () { 1800 player.ads.debug('adtimeout (Preroll)'); 1801 1802 _this5.resumeAfterNoPreroll(player); 1803 }); 1804 } 1805 /* 1806 * Check if nopreroll event was too late before handling it. 1807 */ 1808 ; 1809 1810 _proto.onNoPreroll = function onNoPreroll(player) { 1811 if (player.ads.inAdBreak() || this.isContentResuming()) { 1812 videojs.log.warn('Unexpected nopreroll event (Preroll)'); 1813 } else { 1814 this.noPreroll(); 1815 } 1816 }; 1817 1818 _proto.resumeAfterNoPreroll = function resumeAfterNoPreroll(player) { 1819 // Resume to content and unblock play as there is no preroll ad 1820 this.contentResuming = true; 1821 player.ads._shouldBlockPlay = false; 1822 this.cleanupPartial(player); // Play the content if we had requested play or we paused on 'contentupdate' 1823 // and we haven't played yet. This happens if there was no preroll or if it 1824 // errored, timed out, etc. Otherwise snapshot restore would play. 1825 1826 if (player.paused() && (player.ads._playRequested || player.ads._pausedOnContentupdate)) { 1827 var playPromise = player.play(); 1828 1829 if (playPromise && playPromise.then) { 1830 playPromise.then(null, function (e) {}); 1831 } 1832 } 1833 } 1834 /* 1835 * Cleanup timeouts and spinner. 1836 */ 1837 ; 1838 1839 _proto.cleanup = function cleanup(player) { 1840 if (!player.ads._hasThereBeenALoadStartDuringPlayerLife) { 1841 videojs.log.warn('Leaving Preroll state before loadstart event can cause issues.'); 1842 } 1843 1844 this.cleanupPartial(player); 1845 } 1846 /* 1847 * Performs cleanup tasks without depending on a state transition. This is 1848 * used mainly in cases where a preroll failed. 1849 */ 1850 ; 1851 1852 _proto.cleanupPartial = function cleanupPartial(player) { 1853 player.removeClass('vjs-ad-loading'); 1854 player.removeClass('vjs-ad-content-resuming'); 1855 this.clearTimeout(player); 1856 } 1857 /* 1858 * Clear the preroll timeout and nulls out the pointer. 1859 */ 1860 ; 1861 1862 _proto.clearTimeout = function clearTimeout(player) { 1863 player.clearTimeout(this._timeout); 1864 this._timeout = null; 1865 }; 1866 1867 return Preroll; 1868 }(AdState$1); 1869 1870 States.registerState('Preroll', Preroll); 1871 1872 var ContentState$2 = States.getState('ContentState'); 1873 /* 1874 * This is the initial state for a player with an ad plugin. Normally, it remains in this 1875 * state until a "play" event is seen. After that, we enter the Preroll state to check for 1876 * prerolls. This happens regardless of whether or not any prerolls ultimately will play. 1877 * Errors and other conditions may lead us directly from here to ContentPlayback. 1878 */ 1879 1880 var BeforePreroll = 1881 /*#__PURE__*/ 1882 function (_ContentState) { 1883 _inheritsLoose(BeforePreroll, _ContentState); 1884 1885 function BeforePreroll() { 1886 return _ContentState.apply(this, arguments) || this; 1887 } 1888 1889 /* 1890 * Allows state name to be logged even after minification. 1891 */ 1892 BeforePreroll._getName = function _getName() { 1893 return 'BeforePreroll'; 1894 } 1895 /* 1896 * For state transitions to work correctly, initialization should 1897 * happen here, not in a constructor. 1898 */ 1899 ; 1900 1901 var _proto = BeforePreroll.prototype; 1902 1903 _proto.init = function init(player) { 1904 this.adsReady = false;
1905 this.shouldResumeToContent = false; // Content playback should be blocked until we are done 1906 // playing ads or we know there are no ads to play 1907 1908 player.ads._shouldBlockPlay = true; 1909 } 1910 /* 1911 * The ad plugin may trigger adsready before the play request. If so, 1912 * we record that adsready already happened so the Preroll state will know. 1913 */ 1914 ; 1915 1916 _proto.onAdsReady = function onAdsReady(player) { 1917 player.ads.debug('Received adsready event (BeforePreroll)'); 1918 this.adsReady = true; 1919 } 1920 /* 1921 * Ad mode officially begins on the play request, because at this point 1922 * content playback is blocked by the ad plugin. 1923 */ 1924 ; 1925 1926 _proto.onPlay = function onPlay(player) { 1927 var Preroll = States.getState('Preroll'); 1928 player.ads.debug('Received play event (BeforePreroll)'); // Check for prerolls 1929 1930 this.transitionTo(Preroll, this.adsReady, this.shouldResumeToContent); 1931 } 1932 /* 1933 * All ads for the entire video are canceled. 1934 */ 1935 ; 1936 1937 _proto.onAdsCanceled = function onAdsCanceled(player) { 1938 player.ads.debug('adscanceled (BeforePreroll)'); 1939 this.shouldResumeToContent = true; 1940 } 1941 /* 1942 * An ad error occured. Play content instead. 1943 */ 1944 ; 1945 1946 _proto.onAdsError = function onAdsError() { 1947 this.player.ads.debug('adserror (BeforePreroll)'); 1948 this.shouldResumeToContent = true; 1949 } 1950 /* 1951 * If there is no preroll, don't wait for a play event to move forward. 1952 */ 1953 ; 1954 1955 _proto.onNoPreroll = function onNoPreroll() { 1956 this.player.ads.debug('Skipping prerolls due to nopreroll event (BeforePreroll)'); 1957 this.shouldResumeToContent = true; 1958 } 1959 /*
vendor: 28,175 bytes, lines 1960-2687
1960 * Prerolls skipped by ad plugin. Play content instead. 1961 */ 1962 ; 1963 1964 _proto.skipLinearAdMode = function skipLinearAdMode() { 1965 var player = this.player; 1966 player.trigger('adskip'); 1967 player.ads.debug('skipLinearAdMode (BeforePreroll)'); 1968 this.shouldResumeToContent = true; 1969 }; 1970 1971 _proto.onContentChanged = function onContentChanged() { 1972 this.init(this.player); 1973 }; 1974 1975 return BeforePreroll; 1976 }(ContentState$2); 1977 1978 States.registerState('BeforePreroll', BeforePreroll); 1979 1980 var AdState$2 = States.getState('AdState'); 1981 1982 var Midroll = 1983 /*#__PURE__*/ 1984 function (_AdState) { 1985 _inheritsLoose(Midroll, _AdState); 1986 1987 function Midroll() { 1988 return _AdState.apply(this, arguments) || this; 1989 } 1990 1991 /* 1992 * Allows state name to be logged even after minification. 1993 */ 1994 Midroll._getName = function _getName() { 1995 return 'Midroll'; 1996 } 1997 /* 1998 * Midroll breaks happen when the ad plugin calls startLinearAdMode, 1999 * which can happen at any time during content playback. 2000 */ 2001 ; 2002 2003 var _proto = Midroll.prototype; 2004 2005 _proto.init = function init(player) { 2006 player.ads.adType = 'midroll'; 2007 obj$1.start(player); 2008 player.addClass('vjs-ad-loading'); 2009 } 2010 /* 2011 * An ad has actually started playing. 2012 * Remove the loading spinner. 2013 */ 2014 ; 2015 2016 _proto.onAdStarted = function onAdStarted(player) { 2017 player.removeClass('vjs-ad-loading'); 2018 } 2019 /* 2020 * Midroll break is done. 2021 */ 2022 ; 2023 2024 _proto.endLinearAdMode = function endLinearAdMode() { 2025 var player = this.player; 2026 2027 if (this.inAdBreak()) { 2028 this.contentResuming = true; 2029 player.addClass('vjs-ad-content-resuming'); 2030 player.removeClass('vjs-ad-loading'); 2031 obj$1.end(player); 2032 } 2033 } 2034 /* 2035 * End midroll break if there is an error. 2036 */ 2037 ; 2038 2039 _proto.onAdsError = function onAdsError(player) { 2040 // In the future, we may not want to do this automatically. 2041 // Ad plugins should be able to choose to continue the ad break 2042 // if there was an error. 2043 if (this.inAdBreak()) { 2044 player.ads.endLinearAdMode(); 2045 } 2046 } 2047 /* 2048 * Cleanup CSS classes. 2049 */ 2050 ; 2051 2052 _proto.cleanup = function cleanup(player) { 2053 player.removeClass('vjs-ad-loading'); 2054 player.removeClass('vjs-ad-content-resuming'); 2055 }; 2056 2057 return Midroll; 2058 }(AdState$2); 2059 2060 States.registerState('Midroll', Midroll); 2061 2062 var AdState$3 = States.getState('AdState'); 2063 2064 var Postroll = 2065 /*#__PURE__*/ 2066 function (_AdState) { 2067 _inheritsLoose(Postroll, _AdState); 2068 2069 function Postroll() { 2070 return _AdState.apply(this, arguments) || this; 2071 } 2072 2073 /* 2074 * Allows state name to be logged even after minification. 2075 */ 2076 Postroll._getName = function _getName() { 2077 return 'Postroll'; 2078 } 2079 /* 2080 * For state transitions to work correctly, initialization should 2081 * happen here, not in a constructor. 2082 */ 2083 ; 2084 2085 var _proto = Postroll.prototype; 2086 2087 _proto.init = function init(player) { 2088 this.waitingForAdBreak = true; // Legacy name that now simply means "handling postrolls". 2089 2090 player.ads._contentEnding = true; // Start postroll process. 2091 2092 if (!player.ads.nopostroll_) { 2093 player.addClass('vjs-ad-loading'); // Determine postroll timeout based on plugin settings 2094 2095 var timeout = player.ads.settings.timeout; 2096 2097 if (typeof player.ads.settings.postrollTimeout === 'number') { 2098 timeout = player.ads.settings.postrollTimeout; 2099 } 2100 2101 this._postrollTimeout = player.setTimeout(function () { 2102 player.trigger('adtimeout'); 2103 }, timeout); // No postroll, ads are done 2104 } else { 2105 this.resumeContent(player); 2106 var AdsDone = States.getState('AdsDone'); 2107 this.transitionTo(AdsDone); 2108 } 2109 } 2110 /* 2111 * Start the postroll if it's not too late. 2112 */ 2113 ; 2114 2115 _proto.startLinearAdMode = function startLinearAdMode() { 2116 var player = this.player; 2117 2118 if (!player.ads.inAdBreak() && !this.isContentResuming()) { 2119 player.ads.adType = 'postroll'; 2120 player.clearTimeout(this._postrollTimeout); 2121 this.waitingForAdBreak = false; 2122 obj$1.start(player); 2123 } else { 2124 videojs.log.warn('Unexpected startLinearAdMode invocation (Postroll)'); 2125 } 2126 } 2127 /* 2128 * An ad has actually started playing. 2129 * Remove the loading spinner. 2130 */ 2131 ; 2132 2133 _proto.onAdStarted = function onAdStarted(player) { 2134 player.removeClass('vjs-ad-loading'); 2135 } 2136 /* 2137 * Ending a postroll triggers the ended event. 2138 */ 2139 ; 2140 2141 _proto.endLinearAdMode = function endLinearAdMode() { 2142 var _this = this; 2143 2144 var player = this.player; 2145 var AdsDone = States.getState('AdsDone'); 2146 2147 if (this.inAdBreak()) { 2148 player.removeClass('vjs-ad-loading'); 2149 this.resumeContent(player); 2150 obj$1.end(player, function () { 2151 _this.transitionTo(AdsDone); 2152 }); 2153 } 2154 } 2155 /* 2156 * Postroll skipped, time to clean up. 2157 */ 2158 ; 2159 2160 _proto.skipLinearAdMode = function skipLinearAdMode() { 2161 var player = this.player; 2162 2163 if (player.ads.inAdBreak() || this.isContentResuming()) { 2164 videojs.log.warn('Unexpected skipLinearAdMode invocation'); 2165 } else { 2166 player.ads.debug('Postroll abort (skipLinearAdMode)'); 2167 player.trigger('adskip'); 2168 this.abort(player); 2169 } 2170 } 2171 /* 2172 * Postroll timed out, time to clean up. 2173 */ 2174 ; 2175 2176 _proto.onAdTimeout = function onAdTimeout(player) { 2177 player.ads.debug('Postroll abort (adtimeout)'); 2178 this.abort(player); 2179 } 2180 /* 2181 * Postroll errored out, time to clean up. 2182 */ 2183 ; 2184 2185 _proto.onAdsError = function onAdsError(player) { 2186 2187 player.ads.debug('Postroll abort (adserror)'); // In the future, we may not want to do this automatically. 2188 // Ad plugins should be able to choose to continue the ad break 2189 // if there was an error. 2190 2191 if (player.ads.inAdBreak()) { 2192 player.ads.endLinearAdMode(); 2193 } else { 2194 this.abort(player); 2195 } 2196 } 2197 /* 2198 * Handle content change if we're not in an ad break. 2199 */ 2200 ; 2201 2202 _proto.onContentChanged = function onContentChanged(player) { 2203 // Content resuming after Postroll. Content is paused 2204 // at this point, since it is done playing. 2205 if (this.isContentResuming()) { 2206 var BeforePreroll = States.getState('BeforePreroll'); 2207 this.transitionTo(BeforePreroll); // Waiting for postroll to start. Content is considered playing 2208 // at this point, since it had to be playing to start the postroll. 2209 } else if (!this.inAdBreak()) { 2210 var Preroll = States.getState('Preroll'); 2211 this.transitionTo(Preroll); 2212 } 2213 } 2214 /* 2215 * Wrap up if there is no postroll. 2216 */ 2217 ; 2218 2219 _proto.onNoPostroll = function onNoPostroll(player) { 2220 if (!this.isContentResuming() && !this.inAdBreak()) { 2221 this.abort(player); 2222 } else { 2223 videojs.log.warn('Unexpected nopostroll event (Postroll)'); 2224 } 2225 }; 2226 2227 _proto.resumeContent = function resumeContent(player) { 2228 this.contentResuming = true; 2229 player.addClass('vjs-ad-content-resuming'); 2230 } 2231 /* 2232 * Helper for ending Postrolls. In the future we may want to 2233 * refactor this class so that `cleanup` handles all of this. 2234 */ 2235 ; 2236 2237 _proto.abort = function abort(player) { 2238 var AdsDone = States.getState('AdsDone'); 2239 this.resumeContent(player); 2240 player.removeClass('vjs-ad-loading'); 2241 this.transitionTo(AdsDone); 2242 } 2243 /* 2244 * Cleanup timeouts and state. 2245 */ 2246 ; 2247 2248 _proto.cleanup = function cleanup(player) { 2249 player.removeClass('vjs-ad-content-resuming'); 2250 player.clearTimeout(this._postrollTimeout); 2251 player.ads._contentEnding = false; 2252 }; 2253 2254 return Postroll; 2255 }(AdState$3); 2256 2257 States.registerState('Postroll', Postroll); 2258 2259 var ContentState$3 = States.getState('ContentState'); 2260 /* 2261 * This state represents content playback the first time through before 2262 * content ends. After content has ended once, we check for postrolls and 2263 * move on to the AdsDone state rather than returning here. 2264 */ 2265 2266 var ContentPlayback = 2267 /*#__PURE__*/ 2268 function (_ContentState) { 2269 _inheritsLoose(ContentPlayback, _ContentState); 2270 2271 function ContentPlayback() { 2272 return _ContentState.apply(this, arguments) || this; 2273 } 2274 2275 /* 2276 * Allows state name to be logged even after minification. 2277 */ 2278 ContentPlayback._getName = function _getName() { 2279 return 'ContentPlayback'; 2280 } 2281 /* 2282 * For state transitions to work correctly, initialization should 2283 * happen here, not in a constructor. 2284 */ 2285 ; 2286 2287 var _proto = ContentPlayback.prototype; 2288 2289 _proto.init = function init(player) { 2290 // Don't block calls to play in content playback 2291 player.ads._shouldBlockPlay = false; 2292 } 2293 /* 2294 * In the case of a timeout, adsready might come in late. This assumes the behavior 2295 * that if an ad times out, it could still interrupt the content and start playing. 2296 * An ad plugin could behave otherwise by ignoring this event. 2297 */ 2298 ; 2299 2300 _proto.onAdsReady = function onAdsReady(player) { 2301 player.ads.debug('Received adsready event (ContentPlayback)'); 2302 2303 if (!player.ads.nopreroll_) { 2304 player.ads.debug('Triggered readyforpreroll event (ContentPlayback)'); 2305 player.trigger('readyforpreroll'); 2306 } 2307 } 2308 /* 2309 * Content ended before postroll checks. 2310 */ 2311 ; 2312 2313 _proto.onReadyForPostroll = function onReadyForPostroll(player) { 2314 var Postroll = States.getState('Postroll'); 2315 player.ads.debug('Received readyforpostroll event'); 2316 this.transitionTo(Postroll); 2317 } 2318 /* 2319 * This is how midrolls start. 2320 */ 2321 ; 2322 2323 _proto.startLinearAdMode = function startLinearAdMode() { 2324 var Midroll = States.getState('Midroll'); 2325 this.transitionTo(Midroll); 2326 }; 2327 2328 return ContentPlayback; 2329 }(ContentState$3); 2330 2331 States.registerState('ContentPlayback', ContentPlayback); 2332 2333 var ContentState$4 = States.getState('ContentState'); 2334 /* 2335 * This state represents content playback when stitched ads are in play. 2336 */ 2337 2338 var StitchedContentPlayback = 2339 /*#__PURE__*/ 2340 function (_ContentState) { 2341 _inheritsLoose(StitchedContentPlayback, _ContentState); 2342 2343 function StitchedContentPlayback() { 2344 return _ContentState.apply(this, arguments) || this; 2345 } 2346 2347 /* 2348 * Allows state name to be logged even after minification. 2349 */ 2350 StitchedContentPlayback._getName = function _getName() { 2351 return 'StitchedContentPlayback'; 2352 } 2353 /* 2354 * For state transitions to work correctly, initialization should 2355 * happen here, not in a constructor. 2356 */ 2357 ; 2358 2359 var _proto = StitchedContentPlayback.prototype; 2360 2361 _proto.init = function init() { 2362 // Don't block calls to play in stitched ad players, ever. 2363 this.player.ads._shouldBlockPlay = false; 2364 } 2365 /* 2366 * Source change does not do anything for stitched ad players. 2367 * contentchanged does not fire during ad breaks, so we don't need to 2368 * worry about that. 2369 */ 2370 ; 2371 2372 _proto.onContentChanged = function onContentChanged() { 2373 this.player.ads.debug("Received contentchanged event (" + this.constructor._getName() + ")"); 2374 } 2375 /* 2376 * This is how stitched ads start. 2377 */ 2378 ; 2379 2380 _proto.startLinearAdMode = function startLinearAdMode() { 2381 var StitchedAdRoll = States.getState('StitchedAdRoll'); 2382 this.transitionTo(StitchedAdRoll); 2383 }; 2384 2385 return StitchedContentPlayback; 2386 }(ContentState$4); 2387 2388 States.registerState('StitchedContentPlayback', StitchedContentPlayback); 2389 2390 var AdState$4 = States.getState('AdState'); 2391 2392 var StitchedAdRoll = 2393 /*#__PURE__*/ 2394 function (_AdState) { 2395 _inheritsLoose(StitchedAdRoll, _AdState); 2396 2397 function StitchedAdRoll() { 2398 return _AdState.apply(this, arguments) || this; 2399 } 2400 2401 /* 2402 * Allows state name to be logged even after minification. 2403 */ 2404 StitchedAdRoll._getName = function _getName() { 2405 return 'StitchedAdRoll'; 2406 } 2407 /* 2408 * StitchedAdRoll breaks happen when the ad plugin calls startLinearAdMode, 2409 * which can happen at any time during content playback. 2410 */ 2411 ; 2412 2413 var _proto = StitchedAdRoll.prototype; 2414 2415 _proto.init = function init() { 2416 this.waitingForAdBreak = false; 2417 this.contentResuming = false; 2418 this.player.ads.adType = 'stitched'; 2419 obj$1.start(this.player); 2420 } 2421 /* 2422 * For stitched ads, there is no "content resuming" scenario, so a "playing" 2423 * event is not relevant. 2424 */ 2425 ; 2426 2427 _proto.onPlaying = function onPlaying() {} 2428 /* 2429 * For stitched ads, there is no "content resuming" scenario, so a 2430 * "contentresumed" event is not relevant. 2431 */ 2432 ; 2433 2434 _proto.onContentResumed = function onContentResumed() {} 2435 /* 2436 * When we see an "adended" event, it means that we are in a postroll that 2437 * has ended (because the media ended and we are still in an ad state). 2438 * 2439 * In these cases, we transition back to content mode and fire ended. 2440 */ 2441 ; 2442 2443 _proto.onAdEnded = function onAdEnded() { 2444 this.endLinearAdMode(); 2445 this.player.trigger('ended'); 2446 } 2447 /* 2448 * StitchedAdRoll break is done. 2449 */ 2450 ; 2451 2452 _proto.endLinearAdMode = function endLinearAdMode() { 2453 var StitchedContentPlayback = States.getState('StitchedContentPlayback'); 2454 obj$1.end(this.player); 2455 this.transitionTo(StitchedContentPlayback); 2456 }; 2457 2458 return StitchedAdRoll; 2459 }(AdState$4); 2460 2461 States.registerState('StitchedAdRoll', StitchedAdRoll); 2462 2463 /* 2464 This main plugin file is responsible for the public API and enabling the features 2465 that live in in separate files. 2466 */ 2467 var isMiddlewareMediatorSupported$1 = obj.isMiddlewareMediatorSupported; 2468 var VIDEO_EVENTS = videojs.getTech('Html5').Events; // Default settings 2469 2470 var defaults = { 2471 // Maximum amount of time in ms to wait to receive `adsready` from the ad 2472 // implementation after play has been requested. Ad implementations are 2473 // expected to load any dynamic libraries and make any requests to determine 2474 // ad policies for a video during this time. 2475 timeout: 5000, 2476 // Maximum amount of time in ms to wait for the ad implementation to start 2477 // linear ad mode after `readyforpreroll` has fired. This is in addition to 2478 // the standard timeout. 2479 prerollTimeout: undefined, 2480 // Maximum amount of time in ms to wait for the ad implementation to start 2481 // linear ad mode after `readyforpostroll` has fired. 2482 postrollTimeout: undefined, 2483 // When truthy, instructs the plugin to output additional information about 2484 // plugin state to the video.js log. On most devices, the video.js log is 2485 // the same as the developer console. 2486 debug: false, 2487 // Set this to true when using ads that are part of the content video 2488 stitchedAds: false, 2489 // Force content to be treated as live or not live 2490 // if not defined, the code will try to infer if content is live, 2491 // which can have limitations. 2492 contentIsLive: undefined, 2493 // If set to true, content will play muted behind ads on supported platforms. This is 2494 // to support ads on video metadata cuepoints during a live stream. It also results in 2495 // more precise resumes after ads during a live stream. 2496 liveCuePoints: true 2497 }; 2498 2499 var contribAdsPlugin = function contribAdsPlugin(options) { 2500 var player = this; // eslint-disable-line consistent-this 2501 2502 var settings = videojs.mergeOptions(defaults, options); // Prefix all video element events during ad playback 2503 // if the video element emits ad-related events directly, 2504 // plugins that aren't ad-aware will break. prefixing allows 2505 // plugins that wish to handle ad events to do so while 2506 // avoiding the complexity for common usage 2507 2508 var videoEvents = []; // dedupe event names 2509 2510 VIDEO_EVENTS.concat(['firstplay', 'loadedalldata']).forEach(function (eventName) { 2511 if (videoEvents.indexOf(eventName) === -1) { 2512 videoEvents.push(eventName); 2513 } 2514 }); // Set up redispatching of player events 2515 2516 player.on(videoEvents, redispatch); // Set up features to block content playback while waiting for ads. 2517 // Play middleware is only supported on later versions of video.js 2518 // and on desktop currently(as the user-gesture requirement on mobile 2519 // will disallow calling play once play blocking is lifted) 2520 // The middleware must also be registered outside of the plugin, 2521 // to avoid a middleware factory being created for each player 2522 2523 if (!isMiddlewareMediatorSupported$1()) { 2524 initCancelContentPlay(player, settings.debug); 2525 } // If we haven't seen a loadstart after 5 seconds, the plugin was not initialized 2526 // correctly. 2527 2528 2529 player.setTimeout(function () { 2530 if (!player.ads._hasThereBeenALoadStartDuringPlayerLife && player.src() !== '') { 2531 videojs.log.error('videojs-contrib-ads has not seen a loadstart event 5 seconds ' + 'after being initialized, but a source is present. This indicates that ' + 'videojs-contrib-ads was initialized too late. It must be initialized ' + 'immediately after video.js in the same tick. As a result, some ads will not ' + 'play and some media events will be incorrect. For more information, see ' + 'http://videojs.github.io/videojs-contrib-ads/integrator/getting-started.html'); 2532 } 2533 }, 5000); // "vjs-has-started" should be present at the end of a video. This makes sure it's 2534 // always there. 2535 2536 player.on('ended', function () { 2537 if (!player.hasClass('vjs-has-started')) { 2538 player.addClass('vjs-has-started'); 2539 } 2540 }); // video.js removes the vjs-waiting class on timeupdate. We want 2541 // to make sure this still happens during content restoration. 2542 2543 player.on('contenttimeupdate', function () { 2544 player.removeClass('vjs-waiting'); 2545 }); // We now auto-play when an ad gets loaded if we're playing ads in the same video 2546 // element as the content. 2547 // The problem is that in IE11, we cannot play in addurationchange but in iOS8, we 2548 // cannot play from adcanplay. 2549 // This will prevent ad plugins from needing to do this themselves. 2550 2551 player.on(['addurationchange', 'adcanplay'], function () { 2552 // We don't need to handle this for stitched ads because 2553 // linear ads in such cases are stitched into the content. 2554 if (player.ads.settings.stitchedAds) { 2555 return; 2556 } // Some techs may retrigger canplay after playback has begun. 2557 // So we want to procceed only if playback hasn't started. 2558 2559 2560 if (player.hasStarted()) { 2561 return; 2562 } 2563 2564 if (player.ads.snapshot && player.currentSrc() === player.ads.snapshot.currentSrc) { 2565 return; 2566 } // If an ad isn't playing, don't try to play an ad. This could result from prefixed 2567 // events when the player is blocked by a preroll check, but there is no preroll. 2568 2569 2570 if (!player.ads.inAdBreak()) { 2571 return; 2572 } 2573 2574 var playPromise = player.play(); 2575 2576 if (playPromise && playPromise.catch) { 2577 playPromise.catch(function (error) { 2578 videojs.log.warn('Play promise rejected when playing ad', error); 2579 }); 2580 } 2581 }); 2582 player.on('nopreroll', function () { 2583 player.ads.debug('Received nopreroll event'); 2584 player.ads.nopreroll_ = true; 2585 }); 2586 player.on('nopostroll', function () { 2587 player.ads.debug('Received nopostroll event'); 2588 player.ads.nopostroll_ = true; 2589 }); // Restart the cancelContentPlay process. 2590 2591 player.on('playing', function () { 2592 player.ads._cancelledPlay = false; 2593 player.ads._pausedOnContentupdate = false; 2594 }); // Keep track of whether a play event has happened 2595 2596 player.on('play', function () { 2597 player.ads._playRequested = true; 2598 }); 2599 player.one('loadstart', function () { 2600 player.ads._hasThereBeenALoadStartDuringPlayerLife = true; 2601 }); 2602 player.on('loadeddata', function () { 2603 player.ads._hasThereBeenALoadedData = true; 2604 }); 2605 player.on('loadedmetadata', function () { 2606 player.ads._hasThereBeenALoadedMetaData = true; 2607 }); // Replace the plugin constructor with the ad namespace 2608 2609 player.ads = getAds(player); 2610 player.ads.settings = settings; // Set the stitched ads state. This needs to happen before the `_state` is 2611 // initialized below - BeforePreroll needs to know whether contrib-ads is 2612 // playing stitched ads or not. 2613 // The setter is deprecated, so this does not use it. 2614 // But first, cast to boolean. 2615 2616 settings.stitchedAds = !!settings.stitchedAds; 2617 2618 if (settings.stitchedAds) { 2619 player.ads._state = new (States.getState('StitchedContentPlayback'))(player); 2620 } else { 2621 player.ads._state = new (States.getState('BeforePreroll'))(player); 2622 } 2623 2624 player.ads._state.init(player); 2625 2626 player.ads.cueTextTracks = cueTextTracks; 2627 player.ads.adMacroReplacement = adMacroReplacement.bind(player); // Start sending contentupdate and contentchanged events for this player 2628 2629 initializeContentupdate(player); // Global contentchanged handler for resetting plugin state 2630 2631 player.on('contentchanged', player.ads.reset); // A utility method for textTrackChangeHandler to define the conditions 2632 // when text tracks should be disabled. 2633 // Currently this includes: 2634 // - on iOS with native text tracks, during an ad playing 2635 2636 var shouldDisableTracks = function shouldDisableTracks() { 2637 // If the platform matches iOS with native text tracks 2638 // and this occurs during ad playback, we should disable tracks again. 2639 // If shouldPlayContentBehindAd, no special handling is needed. 2640 return !player.ads.shouldPlayContentBehindAd(player) && player.ads.inAdBreak() && player.tech_.featuresNativeTextTracks && videojs.browser.IS_IOS && // older versions of video.js did not use an emulated textTrackList 2641 !Array.isArray(player.textTracks()); 2642 }; 2643 /* 2644 * iOS Safari will change caption mode to 'showing' if a user previously 2645 * turned captions on manually for that video source, so this TextTrackList 2646 * 'change' event handler will re-disable them in case that occurs during ad playback 2647 */ 2648 2649 2650 var textTrackChangeHandler = function textTrackChangeHandler() { 2651 var textTrackList = player.textTracks(); 2652 2653 if (shouldDisableTracks()) { 2654 // We must double check all tracks 2655 for (var i = 0; i < textTrackList.length; i++) { 2656 var track = textTrackList[i]; 2657 2658 if (track.mode === 'showing') { 2659 track.mode = 'disabled'; 2660 } 2661 } 2662 } 2663 }; // Add the listener to the text track list 2664 2665 2666 player.ready(function () { 2667 player.textTracks().addEventListener('change', textTrackChangeHandler); 2668 }); // Event handling for the current state. 2669 2670 player.on(['play', 'playing', 'ended', 'adsready', 'adscanceled', 'adskip', 'adserror', 'adtimeout', 'adended', 'ads-ad-started', 'contentchanged', 'dispose', 'contentresumed', 'readyforpostroll', 'nopreroll', 'nopostroll'], function (e) { 2671 player.ads._state.handleEvent(e.type); 2672 }); // Clear timeouts and handlers when player is disposed 2673 2674 player.on('dispose', function () { 2675 player.ads.reset(); 2676 player.textTracks().removeEventListener('change', textTrackChangeHandler); 2677 }); 2678 }; // Expose the contrib-ads version before it is initialized. Will be replaced 2679 // after initialization in ads.js 2680 2681 2682 contribAdsPlugin.VERSION = version; // Attempt to register the plugin, if we can. 2683 2684 register(contribAdsPlugin); 2685 2686 return contribAdsPlugin; 2687})));
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.