UNPKG

videojs-playlist-ui

Version:
500 lines (374 loc) 14 kB
'use strict'; function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } var videojs = _interopDefault(require('video.js')); var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; 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 document_1 = doccy; var 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 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++) { if (array[i] === target) { return i; } } return -1; }; // see https://github.com/Modernizr/Modernizr/blob/master/feature-detects/css/pointerevents.js var supportsCssPointerEvents = function () { var element = document_1.createElement('x'); element.style.cssText = 'pointer-events:auto'; return element.style.pointerEvents === 'auto'; }(); var defaults$$1 = { className: 'vjs-playlist', playOnSelect: false, supportsCssPointerEvents: supportsCssPointerEvents }; // 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 (el.thumbnail) { dom.removeClass(el.thumbnail, 'vjs-playlist-now-playing'); } }; 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_1.createElement('div'); placeholder.className = 'vjs-playlist-thumbnail vjs-playlist-thumbnail-placeholder'; return placeholder; } var picture = document_1.createElement('picture'); 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); } // 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_); return _this; } 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); // Up next var upNextEl = document_1.createElement('span'); var upNextText = this.localize('Up Next'); upNextEl.className = 'vjs-up-next-text'; upNextEl.appendChild(document_1.createTextNode(upNextText)); upNextEl.setAttribute('title', upNextText); titleContainerEl.appendChild(upNextEl); // Video title var titleEl = document_1.createElement('cite'); var titleText = item.name || this.localize('Untitled Video'); titleEl.className = 'vjs-playlist-name'; titleEl.appendChild(document_1.createTextNode(titleText)); titleEl.setAttribute('title', titleText); titleContainerEl.appendChild(titleEl); return li; }; return PlaylistMenuItem; }(Component); var PlaylistMenu = function (_Component2) { inherits(PlaylistMenu, _Component2); function PlaylistMenu(player, settings) { classCallCheck(this, PlaylistMenu); if (!player.playlist) { throw new Error('videojs-playlist is required for the playlist component'); } var _this2 = possibleConstructorReturn(this, _Component2.call(this, player, settings)); _this2.items = []; // 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 (settings.supportsCssPointerEvents) { _this2.addClass('vjs-csspointerevents'); } _this2.createPlaylist_(); if (!videojs.browser.TOUCH_ENABLED) { _this2.addClass('vjs-mouse'); } player.on(['loadstart', 'playlistchange'], 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 () { if (player.ended()) { // player.ended() is true because the content is done, but the ended event doesn't // trigger until after the postroll is done and the ad implementation has finished // its cycle. We don't consider a postroll ad ended until the "ended" event. player.one('ended', function () { _this2.removeClass('vjs-ad-playing'); }); } else { _this2.removeClass('vjs-ad-playing'); } }); return _this2; } PlaylistMenu.prototype.createEl = function createEl() { var settings = this.options_; if (settings.el) { return settings.el; } var ol = document_1.createElement('ol'); ol.className = settings.className; settings.el = ol; return ol; }; 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'); } } }; PlaylistMenu.prototype.update = function update() { // replace the playlist items being displayed, if necessary var playlist = this.player_.playlist(); 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; } } // 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 PlaylistMenu; }(Component); /** * Initialize the plugin. * @param options (optional) {object} configuration for the plugin */ var playlistUi = function playlistUi(options) { var player = this; var settings = void 0; var elem = void 0; if (!player.playlist) { throw new Error('videojs-playlist is required for the playlist component'); } // if the first argument is a DOM element, use it to build the component if (typeof window_1.HTMLElement !== 'undefined' && options instanceof window_1.HTMLElement || // IE8 does not define HTMLElement so use a hackier type check options && options.nodeType === 1) { elem = options; settings = videojs.mergeOptions(defaults$$1); } else { // lookup the elements to use by class name settings = videojs.mergeOptions(defaults$$1, options); elem = document_1.querySelector('.' + settings.className); } // build the playlist menu settings.el = elem; player.playlistMenu = new PlaylistMenu(player, settings); }; // register components videojs.registerComponent('PlaylistMenu', PlaylistMenu); videojs.registerComponent('PlaylistMenuItem', PlaylistMenuItem); // register the plugin registerPlugin('playlistUi', playlistUi);