UNPKG

le-player

Version:

The best HTML5 video player made for Lectoriy.

1,977 lines (1,656 loc) 694 kB
/******/ (function(modules) { // webpackBootstrap /******/ // The module cache /******/ var installedModules = {}; /******/ /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ /******/ // Check if module is in cache /******/ if(installedModules[moduleId]) /******/ return installedModules[moduleId].exports; /******/ /******/ // Create a new module (and put it into the cache) /******/ var module = installedModules[moduleId] = { /******/ exports: {}, /******/ id: moduleId, /******/ loaded: false /******/ }; /******/ /******/ // Execute the module function /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); /******/ /******/ // Flag the module as loaded /******/ module.loaded = true; /******/ /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ /******/ /******/ // expose the modules object (__webpack_modules__) /******/ __webpack_require__.m = modules; /******/ /******/ // expose the module cache /******/ __webpack_require__.c = installedModules; /******/ /******/ // __webpack_public_path__ /******/ __webpack_require__.p = "/dist/js/"; /******/ /******/ // Load entry module and return exports /******/ return __webpack_require__(0); /******/ }) /************************************************************************/ /******/ ([ /* 0 */ /***/ (function(module, exports, __webpack_require__) { module.exports = __webpack_require__(1); /***/ }), /* 1 */ /***/ (function(module, exports, __webpack_require__) { 'use strict'; 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; }; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var _jquery = __webpack_require__(2); var _jquery2 = _interopRequireDefault(_jquery); var _Control = __webpack_require__(3); var _Control2 = _interopRequireDefault(_Control); var _Component2 = __webpack_require__(4); var _Component3 = _interopRequireDefault(_Component2); var _PlayButton = __webpack_require__(6); var _PlayButton2 = _interopRequireDefault(_PlayButton); var _SplashIcon = __webpack_require__(9); var _SplashIcon2 = _interopRequireDefault(_SplashIcon); var _Icon = __webpack_require__(5); var _Icon2 = _interopRequireDefault(_Icon); var _Time = __webpack_require__(10); var _Time2 = _interopRequireDefault(_Time); var _ControlCollection = __webpack_require__(11); var _ControlCollection2 = _interopRequireDefault(_ControlCollection); var _Sections = __webpack_require__(12); var _Sections2 = _interopRequireDefault(_Sections); var _ErrorDisplay = __webpack_require__(13); var _ErrorDisplay2 = _interopRequireDefault(_ErrorDisplay); var _Poster = __webpack_require__(14); var _Poster2 = _interopRequireDefault(_Poster); var _FullscreenApi = __webpack_require__(15); var _FullscreenApi2 = _interopRequireDefault(_FullscreenApi); var _utils = __webpack_require__(7); var _browser = __webpack_require__(8); var _cookie = __webpack_require__(16); var _cookie2 = _interopRequireDefault(_cookie); var _MediaError = __webpack_require__(17); var _MediaError2 = _interopRequireDefault(_MediaError); __webpack_require__(18); __webpack_require__(19); __webpack_require__(21); __webpack_require__(25); __webpack_require__(27); __webpack_require__(28); __webpack_require__(29); __webpack_require__(30); __webpack_require__(32); __webpack_require__(33); __webpack_require__(34); __webpack_require__(35); __webpack_require__(36); __webpack_require__(38); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _possibleConstructorReturn(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; } function _inherits(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; } // Register common controls _Control2.default.registerControl('divider', function () { return { element: (0, _jquery2.default)('<div/>').addClass('divider') }; }); /** * Return array with excluded dist's items from source array * * @access private * @param {Array} source * @param {Array} dist * @return {Array} */ function excludeArray(source, dist) { var result = [].concat(source); dist.forEach(function (item) { var index = result.indexOf(item); if (index > -1) { result.splice(index, 1); return; } }); return result; } var defaultOptions = { entity: 'Html5', autoplay: false, height: 'auto', loop: false, muted: false, preload: 'metadata', poster: null, svgPath: '', innactivityTimeout: 5000, rate: { step: 0.25, min: 0.5, max: 4.0, default: 1 }, playback: { step: { short: 5, medium: 10, long: 30 } }, controls: { common: [['play', 'volume', 'divider', 'timeline', 'divider', 'section', 'divider', 'fullscreen'], ['rate', 'divider', 'backward', 'divider', 'source', 'divider', 'subtitle', 'divider', 'download', 'divider', 'keybinding info']], fullscreen: [['play', 'volume', 'divider', 'timeline', 'divider', 'rate', 'divider', 'keybinding info', 'divider', 'backward', 'divider', 'source', 'divider', 'subtitle', 'divider', 'download', 'divider', 'section', 'divider', 'fullscreen']], mini: [['play', 'volume', 'divider', 'fullscreen', 'divider', 'timeinfo']], 'common:android': [['play', 'timeline', 'fullscreen'], ['rate', 'source', 'section']], 'fullscreen:mobile': [['play', 'timeline', 'fullscreen'], ['rate', 'source', 'section']], 'common:ios': [['play', 'rate', 'timeline', 'source']] }, controlsOptions: { common: { align: ['justify', 'left'] // mobile : true }, fullscreen: { align: 'justify' }, 'common:android': { align: ['justify', 'right'] }, 'fullscreen:mobile': { align: ['justify', 'right'] } }, volume: { default: 0.4, mutelimit: 0.05, step: 0.1 }, keyBinding: [{ key: 32, info: ['Space'], description: 'Начать проигрывание / поставить на паузу', fn: function fn(player) { player.video.togglePlay(); } }, { key: 37, info: ['←'], description: '\u041F\u0435\u0440\u0435\u043C\u043E\u0442\u0430\u0442\u044C \u043D\u0430 10 \u0441\u0435\u043A\u0443\u043D\u0434 \u043D\u0430\u0437\u0430\u0434', fn: function fn(player) { player.video.currentTime -= player.options.playback.step.medium; player.splashIcon.show('undo'); } }, { key: 39, info: ['→'], description: '\u041F\u0435\u0440\u0435\u043C\u043E\u0442\u0430\u0442\u044C \u043D\u0430 10 \u0441\u0435\u043A\u0443\u043D\u0434 \u0432\u043F\u0435\u0440\u0451\u0434', fn: function fn(player) { player.video.currentTime += player.options.playback.step.medium; player.splashIcon.show('redo'); } }, { shiftKey: true, info: ['Shift', '←'], description: 'Перейти на предыдущую секцию', key: 37, fn: function fn(player) { if (player.sections == null) { return; } player.sections.prev(); } }, { shiftKey: true, key: 39, info: ['Shift', '→'], description: 'Перейти на следующую секцию', fn: function fn(player) { if (player.sections == null) { return; } player.sections.next(); } }, { key: 38, info: ['↑'], description: 'Увеличить громкость', fn: function fn(player) { player.video.volume += player.options.volume.step; player.splashIcon.show(player.calcVolumeIcon(player.video.volume)); } }, { key: 40, info: ['↓'], description: 'Уменьшить громкость', fn: function fn(player) { player.video.volume -= player.options.volume.step; player.splashIcon.show(player.calcVolumeIcon(player.video.volume)); } }, { key: 70, info: ['f'], description: 'Открыть/закрыть полноэкраный режим', fn: function fn(player) { player.toggleFullscreen(); } }], plugins: { miniplayer: {} }, onPlayerInited: _utils.noop }; /** * @class Player * @extends Component * @param {jQuery} element Element when player will init * @param {Object} [options] * @param {Boolean} [options.autoplay=false] * When present, the video will automatically start playing as soon as it can do so without stopping. * @param {String|Number} [options.height='auto'] Height of video container * @param {String} [options.width] Width of video container * @param {Boolean} [options.loop=false] * When present, it specifies that the video will start over again, every time it is finished. * @param {Boolean} [options.muted=false] * When present, it specifies that the audio output of the video should be muted. * @param {String} [options.preload='metadata'] Can be ('auto'|'metadata'|'none') * @param {String} [options.poster] Path to poster of video * @param {String} [options.svgPath] Path to svg sprite for icons * @param {Object} [options.rate] Rate options * @param {Number} [options.rate.step=0.25] Step of increase/decrease by rate control * @param {Number} [options.rate.min=0.5] Min of rate * @param {Number} [options.rate.max=4.0] Max of rate * @param {Number} [options.rate.default=1] * @param {Object} [options.playback] Playback options * @param {Object} [options.playback.step] * @param {Nubmer} [options.playback.step.short=5] * @param {Nubmer} [options.playback.step.medium=30] * @param {Nubmer} [options.playback.step.long=60] * @param {Obejct} [options.controls] Object of controls * @param {String[]} [options.controls.common] Array of controls for default view * @param {String[]} [options.controls.fullscreen] Array of control for fullsreen view * @param {String[]} [options.controls.mini] Array of control for miniplayer * @param {Object} [options.excludeControls] Object of exclude controls. Structure is the same as that of options.controls * @param {Object} [options.volume] Volume's options * @param {Number} [options.volume.default=0.4] Default volume * @param {Number} [options.volume.mutelimit=0.05] Delta when volume is muted * @param {Number} [options.volume.step=0.05] * @param {Object[]} [options.keybinding] * Object with keybinding options, when key it's name of key binding, and value it's key binding settings * @param {Number} [options.keybinding[].key] Code of key binding (for example 32 it's space) * @param {String[]} [options.keybinding[].info] Array of keystrokes order * @param {String} options.keybinding[].description] Description of key binding * @param {Function} options.keybinding[].fn] Callback * @param {Object|Boolean} [options.miniplayer=false] * @param {String} [options.miniplayer.width] Width of miniplayer container * @param {String} [options.miniplayer.width] MiniPlayer's width * @param {String} [options.sectionContainer] Selector for sections * @param {Object} [options.plugins] Keys of objects are name of plugin, value - plugin options * @param {String|Object} [options.data] Url or JSON with data for player * @param {Array} [options.data.sections] Sections array */ var Player = function (_Component) { _inherits(Player, _Component); function Player(element, options) { _classCallCheck(this, Player); options.createElement = false; var _this = _possibleConstructorReturn(this, (Player.__proto__ || Object.getPrototypeOf(Player)).call(this, null, options)); _this._element = element; /** * DOM container to hold inner of player * * @memberof! Player# * @type {jQuery} */ _this.innerElement = (0, _utils.createEl)('div'); // Users options _this._userOptions = options; _this._initOptions(); if (_this.options.svgPath === '') { Player._loadSVGSprite(Player.defaultSprite); } _this._view = 'common'; /** * DOM container to hold all player * * @memberof! Player# * @type {jQuery} */ _this.element = _this.createElement(); _this.loadEntity(_this.options.entity, { ctx: element }); /** * Video html5 component * * @memberof! Player# * @type {Entity} */ _this.video = _this.entity; // Create controls // TODO: move this action to the createElement _this.controls = {}; _this._initControls(); /** * @access private */ _this._dblclickTimeout = null; _this._initSections().then(function (data) { /** * Sections init event * * @event Player#sectionsinit * @example * const player = new Player($('#video'), options); * player.on('sectionsinit', (e, data) => cosnole.log(data)); * */ _this.trigger('sectionsinit', data); }); _this._initPlugins(); _this._listenHotKeys(); _this._userActivity = false; _this._listenUserActivity(); _this._waitingTimeouts = []; /* Retrigger {@link Entity} Events */ [ /** * durationchange player event * * @event Player#durationchange */ 'durationchange', /** * progress html5 media event * * @event Player#progress */ 'progress', /** * dblclick * * @event Player#dbclick */ 'dblclick', /** * dblclick * * @event Player#dbclick */ 'click', /** * canplay html5 media event * * @event Player#canplay */ 'canplay', /** * qualitychange html5 * * @event Player#qualitychange */ 'qualitychange', /** * qualitychange html5 * * @event Player#trackschange */ 'trackschange'].forEach(function (eventName) { _this.video.on(eventName, function () { _this.trigger(eventName); }); }); _this.video.one('play', function () { /** * First play event * * @event Player#firstplay */ _this.trigger('firstplay'); _this.removeClass('leplayer--virgin'); }); _this.video.on('timeupdate', function () { if (_this.video.currentTime > 0) { _this.removeClass('leplayer--virgin'); } /** * timeupdate html5 media event * * @event Player#timeupdate */ _this.trigger('timeupdate', { time: _this.video.currentTime, duration: _this.video.duration }); }); _this.video.on('loadstart', function () { _this.removeClass('leplayer--ended'); _this.error = null; /** * loadstart player event * * @event Player#loadstart */ _this.trigger('loadstart'); }); _this.video.on('seeking', function () { _this._startWaiting(); /** * seeking html5 media event * * @event Player#seeking */ _this.trigger('seeking'); }); _this.video.on('seeked', function () { _this._stopWayting(); /** * seeked html5 media event * * @event Player#seeked */ _this.trigger('seeked'); }); _this.video.on('volumechange', function () { /** * volumechange html5 media event * * @event Player#volumechange */ _this.trigger('volumechange', { volume: _this.video.volume }); }); _this.video.on('posterchange', function (e, data) { var url = data.url; _this.poster.url = url; _this.trigger('posterchange'); }); _this.video.on('play', function (e) { _this.removeClass('leplayer--ended'); _this.removeClass('leplayer--paused'); _this.addClass('leplayer--playing'); /** * play html5 media event * * @event Player#play */ _this.trigger('play'); }); _this.video.on('pause', function () { _this.removeClass('leplayer--playing'); _this.addClass('leplayer--paused'); /** * pause html5 media event * * @event Player#pause */ _this.trigger('pause'); }); _this.video.on('playing', function () { _this._stopWayting(); /** * playing html5 media event * * @event Player#playing */ _this.trigger('playing'); }); _this.video.on('ratechange', function () { /** * rate html5 media event * * @event Player#rate */ _this.trigger('ratechange', { rate: _this.video.rate }); }); _this.video.on('ended', function () { _this.addClass('leplayer--ended'); if (_this.options.loop) { _this.currentTime = 0; _this.video.play(); } else if (!_this.video.paused) { _this.video.pause(); } /** * ended html5 media event * * @event Player#ended */ _this.trigger('ended'); }); _this.video.on('canplaythrough', function () { _this._stopWayting(); /** * canplaythrough html5 media event * * @event Player#canplaythrough */ _this.trigger('canplaythrough'); }); _this.video.on('waiting', function () { _this._startWaiting(); _this.video.one('timeupdate', function () { return _this._stopWayting(); }); /** * waiting html5 media event * * @event Player#waiting */ _this.trigger('waiting'); }); _this.video.on('error', function (e, data) { _this.error = new _MediaError2.default(data.code); }); _this.video.init().then(function () { /** * Player init event * * @event Player#inited */ _this.trigger('inited'); if (_this.options.time) { _this.currentTime = _this.options.time; } if (_this.video.src != null && _this.options.autoplay) { _this.play(); } }); _this.on('fullscreenchange', _this._onFullscreenChange.bind(_this)); _this.on('click', _this._onClick.bind(_this)); _this.on('dblclick', _this._onDbclick.bind(_this)); _this.on('inited', _this._onInited.bind(_this)); _this.on('play', _this._onPlay.bind(_this)); _this.on('pause', _this._onPause.bind(_this)); (0, _jquery2.default)(document).on(_FullscreenApi2.default.fullscreenchange, _this._onEntityFullscrenChange.bind(_this)); return _this; } _createClass(Player, [{ key: 'loadEntity', value: function loadEntity(name, options) { var Entity = Player.getComponent(name); this._entity = new Entity(this, options); } /** * Starts playing the video * * * @access public * @example * const player = new Player($("#video"),options); * $('.some-button').on('click', () => player.play()); */ }, { key: 'play', value: function play() { return this.video.play(); } /** * Pauses the currently playing video * * @access public */ }, { key: 'pause', value: function pause() { return this.video.pause(); } /** * Toggle the currently playing video * * @access public */ }, { key: 'togglePlay', value: function togglePlay() { return this.video.togglePlay(); } /** * Begin loading the src data * * @access public */ }, { key: 'load', value: function load() { return this.video.load(); } /** * On set view callback * * @access public * @param {String} view View name * @returns {Player} this * @example * const player = new Player($('#video'), options); * player.onSetView('mini', () => console.log('Miniplayer yeah!') * .onSetView('fullscreen', () => console.log('Fullscreen boom!') * .onSetView('common', () => console.log('Common view - lol'); */ }, { key: 'onSetView', value: function onSetView(view) { for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { args[_key - 1] = arguments[_key]; } this.on.apply(this, ['setview.' + view].concat(args)); return this; } /** * Change source and save time, rate * * @access public * @param {Object} quality * @param {String} [quality.title] The name of qualitut e.x SD or HD * @param {String} quality.url */ }, { key: 'changeQuality', value: function changeQuality(quality) { var video = this.video; if (quality == null) return; var time = this.currentTime; var rate = this.rate; var isPaused = this.paused; video.src = quality; this.playbackRate = rate; this.currentTime = time; if (isPaused) { this.pause(); } else { this.play(); } } /** * On del view callback * * @access public * @param {String} view View name * @returns {Player} this * @example * const player = new Player($('#video'), options); * player.onDelView('mini', () => console.log('Exit miniplayer') */ }, { key: 'onDelView', value: function onDelView(view) { for (var _len2 = arguments.length, args = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) { args[_key2 - 1] = arguments[_key2]; } this.on.apply(this, ['delview.' + view].concat(args)); return this; } /** * Get some data for player * * @access public * @returns {jQuery.promise} Promise */ }, { key: 'getData', value: function getData() { var dfd = new _jquery2.default.Deferred(); if (this._data !== undefined) { dfd.resolve(this._data); return dfd.promise(); } if (typeof this.options.data === 'string') { return _jquery2.default.ajax({ url: this.options.data, method: 'GET', dataType: 'json' }).promise(); } else if (_typeof(this.options.data) === 'object') { dfd.resolve(this.options.data); return dfd.promise(); } } }, { key: 'getSectionData', value: function getSectionData() { return this.getData().then(function (data) { return data.sections; }); } /** * Request fullscreen * * @access public * @fires Player#fullscreenchange */ }, { key: 'requestFullscreen', value: function requestFullscreen() { var fsApi = _FullscreenApi2.default; if (fsApi.requestFullscreen) { // Call HTML5 Video api requestFullscreen this.element[0][fsApi.requestFullscreen](); /** * fullscreenchange html5 media event * * @event Player#fullscreenchange */ this.trigger('fullscreenchange', true); } else if (this.video.supportsFullScreen()) { this.video.enterFullscreen(); } } /** * Exit fullscreen * * @access public * @fires Player#fullscreenchange */ }, { key: 'exitFullscreen', value: function exitFullscreen() { var fsApi = _FullscreenApi2.default; if (fsApi.exitFullscreen) { document[fsApi.exitFullscreen](); } else if (this.video.supportsFullScreen()) { this.video.exitFullscreen(); } this.trigger('fullscreenchange', false); } /** * Toggle fullscreen * * @access public * @fires Player#fullscreenchange */ }, { key: 'toggleFullscreen', value: function toggleFullscreen() { if (this.view === 'fullscreen') { this.exitFullscreen(); } else { this.requestFullscreen(); } } /** * Get ControlCollection of Player by name (e.x 'common', 'fullscreen') * * @access public * @param {String} name - Name of ControlCollection * @returns {ControlCollection} */ }, { key: 'getControls', value: function getControls(name) { return this.controls[name]; } /** * Return the width of player. * * @access public * @returns {Number} Width in px */ }, { key: 'getWidth', value: function getWidth() { return this.element.width(); } /** * Complete the sections, by the additional field 'end' in each section * * @access private * @param {Object} sections - Sections * @returns {Object} New sections */ }, { key: '_completeSections', value: function _completeSections(sections) { if (sections == null || sections.length === 0) { return; } var newSections = [].concat(sections); for (var i = 0; i < newSections.length; i++) { var endSection = void 0; if (i < newSections.length - 1) { endSection = newSections[i + 1].begin; } else { endSection = this.video.duration; } newSections[i].end = endSection; } return newSections; } /** * Get and set the current playback position in the audio/video (in seconds) * Getter and setter * * @access public * @memberof! Player# * @type {Nubmer} */ }, { key: 'createElement', /** * Remove unnecessary attributes, and set some attrs from options (loop, poster etc...). Create main DOM objects * * @override */ value: function createElement() { var options = this.options; var element = this._element; this.element = (0, _utils.createEl)('div'); this.element = this.element.addClass('leplayer').attr('tabindex', 0).css('width', options.width && '100%').css('max-width', options.width); /** * Error display component. * * @type {ErrorDisplay} * @memberof! Player# */ this.errorDisplay = new _ErrorDisplay2.default(this); /** * Play button component. * * @type {PlayButton} * @memberof! Player# */ this.playButton = new _PlayButton2.default(this); // TODO: Вынести это в отдельнеый компонент this.loader = (0, _jquery2.default)('<div />').addClass('leplayer-loader-container').append(new _Icon2.default(this, { iconName: 'refresh', className: 'leplayer-loader-container__icon' }).element); /** * Splash icon component. * * @type {SplashIcon} * @memberof! Player# */ this.splashIcon = new _SplashIcon2.default(this); this.videoContainer = (0, _utils.createEl)('div', { className: 'leplayer-video' }).append(this.errorDisplay.element).append(this.playButton.element).append(this.loader).append(this.splashIcon.element); this.poster = new _Poster2.default(this); this.videoContainer.append(this.poster.element); var lastTimer = new _Time2.default(this, { fn: function fn(player) { var video = player.video; return (0, _utils.secondsToTime)(video.duration - video.currentTime); } }); if (this.options.videoInfo) { console.warn('options.videoInfo is deprecated, please use istead options.description'); } this.infoElement = (0, _utils.createEl)('div', { className: 'leplayer__info' }).append((0, _utils.createEl)('div', { className: 'leplayer__title', html: this.options.title || "" })).append((0, _utils.createEl)('div', { className: 'leplayer__video-info', html: this.options.description || this.options.videoInfo || "" })).append((0, _utils.createEl)('div', { className: 'leplayer__last', html: '\u0412\u0438\u0434\u0435\u043E \u0437\u0430\u043A\u043E\u043D\u0447\u0438\u0442\u0441\u044F \u0447\u0435\u0440\u0435\u0437 ' }).append(lastTimer.element)); this.innerElement = (0, _jquery2.default)('<div />').addClass('leplayer__inner').append(this.videoContainer).append(this.infoElement); this.element = this.element.append(this.innerElement); this.addClass('leplayer--paused'); this.addClass('leplayer--virgin'); if (_browser.IS_IPHONE) { this.addClass('leplayer--iphone'); } if (_browser.IS_ANDROID) { this.addClass('leplayer--android'); } if (_browser.IS_MOBILE) { this.addClass('leplayer--mobile'); } if (options.sectionContainer) { this.sectionsContainer = (0, _jquery2.default)(options.sectionContainer); } element.before(this.element); this.videoContainer.append(element); return this.element; } /** * Get options from video's attribute ( height, width, poster, preload etc...) * Get source video from src attr or <source> element with data attr 'data-quality' * Also get sources for different quality from <source> element with data attr 'data-quality' * * @access private * @returns {Object} options */ }, { key: '_optionsFromElement', value: function _optionsFromElement() { // Copy video attrs to the opitons var element = this._element; if (element == null || element.length === 0) { return {}; } var attrOptions = ['height', 'width', 'poster', 'autoplay', 'loop', 'muted', 'preload'].reduce(function (obj, item) { var val = element.attr(item); if (val != null) { obj[item] = element.attr(item); } return obj; }, {}); attrOptions.sources = []; // Src it is main source, that will be load if (element.attr('src')) { attrOptions.src = { url: element.attr('src'), title: element.attr('data-quality') || element.attr('title') || 'default' }; } // Copy sources from HTML5 source element with data-quality attr // If data-quality attr does not exist - no element.find('source').each(function (i, item) { item = (0, _jquery2.default)(item); if (!item.attr('data-quality')) { return; } attrOptions.sources = attrOptions.sources.concat({ url: item.attr('src'), title: item.attr('data-quality') || item.attr('title') || 'default' }); }); return attrOptions; } /** * Return a name of icon. If less then 0.1 return volume-off, * if less then 0.5 return volume down, else return volume-up * * @access private * @param {Number} value Volume value * @returns {String} Icon name */ }, { key: 'calcVolumeIcon', value: function calcVolumeIcon(value) { if (value == null) { value = this.video.volume; } var volume = value; if (volume < this.options.volume.mutelimit) { return 'volume-off'; } else if (value < 0.5) { return 'volume-down'; } else { return 'volume-up'; } } }, { key: 'toggleSections', value: function toggleSections(flag) { if (this.sections) { this.sections.visible = flag; } if (this.outsideSections) { this.outsideSections.visible = flag; } } /** * Merge defaultOptions, presetOptions with attrOptions and user's options; * * And complement two objects: controls and excludeControls * * @access private */ }, { key: '_initOptions', value: function _initOptions() { var _this2 = this; var attrOptions = this._optionsFromElement(); var presetOptions = {}; if (this._userOptions.preset && Player.getPreset(this._userOptions.preset)) { presetOptions = Player.getPreset(this._userOptions.preset).options; } // Merge default options + preset options + video attributts+ user options this.options = _jquery2.default.extend(true, {}, defaultOptions, presetOptions, attrOptions, this._userOptions); if (this.options.sources && !Array.isArray(this.options.sources)) { this.options.sources = [this.options.sources]; } if (typeof this.options.src === 'string') { this.options.src = { url: this.options.src }; } if (this.options.src == null && this.options.sources.length > 0) { this.options.src = this.options.sources[0]; } // Generate android:fullscreen, android:common and etc controls options // Merge correctly controls, without deep merge this.options.controls = _jquery2.default.extend({}, defaultOptions.controls, presetOptions.controls, this._userOptions.controls); // exclude controls option // TODO(adinvadim): // Set depreceted flag for this option; var _loop = function _loop(name) { if (!_this2.options.excludeControls.hasOwnProperty(name)) return { v: void 0 }; var controlCollection = _this2.options.excludeControls[name]; controlCollection.forEach(function (row, index) { if (_this2.options.controls[name] && _this2.options.controls[name][index]) { _this2.options.controls[name][index] = excludeArray(_this2.options.controls[name][index], row); } }); }; for (var name in this.options.excludeControls) { var _ret = _loop(name); if ((typeof _ret === 'undefined' ? 'undefined' : _typeof(_ret)) === "object") return _ret.v; } if (this.options.preset && Player.getPreset(this.options.preset)) { Player.getPreset(this.options.preset).initOptions(); } } /** * Create and init all controls * * @access private */ }, { key: '_initControls', value: function _initControls() { var _arr = ['common', 'fullscreen']; for (var _i = 0; _i < _arr.length; _i++) { var name = _arr[_i]; if (!this.options.controls.hasOwnProperty(name)) return; var controlCollection = new _ControlCollection2.default(this, { name: name }); this.element.append(controlCollection.element); } if (this.controls.common != null) { this.controls.common.active = true; } } }, { key: '_listenHotKeys', value: function _listenHotKeys() { var _this3 = this; var isKeyBinding = function isKeyBinding(e, binding) { return (e.which === binding.key || e.key === binding.key) && !!binding.shiftKey === e.shiftKey && !!binding.ctrlKey === e.ctrlKey; }; this.element.on('keydown.leplayer.hotkey', function (e) { _this3.options.keyBinding.forEach(function (binding) { if (isKeyBinding(e, binding)) { e.preventDefault(); binding.fn(_this3); return false; } }); }); } /** * Init sections, get ajax or json with sections data and create Sections object and added them to the DOM * * @access private * @returns {jqPromise} jQuery promise */ }, { key: '_initSections', value: function _initSections() { var _this4 = this; var dfd = _jquery2.default.Deferred(); if (this.options.data == null) { dfd.reject(null); } else { this.getSectionData().done(function (sections) { sections = [].concat(_toConsumableArray(sections)); var isSectionOutside = _this4.sectionsContainer && _this4.sectionsContainer.length > 0; if (sections == null || sections.length === 0) { dfd.reject(null); return; } sections = _this4._completeSections(sections); _this4.sections = new _Sections2.default(_this4, { items: sections, fullscreenOnly: isSectionOutside, hideScroll: true }); _this4.innerElement.append(_this4.sections.element); if (isSectionOutside) { _this4.outsideSections = new _Sections2.default(_this4, { items: sections }); _this4.sectionsContainer.append(_this4.outsideSections.element); } dfd.resolve({ items: sections }); }); } return dfd.promise(); } /** * Function, than init all plugins from player options. * If plugin doesn't exist throw an error * * @access private * @returns {Player} this */ }, { key: '_initPlugins', value: function _initPlugins() { if (this.options.plugins) { for (var name in this.options.plugins) { if (!this.options.plugins.hasOwnProperty(name)) return; var pluginOptions = this.options.plugins[name]; if (this[name]) { if (pluginOptions) { this[name](pluginOptions); } } else { console.error('Plugin \'' + name + '\' doesn\'t exist'); } } } return this; } /** * @access private */ }, { key: '_listenUserActivity', value: function _listenUserActivity() { var _this5 = this; var mouseInProgress = void 0; var lastMoveX = void 0; var lastMoveY = void 0; var onMouseMove = function onMouseMove(e) { if (e.screenX !== lastMoveX || e.screenY !== lastMoveY) { lastMoveX = e.screenX; lastMoveY = e.screenY; _this5._userActivity = true; } }; var onMouseDown = function onMouseDown(e) { _this5._userActivity = true; // While user is pressing mouse or touch, dispatch user activity clearInterval(mouseInProgress); mouseInProgress = setInterval(function () { _this5._userActivity = true; }, 250); }; var onMouseUp = function onMouseUp(e) { _this5._userActivity = true; clearInterval(mouseInProgress); }; this.element.on('mousemove', onMouseMove); this.element.on('mousedown', onMouseDown); this.element.on('mouseup', onMouseUp); this.element.on('keydown', function (e) { return _this5._userActivity = true; }); this.element.on('keyup', function (e) { return _this5._userActivity = true; }); // See http://ejohn.org/blog/learning-from-twitter/ var inactivityTimeout = void 0; var delay = this.options.innactivityTimeout; setInterval(function () { if (_this5._userActivity) { // Reset user activuty tracker _this5._userActivity = false; _this5.userActive = true; clearTimeout(inactivityTimeout); if (delay > 0) { inactivityTimeout = setTimeout(function () { if (!_this5._userActivity) { _this5.userActive = false; } }, delay); } } }, 250); } /** * Stop showing spinner and clear delay of showing spinner * * @access private */ }, { key: '_stopWayting', value: function _stopWayting() { this._waitingTimeouts.forEach(function (item) { return clearTimeout(item); }); this._waitingTimeouts = []; this.removeClass('leplayer--waiting'); } /** * Show spinner with delay in 300ms * * @access private */ }, { key: '_startWaiting', value: function _startWaiting() { var _this6 = this; this._waitingTimeouts.push(setTimeout(function () { _this6.addClass('leplayer--waiting'); }, 300)); } /** * On inited player event handler * * @access private * @param {PlayerEvent} e */ }, { key: '_onInited', value: function _onInited(e) { this.addClass('leplayer--inited'); this.options.onPlayerInited.call(this, e); } /** * On click video event handler. Focus on video and togglePlay * * @access private * @param {PlayerEvent} e */ }, { key: '_onClick', value: function _onClick(e) { var _this7 = this; clearTimeout(this._dblclickTimeout); var togglePlay = function togglePlay() { _this7._dblclickTimeout = setTimeout(function () { _this7.video.element.focus(); _this7.togglePlay(); }, 300); }; /** * See LPLR-290 * On touch devices in fullscreen if user not active we don't should toggle * At first we show him a controls */ if ((0, _browser.IS_TOUCH)() && this.view === 'fullscreen') { if (this.player.userActive) { togglePlay(); } } else { togglePlay(); } } /** * On dblclick on the video player event handler * * @access private * @param {PlayerEvent} e */ }, { key: '_onDbclick', value: function _onDbclick(e) { clearTimeout(this._dblclickTimeout); this.toggleFullscreen(); } /** * On fullscreen change player event handler * * @access private * @param {PlayerEvent} e */ }, { key: '_onFullscreenChange', value: function _onFullscreenChange(e, isFs) { if (isFs) { this.view = 'fullscreen'; // Hide sections by default on mobile fullscreen if (_browser.IS_ANDROID) { this._lastSectionsValue = this.sections.visible; this.sections.visible = false; } this.focus(); } else { this.view = 'common'; if (_browser.IS_ANDROID) { this.sections.visible = this._lastSectionsValue; } // Pause video on exit fullscreeen on mobile if (_browser.IS_ANDROID_PHONE || _browser.IS_IPHONE || _browser.IS_IPOD) { this.pause(); } } } /** * On play event handler * * @access private * @param {PlayerEvent} e */ }, { key: '_onPlay', value: function _onPlay() { this.splashIcon.show('play'); } /** * On pause player event handler * Show pause icon in the center of video * * @access private */ }, { key: '_onPause', value: function _onPause() { this.splashIcon.show('pause'); } }, { key: '_onEntityFullscrenChange', value: function _onEntityFullscrenChange() { var fsApi = _FullscreenApi2.default; var isFs = !!document[fsApi.fullscreenElement]; this.trigger('fullscreenchange', isFs); } }, { key: 'entity', get: function get() { return this._entity; } }, { key: 'currentTime', get: function get() { return this.video.currentTime; }, set: function set(value) { this.video.currentTime = value; } /** * Returns the length of the current audio/video (in seconds) * Getter * * @access public * @memberof! Player# * @type {Nubmer} */ }, { key: 'duration', get: function get() { return this.video.duration; } /** * Returns whether the playback of the audio/video has ended or not * Getter * * @memberof! Player# * @type {Boolean} */ }, { key: 'ended', get: function get() { return this.video.ended; } /** * Returns and set whether the playback of the audio/video has ended or not * Getter and setter * * @access public * @memberof! Player# * @type {MediaError|String} * @fires Player#error */ }, { key: 'error', get: function get() { return this._error || null; }, set: function set(value) { if (value === null) { this._error = null; this.removeClass('leplayer--error'); if (this.errorDisplay) { this.errorDisplay.element.hide(); } return this; } this._error = new _MediaError2.default(value); this.addClass('leplayer--error'); /** * error event * * @event Player#error * @property {MediaError} error * @example * const player = new Player($('#video'), options); * player.on('error', (e, data) => console.error(data.error)); */ this.trigger('error', { error: this._error }); return this; } }, { key: 'rate', get: function get() { return this.video.rate; }, set: function set(value) { this.video.rate = value; } }, { key: 'paused', get: function get() { return this.video.paused; } /** * Return the height of player. If you want get height only of video element, use this.video.height or whatever * * @access public * @type {Number} * @memberof! Player# */ }, { key: 'height', get: function get() { return this.element.height(); } /** * Return unnecessary video heigth * @access public * @type {Number} * @memberof! Player# */ }, { key: 'videoHeight', get: function get() { return this.video.height; } /** * @access public * @type {Boolean} * @mebmerof! Player# */ }, { key: 'userActive', get: function get() { return this._userActive || false; }, set: function set(value) { if (value !== this.getUserActive) { this._userActive = value; this.toggleClass('leplayer--user-active', value); /** * User active event * * @event Player#useractive */ this.trigger('useractive'); } } /** * Set and get player view. View Can be 'common', 'fullscreen', 'mini'w * * @access public * @type {String} * @memberof! Player# */ }, { key: 'view', get: function get() { return this._view; }, set: function set(view) { if (this.view != null) { this.removeClass('leplayer--' + this.view); this.trigger('delview.' + this.view); } this._view = view; this.element.addClass('leplayer--' + view); this.trigger('setview.' + view); return this; } }]); return Player; }(_Component3.default); /** * Static helper for creating a plugins for leplayer * * @access public * @static * @param {String} name The name of plugin * @param {Function} fn Plugin init function * * @example * Player.plugin('helloWorld', function(pluginOptions) { * const player = this; * player.on('click', () => console.log('Hello world')); * }) * */ Player.plugin = function (name, fn) { Player.prototype[name] = fn; }; /** * Get by name registered component * * @param {String} name - Name of component * @return {Component} */ Player.getComponent = _Component3.default.getComponent; /** * Register component * * @access public * @static * @param {String} name * @param {Component} component * * @example * Player.registerComponent('ErrorDisplay', ErrorDisplay); */ Player.registerComponent = _Component3.default.registerComponent; /** * Register control * * @access public * @static * @param {String} name * @param {Control} control */ Player.getControl = _Control2.default.getControl; /** * Get by name registered control * * @access public * @static * @param {String} name * @returns {Control} * * @example * Player.registerControl('backward', BackwardControl); */ Player.registerControl = _Control2.default.registerControl; /** * Convert seconds to format string 'hh?:mm:ss' * * @access public * @param {Number} seconds Seconds * @param {Boolean} showHours convert to format 'hh:mm:ss' * @returns {String} */ Player.secondsToTime = _utils.secondsToTime; /** * Static helper for creating a plugins for leplayer * * @access public * @static * @param {String} name The name of plugin * @param {Function|Object} fn Plugin init function * * @example * Player.preset('common', { * width : '100%', * plugins : { * miniplayer : true * } * }); */ Player.preset = function (name, obj) { if ((typeof obj === 'undefined' ? 'undefined' : _typeof(obj)) === 'object') { Player._presets[name] = _jquery2.default.extend({}, { options: {}, initOptions: _utils.noop }, obj); } else if (typeof obj === 'function') { Player._presets[name] = obj(); } }; Player.getPreset = function (name) { if (Player._presets[name]) { return Player._presets[name]; } else { console.error('preset ' + name + ' doesn\'t exist'); return null; } }; Player._presets = {}; Player.Cookie = _cookie2.default; Player._loadSVGSprite = function (svg) { var hiddenElement = (0, _jquery2.default)('<div/>').hide(); (0, _jquery2.default)('body').prepend(hiddenElement.append(svg)); return hiddenElement; }; Player.defaultSprite = __webpack_require__(64); /* global VERSION */ Player.version = ("0.5.4"); window.$.fn.lePlayer = function (options) { return this.each(function () { return new Player((0, _jquery2.default)(this), options); }); }; window.$.lePlayer = Player; window.lePlayer = Player; /** * Mini Player plugin * * @plugin */ Player.plugin('miniplayer', function (pluginOptions) { var _this8 = this; var player = this; // Мержим с this.options.miniplayer, чтобы не сломать обратную совместимось, так как раньше // миниплеер не был плагином плеера. var options = _jquery2.default.extend({}, { width: '100%', offsetShow: function offsetShow(player) { var offset = player.element.offset().top + player.element.outerHeight() - player.getControls('common').element.height(); return offset; } }, this.options.miniplayer, pluginOptions); var controls = new _ControlCollection2.default(this, { name: 'mini', controls: options.controls, controlOptions: { control: { disable: false } } }); // Вставляем в infoElement под title и description this.infoElement.append(controls.element); /** * Return offset on oY , when miniplayer should showing or hiding * * @returns {Number} */ var offsetShow = function offsetShow() { if (_jquery2.default.isFunction(options.offsetShow)) { return options.offsetShow(player); } return options.offsetShow; }; var getWidth = function getWidth() { return options.width || _this8.element.width(); }; this._updateMiniPlayer = function (e, force) {