videojs-playlist.js 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909
  1. /*! @name videojs-playlist @version 4.3.1 @license Apache-2.0 */
  2. (function (global, factory) {
  3. typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('video.js')) :
  4. typeof define === 'function' && define.amd ? define(['video.js'], factory) :
  5. (global = global || self, global.videojsPlaylist = factory(global.videojs));
  6. }(this, function (videojs) { 'use strict';
  7. videojs = videojs && videojs.hasOwnProperty('default') ? videojs['default'] : videojs;
  8. /**
  9. * Validates a number of seconds to use as the auto-advance delay.
  10. *
  11. * @private
  12. * @param {number} s
  13. * The number to check
  14. *
  15. * @return {boolean}
  16. * Whether this is a valid second or not
  17. */
  18. var validSeconds = function validSeconds(s) {
  19. return typeof s === 'number' && !isNaN(s) && s >= 0 && s < Infinity;
  20. };
  21. /**
  22. * Resets the auto-advance behavior of a player.
  23. *
  24. * @param {Player} player
  25. * The player to reset the behavior on
  26. */
  27. var reset = function reset(player) {
  28. var aa = player.playlist.autoadvance_;
  29. if (aa.timeout) {
  30. player.clearTimeout(aa.timeout);
  31. }
  32. if (aa.trigger) {
  33. player.off('ended', aa.trigger);
  34. }
  35. aa.timeout = null;
  36. aa.trigger = null;
  37. };
  38. /**
  39. * Sets up auto-advance behavior on a player.
  40. *
  41. * @param {Player} player
  42. * the current player
  43. *
  44. * @param {number} delay
  45. * The number of seconds to wait before each auto-advance.
  46. *
  47. * @return {undefined}
  48. * Used to short circuit function logic
  49. */
  50. var setup = function setup(player, delay) {
  51. reset(player); // Before queuing up new auto-advance behavior, check if `seconds` was
  52. // called with a valid value.
  53. if (!validSeconds(delay)) {
  54. player.playlist.autoadvance_.delay = null;
  55. return;
  56. }
  57. player.playlist.autoadvance_.delay = delay;
  58. player.playlist.autoadvance_.trigger = function () {
  59. // This calls setup again, which will reset the existing auto-advance and
  60. // set up another auto-advance for the next "ended" event.
  61. var cancelOnPlay = function cancelOnPlay() {
  62. return setup(player, delay);
  63. }; // If there is a "play" event while we're waiting for an auto-advance,
  64. // we need to cancel the auto-advance. This could mean the user seeked
  65. // back into the content or restarted the content. This is reproducible
  66. // with an auto-advance > 0.
  67. player.one('play', cancelOnPlay);
  68. player.playlist.autoadvance_.timeout = player.setTimeout(function () {
  69. reset(player);
  70. player.off('play', cancelOnPlay);
  71. player.playlist.next();
  72. }, delay * 1000);
  73. };
  74. player.one('ended', player.playlist.autoadvance_.trigger);
  75. };
  76. /**
  77. * Removes all remote text tracks from a player.
  78. *
  79. * @param {Player} player
  80. * The player to clear tracks on
  81. */
  82. var clearTracks = function clearTracks(player) {
  83. var tracks = player.remoteTextTracks();
  84. var i = tracks && tracks.length || 0; // This uses a `while` loop rather than `forEach` because the
  85. // `TextTrackList` object is a live DOM list (not an array).
  86. while (i--) {
  87. player.removeRemoteTextTrack(tracks[i]);
  88. }
  89. };
  90. /**
  91. * Plays an item on a player's playlist.
  92. *
  93. * @param {Player} player
  94. * The player to play the item on
  95. *
  96. * @param {Object} item
  97. * A source from the playlist.
  98. *
  99. * @return {Player}
  100. * The player that is now playing the item
  101. */
  102. var playItem = function playItem(player, item) {
  103. var replay = !player.paused() || player.ended();
  104. player.trigger('beforeplaylistitem', item.originalValue || item);
  105. if (item.playlistItemId_) {
  106. player.playlist.currentPlaylistItemId_ = item.playlistItemId_;
  107. }
  108. player.poster(item.poster || '');
  109. player.src(item.sources);
  110. clearTracks(player);
  111. player.ready(function () {
  112. (item.textTracks || []).forEach(player.addRemoteTextTrack.bind(player));
  113. player.trigger('playlistitem', item.originalValue || item);
  114. if (replay) {
  115. var playPromise = player.play(); // silence error when a pause interrupts a play request
  116. // on browsers which return a promise
  117. if (typeof playPromise !== 'undefined' && typeof playPromise.then === 'function') {
  118. playPromise.then(null, function (e) {});
  119. }
  120. }
  121. setup(player, player.playlist.autoadvance_.delay);
  122. });
  123. return player;
  124. };
  125. /**
  126. * Returns whether a playlist item is an object of any kind, excluding null.
  127. *
  128. * @private
  129. *
  130. * @param {Object}
  131. * value to be checked
  132. *
  133. * @return {boolean}
  134. * The result
  135. */
  136. var isItemObject = function isItemObject(value) {
  137. return !!value && typeof value === 'object';
  138. };
  139. /**
  140. * Look through an array of playlist items and transform any primitive
  141. * as well as null values to objects. This method also adds a property
  142. * to the transformed item containing original value passed in an input list.
  143. *
  144. * @private
  145. *
  146. * @param {Array} arr
  147. * An array of playlist items
  148. *
  149. * @return {Array}
  150. * A new array with transformed items
  151. */
  152. var transformPrimitiveItems = function transformPrimitiveItems(arr) {
  153. var list = [];
  154. var tempItem;
  155. arr.forEach(function (item) {
  156. if (!isItemObject(item)) {
  157. tempItem = Object(item);
  158. tempItem.originalValue = item;
  159. } else {
  160. tempItem = item;
  161. }
  162. list.push(tempItem);
  163. });
  164. return list;
  165. };
  166. /**
  167. * Generate a unique id for each playlist item object. This id will be used to determine
  168. * index of an item in the playlist array for cases where there are multiple items with
  169. * the same source set.
  170. *
  171. * @private
  172. *
  173. * @param {Array} arr
  174. * An array of playlist items
  175. */
  176. var generatePlaylistItemId = function generatePlaylistItemId(arr) {
  177. var guid = 1;
  178. arr.forEach(function (item) {
  179. item.playlistItemId_ = guid++;
  180. });
  181. };
  182. /**
  183. * Look through an array of playlist items for a specific playlist item id.
  184. *
  185. * @private
  186. * @param {Array} list
  187. * An array of playlist items to look through
  188. *
  189. * @param {number} currentItemId
  190. * The current item ID.
  191. *
  192. * @return {number}
  193. * The index of the playlist item or -1 if not found
  194. */
  195. var indexInPlaylistItemIds = function indexInPlaylistItemIds(list, currentItemId) {
  196. for (var i = 0; i < list.length; i++) {
  197. if (list[i].playlistItemId_ === currentItemId) {
  198. return i;
  199. }
  200. }
  201. return -1;
  202. };
  203. /**
  204. * Given two sources, check to see whether the two sources are equal.
  205. * If both source urls have a protocol, the protocols must match, otherwise, protocols
  206. * are ignored.
  207. *
  208. * @private
  209. * @param {string|Object} source1
  210. * The first source
  211. *
  212. * @param {string|Object} source2
  213. * The second source
  214. *
  215. * @return {boolean}
  216. * The result
  217. */
  218. var sourceEquals = function sourceEquals(source1, source2) {
  219. var src1 = source1;
  220. var src2 = source2;
  221. if (typeof source1 === 'object') {
  222. src1 = source1.src;
  223. }
  224. if (typeof source2 === 'object') {
  225. src2 = source2.src;
  226. }
  227. if (/^\/\//.test(src1)) {
  228. src2 = src2.slice(src2.indexOf('//'));
  229. }
  230. if (/^\/\//.test(src2)) {
  231. src1 = src1.slice(src1.indexOf('//'));
  232. }
  233. return src1 === src2;
  234. };
  235. /**
  236. * Look through an array of playlist items for a specific `source`;
  237. * checking both the value of elements and the value of their `src`
  238. * property.
  239. *
  240. * @private
  241. * @param {Array} arr
  242. * An array of playlist items to look through
  243. *
  244. * @param {string} src
  245. * The source to look for
  246. *
  247. * @return {number}
  248. * The index of that source or -1
  249. */
  250. var indexInSources = function indexInSources(arr, src) {
  251. for (var i = 0; i < arr.length; i++) {
  252. var sources = arr[i].sources;
  253. if (Array.isArray(sources)) {
  254. for (var j = 0; j < sources.length; j++) {
  255. var source = sources[j];
  256. if (source && sourceEquals(source, src)) {
  257. return i;
  258. }
  259. }
  260. }
  261. }
  262. return -1;
  263. };
  264. /**
  265. * Randomize the contents of an array.
  266. *
  267. * @private
  268. * @param {Array} arr
  269. * An array.
  270. *
  271. * @return {Array}
  272. * The same array that was passed in.
  273. */
  274. var randomize = function randomize(arr) {
  275. var index = -1;
  276. var lastIndex = arr.length - 1;
  277. while (++index < arr.length) {
  278. var rand = index + Math.floor(Math.random() * (lastIndex - index + 1));
  279. var value = arr[rand];
  280. arr[rand] = arr[index];
  281. arr[index] = value;
  282. }
  283. return arr;
  284. };
  285. /**
  286. * Factory function for creating new playlist implementation on the given player.
  287. *
  288. * API summary:
  289. *
  290. * playlist(['a', 'b', 'c']) // setter
  291. * playlist() // getter
  292. * playlist.currentItem() // getter, 0
  293. * playlist.currentItem(1) // setter, 1
  294. * playlist.next() // 'c'
  295. * playlist.previous() // 'b'
  296. * playlist.first() // 'a'
  297. * playlist.last() // 'c'
  298. * playlist.autoadvance(5) // 5 second delay
  299. * playlist.autoadvance() // cancel autoadvance
  300. *
  301. * @param {Player} player
  302. * The current player
  303. *
  304. * @param {Array=} initialList
  305. * If given, an initial list of sources with which to populate
  306. * the playlist.
  307. *
  308. * @param {number=} initialIndex
  309. * If given, the index of the item in the list that should
  310. * be loaded first. If -1, no video is loaded. If omitted, The
  311. * the first video is loaded.
  312. *
  313. * @return {Function}
  314. * Returns the playlist function specific to the given player.
  315. */
  316. function factory(player, initialList, initialIndex) {
  317. if (initialIndex === void 0) {
  318. initialIndex = 0;
  319. }
  320. var list = null;
  321. var changing = false;
  322. /**
  323. * Get/set the playlist for a player.
  324. *
  325. * This function is added as an own property of the player and has its
  326. * own methods which can be called to manipulate the internal state.
  327. *
  328. * @param {Array} [newList]
  329. * If given, a new list of sources with which to populate the
  330. * playlist. Without this, the function acts as a getter.
  331. *
  332. * @param {number} [newIndex]
  333. * If given, the index of the item in the list that should
  334. * be loaded first. If -1, no video is loaded. If omitted, The
  335. * the first video is loaded.
  336. *
  337. * @return {Array}
  338. * The playlist
  339. */
  340. var playlist = player.playlist = function (newList, newIndex) {
  341. if (newIndex === void 0) {
  342. newIndex = 0;
  343. }
  344. if (changing) {
  345. throw new Error('do not call playlist() during a playlist change');
  346. }
  347. if (Array.isArray(newList)) {
  348. // @todo - Simplify this to `list.slice()` for v5.
  349. var previousPlaylist = Array.isArray(list) ? list.slice() : null;
  350. var nextPlaylist = newList.slice();
  351. list = nextPlaylist.slice(); // Transform any primitive and null values in an input list to objects
  352. if (list.filter(function (item) {
  353. return isItemObject(item);
  354. }).length !== list.length) {
  355. list = transformPrimitiveItems(list);
  356. } // Add unique id to each playlist item. This id will be used
  357. // to determine index in cases where there are more than one
  358. // identical sources in the playlist.
  359. generatePlaylistItemId(list); // Mark the playlist as changing during the duringplaylistchange lifecycle.
  360. changing = true;
  361. player.trigger({
  362. type: 'duringplaylistchange',
  363. nextIndex: newIndex,
  364. nextPlaylist: nextPlaylist,
  365. previousIndex: playlist.currentIndex_,
  366. // @todo - Simplify this to simply pass along `previousPlaylist` for v5.
  367. previousPlaylist: previousPlaylist || []
  368. });
  369. changing = false;
  370. if (newIndex !== -1) {
  371. playlist.currentItem(newIndex);
  372. } // The only time the previous playlist is null is the first call to this
  373. // function. This allows us to fire the `duringplaylistchange` event
  374. // every time the playlist is populated and to maintain backward
  375. // compatibility by not firing the `playlistchange` event on the initial
  376. // population of the list.
  377. //
  378. // @todo - Remove this condition in preparation for v5.
  379. if (previousPlaylist) {
  380. player.setTimeout(function () {
  381. player.trigger('playlistchange');
  382. }, 0);
  383. }
  384. } // Always return a shallow clone of the playlist list.
  385. // We also want to return originalValue if any item in the list has it.
  386. return list.map(function (item) {
  387. return item.originalValue || item;
  388. }).slice();
  389. }; // On a new source, if there is no current item, disable auto-advance.
  390. player.on('loadstart', function () {
  391. if (playlist.currentItem() === -1) {
  392. reset(player);
  393. }
  394. });
  395. playlist.currentIndex_ = -1;
  396. playlist.player_ = player;
  397. playlist.autoadvance_ = {};
  398. playlist.repeat_ = false;
  399. playlist.currentPlaylistItemId_ = null;
  400. /**
  401. * Get or set the current item in the playlist.
  402. *
  403. * During the duringplaylistchange event, acts only as a getter.
  404. *
  405. * @param {number} [index]
  406. * If given as a valid value, plays the playlist item at that index.
  407. *
  408. * @return {number}
  409. * The current item index.
  410. */
  411. playlist.currentItem = function (index) {
  412. // If the playlist is changing, only act as a getter.
  413. if (changing) {
  414. return playlist.currentIndex_;
  415. } // Act as a setter when the index is given and is a valid number.
  416. if (typeof index === 'number' && playlist.currentIndex_ !== index && index >= 0 && index < list.length) {
  417. playlist.currentIndex_ = index;
  418. playItem(playlist.player_, list[playlist.currentIndex_]);
  419. return playlist.currentIndex_;
  420. }
  421. var src = playlist.player_.currentSrc() || ''; // If there is a currentPlaylistItemId_, validate that it matches the
  422. // current source URL returned by the player. This is sufficient evidence
  423. // to suggest that the source was set by the playlist plugin. This code
  424. // exists primarily to deal with playlists where multiple items have the
  425. // same source.
  426. if (playlist.currentPlaylistItemId_) {
  427. var indexInItemIds = indexInPlaylistItemIds(list, playlist.currentPlaylistItemId_);
  428. var item = list[indexInItemIds]; // Found a match, this is our current index!
  429. if (item && Array.isArray(item.sources) && indexInSources([item], src) > -1) {
  430. playlist.currentIndex_ = indexInItemIds;
  431. return playlist.currentIndex_;
  432. } // If this does not match the current source, null it out so subsequent
  433. // calls can skip this step.
  434. playlist.currentPlaylistItemId_ = null;
  435. } // Finally, if we don't have a valid, current playlist item ID, we can
  436. // auto-detect it based on the player's current source URL.
  437. playlist.currentIndex_ = playlist.indexOf(src);
  438. return playlist.currentIndex_;
  439. };
  440. /**
  441. * Checks if the playlist contains a value.
  442. *
  443. * @param {string|Object|Array} value
  444. * The value to check
  445. *
  446. * @return {boolean}
  447. * The result
  448. */
  449. playlist.contains = function (value) {
  450. return playlist.indexOf(value) !== -1;
  451. };
  452. /**
  453. * Gets the index of a value in the playlist or -1 if not found.
  454. *
  455. * @param {string|Object|Array} value
  456. * The value to find the index of
  457. *
  458. * @return {number}
  459. * The index or -1
  460. */
  461. playlist.indexOf = function (value) {
  462. if (typeof value === 'string') {
  463. return indexInSources(list, value);
  464. }
  465. var sources = Array.isArray(value) ? value : value.sources;
  466. for (var i = 0; i < sources.length; i++) {
  467. var source = sources[i];
  468. if (typeof source === 'string') {
  469. return indexInSources(list, source);
  470. } else if (source.src) {
  471. return indexInSources(list, source.src);
  472. }
  473. }
  474. return -1;
  475. };
  476. /**
  477. * Get the index of the current item in the playlist. This is identical to
  478. * calling `currentItem()` with no arguments.
  479. *
  480. * @return {number}
  481. * The current item index.
  482. */
  483. playlist.currentIndex = function () {
  484. return playlist.currentItem();
  485. };
  486. /**
  487. * Get the index of the last item in the playlist.
  488. *
  489. * @return {number}
  490. * The index of the last item in the playlist or -1 if there are no
  491. * items.
  492. */
  493. playlist.lastIndex = function () {
  494. return list.length - 1;
  495. };
  496. /**
  497. * Get the index of the next item in the playlist.
  498. *
  499. * @return {number}
  500. * The index of the next item in the playlist or -1 if there is no
  501. * current item.
  502. */
  503. playlist.nextIndex = function () {
  504. var current = playlist.currentItem();
  505. if (current === -1) {
  506. return -1;
  507. }
  508. var lastIndex = playlist.lastIndex(); // When repeating, loop back to the beginning on the last item.
  509. if (playlist.repeat_ && current === lastIndex) {
  510. return 0;
  511. } // Don't go past the end of the playlist.
  512. return Math.min(current + 1, lastIndex);
  513. };
  514. /**
  515. * Get the index of the previous item in the playlist.
  516. *
  517. * @return {number}
  518. * The index of the previous item in the playlist or -1 if there is
  519. * no current item.
  520. */
  521. playlist.previousIndex = function () {
  522. var current = playlist.currentItem();
  523. if (current === -1) {
  524. return -1;
  525. } // When repeating, loop back to the end of the playlist.
  526. if (playlist.repeat_ && current === 0) {
  527. return playlist.lastIndex();
  528. } // Don't go past the beginning of the playlist.
  529. return Math.max(current - 1, 0);
  530. };
  531. /**
  532. * Plays the first item in the playlist.
  533. *
  534. * @return {Object|undefined}
  535. * Returns undefined and has no side effects if the list is empty.
  536. */
  537. playlist.first = function () {
  538. if (changing) {
  539. return;
  540. }
  541. var newItem = playlist.currentItem(0);
  542. if (list.length) {
  543. return list[newItem].originalValue || list[newItem];
  544. }
  545. playlist.currentIndex_ = -1;
  546. };
  547. /**
  548. * Plays the last item in the playlist.
  549. *
  550. * @return {Object|undefined}
  551. * Returns undefined and has no side effects if the list is empty.
  552. */
  553. playlist.last = function () {
  554. if (changing) {
  555. return;
  556. }
  557. var newItem = playlist.currentItem(playlist.lastIndex());
  558. if (list.length) {
  559. return list[newItem].originalValue || list[newItem];
  560. }
  561. playlist.currentIndex_ = -1;
  562. };
  563. /**
  564. * Plays the next item in the playlist.
  565. *
  566. * @return {Object|undefined}
  567. * Returns undefined and has no side effects if on last item.
  568. */
  569. playlist.next = function () {
  570. if (changing) {
  571. return;
  572. }
  573. var index = playlist.nextIndex();
  574. if (index !== playlist.currentIndex_) {
  575. var newItem = playlist.currentItem(index);
  576. return list[newItem].originalValue || list[newItem];
  577. }
  578. };
  579. /**
  580. * Plays the previous item in the playlist.
  581. *
  582. * @return {Object|undefined}
  583. * Returns undefined and has no side effects if on first item.
  584. */
  585. playlist.previous = function () {
  586. if (changing) {
  587. return;
  588. }
  589. var index = playlist.previousIndex();
  590. if (index !== playlist.currentIndex_) {
  591. var newItem = playlist.currentItem(index);
  592. return list[newItem].originalValue || list[newItem];
  593. }
  594. };
  595. /**
  596. * Set up auto-advance on the playlist.
  597. *
  598. * @param {number} [delay]
  599. * The number of seconds to wait before each auto-advance.
  600. */
  601. playlist.autoadvance = function (delay) {
  602. setup(playlist.player_, delay);
  603. };
  604. /**
  605. * Sets `repeat` option, which makes the "next" video of the last video in
  606. * the playlist be the first video in the playlist.
  607. *
  608. * @param {boolean} [val]
  609. * The value to set repeat to
  610. *
  611. * @return {boolean}
  612. * The current value of repeat
  613. */
  614. playlist.repeat = function (val) {
  615. if (val === undefined) {
  616. return playlist.repeat_;
  617. }
  618. if (typeof val !== 'boolean') {
  619. videojs.log.error('videojs-playlist: Invalid value for repeat', val);
  620. return;
  621. }
  622. playlist.repeat_ = !!val;
  623. return playlist.repeat_;
  624. };
  625. /**
  626. * Sorts the playlist array.
  627. *
  628. * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort}
  629. * @fires playlistsorted
  630. *
  631. * @param {Function} compare
  632. * A comparator function as per the native Array method.
  633. */
  634. playlist.sort = function (compare) {
  635. // Bail if the array is empty.
  636. if (!list.length) {
  637. return;
  638. }
  639. list.sort(compare); // If the playlist is changing, don't trigger events.
  640. if (changing) {
  641. return;
  642. }
  643. /**
  644. * Triggered after the playlist is sorted internally.
  645. *
  646. * @event playlistsorted
  647. * @type {Object}
  648. */
  649. player.trigger('playlistsorted');
  650. };
  651. /**
  652. * Reverses the playlist array.
  653. *
  654. * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reverse}
  655. * @fires playlistsorted
  656. */
  657. playlist.reverse = function () {
  658. // Bail if the array is empty.
  659. if (!list.length) {
  660. return;
  661. }
  662. list.reverse(); // If the playlist is changing, don't trigger events.
  663. if (changing) {
  664. return;
  665. }
  666. /**
  667. * Triggered after the playlist is sorted internally.
  668. *
  669. * @event playlistsorted
  670. * @type {Object}
  671. */
  672. player.trigger('playlistsorted');
  673. };
  674. /**
  675. * Shuffle the contents of the list randomly.
  676. *
  677. * @see {@link https://github.com/lodash/lodash/blob/40e096b6d5291a025e365a0f4c010d9a0efb9a69/shuffle.js}
  678. * @fires playlistsorted
  679. * @todo Make the `rest` option default to `true` in v5.0.0.
  680. * @param {Object} [options]
  681. * An object containing shuffle options.
  682. *
  683. * @param {boolean} [options.rest = false]
  684. * By default, the entire playlist is randomized. However, this may
  685. * not be desirable in all cases, such as when a user is already
  686. * watching a video.
  687. *
  688. * When `true` is passed for this option, it will only shuffle
  689. * playlist items after the current item. For example, when on the
  690. * first item, will shuffle the second item and beyond.
  691. */
  692. playlist.shuffle = function (_temp) {
  693. var _ref = _temp === void 0 ? {} : _temp,
  694. rest = _ref.rest;
  695. var index = 0;
  696. var arr = list; // When options.rest is true, start randomization at the item after the
  697. // current item.
  698. if (rest) {
  699. index = playlist.currentIndex_ + 1;
  700. arr = list.slice(index);
  701. } // Bail if the array is empty or too short to shuffle.
  702. if (arr.length <= 1) {
  703. return;
  704. }
  705. randomize(arr); // When options.rest is true, splice the randomized sub-array back into
  706. // the original array.
  707. if (rest) {
  708. var _list;
  709. (_list = list).splice.apply(_list, [index, arr.length].concat(arr));
  710. } // If the playlist is changing, don't trigger events.
  711. if (changing) {
  712. return;
  713. }
  714. /**
  715. * Triggered after the playlist is sorted internally.
  716. *
  717. * @event playlistsorted
  718. * @type {Object}
  719. */
  720. player.trigger('playlistsorted');
  721. }; // If an initial list was given, populate the playlist with it.
  722. if (Array.isArray(initialList)) {
  723. playlist(initialList.slice(), initialIndex); // If there is no initial list given, silently set an empty array.
  724. } else {
  725. list = [];
  726. }
  727. return playlist;
  728. }
  729. var version = "4.3.1";
  730. var registerPlugin = videojs.registerPlugin || videojs.plugin;
  731. /**
  732. * The video.js playlist plugin. Invokes the playlist-maker to create a
  733. * playlist function on the specific player.
  734. *
  735. * @param {Array} list
  736. * a list of sources
  737. *
  738. * @param {number} item
  739. * The index to start at
  740. */
  741. var plugin = function plugin(list, item) {
  742. factory(this, list, item);
  743. };
  744. registerPlugin('playlist', plugin);
  745. plugin.VERSION = version;
  746. return plugin;
  747. }));