videojs-playlist-ui
Advanced tools
Comparing version 3.4.0 to 3.4.1
@@ -0,1 +1,19 @@ | ||
<a name="3.4.1"></a> | ||
## [3.4.1](https://github.com/brightcove/videojs-playlist-ui/compare/v3.0.7...v3.4.1) (2018-07-20) | ||
### Bug Fixes | ||
* css builds ([#63](https://github.com/brightcove/videojs-playlist-ui/issues/63)) ([603ec73](https://github.com/brightcove/videojs-playlist-ui/commit/603ec73)) | ||
### Reverts | ||
* unintended pkg changes ([#64](https://github.com/brightcove/videojs-playlist-ui/issues/64)) ([be83683](https://github.com/brightcove/videojs-playlist-ui/commit/be83683)) | ||
<a name="3.0.7"></a> | ||
## [3.0.7](https://github.com/brightcove/videojs-playlist-ui/compare/v3.4.0...v3.0.7) (2018-07-05) | ||
### Chores | ||
* generator v6 ([#58](https://github.com/brightcove/videojs-playlist-ui/issues/58)) ([e9c2b00](https://github.com/brightcove/videojs-playlist-ui/commit/e9c2b00)) | ||
<a name="3.4.0"></a> | ||
@@ -2,0 +20,0 @@ # [3.4.0](https://github.com/brightcove/videojs-playlist-ui/compare/v3.3.0...v3.4.0) (2018-03-29) |
@@ -0,1 +1,2 @@ | ||
/*! @name videojs-playlist-ui @version 3.4.1 @license Apache-2.0 */ | ||
'use strict'; | ||
@@ -8,121 +9,4 @@ | ||
var version = "3.4.0"; | ||
var version = "3.4.1"; | ||
var asyncGenerator = function () { | ||
function AwaitValue(value) { | ||
this.value = value; | ||
} | ||
function AsyncGenerator(gen) { | ||
var front, back; | ||
function send(key, arg) { | ||
return new Promise(function (resolve, reject) { | ||
var request = { | ||
key: key, | ||
arg: arg, | ||
resolve: resolve, | ||
reject: reject, | ||
next: null | ||
}; | ||
if (back) { | ||
back = back.next = request; | ||
} else { | ||
front = back = request; | ||
resume(key, arg); | ||
} | ||
}); | ||
} | ||
function resume(key, arg) { | ||
try { | ||
var result = gen[key](arg); | ||
var value = result.value; | ||
if (value instanceof AwaitValue) { | ||
Promise.resolve(value.value).then(function (arg) { | ||
resume("next", arg); | ||
}, function (arg) { | ||
resume("throw", arg); | ||
}); | ||
} else { | ||
settle(result.done ? "return" : "normal", result.value); | ||
} | ||
} catch (err) { | ||
settle("throw", err); | ||
} | ||
} | ||
function settle(type, value) { | ||
switch (type) { | ||
case "return": | ||
front.resolve({ | ||
value: value, | ||
done: true | ||
}); | ||
break; | ||
case "throw": | ||
front.reject(value); | ||
break; | ||
default: | ||
front.resolve({ | ||
value: value, | ||
done: false | ||
}); | ||
break; | ||
} | ||
front = front.next; | ||
if (front) { | ||
resume(front.key, front.arg); | ||
} else { | ||
back = null; | ||
} | ||
} | ||
this._invoke = send; | ||
if (typeof gen.return !== "function") { | ||
this.return = undefined; | ||
} | ||
} | ||
if (typeof Symbol === "function" && Symbol.asyncIterator) { | ||
AsyncGenerator.prototype[Symbol.asyncIterator] = function () { | ||
return this; | ||
}; | ||
} | ||
AsyncGenerator.prototype.next = function (arg) { | ||
return this._invoke("next", arg); | ||
}; | ||
AsyncGenerator.prototype.throw = function (arg) { | ||
return this._invoke("throw", arg); | ||
}; | ||
AsyncGenerator.prototype.return = function (arg) { | ||
return this._invoke("return", arg); | ||
}; | ||
return { | ||
wrap: function (fn) { | ||
return function () { | ||
return new AsyncGenerator(fn.apply(this, arguments)); | ||
}; | ||
}, | ||
await: function (value) { | ||
return new AwaitValue(value); | ||
} | ||
}; | ||
}(); | ||
var classCallCheck = function (instance, Constructor) { | ||
@@ -134,12 +18,2 @@ if (!(instance instanceof Constructor)) { | ||
var inherits = function (subClass, superClass) { | ||
@@ -161,12 +35,2 @@ if (typeof superClass !== "function" && superClass !== null) { | ||
var possibleConstructorReturn = function (self, call) { | ||
@@ -180,3 +44,2 @@ if (!self) { | ||
// support VJS5 & VJS6 at the same time | ||
var dom = videojs.dom || videojs; | ||
@@ -203,3 +66,3 @@ var registerPlugin = videojs.registerPlugin || videojs.plugin; | ||
var defaults = { | ||
var defaults$1 = { | ||
className: 'vjs-playlist', | ||
@@ -605,3 +468,3 @@ playOnSelect: false, | ||
options = videojs.mergeOptions(defaults, options); | ||
options = videojs.mergeOptions(defaults$1, options); | ||
@@ -608,0 +471,0 @@ // If the player is already using this plugin, remove the pre-existing |
@@ -0,180 +1,13 @@ | ||
/*! @name videojs-playlist-ui @version 3.4.1 @license Apache-2.0 */ | ||
import document from 'global/document'; | ||
import videojs from 'video.js'; | ||
var version = "3.4.0"; | ||
var version = "3.4.1"; | ||
var asyncGenerator = function () { | ||
function AwaitValue(value) { | ||
this.value = value; | ||
} | ||
const dom = videojs.dom || videojs; | ||
const registerPlugin = videojs.registerPlugin || videojs.plugin; | ||
function AsyncGenerator(gen) { | ||
var front, back; | ||
function send(key, arg) { | ||
return new Promise(function (resolve, reject) { | ||
var request = { | ||
key: key, | ||
arg: arg, | ||
resolve: resolve, | ||
reject: reject, | ||
next: null | ||
}; | ||
if (back) { | ||
back = back.next = request; | ||
} else { | ||
front = back = request; | ||
resume(key, arg); | ||
} | ||
}); | ||
} | ||
function resume(key, arg) { | ||
try { | ||
var result = gen[key](arg); | ||
var value = result.value; | ||
if (value instanceof AwaitValue) { | ||
Promise.resolve(value.value).then(function (arg) { | ||
resume("next", arg); | ||
}, function (arg) { | ||
resume("throw", arg); | ||
}); | ||
} else { | ||
settle(result.done ? "return" : "normal", result.value); | ||
} | ||
} catch (err) { | ||
settle("throw", err); | ||
} | ||
} | ||
function settle(type, value) { | ||
switch (type) { | ||
case "return": | ||
front.resolve({ | ||
value: value, | ||
done: true | ||
}); | ||
break; | ||
case "throw": | ||
front.reject(value); | ||
break; | ||
default: | ||
front.resolve({ | ||
value: value, | ||
done: false | ||
}); | ||
break; | ||
} | ||
front = front.next; | ||
if (front) { | ||
resume(front.key, front.arg); | ||
} else { | ||
back = null; | ||
} | ||
} | ||
this._invoke = send; | ||
if (typeof gen.return !== "function") { | ||
this.return = undefined; | ||
} | ||
} | ||
if (typeof Symbol === "function" && Symbol.asyncIterator) { | ||
AsyncGenerator.prototype[Symbol.asyncIterator] = function () { | ||
return this; | ||
}; | ||
} | ||
AsyncGenerator.prototype.next = function (arg) { | ||
return this._invoke("next", arg); | ||
}; | ||
AsyncGenerator.prototype.throw = function (arg) { | ||
return this._invoke("throw", arg); | ||
}; | ||
AsyncGenerator.prototype.return = function (arg) { | ||
return this._invoke("return", arg); | ||
}; | ||
return { | ||
wrap: function (fn) { | ||
return function () { | ||
return new AsyncGenerator(fn.apply(this, arguments)); | ||
}; | ||
}, | ||
await: function (value) { | ||
return new AwaitValue(value); | ||
} | ||
}; | ||
}(); | ||
var classCallCheck = function (instance, Constructor) { | ||
if (!(instance instanceof Constructor)) { | ||
throw new TypeError("Cannot call a class as a function"); | ||
} | ||
}; | ||
var inherits = function (subClass, superClass) { | ||
if (typeof superClass !== "function" && superClass !== null) { | ||
throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); | ||
} | ||
subClass.prototype = Object.create(superClass && superClass.prototype, { | ||
constructor: { | ||
value: subClass, | ||
enumerable: false, | ||
writable: true, | ||
configurable: true | ||
} | ||
}); | ||
if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; | ||
}; | ||
var possibleConstructorReturn = function (self, call) { | ||
if (!self) { | ||
throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); | ||
} | ||
return call && (typeof call === "object" || typeof call === "function") ? call : self; | ||
}; | ||
// support VJS5 & VJS6 at the same time | ||
var dom = videojs.dom || videojs; | ||
var registerPlugin = videojs.registerPlugin || videojs.plugin; | ||
// Array#indexOf analog for IE8 | ||
var indexOf = function indexOf(array, target) { | ||
for (var i = 0, length = array.length; i < length; i++) { | ||
const indexOf = function(array, target) { | ||
for (let i = 0, length = array.length; i < length; i++) { | ||
if (array[i] === target) { | ||
@@ -188,13 +21,13 @@ return i; | ||
// see https://github.com/Modernizr/Modernizr/blob/master/feature-detects/css/pointerevents.js | ||
var supportsCssPointerEvents = function () { | ||
var element = document.createElement('x'); | ||
const supportsCssPointerEvents = (() => { | ||
const element = document.createElement('x'); | ||
element.style.cssText = 'pointer-events:auto'; | ||
return element.style.pointerEvents === 'auto'; | ||
}(); | ||
})(); | ||
var defaults = { | ||
const defaults = { | ||
className: 'vjs-playlist', | ||
playOnSelect: false, | ||
supportsCssPointerEvents: supportsCssPointerEvents | ||
supportsCssPointerEvents | ||
}; | ||
@@ -205,6 +38,6 @@ | ||
// since it'll get added when we mouse out | ||
var addSelectedClass = function addSelectedClass(el) { | ||
const addSelectedClass = function(el) { | ||
el.addClass('vjs-selected'); | ||
}; | ||
var removeSelectedClass = function removeSelectedClass(el) { | ||
const removeSelectedClass = function(el) { | ||
el.removeClass('vjs-selected'); | ||
@@ -217,12 +50,12 @@ | ||
var upNext = function upNext(el) { | ||
const upNext = function(el) { | ||
el.addClass('vjs-up-next'); | ||
}; | ||
var notUpNext = function notUpNext(el) { | ||
const notUpNext = function(el) { | ||
el.removeClass('vjs-up-next'); | ||
}; | ||
var createThumbnail = function createThumbnail(thumbnail) { | ||
const createThumbnail = function(thumbnail) { | ||
if (!thumbnail) { | ||
var placeholder = document.createElement('div'); | ||
const placeholder = document.createElement('div'); | ||
@@ -233,3 +66,3 @@ placeholder.className = 'vjs-playlist-thumbnail vjs-playlist-thumbnail-placeholder'; | ||
var picture = document.createElement('picture'); | ||
const picture = document.createElement('picture'); | ||
@@ -240,3 +73,3 @@ picture.className = 'vjs-playlist-thumbnail'; | ||
// simple thumbnails | ||
var img = document.createElement('img'); | ||
const img = document.createElement('img'); | ||
@@ -251,9 +84,9 @@ img.src = thumbnail; | ||
// <source> elements | ||
for (var i = 0; i < thumbnail.length - 1; i++) { | ||
var _variant = thumbnail[i]; | ||
var source = document.createElement('source'); | ||
for (let i = 0; i < thumbnail.length - 1; i++) { | ||
const variant = thumbnail[i]; | ||
const source = document.createElement('source'); | ||
// transfer the properties of each variant onto a <source> | ||
for (var prop in _variant) { | ||
source[prop] = _variant[prop]; | ||
for (const prop in variant) { | ||
source[prop] = variant[prop]; | ||
} | ||
@@ -264,10 +97,10 @@ picture.appendChild(source); | ||
// the default version of a <picture> is specified by an <img> | ||
var variant = thumbnail[thumbnail.length - 1]; | ||
var _img = document.createElement('img'); | ||
const variant = thumbnail[thumbnail.length - 1]; | ||
const img = document.createElement('img'); | ||
_img.alt = ''; | ||
for (var _prop in variant) { | ||
_img[_prop] = variant[_prop]; | ||
img.alt = ''; | ||
for (const prop in variant) { | ||
img[prop] = variant[prop]; | ||
} | ||
picture.appendChild(_img); | ||
picture.appendChild(img); | ||
} | ||
@@ -277,10 +110,7 @@ return picture; | ||
var Component = videojs.getComponent('Component'); | ||
const Component = videojs.getComponent('Component'); | ||
var PlaylistMenuItem = function (_Component) { | ||
inherits(PlaylistMenuItem, _Component); | ||
class PlaylistMenuItem extends Component { | ||
function PlaylistMenuItem(player, playlistItem, settings) { | ||
classCallCheck(this, PlaylistMenuItem); | ||
constructor(player, playlistItem, settings) { | ||
if (!playlistItem.item) { | ||
@@ -290,17 +120,15 @@ throw new Error('Cannot construct a PlaylistMenuItem without an item option'); | ||
var _this = possibleConstructorReturn(this, _Component.call(this, player, playlistItem)); | ||
super(player, playlistItem); | ||
this.item = playlistItem.item; | ||
_this.item = playlistItem.item; | ||
this.playOnSelect = settings.playOnSelect; | ||
_this.playOnSelect = settings.playOnSelect; | ||
this.emitTapEvents(); | ||
_this.emitTapEvents(); | ||
this.on(['click', 'tap'], this.switchPlaylistItem_); | ||
this.on('keydown', this.handleKeyDown_); | ||
_this.on(['click', 'tap'], _this.switchPlaylistItem_); | ||
_this.on('keydown', _this.handleKeyDown_); | ||
return _this; | ||
} | ||
PlaylistMenuItem.prototype.handleKeyDown_ = function handleKeyDown_(event) { | ||
handleKeyDown_(event) { | ||
// keycode 13 is <Enter> | ||
@@ -311,5 +139,5 @@ // keycode 32 is <Space> | ||
} | ||
}; | ||
} | ||
PlaylistMenuItem.prototype.switchPlaylistItem_ = function switchPlaylistItem_(event) { | ||
switchPlaylistItem_(event) { | ||
this.player_.playlist.currentItem(indexOf(this.player_.playlist(), this.item)); | ||
@@ -319,7 +147,7 @@ if (this.playOnSelect) { | ||
} | ||
}; | ||
} | ||
PlaylistMenuItem.prototype.createEl = function createEl() { | ||
var li = document.createElement('li'); | ||
var item = this.options_.item; | ||
createEl() { | ||
const li = document.createElement('li'); | ||
const item = this.options_.item; | ||
@@ -335,4 +163,4 @@ li.className = 'vjs-playlist-item'; | ||
if (item.duration) { | ||
var duration = document.createElement('time'); | ||
var time = videojs.formatTime(item.duration); | ||
const duration = document.createElement('time'); | ||
const time = videojs.formatTime(item.duration); | ||
@@ -346,4 +174,4 @@ duration.className = 'vjs-playlist-duration'; | ||
// Now playing | ||
var nowPlayingEl = document.createElement('span'); | ||
var nowPlayingText = this.localize('Now Playing'); | ||
const nowPlayingEl = document.createElement('span'); | ||
const nowPlayingText = this.localize('Now Playing'); | ||
@@ -356,3 +184,3 @@ nowPlayingEl.className = 'vjs-playlist-now-playing-text'; | ||
// Title container contains title and "up next" | ||
var titleContainerEl = document.createElement('div'); | ||
const titleContainerEl = document.createElement('div'); | ||
@@ -363,4 +191,4 @@ titleContainerEl.className = 'vjs-playlist-title-container'; | ||
// Up next | ||
var upNextEl = document.createElement('span'); | ||
var upNextText = this.localize('Up Next'); | ||
const upNextEl = document.createElement('span'); | ||
const upNextText = this.localize('Up Next'); | ||
@@ -373,4 +201,4 @@ upNextEl.className = 'vjs-up-next-text'; | ||
// Video title | ||
var titleEl = document.createElement('cite'); | ||
var titleText = item.name || this.localize('Untitled Video'); | ||
const titleEl = document.createElement('cite'); | ||
const titleText = item.name || this.localize('Untitled Video'); | ||
@@ -383,13 +211,8 @@ titleEl.className = 'vjs-playlist-name'; | ||
return li; | ||
}; | ||
} | ||
} | ||
return PlaylistMenuItem; | ||
}(Component); | ||
class PlaylistMenu extends Component { | ||
var PlaylistMenu = function (_Component2) { | ||
inherits(PlaylistMenu, _Component2); | ||
function PlaylistMenu(player, options) { | ||
classCallCheck(this, PlaylistMenu); | ||
constructor(player, options) { | ||
if (!player.playlist) { | ||
@@ -399,10 +222,9 @@ throw new Error('videojs-playlist is required for the playlist component'); | ||
var _this2 = possibleConstructorReturn(this, _Component2.call(this, player, options)); | ||
super(player, options); | ||
this.items = []; | ||
_this2.items = []; | ||
if (options.horizontal) { | ||
_this2.addClass('vjs-playlist-horizontal'); | ||
this.addClass('vjs-playlist-horizontal'); | ||
} else { | ||
_this2.addClass('vjs-playlist-vertical'); | ||
this.addClass('vjs-playlist-vertical'); | ||
} | ||
@@ -414,13 +236,13 @@ | ||
if (options.supportsCssPointerEvents) { | ||
_this2.addClass('vjs-csspointerevents'); | ||
this.addClass('vjs-csspointerevents'); | ||
} | ||
_this2.createPlaylist_(); | ||
this.createPlaylist_(); | ||
if (!videojs.browser.TOUCH_ENABLED) { | ||
_this2.addClass('vjs-mouse'); | ||
this.addClass('vjs-mouse'); | ||
} | ||
player.on(['loadstart', 'playlistchange', 'playlistsorted'], function (event) { | ||
_this2.update(); | ||
player.on(['loadstart', 'playlistchange', 'playlistsorted'], (event) => { | ||
this.update(); | ||
}); | ||
@@ -430,20 +252,19 @@ | ||
// appearance can be adapted appropriately | ||
player.on('adstart', function () { | ||
_this2.addClass('vjs-ad-playing'); | ||
player.on('adstart', () => { | ||
this.addClass('vjs-ad-playing'); | ||
}); | ||
player.on('adend', function () { | ||
_this2.removeClass('vjs-ad-playing'); | ||
player.on('adend', () => { | ||
this.removeClass('vjs-ad-playing'); | ||
}); | ||
return _this2; | ||
} | ||
PlaylistMenu.prototype.createEl = function createEl() { | ||
return dom.createEl('div', { className: this.options_.className }); | ||
}; | ||
createEl() { | ||
return dom.createEl('div', {className: this.options_.className}); | ||
} | ||
PlaylistMenu.prototype.createPlaylist_ = function createPlaylist_() { | ||
var playlist = this.player_.playlist() || []; | ||
var list = this.el_.querySelector('.vjs-playlist-item-list'); | ||
var overlay = this.el_.querySelector('.vjs-playlist-ad-overlay'); | ||
createPlaylist_() { | ||
const playlist = this.player_.playlist() || []; | ||
let list = this.el_.querySelector('.vjs-playlist-item-list'); | ||
let overlay = this.el_.querySelector('.vjs-playlist-ad-overlay'); | ||
@@ -457,3 +278,3 @@ if (!list) { | ||
// remove any existing items | ||
for (var i = 0; i < this.items.length; i++) { | ||
for (let i = 0; i < this.items.length; i++) { | ||
list.removeChild(this.items[i].el_); | ||
@@ -464,5 +285,5 @@ } | ||
// create new items | ||
for (var _i = 0; _i < playlist.length; _i++) { | ||
var item = new PlaylistMenuItem(this.player_, { | ||
item: playlist[_i] | ||
for (let i = 0; i < playlist.length; i++) { | ||
const item = new PlaylistMenuItem(this.player_, { | ||
item: playlist[i] | ||
}, this.options_); | ||
@@ -487,3 +308,3 @@ | ||
// select the current playlist item | ||
var selectedIndex = this.player_.playlist.currentItem(); | ||
const selectedIndex = this.player_.playlist.currentItem(); | ||
@@ -493,3 +314,3 @@ if (this.items.length && selectedIndex >= 0) { | ||
var thumbnail = this.items[selectedIndex].$('.vjs-playlist-thumbnail'); | ||
const thumbnail = this.items[selectedIndex].$('.vjs-playlist-thumbnail'); | ||
@@ -500,7 +321,7 @@ if (thumbnail) { | ||
} | ||
}; | ||
} | ||
PlaylistMenu.prototype.update = function update() { | ||
update() { | ||
// replace the playlist items being displayed, if necessary | ||
var playlist = this.player_.playlist(); | ||
const playlist = this.player_.playlist(); | ||
@@ -514,3 +335,3 @@ if (this.items.length !== playlist.length) { | ||
for (var i = 0; i < this.items.length; i++) { | ||
for (let i = 0; i < this.items.length; i++) { | ||
if (this.items[i].item !== playlist[i]) { | ||
@@ -525,8 +346,8 @@ // if any of the playlist items have changed, rebuild the | ||
// the playlist itself is unchanged so just update the selection | ||
var currentItem = this.player_.playlist.currentItem(); | ||
const currentItem = this.player_.playlist.currentItem(); | ||
for (var _i2 = 0; _i2 < this.items.length; _i2++) { | ||
var item = this.items[_i2]; | ||
for (let i = 0; i < this.items.length; i++) { | ||
const item = this.items[i]; | ||
if (_i2 === currentItem) { | ||
if (i === currentItem) { | ||
addSelectedClass(item); | ||
@@ -537,3 +358,3 @@ if (document.activeElement !== item.el()) { | ||
notUpNext(item); | ||
} else if (_i2 === currentItem + 1) { | ||
} else if (i === currentItem + 1) { | ||
removeSelectedClass(item); | ||
@@ -546,7 +367,5 @@ upNext(item); | ||
} | ||
}; | ||
} | ||
} | ||
return PlaylistMenu; | ||
}(Component); | ||
/** | ||
@@ -563,6 +382,4 @@ * Returns a boolean indicating whether an element has child elements. | ||
*/ | ||
var hasChildEls = function hasChildEls(el) { | ||
for (var i = 0; i < el.childNodes.length; i++) { | ||
const hasChildEls = (el) => { | ||
for (let i = 0; i < el.childNodes.length; i++) { | ||
if (dom.isEl(el.childNodes[i])) { | ||
@@ -584,7 +401,7 @@ return true; | ||
*/ | ||
var findRoot = function findRoot(className) { | ||
var all = document.querySelectorAll('.' + className); | ||
var el = void 0; | ||
const findRoot = (className) => { | ||
const all = document.querySelectorAll('.' + className); | ||
let el; | ||
for (var i = 0; i < all.length; i++) { | ||
for (let i = 0; i < all.length; i++) { | ||
if (!hasChildEls(all[i])) { | ||
@@ -615,4 +432,4 @@ el = all[i]; | ||
*/ | ||
var playlistUi = function playlistUi(options) { | ||
var player = this; | ||
const playlistUi = function(options) { | ||
const player = this; | ||
@@ -625,3 +442,3 @@ if (!player.playlist) { | ||
videojs.log.warn('videojs-playlist-ui: Passing an element directly to playlistUi() is deprecated, use the "el" option instead!'); | ||
options = { el: options }; | ||
options = {el: options}; | ||
} | ||
@@ -635,3 +452,3 @@ | ||
if (player.playlistMenu) { | ||
var el = player.playlistMenu.el(); | ||
const el = player.playlistMenu.el(); | ||
@@ -641,4 +458,4 @@ // Catch cases where the menu may have been disposed elsewhere or the | ||
if (el) { | ||
var parentNode = el.parentNode; | ||
var nextSibling = el.nextSibling; | ||
const parentNode = el.parentNode; | ||
const nextSibling = el.nextSibling; | ||
@@ -645,0 +462,0 @@ // Disposing the menu will remove `el` from the DOM, but we need to |
@@ -1,684 +0,513 @@ | ||
/** | ||
* videojs-playlist-ui | ||
* @version 3.4.0 | ||
* @copyright 2018 Brightcove, Inc. | ||
* @license Apache-2.0 | ||
*/ | ||
/*! @name videojs-playlist-ui @version 3.4.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.videojsPlaylistUi = factory(global.videojs)); | ||
}(this, (function (videojs) { 'use strict'; | ||
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('global/document'), require('video.js')) : | ||
typeof define === 'function' && define.amd ? define(['global/document', 'video.js'], factory) : | ||
(global.videojsPlaylistUi = factory(global.document,global.videojs)); | ||
}(this, (function (document,videojs) { 'use strict'; | ||
videojs = videojs && videojs.hasOwnProperty('default') ? videojs['default'] : videojs; | ||
document = document && document.hasOwnProperty('default') ? document['default'] : document; | ||
videojs = videojs && videojs.hasOwnProperty('default') ? videojs['default'] : videojs; | ||
var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; | ||
var version = "3.4.1"; | ||
var empty = {}; | ||
var empty$1 = (Object.freeze || Object)({ | ||
'default': empty | ||
}); | ||
var minDoc = ( empty$1 && empty ) || empty$1; | ||
var topLevel = typeof commonjsGlobal !== 'undefined' ? commonjsGlobal : | ||
typeof window !== 'undefined' ? window : {}; | ||
var doccy; | ||
if (typeof document !== 'undefined') { | ||
doccy = document; | ||
} else { | ||
doccy = topLevel['__GLOBAL_DOCUMENT_CACHE@4']; | ||
if (!doccy) { | ||
doccy = topLevel['__GLOBAL_DOCUMENT_CACHE@4'] = minDoc; | ||
var classCallCheck = function (instance, Constructor) { | ||
if (!(instance instanceof Constructor)) { | ||
throw new TypeError("Cannot call a class as a function"); | ||
} | ||
} | ||
}; | ||
var document_1 = doccy; | ||
var version = "3.4.0"; | ||
var asyncGenerator = function () { | ||
function AwaitValue(value) { | ||
this.value = value; | ||
} | ||
function AsyncGenerator(gen) { | ||
var front, back; | ||
function send(key, arg) { | ||
return new Promise(function (resolve, reject) { | ||
var request = { | ||
key: key, | ||
arg: arg, | ||
resolve: resolve, | ||
reject: reject, | ||
next: null | ||
}; | ||
if (back) { | ||
back = back.next = request; | ||
} else { | ||
front = back = request; | ||
resume(key, arg); | ||
} | ||
}); | ||
var inherits = function (subClass, superClass) { | ||
if (typeof superClass !== "function" && superClass !== null) { | ||
throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); | ||
} | ||
function resume(key, arg) { | ||
try { | ||
var result = gen[key](arg); | ||
var value = result.value; | ||
subClass.prototype = Object.create(superClass && superClass.prototype, { | ||
constructor: { | ||
value: subClass, | ||
enumerable: false, | ||
writable: true, | ||
configurable: true | ||
} | ||
}); | ||
if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; | ||
}; | ||
if (value instanceof AwaitValue) { | ||
Promise.resolve(value.value).then(function (arg) { | ||
resume("next", arg); | ||
}, function (arg) { | ||
resume("throw", arg); | ||
}); | ||
} else { | ||
settle(result.done ? "return" : "normal", result.value); | ||
} | ||
} catch (err) { | ||
settle("throw", err); | ||
} | ||
var possibleConstructorReturn = function (self, call) { | ||
if (!self) { | ||
throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); | ||
} | ||
function settle(type, value) { | ||
switch (type) { | ||
case "return": | ||
front.resolve({ | ||
value: value, | ||
done: true | ||
}); | ||
break; | ||
return call && (typeof call === "object" || typeof call === "function") ? call : self; | ||
}; | ||
case "throw": | ||
front.reject(value); | ||
break; | ||
var dom = videojs.dom || videojs; | ||
var registerPlugin = videojs.registerPlugin || videojs.plugin; | ||
default: | ||
front.resolve({ | ||
value: value, | ||
done: false | ||
}); | ||
break; | ||
// Array#indexOf analog for IE8 | ||
var indexOf = function indexOf(array, target) { | ||
for (var i = 0, length = array.length; i < length; i++) { | ||
if (array[i] === target) { | ||
return i; | ||
} | ||
front = front.next; | ||
if (front) { | ||
resume(front.key, front.arg); | ||
} else { | ||
back = null; | ||
} | ||
} | ||
return -1; | ||
}; | ||
this._invoke = send; | ||
// see https://github.com/Modernizr/Modernizr/blob/master/feature-detects/css/pointerevents.js | ||
var supportsCssPointerEvents = function () { | ||
var element = document.createElement('x'); | ||
if (typeof gen.return !== "function") { | ||
this.return = undefined; | ||
} | ||
} | ||
element.style.cssText = 'pointer-events:auto'; | ||
return element.style.pointerEvents === 'auto'; | ||
}(); | ||
if (typeof Symbol === "function" && Symbol.asyncIterator) { | ||
AsyncGenerator.prototype[Symbol.asyncIterator] = function () { | ||
return this; | ||
}; | ||
} | ||
AsyncGenerator.prototype.next = function (arg) { | ||
return this._invoke("next", arg); | ||
var defaults$1 = { | ||
className: 'vjs-playlist', | ||
playOnSelect: false, | ||
supportsCssPointerEvents: supportsCssPointerEvents | ||
}; | ||
AsyncGenerator.prototype.throw = function (arg) { | ||
return this._invoke("throw", arg); | ||
// we don't add `vjs-playlist-now-playing` in addSelectedClass | ||
// so it won't conflict with `vjs-icon-play | ||
// since it'll get added when we mouse out | ||
var addSelectedClass = function addSelectedClass(el) { | ||
el.addClass('vjs-selected'); | ||
}; | ||
var removeSelectedClass = function removeSelectedClass(el) { | ||
el.removeClass('vjs-selected'); | ||
AsyncGenerator.prototype.return = function (arg) { | ||
return this._invoke("return", arg); | ||
if (el.thumbnail) { | ||
dom.removeClass(el.thumbnail, 'vjs-playlist-now-playing'); | ||
} | ||
}; | ||
return { | ||
wrap: function (fn) { | ||
return function () { | ||
return new AsyncGenerator(fn.apply(this, arguments)); | ||
}; | ||
}, | ||
await: function (value) { | ||
return new AwaitValue(value); | ||
} | ||
var upNext = function upNext(el) { | ||
el.addClass('vjs-up-next'); | ||
}; | ||
}(); | ||
var notUpNext = function notUpNext(el) { | ||
el.removeClass('vjs-up-next'); | ||
}; | ||
var createThumbnail = function createThumbnail(thumbnail) { | ||
if (!thumbnail) { | ||
var placeholder = document.createElement('div'); | ||
placeholder.className = 'vjs-playlist-thumbnail vjs-playlist-thumbnail-placeholder'; | ||
return placeholder; | ||
} | ||
var picture = document.createElement('picture'); | ||
picture.className = 'vjs-playlist-thumbnail'; | ||
var classCallCheck = function (instance, Constructor) { | ||
if (!(instance instanceof Constructor)) { | ||
throw new TypeError("Cannot call a class as a function"); | ||
} | ||
}; | ||
if (typeof thumbnail === 'string') { | ||
// simple thumbnails | ||
var img = document.createElement('img'); | ||
img.src = thumbnail; | ||
img.alt = ''; | ||
picture.appendChild(img); | ||
} else { | ||
// responsive thumbnails | ||
// additional variations of a <picture> are specified as | ||
// <source> elements | ||
for (var i = 0; i < thumbnail.length - 1; i++) { | ||
var _variant = thumbnail[i]; | ||
var source = document.createElement('source'); | ||
// transfer the properties of each variant onto a <source> | ||
for (var prop in _variant) { | ||
source[prop] = _variant[prop]; | ||
} | ||
picture.appendChild(source); | ||
} | ||
// the default version of a <picture> is specified by an <img> | ||
var variant = thumbnail[thumbnail.length - 1]; | ||
var _img = document.createElement('img'); | ||
var inherits = function (subClass, superClass) { | ||
if (typeof superClass !== "function" && superClass !== null) { | ||
throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); | ||
} | ||
subClass.prototype = Object.create(superClass && superClass.prototype, { | ||
constructor: { | ||
value: subClass, | ||
enumerable: false, | ||
writable: true, | ||
configurable: true | ||
_img.alt = ''; | ||
for (var _prop in variant) { | ||
_img[_prop] = variant[_prop]; | ||
} | ||
picture.appendChild(_img); | ||
} | ||
}); | ||
if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; | ||
}; | ||
return picture; | ||
}; | ||
var Component = videojs.getComponent('Component'); | ||
var PlaylistMenuItem = function (_Component) { | ||
inherits(PlaylistMenuItem, _Component); | ||
function PlaylistMenuItem(player, playlistItem, settings) { | ||
classCallCheck(this, PlaylistMenuItem); | ||
if (!playlistItem.item) { | ||
throw new Error('Cannot construct a PlaylistMenuItem without an item option'); | ||
} | ||
var _this = possibleConstructorReturn(this, _Component.call(this, player, playlistItem)); | ||
_this.item = playlistItem.item; | ||
_this.playOnSelect = settings.playOnSelect; | ||
_this.emitTapEvents(); | ||
_this.on(['click', 'tap'], _this.switchPlaylistItem_); | ||
_this.on('keydown', _this.handleKeyDown_); | ||
var possibleConstructorReturn = function (self, call) { | ||
if (!self) { | ||
throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); | ||
} | ||
return call && (typeof call === "object" || typeof call === "function") ? call : self; | ||
}; | ||
// support VJS5 & VJS6 at the same time | ||
var dom = videojs.dom || videojs; | ||
var registerPlugin = videojs.registerPlugin || videojs.plugin; | ||
// Array#indexOf analog for IE8 | ||
var indexOf = function indexOf(array, target) { | ||
for (var i = 0, length = array.length; i < length; i++) { | ||
if (array[i] === target) { | ||
return i; | ||
return _this; | ||
} | ||
} | ||
return -1; | ||
}; | ||
// see https://github.com/Modernizr/Modernizr/blob/master/feature-detects/css/pointerevents.js | ||
var supportsCssPointerEvents = function () { | ||
var element = document_1.createElement('x'); | ||
PlaylistMenuItem.prototype.handleKeyDown_ = function handleKeyDown_(event) { | ||
// keycode 13 is <Enter> | ||
// keycode 32 is <Space> | ||
if (event.which === 13 || event.which === 32) { | ||
this.switchPlaylistItem_(); | ||
} | ||
}; | ||
element.style.cssText = 'pointer-events:auto'; | ||
return element.style.pointerEvents === 'auto'; | ||
}(); | ||
PlaylistMenuItem.prototype.switchPlaylistItem_ = function switchPlaylistItem_(event) { | ||
this.player_.playlist.currentItem(indexOf(this.player_.playlist(), this.item)); | ||
if (this.playOnSelect) { | ||
this.player_.play(); | ||
} | ||
}; | ||
var defaults = { | ||
className: 'vjs-playlist', | ||
playOnSelect: false, | ||
supportsCssPointerEvents: supportsCssPointerEvents | ||
}; | ||
PlaylistMenuItem.prototype.createEl = function createEl() { | ||
var li = document.createElement('li'); | ||
var item = this.options_.item; | ||
// we don't add `vjs-playlist-now-playing` in addSelectedClass | ||
// so it won't conflict with `vjs-icon-play | ||
// since it'll get added when we mouse out | ||
var addSelectedClass = function addSelectedClass(el) { | ||
el.addClass('vjs-selected'); | ||
}; | ||
var removeSelectedClass = function removeSelectedClass(el) { | ||
el.removeClass('vjs-selected'); | ||
li.className = 'vjs-playlist-item'; | ||
li.setAttribute('tabIndex', 0); | ||
if (el.thumbnail) { | ||
dom.removeClass(el.thumbnail, 'vjs-playlist-now-playing'); | ||
} | ||
}; | ||
// Thumbnail image | ||
this.thumbnail = createThumbnail(item.thumbnail); | ||
li.appendChild(this.thumbnail); | ||
var upNext = function upNext(el) { | ||
el.addClass('vjs-up-next'); | ||
}; | ||
var notUpNext = function notUpNext(el) { | ||
el.removeClass('vjs-up-next'); | ||
}; | ||
// Duration | ||
if (item.duration) { | ||
var duration = document.createElement('time'); | ||
var time = videojs.formatTime(item.duration); | ||
var createThumbnail = function createThumbnail(thumbnail) { | ||
if (!thumbnail) { | ||
var placeholder = document_1.createElement('div'); | ||
duration.className = 'vjs-playlist-duration'; | ||
duration.setAttribute('datetime', 'PT0H0M' + item.duration + 'S'); | ||
duration.appendChild(document.createTextNode(time)); | ||
li.appendChild(duration); | ||
} | ||
placeholder.className = 'vjs-playlist-thumbnail vjs-playlist-thumbnail-placeholder'; | ||
return placeholder; | ||
} | ||
// Now playing | ||
var nowPlayingEl = document.createElement('span'); | ||
var nowPlayingText = this.localize('Now Playing'); | ||
var picture = document_1.createElement('picture'); | ||
nowPlayingEl.className = 'vjs-playlist-now-playing-text'; | ||
nowPlayingEl.appendChild(document.createTextNode(nowPlayingText)); | ||
nowPlayingEl.setAttribute('title', nowPlayingText); | ||
this.thumbnail.appendChild(nowPlayingEl); | ||
picture.className = 'vjs-playlist-thumbnail'; | ||
// Title container contains title and "up next" | ||
var titleContainerEl = document.createElement('div'); | ||
if (typeof thumbnail === 'string') { | ||
// simple thumbnails | ||
var img = document_1.createElement('img'); | ||
titleContainerEl.className = 'vjs-playlist-title-container'; | ||
this.thumbnail.appendChild(titleContainerEl); | ||
img.src = thumbnail; | ||
img.alt = ''; | ||
picture.appendChild(img); | ||
} else { | ||
// responsive thumbnails | ||
// Up next | ||
var upNextEl = document.createElement('span'); | ||
var upNextText = this.localize('Up Next'); | ||
// additional variations of a <picture> are specified as | ||
// <source> elements | ||
for (var i = 0; i < thumbnail.length - 1; i++) { | ||
var _variant = thumbnail[i]; | ||
var source = document_1.createElement('source'); | ||
upNextEl.className = 'vjs-up-next-text'; | ||
upNextEl.appendChild(document.createTextNode(upNextText)); | ||
upNextEl.setAttribute('title', upNextText); | ||
titleContainerEl.appendChild(upNextEl); | ||
// transfer the properties of each variant onto a <source> | ||
for (var prop in _variant) { | ||
source[prop] = _variant[prop]; | ||
} | ||
picture.appendChild(source); | ||
} | ||
// Video title | ||
var titleEl = document.createElement('cite'); | ||
var titleText = item.name || this.localize('Untitled Video'); | ||
// the default version of a <picture> is specified by an <img> | ||
var variant = thumbnail[thumbnail.length - 1]; | ||
var _img = document_1.createElement('img'); | ||
titleEl.className = 'vjs-playlist-name'; | ||
titleEl.appendChild(document.createTextNode(titleText)); | ||
titleEl.setAttribute('title', titleText); | ||
titleContainerEl.appendChild(titleEl); | ||
_img.alt = ''; | ||
for (var _prop in variant) { | ||
_img[_prop] = variant[_prop]; | ||
} | ||
picture.appendChild(_img); | ||
} | ||
return picture; | ||
}; | ||
return li; | ||
}; | ||
var Component = videojs.getComponent('Component'); | ||
return PlaylistMenuItem; | ||
}(Component); | ||
var PlaylistMenuItem = function (_Component) { | ||
inherits(PlaylistMenuItem, _Component); | ||
var PlaylistMenu = function (_Component2) { | ||
inherits(PlaylistMenu, _Component2); | ||
function PlaylistMenuItem(player, playlistItem, settings) { | ||
classCallCheck(this, PlaylistMenuItem); | ||
function PlaylistMenu(player, options) { | ||
classCallCheck(this, PlaylistMenu); | ||
if (!playlistItem.item) { | ||
throw new Error('Cannot construct a PlaylistMenuItem without an item option'); | ||
} | ||
if (!player.playlist) { | ||
throw new Error('videojs-playlist is required for the playlist component'); | ||
} | ||
var _this = possibleConstructorReturn(this, _Component.call(this, player, playlistItem)); | ||
var _this2 = possibleConstructorReturn(this, _Component2.call(this, player, options)); | ||
_this.item = playlistItem.item; | ||
_this2.items = []; | ||
_this.playOnSelect = settings.playOnSelect; | ||
if (options.horizontal) { | ||
_this2.addClass('vjs-playlist-horizontal'); | ||
} else { | ||
_this2.addClass('vjs-playlist-vertical'); | ||
} | ||
_this.emitTapEvents(); | ||
// If CSS pointer events aren't supported, we have to prevent | ||
// clicking on playlist items during ads with slightly more | ||
// invasive techniques. Details in the stylesheet. | ||
if (options.supportsCssPointerEvents) { | ||
_this2.addClass('vjs-csspointerevents'); | ||
} | ||
_this.on(['click', 'tap'], _this.switchPlaylistItem_); | ||
_this.on('keydown', _this.handleKeyDown_); | ||
_this2.createPlaylist_(); | ||
return _this; | ||
} | ||
if (!videojs.browser.TOUCH_ENABLED) { | ||
_this2.addClass('vjs-mouse'); | ||
} | ||
PlaylistMenuItem.prototype.handleKeyDown_ = function handleKeyDown_(event) { | ||
// keycode 13 is <Enter> | ||
// keycode 32 is <Space> | ||
if (event.which === 13 || event.which === 32) { | ||
this.switchPlaylistItem_(); | ||
} | ||
}; | ||
player.on(['loadstart', 'playlistchange', 'playlistsorted'], function (event) { | ||
_this2.update(); | ||
}); | ||
PlaylistMenuItem.prototype.switchPlaylistItem_ = function switchPlaylistItem_(event) { | ||
this.player_.playlist.currentItem(indexOf(this.player_.playlist(), this.item)); | ||
if (this.playOnSelect) { | ||
this.player_.play(); | ||
} | ||
}; | ||
// Keep track of whether an ad is playing so that the menu | ||
// appearance can be adapted appropriately | ||
player.on('adstart', function () { | ||
_this2.addClass('vjs-ad-playing'); | ||
}); | ||
PlaylistMenuItem.prototype.createEl = function createEl() { | ||
var li = document_1.createElement('li'); | ||
var item = this.options_.item; | ||
li.className = 'vjs-playlist-item'; | ||
li.setAttribute('tabIndex', 0); | ||
// Thumbnail image | ||
this.thumbnail = createThumbnail(item.thumbnail); | ||
li.appendChild(this.thumbnail); | ||
// Duration | ||
if (item.duration) { | ||
var duration = document_1.createElement('time'); | ||
var time = videojs.formatTime(item.duration); | ||
duration.className = 'vjs-playlist-duration'; | ||
duration.setAttribute('datetime', 'PT0H0M' + item.duration + 'S'); | ||
duration.appendChild(document_1.createTextNode(time)); | ||
li.appendChild(duration); | ||
player.on('adend', function () { | ||
_this2.removeClass('vjs-ad-playing'); | ||
}); | ||
return _this2; | ||
} | ||
// Now playing | ||
var nowPlayingEl = document_1.createElement('span'); | ||
var nowPlayingText = this.localize('Now Playing'); | ||
PlaylistMenu.prototype.createEl = function createEl() { | ||
return dom.createEl('div', { className: this.options_.className }); | ||
}; | ||
nowPlayingEl.className = 'vjs-playlist-now-playing-text'; | ||
nowPlayingEl.appendChild(document_1.createTextNode(nowPlayingText)); | ||
nowPlayingEl.setAttribute('title', nowPlayingText); | ||
this.thumbnail.appendChild(nowPlayingEl); | ||
PlaylistMenu.prototype.createPlaylist_ = function createPlaylist_() { | ||
var playlist = this.player_.playlist() || []; | ||
var list = this.el_.querySelector('.vjs-playlist-item-list'); | ||
var overlay = this.el_.querySelector('.vjs-playlist-ad-overlay'); | ||
// Title container contains title and "up next" | ||
var titleContainerEl = document_1.createElement('div'); | ||
if (!list) { | ||
list = document.createElement('ol'); | ||
list.className = 'vjs-playlist-item-list'; | ||
this.el_.appendChild(list); | ||
} | ||
titleContainerEl.className = 'vjs-playlist-title-container'; | ||
this.thumbnail.appendChild(titleContainerEl); | ||
// remove any existing items | ||
for (var i = 0; i < this.items.length; i++) { | ||
list.removeChild(this.items[i].el_); | ||
} | ||
this.items.length = 0; | ||
// Up next | ||
var upNextEl = document_1.createElement('span'); | ||
var upNextText = this.localize('Up Next'); | ||
// create new items | ||
for (var _i = 0; _i < playlist.length; _i++) { | ||
var item = new PlaylistMenuItem(this.player_, { | ||
item: playlist[_i] | ||
}, this.options_); | ||
upNextEl.className = 'vjs-up-next-text'; | ||
upNextEl.appendChild(document_1.createTextNode(upNextText)); | ||
upNextEl.setAttribute('title', upNextText); | ||
titleContainerEl.appendChild(upNextEl); | ||
this.items.push(item); | ||
list.appendChild(item.el_); | ||
} | ||
// Video title | ||
var titleEl = document_1.createElement('cite'); | ||
var titleText = item.name || this.localize('Untitled Video'); | ||
// Inject the ad overlay. IE<11 doesn't support "pointer-events: | ||
// none" so we use this element to block clicks during ad | ||
// playback. | ||
if (!overlay) { | ||
overlay = document.createElement('li'); | ||
overlay.className = 'vjs-playlist-ad-overlay'; | ||
list.appendChild(overlay); | ||
} else { | ||
// Move overlay to end of list | ||
list.appendChild(overlay); | ||
} | ||
titleEl.className = 'vjs-playlist-name'; | ||
titleEl.appendChild(document_1.createTextNode(titleText)); | ||
titleEl.setAttribute('title', titleText); | ||
titleContainerEl.appendChild(titleEl); | ||
// select the current playlist item | ||
var selectedIndex = this.player_.playlist.currentItem(); | ||
return li; | ||
}; | ||
if (this.items.length && selectedIndex >= 0) { | ||
addSelectedClass(this.items[selectedIndex]); | ||
return PlaylistMenuItem; | ||
}(Component); | ||
var thumbnail = this.items[selectedIndex].$('.vjs-playlist-thumbnail'); | ||
var PlaylistMenu = function (_Component2) { | ||
inherits(PlaylistMenu, _Component2); | ||
if (thumbnail) { | ||
dom.addClass(thumbnail, 'vjs-playlist-now-playing'); | ||
} | ||
} | ||
}; | ||
function PlaylistMenu(player, options) { | ||
classCallCheck(this, PlaylistMenu); | ||
PlaylistMenu.prototype.update = function update() { | ||
// replace the playlist items being displayed, if necessary | ||
var playlist = this.player_.playlist(); | ||
if (!player.playlist) { | ||
throw new Error('videojs-playlist is required for the playlist component'); | ||
} | ||
if (this.items.length !== playlist.length) { | ||
// if the menu is currently empty or the state is obviously out | ||
// of date, rebuild everything. | ||
this.createPlaylist_(); | ||
return; | ||
} | ||
var _this2 = possibleConstructorReturn(this, _Component2.call(this, player, options)); | ||
for (var i = 0; i < this.items.length; i++) { | ||
if (this.items[i].item !== playlist[i]) { | ||
// if any of the playlist items have changed, rebuild the | ||
// entire playlist | ||
this.createPlaylist_(); | ||
return; | ||
} | ||
} | ||
_this2.items = []; | ||
// the playlist itself is unchanged so just update the selection | ||
var currentItem = this.player_.playlist.currentItem(); | ||
if (options.horizontal) { | ||
_this2.addClass('vjs-playlist-horizontal'); | ||
} else { | ||
_this2.addClass('vjs-playlist-vertical'); | ||
} | ||
for (var _i2 = 0; _i2 < this.items.length; _i2++) { | ||
var item = this.items[_i2]; | ||
// If CSS pointer events aren't supported, we have to prevent | ||
// clicking on playlist items during ads with slightly more | ||
// invasive techniques. Details in the stylesheet. | ||
if (options.supportsCssPointerEvents) { | ||
_this2.addClass('vjs-csspointerevents'); | ||
} | ||
if (_i2 === currentItem) { | ||
addSelectedClass(item); | ||
if (document.activeElement !== item.el()) { | ||
dom.addClass(item.thumbnail, 'vjs-playlist-now-playing'); | ||
} | ||
notUpNext(item); | ||
} else if (_i2 === currentItem + 1) { | ||
removeSelectedClass(item); | ||
upNext(item); | ||
} else { | ||
removeSelectedClass(item); | ||
notUpNext(item); | ||
} | ||
} | ||
}; | ||
_this2.createPlaylist_(); | ||
return PlaylistMenu; | ||
}(Component); | ||
if (!videojs.browser.TOUCH_ENABLED) { | ||
_this2.addClass('vjs-mouse'); | ||
} | ||
/** | ||
* Returns a boolean indicating whether an element has child elements. | ||
* | ||
* Note that this is distinct from whether it has child _nodes_. | ||
* | ||
* @param {HTMLElement} el | ||
* A DOM element. | ||
* | ||
* @return {boolean} | ||
* Whether the element has child elements. | ||
*/ | ||
player.on(['loadstart', 'playlistchange', 'playlistsorted'], function (event) { | ||
_this2.update(); | ||
}); | ||
// Keep track of whether an ad is playing so that the menu | ||
// appearance can be adapted appropriately | ||
player.on('adstart', function () { | ||
_this2.addClass('vjs-ad-playing'); | ||
}); | ||
player.on('adend', function () { | ||
_this2.removeClass('vjs-ad-playing'); | ||
}); | ||
return _this2; | ||
} | ||
PlaylistMenu.prototype.createEl = function createEl() { | ||
return dom.createEl('div', { className: this.options_.className }); | ||
}; | ||
PlaylistMenu.prototype.createPlaylist_ = function createPlaylist_() { | ||
var playlist = this.player_.playlist() || []; | ||
var list = this.el_.querySelector('.vjs-playlist-item-list'); | ||
var overlay = this.el_.querySelector('.vjs-playlist-ad-overlay'); | ||
if (!list) { | ||
list = document_1.createElement('ol'); | ||
list.className = 'vjs-playlist-item-list'; | ||
this.el_.appendChild(list); | ||
} | ||
// remove any existing items | ||
for (var i = 0; i < this.items.length; i++) { | ||
list.removeChild(this.items[i].el_); | ||
} | ||
this.items.length = 0; | ||
// create new items | ||
for (var _i = 0; _i < playlist.length; _i++) { | ||
var item = new PlaylistMenuItem(this.player_, { | ||
item: playlist[_i] | ||
}, this.options_); | ||
this.items.push(item); | ||
list.appendChild(item.el_); | ||
} | ||
// Inject the ad overlay. IE<11 doesn't support "pointer-events: | ||
// none" so we use this element to block clicks during ad | ||
// playback. | ||
if (!overlay) { | ||
overlay = document_1.createElement('li'); | ||
overlay.className = 'vjs-playlist-ad-overlay'; | ||
list.appendChild(overlay); | ||
} else { | ||
// Move overlay to end of list | ||
list.appendChild(overlay); | ||
} | ||
// select the current playlist item | ||
var selectedIndex = this.player_.playlist.currentItem(); | ||
if (this.items.length && selectedIndex >= 0) { | ||
addSelectedClass(this.items[selectedIndex]); | ||
var thumbnail = this.items[selectedIndex].$('.vjs-playlist-thumbnail'); | ||
if (thumbnail) { | ||
dom.addClass(thumbnail, 'vjs-playlist-now-playing'); | ||
var hasChildEls = function hasChildEls(el) { | ||
for (var i = 0; i < el.childNodes.length; i++) { | ||
if (dom.isEl(el.childNodes[i])) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
}; | ||
PlaylistMenu.prototype.update = function update() { | ||
// replace the playlist items being displayed, if necessary | ||
var playlist = this.player_.playlist(); | ||
/** | ||
* Finds the first empty root element. | ||
* | ||
* @param {string} className | ||
* An HTML class name to search for. | ||
* | ||
* @return {HTMLElement} | ||
* A DOM element to use as the root for a playlist. | ||
*/ | ||
var findRoot = function findRoot(className) { | ||
var all = document.querySelectorAll('.' + className); | ||
var el = void 0; | ||
if (this.items.length !== playlist.length) { | ||
// if the menu is currently empty or the state is obviously out | ||
// of date, rebuild everything. | ||
this.createPlaylist_(); | ||
return; | ||
} | ||
for (var i = 0; i < this.items.length; i++) { | ||
if (this.items[i].item !== playlist[i]) { | ||
// if any of the playlist items have changed, rebuild the | ||
// entire playlist | ||
this.createPlaylist_(); | ||
return; | ||
for (var i = 0; i < all.length; i++) { | ||
if (!hasChildEls(all[i])) { | ||
el = all[i]; | ||
break; | ||
} | ||
} | ||
// the playlist itself is unchanged so just update the selection | ||
var currentItem = this.player_.playlist.currentItem(); | ||
for (var _i2 = 0; _i2 < this.items.length; _i2++) { | ||
var item = this.items[_i2]; | ||
if (_i2 === currentItem) { | ||
addSelectedClass(item); | ||
if (document_1.activeElement !== item.el()) { | ||
dom.addClass(item.thumbnail, 'vjs-playlist-now-playing'); | ||
} | ||
notUpNext(item); | ||
} else if (_i2 === currentItem + 1) { | ||
removeSelectedClass(item); | ||
upNext(item); | ||
} else { | ||
removeSelectedClass(item); | ||
notUpNext(item); | ||
} | ||
} | ||
return el; | ||
}; | ||
return PlaylistMenu; | ||
}(Component); | ||
/** | ||
* Initialize the plugin on a player. | ||
* | ||
* @param {Object} [options] | ||
* An options object. | ||
* | ||
* @param {HTMLElement} [options.el] | ||
* A DOM element to use as a root node for the playlist. | ||
* | ||
* @param {string} [options.className] | ||
* An HTML class name to use to find a root node for the playlist. | ||
* | ||
* @param {boolean} [options.playOnSelect = false] | ||
* If true, will attempt to begin playback upon selecting a new | ||
* playlist item in the UI. | ||
*/ | ||
var playlistUi = function playlistUi(options) { | ||
var player = this; | ||
/** | ||
* Returns a boolean indicating whether an element has child elements. | ||
* | ||
* Note that this is distinct from whether it has child _nodes_. | ||
* | ||
* @param {HTMLElement} el | ||
* A DOM element. | ||
* | ||
* @return {boolean} | ||
* Whether the element has child elements. | ||
*/ | ||
var hasChildEls = function hasChildEls(el) { | ||
for (var i = 0; i < el.childNodes.length; i++) { | ||
if (dom.isEl(el.childNodes[i])) { | ||
return true; | ||
if (!player.playlist) { | ||
throw new Error('videojs-playlist plugin is required by the videojs-playlist-ui plugin'); | ||
} | ||
} | ||
return false; | ||
}; | ||
/** | ||
* Finds the first empty root element. | ||
* | ||
* @param {string} className | ||
* An HTML class name to search for. | ||
* | ||
* @return {HTMLElement} | ||
* A DOM element to use as the root for a playlist. | ||
*/ | ||
var findRoot = function findRoot(className) { | ||
var all = document_1.querySelectorAll('.' + className); | ||
var el = void 0; | ||
for (var i = 0; i < all.length; i++) { | ||
if (!hasChildEls(all[i])) { | ||
el = all[i]; | ||
break; | ||
if (dom.isEl(options)) { | ||
videojs.log.warn('videojs-playlist-ui: Passing an element directly to playlistUi() is deprecated, use the "el" option instead!'); | ||
options = { el: options }; | ||
} | ||
} | ||
return el; | ||
}; | ||
options = videojs.mergeOptions(defaults$1, options); | ||
/** | ||
* Initialize the plugin on a player. | ||
* | ||
* @param {Object} [options] | ||
* An options object. | ||
* | ||
* @param {HTMLElement} [options.el] | ||
* A DOM element to use as a root node for the playlist. | ||
* | ||
* @param {string} [options.className] | ||
* An HTML class name to use to find a root node for the playlist. | ||
* | ||
* @param {boolean} [options.playOnSelect = false] | ||
* If true, will attempt to begin playback upon selecting a new | ||
* playlist item in the UI. | ||
*/ | ||
var playlistUi = function playlistUi(options) { | ||
var player = this; | ||
// If the player is already using this plugin, remove the pre-existing | ||
// PlaylistMenu, but retain the element and its location in the DOM because | ||
// it will be re-used. | ||
if (player.playlistMenu) { | ||
var el = player.playlistMenu.el(); | ||
if (!player.playlist) { | ||
throw new Error('videojs-playlist plugin is required by the videojs-playlist-ui plugin'); | ||
} | ||
// Catch cases where the menu may have been disposed elsewhere or the | ||
// element removed from the DOM. | ||
if (el) { | ||
var parentNode = el.parentNode; | ||
var nextSibling = el.nextSibling; | ||
if (dom.isEl(options)) { | ||
videojs.log.warn('videojs-playlist-ui: Passing an element directly to playlistUi() is deprecated, use the "el" option instead!'); | ||
options = { el: options }; | ||
} | ||
// Disposing the menu will remove `el` from the DOM, but we need to | ||
// empty it ourselves to be sure. | ||
player.playlistMenu.dispose(); | ||
dom.emptyEl(el); | ||
options = videojs.mergeOptions(defaults, options); | ||
// Put the element back in its place. | ||
if (nextSibling) { | ||
parentNode.insertBefore(el, nextSibling); | ||
} else { | ||
parentNode.appendChild(el); | ||
} | ||
// If the player is already using this plugin, remove the pre-existing | ||
// PlaylistMenu, but retain the element and its location in the DOM because | ||
// it will be re-used. | ||
if (player.playlistMenu) { | ||
var el = player.playlistMenu.el(); | ||
// Catch cases where the menu may have been disposed elsewhere or the | ||
// element removed from the DOM. | ||
if (el) { | ||
var parentNode = el.parentNode; | ||
var nextSibling = el.nextSibling; | ||
// Disposing the menu will remove `el` from the DOM, but we need to | ||
// empty it ourselves to be sure. | ||
player.playlistMenu.dispose(); | ||
dom.emptyEl(el); | ||
// Put the element back in its place. | ||
if (nextSibling) { | ||
parentNode.insertBefore(el, nextSibling); | ||
} else { | ||
parentNode.appendChild(el); | ||
options.el = el; | ||
} | ||
} | ||
options.el = el; | ||
if (!dom.isEl(options.el)) { | ||
options.el = findRoot(options.className); | ||
} | ||
} | ||
if (!dom.isEl(options.el)) { | ||
options.el = findRoot(options.className); | ||
} | ||
player.playlistMenu = new PlaylistMenu(player, options); | ||
}; | ||
player.playlistMenu = new PlaylistMenu(player, options); | ||
}; | ||
// register components | ||
videojs.registerComponent('PlaylistMenu', PlaylistMenu); | ||
videojs.registerComponent('PlaylistMenuItem', PlaylistMenuItem); | ||
// register components | ||
videojs.registerComponent('PlaylistMenu', PlaylistMenu); | ||
videojs.registerComponent('PlaylistMenuItem', PlaylistMenuItem); | ||
// register the plugin | ||
registerPlugin('playlistUi', playlistUi); | ||
// register the plugin | ||
registerPlugin('playlistUi', playlistUi); | ||
playlistUi.VERSION = version; | ||
playlistUi.VERSION = version; | ||
return playlistUi; | ||
return playlistUi; | ||
}))); |
@@ -1,7 +0,2 @@ | ||
/** | ||
* videojs-playlist-ui | ||
* @version 3.4.0 | ||
* @copyright 2018 Brightcove, Inc. | ||
* @license Apache-2.0 | ||
*/ | ||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t(require("video.js")):"function"==typeof define&&define.amd?define(["video.js"],t):e.videojsPlaylistUi=t(e.videojs)}(this,function(e){"use strict";e=e&&e.hasOwnProperty("default")?e["default"]:e;var t,i="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},n={},l=(Object.freeze||Object)({"default":n}),s=l&&n||l,a=void 0!==i?i:"undefined"!=typeof window?window:{};"undefined"!=typeof document?t=document:(t=a["__GLOBAL_DOCUMENT_CACHE@4"])||(t=a["__GLOBAL_DOCUMENT_CACHE@4"]=s);var r,o=t,p=(function(){function e(e){this.value=e}function t(t){var i,n;function l(i,n){try{var a=t[i](n),r=a.value;r instanceof e?Promise.resolve(r.value).then(function(e){l("next",e)},function(e){l("throw",e)}):s(a.done?"return":"normal",a.value)}catch(o){s("throw",o)}}function s(e,t){switch(e){case"return":i.resolve({value:t,done:!0});break;case"throw":i.reject(t);break;default:i.resolve({value:t,done:!1})}(i=i.next)?l(i.key,i.arg):n=null}this._invoke=function(e,t){return new Promise(function(s,a){var r={key:e,arg:t,resolve:s,reject:a,next:null};n?n=n.next=r:(i=n=r,l(e,t))})},"function"!=typeof t["return"]&&(this["return"]=undefined)}"function"==typeof Symbol&&Symbol.asyncIterator&&(t.prototype[Symbol.asyncIterator]=function(){return this}),t.prototype.next=function(e){return this._invoke("next",e)},t.prototype["throw"]=function(e){return this._invoke("throw",e)},t.prototype["return"]=function(e){return this._invoke("return",e)}}(),function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}),u=function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)},c=function(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t},d=e.dom||e,h=e.registerPlugin||e.plugin,y={className:"vjs-playlist",playOnSelect:!1,supportsCssPointerEvents:((r=o.createElement("x")).style.cssText="pointer-events:auto","auto"===r.style.pointerEvents)},f=function(e){e.addClass("vjs-selected")},m=function(e){e.removeClass("vjs-selected"),e.thumbnail&&d.removeClass(e.thumbnail,"vjs-playlist-now-playing")},v=function(e){e.removeClass("vjs-up-next")},w=e.getComponent("Component"),C=function(t){function i(e,n,l){if(p(this,i),!n.item)throw new Error("Cannot construct a PlaylistMenuItem without an item option");var s=c(this,t.call(this,e,n));return s.item=n.item,s.playOnSelect=l.playOnSelect,s.emitTapEvents(),s.on(["click","tap"],s.switchPlaylistItem_),s.on("keydown",s.handleKeyDown_),s}return u(i,t),i.prototype.handleKeyDown_=function(e){13!==e.which&&32!==e.which||this.switchPlaylistItem_()},i.prototype.switchPlaylistItem_=function(e){this.player_.playlist.currentItem(function(e,t){for(var i=0,n=e.length;i<n;i++)if(e[i]===t)return i;return-1}(this.player_.playlist(),this.item)),this.playOnSelect&&this.player_.play()},i.prototype.createEl=function(){var t=o.createElement("li"),i=this.options_.item;if(t.className="vjs-playlist-item",t.setAttribute("tabIndex",0),this.thumbnail=function(e){if(!e){var t=o.createElement("div");return t.className="vjs-playlist-thumbnail vjs-playlist-thumbnail-placeholder",t}var i=o.createElement("picture");if(i.className="vjs-playlist-thumbnail","string"==typeof e){var n=o.createElement("img");n.src=e,n.alt="",i.appendChild(n)}else{for(var l=0;l<e.length-1;l++){var s=e[l],a=o.createElement("source");for(var r in s)a[r]=s[r];i.appendChild(a)}var p=e[e.length-1],u=o.createElement("img");u.alt="";for(var c in p)u[c]=p[c];i.appendChild(u)}return i}(i.thumbnail),t.appendChild(this.thumbnail),i.duration){var n=o.createElement("time"),l=e.formatTime(i.duration);n.className="vjs-playlist-duration",n.setAttribute("datetime","PT0H0M"+i.duration+"S"),n.appendChild(o.createTextNode(l)),t.appendChild(n)}var s=o.createElement("span"),a=this.localize("Now Playing");s.className="vjs-playlist-now-playing-text",s.appendChild(o.createTextNode(a)),s.setAttribute("title",a),this.thumbnail.appendChild(s);var r=o.createElement("div");r.className="vjs-playlist-title-container",this.thumbnail.appendChild(r);var p=o.createElement("span"),u=this.localize("Up Next");p.className="vjs-up-next-text",p.appendChild(o.createTextNode(u)),p.setAttribute("title",u),r.appendChild(p);var c=o.createElement("cite"),d=i.name||this.localize("Untitled Video");return c.className="vjs-playlist-name",c.appendChild(o.createTextNode(d)),c.setAttribute("title",d),r.appendChild(c),t},i}(w),j=function(t){function i(n,l){if(p(this,i),!n.playlist)throw new Error("videojs-playlist is required for the playlist component");var s=c(this,t.call(this,n,l));return s.items=[],l.horizontal?s.addClass("vjs-playlist-horizontal"):s.addClass("vjs-playlist-vertical"),l.supportsCssPointerEvents&&s.addClass("vjs-csspointerevents"),s.createPlaylist_(),e.browser.TOUCH_ENABLED||s.addClass("vjs-mouse"),n.on(["loadstart","playlistchange","playlistsorted"],function(e){s.update()}),n.on("adstart",function(){s.addClass("vjs-ad-playing")}),n.on("adend",function(){s.removeClass("vjs-ad-playing")}),s}return u(i,t),i.prototype.createEl=function(){return d.createEl("div",{className:this.options_.className})},i.prototype.createPlaylist_=function(){var e=this.player_.playlist()||[],t=this.el_.querySelector(".vjs-playlist-item-list"),i=this.el_.querySelector(".vjs-playlist-ad-overlay");t||((t=o.createElement("ol")).className="vjs-playlist-item-list",this.el_.appendChild(t));for(var n=0;n<this.items.length;n++)t.removeChild(this.items[n].el_);this.items.length=0;for(var l=0;l<e.length;l++){var s=new C(this.player_,{item:e[l]},this.options_);this.items.push(s),t.appendChild(s.el_)}i?t.appendChild(i):((i=o.createElement("li")).className="vjs-playlist-ad-overlay",t.appendChild(i));var a=this.player_.playlist.currentItem();if(this.items.length&&a>=0){f(this.items[a]);var r=this.items[a].$(".vjs-playlist-thumbnail");r&&d.addClass(r,"vjs-playlist-now-playing")}},i.prototype.update=function(){var e=this.player_.playlist();if(this.items.length===e.length){for(var t=0;t<this.items.length;t++)if(this.items[t].item!==e[t])return void this.createPlaylist_();for(var i=this.player_.playlist.currentItem(),n=0;n<this.items.length;n++){var l=this.items[n];n===i?(f(l),o.activeElement!==l.el()&&d.addClass(l.thumbnail,"vjs-playlist-now-playing"),v(l)):n===i+1?(m(l),l.addClass("vjs-up-next")):(m(l),v(l))}}else this.createPlaylist_()},i}(w),b=function(e){for(var t=0;t<e.childNodes.length;t++)if(d.isEl(e.childNodes[t]))return!0;return!1},_=function(t){if(!this.playlist)throw new Error("videojs-playlist plugin is required by the videojs-playlist-ui plugin");if(d.isEl(t)&&(e.log.warn('videojs-playlist-ui: Passing an element directly to playlistUi() is deprecated, use the "el" option instead!'),t={el:t}),t=e.mergeOptions(y,t),this.playlistMenu){var i=this.playlistMenu.el();if(i){var n=i.parentNode,l=i.nextSibling;this.playlistMenu.dispose(),d.emptyEl(i),l?n.insertBefore(i,l):n.appendChild(i),t.el=i}}d.isEl(t.el)||(t.el=function(e){for(var t=o.querySelectorAll("."+e),i=void 0,n=0;n<t.length;n++)if(!b(t[n])){i=t[n];break}return i}(t.className)),this.playlistMenu=new j(this,t)};return e.registerComponent("PlaylistMenu",j),e.registerComponent("PlaylistMenuItem",C),h("playlistUi",_),_.VERSION="3.4.0",_}); | ||
/*! @name videojs-playlist-ui @version 3.4.1 @license Apache-2.0 */ | ||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t(require("global/document"),require("video.js")):"function"==typeof define&&define.amd?define(["global/document","video.js"],t):e.videojsPlaylistUi=t(e.document,e.videojs)}(this,function(e,t){"use strict";e=e&&e.hasOwnProperty("default")?e.default:e;var i,l=function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")},s=function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)},a=function(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t},n=(t=t&&t.hasOwnProperty("default")?t.default:t).dom||t,r=t.registerPlugin||t.plugin,o={className:"vjs-playlist",playOnSelect:!1,supportsCssPointerEvents:((i=e.createElement("x")).style.cssText="pointer-events:auto","auto"===i.style.pointerEvents)},p=function(e){e.addClass("vjs-selected")},c=function(e){e.removeClass("vjs-selected"),e.thumbnail&&n.removeClass(e.thumbnail,"vjs-playlist-now-playing")},d=function(e){e.removeClass("vjs-up-next")},h=t.getComponent("Component"),u=function(i){function n(e,t,s){if(l(this,n),!t.item)throw new Error("Cannot construct a PlaylistMenuItem without an item option");var r=a(this,i.call(this,e,t));return r.item=t.item,r.playOnSelect=s.playOnSelect,r.emitTapEvents(),r.on(["click","tap"],r.switchPlaylistItem_),r.on("keydown",r.handleKeyDown_),r}return s(n,i),n.prototype.handleKeyDown_=function(e){13!==e.which&&32!==e.which||this.switchPlaylistItem_()},n.prototype.switchPlaylistItem_=function(e){this.player_.playlist.currentItem(function(e,t){for(var i=0,l=e.length;i<l;i++)if(e[i]===t)return i;return-1}(this.player_.playlist(),this.item)),this.playOnSelect&&this.player_.play()},n.prototype.createEl=function(){var i=e.createElement("li"),l=this.options_.item;if(i.className="vjs-playlist-item",i.setAttribute("tabIndex",0),this.thumbnail=function(t){if(!t){var i=e.createElement("div");return i.className="vjs-playlist-thumbnail vjs-playlist-thumbnail-placeholder",i}var l=e.createElement("picture");if(l.className="vjs-playlist-thumbnail","string"==typeof t){var s=e.createElement("img");s.src=t,s.alt="",l.appendChild(s)}else{for(var a=0;a<t.length-1;a++){var n=t[a],r=e.createElement("source");for(var o in n)r[o]=n[o];l.appendChild(r)}var p=t[t.length-1],c=e.createElement("img");for(var d in c.alt="",p)c[d]=p[d];l.appendChild(c)}return l}(l.thumbnail),i.appendChild(this.thumbnail),l.duration){var s=e.createElement("time"),a=t.formatTime(l.duration);s.className="vjs-playlist-duration",s.setAttribute("datetime","PT0H0M"+l.duration+"S"),s.appendChild(e.createTextNode(a)),i.appendChild(s)}var n=e.createElement("span"),r=this.localize("Now Playing");n.className="vjs-playlist-now-playing-text",n.appendChild(e.createTextNode(r)),n.setAttribute("title",r),this.thumbnail.appendChild(n);var o=e.createElement("div");o.className="vjs-playlist-title-container",this.thumbnail.appendChild(o);var p=e.createElement("span"),c=this.localize("Up Next");p.className="vjs-up-next-text",p.appendChild(e.createTextNode(c)),p.setAttribute("title",c),o.appendChild(p);var d=e.createElement("cite"),h=l.name||this.localize("Untitled Video");return d.className="vjs-playlist-name",d.appendChild(e.createTextNode(h)),d.setAttribute("title",h),o.appendChild(d),i},n}(h),y=function(i){function r(e,s){if(l(this,r),!e.playlist)throw new Error("videojs-playlist is required for the playlist component");var n=a(this,i.call(this,e,s));return n.items=[],s.horizontal?n.addClass("vjs-playlist-horizontal"):n.addClass("vjs-playlist-vertical"),s.supportsCssPointerEvents&&n.addClass("vjs-csspointerevents"),n.createPlaylist_(),t.browser.TOUCH_ENABLED||n.addClass("vjs-mouse"),e.on(["loadstart","playlistchange","playlistsorted"],function(e){n.update()}),e.on("adstart",function(){n.addClass("vjs-ad-playing")}),e.on("adend",function(){n.removeClass("vjs-ad-playing")}),n}return s(r,i),r.prototype.createEl=function(){return n.createEl("div",{className:this.options_.className})},r.prototype.createPlaylist_=function(){var t=this.player_.playlist()||[],i=this.el_.querySelector(".vjs-playlist-item-list"),l=this.el_.querySelector(".vjs-playlist-ad-overlay");i||((i=e.createElement("ol")).className="vjs-playlist-item-list",this.el_.appendChild(i));for(var s=0;s<this.items.length;s++)i.removeChild(this.items[s].el_);this.items.length=0;for(var a=0;a<t.length;a++){var r=new u(this.player_,{item:t[a]},this.options_);this.items.push(r),i.appendChild(r.el_)}l?i.appendChild(l):((l=e.createElement("li")).className="vjs-playlist-ad-overlay",i.appendChild(l));var o=this.player_.playlist.currentItem();if(this.items.length&&o>=0){p(this.items[o]);var c=this.items[o].$(".vjs-playlist-thumbnail");c&&n.addClass(c,"vjs-playlist-now-playing")}},r.prototype.update=function(){var t=this.player_.playlist();if(this.items.length===t.length){for(var i=0;i<this.items.length;i++)if(this.items[i].item!==t[i])return void this.createPlaylist_();for(var l=this.player_.playlist.currentItem(),s=0;s<this.items.length;s++){var a=this.items[s];s===l?(p(a),e.activeElement!==a.el()&&n.addClass(a.thumbnail,"vjs-playlist-now-playing"),d(a)):s===l+1?(c(a),a.addClass("vjs-up-next")):(c(a),d(a))}}else this.createPlaylist_()},r}(h),m=function(e){for(var t=0;t<e.childNodes.length;t++)if(n.isEl(e.childNodes[t]))return!0;return!1},v=function(i){if(!this.playlist)throw new Error("videojs-playlist plugin is required by the videojs-playlist-ui plugin");if(n.isEl(i)&&(t.log.warn('videojs-playlist-ui: Passing an element directly to playlistUi() is deprecated, use the "el" option instead!'),i={el:i}),i=t.mergeOptions(o,i),this.playlistMenu){var l=this.playlistMenu.el();if(l){var s=l.parentNode,a=l.nextSibling;this.playlistMenu.dispose(),n.emptyEl(l),a?s.insertBefore(l,a):s.appendChild(l),i.el=l}}n.isEl(i.el)||(i.el=function(t){for(var i=e.querySelectorAll("."+t),l=void 0,s=0;s<i.length;s++)if(!m(i[s])){l=i[s];break}return l}(i.className)),this.playlistMenu=new y(this,i)};return t.registerComponent("PlaylistMenu",y),t.registerComponent("PlaylistMenuItem",u),r("playlistUi",v),v.VERSION="3.4.1",v}); |
103
package.json
{ | ||
"name": "videojs-playlist-ui", | ||
"version": "3.4.0", | ||
"version": "3.4.1", | ||
"author": "Brightcove, Inc.", | ||
@@ -15,31 +15,25 @@ "description": "A user interface for the videojs-playlist API", | ||
"build": "npm-run-all -p build:*", | ||
"build:css": "npm-run-all build:css:sass build:css:bannerize build:css:copy-vertical build:css:copy-no-prefix", | ||
"build:css:bannerize": "bannerize dist/videojs-playlist-ui.css --banner=scripts/banner.ejs", | ||
"build:css": "npm-run-all build:css:sass build:css:copy-vertical build:css:copy-no-prefix", | ||
"build:css:sass": "node-sass src/plugin.scss dist/videojs-playlist-ui.css --output-style=compressed --linefeed=lf", | ||
"build:css:copy-no-prefix": "cp dist/videojs-playlist-ui.css dist/videojs-playlist-ui.vertical.no-prefix.css", | ||
"build:css:copy-vertical": "cp dist/videojs-playlist-ui.css dist/videojs-playlist-ui.vertical.css", | ||
"build:css:sass": "node-sass src/plugin.scss dist/videojs-playlist-ui.css --output-style=compressed --linefeed=lf", | ||
"build:js": "npm-run-all build:js:rollup-modules build:js:rollup-umd build:js:bannerize build:js:uglify", | ||
"build:js:bannerize": "bannerize dist/videojs-playlist-ui.js --banner=scripts/banner.ejs", | ||
"build:js:rollup-modules": "rollup -c scripts/modules.rollup.config.js", | ||
"build:js:rollup-umd": "rollup -c scripts/umd.rollup.config.js", | ||
"build:js:uglify": "uglifyjs dist/videojs-playlist-ui.js --comments --mangle --compress --ie8 -o dist/videojs-playlist-ui.min.js", | ||
"build:test": "rollup -c scripts/test.rollup.config.js", | ||
"build:js": "rollup -c scripts/rollup.config.js", | ||
"clean": "rimraf dist test/dist", | ||
"postclean": "mkdirp dist test/dist", | ||
"docs": "npm-run-all docs:*", | ||
"docs:api": "jsdoc src -r -c scripts/jsdoc.json -d docs/api", | ||
"docs:toc": "doctoc README.md", | ||
"lint": "vjsstandard", | ||
"prestart": "npm run build", | ||
"start": "npm-run-all -p start:server watch", | ||
"start:server": "node scripts/server.js", | ||
"server": "karma start scripts/karma.conf.js --singleRun=false --auto-watch --no-browsers", | ||
"start": "npm-run-all -p server watch", | ||
"pretest": "npm-run-all lint build", | ||
"test": "karma start test/karma.conf.js", | ||
"test": "karma start scripts/karma.conf.js", | ||
"preversion": "npm test", | ||
"version": "node scripts/version.js", | ||
"watch": "npm-run-all -p watch:*", | ||
"watch:css": "npm-run-all build:css:sass watch:css:sass", | ||
"watch:css:sass": "node-sass src/plugin.scss dist/videojs-playlist-ui.css --output-style=compressed --linefeed=lf --watch src/**/*.scss", | ||
"watch:js-modules": "rollup -c scripts/modules.rollup.config.js -w", | ||
"watch:js-umd": "rollup -c scripts/umd.rollup.config.js -w", | ||
"watch:test": "rollup -c scripts/test.rollup.config.js -w", | ||
"watch:css": "npm run build:css:sass -- -w", | ||
"watch:js": "npm run build:js -- -w", | ||
"prepublish": "not-in-install && npm run build || in-install", | ||
"prepush": "npm run lint" | ||
"prepush": "npm run lint", | ||
"precommit": "npm run docs:toc && git add README.md" | ||
}, | ||
@@ -52,40 +46,46 @@ "repository": { | ||
"global": "^4.3.2", | ||
"video.js": "^5.19.2" | ||
"video.js": "^6 || ^7" | ||
}, | ||
"devDependencies": { | ||
"autoprefixer": "^8.6.0", | ||
"babel-core": "^6.26.3", | ||
"babel-plugin-external-helpers": "^6.22.0", | ||
"babel-plugin-transform-object-assign": "^6.8.0", | ||
"babel-preset-es2015": "^6.14.0", | ||
"babel-preset-es3": "^1.0.1", | ||
"bannerize": "^1.0.2", | ||
"conventional-changelog-cli": "^1.3.1", | ||
"babel-plugin-transform-object-assign": "^6.22.0", | ||
"babel-preset-env": "^1.7.0", | ||
"conventional-changelog-cli": "^2.0.0", | ||
"conventional-changelog-videojs": "^3.0.0", | ||
"es5-shim": "^4.5.9", | ||
"husky": "^0.13.3", | ||
"cssnano": "^3.10.0", | ||
"doctoc": "^1.3.1", | ||
"husky": "^0.14.3", | ||
"in-publish": "^2.0.0", | ||
"karma": "~1.3.0", | ||
"karma-chrome-launcher": "^2.1.1", | ||
"karma-detect-browsers": "^2.2.5", | ||
"karma-firefox-launcher": "^1.0.1", | ||
"jsdoc": "^3.4.3", | ||
"karma": "^2.0.2", | ||
"karma-chrome-launcher": "^2.2.0", | ||
"karma-detect-browsers": "^2.2.6", | ||
"karma-firefox-launcher": "^1.1.0", | ||
"karma-ie-launcher": "^1.0.0", | ||
"karma-qunit": "^1.2.1", | ||
"karma-qunit": "^2.0.0", | ||
"karma-safari-launcher": "^1.0.0", | ||
"mkdirp": "^0.5.1", | ||
"node-sass": "4.5.3", | ||
"node-static": "^0.7.9", | ||
"npm-run-all": "^4.0.2", | ||
"portscanner": "^2.1.1", | ||
"qunitjs": "^1.21.0", | ||
"node-sass": "^4.5.3", | ||
"npm-run-all": "^4.1.2", | ||
"postcss-banner": "^3.0.0", | ||
"postcss-cli": "^5.0.0", | ||
"postcss-import": "^11.1.0", | ||
"postcss-preset-env": "^5.1.0", | ||
"postcss-simple-vars": "^4.1.0", | ||
"qunit": "^2.5.1", | ||
"rimraf": "^2.6.1", | ||
"rollup": "^0.50.0", | ||
"rollup-plugin-babel": "^2.7.1", | ||
"rollup-plugin-commonjs": "^8.0.2", | ||
"rollup-plugin-json": "^2.1.1", | ||
"rollup-plugin-multi-entry": "^2.0.1", | ||
"rollup": "^0.60.1", | ||
"rollup-plugin-babel": "^3.0.4", | ||
"rollup-plugin-commonjs": "^9.1.3", | ||
"rollup-plugin-json": "^3.0.0", | ||
"rollup-plugin-multi-entry": "^2.0.2", | ||
"rollup-plugin-node-resolve": "^3.0.0", | ||
"rollup-plugin-replace": "^1.1.1", | ||
"rollup-watch": "^3.2.2", | ||
"semver": "^5.3.0", | ||
"sinon": "^2.2.0", | ||
"uglify-js": "^3.0.7", | ||
"rollup-plugin-uglify": "^4.0.0", | ||
"semver": "^5.1.0", | ||
"serve-static": "^1.13.2", | ||
"sinon": "^5.1.0", | ||
"uglify-es": "^3.3.9", | ||
"videojs-playlist": "^4.0.0", | ||
@@ -97,3 +97,3 @@ "videojs-standard": "^6.0.0" | ||
"generator-videojs-plugin": { | ||
"version": "5.1.1" | ||
"version": "6.0.9" | ||
}, | ||
@@ -104,4 +104,3 @@ "vjsstandard": { | ||
"docs", | ||
"test/dist", | ||
"test/karma.conf.js" | ||
"test/dist" | ||
] | ||
@@ -117,3 +116,7 @@ }, | ||
"test/" | ||
], | ||
"browserslist": [ | ||
"defaults", | ||
"ie 11" | ||
] | ||
} |
@@ -0,6 +1,30 @@ | ||
# videojs-playlist-ui | ||
[![Build Status](https://travis-ci.org/brightcove/videojs-playlist-ui.svg?branch=master)](https://travis-ci.org/brightcove/videojs-playlist-ui) | ||
[![Greenkeeper badge](https://badges.greenkeeper.io/brightcove/videojs-playlist-ui.svg)](https://greenkeeper.io/) | ||
[![Slack Status](http://slack.videojs.com/badge.svg)](http://slack.videojs.com) | ||
# Video.js Playlist UI | ||
[![NPM](https://nodei.co/npm/videojs-playlist-ui.png?downloads=true&downloadRank=true)](https://nodei.co/npm/videojs-playlist-ui/) | ||
A playlist video picker for video.js and videojs-playlist | ||
Maintenance Status: Stable | ||
<!-- START doctoc generated TOC please keep comment here to allow auto update --> | ||
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --> | ||
- [Getting Started](#getting-started) | ||
- [Root Element](#root-element) | ||
- [Using Automatic Discovery (default, example)](#using-automatic-discovery-default-example) | ||
- [Using a Custom Class (example)](#using-a-custom-class-example) | ||
- [Using a Custom Element (example)](#using-a-custom-element-example) | ||
- [Other Options](#other-options) | ||
- [`className`](#classname) | ||
- [playOnSelect](#playonselect) | ||
- [Playlists and Advertisements](#playlists-and-advertisements) | ||
<!-- END doctoc generated TOC please keep comment here to allow auto update --> | ||
## Getting Started | ||
@@ -7,0 +31,0 @@ Include the plugin script in your page, and a placeholder list element with the class `vjs-playlist` to house the playlist menu: |
@@ -5,3 +5,2 @@ import document from 'global/document'; | ||
// support VJS5 & VJS6 at the same time | ||
const dom = videojs.dom || videojs; | ||
@@ -8,0 +7,0 @@ const registerPlugin = videojs.registerPlugin || videojs.plugin; |
@@ -1,1894 +0,1764 @@ | ||
(function (QUnit,videojs) { | ||
'use strict'; | ||
(function (videojs,QUnit) { | ||
'use strict'; | ||
QUnit = QUnit && QUnit.hasOwnProperty('default') ? QUnit['default'] : QUnit; | ||
videojs = videojs && videojs.hasOwnProperty('default') ? videojs['default'] : videojs; | ||
videojs = videojs && videojs.hasOwnProperty('default') ? videojs['default'] : videojs; | ||
QUnit = QUnit && QUnit.hasOwnProperty('default') ? QUnit['default'] : QUnit; | ||
var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; | ||
var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; | ||
var empty = {}; | ||
var empty = {}; | ||
var empty$1 = /*#__PURE__*/Object.freeze({ | ||
default: empty | ||
}); | ||
var empty$1 = (Object.freeze || Object)({ | ||
'default': empty | ||
}); | ||
var minDoc = ( empty$1 && empty ) || empty$1; | ||
var minDoc = ( empty$1 && empty ) || empty$1; | ||
var topLevel = typeof commonjsGlobal !== 'undefined' ? commonjsGlobal : | ||
typeof window !== 'undefined' ? window : {}; | ||
var topLevel = typeof commonjsGlobal !== 'undefined' ? commonjsGlobal : | ||
typeof window !== 'undefined' ? window : {}; | ||
var doccy; | ||
var doccy; | ||
if (typeof document !== 'undefined') { | ||
doccy = document; | ||
} else { | ||
doccy = topLevel['__GLOBAL_DOCUMENT_CACHE@4']; | ||
if (typeof document !== 'undefined') { | ||
doccy = document; | ||
} else { | ||
doccy = topLevel['__GLOBAL_DOCUMENT_CACHE@4']; | ||
if (!doccy) { | ||
doccy = topLevel['__GLOBAL_DOCUMENT_CACHE@4'] = minDoc; | ||
} | ||
} | ||
if (!doccy) { | ||
doccy = topLevel['__GLOBAL_DOCUMENT_CACHE@4'] = minDoc; | ||
} | ||
} | ||
var document_1 = doccy; | ||
var document_1 = doccy; | ||
var win; | ||
var win; | ||
if (typeof window !== "undefined") { | ||
win = window; | ||
} else if (typeof commonjsGlobal !== "undefined") { | ||
win = commonjsGlobal; | ||
} else if (typeof self !== "undefined"){ | ||
win = self; | ||
} else { | ||
win = {}; | ||
} | ||
if (typeof window !== "undefined") { | ||
win = window; | ||
} else if (typeof commonjsGlobal !== "undefined") { | ||
win = commonjsGlobal; | ||
} else if (typeof self !== "undefined"){ | ||
win = self; | ||
} else { | ||
win = {}; | ||
} | ||
var window_1 = win; | ||
var window_1 = win; | ||
/*! @name videojs-playlist @version 4.2.2 @license Apache-2.0 */ | ||
/** | ||
* 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; | ||
}; | ||
/** | ||
* 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 | ||
*/ | ||
const validSeconds = s => | ||
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_; | ||
/** | ||
* Resets the auto-advance behavior of a player. | ||
* | ||
* @param {Player} player | ||
* The player to reset the behavior on | ||
*/ | ||
let reset = (player) => { | ||
const aa = player.playlist.autoadvance_; | ||
if (aa.timeout) { | ||
player.clearTimeout(aa.timeout); | ||
} | ||
if (aa.timeout) { | ||
player.clearTimeout(aa.timeout); | ||
} | ||
if (aa.trigger) { | ||
player.off('ended', aa.trigger); | ||
} | ||
if (aa.trigger) { | ||
player.off('ended', aa.trigger); | ||
} | ||
aa.timeout = null; | ||
aa.trigger = null; | ||
}; | ||
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$1 = function setup(player, delay) { | ||
reset(player); | ||
/** | ||
* 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 | ||
*/ | ||
const 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; | ||
} | ||
// 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_.delay = delay; | ||
player.playlist.autoadvance_.trigger = function () { | ||
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); | ||
}; | ||
// This calls setup again, which will reset the existing auto-advance and | ||
// set up another auto-advance for the next "ended" event. | ||
const cancelOnPlay = () => 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); | ||
// 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.playlist.autoadvance_.timeout = player.setTimeout(() => { | ||
reset(player); | ||
player.off('play', cancelOnPlay); | ||
player.playlist.next(); | ||
}, delay * 1000); | ||
}; | ||
player.one('ended', player.playlist.autoadvance_.trigger); | ||
}; | ||
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; | ||
/** | ||
* Removes all remote text tracks from a player. | ||
* | ||
* @param {Player} player | ||
* The player to clear tracks on | ||
*/ | ||
const clearTracks = (player) => { | ||
const tracks = player.remoteTextTracks(); | ||
let 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]); | ||
} | ||
}; | ||
// 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(); | ||
/** | ||
* 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 | ||
*/ | ||
const playItem = (player, item) => { | ||
const replay = !player.paused() || player.ended(); | ||
player.trigger('beforeplaylistitem', item); | ||
player.poster(item.poster || ''); | ||
player.src(item.sources); | ||
clearTracks(player); | ||
player.trigger('beforeplaylistitem', item); | ||
player.poster(item.poster || ''); | ||
player.src(item.sources); | ||
clearTracks(player); | ||
player.ready(function () { | ||
(item.textTracks || []).forEach(player.addRemoteTextTrack.bind(player)); | ||
player.trigger('playlistitem', item); | ||
player.ready(() => { | ||
(item.textTracks || []).forEach(player.addRemoteTextTrack.bind(player)); | ||
player.trigger('playlistitem', item); | ||
if (replay) { | ||
player.play(); | ||
} | ||
if (replay) { | ||
const playPromise = player.play(); | ||
setup$1(player, player.playlist.autoadvance_.delay); | ||
}); | ||
// 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, (e) => {}); | ||
} | ||
} | ||
return player; | ||
}; | ||
setup(player, player.playlist.autoadvance_.delay); | ||
}); | ||
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { | ||
return typeof obj; | ||
} : function (obj) { | ||
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; | ||
}; | ||
return player; | ||
}; | ||
/** | ||
* 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 | ||
*/ | ||
const sourceEquals = (source1, source2) => { | ||
let src1 = source1; | ||
let 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; | ||
}; | ||
var asyncGenerator = function () { | ||
function AwaitValue(value) { | ||
this.value = value; | ||
} | ||
/** | ||
* 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 | ||
*/ | ||
const indexInSources = (arr, src) => { | ||
for (let i = 0; i < arr.length; i++) { | ||
const sources = arr[i].sources; | ||
function AsyncGenerator(gen) { | ||
var front, back; | ||
if (Array.isArray(sources)) { | ||
for (let j = 0; j < sources.length; j++) { | ||
const source = sources[j]; | ||
function send(key, arg) { | ||
return new Promise(function (resolve, reject) { | ||
var request = { | ||
key: key, | ||
arg: arg, | ||
resolve: resolve, | ||
reject: reject, | ||
next: null | ||
}; | ||
if (source && sourceEquals(source, src)) { | ||
return i; | ||
} | ||
} | ||
} | ||
} | ||
if (back) { | ||
back = back.next = request; | ||
} else { | ||
front = back = request; | ||
resume(key, arg); | ||
} | ||
}); | ||
} | ||
return -1; | ||
}; | ||
function resume(key, arg) { | ||
try { | ||
var result = gen[key](arg); | ||
var value = result.value; | ||
/** | ||
* Randomize the contents of an array. | ||
* | ||
* @private | ||
* @param {Array} arr | ||
* An array. | ||
* | ||
* @return {Array} | ||
* The same array that was passed in. | ||
*/ | ||
const randomize = (arr) => { | ||
let index = -1; | ||
const lastIndex = arr.length - 1; | ||
if (value instanceof AwaitValue) { | ||
Promise.resolve(value.value).then(function (arg) { | ||
resume("next", arg); | ||
}, function (arg) { | ||
resume("throw", arg); | ||
}); | ||
} else { | ||
settle(result.done ? "return" : "normal", result.value); | ||
} | ||
} catch (err) { | ||
settle("throw", err); | ||
} | ||
} | ||
while (++index < arr.length) { | ||
const rand = index + Math.floor(Math.random() * (lastIndex - index + 1)); | ||
const value = arr[rand]; | ||
function settle(type, value) { | ||
switch (type) { | ||
case "return": | ||
front.resolve({ | ||
value: value, | ||
done: true | ||
}); | ||
break; | ||
arr[rand] = arr[index]; | ||
arr[index] = value; | ||
} | ||
case "throw": | ||
front.reject(value); | ||
break; | ||
return arr; | ||
}; | ||
default: | ||
front.resolve({ | ||
value: value, | ||
done: false | ||
}); | ||
break; | ||
} | ||
/** | ||
* 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 = 0) { | ||
let list = null; | ||
let changing = false; | ||
front = front.next; | ||
/** | ||
* 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 | ||
*/ | ||
const playlist = player.playlist = (newList, newIndex = 0) => { | ||
if (changing) { | ||
throw new Error('do not call playlist() during a playlist change'); | ||
} | ||
if (front) { | ||
resume(front.key, front.arg); | ||
} else { | ||
back = null; | ||
} | ||
} | ||
if (Array.isArray(newList)) { | ||
this._invoke = send; | ||
// @todo - Simplify this to `list.slice()` for v5. | ||
const previousPlaylist = Array.isArray(list) ? list.slice() : null; | ||
if (typeof gen.return !== "function") { | ||
this.return = undefined; | ||
} | ||
} | ||
list = newList.slice(); | ||
if (typeof Symbol === "function" && Symbol.asyncIterator) { | ||
AsyncGenerator.prototype[Symbol.asyncIterator] = function () { | ||
return this; | ||
}; | ||
} | ||
// Mark the playlist as changing during the duringplaylistchange lifecycle. | ||
changing = true; | ||
AsyncGenerator.prototype.next = function (arg) { | ||
return this._invoke("next", arg); | ||
}; | ||
player.trigger({ | ||
type: 'duringplaylistchange', | ||
nextIndex: newIndex, | ||
nextPlaylist: list, | ||
previousIndex: playlist.currentIndex_, | ||
AsyncGenerator.prototype.throw = function (arg) { | ||
return this._invoke("throw", arg); | ||
}; | ||
// @todo - Simplify this to simply pass along `previousPlaylist` for v5. | ||
previousPlaylist: previousPlaylist || [] | ||
}); | ||
AsyncGenerator.prototype.return = function (arg) { | ||
return this._invoke("return", arg); | ||
}; | ||
changing = false; | ||
return { | ||
wrap: function (fn) { | ||
return function () { | ||
return new AsyncGenerator(fn.apply(this, arguments)); | ||
}; | ||
}, | ||
await: function (value) { | ||
return new AwaitValue(value); | ||
} | ||
}; | ||
}(); | ||
if (newIndex !== -1) { | ||
playlist.currentItem(newIndex); | ||
} | ||
/** | ||
* 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; | ||
// 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(() => { | ||
player.trigger('playlistchange'); | ||
}, 0); | ||
} | ||
} | ||
if ((typeof source1 === 'undefined' ? 'undefined' : _typeof(source1)) === 'object') { | ||
src1 = source1.src; | ||
} | ||
if ((typeof source2 === 'undefined' ? 'undefined' : _typeof(source2)) === 'object') { | ||
src2 = source2.src; | ||
} | ||
// Always return a shallow clone of the playlist list. | ||
return list.slice(); | ||
}; | ||
if (/^\/\//.test(src1)) { | ||
src2 = src2.slice(src2.indexOf('//')); | ||
} | ||
if (/^\/\//.test(src2)) { | ||
src1 = src1.slice(src1.indexOf('//')); | ||
} | ||
// On a new source, if there is no current item, disable auto-advance. | ||
player.on('loadstart', () => { | ||
if (playlist.currentItem() === -1) { | ||
reset(player); | ||
} | ||
}); | ||
return src1 === src2; | ||
}; | ||
playlist.currentIndex_ = -1; | ||
playlist.player_ = player; | ||
playlist.autoadvance_ = {}; | ||
playlist.repeat_ = false; | ||
/** | ||
* 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; | ||
/** | ||
* 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 = (index) => { | ||
if (Array.isArray(sources)) { | ||
for (var j = 0; j < sources.length; j++) { | ||
var source = sources[j]; | ||
// If the playlist is changing, only act as a getter. | ||
if (changing) { | ||
return playlist.currentIndex_; | ||
} | ||
if (source && sourceEquals(source, src)) { | ||
return i; | ||
} | ||
} | ||
} | ||
} | ||
if ( | ||
typeof index === 'number' && | ||
playlist.currentIndex_ !== index && | ||
index >= 0 && | ||
index < list.length | ||
) { | ||
playlist.currentIndex_ = index; | ||
playItem( | ||
playlist.player_, | ||
list[playlist.currentIndex_] | ||
); | ||
} else { | ||
playlist.currentIndex_ = playlist.indexOf(playlist.player_.currentSrc() || ''); | ||
} | ||
return -1; | ||
}; | ||
return playlist.currentIndex_; | ||
}; | ||
/** | ||
* 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) { | ||
var initialIndex = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0; | ||
/** | ||
* Checks if the playlist contains a value. | ||
* | ||
* @param {string|Object|Array} value | ||
* The value to check | ||
* | ||
* @return {boolean} | ||
* The result | ||
*/ | ||
playlist.contains = (value) => { | ||
return playlist.indexOf(value) !== -1; | ||
}; | ||
var list = Array.isArray(initialList) ? initialList.slice() : []; | ||
/** | ||
* 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 = (value) => { | ||
if (typeof value === 'string') { | ||
return indexInSources(list, value); | ||
} | ||
/** | ||
* 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) { | ||
var newIndex = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; | ||
const sources = Array.isArray(value) ? value : value.sources; | ||
if (Array.isArray(newList)) { | ||
list = newList.slice(); | ||
if (newIndex !== -1) { | ||
playlist.currentItem(newIndex); | ||
} | ||
player.setTimeout(function () { | ||
return player.trigger('playlistchange'); | ||
}, 0); | ||
} | ||
for (let i = 0; i < sources.length; i++) { | ||
const source = sources[i]; | ||
// Always return a shallow clone of the playlist list. | ||
return list.slice(); | ||
}; | ||
if (typeof source === 'string') { | ||
return indexInSources(list, source); | ||
} else if (source.src) { | ||
return indexInSources(list, source.src); | ||
} | ||
} | ||
// On a new source, if there is no current item, disable auto-advance. | ||
player.on('loadstart', function () { | ||
if (playlist.currentItem() === -1) { | ||
reset(player); | ||
} | ||
}); | ||
return -1; | ||
}; | ||
playlist.currentIndex_ = -1; | ||
playlist.player_ = player; | ||
playlist.autoadvance_ = {}; | ||
playlist.repeat_ = false; | ||
/** | ||
* 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 = () => playlist.currentItem(); | ||
/** | ||
* Get or set the current item in the playlist. | ||
* | ||
* @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 (typeof index === 'number' && playlist.currentIndex_ !== index && index >= 0 && index < list.length) { | ||
playlist.currentIndex_ = index; | ||
playItem(playlist.player_, list[playlist.currentIndex_]); | ||
} else { | ||
playlist.currentIndex_ = playlist.indexOf(playlist.player_.currentSrc() || ''); | ||
} | ||
/** | ||
* 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 = () => list.length - 1; | ||
return playlist.currentIndex_; | ||
}; | ||
/** | ||
* 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 = () => { | ||
const current = playlist.currentItem(); | ||
/** | ||
* 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; | ||
}; | ||
if (current === -1) { | ||
return -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); | ||
} | ||
const lastIndex = playlist.lastIndex(); | ||
var sources = Array.isArray(value) ? value : value.sources; | ||
// When repeating, loop back to the beginning on the last item. | ||
if (playlist.repeat_ && current === lastIndex) { | ||
return 0; | ||
} | ||
for (var i = 0; i < sources.length; i++) { | ||
var source = sources[i]; | ||
// Don't go past the end of the playlist. | ||
return Math.min(current + 1, lastIndex); | ||
}; | ||
if (typeof source === 'string') { | ||
return indexInSources(list, source); | ||
} else if (source.src) { | ||
return indexInSources(list, source.src); | ||
} | ||
} | ||
/** | ||
* 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 = () => { | ||
const current = playlist.currentItem(); | ||
return -1; | ||
}; | ||
if (current === -1) { | ||
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(); | ||
}; | ||
// When repeating, loop back to the end of the playlist. | ||
if (playlist.repeat_ && current === 0) { | ||
return playlist.lastIndex(); | ||
} | ||
/** | ||
* 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; | ||
}; | ||
// Don't go past the beginning of the playlist. | ||
return Math.max(current - 1, 0); | ||
}; | ||
/** | ||
* 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(); | ||
/** | ||
* Plays the first item in the playlist. | ||
* | ||
* @return {Object|undefined} | ||
* Returns undefined and has no side effects if the list is empty. | ||
*/ | ||
playlist.first = () => { | ||
if (changing) { | ||
return; | ||
} | ||
if (current === -1) { | ||
return -1; | ||
} | ||
if (list.length) { | ||
return list[playlist.currentItem(0)]; | ||
} | ||
var lastIndex = playlist.lastIndex(); | ||
playlist.currentIndex_ = -1; | ||
}; | ||
// When repeating, loop back to the beginning on the last item. | ||
if (playlist.repeat_ && current === lastIndex) { | ||
return 0; | ||
} | ||
/** | ||
* Plays the last item in the playlist. | ||
* | ||
* @return {Object|undefined} | ||
* Returns undefined and has no side effects if the list is empty. | ||
*/ | ||
playlist.last = () => { | ||
if (changing) { | ||
return; | ||
} | ||
// Don't go past the end of the playlist. | ||
return Math.min(current + 1, lastIndex); | ||
}; | ||
if (list.length) { | ||
return list[playlist.currentItem(playlist.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(); | ||
playlist.currentIndex_ = -1; | ||
}; | ||
if (current === -1) { | ||
return -1; | ||
} | ||
/** | ||
* Plays the next item in the playlist. | ||
* | ||
* @return {Object|undefined} | ||
* Returns undefined and has no side effects if on last item. | ||
*/ | ||
playlist.next = () => { | ||
if (changing) { | ||
return; | ||
} | ||
// When repeating, loop back to the end of the playlist. | ||
if (playlist.repeat_ && current === 0) { | ||
return playlist.lastIndex(); | ||
} | ||
const index = playlist.nextIndex(); | ||
// Don't go past the beginning of the playlist. | ||
return Math.max(current - 1, 0); | ||
}; | ||
if (index !== playlist.currentIndex_) { | ||
return list[playlist.currentItem(index)]; | ||
} | ||
}; | ||
/** | ||
* 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 (list.length) { | ||
return list[playlist.currentItem(0)]; | ||
} | ||
/** | ||
* Plays the previous item in the playlist. | ||
* | ||
* @return {Object|undefined} | ||
* Returns undefined and has no side effects if on first item. | ||
*/ | ||
playlist.previous = () => { | ||
if (changing) { | ||
return; | ||
} | ||
playlist.currentIndex_ = -1; | ||
}; | ||
const index = playlist.previousIndex(); | ||
/** | ||
* 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 (list.length) { | ||
return list[playlist.currentItem(playlist.lastIndex())]; | ||
} | ||
if (index !== playlist.currentIndex_) { | ||
return list[playlist.currentItem(index)]; | ||
} | ||
}; | ||
playlist.currentIndex_ = -1; | ||
}; | ||
/** | ||
* Set up auto-advance on the playlist. | ||
* | ||
* @param {number} [delay] | ||
* The number of seconds to wait before each auto-advance. | ||
*/ | ||
playlist.autoadvance = (delay) => { | ||
setup(playlist.player_, delay); | ||
}; | ||
/** | ||
* Plays the next item in the playlist. | ||
* | ||
* @return {Object|undefined} | ||
* Returns undefined and has no side effects if on last item. | ||
*/ | ||
playlist.next = function () { | ||
var index = playlist.nextIndex(); | ||
/** | ||
* 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 = (val) => { | ||
if (val === undefined) { | ||
return playlist.repeat_; | ||
} | ||
if (index !== playlist.currentIndex_) { | ||
return list[playlist.currentItem(index)]; | ||
} | ||
}; | ||
if (typeof val !== 'boolean') { | ||
videojs.log.error('videojs-playlist: Invalid value for repeat', val); | ||
return; | ||
} | ||
/** | ||
* Plays the previous item in the playlist. | ||
* | ||
* @return {Object|undefined} | ||
* Returns undefined and has no side effects if on first item. | ||
*/ | ||
playlist.previous = function () { | ||
var index = playlist.previousIndex(); | ||
playlist.repeat_ = !!val; | ||
return playlist.repeat_; | ||
}; | ||
if (index !== playlist.currentIndex_) { | ||
return list[playlist.currentItem(index)]; | ||
} | ||
}; | ||
/** | ||
* 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 = (compare) => { | ||
/** | ||
* 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$1(playlist.player_, delay); | ||
}; | ||
// Bail if the array is empty. | ||
if (!list.length) { | ||
return; | ||
} | ||
/** | ||
* 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_; | ||
} | ||
list.sort(compare); | ||
if (typeof val !== 'boolean') { | ||
videojs.log.error('videojs-playlist: Invalid value for repeat', val); | ||
return; | ||
} | ||
// If the playlist is changing, don't trigger events. | ||
if (changing) { | ||
return; | ||
} | ||
playlist.repeat_ = !!val; | ||
return playlist.repeat_; | ||
}; | ||
/** | ||
* Triggered after the playlist is sorted internally. | ||
* | ||
* @event playlistsorted | ||
* @type {Object} | ||
*/ | ||
player.trigger('playlistsorted'); | ||
}; | ||
/** | ||
* 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) { | ||
/** | ||
* Reverses the playlist array. | ||
* | ||
* @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reverse} | ||
* @fires playlistsorted | ||
*/ | ||
playlist.reverse = () => { | ||
// Bail if the array is empty. | ||
if (!list.length) { | ||
return; | ||
} | ||
// Bail if the array is empty. | ||
if (!list.length) { | ||
return; | ||
} | ||
list.sort(compare); | ||
list.reverse(); | ||
/** | ||
* Triggered after the playlist is sorted internally. | ||
* | ||
* @event playlistsorted | ||
* @type {Object} | ||
*/ | ||
player.trigger('playlistsorted'); | ||
}; | ||
// If the playlist is changing, don't trigger events. | ||
if (changing) { | ||
return; | ||
} | ||
/** | ||
* 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 () { | ||
/** | ||
* Triggered after the playlist is sorted internally. | ||
* | ||
* @event playlistsorted | ||
* @type {Object} | ||
*/ | ||
player.trigger('playlistsorted'); | ||
}; | ||
// Bail if the array is empty. | ||
if (!list.length) { | ||
return; | ||
} | ||
/** | ||
* 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 = ({rest} = {}) => { | ||
let index = 0; | ||
let arr = list; | ||
list.reverse(); | ||
// When options.rest is true, start randomization at the item after the | ||
// current item. | ||
if (rest) { | ||
index = playlist.currentIndex_ + 1; | ||
arr = list.slice(index); | ||
} | ||
/** | ||
* Triggered after the playlist is sorted internally. | ||
* | ||
* @event playlistsorted | ||
* @type {Object} | ||
*/ | ||
player.trigger('playlistsorted'); | ||
}; | ||
// Bail if the array is empty or too short to shuffle. | ||
if (arr.length <= 1) { | ||
return; | ||
} | ||
/** | ||
* Shuffle the contents of the list randomly. | ||
* | ||
* @see {@link https://github.com/lodash/lodash/blob/40e096b6d5291a025e365a0f4c010d9a0efb9a69/shuffle.js} | ||
* @fires playlistsorted | ||
*/ | ||
playlist.shuffle = function () { | ||
var index = -1; | ||
var length = list.length; | ||
randomize(arr); | ||
// Bail if the array is empty. | ||
if (!length) { | ||
return; | ||
} | ||
// When options.rest is true, splice the randomized sub-array back into | ||
// the original array. | ||
if (rest) { | ||
list.splice(...[index, arr.length].concat(arr)); | ||
} | ||
var lastIndex = length - 1; | ||
// If the playlist is changing, don't trigger events. | ||
if (changing) { | ||
return; | ||
} | ||
while (++index < length) { | ||
var rand = index + Math.floor(Math.random() * (lastIndex - index + 1)); | ||
var value = list[rand]; | ||
/** | ||
* Triggered after the playlist is sorted internally. | ||
* | ||
* @event playlistsorted | ||
* @type {Object} | ||
*/ | ||
player.trigger('playlistsorted'); | ||
}; | ||
list[rand] = list[index]; | ||
list[index] = value; | ||
} | ||
// If an initial list was given, populate the playlist with it. | ||
if (Array.isArray(initialList)) { | ||
playlist(initialList.slice(), initialIndex); | ||
/** | ||
* Triggered after the playlist is sorted internally. | ||
* | ||
* @event playlistsorted | ||
* @type {Object} | ||
*/ | ||
player.trigger('playlistsorted'); | ||
}; | ||
// If there is no initial list given, silently set an empty array. | ||
} else { | ||
list = []; | ||
} | ||
playlist.currentItem(initialIndex); | ||
return playlist; | ||
} | ||
return playlist; | ||
} | ||
var version = "4.2.2"; | ||
// Video.js 5/6 cross-compatible. | ||
var registerPlugin = videojs.registerPlugin || videojs.plugin; | ||
// Video.js 5/6 cross-compatible. | ||
const 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); | ||
}; | ||
/** | ||
* 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 | ||
*/ | ||
const plugin = function(list, item) { | ||
factory(this, list, item); | ||
}; | ||
registerPlugin('playlist', plugin); | ||
registerPlugin('playlist', plugin); | ||
var version = "3.4.0"; | ||
plugin.VERSION = version; | ||
var asyncGenerator$1 = function () { | ||
function AwaitValue(value) { | ||
this.value = value; | ||
} | ||
var version$1 = "3.4.1"; | ||
function AsyncGenerator(gen) { | ||
var front, back; | ||
var classCallCheck = function (instance, Constructor) { | ||
if (!(instance instanceof Constructor)) { | ||
throw new TypeError("Cannot call a class as a function"); | ||
} | ||
}; | ||
function send(key, arg) { | ||
return new Promise(function (resolve, reject) { | ||
var request = { | ||
key: key, | ||
arg: arg, | ||
resolve: resolve, | ||
reject: reject, | ||
next: null | ||
}; | ||
var inherits = function (subClass, superClass) { | ||
if (typeof superClass !== "function" && superClass !== null) { | ||
throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); | ||
} | ||
if (back) { | ||
back = back.next = request; | ||
} else { | ||
front = back = request; | ||
resume(key, arg); | ||
} | ||
}); | ||
} | ||
subClass.prototype = Object.create(superClass && superClass.prototype, { | ||
constructor: { | ||
value: subClass, | ||
enumerable: false, | ||
writable: true, | ||
configurable: true | ||
} | ||
}); | ||
if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; | ||
}; | ||
function resume(key, arg) { | ||
try { | ||
var result = gen[key](arg); | ||
var value = result.value; | ||
var possibleConstructorReturn = function (self, call) { | ||
if (!self) { | ||
throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); | ||
} | ||
if (value instanceof AwaitValue) { | ||
Promise.resolve(value.value).then(function (arg) { | ||
resume("next", arg); | ||
}, function (arg) { | ||
resume("throw", arg); | ||
}); | ||
} else { | ||
settle(result.done ? "return" : "normal", result.value); | ||
} | ||
} catch (err) { | ||
settle("throw", err); | ||
} | ||
} | ||
return call && (typeof call === "object" || typeof call === "function") ? call : self; | ||
}; | ||
function settle(type, value) { | ||
switch (type) { | ||
case "return": | ||
front.resolve({ | ||
value: value, | ||
done: true | ||
}); | ||
break; | ||
var dom = videojs.dom || videojs; | ||
var registerPlugin$1 = videojs.registerPlugin || videojs.plugin; | ||
case "throw": | ||
front.reject(value); | ||
break; | ||
// Array#indexOf analog for IE8 | ||
var indexOf = function indexOf(array, target) { | ||
for (var i = 0, length = array.length; i < length; i++) { | ||
if (array[i] === target) { | ||
return i; | ||
} | ||
} | ||
return -1; | ||
}; | ||
default: | ||
front.resolve({ | ||
value: value, | ||
done: false | ||
}); | ||
break; | ||
} | ||
// see https://github.com/Modernizr/Modernizr/blob/master/feature-detects/css/pointerevents.js | ||
var supportsCssPointerEvents = function () { | ||
var element = document_1.createElement('x'); | ||
front = front.next; | ||
element.style.cssText = 'pointer-events:auto'; | ||
return element.style.pointerEvents === 'auto'; | ||
}(); | ||
if (front) { | ||
resume(front.key, front.arg); | ||
} else { | ||
back = null; | ||
} | ||
} | ||
var defaults$1 = { | ||
className: 'vjs-playlist', | ||
playOnSelect: false, | ||
supportsCssPointerEvents: supportsCssPointerEvents | ||
}; | ||
this._invoke = send; | ||
// we don't add `vjs-playlist-now-playing` in addSelectedClass | ||
// so it won't conflict with `vjs-icon-play | ||
// since it'll get added when we mouse out | ||
var addSelectedClass = function addSelectedClass(el) { | ||
el.addClass('vjs-selected'); | ||
}; | ||
var removeSelectedClass = function removeSelectedClass(el) { | ||
el.removeClass('vjs-selected'); | ||
if (typeof gen.return !== "function") { | ||
this.return = undefined; | ||
} | ||
} | ||
if (el.thumbnail) { | ||
dom.removeClass(el.thumbnail, 'vjs-playlist-now-playing'); | ||
} | ||
}; | ||
if (typeof Symbol === "function" && Symbol.asyncIterator) { | ||
AsyncGenerator.prototype[Symbol.asyncIterator] = function () { | ||
return this; | ||
}; | ||
} | ||
var upNext = function upNext(el) { | ||
el.addClass('vjs-up-next'); | ||
}; | ||
var notUpNext = function notUpNext(el) { | ||
el.removeClass('vjs-up-next'); | ||
}; | ||
AsyncGenerator.prototype.next = function (arg) { | ||
return this._invoke("next", arg); | ||
}; | ||
var createThumbnail = function createThumbnail(thumbnail) { | ||
if (!thumbnail) { | ||
var placeholder = document_1.createElement('div'); | ||
AsyncGenerator.prototype.throw = function (arg) { | ||
return this._invoke("throw", arg); | ||
}; | ||
placeholder.className = 'vjs-playlist-thumbnail vjs-playlist-thumbnail-placeholder'; | ||
return placeholder; | ||
} | ||
AsyncGenerator.prototype.return = function (arg) { | ||
return this._invoke("return", arg); | ||
}; | ||
var picture = document_1.createElement('picture'); | ||
return { | ||
wrap: function (fn) { | ||
return function () { | ||
return new AsyncGenerator(fn.apply(this, arguments)); | ||
}; | ||
}, | ||
await: function (value) { | ||
return new AwaitValue(value); | ||
} | ||
}; | ||
}(); | ||
picture.className = 'vjs-playlist-thumbnail'; | ||
if (typeof thumbnail === 'string') { | ||
// simple thumbnails | ||
var img = document_1.createElement('img'); | ||
img.src = thumbnail; | ||
img.alt = ''; | ||
picture.appendChild(img); | ||
} else { | ||
// responsive thumbnails | ||
// additional variations of a <picture> are specified as | ||
// <source> elements | ||
for (var i = 0; i < thumbnail.length - 1; i++) { | ||
var _variant = thumbnail[i]; | ||
var source = document_1.createElement('source'); | ||
// transfer the properties of each variant onto a <source> | ||
for (var prop in _variant) { | ||
source[prop] = _variant[prop]; | ||
} | ||
picture.appendChild(source); | ||
} | ||
var classCallCheck = function (instance, Constructor) { | ||
if (!(instance instanceof Constructor)) { | ||
throw new TypeError("Cannot call a class as a function"); | ||
} | ||
}; | ||
// the default version of a <picture> is specified by an <img> | ||
var variant = thumbnail[thumbnail.length - 1]; | ||
var _img = document_1.createElement('img'); | ||
_img.alt = ''; | ||
for (var _prop in variant) { | ||
_img[_prop] = variant[_prop]; | ||
} | ||
picture.appendChild(_img); | ||
} | ||
return picture; | ||
}; | ||
var Component = videojs.getComponent('Component'); | ||
var PlaylistMenuItem = function (_Component) { | ||
inherits(PlaylistMenuItem, _Component); | ||
function PlaylistMenuItem(player, playlistItem, settings) { | ||
classCallCheck(this, PlaylistMenuItem); | ||
if (!playlistItem.item) { | ||
throw new Error('Cannot construct a PlaylistMenuItem without an item option'); | ||
} | ||
var _this = possibleConstructorReturn(this, _Component.call(this, player, playlistItem)); | ||
_this.item = playlistItem.item; | ||
_this.playOnSelect = settings.playOnSelect; | ||
_this.emitTapEvents(); | ||
_this.on(['click', 'tap'], _this.switchPlaylistItem_); | ||
_this.on('keydown', _this.handleKeyDown_); | ||
var inherits = function (subClass, superClass) { | ||
if (typeof superClass !== "function" && superClass !== null) { | ||
throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); | ||
} | ||
return _this; | ||
} | ||
subClass.prototype = Object.create(superClass && superClass.prototype, { | ||
constructor: { | ||
value: subClass, | ||
enumerable: false, | ||
writable: true, | ||
configurable: true | ||
} | ||
}); | ||
if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; | ||
}; | ||
PlaylistMenuItem.prototype.handleKeyDown_ = function handleKeyDown_(event) { | ||
// keycode 13 is <Enter> | ||
// keycode 32 is <Space> | ||
if (event.which === 13 || event.which === 32) { | ||
this.switchPlaylistItem_(); | ||
} | ||
}; | ||
PlaylistMenuItem.prototype.switchPlaylistItem_ = function switchPlaylistItem_(event) { | ||
this.player_.playlist.currentItem(indexOf(this.player_.playlist(), this.item)); | ||
if (this.playOnSelect) { | ||
this.player_.play(); | ||
} | ||
}; | ||
PlaylistMenuItem.prototype.createEl = function createEl() { | ||
var li = document_1.createElement('li'); | ||
var item = this.options_.item; | ||
li.className = 'vjs-playlist-item'; | ||
li.setAttribute('tabIndex', 0); | ||
// Thumbnail image | ||
this.thumbnail = createThumbnail(item.thumbnail); | ||
li.appendChild(this.thumbnail); | ||
// Duration | ||
if (item.duration) { | ||
var duration = document_1.createElement('time'); | ||
var time = videojs.formatTime(item.duration); | ||
duration.className = 'vjs-playlist-duration'; | ||
duration.setAttribute('datetime', 'PT0H0M' + item.duration + 'S'); | ||
duration.appendChild(document_1.createTextNode(time)); | ||
li.appendChild(duration); | ||
} | ||
// Now playing | ||
var nowPlayingEl = document_1.createElement('span'); | ||
var nowPlayingText = this.localize('Now Playing'); | ||
nowPlayingEl.className = 'vjs-playlist-now-playing-text'; | ||
nowPlayingEl.appendChild(document_1.createTextNode(nowPlayingText)); | ||
nowPlayingEl.setAttribute('title', nowPlayingText); | ||
this.thumbnail.appendChild(nowPlayingEl); | ||
// Title container contains title and "up next" | ||
var titleContainerEl = document_1.createElement('div'); | ||
titleContainerEl.className = 'vjs-playlist-title-container'; | ||
this.thumbnail.appendChild(titleContainerEl); | ||
var possibleConstructorReturn = function (self, call) { | ||
if (!self) { | ||
throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); | ||
} | ||
// Up next | ||
var upNextEl = document_1.createElement('span'); | ||
var upNextText = this.localize('Up Next'); | ||
return call && (typeof call === "object" || typeof call === "function") ? call : self; | ||
}; | ||
upNextEl.className = 'vjs-up-next-text'; | ||
upNextEl.appendChild(document_1.createTextNode(upNextText)); | ||
upNextEl.setAttribute('title', upNextText); | ||
titleContainerEl.appendChild(upNextEl); | ||
// support VJS5 & VJS6 at the same time | ||
var dom$1 = videojs.dom || videojs; | ||
var registerPlugin$1 = videojs.registerPlugin || videojs.plugin; | ||
// Video title | ||
var titleEl = document_1.createElement('cite'); | ||
var titleText = item.name || this.localize('Untitled Video'); | ||
// Array#indexOf analog for IE8 | ||
var indexOf = function indexOf(array, target) { | ||
for (var i = 0, length = array.length; i < length; i++) { | ||
if (array[i] === target) { | ||
return i; | ||
} | ||
} | ||
return -1; | ||
}; | ||
titleEl.className = 'vjs-playlist-name'; | ||
titleEl.appendChild(document_1.createTextNode(titleText)); | ||
titleEl.setAttribute('title', titleText); | ||
titleContainerEl.appendChild(titleEl); | ||
// see https://github.com/Modernizr/Modernizr/blob/master/feature-detects/css/pointerevents.js | ||
var supportsCssPointerEvents = function () { | ||
var element = document_1.createElement('x'); | ||
return li; | ||
}; | ||
element.style.cssText = 'pointer-events:auto'; | ||
return element.style.pointerEvents === 'auto'; | ||
}(); | ||
return PlaylistMenuItem; | ||
}(Component); | ||
var defaults = { | ||
className: 'vjs-playlist', | ||
playOnSelect: false, | ||
supportsCssPointerEvents: supportsCssPointerEvents | ||
}; | ||
var PlaylistMenu = function (_Component2) { | ||
inherits(PlaylistMenu, _Component2); | ||
// we don't add `vjs-playlist-now-playing` in addSelectedClass | ||
// so it won't conflict with `vjs-icon-play | ||
// since it'll get added when we mouse out | ||
var addSelectedClass = function addSelectedClass(el) { | ||
el.addClass('vjs-selected'); | ||
}; | ||
var removeSelectedClass = function removeSelectedClass(el) { | ||
el.removeClass('vjs-selected'); | ||
function PlaylistMenu(player, options) { | ||
classCallCheck(this, PlaylistMenu); | ||
if (el.thumbnail) { | ||
dom$1.removeClass(el.thumbnail, 'vjs-playlist-now-playing'); | ||
} | ||
}; | ||
if (!player.playlist) { | ||
throw new Error('videojs-playlist is required for the playlist component'); | ||
} | ||
var upNext = function upNext(el) { | ||
el.addClass('vjs-up-next'); | ||
}; | ||
var notUpNext = function notUpNext(el) { | ||
el.removeClass('vjs-up-next'); | ||
}; | ||
var _this2 = possibleConstructorReturn(this, _Component2.call(this, player, options)); | ||
var createThumbnail = function createThumbnail(thumbnail) { | ||
if (!thumbnail) { | ||
var placeholder = document_1.createElement('div'); | ||
_this2.items = []; | ||
placeholder.className = 'vjs-playlist-thumbnail vjs-playlist-thumbnail-placeholder'; | ||
return placeholder; | ||
} | ||
if (options.horizontal) { | ||
_this2.addClass('vjs-playlist-horizontal'); | ||
} else { | ||
_this2.addClass('vjs-playlist-vertical'); | ||
} | ||
var picture = document_1.createElement('picture'); | ||
// If CSS pointer events aren't supported, we have to prevent | ||
// clicking on playlist items during ads with slightly more | ||
// invasive techniques. Details in the stylesheet. | ||
if (options.supportsCssPointerEvents) { | ||
_this2.addClass('vjs-csspointerevents'); | ||
} | ||
picture.className = 'vjs-playlist-thumbnail'; | ||
_this2.createPlaylist_(); | ||
if (typeof thumbnail === 'string') { | ||
// simple thumbnails | ||
var img = document_1.createElement('img'); | ||
if (!videojs.browser.TOUCH_ENABLED) { | ||
_this2.addClass('vjs-mouse'); | ||
} | ||
img.src = thumbnail; | ||
img.alt = ''; | ||
picture.appendChild(img); | ||
} else { | ||
// responsive thumbnails | ||
player.on(['loadstart', 'playlistchange', 'playlistsorted'], function (event) { | ||
_this2.update(); | ||
}); | ||
// additional variations of a <picture> are specified as | ||
// <source> elements | ||
for (var i = 0; i < thumbnail.length - 1; i++) { | ||
var _variant = thumbnail[i]; | ||
var source = document_1.createElement('source'); | ||
// Keep track of whether an ad is playing so that the menu | ||
// appearance can be adapted appropriately | ||
player.on('adstart', function () { | ||
_this2.addClass('vjs-ad-playing'); | ||
}); | ||
// transfer the properties of each variant onto a <source> | ||
for (var prop in _variant) { | ||
source[prop] = _variant[prop]; | ||
} | ||
picture.appendChild(source); | ||
} | ||
player.on('adend', function () { | ||
_this2.removeClass('vjs-ad-playing'); | ||
}); | ||
return _this2; | ||
} | ||
// the default version of a <picture> is specified by an <img> | ||
var variant = thumbnail[thumbnail.length - 1]; | ||
var _img = document_1.createElement('img'); | ||
PlaylistMenu.prototype.createEl = function createEl() { | ||
return dom.createEl('div', { className: this.options_.className }); | ||
}; | ||
_img.alt = ''; | ||
for (var _prop in variant) { | ||
_img[_prop] = variant[_prop]; | ||
} | ||
picture.appendChild(_img); | ||
} | ||
return picture; | ||
}; | ||
PlaylistMenu.prototype.createPlaylist_ = function createPlaylist_() { | ||
var playlist = this.player_.playlist() || []; | ||
var list = this.el_.querySelector('.vjs-playlist-item-list'); | ||
var overlay = this.el_.querySelector('.vjs-playlist-ad-overlay'); | ||
var Component = videojs.getComponent('Component'); | ||
if (!list) { | ||
list = document_1.createElement('ol'); | ||
list.className = 'vjs-playlist-item-list'; | ||
this.el_.appendChild(list); | ||
} | ||
var PlaylistMenuItem = function (_Component) { | ||
inherits(PlaylistMenuItem, _Component); | ||
// remove any existing items | ||
for (var i = 0; i < this.items.length; i++) { | ||
list.removeChild(this.items[i].el_); | ||
} | ||
this.items.length = 0; | ||
function PlaylistMenuItem(player, playlistItem, settings) { | ||
classCallCheck(this, PlaylistMenuItem); | ||
// create new items | ||
for (var _i = 0; _i < playlist.length; _i++) { | ||
var item = new PlaylistMenuItem(this.player_, { | ||
item: playlist[_i] | ||
}, this.options_); | ||
if (!playlistItem.item) { | ||
throw new Error('Cannot construct a PlaylistMenuItem without an item option'); | ||
} | ||
this.items.push(item); | ||
list.appendChild(item.el_); | ||
} | ||
var _this = possibleConstructorReturn(this, _Component.call(this, player, playlistItem)); | ||
// Inject the ad overlay. IE<11 doesn't support "pointer-events: | ||
// none" so we use this element to block clicks during ad | ||
// playback. | ||
if (!overlay) { | ||
overlay = document_1.createElement('li'); | ||
overlay.className = 'vjs-playlist-ad-overlay'; | ||
list.appendChild(overlay); | ||
} else { | ||
// Move overlay to end of list | ||
list.appendChild(overlay); | ||
} | ||
_this.item = playlistItem.item; | ||
// select the current playlist item | ||
var selectedIndex = this.player_.playlist.currentItem(); | ||
_this.playOnSelect = settings.playOnSelect; | ||
if (this.items.length && selectedIndex >= 0) { | ||
addSelectedClass(this.items[selectedIndex]); | ||
_this.emitTapEvents(); | ||
var thumbnail = this.items[selectedIndex].$('.vjs-playlist-thumbnail'); | ||
_this.on(['click', 'tap'], _this.switchPlaylistItem_); | ||
_this.on('keydown', _this.handleKeyDown_); | ||
if (thumbnail) { | ||
dom.addClass(thumbnail, 'vjs-playlist-now-playing'); | ||
} | ||
} | ||
}; | ||
return _this; | ||
} | ||
PlaylistMenu.prototype.update = function update() { | ||
// replace the playlist items being displayed, if necessary | ||
var playlist = this.player_.playlist(); | ||
PlaylistMenuItem.prototype.handleKeyDown_ = function handleKeyDown_(event) { | ||
// keycode 13 is <Enter> | ||
// keycode 32 is <Space> | ||
if (event.which === 13 || event.which === 32) { | ||
this.switchPlaylistItem_(); | ||
} | ||
}; | ||
if (this.items.length !== playlist.length) { | ||
// if the menu is currently empty or the state is obviously out | ||
// of date, rebuild everything. | ||
this.createPlaylist_(); | ||
return; | ||
} | ||
PlaylistMenuItem.prototype.switchPlaylistItem_ = function switchPlaylistItem_(event) { | ||
this.player_.playlist.currentItem(indexOf(this.player_.playlist(), this.item)); | ||
if (this.playOnSelect) { | ||
this.player_.play(); | ||
} | ||
}; | ||
for (var i = 0; i < this.items.length; i++) { | ||
if (this.items[i].item !== playlist[i]) { | ||
// if any of the playlist items have changed, rebuild the | ||
// entire playlist | ||
this.createPlaylist_(); | ||
return; | ||
} | ||
} | ||
PlaylistMenuItem.prototype.createEl = function createEl() { | ||
var li = document_1.createElement('li'); | ||
var item = this.options_.item; | ||
// the playlist itself is unchanged so just update the selection | ||
var currentItem = this.player_.playlist.currentItem(); | ||
li.className = 'vjs-playlist-item'; | ||
li.setAttribute('tabIndex', 0); | ||
for (var _i2 = 0; _i2 < this.items.length; _i2++) { | ||
var item = this.items[_i2]; | ||
// Thumbnail image | ||
this.thumbnail = createThumbnail(item.thumbnail); | ||
li.appendChild(this.thumbnail); | ||
if (_i2 === currentItem) { | ||
addSelectedClass(item); | ||
if (document_1.activeElement !== item.el()) { | ||
dom.addClass(item.thumbnail, 'vjs-playlist-now-playing'); | ||
} | ||
notUpNext(item); | ||
} else if (_i2 === currentItem + 1) { | ||
removeSelectedClass(item); | ||
upNext(item); | ||
} else { | ||
removeSelectedClass(item); | ||
notUpNext(item); | ||
} | ||
} | ||
}; | ||
// Duration | ||
if (item.duration) { | ||
var duration = document_1.createElement('time'); | ||
var time = videojs.formatTime(item.duration); | ||
return PlaylistMenu; | ||
}(Component); | ||
duration.className = 'vjs-playlist-duration'; | ||
duration.setAttribute('datetime', 'PT0H0M' + item.duration + 'S'); | ||
duration.appendChild(document_1.createTextNode(time)); | ||
li.appendChild(duration); | ||
} | ||
/** | ||
* Returns a boolean indicating whether an element has child elements. | ||
* | ||
* Note that this is distinct from whether it has child _nodes_. | ||
* | ||
* @param {HTMLElement} el | ||
* A DOM element. | ||
* | ||
* @return {boolean} | ||
* Whether the element has child elements. | ||
*/ | ||
// Now playing | ||
var nowPlayingEl = document_1.createElement('span'); | ||
var nowPlayingText = this.localize('Now Playing'); | ||
nowPlayingEl.className = 'vjs-playlist-now-playing-text'; | ||
nowPlayingEl.appendChild(document_1.createTextNode(nowPlayingText)); | ||
nowPlayingEl.setAttribute('title', nowPlayingText); | ||
this.thumbnail.appendChild(nowPlayingEl); | ||
var hasChildEls = function hasChildEls(el) { | ||
for (var i = 0; i < el.childNodes.length; i++) { | ||
if (dom.isEl(el.childNodes[i])) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
}; | ||
// Title container contains title and "up next" | ||
var titleContainerEl = document_1.createElement('div'); | ||
/** | ||
* Finds the first empty root element. | ||
* | ||
* @param {string} className | ||
* An HTML class name to search for. | ||
* | ||
* @return {HTMLElement} | ||
* A DOM element to use as the root for a playlist. | ||
*/ | ||
var findRoot = function findRoot(className) { | ||
var all = document_1.querySelectorAll('.' + className); | ||
var el = void 0; | ||
titleContainerEl.className = 'vjs-playlist-title-container'; | ||
this.thumbnail.appendChild(titleContainerEl); | ||
for (var i = 0; i < all.length; i++) { | ||
if (!hasChildEls(all[i])) { | ||
el = all[i]; | ||
break; | ||
} | ||
} | ||
// Up next | ||
var upNextEl = document_1.createElement('span'); | ||
var upNextText = this.localize('Up Next'); | ||
return el; | ||
}; | ||
upNextEl.className = 'vjs-up-next-text'; | ||
upNextEl.appendChild(document_1.createTextNode(upNextText)); | ||
upNextEl.setAttribute('title', upNextText); | ||
titleContainerEl.appendChild(upNextEl); | ||
/** | ||
* Initialize the plugin on a player. | ||
* | ||
* @param {Object} [options] | ||
* An options object. | ||
* | ||
* @param {HTMLElement} [options.el] | ||
* A DOM element to use as a root node for the playlist. | ||
* | ||
* @param {string} [options.className] | ||
* An HTML class name to use to find a root node for the playlist. | ||
* | ||
* @param {boolean} [options.playOnSelect = false] | ||
* If true, will attempt to begin playback upon selecting a new | ||
* playlist item in the UI. | ||
*/ | ||
var playlistUi = function playlistUi(options) { | ||
var player = this; | ||
// Video title | ||
var titleEl = document_1.createElement('cite'); | ||
var titleText = item.name || this.localize('Untitled Video'); | ||
if (!player.playlist) { | ||
throw new Error('videojs-playlist plugin is required by the videojs-playlist-ui plugin'); | ||
} | ||
titleEl.className = 'vjs-playlist-name'; | ||
titleEl.appendChild(document_1.createTextNode(titleText)); | ||
titleEl.setAttribute('title', titleText); | ||
titleContainerEl.appendChild(titleEl); | ||
if (dom.isEl(options)) { | ||
videojs.log.warn('videojs-playlist-ui: Passing an element directly to playlistUi() is deprecated, use the "el" option instead!'); | ||
options = { el: options }; | ||
} | ||
return li; | ||
}; | ||
options = videojs.mergeOptions(defaults$1, options); | ||
return PlaylistMenuItem; | ||
}(Component); | ||
// If the player is already using this plugin, remove the pre-existing | ||
// PlaylistMenu, but retain the element and its location in the DOM because | ||
// it will be re-used. | ||
if (player.playlistMenu) { | ||
var el = player.playlistMenu.el(); | ||
var PlaylistMenu = function (_Component2) { | ||
inherits(PlaylistMenu, _Component2); | ||
// Catch cases where the menu may have been disposed elsewhere or the | ||
// element removed from the DOM. | ||
if (el) { | ||
var parentNode = el.parentNode; | ||
var nextSibling = el.nextSibling; | ||
function PlaylistMenu(player, options) { | ||
classCallCheck(this, PlaylistMenu); | ||
// Disposing the menu will remove `el` from the DOM, but we need to | ||
// empty it ourselves to be sure. | ||
player.playlistMenu.dispose(); | ||
dom.emptyEl(el); | ||
if (!player.playlist) { | ||
throw new Error('videojs-playlist is required for the playlist component'); | ||
} | ||
// Put the element back in its place. | ||
if (nextSibling) { | ||
parentNode.insertBefore(el, nextSibling); | ||
} else { | ||
parentNode.appendChild(el); | ||
} | ||
var _this2 = possibleConstructorReturn(this, _Component2.call(this, player, options)); | ||
options.el = el; | ||
} | ||
} | ||
_this2.items = []; | ||
if (!dom.isEl(options.el)) { | ||
options.el = findRoot(options.className); | ||
} | ||
if (options.horizontal) { | ||
_this2.addClass('vjs-playlist-horizontal'); | ||
} else { | ||
_this2.addClass('vjs-playlist-vertical'); | ||
} | ||
player.playlistMenu = new PlaylistMenu(player, options); | ||
}; | ||
// If CSS pointer events aren't supported, we have to prevent | ||
// clicking on playlist items during ads with slightly more | ||
// invasive techniques. Details in the stylesheet. | ||
if (options.supportsCssPointerEvents) { | ||
_this2.addClass('vjs-csspointerevents'); | ||
} | ||
// register components | ||
videojs.registerComponent('PlaylistMenu', PlaylistMenu); | ||
videojs.registerComponent('PlaylistMenuItem', PlaylistMenuItem); | ||
_this2.createPlaylist_(); | ||
// register the plugin | ||
registerPlugin$1('playlistUi', playlistUi); | ||
if (!videojs.browser.TOUCH_ENABLED) { | ||
_this2.addClass('vjs-mouse'); | ||
} | ||
playlistUi.VERSION = version$1; | ||
player.on(['loadstart', 'playlistchange', 'playlistsorted'], function (event) { | ||
_this2.update(); | ||
}); | ||
var playlist = [{ | ||
name: 'Movie 1', | ||
description: 'Movie 1 description', | ||
duration: 100, | ||
sources: [{ | ||
src: '//example.com/movie1.mp4', | ||
type: 'video/mp4' | ||
}] | ||
}, { | ||
sources: [{ | ||
src: '//example.com/movie2.mp4', | ||
type: 'video/mp4' | ||
}], | ||
thumbnail: '//example.com/movie2.jpg' | ||
}]; | ||
// Keep track of whether an ad is playing so that the menu | ||
// appearance can be adapted appropriately | ||
player.on('adstart', function () { | ||
_this2.addClass('vjs-ad-playing'); | ||
}); | ||
var resolveUrl = function resolveUrl(url) { | ||
var a = document_1.createElement('a'); | ||
player.on('adend', function () { | ||
_this2.removeClass('vjs-ad-playing'); | ||
}); | ||
return _this2; | ||
} | ||
a.href = url; | ||
return a.href; | ||
}; | ||
PlaylistMenu.prototype.createEl = function createEl() { | ||
return dom$1.createEl('div', { className: this.options_.className }); | ||
}; | ||
var dom$1 = videojs.dom || videojs; | ||
var Html5 = videojs.getTech('Html5'); | ||
PlaylistMenu.prototype.createPlaylist_ = function createPlaylist_() { | ||
var playlist = this.player_.playlist() || []; | ||
var list = this.el_.querySelector('.vjs-playlist-item-list'); | ||
var overlay = this.el_.querySelector('.vjs-playlist-ad-overlay'); | ||
QUnit.test('the environment is sane', function (assert) { | ||
assert.ok(true, 'everything is swell'); | ||
}); | ||
if (!list) { | ||
list = document_1.createElement('ol'); | ||
list.className = 'vjs-playlist-item-list'; | ||
this.el_.appendChild(list); | ||
} | ||
function setup$1() { | ||
this.oldVideojsBrowser = videojs.browser; | ||
videojs.browser = videojs.mergeOptions({}, videojs.browser); | ||
// remove any existing items | ||
for (var i = 0; i < this.items.length; i++) { | ||
list.removeChild(this.items[i].el_); | ||
} | ||
this.items.length = 0; | ||
this.fixture = document_1.querySelector('#qunit-fixture'); | ||
// create new items | ||
for (var _i = 0; _i < playlist.length; _i++) { | ||
var item = new PlaylistMenuItem(this.player_, { | ||
item: playlist[_i] | ||
}, this.options_); | ||
// force HTML support so the tests run in a reasonable | ||
// environment under phantomjs | ||
this.realIsHtmlSupported = Html5.isSupported; | ||
Html5.isSupported = function () { | ||
return true; | ||
}; | ||
this.items.push(item); | ||
list.appendChild(item.el_); | ||
} | ||
// create a video element | ||
var video = document_1.createElement('video'); | ||
// Inject the ad overlay. IE<11 doesn't support "pointer-events: | ||
// none" so we use this element to block clicks during ad | ||
// playback. | ||
if (!overlay) { | ||
overlay = document_1.createElement('li'); | ||
overlay.className = 'vjs-playlist-ad-overlay'; | ||
list.appendChild(overlay); | ||
} else { | ||
// Move overlay to end of list | ||
list.appendChild(overlay); | ||
} | ||
this.fixture.appendChild(video); | ||
// select the current playlist item | ||
var selectedIndex = this.player_.playlist.currentItem(); | ||
// create a video.js player | ||
this.player = videojs(video); | ||
if (this.items.length && selectedIndex >= 0) { | ||
addSelectedClass(this.items[selectedIndex]); | ||
// Create two playlist container elements. | ||
this.fixture.appendChild(dom$1.createEl('div', { className: 'vjs-playlist' })); | ||
this.fixture.appendChild(dom$1.createEl('div', { className: 'vjs-playlist' })); | ||
} | ||
var thumbnail = this.items[selectedIndex].$('.vjs-playlist-thumbnail'); | ||
function teardown() { | ||
videojs.browser = this.oldVideojsBrowser; | ||
Html5.isSupported = this.realIsHtmlSupported; | ||
this.player.dispose(); | ||
this.player = null; | ||
dom$1.emptyEl(this.fixture); | ||
} | ||
if (thumbnail) { | ||
dom$1.addClass(thumbnail, 'vjs-playlist-now-playing'); | ||
} | ||
} | ||
}; | ||
QUnit.module('videojs-playlist-ui', { beforeEach: setup$1, afterEach: teardown }); | ||
PlaylistMenu.prototype.update = function update() { | ||
// replace the playlist items being displayed, if necessary | ||
var playlist = this.player_.playlist(); | ||
QUnit.test('registers itself', function (assert) { | ||
assert.ok(this.player.playlistUi, 'registered the plugin'); | ||
}); | ||
if (this.items.length !== playlist.length) { | ||
// if the menu is currently empty or the state is obviously out | ||
// of date, rebuild everything. | ||
this.createPlaylist_(); | ||
return; | ||
} | ||
QUnit.test('errors if used without the playlist plugin', function (assert) { | ||
assert.throws(function () { | ||
this.player.playlist = null; | ||
this.player.playlistUi(); | ||
}, 'threw on init'); | ||
}); | ||
for (var i = 0; i < this.items.length; i++) { | ||
if (this.items[i].item !== playlist[i]) { | ||
// if any of the playlist items have changed, rebuild the | ||
// entire playlist | ||
this.createPlaylist_(); | ||
return; | ||
} | ||
} | ||
QUnit.test('is empty if the playlist plugin isn\'t initialized', function (assert) { | ||
this.player.playlistUi(); | ||
// the playlist itself is unchanged so just update the selection | ||
var currentItem = this.player_.playlist.currentItem(); | ||
var items = this.fixture.querySelectorAll('.vjs-playlist-item'); | ||
for (var _i2 = 0; _i2 < this.items.length; _i2++) { | ||
var item = this.items[_i2]; | ||
assert.ok(this.fixture.querySelector('.vjs-playlist'), 'created the menu'); | ||
assert.strictEqual(items.length, 0, 'displayed no items'); | ||
}); | ||
if (_i2 === currentItem) { | ||
addSelectedClass(item); | ||
if (document_1.activeElement !== item.el()) { | ||
dom$1.addClass(item.thumbnail, 'vjs-playlist-now-playing'); | ||
} | ||
notUpNext(item); | ||
} else if (_i2 === currentItem + 1) { | ||
removeSelectedClass(item); | ||
upNext(item); | ||
} else { | ||
removeSelectedClass(item); | ||
notUpNext(item); | ||
} | ||
} | ||
}; | ||
QUnit.test('can be initialized with an element (deprecated form)', function (assert) { | ||
var elem = dom$1.createEl('div'); | ||
return PlaylistMenu; | ||
}(Component); | ||
this.player.playlist(playlist); | ||
this.player.playlistUi(elem); | ||
/** | ||
* Returns a boolean indicating whether an element has child elements. | ||
* | ||
* Note that this is distinct from whether it has child _nodes_. | ||
* | ||
* @param {HTMLElement} el | ||
* A DOM element. | ||
* | ||
* @return {boolean} | ||
* Whether the element has child elements. | ||
*/ | ||
assert.strictEqual(elem.querySelectorAll('li.vjs-playlist-item').length, playlist.length, 'created an element for each playlist item'); | ||
}); | ||
QUnit.test('can be initialized with an element', function (assert) { | ||
var elem = dom$1.createEl('div'); | ||
var hasChildEls = function hasChildEls(el) { | ||
for (var i = 0; i < el.childNodes.length; i++) { | ||
if (dom$1.isEl(el.childNodes[i])) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
}; | ||
this.player.playlist(playlist); | ||
this.player.playlistUi({ el: elem }); | ||
/** | ||
* Finds the first empty root element. | ||
* | ||
* @param {string} className | ||
* An HTML class name to search for. | ||
* | ||
* @return {HTMLElement} | ||
* A DOM element to use as the root for a playlist. | ||
*/ | ||
var findRoot = function findRoot(className) { | ||
var all = document_1.querySelectorAll('.' + className); | ||
var el = void 0; | ||
assert.strictEqual(elem.querySelectorAll('li.vjs-playlist-item').length, playlist.length, 'created an element for each playlist item'); | ||
}); | ||
for (var i = 0; i < all.length; i++) { | ||
if (!hasChildEls(all[i])) { | ||
el = all[i]; | ||
break; | ||
} | ||
} | ||
QUnit.test('can look for an element with the class "vjs-playlist" that is not already in use', function (assert) { | ||
var firstEl = this.fixture.querySelectorAll('.vjs-playlist')[0]; | ||
var secondEl = this.fixture.querySelectorAll('.vjs-playlist')[1]; | ||
return el; | ||
}; | ||
// Give the firstEl a child, so the plugin thinks it is in use and moves on | ||
// to the next one. | ||
firstEl.appendChild(dom$1.createEl('div')); | ||
/** | ||
* Initialize the plugin on a player. | ||
* | ||
* @param {Object} [options] | ||
* An options object. | ||
* | ||
* @param {HTMLElement} [options.el] | ||
* A DOM element to use as a root node for the playlist. | ||
* | ||
* @param {string} [options.className] | ||
* An HTML class name to use to find a root node for the playlist. | ||
* | ||
* @param {boolean} [options.playOnSelect = false] | ||
* If true, will attempt to begin playback upon selecting a new | ||
* playlist item in the UI. | ||
*/ | ||
var playlistUi = function playlistUi(options) { | ||
var player = this; | ||
this.player.playlist(playlist); | ||
this.player.playlistUi(); | ||
if (!player.playlist) { | ||
throw new Error('videojs-playlist plugin is required by the videojs-playlist-ui plugin'); | ||
} | ||
assert.strictEqual(this.player.playlistMenu.el(), secondEl, 'used the first matching/empty element'); | ||
assert.strictEqual(secondEl.querySelectorAll('li.vjs-playlist-item').length, playlist.length, 'found an element for each playlist item'); | ||
}); | ||
if (dom$1.isEl(options)) { | ||
videojs.log.warn('videojs-playlist-ui: Passing an element directly to playlistUi() is deprecated, use the "el" option instead!'); | ||
options = { el: options }; | ||
} | ||
QUnit.test('can look for an element with a custom class that is not already in use', function (assert) { | ||
var firstEl = dom$1.createEl('div', { className: 'super-playlist' }); | ||
var secondEl = dom$1.createEl('div', { className: 'super-playlist' }); | ||
options = videojs.mergeOptions(defaults, options); | ||
// Give the firstEl a child, so the plugin thinks it is in use and moves on | ||
// to the next one. | ||
firstEl.appendChild(dom$1.createEl('div')); | ||
// If the player is already using this plugin, remove the pre-existing | ||
// PlaylistMenu, but retain the element and its location in the DOM because | ||
// it will be re-used. | ||
if (player.playlistMenu) { | ||
var el = player.playlistMenu.el(); | ||
this.fixture.appendChild(firstEl); | ||
this.fixture.appendChild(secondEl); | ||
// Catch cases where the menu may have been disposed elsewhere or the | ||
// element removed from the DOM. | ||
if (el) { | ||
var parentNode = el.parentNode; | ||
var nextSibling = el.nextSibling; | ||
this.player.playlist(playlist); | ||
this.player.playlistUi({ | ||
className: 'super-playlist' | ||
}); | ||
// Disposing the menu will remove `el` from the DOM, but we need to | ||
// empty it ourselves to be sure. | ||
player.playlistMenu.dispose(); | ||
dom$1.emptyEl(el); | ||
assert.strictEqual(this.player.playlistMenu.el(), secondEl, 'used the first matching/empty element'); | ||
assert.strictEqual(this.fixture.querySelectorAll('li.vjs-playlist-item').length, playlist.length, 'created an element for each playlist item'); | ||
}); | ||
// Put the element back in its place. | ||
if (nextSibling) { | ||
parentNode.insertBefore(el, nextSibling); | ||
} else { | ||
parentNode.appendChild(el); | ||
} | ||
QUnit.test('specializes the class name if touch input is absent', function (assert) { | ||
videojs.browser.TOUCH_ENABLED = false; | ||
options.el = el; | ||
} | ||
} | ||
this.player.playlist(playlist); | ||
this.player.playlistUi(); | ||
if (!dom$1.isEl(options.el)) { | ||
options.el = findRoot(options.className); | ||
} | ||
assert.ok(this.player.playlistMenu.hasClass('vjs-mouse'), 'marked the playlist menu'); | ||
}); | ||
player.playlistMenu = new PlaylistMenu(player, options); | ||
}; | ||
QUnit.test('can be re-initialized without doubling the contents of the list', function (assert) { | ||
var el = this.fixture.querySelectorAll('.vjs-playlist')[0]; | ||
// register components | ||
videojs.registerComponent('PlaylistMenu', PlaylistMenu); | ||
videojs.registerComponent('PlaylistMenuItem', PlaylistMenuItem); | ||
this.player.playlist(playlist); | ||
this.player.playlistUi(); | ||
this.player.playlistUi(); | ||
this.player.playlistUi(); | ||
// register the plugin | ||
registerPlugin$1('playlistUi', playlistUi); | ||
assert.strictEqual(this.player.playlistMenu.el(), el, 'used the first matching/empty element'); | ||
assert.strictEqual(el.querySelectorAll('li.vjs-playlist-item').length, playlist.length, 'found an element for each playlist item'); | ||
}); | ||
playlistUi.VERSION = version; | ||
QUnit.module('videojs-playlist-ui: Components', { beforeEach: setup$1, afterEach: teardown }); | ||
var playlist = [{ | ||
name: 'Movie 1', | ||
description: 'Movie 1 description', | ||
duration: 100, | ||
sources: [{ | ||
src: '//example.com/movie1.mp4', | ||
type: 'video/mp4' | ||
}] | ||
}, { | ||
sources: [{ | ||
src: '//example.com/movie2.mp4', | ||
type: 'video/mp4' | ||
}], | ||
thumbnail: '//example.com/movie2.jpg' | ||
}]; | ||
// -------------------- | ||
// Creation and Updates | ||
// -------------------- | ||
var resolveUrl = function resolveUrl(url) { | ||
var a = document_1.createElement('a'); | ||
QUnit.test('includes the video name if provided', function (assert) { | ||
this.player.playlist(playlist); | ||
this.player.playlistUi(); | ||
a.href = url; | ||
return a.href; | ||
}; | ||
var items = this.fixture.querySelectorAll('.vjs-playlist-item'); | ||
var dom = videojs.dom || videojs; | ||
var Html5 = videojs.getTech('Html5'); | ||
assert.strictEqual(items[0].querySelector('.vjs-playlist-name').textContent, playlist[0].name, 'wrote the name'); | ||
assert.strictEqual(items[1].querySelector('.vjs-playlist-name').textContent, 'Untitled Video', 'wrote a placeholder for the name'); | ||
}); | ||
QUnit.test('the environment is sane', function (assert) { | ||
assert.ok(true, 'everything is swell'); | ||
}); | ||
QUnit.test('outputs a <picture> for simple thumbnails', function (assert) { | ||
this.player.playlist(playlist); | ||
this.player.playlistUi(); | ||
function setup() { | ||
this.oldVideojsBrowser = videojs.browser; | ||
videojs.browser = videojs.mergeOptions({}, videojs.browser); | ||
var pictures = this.fixture.querySelectorAll('.vjs-playlist-item picture'); | ||
this.fixture = document_1.querySelector('#qunit-fixture'); | ||
assert.strictEqual(pictures.length, 1, 'output one picture'); | ||
var imgs = pictures[0].querySelectorAll('img'); | ||
// force HTML support so the tests run in a reasonable | ||
// environment under phantomjs | ||
this.realIsHtmlSupported = Html5.isSupported; | ||
Html5.isSupported = function () { | ||
return true; | ||
}; | ||
assert.strictEqual(imgs.length, 1, 'output one img'); | ||
assert.strictEqual(imgs[0].src, window_1.location.protocol + playlist[1].thumbnail, 'set the src attribute'); | ||
}); | ||
// create a video element | ||
var video = document_1.createElement('video'); | ||
QUnit.test('outputs a <picture> for responsive thumbnails', function (assert) { | ||
var playlistOverride = [{ | ||
sources: [{ | ||
src: '//example.com/movie.mp4', | ||
type: 'video/mp4' | ||
}], | ||
thumbnail: [{ | ||
srcset: '/test/example/oceans.jpg', | ||
type: 'image/jpeg', | ||
media: '(min-width: 400px;)' | ||
}, { | ||
src: '/test/example/oceans-low.jpg' | ||
}] | ||
}]; | ||
this.fixture.appendChild(video); | ||
this.player.playlist(playlistOverride); | ||
this.player.playlistUi(); | ||
// create a video.js player | ||
this.player = videojs(video); | ||
var sources = this.fixture.querySelectorAll('.vjs-playlist-item picture source'); | ||
var imgs = this.fixture.querySelectorAll('.vjs-playlist-item picture img'); | ||
// Create two playlist container elements. | ||
this.fixture.appendChild(dom.createEl('div', { className: 'vjs-playlist' })); | ||
this.fixture.appendChild(dom.createEl('div', { className: 'vjs-playlist' })); | ||
} | ||
assert.strictEqual(sources.length, 1, 'output one source'); | ||
assert.strictEqual(sources[0].srcset, playlistOverride[0].thumbnail[0].srcset, 'wrote the srcset attribute'); | ||
assert.strictEqual(sources[0].type, playlistOverride[0].thumbnail[0].type, 'wrote the type attribute'); | ||
assert.strictEqual(sources[0].media, playlistOverride[0].thumbnail[0].media, 'wrote the type attribute'); | ||
assert.strictEqual(imgs.length, 1, 'output one img'); | ||
assert.strictEqual(imgs[0].src, resolveUrl(playlistOverride[0].thumbnail[1].src), 'output the img src attribute'); | ||
}); | ||
function teardown() { | ||
videojs.browser = this.oldVideojsBrowser; | ||
Html5.isSupported = this.realIsHtmlSupported; | ||
this.player.dispose(); | ||
this.player = null; | ||
dom.emptyEl(this.fixture); | ||
} | ||
QUnit.test('outputs a placeholder for items without thumbnails', function (assert) { | ||
this.player.playlist(playlist); | ||
this.player.playlistUi(); | ||
QUnit.module('videojs-playlist-ui', { setup: setup, teardown: teardown }); | ||
var thumbnails = this.fixture.querySelectorAll('.vjs-playlist-item .vjs-playlist-thumbnail'); | ||
QUnit.test('registers itself', function (assert) { | ||
assert.ok(this.player.playlistUi, 'registered the plugin'); | ||
}); | ||
assert.strictEqual(thumbnails.length, playlist.length, 'output two thumbnails'); | ||
assert.strictEqual(thumbnails[0].nodeName.toLowerCase(), 'div', 'the second is a placeholder'); | ||
}); | ||
QUnit.test('errors if used without the playlist plugin', function (assert) { | ||
assert.throws(function () { | ||
this.player.playlist = null; | ||
this.player.playlistUi(); | ||
}, 'threw on init'); | ||
}); | ||
QUnit.test('includes the duration if one is provided', function (assert) { | ||
this.player.playlist(playlist); | ||
this.player.playlistUi(); | ||
QUnit.test('is empty if the playlist plugin isn\'t initialized', function (assert) { | ||
this.player.playlistUi(); | ||
var durations = this.fixture.querySelectorAll('.vjs-playlist-item .vjs-playlist-duration'); | ||
var items = this.fixture.querySelectorAll('.vjs-playlist-item'); | ||
assert.strictEqual(durations.length, 1, 'skipped the item without a duration'); | ||
assert.strictEqual(durations[0].textContent, '1:40', 'wrote the duration'); | ||
assert.strictEqual(durations[0].getAttribute('datetime'), 'PT0H0M' + playlist[0].duration + 'S', 'wrote a machine-readable datetime'); | ||
}); | ||
assert.ok(this.fixture.querySelector('.vjs-playlist'), 'created the menu'); | ||
assert.strictEqual(items.length, 0, 'displayed no items'); | ||
}); | ||
QUnit.test('marks the selected playlist item on startup', function (assert) { | ||
this.player.playlist(playlist); | ||
this.player.currentSrc = function () { | ||
return playlist[0].sources[0].src; | ||
}; | ||
this.player.playlistUi(); | ||
QUnit.test('can be initialized with an element (deprecated form)', function (assert) { | ||
var elem = dom.createEl('div'); | ||
var selectedItems = this.fixture.querySelectorAll('.vjs-playlist-item.vjs-selected'); | ||
this.player.playlist(playlist); | ||
this.player.playlistUi(elem); | ||
assert.strictEqual(selectedItems.length, 1, 'marked one playlist item'); | ||
assert.strictEqual(selectedItems[0].querySelector('.vjs-playlist-name').textContent, playlist[0].name, 'marked the first playlist item'); | ||
}); | ||
assert.strictEqual(elem.querySelectorAll('li.vjs-playlist-item').length, playlist.length, 'created an element for each playlist item'); | ||
}); | ||
QUnit.test('updates the selected playlist item on loadstart', function (assert) { | ||
this.player.playlist(playlist); | ||
this.player.playlistUi(); | ||
QUnit.test('can be initialized with an element', function (assert) { | ||
var elem = dom.createEl('div'); | ||
this.player.playlist.currentItem(1); | ||
this.player.currentSrc = function () { | ||
return playlist[1].sources[0].src; | ||
}; | ||
this.player.trigger('loadstart'); | ||
this.player.playlist(playlist); | ||
this.player.playlistUi({ el: elem }); | ||
var selectedItems = this.fixture.querySelectorAll('.vjs-playlist-item.vjs-selected'); | ||
assert.strictEqual(elem.querySelectorAll('li.vjs-playlist-item').length, playlist.length, 'created an element for each playlist item'); | ||
}); | ||
assert.strictEqual(this.fixture.querySelectorAll('.vjs-playlist-item').length, playlist.length, 'displayed the correct number of items'); | ||
assert.strictEqual(selectedItems.length, 1, 'marked one playlist item'); | ||
assert.strictEqual(selectedItems[0].querySelector('img').src, resolveUrl(playlist[1].thumbnail), 'marked the second playlist item'); | ||
}); | ||
QUnit.test('can look for an element with the class "vjs-playlist" that is not already in use', function (assert) { | ||
var firstEl = this.fixture.querySelectorAll('.vjs-playlist')[0]; | ||
var secondEl = this.fixture.querySelectorAll('.vjs-playlist')[1]; | ||
QUnit.test('selects no item if the playlist is not in use', function (assert) { | ||
this.player.playlist(playlist); | ||
this.player.playlist.currentItem = function () { | ||
return -1; | ||
}; | ||
this.player.playlistUi(); | ||
// Give the firstEl a child, so the plugin thinks it is in use and moves on | ||
// to the next one. | ||
firstEl.appendChild(dom.createEl('div')); | ||
this.player.trigger('loadstart'); | ||
this.player.playlist(playlist); | ||
this.player.playlistUi(); | ||
assert.strictEqual(this.fixture.querySelectorAll('.vjs-playlist-item.vjs-selected').length, 0, 'no items selected'); | ||
}); | ||
assert.strictEqual(this.player.playlistMenu.el(), secondEl, 'used the first matching/empty element'); | ||
assert.strictEqual(secondEl.querySelectorAll('li.vjs-playlist-item').length, playlist.length, 'found an element for each playlist item'); | ||
}); | ||
QUnit.test('updates on "playlistchange", different lengths', function (assert) { | ||
this.player.playlist([]); | ||
this.player.playlistUi(); | ||
QUnit.test('can look for an element with a custom class that is not already in use', function (assert) { | ||
var firstEl = dom.createEl('div', { className: 'super-playlist' }); | ||
var secondEl = dom.createEl('div', { className: 'super-playlist' }); | ||
var items = this.fixture.querySelectorAll('.vjs-playlist-item'); | ||
// Give the firstEl a child, so the plugin thinks it is in use and moves on | ||
// to the next one. | ||
firstEl.appendChild(dom.createEl('div')); | ||
assert.strictEqual(items.length, 0, 'no items initially'); | ||
this.fixture.appendChild(firstEl); | ||
this.fixture.appendChild(secondEl); | ||
this.player.playlist(playlist); | ||
this.player.trigger('playlistchange'); | ||
items = this.fixture.querySelectorAll('.vjs-playlist-item'); | ||
assert.strictEqual(items.length, playlist.length, 'updated with the new items'); | ||
}); | ||
this.player.playlist(playlist); | ||
this.player.playlistUi({ | ||
className: 'super-playlist' | ||
}); | ||
QUnit.test('updates on "playlistchange", equal lengths', function (assert) { | ||
this.player.playlist([{ sources: [] }, { sources: [] }]); | ||
this.player.playlistUi(); | ||
assert.strictEqual(this.player.playlistMenu.el(), secondEl, 'used the first matching/empty element'); | ||
assert.strictEqual(this.fixture.querySelectorAll('li.vjs-playlist-item').length, playlist.length, 'created an element for each playlist item'); | ||
}); | ||
var items = this.fixture.querySelectorAll('.vjs-playlist-item'); | ||
QUnit.test('specializes the class name if touch input is absent', function (assert) { | ||
videojs.browser.TOUCH_ENABLED = false; | ||
assert.strictEqual(items.length, 2, 'two items initially'); | ||
this.player.playlist(playlist); | ||
this.player.playlistUi(); | ||
this.player.playlist(playlist); | ||
this.player.trigger('playlistchange'); | ||
items = this.fixture.querySelectorAll('.vjs-playlist-item'); | ||
assert.strictEqual(items.length, playlist.length, 'updated with the new items'); | ||
assert.strictEqual(this.player.playlistMenu.items[0].item, playlist[0], 'we have updated items'); | ||
assert.strictEqual(this.player.playlistMenu.items[1].item, playlist[1], 'we have updated items'); | ||
}); | ||
assert.ok(this.player.playlistMenu.hasClass('vjs-mouse'), 'marked the playlist menu'); | ||
}); | ||
QUnit.test('updates on "playlistchange", update selection', function (assert) { | ||
this.player.playlist(playlist); | ||
this.player.currentSrc = function () { | ||
return playlist[0].sources[0].src; | ||
}; | ||
this.player.playlistUi(); | ||
QUnit.test('can be re-initialized without doubling the contents of the list', function (assert) { | ||
var el = this.fixture.querySelectorAll('.vjs-playlist')[0]; | ||
var items = this.fixture.querySelectorAll('.vjs-playlist-item'); | ||
this.player.playlist(playlist); | ||
this.player.playlistUi(); | ||
this.player.playlistUi(); | ||
this.player.playlistUi(); | ||
assert.strictEqual(items.length, 2, 'two items initially'); | ||
assert.strictEqual(this.player.playlistMenu.el(), el, 'used the first matching/empty element'); | ||
assert.strictEqual(el.querySelectorAll('li.vjs-playlist-item').length, playlist.length, 'found an element for each playlist item'); | ||
}); | ||
assert.ok(/vjs-selected/.test(items[0].getAttribute('class')), 'first item is selected by default'); | ||
this.player.playlist.currentItem(1); | ||
this.player.currentSrc = function () { | ||
return playlist[1].sources[0].src; | ||
}; | ||
QUnit.module('videojs-playlist-ui: Components', { setup: setup, teardown: teardown }); | ||
this.player.trigger('playlistchange'); | ||
items = this.fixture.querySelectorAll('.vjs-playlist-item'); | ||
assert.strictEqual(items.length, playlist.length, 'updated with the new items'); | ||
assert.ok(/vjs-selected/.test(items[1].getAttribute('class')), 'second item is selected after update'); | ||
assert.ok(!/vjs-selected/.test(items[0].getAttribute('class')), 'first item is not selected after update'); | ||
}); | ||
// -------------------- | ||
// Creation and Updates | ||
// -------------------- | ||
QUnit.test('updates on "playlistsorted", different lengths', function (assert) { | ||
this.player.playlist([]); | ||
this.player.playlistUi(); | ||
QUnit.test('includes the video name if provided', function (assert) { | ||
this.player.playlist(playlist); | ||
this.player.playlistUi(); | ||
var items = this.fixture.querySelectorAll('.vjs-playlist-item'); | ||
var items = this.fixture.querySelectorAll('.vjs-playlist-item'); | ||
assert.strictEqual(items.length, 0, 'no items initially'); | ||
assert.strictEqual(items[0].querySelector('.vjs-playlist-name').textContent, playlist[0].name, 'wrote the name'); | ||
assert.strictEqual(items[1].querySelector('.vjs-playlist-name').textContent, 'Untitled Video', 'wrote a placeholder for the name'); | ||
}); | ||
this.player.playlist(playlist); | ||
this.player.trigger('playlistsorted'); | ||
items = this.fixture.querySelectorAll('.vjs-playlist-item'); | ||
assert.strictEqual(items.length, playlist.length, 'updated with the new items'); | ||
}); | ||
QUnit.test('outputs a <picture> for simple thumbnails', function (assert) { | ||
this.player.playlist(playlist); | ||
this.player.playlistUi(); | ||
QUnit.test('updates on "playlistsorted", equal lengths', function (assert) { | ||
this.player.playlist([{ sources: [] }, { sources: [] }]); | ||
this.player.playlistUi(); | ||
var pictures = this.fixture.querySelectorAll('.vjs-playlist-item picture'); | ||
var items = this.fixture.querySelectorAll('.vjs-playlist-item'); | ||
assert.strictEqual(pictures.length, 1, 'output one picture'); | ||
var imgs = pictures[0].querySelectorAll('img'); | ||
assert.strictEqual(items.length, 2, 'two items initially'); | ||
assert.strictEqual(imgs.length, 1, 'output one img'); | ||
assert.strictEqual(imgs[0].src, window_1.location.protocol + playlist[1].thumbnail, 'set the src attribute'); | ||
}); | ||
this.player.playlist(playlist); | ||
this.player.trigger('playlistsorted'); | ||
items = this.fixture.querySelectorAll('.vjs-playlist-item'); | ||
assert.strictEqual(items.length, playlist.length, 'updated with the new items'); | ||
assert.strictEqual(this.player.playlistMenu.items[0].item, playlist[0], 'we have updated items'); | ||
assert.strictEqual(this.player.playlistMenu.items[1].item, playlist[1], 'we have updated items'); | ||
}); | ||
QUnit.test('outputs a <picture> for responsive thumbnails', function (assert) { | ||
var playlistOverride = [{ | ||
sources: [{ | ||
src: '//example.com/movie.mp4', | ||
type: 'video/mp4' | ||
}], | ||
thumbnail: [{ | ||
srcset: '/test/example/oceans.jpg', | ||
type: 'image/jpeg', | ||
media: '(min-width: 400px;)' | ||
}, { | ||
src: '/test/example/oceans-low.jpg' | ||
}] | ||
}]; | ||
QUnit.test('updates on "playlistsorted", update selection', function (assert) { | ||
this.player.playlist(playlist); | ||
this.player.currentSrc = function () { | ||
return playlist[0].sources[0].src; | ||
}; | ||
this.player.playlistUi(); | ||
this.player.playlist(playlistOverride); | ||
this.player.playlistUi(); | ||
var items = this.fixture.querySelectorAll('.vjs-playlist-item'); | ||
var sources = this.fixture.querySelectorAll('.vjs-playlist-item picture source'); | ||
var imgs = this.fixture.querySelectorAll('.vjs-playlist-item picture img'); | ||
assert.strictEqual(items.length, 2, 'two items initially'); | ||
assert.strictEqual(sources.length, 1, 'output one source'); | ||
assert.strictEqual(sources[0].srcset, playlistOverride[0].thumbnail[0].srcset, 'wrote the srcset attribute'); | ||
assert.strictEqual(sources[0].type, playlistOverride[0].thumbnail[0].type, 'wrote the type attribute'); | ||
assert.strictEqual(sources[0].media, playlistOverride[0].thumbnail[0].media, 'wrote the type attribute'); | ||
assert.strictEqual(imgs.length, 1, 'output one img'); | ||
assert.strictEqual(imgs[0].src, resolveUrl(playlistOverride[0].thumbnail[1].src), 'output the img src attribute'); | ||
}); | ||
assert.ok(/vjs-selected/.test(items[0].getAttribute('class')), 'first item is selected by default'); | ||
this.player.playlist.currentItem(1); | ||
this.player.currentSrc = function () { | ||
return playlist[1].sources[0].src; | ||
}; | ||
QUnit.test('outputs a placeholder for items without thumbnails', function (assert) { | ||
this.player.playlist(playlist); | ||
this.player.playlistUi(); | ||
this.player.trigger('playlistsorted'); | ||
items = this.fixture.querySelectorAll('.vjs-playlist-item'); | ||
assert.strictEqual(items.length, playlist.length, 'updated with the new items'); | ||
assert.ok(/vjs-selected/.test(items[1].getAttribute('class')), 'second item is selected after update'); | ||
assert.ok(!/vjs-selected/.test(items[0].getAttribute('class')), 'first item is not selected after update'); | ||
}); | ||
var thumbnails = this.fixture.querySelectorAll('.vjs-playlist-item .vjs-playlist-thumbnail'); | ||
QUnit.test('tracks when an ad is playing', function (assert) { | ||
this.player.playlist([]); | ||
this.player.playlistUi(); | ||
assert.strictEqual(thumbnails.length, playlist.length, 'output two thumbnails'); | ||
assert.strictEqual(thumbnails[0].nodeName.toLowerCase(), 'div', 'the second is a placeholder'); | ||
}); | ||
this.player.duration = function () { | ||
return 5; | ||
}; | ||
QUnit.test('includes the duration if one is provided', function (assert) { | ||
this.player.playlist(playlist); | ||
this.player.playlistUi(); | ||
var playlistMenu = this.player.playlistMenu; | ||
var durations = this.fixture.querySelectorAll('.vjs-playlist-item .vjs-playlist-duration'); | ||
assert.ok(!playlistMenu.hasClass('vjs-ad-playing'), 'does not have class vjs-ad-playing'); | ||
this.player.trigger('adstart'); | ||
assert.ok(playlistMenu.hasClass('vjs-ad-playing'), 'has class vjs-ad-playing'); | ||
assert.strictEqual(durations.length, 1, 'skipped the item without a duration'); | ||
assert.strictEqual(durations[0].textContent, '1:40', 'wrote the duration'); | ||
assert.strictEqual(durations[0].getAttribute('datetime'), 'PT0H0M' + playlist[0].duration + 'S', 'wrote a machine-readable datetime'); | ||
}); | ||
this.player.trigger('adend'); | ||
assert.ok(!playlistMenu.hasClass('vjs-ad-playing'), 'does not have class vjs-ad-playing'); | ||
}); | ||
QUnit.test('marks the selected playlist item on startup', function (assert) { | ||
this.player.playlist(playlist); | ||
this.player.currentSrc = function () { | ||
return playlist[0].sources[0].src; | ||
}; | ||
this.player.playlistUi(); | ||
// ----------- | ||
// Interaction | ||
// ----------- | ||
var selectedItems = this.fixture.querySelectorAll('.vjs-playlist-item.vjs-selected'); | ||
QUnit.test('changes the selection when tapped', function (assert) { | ||
var playCalled = false; | ||
assert.strictEqual(selectedItems.length, 1, 'marked one playlist item'); | ||
assert.strictEqual(selectedItems[0].querySelector('.vjs-playlist-name').textContent, playlist[0].name, 'marked the first playlist item'); | ||
}); | ||
this.player.playlist(playlist); | ||
this.player.playlistUi({ playOnSelect: true }); | ||
this.player.play = function () { | ||
playCalled = true; | ||
}; | ||
QUnit.test('updates the selected playlist item on loadstart', function (assert) { | ||
this.player.playlist(playlist); | ||
this.player.playlistUi(); | ||
var sources = void 0; | ||
this.player.playlist.currentItem(1); | ||
this.player.currentSrc = function () { | ||
return playlist[1].sources[0].src; | ||
}; | ||
this.player.trigger('loadstart'); | ||
this.player.src = function (src) { | ||
if (src) { | ||
sources = src; | ||
} | ||
return sources[0]; | ||
}; | ||
this.player.currentSrc = function () { | ||
return sources[0].src; | ||
}; | ||
this.player.playlistMenu.items[1].trigger('tap'); | ||
// trigger a loadstart synchronously to simplify the test | ||
this.player.trigger('loadstart'); | ||
var selectedItems = this.fixture.querySelectorAll('.vjs-playlist-item.vjs-selected'); | ||
assert.ok(this.player.playlistMenu.items[1].hasClass('vjs-selected'), 'selected the new item'); | ||
assert.ok(!this.player.playlistMenu.items[0].hasClass('vjs-selected'), 'deselected the old item'); | ||
assert.strictEqual(playCalled, true, 'play gets called if option is set'); | ||
}); | ||
assert.strictEqual(this.fixture.querySelectorAll('.vjs-playlist-item').length, playlist.length, 'displayed the correct number of items'); | ||
assert.strictEqual(selectedItems.length, 1, 'marked one playlist item'); | ||
assert.strictEqual(selectedItems[0].querySelector('img').src, resolveUrl(playlist[1].thumbnail), 'marked the second playlist item'); | ||
}); | ||
QUnit.test('play should not get called by default upon selection of menu items ', function (assert) { | ||
var playCalled = false; | ||
QUnit.test('selects no item if the playlist is not in use', function (assert) { | ||
this.player.playlist(playlist); | ||
this.player.playlist.currentItem = function () { | ||
return -1; | ||
}; | ||
this.player.playlistUi(); | ||
this.player.playlist(playlist); | ||
this.player.playlistUi(); | ||
this.player.play = function () { | ||
playCalled = true; | ||
}; | ||
this.player.trigger('loadstart'); | ||
var sources = void 0; | ||
assert.strictEqual(this.fixture.querySelectorAll('.vjs-playlist-item.vjs-selected').length, 0, 'no items selected'); | ||
}); | ||
this.player.src = function (src) { | ||
if (src) { | ||
sources = src; | ||
} | ||
return sources[0]; | ||
}; | ||
this.player.currentSrc = function () { | ||
return sources[0].src; | ||
}; | ||
this.player.playlistMenu.items[1].trigger('tap'); | ||
// trigger a loadstart synchronously to simplify the test | ||
this.player.trigger('loadstart'); | ||
assert.strictEqual(playCalled, false, 'play should not get called by default'); | ||
}); | ||
QUnit.test('updates on "playlistchange", different lengths', function (assert) { | ||
this.player.playlist([]); | ||
this.player.playlistUi(); | ||
var items = this.fixture.querySelectorAll('.vjs-playlist-item'); | ||
assert.strictEqual(items.length, 0, 'no items initially'); | ||
this.player.playlist(playlist); | ||
this.player.trigger('playlistchange'); | ||
items = this.fixture.querySelectorAll('.vjs-playlist-item'); | ||
assert.strictEqual(items.length, playlist.length, 'updated with the new items'); | ||
}); | ||
QUnit.test('updates on "playlistchange", equal lengths', function (assert) { | ||
this.player.playlist([{ sources: [] }, { sources: [] }]); | ||
this.player.playlistUi(); | ||
var items = this.fixture.querySelectorAll('.vjs-playlist-item'); | ||
assert.strictEqual(items.length, 2, 'two items initially'); | ||
this.player.playlist(playlist); | ||
this.player.trigger('playlistchange'); | ||
items = this.fixture.querySelectorAll('.vjs-playlist-item'); | ||
assert.strictEqual(items.length, playlist.length, 'updated with the new items'); | ||
assert.strictEqual(this.player.playlistMenu.items[0].item, playlist[0], 'we have updated items'); | ||
assert.strictEqual(this.player.playlistMenu.items[1].item, playlist[1], 'we have updated items'); | ||
}); | ||
QUnit.test('updates on "playlistchange", update selection', function (assert) { | ||
this.player.playlist(playlist); | ||
this.player.currentSrc = function () { | ||
return playlist[0].sources[0].src; | ||
}; | ||
this.player.playlistUi(); | ||
var items = this.fixture.querySelectorAll('.vjs-playlist-item'); | ||
assert.strictEqual(items.length, 2, 'two items initially'); | ||
assert.ok(/vjs-selected/.test(items[0].getAttribute('class')), 'first item is selected by default'); | ||
this.player.playlist.currentItem(1); | ||
this.player.currentSrc = function () { | ||
return playlist[1].sources[0].src; | ||
}; | ||
this.player.trigger('playlistchange'); | ||
items = this.fixture.querySelectorAll('.vjs-playlist-item'); | ||
assert.strictEqual(items.length, playlist.length, 'updated with the new items'); | ||
assert.ok(/vjs-selected/.test(items[1].getAttribute('class')), 'second item is selected after update'); | ||
assert.ok(!/vjs-selected/.test(items[0].getAttribute('class')), 'first item is not selected after update'); | ||
}); | ||
QUnit.test('updates on "playlistsorted", different lengths', function (assert) { | ||
this.player.playlist([]); | ||
this.player.playlistUi(); | ||
var items = this.fixture.querySelectorAll('.vjs-playlist-item'); | ||
assert.strictEqual(items.length, 0, 'no items initially'); | ||
this.player.playlist(playlist); | ||
this.player.trigger('playlistsorted'); | ||
items = this.fixture.querySelectorAll('.vjs-playlist-item'); | ||
assert.strictEqual(items.length, playlist.length, 'updated with the new items'); | ||
}); | ||
QUnit.test('updates on "playlistsorted", equal lengths', function (assert) { | ||
this.player.playlist([{ sources: [] }, { sources: [] }]); | ||
this.player.playlistUi(); | ||
var items = this.fixture.querySelectorAll('.vjs-playlist-item'); | ||
assert.strictEqual(items.length, 2, 'two items initially'); | ||
this.player.playlist(playlist); | ||
this.player.trigger('playlistsorted'); | ||
items = this.fixture.querySelectorAll('.vjs-playlist-item'); | ||
assert.strictEqual(items.length, playlist.length, 'updated with the new items'); | ||
assert.strictEqual(this.player.playlistMenu.items[0].item, playlist[0], 'we have updated items'); | ||
assert.strictEqual(this.player.playlistMenu.items[1].item, playlist[1], 'we have updated items'); | ||
}); | ||
QUnit.test('updates on "playlistsorted", update selection', function (assert) { | ||
this.player.playlist(playlist); | ||
this.player.currentSrc = function () { | ||
return playlist[0].sources[0].src; | ||
}; | ||
this.player.playlistUi(); | ||
var items = this.fixture.querySelectorAll('.vjs-playlist-item'); | ||
assert.strictEqual(items.length, 2, 'two items initially'); | ||
assert.ok(/vjs-selected/.test(items[0].getAttribute('class')), 'first item is selected by default'); | ||
this.player.playlist.currentItem(1); | ||
this.player.currentSrc = function () { | ||
return playlist[1].sources[0].src; | ||
}; | ||
this.player.trigger('playlistsorted'); | ||
items = this.fixture.querySelectorAll('.vjs-playlist-item'); | ||
assert.strictEqual(items.length, playlist.length, 'updated with the new items'); | ||
assert.ok(/vjs-selected/.test(items[1].getAttribute('class')), 'second item is selected after update'); | ||
assert.ok(!/vjs-selected/.test(items[0].getAttribute('class')), 'first item is not selected after update'); | ||
}); | ||
QUnit.test('tracks when an ad is playing', function (assert) { | ||
this.player.playlist([]); | ||
this.player.playlistUi(); | ||
this.player.duration = function () { | ||
return 5; | ||
}; | ||
var playlistMenu = this.player.playlistMenu; | ||
assert.ok(!playlistMenu.hasClass('vjs-ad-playing'), 'does not have class vjs-ad-playing'); | ||
this.player.trigger('adstart'); | ||
assert.ok(playlistMenu.hasClass('vjs-ad-playing'), 'has class vjs-ad-playing'); | ||
this.player.trigger('adend'); | ||
assert.ok(!playlistMenu.hasClass('vjs-ad-playing'), 'does not have class vjs-ad-playing'); | ||
}); | ||
// ----------- | ||
// Interaction | ||
// ----------- | ||
QUnit.test('changes the selection when tapped', function (assert) { | ||
var playCalled = false; | ||
this.player.playlist(playlist); | ||
this.player.playlistUi({ playOnSelect: true }); | ||
this.player.play = function () { | ||
playCalled = true; | ||
}; | ||
var sources = void 0; | ||
this.player.src = function (src) { | ||
if (src) { | ||
sources = src; | ||
} | ||
return sources[0]; | ||
}; | ||
this.player.currentSrc = function () { | ||
return sources[0].src; | ||
}; | ||
this.player.playlistMenu.items[1].trigger('tap'); | ||
// trigger a loadstart synchronously to simplify the test | ||
this.player.trigger('loadstart'); | ||
assert.ok(this.player.playlistMenu.items[1].hasClass('vjs-selected'), 'selected the new item'); | ||
assert.ok(!this.player.playlistMenu.items[0].hasClass('vjs-selected'), 'deselected the old item'); | ||
assert.strictEqual(playCalled, true, 'play gets called if option is set'); | ||
}); | ||
QUnit.test('play should not get called by default upon selection of menu items ', function (assert) { | ||
var playCalled = false; | ||
this.player.playlist(playlist); | ||
this.player.playlistUi(); | ||
this.player.play = function () { | ||
playCalled = true; | ||
}; | ||
var sources = void 0; | ||
this.player.src = function (src) { | ||
if (src) { | ||
sources = src; | ||
} | ||
return sources[0]; | ||
}; | ||
this.player.currentSrc = function () { | ||
return sources[0].src; | ||
}; | ||
this.player.playlistMenu.items[1].trigger('tap'); | ||
// trigger a loadstart synchronously to simplify the test | ||
this.player.trigger('loadstart'); | ||
assert.strictEqual(playCalled, false, 'play should not get called by default'); | ||
}); | ||
}(QUnit,videojs)); | ||
}(videojs,QUnit)); |
@@ -73,3 +73,3 @@ import document from 'global/document'; | ||
QUnit.module('videojs-playlist-ui', {setup, teardown}); | ||
QUnit.module('videojs-playlist-ui', {beforeEach: setup, afterEach: teardown}); | ||
@@ -190,3 +190,3 @@ QUnit.test('registers itself', function(assert) { | ||
QUnit.module('videojs-playlist-ui: Components', {setup, teardown}); | ||
QUnit.module('videojs-playlist-ui: Components', {beforeEach: setup, afterEach: teardown}); | ||
@@ -193,0 +193,0 @@ // -------------------- |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
111
292481
43
26
3803
1
+ Added@babel/runtime@7.26.0(transitive)
+ Added@videojs/http-streaming@2.16.3(transitive)
+ Added@videojs/vhs-utils@3.0.5(transitive)
+ Added@videojs/xhr@2.6.0(transitive)
+ Added@xmldom/xmldom@0.8.10(transitive)
+ Addedaes-decrypter@3.1.3(transitive)
+ Addedkeycode@2.2.1(transitive)
+ Addedm3u8-parser@4.8.0(transitive)
+ Addedmpd-parser@0.22.1(transitive)
+ Addedmux.js@6.0.1(transitive)
+ Addedpkcs7@1.0.4(transitive)
+ Addedregenerator-runtime@0.14.1(transitive)
+ Addedurl-toolkit@2.2.5(transitive)
+ Addedvideo.js@7.21.6(transitive)
+ Addedvideojs-font@3.2.0(transitive)
+ Addedvideojs-vtt.js@0.15.5(transitive)
- Removedbabel-runtime@6.26.0(transitive)
- Removedcore-js@2.6.12(transitive)
- Removedes5-shim@4.6.7(transitive)
- Removedglobal@4.3.04.3.2(transitive)
- Removedparse-headers@2.0.5(transitive)
- Removedprocess@0.5.2(transitive)
- Removedregenerator-runtime@0.11.1(transitive)
- Removedtsml@1.0.1(transitive)
- Removedvideo.js@5.20.5(transitive)
- Removedvideojs-font@2.0.0(transitive)
- Removedvideojs-ie8@1.1.2(transitive)
- Removedvideojs-swf@5.4.1(transitive)
- Removedvideojs-vtt.js@0.12.6(transitive)
- Removedxhr@2.2.2(transitive)
- Removedxtend@4.0.2(transitive)
Updatedvideo.js@^6 || ^7