PageSourceSearch

https://cachemamadvisor.magicmaman.com/media/videojs/videojs.ima.js

js magicmaman.com collected 2026-09-24 17:56:51 UTC 36,279 bytes, 1,192 lines download raw bytes

1/**
2 * Copyright 2014 Google Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 * IMA SDK integration plugin for Video.js. For more information see
17 * https://www.github.com/googleads/videojs-ima
18 */
19
20(function(vjs) {
21  'use strict';
22  var extend = function(obj) {
23    var arg;
24    var index;
25    var key;
26    for (index = 1; index < arguments.length; index++) {
27      arg = arguments[index];
28      for (key in arg) {
29        if (arg.hasOwnProperty(key)) {
30          obj[key] = arg[key];
31        }
32      }
33    }
34    return obj;
35  },
36
37  ima_defaults = {
38    debug: false,
39    timeout: 5000,
40    prerollTimeout: 100,
41    adLabel: 'Advertisement'
42  },
43
44  imaPlugin = function(options, readyCallback) {
45    var player = this;
46
47    /**
48     * Creates the ad container passed to the IMA SDK.
49     * @private
50     */
51    player.ima.createAdContainer_ = function() {
52      // The adContainerDiv is the DOM of the element that will house
53      // the ads and ad controls.
54      vjsControls = player.getChild('controlBar');
55      adContainerDiv =
56          vjsControls.el().parentNode.appendChild(
57              document.createElement('div'));
58      adContainerDiv.id = 'ima-ad-container';
59      adContainerDiv.style.position = "absolute";
60      adContainerDiv.style.zIndex = 1111;
61      adContainerDiv.addEventListener(
62          'mouseover',
63          player.ima.showAdControls_,
64          false);
65      adContainerDiv.addEventListener(
66          'mouseout',
67          player.ima.hideAdControls_,
68          false);
69      player.ima.createControls_();
70      adDisplayContainer =
71          new google.ima.AdDisplayContainer(adContainerDiv, contentPlayer);
72    };
73
74    /**
75     * Creates the controls for the ad.
76     * @private
77     */
78    player.ima.createControls_ = function() {
79      controlsDiv = document.createElement('div');
80      controlsDiv.id = 'ima-controls-div';
81      controlsDiv.style.width = '100%';
82      countdownDiv = document.createElement('div');
83      countdownDiv.id = 'ima-countdown-div';
84      countdownDiv.innerHTML = settings.adLabel;
85      countdownDiv.style.display = showCountdown ? 'block' : 'none';
86      seekBarDiv = document.createElement('div');
87      seekBarDiv.id = 'ima-seek-bar-div';
88      seekBarDiv.style.width = '100%';
89      progressDiv = document.createElement('div');
90      progressDiv.id = 'ima-progress-div';
91      playPauseDiv = document.createElement('div');
92      playPauseDiv.id = 'ima-play-pause-div';
93      playPauseDiv.className = 'ima-playing';
94      playPauseDiv.addEventListener(
95          'click',
96          player.ima.onAdPlayPauseClick_,
97          false);
98      muteDiv = document.createElement('div');
99      muteDiv.id = 'ima-mute-div';
100      muteDiv.className = 'ima-non-muted';
101      muteDiv.addEventListener(
102          'click',
103          player.ima.onAdMuteClick_,
104          false);
105      sliderDiv = document.createElement('div');
106      sliderDiv.id = 'ima-slider-div';
107      sliderDiv.addEventListener(
108          'mousedown',
109          player.ima.onAdVolumeSliderMouseDown_,
110          false);
111      sliderLevelDiv = document.createElement('div');
112      sliderLevelDiv.id = 'ima-slider-level-div';
113      fullscreenDiv = document.createElement('div');
114      fullscreenDiv.id = 'ima-fullscreen-div';
115      fullscreenDiv.className = 'ima-non-fullscreen';
116      fullscreenDiv.addEventListener(
117          'click',
118          player.ima.onAdFullscreenClick_,
119          false);
120      adContainerDiv.appendChild(controlsDiv);
121      controlsDiv.appendChild(countdownDiv);
122      controlsDiv.appendChild(seekBarDiv);
123      controlsDiv.appendChild(playPauseDiv);
124      controlsDiv.appendChild(muteDiv);
125      controlsDiv.appendChild(sliderDiv);
126      controlsDiv.appendChild(fullscreenDiv);
127      seekBarDiv.appendChild(progressDiv);
128      sliderDiv.appendChild(sliderLevelDiv);
129    };
130
131    /**
132     * Initializes the AdDisplayContainer. On mobile, this must be done as a
133     * result of user action.
134     */
135    player.ima.initializeAdDisplayContainer = function() {
136      adDisplayContainerInitialized = true;
137      adDisplayContainer.initialize();
138    }
139
140    /**
141     * Creates the AdsRequest and request ads through the AdsLoader.
142     */
143    player.ima.requestAds = function() {
144      if (!adDisplayContainerInitialized) {
145        adDisplayContainer.initialize();
146      }
147      var adsRequest = new google.ima.AdsRequest();
148      adsRequest.adTagUrl = settings.adTagUrl;
149      if (settings.forceNonLinearFullSlot) {
150        adsRequest.forceNonLinearFullSlot = true;
151      }
152
153      adsRequest.linearAdSlotWidth = player.ima.getPlayerWidth();
154      adsRequest.linearAdSlotHeight = player.ima.getPlayerHeight();
155      adsRequest.nonLinearAdSlotWidth =
156          settings.nonLinearWidth || player.ima.getPlayerWidth();
157      adsRequest.nonLinearAdSlotHeight =
158          settings.nonLinearHeight || (player.ima.getPlayerHeight() / 3);
159
160      adsLoader.requestAds(adsRequest);
161    };
162
163    /**
164     * Listener for the ADS_MANAGER_LOADED event. Creates the AdsManager,
165     * sets up event listeners, and triggers the 'adsready' event for
166     * videojs-ads-contrib.
167     * @private
168     */
169    player.ima.onAdsManagerLoaded_ = function(adsManagerLoadedEvent) {
170      adsManager = adsManagerLoadedEvent.getAdsManager(
171          contentPlayheadTracker, adsRenderingSettings);
172
173      adsManager.addEventListener(
174          google.ima.AdErrorEvent.Type.AD_ERROR,
175          player.ima.onAdError_);
176      adsManager.addEventListener(
177          google.ima.AdEvent.Type.AD_BREAK_READY,
178          player.ima.onAdBreakReady_);
179      adsManager.addEventListener(
180          google.ima.AdEvent.Type.CONTENT_PAUSE_REQUESTED,
181          player.ima.onContentPauseRequested_);
182      adsManager.addEventListener(
183          google.ima.AdEvent.Type.CONTENT_RESUME_REQUESTED,
184          player.ima.onContentResumeRequested_);
185      adsManager.addEventListener(
186          google.ima.AdEvent.Type.ALL_ADS_COMPLETED,
187          player.ima.onAllAdsCompleted_);
188
189      adsManager.addEventListener(
190          google.ima.AdEvent.Type.LOADED,
191          player.ima.onAdLoaded_);
192      adsManager.addEventListener(
193          google.ima.AdEvent.Type.STARTED,
194          player.ima.onAdStarted_);
195      adsManager.addEventListener(
196          google.ima.AdEvent.Type.CLICK,
197          player.ima.onAdPlayPauseClick_);
198      adsManager.addEventListener(
199          google.ima.AdEvent.Type.COMPLETE,
200          player.ima.onAdComplete_);
201      adsManager.addEventListener(
202          google.ima.AdEvent.Type.SKIPPED,
203          player.ima.onAdComplete_);
204
205      if (!autoPlayAdBreaks) {
206        try {
207          var initWidth = player.ima.getPlayerWidth();
208          var initHeight = player.ima.getPlayerHeight();
209          adsManagerDimensions.width = initWidth;
210          adsManagerDimensions.height = initHeight;
211          adsManager.init(
212              initWidth,
213              initHeight,
214              google.ima.ViewMode.NORMAL);
215          adsManager.setVolume(player.muted() ? 0 : player.volume());
216        } catch (adError) {
217          player.ima.onAdError_(adError);
218        }
219      }
220
221      player.trigger('adsready');
222    };
223
224    /**
225     * Start ad playback, or content video playback in the absence of a
226     * pre-roll.
227     */
228    player.ima.start = function() {
229      if (autoPlayAdBreaks) {
230        try {
231          adsManager.init(
232              player.ima.getPlayerWidth(),
233              player.ima.getPlayerHeight(),
234              google.ima.ViewMode.NORMAL);
235          adsManager.setVolume(player.muted() ? 0 : player.volume());
236          adsManager.start();
237        } catch (adError) {
238          player.ima.onAdError_(adError);
239        }
240      }
241    };
242
243    /**
244     * Listener for errors fired by the AdsLoader.
245     * @param {google.ima.AdErrorEvent} event The error event thrown by the
246     *     AdsLoader. See
247     *     https://developers.google.com/interactive-media-ads/docs/sdks/html5/v3/apis#ima.AdError.Type
248     * @private
249     */
250    player.ima.onAdsLoaderError_ = function(event) {
251      window.console.log('AdsLoader error: ' + event.getError());
252      if (adsManager) {
253        adsManager.destroy();
254      }
255      player.trigger('adserror');
256    };
257
258    /**
259     * Listener for errors thrown by the AdsManager.
260     * @param {google.ima.AdErrorEvent} adErrorEvent The error event thrown by
261     *     the AdsManager.
262     * @private
263     */
264    player.ima.onAdError_ = function(adErrorEvent) {
265      window.console.log('Ad error: ' + adErrorEvent.getError());
266      vjsControls.show();
267      adsManager.destroy();
268      adContainerDiv.style.display = 'none';
269      player.trigger('adserror');
270    };
271
272    /**
273     * Listener for AD_BREAK_READY. Passes event on to publisher's listener.
274     * @param {google.ima.AdEvent} adEvent AdEvent thrown by the AdsManager.
275     * @private
276     */
277    player.ima.onAdBreakReady_ = function(adEvent) {
278      adBreakReadyListener(adEvent);
279    };
280
281    /**
282     * Called by publishers in manual ad break playback mode to start an ad
283     * break.
284     */
285    player.ima.playAdBreak = function() {
286      if (!autoPlayAdBreaks) {
287        adsManager.start();
288      }
289    }
290
291    /**
292     * Pauses the content video and displays the ad container so ads can play.
293     * @param {google.ima.AdEvent} adEvent The AdEvent thrown by the AdsManager.
294     * @private
295     */
296    player.ima.onContentPauseRequested_ = function(adEvent) {
297      adsActive = true;
298      adPlaying = true;
299      player.off('ended', localContentEndedListener);
300      if (adEvent.getAd().getAdPodInfo().getPodIndex() != -1) {
301        // Skip this call for post-roll ads
302        player.ads.startLinearAdMode();
303      }
304      adContainerDiv.style.display = 'block';
305      controlsDiv.style.display = 'block';
306      vjsControls.hide();
307      player.pause();
308    };
309
310    /**
311     * Resumes content video and hides the ad container.
312     * @param {google.ima.AdEvent} adEvent The AdEvent thrown by the AdsManager.
313     * @private
314     */
315    player.ima.onContentResumeRequested_ = function(adEvent) {
316      adsActive = false;
317      adPlaying = false;
318      player.on('ended', localContentEndedListener);
319      if (currentAd && currentAd.isLinear()) {
320        adContainerDiv.style.display = 'none';
321      }
322      vjsControls.show();
323      if (!currentAd) {
324        // Something went wrong playing the ad
325        player.ads.endLinearAdMode();
326      } else if (!contentComplete &&
327          // Don't exit linear mode after post-roll or content will auto-replay
328          currentAd.getAdPodInfo().getPodIndex() != -1 ) {
329        player.ads.endLinearAdMode();
330      }
331      countdownDiv.innerHTML = '';
332    };
333
334    /**
335     * Records that ads have completed and calls contentAndAdsEndedListeners
336     * if content is also complete.
337     * @param {google.ima.AdEvent} adEvent The AdEvent thrown by the AdsManager.
338     * @private
339     */
340    player.ima.onAllAdsCompleted_ = function(adEvent) {
341      allAdsCompleted = true;
342      if (contentComplete == true) {
343        for (var index in contentAndAdsEndedListeners) {
344          contentAndAdsEndedListeners[index]();
345        }
346      }
347    }
348
349    /**
350     * Starts the content video when a non-linear ad is loaded.
351     * @param {google.ima.AdEvent} adEvent The AdEvent thrown by the AdsManager.
352     * @private
353     */
354    player.ima.onAdLoaded_ = function(adEvent) {
355      if (!adEvent.getAd().isLinear()) {
356        player.play();
357      }
358    };
359
360    /**
361     * Starts the interval timer to check the current ad time when an ad starts
362     * playing.
363     * @param {google.ima.AdEvent} adEvent The AdEvent thrown by the AdsManager.
364     * @private
365     */
366    player.ima.onAdStarted_ = function(adEvent) {
367      currentAd = adEvent.getAd();
368      if (currentAd.isLinear()) {
369        adTrackingTimer = setInterval(
370            player.ima.onAdPlayheadTrackerInterval_, 250);
371        // Don't bump container when controls are shown
372        adContainerDiv.className = '';
373      } else {
374        // Bump container when controls are shown
375        adContainerDiv.className = 'bumpable-ima-ad-container';
376      }
377    };
378
379    /**
380     * Clears the interval timer for current ad time when an ad completes.
381     * @param {google.ima.AdEvent} adEvent The AdEvent thrown by the AdsManager.
382     * @private
383     */
384    player.ima.onAdComplete_ = function(adEvent) {
385      if (currentAd.isLinear()) {
386        clearInterval(adTrackingTimer);
387      }
388    };
389
390    /**
391     * Gets the current time and duration of the ad and calls the method to
392     * update the ad UI.
393     * @private
394     */
395    player.ima.onAdPlayheadTrackerInterval_ = function() {
396      var remainingTime = adsManager.getRemainingTime();
397      var duration =  currentAd.getDuration();
398      var currentTime = duration - remainingTime;
399      currentTime = currentTime > 0 ? currentTime : 0;
400      var isPod = false;
401      var adPosition, totalAds;
402      if (currentAd.getAdPodInfo()) {
403        isPod = true;
404        adPosition = currentAd.getAdPodInfo().getAdPosition();
405        totalAds = currentAd.getAdPodInfo().getTotalAds();
406      }
407
408      // Update countdown timer data
409      var remainingMinutes = Math.floor(remainingTime / 60);
410      var remainingSeconds = Math.floor(remainingTime % 60);
411      if (remainingSeconds.toString().length < 2) {
412        remainingSeconds = '0' + remainingSeconds;
413      }
414      var podCount = ': ';
415      if (isPod) {
416        podCount = ' (' + adPosition + ' of ' + totalAds + '): ';
417      }
418      countdownDiv.innerHTML =
419          settings.adLabel + podCount +
420          remainingMinutes + ':' + remainingSeconds;
421
422      // Update UI
423      var playProgressRatio = currentTime / duration;
424      var playProgressPercent = playProgressRatio * 100;
425      progressDiv.style.width = playProgressPercent + '%';
426    };
427
428    player.ima.getPlayerWidth = function() {
429      var retVal = parseInt(getComputedStyle(player.el()).width, 10) ||
430          player.width();
431      return retVal;
432    };
433
434    player.ima.getPlayerHeight = function() {
435      var retVal = parseInt(getComputedStyle(player.el()).height, 10) ||
436          player.height();
437      return retVal;
438    }
439
440    /**
441     * Hides the ad controls on mouseout.
442     * @private
443     */
444    player.ima.hideAdControls_ = function() {
445      playPauseDiv.style.display = 'none';
446      muteDiv.style.display = 'none';
447      fullscreenDiv.style.display = 'none';
448      controlsDiv.style.height = '14px';
449    };
450
451    /**
452     * Shows ad controls on mouseover.
453     * @private
454     */
455    player.ima.showAdControls_ = function() {
456      controlsDiv.style.height = '37px';
457      playPauseDiv.style.display = 'block';
458      muteDiv.style.display = 'block';
459      sliderDiv.style.display = 'block';
460      fullscreenDiv.style.display = 'block';
461    };
462
463    /**
464     * Listener for clicks on the play/pause button during ad playback.
465     * @private
466     */
467    player.ima.onAdPlayPauseClick_ = function() {
468      if (adPlaying) {
469        playPauseDiv.className = 'ima-paused';
470        adsManager.pause();
471        adPlaying = false;
472      } else {
473        playPauseDiv.className = 'ima-playing';
474        adsManager.resume();
475        adPlaying = true;
476      }
477    };
478
479    /**
480     * Listener for clicks on the mute button during ad playback.
481     * @private
482     */
483    player.ima.onAdMuteClick_ = function() {
484      if (adMuted) {
485        muteDiv.className = 'ima-non-muted';
486        adsManager.setVolume(1);
487        // Bubble down to content player
488        player.muted(false);
489        adMuted = false;
490        sliderLevelDiv.style.width = player.volume() * 100 + "%";
491      } else {
492        muteDiv.className = 'ima-muted';
493        adsManager.setVolume(0);
494        // Bubble down to content player
495        player.muted(true);
496        adMuted = true;
497        sliderLevelDiv.style.width = "0%";
498      }
499    };
500
501    /* Listener for mouse down events during ad playback. Used for volume.
502     * @private
503     */
504    player.ima.onAdVolumeSliderMouseDown_ = function() {
505       document.addEventListener('mouseup', player.ima.onMouseUp_, false);
506       document.addEventListener('mousemove', player.ima.onMouseMove_, false);
507    }
508
509    /* Mouse movement listener used for volume slider.
510     * @private
511     */
512    player.ima.onMouseMove_ = function(event) {
513      player.ima.setVolumeSlider_(event);
514    }
515
516    /* Mouse release listener used for volume slider.
517     * @private
518     */
519    player.ima.onMouseUp_ = function(event) {
520      player.ima.setVolumeSlider_(event);
521      document.removeEventListener('mousemove', player.ima.onMouseMove_);
522      document.removeEventListener('mouseup', player.ima.onMouseUp_);
523    }
524
525    /* Utility function to set volume and associated UI
526     * @private
527     */
528    player.ima.setVolumeSlider_ = function(event) {
529      var percent =
530          (event.clientX - sliderDiv.getBoundingClientRect().left) /
531              sliderDiv.offsetWidth;
532      percent *= 100;
533      //Bounds value 0-100 if mouse is outside slider region.
534      percent = Math.min(Math.max(percent, 0), 100);
535      sliderLevelDiv.style.width = percent + "%";
536      player.volume(percent / 100); //0-1
537      adsManager.setVolume(percent / 100);
538      if (player.volume() == 0) {
539        muteDiv.className = 'ima-muted';
540        player.muted(true);
541        adMuted = true;
542      }
543      else
544      {
545        muteDiv.className = 'ima-non-muted';
546        player.muted(false);
547        adMuted = false;
548      }
549    }
550
551    /**
552     * Listener for clicks on the fullscreen button during ad playback.
553     * @private
554     */
555    player.ima.onAdFullscreenClick_ = function() {
556      if (player.isFullscreen()) {
557        player.exitFullscreen();
558      } else {
559        player.requestFullscreen();
560      }
561    };
562
563    /**
564     * Listens for the video.js player to change its fullscreen status. This
565     * keeps the fullscreen-ness of the AdContainer in sync with the player.
566     * @private
567     */
568    player.ima.onFullscreenChange_ = function() {
569      if (player.isFullscreen()) {
570        fullscreenDiv.className = 'ima-fullscreen';
571        if (adsManager) {
572          adsManager.resize(
573              window.screen.width,
574              window.screen.height,
575              google.ima.ViewMode.FULLSCREEN);
576        }
577      } else {
578        fullscreenDiv.className = 'ima-non-fullscreen';
579        if (adsManager) {
580          adsManager.resize(
581              player.ima.getPlayerWidth(),
582              player.ima.getPlayerHeight(),
583              google.ima.ViewMode.NORMAL);
584        }
585      }
586    };
587
588    /**
589     * Listens for the video.js player to change its volume. This keeps the ad
590     * volume in sync with the content volume if the volume of the player is
591     * changed while content is playing
592     * @private
593     */
594    player.ima.onVolumeChange_ = function() {
595      var newVolume = player.muted() ? 0 : player.volume();
596      if (adsManager) {
597        adsManager.setVolume(newVolume);
598      }
599      // Update UI
600      if (newVolume == 0) {
601        adMuted = true;
602        muteDiv.className = 'ima-muted';
603        sliderLevelDiv.style.width = '0%';
604      } else {
605        adMuted = false;
606        muteDiv.className = 'ima-non-muted';
607        sliderLevelDiv.style.width = newVolume * 100 + '%';
608      }
609    };
610
611    /**
612     * Seeks content to 00:00:00. This is used as an event handler for the
613     * loadedmetadata event, since seeking is not possible until that event has
614     * fired.
615     * @private
616     */
617    player.ima.seekContentToZero_ = function() {
618      player.off('loadedmetadata', player.ima.seekContentToZero_);
619      player.currentTime(0);
620    };
621
622    /**
623     * Seeks content to 00:00:00 and starts playback. This is used as an event
624     * handler for the loadedmetadata event, since seeking is not possible until
625     * that event has fired.
626     * @private
627     */
628    player.ima.playContentFromZero_ = function() {
629      player.off('loadedmetadata', player.ima.playContentFromZero_);
630      player.currentTime(0);
631      player.play();
632    };
633
634    /**
635     * Destroys the AdsManager, sets it to null, and calls contentComplete to
636     * reset correlators. Once this is done it requests ads again to keep the
637     * inventory available.
638     * @private
639     */
640    player.ima.resetIMA_ = function() {
641      adsActive = false;
642      adPlaying = false;
643      player.on('ended', localContentEndedListener);
644      if (currentAd && currentAd.isLinear()) {
645        adContainerDiv.style.display = 'none';
646      }
647      vjsControls.show();
648      player.ads.endLinearAdMode();
649      if (adTrackingTimer) {
650        // If this is called while an ad is playing, stop trying to get that
651        // ad's current time.
652        clearInterval(adTrackingTimer);
653      }
654      if (adsManager) {
655        adsManager.destroy();
656        adsManager = null;
657      }
658      if (adsLoader && !contentComplete) {
659        adsLoader.contentComplete();
660      }
661      contentComplete = false;
662      allAdsCompleted = false;
663    };
664
665    /**
666     * Ads an EventListener to the AdsManager. For a list of available events,
667     * see
668     * https://developers.google.com/interactive-media-ads/docs/sdks/html5/v3/apis#ima.AdEvent.Type
669     * @param {google.ima.AdEvent.Type} event The AdEvent.Type for which to listen.
670     * @param {function} callback The method to call when the event is fired.
671     */
672    player.ima.addEventListener = function(event, callback) {
673      if (adsManager) {
674        adsManager.addEventListener(event, callback);
675      }
676    };
677
678    /**
679     * Returns the instance of the AdsManager.
680     * @return {google.ima.AdsManager} The AdsManager being used by the plugin.
681     */
682    player.ima.getAdsManager = function() {
683      return adsManager;
684    };
685
686    /**
687     * Sets the content of the video player. You should use this method instead
688     * of setting the content src directly to ensure the proper ad tag is
689     * requested when the video content is loaded.
690     * @param {?string} contentSrc The URI for the content to be played. Leave
691     *     blank to use the existing content.
692     * @param {?string} adTag The ad tag to be requested when the content loads.
693     *     Leave blank to use the existing ad tag.
694     * @param {?boolean} playOnLoad True to play the content once it has loaded,
695     *     false to only load the content but not start playback.
696     */
697    player.ima.setContent =
698        function(contentSrc, adTag, playOnLoad) {
699      player.ima.resetIMA_();
700      settings.adTagUrl = adTag ? adTag : settings.adTagUrl;
701      //only try to pause the player when initialised with a source already
702      if (!!player.currentSrc()) {
703        player.currentTime(0);
704        player.pause();
705      }
706      if (contentSrc) {
707        player.src(contentSrc);
708      }
709      if (playOnLoad) {
710        player.on('loadedmetadata', player.ima.playContentFromZero_);
711      } else {
712        player.on('loadedmetadata', player.ima.seekContentToZero_);
713      }
714    };
715
716    /**
717     * Adds a listener for the 'ended' event of the video player. This should be
718     * used instead of setting an 'ended' listener directly to ensure that the
719     * ima can do proper cleanup of the SDK before other event listeners
720     * are called.
721     * @param {function} listener The listener to be called when content completes.
722     */
723    player.ima.addContentEndedListener = function(listener) {
724      contentEndedListeners.push(listener);
725    };
726
727    /**
728     * Adds a listener that will be called when content and all ads have
729     * finished playing.
730     * @param {function} listener The listener to be called when content and ads complete.
731     */
732    player.ima.addContentAndAdsEndedListener = function(listener) {
733      contentAndAdsEndedListeners.push(listener);
734    }
735
736    /**
737     * Sets the listener to be called to trigger manual ad break playback.
738     * @param {function} listener The listener to be called to trigger manual ad break playback.
739     */
740    player.ima.setAdBreakReadyListener = function(listener) {
741      adBreakReadyListener = listener;
742    }
743
744    /**
745     * Pauses the ad.
746     */
747    player.ima.pauseAd = function() {
748      if (adsActive && adPlaying) {
749        playPauseDiv.className = 'ima-paused';
750        adsManager.pause();
751        adPlaying = false;
752      }
753    };
754
755    /**
756     * Resumes the ad.
757     */
758    player.ima.resumeAd = function() {
759      if (adsActive && !adPlaying) {
760        playPauseDiv.className = 'ima-playing';
761        adsManager.resume();
762        adPlaying = true;
763      }
764    };
765
766    /**
767     * Set up intervals to check for seeking and update current video time.
768     * @private
769     */
770    player.ima.setUpPlayerIntervals_ = function() {
771      updateTimeIntervalHandle =
772          setInterval(player.ima.updateCurrentTime_, seekCheckInterval);
773      seekCheckIntervalHandle =
774          setInterval(player.ima.checkForSeeking_, seekCheckInterval);
775      resizeCheckIntervalHandle =
776          setInterval(player.ima.checkForResize_, resizeCheckInterval);
777    };
778
779    /**
780     * Updates the current time of the video
781     * @private
782     */
783    player.ima.updateCurrentTime_ = function() {
784      if (!contentPlayheadTracker.seeking) {
785        contentPlayheadTracker.currentTime = player.currentTime();
786      }
787    };
788
789    /**
790     * Detects when the user is seeking through a video.
791     * This is used to prevent mid-rolls from playing while a user is seeking.
792     *
793     * There *is* a seeking property of the HTML5 video element, but it's not
794     * properly implemented on all platforms (e.g. mobile safari), so we have to
795     * check ourselves to be sure.
796     *
797     * @private
798     */
799    player.ima.checkForSeeking_ = function() {
800      var tempCurrentTime = player.currentTime();
801      var diff = (tempCurrentTime - contentPlayheadTracker.previousTime) * 1000;
802      if (Math.abs(diff) > seekCheckInterval + seekThreshold) {
803        contentPlayheadTracker.seeking = true;
804      } else {
805        contentPlayheadTracker.seeking = false;
806      }
807      contentPlayheadTracker.previousTime = player.currentTime();
808    };
809
810    /**
811     * Detects when the player is resized (for fluid support) and resizes the
812     * ads manager to match.
813     *
814     * @private
815     */
816    player.ima.checkForResize_ = function() {
817      var currentWidth = player.ima.getPlayerWidth();
818      var currentHeight = player.ima.getPlayerHeight();
819
820      if (adsManager && (currentWidth != adsManagerDimensions.width ||
821          currentHeight != adsManagerDimensions.height)) {
822        adsManagerDimensions.width = currentWidth;
823        adsManagerDimensions.height = currentHeight;
824        adsManager.resize(currentWidth, currentHeight, google.ima.ViewMode.NORMAL);
825      }
826    }
827
828    /**
829     * Changes the flag to show or hide the ad countdown timer.
830     *
831     * @param {boolean} showCountdownIn Show or hide the countdown timer.
832     */
833    player.ima.setShowCountdown = function(showCountdownIn) {
834      showCountdown = showCountdownIn;
835      countdownDiv.style.display = showCountdown ? 'block' : 'none';
836    };
837
838    /**
839     * Current plugin version.
840     */
841    var VERSION = '0.2.0';
842
843    /**
844     * Stores user-provided settings.
845     */
846    var settings;
847
848    /**
849     * Video element playing content.
850     */
851    var contentPlayer;
852
853    /**
854     * Boolean flag to show or hide the ad countdown timer.
855     */
856    var showCountdown;
857
858    /**
859     * Boolena flag to enable manual ad break playback.
860     */
861    var autoPlayAdBreaks;
862
863    /**
864     * Video.js control bar.
865     */
866    var vjsControls;
867
868    /**
869     * Div used as an ad container.
870     */
871    var adContainerDiv;
872
873    /**
874     * Div used to display ad controls.
875     */
876    var controlsDiv;
877
878    /**
879     * Div used to display ad countdown timer.
880     */
881    var countdownDiv;
882
883    /**
884     * Div used to display add seek bar.
885     */
886    var seekBarDiv;
887
888    /**
889     * Div used to display ad progress (in seek bar).
890     */
891    var progressDiv;
892
893    /**
894     * Div used to display ad play/pause button.
895     */
896    var playPauseDiv;
897
898    /**
899     * Div used to display ad mute button.
900     */
901    var muteDiv;
902
903    /**
904     * Div used by the volume slider.
905     */
906    var sliderDiv;
907
908    /**
909     * Volume slider level visuals
910     */
911    var sliderLevelDiv;
912
913    /**
914     * Div used to display ad fullscreen button.
915     */
916    var fullscreenDiv;
917
918    /**
919     * IMA SDK AdDisplayContainer.
920     */
921    var adDisplayContainer;
922
923    /**
924     * True if the AdDisplayContainer has been initialized. False otherwise.
925     */
926    var adDisplayContainerInitialized = false;
927
928    /**
929     * IMA SDK AdsLoader
930     */
931    var adsLoader;
932
933    /**
934     * IMA SDK AdsManager
935     */
936    var adsManager;
937
938    /**
939     * IMA SDK AdsRenderingSettings.
940     */
941    var adsRenderingSettings = null;
942
943    /**
944     * Ad tag URL. Should return VAST, VMAP, or ad rules.
945     */
946    var adTagUrl;
947
948    /**
949     * Current IMA SDK Ad.
950     */
951    var currentAd;
952
953    /**
954     * Timer used to track content progress.
955     */
956    var contentTrackingTimer;
957
958    /**
959     * Timer used to track ad progress.
960     */
961    var adTrackingTimer;
962
963    /**
964     * True if ads are currently displayed, false otherwise.
965     * True regardless of ad pause state if an ad is currently being displayed.
966     */
967    var adsActive = false;
968
969    /**
970     * True if ad is currently playing, false if ad is paused or ads are not
971     * currently displayed.
972     */
973    var adPlaying = false;
974
975    /**
976     * True if the ad is muted, false otherwise.
977     */
978    var adMuted = false;
979
980    /**
981     * True if our content video has completed, false otherwise.
982     */
983    var contentComplete = false;
984
985    /**
986     * True if ALL_ADS_COMPLETED has fired, false until then.
987     */
988     var allAdsCompleted = false;
989
990    /**
991     * Handle to interval that repeatedly updates current time.
992     */
993    var updateTimeIntervalHandle;
994
995    /**
996     * Handle to interval that repeatedly checks for seeking.
997     */
998    var seekCheckIntervalHandle;
999
1000    /**
1001     * Interval (ms) on which to check if the user is seeking through the
1002     * content.
1003     */
1004    var seekCheckInterval = 1000;
1005
1006    /**
1007     * Handle to interval that repeatedly checks for player resize.
1008     */
1009    var resizeCheckIntervalHandle;
1010
1011    /**
1012     * Interval (ms) to check for player resize for fluid support.
1013     */
1014    var resizeCheckInterval = 250;
1015
1016    /**
1017     * Threshold by which to judge user seeking. We check every 1000 ms to see
1018     * if the user is seeking. In order for us to decide that they are *not*
1019     * seeking, the content video playhead must only change by 900-1100 ms
1020     * between checks. Any greater change and we assume the user is seeking
1021     * through the video.
1022     */
1023    var seekThreshold = 100;
1024
1025    /**
1026     * Stores data for the content playhead tracker.
1027     */
1028    var contentPlayheadTracker = {
1029      currentTime: 0,
1030      previousTime: 0,
1031      seeking: false,
1032      duration: 0
1033    };
1034
1035    /**
1036     * Stores data for the ad playhead tracker.
1037     */
1038    var adPlayheadTracker = {
1039      currentTime: 0,
1040      duration: 0,
1041      isPod: false,
1042      adPosition: 0,
1043      totalAds: 0
1044    };
1045
1046    /**
1047     * Stores the dimensions for the ads manager.
1048     */
1049    var adsManagerDimensions = {
1050      width: 0,
1051      height: 0
1052    };
1053
1054    /**
1055     * Content ended listeners passed by the publisher to the plugin. Publishers
1056     * should allow the plugin to handle content ended to ensure proper support
1057     * of custom ad playback.
1058     */
1059    var contentEndedListeners = [];
1060
1061    /**
1062     * Content and ads ended listeners passed by the publisher to the plugin.
1063     * These will be called when the plugin detects that content *and all
1064     * ads* have completed. This differs from the contentEndedListeners in that
1065     * contentEndedListeners will fire between content ending and a post-roll
1066     * playing, whereas the contentAndAdsEndedListeners will fire after the
1067     * post-roll completes.
1068     */
1069    var contentAndAdsEndedListeners = [];
1070
1071     /**
1072      * Listener to be called to trigger manual ad break playback.
1073      */
1074    var adBreakReadyListener = undefined;
1075
1076    /**
1077     * Local content ended listener for contentComplete.
1078     */
1079    var localContentEndedListener = function() {
1080      if (adsLoader && !contentComplete) {
1081        adsLoader.contentComplete();
1082        contentComplete = true;
1083      }
1084      for (var index in contentEndedListeners) {
1085        contentEndedListeners[index]();
1086      }
1087      if (allAdsCompleted) {
1088        for (var index in contentAndAdsEndedListeners) {
1089          contentAndAdsEndedListeners[index]();
1090        }
1091      }
1092      clearInterval(updateTimeIntervalHandle);
1093      clearInterval(seekCheckIntervalHandle);
1094      clearInterval(resizeCheckIntervalHandle);
1095      player.one('play', player.ima.setUpPlayerIntervals_);
1096    };
1097
1098    settings = extend({}, ima_defaults, options || {});
1099
1100    // Currently this isn't used but I can see it being needed in the future, so
1101    // to avoid implementation problems with later updates I'm requiring it.
1102    if (!settings['id']) {
1103      window.console.log('Error: must provide id of video.js div');
1104      return;
1105    }
1106    contentPlayer = document.getElementById(settings['id'] + '_html5_api');
1107    // Default showing countdown timer to true.
1108    showCountdown = true;
1109    if (settings['showCountdown'] == false) {
1110      showCountdown = false;
1111    }
1112
1113    autoPlayAdBreaks = true;
1114    if (settings['autoPlayAdBreaks'] == false) {
1115      autoPlayAdBreaks = false;
1116    }
1117
1118    player.one('play', player.ima.setUpPlayerIntervals_);
1119
1120    player.on('ended', localContentEndedListener);
1121
1122    var contrib_ads_defaults = {
1123      debug: settings.debug,
1124      timeout: settings.timeout,
1125      prerollTimeout: settings.prerollTimeout
1126    };
1127
1128    var ads_plugin_settings =
1129        extend({}, contrib_ads_defaults, options['contribAdsSettings'] || {});
1130
1131    player.ads(ads_plugin_settings);
1132
1133    adsRenderingSettings = new google.ima.AdsRenderingSettings();
1134    adsRenderingSettings.restoreCustomPlaybackStateOnAdBreakComplete = true;
1135    if (settings['adsRenderingSettings']) {
1136      for (var setting in settings['adsRenderingSettings']) {
1137        adsRenderingSettings[setting] =
1138            settings['adsRenderingSettings'][setting];
1139      }
1140    }
1141
1142    if (settings['locale']) {
1143      google.ima.settings.setLocale(settings['locale']);
1144    }
1145
1146    player.ima.createAdContainer_();
1147
1148    adsLoader = new google.ima.AdsLoader(adDisplayContainer);
1149
1150    adsLoader.getSettings().setVpaidMode(
1151        google.ima.ImaSdkSettings.VpaidMode.ENABLED);
1152    if (settings.vpaidAllowed == false) {
1153      adsLoader.getSettings().setVpaidMode(
1154          google.ima.ImaSdkSettings.VpaidMode.DISABLED);
1155    }
1156    if (settings.vpaidMode) {
1157      adsLoader.getSettings().setVpaidMode(settings.vpaidMode);
1158    }
1159
1160    if (settings.locale) {
1161      adsLoader.getSettings().setLocale(settings.locale);
1162    }
1163
1164    if (settings.numRedirects) {
1165      adsLoader.getSettings().setNumRedirects(settings.numRedirects);
1166    }
1167
1168    adsLoader.getSettings().setPlayerType('videojs-ima');
1169    adsLoader.getSettings().setPlayerVersion(VERSION);
1170    adsLoader.getSettings().setAutoPlayAdBreaks(autoPlayAdBreaks);
1171
1172    adsLoader.addEventListener(
1173      google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED,
1174      player.ima.onAdsManagerLoaded_,
1175      false);
1176    adsLoader.addEventListener(
1177      google.ima.AdErrorEvent.Type.AD_ERROR,
1178      player.ima.onAdsLoaderError_,
1179      false);
1180
1181    if (!readyCallback) {
1182      readyCallback = player.ima.start;
1183    }
1184    player.on('readyforpreroll', readyCallback);
1185    player.ready(function() {
1186      player.on('fullscreenchange', player.ima.onFullscreenChange_);
1187      player.on('volumechange', player.ima.onVolumeChange_);
1188    });
1189  };
1190
1191  videojs.plugin('ima', imaPlugin);
1192}(window.videojs));

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.