UNPKG

videojs-contrib-ads

Version:

A framework that provides common functionality needed by video advertisement libraries working with video.js.

137 lines (115 loc) 5.61 kB
'use strict'; var _video = require('video.js'); var _video2 = _interopRequireDefault(_video); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } // Stop propogation for an event var cancelEvent = function cancelEvent(player, event) { // Pretend we called stopImmediatePropagation because we want the native // element events to continue propagating event.isImmediatePropagationStopped = function () { return true; }; event.cancelBubble = true; event.isPropagationStopped = function () { return true; }; }; // Stop propogation for an event, then send a new event with the type of the original // event with the given prefix added. /* The goal of this feature is to make player events work as an integrator would expect despite the presense of ads. For example, an integrator would expect an `ended` event to happen once the content is ended. If an `ended` event is sent as a result of an ad ending, that is a bug. The `redispatch` method should recognize such `ended` events and prefix them so they are sent as `adended`, and so on with all other player events. */ var prefixEvent = function prefixEvent(player, prefix, event) { cancelEvent(player, event); player.trigger({ type: prefix + event.type, state: player.ads.state, originalEvent: event }); }; // Handle a player event, either by redispatching it with a prefix, or by // letting it go on its way without any meddling. var redispatch = function redispatch(event) { // We do a quick play/pause before we check for prerolls. This creates a "playing" // event. This conditional block prefixes that event so it's "adplaying" if it // happens while we're in the "preroll?" state. Not every browser is in the // "preroll?" state for this event, so the following browsers come through here: // * iPad // * iPhone // * Android // * Safari // This is too soon to check videoElementRecycled because there is no snapshot // yet. We rely on the coincidence that all browsers for which // videoElementRecycled would be true also happen to send their initial playing // event during "preroll?" if (event.type === 'playing' && this.ads.state === 'preroll?') { prefixEvent(this, 'ad', event); // Here we send "adplaying" for browsers that send their initial "playing" event // (caused by the the initial play/pause) during the "ad-playback" state. // The following browsers come through here: // * Chrome // * IE11 // If the ad plays in the content tech (aka videoElementRecycled) there will be // another playing event when the ad starts. We check videoElementRecycled to // avoid a second adplaying event. Thankfully, at this point a snapshot exists // so we can safely check videoElementRecycled. } else if (event.type === 'playing' && this.ads.state === 'ad-playback' && !this.ads.videoElementRecycled()) { prefixEvent(this, 'ad', event); // If the ad takes a long time to load, "playing" caused by play/pause can happen // during "ads-ready?" instead of "preroll?" or "ad-playback", skipping the // other conditions that would normally catch it } else if (event.type === 'playing' && this.ads.state === 'ads-ready?') { prefixEvent(this, 'ad', event); // When an ad is playing in content tech, we would normally prefix // "playing" with "ad" to send "adplaying". However, when we did a play/pause // before the preroll, we already sent "adplaying". This condition prevents us // from sending another. } else if (event.type === 'playing' && this.ads.state === 'ad-playback' && this.ads.videoElementRecycled()) { cancelEvent(this, event); return; // When ad is playing in content tech, prefix everything with "ad". // This block catches many events such as emptied, play, timeupdate, and ended. } else if (this.ads.state === 'ad-playback') { if (this.ads.videoElementRecycled() || this.ads.stitchedAds()) { prefixEvent(this, 'ad', event); } // Send contentended if ended happens during content. // We will make sure an ended event is sent after postrolls. } else if (this.ads.state === 'content-playback' && event.type === 'ended') { prefixEvent(this, 'content', event); // Event prefixing during content resuming is complicated } else if (this.ads.state === 'content-resuming') { // This does not happen during normal circumstances. I wasn't able to reproduce // it, but the working theory is that it handles cases where restoring the // snapshot takes a long time, such as in iOS7 and older Firefox. if (this.ads.snapshot && this.currentSrc() !== this.ads.snapshot.currentSrc) { // Don't prefix `loadstart` event if (event.type === 'loadstart') { return; } // All other events get "content" prefix return prefixEvent(this, 'content', event); // Content resuming after postroll } else if (this.ads.snapshot && this.ads.snapshot.ended) { // Don't prefix `pause` and `ended` events // They don't always happen during content-resuming, but they might. // It seems to happen most often on iOS and Android. if (event.type === 'pause' || event.type === 'ended') { return; } // All other events get "content" prefix return prefixEvent(this, 'content', event); } // Content resuming after preroll or midroll // Events besides "playing" get "content" prefix if (event.type !== 'playing') { prefixEvent(this, 'content', event); } } }; module.exports = redispatch;