PageSourceSearch

https://latempestad.mx/lib/videojs-contrib-ads/videojs.ads.js

js latempestad.mx collected 2026-10-02 06:02:48 UTC 16,146 bytes, 525 lines download raw bytes

1/**
2 * Basic Ad support plugin for video.js.
3 *
4 * Common code to support ad integrations.
5 */
6(function(window, document, vjs, undefined) {
7"use strict";
8
9var
10
11  /**
12   * Copies properties from one or more objects onto an original.
13   */
14  extend = function(obj /*, arg1, arg2, ... */) {
15    var arg, i, k;
16    for (i=1; i<arguments.length; i++) {
17      arg = arguments[i];
18      for (k in arg) {
19        if (arg.hasOwnProperty(k)) {
20          obj[k] = arg[k];
21        }
22      }
23    }
24    return obj;
25  },
26
27  /**
28   * Add a handler for multiple listeners to an object that supports addEventListener() or on().
29   *
30   * @param {object} obj The object to which the handler will be assigned.
31   * @param {mixed} events A string, array of strings, or hash of string/callback pairs.
32   * @param {function} callback Invoked when specified events occur, if events param is not a hash.
33   *
34   * @return {object} obj The object passed in.
35   */
36  on = function(obj, events, handler) {
37
38    var
39
40      type = Object.prototype.toString.call(events),
41
42      register = function(obj, event, handler) {
43        if (obj.addEventListener) {
44          obj.addEventListener(event, handler);
45        } else if (obj.on) {
46          obj.on(event, handler);
47        } else if (obj.attachEvent) {
48          obj.attachEvent('on' + event, handler);
49        } else {
50          throw new Error('object has no mechanism for adding event listeners');
51        }
52      },
53
54      i,
55      ii;
56
57    switch (type) {
58      case '[object String]':
59        register(obj, events, handler);
60        break;
61      case '[object Array]':
62        for (i = 0, ii = events.length; i<ii; i++) {
63          register(obj, events[i], handler);
64        }
65        break;
66      case '[object Object]':
67        for (i in events) {
68          if (events.hasOwnProperty(i)) {
69            register(obj, i, events[i]);
70          }
71        }
72        break;
73      default:
74        throw new Error('Unrecognized events parameter type: ' + type);
75    }
76
77    return obj;
78
79  },
80
81  /**
82   * Runs the callback at the next available opportunity.
83   * @see https://developer.mozilla.org/en-US/docs/Web/API/window.setImmediate
84   */
85  setImmediate = function(callback) {
86    return (
87      window.setImmediate ||
88      window.requestAnimationFrame ||
89      window.mozRequestAnimationFrame ||
90      window.webkitRequestAnimationFrame ||
91      window.setTimeout
92    )(callback, 0);
93  },
94
95  /**
96   * Clears a callback previously registered with `setImmediate`.
97   * @param {id} id The identifier of the callback to abort
98   */
99  clearImmediate = function(id) {
100    return (window.clearImmediate ||
101            window.cancelAnimationFrame ||
102            window.webkitCancelAnimationFrame ||
103            window.mozCancelAnimationFrame ||
104            window.clearTimeout)(id);
105  },
106
107  /**
108   * If ads are not playing, pauses the player at the next available
109   * opportunity. Has no effect if ads have started. This function is necessary
110   * because pausing a video element while processing a `play` event on iOS can
111   * cause the video element to continuously toggle between playing and paused
112   * states.
113   *
114   * @param {object} player The video player
115   */
116  cancelContentPlay = function(player) {
117    if (player.ads.cancelPlayTimeout) {
118      // another cancellation is already in flight, so do nothing
119      return;
120    }
121
122    player.ads.cancelPlayTimeout = setImmediate(function() {
123
124      // deregister the cancel timeout so subsequent cancels are scheduled
125      player.ads.cancelPlayTimeout = null;
126
127      if (!player.paused()) {
128        player.pause();
129      }
130    });
131  },
132
133  /**
134   * Returns an object that captures the portions of player state relevant to
135   * video playback. The result of this function can be passed to
136   * restorePlayerSnapshot with a player to return the player to the state it
137   * was in when this function was invoked.
138   * @param {object} player The videojs player object
139   */
140  getPlayerSnapshot = function(player) {
141    var
142      tech = player.el().querySelector('.vjs-tech'),
143      snapshot = {
144        src: player.currentSrc(),
145        currentTime: player.currentTime(),
146        type: player.currentType()
147      };
148
149    if (tech) {
150      snapshot.nativePoster = tech.poster;
151    }
152
153    return snapshot;
154  },
155
156  removeClass = function(element, className) {
157    var
158      classes = element.className.split(/\s+/),
159      i = classes.length,
160      newClasses = [];
161    while (i--) {
162      if (classes[i] !== className) {
163        newClasses.push(classes[i]);
164      }
165    }
166    element.className = newClasses.join(' ');
167  },
168
169  /**
170   * Attempts to modify the specified player so that its state is equivalent to
171   * the state of the snapshot.
172   * @param {object} snapshot - the player state to apply
173   */
174  restorePlayerSnapshot = function(player, snapshot) {
175    var
176      // the playback tech
177      tech = player.el().querySelector('.vjs-tech'),
178
179      // the number of remaining attempts to restore the snapshot
180      attempts = 20,
181
182      // finish restoring the playback state
183      resume = function() {
184        player.currentTime(snapshot.currentTime);
185        //If this wasn't a postroll resume
186        if (!player.ended()) {
187          player.play();
188        }
189      },
190
191      // determine if the video element has loaded enough of the snapshot source
192      // to be ready to apply the rest of the state
193      tryToResume = function() {
194        if (tech.seekable === undefined) {
195          // if the tech doesn't expose the seekable time ranges, try to
196          // resume playback immediately
197          resume();
198          return;
199        }
200        if (tech.seekable.length > 0) {
201          // if some period of the video is seekable, resume playback
202          resume();
203          return;
204        }
205
206        // delay a bit and then check again unless we're out of attempts
207        if (attempts--) {
208          setTimeout(tryToResume, 50);
209        }
210      },
211
212      // whether the video element has been modified since the
213      // snapshot was taken
214      srcChanged;
215
216    if (snapshot.nativePoster) {
217      tech.poster = snapshot.nativePoster;
218    }
219
220    // Determine whether the player needs to be restored to its state
221    // before ad playback began. With a custom ad display or burned-in
222    // ads, the content player state hasn't been modified and so no
223    // restoration is required
224
225    if (player.src()) {
226      // the player was in src attribute mode before the ad and the
227      // src attribute has not been modified, no restoration is required
228      // to resume playback
229      srcChanged = player.src() !== snapshot.src;
230    } else {
231      // the player was configured through source element children
232      // and the currentSrc hasn't changed, no restoration is required
233      // to resume playback
234      srcChanged = player.currentSrc() !== snapshot.src;
235    }
236
237    if (srcChanged) {
238      // if the src changed for ad playback, reset it
239      player.src({ src: snapshot.src, type: snapshot.type });
240      // safari requires a call to `load` to pick up a changed source
241      player.load();
242      // and then resume from the snapshots time once the original src has loaded
243      player.one('loadedmetadata', tryToResume);
244    } else if (!player.ended()) {
245      // the src didn't change and this wasn't a postroll
246      // just resume playback at the current time.
247      player.play();
248    }
249  },
250
251  /**
252   * Remove the poster attribute from the video element tech, if present. When
253   * reusing a video element for multiple videos, the poster image will briefly
254   * reappear while the new source loads. Removing the attribute ahead of time
255   * prevents the poster from showing up between videos.
256   * @param {object} player The videojs player object
257   */
258  removeNativePoster = function(player) {
259    var tech = player.el().querySelector('.vjs-tech');
260    if (tech) {
261      tech.removeAttribute('poster');
262    }
263  },
264
265  // ---------------------------------------------------------------------------
266  // Ad Framework
267  // ---------------------------------------------------------------------------
268
269  // default framework settings
270  defaults = {
271    // maximum amount of time in ms to wait to receive `adsready` from the ad
272    // implementation after play has been requested. Ad implementations are
273    // expected to load any dynamic libraries and make any requests to determine
274    // ad policies for a video during this time.
275    timeout: 5000,
276
277    // maximum amount of time in ms to wait for the ad implementation to start
278    // linear ad mode after `readyforpreroll` has fired. This is in addition to
279    // the standard timeout.
280    prerollTimeout: 100,
281
282    // when truthy, instructs the plugin to output additional information about
283    // plugin state to the video.js log. On most devices, the video.js log is
284    // the same as the developer console.
285    debug: false
286  },
287
288  adFramework = function(options) {
289    var
290      player = this,
291
292      // merge options and defaults
293      settings = extend({}, defaults, options || {}),
294
295      fsmHandler;
296
297    // replace the ad initializer with the ad namespace
298    player.ads = {
299      state: 'content-set',
300
301      startLinearAdMode: function() {
302        player.trigger('adstart');
303      },
304
305      endLinearAdMode: function() {
306        player.trigger('adend');
307      }
308    };
309
310    fsmHandler = function(event) {
311
312      // Ad Playback State Machine
313      var
314        fsm = {
315          'content-set': {
316            events: {
317              'adsready': function() {
318                this.state = 'ads-ready';
319              },
320              'play': function() {
321                this.state = 'ads-ready?';
322                cancelContentPlay(player);
323
324                // remove the poster so it doesn't flash between videos
325                removeNativePoster(player);
326              }
327            }
328          },
329          'ads-ready': {
330            events: {
331              'play': function() {
332                this.state = 'preroll?';
333                cancelContentPlay(player);
334              }
335            }
336          },
337          'preroll?': {
338            enter: function() {
339              // change class to show that we're waiting on ads
340              player.el().className += ' vjs-ad-loading';
341
342              // schedule an adtimeout event to fire if we waited too long
343              player.ads.timeout = window.setTimeout(function() {
344                player.trigger('adtimeout');
345              }, settings.prerollTimeout);
346
347              // signal to ad plugin that it's their opportunity to play a preroll
348              player.trigger('readyforpreroll');
349
350            },
351            leave: function() {
352              window.clearTimeout(player.ads.timeout);
353
354              clearImmediate(player.ads.cancelPlayTimeout);
355              player.ads.cancelPlayTimeout = null;
356
357              removeClass(player.el(), 'vjs-ad-loading');
358            },
359            events: {
360              'play': function() {
361                cancelContentPlay(player);
362              },
363              'adstart': function() {
364                this.state = 'ad-playback';
365                player.el().className += ' vjs-ad-playing';
366              },
367              'adtimeout': function() {
368                this.state = 'content-playback';
369                player.play();
370              }
371            }
372          },
373          'ads-ready?': {
374            enter: function() {
375              player.el().className += ' vjs-ad-loading';
376              player.ads.timeout = window.setTimeout(function() {
377                player.trigger('adtimeout');
378              }, settings.timeout);
379            },
380            leave: function() {
381              window.clearTimeout(player.ads.timeout);
382              removeClass(player.el(), 'vjs-ad-loading');
383            },
384            events: {
385              'play': function() {
386                cancelContentPlay(player);
387              },
388              'adsready': function() {
389                this.state = 'preroll?';
390              },
391              'adtimeout': function() {
392                this.state = 'ad-timeout-playback';
393              }
394            }
395          },
396          'ad-timeout-playback': {
397            events: {
398              'adsready': function() {
399                if (player.paused()) {
400                  this.state = 'ads-ready';
401                } else {
402                  this.state = 'preroll?';
403                }
404              },
405              'contentupdate': function() {
406                if (player.paused()) {
407                  this.state = 'content-set';
408                } else {
409                  this.state = 'ads-ready?';
410                }
411              }
412            }
413          },
414          'ad-playback': {
415            enter: function() {
416              // capture current player state snapshot (playing, currentTime, src)
417              this.snapshot = getPlayerSnapshot(player);
418
419              // remove the poster so it doesn't flash between videos
420              removeNativePoster(player);
421            },
422            leave: function() {
423              removeClass(player.el(), 'vjs-ad-playing');
424              restorePlayerSnapshot(player, this.snapshot);
425            },
426            events: {
427              'adend': function() {
428                this.state = 'content-playback';
429              }
430            }
431          },
432          'content-playback': {
433            events: {
434              'adstart': function() {
435                this.state = 'ad-playback';
436                player.el().className += ' vjs-ad-playing';
437
438                // remove the poster so it doesn't flash between videos
439                removeNativePoster(player);
440              },
441              'contentupdate': function() {
442                if (player.paused()) {
443                  this.state = 'content-set';
444                } else {
445                  this.state = 'ads-ready?';
446                }
447              }
448            }
449          }
450        };
451
452      (function(state) {
453
454        var noop = function() {};
455
456        // process the current event with a noop default handler
457        (fsm[state].events[event.type] || noop).apply(player.ads);
458
459        // execute leave/enter callbacks if present
460        if (state !== player.ads.state) {
461          (fsm[state].leave || noop).apply(player.ads);
462          (fsm[player.ads.state].enter || noop).apply(player.ads);
463
464          if (settings.debug) {
465            videojs.log('ads', state + ' -> ' + player.ads.state);
466          }
467        }
468
469      })(player.ads.state);
470
471    };
472
473    // register for the events we're interested in
474    on(player, vjs.Html5.Events.concat([
475      // events emitted by ad plugin
476      'adtimeout',
477      'contentupdate',
478      // events emitted by third party ad implementors
479      'adsready',
480      'adstart',  // startLinearAdMode()
481      'adend'     // endLinearAdMode()
482    ]), fsmHandler);
483    
484    // keep track of the current content source
485    // if you want to change the src of the video without triggering
486    // the ad workflow to restart, you can update this variable before
487    // modifying the player's source
488    player.ads.contentSrc = player.currentSrc();
489    
490    // implement 'contentupdate' event.
491    (function(){
492      var
493        // check if a new src has been set, if so, trigger contentupdate
494        checkSrc = function() {
495          var src;
496          if (player.ads.state !== 'ad-playback') {
497            src = player.currentSrc();
498            if (src !== player.ads.contentSrc) {
499              player.trigger({
500                type: 'contentupdate',
501                oldValue: player.ads.contentSrc,
502                newValue: src
503              });
504              player.ads.contentSrc = src;
505            }
506          }
507        };
508      // loadstart reliably indicates a new src has been set
509      player.on('loadstart', checkSrc);
510      // check immediately in case we missed the loadstart
511      setImmediate(checkSrc);
512    })();
513
514    // kick off the fsm
515    if (!player.paused()) {
516      // simulate a play event if we're autoplaying
517      fsmHandler({type:'play'});
518    }
519
520  };
521
522  // register the ad plugin framework
523  vjs.plugin('ads', adFramework);
524
525})(window, document, 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.