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.
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 google.ima.settings.setVpaidMode(google.ima.ImaSdkSettings.VpaidMode.INSECURE); 1266 1267 this.adsLoader.getSettings().setVpaidMode(google.ima.ImaSdkSettings.VpaidMode.INSECURE); 1268 1269 if (this.controller.getSettings().locale) { 1270 this.adsLoader.getSettings().setLocale(this.controller.getSettings().locale); 1271 } 1272 1273 if (this.controller.getSettings().numRedirects) { 1274 this.adsLoader.getSettings().setNumRedirects(this.controller.getSettings().numRedirects); 1275 } 1276 1277 this.adsLoader.getSettings().setPlayerType('videojs-ima'); 1278 this.adsLoader.getSettings().setPlayerVersion(pkg.version); 1279 this.adsLoader.getSettings().setAutoPlayAdBreaks(this.autoPlayAdBreaks); 1280 1281 this.adsLoader.addEventListener(google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED, this.onAdsManagerLoaded.bind(this), false); 1282 this.adsLoader.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR, this.onAdsLoaderError.bind(this), false); 1283}; 1284 1285/** 1286 * Creates the AdsRequest and request ads through the AdsLoader. 1287 */ 1288SdkImpl.prototype.requestAds = function () { 1289 var adsRequest = new google.ima.AdsRequest(); 1290 if (this.controller.getSettings().adTagUrl) { 1291 adsRequest.adTagUrl = this.controller.getSettings().adTagUrl; 1292 } else { 1293 adsRequest.adsResponse = this.controller.getSettings().adsResponse; 1294 } 1295 if (this.controller.getSettings().forceNonLinearFullSlot) { 1296 adsRequest.forceNonLinearFullSlot = true; 1297 } 1298 1299 if (this.controller.getSettings().vastLoadTimeout) { 1300 adsRequest.vastLoadTimeout = this.controller.getSettings().vastLoadTimeout; 1301 } 1302 1303 adsRequest.linearAdSlotWidth = this.controller.getPlayerWidth(); 1304 adsRequest.linearAdSlotHeight = this.controller.getPlayerHeight(); 1305 adsRequest.nonLinearAdSlotWidth = this.controller.getSettings().nonLinearWidth || this.controller.getPlayerWidth(); 1306 adsRequest.nonLinearAdSlotHeight = this.controller.getSettings().nonLinearHeight || this.controller.getPlayerHeight(); 1307 adsRequest.setAdWillAutoPlay(this.controller.adsWillAutoplay()); 1308 adsRequest.setAdWillPlayMuted(this.controller.adsWillPlayMuted()); 1309 1310 this.adsLoader.requestAds(adsRequest); 1311 this.controller.triggerPlayerEvent('ads-request', adsRequest); 1312}; 1313 1314/** 1315 * Listener for the ADS_MANAGER_LOADED event. Creates the AdsManager,
1316 * sets up event listeners, and triggers the 'adsready' event for 1317 * videojs-ads-contrib. 1318 * 1319 * @param {google.ima.AdsManagerLoadedEvent} adsManagerLoadedEvent Fired when 1320 * the AdsManager loads. 1321 */ 1322SdkImpl.prototype.onAdsManagerLoaded = function (adsManagerLoadedEvent) { 1323 this.createAdsRenderingSettings(); 1324 1325 this.adsManager = adsManagerLoadedEvent.getAdsManager(this.controller.getContentPlayheadTracker(), this.adsRenderingSettings); 1326 1327 this.adsManager.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR, this.onAdError.bind(this)); 1328 this.adsManager.addEventListener(google.ima.AdEvent.Type.AD_BREAK_READY, this.onAdBreakReady.bind(this)); 1329 this.adsManager.addEventListener(google.ima.AdEvent.Type.CONTENT_PAUSE_REQUESTED, this.onContentPauseRequested.bind(this)); 1330 this.adsManager.addEventListener(google.ima.AdEvent.Type.CONTENT_RESUME_REQUESTED, this.onContentResumeRequested.bind(this)); 1331 this.adsManager.addEventListener(google.ima.AdEvent.Type.ALL_ADS_COMPLETED, this.onAllAdsCompleted.bind(this)); 1332 1333 this.adsManager.addEventListener(google.ima.AdEvent.Type.LOADED, this.onAdLoaded.bind(this)); 1334 this.adsManager.addEventListener(google.ima.AdEvent.Type.STARTED, this.onAdStarted.bind(this)); 1335 this.adsManager.addEventListener(google.ima.AdEvent.Type.CLICK, this.onAdPaused.bind(this)); 1336 this.adsManager.addEventListener(google.ima.AdEvent.Type.COMPLETE, this.onAdComplete.bind(this)); 1337 this.adsManager.addEventListener(google.ima.AdEvent.Type.SKIPPED, this.onAdComplete.bind(this)); 1338 1339 if (this.controller.getIsMobile()) { 1340 // Show/hide controls on pause and resume (triggered by tap). 1341 this.adsManager.addEventListener(google.ima.AdEvent.Type.PAUSED, this.onAdPaused.bind(this)); 1342 this.adsManager.addEventListener(google.ima.AdEvent.Type.RESUMED, this.onAdResumed.bind(this)); 1343 } 1344 1345 if (!this.autoPlayAdBreaks) { 1346 this.initAdsManager(); 1347 } 1348 1349 this.controller.onAdsReady(); 1350 1351 if (this.controller.getSettings().adsManagerLoadedCallback) { 1352 this.controller.getSettings().adsManagerLoadedCallback(); 1353 } 1354}; 1355 1356/** 1357 * Listener for errors fired by the AdsLoader. 1358 * @param {google.ima.AdErrorEvent} event The error event thrown by the 1359 * AdsLoader. See 1360 * https://developers.google.com/interactive-media-ads/docs/sdks/html5/v3/apis#ima.AdError.Type 1361 */ 1362SdkImpl.prototype.onAdsLoaderError = function (event) { 1363 window.console.warn('AdsLoader error: ' + event.getError()); 1364 this.controller.onErrorLoadingAds(event); 1365 if (this.adsManager) { 1366 this.adsManager.destroy(); 1367 } 1368}; 1369 1370/** 1371 * Initialize the ads manager. 1372 */ 1373SdkImpl.prototype.initAdsManager = function () { 1374 try { 1375 var initWidth = this.controller.getPlayerWidth(); 1376 var initHeight = this.controller.getPlayerHeight(); 1377 this.adsManagerDimensions.width = initWidth; 1378 this.adsManagerDimensions.height = initHeight; 1379 this.adsManager.init(initWidth, initHeight, google.ima.ViewMode.NORMAL); 1380 this.adsManager.setVolume(this.controller.getPlayerVolume()); 1381 if (!this.adDisplayContainerInitialized) { 1382 this.adDisplayContainer.initialize(); 1383 this.adDisplayContainer.initialized = true; 1384 } 1385 } catch (adError) { 1386 this.onAdError(adError); 1387 } 1388}; 1389 1390/** 1391 * Create AdsRenderingSettings for the IMA SDK. 1392 */ 1393SdkImpl.prototype.createAdsRenderingSettings = function () { 1394 this.adsRenderingSettings = new google.ima.AdsRenderingSettings(); 1395 this.adsRenderingSettings.restoreCustomPlaybackStateOnAdBreakComplete = true; 1396 if (this.controller.getSettings().adsRenderingSettings) { 1397 for (var setting in this.controller.getSettings().adsRenderingSettings) { 1398 if (setting !== '') { 1399 this.adsRenderingSettings[setting] = this.controller.getSettings().adsRenderingSettings[setting]; 1400 } 1401 } 1402 } 1403}; 1404 1405/** 1406 * Listener for errors thrown by the AdsManager. 1407 * @param {google.ima.AdErrorEvent} adErrorEvent The error event thrown by 1408 * the AdsManager. 1409 */ 1410SdkImpl.prototype.onAdError = function (adErrorEvent) { 1411 var errorMessage = adErrorEvent.getError !== undefined ? adErrorEvent.getError() : adErrorEvent.stack; 1412 window.console.warn('Ad error: ' + errorMessage); 1413 1414 this.adsManager.destroy(); 1415 this.controller.onAdError(adErrorEvent); 1416 1417 // reset these so consumers don't think we are still in an ad break, 1418 // but reset them after any prior cleanup happens 1419 this.adsActive = false;
vendor: 25,929 bytes, lines 1420-2377
1420 this.adPlaying = false; 1421}; 1422 1423/** 1424 * Listener for AD_BREAK_READY. Passes event on to publisher's listener. 1425 * @param {google.ima.AdEvent} adEvent AdEvent thrown by the AdsManager. 1426 */ 1427SdkImpl.prototype.onAdBreakReady = function (adEvent) { 1428 this.adBreakReadyListener(adEvent); 1429}; 1430 1431/** 1432 * Pauses the content video and displays the ad container so ads can play. 1433 * @param {google.ima.AdEvent} adEvent The AdEvent thrown by the AdsManager. 1434 */ 1435SdkImpl.prototype.onContentPauseRequested = function (adEvent) { 1436 this.adsActive = true; 1437 this.adPlaying = true; 1438 this.controller.onAdBreakStart(adEvent); 1439}; 1440 1441/** 1442 * Resumes content video and hides the ad container. 1443 * @param {google.ima.AdEvent} adEvent The AdEvent thrown by the AdsManager. 1444 */ 1445SdkImpl.prototype.onContentResumeRequested = function (adEvent) { 1446 this.adsActive = false; 1447 this.adPlaying = false; 1448 this.controller.onAdBreakEnd(); 1449 // Hide controls in case of future non-linear ads. They'll be unhidden in 1450 // content_pause_requested. 1451}; 1452 1453/** 1454 * Records that ads have completed and calls contentAndAdsEndedListeners 1455 * if content is also complete. 1456 * @param {google.ima.AdEvent} adEvent The AdEvent thrown by the AdsManager. 1457 */ 1458SdkImpl.prototype.onAllAdsCompleted = function (adEvent) { 1459 this.allAdsCompleted = true; 1460 this.controller.onAllAdsCompleted(); 1461}; 1462 1463/** 1464 * Starts the content video when a non-linear ad is loaded. 1465 * @param {google.ima.AdEvent} adEvent The AdEvent thrown by the AdsManager. 1466 */ 1467SdkImpl.prototype.onAdLoaded = function (adEvent) { 1468 if (!adEvent.getAd().isLinear()) { 1469 this.controller.onNonLinearAdLoad(); 1470 this.controller.playContent(); 1471 } 1472}; 1473 1474/** 1475 * Starts the interval timer to check the current ad time when an ad starts 1476 * playing. 1477 * @param {google.ima.AdEvent} adEvent The AdEvent thrown by the AdsManager. 1478 */ 1479SdkImpl.prototype.onAdStarted = function (adEvent) { 1480 this.currentAd = adEvent.getAd(); 1481 if (this.currentAd.isLinear()) { 1482 this.adTrackingTimer = setInterval(this.onAdPlayheadTrackerInterval.bind(this), 250); 1483 this.controller.onLinearAdStart(); 1484 } else { 1485 this.controller.onNonLinearAdStart(); 1486 } 1487}; 1488 1489/** 1490 * Handles an ad click. Puts the player UI in a paused state. 1491 */ 1492SdkImpl.prototype.onAdPaused = function () { 1493 this.controller.onAdsPaused(); 1494}; 1495 1496/** 1497 * Syncs controls when an ad resumes. 1498 * @param {google.ima.AdEvent} adEvent The AdEvent thrown by the AdsManager. 1499 */ 1500SdkImpl.prototype.onAdResumed = function (adEvent) { 1501 this.controller.onAdsResumed(); 1502}; 1503 1504/** 1505 * Clears the interval timer for current ad time when an ad completes. 1506 */ 1507SdkImpl.prototype.onAdComplete = function () { 1508 if (this.currentAd.isLinear()) { 1509 clearInterval(this.adTrackingTimer); 1510 } 1511}; 1512 1513/** 1514 * Gets the current time and duration of the ad and calls the method to 1515 * update the ad UI. 1516 */ 1517SdkImpl.prototype.onAdPlayheadTrackerInterval = function () { 1518 var remainingTime = this.adsManager.getRemainingTime(); 1519 var duration = this.currentAd.getDuration(); 1520 var currentTime = duration - remainingTime; 1521 currentTime = currentTime > 0 ? currentTime : 0; 1522 var totalAds = 0; 1523 var adPosition = void 0; 1524 if (this.currentAd.getAdPodInfo()) { 1525 adPosition = this.currentAd.getAdPodInfo().getAdPosition(); 1526 totalAds = this.currentAd.getAdPodInfo().getTotalAds(); 1527 } 1528 1529 this.controller.onAdPlayheadUpdated(currentTime, remainingTime, duration, adPosition, totalAds); 1530}; 1531 1532/** 1533 * Called by the player wrapper when content completes. 1534 */ 1535SdkImpl.prototype.onContentComplete = function () { 1536 if (this.adsLoader) { 1537 this.adsLoader.contentComplete(); 1538 this.contentCompleteCalled = true; 1539 } 1540 1541 if (this.adsManager && this.adsManager.getCuePoints() && !this.adsManager.getCuePoints().includes(-1)) { 1542 this.controller.onNoPostroll(); 1543 } 1544 1545 if (this.allAdsCompleted) { 1546 this.controller.onContentAndAdsCompleted(); 1547 } 1548}; 1549 1550/** 1551 * Called when the player is disposed. 1552 */ 1553SdkImpl.prototype.onPlayerDisposed = function () { 1554 if (this.adTrackingTimer) { 1555 clearInterval(this.adTrackingTimer); 1556 } 1557 if (this.adsManager) { 1558 this.adsManager.destroy(); 1559 this.adsManager = null; 1560 } 1561}; 1562 1563SdkImpl.prototype.onPlayerReadyForPreroll = function () { 1564 if (this.autoPlayAdBreaks) { 1565 this.initAdsManager(); 1566 try { 1567 this.controller.showAdContainer(); 1568 // Sync ad volume with content volume. 1569 this.adsManager.setVolume(this.controller.getPlayerVolume()); 1570 this.adsManager.start(); 1571 } catch (adError) { 1572 this.onAdError(adError); 1573 } 1574 } 1575}; 1576 1577SdkImpl.prototype.onPlayerReady = function () { 1578 this.initAdObjects(); 1579 1580 if (this.controller.getSettings().adTagUrl || this.controller.getSettings().adsResponse) { 1581 this.requestAds(); 1582 } 1583}; 1584 1585SdkImpl.prototype.onPlayerEnterFullscreen = function () { 1586 if (this.adsManager) { 1587 this.adsManager.resize(window.screen.width, window.screen.height, google.ima.ViewMode.FULLSCREEN); 1588 } 1589}; 1590 1591SdkImpl.prototype.onPlayerExitFullscreen = function () { 1592 if (this.adsManager) { 1593 this.adsManager.resize(this.controller.getPlayerWidth(), this.controller.getPlayerHeight(), google.ima.ViewMode.NORMAL); 1594 } 1595}; 1596 1597/** 1598 * Called when the player volume changes. 1599 * 1600 * @param {number} volume The new player volume. 1601 */ 1602SdkImpl.prototype.onPlayerVolumeChanged = function (volume) { 1603 if (this.adsManager) { 1604 this.adsManager.setVolume(volume); 1605 } 1606 1607 if (volume == 0) { 1608 this.adMuted = true; 1609 } else { 1610 this.adMuted = false; 1611 } 1612}; 1613 1614/** 1615 * Called when the player wrapper detects that the player has been resized. 1616 * 1617 * @param {number} width The post-resize width of the player. 1618 * @param {number} height The post-resize height of the player. 1619 */ 1620SdkImpl.prototype.onPlayerResize = function (width, height) { 1621 if (this.adsManager) { 1622 this.adsManagerDimensions.width = width; 1623 this.adsManagerDimensions.height = height; 1624 /* global google */ 1625 /* eslint no-undef: 'error' */ 1626 this.adsManager.resize(width, height, google.ima.ViewMode.NORMAL); 1627 } 1628}; 1629 1630/** 1631 * @return {Object} The current ad. 1632 */ 1633SdkImpl.prototype.getCurrentAd = function () { 1634 return this.currentAd; 1635}; 1636 1637/** 1638 * Listener JSDoc for ESLint. This listener can be passed to 1639 * setAdBreakReadyListener. 1640 * @callback listener 1641 */ 1642 1643/** 1644 * Sets the listener to be called to trigger manual ad break playback. 1645 * @param {listener} listener The listener to be called to trigger manual ad 1646 * break playback. 1647 */ 1648SdkImpl.prototype.setAdBreakReadyListener = function (listener) { 1649 this.adBreakReadyListener = listener; 1650}; 1651 1652/** 1653 * @return {boolean} True if an ad is currently playing. False otherwise. 1654 */ 1655SdkImpl.prototype.isAdPlaying = function () { 1656 return this.adPlaying; 1657}; 1658 1659/** 1660 * @return {boolean} True if an ad is currently playing. False otherwise. 1661 */ 1662SdkImpl.prototype.isAdMuted = function () { 1663 return this.adMuted; 1664}; 1665 1666/** 1667 * Pause ads. 1668 */ 1669SdkImpl.prototype.pauseAds = function () { 1670 this.adsManager.pause(); 1671 this.adPlaying = false; 1672}; 1673 1674/** 1675 * Resume ads. 1676 */ 1677SdkImpl.prototype.resumeAds = function () { 1678 this.adsManager.resume(); 1679 this.adPlaying = true; 1680}; 1681 1682/** 1683 * Unmute ads. 1684 */ 1685SdkImpl.prototype.unmute = function () { 1686 this.adsManager.setVolume(1); 1687 this.adMuted = false; 1688}; 1689 1690/** 1691 * Mute ads. 1692 */ 1693SdkImpl.prototype.mute = function () { 1694 this.adsManager.setVolume(0); 1695 this.adMuted = true; 1696}; 1697 1698/** 1699 * Set the volume of the ads. 0-1. 1700 * 1701 * @param {number} volume The new volume. 1702 */ 1703SdkImpl.prototype.setVolume = function (volume) { 1704 this.adsManager.setVolume(volume); 1705 if (volume == 0) { 1706 this.adMuted = true; 1707 } else { 1708 this.adMuted = false; 1709 } 1710}; 1711 1712/** 1713 * Initializes the AdDisplayContainer. On mobile, this must be done as a 1714 * result of user action. 1715 */ 1716SdkImpl.prototype.initializeAdDisplayContainer = function () { 1717 if (this.adDisplayContainer) { 1718 this.adDisplayContainerInitialized = true; 1719 this.adDisplayContainer.initialize(); 1720 } 1721}; 1722 1723/** 1724 * Called by publishers in manual ad break playback mode to start an ad 1725 * break. 1726 */ 1727SdkImpl.prototype.playAdBreak = function () { 1728 if (!this.autoPlayAdBreaks) { 1729 this.controller.showAdContainer(); 1730 // Sync ad volume with content volume. 1731 this.adsManager.setVolume(this.controller.getPlayerVolume()); 1732 this.adsManager.start(); 1733 } 1734}; 1735 1736/** 1737 * Callback JSDoc for ESLint. This callback can be passed to addEventListener. 1738 * @callback callback 1739 */ 1740 1741/** 1742 * Ads an EventListener to the AdsManager. For a list of available events, 1743 * see 1744 * https://developers.google.com/interactive-media-ads/docs/sdks/html5/v3/apis#ima.AdEvent.Type 1745 * @param {google.ima.AdEvent.Type} event The AdEvent.Type for which to 1746 * listen. 1747 * @param {callback} callback The method to call when the event is fired. 1748 */ 1749SdkImpl.prototype.addEventListener = function (event, callback) { 1750 if (this.adsManager) { 1751 this.adsManager.addEventListener(event, callback); 1752 } 1753}; 1754 1755/** 1756 * Returns the instance of the AdsManager. 1757 * @return {google.ima.AdsManager} The AdsManager being used by the plugin. 1758 */ 1759SdkImpl.prototype.getAdsManager = function () { 1760 return this.adsManager; 1761}; 1762 1763/** 1764 * Reset the SDK implementation. 1765 */ 1766SdkImpl.prototype.reset = function () { 1767 this.adsActive = false; 1768 this.adPlaying = false; 1769 if (this.adTrackingTimer) { 1770 // If this is called while an ad is playing, stop trying to get that 1771 // ad's current time. 1772 clearInterval(this.adTrackingTimer); 1773 } 1774 if (this.adsManager) { 1775 this.adsManager.destroy(); 1776 this.adsManager = null; 1777 } 1778 if (this.adsLoader && !this.contentCompleteCalled) { 1779 this.adsLoader.contentComplete(); 1780 } 1781 this.contentCompleteCalled = false; 1782 this.allAdsCompleted = false; 1783}; 1784 1785/** 1786 * Copyright 2017 Google Inc. 1787 * 1788 * Licensed under the Apache License, Version 2.0 (the "License"); 1789 * you may not use this file except in compliance with the License. 1790 * You may obtain a copy of the License at 1791 * 1792 * http://www.apache.org/licenses/LICENSE-2.0 1793 * 1794 * Unless required by applicable law or agreed to in writing, software 1795 * distributed under the License is distributed on an "AS IS" BASIS, 1796 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1797 * See the License for the specific language governing permissions and 1798 * limitations under the License. 1799 * 1800 * IMA SDK integration plugin for Video.js. For more information see 1801 * https://www.github.com/googleads/videojs-ima 1802 */ 1803/** 1804 * The grand coordinator of the plugin. Facilitates communication between all 1805 * other plugin classes. 1806 * 1807 * @param {Object} player Instance of the video.js player. 1808 * @param {Object} options Options provided by the implementation. 1809 * @constructor 1810 * @struct 1811 * @final 1812 */ 1813var Controller = function Controller(player, options) { 1814 /** 1815 * Stores user-provided settings. 1816 * @type {Object} 1817 */ 1818 this.settings = {}; 1819 1820 /** 1821 * Content and ads ended listeners passed by the publisher to the plugin. 1822 * These will be called when the plugin detects that content *and all 1823 * ads* have completed. This differs from the contentEndedListeners in that 1824 * contentEndedListeners will fire between content ending and a post-roll 1825 * playing, whereas the contentAndAdsEndedListeners will fire after the 1826 * post-roll completes. 1827 */ 1828 this.contentAndAdsEndedListeners = []; 1829 1830 /** 1831 * Whether or not we are running on a mobile platform. 1832 */ 1833 this.isMobile = navigator.userAgent.match(/iPhone/i) || navigator.userAgent.match(/iPad/i) || navigator.userAgent.match(/Android/i); 1834 1835 /** 1836 * Whether or not we are running on an iOS platform. 1837 */ 1838 this.isIos = navigator.userAgent.match(/iPhone/i) || navigator.userAgent.match(/iPad/i); 1839 1840 this.initWithSettings(options); 1841 1842 /** 1843 * Stores contrib-ads default settings. 1844 */ 1845 var contribAdsDefaults = { 1846 debug: this.settings.debug, 1847 timeout: this.settings.timeout, 1848 prerollTimeout: this.settings.prerollTimeout 1849 }; 1850 var adsPluginSettings = this.extend({}, contribAdsDefaults, options.contribAdsSettings || {}); 1851 1852 this.playerWrapper = new PlayerWrapper(player, adsPluginSettings, this); 1853 this.adUi = new AdUi(this); 1854 this.sdkImpl = new SdkImpl(this); 1855}; 1856 1857Controller.IMA_DEFAULTS = { 1858 debug: false, 1859 timeout: 5000, 1860 prerollTimeout: 1000, 1861 adLabel: 'Advertisement', 1862 adLabelNofN: 'of', 1863 showControlsForJSAds: true 1864}; 1865 1866/** 1867 * Extends the settings to include user-provided settings. 1868 * 1869 * @param {Object} options Options to be used in initialization. 1870 */ 1871Controller.prototype.initWithSettings = function (options) { 1872 this.settings = this.extend({}, Controller.IMA_DEFAULTS, options || {}); 1873 1874 this.warnAboutDeprecatedSettings(); 1875 1876 // Default showing countdown timer to true. 1877 this.showCountdown = true; 1878 if (this.settings.showCountdown === false) { 1879 this.showCountdown = false; 1880 } 1881}; 1882 1883/** 1884 * Logs console warnings when deprecated settings are used. 1885 */ 1886Controller.prototype.warnAboutDeprecatedSettings = function () { 1887 var _this = this; 1888 1889 var deprecatedSettings = ['adWillAutoplay', 'adsWillAutoplay', 'adWillPlayMuted', 'adsWillPlayMuted']; 1890 deprecatedSettings.forEach(function (setting) { 1891 if (_this.settings[setting] !== undefined) { 1892 console.warn('WARNING: videojs.ima setting ' + setting + ' is deprecated'); 1893 } 1894 }); 1895}; 1896 1897/** 1898 * Return the settings object. 1899 * 1900 * @return {Object} The settings object. 1901 */ 1902Controller.prototype.getSettings = function () { 1903 return this.settings; 1904}; 1905 1906/** 1907 * Return whether or not we're in a mobile environment. 1908 * 1909 * @return {boolean} True if running on mobile, false otherwise. 1910 */ 1911Controller.prototype.getIsMobile = function () { 1912 return this.isMobile; 1913}; 1914 1915/** 1916 * Return whether or not we're in an iOS environment. 1917 * 1918 * @return {boolean} True if running on iOS, false otherwise. 1919 */ 1920Controller.prototype.getIsIos = function () { 1921 return this.isIos; 1922}; 1923 1924/** 1925 * Inject the ad container div into the DOM. 1926 * 1927 * @param{HTMLElement} adContainerDiv The ad container div. 1928 */ 1929Controller.prototype.injectAdContainerDiv = function (adContainerDiv) { 1930 this.playerWrapper.injectAdContainerDiv(adContainerDiv); 1931}; 1932 1933/** 1934 * @return {HTMLElement} The div for the ad container. 1935 */ 1936Controller.prototype.getAdContainerDiv = function () { 1937 return this.adUi.getAdContainerDiv(); 1938}; 1939 1940/** 1941 * @return {Object} The content player. 1942 */ 1943Controller.prototype.getContentPlayer = function () { 1944 return this.playerWrapper.getContentPlayer(); 1945}; 1946 1947/** 1948 * Returns the content playhead tracker. 1949 * 1950 * @return {Object} The content playhead tracker. 1951 */ 1952Controller.prototype.getContentPlayheadTracker = function () { 1953 return this.playerWrapper.getContentPlayheadTracker(); 1954}; 1955 1956/** 1957 * Requests ads. 1958 */ 1959Controller.prototype.requestAds = function () { 1960 this.sdkImpl.requestAds(); 1961}; 1962 1963/** 1964 * Add or modify a setting. 1965 * 1966 * @param {string} key Key to modify 1967 * @param {Object} value Value to set at key. 1968 */ 1969Controller.prototype.setSetting = function (key, value) { 1970 this.settings[key] = value; 1971}; 1972 1973/** 1974 * Called when there is an error loading ads. 1975 * 1976 * @param {Object} adErrorEvent The ad error event thrown by the IMA SDK. 1977 */ 1978Controller.prototype.onErrorLoadingAds = function (adErrorEvent) { 1979 this.adUi.onAdError(); 1980 this.playerWrapper.onAdError(adErrorEvent); 1981}; 1982 1983/** 1984 * Called by the ad UI when the play/pause button is clicked. 1985 */ 1986Controller.prototype.onAdPlayPauseClick = function () { 1987 if (this.sdkImpl.isAdPlaying()) { 1988 this.adUi.onAdsPaused(); 1989 this.sdkImpl.pauseAds(); 1990 } else { 1991 this.adUi.onAdsPlaying(); 1992 this.sdkImpl.resumeAds(); 1993 } 1994}; 1995 1996/** 1997 * Called by the ad UI when the mute button is clicked. 1998 * 1999 */ 2000Controller.prototype.onAdMuteClick = function () { 2001 if (this.sdkImpl.isAdMuted()) { 2002 this.playerWrapper.unmute(); 2003 this.adUi.unmute(); 2004 this.sdkImpl.unmute(); 2005 } else { 2006 this.playerWrapper.mute(); 2007 this.adUi.mute(); 2008 this.sdkImpl.mute(); 2009 } 2010}; 2011 2012/** 2013 * Set the volume of the player and ads. 0-1. 2014 * 2015 * @param {number} volume The new volume. 2016 */ 2017Controller.prototype.setVolume = function (volume) { 2018 this.playerWrapper.setVolume(volume); 2019 this.sdkImpl.setVolume(volume); 2020}; 2021 2022/** 2023 * @return {number} The volume of the content player. 2024 */ 2025Controller.prototype.getPlayerVolume = function () { 2026 return this.playerWrapper.getVolume(); 2027}; 2028 2029/** 2030 * Toggle fullscreen state. 2031 */ 2032Controller.prototype.toggleFullscreen = function () { 2033 this.playerWrapper.toggleFullscreen(); 2034}; 2035 2036/** 2037 * Relays ad errors to the player wrapper. 2038 * 2039 * @param {Object} adErrorEvent The ad error event thrown by the IMA SDK. 2040 */ 2041Controller.prototype.onAdError = function (adErrorEvent) { 2042 this.adUi.onAdError(); 2043 this.playerWrapper.onAdError(adErrorEvent); 2044}; 2045 2046/** 2047 * Handles ad break starting. 2048 * 2049 * @param {Object} adEvent The event fired by the IMA SDK. 2050 */ 2051Controller.prototype.onAdBreakStart = function (adEvent) { 2052 this.playerWrapper.onAdBreakStart(); 2053 this.adUi.onAdBreakStart(adEvent); 2054}; 2055 2056/** 2057 * Show the ad container. 2058 */ 2059Controller.prototype.showAdContainer = function () { 2060 this.adUi.showAdContainer(); 2061}; 2062 2063/** 2064 * Handles ad break ending. 2065 */ 2066Controller.prototype.onAdBreakEnd = function () { 2067 this.playerWrapper.onAdBreakEnd(); 2068 this.adUi.onAdBreakEnd(); 2069}; 2070 2071/** 2072 * Handles when all ads have finished playing. 2073 */ 2074Controller.prototype.onAllAdsCompleted = function () { 2075 this.adUi.onAllAdsCompleted(); 2076 this.playerWrapper.onAllAdsCompleted(); 2077}; 2078 2079/** 2080 * Handles the SDK firing an ad paused event. 2081 */ 2082Controller.prototype.onAdsPaused = function () { 2083 this.adUi.onAdsPaused(); 2084}; 2085 2086/** 2087 * Handles the SDK firing an ad resumed event. 2088 */ 2089Controller.prototype.onAdsResumed = function () { 2090 this.adUi.onAdsResumed(); 2091}; 2092 2093/** 2094 * Takes data from the sdk impl and passes it to the ad UI to update the UI. 2095 * 2096 * @param {number} currentTime Current time of the ad. 2097 * @param {number} remainingTime Remaining time of the ad. 2098 * @param {number} duration Duration of the ad. 2099 * @param {number} adPosition Index of the ad in the pod. 2100 * @param {number} totalAds Total number of ads in the pod. 2101 */ 2102Controller.prototype.onAdPlayheadUpdated = function (currentTime, remainingTime, duration, adPosition, totalAds) { 2103 this.adUi.updateAdUi(currentTime, remainingTime, duration, adPosition, totalAds); 2104}; 2105 2106/** 2107 * @return {Object} The current ad. 2108 */ 2109Controller.prototype.getCurrentAd = function () { 2110 return this.sdkImpl.getCurrentAd(); 2111}; 2112 2113/** 2114 * Play content. 2115 */ 2116Controller.prototype.playContent = function () { 2117 this.playerWrapper.play(); 2118}; 2119 2120/** 2121 * Handles when a linear ad starts. 2122 */ 2123Controller.prototype.onLinearAdStart = function () { 2124 this.adUi.onLinearAdStart(); 2125 this.playerWrapper.onAdStart(); 2126}; 2127 2128/** 2129 * Handles when a non-linear ad loads. 2130 */ 2131Controller.prototype.onNonLinearAdLoad = function () { 2132 this.adUi.onNonLinearAdLoad(); 2133}; 2134 2135/** 2136 * Handles when a non-linear ad starts. 2137 */ 2138Controller.prototype.onNonLinearAdStart = function () { 2139 this.adUi.onNonLinearAdLoad(); 2140 this.playerWrapper.onAdStart(); 2141}; 2142 2143/** 2144 * Get the player width. 2145 * 2146 * @return {number} The width of the player. 2147 */ 2148Controller.prototype.getPlayerWidth = function () { 2149 return this.playerWrapper.getPlayerWidth(); 2150}; 2151 2152/** 2153 * Get the player height. 2154 * 2155 * @return {number} The height of the player. 2156 */ 2157Controller.prototype.getPlayerHeight = function () { 2158 return this.playerWrapper.getPlayerHeight(); 2159}; 2160 2161/** 2162 * Tells the player wrapper that ads are ready. 2163 */ 2164Controller.prototype.onAdsReady = function () { 2165 this.playerWrapper.onAdsReady(); 2166}; 2167 2168/** 2169 * Called when the player wrapper detects that the player has been resized. 2170 * 2171 * @param {number} width The post-resize width of the player. 2172 * @param {number} height The post-resize height of the player. 2173 */ 2174Controller.prototype.onPlayerResize = function (width, height) { 2175 this.sdkImpl.onPlayerResize(width, height); 2176}; 2177 2178/** 2179 * Called by the player wrapper when content completes. 2180 */ 2181Controller.prototype.onContentComplete = function () { 2182 this.sdkImpl.onContentComplete(); 2183}; 2184 2185/** 2186 * Called by the player wrapper when it's time to play a post-roll but we don't 2187 * have one to play. 2188 */ 2189Controller.prototype.onNoPostroll = function () { 2190 this.playerWrapper.onNoPostroll(); 2191}; 2192 2193/** 2194 * Called when content and all ads have completed. 2195 */ 2196Controller.prototype.onContentAndAdsCompleted = function () { 2197 for (var index in this.contentAndAdsEndedListeners) { 2198 if (typeof this.contentAndAdsEndedListeners[index] === 'function') { 2199 this.contentAndAdsEndedListeners[index](); 2200 } 2201 } 2202}; 2203 2204/** 2205 * Called when the player is disposed. 2206 */ 2207Controller.prototype.onPlayerDisposed = function () { 2208 this.contentAndAdsEndedListeners = []; 2209 this.sdkImpl.onPlayerDisposed(); 2210}; 2211 2212/** 2213 * Called when the player is ready to play a pre-roll. 2214 */ 2215Controller.prototype.onPlayerReadyForPreroll = function () { 2216 this.sdkImpl.onPlayerReadyForPreroll(); 2217}; 2218 2219/** 2220 * Called when the player is ready. 2221 */ 2222Controller.prototype.onPlayerReady = function () { 2223 this.sdkImpl.onPlayerReady(); 2224}; 2225 2226/** 2227 * Called when the player enters fullscreen. 2228 */ 2229Controller.prototype.onPlayerEnterFullscreen = function () { 2230 this.adUi.onPlayerEnterFullscreen(); 2231 this.sdkImpl.onPlayerEnterFullscreen(); 2232}; 2233 2234/** 2235 * Called when the player exits fullscreen. 2236 */ 2237Controller.prototype.onPlayerExitFullscreen = function () { 2238 this.adUi.onPlayerExitFullscreen(); 2239 this.sdkImpl.onPlayerExitFullscreen(); 2240}; 2241 2242/** 2243 * Called when the player volume changes. 2244 * 2245 * @param {number} volume The new player volume. 2246 */ 2247Controller.prototype.onPlayerVolumeChanged = function (volume) { 2248 this.adUi.onPlayerVolumeChanged(volume); 2249 this.sdkImpl.onPlayerVolumeChanged(volume); 2250}; 2251 2252/** 2253 * Sets the content of the video player. You should use this method instead 2254 * of setting the content src directly to ensure the proper ad tag is 2255 * requested when the video content is loaded. 2256 * @param {?string} contentSrc The URI for the content to be played. Leave 2257 * blank to use the existing content. 2258 * @param {?string} adTag The ad tag to be requested when the content loads. 2259 * Leave blank to use the existing ad tag. 2260 * @param {?boolean} playOnLoad True to play the content once it has loaded, 2261 * false to only load the content but not start playback. 2262 */ 2263Controller.prototype.setContentWithAdTag = function (contentSrc, adTag, playOnLoad) { 2264 this.reset(); 2265 this.settings.adTagUrl = adTag ? adTag : this.settings.adTagUrl; 2266 this.playerWrapper.changeSource(contentSrc, playOnLoad); 2267}; 2268 2269/** 2270 * Sets the content of the video player. You should use this method instead 2271 * of setting the content src directly to ensure the proper ads response is 2272 * used when the video content is loaded. 2273 * @param {?string} contentSrc The URI for the content to be played. Leave 2274 * blank to use the existing content. 2275 * @param {?string} adsResponse The ads response to be requested when the 2276 * content loads. Leave blank to use the existing ads response. 2277 * @param {?boolean} playOnLoad True to play the content once it has loaded, 2278 * false to only load the content but not start playback. 2279 */ 2280Controller.prototype.setContentWithAdsResponse = function (contentSrc, adsResponse, playOnLoad) { 2281 this.reset(); 2282 this.settings.adsResponse = adsResponse ? adsResponse : this.settings.adsResponse; 2283 this.playerWrapper.changeSource(contentSrc, playOnLoad); 2284}; 2285 2286/** 2287 * Resets the state of the plugin. 2288 */ 2289Controller.prototype.reset = function () { 2290 this.sdkImpl.reset(); 2291 this.playerWrapper.reset(); 2292}; 2293 2294/** 2295 * Listener JSDoc for ESLint. This listener can be passed to 2296 * (add|remove)ContentEndedListener. 2297 * @callback listener 2298 */ 2299 2300/** 2301 * Adds a listener for the 'contentended' event of the video player. This should 2302 * be used instead of setting an 'contentended' listener directly to ensure that 2303 * the ima can do proper cleanup of the SDK before other event listeners are 2304 * called. 2305 * @param {listener} listener The listener to be called when content 2306 * completes. 2307 */ 2308Controller.prototype.addContentEndedListener = function (listener) { 2309 this.playerWrapper.addContentEndedListener(listener); 2310}; 2311 2312/** 2313 * Adds a listener that will be called when content and all ads have 2314 * finished playing. 2315 * @param {listener} listener The listener to be called when content and ads 2316 * complete. 2317 */ 2318Controller.prototype.addContentAndAdsEndedListener = function (listener) { 2319 this.contentAndAdsEndedListeners.push(listener); 2320}; 2321 2322/** 2323 * Sets the listener to be called to trigger manual ad break playback. 2324 * @param {listener} listener The listener to be called to trigger manual ad 2325 * break playback. 2326 */ 2327Controller.prototype.setAdBreakReadyListener = function (listener) { 2328 this.sdkImpl.setAdBreakReadyListener(listener); 2329}; 2330 2331/** 2332 * Changes the flag to show or hide the ad countdown timer. 2333 * 2334 * @param {boolean} showCountdownIn Show or hide the countdown timer. 2335 */ 2336Controller.prototype.setShowCountdown = function (showCountdownIn) { 2337 this.adUi.setShowCountdown(showCountdownIn); 2338 this.showCountdown = showCountdownIn; 2339 this.countdownDiv.style.display = this.showCountdown ? 'block' : 'none'; 2340}; 2341 2342/** 2343 * Initializes the AdDisplayContainer. On mobile, this must be done as a 2344 * result of user action. 2345 */ 2346Controller.prototype.initializeAdDisplayContainer = function () { 2347 this.sdkImpl.initializeAdDisplayContainer(); 2348}; 2349 2350/** 2351 * Called by publishers in manual ad break playback mode to start an ad 2352 * break. 2353 */ 2354Controller.prototype.playAdBreak = function () { 2355 this.sdkImpl.playAdBreak(); 2356}; 2357 2358/** 2359 * Callback JSDoc for ESLint. This callback can be passed to addEventListener. 2360 * @callback callback 2361 */ 2362 2363/** 2364 * Ads an EventListener to the AdsManager. For a list of available events, 2365 * see 2366 * https://developers.google.com/interactive-media-ads/docs/sdks/html5/v3/apis#ima.AdEvent.Type 2367 * @param {google.ima.AdEvent.Type} event The AdEvent.Type for which to 2368 * listen. 2369 * @param {callback} callback The method to call when the event is fired. 2370 */ 2371Controller.prototype.addEventListener = function (event, callback) { 2372 this.sdkImpl.addEventListener(event, callback); 2373}; 2374 2375/** 2376 * Returns the instance of the AdsManager. 2377 * @return {google.ima.AdsManager} The AdsManager being used by the plug
2377in. 2378 */ 2379Controller.prototype.getAdsManager = function () { 2380 return this.sdkImpl.getAdsManager(); 2381}; 2382 2383/** 2384 * Returns the instance of the player id. 2385 * @return {string} The player id. 2386 */ 2387Controller.prototype.getPlayerId = function () { 2388 return this.playerWrapper.getPlayerId(); 2389}; 2390 2391/** 2392 * Changes the ad tag. You will need to call requestAds after this method 2393 * for the new ads to be requested. 2394 * @param {?string} adTag The ad tag to be requested the next time 2395 * requestAds is called. 2396 */ 2397Controller.prototype.changeAdTag = function (adTag) { 2398 this.reset(); 2399 this.settings.adTagUrl = adTag; 2400}; 2401 2402/** 2403 * Pauses the ad. 2404 */ 2405Controller.prototype.pauseAd = function () { 2406 this.adUi.onAdsPaused(); 2407 this.sdkImpl.pauseAds(); 2408}; 2409 2410/** 2411 * Resumes the ad. 2412 */ 2413Controller.prototype.resumeAd = function () { 2414 this.adUi.onAdsPlaying(); 2415 this.sdkImpl.resumeAds(); 2416}; 2417 2418/** 2419 * @return {boolean} true if we expect that ads will autoplay. false otherwise. 2420 */ 2421Controller.prototype.adsWillAutoplay = function () { 2422 if (this.settings.adsWillAutoplay !== undefined) { 2423 return this.settings.adsWillAutoplay; 2424 } else if (this.settings.adWillAutoplay !== undefined) { 2425 return this.settings.adWillAutoplay; 2426 } else { 2427 return !!this.playerWrapper.getPlayerOptions().autoplay; 2428 } 2429}; 2430 2431/** 2432 * @return {boolean} true if we expect that ads will autoplay. false otherwise. 2433 */ 2434Controller.prototype.adsWillPlayMuted = function () { 2435 if (this.settings.adsWillPlayMuted !== undefined) { 2436 return this.settings.adsWillPlayMuted; 2437 } else if (this.settings.adWillPlayMuted !== undefined) { 2438 return this.settings.adWillPlayMuted; 2439 } else if (this.playerWrapper.getPlayerOptions().muted !== undefined) { 2440 return this.playerWrapper.getPlayerOptions().muted; 2441 } else { 2442 return this.playerWrapper.getVolume() == 0; 2443 } 2444}; 2445 2446/** 2447 * Triggers an event on the VJS player 2448 * @param {string} name The event name. 2449 * @param {Object} data The event data. 2450 */ 2451Controller.prototype.triggerPlayerEvent = function (name, data) { 2452 this.playerWrapper.triggerPlayerEvent(name, data); 2453}; 2454 2455/** 2456 * Extends an object to include the contents of objects at parameters 2 onward. 2457 * 2458 * @param {Object} obj The object onto which the subsequent objects' parameters 2459 * will be extended. This object will be modified. 2460 * @param {...Object} var_args The objects whose properties are to be extended 2461 * onto obj. 2462 * @return {Object} The extended object. 2463 */ 2464Controller.prototype.extend = function (obj) { 2465 var arg = void 0; 2466 var index = void 0; 2467 var key = void 0; 2468 2469 for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { 2470 args[_key - 1] = arguments[_key]; 2471 } 2472 2473 for (index = 0; index < args.length; index++) { 2474 arg = args[index]; 2475 for (key in arg) { 2476 if (arg.hasOwnProperty(key)) { 2477 obj[key] = arg[key]; 2478 } 2479 } 2480 } 2481 return obj; 2482}; 2483 2484/** 2485 * Copyright 2017 Google Inc. 2486 * 2487 * Licensed under the Apache License, Version 2.0 (the "License"); 2488 * you may not use this file except in compliance with the License. 2489 * You may obtain a copy of the License at 2490 * 2491 * http://www.apache.org/licenses/LICENSE-2.0 2492 * 2493 * Unless required by applicable law or agreed to in writing, software 2494 * distributed under the License is distributed on an "AS IS" BASIS, 2495 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2496 * See the License for the specific language governing permissions and 2497 * limitations under the License. 2498 * 2499 * IMA SDK integration plugin for Video.js. For more information see 2500 * https://www.github.com/googleads/videojs-ima 2501 */ 2502 2503/** 2504 * Exposes the ImaPlugin to a publisher implementation. 2505 * 2506 * @param {Object} player Instance of the video.js player to which this plugin 2507 * will be added. 2508 * @param {Object} options Options provided by the implementation. 2509 * @constructor 2510 * @struct 2511 * @final 2512 */ 2513var ImaPlugin = function ImaPlugin(player, options) { 2514 this.controller = new Controller(player, options); 2515 2516 /** 2517 * Listener JSDoc for ESLint. This listener can be passed to 2518 * addContent(AndAds)EndedListener. 2519 * @callback listener 2520 */ 2521 2522 /** 2523 * Adds a listener that will be called when content and all ads have 2524 * finished playing. 2525 * @param {listener} listener The listener to be called when content and ads 2526 * complete. 2527 */ 2528 this.addContentAndAdsEndedListener = function (listener) { 2529 this.controller.addContentAndAdsEndedListener(listener); 2530 }.bind(this); 2531 2532 /** 2533 * Adds a listener for the 'contentended' event of the video player. This 2534 * should be used instead of setting an 'contentended' listener directly to 2535 * ensure that the ima can do proper cleanup of the SDK before other event 2536 * listeners are called. 2537 * @param {listener} listener The listener to be called when content 2538 * completes. 2539 */ 2540 this.addContentEndedListener = function (listener) { 2541 this.controller.addContentEndedListener(listener); 2542 }.bind(this); 2543 2544 /** 2545 * Callback JSDoc for ESLint. This callback can be passed to addEventListener. 2546 * @callback callback 2547 */ 2548 2549 /** 2550 * Ads an EventListener to the AdsManager. For a list of available events, 2551 * see 2552 * https://developers.google.com/interactive-media-ads/docs/sdks/html5/v3/apis#ima.AdEvent.Type 2553 * @param {google.ima.AdEvent.Type} event The AdEvent.Type for which to 2554 * listen. 2555 * @param {callback} callback The method to call when the event is fired. 2556 */ 2557 this.addEventListener = function (event, callback) { 2558 this.controller.addEventListener(event, callback); 2559 }.bind(this); 2560 2561 /** 2562 * Changes the ad tag. You will need to call requestAds after this method 2563 * for the new ads to be requested. 2564 * @param {?string} adTag The ad tag to be requested the next time requestAds 2565 * is called. 2566 */ 2567 this.changeAdTag = function (adTag) { 2568 this.controller.changeAdTag(adTag); 2569 }.bind(this); 2570 2571 /** 2572 * Returns the instance of the AdsManager. 2573 * @return {google.ima.AdsManager} The AdsManager being used by the plugin. 2574 */ 2575 this.getAdsManager = function () { 2576 return this.controller.getAdsManager(); 2577 }.bind(this); 2578 2579 /** 2580 * Initializes the AdDisplayContainer. On mobile, this must be done as a 2581 * result of user action. 2582 */ 2583 this.initializeAdDisplayContainer = function () { 2584 this.controller.initializeAdDisplayContainer(); 2585 }.bind(this); 2586 2587 /** 2588 * Pauses the ad. 2589 */ 2590 this.pauseAd = function () { 2591 this.controller.pauseAd(); 2592 }.bind(this); 2593 2594 /** 2595 * Called by publishers in manual ad break playback mode to start an ad 2596 * break. 2597 */ 2598 this.playAdBreak = function () { 2599 this.controller.playAdBreak(); 2600 }.bind(this); 2601 2602 /** 2603 * Creates the AdsRequest and request ads through the AdsLoader. 2604 */ 2605 this.requestAds = function () { 2606 this.controller.requestAds(); 2607 }.bind(this); 2608 2609 /** 2610 * Resumes the ad. 2611 */ 2612 this.resumeAd = function () { 2613 this.controller.resumeAd(); 2614 }.bind(this); 2615 2616 /** 2617 * Sets the listener to be called to trigger manual ad break playback. 2618 * @param {listener} listener The listener to be called to trigger manual ad 2619 * break playback. 2620 */ 2621 this.setAdBreakReadyListener = function (listener) { 2622 this.controller.setAdBreakReadyListener(listener); 2623 }.bind(this); 2624 2625 /** 2626 * Sets the content of the video player. You should use this method instead 2627 * of setting the content src directly to ensure the proper ad tag is 2628 * requested when the video content is loaded. 2629 * @param {?string} contentSrc The URI for the content to be played. Leave 2630 * blank to use the existing content. 2631 * @param {?string} adTag The ad tag to be requested when the content loads. 2632 * Leave blank to use the existing ad tag. 2633 * @param {?boolean} playOnLoad True to play the content once it has loaded, 2634 * false to only load the content but not start playback. 2635 */ 2636 this.setContentWithAdTag = function (contentSrc, adTag, playOnLoad) { 2637 this.controller.setContentWithAdTag(contentSrc, adTag, playOnLoad); 2638 }.bind(this); 2639 2640 /** 2641 * Sets the content of the video player. You should use this method instead 2642 * of setting the content src directly to ensure the proper ads response is 2643 * used when the video content is loaded. 2644 * @param {?string} contentSrc The URI for the content to be played. Leave 2645 * blank to use the existing content. 2646 * @param {?string} adsResponse The ads response to be requested when the 2647 * content loads. Leave blank to use the existing ads response. 2648 * @param {?boolean} playOnLoad True to play the content once it has loaded, 2649 * false to only load the content but not start playback. 2650 */ 2651 this.setContentWithAdsResponse = function (contentSrc, adsResponse, playOnLoad) { 2652 this.controller.setContentWithAdsResponse(contentSrc, adsResponse, playOnLoad); 2653 }.bind(this); 2654 2655 /** 2656 * Changes the flag to show or hide the ad countdown timer. 2657 * 2658 * @param {boolean} showCountdownIn Show or hide the countdown timer. 2659 */ 2660 this.setShowCountdown = function (showCountdownIn) { 2661 this.controller.setShowCountdown(showCountdownIn); 2662 }.bind(this); 2663}; 2664 2665var init = function init(options) { 2666 /* eslint no-invalid-this: 'off' */ 2667 this.ima = new ImaPlugin(this, options); 2668}; 2669 2670var registerPlugin = videojs.registerPlugin || videojs.plugin; 2671registerPlugin('ima', init); 2672 2673})));
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.