123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909 |
- /*! @name videojs-playlist @version 4.3.1 @license Apache-2.0 */
- (function (global, factory) {
- typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('video.js')) :
- typeof define === 'function' && define.amd ? define(['video.js'], factory) :
- (global = global || self, global.videojsPlaylist = factory(global.videojs));
- }(this, function (videojs) { 'use strict';
- videojs = videojs && videojs.hasOwnProperty('default') ? videojs['default'] : videojs;
- /**
- * Validates a number of seconds to use as the auto-advance delay.
- *
- * @private
- * @param {number} s
- * The number to check
- *
- * @return {boolean}
- * Whether this is a valid second or not
- */
- var validSeconds = function validSeconds(s) {
- return typeof s === 'number' && !isNaN(s) && s >= 0 && s < Infinity;
- };
- /**
- * Resets the auto-advance behavior of a player.
- *
- * @param {Player} player
- * The player to reset the behavior on
- */
- var reset = function reset(player) {
- var aa = player.playlist.autoadvance_;
- if (aa.timeout) {
- player.clearTimeout(aa.timeout);
- }
- if (aa.trigger) {
- player.off('ended', aa.trigger);
- }
- aa.timeout = null;
- aa.trigger = null;
- };
- /**
- * Sets up auto-advance behavior on a player.
- *
- * @param {Player} player
- * the current player
- *
- * @param {number} delay
- * The number of seconds to wait before each auto-advance.
- *
- * @return {undefined}
- * Used to short circuit function logic
- */
- var setup = function setup(player, delay) {
- reset(player); // Before queuing up new auto-advance behavior, check if `seconds` was
- // called with a valid value.
- if (!validSeconds(delay)) {
- player.playlist.autoadvance_.delay = null;
- return;
- }
- player.playlist.autoadvance_.delay = delay;
- player.playlist.autoadvance_.trigger = function () {
- // This calls setup again, which will reset the existing auto-advance and
- // set up another auto-advance for the next "ended" event.
- var cancelOnPlay = function cancelOnPlay() {
- return setup(player, delay);
- }; // If there is a "play" event while we're waiting for an auto-advance,
- // we need to cancel the auto-advance. This could mean the user seeked
- // back into the content or restarted the content. This is reproducible
- // with an auto-advance > 0.
- player.one('play', cancelOnPlay);
- player.playlist.autoadvance_.timeout = player.setTimeout(function () {
- reset(player);
- player.off('play', cancelOnPlay);
- player.playlist.next();
- }, delay * 1000);
- };
- player.one('ended', player.playlist.autoadvance_.trigger);
- };
- /**
- * Removes all remote text tracks from a player.
- *
- * @param {Player} player
- * The player to clear tracks on
- */
- var clearTracks = function clearTracks(player) {
- var tracks = player.remoteTextTracks();
- var i = tracks && tracks.length || 0; // This uses a `while` loop rather than `forEach` because the
- // `TextTrackList` object is a live DOM list (not an array).
- while (i--) {
- player.removeRemoteTextTrack(tracks[i]);
- }
- };
- /**
- * Plays an item on a player's playlist.
- *
- * @param {Player} player
- * The player to play the item on
- *
- * @param {Object} item
- * A source from the playlist.
- *
- * @return {Player}
- * The player that is now playing the item
- */
- var playItem = function playItem(player, item) {
- var replay = !player.paused() || player.ended();
- player.trigger('beforeplaylistitem', item.originalValue || item);
- if (item.playlistItemId_) {
- player.playlist.currentPlaylistItemId_ = item.playlistItemId_;
- }
- player.poster(item.poster || '');
- player.src(item.sources);
- clearTracks(player);
- player.ready(function () {
- (item.textTracks || []).forEach(player.addRemoteTextTrack.bind(player));
- player.trigger('playlistitem', item.originalValue || item);
- if (replay) {
- var playPromise = player.play(); // silence error when a pause interrupts a play request
- // on browsers which return a promise
- if (typeof playPromise !== 'undefined' && typeof playPromise.then === 'function') {
- playPromise.then(null, function (e) {});
- }
- }
- setup(player, player.playlist.autoadvance_.delay);
- });
- return player;
- };
- /**
- * Returns whether a playlist item is an object of any kind, excluding null.
- *
- * @private
- *
- * @param {Object}
- * value to be checked
- *
- * @return {boolean}
- * The result
- */
- var isItemObject = function isItemObject(value) {
- return !!value && typeof value === 'object';
- };
- /**
- * Look through an array of playlist items and transform any primitive
- * as well as null values to objects. This method also adds a property
- * to the transformed item containing original value passed in an input list.
- *
- * @private
- *
- * @param {Array} arr
- * An array of playlist items
- *
- * @return {Array}
- * A new array with transformed items
- */
- var transformPrimitiveItems = function transformPrimitiveItems(arr) {
- var list = [];
- var tempItem;
- arr.forEach(function (item) {
- if (!isItemObject(item)) {
- tempItem = Object(item);
- tempItem.originalValue = item;
- } else {
- tempItem = item;
- }
- list.push(tempItem);
- });
- return list;
- };
- /**
- * Generate a unique id for each playlist item object. This id will be used to determine
- * index of an item in the playlist array for cases where there are multiple items with
- * the same source set.
- *
- * @private
- *
- * @param {Array} arr
- * An array of playlist items
- */
- var generatePlaylistItemId = function generatePlaylistItemId(arr) {
- var guid = 1;
- arr.forEach(function (item) {
- item.playlistItemId_ = guid++;
- });
- };
- /**
- * Look through an array of playlist items for a specific playlist item id.
- *
- * @private
- * @param {Array} list
- * An array of playlist items to look through
- *
- * @param {number} currentItemId
- * The current item ID.
- *
- * @return {number}
- * The index of the playlist item or -1 if not found
- */
- var indexInPlaylistItemIds = function indexInPlaylistItemIds(list, currentItemId) {
- for (var i = 0; i < list.length; i++) {
- if (list[i].playlistItemId_ === currentItemId) {
- return i;
- }
- }
- return -1;
- };
- /**
- * Given two sources, check to see whether the two sources are equal.
- * If both source urls have a protocol, the protocols must match, otherwise, protocols
- * are ignored.
- *
- * @private
- * @param {string|Object} source1
- * The first source
- *
- * @param {string|Object} source2
- * The second source
- *
- * @return {boolean}
- * The result
- */
- var sourceEquals = function sourceEquals(source1, source2) {
- var src1 = source1;
- var src2 = source2;
- if (typeof source1 === 'object') {
- src1 = source1.src;
- }
- if (typeof source2 === 'object') {
- src2 = source2.src;
- }
- if (/^\/\//.test(src1)) {
- src2 = src2.slice(src2.indexOf('//'));
- }
- if (/^\/\//.test(src2)) {
- src1 = src1.slice(src1.indexOf('//'));
- }
- return src1 === src2;
- };
- /**
- * Look through an array of playlist items for a specific `source`;
- * checking both the value of elements and the value of their `src`
- * property.
- *
- * @private
- * @param {Array} arr
- * An array of playlist items to look through
- *
- * @param {string} src
- * The source to look for
- *
- * @return {number}
- * The index of that source or -1
- */
- var indexInSources = function indexInSources(arr, src) {
- for (var i = 0; i < arr.length; i++) {
- var sources = arr[i].sources;
- if (Array.isArray(sources)) {
- for (var j = 0; j < sources.length; j++) {
- var source = sources[j];
- if (source && sourceEquals(source, src)) {
- return i;
- }
- }
- }
- }
- return -1;
- };
- /**
- * Randomize the contents of an array.
- *
- * @private
- * @param {Array} arr
- * An array.
- *
- * @return {Array}
- * The same array that was passed in.
- */
- var randomize = function randomize(arr) {
- var index = -1;
- var lastIndex = arr.length - 1;
- while (++index < arr.length) {
- var rand = index + Math.floor(Math.random() * (lastIndex - index + 1));
- var value = arr[rand];
- arr[rand] = arr[index];
- arr[index] = value;
- }
- return arr;
- };
- /**
- * Factory function for creating new playlist implementation on the given player.
- *
- * API summary:
- *
- * playlist(['a', 'b', 'c']) // setter
- * playlist() // getter
- * playlist.currentItem() // getter, 0
- * playlist.currentItem(1) // setter, 1
- * playlist.next() // 'c'
- * playlist.previous() // 'b'
- * playlist.first() // 'a'
- * playlist.last() // 'c'
- * playlist.autoadvance(5) // 5 second delay
- * playlist.autoadvance() // cancel autoadvance
- *
- * @param {Player} player
- * The current player
- *
- * @param {Array=} initialList
- * If given, an initial list of sources with which to populate
- * the playlist.
- *
- * @param {number=} initialIndex
- * If given, the index of the item in the list that should
- * be loaded first. If -1, no video is loaded. If omitted, The
- * the first video is loaded.
- *
- * @return {Function}
- * Returns the playlist function specific to the given player.
- */
- function factory(player, initialList, initialIndex) {
- if (initialIndex === void 0) {
- initialIndex = 0;
- }
- var list = null;
- var changing = false;
- /**
- * Get/set the playlist for a player.
- *
- * This function is added as an own property of the player and has its
- * own methods which can be called to manipulate the internal state.
- *
- * @param {Array} [newList]
- * If given, a new list of sources with which to populate the
- * playlist. Without this, the function acts as a getter.
- *
- * @param {number} [newIndex]
- * If given, the index of the item in the list that should
- * be loaded first. If -1, no video is loaded. If omitted, The
- * the first video is loaded.
- *
- * @return {Array}
- * The playlist
- */
- var playlist = player.playlist = function (newList, newIndex) {
- if (newIndex === void 0) {
- newIndex = 0;
- }
- if (changing) {
- throw new Error('do not call playlist() during a playlist change');
- }
- if (Array.isArray(newList)) {
- // @todo - Simplify this to `list.slice()` for v5.
- var previousPlaylist = Array.isArray(list) ? list.slice() : null;
- var nextPlaylist = newList.slice();
- list = nextPlaylist.slice(); // Transform any primitive and null values in an input list to objects
- if (list.filter(function (item) {
- return isItemObject(item);
- }).length !== list.length) {
- list = transformPrimitiveItems(list);
- } // Add unique id to each playlist item. This id will be used
- // to determine index in cases where there are more than one
- // identical sources in the playlist.
- generatePlaylistItemId(list); // Mark the playlist as changing during the duringplaylistchange lifecycle.
- changing = true;
- player.trigger({
- type: 'duringplaylistchange',
- nextIndex: newIndex,
- nextPlaylist: nextPlaylist,
- previousIndex: playlist.currentIndex_,
- // @todo - Simplify this to simply pass along `previousPlaylist` for v5.
- previousPlaylist: previousPlaylist || []
- });
- changing = false;
- if (newIndex !== -1) {
- playlist.currentItem(newIndex);
- } // The only time the previous playlist is null is the first call to this
- // function. This allows us to fire the `duringplaylistchange` event
- // every time the playlist is populated and to maintain backward
- // compatibility by not firing the `playlistchange` event on the initial
- // population of the list.
- //
- // @todo - Remove this condition in preparation for v5.
- if (previousPlaylist) {
- player.setTimeout(function () {
- player.trigger('playlistchange');
- }, 0);
- }
- } // Always return a shallow clone of the playlist list.
- // We also want to return originalValue if any item in the list has it.
- return list.map(function (item) {
- return item.originalValue || item;
- }).slice();
- }; // On a new source, if there is no current item, disable auto-advance.
- player.on('loadstart', function () {
- if (playlist.currentItem() === -1) {
- reset(player);
- }
- });
- playlist.currentIndex_ = -1;
- playlist.player_ = player;
- playlist.autoadvance_ = {};
- playlist.repeat_ = false;
- playlist.currentPlaylistItemId_ = null;
- /**
- * Get or set the current item in the playlist.
- *
- * During the duringplaylistchange event, acts only as a getter.
- *
- * @param {number} [index]
- * If given as a valid value, plays the playlist item at that index.
- *
- * @return {number}
- * The current item index.
- */
- playlist.currentItem = function (index) {
- // If the playlist is changing, only act as a getter.
- if (changing) {
- return playlist.currentIndex_;
- } // Act as a setter when the index is given and is a valid number.
- if (typeof index === 'number' && playlist.currentIndex_ !== index && index >= 0 && index < list.length) {
- playlist.currentIndex_ = index;
- playItem(playlist.player_, list[playlist.currentIndex_]);
- return playlist.currentIndex_;
- }
- var src = playlist.player_.currentSrc() || ''; // If there is a currentPlaylistItemId_, validate that it matches the
- // current source URL returned by the player. This is sufficient evidence
- // to suggest that the source was set by the playlist plugin. This code
- // exists primarily to deal with playlists where multiple items have the
- // same source.
- if (playlist.currentPlaylistItemId_) {
- var indexInItemIds = indexInPlaylistItemIds(list, playlist.currentPlaylistItemId_);
- var item = list[indexInItemIds]; // Found a match, this is our current index!
- if (item && Array.isArray(item.sources) && indexInSources([item], src) > -1) {
- playlist.currentIndex_ = indexInItemIds;
- return playlist.currentIndex_;
- } // If this does not match the current source, null it out so subsequent
- // calls can skip this step.
- playlist.currentPlaylistItemId_ = null;
- } // Finally, if we don't have a valid, current playlist item ID, we can
- // auto-detect it based on the player's current source URL.
- playlist.currentIndex_ = playlist.indexOf(src);
- return playlist.currentIndex_;
- };
- /**
- * Checks if the playlist contains a value.
- *
- * @param {string|Object|Array} value
- * The value to check
- *
- * @return {boolean}
- * The result
- */
- playlist.contains = function (value) {
- return playlist.indexOf(value) !== -1;
- };
- /**
- * Gets the index of a value in the playlist or -1 if not found.
- *
- * @param {string|Object|Array} value
- * The value to find the index of
- *
- * @return {number}
- * The index or -1
- */
- playlist.indexOf = function (value) {
- if (typeof value === 'string') {
- return indexInSources(list, value);
- }
- var sources = Array.isArray(value) ? value : value.sources;
- for (var i = 0; i < sources.length; i++) {
- var source = sources[i];
- if (typeof source === 'string') {
- return indexInSources(list, source);
- } else if (source.src) {
- return indexInSources(list, source.src);
- }
- }
- return -1;
- };
- /**
- * Get the index of the current item in the playlist. This is identical to
- * calling `currentItem()` with no arguments.
- *
- * @return {number}
- * The current item index.
- */
- playlist.currentIndex = function () {
- return playlist.currentItem();
- };
- /**
- * Get the index of the last item in the playlist.
- *
- * @return {number}
- * The index of the last item in the playlist or -1 if there are no
- * items.
- */
- playlist.lastIndex = function () {
- return list.length - 1;
- };
- /**
- * Get the index of the next item in the playlist.
- *
- * @return {number}
- * The index of the next item in the playlist or -1 if there is no
- * current item.
- */
- playlist.nextIndex = function () {
- var current = playlist.currentItem();
- if (current === -1) {
- return -1;
- }
- var lastIndex = playlist.lastIndex(); // When repeating, loop back to the beginning on the last item.
- if (playlist.repeat_ && current === lastIndex) {
- return 0;
- } // Don't go past the end of the playlist.
- return Math.min(current + 1, lastIndex);
- };
- /**
- * Get the index of the previous item in the playlist.
- *
- * @return {number}
- * The index of the previous item in the playlist or -1 if there is
- * no current item.
- */
- playlist.previousIndex = function () {
- var current = playlist.currentItem();
- if (current === -1) {
- return -1;
- } // When repeating, loop back to the end of the playlist.
- if (playlist.repeat_ && current === 0) {
- return playlist.lastIndex();
- } // Don't go past the beginning of the playlist.
- return Math.max(current - 1, 0);
- };
- /**
- * Plays the first item in the playlist.
- *
- * @return {Object|undefined}
- * Returns undefined and has no side effects if the list is empty.
- */
- playlist.first = function () {
- if (changing) {
- return;
- }
- var newItem = playlist.currentItem(0);
- if (list.length) {
- return list[newItem].originalValue || list[newItem];
- }
- playlist.currentIndex_ = -1;
- };
- /**
- * Plays the last item in the playlist.
- *
- * @return {Object|undefined}
- * Returns undefined and has no side effects if the list is empty.
- */
- playlist.last = function () {
- if (changing) {
- return;
- }
- var newItem = playlist.currentItem(playlist.lastIndex());
- if (list.length) {
- return list[newItem].originalValue || list[newItem];
- }
- playlist.currentIndex_ = -1;
- };
- /**
- * Plays the next item in the playlist.
- *
- * @return {Object|undefined}
- * Returns undefined and has no side effects if on last item.
- */
- playlist.next = function () {
- if (changing) {
- return;
- }
- var index = playlist.nextIndex();
- if (index !== playlist.currentIndex_) {
- var newItem = playlist.currentItem(index);
- return list[newItem].originalValue || list[newItem];
- }
- };
- /**
- * Plays the previous item in the playlist.
- *
- * @return {Object|undefined}
- * Returns undefined and has no side effects if on first item.
- */
- playlist.previous = function () {
- if (changing) {
- return;
- }
- var index = playlist.previousIndex();
- if (index !== playlist.currentIndex_) {
- var newItem = playlist.currentItem(index);
- return list[newItem].originalValue || list[newItem];
- }
- };
- /**
- * Set up auto-advance on the playlist.
- *
- * @param {number} [delay]
- * The number of seconds to wait before each auto-advance.
- */
- playlist.autoadvance = function (delay) {
- setup(playlist.player_, delay);
- };
- /**
- * Sets `repeat` option, which makes the "next" video of the last video in
- * the playlist be the first video in the playlist.
- *
- * @param {boolean} [val]
- * The value to set repeat to
- *
- * @return {boolean}
- * The current value of repeat
- */
- playlist.repeat = function (val) {
- if (val === undefined) {
- return playlist.repeat_;
- }
- if (typeof val !== 'boolean') {
- videojs.log.error('videojs-playlist: Invalid value for repeat', val);
- return;
- }
- playlist.repeat_ = !!val;
- return playlist.repeat_;
- };
- /**
- * Sorts the playlist array.
- *
- * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort}
- * @fires playlistsorted
- *
- * @param {Function} compare
- * A comparator function as per the native Array method.
- */
- playlist.sort = function (compare) {
- // Bail if the array is empty.
- if (!list.length) {
- return;
- }
- list.sort(compare); // If the playlist is changing, don't trigger events.
- if (changing) {
- return;
- }
- /**
- * Triggered after the playlist is sorted internally.
- *
- * @event playlistsorted
- * @type {Object}
- */
- player.trigger('playlistsorted');
- };
- /**
- * Reverses the playlist array.
- *
- * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reverse}
- * @fires playlistsorted
- */
- playlist.reverse = function () {
- // Bail if the array is empty.
- if (!list.length) {
- return;
- }
- list.reverse(); // If the playlist is changing, don't trigger events.
- if (changing) {
- return;
- }
- /**
- * Triggered after the playlist is sorted internally.
- *
- * @event playlistsorted
- * @type {Object}
- */
- player.trigger('playlistsorted');
- };
- /**
- * Shuffle the contents of the list randomly.
- *
- * @see {@link https://github.com/lodash/lodash/blob/40e096b6d5291a025e365a0f4c010d9a0efb9a69/shuffle.js}
- * @fires playlistsorted
- * @todo Make the `rest` option default to `true` in v5.0.0.
- * @param {Object} [options]
- * An object containing shuffle options.
- *
- * @param {boolean} [options.rest = false]
- * By default, the entire playlist is randomized. However, this may
- * not be desirable in all cases, such as when a user is already
- * watching a video.
- *
- * When `true` is passed for this option, it will only shuffle
- * playlist items after the current item. For example, when on the
- * first item, will shuffle the second item and beyond.
- */
- playlist.shuffle = function (_temp) {
- var _ref = _temp === void 0 ? {} : _temp,
- rest = _ref.rest;
- var index = 0;
- var arr = list; // When options.rest is true, start randomization at the item after the
- // current item.
- if (rest) {
- index = playlist.currentIndex_ + 1;
- arr = list.slice(index);
- } // Bail if the array is empty or too short to shuffle.
- if (arr.length <= 1) {
- return;
- }
- randomize(arr); // When options.rest is true, splice the randomized sub-array back into
- // the original array.
- if (rest) {
- var _list;
- (_list = list).splice.apply(_list, [index, arr.length].concat(arr));
- } // If the playlist is changing, don't trigger events.
- if (changing) {
- return;
- }
- /**
- * Triggered after the playlist is sorted internally.
- *
- * @event playlistsorted
- * @type {Object}
- */
- player.trigger('playlistsorted');
- }; // If an initial list was given, populate the playlist with it.
- if (Array.isArray(initialList)) {
- playlist(initialList.slice(), initialIndex); // If there is no initial list given, silently set an empty array.
- } else {
- list = [];
- }
- return playlist;
- }
- var version = "4.3.1";
- var registerPlugin = videojs.registerPlugin || videojs.plugin;
- /**
- * The video.js playlist plugin. Invokes the playlist-maker to create a
- * playlist function on the specific player.
- *
- * @param {Array} list
- * a list of sources
- *
- * @param {number} item
- * The index to start at
- */
- var plugin = function plugin(list, item) {
- factory(this, list, item);
- };
- registerPlugin('playlist', plugin);
- plugin.VERSION = version;
- return plugin;
- }));
|