1(function (global, factory) { 2 typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('video.js')) : 3 typeof define === 'function' && define.amd ? define(['exports', 'video.js'], factory) : 4 (factory((global.videojsIma = {}),global.videojs)); 5}(this, (function (exports,videojs) { 'use strict'; 6 7videojs = videojs && videojs.hasOwnProperty('default') ? videojs['default'] : videojs; 8 9/** 10 * Copyright 2017 Google Inc. 11 * 12 * Licensed under the Apache License, Version 2.0 (the "License"); 13 * you may not use this file except in compliance with the License. 14 * You may obtain a copy of the License at 15 * 16 * http://www.apache.org/licenses/LICENSE-2.0 17 * 18 * Unless required by applicable law or agreed to in writing, software 19 * distributed under the License is distributed on an "AS IS" BASIS, 20 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 * See the License for the specific language governing permissions and 22 * limitations under the License. 23 * 24 * IMA SDK integration plugin for Video.js. For more information see 25 * https://www.github.com/googleads/videojs-ima 26 */ 27 28/** 29 * Wraps the video.js player for the plugin. 30 * 31 * @param {Object} player Video.js player instance. 32 * @param {Object} adsPluginSettings Settings for the contrib-ads plugin. 33 * @param {Controller} controller Reference to the parent controller. 34 */ 35const PlayerWrapper = function (player, adsPluginSettings, controller) { 36 /** 37 * Instance of the video.js player. 38 */ 39 this.vjsPlayer = player; 40 41 /** 42 * Plugin controller. 43 */ 44 this.controller = controller; 45 46 /** 47 * Timer used to track content progress. 48 */ 49 this.contentTrackingTimer = null; 50 51 /** 52 * True if our content video has completed, false otherwise. 53 */ 54 this.contentComplete = false; 55 56 /** 57 * Handle to interval that repeatedly updates current time. 58 */ 59 this.updateTimeIntervalHandle = null; 60 61 /** 62 * Interval (ms) to check for player resize for fluid support. 63 */ 64 this.updateTimeInterval = 1000; 65 66 /** 67 * Handle to interval that repeatedly checks for seeking. 68 */ 69 this.seekCheckIntervalHandle = null; 70 71 /** 72 * Interval (ms) on which to check if the user is seeking through the 73 * content. 74 */ 75 this.seekCheckInterval = 1000; 76 77 /** 78 * Handle to interval that repeatedly checks for player resize. 79 */ 80 this.resizeCheckIntervalHandle = null; 81 82 /** 83 * Interval (ms) to check for player resize for fluid support. 84 */ 85 this.resizeCheckInterval = 250; 86 87 /** 88 * Threshold by which to judge user seeking. We check every 1000 ms to see 89 * if the user is seeking. In order for us to decide that they are *not* 90 * seeking, the content video playhead must only change by 900-1100 ms 91 * between checks. Any greater change and we assume the user is seeking 92 * through the video. 93 */ 94 this.seekThreshold = 100; 95 96 /** 97 * Content ended listeners passed by the publisher to the plugin. Publishers 98 * should allow the plugin to handle content ended to ensure proper support 99 * of custom ad playback. 100 */ 101 this.contentEndedListeners = []; 102 103 /** 104 * Stores the content source so we can re-populate it manually after a 105 * post-roll on iOS. 106 */ 107 this.contentSource = ''; 108 109 /** 110 * Stores the content source type so we can re-populate it manually after a 111 * post-roll. 112 */ 113 this.contentSourceType = ''; 114 115 /** 116 * Stores data for the content playhead tracker. 117 */ 118 this.contentPlayheadTracker = { 119 currentTime: 0, 120 previousTime: 0, 121 seeking: false, 122 duration: 0 123 }; 124 125 /** 126 * Player dimensions. Used in our resize check. 127 */ 128 this.vjsPlayerDimensions = { 129 width: this.getPlayerWidth(), 130 height: this.getPlayerHeight() 131 }; 132 133 /** 134 * Video.js control bar. 135 */ 136 this.vjsControls = this.vjsPlayer.getChild('controlBar'); 137 138 /** 139 * Vanilla HTML5 video player underneath the video.js player. 140 */ 141 this.h5Player = null; 142 143 this.vjsPlayer.one('play', this.setUpPlayerIntervals.bind(this)); 144 this.boundContentEndedListener = this.localContentEndedListener.bind(this); 145 this.vjsPlayer.on('contentended', this.boundContentEndedListener); 146 this.vjsPlayer.on('dispose', this.playerDisposedListener.bind(this)); 147 this.vjsPlayer.on('readyforpreroll', this.onReadyForPreroll.bind(this)); 148 this.vjsPlayer.on('adtimeout', this.onAdTimeout.bind(this)); 149 this.vjsPlayer.ready(this.onPlayerReady.bind(this)); 150 151 if (this.controller.getSettings().requestMode === 'onPlay') { 152 this.vjsPlayer.one('play', this.controller.requestAds.bind(this.controller)); 153 } 154 155 this.vjsPlayer.ads(adsPluginSettings); 156}; 157 158/** 159 * Set up the intervals we use on the player. 160 */ 161PlayerWrapper.prototype.setUpPlayerIntervals = function () { 162 this.updateTimeIntervalHandle = setInterval(this.updateCurrentTime.bind(this), this.updateTimeInterval); 163 this.seekCheckIntervalHandle = setInterval(this.checkForSeeking.bind(this), this.seekCheckInterval); 164 this.resizeCheckIntervalHandle = setInterval(this.checkForResize.bind(this), this.resizeCheckInterval); 165}; 166 167/** 168 * Updates the current time of the video 169 */ 170PlayerWrapper.prototype.updateCurrentTime = function () { 171 if (!this.contentPlayheadTracker.seeking) { 172 this.contentPlayheadTracker.currentTime = this.vjsPlayer.currentTime(); 173 } 174}; 175 176/** 177 * Detects when the user is seeking through a video. 178 * This is used to prevent mid-rolls from playing while a user is seeking. 179 * 180 * There *is* a seeking property of the HTML5 video element, but it's not 181 * properly implemented on all platforms (e.g. mobile safari), so we have to 182 * check ourselves to be sure. 183 */ 184PlayerWrapper.prototype.checkForSeeking = function () { 185 const tempCurrentTime = this.vjsPlayer.currentTime(); 186 const diff = (tempCurrentTime - this.contentPlayheadTracker.previousTime) * 1000; 187 if (Math.abs(diff) > this.seekCheckInterval + this.seekThreshold) { 188 this.contentPlayheadTracker.seeking = true; 189 } else { 190 this.contentPlayheadTracker.seeking = false;
191 } 192 this.contentPlayheadTracker.previousTime = this.vjsPlayer.currentTime(); 193}; 194 195/** 196 * Detects when the player is resized (for fluid support) and resizes the 197 * ads manager to match. 198 */ 199PlayerWrapper.prototype.checkForResize = function () { 200 const currentWidth = this.getPlayerWidth(); 201 const currentHeight = this.getPlayerHeight(); 202 203 if (currentWidth != this.vjsPlayerDimensions.width || currentHeight != this.vjsPlayerDimensions.height) { 204 this.vjsPlayerDimensions.width = currentWidth; 205 this.vjsPlayerDimensions.height = currentHeight; 206 this.controller.onPlayerResize(currentWidth, currentHeight); 207 } 208}; 209 210/** 211 * Local content ended listener for contentComplete. 212 */ 213PlayerWrapper.prototype.localContentEndedListener = function () { 214 if (!this.contentComplete) { 215 this.contentComplete = true; 216 this.controller.onContentComplete(); 217 } 218 219 for (let index in this.contentEndedListeners) { 220 if (typeof this.contentEndedListeners[index] === 'function') { 221 this.contentEndedListeners[index](); 222 } 223 } 224 225 clearInterval(this.updateTimeIntervalHandle); 226 clearInterval(this.seekCheckIntervalHandle); 227 clearInterval(this.resizeCheckIntervalHandle); 228 if (this.vjsPlayer.el()) { 229 this.vjsPlayer.one('play', this.setUpPlayerIntervals.bind(this)); 230 } 231}; 232 233/** 234 * Called when it's time to play a post-roll but we don't have one to play. 235 */ 236PlayerWrapper.prototype.onNoPostroll = function () { 237 this.vjsPlayer.trigger('nopostroll'); 238}; 239 240/** 241 * Detects when the video.js player has been disposed. 242 */ 243PlayerWrapper.prototype.playerDisposedListener = function () { 244 this.contentEndedListeners = []; 245 this.controller.onPlayerDisposed(); 246 247 this.contentComplete = true; 248 this.vjsPlayer.off('contentended', this.boundContentEndedListener); 249 250 // Bug fix: https://github.com/googleads/videojs-ima/issues/306 251 if (this.vjsPlayer.ads.adTimeoutTimeout) { 252 clearTimeout(this.vjsPlayer.ads.adTimeoutTimeout); 253 } 254 255 const intervalsToClear = [this.updateTimeIntervalHandle, this.seekCheckIntervalHandle, this.resizeCheckIntervalHandle]; 256 for (let index in intervalsToClear) { 257 if (intervalsToClear[index]) { 258 clearInterval(intervalsToClear[index]); 259 } 260 } 261}; 262 263/** 264 * Start ad playback, or content video playback in the absence of a 265 * pre-roll. 266 */ 267PlayerWrapper.prototype.onReadyForPreroll = function () { 268 this.controller.onPlayerReadyForPreroll(); 269}; 270 271/** 272 * Detects if the ad has timed out. 273 */ 274PlayerWrapper.prototype.onAdTimeout = function () { 275 this.controller.onAdTimeout(); 276}; 277 278/** 279 * Called when the player fires its 'ready' event. 280 */ 281PlayerWrapper.prototype.onPlayerReady = function () { 282 this.h5Player = document.getElementById(this.getPlayerId()).getElementsByClassName('vjs-tech')[0]; 283 284 // Detect inline options 285 if (this.h5Player.hasAttribute('autoplay')) { 286 this.controller.setSetting('adWillAutoPlay', true); 287 } 288 289 // Sync ad volume with player volume. 290 this.onVolumeChange(); 291 this.vjsPlayer.on('fullscreenchange', this.onFullscreenChange.bind(this)); 292 this.vjsPlayer.on('volumechange', this.onVolumeChange.bind(this)); 293 294 this.controller.onPlayerReady(); 295}; 296 297/** 298 * Listens for the video.js player to change its fullscreen status. This 299 * keeps the fullscreen-ness of the AdContainer in sync with the player. 300 */ 301PlayerWrapper.prototype.onFullscreenChange = function () { 302 if (this.vjsPlayer.isFullscreen()) { 303 this.controller.onPlayerEnterFullscreen(); 304 } else { 305 this.controller.onPlayerExitFullscreen(); 306 } 307}; 308 309/** 310 * Listens for the video.js player to change its volume. This keeps the ad 311 * volume in sync with the content volume if the volume of the player is 312 * changed while content is playing. 313 */ 314PlayerWrapper.prototype.onVolumeChange = function () { 315 const newVolume = this.vjsPlayer.muted() ? 0 : this.vjsPlayer.volume(); 316 this.controller.onPlayerVolumeChanged(newVolume); 317}; 318 319/** 320 * Inject the ad container div into the DOM. 321 * 322 * @param{HTMLElement} adContainerDiv The ad container div. 323 */ 324PlayerWrapper.prototype.injectAdContainerDiv = function (adContainerDiv) { 325 this.vjsControls.el().parentNode.appendChild(adContainerDiv); 326}; 327 328/** 329 * @return {Object} The content player. 330 */ 331PlayerWrapper.prototype.getContentPlayer = function () { 332 return this.h5Player; 333}; 334 335/** 336 * @return {number} The volume, 0-1. 337 */ 338PlayerWrapper.prototype.getVolume = function () { 339 return this.vjsPlayer.muted() ? 0 : this.vjsPlayer.volume(); 340}; 341 342/** 343 * Set the volume of the player. 0-1. 344 * 345 * @param {number} volume The new volume. 346 */ 347PlayerWrapper.prototype.setVolume = function (volume) { 348 this.vjsPlayer.volume(volume); 349 if (volume == 0) { 350 this.vjsPlayer.muted(true); 351 } else { 352 this.vjsPlayer.muted(false); 353 } 354}; 355 356/** 357 * Ummute the player. 358 */ 359PlayerWrapper.prototype.unmute = function () { 360 this.vjsPlayer.muted(false); 361}; 362 363/** 364 * Mute the player. 365 */ 366PlayerWrapper.prototype.mute = function () { 367 this.vjsPlayer.muted(true); 368}; 369 370/** 371 * Play the video. 372 */ 373PlayerWrapper.prototype.play = function () { 374 this.vjsPlayer.play(); 375}; 376 377/** 378 * Toggles playback of the video. 379 */ 380PlayerWrapper.prototype.togglePlayback = function () { 381 if (this.vjsPlayer.paused()) { 382 this.vjsPlayer.play(); 383 } else { 384 this.vjsPlayer.pause(); 385 } 386}; 387 388/** 389 * Get the player width. 390 * 391 * @return {number}
vendor: 12,517 bytes, lines 391-826
391 The player's width. 392 */ 393PlayerWrapper.prototype.getPlayerWidth = function () { 394 let width = (getComputedStyle(this.vjsPlayer.el()) || {}).width; 395 396 if (!width || parseFloat(width) === 0) { 397 width = (this.vjsPlayer.el().getBoundingClientRect() || {}).width; 398 } 399 400 return parseFloat(width) || this.vjsPlayer.width(); 401}; 402 403/** 404 * Get the player height. 405 * 406 * @return {number} The player's height. 407 */ 408PlayerWrapper.prototype.getPlayerHeight = function () { 409 let height = (getComputedStyle(this.vjsPlayer.el()) || {}).height; 410 411 if (!height || parseFloat(height) === 0) { 412 height = (this.vjsPlayer.el().getBoundingClientRect() || {}).height; 413 } 414 415 return parseFloat(height) || this.vjsPlayer.height(); 416}; 417 418/** 419 * @return {Object} The vjs player's options object. 420 */ 421PlayerWrapper.prototype.getPlayerOptions = function () { 422 return this.vjsPlayer.options_; 423}; 424 425/** 426 * Returns the instance of the player id. 427 * @return {string} The player id. 428 */ 429PlayerWrapper.prototype.getPlayerId = function () { 430 return this.vjsPlayer.id(); 431}; 432 433/** 434 * Toggle fullscreen state. 435 */ 436PlayerWrapper.prototype.toggleFullscreen = function () { 437 if (this.vjsPlayer.isFullscreen()) { 438 this.vjsPlayer.exitFullscreen(); 439 } else { 440 this.vjsPlayer.requestFullscreen(); 441 } 442}; 443 444/** 445 * Returns the content playhead tracker. 446 * 447 * @return {Object} The content playhead tracker. 448 */ 449PlayerWrapper.prototype.getContentPlayheadTracker = function () { 450 return this.contentPlayheadTracker; 451}; 452 453/** 454 * Handles ad errors. 455 * 456 * @param {Object} adErrorEvent The ad error event thrown by the IMA SDK. 457 */ 458PlayerWrapper.prototype.onAdError = function (adErrorEvent) { 459 this.vjsControls.show(); 460 const errorMessage = adErrorEvent.getError !== undefined ? adErrorEvent.getError() : adErrorEvent.stack; 461 this.vjsPlayer.trigger({ type: 'adserror', data: { 462 AdError: errorMessage, 463 AdErrorEvent: adErrorEvent 464 } }); 465}; 466 467/** 468 * Handles ad log messages. 469 * @param {google.ima.AdEvent} adEvent The AdEvent thrown by the IMA SDK. 470 */ 471PlayerWrapper.prototype.onAdLog = function (adEvent) { 472 const adData = adEvent.getAdData(); 473 const errorMessage = adData['adError'] !== undefined ? adData['adError'].getMessage() : undefined; 474 this.vjsPlayer.trigger({ type: 'adslog', data: { 475 AdError: errorMessage, 476 AdEvent: adEvent 477 } }); 478}; 479 480/** 481 * Handles ad break starting. 482 */ 483PlayerWrapper.prototype.onAdBreakStart = function () { 484 this.contentSource = this.vjsPlayer.currentSrc(); 485 this.contentSourceType = this.vjsPlayer.currentType(); 486 this.vjsPlayer.off('contentended', this.boundContentEndedListener); 487 this.vjsPlayer.ads.startLinearAdMode(); 488 this.vjsControls.hide(); 489 this.vjsPlayer.pause(); 490}; 491 492/** 493 * Handles ad break ending. 494 */ 495PlayerWrapper.prototype.onAdBreakEnd = function () { 496 this.vjsPlayer.on('contentended', this.boundContentEndedListener); 497 if (this.vjsPlayer.ads.inAdBreak()) { 498 this.vjsPlayer.ads.endLinearAdMode(); 499 } 500 this.vjsControls.show(); 501}; 502 503/** 504 * Handles an individual ad start. 505 */ 506PlayerWrapper.prototype.onAdStart = function () { 507 this.vjsPlayer.trigger('ads-ad-started'); 508}; 509 510/** 511 * Handles when all ads have finished playing. 512 */ 513PlayerWrapper.prototype.onAllAdsCompleted = function () { 514 if (this.contentComplete == true) { 515 // The null check on this.contentSource was added to fix 516 // an error when the post-roll was an empty VAST tag. 517 if (this.contentSource && this.vjsPlayer.currentSrc() != this.contentSource) { 518 this.vjsPlayer.src({ 519 src: this.contentSource, 520 type: this.contentSourceType 521 }); 522 } 523 this.controller.onContentAndAdsCompleted(); 524 } 525}; 526 527/** 528 * Triggers adsready for contrib-ads. 529 */ 530PlayerWrapper.prototype.onAdsReady = function () { 531 this.vjsPlayer.trigger('adsready'); 532}; 533 534/** 535 * Changes the player source. 536 * @param {?string} contentSrc The URI for the content to be played. Leave 537 * blank to use the existing content. 538 */ 539PlayerWrapper.prototype.changeSource = function (contentSrc) { 540 // Only try to pause the player when initialised with a source already 541 if (this.vjsPlayer.currentSrc()) { 542 this.vjsPlayer.currentTime(0); 543 this.vjsPlayer.pause(); 544 } 545 if (contentSrc) { 546 this.vjsPlayer.src(contentSrc); 547 } 548 this.vjsPlayer.one('loadedmetadata', this.seekContentToZero.bind(this)); 549}; 550 551/** 552 * Seeks content to 00:00:00. This is used as an event handler for the 553 * loadedmetadata event, since seeking is not possible until that event has 554 * fired. 555 */ 556PlayerWrapper.prototype.seekContentToZero = function () { 557 this.vjsPlayer.currentTime(0); 558}; 559 560/** 561 * Triggers an event on the VJS player 562 * @param {string} name The event name. 563 * @param {Object} data The event data. 564 */ 565PlayerWrapper.prototype.triggerPlayerEvent = function (name, data) { 566 this.vjsPlayer.trigger(name, data); 567}; 568 569/** 570 * Listener JSDoc for ESLint. This listener can be passed to 571 * addContentEndedListener. 572 * @callback listener 573 */ 574 575/** 576 * Adds a listener for the 'contentended' event of the video player. This should 577 * be used instead of setting an 'contentended' listener directly to ensure that 578 * the ima can do proper cleanup of the SDK before other event listeners are 579 * called. 580 * @param {listener} listener The listener to be called when content 581 * completes. 582 */ 583PlayerWrapper.prototype.addContentEndedListener = function (listener) { 584 this.contentEndedListeners.push(listener); 585}; 586 587/** 588 * Reset the player. 589 */ 590PlayerWrapper.prototype.reset = function () { 591 // Attempts to remove the contentEndedListener before adding it. 592 // This is to prevent an error where an erroring video caused multiple 593 // contentEndedListeners to be added. 594 this.vjsPlayer.off('contentended', this.boundContentEndedListener); 595 596 this.vjsPlayer.on('contentended', this.boundContentEndedListener); 597 this.vjsControls.show(); 598 if (this.vjsPlayer.ads.inAdBreak()) { 599 this.vjsPlayer.ads.endLinearAdMode(); 600 } 601 // Reset the content time we give the SDK. Fixes an issue where requesting 602 // VMAP followed by VMAP would play the second mid-rolls as pre-rolls if 603 // the first playthrough of the video passed the second response's 604 // mid-roll time. 605 this.contentPlayheadTracker.currentTime = 0; 606 this.contentComplete = false; 607}; 608 609/** 610 * Copyright 2017 Google Inc. 611 * 612 * Licensed under the Apache License, Version 2.0 (the "License"); 613 * you may not use this file except in compliance with the License. 614 * You may obtain a copy of the License at 615 * 616 * http://www.apache.org/licenses/LICENSE-2.0 617 * 618 * Unless required by applicable law or agreed to in writing, software 619 * distributed under the License is distributed on an "AS IS" BASIS, 620 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 621 * See the License for the specific language governing permissions and 622 * limitations under the License. 623 * 624 * IMA SDK integration plugin for Video.js. For more information see 625 * https://www.github.com/googleads/videojs-ima 626 */ 627 628/** 629 * Ad UI implementation. 630 * 631 * @param {Controller} controller Plugin controller. 632 * @constructor 633 * @struct 634 * @final 635 */ 636const AdUi = function (controller) { 637 /** 638 * Plugin controller. 639 */ 640 this.controller = controller; 641 642 /** 643 * Div used as an ad container. 644 */ 645 this.adContainerDiv = document.createElement('div'); 646 647 /** 648 * Div used to display ad controls. 649 */ 650 this.controlsDiv = document.createElement('div'); 651 652 /** 653 * Div used to display ad countdown timer. 654 */ 655 this.countdownDiv = document.createElement('div'); 656 657 /** 658 * Div used to display add seek bar. 659 */ 660 this.seekBarDiv = document.createElement('div'); 661 662 /** 663 * Div used to display ad progress (in seek bar). 664 */ 665 this.progressDiv = document.createElement('div'); 666 667 /** 668 * Div used to display ad play/pause button. 669 */ 670 this.playPauseDiv = document.createElement('div'); 671 672 /** 673 * Div used to display ad mute button. 674 */ 675 this.muteDiv = document.createElement('div'); 676 677 /** 678 * Div used by the volume slider. 679 */ 680 this.sliderDiv = document.createElement('div'); 681 682 /** 683 * Volume slider level visuals 684 */ 685 this.sliderLevelDiv = document.createElement('div'); 686 687 /** 688 * Div used to display ad fullscreen button. 689 */ 690 this.fullscreenDiv = document.createElement('div'); 691 692 /** 693 * Bound event handler for onMouseUp. 694 */ 695 this.boundOnMouseUp = this.onMouseUp.bind(this); 696 697 /** 698 * Bound event handler for onMouseMove. 699 */ 700 this.boundOnMouseMove = this.onMouseMove.bind(this); 701 702 /** 703 * Stores data for the ad playhead tracker. 704 */ 705 this.adPlayheadTracker = { 706 'currentTime': 0, 707 'duration': 0, 708 'isPod': false, 709 'adPosition': 0, 710 'totalAds': 0 711 }; 712 713 /** 714 * Used to prefix videojs ima controls. 715 */ 716 this.controlPrefix = this.controller.getPlayerId() + '_'; 717 718 /** 719 * Boolean flag to show or hide the ad countdown timer. 720 */ 721 this.showCountdown = true; 722 if (this.controller.getSettings().showCountdown === false) { 723 this.showCountdown = false; 724 } 725 726 /** 727 * Boolean flag if the current ad is nonlinear. 728 */ 729 this.isAdNonlinear = false; 730 731 this.createAdContainer(); 732}; 733 734/** 735 * Creates the ad container. 736 */ 737AdUi.prototype.createAdContainer = function () { 738 this.assignControlAttributes(this.adContainerDiv, 'ima-ad-container'); 739 this.adContainerDiv.style.position = 'absolute'; 740 this.adContainerDiv.style.zIndex = 1111; 741 this.adContainerDiv.addEventListener('mouseenter', this.showAdControls.bind(this), false); 742 this.adContainerDiv.addEventListener('mouseleave', this.hideAdControls.bind(this), false); 743 this.adContainerDiv.addEventListener('click', this.onAdContainerClick.bind(this), false); 744 this.createControls(); 745 this.controller.injectAdContainerDiv(this.adContainerDiv); 746}; 747 748/** 749 * Create the controls. 750 */ 751AdUi.prototype.createControls = function () { 752 this.assignControlAttributes(this.controlsDiv, 'ima-controls-div'); 753 this.controlsDiv.style.width = '100%'; 754 755 if (!this.controller.getIsMobile()) { 756 this.assignControlAttributes(this.countdownDiv, 'ima-countdown-div'); 757 this.countdownDiv.innerHTML = this.controller.getSettings().adLabel; 758 this.countdownDiv.style.display = this.showCountdown ? 'block' : 'none'; 759 } else { 760 this.countdownDiv.style.display = 'none'; 761 } 762 763 this.assignControlAttributes(this.seekBarDiv, 'ima-seek-bar-div'); 764 this.seekBarDiv.style.width = '100%'; 765 766 this.assignControlAttributes(this.progressDiv, 'ima-progress-div'); 767 768 this.assignControlAttributes(this.playPauseDiv, 'ima-play-pause-div'); 769 this.addClass(this.playPauseDiv, 'ima-playing'); 770 this.playPauseDiv.addEventListener('click', this.onAdPlayPauseClick.bind(this), false); 771 772 this.assignControlAttributes(this.muteDiv, 'ima-mute-div'); 773 this.addClass(this.muteDiv, 'ima-non-muted'); 774 this.muteDiv.addEventListener('click', this.onAdMuteClick.bind(this), false); 775 776 this.assignControlAttributes(this.sliderDiv, 'ima-slider-div'); 777 this.sliderDiv.addEventListener('mousedown', this.onAdVolumeSliderMouseDown.bind(this), false); 778 779 // Hide volume slider controls on iOS as they aren't supported. 780 if (this.controller.getIsIos()) { 781 this.sliderDiv.style.display = 'none'; 782 } 783 784 this.assignControlAttributes(this.sliderLevelDiv, 'ima-slider-level-div'); 785 786 this.assignControlAttributes(this.fullscreenDiv, 'ima-fullscreen-div'); 787 this.addClass(this.fullscreenDiv, 'ima-non-fullscreen'); 788 this.fullscreenDiv.addEventListener('click', this.onAdFullscreenClick.bind(this), false); 789 790 this.adContainerDiv.appendChild(this.controlsDiv); 791 this.controlsDiv.appendChild(this.countdownDiv); 792 this.controlsDiv.appendChild(this.seekBarDiv); 793 this.controlsDiv.appendChild(this.playPauseDiv); 794 this.controlsDiv.appendChild(this.muteDiv); 795 this.controlsDiv.appendChild(this.sliderDiv); 796 this.controlsDiv.appendChild(this.fullscreenDiv); 797 this.seekBarDiv.appendChild(this.progressDiv); 798 this.sliderDiv.appendChild(this.sliderLevelDiv); 799}; 800 801/** 802 * Listener for clicks on the play/pause button during ad playback. 803 */ 804AdUi.prototype.onAdPlayPauseClick = function () { 805 this.controller.onAdPlayPauseClick(); 806}; 807 808/** 809 * Listener for clicks on the play/pause button during ad playback. 810 */ 811AdUi.prototype.onAdMuteClick = function () { 812 this.controller.onAdMuteClick(); 813}; 814 815/** 816 * Listener for clicks on the fullscreen button during ad playback. 817 */ 818AdUi.prototype.onAdFullscreenClick = function () { 819 this.controller.toggleFullscreen(); 820}; 821 822/** 823 * Show pause and hide play button 824 */ 825AdUi.prototype.onAdsPaused = function () { 826 this.controller.sdkImpl.adPlaying = false;
827 this.addClass(this.playPauseDiv, 'ima-paused'); 828 this.removeClass(this.playPauseDiv, 'ima-playing'); 829 this.showAdControls(); 830}; 831 832/** 833 * Show pause and hide play button 834 */ 835AdUi.prototype.onAdsResumed = function () { 836 this.onAdsPlaying(); 837 this.showAdControls(); 838}; 839 840/** 841 * Show play and hide pause button 842 */ 843AdUi.prototype.onAdsPlaying = function () { 844 this.controller.sdkImpl.adPlaying = true; 845 this.addClass(this.playPauseDiv, 'ima-playing'); 846 this.removeClass(this.playPauseDiv, 'ima-paused'); 847}; 848 849/** 850 * Takes data from the controller to update the UI. 851 * 852 * @param {number} currentTime Current time of the ad. 853 * @param {number} remainingTime Remaining time of the ad. 854 * @param {number} duration Duration of the ad. 855 * @param {number} adPosition Index of the ad in the pod. 856 * @param {number} totalAds Total number of ads in the pod. 857 */ 858AdUi.prototype.updateAdUi = function (currentTime, remainingTime, duration, adPosition, totalAds) { 859 // Update countdown timer data 860 const remainingMinutes = Math.floor(remainingTime / 60); 861 let remainingSeconds = Math.floor(remainingTime % 60); 862 if (remainingSeconds.toString().length < 2) { 863 remainingSeconds = '0' + remainingSeconds; 864 } 865 let podCount = ': '; 866 if (totalAds > 1) { 867 podCount = ' (' + adPosition + ' ' + this.controller.getSettings().adLabelNofN + ' ' + totalAds + '): '; 868 } 869 this.countdownDiv.innerHTML = this.controller.getSettings().adLabel + podCount + remainingMinutes + ':' + remainingSeconds; 870 871 // Update UI 872 const playProgressRatio = currentTime / duration; 873 const playProgressPercent = playProgressRatio * 100; 874 this.progressDiv.style.width = playProgressPercent + '%'; 875}; 876 877/** 878 * Handles UI changes when the ad is unmuted. 879 */ 880AdUi.prototype.unmute = function () { 881 this.addClass(this.muteDiv, 'ima-non-muted'); 882 this.removeClass(this.muteDiv, 'ima-muted'); 883 this.sliderLevelDiv.style.width = this.controller.getPlayerVolume() * 100 + '%'; 884}; 885 886/** 887 * Handles UI changes when the ad is muted. 888 */ 889AdUi.prototype.mute = function () { 890 this.addClass(this.muteDiv, 'ima-muted'); 891 this.removeClass(this.muteDiv, 'ima-non-muted'); 892 this.sliderLevelDiv.style.width = '0%'; 893}; 894 895/* 896 * Listener for mouse down events during ad playback. Used for volume. 897 */ 898AdUi.prototype.onAdVolumeSliderMouseDown = function () { 899 document.addEventListener('mouseup', this.boundOnMouseUp, false); 900 document.addEventListener('mousemove', this.boundOnMouseMove, false); 901}; 902 903/* 904 * Mouse movement listener used for volume slider. 905 */ 906AdUi.prototype.onMouseMove = function (event) { 907 this.changeVolume(event); 908}; 909 910/* 911 * Mouse release listener used for volume slider. 912 */ 913AdUi.prototype.onMouseUp = function (event) { 914 this.changeVolume(event); 915 document.removeEventListener('mouseup', this.boundOnMouseUp); 916 document.removeEventListener('mousemove', this.boundOnMouseMove); 917}; 918 919/* 920 * Utility function to set volume and associated UI 921 */ 922AdUi.prototype.changeVolume = function (event) { 923 let percent = (event.clientX - this.sliderDiv.getBoundingClientRect().left) / this.sliderDiv.offsetWidth; 924 percent *= 100; 925 // Bounds value 0-100 if mouse is outside slider region. 926 percent = Math.min(Math.max(percent, 0), 100); 927 this.sliderLevelDiv.style.width = percent + '%'; 928 if (this.percent == 0) { 929 this.addClass(this.muteDiv, 'ima-muted'); 930 this.removeClass(this.muteDiv, 'ima-non-muted'); 931 } else { 932 this.addClass(this.muteDiv, 'ima-non-muted'); 933 this.removeClass(this.muteDiv, 'ima-muted'); 934 } 935 this.controller.setVolume(percent / 100); // 0-1 936}; 937 938/** 939 * Show the ad container. 940 */ 941AdUi.prototype.showAdContainer = function () { 942 this.adContainerDiv.style.display = 'block'; 943}; 944 945/** 946 * Hide the ad container 947 */ 948AdUi.prototype.hideAdContainer = function () { 949 this.adContainerDiv.style.display = 'none'; 950}; 951 952/** 953 * Handles clicks on the ad container 954 */ 955AdUi.prototype.onAdContainerClick = function () { 956 if (this.isAdNonlinear) { 957 this.controller.togglePlayback(); 958 } 959}; 960 961/** 962 * Resets the state of the ad ui. 963 */ 964AdUi.prototype.reset = function () { 965 this.hideAdContainer(); 966}; 967 968/** 969 * Handles ad errors. 970 */ 971AdUi.prototype.onAdError = function () { 972 this.hideAdContainer(); 973}; 974 975/** 976 * Handles ad break starting. 977 * 978 * @param {Object} adEvent The event fired by the IMA SDK. 979 */ 980AdUi.prototype.onAdBreakStart = function (adEvent) { 981 this.showAdContainer(); 982 983 const contentType = adEvent.getAd().getContentType(); 984 if (contentType === 'application/javascript' && !this.controller.getSettings().showControlsForJSAds) { 985 this.controlsDiv.style.display = 'none'; 986 } else { 987 this.controlsDiv.style.display = 'block'; 988 } 989 this.onAdsPlaying(); 990 // Start with the ad controls minimized. 991 this.hideAdControls(); 992}; 993 994/** 995 * Handles ad break ending. 996 */ 997AdUi.prototype.onAdBreakEnd = function () { 998 const currentAd = this.controller.getCurrentAd(); 999 if (currentAd == null || // hide for post-roll only playlist 1000 currentAd.isLinear()) { 1001 // don't hide for non-linear ads 1002 this.hideAdContainer(); 1003 } 1004 this.controlsDiv.style.display = 'none'; 1005 this.countdownDiv.innerHTML = ''; 1006}; 1007 1008/** 1009 * Handles when all ads have finished playing. 1010 */ 1011AdUi.prototype.onAllAdsCompleted = function () { 1012 this.hideAdContainer(); 1013}; 1014 1015/** 1016 * Handles when a linear ad starts. 1017 */ 1018AdUi.prototype.onLinearAdStart = function () { 1019 // Don't bump container when controls are shown 1020 this.removeClass(this.adContainerDiv, 'bumpable-ima-ad-container'); 1021 this.isAdNonlinear = false;
vendor: 5,604 bytes, lines 1022-1164
1022}; 1023 1024/** 1025 * Handles when a non-linear ad starts. 1026 */ 1027AdUi.prototype.onNonLinearAdLoad = function () { 1028 // For non-linear ads that show after a linear ad. For linear ads, we show the 1029 // ad container in onAdBreakStart to prevent blinking in pods. 1030 this.adContainerDiv.style.display = 'block'; 1031 // Bump container when controls are shown 1032 this.addClass(this.adContainerDiv, 'bumpable-ima-ad-container'); 1033 this.isAdNonlinear = true; 1034}; 1035 1036AdUi.prototype.onPlayerEnterFullscreen = function () { 1037 this.addClass(this.fullscreenDiv, 'ima-fullscreen'); 1038 this.removeClass(this.fullscreenDiv, 'ima-non-fullscreen'); 1039}; 1040 1041AdUi.prototype.onPlayerExitFullscreen = function () { 1042 this.addClass(this.fullscreenDiv, 'ima-non-fullscreen'); 1043 this.removeClass(this.fullscreenDiv, 'ima-fullscreen'); 1044}; 1045 1046/** 1047 * Called when the player volume changes. 1048 * 1049 * @param {number} volume The new player volume. 1050 */ 1051AdUi.prototype.onPlayerVolumeChanged = function (volume) { 1052 if (volume == 0) { 1053 this.addClass(this.muteDiv, 'ima-muted'); 1054 this.removeClass(this.muteDiv, 'ima-non-muted'); 1055 this.sliderLevelDiv.style.width = '0%'; 1056 } else { 1057 this.addClass(this.muteDiv, 'ima-non-muted'); 1058 this.removeClass(this.muteDiv, 'ima-muted'); 1059 this.sliderLevelDiv.style.width = volume * 100 + '%'; 1060 } 1061}; 1062 1063/** 1064 * Shows ad controls on mouseover. 1065 */ 1066AdUi.prototype.showAdControls = function () { 1067 const { disableAdControls } = this.controller.getSettings(); 1068 if (!disableAdControls) { 1069 this.addClass(this.controlsDiv, 'ima-controls-div-showing'); 1070 } 1071}; 1072 1073/** 1074 * Hide the ad controls. 1075 */ 1076AdUi.prototype.hideAdControls = function () { 1077 this.removeClass(this.controlsDiv, 'ima-controls-div-showing'); 1078}; 1079 1080/** 1081 * Assigns the unique id and class names to the given element as well as the 1082 * style class. 1083 * @param {HTMLElement} element Element that needs the controlName assigned. 1084 * @param {string} controlName Control name to assign. 1085 */ 1086AdUi.prototype.assignControlAttributes = function (element, controlName) { 1087 element.id = this.controlPrefix + controlName; 1088 element.className = this.controlPrefix + controlName + ' ' + controlName; 1089}; 1090 1091/** 1092 * Returns a regular expression to test a string for the given className. 1093 * 1094 * @param {string} className The name of the class. 1095 * @return {RegExp} The regular expression used to test for that class. 1096 */ 1097AdUi.prototype.getClassRegexp = function (className) { 1098 // Matches on 1099 // (beginning of string OR NOT word char) 1100 // classname 1101 // (negative lookahead word char OR end of string) 1102 return new RegExp('(^|[^A-Za-z-])' + className + '((?![A-Za-z-])|$)', 'gi'); 1103}; 1104 1105/** 1106 * Returns whether or not the provided element has the provied class in its 1107 * className. 1108 * @param {HTMLElement} element Element to tes.t 1109 * @param {string} className Class to look for. 1110 * @return {boolean} True if element has className in class list. False 1111 * otherwise. 1112 */ 1113AdUi.prototype.elementHasClass = function (element, className) { 1114 const classRegexp = this.getClassRegexp(className); 1115 return classRegexp.test(element.className); 1116}; 1117 1118/** 1119 * Adds a class to the given element if it doesn't already have the class 1120 * @param {HTMLElement} element Element to which the class will be added. 1121 * @param {string} classToAdd Class to add. 1122 */ 1123AdUi.prototype.addClass = function (element, classToAdd) { 1124 element.className = element.className.trim() + ' ' + classToAdd; 1125}; 1126 1127/** 1128 * Removes a class from the given element if it has the given class 1129 * 1130 * @param {HTMLElement} element Element from which the class will be removed. 1131 * @param {string} classToRemove Class to remove. 1132 */ 1133AdUi.prototype.removeClass = function (element, classToRemove) { 1134 const classRegexp = this.getClassRegexp(classToRemove); 1135 element.className = element.className.trim().replace(classRegexp, ''); 1136}; 1137 1138/** 1139 * @return {HTMLElement} The div for the ad container. 1140 */ 1141AdUi.prototype.getAdContainerDiv = function () { 1142 return this.adContainerDiv; 1143}; 1144 1145/** 1146 * Changes the flag to show or hide the ad countdown timer. 1147 * 1148 * @param {boolean} showCountdownIn Show or hide the countdown timer. 1149 */ 1150AdUi.prototype.setShowCountdown = function (showCountdownIn) { 1151 this.showCountdown = showCountdownIn; 1152 this.countdownDiv.style.display = this.showCountdown ? 'block' : 'none'; 1153}; 1154 1155var name = "videojs-ima"; 1156var version = "2.0.1"; 1157var license = "Apache-2.0"; 1158var main = "./dist/videojs.ima.js"; 1159var module$1 = "./dist/videojs.ima.es.js"; 1160var author = { "name": "Google Inc." }; 1161var engines = { "node": ">=0.8.0" }; 1162var scripts = { "contBuild": "watch 'npm run rollup:max' src", "predevServer": "echo \"Starting up server on localhost:8000.\"", "devServer": "npm-run-all -p testServer contBuild", "lint": "eslint \"src/**/*.js\"", "rollup": "npm-run-all rollup:*", "rollup:max": "rollup -c configs/rollup.config.js", "rollup:es": "rollup -c configs/rollup.config.es.js", "rollup:min": "rollup -c configs/rollup.config.min.js", "pretest": "npm run rollup", "start": "npm run devServer", "test": "npm-run-all test:*", "test:vjs6": "npm install video.js@6 --no-save && npm-run-all -p -r testServer webdriver", "test:vjs7": "npm install video.js@7 --no-save && npm-run-all -p -r testServer webdriver", "testServer": "http-server --cors -p 8000 --silent", "preversion": "node scripts/preversion.js && npm run lint && npm test", "version": "node scripts/version.js", "postversion": "node scripts/postversion.js", "webdriver": "mocha test/webdriver/*.js --no-timeouts" }; 1163var repository = { "type": "git", "url": "https://github.com/googleads/videojs-ima" }; 1164var files = ["CHANGELOG.md", "LICENSE", "README.md", "dist/", "src/"];
1165var peerDependencies = { "video.js": "^5.19.2 || ^6 || ^7" }; 1166var dependencies = { "@hapi/cryptiles": "^5.1.0", "can-autoplay": "^3.0.2", "extend": ">=3.0.2", "videojs-contrib-ads": "^6.9.0" }; 1167var devDependencies = { "axios": "^0.25.0", "babel-core": "^6.26.3", "babel-preset-env": "^1.7.0", "child_process": "^1.0.2", "chromedriver": "^102.0.0", "conventional-changelog-cli": "^2.2.2", "conventional-changelog-videojs": "^3.0.2", "ecstatic": "^4.1.4", "eslint": "^8.8.0", "eslint-config-google": "^0.9.1", "eslint-plugin-jsdoc": "^3.15.1", "geckodriver": "^2.0.4", "http-server": "^14.1.0", "ini": ">=1.3.7", "mocha": "^9.2.0", "npm-run-all": "^4.1.5", "path": "^0.12.7", "protractor": "^7.0.0", "rimraf": "^2.7.1", "rollup": "^0.51.8", "rollup-plugin-babel": "^3.0.7", "rollup-plugin-copy": "^0.2.3", "rollup-plugin-json": "^2.3.1", "rollup-plugin-uglify": "^2.0.1", "selenium-webdriver": "^3.6.0", "uglify-es": "^3.3.9", "video.js": "^7.17.0", "watch": "^0.13.0", "webdriver-manager": "^12.1.7", "xmldom": "^0.6.0" }; 1168var keywords = ["videojs", "videojs-plugin"]; 1169var pkg = { 1170 name: name, 1171 version: version, 1172 license: license, 1173 main: main, 1174 module: module$1, 1175 author: author, 1176 engines: engines, 1177 scripts: scripts, 1178 repository: repository, 1179 files: files, 1180 peerDependencies: peerDependencies, 1181 dependencies: dependencies, 1182 devDependencies: devDependencies, 1183 keywords: keywords 1184}; 1185 1186/** 1187 * Copyright 2017 Google Inc. 1188 * 1189 * Licensed under the Apache License, Version 2.0 (the "License"); 1190 * you may not use this file except in compliance with the License. 1191 * You may obtain a copy of the License at 1192 * 1193 * http://www.apache.org/licenses/LICENSE-2.0 1194 * 1195 * Unless required by applicable law or agreed to in writing, software 1196 * distributed under the License is distributed on an "AS IS" BASIS, 1197 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1198 * See the License for the specific language governing permissions and 1199 * limitations under the License. 1200 * 1201 * IMA SDK integration plugin for Video.js. For more information see 1202 * https://www.github.com/googleads/videojs-ima 1203 */ 1204 1205/** 1206 * Implementation of the IMA SDK for the plugin. 1207 * 1208 * @param {Object} controller Reference to the parent controller. 1209 * 1210 * @constructor 1211 * @struct 1212 * @final 1213 */ 1214const SdkImpl = function (controller) { 1215 /** 1216 * Plugin controller. 1217 */ 1218 this.controller = controller; 1219 1220 /** 1221 * IMA SDK AdDisplayContainer. 1222 */ 1223 this.adDisplayContainer = null; 1224 1225 /** 1226 * True if the AdDisplayContainer has been initialized. False otherwise. 1227 */ 1228 this.adDisplayContainerInitialized = false; 1229 1230 /** 1231 * IMA SDK AdsLoader 1232 */ 1233 this.adsLoader = null; 1234 1235 /** 1236 * IMA SDK AdsManager 1237 */ 1238 this.adsManager = null; 1239 1240 /** 1241 * IMA SDK AdsRenderingSettings. 1242 */ 1243 this.adsRenderingSettings = null; 1244 1245 /** 1246 * VAST, VMAP, or ad rules response. Used in lieu of fetching a response 1247 * from an ad tag URL. 1248 */ 1249 this.adsResponse = null; 1250 1251 /** 1252 * Current IMA SDK Ad. 1253 */ 1254 this.currentAd = null; 1255 1256 /** 1257 * Timer used to track ad progress. 1258 */ 1259 this.adTrackingTimer = null; 1260 1261 /** 1262 * True if ALL_ADS_COMPLETED has fired, false until then. 1263 */ 1264 this.allAdsCompleted = false; 1265 1266 /** 1267 * True if ads are currently displayed, false otherwise. 1268 * True regardless of ad pause state if an ad is currently being displayed. 1269 */ 1270 this.adsActive = false; 1271 1272 /** 1273 * True if ad is currently playing, false if ad is paused or ads are not 1274 * currently displayed. 1275 */ 1276 this.adPlaying = false; 1277 1278 /** 1279 * True if the ad is muted, false otherwise. 1280 */ 1281 this.adMuted = false; 1282 1283 /** 1284 * Listener to be called to trigger manual ad break playback. 1285 */ 1286 this.adBreakReadyListener = undefined; 1287 1288 /** 1289 * Tracks whether or not we have already called adsLoader.contentComplete(). 1290 */ 1291 this.contentCompleteCalled = false; 1292 1293 /** 1294 * True if the ad has timed out. 1295 */ 1296 this.isAdTimedOut = false; 1297 1298 /** 1299 * Stores the dimensions for the ads manager. 1300 */ 1301 this.adsManagerDimensions = { 1302 width: 0, 1303 height: 0 1304 }; 1305 1306 /** 1307 * Boolean flag to enable manual ad break playback. 1308 */ 1309 this.autoPlayAdBreaks = true; 1310 if (this.controller.getSettings().autoPlayAdBreaks === false) { 1311 this.autoPlayAdBreaks = false;
vendor: 4,817 bytes, lines 1312-1434
1312 } 1313 1314 // Set SDK settings from plugin settings. 1315 if (this.controller.getSettings().locale) { 1316 /* eslint no-undef: 'error' */ 1317 /* global google */ 1318 google.ima.settings.setLocale(this.controller.getSettings().locale); 1319 } 1320 if (this.controller.getSettings().disableFlashAds) { 1321 google.ima.settings.setDisableFlashAds(this.controller.getSettings().disableFlashAds); 1322 } 1323 if (this.controller.getSettings().disableCustomPlaybackForIOS10Plus) { 1324 google.ima.settings.setDisableCustomPlaybackForIOS10Plus(this.controller.getSettings().disableCustomPlaybackForIOS10Plus); 1325 } 1326 1327 if (this.controller.getSettings().ppid) { 1328 google.ima.settings.setPpid(this.controller.getSettings().ppid); 1329 } 1330 1331 if (this.controller.getSettings().featureFlags) { 1332 google.ima.settings.setFeatureFlags(this.controller.getSettings().featureFlags); 1333 } 1334}; 1335 1336/** 1337 * Creates and initializes the IMA SDK objects. 1338 */ 1339SdkImpl.prototype.initAdObjects = function () { 1340 this.adDisplayContainer = new google.ima.AdDisplayContainer(this.controller.getAdContainerDiv(), this.controller.getContentPlayer()); 1341 1342 this.adsLoader = new google.ima.AdsLoader(this.adDisplayContainer); 1343 1344 this.adsLoader.getSettings().setVpaidMode(google.ima.ImaSdkSettings.VpaidMode.ENABLED); 1345 if (this.controller.getSettings().vpaidAllowed == false) { 1346 this.adsLoader.getSettings().setVpaidMode(google.ima.ImaSdkSettings.VpaidMode.DISABLED); 1347 } 1348 if (this.controller.getSettings().vpaidMode !== undefined) { 1349 this.adsLoader.getSettings().setVpaidMode(this.controller.getSettings().vpaidMode); 1350 } 1351 1352 if (this.controller.getSettings().locale) { 1353 this.adsLoader.getSettings().setLocale(this.controller.getSettings().locale); 1354 } 1355 1356 if (this.controller.getSettings().numRedirects) { 1357 this.adsLoader.getSettings().setNumRedirects(this.controller.getSettings().numRedirects); 1358 } 1359 1360 if (this.controller.getSettings().sessionId) { 1361 this.adsLoader.getSettings().setSessionId(this.controller.getSettings().sessionId); 1362 } 1363 1364 this.adsLoader.getSettings().setPlayerType('videojs-ima'); 1365 this.adsLoader.getSettings().setPlayerVersion(pkg.version); 1366 this.adsLoader.getSettings().setAutoPlayAdBreaks(this.autoPlayAdBreaks); 1367 1368 this.adsLoader.addEventListener(google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED, this.onAdsManagerLoaded.bind(this), false); 1369 this.adsLoader.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR, this.onAdsLoaderError.bind(this), false); 1370 1371 this.controller.playerWrapper.vjsPlayer.trigger({ 1372 type: 'ads-loader', 1373 adsLoader: this.adsLoader 1374 }); 1375}; 1376 1377/** 1378 * Creates the AdsRequest and request ads through the AdsLoader. 1379 */ 1380SdkImpl.prototype.requestAds = function () { 1381 const adsRequest = new google.ima.AdsRequest(); 1382 if (this.controller.getSettings().adTagUrl) { 1383 adsRequest.adTagUrl = this.controller.getSettings().adTagUrl; 1384 } else { 1385 adsRequest.adsResponse = this.controller.getSettings().adsResponse; 1386 } 1387 if (this.controller.getSettings().forceNonLinearFullSlot) { 1388 adsRequest.forceNonLinearFullSlot = true; 1389 } 1390 1391 if (this.controller.getSettings().vastLoadTimeout) { 1392 adsRequest.vastLoadTimeout = this.controller.getSettings().vastLoadTimeout; 1393 } 1394 1395 if (this.controller.getSettings().omidMode) { 1396 adsRequest.omidAccessModeRules = {}; 1397 const omidValues = this.controller.getSettings().omidMode; 1398 1399 if (omidValues.FULL) { 1400 adsRequest.omidAccessModeRules[google.ima.OmidAccessMode.FULL] = omidValues.FULL; 1401 } 1402 if (omidValues.DOMAIN) { 1403 adsRequest.omidAccessModeRules[google.ima.OmidAccessMode.DOMAIN] = omidValues.DOMAIN; 1404 } 1405 if (omidValues.LIMITED) { 1406 adsRequest.omidAccessModeRules[google.ima.OmidAccessMode.LIMITED] = omidValues.LIMITED; 1407 } 1408 } 1409 1410 adsRequest.linearAdSlotWidth = this.controller.getPlayerWidth(); 1411 adsRequest.linearAdSlotHeight = this.controller.getPlayerHeight(); 1412 adsRequest.nonLinearAdSlotWidth = this.controller.getSettings().nonLinearWidth || this.controller.getPlayerWidth(); 1413 adsRequest.nonLinearAdSlotHeight = this.controller.getSettings().nonLinearHeight || this.controller.getPlayerHeight(); 1414 adsRequest.setAdWillAutoPlay(this.controller.adsWillAutoplay()); 1415 adsRequest.setAdWillPlayMuted(this.controller.adsWillPlayMuted()); 1416 1417 // Populate the adsRequestproperties with those provided in the AdsRequest 1418 // object in the settings. 1419 let providedAdsRequest = this.controller.getSettings().adsRequest; 1420 if (providedAdsRequest && typeof providedAdsRequest === 'object') { 1421 Object.keys(providedAdsRequest).forEach(key => { 1422 adsRequest[key] = providedAdsRequest[key]; 1423 }); 1424 } 1425 1426 this.adsLoader.requestAds(adsRequest); 1427 this.controller.playerWrapper.vjsPlayer.trigger({ 1428 type: 'ads-request', 1429 AdsRequest: adsRequest 1430 }); 1431}; 1432 1433/** 1434 * Listener for the ADS_MANAGER_LOADED event. Creates the AdsManager,
1435 * sets up event listeners, and triggers the 'adsready' event for 1436 * videojs-ads-contrib. 1437 * 1438 * @param {google.ima.AdsManagerLoadedEvent} adsManagerLoadedEvent Fired when 1439 * the AdsManager loads. 1440 */ 1441SdkImpl.prototype.onAdsManagerLoaded = function (adsManagerLoadedEvent) { 1442 this.createAdsRenderingSettings(); 1443 1444 this.adsManager = adsManagerLoadedEvent.getAdsManager(this.controller.getContentPlayheadTracker(), this.adsRenderingSettings); 1445 1446 this.adsManager.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR, this.onAdError.bind(this)); 1447 this.adsManager.addEventListener(google.ima.AdEvent.Type.AD_BREAK_READY, this.onAdBreakReady.bind(this)); 1448 this.adsManager.addEventListener(google.ima.AdEvent.Type.CONTENT_PAUSE_REQUESTED, this.onContentPauseRequested.bind(this)); 1449 this.adsManager.addEventListener(google.ima.AdEvent.Type.CONTENT_RESUME_REQUESTED, this.onContentResumeRequested.bind(this)); 1450 this.adsManager.addEventListener(google.ima.AdEvent.Type.ALL_ADS_COMPLETED, this.onAllAdsCompleted.bind(this)); 1451 1452 this.adsManager.addEventListener(google.ima.AdEvent.Type.LOADED, this.onAdLoaded.bind(this)); 1453 this.adsManager.addEventListener(google.ima.AdEvent.Type.STARTED, this.onAdStarted.bind(this)); 1454 this.adsManager.addEventListener(google.ima.AdEvent.Type.COMPLETE, this.onAdComplete.bind(this)); 1455 this.adsManager.addEventListener(google.ima.AdEvent.Type.SKIPPED, this.onAdComplete.bind(this)); 1456 this.adsManager.addEventListener(google.ima.AdEvent.Type.LOG, this.onAdLog.bind(this)); 1457 this.adsManager.addEventListener(google.ima.AdEvent.Type.PAUSED, this.onAdPaused.bind(this)); 1458 this.adsManager.addEventListener(google.ima.AdEvent.Type.RESUMED, this.onAdResumed.bind(this)); 1459 1460 this.controller.playerWrapper.vjsPlayer.trigger({ 1461 type: 'ads-manager', 1462 adsManager: this.adsManager 1463 }); 1464 1465 if (!this.autoPlayAdBreaks) { 1466 this.initAdsManager(); 1467 } 1468 1469 const { preventLateAdStart } = this.controller.getSettings(); 1470 1471 if (!preventLateAdStart) { 1472 this.controller.onAdsReady(); 1473 } else if (preventLateAdStart && !this.isAdTimedOut) { 1474 this.controller.onAdsReady(); 1475 } 1476 1477 if (this.controller.getSettings().adsManagerLoadedCallback) { 1478 this.controller.getSettings().adsManagerLoadedCallback(); 1479 } 1480}; 1481 1482/** 1483 * Listener for errors fired by the AdsLoader. 1484 * @param {google.ima.AdErrorEvent} event The error event thrown by the 1485 * AdsLoader. See 1486 * https://developers.google.com/interactive-media-ads/docs/sdks/html5/client-side/reference/js/google.ima.AdError#.Type 1487 */ 1488SdkImpl.prototype.onAdsLoaderError = function (event) { 1489 window.console.warn('AdsLoader error: ' + event.getError()); 1490 this.controller.onErrorLoadingAds(event); 1491 if (this.adsManager) { 1492 this.adsManager.destroy(); 1493 } 1494}; 1495 1496/** 1497 * Initialize the ads manager. 1498 */ 1499SdkImpl.prototype.initAdsManager = function () { 1500 try { 1501 const initWidth = this.controller.getPlayerWidth(); 1502 const initHeight = this.controller.getPlayerHeight(); 1503 this.adsManagerDimensions.width = initWidth; 1504 this.adsManagerDimensions.height = initHeight; 1505 this.adsManager.init(initWidth, initHeight, google.ima.ViewMode.NORMAL); 1506 this.adsManager.setVolume(this.controller.getPlayerVolume()); 1507 this.initializeAdDisplayContainer(); 1508 } catch (adError) { 1509 this.onAdError(adError); 1510 } 1511}; 1512 1513/** 1514 * Create AdsRenderingSettings for the IMA SDK. 1515 */ 1516SdkImpl.prototype.createAdsRenderingSettings = function () { 1517 this.adsRenderingSettings = new google.ima.AdsRenderingSettings(); 1518 this.adsRenderingSettings.restoreCustomPlaybackStateOnAdBreakComplete = true; 1519 if (this.controller.getSettings().adsRenderingSettings) { 1520 for (let setting in this.controller.getSettings().adsRenderingSettings) { 1521 if (setting !== '') { 1522 this.adsRenderingSettings[setting] = this.controller.getSettings().adsRenderingSettings[setting]; 1523 } 1524 } 1525 } 1526}; 1527 1528/** 1529 * Listener for errors thrown by the AdsManager. 1530 * @param {google.ima.AdErrorEvent} adErrorEvent The error event thrown by 1531 * the AdsManager. 1532 */ 1533SdkImpl.prototype.onAdError = function (adErrorEvent) { 1534 const errorMessage = adErrorEvent.getError !== undefined ? adErrorEvent.getError() : adErrorEvent.stack; 1535 window.console.warn('Ad error: ' + errorMessage); 1536 1537 this.adsManager.destroy(); 1538 this.controller.onAdError(adErrorEvent); 1539 1540 // reset these so consumers don't think we are still in an ad break, 1541 // but reset them after any prior cleanup happens 1542 this.adsActive = false;
vendor: 29,487 bytes, lines 1543-2630
1543 this.adPlaying = false; 1544}; 1545 1546/** 1547 * Listener for AD_BREAK_READY. Passes event on to publisher's listener. 1548 * @param {google.ima.AdEvent} adEvent AdEvent thrown by the AdsManager. 1549 */ 1550SdkImpl.prototype.onAdBreakReady = function (adEvent) { 1551 this.adBreakReadyListener(adEvent); 1552}; 1553 1554/** 1555 * Pauses the content video and displays the ad container so ads can play. 1556 * @param {google.ima.AdEvent} adEvent The AdEvent thrown by the AdsManager. 1557 */ 1558SdkImpl.prototype.onContentPauseRequested = function (adEvent) { 1559 this.adsActive = true; 1560 this.adPlaying = true; 1561 this.controller.onAdBreakStart(adEvent); 1562}; 1563 1564/** 1565 * Resumes content video and hides the ad container. 1566 * @param {google.ima.AdEvent} adEvent The AdEvent thrown by the AdsManager. 1567 */ 1568SdkImpl.prototype.onContentResumeRequested = function (adEvent) { 1569 this.adsActive = false; 1570 this.adPlaying = false; 1571 this.controller.onAdBreakEnd(); 1572 // Hide controls in case of future non-linear ads. They'll be unhidden in 1573 // content_pause_requested. 1574}; 1575 1576/** 1577 * Records that ads have completed and calls contentAndAdsEndedListeners 1578 * if content is also complete. 1579 * @param {google.ima.AdEvent} adEvent The AdEvent thrown by the AdsManager. 1580 */ 1581SdkImpl.prototype.onAllAdsCompleted = function (adEvent) { 1582 this.allAdsCompleted = true; 1583 this.controller.onAllAdsCompleted(); 1584}; 1585 1586/** 1587 * Starts the content video when a non-linear ad is loaded. 1588 * @param {google.ima.AdEvent} adEvent The AdEvent thrown by the AdsManager. 1589 */ 1590SdkImpl.prototype.onAdLoaded = function (adEvent) { 1591 if (!adEvent.getAd().isLinear()) { 1592 this.controller.onNonLinearAdLoad(); 1593 this.controller.playContent(); 1594 } 1595}; 1596 1597/** 1598 * Starts the interval timer to check the current ad time when an ad starts 1599 * playing. 1600 * @param {google.ima.AdEvent} adEvent The AdEvent thrown by the AdsManager. 1601 */ 1602SdkImpl.prototype.onAdStarted = function (adEvent) { 1603 this.currentAd = adEvent.getAd(); 1604 if (this.currentAd.isLinear()) { 1605 this.adTrackingTimer = setInterval(this.onAdPlayheadTrackerInterval.bind(this), 250); 1606 this.controller.onLinearAdStart(); 1607 } else { 1608 this.controller.onNonLinearAdStart(); 1609 } 1610}; 1611 1612/** 1613 * Handles an ad click. Puts the player UI in a paused state. 1614 */ 1615SdkImpl.prototype.onAdPaused = function () { 1616 this.controller.onAdsPaused(); 1617}; 1618 1619/** 1620 * Syncs controls when an ad resumes. 1621 * @param {google.ima.AdEvent} adEvent The AdEvent thrown by the AdsManager. 1622 */ 1623SdkImpl.prototype.onAdResumed = function (adEvent) { 1624 this.controller.onAdsResumed(); 1625}; 1626 1627/** 1628 * Clears the interval timer for current ad time when an ad completes. 1629 */ 1630SdkImpl.prototype.onAdComplete = function () { 1631 if (this.currentAd.isLinear()) { 1632 clearInterval(this.adTrackingTimer); 1633 } 1634}; 1635 1636/** 1637 * Handles ad log messages. 1638 * @param {google.ima.AdEvent} adEvent The AdEvent thrown by the AdsManager. 1639 */ 1640SdkImpl.prototype.onAdLog = function (adEvent) { 1641 this.controller.onAdLog(adEvent); 1642}; 1643 1644/** 1645 * Gets the current time and duration of the ad and calls the method to 1646 * update the ad UI. 1647 */ 1648SdkImpl.prototype.onAdPlayheadTrackerInterval = function () { 1649 if (this.adsManager === null) return; 1650 const remainingTime = this.adsManager.getRemainingTime(); 1651 const duration = this.currentAd.getDuration(); 1652 let currentTime = duration - remainingTime; 1653 currentTime = currentTime > 0 ? currentTime : 0; 1654 let totalAds = 0; 1655 let adPosition; 1656 if (this.currentAd.getAdPodInfo()) { 1657 adPosition = this.currentAd.getAdPodInfo().getAdPosition(); 1658 totalAds = this.currentAd.getAdPodInfo().getTotalAds(); 1659 } 1660 1661 this.controller.onAdPlayheadUpdated(currentTime, remainingTime, duration, adPosition, totalAds); 1662}; 1663 1664/** 1665 * Called by the player wrapper when content completes. 1666 */ 1667SdkImpl.prototype.onContentComplete = function () { 1668 if (this.adsLoader) { 1669 this.adsLoader.contentComplete(); 1670 this.contentCompleteCalled = true; 1671 } 1672 1673 if (this.adsManager && this.adsManager.getCuePoints() && !this.adsManager.getCuePoints().includes(-1) || !this.adsManager) { 1674 this.controller.onNoPostroll(); 1675 } 1676 1677 if (this.allAdsCompleted) { 1678 this.controller.onContentAndAdsCompleted(); 1679 } 1680}; 1681 1682/** 1683 * Called when the player is disposed. 1684 */ 1685SdkImpl.prototype.onPlayerDisposed = function () { 1686 if (this.adTrackingTimer) { 1687 clearInterval(this.adTrackingTimer); 1688 } 1689 if (this.adsManager) { 1690 this.adsManager.destroy(); 1691 this.adsManager = null; 1692 } 1693}; 1694 1695SdkImpl.prototype.onPlayerReadyForPreroll = function () { 1696 if (this.autoPlayAdBreaks) { 1697 this.initAdsManager(); 1698 try { 1699 this.controller.showAdContainer(); 1700 // Sync ad volume with content volume. 1701 this.adsManager.setVolume(this.controller.getPlayerVolume()); 1702 this.adsManager.start(); 1703 } catch (adError) { 1704 this.onAdError(adError); 1705 } 1706 } 1707}; 1708 1709SdkImpl.prototype.onAdTimeout = function () { 1710 this.isAdTimedOut = true; 1711}; 1712 1713SdkImpl.prototype.onPlayerReady = function () { 1714 this.initAdObjects(); 1715 1716 if ((this.controller.getSettings().adTagUrl || this.controller.getSettings().adsResponse) && this.controller.getSettings().requestMode === 'onLoad') { 1717 this.requestAds(); 1718 } 1719}; 1720 1721SdkImpl.prototype.onPlayerEnterFullscreen = function () { 1722 if (this.adsManager) { 1723 this.adsManager.resize(window.screen.width, window.screen.height, google.ima.ViewMode.FULLSCREEN); 1724 } 1725}; 1726 1727SdkImpl.prototype.onPlayerExitFullscreen = function () { 1728 if (this.adsManager) { 1729 this.adsManager.resize(this.controller.getPlayerWidth(), this.controller.getPlayerHeight(), google.ima.ViewMode.NORMAL); 1730 } 1731}; 1732 1733/** 1734 * Called when the player volume changes. 1735 * 1736 * @param {number} volume The new player volume. 1737 */ 1738SdkImpl.prototype.onPlayerVolumeChanged = function (volume) { 1739 if (this.adsManager) { 1740 this.adsManager.setVolume(volume); 1741 } 1742 1743 if (volume == 0) { 1744 this.adMuted = true; 1745 } else { 1746 this.adMuted = false; 1747 } 1748}; 1749 1750/** 1751 * Called when the player wrapper detects that the player has been resized. 1752 * 1753 * @param {number} width The post-resize width of the player. 1754 * @param {number} height The post-resize height of the player. 1755 */ 1756SdkImpl.prototype.onPlayerResize = function (width, height) { 1757 if (this.adsManager) { 1758 this.adsManagerDimensions.width = width; 1759 this.adsManagerDimensions.height = height; 1760 /* eslint no-undef: 'error' */ 1761 this.adsManager.resize(width, height, google.ima.ViewMode.NORMAL); 1762 } 1763}; 1764 1765/** 1766 * @return {Object} The current ad. 1767 */ 1768SdkImpl.prototype.getCurrentAd = function () { 1769 return this.currentAd; 1770}; 1771 1772/** 1773 * Listener JSDoc for ESLint. This listener can be passed to 1774 * setAdBreakReadyListener. 1775 * @callback listener 1776 */ 1777 1778/** 1779 * Sets the listener to be called to trigger manual ad break playback. 1780 * @param {listener} listener The listener to be called to trigger manual ad 1781 * break playback. 1782 */ 1783SdkImpl.prototype.setAdBreakReadyListener = function (listener) { 1784 this.adBreakReadyListener = listener; 1785}; 1786 1787/** 1788 * @return {boolean} True if an ad is currently playing. False otherwise. 1789 */ 1790SdkImpl.prototype.isAdPlaying = function () { 1791 return this.adPlaying; 1792}; 1793 1794/** 1795 * @return {boolean} True if an ad is currently playing. False otherwise. 1796 */ 1797SdkImpl.prototype.isAdMuted = function () { 1798 return this.adMuted; 1799}; 1800 1801/** 1802 * Pause ads. 1803 */ 1804SdkImpl.prototype.pauseAds = function () { 1805 this.adsManager.pause(); 1806 this.adPlaying = false; 1807}; 1808 1809/** 1810 * Resume ads. 1811 */ 1812SdkImpl.prototype.resumeAds = function () { 1813 this.adsManager.resume(); 1814 this.adPlaying = true; 1815}; 1816 1817/** 1818 * Unmute ads. 1819 */ 1820SdkImpl.prototype.unmute = function () { 1821 this.adsManager.setVolume(1); 1822 this.adMuted = false; 1823}; 1824 1825/** 1826 * Mute ads. 1827 */ 1828SdkImpl.prototype.mute = function () { 1829 this.adsManager.setVolume(0); 1830 this.adMuted = true; 1831}; 1832 1833/** 1834 * Set the volume of the ads. 0-1. 1835 * 1836 * @param {number} volume The new volume. 1837 */ 1838SdkImpl.prototype.setVolume = function (volume) { 1839 this.adsManager.setVolume(volume); 1840 if (volume == 0) { 1841 this.adMuted = true; 1842 } else { 1843 this.adMuted = false; 1844 } 1845}; 1846 1847/** 1848 * Initializes the AdDisplayContainer. On mobile, this must be done as a 1849 * result of user action. 1850 */ 1851SdkImpl.prototype.initializeAdDisplayContainer = function () { 1852 if (this.adDisplayContainer) { 1853 if (!this.adDisplayContainerInitialized) { 1854 this.adDisplayContainer.initialize(); 1855 this.adDisplayContainerInitialized = true; 1856 } 1857 } 1858}; 1859 1860/** 1861 * Called by publishers in manual ad break playback mode to start an ad 1862 * break. 1863 */ 1864SdkImpl.prototype.playAdBreak = function () { 1865 if (!this.autoPlayAdBreaks) { 1866 this.controller.showAdContainer(); 1867 // Sync ad volume with content volume. 1868 this.adsManager.setVolume(this.controller.getPlayerVolume()); 1869 this.adsManager.start(); 1870 } 1871}; 1872 1873/** 1874 * Callback JSDoc for ESLint. This callback can be passed to addEventListener. 1875 * @callback callback 1876 */ 1877 1878/** 1879 * Ads an EventListener to the AdsManager. For a list of available events, 1880 * see 1881 * https://developers.google.com/interactive-media-ads/docs/sdks/html5/client-side/reference/js/google.ima.AdEvent#.Type 1882 * @param {google.ima.AdEvent.Type} event The AdEvent.Type for which to 1883 * listen. 1884 * @param {callback} callback The method to call when the event is fired. 1885 */ 1886SdkImpl.prototype.addEventListener = function (event, callback) { 1887 if (this.adsManager) { 1888 this.adsManager.addEventListener(event, callback); 1889 } 1890}; 1891 1892/** 1893 * Returns the instance of the AdsManager. 1894 * @return {google.ima.AdsManager} The AdsManager being used by the plugin. 1895 */ 1896SdkImpl.prototype.getAdsManager = function () { 1897 return this.adsManager; 1898}; 1899 1900/** 1901 * Reset the SDK implementation. 1902 */ 1903SdkImpl.prototype.reset = function () { 1904 this.adsActive = false; 1905 this.adPlaying = false; 1906 if (this.adTrackingTimer) { 1907 // If this is called while an ad is playing, stop trying to get that 1908 // ad's current time. 1909 clearInterval(this.adTrackingTimer); 1910 } 1911 if (this.adsManager) { 1912 this.adsManager.destroy(); 1913 this.adsManager = null; 1914 } 1915 if (this.adsLoader && !this.contentCompleteCalled) { 1916 this.adsLoader.contentComplete(); 1917 } 1918 this.contentCompleteCalled = false; 1919 this.allAdsCompleted = false; 1920}; 1921 1922/** 1923 * Copyright 2017 Google Inc. 1924 * 1925 * Licensed under the Apache License, Version 2.0 (the "License"); 1926 * you may not use this file except in compliance with the License. 1927 * You may obtain a copy of the License at 1928 * 1929 * http://www.apache.org/licenses/LICENSE-2.0 1930 * 1931 * Unless required by applicable law or agreed to in writing, software 1932 * distributed under the License is distributed on an "AS IS" BASIS, 1933 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1934 * See the License for the specific language governing permissions and 1935 * limitations under the License. 1936 * 1937 * IMA SDK integration plugin for Video.js. For more information see 1938 * https://www.github.com/googleads/videojs-ima 1939 */ 1940/** 1941 * The grand coordinator of the plugin. Facilitates communication between all 1942 * other plugin classes. 1943 * 1944 * @param {Object} player Instance of the video.js player. 1945 * @param {Object} options Options provided by the implementation. 1946 * @constructor 1947 * @struct 1948 * @final 1949 */ 1950const Controller = function (player, options) { 1951 /** 1952 * Stores user-provided settings. 1953 * @type {Object} 1954 */ 1955 this.settings = {}; 1956 1957 /** 1958 * Content and ads ended listeners passed by the publisher to the plugin. 1959 * These will be called when the plugin detects that content *and all 1960 * ads* have completed. This differs from the contentEndedListeners in that 1961 * contentEndedListeners will fire between content ending and a post-roll 1962 * playing, whereas the contentAndAdsEndedListeners will fire after the 1963 * post-roll completes. 1964 */ 1965 this.contentAndAdsEndedListeners = []; 1966 1967 /** 1968 * Whether or not we are running on a mobile platform. 1969 */ 1970 this.isMobile = navigator.userAgent.match(/iPhone/i) || navigator.userAgent.match(/iPad/i) || navigator.userAgent.match(/Android/i); 1971 1972 /** 1973 * Whether or not we are running on an iOS platform. 1974 */ 1975 this.isIos = navigator.userAgent.match(/iPhone/i) || navigator.userAgent.match(/iPad/i); 1976 1977 this.initWithSettings(options); 1978 1979 /** 1980 * Stores contrib-ads default settings. 1981 */ 1982 const contribAdsDefaults = { 1983 debug: this.settings.debug, 1984 timeout: this.settings.timeout, 1985 prerollTimeout: this.settings.prerollTimeout 1986 }; 1987 const adsPluginSettings = Object.assign({}, contribAdsDefaults, options.contribAdsSettings || {}); 1988 1989 this.playerWrapper = new PlayerWrapper(player, adsPluginSettings, this); 1990 this.adUi = new AdUi(this); 1991 this.sdkImpl = new SdkImpl(this); 1992}; 1993 1994Controller.IMA_DEFAULTS = { 1995 adLabel: 'Advertisement', 1996 adLabelNofN: 'of', 1997 debug: false, 1998 disableAdControls: false, 1999 prerollTimeout: 1000, 2000 preventLateAdStart: false, 2001 requestMode: 'onLoad', 2002 showControlsForJSAds: true, 2003 timeout: 5000 2004}; 2005 2006/** 2007 * Extends the settings to include user-provided settings. 2008 * 2009 * @param {Object} options Options to be used in initialization. 2010 */ 2011Controller.prototype.initWithSettings = function (options) { 2012 this.settings = Object.assign({}, Controller.IMA_DEFAULTS, options || {}); 2013 2014 this.warnAboutDeprecatedSettings(); 2015 2016 // Default showing countdown timer to true. 2017 this.showCountdown = true; 2018 if (this.settings.showCountdown === false) { 2019 this.showCountdown = false; 2020 } 2021}; 2022 2023/** 2024 * Logs console warnings when deprecated settings are used. 2025 */ 2026Controller.prototype.warnAboutDeprecatedSettings = function () { 2027 const deprecatedSettings = ['adWillAutoplay', 'adsWillAutoplay', 'adWillPlayMuted', 'adsWillPlayMuted']; 2028 deprecatedSettings.forEach(setting => { 2029 if (this.settings[setting] !== undefined) { 2030 console.warn('WARNING: videojs.ima setting ' + setting + ' is deprecated'); 2031 } 2032 }); 2033}; 2034 2035/** 2036 * Return the settings object. 2037 * 2038 * @return {Object} The settings object. 2039 */ 2040Controller.prototype.getSettings = function () { 2041 return this.settings; 2042}; 2043 2044/** 2045 * Return whether or not we're in a mobile environment. 2046 * 2047 * @return {boolean} True if running on mobile, false otherwise. 2048 */ 2049Controller.prototype.getIsMobile = function () { 2050 return this.isMobile; 2051}; 2052 2053/** 2054 * Return whether or not we're in an iOS environment. 2055 * 2056 * @return {boolean} True if running on iOS, false otherwise. 2057 */ 2058Controller.prototype.getIsIos = function () { 2059 return this.isIos; 2060}; 2061 2062/** 2063 * Inject the ad container div into the DOM. 2064 * 2065 * @param{HTMLElement} adContainerDiv The ad container div. 2066 */ 2067Controller.prototype.injectAdContainerDiv = function (adContainerDiv) { 2068 this.playerWrapper.injectAdContainerDiv(adContainerDiv); 2069}; 2070 2071/** 2072 * @return {HTMLElement} The div for the ad container. 2073 */ 2074Controller.prototype.getAdContainerDiv = function () { 2075 return this.adUi.getAdContainerDiv(); 2076}; 2077 2078/** 2079 * @return {Object} The content player. 2080 */ 2081Controller.prototype.getContentPlayer = function () { 2082 return this.playerWrapper.getContentPlayer(); 2083}; 2084 2085/** 2086 * Returns the content playhead tracker. 2087 * 2088 * @return {Object} The content playhead tracker. 2089 */ 2090Controller.prototype.getContentPlayheadTracker = function () { 2091 return this.playerWrapper.getContentPlayheadTracker(); 2092}; 2093 2094/** 2095 * Requests ads. 2096 */ 2097Controller.prototype.requestAds = function () { 2098 this.sdkImpl.requestAds(); 2099}; 2100 2101/** 2102 * Add or modify a setting. 2103 * 2104 * @param {string} key Key to modify 2105 * @param {Object} value Value to set at key. 2106 */ 2107Controller.prototype.setSetting = function (key, value) { 2108 this.settings[key] = value; 2109}; 2110 2111/** 2112 * Called when there is an error loading ads. 2113 * 2114 * @param {Object} adErrorEvent The ad error event thrown by the IMA SDK. 2115 */ 2116Controller.prototype.onErrorLoadingAds = function (adErrorEvent) { 2117 this.adUi.onAdError(); 2118 this.playerWrapper.onAdError(adErrorEvent); 2119}; 2120 2121/** 2122 * Called by the ad UI when the play/pause button is clicked. 2123 */ 2124Controller.prototype.onAdPlayPauseClick = function () { 2125 if (this.sdkImpl.isAdPlaying()) { 2126 this.adUi.onAdsPaused(); 2127 this.sdkImpl.pauseAds(); 2128 } else { 2129 this.adUi.onAdsPlaying(); 2130 this.sdkImpl.resumeAds(); 2131 } 2132}; 2133 2134/** 2135 * Called by the ad UI when the mute button is clicked. 2136 * 2137 */ 2138Controller.prototype.onAdMuteClick = function () { 2139 if (this.sdkImpl.isAdMuted()) { 2140 this.playerWrapper.unmute(); 2141 this.adUi.unmute(); 2142 this.sdkImpl.unmute(); 2143 } else { 2144 this.playerWrapper.mute(); 2145 this.adUi.mute(); 2146 this.sdkImpl.mute(); 2147 } 2148}; 2149 2150/** 2151 * Set the volume of the player and ads. 0-1. 2152 * 2153 * @param {number} volume The new volume. 2154 */ 2155Controller.prototype.setVolume = function (volume) { 2156 this.playerWrapper.setVolume(volume); 2157 this.sdkImpl.setVolume(volume); 2158}; 2159 2160/** 2161 * @return {number} The volume of the content player. 2162 */ 2163Controller.prototype.getPlayerVolume = function () { 2164 return this.playerWrapper.getVolume(); 2165}; 2166 2167/** 2168 * Toggle fullscreen state. 2169 */ 2170Controller.prototype.toggleFullscreen = function () { 2171 this.playerWrapper.toggleFullscreen(); 2172}; 2173 2174/** 2175 * Relays ad errors to the player wrapper. 2176 * 2177 * @param {Object} adErrorEvent The ad error event thrown by the IMA SDK. 2178 */ 2179Controller.prototype.onAdError = function (adErrorEvent) { 2180 this.adUi.onAdError(); 2181 this.playerWrapper.onAdError(adErrorEvent); 2182}; 2183 2184/** 2185 * Handles ad break starting. 2186 * 2187 * @param {Object} adEvent The event fired by the IMA SDK. 2188 */ 2189Controller.prototype.onAdBreakStart = function (adEvent) { 2190 this.playerWrapper.onAdBreakStart(); 2191 this.adUi.onAdBreakStart(adEvent); 2192}; 2193 2194/** 2195 * Show the ad container. 2196 */ 2197Controller.prototype.showAdContainer = function () { 2198 this.adUi.showAdContainer(); 2199}; 2200 2201/** 2202 * Handles ad break ending. 2203 */ 2204Controller.prototype.onAdBreakEnd = function () { 2205 this.playerWrapper.onAdBreakEnd(); 2206 this.adUi.onAdBreakEnd(); 2207}; 2208 2209/** 2210 * Handles when all ads have finished playing. 2211 */ 2212Controller.prototype.onAllAdsCompleted = function () { 2213 this.adUi.onAllAdsCompleted(); 2214 this.playerWrapper.onAllAdsCompleted(); 2215}; 2216 2217/** 2218 * Handles the SDK firing an ad paused event. 2219 */ 2220Controller.prototype.onAdsPaused = function () { 2221 this.adUi.onAdsPaused(); 2222}; 2223 2224/** 2225 * Handles the SDK firing an ad resumed event. 2226 */ 2227Controller.prototype.onAdsResumed = function () { 2228 this.adUi.onAdsResumed(); 2229}; 2230 2231/** 2232 * Takes data from the sdk impl and passes it to the ad UI to update the UI. 2233 * 2234 * @param {number} currentTime Current time of the ad. 2235 * @param {number} remainingTime Remaining time of the ad. 2236 * @param {number} duration Duration of the ad. 2237 * @param {number} adPosition Index of the ad in the pod. 2238 * @param {number} totalAds Total number of ads in the pod. 2239 */ 2240Controller.prototype.onAdPlayheadUpdated = function (currentTime, remainingTime, duration, adPosition, totalAds) { 2241 this.adUi.updateAdUi(currentTime, remainingTime, duration, adPosition, totalAds); 2242}; 2243 2244/** 2245 * Handles ad log messages. 2246 * @param {google.ima.AdEvent} adEvent The AdEvent thrown by the IMA SDK. 2247 */ 2248Controller.prototype.onAdLog = function (adEvent) { 2249 this.playerWrapper.onAdLog(adEvent); 2250}; 2251 2252/** 2253 * @return {Object} The current ad. 2254 */ 2255Controller.prototype.getCurrentAd = function () { 2256 return this.sdkImpl.getCurrentAd(); 2257}; 2258 2259/** 2260 * Play content. 2261 */ 2262Controller.prototype.playContent = function () { 2263 this.playerWrapper.play(); 2264}; 2265 2266/** 2267 * Handles when a linear ad starts. 2268 */ 2269Controller.prototype.onLinearAdStart = function () { 2270 this.adUi.onLinearAdStart(); 2271 this.playerWrapper.onAdStart(); 2272}; 2273 2274/** 2275 * Handles when a non-linear ad loads. 2276 */ 2277Controller.prototype.onNonLinearAdLoad = function () { 2278 this.adUi.onNonLinearAdLoad(); 2279}; 2280 2281/** 2282 * Handles when a non-linear ad starts. 2283 */ 2284Controller.prototype.onNonLinearAdStart = function () { 2285 this.adUi.onNonLinearAdLoad(); 2286 this.playerWrapper.onAdStart(); 2287}; 2288 2289/** 2290 * Get the player width. 2291 * 2292 * @return {number} The width of the player. 2293 */ 2294Controller.prototype.getPlayerWidth = function () { 2295 return this.playerWrapper.getPlayerWidth(); 2296}; 2297 2298/** 2299 * Get the player height. 2300 * 2301 * @return {number} The height of the player. 2302 */ 2303Controller.prototype.getPlayerHeight = function () { 2304 return this.playerWrapper.getPlayerHeight(); 2305}; 2306 2307/** 2308 * Tells the player wrapper that ads are ready. 2309 */ 2310Controller.prototype.onAdsReady = function () { 2311 this.playerWrapper.onAdsReady(); 2312}; 2313 2314/** 2315 * Called when the player wrapper detects that the player has been resized. 2316 * 2317 * @param {number} width The post-resize width of the player. 2318 * @param {number} height The post-resize height of the player. 2319 */ 2320Controller.prototype.onPlayerResize = function (width, height) { 2321 this.sdkImpl.onPlayerResize(width, height); 2322}; 2323 2324/** 2325 * Called by the player wrapper when content completes. 2326 */ 2327Controller.prototype.onContentComplete = function () { 2328 this.sdkImpl.onContentComplete(); 2329}; 2330 2331/** 2332 * Called by the player wrapper when it's time to play a post-roll but we don't 2333 * have one to play. 2334 */ 2335Controller.prototype.onNoPostroll = function () { 2336 this.playerWrapper.onNoPostroll(); 2337}; 2338 2339/** 2340 * Called when content and all ads have completed. 2341 */ 2342Controller.prototype.onContentAndAdsCompleted = function () { 2343 for (let index in this.contentAndAdsEndedListeners) { 2344 if (typeof this.contentAndAdsEndedListeners[index] === 'function') { 2345 this.contentAndAdsEndedListeners[index](); 2346 } 2347 } 2348}; 2349 2350/** 2351 * Called when the player is disposed. 2352 */ 2353Controller.prototype.onPlayerDisposed = function () { 2354 this.contentAndAdsEndedListeners = []; 2355 this.sdkImpl.onPlayerDisposed(); 2356}; 2357 2358/** 2359 * Called when the player is ready to play a pre-roll. 2360 */ 2361Controller.prototype.onPlayerReadyForPreroll = function () { 2362 this.sdkImpl.onPlayerReadyForPreroll(); 2363}; 2364 2365/** 2366 * Called if the ad times out. 2367 */ 2368Controller.prototype.onAdTimeout = function () { 2369 this.sdkImpl.onAdTimeout(); 2370}; 2371 2372/** 2373 * Called when the player is ready. 2374 */ 2375Controller.prototype.onPlayerReady = function () { 2376 this.sdkImpl.onPlayerReady(); 2377}; 2378 2379/** 2380 * Called when the player enters fullscreen. 2381 */ 2382Controller.prototype.onPlayerEnterFullscreen = function () { 2383 this.adUi.onPlayerEnterFullscreen(); 2384 this.sdkImpl.onPlayerEnterFullscreen(); 2385}; 2386 2387/** 2388 * Called when the player exits fullscreen. 2389 */ 2390Controller.prototype.onPlayerExitFullscreen = function () { 2391 this.adUi.onPlayerExitFullscreen(); 2392 this.sdkImpl.onPlayerExitFullscreen(); 2393}; 2394 2395/** 2396 * Called when the player volume changes. 2397 * 2398 * @param {number} volume The new player volume. 2399 */ 2400Controller.prototype.onPlayerVolumeChanged = function (volume) { 2401 this.adUi.onPlayerVolumeChanged(volume); 2402 this.sdkImpl.onPlayerVolumeChanged(volume); 2403}; 2404 2405/** 2406 * Sets the content of the video player. You should use this method instead 2407 * of setting the content src directly to ensure the proper ad tag is 2408 * requested when the video content is loaded. 2409 * @param {?string} contentSrc The URI for the content to be played. Leave 2410 * blank to use the existing content. 2411 * @param {?string} adTag The ad tag to be requested when the content loads. 2412 * Leave blank to use the existing ad tag. 2413 */ 2414Controller.prototype.setContentWithAdTag = function (contentSrc, adTag) { 2415 this.reset(); 2416 this.settings.adTagUrl = adTag ? adTag : this.settings.adTagUrl; 2417 this.playerWrapper.changeSource(contentSrc); 2418}; 2419 2420/** 2421 * Sets the content of the video player. You should use this method instead 2422 * of setting the content src directly to ensure the proper ads response is 2423 * used when the video content is loaded. 2424 * @param {?string} contentSrc The URI for the content to be played. Leave 2425 * blank to use the existing content. 2426 * @param {?string} adsResponse The ads response to be requested when the 2427 * content loads. Leave blank to use the existing ads response. 2428 */ 2429Controller.prototype.setContentWithAdsResponse = function (contentSrc, adsResponse) { 2430 this.reset(); 2431 this.settings.adsResponse = adsResponse ? adsResponse : this.settings.adsResponse; 2432 this.playerWrapper.changeSource(contentSrc); 2433}; 2434 2435/** 2436 * Sets the content of the video player. You should use this method instead 2437 * of setting the content src directly to ensure the proper ads request is 2438 * used when the video content is loaded. 2439 * @param {?string} contentSrc The URI for the content to be played. Leave 2440 * blank to use the existing content. 2441 * @param {?Object} adsRequest The ads request to be requested when the 2442 * content loads. Leave blank to use the existing ads request. 2443 */ 2444Controller.prototype.setContentWithAdsRequest = function (contentSrc, adsRequest) { 2445 this.reset(); 2446 this.settings.adsRequest = adsRequest ? adsRequest : this.settings.adsRequest; 2447 this.playerWrapper.changeSource(contentSrc); 2448}; 2449 2450/** 2451 * Resets the state of the plugin. 2452 */ 2453Controller.prototype.reset = function () { 2454 this.sdkImpl.reset(); 2455 this.playerWrapper.reset(); 2456 this.adUi.reset(); 2457}; 2458 2459/** 2460 * Listener JSDoc for ESLint. This listener can be passed to 2461 * (add|remove)ContentEndedListener. 2462 * @callback listener 2463 */ 2464 2465/** 2466 * Adds a listener for the 'contentended' event of the video player. This should 2467 * be used instead of setting an 'contentended' listener directly to ensure that 2468 * the ima can do proper cleanup of the SDK before other event listeners are 2469 * called. 2470 * @param {listener} listener The listener to be called when content 2471 * completes. 2472 */ 2473Controller.prototype.addContentEndedListener = function (listener) { 2474 this.playerWrapper.addContentEndedListener(listener); 2475}; 2476 2477/** 2478 * Adds a listener that will be called when content and all ads have 2479 * finished playing. 2480 * @param {listener} listener The listener to be called when content and ads 2481 * complete. 2482 */ 2483Controller.prototype.addContentAndAdsEndedListener = function (listener) { 2484 this.contentAndAdsEndedListeners.push(listener); 2485}; 2486 2487/** 2488 * Sets the listener to be called to trigger manual ad break playback. 2489 * @param {listener} listener The listener to be called to trigger manual ad 2490 * break playback. 2491 */ 2492Controller.prototype.setAdBreakReadyListener = function (listener) { 2493 this.sdkImpl.setAdBreakReadyListener(listener); 2494}; 2495 2496/** 2497 * Changes the flag to show or hide the ad countdown timer. 2498 * 2499 * @param {boolean} showCountdownIn Show or hide the countdown timer. 2500 */ 2501Controller.prototype.setShowCountdown = function (showCountdownIn) { 2502 this.adUi.setShowCountdown(showCountdownIn); 2503 this.showCountdown = showCountdownIn; 2504 this.adUi.countdownDiv.style.display = this.showCountdown ? 'block' : 'none'; 2505}; 2506 2507/** 2508 * Initializes the AdDisplayContainer. On mobile, this must be done as a 2509 * result of user action. 2510 */ 2511Controller.prototype.initializeAdDisplayContainer = function () { 2512 this.sdkImpl.initializeAdDisplayContainer(); 2513}; 2514 2515/** 2516 * Called by publishers in manual ad break playback mode to start an ad 2517 * break. 2518 */ 2519Controller.prototype.playAdBreak = function () { 2520 this.sdkImpl.playAdBreak(); 2521}; 2522 2523/** 2524 * Callback JSDoc for ESLint. This callback can be passed to addEventListener. 2525 * @callback callback 2526 */ 2527 2528/** 2529 * Ads an EventListener to the AdsManager. For a list of available events, 2530 * see 2531 * https://developers.google.com/interactive-media-ads/docs/sdks/html5/client-side/reference/js/google.ima.AdEvent#.Type 2532 * @param {google.ima.AdEvent.Type} event The AdEvent.Type for which to 2533 * listen. 2534 * @param {callback} callback The method to call when the event is fired. 2535 */ 2536Controller.prototype.addEventListener = function (event, callback) { 2537 this.sdkImpl.addEventListener(event, callback); 2538}; 2539 2540/** 2541 * Returns the instance of the AdsManager. 2542 * @return {google.ima.AdsManager} The AdsManager being used by the plugin. 2543 */ 2544Controller.prototype.getAdsManager = function () { 2545 return this.sdkImpl.getAdsManager(); 2546}; 2547 2548/** 2549 * Returns the instance of the player id. 2550 * @return {string} The player id. 2551 */ 2552Controller.prototype.getPlayerId = function () { 2553 return this.playerWrapper.getPlayerId(); 2554}; 2555 2556/** 2557 * Changes the ad tag. You will need to call requestAds after this method 2558 * for the new ads to be requested. 2559 * @param {?string} adTag The ad tag to be requested the next time 2560 * requestAds is called. 2561 */ 2562Controller.prototype.changeAdTag = function (adTag) { 2563 this.reset(); 2564 this.settings.adTagUrl = adTag; 2565}; 2566 2567/** 2568 * Pauses the ad. 2569 */ 2570Controller.prototype.pauseAd = function () { 2571 this.adUi.onAdsPaused(); 2572 this.sdkImpl.pauseAds(); 2573}; 2574 2575/** 2576 * Resumes the ad. 2577 */ 2578Controller.prototype.resumeAd = function () { 2579 this.adUi.onAdsPlaying(); 2580 this.sdkImpl.resumeAds(); 2581}; 2582 2583/** 2584 * Toggles video/ad playback. 2585 */ 2586Controller.prototype.togglePlayback = function () { 2587 this.playerWrapper.togglePlayback(); 2588}; 2589 2590/** 2591 * @return {boolean} true if we expect that ads will autoplay. false otherwise. 2592 */ 2593Controller.prototype.adsWillAutoplay = function () { 2594 if (this.settings.adsWillAutoplay !== undefined) { 2595 return this.settings.adsWillAutoplay; 2596 } else if (this.settings.adWillAutoplay !== undefined) { 2597 return this.settings.adWillAutoplay; 2598 } else { 2599 return !!this.playerWrapper.getPlayerOptions().autoplay; 2600 } 2601}; 2602 2603/** 2604 * @return {boolean} true if we expect that ads will autoplay. false otherwise. 2605 */ 2606Controller.prototype.adsWillPlayMuted = function () { 2607 if (this.settings.adsWillPlayMuted !== undefined) { 2608 return this.settings.adsWillPlayMuted; 2609 } else if (this.settings.adWillPlayMuted !== undefined) { 2610 return this.settings.adWillPlayMuted; 2611 } else if (this.playerWrapper.getPlayerOptions().muted !== undefined) { 2612 return this.playerWrapper.getPlayerOptions().muted; 2613 } else { 2614 return this.playerWrapper.getVolume() == 0; 2615 } 2616}; 2617 2618/** 2619 * Triggers an event on the VJS player 2620 * @param {string} name The event name. 2621 * @param {Object} data The event data. 2622 */ 2623Controller.prototype.triggerPlayerEvent = function (name, data) { 2624 this.playerWrapper.triggerPlayerEvent(name, data); 2625}; 2626 2627/** 2628 * Copyright 2021 Google Inc. 2629 * 2630 * Licensed under the Apache License, Version 2.0 (the "License");
2631 * you may not use this file except in compliance with the License. 2632 * You may obtain a copy of the License at 2633 * 2634 * http://www.apache.org/licenses/LICENSE-2.0 2635 * 2636 * Unless required by applicable law or agreed to in writing, software 2637 * distributed under the License is distributed on an "AS IS" BASIS, 2638 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2639 * See the License for the specific language governing permissions and 2640 * limitations under the License. 2641 * 2642 * IMA SDK integration plugin for Video.js. For more information see 2643 * https://www.github.com/googleads/videojs-ima 2644 */ 2645 2646/** 2647 * Wraps the video.js stream player for the plugin. 2648 * 2649 * @param {!Object} player Video.js player instance. 2650 * @param {!Object} adsPluginSettings Settings for the contrib-ads plugin. 2651 * @param {!DaiController} daiController Reference to the parent controller. 2652 */ 2653const PlayerWrapper$2 = function (player, adsPluginSettings, daiController) { 2654 /** 2655 * Instance of the video.js player. 2656 */ 2657 this.vjsPlayer = player; 2658 2659 /** 2660 * Plugin DAI controller. 2661 */ 2662 this.daiController = daiController; 2663 2664 /** 2665 * Video.js control bar. 2666 */ 2667 this.vjsControls = this.vjsPlayer.getChild('controlBar'); 2668 2669 /** 2670 * Vanilla HTML5 video player underneath the video.js player. 2671 */ 2672 this.h5Player = null; 2673 2674 this.vjsPlayer.on('dispose', this.playerDisposedListener.bind(this)); 2675 this.vjsPlayer.on('pause', this.onPause.bind(this)); 2676 this.vjsPlayer.on('play', this.onPlay.bind(this)); 2677 this.vjsPlayer.on('seeked', this.onSeekEnd.bind(this)); 2678 this.vjsPlayer.ready(this.onPlayerReady.bind(this)); 2679 this.vjsPlayer.ads(adsPluginSettings); 2680}; 2681 2682/** 2683 * Called in response to the video.js player's 'disposed' event. 2684 */ 2685PlayerWrapper$2.prototype.playerDisposedListener = function () { 2686 this.contentEndedListeners = []; 2687 this.daiController.onPlayerDisposed(); 2688}; 2689 2690/** 2691 * Called on the player 'pause' event. Handles displaying controls during 2692 * paused ad breaks. 2693 */ 2694PlayerWrapper$2.prototype.onPause = function () { 2695 // This code will run if the stream is paused during an ad break. Since 2696 // controls are usually hidden during ads, they will now show to allow 2697 // users to resume ad playback. 2698 if (this.daiController.isInAdBreak()) { 2699 this.vjsControls.show(); 2700 } 2701}; 2702 2703/** 2704 * Called on the player 'play' event. Handles hiding controls during 2705 * ad breaks while playing. 2706 */ 2707PlayerWrapper$2.prototype.onPlay = function () { 2708 if (this.daiController.isInAdBreak()) { 2709 this.vjsControls.hide(); 2710 } 2711}; 2712 2713/** 2714 * Called on the player's 'seeked' event. Sets up handling for ad break 2715 * snapback for VOD streams. 2716 */ 2717PlayerWrapper$2.prototype.onSeekEnd = function () { 2718 this.daiController.onSeekEnd(this.vjsPlayer.currentTime()); 2719}; 2720 2721/** 2722 * Called on the player's 'ready' event to begin initiating IMA. 2723 */ 2724PlayerWrapper$2.prototype.onPlayerReady = function () { 2725 this.h5Player = document.getElementById(this.getPlayerId()).getElementsByClassName('vjs-tech')[0]; 2726 this.daiController.onPlayerReady(); 2727}; 2728 2729/** 2730 * @return {!Object} The stream player. 2731 */ 2732PlayerWrapper$2.prototype.getStreamPlayer = function () { 2733 return this.h5Player; 2734}; 2735 2736/** 2737 * @return {!Object} The video.js player. 2738 */ 2739PlayerWrapper$2.prototype.getVjsPlayer = function () { 2740 return this.vjsPlayer; 2741}; 2742 2743/** 2744 * @return {!Object} The vjs player's options object. 2745 */ 2746PlayerWrapper$2.prototype.getPlayerOptions = function () { 2747 return this.vjsPlayer.options_; 2748}; 2749 2750/** 2751 * Returns the instance of the player id. 2752 * @return {string} The player id. 2753 */ 2754PlayerWrapper$2.prototype.getPlayerId = function () { 2755 return this.vjsPlayer.id(); 2756}; 2757 2758/** 2759 * Handles ad errors. 2760 * 2761 * @param {!Object} adErrorEvent The ad error event thrown by the IMA SDK. 2762 */ 2763PlayerWrapper$2.prototype.onAdError = function (adErrorEvent) { 2764 this.vjsControls.show(); 2765 const errorMessage = adErrorEvent.getError !== undefined ? adErrorEvent.getError() : adErrorEvent.stack; 2766 this.vjsPlayer.trigger({ type: 'adserror', data: { 2767 AdError: errorMessage, 2768 AdErrorEvent: adErrorEvent 2769 } }); 2770}; 2771 2772/** 2773 * Handles ad break starting. 2774 */ 2775PlayerWrapper$2.prototype.onAdBreakStart = function () { 2776 this.vjsControls.hide(); 2777}; 2778 2779/** 2780 * Handles ad break ending. 2781 */ 2782PlayerWrapper$2.prototype.onAdBreakEnd = function () { 2783 this.vjsControls.show(); 2784}; 2785 2786/** 2787 * Reset the player. 2788 */ 2789PlayerWrapper$2.prototype.reset = function () { 2790 this.vjsControls.show(); 2791}; 2792 2793/** 2794 * Copyright 2021 Google Inc. 2795 * 2796 * Licensed under the Apache License, Version 2.0 (the "License");
2797 * you may not use this file except in compliance with the License. 2798 * You may obtain a copy of the License at 2799 * 2800 * http://www.apache.org/licenses/LICENSE-2.0 2801 * 2802 * Unless required by applicable law or agreed to in writing, software 2803 * distributed under the License is distributed on an "AS IS" BASIS, 2804 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2805 * See the License for the specific language governing permissions and 2806 * limitations under the License. 2807 * 2808 * IMA SDK integration plugin for Video.js. For more information see 2809 * https://www.github.com/googleads/videojs-ima 2810 */ 2811 2812/** 2813 * Implementation of the IMA DAI SDK for the plugin. 2814 * 2815 * @param {DaiController!} daiController Reference to the parent DAI 2816 * controller. 2817 * 2818 * @constructor 2819 * @struct 2820 * @final 2821 */ 2822const SdkImpl$2 = function (daiController) { 2823 /** 2824 * Plugin DAI controller. 2825 */ 2826 this.daiController = daiController; 2827 2828 /** 2829 * The html5 stream player. 2830 */ 2831 this.streamPlayer = null; 2832 2833 /** 2834 * The videoJS stream player. 2835 */ 2836 this.vjsPlayer = null; 2837 2838 /** 2839 * IMA SDK StreamManager 2840 */ 2841 this.streamManager = null; 2842 2843 /** 2844 * IMA stream UI settings. 2845 */ 2846 /* eslint no-undef: 'error' */ 2847 /* global google */ 2848 this.uiSettings = new google.ima.dai.api.UiSettings(); 2849 2850 /** 2851 * If the stream is currently in an ad break. 2852 */ 2853 this.isAdBreak = false; 2854 2855 /** 2856 * If the stream is currently seeking from a snapback. 2857 */ 2858 this.isSnapback = false; 2859 2860 /** 2861 * Originally seeked to time, to return stream to after ads. 2862 */ 2863 this.snapForwardTime = 0; 2864 2865 /** 2866 * Timed metadata for the stream. 2867 */ 2868 this.timedMetadata; 2869 2870 /** 2871 * Timed metadata record. 2872 */ 2873 this.metadataLoaded = {}; 2874 2875 this.SOURCE_TYPES = { 2876 hls: 'application/x-mpegURL', 2877 dash: 'application/dash+xml' 2878 }; 2879}; 2880 2881/** 2882 * Creates and initializes the IMA DAI SDK objects. 2883 */ 2884SdkImpl$2.prototype.initImaDai = function () { 2885 this.streamPlayer = this.daiController.getStreamPlayer(); 2886 this.vjsPlayer = this.daiController.getVjsPlayer(); 2887 2888 this.createAdUiDiv(); 2889 if (this.daiController.getSettings().locale) { 2890 this.uiSettings.setLocale(this.daiController.getSettings().locale); 2891 } 2892 2893 this.streamManager = new google.ima.dai.api.StreamManager(this.streamPlayer, this.adUiDiv, this.uiSettings); 2894 2895 this.streamPlayer.addEventListener('pause', this.onStreamPause); 2896 this.streamPlayer.addEventListener('play', this.onStreamPlay); 2897 2898 this.streamManager.addEventListener([google.ima.dai.api.StreamEvent.Type.LOADED, google.ima.dai.api.StreamEvent.Type.ERROR, google.ima.dai.api.StreamEvent.Type.AD_BREAK_STARTED, google.ima.dai.api.StreamEvent.Type.AD_BREAK_ENDED], this.onStreamEvent.bind(this), false); 2899 2900 this.vjsPlayer.textTracks().onaddtrack = this.onAddTrack.bind(this); 2901 2902 this.vjsPlayer.trigger({ 2903 type: 'stream-manager', 2904 StreamManager: this.streamManager 2905 }); 2906 2907 this.requestStream(); 2908}; 2909 2910/** 2911 * Called when the video player has metadata to process. 2912 * @param {Event!} event The event that triggered this call. 2913 */ 2914SdkImpl$2.prototype.onAddTrack = function (event) { 2915 const track = event.track; 2916 if (track.kind === 'metadata') { 2917 track.mode = 'hidden'; 2918 track.oncuechange = e => { 2919 for (const cue of track.activeCues_) { 2920 const metadata = {}; 2921 metadata[cue.value.key] = cue.value.data; 2922 this.streamManager.onTimedMetadata(metadata); 2923 } 2924 }; 2925 } 2926}; 2927 2928/** 2929 * Creates the ad UI container. 2930 */ 2931SdkImpl$2.prototype.createAdUiDiv = function () { 2932 const uiDiv = document.createElement('div'); 2933 uiDiv.id = 'ad-ui'; 2934 // 3em is the height of the control bar. 2935 uiDiv.style.height = 'calc(100% - 3em)'; 2936 this.streamPlayer.parentNode.appendChild(uiDiv); 2937 this.adUiDiv = uiDiv; 2938}; 2939 2940/** 2941 * Called on pause to update the ad UI. 2942 */ 2943SdkImpl$2.prototype.onStreamPause = function () { 2944 if (this.isAdBreak) { 2945 this.adUiDiv.style.display = 'none'; 2946 } 2947}; 2948 2949/** 2950 * Called on play to update the ad UI. 2951 */ 2952SdkImpl$2.prototype.onStreamPlay = function () { 2953 if (this.isAdBreak) { 2954 this.adUiDiv.style.display = 'block'; 2955 } 2956}; 2957 2958/** 2959 * Called on play to update the ad UI. 2960 * @param {number} currentTime the current time of the stream. 2961 */ 2962SdkImpl$2.prototype.onSeekEnd = function (currentTime) { 2963 const streamType = this.daiController.getSettings().streamType; 2964 if (streamType === 'live') { 2965 return; 2966 } 2967 if (this.isSnapback) { 2968 this.isSnapback = false;
vendor: 5,234 bytes, lines 2969-3128
2969 return; 2970 } 2971 const previousCuePoint = this.streamManager.previousCuePointForStreamTime(currentTime); 2972 if (previousCuePoint && !previousCuePoint.played) { 2973 this.isSnapback = true; 2974 this.snapForwardTime = currentTime; 2975 this.vjsPlayer.currentTime(previousCuePoint.start); 2976 } 2977}; 2978 2979/** 2980 * Handles IMA events. 2981 * @param {google.ima.StreamEvent!} event the IMA event 2982 */ 2983SdkImpl$2.prototype.onStreamEvent = function (event) { 2984 switch (event.type) { 2985 case google.ima.dai.api.StreamEvent.Type.LOADED: 2986 this.loadUrl(event.getStreamData().url); 2987 break; 2988 case google.ima.dai.api.StreamEvent.Type.ERROR: 2989 window.console.warn('Error loading stream, attempting to play backup ' + 'stream. ' + event.getStreamData().errorMessage); 2990 this.daiController.onErrorLoadingAds(event); 2991 if (this.daiController.getSettings().fallbackStreamUrl) { 2992 this.loadurl(this.daiController.getSettings().fallbackStreamUrl); 2993 } 2994 break; 2995 case google.ima.dai.api.StreamEvent.Type.AD_BREAK_STARTED: 2996 this.isAdBreak = true; 2997 this.adUiDiv.style.display = 'block'; 2998 this.daiController.onAdBreakStart(); 2999 break; 3000 case google.ima.dai.api.StreamEvent.Type.AD_BREAK_ENDED: 3001 this.isAdBreak = false; 3002 this.adUiDiv.style.display = 'none'; 3003 this.daiController.onAdBreakEnd(); 3004 if (this.snapForwardTime && this.snapForwardTime > this.vjsPlayer.currentTime()) { 3005 this.vjsPlayer.currentTime(this.snapForwardTime); 3006 this.snapForwardTime = 0; 3007 } 3008 break; 3009 default: 3010 break; 3011 } 3012}; 3013 3014/** 3015 * Loads the stream URL . 3016 * @param {string} streamUrl the URL for the stream being loaded. 3017 */ 3018SdkImpl$2.prototype.loadUrl = function (streamUrl) { 3019 this.vjsPlayer.ready(function () { 3020 const streamFormat = this.daiController.getSettings().streamFormat; 3021 this.vjsPlayer.src({ 3022 src: streamUrl, 3023 type: this.SOURCE_TYPES[streamFormat] 3024 }); 3025 3026 const bookmarkTime = this.daiController.getSettings().bookmarkTime; 3027 if (bookmarkTime) { 3028 const startTime = this.streamManager.streamTimeForContentTime(bookmarkTime); 3029 // Seeking on load triggers the onSeekEnd event, so treat this seek as 3030 // if it's snapback. Without this, resuming at a bookmark kicks you 3031 // back to the ad before the bookmark. 3032 this.isSnapback = true; 3033 this.vjsPlayer.currentTime(startTime); 3034 } 3035 }.bind(this)); 3036}; 3037 3038/** 3039 * Creates the AdsRequest and request ads through the AdsLoader. 3040 */ 3041SdkImpl$2.prototype.requestStream = function () { 3042 let streamRequest; 3043 const streamType = this.daiController.getSettings().streamType; 3044 if (streamType === 'vod') { 3045 streamRequest = new google.ima.dai.api.VODStreamRequest(); 3046 streamRequest.contentSourceId = this.daiController.getSettings().cmsId; 3047 streamRequest.videoId = this.daiController.getSettings().videoId; 3048 } else if (streamType === 'live') { 3049 streamRequest = new google.ima.dai.api.LiveStreamRequest(); 3050 streamRequest.assetKey = this.daiController.getSettings().assetKey; 3051 } else { 3052 window.console.warn('No valid stream type selected'); 3053 } 3054 streamRequest.format = this.daiController.getSettings().streamFormat; 3055 3056 if (this.daiController.getSettings().apiKey) { 3057 streamRequest.apiKey = this.daiController.getSettings().apiKey; 3058 } 3059 if (this.daiController.getSettings().authKey) { 3060 streamRequest.authKey = this.daiController.getSettings().authKey; 3061 } 3062 if (this.daiController.getSettings().adTagParameters) { 3063 streamRequest.adTagParameters = this.daiController.getSettings().adTagParameters; 3064 } 3065 if (this.daiController.getSettings().streamActivityMonitorId) { 3066 streamRequest.streamActivityMonitorId = this.daiController.getSettings().streamActivityMonitorId; 3067 } 3068 3069 if (this.daiController.getSettings().omidMode) { 3070 streamRequest.omidAccessModeRules = {}; 3071 const omidValues = this.daiController.getSettings().omidMode; 3072 3073 if (omidValues.FULL) { 3074 streamRequest.omidAccessModeRules[google.ima.OmidAccessMode.FULL] = omidValues.FULL; 3075 } 3076 if (omidValues.DOMAIN) { 3077 streamRequest.omidAccessModeRules[google.ima.OmidAccessMode.DOMAIN] = omidValues.DOMAIN; 3078 } 3079 if (omidValues.LIMITED) { 3080 streamRequest.omidAccessModeRules[google.ima.OmidAccessMode.LIMITED] = omidValues.LIMITED; 3081 } 3082 } 3083 3084 this.streamManager.requestStream(streamRequest); 3085 this.vjsPlayer.trigger({ 3086 type: 'stream-request', 3087 StreamRequest: streamRequest 3088 }); 3089}; 3090 3091/** 3092 * Initiates IMA when the player is ready. 3093 */ 3094SdkImpl$2.prototype.onPlayerReady = function () { 3095 this.initImaDai(); 3096}; 3097 3098/** 3099 * Reset the StreamManager when the player is disposed. 3100 */ 3101SdkImpl$2.prototype.onPlayerDisposed = function () { 3102 if (this.streamManager) { 3103 this.streamManager.reset(); 3104 } 3105}; 3106 3107/** 3108 * Returns the instance of the StreamManager. 3109 * @return {google.ima.StreamManager!} The StreamManager being used by the 3110 * plugin. 3111 */ 3112SdkImpl$2.prototype.getStreamManager = function () { 3113 return this.StreamManager; 3114}; 3115 3116/** 3117 * Reset the SDK implementation. 3118 */ 3119SdkImpl$2.prototype.reset = function () { 3120 if (this.StreamManager) { 3121 this.StreamManager.reset(); 3122 } 3123}; 3124 3125/** 3126 * Copyright 2021 Google Inc. 3127 * 3128 * Licensed under the Apache License, Version 2.0 (the "License");
3129 * you may not use this file except in compliance with the License. 3130 * You may obtain a copy of the License at 3131 * 3132 * http://www.apache.org/licenses/LICENSE-2.0 3133 * 3134 * Unless required by applicable law or agreed to in writing, software 3135 * distributed under the License is distributed on an "AS IS" BASIS, 3136 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3137 * See the License for the specific language governing permissions and 3138 * limitations under the License. 3139 * 3140 * IMA SDK integration plugin for Video.js. For more information see 3141 * https://www.github.com/googleads/videojs-ima 3142 */ 3143/** 3144 * The coordinator for the DAI portion of the plugin. Facilitates 3145 * communication between all other plugin classes. 3146 * 3147 * @param {Object!} player Instance of the video.js player. 3148 * @param {Object!} options Options provided by the implementation. 3149 * @constructor 3150 * @struct 3151 * @final 3152 */ 3153const DaiController = function (player, options) { 3154 /** 3155 * If the stream is currently in an ad break. 3156 * @type {boolean} 3157 */ 3158 this.inAdBreak = false; 3159 3160 /** 3161 * Stores user-provided settings. 3162 * @type {Object!} 3163 */ 3164 this.settings = {}; 3165 3166 /** 3167 * Whether or not we are running on a mobile platform. 3168 */ 3169 this.isMobile = navigator.userAgent.match(/iPhone/i) || navigator.userAgent.match(/iPad/i) || navigator.userAgent.match(/Android/i); 3170 3171 /** 3172 * Whether or not we are running on an iOS platform. 3173 */ 3174 this.isIos = navigator.userAgent.match(/iPhone/i) || navigator.userAgent.match(/iPad/i); 3175 3176 this.initWithSettings(options); 3177 3178 /** 3179 * Stores contrib-ads default settings. 3180 */ 3181 const contribAdsDefaults = { 3182 debug: this.settings.debug, 3183 timeout: this.settings.timeout, 3184 prerollTimeout: this.settings.prerollTimeout 3185 }; 3186 const adsPluginSettings = Object.assign({}, contribAdsDefaults, options.contribAdsSettings || {}); 3187 3188 this.playerWrapper = new PlayerWrapper$2(player, adsPluginSettings, this); 3189 this.sdkImpl = new SdkImpl$2(this); 3190}; 3191 3192DaiController.IMA_DEFAULTS = { 3193 adLabel: 'Advertisement', 3194 adLabelNofN: 'of', 3195 debug: false, 3196 disableAdControls: false, 3197 showControlsForJSAds: true 3198}; 3199 3200/** 3201 * Extends the settings to include user-provided settings. 3202 * 3203 * @param {Object!} options Options to be used in initialization. 3204 */ 3205DaiController.prototype.initWithSettings = function (options) { 3206 this.settings = Object.assign({}, DaiController.IMA_DEFAULTS, options || {}); 3207 3208 this.warnAboutDeprecatedSettings(); 3209 3210 // Default showing countdown timer to true. 3211 this.showCountdown = true; 3212 if (this.settings.showCountdown === false) { 3213 this.showCountdown = false; 3214 } 3215}; 3216 3217/** 3218 * Logs console warnings when deprecated settings are used. 3219 */ 3220DaiController.prototype.warnAboutDeprecatedSettings = function () { 3221 const deprecatedSettings = [ 3222 // Currently no DAI plugin settings are deprecated. 3223 ]; 3224 deprecatedSettings.forEach(setting => { 3225 if (this.settings[setting] !== undefined) { 3226 console.warn('WARNING: videojs.imaDai setting ' + setting + ' is deprecated'); 3227 } 3228 }); 3229}; 3230 3231/** 3232 * Return the settings object. 3233 * 3234 * @return {Object!} The settings object. 3235 */ 3236DaiController.prototype.getSettings = function () { 3237 return this.settings; 3238}; 3239 3240/** 3241 * Return whether or not we're in a mobile environment. 3242 * 3243 * @return {boolean} True if running on mobile, false otherwise. 3244 */ 3245DaiController.prototype.getIsMobile = function () { 3246 return this.isMobile; 3247}; 3248 3249/** 3250 * Return whether or not we're in an iOS environment. 3251 * 3252 * @return {boolean} True if running on iOS, false otherwise. 3253 */ 3254DaiController.prototype.getIsIos = function () { 3255 return this.isIos; 3256}; 3257 3258/** 3259 * @return {Object!} The html5 player. 3260 */ 3261DaiController.prototype.getStreamPlayer = function () { 3262 return this.playerWrapper.getStreamPlayer(); 3263}; 3264 3265/** 3266 * @return {Object!} The video.js player. 3267 */ 3268DaiController.prototype.getVjsPlayer = function () { 3269 return this.playerWrapper.getVjsPlayer(); 3270}; 3271 3272/** 3273 * Requests the stream. 3274 */ 3275DaiController.prototype.requestStream = function () { 3276 this.sdkImpl.requestStream(); 3277}; 3278 3279/** 3280 * Add or modify a setting. 3281 * 3282 * @param {string} key Key to modify 3283 * @param {Object!} value Value to set at key. 3284*/ 3285DaiController.prototype.setSetting = function (key, value) { 3286 this.settings[key] = value; 3287}; 3288 3289/** 3290 * Called when there is an error loading ads. 3291 * 3292 * @param {Object!} adErrorEvent The ad error event thrown by the IMA SDK. 3293 */ 3294DaiController.prototype.onErrorLoadingAds = function (adErrorEvent) { 3295 this.playerWrapper.onAdError(adErrorEvent); 3296}; 3297 3298/** 3299 * Relays ad errors to the player wrapper. 3300 * 3301 * @param {Object!} adErrorEvent The ad error event thrown by the IMA SDK. 3302 */ 3303DaiController.prototype.onAdError = function (adErrorEvent) { 3304 this.playerWrapper.onAdError(adErrorEvent); 3305}; 3306 3307/** 3308 * Signals player that an ad break has started. 3309 */ 3310DaiController.prototype.onAdBreakStart = function () { 3311 this.inAdBreak = true; 3312 this.playerWrapper.onAdBreakStart(); 3313}; 3314 3315/** 3316 * Signals player that an ad break has ended. 3317 */ 3318DaiController.prototype.onAdBreakEnd = function () { 3319 this.inAdBreak = false;
vendor: 10,215 bytes, lines 3320-3639
3320 this.playerWrapper.onAdBreakEnd(); 3321}; 3322 3323/** 3324 * Called when the player is disposed. 3325 */ 3326DaiController.prototype.onPlayerDisposed = function () { 3327 this.contentAndAdsEndedListeners = []; 3328 this.sdkImpl.onPlayerDisposed(); 3329}; 3330 3331/** 3332 * Returns if the stream is currently in an ad break. 3333 * @return {boolean} If the stream is currently in an ad break. 3334 */ 3335DaiController.prototype.isInAdBreak = function () { 3336 return this.inAdBreak; 3337}; 3338 3339/** 3340 * Called on seek end to check for ad snapback. 3341 * @param {number} currentTime the current time of the stream. 3342 */ 3343DaiController.prototype.onSeekEnd = function (currentTime) { 3344 this.sdkImpl.onSeekEnd(currentTime); 3345}; 3346 3347/** 3348 * Called when the player is ready. 3349 */ 3350DaiController.prototype.onPlayerReady = function () { 3351 this.sdkImpl.onPlayerReady(); 3352}; 3353 3354/** 3355 * Resets the state of the plugin. 3356 */ 3357DaiController.prototype.reset = function () { 3358 this.sdkImpl.reset(); 3359 this.playerWrapper.reset(); 3360}; 3361 3362/** 3363 * Adds an EventListener to the StreamManager. For a list of available events, 3364 * see 3365 * https://developers.google.com/interactive-media-ads/docs/sdks/html5/dai/reference/js/StreamEvent 3366 * @param {google.ima.StreamEvent.Type!} event The AdEvent.Type for which to 3367 * listen. 3368 * @param {callback!} callback The method to call when the event is fired. 3369 */ 3370DaiController.prototype.addEventListener = function (event, callback) { 3371 this.sdkImpl.addEventListener(event, callback); 3372}; 3373 3374/** 3375 * Returns the instance of the StreamManager. 3376 * @return {google.ima.StreamManager!} The StreamManager being used by the 3377 * plugin. 3378 */ 3379DaiController.prototype.getStreamManager = function () { 3380 return this.sdkImpl.getStreamManager(); 3381}; 3382 3383/** 3384 * Returns the instance of the player id. 3385 * @return {string} The player id. 3386 */ 3387DaiController.prototype.getPlayerId = function () { 3388 return this.playerWrapper.getPlayerId(); 3389}; 3390 3391/** 3392 * @return {boolean} true if we expect that the stream will autoplay. false 3393 * otherwise. 3394 */ 3395DaiController.prototype.streamWillAutoplay = function () { 3396 if (this.settings.streamWillAutoplay !== undefined) { 3397 return this.settings.streamWillAutoplay; 3398 } else { 3399 return !!this.playerWrapper.getPlayerOptions().autoplay; 3400 } 3401}; 3402 3403/** 3404 * Triggers an event on the VJS player 3405 * @param {string} name The event name. 3406 * @param {Object!} data The event data. 3407 */ 3408DaiController.prototype.triggerPlayerEvent = function (name, data) { 3409 this.playerWrapper.triggerPlayerEvent(name, data); 3410}; 3411 3412/** 3413 * Copyright 2017 Google Inc. 3414 * 3415 * Licensed under the Apache License, Version 2.0 (the "License"); 3416 * you may not use this file except in compliance with the License. 3417 * You may obtain a copy of the License at 3418 * 3419 * http://www.apache.org/licenses/LICENSE-2.0 3420 * 3421 * Unless required by applicable law or agreed to in writing, software 3422 * distributed under the License is distributed on an "AS IS" BASIS, 3423 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3424 * See the License for the specific language governing permissions and 3425 * limitations under the License. 3426 * 3427 * IMA SDK integration plugin for Video.js. For more information see 3428 * https://www.github.com/googleads/videojs-ima 3429 */ 3430 3431/** 3432 * Exposes the ImaPlugin to a publisher implementation. 3433 * 3434 * @param {Object} player Instance of the video.js player to which this plugin 3435 * will be added. 3436 * @param {Object} options Options provided by the implementation. 3437 * @constructor 3438 * @struct 3439 * @final 3440 */ 3441const ImaPlugin = function (player, options) { 3442 this.controller = new Controller(player, options); 3443 3444 /** 3445 * Listener JSDoc for ESLint. This listener can be passed to 3446 * addContent(AndAds)EndedListener. 3447 * @callback listener 3448 */ 3449 3450 /** 3451 * Adds a listener that will be called when content and all ads have 3452 * finished playing. 3453 * @param {listener} listener The listener to be called when content and ads 3454 * complete. 3455 */ 3456 this.addContentAndAdsEndedListener = function (listener) { 3457 this.controller.addContentAndAdsEndedListener(listener); 3458 }.bind(this); 3459 3460 /** 3461 * Adds a listener for the 'contentended' event of the video player. This 3462 * should be used instead of setting an 'contentended' listener directly to 3463 * ensure that the ima can do proper cleanup of the SDK before other event 3464 * listeners are called. 3465 * @param {listener} listener The listener to be called when content 3466 * completes. 3467 */ 3468 this.addContentEndedListener = function (listener) { 3469 this.controller.addContentEndedListener(listener); 3470 }.bind(this); 3471 3472 /** 3473 * Callback JSDoc for ESLint. This callback can be passed to addEventListener. 3474 * @callback callback 3475 */ 3476 3477 /** 3478 * Ads an EventListener to the AdsManager. For a list of available events, 3479 * see 3480 * https://developers.google.com/interactive-media-ads/docs/sdks/html5/client-side/reference/js/google.ima.AdEvent#.Type 3481 * @param {google.ima.AdEvent.Type} event The AdEvent.Type for which to 3482 * listen. 3483 * @param {callback} callback The method to call when the event is fired. 3484 */ 3485 this.addEventListener = function (event, callback) { 3486 this.controller.addEventListener(event, callback); 3487 }.bind(this); 3488 3489 /** 3490 * Changes the ad tag. You will need to call requestAds after this method 3491 * for the new ads to be requested. 3492 * @param {?string} adTag The ad tag to be requested the next time requestAds 3493 * is called. 3494 */ 3495 this.changeAdTag = function (adTag) { 3496 this.controller.changeAdTag(adTag); 3497 }.bind(this); 3498 3499 /** 3500 * Returns the instance of the AdsManager. 3501 * @return {google.ima.AdsManager} The AdsManager being used by the plugin. 3502 */ 3503 this.getAdsManager = function () { 3504 return this.controller.getAdsManager(); 3505 }.bind(this); 3506 3507 /** 3508 * Initializes the AdDisplayContainer. On mobile, this must be done as a 3509 * result of user action. 3510 */ 3511 this.initializeAdDisplayContainer = function () { 3512 this.controller.initializeAdDisplayContainer(); 3513 }.bind(this); 3514 3515 /** 3516 * Pauses the ad. 3517 */ 3518 this.pauseAd = function () { 3519 this.controller.pauseAd(); 3520 }.bind(this); 3521 3522 /** 3523 * Called by publishers in manual ad break playback mode to start an ad 3524 * break. 3525 */ 3526 this.playAdBreak = function () { 3527 this.controller.playAdBreak(); 3528 }.bind(this); 3529 3530 /** 3531 * Creates the AdsRequest and request ads through the AdsLoader. 3532 */ 3533 this.requestAds = function () { 3534 this.controller.requestAds(); 3535 }.bind(this); 3536 3537 /** 3538 * Resumes the ad. 3539 */ 3540 this.resumeAd = function () { 3541 this.controller.resumeAd(); 3542 }.bind(this); 3543 3544 /** 3545 * Sets the listener to be called to trigger manual ad break playback. 3546 * @param {listener} listener The listener to be called to trigger manual ad 3547 * break playback. 3548 */ 3549 this.setAdBreakReadyListener = function (listener) { 3550 this.controller.setAdBreakReadyListener(listener); 3551 }.bind(this); 3552 3553 /** 3554 * Sets the content of the video player. You should use this method instead 3555 * of setting the content src directly to ensure the proper ad tag is 3556 * requested when the video content is loaded. 3557 * @param {?string} contentSrc The URI for the content to be played. Leave 3558 * blank to use the existing content. 3559 * @param {?string} adTag The ad tag to be requested when the content loads. 3560 * Leave blank to use the existing ad tag. 3561 */ 3562 this.setContentWithAdTag = function (contentSrc, adTag) { 3563 this.controller.setContentWithAdTag(contentSrc, adTag); 3564 }.bind(this); 3565 3566 /** 3567 * Sets the content of the video player. You should use this method instead 3568 * of setting the content src directly to ensure the proper ads response is 3569 * used when the video content is loaded. 3570 * @param {?string} contentSrc The URI for the content to be played. Leave 3571 * blank to use the existing content. 3572 * @param {?string} adsResponse The ads response to be requested when the 3573 * content loads. Leave blank to use the existing ads response. 3574 */ 3575 this.setContentWithAdsResponse = function (contentSrc, adsResponse) { 3576 this.controller.setContentWithAdsResponse(contentSrc, adsResponse); 3577 }.bind(this); 3578 3579 /** 3580 * Sets the content of the video player. You should use this method instead 3581 * of setting the content src directly to ensure the proper ads request is 3582 * used when the video content is loaded. 3583 * @param {?string} contentSrc The URI for the content to be played. Leave 3584 * blank to use the existing content. 3585 * @param {?Object} adsRequest The ads request to be requested when the 3586 * content loads. Leave blank to use the existing ads request. 3587 */ 3588 this.setContentWithAdsRequest = function (contentSrc, adsRequest) { 3589 this.controller.setContentWithAdsRequest(contentSrc, adsRequest); 3590 }.bind(this); 3591 3592 /** 3593 * Changes the flag to show or hide the ad countdown timer. 3594 * 3595 * @param {boolean} showCountdownIn Show or hide the countdown timer. 3596 */ 3597 this.setShowCountdown = function (showCountdownIn) { 3598 this.controller.setShowCountdown(showCountdownIn); 3599 }.bind(this); 3600}; 3601 3602/** 3603 * Exposes the ImaDaiPlugin to a publisher implementation. 3604 * 3605 * @param {Object} player Instance of the video.js player to which this plugin 3606 * will be added. 3607 * @param {Object} options Options provided by the implementation. 3608 * @constructor 3609 * @struct 3610 * @final 3611 */ 3612const ImaDaiPlugin = function (player, options) { 3613 this.controller = new DaiController(player, options); 3614 3615 /** 3616 * Adds a listener that will be called when content and all ads in the 3617 * stream have finished playing. VOD stream only. 3618 * @param {listener} listener The listener to be called when content and ads 3619 * complete. 3620 */ 3621 this.streamEndedListener = function (listener) { 3622 this.controller.addStreamEndedListener(listener); 3623 }.bind(this); 3624 3625 /** 3626 * Adds an EventListener to the StreamManager. 3627 * @param {google.ima.StreamEvent.Type} event The StreamEvent.Type for which 3628 * to listen. 3629 * @param {callback} callback The method to call when the event is fired. 3630 */ 3631 this.addEventListener = function (event, callback) { 3632 this.controller.addEventListener(event, callback); 3633 }.bind(this); 3634 3635 /** 3636 * Returns the instance of the StreamManager. 3637 * @return {google.ima.StreamManager} The StreamManager being used by the 3638 * plugin. 3639 */
3640 this.getStreamManager = function () { 3641 return this.controller.getStreamManager(); 3642 }.bind(this); 3643}; 3644 3645/** 3646 * Initializes the plugin for client-side ads. 3647 * @param {Object} options Plugin option set on initiation. 3648 */ 3649const init = function (options) { 3650 /* eslint no-invalid-this: 'off' */ 3651 this.ima = new ImaPlugin(this, options); 3652}; 3653 3654/** 3655 * LiveStream class used for DAI live streams. 3656 */ 3657class LiveStream { 3658 /** 3659 * LiveStream class constructor used for DAI live streams. 3660 * @param {string} streamFormat stream format, plugin currently supports only 3661 * 'hls' streams. 3662 * @param {string} assetKey live stream's asset key. 3663 */ 3664 constructor(streamFormat, assetKey) { 3665 streamFormat = streamFormat.toLowerCase(); 3666 if (streamFormat !== 'hls' && streamFormat !== 'dash') { 3667 window.console.error('VodStream error: incorrect streamFormat.'); 3668 return; 3669 } else if (streamFormat === 'dash') { 3670 window.console.error('streamFormat error: DASH streams are not' + 'currently supported by this plugin.'); 3671 return; 3672 } else if (typeof assetKey !== 'string') { 3673 window.console.error('assetKey error: value must be string.'); 3674 return; 3675 } 3676 this.streamFormat = streamFormat; 3677 this.assetKey = assetKey; 3678 } 3679} 3680 3681/** 3682 * VodStream class used for DAI VOD streams. 3683 */ 3684class VodStream { 3685 /** 3686 * VodStream class constructor used for DAI VOD streams. 3687 * @param {string} streamFormat stream format, plugin currently supports only 3688 * 'hls' streams. 3689 * @param {string} cmsId VOD stream's CMS ID. 3690 * @param {string} videoId VOD stream's video ID. 3691 */ 3692 constructor(streamFormat, cmsId, videoId) { 3693 streamFormat = streamFormat.toLowerCase(); 3694 if (streamFormat !== 'hls' && streamFormat !== 'dash') { 3695 window.console.error('VodStream error: incorrect streamFormat.'); 3696 return; 3697 } else if (streamFormat === 'dash') { 3698 window.console.error('streamFormat error: DASH streams are not' + 'currently supported by this plugin.'); 3699 return; 3700 } else if (typeof cmsId !== 'string') { 3701 window.console.error('cmsId error: value must be string.'); 3702 return; 3703 } else if (typeof videoId !== 'string') { 3704 window.console.error('videoId error: value must be string.'); 3705 return; 3706 } 3707 3708 this.streamFormat = streamFormat; 3709 this.cmsId = cmsId; 3710 this.videoId = videoId; 3711 } 3712} 3713 3714/** 3715 * Initializes the plugin for DAI ads. 3716 * @param {Object} stream Accepts either an instance of the LiveStream or 3717 * VodStream classes. 3718 * @param {Object} options Plugin option set on initiation. 3719 */ 3720const initDai = function (stream, options) { 3721 if (stream instanceof LiveStream) { 3722 options.streamType = 'live'; 3723 options.assetKey = stream.assetKey; 3724 } else if (stream instanceof VodStream) { 3725 options.streamType = 'vod'; 3726 options.cmsId = stream.cmsId; 3727 options.videoId = stream.videoId; 3728 } else { 3729 window.console.error('initDai() first parameter must be an instance of LiveStream or ' + 'VodStream.'); 3730 return; 3731 } 3732 3733 options.streamFormat = stream.streamFormat; 3734 /* eslint no-invalid-this: 'off' */ 3735 this.imaDai = new ImaDaiPlugin(this, options); 3736}; 3737 3738const registerPlugin = videojs.registerPlugin || videojs.plugin; 3739registerPlugin('ima', init); 3740registerPlugin('imaDai', initDai); 3741 3742exports['default'] = ImaPlugin; 3743exports.VodStream = VodStream; 3744exports.LiveStream = LiveStream; 3745 3746Object.defineProperty(exports, '__esModule', { value: true }); 3747 3748})));
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.