1(function (global, factory) { 2 typeof exports === 'object' && typeof module !== 'undefined' ? factory(require('video.js')) : 3 typeof define === 'function' && define.amd ? define(['video.js'], factory) : 4 (factory(global.videojs)); 5}(this, (function (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 */ 35var PlayerWrapper = function PlayerWrapper(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 data for the content playhead tracker. 111 */ 112 this.contentPlayheadTracker = { 113 currentTime: 0, 114 previousTime: 0, 115 seeking: false, 116 duration: 0 117 }; 118 119 /** 120 * Player dimensions. Used in our resize check. 121 */ 122 this.vjsPlayerDimensions = { 123 width: this.getPlayerWidth(), 124 height: this.getPlayerHeight() 125 }; 126 127 /** 128 * Video.js control bar. 129 */ 130 this.vjsControls = this.vjsPlayer.getChild('controlBar'); 131 132 /** 133 * Vanilla HTML5 video player underneath the video.js player. 134 */ 135 this.h5Player = null; 136 137 this.vjsPlayer.one('play', this.setUpPlayerIntervals.bind(this)); 138 this.boundContentEndedListener = this.localContentEndedListener.bind(this); 139 this.vjsPlayer.on('contentended', this.boundContentEndedListener); 140 this.vjsPlayer.on('dispose', this.playerDisposedListener.bind(this)); 141 this.vjsPlayer.on('readyforpreroll', this.onReadyForPreroll.bind(this)); 142 this.vjsPlayer.ready(this.onPlayerReady.bind(this)); 143 144 this.vjsPlayer.ads(adsPluginSettings); 145}; 146 147/** 148 * Set up the intervals we use on the player. 149 */ 150PlayerWrapper.prototype.setUpPlayerIntervals = function () { 151 this.updateTimeIntervalHandle = setInterval(this.updateCurrentTime.bind(this), this.updateTimeInterval); 152 this.seekCheckIntervalHandle = setInterval(this.checkForSeeking.bind(this), this.seekCheckInterval); 153 this.resizeCheckIntervalHandle = setInterval(this.checkForResize.bind(this), this.resizeCheckInterval); 154}; 155 156/** 157 * Updates the current time of the video 158 */ 159PlayerWrapper.prototype.updateCurrentTime = function () { 160 if (!this.contentPlayheadTracker.seeking) { 161 this.contentPlayheadTracker.currentTime = this.vjsPlayer.currentTime(); 162 } 163}; 164 165/** 166 * Detects when the user is seeking through a video. 167 * This is used to prevent mid-rolls from playing while a user is seeking. 168 * 169 * There *is* a seeking property of the HTML5 video element, but it's not 170 * properly implemented on all platforms (e.g. mobile safari), so we have to 171 * check ourselves to be sure. 172 */ 173PlayerWrapper.prototype.checkForSeeking = function () { 174 var tempCurrentTime = this.vjsPlayer.currentTime(); 175 var diff = (tempCurrentTime - this.contentPlayheadTracker.previousTime) * 1000; 176 if (Math.abs(diff) > this.seekCheckInterval + this.seekThreshold) { 177 this.contentPlayheadTracker.seeking = true; 178 } else { 179 this.contentPlayheadTracker.seeking = false;
vendor: 4,937 bytes, lines 180-362
180 } 181 this.contentPlayheadTracker.previousTime = this.vjsPlayer.currentTime(); 182}; 183 184/** 185 * Detects when the player is resized (for fluid support) and resizes the 186 * ads manager to match. 187 */ 188PlayerWrapper.prototype.checkForResize = function () { 189 var currentWidth = this.getPlayerWidth(); 190 var currentHeight = this.getPlayerHeight(); 191 192 if (currentWidth != this.vjsPlayerDimensions.width || currentHeight != this.vjsPlayerDimensions.height) { 193 this.vjsPlayerDimensions.width = currentWidth; 194 this.vjsPlayerDimensions.height = currentHeight; 195 this.controller.onPlayerResize(currentWidth, currentHeight); 196 } 197}; 198 199/** 200 * Local content ended listener for contentComplete. 201 */ 202PlayerWrapper.prototype.localContentEndedListener = function () { 203 if (!this.contentComplete) { 204 this.contentComplete = true; 205 this.controller.onContentComplete(); 206 } 207 208 for (var index in this.contentEndedListeners) { 209 if (typeof this.contentEndedListeners[index] === 'function') { 210 this.contentEndedListeners[index](); 211 } 212 } 213 214 clearInterval(this.updateTimeIntervalHandle); 215 clearInterval(this.seekCheckIntervalHandle); 216 clearInterval(this.resizeCheckIntervalHandle); 217 if (this.vjsPlayer.el()) { 218 this.vjsPlayer.one('play', this.setUpPlayerIntervals.bind(this)); 219 } 220}; 221 222/** 223 * Called when it's time to play a post-roll but we don't have one to play. 224 */ 225PlayerWrapper.prototype.onNoPostroll = function () { 226 this.vjsPlayer.trigger('nopostroll'); 227}; 228 229/** 230 * Detects when the video.js player has been disposed. 231 */ 232PlayerWrapper.prototype.playerDisposedListener = function () { 233 this.contentEndedListeners = []; 234 this.controller.onPlayerDisposed(); 235 236 this.contentComplete = true; 237 this.vjsPlayer.off('contentended', this.boundContentEndedListener); 238 239 // Bug fix: https://github.com/googleads/videojs-ima/issues/306 240 if (this.vjsPlayer.ads.adTimeoutTimeout) { 241 clearTimeout(this.vjsPlayer.ads.adTimeoutTimeout); 242 } 243 244 var intervalsToClear = [this.updateTimeIntervalHandle, this.seekCheckIntervalHandle, this.resizeCheckIntervalHandle]; 245 for (var index in intervalsToClear) { 246 if (intervalsToClear[index]) { 247 clearInterval(intervalsToClear[index]); 248 } 249 } 250}; 251 252/** 253 * Start ad playback, or content video playback in the absence of a 254 * pre-roll. 255 */ 256PlayerWrapper.prototype.onReadyForPreroll = function () { 257 this.controller.onPlayerReadyForPreroll(); 258}; 259 260/** 261 * Called when the player fires its 'ready' event. 262 */ 263PlayerWrapper.prototype.onPlayerReady = function () { 264 this.h5Player = document.getElementById(this.getPlayerId()).getElementsByClassName('vjs-tech')[0]; 265 266 // Detect inline options 267 if (this.h5Player.hasAttribute('autoplay')) { 268 this.controller.setSetting('adWillAutoPlay', true); 269 } 270 271 // Sync ad volume with player volume. 272 this.onVolumeChange(); 273 this.vjsPlayer.on('fullscreenchange', this.onFullscreenChange.bind(this)); 274 this.vjsPlayer.on('volumechange', this.onVolumeChange.bind(this)); 275 276 this.controller.onPlayerReady(); 277}; 278 279/** 280 * Listens for the video.js player to change its fullscreen status. This 281 * keeps the fullscreen-ness of the AdContainer in sync with the player. 282 */ 283PlayerWrapper.prototype.onFullscreenChange = function () { 284 if (this.vjsPlayer.isFullscreen()) { 285 this.controller.onPlayerEnterFullscreen(); 286 } else { 287 this.controller.onPlayerExitFullscreen(); 288 } 289}; 290 291/** 292 * Listens for the video.js player to change its volume. This keeps the ad 293 * volume in sync with the content volume if the volume of the player is 294 * changed while content is playing. 295 */ 296PlayerWrapper.prototype.onVolumeChange = function () { 297 var newVolume = this.vjsPlayer.muted() ? 0 : this.vjsPlayer.volume(); 298 this.controller.onPlayerVolumeChanged(newVolume); 299}; 300 301/** 302 * Inject the ad container div into the DOM. 303 * 304 * @param{HTMLElement} adContainerDiv The ad container div. 305 */ 306PlayerWrapper.prototype.injectAdContainerDiv = function (adContainerDiv) { 307 this.vjsControls.el().parentNode.appendChild(adContainerDiv); 308}; 309 310/** 311 * @return {Object} The content player. 312 */ 313PlayerWrapper.prototype.getContentPlayer = function () { 314 return this.h5Player; 315}; 316 317/** 318 * @return {number} The volume, 0-1. 319 */ 320PlayerWrapper.prototype.getVolume = function () { 321 return this.vjsPlayer.muted() ? 0 : this.vjsPlayer.volume(); 322}; 323 324/** 325 * Set the volume of the player. 0-1. 326 * 327 * @param {number} volume The new volume. 328 */ 329PlayerWrapper.prototype.setVolume = function (volume) { 330 this.vjsPlayer.volume(volume); 331 if (volume == 0) { 332 this.vjsPlayer.muted(true); 333 } else { 334 this.vjsPlayer.muted(false); 335 } 336}; 337 338/** 339 * Ummute the player. 340 */ 341PlayerWrapper.prototype.unmute = function () { 342 this.vjsPlayer.muted(false); 343}; 344 345/** 346 * Mute the player. 347 */ 348PlayerWrapper.prototype.mute = function () { 349 this.vjsPlayer.muted(true); 350}; 351 352/** 353 * Play the video. 354 */ 355PlayerWrapper.prototype.play = function () { 356 this.vjsPlayer.play(); 357}; 358 359/** 360 * Get the player width. 361 * 362 * @return {number}
362 The player's width. 363 */ 364PlayerWrapper.prototype.getPlayerWidth = function () { 365 var width = (getComputedStyle(this.vjsPlayer.el()) || {}).width; 366 367 if (!width || parseInt(width, 10) === 0) { 368 width = (this.vjsPlayer.el().getBoundingClientRect() || {}).width; 369 } 370 371 return parseInt(width, 10) || this.vjsPlayer.width(); 372}; 373 374/** 375 * Get the player height. 376 * 377 * @return {number} The player's height. 378 */ 379PlayerWrapper.prototype.getPlayerHeight = function () { 380 var height = (getComputedStyle(this.vjsPlayer.el()) || {}).height; 381 382 if (!height || parseInt(height, 10) === 0) { 383 height = (this.vjsPlayer.el().getBoundingClientRect() || {}).height; 384 } 385 386 return parseInt(height, 10) || this.vjsPlayer.height(); 387}; 388 389/** 390 * @return {Object} The vjs player's options object. 391 */ 392PlayerWrapper.prototype.getPlayerOptions = function () { 393 return this.vjsPlayer.options_; 394}; 395 396/** 397 * Returns the instance of the player id. 398 * @return {string} The player id. 399 */ 400PlayerWrapper.prototype.getPlayerId = function () { 401 return this.vjsPlayer.id(); 402}; 403 404/** 405 * Toggle fullscreen state. 406 */ 407PlayerWrapper.prototype.toggleFullscreen = function () { 408 if (this.vjsPlayer.isFullscreen()) { 409 this.vjsPlayer.exitFullscreen(); 410 } else { 411 this.vjsPlayer.requestFullscreen(); 412 } 413}; 414 415/** 416 * Returns the content playhead tracker. 417 * 418 * @return {Object} The content playhead tracker. 419 */ 420PlayerWrapper.prototype.getContentPlayheadTracker = function () { 421 return this.contentPlayheadTracker; 422}; 423 424/** 425 * Handles ad errors. 426 * 427 * @param {Object} adErrorEvent The ad error event thrown by the IMA SDK. 428 */ 429PlayerWrapper.prototype.onAdError = function (adErrorEvent) { 430 this.vjsControls.show(); 431 var errorMessage = adErrorEvent.getError !== undefined ? adErrorEvent.getError() : adErrorEvent.stack; 432 this.vjsPlayer.trigger({ type: 'adserror', data: { 433 AdError: errorMessage, 434 AdErrorEvent: adErrorEvent 435 } }); 436}; 437 438/** 439 * Handles ad break starting. 440 */ 441PlayerWrapper.prototype.onAdBreakStart = function () { 442 this.contentSource = this.vjsPlayer.currentSrc(); 443 this.vjsPlayer.off('contentended', this.boundContentEndedListener); 444 this.vjsPlayer.ads.startLinearAdMode(); 445 this.vjsControls.hide(); 446 this.vjsPlayer.pause(); 447}; 448 449/** 450 * Handles ad break ending. 451 */ 452PlayerWrapper.prototype.onAdBreakEnd = function () { 453 this.vjsPlayer.on('contentended', this.boundContentEndedListener); 454 if (this.vjsPlayer.ads.inAdBreak()) { 455 this.vjsPlayer.ads.endLinearAdMode(); 456 } 457 this.vjsControls.show(); 458}; 459 460/** 461 * Handles an individual ad start. 462 */ 463PlayerWrapper.prototype.onAdStart = function () { 464 this.vjsPlayer.trigger('ads-ad-started'); 465}; 466 467/** 468 * Handles when all ads have finished playing. 469 */ 470PlayerWrapper.prototype.onAllAdsCompleted = function () { 471 if (this.contentComplete == true) { 472 if (this.h5Player.src != this.contentSource) { 473 this.vjsPlayer.src(this.contentSource); 474 } 475 this.controller.onContentAndAdsCompleted(); 476 } 477}; 478 479/** 480 * Triggers adsready for contrib-ads. 481 */ 482PlayerWrapper.prototype.onAdsReady = function () { 483 this.vjsPlayer.trigger('adsready'); 484}; 485 486/** 487 * Changes the player source. 488 * @param {?string} contentSrc The URI for the content to be played. Leave 489 * blank to use the existing content. 490 * @param {?boolean} playOnLoad True to play the content once it has loaded, 491 * false to only load the content but not start playback. 492 */ 493PlayerWrapper.prototype.changeSource = function (contentSrc, playOnLoad) { 494 // Only try to pause the player when initialised with a source already 495 if (this.vjsPlayer.currentSrc()) { 496 this.vjsPlayer.currentTime(0); 497 this.vjsPlayer.pause(); 498 } 499 if (contentSrc) { 500 this.vjsPlayer.src(contentSrc); 501 } 502 if (playOnLoad) { 503 this.vjsPlayer.one('loadedmetadata', this.playContentFromZero.bind(this)); 504 } else { 505 this.vjsPlayer.one('loadedmetadata', this.seekContentToZero.bind(this)); 506 } 507}; 508 509/** 510 * Seeks content to 00:00:00. This is used as an event handler for the 511 * loadedmetadata event, since seeking is not possible until that event has 512 * fired. 513 */ 514PlayerWrapper.prototype.seekContentToZero = function () { 515 this.vjsPlayer.currentTime(0); 516}; 517 518/** 519 * Seeks content to 00:00:00 and starts playback. This is used as an event 520 * handler for the loadedmetadata event, since seeking is not possible until 521 * that event has fired. 522 */ 523PlayerWrapper.prototype.playContentFromZero = function () { 524 this.vjsPlayer.currentTime(0); 525 this.vjsPlayer.play(); 526}; 527 528/** 529 * Triggers an event on the VJS player 530 * @param {string} name The event name. 531 * @param {Object} data The event data. 532 */ 533PlayerWrapper.prototype.triggerPlayerEvent = function (name, data) { 534 this.vjsPlayer.trigger(name, data); 535}; 536 537/** 538 * Listener JSDoc for ESLint. This listener can be passed to 539 * addContentEndedListener. 540 * @callback listener 541 */ 542 543/** 544 * Adds a listener for the 'contentended' event of the video player. This should 545 * be used instead of setting an 'contentended' listener directly to ensure that 546 * the ima can do proper cleanup of the SDK before other event listeners are 547 * called. 548 * @param {listener} listener The listener to be called when content 549 * completes. 550 */ 551PlayerWrapper.prototype.addContentEndedListener = function (listener) { 552 this.contentEndedListeners.push(listener); 553}; 554 555/** 556 * Reset the player. 557 */ 558PlayerWrapper.prototype.reset = function () { 559 this.vjsPlayer.on('contentended', this.boundContentEndedListener); 560 this.vjsControls.show(); 561 if (this.vjsPlayer.ads.inAdBreak()) { 562 this.vjsPlayer.ads.endLinearAdMode(); 563 } 564 // Reset the content time we give the SDK. Fixes an issue where requesting 565 // VMAP followed by VMAP would play the second mid-rolls as pre-rolls if 566 // the first playthrough of the video passed the second response's 567 // mid-roll time. 568 this.contentPlayheadTracker.currentTime = 0; 569 this.contentComplete = false;
vendor: 13,003 bytes, lines 570-1014
570}; 571 572/** 573 * Copyright 2017 Google Inc. 574 * 575 * Licensed under the Apache License, Version 2.0 (the "License"); 576 * you may not use this file except in compliance with the License. 577 * You may obtain a copy of the License at 578 * 579 * http://www.apache.org/licenses/LICENSE-2.0 580 * 581 * Unless required by applicable law or agreed to in writing, software 582 * distributed under the License is distributed on an "AS IS" BASIS, 583 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 584 * See the License for the specific language governing permissions and 585 * limitations under the License. 586 * 587 * IMA SDK integration plugin for Video.js. For more information see 588 * https://www.github.com/googleads/videojs-ima 589 */ 590 591/** 592 * Ad UI implementation. 593 * 594 * @param {Controller} controller Plugin controller. 595 * @constructor 596 * @struct 597 * @final 598 */ 599var AdUi = function AdUi(controller) { 600 /** 601 * Plugin controller. 602 */ 603 this.controller = controller; 604 605 /** 606 * Div used as an ad container. 607 */ 608 this.adContainerDiv = document.createElement('div'); 609 610 /** 611 * Div used to display ad controls. 612 */ 613 this.controlsDiv = document.createElement('div'); 614 615 /** 616 * Div used to display ad countdown timer. 617 */ 618 this.countdownDiv = document.createElement('div'); 619 620 /** 621 * Div used to display add seek bar. 622 */ 623 this.seekBarDiv = document.createElement('div'); 624 625 /** 626 * Div used to display ad progress (in seek bar). 627 */ 628 this.progressDiv = document.createElement('div'); 629 630 /** 631 * Div used to display ad play/pause button. 632 */ 633 this.playPauseDiv = document.createElement('div'); 634 635 /** 636 * Div used to display ad mute button. 637 */ 638 this.muteDiv = document.createElement('div'); 639 640 /** 641 * Div used by the volume slider. 642 */ 643 this.sliderDiv = document.createElement('div'); 644 645 /** 646 * Volume slider level visuals 647 */ 648 this.sliderLevelDiv = document.createElement('div'); 649 650 /** 651 * Div used to display ad fullscreen button. 652 */ 653 this.fullscreenDiv = document.createElement('div'); 654 655 /** 656 * Bound event handler for onMouseUp. 657 */ 658 this.boundOnMouseUp = this.onMouseUp.bind(this); 659 660 /** 661 * Bound event handler for onMouseMove. 662 */ 663 this.boundOnMouseMove = this.onMouseMove.bind(this); 664 665 /** 666 * Stores data for the ad playhead tracker. 667 */ 668 this.adPlayheadTracker = { 669 'currentTime': 0, 670 'duration': 0, 671 'isPod': false, 672 'adPosition': 0, 673 'totalAds': 0 674 }; 675 676 /** 677 * Used to prefix videojs ima controls. 678 */ 679 this.controlPrefix = this.controller.getPlayerId() + '_'; 680 681 /** 682 * Boolean flag to show or hide the ad countdown timer. 683 */ 684 this.showCountdown = true; 685 if (this.controller.getSettings().showCountdown === false) { 686 this.showCountdown = false; 687 } 688 689 this.createAdContainer(); 690}; 691 692/** 693 * Creates the ad container. 694 */ 695AdUi.prototype.createAdContainer = function () { 696 this.assignControlAttributes(this.adContainerDiv, 'ima-ad-container'); 697 this.adContainerDiv.style.position = 'absolute'; 698 this.adContainerDiv.style.zIndex = 1111; 699 this.adContainerDiv.addEventListener('mouseenter', this.showAdControls.bind(this), false); 700 this.adContainerDiv.addEventListener('mouseleave', this.hideAdControls.bind(this), false); 701 this.createControls(); 702 this.controller.injectAdContainerDiv(this.adContainerDiv); 703}; 704 705/** 706 * Create the controls. 707 */ 708AdUi.prototype.createControls = function () { 709 this.assignControlAttributes(this.controlsDiv, 'ima-controls-div'); 710 this.controlsDiv.style.width = '100%'; 711 712 this.assignControlAttributes(this.countdownDiv, 'ima-countdown-div'); 713 this.countdownDiv.innerHTML = this.controller.getSettings().adLabel; 714 this.countdownDiv.style.display = this.showCountdown ? 'block' : 'none'; 715 716 this.assignControlAttributes(this.seekBarDiv, 'ima-seek-bar-div'); 717 this.seekBarDiv.style.width = '100%'; 718 719 this.assignControlAttributes(this.progressDiv, 'ima-progress-div'); 720 721 this.assignControlAttributes(this.playPauseDiv, 'ima-play-pause-div'); 722 this.addClass(this.playPauseDiv, 'ima-playing'); 723 this.playPauseDiv.addEventListener('click', this.onAdPlayPauseClick.bind(this), false); 724 725 this.assignControlAttributes(this.muteDiv, 'ima-mute-div'); 726 this.addClass(this.muteDiv, 'ima-non-muted'); 727 this.muteDiv.addEventListener('click', this.onAdMuteClick.bind(this), false); 728 729 this.assignControlAttributes(this.sliderDiv, 'ima-slider-div'); 730 this.sliderDiv.addEventListener('mousedown', this.onAdVolumeSliderMouseDown.bind(this), false); 731 732 // Hide volume slider controls on iOS as they aren't supported. 733 if (this.controller.getIsIos()) { 734 this.sliderDiv.style.display = 'none'; 735 } 736 737 this.assignControlAttributes(this.sliderLevelDiv, 'ima-slider-level-div'); 738 739 this.assignControlAttributes(this.fullscreenDiv, 'ima-fullscreen-div'); 740 this.addClass(this.fullscreenDiv, 'ima-non-fullscreen'); 741 this.fullscreenDiv.addEventListener('click', this.onAdFullscreenClick.bind(this), false); 742 743 this.adContainerDiv.appendChild(this.controlsDiv); 744 this.controlsDiv.appendChild(this.countdownDiv); 745 this.controlsDiv.appendChild(this.seekBarDiv); 746 this.controlsDiv.appendChild(this.playPauseDiv); 747 this.controlsDiv.appendChild(this.muteDiv); 748 this.controlsDiv.appendChild(this.sliderDiv); 749 this.controlsDiv.appendChild(this.fullscreenDiv); 750 this.seekBarDiv.appendChild(this.progressDiv); 751 this.sliderDiv.appendChild(this.sliderLevelDiv); 752}; 753 754/** 755 * Listener for clicks on the play/pause button during ad playback. 756 */ 757AdUi.prototype.onAdPlayPauseClick = function () { 758 this.controller.onAdPlayPauseClick(); 759}; 760 761/** 762 * Listener for clicks on the play/pause button during ad playback. 763 */ 764AdUi.prototype.onAdMuteClick = function () { 765 this.controller.onAdMuteClick(); 766}; 767 768/** 769 * Listener for clicks on the fullscreen button during ad playback. 770 */ 771AdUi.prototype.onAdFullscreenClick = function () { 772 this.controller.toggleFullscreen(); 773}; 774 775/** 776 * Show pause and hide play button 777 */ 778AdUi.prototype.onAdsPaused = function () { 779 this.addClass(this.playPauseDiv, 'ima-paused'); 780 this.removeClass(this.playPauseDiv, 'ima-playing'); 781 this.showAdControls(); 782}; 783 784/** 785 * Show pause and hide play button 786 */ 787AdUi.prototype.onAdsResumed = function () { 788 this.onAdsPlaying(); 789 this.showAdControls(); 790}; 791 792/** 793 * Show play and hide pause button 794 */ 795AdUi.prototype.onAdsPlaying = function () { 796 this.addClass(this.playPauseDiv, 'ima-playing'); 797 this.removeClass(this.playPauseDiv, 'ima-paused'); 798}; 799 800/** 801 * Takes data from the controller to update the UI. 802 * 803 * @param {number} currentTime Current time of the ad. 804 * @param {number} remainingTime Remaining time of the ad. 805 * @param {number} duration Duration of the ad. 806 * @param {number} adPosition Index of the ad in the pod. 807 * @param {number} totalAds Total number of ads in the pod. 808 */ 809AdUi.prototype.updateAdUi = function (currentTime, remainingTime, duration, adPosition, totalAds) { 810 // Update countdown timer data 811 var remainingMinutes = Math.floor(remainingTime / 60); 812 var remainingSeconds = Math.floor(remainingTime % 60); 813 if (remainingSeconds.toString().length < 2) { 814 remainingSeconds = '0' + remainingSeconds; 815 } 816 var podCount = ': '; 817 if (totalAds > 1) { 818 podCount = ' (' + adPosition + ' ' + this.controller.getSettings().adLabelNofN + ' ' + totalAds + '): '; 819 } 820 this.countdownDiv.innerHTML = this.controller.getSettings().adLabel + podCount + remainingMinutes + ':' + remainingSeconds; 821 822 // Update UI 823 var playProgressRatio = currentTime / duration; 824 var playProgressPercent = playProgressRatio * 100; 825 this.progressDiv.style.width = playProgressPercent + '%'; 826}; 827 828/** 829 * Handles UI changes when the ad is unmuted. 830 */ 831AdUi.prototype.unmute = function () { 832 this.addClass(this.muteDiv, 'ima-non-muted'); 833 this.removeClass(this.muteDiv, 'ima-muted'); 834 this.sliderLevelDiv.style.width = this.controller.getPlayerVolume() * 100 + '%'; 835}; 836 837/** 838 * Handles UI changes when the ad is muted. 839 */ 840AdUi.prototype.mute = function () { 841 this.addClass(this.muteDiv, 'ima-muted'); 842 this.removeClass(this.muteDiv, 'ima-non-muted'); 843 this.sliderLevelDiv.style.width = '0%'; 844}; 845 846/* 847 * Listener for mouse down events during ad playback. Used for volume. 848 */ 849AdUi.prototype.onAdVolumeSliderMouseDown = function () { 850 document.addEventListener('mouseup', this.boundOnMouseUp, false); 851 document.addEventListener('mousemove', this.boundOnMouseMove, false); 852}; 853 854/* 855 * Mouse movement listener used for volume slider. 856 */ 857AdUi.prototype.onMouseMove = function (event) { 858 this.changeVolume(event); 859}; 860 861/* 862 * Mouse release listener used for volume slider. 863 */ 864AdUi.prototype.onMouseUp = function (event) { 865 this.changeVolume(event); 866 document.removeEventListener('mouseup', this.boundOnMouseUp); 867 document.removeEventListener('mousemove', this.boundOnMouseMove); 868}; 869 870/* 871 * Utility function to set volume and associated UI 872 */ 873AdUi.prototype.changeVolume = function (event) { 874 var percent = (event.clientX - this.sliderDiv.getBoundingClientRect().left) / this.sliderDiv.offsetWidth; 875 percent *= 100; 876 // Bounds value 0-100 if mouse is outside slider region. 877 percent = Math.min(Math.max(percent, 0), 100); 878 this.sliderLevelDiv.style.width = percent + '%'; 879 if (this.percent == 0) { 880 this.addClass(this.muteDiv, 'ima-muted'); 881 this.removeClass(this.muteDiv, 'ima-non-muted'); 882 } else { 883 this.addClass(this.muteDiv, 'ima-non-muted'); 884 this.removeClass(this.muteDiv, 'ima-muted'); 885 } 886 this.controller.setVolume(percent / 100); // 0-1 887}; 888 889/** 890 * Show the ad container. 891 */ 892AdUi.prototype.showAdContainer = function () { 893 this.adContainerDiv.style.display = 'block'; 894}; 895 896/** 897 * Handles ad errors. 898 */ 899AdUi.prototype.onAdError = function () { 900 this.adContainerDiv.style.display = 'none'; 901}; 902 903/** 904 * Handles ad break starting. 905 * 906 * @param {Object} adEvent The event fired by the IMA SDK. 907 */ 908AdUi.prototype.onAdBreakStart = function (adEvent) { 909 this.adContainerDiv.style.display = 'block'; 910 911 var contentType = adEvent.getAd().getContentType(); 912 if (contentType === 'application/javascript' && !this.controller.getSettings().showControlsForJSAds) { 913 this.controlsDiv.style.display = 'none'; 914 } else { 915 this.controlsDiv.style.display = 'block'; 916 } 917 this.onAdsPlaying(); 918 // Start with the ad controls minimized. 919 this.hideAdControls(); 920}; 921 922/** 923 * Handles ad break ending. 924 */ 925AdUi.prototype.onAdBreakEnd = function () { 926 var currentAd = this.controller.getCurrentAd(); 927 if (currentAd == null || // hide for post-roll only playlist 928 currentAd.isLinear()) { 929 // don't hide for non-linear ads 930 this.adContainerDiv.style.display = 'none'; 931 } 932 this.controlsDiv.style.display = 'none'; 933 this.countdownDiv.innerHTML = ''; 934}; 935 936/** 937 * Handles when all ads have finished playing. 938 */ 939AdUi.prototype.onAllAdsCompleted = function () { 940 this.adContainerDiv.style.display = 'none'; 941}; 942 943/** 944 * Handles when a linear ad starts. 945 */ 946AdUi.prototype.onLinearAdStart = function () { 947 // Don't bump container when controls are shown 948 this.removeClass(this.adContainerDiv, 'bumpable-ima-ad-container'); 949}; 950 951/** 952 * Handles when a non-linear ad starts. 953 */ 954AdUi.prototype.onNonLinearAdLoad = function () { 955 // For non-linear ads that show after a linear ad. For linear ads, we show the 956 // ad container in onAdBreakStart to prevent blinking in pods. 957 this.adContainerDiv.style.display = 'block'; 958 // Bump container when controls are shown 959 this.addClass(this.adContainerDiv, 'bumpable-ima-ad-container'); 960}; 961 962AdUi.prototype.onPlayerEnterFullscreen = function () { 963 this.addClass(this.fullscreenDiv, 'ima-fullscreen'); 964 this.removeClass(this.fullscreenDiv, 'ima-non-fullscreen'); 965}; 966 967AdUi.prototype.onPlayerExitFullscreen = function () { 968 this.addClass(this.fullscreenDiv, 'ima-non-fullscreen'); 969 this.removeClass(this.fullscreenDiv, 'ima-fullscreen'); 970}; 971 972/** 973 * Called when the player volume changes. 974 * 975 * @param {number} volume The new player volume. 976 */ 977AdUi.prototype.onPlayerVolumeChanged = function (volume) { 978 if (volume == 0) { 979 this.addClass(this.muteDiv, 'ima-muted'); 980 this.removeClass(this.muteDiv, 'ima-non-muted'); 981 this.sliderLevelDiv.style.width = '0%'; 982 } else { 983 this.addClass(this.muteDiv, 'ima-non-muted'); 984 this.removeClass(this.muteDiv, 'ima-muted'); 985 this.sliderLevelDiv.style.width = volume * 100 + '%'; 986 } 987}; 988 989/** 990 * Shows ad controls on mouseover. 991 */ 992AdUi.prototype.showAdControls = function () { 993 this.addClass(this.controlsDiv, 'ima-controls-div-showing'); 994 this.playPauseDiv.style.display = 'block'; 995 this.muteDiv.style.display = 'block'; 996 this.fullscreenDiv.style.display = 'block'; 997 // Don't show on iOS. 998 if (!this.controller.getIsIos()) { 999 this.sliderDiv.style.display = 'block'; 1000 } 1001}; 1002 1003/** 1004 * Hide the ad controls. 1005 */ 1006AdUi.prototype.hideAdControls = function () { 1007 this.removeClass(this.controlsDiv, 'ima-controls-div-showing'); 1008 this.playPauseDiv.style.display = 'none'; 1009 this.muteDiv.style.display = 'none'; 1010 this.sliderDiv.style.display = 'none'; 1011 this.fullscreenDiv.style.display = 'none'; 1012}; 1013 1014/**
1015 * Assigns the unique id and class names to the given element as well as the 1016 * style class. 1017 * @param {HTMLElement} element Element that needs the controlName assigned. 1018 * @param {string} controlName Control name to assign. 1019 */ 1020AdUi.prototype.assignControlAttributes = function (element, controlName) { 1021 element.id = this.controlPrefix + controlName; 1022 element.className = this.controlPrefix + controlName + ' ' + controlName; 1023}; 1024 1025/** 1026 * Returns a regular expression to test a string for the given className. 1027 * 1028 * @param {string} className The name of the class. 1029 * @return {RegExp} The regular expression used to test for that class. 1030 */ 1031AdUi.prototype.getClassRegexp = function (className) { 1032 // Matches on 1033 // (beginning of string OR NOT word char) 1034 // classname 1035 // (negative lookahead word char OR end of string) 1036 return new RegExp('(^|[^A-Za-z-])' + className + '((?![A-Za-z-])|$)', 'gi'); 1037}; 1038 1039/** 1040 * Returns whether or not the provided element has the provied class in its 1041 * className. 1042 * @param {HTMLElement} element Element to tes.t 1043 * @param {string} className Class to look for. 1044 * @return {boolean} True if element has className in class list. False 1045 * otherwise. 1046 */ 1047AdUi.prototype.elementHasClass = function (element, className) { 1048 var classRegexp = this.getClassRegexp(className); 1049 return classRegexp.test(element.className); 1050}; 1051 1052/** 1053 * Adds a class to the given element if it doesn't already have the class 1054 * @param {HTMLElement} element Element to which the class will be added. 1055 * @param {string} classToAdd Class to add. 1056 */ 1057AdUi.prototype.addClass = function (element, classToAdd) { 1058 element.className = element.className.trim() + ' ' + classToAdd; 1059}; 1060 1061/** 1062 * Removes a class from the given element if it has the given class 1063 * 1064 * @param {HTMLElement} element Element from which the class will be removed. 1065 * @param {string} classToRemove Class to remove. 1066 */ 1067AdUi.prototype.removeClass = function (element, classToRemove) { 1068 var classRegexp = this.getClassRegexp(classToRemove); 1069 element.className = element.className.trim().replace(classRegexp, ''); 1070}; 1071 1072/** 1073 * @return {HTMLElement} The div for the ad container. 1074 */ 1075AdUi.prototype.getAdContainerDiv = function () { 1076 return this.adContainerDiv; 1077}; 1078 1079/** 1080 * Changes the flag to show or hide the ad countdown timer. 1081 * 1082 * @param {boolean} showCountdownIn Show or hide the countdown timer. 1083 */ 1084AdUi.prototype.setShowCountdown = function (showCountdownIn) { 1085 this.showCountdown = showCountdownIn; 1086 this.countdownDiv.style.display = this.showCountdown ? 'block' : 'none'; 1087}; 1088 1089var name = "videojs-ima"; 1090var version = "1.5.1"; 1091var license = "Apache-2.0"; 1092var main = "./dist/videojs.ima.js"; 1093var author = { "name": "Google Inc." }; 1094var engines = { "node": ">=0.8.0" }; 1095var scripts = { "contBuild": "watch 'npm run rollup:max' src", "predevServer": "echo \"Starting up server on localhost:8000.\"", "devServer": "forever start ./node_modules/http-server/bin/http-server -p 8000 && npm run contBuild", "postdevServer": "forever stop ./node_modules/http-server/bin/http-server", "lint": "eslint \"src/*.js\"", "rollup": "npm-run-all rollup:*", "rollup:max": "rollup -c configs/rollup.config.js", "rollup:min": "rollup -c configs/rollup.config.min.js", "pretest": "npm run rollup", "start": "npm run devServer", "test": "npm-run-all test:*", "test:vjs5": "npm install [email protected] --no-save && npm-run-all -p -r testServer webdriver", "test:vjs6": "npm install video.js@6 --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" }; 1096var repository = { "type": "git", "url": "https://github.com/googleads/videojs-ima" }; 1097var files = ["CHANGELOG.md", "LICENSE", "README.md", "dist/", "src/"]; 1098var dependencies = { "can-autoplay": "^3.0.0", "video.js": "^5.19.2 || ^6", "videojs-contrib-ads": "^6" }; 1099var devDependencies = { "babel-core": "^6.26.0", "babel-preset-env": "^1.6.1", "child_process": "^1.0.2", "chromedriver": "^2.35.
vendor: 7,087 bytes, lines 1099-1320
10990", "conventional-changelog-cli": "^1.3.5", "conventional-changelog-videojs": "^3.0.0", "eslint": "^4.11.0", "eslint-config-google": "^0.9.1", "eslint-plugin-jsdoc": "^3.2.0", "forever": "^0.15.3", "geckodriver": "^1.10.0", "http-server": "^0.10.0", "mocha": "^4.0.1", "npm-run-all": "^4.1.2", "path": "^0.12.7", "rimraf": "^2.6.2", "rollup": "^0.51.8", "rollup-plugin-babel": "^3.0.3", "rollup-plugin-copy": "^0.2.3", "rollup-plugin-json": "^2.3.0", "rollup-plugin-uglify": "^2.0.1", "selenium-webdriver": "^3.6.0", "uglify-es": "^3.1.10", "watch": "^1.0.2" }; 1100var keywords = ["videojs", "videojs-plugin"]; 1101var pkg = { 1102 name: name, 1103 version: version, 1104 license: license, 1105 main: main, 1106 author: author, 1107 engines: engines, 1108 scripts: scripts, 1109 repository: repository, 1110 files: files, 1111 dependencies: dependencies, 1112 devDependencies: devDependencies, 1113 keywords: keywords 1114}; 1115 1116/** 1117 * Copyright 2017 Google Inc. 1118 * 1119 * Licensed under the Apache License, Version 2.0 (the "License"); 1120 * you may not use this file except in compliance with the License. 1121 * You may obtain a copy of the License at 1122 * 1123 * http://www.apache.org/licenses/LICENSE-2.0 1124 * 1125 * Unless required by applicable law or agreed to in writing, software 1126 * distributed under the License is distributed on an "AS IS" BASIS, 1127 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1128 * See the License for the specific language governing permissions and 1129 * limitations under the License. 1130 * 1131 * IMA SDK integration plugin for Video.js. For more information see 1132 * https://www.github.com/googleads/videojs-ima 1133 */ 1134 1135/** 1136 * Implementation of the IMA SDK for the plugin. 1137 * 1138 * @param {Object} controller Reference to the parent controller. 1139 * 1140 * @constructor 1141 * @struct 1142 * @final 1143 */ 1144var SdkImpl = function SdkImpl(controller) { 1145 /** 1146 * Plugin controller. 1147 */ 1148 this.controller = controller; 1149 1150 /** 1151 * IMA SDK AdDisplayContainer. 1152 */ 1153 this.adDisplayContainer = null; 1154 1155 /** 1156 * True if the AdDisplayContainer has been initialized. False otherwise. 1157 */ 1158 this.adDisplayContainerInitialized = false; 1159 1160 /** 1161 * IMA SDK AdsLoader 1162 */ 1163 this.adsLoader = null; 1164 1165 /** 1166 * IMA SDK AdsManager 1167 */ 1168 this.adsManager = null; 1169 1170 /** 1171 * IMA SDK AdsRenderingSettings. 1172 */ 1173 this.adsRenderingSettings = null; 1174 1175 /** 1176 * Ad tag URL. Should return VAST, VMAP, or ad rules. 1177 */ 1178 this.adTagUrl = null; 1179 1180 /** 1181 * VAST, VMAP, or ad rules response. Used in lieu of fetching a response 1182 * from an ad tag URL. 1183 */ 1184 this.adsResponse = null; 1185 1186 /** 1187 * Current IMA SDK Ad. 1188 */ 1189 this.currentAd = null; 1190 1191 /** 1192 * Timer used to track ad progress. 1193 */ 1194 this.adTrackingTimer = null; 1195 1196 /** 1197 * True if ALL_ADS_COMPLETED has fired, false until then. 1198 */ 1199 this.allAdsCompleted = false; 1200 1201 /** 1202 * True if ads are currently displayed, false otherwise. 1203 * True regardless of ad pause state if an ad is currently being displayed. 1204 */ 1205 this.adsActive = false; 1206 1207 /** 1208 * True if ad is currently playing, false if ad is paused or ads are not 1209 * currently displayed. 1210 */ 1211 this.adPlaying = false; 1212 1213 /** 1214 * True if the ad is muted, false otherwise. 1215 */ 1216 this.adMuted = false; 1217 1218 /** 1219 * Listener to be called to trigger manual ad break playback. 1220 */ 1221 this.adBreakReadyListener = undefined; 1222 1223 /** 1224 * Tracks whether or not we have already called adsLoader.contentComplete(). 1225 */ 1226 this.contentCompleteCalled = false; 1227 1228 /** 1229 * Stores the dimensions for the ads manager. 1230 */ 1231 this.adsManagerDimensions = { 1232 width: 0, 1233 height: 0 1234 }; 1235 1236 /** 1237 * Boolean flag to enable manual ad break playback. 1238 */ 1239 this.autoPlayAdBreaks = true; 1240 if (this.controller.getSettings().autoPlayAdBreaks === false) { 1241 this.autoPlayAdBreaks = false; 1242 } 1243 1244 // Set SDK settings from plugin settings. 1245 if (this.controller.getSettings().locale) { 1246 /* eslint no-undef: 'error' */ 1247 /* global google */ 1248 google.ima.settings.setLocale(this.controller.getSettings().locale); 1249 } 1250 if (this.controller.getSettings().disableFlashAds) { 1251 google.ima.settings.setDisableFlashAds(this.controller.getSettings().disableFlashAds); 1252 } 1253 if (this.controller.getSettings().disableCustomPlaybackForIOS10Plus) { 1254 google.ima.settings.setDisableCustomPlaybackForIOS10Plus(this.controller.getSettings().disableCustomPlaybackForIOS10Plus); 1255 } 1256}; 1257 1258/** 1259 * Creates and initializes the IMA SDK objects. 1260 */ 1261SdkImpl.prototype.initAdObjects = function () { 1262 this.adDisplayContainer = new google.ima.AdDisplayContainer(this.controller.getAdContainerDiv(), this.controller.getContentPlayer()); 1263 1264 this.adsLoader = new google.ima.AdsLoader(this.adDisplayContainer); 1265 1266 this.adsLoader.getSettings().setVpaidMode(google.ima.ImaSdkSettings.VpaidMode.ENABLED); 1267 if (this.controller.getSettings().vpaidAllowed == false) { 1268 this.adsLoader.getSettings().setVpaidMode(google.ima.ImaSdkSettings.VpaidMode.DISABLED); 1269 } 1270 if (this.controller.getSettings().vpaidMode) { 1271 this.adsLoader.getSettings().setVpaidMode(this.controller.getSettings().vpaidMode); 1272 } 1273 1274 if (this.controller.getSettings().locale) { 1275 this.adsLoader.getSettings().setLocale(this.controller.getSettings().locale); 1276 } 1277 1278 if (this.controller.getSettings().numRedirects) { 1279 this.adsLoader.getSettings().setNumRedirects(this.controller.getSettings().numRedirects); 1280 } 1281 1282 this.adsLoader.getSettings().setPlayerType('videojs-ima'); 1283 this.adsLoader.getSettings().setPlayerVersion(pkg.version); 1284 this.adsLoader.getSettings().setAutoPlayAdBreaks(this.autoPlayAdBreaks); 1285 1286 this.adsLoader.addEventListener(google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED, this.onAdsManagerLoaded.bind(this), false); 1287 this.adsLoader.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR, this.onAdsLoaderError.bind(this), false); 1288}; 1289 1290/** 1291 * Creates the AdsRequest and request ads through the AdsLoader. 1292 */ 1293SdkImpl.prototype.requestAds = function () { 1294 var adsRequest = new google.ima.AdsRequest(); 1295 if (this.controller.getSettings().adTagUrl) { 1296 adsRequest.adTagUrl = this.controller.getSettings().adTagUrl; 1297 } else { 1298 adsRequest.adsResponse = this.controller.getSettings().adsResponse; 1299 } 1300 if (this.controller.getSettings().forceNonLinearFullSlot) { 1301 adsRequest.forceNonLinearFullSlot = true; 1302 } 1303 1304 if (this.controller.getSettings().vastLoadTimeout) { 1305 adsRequest.vastLoadTimeout = this.controller.getSettings().vastLoadTimeout; 1306 } 1307 1308 adsRequest.linearAdSlotWidth = this.controller.getPlayerWidth(); 1309 adsRequest.linearAdSlotHeight = this.controller.getPlayerHeight(); 1310 adsRequest.nonLinearAdSlotWidth = this.controller.getSettings().nonLinearWidth || this.controller.getPlayerWidth(); 1311 adsRequest.nonLinearAdSlotHeight = this.controller.getSettings().nonLinearHeight || this.controller.getPlayerHeight(); 1312 adsRequest.setAdWillAutoPlay(this.controller.adsWillAutoplay()); 1313 adsRequest.setAdWillPlayMuted(this.controller.adsWillPlayMuted()); 1314 1315 this.adsLoader.requestAds(adsRequest); 1316 this.controller.triggerPlayerEvent('ads-request', adsRequest); 1317}; 1318 1319/** 1320 * Listener for the ADS_MANAGER_LOADED event. Creates the AdsManager,
1321 * sets up event listeners, and triggers the 'adsready' event for 1322 * videojs-ads-contrib. 1323 * 1324 * @param {google.ima.AdsManagerLoadedEvent} adsManagerLoadedEvent Fired when 1325 * the AdsManager loads. 1326 */ 1327SdkImpl.prototype.onAdsManagerLoaded = function (adsManagerLoadedEvent) { 1328 this.createAdsRenderingSettings(); 1329 1330 this.adsManager = adsManagerLoadedEvent.getAdsManager(this.controller.getContentPlayheadTracker(), this.adsRenderingSettings); 1331 1332 this.adsManager.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR, this.onAdError.bind(this)); 1333 this.adsManager.addEventListener(google.ima.AdEvent.Type.AD_BREAK_READY, this.onAdBreakReady.bind(this)); 1334 this.adsManager.addEventListener(google.ima.AdEvent.Type.CONTENT_PAUSE_REQUESTED, this.onContentPauseRequested.bind(this)); 1335 this.adsManager.addEventListener(google.ima.AdEvent.Type.CONTENT_RESUME_REQUESTED, this.onContentResumeRequested.bind(this)); 1336 this.adsManager.addEventListener(google.ima.AdEvent.Type.ALL_ADS_COMPLETED, this.onAllAdsCompleted.bind(this)); 1337 1338 this.adsManager.addEventListener(google.ima.AdEvent.Type.LOADED, this.onAdLoaded.bind(this)); 1339 this.adsManager.addEventListener(google.ima.AdEvent.Type.STARTED, this.onAdStarted.bind(this)); 1340 this.adsManager.addEventListener(google.ima.AdEvent.Type.CLICK, this.onAdPaused.bind(this)); 1341 this.adsManager.addEventListener(google.ima.AdEvent.Type.COMPLETE, this.onAdComplete.bind(this)); 1342 this.adsManager.addEventListener(google.ima.AdEvent.Type.SKIPPED, this.onAdComplete.bind(this)); 1343 1344 if (this.controller.getIsMobile()) { 1345 // Show/hide controls on pause and resume (triggered by tap). 1346 this.adsManager.addEventListener(google.ima.AdEvent.Type.PAUSED, this.onAdPaused.bind(this)); 1347 this.adsManager.addEventListener(google.ima.AdEvent.Type.RESUMED, this.onAdResumed.bind(this)); 1348 } 1349 1350 if (!this.autoPlayAdBreaks) { 1351 this.initAdsManager(); 1352 } 1353 1354 this.controller.onAdsReady(); 1355 1356 if (this.controller.getSettings().adsManagerLoadedCallback) { 1357 this.controller.getSettings().adsManagerLoadedCallback(); 1358 } 1359}; 1360 1361/** 1362 * Listener for errors fired by the AdsLoader. 1363 * @param {google.ima.AdErrorEvent} event The error event thrown by the 1364 * AdsLoader. See 1365 * https://developers.google.com/interactive-media-ads/docs/sdks/html5/v3/apis#ima.AdError.Type 1366 */ 1367SdkImpl.prototype.onAdsLoaderError = function (event) { 1368 window.console.warn('AdsLoader error: ' + event.getError()); 1369 this.controller.onErrorLoadingAds(event); 1370 if (this.adsManager) { 1371 this.adsManager.destroy(); 1372 } 1373}; 1374 1375/** 1376 * Initialize the ads manager. 1377 */ 1378SdkImpl.prototype.initAdsManager = function () { 1379 try { 1380 var initWidth = this.controller.getPlayerWidth(); 1381 var initHeight = this.controller.getPlayerHeight(); 1382 this.adsManagerDimensions.width = initWidth; 1383 this.adsManagerDimensions.height = initHeight; 1384 this.adsManager.init(initWidth, initHeight, google.ima.ViewMode.NORMAL); 1385 this.adsManager.setVolume(this.controller.getPlayerVolume()); 1386 if (!this.adDisplayContainerInitialized) { 1387 this.adDisplayContainer.initialize(); 1388 this.adDisplayContainer.initialized = true; 1389 } 1390 } catch (adError) { 1391 this.onAdError(adError); 1392 } 1393}; 1394 1395/** 1396 * Create AdsRenderingSettings for the IMA SDK. 1397 */ 1398SdkImpl.prototype.createAdsRenderingSettings = function () { 1399 this.adsRenderingSettings = new google.ima.AdsRenderingSettings(); 1400 this.adsRenderingSettings.restoreCustomPlaybackStateOnAdBreakComplete = true; 1401 if (this.controller.getSettings().adsRenderingSettings) { 1402 for (var setting in this.controller.getSettings().adsRenderingSettings) { 1403 if (setting !== '') { 1404 this.adsRenderingSettings[setting] = this.controller.getSettings().adsRenderingSettings[setting]; 1405 } 1406 } 1407 } 1408}; 1409 1410/** 1411 * Listener for errors thrown by the AdsManager. 1412 * @param {google.ima.AdErrorEvent} adErrorEvent The error event thrown by 1413 * the AdsManager. 1414 */ 1415SdkImpl.prototype.onAdError = function (adErrorEvent) { 1416 var errorMessage = adErrorEvent.getError !== undefined ? adErrorEvent.getError() : adErrorEvent.stack; 1417 window.console.warn('Ad error: ' + errorMessage); 1418 1419 this.adsManager.destroy(); 1420 this.controller.onAdError(adErrorEvent); 1421 1422 // reset these so consumers don't think we are still in an ad break, 1423 // but reset them after any prior cleanup happens 1424 this.adsActive = false;
vendor: 35,251 bytes, lines 1425-2678
1425 this.adPlaying = false; 1426}; 1427 1428/** 1429 * Listener for AD_BREAK_READY. Passes event on to publisher's listener. 1430 * @param {google.ima.AdEvent} adEvent AdEvent thrown by the AdsManager. 1431 */ 1432SdkImpl.prototype.onAdBreakReady = function (adEvent) { 1433 this.adBreakReadyListener(adEvent); 1434}; 1435 1436/** 1437 * Pauses the content video and displays the ad container so ads can play. 1438 * @param {google.ima.AdEvent} adEvent The AdEvent thrown by the AdsManager. 1439 */ 1440SdkImpl.prototype.onContentPauseRequested = function (adEvent) { 1441 this.adsActive = true; 1442 this.adPlaying = true; 1443 this.controller.onAdBreakStart(adEvent); 1444}; 1445 1446/** 1447 * Resumes content video and hides the ad container. 1448 * @param {google.ima.AdEvent} adEvent The AdEvent thrown by the AdsManager. 1449 */ 1450SdkImpl.prototype.onContentResumeRequested = function (adEvent) { 1451 this.adsActive = false; 1452 this.adPlaying = false; 1453 this.controller.onAdBreakEnd(); 1454 // Hide controls in case of future non-linear ads. They'll be unhidden in 1455 // content_pause_requested. 1456}; 1457 1458/** 1459 * Records that ads have completed and calls contentAndAdsEndedListeners 1460 * if content is also complete. 1461 * @param {google.ima.AdEvent} adEvent The AdEvent thrown by the AdsManager. 1462 */ 1463SdkImpl.prototype.onAllAdsCompleted = function (adEvent) { 1464 this.allAdsCompleted = true; 1465 this.controller.onAllAdsCompleted(); 1466}; 1467 1468/** 1469 * Starts the content video when a non-linear ad is loaded. 1470 * @param {google.ima.AdEvent} adEvent The AdEvent thrown by the AdsManager. 1471 */ 1472SdkImpl.prototype.onAdLoaded = function (adEvent) { 1473 if (!adEvent.getAd().isLinear()) { 1474 this.controller.onNonLinearAdLoad(); 1475 this.controller.playContent(); 1476 } 1477}; 1478 1479/** 1480 * Starts the interval timer to check the current ad time when an ad starts 1481 * playing. 1482 * @param {google.ima.AdEvent} adEvent The AdEvent thrown by the AdsManager. 1483 */ 1484SdkImpl.prototype.onAdStarted = function (adEvent) { 1485 this.currentAd = adEvent.getAd(); 1486 if (this.currentAd.isLinear()) { 1487 this.adTrackingTimer = setInterval(this.onAdPlayheadTrackerInterval.bind(this), 250); 1488 this.controller.onLinearAdStart(); 1489 } else { 1490 this.controller.onNonLinearAdStart(); 1491 } 1492}; 1493 1494/** 1495 * Handles an ad click. Puts the player UI in a paused state. 1496 */ 1497SdkImpl.prototype.onAdPaused = function () { 1498 this.controller.onAdsPaused(); 1499}; 1500 1501/** 1502 * Syncs controls when an ad resumes. 1503 * @param {google.ima.AdEvent} adEvent The AdEvent thrown by the AdsManager. 1504 */ 1505SdkImpl.prototype.onAdResumed = function (adEvent) { 1506 this.controller.onAdsResumed(); 1507}; 1508 1509/** 1510 * Clears the interval timer for current ad time when an ad completes. 1511 */ 1512SdkImpl.prototype.onAdComplete = function () { 1513 if (this.currentAd.isLinear()) { 1514 clearInterval(this.adTrackingTimer); 1515 } 1516}; 1517 1518/** 1519 * Gets the current time and duration of the ad and calls the method to 1520 * update the ad UI. 1521 */ 1522SdkImpl.prototype.onAdPlayheadTrackerInterval = function () { 1523 var remainingTime = this.adsManager.getRemainingTime(); 1524 var duration = this.currentAd.getDuration(); 1525 var currentTime = duration - remainingTime; 1526 currentTime = currentTime > 0 ? currentTime : 0; 1527 var totalAds = 0; 1528 var adPosition = void 0; 1529 if (this.currentAd.getAdPodInfo()) { 1530 adPosition = this.currentAd.getAdPodInfo().getAdPosition(); 1531 totalAds = this.currentAd.getAdPodInfo().getTotalAds(); 1532 } 1533 1534 this.controller.onAdPlayheadUpdated(currentTime, remainingTime, duration, adPosition, totalAds); 1535}; 1536 1537/** 1538 * Called by the player wrapper when content completes. 1539 */ 1540SdkImpl.prototype.onContentComplete = function () { 1541 if (this.adsLoader) { 1542 this.adsLoader.contentComplete(); 1543 this.contentCompleteCalled = true; 1544 } 1545 1546 if (this.adsManager && this.adsManager.getCuePoints() && !this.adsManager.getCuePoints().includes(-1)) { 1547 this.controller.onNoPostroll(); 1548 } 1549 1550 if (this.allAdsCompleted) { 1551 this.controller.onContentAndAdsCompleted(); 1552 } 1553}; 1554 1555/** 1556 * Called when the player is disposed. 1557 */ 1558SdkImpl.prototype.onPlayerDisposed = function () { 1559 if (this.adTrackingTimer) { 1560 clearInterval(this.adTrackingTimer); 1561 } 1562 if (this.adsManager) { 1563 this.adsManager.destroy(); 1564 this.adsManager = null; 1565 } 1566}; 1567 1568SdkImpl.prototype.onPlayerReadyForPreroll = function () { 1569 if (this.autoPlayAdBreaks) { 1570 this.initAdsManager(); 1571 try { 1572 this.controller.showAdContainer(); 1573 // Sync ad volume with content volume. 1574 this.adsManager.setVolume(this.controller.getPlayerVolume()); 1575 this.adsManager.start(); 1576 } catch (adError) { 1577 this.onAdError(adError); 1578 } 1579 } 1580}; 1581 1582SdkImpl.prototype.onPlayerReady = function () { 1583 this.initAdObjects(); 1584 1585 if (this.controller.getSettings().adTagUrl || this.controller.getSettings().adsResponse) { 1586 this.requestAds(); 1587 } 1588}; 1589 1590SdkImpl.prototype.onPlayerEnterFullscreen = function () { 1591 if (this.adsManager) { 1592 this.adsManager.resize(window.screen.width, window.screen.height, google.ima.ViewMode.FULLSCREEN); 1593 } 1594}; 1595 1596SdkImpl.prototype.onPlayerExitFullscreen = function () { 1597 if (this.adsManager) { 1598 this.adsManager.resize(this.controller.getPlayerWidth(), this.controller.getPlayerHeight(), google.ima.ViewMode.NORMAL); 1599 } 1600}; 1601 1602/** 1603 * Called when the player volume changes. 1604 * 1605 * @param {number} volume The new player volume. 1606 */ 1607SdkImpl.prototype.onPlayerVolumeChanged = function (volume) { 1608 if (this.adsManager) { 1609 this.adsManager.setVolume(volume); 1610 } 1611 1612 if (volume == 0) { 1613 this.adMuted = true; 1614 } else { 1615 this.adMuted = false; 1616 } 1617}; 1618 1619/** 1620 * Called when the player wrapper detects that the player has been resized. 1621 * 1622 * @param {number} width The post-resize width of the player. 1623 * @param {number} height The post-resize height of the player. 1624 */ 1625SdkImpl.prototype.onPlayerResize = function (width, height) { 1626 if (this.adsManager) { 1627 this.adsManagerDimensions.width = width; 1628 this.adsManagerDimensions.height = height; 1629 /* global google */ 1630 /* eslint no-undef: 'error' */ 1631 this.adsManager.resize(width, height, google.ima.ViewMode.NORMAL); 1632 } 1633}; 1634 1635/** 1636 * @return {Object} The current ad. 1637 */ 1638SdkImpl.prototype.getCurrentAd = function () { 1639 return this.currentAd; 1640}; 1641 1642/** 1643 * Listener JSDoc for ESLint. This listener can be passed to 1644 * setAdBreakReadyListener. 1645 * @callback listener 1646 */ 1647 1648/** 1649 * Sets the listener to be called to trigger manual ad break playback. 1650 * @param {listener} listener The listener to be called to trigger manual ad 1651 * break playback. 1652 */ 1653SdkImpl.prototype.setAdBreakReadyListener = function (listener) { 1654 this.adBreakReadyListener = listener; 1655}; 1656 1657/** 1658 * @return {boolean} True if an ad is currently playing. False otherwise. 1659 */ 1660SdkImpl.prototype.isAdPlaying = function () { 1661 return this.adPlaying; 1662}; 1663 1664/** 1665 * @return {boolean} True if an ad is currently playing. False otherwise. 1666 */ 1667SdkImpl.prototype.isAdMuted = function () { 1668 return this.adMuted; 1669}; 1670 1671/** 1672 * Pause ads. 1673 */ 1674SdkImpl.prototype.pauseAds = function () { 1675 this.adsManager.pause(); 1676 this.adPlaying = false; 1677}; 1678 1679/** 1680 * Resume ads. 1681 */ 1682SdkImpl.prototype.resumeAds = function () { 1683 this.adsManager.resume(); 1684 this.adPlaying = true; 1685}; 1686 1687/** 1688 * Unmute ads. 1689 */ 1690SdkImpl.prototype.unmute = function () { 1691 this.adsManager.setVolume(1); 1692 this.adMuted = false; 1693}; 1694 1695/** 1696 * Mute ads. 1697 */ 1698SdkImpl.prototype.mute = function () { 1699 this.adsManager.setVolume(0); 1700 this.adMuted = true; 1701}; 1702 1703/** 1704 * Set the volume of the ads. 0-1. 1705 * 1706 * @param {number} volume The new volume. 1707 */ 1708SdkImpl.prototype.setVolume = function (volume) { 1709 this.adsManager.setVolume(volume); 1710 if (volume == 0) { 1711 this.adMuted = true; 1712 } else { 1713 this.adMuted = false; 1714 } 1715}; 1716 1717/** 1718 * Initializes the AdDisplayContainer. On mobile, this must be done as a 1719 * result of user action. 1720 */ 1721SdkImpl.prototype.initializeAdDisplayContainer = function () { 1722 if (this.adDisplayContainer) { 1723 this.adDisplayContainerInitialized = true; 1724 this.adDisplayContainer.initialize(); 1725 } 1726}; 1727 1728/** 1729 * Called by publishers in manual ad break playback mode to start an ad 1730 * break. 1731 */ 1732SdkImpl.prototype.playAdBreak = function () { 1733 if (!this.autoPlayAdBreaks) { 1734 this.controller.showAdContainer(); 1735 // Sync ad volume with content volume. 1736 this.adsManager.setVolume(this.controller.getPlayerVolume()); 1737 this.adsManager.start(); 1738 } 1739}; 1740 1741/** 1742 * Callback JSDoc for ESLint. This callback can be passed to addEventListener. 1743 * @callback callback 1744 */ 1745 1746/** 1747 * Ads an EventListener to the AdsManager. For a list of available events, 1748 * see 1749 * https://developers.google.com/interactive-media-ads/docs/sdks/html5/v3/apis#ima.AdEvent.Type 1750 * @param {google.ima.AdEvent.Type} event The AdEvent.Type for which to 1751 * listen. 1752 * @param {callback} callback The method to call when the event is fired. 1753 */ 1754SdkImpl.prototype.addEventListener = function (event, callback) { 1755 if (this.adsManager) { 1756 this.adsManager.addEventListener(event, callback); 1757 } 1758}; 1759 1760/** 1761 * Returns the instance of the AdsManager. 1762 * @return {google.ima.AdsManager} The AdsManager being used by the plugin. 1763 */ 1764SdkImpl.prototype.getAdsManager = function () { 1765 return this.adsManager; 1766}; 1767 1768/** 1769 * Reset the SDK implementation. 1770 */ 1771SdkImpl.prototype.reset = function () { 1772 this.adsActive = false; 1773 this.adPlaying = false; 1774 if (this.adTrackingTimer) { 1775 // If this is called while an ad is playing, stop trying to get that 1776 // ad's current time. 1777 clearInterval(this.adTrackingTimer); 1778 } 1779 if (this.adsManager) { 1780 this.adsManager.destroy(); 1781 this.adsManager = null; 1782 } 1783 if (this.adsLoader && !this.contentCompleteCalled) { 1784 this.adsLoader.contentComplete(); 1785 } 1786 this.contentCompleteCalled = false; 1787 this.allAdsCompleted = false; 1788}; 1789 1790/** 1791 * Copyright 2017 Google Inc. 1792 * 1793 * Licensed under the Apache License, Version 2.0 (the "License"); 1794 * you may not use this file except in compliance with the License. 1795 * You may obtain a copy of the License at 1796 * 1797 * http://www.apache.org/licenses/LICENSE-2.0 1798 * 1799 * Unless required by applicable law or agreed to in writing, software 1800 * distributed under the License is distributed on an "AS IS" BASIS, 1801 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1802 * See the License for the specific language governing permissions and 1803 * limitations under the License. 1804 * 1805 * IMA SDK integration plugin for Video.js. For more information see 1806 * https://www.github.com/googleads/videojs-ima 1807 */ 1808/** 1809 * The grand coordinator of the plugin. Facilitates communication between all 1810 * other plugin classes. 1811 * 1812 * @param {Object} player Instance of the video.js player. 1813 * @param {Object} options Options provided by the implementation. 1814 * @constructor 1815 * @struct 1816 * @final 1817 */ 1818var Controller = function Controller(player, options) { 1819 /** 1820 * Stores user-provided settings. 1821 * @type {Object} 1822 */ 1823 this.settings = {}; 1824 1825 /** 1826 * Content and ads ended listeners passed by the publisher to the plugin. 1827 * These will be called when the plugin detects that content *and all 1828 * ads* have completed. This differs from the contentEndedListeners in that 1829 * contentEndedListeners will fire between content ending and a post-roll 1830 * playing, whereas the contentAndAdsEndedListeners will fire after the 1831 * post-roll completes. 1832 */ 1833 this.contentAndAdsEndedListeners = []; 1834 1835 /** 1836 * Whether or not we are running on a mobile platform. 1837 */ 1838 this.isMobile = navigator.userAgent.match(/iPhone/i) || navigator.userAgent.match(/iPad/i) || navigator.userAgent.match(/Android/i); 1839 1840 /** 1841 * Whether or not we are running on an iOS platform. 1842 */ 1843 this.isIos = navigator.userAgent.match(/iPhone/i) || navigator.userAgent.match(/iPad/i); 1844 1845 this.initWithSettings(options); 1846 1847 /** 1848 * Stores contrib-ads default settings. 1849 */ 1850 var contribAdsDefaults = { 1851 debug: this.settings.debug, 1852 timeout: this.settings.timeout, 1853 prerollTimeout: this.settings.prerollTimeout 1854 }; 1855 var adsPluginSettings = this.extend({}, contribAdsDefaults, options.contribAdsSettings || {}); 1856 1857 this.playerWrapper = new PlayerWrapper(player, adsPluginSettings, this); 1858 this.adUi = new AdUi(this); 1859 this.sdkImpl = new SdkImpl(this); 1860}; 1861 1862Controller.IMA_DEFAULTS = { 1863 debug: false, 1864 timeout: 5000, 1865 prerollTimeout: 1000, 1866 adLabel: 'Advertisement', 1867 adLabelNofN: 'of', 1868 showControlsForJSAds: true 1869}; 1870 1871/** 1872 * Extends the settings to include user-provided settings. 1873 * 1874 * @param {Object} options Options to be used in initialization. 1875 */ 1876Controller.prototype.initWithSettings = function (options) { 1877 this.settings = this.extend({}, Controller.IMA_DEFAULTS, options || {}); 1878 1879 this.warnAboutDeprecatedSettings(); 1880 1881 // Default showing countdown timer to true. 1882 this.showCountdown = true; 1883 if (this.settings.showCountdown === false) { 1884 this.showCountdown = false; 1885 } 1886}; 1887 1888/** 1889 * Logs console warnings when deprecated settings are used. 1890 */ 1891Controller.prototype.warnAboutDeprecatedSettings = function () { 1892 var _this = this; 1893 1894 var deprecatedSettings = ['adWillAutoplay', 'adsWillAutoplay', 'adWillPlayMuted', 'adsWillPlayMuted']; 1895 deprecatedSettings.forEach(function (setting) { 1896 if (_this.settings[setting] !== undefined) { 1897 console.warn('WARNING: videojs.ima setting ' + setting + ' is deprecated'); 1898 } 1899 }); 1900}; 1901 1902/** 1903 * Return the settings object. 1904 * 1905 * @return {Object} The settings object. 1906 */ 1907Controller.prototype.getSettings = function () { 1908 return this.settings; 1909}; 1910 1911/** 1912 * Return whether or not we're in a mobile environment. 1913 * 1914 * @return {boolean} True if running on mobile, false otherwise. 1915 */ 1916Controller.prototype.getIsMobile = function () { 1917 return this.isMobile; 1918}; 1919 1920/** 1921 * Return whether or not we're in an iOS environment. 1922 * 1923 * @return {boolean} True if running on iOS, false otherwise. 1924 */ 1925Controller.prototype.getIsIos = function () { 1926 return this.isIos; 1927}; 1928 1929/** 1930 * Inject the ad container div into the DOM. 1931 * 1932 * @param{HTMLElement} adContainerDiv The ad container div. 1933 */ 1934Controller.prototype.injectAdContainerDiv = function (adContainerDiv) { 1935 this.playerWrapper.injectAdContainerDiv(adContainerDiv); 1936}; 1937 1938/** 1939 * @return {HTMLElement} The div for the ad container. 1940 */ 1941Controller.prototype.getAdContainerDiv = function () { 1942 return this.adUi.getAdContainerDiv(); 1943}; 1944 1945/** 1946 * @return {Object} The content player. 1947 */ 1948Controller.prototype.getContentPlayer = function () { 1949 return this.playerWrapper.getContentPlayer(); 1950}; 1951 1952/** 1953 * Returns the content playhead tracker. 1954 * 1955 * @return {Object} The content playhead tracker. 1956 */ 1957Controller.prototype.getContentPlayheadTracker = function () { 1958 return this.playerWrapper.getContentPlayheadTracker(); 1959}; 1960 1961/** 1962 * Requests ads. 1963 */ 1964Controller.prototype.requestAds = function () { 1965 this.sdkImpl.requestAds(); 1966}; 1967 1968/** 1969 * Add or modify a setting. 1970 * 1971 * @param {string} key Key to modify 1972 * @param {Object} value Value to set at key. 1973 */ 1974Controller.prototype.setSetting = function (key, value) { 1975 this.settings[key] = value; 1976}; 1977 1978/** 1979 * Called when there is an error loading ads. 1980 * 1981 * @param {Object} adErrorEvent The ad error event thrown by the IMA SDK. 1982 */ 1983Controller.prototype.onErrorLoadingAds = function (adErrorEvent) { 1984 this.adUi.onAdError(); 1985 this.playerWrapper.onAdError(adErrorEvent); 1986}; 1987 1988/** 1989 * Called by the ad UI when the play/pause button is clicked. 1990 */ 1991Controller.prototype.onAdPlayPauseClick = function () { 1992 if (this.sdkImpl.isAdPlaying()) { 1993 this.adUi.onAdsPaused(); 1994 this.sdkImpl.pauseAds(); 1995 } else { 1996 this.adUi.onAdsPlaying(); 1997 this.sdkImpl.resumeAds(); 1998 } 1999}; 2000 2001/** 2002 * Called by the ad UI when the mute button is clicked. 2003 * 2004 */ 2005Controller.prototype.onAdMuteClick = function () { 2006 if (this.sdkImpl.isAdMuted()) { 2007 this.playerWrapper.unmute(); 2008 this.adUi.unmute(); 2009 this.sdkImpl.unmute(); 2010 } else { 2011 this.playerWrapper.mute(); 2012 this.adUi.mute(); 2013 this.sdkImpl.mute(); 2014 } 2015}; 2016 2017/** 2018 * Set the volume of the player and ads. 0-1. 2019 * 2020 * @param {number} volume The new volume. 2021 */ 2022Controller.prototype.setVolume = function (volume) { 2023 this.playerWrapper.setVolume(volume); 2024 this.sdkImpl.setVolume(volume); 2025}; 2026 2027/** 2028 * @return {number} The volume of the content player. 2029 */ 2030Controller.prototype.getPlayerVolume = function () { 2031 return this.playerWrapper.getVolume(); 2032}; 2033 2034/** 2035 * Toggle fullscreen state. 2036 */ 2037Controller.prototype.toggleFullscreen = function () { 2038 this.playerWrapper.toggleFullscreen(); 2039}; 2040 2041/** 2042 * Relays ad errors to the player wrapper. 2043 * 2044 * @param {Object} adErrorEvent The ad error event thrown by the IMA SDK. 2045 */ 2046Controller.prototype.onAdError = function (adErrorEvent) { 2047 this.adUi.onAdError(); 2048 this.playerWrapper.onAdError(adErrorEvent); 2049}; 2050 2051/** 2052 * Handles ad break starting. 2053 * 2054 * @param {Object} adEvent The event fired by the IMA SDK. 2055 */ 2056Controller.prototype.onAdBreakStart = function (adEvent) { 2057 this.playerWrapper.onAdBreakStart(); 2058 this.adUi.onAdBreakStart(adEvent); 2059}; 2060 2061/** 2062 * Show the ad container. 2063 */ 2064Controller.prototype.showAdContainer = function () { 2065 this.adUi.showAdContainer(); 2066}; 2067 2068/** 2069 * Handles ad break ending. 2070 */ 2071Controller.prototype.onAdBreakEnd = function () { 2072 this.playerWrapper.onAdBreakEnd(); 2073 this.adUi.onAdBreakEnd(); 2074}; 2075 2076/** 2077 * Handles when all ads have finished playing. 2078 */ 2079Controller.prototype.onAllAdsCompleted = function () { 2080 this.adUi.onAllAdsCompleted(); 2081 this.playerWrapper.onAllAdsCompleted(); 2082}; 2083 2084/** 2085 * Handles the SDK firing an ad paused event. 2086 */ 2087Controller.prototype.onAdsPaused = function () { 2088 this.adUi.onAdsPaused(); 2089}; 2090 2091/** 2092 * Handles the SDK firing an ad resumed event. 2093 */ 2094Controller.prototype.onAdsResumed = function () { 2095 this.adUi.onAdsResumed(); 2096}; 2097 2098/** 2099 * Takes data from the sdk impl and passes it to the ad UI to update the UI. 2100 * 2101 * @param {number} currentTime Current time of the ad. 2102 * @param {number} remainingTime Remaining time of the ad. 2103 * @param {number} duration Duration of the ad. 2104 * @param {number} adPosition Index of the ad in the pod. 2105 * @param {number} totalAds Total number of ads in the pod. 2106 */ 2107Controller.prototype.onAdPlayheadUpdated = function (currentTime, remainingTime, duration, adPosition, totalAds) { 2108 this.adUi.updateAdUi(currentTime, remainingTime, duration, adPosition, totalAds); 2109}; 2110 2111/** 2112 * @return {Object} The current ad. 2113 */ 2114Controller.prototype.getCurrentAd = function () { 2115 return this.sdkImpl.getCurrentAd(); 2116}; 2117 2118/** 2119 * Play content. 2120 */ 2121Controller.prototype.playContent = function () { 2122 this.playerWrapper.play(); 2123}; 2124 2125/** 2126 * Handles when a linear ad starts. 2127 */ 2128Controller.prototype.onLinearAdStart = function () { 2129 this.adUi.onLinearAdStart(); 2130 this.playerWrapper.onAdStart(); 2131}; 2132 2133/** 2134 * Handles when a non-linear ad loads. 2135 */ 2136Controller.prototype.onNonLinearAdLoad = function () { 2137 this.adUi.onNonLinearAdLoad(); 2138}; 2139 2140/** 2141 * Handles when a non-linear ad starts. 2142 */ 2143Controller.prototype.onNonLinearAdStart = function () { 2144 this.adUi.onNonLinearAdLoad(); 2145 this.playerWrapper.onAdStart(); 2146}; 2147 2148/** 2149 * Get the player width. 2150 * 2151 * @return {number} The width of the player. 2152 */ 2153Controller.prototype.getPlayerWidth = function () { 2154 return this.playerWrapper.getPlayerWidth(); 2155}; 2156 2157/** 2158 * Get the player height. 2159 * 2160 * @return {number} The height of the player. 2161 */ 2162Controller.prototype.getPlayerHeight = function () { 2163 return this.playerWrapper.getPlayerHeight(); 2164}; 2165 2166/** 2167 * Tells the player wrapper that ads are ready. 2168 */ 2169Controller.prototype.onAdsReady = function () { 2170 this.playerWrapper.onAdsReady(); 2171}; 2172 2173/** 2174 * Called when the player wrapper detects that the player has been resized. 2175 * 2176 * @param {number} width The post-resize width of the player. 2177 * @param {number} height The post-resize height of the player. 2178 */ 2179Controller.prototype.onPlayerResize = function (width, height) { 2180 this.sdkImpl.onPlayerResize(width, height); 2181}; 2182 2183/** 2184 * Called by the player wrapper when content completes. 2185 */ 2186Controller.prototype.onContentComplete = function () { 2187 this.sdkImpl.onContentComplete(); 2188}; 2189 2190/** 2191 * Called by the player wrapper when it's time to play a post-roll but we don't 2192 * have one to play. 2193 */ 2194Controller.prototype.onNoPostroll = function () { 2195 this.playerWrapper.onNoPostroll(); 2196}; 2197 2198/** 2199 * Called when content and all ads have completed. 2200 */ 2201Controller.prototype.onContentAndAdsCompleted = function () { 2202 for (var index in this.contentAndAdsEndedListeners) { 2203 if (typeof this.contentAndAdsEndedListeners[index] === 'function') { 2204 this.contentAndAdsEndedListeners[index](); 2205 } 2206 } 2207}; 2208 2209/** 2210 * Called when the player is disposed. 2211 */ 2212Controller.prototype.onPlayerDisposed = function () { 2213 this.contentAndAdsEndedListeners = []; 2214 this.sdkImpl.onPlayerDisposed(); 2215}; 2216 2217/** 2218 * Called when the player is ready to play a pre-roll. 2219 */ 2220Controller.prototype.onPlayerReadyForPreroll = function () { 2221 this.sdkImpl.onPlayerReadyForPreroll(); 2222}; 2223 2224/** 2225 * Called when the player is ready. 2226 */ 2227Controller.prototype.onPlayerReady = function () { 2228 this.sdkImpl.onPlayerReady(); 2229}; 2230 2231/** 2232 * Called when the player enters fullscreen. 2233 */ 2234Controller.prototype.onPlayerEnterFullscreen = function () { 2235 this.adUi.onPlayerEnterFullscreen(); 2236 this.sdkImpl.onPlayerEnterFullscreen(); 2237}; 2238 2239/** 2240 * Called when the player exits fullscreen. 2241 */ 2242Controller.prototype.onPlayerExitFullscreen = function () { 2243 this.adUi.onPlayerExitFullscreen(); 2244 this.sdkImpl.onPlayerExitFullscreen(); 2245}; 2246 2247/** 2248 * Called when the player volume changes. 2249 * 2250 * @param {number} volume The new player volume. 2251 */ 2252Controller.prototype.onPlayerVolumeChanged = function (volume) { 2253 this.adUi.onPlayerVolumeChanged(volume); 2254 this.sdkImpl.onPlayerVolumeChanged(volume); 2255}; 2256 2257/** 2258 * Sets the content of the video player. You should use this method instead 2259 * of setting the content src directly to ensure the proper ad tag is 2260 * requested when the video content is loaded. 2261 * @param {?string} contentSrc The URI for the content to be played. Leave 2262 * blank to use the existing content. 2263 * @param {?string} adTag The ad tag to be requested when the content loads. 2264 * Leave blank to use the existing ad tag. 2265 * @param {?boolean} playOnLoad True to play the content once it has loaded, 2266 * false to only load the content but not start playback. 2267 */ 2268Controller.prototype.setContentWithAdTag = function (contentSrc, adTag, playOnLoad) { 2269 this.reset(); 2270 this.settings.adTagUrl = adTag ? adTag : this.settings.adTagUrl; 2271 this.playerWrapper.changeSource(contentSrc, playOnLoad); 2272}; 2273 2274/** 2275 * Sets the content of the video player. You should use this method instead 2276 * of setting the content src directly to ensure the proper ads response is 2277 * used when the video content is loaded. 2278 * @param {?string} contentSrc The URI for the content to be played. Leave 2279 * blank to use the existing content. 2280 * @param {?string} adsResponse The ads response to be requested when the 2281 * content loads. Leave blank to use the existing ads response. 2282 * @param {?boolean} playOnLoad True to play the content once it has loaded, 2283 * false to only load the content but not start playback. 2284 */ 2285Controller.prototype.setContentWithAdsResponse = function (contentSrc, adsResponse, playOnLoad) { 2286 this.reset(); 2287 this.settings.adsResponse = adsResponse ? adsResponse : this.settings.adsResponse; 2288 this.playerWrapper.changeSource(contentSrc, playOnLoad); 2289}; 2290 2291/** 2292 * Resets the state of the plugin. 2293 */ 2294Controller.prototype.reset = function () { 2295 this.sdkImpl.reset(); 2296 this.playerWrapper.reset(); 2297}; 2298 2299/** 2300 * Listener JSDoc for ESLint. This listener can be passed to 2301 * (add|remove)ContentEndedListener. 2302 * @callback listener 2303 */ 2304 2305/** 2306 * Adds a listener for the 'contentended' event of the video player. This should 2307 * be used instead of setting an 'contentended' listener directly to ensure that 2308 * the ima can do proper cleanup of the SDK before other event listeners are 2309 * called. 2310 * @param {listener} listener The listener to be called when content 2311 * completes. 2312 */ 2313Controller.prototype.addContentEndedListener = function (listener) { 2314 this.playerWrapper.addContentEndedListener(listener); 2315}; 2316 2317/** 2318 * Adds a listener that will be called when content and all ads have 2319 * finished playing. 2320 * @param {listener} listener The listener to be called when content and ads 2321 * complete. 2322 */ 2323Controller.prototype.addContentAndAdsEndedListener = function (listener) { 2324 this.contentAndAdsEndedListeners.push(listener); 2325}; 2326 2327/** 2328 * Sets the listener to be called to trigger manual ad break playback. 2329 * @param {listener} listener The listener to be called to trigger manual ad 2330 * break playback. 2331 */ 2332Controller.prototype.setAdBreakReadyListener = function (listener) { 2333 this.sdkImpl.setAdBreakReadyListener(listener); 2334}; 2335 2336/** 2337 * Changes the flag to show or hide the ad countdown timer. 2338 * 2339 * @param {boolean} showCountdownIn Show or hide the countdown timer. 2340 */ 2341Controller.prototype.setShowCountdown = function (showCountdownIn) { 2342 this.adUi.setShowCountdown(showCountdownIn); 2343 this.showCountdown = showCountdownIn; 2344 this.countdownDiv.style.display = this.showCountdown ? 'block' : 'none'; 2345}; 2346 2347/** 2348 * Initializes the AdDisplayContainer. On mobile, this must be done as a 2349 * result of user action. 2350 */ 2351Controller.prototype.initializeAdDisplayContainer = function () { 2352 this.sdkImpl.initializeAdDisplayContainer(); 2353}; 2354 2355/** 2356 * Called by publishers in manual ad break playback mode to start an ad 2357 * break. 2358 */ 2359Controller.prototype.playAdBreak = function () { 2360 this.sdkImpl.playAdBreak(); 2361}; 2362 2363/** 2364 * Callback JSDoc for ESLint. This callback can be passed to addEventListener. 2365 * @callback callback 2366 */ 2367 2368/** 2369 * Ads an EventListener to the AdsManager. For a list of available events, 2370 * see 2371 * https://developers.google.com/interactive-media-ads/docs/sdks/html5/v3/apis#ima.AdEvent.Type 2372 * @param {google.ima.AdEvent.Type} event The AdEvent.Type for which to 2373 * listen. 2374 * @param {callback} callback The method to call when the event is fired. 2375 */ 2376Controller.prototype.addEventListener = function (event, callback) { 2377 this.sdkImpl.addEventListener(event, callback); 2378}; 2379 2380/** 2381 * Returns the instance of the AdsManager. 2382 * @return {google.ima.AdsManager} The AdsManager being used by the plugin. 2383 */ 2384Controller.prototype.getAdsManager = function () { 2385 return this.sdkImpl.getAdsManager(); 2386}; 2387 2388/** 2389 * Returns the instance of the player id. 2390 * @return {string} The player id. 2391 */ 2392Controller.prototype.getPlayerId = function () { 2393 return this.playerWrapper.getPlayerId(); 2394}; 2395 2396/** 2397 * Changes the ad tag. You will need to call requestAds after this method 2398 * for the new ads to be requested. 2399 * @param {?string} adTag The ad tag to be requested the next time 2400 * requestAds is called. 2401 */ 2402Controller.prototype.changeAdTag = function (adTag) { 2403 this.reset(); 2404 this.settings.adTagUrl = adTag; 2405}; 2406 2407/** 2408 * Pauses the ad. 2409 */ 2410Controller.prototype.pauseAd = function () { 2411 this.adUi.onAdsPaused(); 2412 this.sdkImpl.pauseAds(); 2413}; 2414 2415/** 2416 * Resumes the ad. 2417 */ 2418Controller.prototype.resumeAd = function () { 2419 this.adUi.onAdsPlaying(); 2420 this.sdkImpl.resumeAds(); 2421}; 2422 2423/** 2424 * @return {boolean} true if we expect that ads will autoplay. false otherwise. 2425 */ 2426Controller.prototype.adsWillAutoplay = function () { 2427 if (this.settings.adsWillAutoplay !== undefined) { 2428 return this.settings.adsWillAutoplay; 2429 } else if (this.settings.adWillAutoplay !== undefined) { 2430 return this.settings.adWillAutoplay; 2431 } else { 2432 return !!this.playerWrapper.getPlayerOptions().autoplay; 2433 } 2434}; 2435 2436/** 2437 * @return {boolean} true if we expect that ads will autoplay. false otherwise. 2438 */ 2439Controller.prototype.adsWillPlayMuted = function () { 2440 if (this.settings.adsWillPlayMuted !== undefined) { 2441 return this.settings.adsWillPlayMuted; 2442 } else if (this.settings.adWillPlayMuted !== undefined) { 2443 return this.settings.adWillPlayMuted; 2444 } else if (this.playerWrapper.getPlayerOptions().muted !== undefined) { 2445 return this.playerWrapper.getPlayerOptions().muted; 2446 } else { 2447 return this.playerWrapper.getVolume() == 0; 2448 } 2449}; 2450 2451/** 2452 * Triggers an event on the VJS player 2453 * @param {string} name The event name. 2454 * @param {Object} data The event data. 2455 */ 2456Controller.prototype.triggerPlayerEvent = function (name, data) { 2457 this.playerWrapper.triggerPlayerEvent(name, data); 2458}; 2459 2460/** 2461 * Extends an object to include the contents of objects at parameters 2 onward. 2462 * 2463 * @param {Object} obj The object onto which the subsequent objects' parameters 2464 * will be extended. This object will be modified. 2465 * @param {...Object} var_args The objects whose properties are to be extended 2466 * onto obj. 2467 * @return {Object} The extended object. 2468 */ 2469Controller.prototype.extend = function (obj) { 2470 var arg = void 0; 2471 var index = void 0; 2472 var key = void 0; 2473 2474 for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { 2475 args[_key - 1] = arguments[_key]; 2476 } 2477 2478 for (index = 0; index < args.length; index++) { 2479 arg = args[index]; 2480 for (key in arg) { 2481 if (arg.hasOwnProperty(key)) { 2482 obj[key] = arg[key]; 2483 } 2484 } 2485 } 2486 return obj; 2487}; 2488 2489/** 2490 * Copyright 2017 Google Inc. 2491 * 2492 * Licensed under the Apache License, Version 2.0 (the "License"); 2493 * you may not use this file except in compliance with the License. 2494 * You may obtain a copy of the License at 2495 * 2496 * http://www.apache.org/licenses/LICENSE-2.0 2497 * 2498 * Unless required by applicable law or agreed to in writing, software 2499 * distributed under the License is distributed on an "AS IS" BASIS, 2500 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2501 * See the License for the specific language governing permissions and 2502 * limitations under the License. 2503 * 2504 * IMA SDK integration plugin for Video.js. For more information see 2505 * https://www.github.com/googleads/videojs-ima 2506 */ 2507 2508/** 2509 * Exposes the ImaPlugin to a publisher implementation. 2510 * 2511 * @param {Object} player Instance of the video.js player to which this plugin 2512 * will be added. 2513 * @param {Object} options Options provided by the implementation. 2514 * @constructor 2515 * @struct 2516 * @final 2517 */ 2518var ImaPlugin = function ImaPlugin(player, options) { 2519 this.controller = new Controller(player, options); 2520 2521 /** 2522 * Listener JSDoc for ESLint. This listener can be passed to 2523 * addContent(AndAds)EndedListener. 2524 * @callback listener 2525 */ 2526 2527 /** 2528 * Adds a listener that will be called when content and all ads have 2529 * finished playing. 2530 * @param {listener} listener The listener to be called when content and ads 2531 * complete. 2532 */ 2533 this.addContentAndAdsEndedListener = function (listener) { 2534 this.controller.addContentAndAdsEndedListener(listener); 2535 }.bind(this); 2536 2537 /** 2538 * Adds a listener for the 'contentended' event of the video player. This 2539 * should be used instead of setting an 'contentended' listener directly to 2540 * ensure that the ima can do proper cleanup of the SDK before other event 2541 * listeners are called. 2542 * @param {listener} listener The listener to be called when content 2543 * completes. 2544 */ 2545 this.addContentEndedListener = function (listener) { 2546 this.controller.addContentEndedListener(listener); 2547 }.bind(this); 2548 2549 /** 2550 * Callback JSDoc for ESLint. This callback can be passed to addEventListener. 2551 * @callback callback 2552 */ 2553 2554 /** 2555 * Ads an EventListener to the AdsManager. For a list of available events, 2556 * see 2557 * https://developers.google.com/interactive-media-ads/docs/sdks/html5/v3/apis#ima.AdEvent.Type 2558 * @param {google.ima.AdEvent.Type} event The AdEvent.Type for which to 2559 * listen. 2560 * @param {callback} callback The method to call when the event is fired. 2561 */ 2562 this.addEventListener = function (event, callback) { 2563 this.controller.addEventListener(event, callback); 2564 }.bind(this); 2565 2566 /** 2567 * Changes the ad tag. You will need to call requestAds after this method 2568 * for the new ads to be requested. 2569 * @param {?string} adTag The ad tag to be requested the next time requestAds 2570 * is called. 2571 */ 2572 this.changeAdTag = function (adTag) { 2573 this.controller.changeAdTag(adTag); 2574 }.bind(this); 2575 2576 /** 2577 * Returns the instance of the AdsManager. 2578 * @return {google.ima.AdsManager} The AdsManager being used by the plugin. 2579 */ 2580 this.getAdsManager = function () { 2581 return this.controller.getAdsManager(); 2582 }.bind(this); 2583 2584 /** 2585 * Initializes the AdDisplayContainer. On mobile, this must be done as a 2586 * result of user action. 2587 */ 2588 this.initializeAdDisplayContainer = function () { 2589 this.controller.initializeAdDisplayContainer(); 2590 }.bind(this); 2591 2592 /** 2593 * Pauses the ad. 2594 */ 2595 this.pauseAd = function () { 2596 this.controller.pauseAd(); 2597 }.bind(this); 2598 2599 /** 2600 * Called by publishers in manual ad break playback mode to start an ad 2601 * break. 2602 */ 2603 this.playAdBreak = function () { 2604 this.controller.playAdBreak(); 2605 }.bind(this); 2606 2607 /** 2608 * Creates the AdsRequest and request ads through the AdsLoader. 2609 */ 2610 this.requestAds = function () { 2611 this.controller.requestAds(); 2612 }.bind(this); 2613 2614 /** 2615 * Resumes the ad. 2616 */ 2617 this.resumeAd = function () { 2618 this.controller.resumeAd(); 2619 }.bind(this); 2620 2621 /** 2622 * Sets the listener to be called to trigger manual ad break playback. 2623 * @param {listener} listener The listener to be called to trigger manual ad 2624 * break playback. 2625 */ 2626 this.setAdBreakReadyListener = function (listener) { 2627 this.controller.setAdBreakReadyListener(listener); 2628 }.bind(this); 2629 2630 /** 2631 * Sets the content of the video player. You should use this method instead 2632 * of setting the content src directly to ensure the proper ad tag is 2633 * requested when the video content is loaded. 2634 * @param {?string} contentSrc The URI for the content to be played. Leave 2635 * blank to use the existing content. 2636 * @param {?string} adTag The ad tag to be requested when the content loads. 2637 * Leave blank to use the existing ad tag. 2638 * @param {?boolean} playOnLoad True to play the content once it has loaded, 2639 * false to only load the content but not start playback. 2640 */ 2641 this.setContentWithAdTag = function (contentSrc, adTag, playOnLoad) { 2642 this.controller.setContentWithAdTag(contentSrc, adTag, playOnLoad); 2643 }.bind(this); 2644 2645 /** 2646 * Sets the content of the video player. You should use this method instead 2647 * of setting the content src directly to ensure the proper ads response is 2648 * used when the video content is loaded. 2649 * @param {?string} contentSrc The URI for the content to be played. Leave 2650 * blank to use the existing content. 2651 * @param {?string} adsResponse The ads response to be requested when the 2652 * content loads. Leave blank to use the existing ads response. 2653 * @param {?boolean} playOnLoad True to play the content once it has loaded, 2654 * false to only load the content but not start playback. 2655 */ 2656 this.setContentWithAdsResponse = function (contentSrc, adsResponse, playOnLoad) { 2657 this.controller.setContentWithAdsResponse(contentSrc, adsResponse, playOnLoad); 2658 }.bind(this); 2659 2660 /** 2661 * Changes the flag to show or hide the ad countdown timer. 2662 * 2663 * @param {boolean} showCountdownIn Show or hide the countdown timer. 2664 */ 2665 this.setShowCountdown = function (showCountdownIn) { 2666 this.controller.setShowCountdown(showCountdownIn); 2667 }.bind(this); 2668}; 2669 2670var init = function init(options) { 2671 /* eslint no-invalid-this: 'off' */ 2672 this.ima = new ImaPlugin(this, options); 2673}; 2674 2675var registerPlugin = videojs.registerPlugin || videojs.plugin; 2676registerPlugin('ima', init); 2677 2678})));
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.