le-player
Version:
The best HTML5 video player made for Lectoriy.
1,977 lines (1,656 loc) • 694 kB
JavaScript
/******/ (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) {