diff options
author | Florian Larysch | 2016-12-29 02:04:25 +0100 |
---|---|---|
committer | Florian Larysch | 2016-12-29 02:04:25 +0100 |
commit | 14cd8a861efe666ab1d8fab44327b38a67fbd162 (patch) | |
tree | cd45bbb56a3edf80916962eebc189383e31c7e1e /assets/clapprio/clappr.js | |
parent | 7f0e67758967cace904fca142581176afca1272c (diff) |
add clappr
Diffstat (limited to 'assets/clapprio/clappr.js')
-rw-r--r-- | assets/clapprio/clappr.js | 31516 |
1 files changed, 31516 insertions, 0 deletions
diff --git a/assets/clapprio/clappr.js b/assets/clapprio/clappr.js new file mode 100644 index 0000000..1ee0b0a --- /dev/null +++ b/assets/clapprio/clappr.js @@ -0,0 +1,31516 @@ +(function webpackUniversalModuleDefinition(root, factory) { + if(typeof exports === 'object' && typeof module === 'object') + module.exports = factory(); + else if(typeof define === 'function' && define.amd) + define([], factory); + else if(typeof exports === 'object') + exports["Clappr"] = factory(); + else + root["Clappr"] = factory(); +})(this, function() { +return /******/ (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 = "<%=baseUrl%>/"; + +/******/ // Load entry module and return exports +/******/ return __webpack_require__(0); +/******/ }) +/************************************************************************/ +/******/ ([ +/* 0 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + var _player = __webpack_require__(1); + + var _player2 = _interopRequireDefault(_player); + + var _utils = __webpack_require__(2); + + var _utils2 = _interopRequireDefault(_utils); + + var _events = __webpack_require__(5); + + var _events2 = _interopRequireDefault(_events); + + var _playback = __webpack_require__(31); + + var _playback2 = _interopRequireDefault(_playback); + + var _container_plugin = __webpack_require__(85); + + var _container_plugin2 = _interopRequireDefault(_container_plugin); + + var _core_plugin = __webpack_require__(105); + + var _core_plugin2 = _interopRequireDefault(_core_plugin); + + var _ui_core_plugin = __webpack_require__(100); + + var _ui_core_plugin2 = _interopRequireDefault(_ui_core_plugin); + + var _ui_container_plugin = __webpack_require__(80); + + var _ui_container_plugin2 = _interopRequireDefault(_ui_container_plugin); + + var _base_object = __webpack_require__(4); + + var _base_object2 = _interopRequireDefault(_base_object); + + var _ui_object = __webpack_require__(18); + + var _ui_object2 = _interopRequireDefault(_ui_object); + + var _browser = __webpack_require__(3); + + var _browser2 = _interopRequireDefault(_browser); + + var _container = __webpack_require__(22); + + var _container2 = _interopRequireDefault(_container); + + var _core = __webpack_require__(13); + + var _core2 = _interopRequireDefault(_core); + + var _loader = __webpack_require__(48); + + var _loader2 = _interopRequireDefault(_loader); + + var _mediator = __webpack_require__(30); + + var _mediator2 = _interopRequireDefault(_mediator); + + var _media_control = __webpack_require__(27); + + var _media_control2 = _interopRequireDefault(_media_control); + + var _player_info = __webpack_require__(45); + + var _player_info2 = _interopRequireDefault(_player_info); + + var _base_flash_playback = __webpack_require__(56); + + var _base_flash_playback2 = _interopRequireDefault(_base_flash_playback); + + var _flash = __webpack_require__(54); + + var _flash2 = _interopRequireDefault(_flash); + + var _flashls = __webpack_require__(63); + + var _flashls2 = _interopRequireDefault(_flashls); + + var _hls = __webpack_require__(67); + + var _hls2 = _interopRequireDefault(_hls); + + var _html5_audio = __webpack_require__(61); + + var _html5_audio2 = _interopRequireDefault(_html5_audio); + + var _html5_video = __webpack_require__(51); + + var _html5_video2 = _interopRequireDefault(_html5_video); + + var _html_img = __webpack_require__(71); + + var _html_img2 = _interopRequireDefault(_html_img); + + var _no_op = __webpack_require__(74); + + var _no_op2 = _interopRequireDefault(_no_op); + + var _poster = __webpack_require__(90); + + var _poster2 = _interopRequireDefault(_poster); + + var _log = __webpack_require__(6); + + var _log2 = _interopRequireDefault(_log); + + var _styler = __webpack_require__(15); + + var _styler2 = _interopRequireDefault(_styler); + + var _vendor = __webpack_require__(8); + + var _vendor2 = _interopRequireDefault(_vendor); + + var _template = __webpack_require__(17); + + var _template2 = _interopRequireDefault(_template); + + var _clapprZepto = __webpack_require__(16); + + var _clapprZepto2 = _interopRequireDefault(_clapprZepto); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + var version = ("0.2.64"); // Copyright 2014 Globo.com Player authors. All rights reserved. + // Use of this source code is governed by a BSD-style + // license that can be found in the LICENSE file. + + exports.default = { + Player: _player2.default, + Mediator: _mediator2.default, + Events: _events2.default, + Browser: _browser2.default, + PlayerInfo: _player_info2.default, + MediaControl: _media_control2.default, + ContainerPlugin: _container_plugin2.default, + UIContainerPlugin: _ui_container_plugin2.default, + CorePlugin: _core_plugin2.default, + UICorePlugin: _ui_core_plugin2.default, + Playback: _playback2.default, + Container: _container2.default, + Core: _core2.default, + Loader: _loader2.default, + BaseObject: _base_object2.default, + UIObject: _ui_object2.default, + Utils: _utils2.default, + BaseFlashPlayback: _base_flash_playback2.default, + Flash: _flash2.default, + FlasHLS: _flashls2.default, + HLS: _hls2.default, + HTML5Audio: _html5_audio2.default, + HTML5Video: _html5_video2.default, + HTMLImg: _html_img2.default, + NoOp: _no_op2.default, + Poster: _poster2.default, + Log: _log2.default, + Styler: _styler2.default, + Vendor: _vendor2.default, + version: version, + template: _template2.default, + $: _clapprZepto2.default + }; + module.exports = exports['default']; + +/***/ }, +/* 1 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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 _utils = __webpack_require__(2); + + var _base_object = __webpack_require__(4); + + var _base_object2 = _interopRequireDefault(_base_object); + + var _events = __webpack_require__(5); + + var _events2 = _interopRequireDefault(_events); + + var _browser = __webpack_require__(3); + + var _browser2 = _interopRequireDefault(_browser); + + var _core_factory = __webpack_require__(11); + + var _core_factory2 = _interopRequireDefault(_core_factory); + + var _loader = __webpack_require__(48); + + var _loader2 = _interopRequireDefault(_loader); + + var _player_info = __webpack_require__(45); + + var _player_info2 = _interopRequireDefault(_player_info); + + var _clapprZepto = __webpack_require__(16); + + var _clapprZepto2 = _interopRequireDefault(_clapprZepto); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + 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; } // Copyright 2014 Globo.com Player authors. All rights reserved. + // Use of this source code is governed by a BSD-style + // license that can be found in the LICENSE file. + + var baseUrl = (0, _utils.currentScriptUrl)().replace(/\/[^\/]+$/, ''); + + /** + * @class Player + * @constructor + * @extends BaseObject + * @module components + * @example + * ### Using the Player + * + * Add the following script on your HTML: + * ```html + * <head> + * <script type="text/javascript" src="http://cdn.clappr.io/latest/clappr.min.js"></script> + * </head> + * ``` + * Now, create the player: + * ```html + * <body> + * <div id="player"></div> + * <script> + * var player = new Clappr.Player({source: "http://your.video/here.mp4", parentId: "#player"}); + * </script> + * </body> + * ``` + */ + + var Player = function (_BaseObject) { + _inherits(Player, _BaseObject); + + _createClass(Player, [{ + key: 'loader', + set: function set(loader) { + this._loader = loader; + }, + get: function get() { + if (!this._loader) { + this._loader = new _loader2.default(this.options.plugins || {}, this.options.playerId); + } + return this._loader; + } + + /** + * Determine if the playback has ended. + * @property ended + * @type Boolean + */ + + }, { + key: 'ended', + get: function get() { + return this.core.mediaControl.container.ended; + } + + /** + * Determine if the playback is having to buffer in order for + * playback to be smooth. + * (i.e if a live stream is playing smoothly, this will be false) + * @property buffering + * @type Boolean + */ + + }, { + key: 'buffering', + get: function get() { + return this.core.mediaControl.container.buffering; + } + + /* + * determine if the player is ready. + * @property isReady + * @type {Boolean} `true` if the player is ready. ie PLAYER_READY event has fired + */ + + }, { + key: 'isReady', + get: function get() { + return !!this._ready; + } + + /** + * An events map that allows the user to add custom callbacks in player's options. + * @property eventsMapping + * @type {Object} + */ + + }, { + key: 'eventsMapping', + get: function get() { + return { + onReady: _events2.default.PLAYER_READY, + onResize: _events2.default.PLAYER_RESIZE, + onPlay: _events2.default.PLAYER_PLAY, + onPause: _events2.default.PLAYER_PAUSE, + onStop: _events2.default.PLAYER_STOP, + onEnded: _events2.default.PLAYER_ENDED, + onSeek: _events2.default.PLAYER_SEEK, + onError: _events2.default.PLAYER_ERROR, + onTimeUpdate: _events2.default.PLAYER_TIMEUPDATE, + onVolumeUpdate: _events2.default.PLAYER_VOLUMEUPDATE + }; + } + + /** + * ## Player's constructor + * + * You might pass the options object to build the player. + * ```javascript + * var options = {source: "http://example.com/video.mp4", param1: "val1"}; + * var player = new Clappr.Player(options); + * ``` + * + * @method constructor + * @param {Object} options Data + * options to build a player instance + * @param {Number} [options.width] + * player's width **default**: `640` + * @param {Number} [options.height] + * player's height **default**: `360` + * @param {String} [options.parentId] + * the id of the element on the page that the player should be inserted into + * @param {Object} [options.parent] + * a reference to a dom element that the player should be inserted into + * @param {String} [options.source] + * The media source URL, or {source: <<source URL>>, mimeType: <<source mime type>>} + * @param {Object} [options.sources] + * An array of media source URL's, or an array of {source: <<source URL>>, mimeType: <<source mime type>>} + * @param {Boolean} [options.autoPlay] + * automatically play after page load **default**: `false` + * @param {Boolean} [options.loop] + * automatically replay after it ends **default**: `false` + * @param {Boolean} [options.chromeless] + * player acts in chromeless mode **default**: `false` + * @param {Boolean} [options.allowUserInteraction] + * whether or not the player should handle click events when in chromeless mode **default**: `false` on desktops browsers, `true` on mobile. + * @param {Boolean} [options.disableKeyboardShortcuts] + * disable keyboard shortcuts. **default**: `false`. `true` if `allowUserInteraction` is `false`. + * @param {Boolean} [options.muted] + * start the video muted **default**: `false` + * @param {String} [options.mimeType] + * add `mimeType: "application/vnd.apple.mpegurl"` if you need to use a url without extension. + * @param {String} [options.actualLiveTime] + * show duration and seek time relative to actual time. + * @param {String} [options.actualLiveServerTime] + * specify server time as a string, format: "2015/11/26 06:01:03". This option is meant to be used with actualLiveTime. + * @param {Boolean} [options.persistConfig] + * persist player's settings (volume) through the same domain **default**: `true` + * @param {String} [options.preload] + * video will be preloaded according to `preload` attribute options **default**: `'metadata'` + * @param {Number} [options.maxBufferLength] + * the default behavior for the **HLS playback** is to keep buffering indefinitely, even on VoD. + * This replicates the behavior for progressive download, which continues buffering when pausing the video, thus making the video available for playback even on slow networks. + * To change this behavior use `maxBufferLength` where **value is in seconds**. + * @param {String} [options.gaAccount] + * enable Google Analytics events dispatch **(play/pause/stop/buffering/etc)** by adding your `gaAccount` + * @param {String} [options.gaTrackerName] + * besides `gaAccount` you can optionally, pass your favorite trackerName as `gaTrackerName` + * @param {Object} [options.mediacontrol] + * customize control bar colors, example: `mediacontrol: {seekbar: "#E113D3", buttons: "#66B2FF"}` + * @param {Boolean} [options.hideMediaControl] + * control media control auto hide **default**: `true` + * @param {Boolean} [options.hideVolumeBar] + * when embedded with width less than 320, volume bar will hide. You can force this behavior for all sizes by adding `true` **default**: `false` + * @param {String} [options.watermark] + * put `watermark: 'http://url/img.png'` on your embed parameters to automatically add watermark on your video. + * You can customize corner position by defining position parameter. Positions can be `bottom-left`, `bottom-right`, `top-left` and `top-right`. + * @param {String} [options.watermarkLink] + * `watermarkLink: 'http://example.net/'` - define URL to open when the watermark is clicked. If not provided watermark will not be clickable. + * @param {Boolean} [options.disableVideoTagContextMenu] + * disables the context menu (right click) on the video element if a HTML5Video playback is used. + * @param {Boolean} [options.autoSeekFromUrl] + * Automatically seek to the seconds provided in the url (e.g example.com?t=100) **default**: `true` + * @param {Boolean} [options.exitFullscreenOnEnd] + * Automatically exit full screen when the media finishes. **default**: `true` + * @param {String} [options.poster] + * define a poster by adding its address `poster: 'http://url/img.png'`. It will appear after video embed, disappear on play and go back when user stops the video. + * @param {String} [options.playbackNotSupportedMessage] + * define a custom message to be displayed when a playback is not supported. + * @param {Object} [options.events] + * Specify listeners which will be registered with their corresponding player events. + * E.g. onReady -> "PLAYER_READY", onTimeUpdate -> "PLAYER_TIMEUPDATE" + */ + + }]); + + function Player(options) { + _classCallCheck(this, Player); + + var _this = _possibleConstructorReturn(this, _BaseObject.call(this, options)); + + var defaultOptions = { playerId: (0, _utils.uniqueId)(''), persistConfig: true, width: 640, height: 360, baseUrl: baseUrl, allowUserInteraction: _browser2.default.isMobile }; + _this._options = _clapprZepto2.default.extend(defaultOptions, options); + _this.options.sources = _this._normalizeSources(options); + if (!_this.options.chromeless) { + // "allowUserInteraction" cannot be false if not in chromeless mode. + _this.options.allowUserInteraction = true; + } + if (!_this.options.allowUserInteraction) { + // if user iteraction is not allowed ensure keyboard shortcuts are disabled + _this.options.disableKeyboardShortcuts = true; + } + _this._registerOptionEventListeners(); + _this._coreFactory = new _core_factory2.default(_this); + _this.playerInfo = _player_info2.default.getInstance(_this.options.playerId); + _this.playerInfo.currentSize = { width: options.width, height: options.height }; + _this.playerInfo.options = _this.options; + if (_this.options.parentId) { + _this.setParentId(_this.options.parentId); + } else if (_this.options.parent) { + _this.attachTo(_this.options.parent); + } + return _this; + } + + /** + * Specify a `parentId` to the player. + * @method setParentId + * @param {String} parentId the element parent id. + * @return {Player} itself + */ + + + Player.prototype.setParentId = function setParentId(parentId) { + var el = document.querySelector(parentId); + if (el) { + this.attachTo(el); + } + return this; + }; + + /** + * You can use this method to attach the player to a given element. You don't need to do this when you specify it during the player instantiation passing the `parentId` param. + * @method attachTo + * @param {Object} element a given element. + * @return {Player} itself + */ + + + Player.prototype.attachTo = function attachTo(element) { + this.options.parentElement = element; + this.core = this._coreFactory.create(); + this._addEventListeners(); + return this; + }; + + Player.prototype._addEventListeners = function _addEventListeners() { + if (!this.core.isReady) { + this.listenToOnce(this.core, _events2.default.CORE_READY, this._onReady); + } else { + this._onReady(); + } + this.listenTo(this.core.mediaControl, _events2.default.MEDIACONTROL_CONTAINERCHANGED, this._containerChanged); + this.listenTo(this.core, _events2.default.CORE_FULLSCREEN, this._onFullscreenChange); + return this; + }; + + Player.prototype._addContainerEventListeners = function _addContainerEventListeners() { + var container = this.core.mediaControl.container; + if (container) { + this.listenTo(container, _events2.default.CONTAINER_PLAY, this._onPlay); + this.listenTo(container, _events2.default.CONTAINER_PAUSE, this._onPause); + this.listenTo(container, _events2.default.CONTAINER_STOP, this._onStop); + this.listenTo(container, _events2.default.CONTAINER_ENDED, this._onEnded); + this.listenTo(container, _events2.default.CONTAINER_SEEK, this._onSeek); + this.listenTo(container, _events2.default.CONTAINER_ERROR, this._onError); + this.listenTo(container, _events2.default.CONTAINER_TIMEUPDATE, this._onTimeUpdate); + this.listenTo(container, _events2.default.CONTAINER_VOLUME, this._onVolumeUpdate); + } + return this; + }; + + Player.prototype._registerOptionEventListeners = function _registerOptionEventListeners() { + var _this2 = this; + + var userEvents = this.options.events || {}; + Object.keys(userEvents).forEach(function (userEvent) { + var eventType = _this2.eventsMapping[userEvent]; + if (eventType) { + var eventFunction = userEvents[userEvent]; + eventFunction = typeof eventFunction === 'function' && eventFunction; + eventFunction && _this2.on(eventType, eventFunction); + } + }); + return this; + }; + + Player.prototype._containerChanged = function _containerChanged() { + this.stopListening(); + this._addEventListeners(); + }; + + Player.prototype._onReady = function _onReady() { + this._ready = true; + this._addContainerEventListeners(); + this.trigger(_events2.default.PLAYER_READY); + }; + + Player.prototype._onFullscreenChange = function _onFullscreenChange(fullscreen) { + this.trigger(_events2.default.PLAYER_FULLSCREEN, fullscreen); + }; + + Player.prototype._onVolumeUpdate = function _onVolumeUpdate(volume) { + this.trigger(_events2.default.PLAYER_VOLUMEUPDATE, volume); + }; + + Player.prototype._onPlay = function _onPlay() { + this.trigger(_events2.default.PLAYER_PLAY); + }; + + Player.prototype._onPause = function _onPause() { + this.trigger(_events2.default.PLAYER_PAUSE); + }; + + Player.prototype._onStop = function _onStop() { + this.trigger(_events2.default.PLAYER_STOP, this.getCurrentTime()); + }; + + Player.prototype._onEnded = function _onEnded() { + this.trigger(_events2.default.PLAYER_ENDED); + }; + + Player.prototype._onSeek = function _onSeek(time) { + this.trigger(_events2.default.PLAYER_SEEK, time); + }; + + Player.prototype._onTimeUpdate = function _onTimeUpdate(timeProgress) { + this.trigger(_events2.default.PLAYER_TIMEUPDATE, timeProgress); + }; + + Player.prototype._onError = function _onError(error) { + this.trigger(_events2.default.PLAYER_ERROR, error); + }; + + Player.prototype._normalizeSources = function _normalizeSources(options) { + var sources = options.sources || (options.source !== undefined ? [options.source] : []); + return sources.length === 0 ? [{ source: '', mimeType: '' }] : sources; + }; + + /** + * resizes the current player canvas. + * @method resize + * @param {Object} size should be a literal object with `height` and `width`. + * @return {Player} itself + * @example + * ```javascript + * player.resize({height: 360, width: 640}) + * ``` + */ + + + Player.prototype.resize = function resize(size) { + this.core.resize(size); + return this; + }; + + /** + * loads a new source. + * @method load + * @param {Array|String} sources source or sources of video. + * An array item can be a string or {source: <<source URL>>, mimeType: <<source mime type>>} + * @param {String} mimeType a mime type, example: `'application/vnd.apple.mpegurl'` + * @param {Boolean} [autoPlay=false] whether playing should be started immediately + * @return {Player} itself + */ + + + Player.prototype.load = function load(sources, mimeType, autoPlay) { + if (autoPlay !== undefined) { + this.configure({ autoPlay: !!autoPlay }); + } + this.core.load(sources, mimeType); + return this; + }; + + /** + * destroys the current player and removes it from the DOM. + * @method destroy + * @return {Player} itself + */ + + + Player.prototype.destroy = function destroy() { + this.core.destroy(); + return this; + }; + + /** + * plays the current video (`source`). + * @method play + * @return {Player} itself + */ + + + Player.prototype.play = function play() { + this.core.mediaControl.container.play(); + return this; + }; + + /** + * pauses the current video (`source`). + * @method pause + * @return {Player} itself + */ + + + Player.prototype.pause = function pause() { + this.core.mediaControl.container.pause(); + return this; + }; + + /** + * stops the current video (`source`). + * @method stop + * @return {Player} itself + */ + + + Player.prototype.stop = function stop() { + this.core.mediaControl.container.stop(); + return this; + }; + + /** + * seeks the current video (`source`). For example, `player.seek(120)` will seek to second 120 (2minutes) of the current video. + * @method seek + * @param {Number} time should be a number between 0 and the video duration. + * @return {Player} itself + */ + + + Player.prototype.seek = function seek(time) { + this.core.mediaControl.container.seek(time); + return this; + }; + + /** + * seeks the current video (`source`). For example, `player.seek(50)` will seek to the middle of the current video. + * @method seekPercentage + * @param {Number} time should be a number between 0 and 100. + * @return {Player} itself + */ + + + Player.prototype.seekPercentage = function seekPercentage(percentage) { + this.core.mediaControl.container.seekPercentage(percentage); + return this; + }; + + /** + * Set the volume for the current video (`source`). + * @method setVolume + * @param {Number} volume should be a number between 0 and 100, 0 being mute and 100 the max volume. + * @return {Player} itself + */ + + + Player.prototype.setVolume = function setVolume(volume) { + if (this.core && this.core.mediaControl) { + this.core.mediaControl.setVolume(volume); + } + return this; + }; + + /** + * Get the volume for the current video + * @method getVolume + * @return {Number} volume should be a number between 0 and 100, 0 being mute and 100 the max volume. + */ + + + Player.prototype.getVolume = function getVolume() { + return this.core && this.core.mediaControl ? this.core.mediaControl.volume : 0; + }; + + /** + * mutes the current video (`source`). + * @method mute + * @return {Player} itself + */ + + + Player.prototype.mute = function mute() { + this._mutedVolume = this.getVolume(); + this.setVolume(0); + return this; + }; + + /** + * unmutes the current video (`source`). + * @method unmute + * @return {Player} itself + */ + + + Player.prototype.unmute = function unmute() { + this.setVolume(typeof this._mutedVolume === 'number' ? this._mutedVolume : 100); + this._mutedVolume = null; + return this; + }; + + /** + * checks if the player is playing. + * @method isPlaying + * @return {Boolean} `true` if the current source is playing, otherwise `false` + */ + + + Player.prototype.isPlaying = function isPlaying() { + return this.core.mediaControl.container.isPlaying(); + }; + + /** + * returns `true` if DVR is enable otherwise `false`. + * @method isDvrEnabled + * @return {Boolean} + */ + + + Player.prototype.isDvrEnabled = function isDvrEnabled() { + return this.core.mediaControl.container.isDvrEnabled(); + }; + + /** + * returns `true` if DVR is in use otherwise `false`. + * @method isDvrInUse + * @return {Boolean} + */ + + + Player.prototype.isDvrInUse = function isDvrInUse() { + return this.core.mediaControl.container.isDvrInUse(); + }; + + /** + * enables to configure a player after its creation + * @method configure + * @param {Object} options all the options to change in form of a javascript object + * @return {Player} itself + */ + + + Player.prototype.configure = function configure(options) { + this.core.configure(options); + return this; + }; + + /** + * get a plugin by its name. + * @method getPlugin + * @param {String} name of the plugin. + * @return {Object} the plugin instance + * @example + * ```javascript + * var poster = player.getPlugin('poster'); + * poster.hidePlayButton(); + * ``` + */ + + + Player.prototype.getPlugin = function getPlugin(name) { + var plugins = this.core.plugins.concat(this.core.mediaControl.container.plugins); + return plugins.filter(function (plugin) { + return plugin.name === name; + })[0]; + }; + + /** + * the current time in seconds. + * @method getCurrentTime + * @return {Number} current time (in seconds) of the current source + */ + + + Player.prototype.getCurrentTime = function getCurrentTime() { + return this.core.mediaControl.container.getCurrentTime(); + }; + + /** + * The time that "0" now represents relative to when playback started. + * For a stream with a sliding window this will increase as content is + * removed from the beginning. + * @method getStartTimeOffset + * @return {Number} time (in seconds) that time "0" represents. + */ + + + Player.prototype.getStartTimeOffset = function getStartTimeOffset() { + return this.core.mediaControl.container.getStartTimeOffset(); + }; + + /** + * the duration time in seconds. + * @method getDuration + * @return {Number} duration time (in seconds) of the current source + */ + + + Player.prototype.getDuration = function getDuration() { + return this.core.mediaControl.container.getDuration(); + }; + + return Player; + }(_base_object2.default); + + exports.default = Player; + module.exports = exports['default']; + +/***/ }, +/* 2 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + exports.cancelAnimationFrame = exports.requestAnimationFrame = exports.QueryString = exports.Config = exports.Fullscreen = undefined; + + 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; }; }(); + + exports.extend = extend; + exports.formatTime = formatTime; + exports.seekStringToSeconds = seekStringToSeconds; + exports.uniqueId = uniqueId; + exports.isNumber = isNumber; + exports.currentScriptUrl = currentScriptUrl; + exports.getBrowserLanguage = getBrowserLanguage; + exports.now = now; + exports.removeArrayItem = removeArrayItem; + + var _browser = __webpack_require__(3); + + var _browser2 = _interopRequireDefault(_browser); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + 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; } // Copyright 2014 Globo.com Player authors. All rights reserved. + // Use of this source code is governed by a BSD-style + // license that can be found in the LICENSE file. + /*jshint -W079 */ + + function assign(obj, source) { + if (source) { + for (var prop in source) { + var propDescriptor = Object.getOwnPropertyDescriptor(source, prop); + propDescriptor ? Object.defineProperty(obj, prop, propDescriptor) : obj[prop] = source[prop]; + } + } + return obj; + } + + function extend(parent, properties) { + var Surrogate = function (_parent) { + _inherits(Surrogate, _parent); + + function Surrogate() { + _classCallCheck(this, Surrogate); + + for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } + + var _this = _possibleConstructorReturn(this, _parent.call.apply(_parent, [this].concat(args))); + + if (properties.initialize) { + properties.initialize.apply(_this, args); + } + return _this; + } + + return Surrogate; + }(parent); + + assign(Surrogate.prototype, properties); + return Surrogate; + } + + function formatTime(time, paddedHours) { + if (!isFinite(time)) { + return '--:--'; + } + time = time * 1000; + time = parseInt(time / 1000); + var seconds = time % 60; + time = parseInt(time / 60); + var minutes = time % 60; + time = parseInt(time / 60); + var hours = time % 24; + var days = parseInt(time / 24); + var out = ''; + if (days && days > 0) { + out += days + ':'; + if (hours < 1) { + out += '00:'; + } + } + if (hours && hours > 0 || paddedHours) { + out += ('0' + hours).slice(-2) + ':'; + } + out += ('0' + minutes).slice(-2) + ':'; + out += ('0' + seconds).slice(-2); + return out.trim(); + } + + var Fullscreen = exports.Fullscreen = { + isFullscreen: function isFullscreen() { + return !!(document.webkitFullscreenElement || document.webkitIsFullScreen || document.mozFullScreen || document.msFullscreenElement); + }, + requestFullscreen: function requestFullscreen(el) { + if (el.requestFullscreen) { + el.requestFullscreen(); + } else if (el.webkitRequestFullscreen) { + el.webkitRequestFullscreen(); + } else if (el.mozRequestFullScreen) { + el.mozRequestFullScreen(); + } else if (el.msRequestFullscreen) { + el.msRequestFullscreen(); + } else if (el.querySelector && el.querySelector('video') && el.querySelector('video').webkitEnterFullScreen) { + el.querySelector('video').webkitEnterFullScreen(); + } + }, + cancelFullscreen: function cancelFullscreen() { + if (document.exitFullscreen) { + document.exitFullscreen(); + } else if (document.webkitCancelFullScreen) { + document.webkitCancelFullScreen(); + } else if (document.webkitExitFullscreen) { + document.webkitExitFullscreen(); + } else if (document.mozCancelFullScreen) { + document.mozCancelFullScreen(); + } else if (document.msExitFullscreen) { + document.msExitFullscreen(); + } + }, + fullscreenEnabled: function fullscreenEnabled() { + return !!(document.fullscreenEnabled || document.webkitFullscreenEnabled || document.mozFullScreenEnabled || document.msFullscreenEnabled); + } + }; + + var Config = exports.Config = function () { + function Config() { + _classCallCheck(this, Config); + } + + Config._defaultConfig = function _defaultConfig() { + return { + volume: { + value: 100, + parse: parseInt + } + }; + }; + + Config._defaultValueFor = function _defaultValueFor(key) { + try { + return this._defaultConfig()[key].parse(this._defaultConfig()[key].value); + } catch (e) { + return undefined; + } + }; + + Config._createKeyspace = function _createKeyspace(key) { + return 'clappr.' + document.domain + '.' + key; + }; + + Config.restore = function restore(key) { + if (_browser2.default.hasLocalstorage && localStorage[this._createKeyspace(key)]) { + return this._defaultConfig()[key].parse(localStorage[this._createKeyspace(key)]); + } + return this._defaultValueFor(key); + }; + + Config.persist = function persist(key, value) { + if (_browser2.default.hasLocalstorage) { + try { + localStorage[this._createKeyspace(key)] = value; + return true; + } catch (e) { + return false; + } + } + }; + + return Config; + }(); + + var QueryString = exports.QueryString = function () { + function QueryString() { + _classCallCheck(this, QueryString); + } + + QueryString.parse = function parse(paramsString) { + var match = void 0; + var pl = /\+/g, + // Regex for replacing addition symbol with a space + search = /([^&=]+)=?([^&]*)/g, + decode = function decode(s) { + return decodeURIComponent(s.replace(pl, ' ')); + }, + params = {}; + while (match = search.exec(paramsString)) { + // eslint-disable-line no-cond-assign + params[decode(match[1]).toLowerCase()] = decode(match[2]); + } + return params; + }; + + _createClass(QueryString, null, [{ + key: 'params', + get: function get() { + var query = window.location.search.substring(1); + if (query !== this.query) { + this._urlParams = this.parse(query); + this.query = query; + } + return this._urlParams; + } + }, { + key: 'hashParams', + get: function get() { + var hash = window.location.hash.substring(1); + if (hash !== this.hash) { + this._hashParams = this.parse(hash); + this.hash = hash; + } + return this._hashParams; + } + }]); + + return QueryString; + }(); + + function seekStringToSeconds() { + var paramName = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 't'; + + var seconds = 0; + var seekString = QueryString.params[paramName] || QueryString.hashParams[paramName] || ''; + var parts = seekString.match(/[0-9]+[hms]+/g) || []; + if (parts.length > 0) { + (function () { + var factor = { 'h': 3600, 'm': 60, 's': 1 }; + parts.forEach(function (el) { + if (el) { + var suffix = el[el.length - 1]; + var time = parseInt(el.slice(0, el.length - 1), 10); + seconds += time * factor[suffix]; + } + }); + })(); + } else if (seekString) { + seconds = parseInt(seekString, 10); + } + return seconds; + } + + var idsCounter = {}; + + function uniqueId(prefix) { + idsCounter[prefix] || (idsCounter[prefix] = 0); + var id = ++idsCounter[prefix]; + return prefix + id; + } + + function isNumber(value) { + return value - parseFloat(value) + 1 >= 0; + } + + function currentScriptUrl() { + var scripts = document.getElementsByTagName('script'); + return scripts.length ? scripts[scripts.length - 1].src : ''; + } + + var requestAnimationFrame = exports.requestAnimationFrame = (window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || function (fn) { + window.setTimeout(fn, 1000 / 60); + }).bind(window); + + var cancelAnimationFrame = exports.cancelAnimationFrame = (window.cancelAnimationFrame || window.mozCancelAnimationFrame || window.webkitCancelAnimationFrame || window.clearTimeout).bind(window); + + function getBrowserLanguage() { + return window.navigator && window.navigator.language; + } + + function now() { + if (window.performance && window.performance.now) { + return performance.now(); + } + return Date.now(); + } + + // remove the item from the array if it exists in the array + function removeArrayItem(arr, item) { + var i = arr.indexOf(item); + if (i >= 0) { + arr.splice(i, 1); + } + } + + exports.default = { + Config: Config, + Fullscreen: Fullscreen, + QueryString: QueryString, + extend: extend, + formatTime: formatTime, + seekStringToSeconds: seekStringToSeconds, + uniqueId: uniqueId, + currentScriptUrl: currentScriptUrl, + isNumber: isNumber, + requestAnimationFrame: requestAnimationFrame, + cancelAnimationFrame: cancelAnimationFrame, + getBrowserLanguage: getBrowserLanguage, + now: now, + removeArrayItem: removeArrayItem + }; + +/***/ }, +/* 3 */ +/***/ function(module, exports) { + + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + // Copyright 2014 Globo.com Player authors. All rights reserved. + // Use of this source code is governed by a BSD-style + // license that can be found in the LICENSE file. + + var Browser = {}; + + var hasLocalstorage = function hasLocalstorage() { + try { + localStorage.setItem('clappr', 'clappr'); + localStorage.removeItem('clappr'); + return true; + } catch (e) { + return false; + } + }; + + var hasFlash = function hasFlash() { + try { + var fo = new ActiveXObject('ShockwaveFlash.ShockwaveFlash'); + return !!fo; + } catch (e) { + return !!(navigator.mimeTypes && navigator.mimeTypes['application/x-shockwave-flash'] !== undefined && navigator.mimeTypes['application/x-shockwave-flash'].enabledPlugin); + } + }; + + var getBrowserInfo = function getBrowserInfo() { + var ua = navigator.userAgent; + var parts = ua.match(/\b(playstation 4|nx|opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i) || [], + extra = void 0; + if (/trident/i.test(parts[1])) { + extra = /\brv[ :]+(\d+)/g.exec(ua) || []; + return { name: 'IE', version: parseInt(extra[1] || '') }; + } else if (parts[1] === 'Chrome') { + extra = ua.match(/\bOPR\/(\d+)/); + if (extra != null) { + return { name: 'Opera', version: parseInt(extra[1]) }; + } + } + parts = parts[2] ? [parts[1], parts[2]] : [navigator.appName, navigator.appVersion, '-?']; + + if (extra = ua.match(/version\/(\d+)/i)) { + parts.splice(1, 1, extra[1]); + } + return { name: parts[0], version: parseInt(parts[1]) }; + }; + + var browserInfo = getBrowserInfo(); + + Browser.isSafari = /safari/i.test(navigator.userAgent) && navigator.userAgent.indexOf('Chrome') === -1; + Browser.isChrome = /chrome/i.test(navigator.userAgent); + Browser.isFirefox = /firefox/i.test(navigator.userAgent); + Browser.isLegacyIE = !!window.ActiveXObject; + Browser.isIE = Browser.isLegacyIE || /trident.*rv:1\d/i.test(navigator.userAgent); + Browser.isIE11 = /trident.*rv:11/i.test(navigator.userAgent); + Browser.isChromecast = Browser.isChrome && /CrKey/i.test(navigator.userAgent); + Browser.isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|Windows Phone|IEMobile|Opera Mini/i.test(navigator.userAgent); + Browser.isiOS = /iPad|iPhone|iPod/i.test(navigator.userAgent); + Browser.isAndroid = /Android/i.test(navigator.userAgent); + Browser.isWindowsPhone = /Windows Phone/i.test(navigator.userAgent); + Browser.isWin8App = /MSAppHost/i.test(navigator.userAgent); + Browser.isWiiU = /WiiU/i.test(navigator.userAgent); + Browser.isPS4 = /PlayStation 4/i.test(navigator.userAgent); + Browser.hasLocalstorage = hasLocalstorage(); + Browser.hasFlash = hasFlash(); + + Browser.name = browserInfo.name; + Browser.version = browserInfo.version; + + exports.default = Browser; + module.exports = exports['default']; + +/***/ }, +/* 4 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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 _utils = __webpack_require__(2); + + var _events = __webpack_require__(5); + + var _events2 = _interopRequireDefault(_events); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + 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; } // Copyright 2014 Globo.com Player authors. All rights reserved. + // Use of this source code is governed by a BSD-style + // license that can be found in the LICENSE file. + + /** + * @class BaseObject + * @constructor + * @extends Events + * @module base + */ + var BaseObject = function (_Events) { + _inherits(BaseObject, _Events); + + _createClass(BaseObject, [{ + key: 'options', + + /** + * returns the object options + * @property options + * @type Object + */ + get: function get() { + return this._options; + } + + /** + * @method constructor + * @param {Object} options + */ + + }]); + + function BaseObject() { + var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + + _classCallCheck(this, BaseObject); + + var _this = _possibleConstructorReturn(this, _Events.call(this, options)); + + _this._options = options; + _this.uniqueId = (0, _utils.uniqueId)('o'); + return _this; + } + /** + * a unique id prefixed with `'o'`, `o1, o232` + * + * @property uniqueId + * @type String + */ + + + return BaseObject; + }(_events2.default); + + exports.default = BaseObject; + module.exports = exports['default']; + +/***/ }, +/* 5 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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; }; // Copyright 2014 Globo.com Player authors. All rights reserved. + // Use of this source code is governed by a BSD-style + // license that can be found in the LICENSE file. + + var _log = __webpack_require__(6); + + var _log2 = _interopRequireDefault(_log); + + var _utils = __webpack_require__(2); + + var _lodash = __webpack_require__(10); + + var _lodash2 = _interopRequireDefault(_lodash); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + + var slice = Array.prototype.slice; + + var eventSplitter = /\s+/; + + var eventsApi = function eventsApi(obj, action, name, rest) { + if (!name) { + return true; + } + + // Handle event maps. + if ((typeof name === 'undefined' ? 'undefined' : _typeof(name)) === 'object') { + for (var key in name) { + obj[action].apply(obj, [key, name[key]].concat(rest)); + } + return false; + } + + // Handle space separated event names. + if (eventSplitter.test(name)) { + var names = name.split(eventSplitter); + for (var i = 0, l = names.length; i < l; i++) { + obj[action].apply(obj, [names[i]].concat(rest)); + } + return false; + } + + return true; + }; + + var triggerEvents = function triggerEvents(events, args, klass, name) { + var ev = void 0, + i = -1; + var l = events.length, + a1 = args[0], + a2 = args[1], + a3 = args[2]; + run(); + + function run() { + try { + switch (args.length) { + case 0: + while (++i < l) { + (ev = events[i]).callback.call(ev.ctx); + }return; + case 1: + while (++i < l) { + (ev = events[i]).callback.call(ev.ctx, a1); + }return; + case 2: + while (++i < l) { + (ev = events[i]).callback.call(ev.ctx, a1, a2); + }return; + case 3: + while (++i < l) { + (ev = events[i]).callback.call(ev.ctx, a1, a2, a3); + }return; + default: + while (++i < l) { + (ev = events[i]).callback.apply(ev.ctx, args); + }return; + } + } catch (exception) { + _log2.default.error.apply(_log2.default, [klass, 'error on event', name, 'trigger', '-', exception]); + run(); + } + } + }; + + /** + * @class Events + * @constructor + * @module base + */ + + var Events = function () { + function Events() { + _classCallCheck(this, Events); + } + + /** + * listen to an event indefinitely, if you want to stop you need to call `off` + * @method on + * @param {String} name + * @param {Function} callback + * @param {Object} context + */ + Events.prototype.on = function on(name, callback, context) { + if (!eventsApi(this, 'on', name, [callback, context]) || !callback) { + return this; + } + this._events || (this._events = {}); + var events = this._events[name] || (this._events[name] = []); + events.push({ callback: callback, context: context, ctx: context || this }); + return this; + }; + + /** + * listen to an event only once + * @method once + * @param {String} name + * @param {Function} callback + * @param {Object} context + */ + + + Events.prototype.once = function once(name, callback, context) { + if (!eventsApi(this, 'once', name, [callback, context]) || !callback) { + return this; + } + var self = this; + var once = (0, _lodash2.default)(function () { + self.off(name, once); + callback.apply(this, arguments); + }); + once._callback = callback; + return this.on(name, once, context); + }; + + /** + * stop listening to an event + * @method off + * @param {String} name + * @param {Function} callback + * @param {Object} context + */ + + + Events.prototype.off = function off(name, callback, context) { + var retain = void 0, + ev = void 0, + events = void 0, + names = void 0, + i = void 0, + l = void 0, + j = void 0, + k = void 0; + if (!this._events || !eventsApi(this, 'off', name, [callback, context])) { + return this; + } + if (!name && !callback && !context) { + this._events = void 0; + return this; + } + names = name ? [name] : Object.keys(this._events); + // jshint maxdepth:5 + for (i = 0, l = names.length; i < l; i++) { + name = names[i]; + events = this._events[name]; + if (events) { + this._events[name] = retain = []; + if (callback || context) { + for (j = 0, k = events.length; j < k; j++) { + ev = events[j]; + if (callback && callback !== ev.callback && callback !== ev.callback._callback || context && context !== ev.context) { + retain.push(ev); + } + } + } + if (!retain.length) { + delete this._events[name]; + } + } + } + return this; + }; + + /** + * triggers an event given its `name` + * @method trigger + * @param {String} name + */ + + + Events.prototype.trigger = function trigger(name) { + var klass = this.name || this.constructor.name; + _log2.default.debug.apply(_log2.default, [klass].concat(Array.prototype.slice.call(arguments))); + if (!this._events) { + return this; + } + var args = slice.call(arguments, 1); + if (!eventsApi(this, 'trigger', name, args)) { + return this; + } + var events = this._events[name]; + var allEvents = this._events.all; + if (events) { + triggerEvents(events, args, klass, name); + } + if (allEvents) { + triggerEvents(allEvents, arguments, klass, name); + } + return this; + }; + + /** + * stop listening an event for a given object + * @method stopListening + * @param {Object} obj + * @param {String} name + * @param {Function} callback + */ + + + Events.prototype.stopListening = function stopListening(obj, name, callback) { + var listeningTo = this._listeningTo; + if (!listeningTo) { + return this; + } + var remove = !name && !callback; + if (!callback && (typeof name === 'undefined' ? 'undefined' : _typeof(name)) === 'object') { + callback = this; + } + if (obj) { + (listeningTo = {})[obj._listenId] = obj; + } + for (var id in listeningTo) { + obj = listeningTo[id]; + obj.off(name, callback, this); + if (remove || Object.keys(obj._events).length === 0) { + delete this._listeningTo[id]; + } + } + return this; + }; + + return Events; + }(); + + /** + * listen to an event indefinitely for a given `obj` + * @method listenTo + * @param {Object} obj + * @param {String} name + * @param {Function} callback + * @param {Object} context + * @example + * ```javascript + * this.listenTo(this.core.playback, Events.PLAYBACK_PAUSE, this.callback) + * ``` + */ + /** + * listen to an event once for a given `obj` + * @method listenToOnce + * @param {Object} obj + * @param {String} name + * @param {Function} callback + * @param {Object} context + * @example + * ```javascript + * this.listenToOnce(this.core.playback, Events.PLAYBACK_PAUSE, this.callback) + * ``` + */ + + + exports.default = Events; + var listenMethods = { listenTo: 'on', listenToOnce: 'once' }; + + Object.keys(listenMethods).forEach(function (method) { + Events.prototype[method] = function (obj, name, callback) { + var listeningTo = this._listeningTo || (this._listeningTo = {}); + var id = obj._listenId || (obj._listenId = (0, _utils.uniqueId)('l')); + listeningTo[id] = obj; + if (!callback && (typeof name === 'undefined' ? 'undefined' : _typeof(name)) === 'object') { + callback = this; + } + obj[listenMethods[method]](name, callback, this); + return this; + }; + }); + + // PLAYER EVENTS + /** + * Fired when the player is ready on startup + * + * @event PLAYER_READY + */ + Events.PLAYER_READY = 'ready'; + /** + * Fired when player resizes + * + * @event PLAYER_RESIZE + * @param {Object} currentSize an object with the current size + */ + Events.PLAYER_RESIZE = 'resize'; + /** + * Fired when player changes its fullscreen state + * + * @event PLAYER_FULLSCREEN + * @param {Boolean} whether or not the player is on fullscreen mode + */ + Events.PLAYER_FULLSCREEN = 'fullscreen'; + /** + * Fired when player starts to play + * + * @event PLAYER_PLAY + */ + Events.PLAYER_PLAY = 'play'; + /** + * Fired when player pauses + * + * @event PLAYER_PAUSE + */ + Events.PLAYER_PAUSE = 'pause'; + /** + * Fired when player stops + * + * @event PLAYER_STOP + */ + Events.PLAYER_STOP = 'stop'; + /** + * Fired when player ends the video + * + * @event PLAYER_ENDED + */ + Events.PLAYER_ENDED = 'ended'; + /** + * Fired when player seeks the video + * + * @event PLAYER_SEEK + * @param {Number} time the current time in seconds + */ + Events.PLAYER_SEEK = 'seek'; + /** + * Fired when player receives an error + * + * @event PLAYER_ERROR + * @param {Object} error the error + */ + Events.PLAYER_ERROR = 'error'; + /** + * Fired when the time is updated on player + * + * @event PLAYER_TIMEUPDATE + * @param {Object} progress Data + * progress object + * @param {Number} [progress.current] + * current time + * @param {Number} [progress.total] + * total time + */ + Events.PLAYER_TIMEUPDATE = 'timeupdate'; + /** + * Fired when player updates its volume + * + * @event PLAYER_VOLUMEUPDATE + * @param {Number} volume the current volume + */ + Events.PLAYER_VOLUMEUPDATE = 'volumeupdate'; + + // Playback Events + /** + * Fired when the playback is downloading the media + * + * @event PLAYBACK_PROGRESS + * @param progress {Object} + * Data progress object + * @param [progress.start] {Number} + * start position of buffered content at current position + * @param [progress.current] {Number} + * end position of buffered content at current position + * @param [progress.total] {Number} + * total content to be downloaded + * @param buffered {Array} + * array of buffered segments ({start, end}). [Only for supported playbacks] + */ + Events.PLAYBACK_PROGRESS = 'playback:progress'; + /** + * Fired when the time is updated on playback + * + * @event PLAYBACK_TIMEUPDATE + * @param {Object} progress Data + * progress object + * @param {Number} [progress.current] + * current time + * @param {Number} [progress.total] + * total time + */ + Events.PLAYBACK_TIMEUPDATE = 'playback:timeupdate'; + /** + * Fired when playback is ready + * + * @event PLAYBACK_READY + */ + Events.PLAYBACK_READY = 'playback:ready'; + /** + * Fired when the playback starts having to buffer because + * playback can currently not be smooth. + * + * This corresponds to the playback `buffering` property being + * `true`. + * + * @event PLAYBACK_BUFFERING + */ + Events.PLAYBACK_BUFFERING = 'playback:buffering'; + /** + * Fired when the playback has enough in the buffer to be + * able to play smoothly, after previously being unable to + * do this. + * + * This corresponds to the playback `buffering` property being + * `false`. + * + * @event PLAYBACK_BUFFERFULL + */ + Events.PLAYBACK_BUFFERFULL = 'playback:bufferfull'; + /** + * Fired when playback changes any settings (volume, seek and etc) + * + * @event PLAYBACK_SETTINGSUPDATE + */ + Events.PLAYBACK_SETTINGSUPDATE = 'playback:settingsupdate'; + /** + * Fired when playback loaded its metadata + * + * @event PLAYBACK_LOADEDMETADATA + * @param {Object} metadata Data + * settings object + * @param {Number} [metadata.duration] + * the playback duration + * @param {Object} [metadata.data] + * extra meta data + */ + Events.PLAYBACK_LOADEDMETADATA = 'playback:loadedmetadata'; + /** + * Fired when playback updates its video quality + * + * @event PLAYBACK_HIGHDEFINITIONUPDATE + * @param {Boolean} isHD + * true when is on HD, false otherwise + */ + Events.PLAYBACK_HIGHDEFINITIONUPDATE = 'playback:highdefinitionupdate'; + /** + * Fired when playback updates its bitrate + * + * @event PLAYBACK_BITRATE + * @param {Object} bitrate Data + * bitrate object + * @param {Number} [bitrate.bandwidth] + * bitrate bandwidth when it's available + * @param {Number} [bitrate.width] + * playback width (ex: 720, 640, 1080) + * @param {Number} [bitrate.height] + * playback height (ex: 240, 480, 720) + * @param {Number} [bitrate.level] + * playback level when it's available, it could be just a map for width (0 => 240, 1 => 480, 2 => 720) + */ + Events.PLAYBACK_BITRATE = 'playback:bitrate'; + /** + * Fired when the playback has its levels + * + * @event PLAYBACK_LEVELS_AVAILABLE + * @param {Array} levels + * the ordered levels, each one with the following format `{id: 1, label: '500kbps'}` ps: id should be a number >= 0 + * @param {Number} initial + * the initial level otherwise -1 (AUTO) + */ + Events.PLAYBACK_LEVELS_AVAILABLE = 'playback:levels:available'; + /** + * Fired when the playback starts to switch level + * + * @event PLAYBACK_LEVEL_SWITCH_START + * + */ + Events.PLAYBACK_LEVEL_SWITCH_START = 'playback:levels:switch:start'; + /** + * Fired when the playback ends the level switch + * + * @event PLAYBACK_LEVEL_SWITCH_END + * + */ + Events.PLAYBACK_LEVEL_SWITCH_END = 'playback:levels:switch:end'; + + /** + * Fired when playback internal state changes + * + * @event PLAYBACK_PLAYBACKSTATE + * @param {Object} state Data + * state object + * @param {String} [state.type] + * the playback type + */ + Events.PLAYBACK_PLAYBACKSTATE = 'playback:playbackstate'; + /** + * Fired when DVR becomes enabled/disabled. + * + * @event PLAYBACK_DVR + * @param {boolean} state true if dvr enabled + */ + Events.PLAYBACK_DVR = 'playback:dvr'; + // TODO doc + Events.PLAYBACK_MEDIACONTROL_DISABLE = 'playback:mediacontrol:disable'; + // TODO doc + Events.PLAYBACK_MEDIACONTROL_ENABLE = 'playback:mediacontrol:enable'; + /** + * Fired when the media for a playback ends. + * + * @event PLAYBACK_ENDED + * @param {String} name the name of the playback + */ + Events.PLAYBACK_ENDED = 'playback:ended'; + /** + * Fired when user requests `play()` + * + * @event PLAYBACK_PLAY_INTENT + */ + Events.PLAYBACK_PLAY_INTENT = 'playback:play:intent'; + /** + * Fired when the media for a playback starts playing. + * This is not necessarily when the user requests `play()` + * The media may have to buffer first. + * I.e. `isPlaying()` might return `true` before this event is fired, + * because `isPlaying()` represents the intended state. + * + * @event PLAYBACK_PLAY + */ + Events.PLAYBACK_PLAY = 'playback:play'; + /** + * Fired when the media for a playback pauses. + * + * @event PLAYBACK_PAUSE + */ + Events.PLAYBACK_PAUSE = 'playback:pause'; + /** + * Fired when the media for a playback is stopped. + * + * @event PLAYBACK_STOP + */ + Events.PLAYBACK_STOP = 'playback:stop'; + /** + * Fired if an error occurs in the playback. + * + * @event PLAYBACK_ERROR + * @param {Object} error An object containing the error details + * @param {String} name Playback name + */ + Events.PLAYBACK_ERROR = 'playback:error'; + // TODO doc + Events.PLAYBACK_STATS_ADD = 'playback:stats:add'; + // TODO doc + Events.PLAYBACK_FRAGMENT_LOADED = 'playback:fragment:loaded'; + // TODO doc + Events.PLAYBACK_LEVEL_SWITCH = 'playback:level:switch'; + + /** + * Fired when the options were changed for the core + * + * @event CORE_OPTIONS_CHANGE + */ + Events.CORE_OPTIONS_CHANGE = 'core:options:change'; + /** + * Fired after creating containers, when the core is ready + * + * @event CORE_READY + */ + Events.CORE_READY = 'core:ready'; + /** + * Fired when the fullscreen state change + * + * @param {Boolean} whether or not the player is on fullscreen mode + * @event CORE_READY + */ + Events.CORE_FULLSCREEN = 'core:fullscreen'; + + // Container Events + /** + * Fired when the container internal state changes + * + * @event CONTAINER_PLAYBACKSTATE + * @param {Object} state Data + * state object + * @param {String} [state.type] + * the playback type + */ + Events.CONTAINER_PLAYBACKSTATE = 'container:playbackstate'; + Events.CONTAINER_PLAYBACKDVRSTATECHANGED = 'container:dvr'; + /** + * Fired when the container updates its bitrate + * + * @event CONTAINER_BITRATE + * @param {Object} bitrate Data + * bitrate object + * @param {Number} [bitrate.bandwidth] + * bitrate bandwidth when it's available + * @param {Number} [bitrate.width] + * playback width (ex: 720, 640, 1080) + * @param {Number} [bitrate.height] + * playback height (ex: 240, 480, 720) + * @param {Number} [bitrate.level] + * playback level when it's available, it could be just a map for width (0 => 240, 1 => 480, 2 => 720) + */ + Events.CONTAINER_BITRATE = 'container:bitrate'; + Events.CONTAINER_STATS_REPORT = 'container:stats:report'; + Events.CONTAINER_DESTROYED = 'container:destroyed'; + /** + * Fired when the container is ready + * + * @event CONTAINER_READY + */ + Events.CONTAINER_READY = 'container:ready'; + Events.CONTAINER_ERROR = 'container:error'; + /** + * Fired when the container loaded its metadata + * + * @event CONTAINER_LOADEDMETADATA + * @param {Object} metadata Data + * settings object + * @param {Number} [metadata.duration] + * the playback duration + * @param {Object} [metadata.data] + * extra meta data + */ + Events.CONTAINER_LOADEDMETADATA = 'container:loadedmetadata'; + /** + * Fired when the time is updated on container + * + * @event CONTAINER_TIMEUPDATE + * @param {Object} progress Data + * progress object + * @param {Number} [progress.current] + * current time + * @param {Number} [progress.total] + * total time + */ + Events.CONTAINER_TIMEUPDATE = 'container:timeupdate'; + /** + * Fired when the container is downloading the media + * + * @event CONTAINER_PROGRESS + * @param {Object} progress Data + * progress object + * @param {Number} [progress.start] + * initial downloaded content + * @param {Number} [progress.current] + * current dowloaded content + * @param {Number} [progress.total] + * total content to be downloaded + */ + Events.CONTAINER_PROGRESS = 'container:progress'; + Events.CONTAINER_PLAY = 'container:play'; + Events.CONTAINER_STOP = 'container:stop'; + Events.CONTAINER_PAUSE = 'container:pause'; + Events.CONTAINER_ENDED = 'container:ended'; + Events.CONTAINER_CLICK = 'container:click'; + Events.CONTAINER_DBLCLICK = 'container:dblclick'; + Events.CONTAINER_CONTEXTMENU = 'container:contextmenu'; + Events.CONTAINER_MOUSE_ENTER = 'container:mouseenter'; + Events.CONTAINER_MOUSE_LEAVE = 'container:mouseleave'; + /** + * Fired when the container seeks the video + * + * @event CONTAINER_SEEK + * @param {Number} time the current time in seconds + */ + Events.CONTAINER_SEEK = 'container:seek'; + Events.CONTAINER_VOLUME = 'container:volume'; + Events.CONTAINER_FULLSCREEN = 'container:fullscreen'; + /** + * Fired when container is buffering + * + * @event CONTAINER_STATE_BUFFERING + */ + Events.CONTAINER_STATE_BUFFERING = 'container:state:buffering'; + /** + * Fired when the container filled the buffer + * + * @event CONTAINER_STATE_BUFFERFULL + */ + Events.CONTAINER_STATE_BUFFERFULL = 'container:state:bufferfull'; + /** + * Fired when the container changes any settings (volume, seek and etc) + * + * @event CONTAINER_SETTINGSUPDATE + */ + Events.CONTAINER_SETTINGSUPDATE = 'container:settingsupdate'; + /** + * Fired when container updates its video quality + * + * @event CONTAINER_HIGHDEFINITIONUPDATE + * @param {Boolean} isHD + * true when is on HD, false otherwise + */ + Events.CONTAINER_HIGHDEFINITIONUPDATE = 'container:highdefinitionupdate'; + + /** + * Fired when the media control shows + * + * @event CONTAINER_MEDIACONTROL_SHOW + */ + Events.CONTAINER_MEDIACONTROL_SHOW = 'container:mediacontrol:show'; + /** + * Fired when the media control hides + * + * @event CONTAINER_MEDIACONTROL_HIDE + */ + Events.CONTAINER_MEDIACONTROL_HIDE = 'container:mediacontrol:hide'; + + Events.CONTAINER_MEDIACONTROL_DISABLE = 'container:mediacontrol:disable'; + Events.CONTAINER_MEDIACONTROL_ENABLE = 'container:mediacontrol:enable'; + Events.CONTAINER_STATS_ADD = 'container:stats:add'; + /** + * Fired when the options were changed for the container + * + * @event CONTAINER_OPTIONS_CHANGE + */ + Events.CONTAINER_OPTIONS_CHANGE = 'container:options:change'; + + // MediaControl Events + Events.MEDIACONTROL_RENDERED = 'mediacontrol:rendered'; + /** + * Fired when the player enters/exit on fullscreen + * + * @event MEDIACONTROL_FULLSCREEN + */ + Events.MEDIACONTROL_FULLSCREEN = 'mediacontrol:fullscreen'; + /** + * Fired when the media control shows + * + * @event MEDIACONTROL_SHOW + */ + Events.MEDIACONTROL_SHOW = 'mediacontrol:show'; + /** + * Fired when the media control hides + * + * @event MEDIACONTROL_HIDE + */ + Events.MEDIACONTROL_HIDE = 'mediacontrol:hide'; + /** + * Fired when mouse enters on the seekbar + * + * @event MEDIACONTROL_MOUSEMOVE_SEEKBAR + * @param {Object} event + * the javascript event + */ + Events.MEDIACONTROL_MOUSEMOVE_SEEKBAR = 'mediacontrol:mousemove:seekbar'; + /** + * Fired when mouse leaves the seekbar + * + * @event MEDIACONTROL_MOUSELEAVE_SEEKBAR + * @param {Object} event + * the javascript event + */ + Events.MEDIACONTROL_MOUSELEAVE_SEEKBAR = 'mediacontrol:mouseleave:seekbar'; + /** + * Fired when the media is being played + * + * @event MEDIACONTROL_PLAYING + */ + Events.MEDIACONTROL_PLAYING = 'mediacontrol:playing'; + /** + * Fired when the media is not being played + * + * @event MEDIACONTROL_NOTPLAYING + */ + Events.MEDIACONTROL_NOTPLAYING = 'mediacontrol:notplaying'; + /** + * Fired when the container was changed + * + * @event MEDIACONTROL_CONTAINERCHANGED + */ + Events.MEDIACONTROL_CONTAINERCHANGED = 'mediacontrol:containerchanged'; + + // Core Events + Events.CORE_CONTAINERS_CREATED = 'core:containers:created'; + module.exports = exports['default']; + +/***/ }, +/* 6 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + module.exports = __webpack_require__(7); + +/***/ }, +/* 7 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + var _vendor = __webpack_require__(8); + + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + // Use of this source code is governed by a BSD-style + // license that can be found in the LICENSE file. + + var BOLD = 'font-weight: bold; font-size: 13px;'; + var INFO = 'color: #006600;' + BOLD; + var DEBUG = 'color: #0000ff;' + BOLD; + var WARN = 'color: #ff8000;' + BOLD; + var ERROR = 'color: #ff0000;' + BOLD; + + var LEVEL_DEBUG = 0; + var LEVEL_INFO = 1; + var LEVEL_WARN = 2; + var LEVEL_ERROR = 3; + var LEVEL_DISABLED = LEVEL_ERROR; + + var COLORS = [DEBUG, INFO, WARN, ERROR, ERROR]; + var DESCRIPTIONS = ['debug', 'info', 'warn', 'error', 'disabled']; + + var Log = function () { + function Log() { + var _this = this; + + var level = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : LEVEL_INFO; + var offLevel = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : LEVEL_DISABLED; + + _classCallCheck(this, Log); + + this.kibo = new _vendor.Kibo(); + this.kibo.down(['ctrl shift d'], function () { + return _this.onOff(); + }); + this.BLACKLIST = ['timeupdate', 'playback:timeupdate', 'playback:progress', 'container:hover', 'container:timeupdate', 'container:progress']; + this.level = level; + this.offLevel = offLevel; + } + + Log.prototype.debug = function debug(klass) { + this.log(klass, LEVEL_DEBUG, Array.prototype.slice.call(arguments, 1)); + }; + + Log.prototype.info = function info(klass) { + this.log(klass, LEVEL_INFO, Array.prototype.slice.call(arguments, 1)); + }; + + Log.prototype.warn = function warn(klass) { + this.log(klass, LEVEL_WARN, Array.prototype.slice.call(arguments, 1)); + }; + + Log.prototype.error = function error(klass) { + this.log(klass, LEVEL_ERROR, Array.prototype.slice.call(arguments, 1)); + }; + + Log.prototype.onOff = function onOff() { + if (this.level === this.offLevel) { + this.level = this.previousLevel; + } else { + this.previousLevel = this.level; + this.level = this.offLevel; + } + // handle instances where console.log is unavailable + if (window.console && window.console.log) { + window.console.log('%c[Clappr.Log] set log level to ' + DESCRIPTIONS[this.level], WARN); + } + }; + + Log.prototype.level = function level(newLevel) { + this.level = newLevel; + }; + + Log.prototype.log = function log(klass, level, message) { + if (this.BLACKLIST.indexOf(message[0]) >= 0) return; + if (level < this.level) return; + + if (!message) { + message = klass; + klass = null; + } + var color = COLORS[level]; + var klassDescription = ''; + if (klass) { + klassDescription = '[' + klass + ']'; + } + if (window.console && window.console.log) { + window.console.log.apply(console, ['%c[' + DESCRIPTIONS[level] + ']' + klassDescription, color].concat(message)); + } + }; + + return Log; + }(); + + exports.default = Log; + + + Log.LEVEL_DEBUG = LEVEL_DEBUG; + Log.LEVEL_INFO = LEVEL_INFO; + Log.LEVEL_WARN = LEVEL_WARN; + Log.LEVEL_ERROR = LEVEL_ERROR; + + Log.getInstance = function () { + if (this._instance === undefined) { + this._instance = new this(); + this._instance.previousLevel = this._instance.level; + this._instance.level = this._instance.offLevel; + } + return this._instance; + }; + + Log.setLevel = function (level) { + this.getInstance().level = level; + }; + + Log.debug = function () { + this.getInstance().debug.apply(this.getInstance(), arguments); + }; + Log.info = function () { + this.getInstance().info.apply(this.getInstance(), arguments); + }; + Log.warn = function () { + this.getInstance().warn.apply(this.getInstance(), arguments); + }; + Log.error = function () { + this.getInstance().error.apply(this.getInstance(), arguments); + }; + module.exports = exports['default']; + +/***/ }, +/* 8 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + var _kibo = __webpack_require__(9); + + var _kibo2 = _interopRequireDefault(_kibo); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + exports.default = { Kibo: _kibo2.default }; + module.exports = exports['default']; + +/***/ }, +/* 9 */ +/***/ function(module, exports) { + + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + /* eslint-disable */ + // Kibo is released under the MIT License. Copyright (c) 2013 marquete. + // see https://github.com/marquete/kibo + + var Kibo = function Kibo(element) { + this.element = element || window.document; + this.initialize(); + }; + + Kibo.KEY_NAMES_BY_CODE = { + 8: 'backspace', 9: 'tab', 13: 'enter', + 16: 'shift', 17: 'ctrl', 18: 'alt', + 20: 'caps_lock', + 27: 'esc', + 32: 'space', + 37: 'left', 38: 'up', 39: 'right', 40: 'down', + 48: '0', 49: '1', 50: '2', 51: '3', 52: '4', 53: '5', 54: '6', 55: '7', 56: '8', 57: '9', + 65: 'a', 66: 'b', 67: 'c', 68: 'd', 69: 'e', 70: 'f', 71: 'g', 72: 'h', 73: 'i', 74: 'j', + 75: 'k', 76: 'l', 77: 'm', 78: 'n', 79: 'o', 80: 'p', 81: 'q', 82: 'r', 83: 's', 84: 't', + 85: 'u', 86: 'v', 87: 'w', 88: 'x', 89: 'y', 90: 'z', 112: 'f1', 113: 'f2', 114: 'f3', + 115: 'f4', 116: 'f5', 117: 'f6', 118: 'f7', 119: 'f8', 120: 'f9', 121: 'f10', 122: 'f11', 123: 'f12' + }; + + Kibo.KEY_CODES_BY_NAME = {}; + (function () { + for (var key in Kibo.KEY_NAMES_BY_CODE) { + if (Object.prototype.hasOwnProperty.call(Kibo.KEY_NAMES_BY_CODE, key)) { + Kibo.KEY_CODES_BY_NAME[Kibo.KEY_NAMES_BY_CODE[key]] = +key; + } + } + })(); + + Kibo.MODIFIERS = ['shift', 'ctrl', 'alt']; + + Kibo.registerEvent = function () { + if (document.addEventListener) { + return function (element, eventName, func) { + element.addEventListener(eventName, func, false); + }; + } else if (document.attachEvent) { + return function (element, eventName, func) { + element.attachEvent('on' + eventName, func); + }; + } + }(); + + Kibo.unregisterEvent = function () { + if (document.removeEventListener) { + return function (element, eventName, func) { + element.removeEventListener(eventName, func, false); + }; + } else if (document.detachEvent) { + return function (element, eventName, func) { + element.detachEvent('on' + eventName, func); + }; + } + }(); + + Kibo.stringContains = function (string, substring) { + return string.indexOf(substring) !== -1; + }; + + Kibo.neatString = function (string) { + return string.replace(/^\s+|\s+$/g, '').replace(/\s+/g, ' '); + }; + + Kibo.capitalize = function (string) { + return string.toLowerCase().replace(/^./, function (match) { + return match.toUpperCase(); + }); + }; + + Kibo.isString = function (what) { + return Kibo.stringContains(Object.prototype.toString.call(what), 'String'); + }; + + Kibo.arrayIncludes = function () { + if (Array.prototype.indexOf) { + return function (haystack, needle) { + return haystack.indexOf(needle) !== -1; + }; + } else { + return function (haystack, needle) { + for (var i = 0; i < haystack.length; i++) { + if (haystack[i] === needle) { + return true; + } + } + return false; + }; + } + }(); + + Kibo.extractModifiers = function (keyCombination) { + var modifiers, i; + modifiers = []; + for (i = 0; i < Kibo.MODIFIERS.length; i++) { + if (Kibo.stringContains(keyCombination, Kibo.MODIFIERS[i])) { + modifiers.push(Kibo.MODIFIERS[i]); + } + } + return modifiers; + }; + + Kibo.extractKey = function (keyCombination) { + var keys, i; + keys = Kibo.neatString(keyCombination).split(' '); + for (i = 0; i < keys.length; i++) { + if (!Kibo.arrayIncludes(Kibo.MODIFIERS, keys[i])) { + return keys[i]; + } + } + }; + + Kibo.modifiersAndKey = function (keyCombination) { + var result, key; + + if (Kibo.stringContains(keyCombination, 'any')) { + return Kibo.neatString(keyCombination).split(' ').slice(0, 2).join(' '); + } + + result = Kibo.extractModifiers(keyCombination); + + key = Kibo.extractKey(keyCombination); + if (key && !Kibo.arrayIncludes(Kibo.MODIFIERS, key)) { + result.push(key); + } + + return result.join(' '); + }; + + Kibo.keyName = function (keyCode) { + return Kibo.KEY_NAMES_BY_CODE[keyCode + '']; + }; + + Kibo.keyCode = function (keyName) { + return +Kibo.KEY_CODES_BY_NAME[keyName]; + }; + + Kibo.prototype.initialize = function () { + var i, + that = this; + + this.lastKeyCode = -1; + this.lastModifiers = {}; + for (i = 0; i < Kibo.MODIFIERS.length; i++) { + this.lastModifiers[Kibo.MODIFIERS[i]] = false; + } + + this.keysDown = { any: [] }; + this.keysUp = { any: [] }; + this.downHandler = this.handler('down'); + this.upHandler = this.handler('up'); + + Kibo.registerEvent(this.element, 'keydown', this.downHandler); + Kibo.registerEvent(this.element, 'keyup', this.upHandler); + Kibo.registerEvent(window, 'unload', function unloader() { + Kibo.unregisterEvent(that.element, 'keydown', that.downHandler); + Kibo.unregisterEvent(that.element, 'keyup', that.upHandler); + Kibo.unregisterEvent(window, 'unload', unloader); + }); + }; + + Kibo.prototype.handler = function (upOrDown) { + var that = this; + return function (e) { + var i, registeredKeys, lastModifiersAndKey; + + e = e || window.event; + + that.lastKeyCode = e.keyCode; + for (i = 0; i < Kibo.MODIFIERS.length; i++) { + that.lastModifiers[Kibo.MODIFIERS[i]] = e[Kibo.MODIFIERS[i] + 'Key']; + } + if (Kibo.arrayIncludes(Kibo.MODIFIERS, Kibo.keyName(that.lastKeyCode))) { + that.lastModifiers[Kibo.keyName(that.lastKeyCode)] = true; + } + + registeredKeys = that['keys' + Kibo.capitalize(upOrDown)]; + + for (i = 0; i < registeredKeys.any.length; i++) { + if (registeredKeys.any[i](e) === false && e.preventDefault) { + e.preventDefault(); + } + } + + lastModifiersAndKey = that.lastModifiersAndKey(); + if (registeredKeys[lastModifiersAndKey]) { + for (i = 0; i < registeredKeys[lastModifiersAndKey].length; i++) { + if (registeredKeys[lastModifiersAndKey][i](e) === false && e.preventDefault) { + e.preventDefault(); + } + } + } + }; + }; + + Kibo.prototype.registerKeys = function (upOrDown, newKeys, func) { + var i, + keys, + registeredKeys = this['keys' + Kibo.capitalize(upOrDown)]; + + if (Kibo.isString(newKeys)) { + newKeys = [newKeys]; + } + + for (i = 0; i < newKeys.length; i++) { + keys = newKeys[i]; + keys = Kibo.modifiersAndKey(keys + ''); + + if (registeredKeys[keys]) { + registeredKeys[keys].push(func); + } else { + registeredKeys[keys] = [func]; + } + } + + return this; + }; + + // jshint maxdepth:5 + Kibo.prototype.unregisterKeys = function (upOrDown, newKeys, func) { + var i, + j, + keys, + registeredKeys = this['keys' + Kibo.capitalize(upOrDown)]; + + if (Kibo.isString(newKeys)) { + newKeys = [newKeys]; + } + + for (i = 0; i < newKeys.length; i++) { + keys = newKeys[i]; + keys = Kibo.modifiersAndKey(keys + ''); + + if (func === null) { + delete registeredKeys[keys]; + } else { + if (registeredKeys[keys]) { + for (j = 0; j < registeredKeys[keys].length; j++) { + if (String(registeredKeys[keys][j]) === String(func)) { + registeredKeys[keys].splice(j, 1); + break; + } + } + } + } + } + + return this; + }; + + Kibo.prototype.off = function (keys) { + return this.unregisterKeys('down', keys, null); + }; + + Kibo.prototype.delegate = function (upOrDown, keys, func) { + return func !== null || func !== undefined ? this.registerKeys(upOrDown, keys, func) : this.unregisterKeys(upOrDown, keys, func); + }; + + Kibo.prototype.down = function (keys, func) { + return this.delegate('down', keys, func); + }; + + Kibo.prototype.up = function (keys, func) { + return this.delegate('up', keys, func); + }; + + Kibo.prototype.lastKey = function (modifier) { + if (!modifier) { + return Kibo.keyName(this.lastKeyCode); + } + + return this.lastModifiers[modifier]; + }; + + Kibo.prototype.lastModifiersAndKey = function () { + var result, i; + + result = []; + for (i = 0; i < Kibo.MODIFIERS.length; i++) { + if (this.lastKey(Kibo.MODIFIERS[i])) { + result.push(Kibo.MODIFIERS[i]); + } + } + + if (!Kibo.arrayIncludes(result, this.lastKey())) { + result.push(this.lastKey()); + } + + return result.join(' '); + }; + + exports.default = Kibo; + module.exports = exports['default']; + +/***/ }, +/* 10 */ +/***/ function(module, exports) { + + /** + * lodash (Custom Build) <https://lodash.com/> + * Build: `lodash modularize exports="npm" -o ./` + * Copyright jQuery Foundation and other contributors <https://jquery.org/> + * Released under MIT license <https://lodash.com/license> + * Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE> + * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors + */ + + /** Used as the `TypeError` message for "Functions" methods. */ + var FUNC_ERROR_TEXT = 'Expected a function'; + + /** Used as references for various `Number` constants. */ + var INFINITY = 1 / 0, + MAX_INTEGER = 1.7976931348623157e+308, + NAN = 0 / 0; + + /** `Object#toString` result references. */ + var symbolTag = '[object Symbol]'; + + /** Used to match leading and trailing whitespace. */ + var reTrim = /^\s+|\s+$/g; + + /** Used to detect bad signed hexadecimal string values. */ + var reIsBadHex = /^[-+]0x[0-9a-f]+$/i; + + /** Used to detect binary string values. */ + var reIsBinary = /^0b[01]+$/i; + + /** Used to detect octal string values. */ + var reIsOctal = /^0o[0-7]+$/i; + + /** Built-in method references without a dependency on `root`. */ + var freeParseInt = parseInt; + + /** Used for built-in method references. */ + var objectProto = Object.prototype; + + /** + * Used to resolve the + * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring) + * of values. + */ + var objectToString = objectProto.toString; + + /** + * Creates a function that invokes `func`, with the `this` binding and arguments + * of the created function, while it's called less than `n` times. Subsequent + * calls to the created function return the result of the last `func` invocation. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Function + * @param {number} n The number of calls at which `func` is no longer invoked. + * @param {Function} func The function to restrict. + * @returns {Function} Returns the new restricted function. + * @example + * + * jQuery(element).on('click', _.before(5, addContactToList)); + * // => Allows adding up to 4 contacts to the list. + */ + function before(n, func) { + var result; + if (typeof func != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + n = toInteger(n); + return function() { + if (--n > 0) { + result = func.apply(this, arguments); + } + if (n <= 1) { + func = undefined; + } + return result; + }; + } + + /** + * Creates a function that is restricted to invoking `func` once. Repeat calls + * to the function return the value of the first invocation. The `func` is + * invoked with the `this` binding and arguments of the created function. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Function + * @param {Function} func The function to restrict. + * @returns {Function} Returns the new restricted function. + * @example + * + * var initialize = _.once(createApplication); + * initialize(); + * initialize(); + * // => `createApplication` is invoked once + */ + function once(func) { + return before(2, func); + } + + /** + * Checks if `value` is the + * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types) + * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an object, else `false`. + * @example + * + * _.isObject({}); + * // => true + * + * _.isObject([1, 2, 3]); + * // => true + * + * _.isObject(_.noop); + * // => true + * + * _.isObject(null); + * // => false + */ + function isObject(value) { + var type = typeof value; + return !!value && (type == 'object' || type == 'function'); + } + + /** + * Checks if `value` is object-like. A value is object-like if it's not `null` + * and has a `typeof` result of "object". + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is object-like, else `false`. + * @example + * + * _.isObjectLike({}); + * // => true + * + * _.isObjectLike([1, 2, 3]); + * // => true + * + * _.isObjectLike(_.noop); + * // => false + * + * _.isObjectLike(null); + * // => false + */ + function isObjectLike(value) { + return !!value && typeof value == 'object'; + } + + /** + * Checks if `value` is classified as a `Symbol` primitive or object. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a symbol, else `false`. + * @example + * + * _.isSymbol(Symbol.iterator); + * // => true + * + * _.isSymbol('abc'); + * // => false + */ + function isSymbol(value) { + return typeof value == 'symbol' || + (isObjectLike(value) && objectToString.call(value) == symbolTag); + } + + /** + * Converts `value` to a finite number. + * + * @static + * @memberOf _ + * @since 4.12.0 + * @category Lang + * @param {*} value The value to convert. + * @returns {number} Returns the converted number. + * @example + * + * _.toFinite(3.2); + * // => 3.2 + * + * _.toFinite(Number.MIN_VALUE); + * // => 5e-324 + * + * _.toFinite(Infinity); + * // => 1.7976931348623157e+308 + * + * _.toFinite('3.2'); + * // => 3.2 + */ + function toFinite(value) { + if (!value) { + return value === 0 ? value : 0; + } + value = toNumber(value); + if (value === INFINITY || value === -INFINITY) { + var sign = (value < 0 ? -1 : 1); + return sign * MAX_INTEGER; + } + return value === value ? value : 0; + } + + /** + * Converts `value` to an integer. + * + * **Note:** This method is loosely based on + * [`ToInteger`](http://www.ecma-international.org/ecma-262/7.0/#sec-tointeger). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to convert. + * @returns {number} Returns the converted integer. + * @example + * + * _.toInteger(3.2); + * // => 3 + * + * _.toInteger(Number.MIN_VALUE); + * // => 0 + * + * _.toInteger(Infinity); + * // => 1.7976931348623157e+308 + * + * _.toInteger('3.2'); + * // => 3 + */ + function toInteger(value) { + var result = toFinite(value), + remainder = result % 1; + + return result === result ? (remainder ? result - remainder : result) : 0; + } + + /** + * Converts `value` to a number. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to process. + * @returns {number} Returns the number. + * @example + * + * _.toNumber(3.2); + * // => 3.2 + * + * _.toNumber(Number.MIN_VALUE); + * // => 5e-324 + * + * _.toNumber(Infinity); + * // => Infinity + * + * _.toNumber('3.2'); + * // => 3.2 + */ + function toNumber(value) { + if (typeof value == 'number') { + return value; + } + if (isSymbol(value)) { + return NAN; + } + if (isObject(value)) { + var other = typeof value.valueOf == 'function' ? value.valueOf() : value; + value = isObject(other) ? (other + '') : other; + } + if (typeof value != 'string') { + return value === 0 ? value : +value; + } + value = value.replace(reTrim, ''); + var isBinary = reIsBinary.test(value); + return (isBinary || reIsOctal.test(value)) + ? freeParseInt(value.slice(2), isBinary ? 2 : 8) + : (reIsBadHex.test(value) ? NAN : +value); + } + + module.exports = once; + + +/***/ }, +/* 11 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + module.exports = __webpack_require__(12); + +/***/ }, +/* 12 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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 _base_object = __webpack_require__(4); + + var _base_object2 = _interopRequireDefault(_base_object); + + var _core = __webpack_require__(13); + + var _core2 = _interopRequireDefault(_core); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + 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; } // Copyright 2014 Globo.com Player authors. All rights reserved. + // Use of this source code is governed by a BSD-style + // license that can be found in the LICENSE file. + + /** + * The Core Factory is responsible for instantiate the core and it's plugins. + * @class CoreFactory + * @constructor + * @extends BaseObject + * @module components + */ + var CoreFactory = function (_BaseObject) { + _inherits(CoreFactory, _BaseObject); + + _createClass(CoreFactory, [{ + key: 'loader', + get: function get() { + return this.player.loader; + } + + /** + * it builds the core factory + * @method constructor + * @param {Player} player the player object + */ + + }]); + + function CoreFactory(player) { + _classCallCheck(this, CoreFactory); + + var _this = _possibleConstructorReturn(this, _BaseObject.call(this)); + + _this.player = player; + _this._options = player.options; + return _this; + } + + /** + * creates a core and its plugins + * @method create + * @return {Core} created core + */ + + + CoreFactory.prototype.create = function create() { + this.options.loader = this.loader; + this.core = new _core2.default(this.options); + this.addCorePlugins(); + this.core.createContainers(this.options); + return this.core; + }; + + /** + * given the core plugins (`loader.corePlugins`) it builds each one + * @method addCorePlugins + * @return {Core} the core with all plugins + */ + + + CoreFactory.prototype.addCorePlugins = function addCorePlugins() { + var _this2 = this; + + this.loader.corePlugins.forEach(function (Plugin) { + var plugin = new Plugin(_this2.core); + _this2.core.addPlugin(plugin); + _this2.setupExternalInterface(plugin); + }); + return this.core; + }; + + CoreFactory.prototype.setupExternalInterface = function setupExternalInterface(plugin) { + var externalFunctions = plugin.getExternalInterface(); + for (var key in externalFunctions) { + this.player[key] = externalFunctions[key].bind(plugin); + } + }; + + return CoreFactory; + }(_base_object2.default); + + exports.default = CoreFactory; + module.exports = exports['default']; + +/***/ }, +/* 13 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + module.exports = __webpack_require__(14); + +/***/ }, +/* 14 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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 _utils = __webpack_require__(2); + + var _events = __webpack_require__(5); + + var _events2 = _interopRequireDefault(_events); + + var _styler = __webpack_require__(15); + + var _styler2 = _interopRequireDefault(_styler); + + var _ui_object = __webpack_require__(18); + + var _ui_object2 = _interopRequireDefault(_ui_object); + + var _browser = __webpack_require__(3); + + var _browser2 = _interopRequireDefault(_browser); + + var _container_factory = __webpack_require__(20); + + var _container_factory2 = _interopRequireDefault(_container_factory); + + var _media_control = __webpack_require__(27); + + var _media_control2 = _interopRequireDefault(_media_control); + + var _mediator = __webpack_require__(30); + + var _mediator2 = _interopRequireDefault(_mediator); + + var _player_info = __webpack_require__(45); + + var _player_info2 = _interopRequireDefault(_player_info); + + var _clapprZepto = __webpack_require__(16); + + var _clapprZepto2 = _interopRequireDefault(_clapprZepto); + + var _style = __webpack_require__(46); + + var _style2 = _interopRequireDefault(_style); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + 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; } // Copyright 2014 Globo.com Player authors. All rights reserved. + // Use of this source code is governed by a BSD-style + // license that can be found in the LICENSE file. + + /** + * The Core is responsible to manage Containers, the mediator, MediaControl + * and the player state. + * @class Core + * @constructor + * @extends UIObject + * @module components + */ + var Core = function (_UIObject) { + _inherits(Core, _UIObject); + + _createClass(Core, [{ + key: 'events', + get: function get() { + return { + 'webkitfullscreenchange': 'handleFullscreenChange', + 'mousemove': 'showMediaControl', + 'mouseleave': 'hideMediaControl' + }; + } + }, { + key: 'attributes', + get: function get() { + return { + 'data-player': '', + tabindex: 9999 + }; + } + + /** + * checks if the core is ready. + * @property isReady + * @type {Boolean} `true` if the core is ready, otherwise `false` + */ + + }, { + key: 'isReady', + get: function get() { + return !!this.ready; + } + + /** + * The internationalization plugin. + * @property i18n + * @type {Strings} + */ + + }, { + key: 'i18n', + get: function get() { + return this.getPlugin('strings') || { t: function t(key) { + return key; + } }; + } + }]); + + function Core(options) { + _classCallCheck(this, Core); + + var _this = _possibleConstructorReturn(this, _UIObject.call(this, options)); + + _this.playerInfo = _player_info2.default.getInstance(options.playerId); + _this.firstResize = true; + _this.plugins = []; + _this.containers = []; + _this.setupMediaControl(null); + //FIXME fullscreen api sucks + _this._boundFullscreenHandler = function () { + return _this.handleFullscreenChange(); + }; + (0, _clapprZepto2.default)(document).bind('fullscreenchange', _this._boundFullscreenHandler); + (0, _clapprZepto2.default)(document).bind('MSFullscreenChange', _this._boundFullscreenHandler); + (0, _clapprZepto2.default)(document).bind('mozfullscreenchange', _this._boundFullscreenHandler); + return _this; + } + + Core.prototype.createContainers = function createContainers(options) { + var _this2 = this; + + this.defer = _clapprZepto2.default.Deferred(); + this.defer.promise(this); + this.containerFactory = new _container_factory2.default(options, options.loader, this.i18n); + this.containerFactory.createContainers().then(function (containers) { + return _this2.setupContainers(containers); + }).then(function (containers) { + return _this2.resolveOnContainersReady(containers); + }); + }; + + Core.prototype.updateSize = function updateSize() { + if (_utils.Fullscreen.isFullscreen()) { + this.setFullscreen(); + } else { + this.setPlayerSize(); + } + }; + + Core.prototype.setFullscreen = function setFullscreen() { + if (!_browser2.default.isiOS) { + this.$el.addClass('fullscreen'); + this.$el.removeAttr('style'); + this.playerInfo.previousSize = { width: this.options.width, height: this.options.height }; + this.playerInfo.currentSize = { width: (0, _clapprZepto2.default)(window).width(), height: (0, _clapprZepto2.default)(window).height() }; + } + }; + + Core.prototype.setPlayerSize = function setPlayerSize() { + this.$el.removeClass('fullscreen'); + this.playerInfo.currentSize = this.playerInfo.previousSize; + this.playerInfo.previousSize = { width: (0, _clapprZepto2.default)(window).width(), height: (0, _clapprZepto2.default)(window).height() }; + this.resize(this.playerInfo.currentSize); + }; + + Core.prototype.resize = function resize(options) { + if (!(0, _utils.isNumber)(options.height) && !(0, _utils.isNumber)(options.width)) { + this.el.style.height = '' + options.height; + this.el.style.width = '' + options.width; + } else { + this.el.style.height = options.height + 'px'; + this.el.style.width = options.width + 'px'; + } + this.playerInfo.previousSize = { width: this.options.width, height: this.options.height }; + this.options.width = options.width; + this.options.height = options.height; + this.playerInfo.currentSize = options; + this.triggerResize(this.playerInfo.currentSize); + }; + + Core.prototype.enableResizeObserver = function enableResizeObserver() { + var _this3 = this; + + var checkSizeCallback = function checkSizeCallback() { + if (_this3.playerInfo.computedSize.width != _this3.el.clientWidth || _this3.playerInfo.computedSize.height != _this3.el.clientHeight) { + _this3.playerInfo.computedSize = { width: _this3.el.clientWidth, height: _this3.el.clientHeight }; + _this3.triggerResize(_this3.playerInfo.computedSize); + } + }; + this.resizeObserverInterval = setInterval(checkSizeCallback, 500); + }; + + Core.prototype.triggerResize = function triggerResize(newSize) { + var thereWasChange = this.firstResize || this.oldHeight !== newSize.height || this.oldWidth !== newSize.width; + if (thereWasChange) { + _mediator2.default.trigger(this.options.playerId + ':' + _events2.default.PLAYER_RESIZE, newSize); + this.oldHeight = newSize.height; + this.oldWidth = newSize.width; + this.firstResize = false; + } + }; + + Core.prototype.disableResizeObserver = function disableResizeObserver() { + if (this.resizeObserverInterval) clearInterval(this.resizeObserverInterval); + }; + + Core.prototype.resolveOnContainersReady = function resolveOnContainersReady(containers) { + var _this4 = this; + + _clapprZepto2.default.when.apply(_clapprZepto2.default, containers).done(function () { + _this4.defer.resolve(_this4); + _this4.ready = true; + _this4.trigger(_events2.default.CORE_READY); + }); + }; + + Core.prototype.addPlugin = function addPlugin(plugin) { + this.plugins.push(plugin); + }; + + Core.prototype.hasPlugin = function hasPlugin(name) { + return !!this.getPlugin(name); + }; + + Core.prototype.getPlugin = function getPlugin(name) { + return this.plugins.filter(function (plugin) { + return plugin.name === name; + })[0]; + }; + + Core.prototype.load = function load(sources, mimeType) { + var _this5 = this; + + this.options.mimeType = mimeType; + sources = sources && sources.constructor === Array ? sources : [sources]; + this.containers.forEach(function (container) { + return container.destroy(); + }); + this.mediaControl.container = null; + this.containerFactory.options = _clapprZepto2.default.extend(this.options, { sources: sources }); + this.containerFactory.createContainers().then(function (containers) { + _this5.setupContainers(containers); + }); + }; + + Core.prototype.destroy = function destroy() { + this.disableResizeObserver(); + this.containers.forEach(function (container) { + return container.destroy(); + }); + this.plugins.forEach(function (plugin) { + return plugin.destroy(); + }); + this.$el.remove(); + this.mediaControl.destroy(); + (0, _clapprZepto2.default)(document).unbind('fullscreenchange', this._boundFullscreenHandler); + (0, _clapprZepto2.default)(document).unbind('MSFullscreenChange', this._boundFullscreenHandler); + (0, _clapprZepto2.default)(document).unbind('mozfullscreenchange', this._boundFullscreenHandler); + }; + + Core.prototype.handleFullscreenChange = function handleFullscreenChange() { + this.trigger(_events2.default.CORE_FULLSCREEN, _utils.Fullscreen.isFullscreen()); + this.updateSize(); + this.mediaControl.show(); + }; + + Core.prototype.setMediaControlContainer = function setMediaControlContainer(container) { + this.mediaControl.setContainer(container); + this.mediaControl.render(); + }; + + Core.prototype.disableMediaControl = function disableMediaControl() { + this.mediaControl.disable(); + this.$el.removeClass('nocursor'); + }; + + Core.prototype.enableMediaControl = function enableMediaControl() { + this.mediaControl.enable(); + }; + + Core.prototype.removeContainer = function removeContainer(container) { + this.stopListening(container); + this.containers = this.containers.filter(function (c) { + return c !== container; + }); + }; + + Core.prototype.appendContainer = function appendContainer(container) { + this.listenTo(container, _events2.default.CONTAINER_DESTROYED, this.removeContainer); + this.containers.push(container); + }; + + Core.prototype.setupContainers = function setupContainers(containers) { + containers.map(this.appendContainer.bind(this)); + this.trigger(_events2.default.CORE_CONTAINERS_CREATED); + this.renderContainers(); + this.setupMediaControl(this.getCurrentContainer()); + this.render(); + this.$el.appendTo(this.options.parentElement); + return this.containers; + }; + + Core.prototype.renderContainers = function renderContainers() { + var _this6 = this; + + this.containers.map(function (container) { + return _this6.el.appendChild(container.render().el); + }); + }; + + Core.prototype.createContainer = function createContainer(source, options) { + var container = this.containerFactory.createContainer(source, options); + this.appendContainer(container); + this.el.appendChild(container.render().el); + return container; + }; + + Core.prototype.setupMediaControl = function setupMediaControl(container) { + if (this.mediaControl) { + this.mediaControl.setContainer(container); + } else { + this.mediaControl = this.createMediaControl(_clapprZepto2.default.extend({ container: container, focusElement: this.el }, this.options)); + this.listenTo(this.mediaControl, _events2.default.MEDIACONTROL_FULLSCREEN, this.toggleFullscreen); + this.listenTo(this.mediaControl, _events2.default.MEDIACONTROL_SHOW, this.onMediaControlShow.bind(this, true)); + this.listenTo(this.mediaControl, _events2.default.MEDIACONTROL_HIDE, this.onMediaControlShow.bind(this, false)); + } + }; + + Core.prototype.createMediaControl = function createMediaControl(options) { + if (options.mediacontrol && options.mediacontrol.external) { + return new options.mediacontrol.external(options).render(); + } else { + return new _media_control2.default(options).render(); + } + }; + + Core.prototype.getCurrentContainer = function getCurrentContainer() { + if (!this.mediaControl || !this.mediaControl.container) { + return this.containers[0]; + } + return this.mediaControl.container; + }; + + Core.prototype.getCurrentPlayback = function getCurrentPlayback() { + var container = this.getCurrentContainer(); + return container && container.playback; + }; + + Core.prototype.getPlaybackType = function getPlaybackType() { + var container = this.getCurrentContainer(); + return container && container.getPlaybackType(); + }; + + Core.prototype.toggleFullscreen = function toggleFullscreen() { + if (!_utils.Fullscreen.isFullscreen()) { + _utils.Fullscreen.requestFullscreen(this.el); + if (!_browser2.default.isiOS) { + this.$el.addClass('fullscreen'); + } + } else { + _utils.Fullscreen.cancelFullscreen(); + if (!_browser2.default.isiOS) { + this.$el.removeClass('fullscreen nocursor'); + } + } + this.mediaControl.show(); + }; + + Core.prototype.showMediaControl = function showMediaControl(event) { + this.mediaControl.show(event); + }; + + Core.prototype.hideMediaControl = function hideMediaControl() { + this.mediaControl.hide(this.options.hideMediaControlDelay); + }; + + Core.prototype.onMediaControlShow = function onMediaControlShow(showing) { + this.getCurrentContainer().trigger(showing ? _events2.default.CONTAINER_MEDIACONTROL_SHOW : _events2.default.CONTAINER_MEDIACONTROL_HIDE); + + if (showing) this.$el.removeClass('nocursor');else if (_utils.Fullscreen.isFullscreen()) this.$el.addClass('nocursor'); + }; + + /** + * enables to configure the container after its creation + * @method configure + * @param {Object} options all the options to change in form of a javascript object + */ + + + Core.prototype.configure = function configure(options) { + var _this7 = this; + + this._options = _clapprZepto2.default.extend(this._options, options); + var sources = options.source || options.sources; + + if (sources) { + this.load(sources, options.mimeType || this.options.mimeType); + } else { + this.trigger(_events2.default.CORE_OPTIONS_CHANGE); + + this.containers.forEach(function (container) { + container.configure(_this7.options); + }); + } + }; + + Core.prototype.render = function render() { + var style = _styler2.default.getStyleFor(_style2.default, { baseUrl: this.options.baseUrl }); + this.$el.append(style); + this.$el.append(this.mediaControl.render().el); + + this.options.width = this.options.width || this.$el.width(); + this.options.height = this.options.height || this.$el.height(); + var size = { width: this.options.width, height: this.options.height }; + this.playerInfo.previousSize = this.playerInfo.currentSize = this.playerInfo.computedSize = size; + this.updateSize(); + + this.previousSize = { width: this.$el.width(), height: this.$el.height() }; + + this.enableResizeObserver(); + + return this; + }; + + return Core; + }(_ui_object2.default); + + exports.default = Core; + module.exports = exports['default']; + +/***/ }, +/* 15 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + var _clapprZepto = __webpack_require__(16); + + var _clapprZepto2 = _interopRequireDefault(_clapprZepto); + + var _template = __webpack_require__(17); + + var _template2 = _interopRequireDefault(_template); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + // Copyright 2014 Globo.com Player authors. All rights reserved. + // Use of this source code is governed by a BSD-style + // license that can be found in the LICENSE file. + + var Styler = { + getStyleFor: function getStyleFor(style) { + var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : { baseUrl: '' }; + + return (0, _clapprZepto2.default)('<style class="clappr-style"></style>').html((0, _template2.default)(style.toString())(options)); + } + }; + + exports.default = Styler; + module.exports = exports['default']; + +/***/ }, +/* 16 */ +/***/ function(module, exports) { + + /* Zepto v1.1.4-80-ga9184b2 - zepto event ajax callbacks deferred touch selector ie - zeptojs.com/license */ + var Zepto=function(){function D(t){return null==t?String(t):j[S.call(t)]||"object"}function L(t){return"function"==D(t)}function k(t){return null!=t&&t==t.window}function Z(t){return null!=t&&t.nodeType==t.DOCUMENT_NODE}function $(t){return"object"==D(t)}function F(t){return $(t)&&!k(t)&&Object.getPrototypeOf(t)==Object.prototype}function R(t){return"number"==typeof t.length}function q(t){return s.call(t,function(t){return null!=t})}function W(t){return t.length>0?n.fn.concat.apply([],t):t}function z(t){return t.replace(/::/g,"/").replace(/([A-Z]+)([A-Z][a-z])/g,"$1_$2").replace(/([a-z\d])([A-Z])/g,"$1_$2").replace(/_/g,"-").toLowerCase()}function H(t){return t in c?c[t]:c[t]=new RegExp("(^|\\s)"+t+"(\\s|$)")}function _(t,e){return"number"!=typeof e||l[z(t)]?e:e+"px"}function I(t){var e,n;return f[t]||(e=u.createElement(t),u.body.appendChild(e),n=getComputedStyle(e,"").getPropertyValue("display"),e.parentNode.removeChild(e),"none"==n&&(n="block"),f[t]=n),f[t]}function U(t){return"children"in t?a.call(t.children):n.map(t.childNodes,function(t){return 1==t.nodeType?t:void 0})}function X(t,e){var n,i=t?t.length:0;for(n=0;i>n;n++)this[n]=t[n];this.length=i,this.selector=e||""}function B(n,i,r){for(e in i)r&&(F(i[e])||A(i[e]))?(F(i[e])&&!F(n[e])&&(n[e]={}),A(i[e])&&!A(n[e])&&(n[e]=[]),B(n[e],i[e],r)):i[e]!==t&&(n[e]=i[e])}function V(t,e){return null==e?n(t):n(t).filter(e)}function Y(t,e,n,i){return L(e)?e.call(t,n,i):e}function J(t,e,n){null==n?t.removeAttribute(e):t.setAttribute(e,n)}function G(e,n){var i=e.className||"",r=i&&i.baseVal!==t;return n===t?r?i.baseVal:i:void(r?i.baseVal=n:e.className=n)}function K(t){try{return t?"true"==t||("false"==t?!1:"null"==t?null:+t+""==t?+t:/^[\[\{]/.test(t)?n.parseJSON(t):t):t}catch(e){return t}}function Q(t,e){e(t);for(var n=0,i=t.childNodes.length;i>n;n++)Q(t.childNodes[n],e)}var t,e,n,i,N,P,r=[],o=r.concat,s=r.filter,a=r.slice,u=window.document,f={},c={},l={"column-count":1,columns:1,"font-weight":1,"line-height":1,opacity:1,"z-index":1,zoom:1},h=/^\s*<(\w+|!)[^>]*>/,p=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,d=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,m=/^(?:body|html)$/i,g=/([A-Z])/g,v=["val","css","html","text","data","width","height","offset"],y=["after","prepend","before","append"],w=u.createElement("table"),x=u.createElement("tr"),b={tr:u.createElement("tbody"),tbody:w,thead:w,tfoot:w,td:x,th:x,"*":u.createElement("div")},E=/complete|loaded|interactive/,T=/^[\w-]*$/,j={},S=j.toString,C={},O=u.createElement("div"),M={tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},A=Array.isArray||function(t){return t instanceof Array};return C.matches=function(t,e){if(!e||!t||1!==t.nodeType)return!1;var n=t.webkitMatchesSelector||t.mozMatchesSelector||t.oMatchesSelector||t.matchesSelector;if(n)return n.call(t,e);var i,r=t.parentNode,o=!r;return o&&(r=O).appendChild(t),i=~C.qsa(r,e).indexOf(t),o&&O.removeChild(t),i},N=function(t){return t.replace(/-+(.)?/g,function(t,e){return e?e.toUpperCase():""})},P=function(t){return s.call(t,function(e,n){return t.indexOf(e)==n})},C.fragment=function(e,i,r){var o,s,f;return p.test(e)&&(o=n(u.createElement(RegExp.$1))),o||(e.replace&&(e=e.replace(d,"<$1></$2>")),i===t&&(i=h.test(e)&&RegExp.$1),i in b||(i="*"),f=b[i],f.innerHTML=""+e,o=n.each(a.call(f.childNodes),function(){f.removeChild(this)})),F(r)&&(s=n(o),n.each(r,function(t,e){v.indexOf(t)>-1?s[t](e):s.attr(t,e)})),o},C.Z=function(t,e){return new X(t,e)},C.isZ=function(t){return t instanceof C.Z},C.init=function(e,i){var r;if(!e)return C.Z();if("string"==typeof e)if(e=e.trim(),"<"==e[0]&&h.test(e))r=C.fragment(e,RegExp.$1,i),e=null;else{if(i!==t)return n(i).find(e);r=C.qsa(u,e)}else{if(L(e))return n(u).ready(e);if(C.isZ(e))return e;if(A(e))r=q(e);else if($(e))r=[e],e=null;else if(h.test(e))r=C.fragment(e.trim(),RegExp.$1,i),e=null;else{if(i!==t)return n(i).find(e);r=C.qsa(u,e)}}return C.Z(r,e)},n=function(t,e){return C.init(t,e)},n.extend=function(t){var e,n=a.call(arguments,1);return"boolean"==typeof t&&(e=t,t=n.shift()),n.forEach(function(n){B(t,n,e)}),t},C.qsa=function(t,e){var n,i="#"==e[0],r=!i&&"."==e[0],o=i||r?e.slice(1):e,s=T.test(o);return t.getElementById&&s&&i?(n=t.getElementById(o))?[n]:[]:1!==t.nodeType&&9!==t.nodeType&&11!==t.nodeType?[]:a.call(s&&!i&&t.getElementsByClassName?r?t.getElementsByClassName(o):t.getElementsByTagName(e):t.querySelectorAll(e))},n.contains=u.documentElement.contains?function(t,e){return t!==e&&t.contains(e)}:function(t,e){for(;e&&(e=e.parentNode);)if(e===t)return!0;return!1},n.type=D,n.isFunction=L,n.isWindow=k,n.isArray=A,n.isPlainObject=F,n.isEmptyObject=function(t){var e;for(e in t)return!1;return!0},n.inArray=function(t,e,n){return r.indexOf.call(e,t,n)},n.camelCase=N,n.trim=function(t){return null==t?"":String.prototype.trim.call(t)},n.uuid=0,n.support={},n.expr={},n.noop=function(){},n.map=function(t,e){var n,r,o,i=[];if(R(t))for(r=0;r<t.length;r++)n=e(t[r],r),null!=n&&i.push(n);else for(o in t)n=e(t[o],o),null!=n&&i.push(n);return W(i)},n.each=function(t,e){var n,i;if(R(t)){for(n=0;n<t.length;n++)if(e.call(t[n],n,t[n])===!1)return t}else for(i in t)if(e.call(t[i],i,t[i])===!1)return t;return t},n.grep=function(t,e){return s.call(t,e)},window.JSON&&(n.parseJSON=JSON.parse),n.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(t,e){j["[object "+e+"]"]=e.toLowerCase()}),n.fn={constructor:C.Z,length:0,forEach:r.forEach,reduce:r.reduce,push:r.push,sort:r.sort,splice:r.splice,indexOf:r.indexOf,concat:function(){var t,e,n=[];for(t=0;t<arguments.length;t++)e=arguments[t],n[t]=C.isZ(e)?e.toArray():e;return o.apply(C.isZ(this)?this.toArray():this,n)},map:function(t){return n(n.map(this,function(e,n){return t.call(e,n,e)}))},slice:function(){return n(a.apply(this,arguments))},ready:function(t){return E.test(u.readyState)&&u.body?t(n):u.addEventListener("DOMContentLoaded",function(){t(n)},!1),this},get:function(e){return e===t?a.call(this):this[e>=0?e:e+this.length]},toArray:function(){return this.get()},size:function(){return this.length},remove:function(){return this.each(function(){null!=this.parentNode&&this.parentNode.removeChild(this)})},each:function(t){return r.every.call(this,function(e,n){return t.call(e,n,e)!==!1}),this},filter:function(t){return L(t)?this.not(this.not(t)):n(s.call(this,function(e){return C.matches(e,t)}))},add:function(t,e){return n(P(this.concat(n(t,e))))},is:function(t){return this.length>0&&C.matches(this[0],t)},not:function(e){var i=[];if(L(e)&&e.call!==t)this.each(function(t){e.call(this,t)||i.push(this)});else{var r="string"==typeof e?this.filter(e):R(e)&&L(e.item)?a.call(e):n(e);this.forEach(function(t){r.indexOf(t)<0&&i.push(t)})}return n(i)},has:function(t){return this.filter(function(){return $(t)?n.contains(this,t):n(this).find(t).size()})},eq:function(t){return-1===t?this.slice(t):this.slice(t,+t+1)},first:function(){var t=this[0];return t&&!$(t)?t:n(t)},last:function(){var t=this[this.length-1];return t&&!$(t)?t:n(t)},find:function(t){var e,i=this;return e=t?"object"==typeof t?n(t).filter(function(){var t=this;return r.some.call(i,function(e){return n.contains(e,t)})}):1==this.length?n(C.qsa(this[0],t)):this.map(function(){return C.qsa(this,t)}):n()},closest:function(t,e){var i=this[0],r=!1;for("object"==typeof t&&(r=n(t));i&&!(r?r.indexOf(i)>=0:C.matches(i,t));)i=i!==e&&!Z(i)&&i.parentNode;return n(i)},parents:function(t){for(var e=[],i=this;i.length>0;)i=n.map(i,function(t){return(t=t.parentNode)&&!Z(t)&&e.indexOf(t)<0?(e.push(t),t):void 0});return V(e,t)},parent:function(t){return V(P(this.pluck("parentNode")),t)},children:function(t){return V(this.map(function(){return U(this)}),t)},contents:function(){return this.map(function(){return this.contentDocument||a.call(this.childNodes)})},siblings:function(t){return V(this.map(function(t,e){return s.call(U(e.parentNode),function(t){return t!==e})}),t)},empty:function(){return this.each(function(){this.innerHTML=""})},pluck:function(t){return n.map(this,function(e){return e[t]})},show:function(){return this.each(function(){"none"==this.style.display&&(this.style.display=""),"none"==getComputedStyle(this,"").getPropertyValue("display")&&(this.style.display=I(this.nodeName))})},replaceWith:function(t){return this.before(t).remove()},wrap:function(t){var e=L(t);if(this[0]&&!e)var i=n(t).get(0),r=i.parentNode||this.length>1;return this.each(function(o){n(this).wrapAll(e?t.call(this,o):r?i.cloneNode(!0):i)})},wrapAll:function(t){if(this[0]){n(this[0]).before(t=n(t));for(var e;(e=t.children()).length;)t=e.first();n(t).append(this)}return this},wrapInner:function(t){var e=L(t);return this.each(function(i){var r=n(this),o=r.contents(),s=e?t.call(this,i):t;o.length?o.wrapAll(s):r.append(s)})},unwrap:function(){return this.parent().each(function(){n(this).replaceWith(n(this).children())}),this},clone:function(){return this.map(function(){return this.cloneNode(!0)})},hide:function(){return this.css("display","none")},toggle:function(e){return this.each(function(){var i=n(this);(e===t?"none"==i.css("display"):e)?i.show():i.hide()})},prev:function(t){return n(this.pluck("previousElementSibling")).filter(t||"*")},next:function(t){return n(this.pluck("nextElementSibling")).filter(t||"*")},html:function(t){return 0 in arguments?this.each(function(e){var i=this.innerHTML;n(this).empty().append(Y(this,t,e,i))}):0 in this?this[0].innerHTML:null},text:function(t){return 0 in arguments?this.each(function(e){var n=Y(this,t,e,this.textContent);this.textContent=null==n?"":""+n}):0 in this?this[0].textContent:null},attr:function(n,i){var r;return"string"!=typeof n||1 in arguments?this.each(function(t){if(1===this.nodeType)if($(n))for(e in n)J(this,e,n[e]);else J(this,n,Y(this,i,t,this.getAttribute(n)))}):this.length&&1===this[0].nodeType?!(r=this[0].getAttribute(n))&&n in this[0]?this[0][n]:r:t},removeAttr:function(t){return this.each(function(){1===this.nodeType&&t.split(" ").forEach(function(t){J(this,t)},this)})},prop:function(t,e){return t=M[t]||t,1 in arguments?this.each(function(n){this[t]=Y(this,e,n,this[t])}):this[0]&&this[0][t]},data:function(e,n){var i="data-"+e.replace(g,"-$1").toLowerCase(),r=1 in arguments?this.attr(i,n):this.attr(i);return null!==r?K(r):t},val:function(t){return 0 in arguments?this.each(function(e){this.value=Y(this,t,e,this.value)}):this[0]&&(this[0].multiple?n(this[0]).find("option").filter(function(){return this.selected}).pluck("value"):this[0].value)},offset:function(t){if(t)return this.each(function(e){var i=n(this),r=Y(this,t,e,i.offset()),o=i.offsetParent().offset(),s={top:r.top-o.top,left:r.left-o.left};"static"==i.css("position")&&(s.position="relative"),i.css(s)});if(!this.length)return null;if(!n.contains(u.documentElement,this[0]))return{top:0,left:0};var e=this[0].getBoundingClientRect();return{left:e.left+window.pageXOffset,top:e.top+window.pageYOffset,width:Math.round(e.width),height:Math.round(e.height)}},css:function(t,i){if(arguments.length<2){var r,o=this[0];if(!o)return;if(r=getComputedStyle(o,""),"string"==typeof t)return o.style[N(t)]||r.getPropertyValue(t);if(A(t)){var s={};return n.each(t,function(t,e){s[e]=o.style[N(e)]||r.getPropertyValue(e)}),s}}var a="";if("string"==D(t))i||0===i?a=z(t)+":"+_(t,i):this.each(function(){this.style.removeProperty(z(t))});else for(e in t)t[e]||0===t[e]?a+=z(e)+":"+_(e,t[e])+";":this.each(function(){this.style.removeProperty(z(e))});return this.each(function(){this.style.cssText+=";"+a})},index:function(t){return t?this.indexOf(n(t)[0]):this.parent().children().indexOf(this[0])},hasClass:function(t){return t?r.some.call(this,function(t){return this.test(G(t))},H(t)):!1},addClass:function(t){return t?this.each(function(e){if("className"in this){i=[];var r=G(this),o=Y(this,t,e,r);o.split(/\s+/g).forEach(function(t){n(this).hasClass(t)||i.push(t)},this),i.length&&G(this,r+(r?" ":"")+i.join(" "))}}):this},removeClass:function(e){return this.each(function(n){if("className"in this){if(e===t)return G(this,"");i=G(this),Y(this,e,n,i).split(/\s+/g).forEach(function(t){i=i.replace(H(t)," ")}),G(this,i.trim())}})},toggleClass:function(e,i){return e?this.each(function(r){var o=n(this),s=Y(this,e,r,G(this));s.split(/\s+/g).forEach(function(e){(i===t?!o.hasClass(e):i)?o.addClass(e):o.removeClass(e)})}):this},scrollTop:function(e){if(this.length){var n="scrollTop"in this[0];return e===t?n?this[0].scrollTop:this[0].pageYOffset:this.each(n?function(){this.scrollTop=e}:function(){this.scrollTo(this.scrollX,e)})}},scrollLeft:function(e){if(this.length){var n="scrollLeft"in this[0];return e===t?n?this[0].scrollLeft:this[0].pageXOffset:this.each(n?function(){this.scrollLeft=e}:function(){this.scrollTo(e,this.scrollY)})}},position:function(){if(this.length){var t=this[0],e=this.offsetParent(),i=this.offset(),r=m.test(e[0].nodeName)?{top:0,left:0}:e.offset();return i.top-=parseFloat(n(t).css("margin-top"))||0,i.left-=parseFloat(n(t).css("margin-left"))||0,r.top+=parseFloat(n(e[0]).css("border-top-width"))||0,r.left+=parseFloat(n(e[0]).css("border-left-width"))||0,{top:i.top-r.top,left:i.left-r.left}}},offsetParent:function(){return this.map(function(){for(var t=this.offsetParent||u.body;t&&!m.test(t.nodeName)&&"static"==n(t).css("position");)t=t.offsetParent;return t})}},n.fn.detach=n.fn.remove,["width","height"].forEach(function(e){var i=e.replace(/./,function(t){return t[0].toUpperCase()});n.fn[e]=function(r){var o,s=this[0];return r===t?k(s)?s["inner"+i]:Z(s)?s.documentElement["scroll"+i]:(o=this.offset())&&o[e]:this.each(function(t){s=n(this),s.css(e,Y(this,r,t,s[e]()))})}}),y.forEach(function(t,e){var i=e%2;n.fn[t]=function(){var t,o,r=n.map(arguments,function(e){return t=D(e),"object"==t||"array"==t||null==e?e:C.fragment(e)}),s=this.length>1;return r.length<1?this:this.each(function(t,a){o=i?a:a.parentNode,a=0==e?a.nextSibling:1==e?a.firstChild:2==e?a:null;var f=n.contains(u.documentElement,o);r.forEach(function(t){if(s)t=t.cloneNode(!0);else if(!o)return n(t).remove();o.insertBefore(t,a),f&&Q(t,function(t){null==t.nodeName||"SCRIPT"!==t.nodeName.toUpperCase()||t.type&&"text/javascript"!==t.type||t.src||window.eval.call(window,t.innerHTML)})})})},n.fn[i?t+"To":"insert"+(e?"Before":"After")]=function(e){return n(e)[t](this),this}}),C.Z.prototype=X.prototype=n.fn,C.uniq=P,C.deserializeValue=K,n.zepto=C,n}();window.Zepto=Zepto,void 0===window.$&&(window.$=Zepto),function(t){function l(t){return t._zid||(t._zid=e++)}function h(t,e,n,i){if(e=p(e),e.ns)var r=d(e.ns);return(s[l(t)]||[]).filter(function(t){return!(!t||e.e&&t.e!=e.e||e.ns&&!r.test(t.ns)||n&&l(t.fn)!==l(n)||i&&t.sel!=i)})}function p(t){var e=(""+t).split(".");return{e:e[0],ns:e.slice(1).sort().join(" ")}}function d(t){return new RegExp("(?:^| )"+t.replace(" "," .* ?")+"(?: |$)")}function m(t,e){return t.del&&!u&&t.e in f||!!e}function g(t){return c[t]||u&&f[t]||t}function v(e,i,r,o,a,u,f){var h=l(e),d=s[h]||(s[h]=[]);i.split(/\s/).forEach(function(i){if("ready"==i)return t(document).ready(r);var s=p(i);s.fn=r,s.sel=a,s.e in c&&(r=function(e){var n=e.relatedTarget;return!n||n!==this&&!t.contains(this,n)?s.fn.apply(this,arguments):void 0}),s.del=u;var l=u||r;s.proxy=function(t){if(t=T(t),!t.isImmediatePropagationStopped()){t.data=o;var i=l.apply(e,t._args==n?[t]:[t].concat(t._args));return i===!1&&(t.preventDefault(),t.stopPropagation()),i}},s.i=d.length,d.push(s),"addEventListener"in e&&e.addEventListener(g(s.e),s.proxy,m(s,f))})}function y(t,e,n,i,r){var o=l(t);(e||"").split(/\s/).forEach(function(e){h(t,e,n,i).forEach(function(e){delete s[o][e.i],"removeEventListener"in t&&t.removeEventListener(g(e.e),e.proxy,m(e,r))})})}function T(e,i){return(i||!e.isDefaultPrevented)&&(i||(i=e),t.each(E,function(t,n){var r=i[t];e[t]=function(){return this[n]=w,r&&r.apply(i,arguments)},e[n]=x}),(i.defaultPrevented!==n?i.defaultPrevented:"returnValue"in i?i.returnValue===!1:i.getPreventDefault&&i.getPreventDefault())&&(e.isDefaultPrevented=w)),e}function j(t){var e,i={originalEvent:t};for(e in t)b.test(e)||t[e]===n||(i[e]=t[e]);return T(i,t)}var n,e=1,i=Array.prototype.slice,r=t.isFunction,o=function(t){return"string"==typeof t},s={},a={},u="onfocusin"in window,f={focus:"focusin",blur:"focusout"},c={mouseenter:"mouseover",mouseleave:"mouseout"};a.click=a.mousedown=a.mouseup=a.mousemove="MouseEvents",t.event={add:v,remove:y},t.proxy=function(e,n){var s=2 in arguments&&i.call(arguments,2);if(r(e)){var a=function(){return e.apply(n,s?s.concat(i.call(arguments)):arguments)};return a._zid=l(e),a}if(o(n))return s?(s.unshift(e[n],e),t.proxy.apply(null,s)):t.proxy(e[n],e);throw new TypeError("expected function")},t.fn.bind=function(t,e,n){return this.on(t,e,n)},t.fn.unbind=function(t,e){return this.off(t,e)},t.fn.one=function(t,e,n,i){return this.on(t,e,n,i,1)};var w=function(){return!0},x=function(){return!1},b=/^([A-Z]|returnValue$|layer[XY]$)/,E={preventDefault:"isDefaultPrevented",stopImmediatePropagation:"isImmediatePropagationStopped",stopPropagation:"isPropagationStopped"};t.fn.delegate=function(t,e,n){return this.on(e,t,n)},t.fn.undelegate=function(t,e,n){return this.off(e,t,n)},t.fn.live=function(e,n){return t(document.body).delegate(this.selector,e,n),this},t.fn.die=function(e,n){return t(document.body).undelegate(this.selector,e,n),this},t.fn.on=function(e,s,a,u,f){var c,l,h=this;return e&&!o(e)?(t.each(e,function(t,e){h.on(t,s,a,e,f)}),h):(o(s)||r(u)||u===!1||(u=a,a=s,s=n),(u===n||a===!1)&&(u=a,a=n),u===!1&&(u=x),h.each(function(n,r){f&&(c=function(t){return y(r,t.type,u),u.apply(this,arguments)}),s&&(l=function(e){var n,o=t(e.target).closest(s,r).get(0);return o&&o!==r?(n=t.extend(j(e),{currentTarget:o,liveFired:r}),(c||u).apply(o,[n].concat(i.call(arguments,1)))):void 0}),v(r,e,u,a,s,l||c)}))},t.fn.off=function(e,i,s){var a=this;return e&&!o(e)?(t.each(e,function(t,e){a.off(t,i,e)}),a):(o(i)||r(s)||s===!1||(s=i,i=n),s===!1&&(s=x),a.each(function(){y(this,e,s,i)}))},t.fn.trigger=function(e,n){return e=o(e)||t.isPlainObject(e)?t.Event(e):T(e),e._args=n,this.each(function(){e.type in f&&"function"==typeof this[e.type]?this[e.type]():"dispatchEvent"in this?this.dispatchEvent(e):t(this).triggerHandler(e,n)})},t.fn.triggerHandler=function(e,n){var i,r;return this.each(function(s,a){i=j(o(e)?t.Event(e):e),i._args=n,i.target=a,t.each(h(a,e.type||e),function(t,e){return r=e.proxy(i),i.isImmediatePropagationStopped()?!1:void 0})}),r},"focusin focusout focus blur load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select keydown keypress keyup error".split(" ").forEach(function(e){t.fn[e]=function(t){return 0 in arguments?this.bind(e,t):this.trigger(e)}}),t.Event=function(t,e){o(t)||(e=t,t=e.type);var n=document.createEvent(a[t]||"Events"),i=!0;if(e)for(var r in e)"bubbles"==r?i=!!e[r]:n[r]=e[r];return n.initEvent(t,i,!0),T(n)}}(Zepto),function(t){function h(e,n,i){var r=t.Event(n);return t(e).trigger(r,i),!r.isDefaultPrevented()}function p(t,e,i,r){return t.global?h(e||n,i,r):void 0}function d(e){e.global&&0===t.active++&&p(e,null,"ajaxStart")}function m(e){e.global&&!--t.active&&p(e,null,"ajaxStop")}function g(t,e){var n=e.context;return e.beforeSend.call(n,t,e)===!1||p(e,n,"ajaxBeforeSend",[t,e])===!1?!1:void p(e,n,"ajaxSend",[t,e])}function v(t,e,n,i){var r=n.context,o="success";n.success.call(r,t,o,e),i&&i.resolveWith(r,[t,o,e]),p(n,r,"ajaxSuccess",[e,n,t]),w(o,e,n)}function y(t,e,n,i,r){var o=i.context;i.error.call(o,n,e,t),r&&r.rejectWith(o,[n,e,t]),p(i,o,"ajaxError",[n,i,t||e]),w(e,n,i)}function w(t,e,n){var i=n.context;n.complete.call(i,e,t),p(n,i,"ajaxComplete",[e,n]),m(n)}function x(){}function b(t){return t&&(t=t.split(";",2)[0]),t&&(t==f?"html":t==u?"json":s.test(t)?"script":a.test(t)&&"xml")||"text"}function E(t,e){return""==e?t:(t+"&"+e).replace(/[&?]{1,2}/,"?")}function T(e){e.processData&&e.data&&"string"!=t.type(e.data)&&(e.data=t.param(e.data,e.traditional)),!e.data||e.type&&"GET"!=e.type.toUpperCase()||(e.url=E(e.url,e.data),e.data=void 0)}function j(e,n,i,r){return t.isFunction(n)&&(r=i,i=n,n=void 0),t.isFunction(i)||(r=i,i=void 0),{url:e,data:n,success:i,dataType:r}}function C(e,n,i,r){var o,s=t.isArray(n),a=t.isPlainObject(n);t.each(n,function(n,u){o=t.type(u),r&&(n=i?r:r+"["+(a||"object"==o||"array"==o?n:"")+"]"),!r&&s?e.add(u.name,u.value):"array"==o||!i&&"object"==o?C(e,u,i,n):e.add(n,u)})}var i,r,e=0,n=window.document,o=/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,s=/^(?:text|application)\/javascript/i,a=/^(?:text|application)\/xml/i,u="application/json",f="text/html",c=/^\s*$/,l=n.createElement("a");l.href=window.location.href,t.active=0,t.ajaxJSONP=function(i,r){if(!("type"in i))return t.ajax(i);var f,h,o=i.jsonpCallback,s=(t.isFunction(o)?o():o)||"jsonp"+ ++e,a=n.createElement("script"),u=window[s],c=function(e){t(a).triggerHandler("error",e||"abort")},l={abort:c};return r&&r.promise(l),t(a).on("load error",function(e,n){clearTimeout(h),t(a).off().remove(),"error"!=e.type&&f?v(f[0],l,i,r):y(null,n||"error",l,i,r),window[s]=u,f&&t.isFunction(u)&&u(f[0]),u=f=void 0}),g(l,i)===!1?(c("abort"),l):(window[s]=function(){f=arguments},a.src=i.url.replace(/\?(.+)=\?/,"?$1="+s),n.head.appendChild(a),i.timeout>0&&(h=setTimeout(function(){c("timeout")},i.timeout)),l)},t.ajaxSettings={type:"GET",beforeSend:x,success:x,error:x,complete:x,context:null,global:!0,xhr:function(){return new window.XMLHttpRequest},accepts:{script:"text/javascript, application/javascript, application/x-javascript",json:u,xml:"application/xml, text/xml",html:f,text:"text/plain"},crossDomain:!1,timeout:0,processData:!0,cache:!0},t.ajax=function(e){var a,u,o=t.extend({},e||{}),s=t.Deferred&&t.Deferred();for(i in t.ajaxSettings)void 0===o[i]&&(o[i]=t.ajaxSettings[i]);d(o),o.crossDomain||(a=n.createElement("a"),a.href=o.url,a.href=a.href,o.crossDomain=l.protocol+"//"+l.host!=a.protocol+"//"+a.host),o.url||(o.url=window.location.toString()),(u=o.url.indexOf("#"))>-1&&(o.url=o.url.slice(0,u)),T(o);var f=o.dataType,h=/\?.+=\?/.test(o.url);if(h&&(f="jsonp"),o.cache!==!1&&(e&&e.cache===!0||"script"!=f&&"jsonp"!=f)||(o.url=E(o.url,"_="+Date.now())),"jsonp"==f)return h||(o.url=E(o.url,o.jsonp?o.jsonp+"=?":o.jsonp===!1?"":"callback=?")),t.ajaxJSONP(o,s);var N,p=o.accepts[f],m={},w=function(t,e){m[t.toLowerCase()]=[t,e]},j=/^([\w-]+:)\/\//.test(o.url)?RegExp.$1:window.location.protocol,S=o.xhr(),C=S.setRequestHeader;if(s&&s.promise(S),o.crossDomain||w("X-Requested-With","XMLHttpRequest"),w("Accept",p||"*/*"),(p=o.mimeType||p)&&(p.indexOf(",")>-1&&(p=p.split(",",2)[0]),S.overrideMimeType&&S.overrideMimeType(p)),(o.contentType||o.contentType!==!1&&o.data&&"GET"!=o.type.toUpperCase())&&w("Content-Type",o.contentType||"application/x-www-form-urlencoded"),o.headers)for(r in o.headers)w(r,o.headers[r]);if(S.setRequestHeader=w,S.onreadystatechange=function(){if(4==S.readyState){S.onreadystatechange=x,clearTimeout(N);var e,n=!1;if(S.status>=200&&S.status<300||304==S.status||0==S.status&&"file:"==j){f=f||b(o.mimeType||S.getResponseHeader("content-type")),e=S.responseText;try{"script"==f?(1,eval)(e):"xml"==f?e=S.responseXML:"json"==f&&(e=c.test(e)?null:t.parseJSON(e))}catch(i){n=i}n?y(n,"parsererror",S,o,s):v(e,S,o,s)}else y(S.statusText||null,S.status?"error":"abort",S,o,s)}},g(S,o)===!1)return S.abort(),y(null,"abort",S,o,s),S;if(o.xhrFields)for(r in o.xhrFields)S[r]=o.xhrFields[r];var P="async"in o?o.async:!0;S.open(o.type,o.url,P,o.username,o.password);for(r in m)C.apply(S,m[r]);return o.timeout>0&&(N=setTimeout(function(){S.onreadystatechange=x,S.abort(),y(null,"timeout",S,o,s)},o.timeout)),S.send(o.data?o.data:null),S},t.get=function(){return t.ajax(j.apply(null,arguments))},t.post=function(){var e=j.apply(null,arguments);return e.type="POST",t.ajax(e)},t.getJSON=function(){var e=j.apply(null,arguments);return e.dataType="json",t.ajax(e)},t.fn.load=function(e,n,i){if(!this.length)return this;var a,r=this,s=e.split(/\s/),u=j(e,n,i),f=u.success;return s.length>1&&(u.url=s[0],a=s[1]),u.success=function(e){r.html(a?t("<div>").html(e.replace(o,"")).find(a):e),f&&f.apply(r,arguments)},t.ajax(u),this};var S=encodeURIComponent;t.param=function(e,n){var i=[];return i.add=function(e,n){t.isFunction(n)&&(n=n()),null==n&&(n=""),this.push(S(e)+"="+S(n))},C(i,e,n),i.join("&").replace(/%20/g,"+")}}(Zepto),function(t){t.Callbacks=function(e){e=t.extend({},e);var n,i,r,o,s,a,u=[],f=!e.once&&[],c=function(t){for(n=e.memory&&t,i=!0,a=o||0,o=0,s=u.length,r=!0;u&&s>a;++a)if(u[a].apply(t[0],t[1])===!1&&e.stopOnFalse){n=!1;break}r=!1,u&&(f?f.length&&c(f.shift()):n?u.length=0:l.disable())},l={add:function(){if(u){var i=u.length,a=function(n){t.each(n,function(t,n){"function"==typeof n?e.unique&&l.has(n)||u.push(n):n&&n.length&&"string"!=typeof n&&a(n)})};a(arguments),r?s=u.length:n&&(o=i,c(n))}return this},remove:function(){return u&&t.each(arguments,function(e,n){for(var i;(i=t.inArray(n,u,i))>-1;)u.splice(i,1),r&&(s>=i&&--s,a>=i&&--a)}),this},has:function(e){return!(!u||!(e?t.inArray(e,u)>-1:u.length))},empty:function(){return s=u.length=0,this},disable:function(){return u=f=n=void 0,this},disabled:function(){return!u},lock:function(){return f=void 0,n||l.disable(),this},locked:function(){return!f},fireWith:function(t,e){return!u||i&&!f||(e=e||[],e=[t,e.slice?e.slice():e],r?f.push(e):c(e)),this},fire:function(){return l.fireWith(this,arguments)},fired:function(){return!!i}};return l}}(Zepto),function(t){function n(e){var i=[["resolve","done",t.Callbacks({once:1,memory:1}),"resolved"],["reject","fail",t.Callbacks({once:1,memory:1}),"rejected"],["notify","progress",t.Callbacks({memory:1})]],r="pending",o={state:function(){return r},always:function(){return s.done(arguments).fail(arguments),this},then:function(){var e=arguments;return n(function(n){t.each(i,function(i,r){var a=t.isFunction(e[i])&&e[i];s[r[1]](function(){var e=a&&a.apply(this,arguments);if(e&&t.isFunction(e.promise))e.promise().done(n.resolve).fail(n.reject).progress(n.notify);else{var i=this===o?n.promise():this,s=a?[e]:arguments;n[r[0]+"With"](i,s)}})}),e=null}).promise()},promise:function(e){return null!=e?t.extend(e,o):o}},s={};return t.each(i,function(t,e){var n=e[2],a=e[3];o[e[1]]=n.add,a&&n.add(function(){r=a},i[1^t][2].disable,i[2][2].lock),s[e[0]]=function(){return s[e[0]+"With"](this===s?o:this,arguments),this},s[e[0]+"With"]=n.fireWith}),o.promise(s),e&&e.call(s,s),s}var e=Array.prototype.slice;t.when=function(i){var f,c,l,r=e.call(arguments),o=r.length,s=0,a=1!==o||i&&t.isFunction(i.promise)?o:0,u=1===a?i:n(),h=function(t,n,i){return function(r){n[t]=this,i[t]=arguments.length>1?e.call(arguments):r,i===f?u.notifyWith(n,i):--a||u.resolveWith(n,i)}};if(o>1)for(f=new Array(o),c=new Array(o),l=new Array(o);o>s;++s)r[s]&&t.isFunction(r[s].promise)?r[s].promise().done(h(s,l,r)).fail(u.reject).progress(h(s,c,f)):--a;return a||u.resolveWith(l,r),u.promise()},t.Deferred=n}(Zepto),function(t){function u(t,e,n,i){return Math.abs(t-e)>=Math.abs(n-i)?t-e>0?"Left":"Right":n-i>0?"Up":"Down"}function f(){o=null,e.last&&(e.el.trigger("longTap"),e={})}function c(){o&&clearTimeout(o),o=null}function l(){n&&clearTimeout(n),i&&clearTimeout(i),r&&clearTimeout(r),o&&clearTimeout(o),n=i=r=o=null,e={}}function h(t){return("touch"==t.pointerType||t.pointerType==t.MSPOINTER_TYPE_TOUCH)&&t.isPrimary}function p(t,e){return t.type=="pointer"+e||t.type.toLowerCase()=="mspointer"+e}var n,i,r,o,a,e={},s=750;t(document).ready(function(){var d,m,y,w,g=0,v=0;"MSGesture"in window&&(a=new MSGesture,a.target=document.body),t(document).bind("MSGestureEnd",function(t){var n=t.velocityX>1?"Right":t.velocityX<-1?"Left":t.velocityY>1?"Down":t.velocityY<-1?"Up":null;n&&(e.el.trigger("swipe"),e.el.trigger("swipe"+n))}).on("touchstart MSPointerDown pointerdown",function(i){(!(w=p(i,"down"))||h(i))&&(y=w?i:i.touches[0],i.touches&&1===i.touches.length&&e.x2&&(e.x2=void 0,e.y2=void 0),d=Date.now(),m=d-(e.last||d),e.el=t("tagName"in y.target?y.target:y.target.parentNode),n&&clearTimeout(n),e.x1=y.pageX,e.y1=y.pageY,m>0&&250>=m&&(e.isDoubleTap=!0),e.last=d,o=setTimeout(f,s),a&&w&&a.addPointer(i.pointerId))}).on("touchmove MSPointerMove pointermove",function(t){(!(w=p(t,"move"))||h(t))&&(y=w?t:t.touches[0],c(),e.x2=y.pageX,e.y2=y.pageY,g+=Math.abs(e.x1-e.x2),v+=Math.abs(e.y1-e.y2))}).on("touchend MSPointerUp pointerup",function(o){(!(w=p(o,"up"))||h(o))&&(c(),e.x2&&Math.abs(e.x1-e.x2)>30||e.y2&&Math.abs(e.y1-e.y2)>30?r=setTimeout(function(){e.el.trigger("swipe"),e.el.trigger("swipe"+u(e.x1,e.x2,e.y1,e.y2)),e={}},0):"last"in e&&(30>g&&30>v?i=setTimeout(function(){var i=t.Event("tap");i.cancelTouch=l,e.el.trigger(i),e.isDoubleTap?(e.el&&e.el.trigger("doubleTap"),e={}):n=setTimeout(function(){n=null,e.el&&e.el.trigger("singleTap"),e={}},250)},0):e={}),g=v=0)}).on("touchcancel MSPointerCancel pointercancel",l),t(window).on("scroll",l)}),["swipe","swipeLeft","swipeRight","swipeUp","swipeDown","doubleTap","tap","singleTap","longTap"].forEach(function(e){t.fn[e]=function(t){return this.on(e,t)}})}(Zepto),function(t){function r(e){return e=t(e),!(!e.width()&&!e.height())&&"none"!==e.css("display")}function f(t,e){t=t.replace(/=#\]/g,'="#"]');var n,i,r=s.exec(t);if(r&&r[2]in o&&(n=o[r[2]],i=r[3],t=r[1],i)){var a=Number(i);i=isNaN(a)?i.replace(/^["']|["']$/g,""):a}return e(t,n,i)}var e=t.zepto,n=e.qsa,i=e.matches,o=t.expr[":"]={visible:function(){return r(this)?this:void 0},hidden:function(){return r(this)?void 0:this},selected:function(){return this.selected?this:void 0},checked:function(){return this.checked?this:void 0},parent:function(){return this.parentNode},first:function(t){return 0===t?this:void 0},last:function(t,e){return t===e.length-1?this:void 0},eq:function(t,e,n){return t===n?this:void 0},contains:function(e,n,i){return t(this).text().indexOf(i)>-1?this:void 0},has:function(t,n,i){return e.qsa(this,i).length?this:void 0}},s=new RegExp("(.*):(\\w+)(?:\\(([^)]+)\\))?$\\s*"),a=/^\s*>/,u="Zepto"+ +new Date;e.qsa=function(i,r){return f(r,function(o,s,f){try{var c;!o&&s?o="*":a.test(o)&&(c=t(i).addClass(u),o="."+u+" "+o);var l=n(i,o)}catch(h){throw console.error("error performing selector: %o",r),h}finally{c&&c.removeClass(u)}return s?e.uniq(t.map(l,function(t,e){return s.call(t,e,l,f)})):l})},e.matches=function(t,e){return f(e,function(e,n,r){return!(e&&!i(t,e)||n&&n.call(t,null,r)!==t)})}}(Zepto),function(){try{getComputedStyle(void 0)}catch(t){var e=getComputedStyle;window.getComputedStyle=function(t){try{return e(t)}catch(n){return null}}}}(); + module.exports = Zepto; + + +/***/ }, +/* 17 */ +/***/ function(module, exports) { + + 'use strict'; + + /* eslint-disable no-var */ + // Simple JavaScript Templating + // Paul Miller (http://paulmillr.com) + // http://underscorejs.org + // (c) 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors + + // By default, Underscore uses ERB-style template delimiters, change the + // following template settings to use alternative delimiters. + var settings = { + evaluate: /<%([\s\S]+?)%>/g, + interpolate: /<%=([\s\S]+?)%>/g, + escape: /<%-([\s\S]+?)%>/g + }; + + // When customizing `templateSettings`, if you don't want to define an + // interpolation, evaluation or escaping regex, we need one that is + // guaranteed not to match. + var noMatch = /(.)^/; + + // Certain characters need to be escaped so that they can be put into a + // string literal. + var escapes = { + '\'': '\'', + '\\': '\\', + '\r': 'r', + '\n': 'n', + '\t': 't', + '\u2028': 'u2028', + '\u2029': 'u2029' + }; + + var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g; + + // List of HTML entities for escaping. + var htmlEntities = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + '\'': ''' + }; + + var entityRe = new RegExp('[&<>"\']', 'g'); + + var escapeExpr = function escapeExpr(string) { + if (string === null) { + return ''; + } + return ('' + string).replace(entityRe, function (match) { + return htmlEntities[match]; + }); + }; + + var counter = 0; + + // JavaScript micro-templating, similar to John Resig's implementation. + // Underscore templating handles arbitrary delimiters, preserves whitespace, + // and correctly escapes quotes within interpolated code. + var tmpl = function tmpl(text, data) { + var render; + + // Combine delimiters into one regular expression via alternation. + var matcher = new RegExp([(settings.escape || noMatch).source, (settings.interpolate || noMatch).source, (settings.evaluate || noMatch).source].join('|') + '|$', 'g'); + + // Compile the template source, escaping string literals appropriately. + var index = 0; + var source = '__p+=\''; + text.replace(matcher, function (match, escape, interpolate, evaluate, offset) { + source += text.slice(index, offset).replace(escaper, function (match) { + return '\\' + escapes[match]; + }); + + if (escape) { + source += '\'+\n((__t=(' + escape + '))==null?\'\':escapeExpr(__t))+\n\''; + } + if (interpolate) { + source += '\'+\n((__t=(' + interpolate + '))==null?\'\':__t)+\n\''; + } + if (evaluate) { + source += '\';\n' + evaluate + '\n__p+=\''; + } + index = offset + match.length; + return match; + }); + source += '\';\n'; + + // If a variable is not specified, place data values in local scope. + if (!settings.variable) { + source = 'with(obj||{}){\n' + source + '}\n'; + } + + source = 'var __t,__p=\'\',__j=Array.prototype.join,' + 'print=function(){__p+=__j.call(arguments,\'\');};\n' + source + 'return __p;\n//# sourceURL=/microtemplates/source[' + counter++ + ']'; + + try { + /*jshint -W054 */ + // TODO: find a way to avoid eval + render = new Function(settings.variable || 'obj', 'escapeExpr', source); + } catch (e) { + e.source = source; + throw e; + } + + if (data) { + return render(data, escapeExpr); + } + var template = function template(data) { + return render.call(this, data, escapeExpr); + }; + + // Provide the compiled function source as a convenience for precompilation. + template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}'; + + return template; + }; + tmpl.settings = settings; + + module.exports = tmpl; + +/***/ }, +/* 18 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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 _utils = __webpack_require__(2); + + var _clapprZepto = __webpack_require__(16); + + var _clapprZepto2 = _interopRequireDefault(_clapprZepto); + + var _lodash = __webpack_require__(19); + + var _lodash2 = _interopRequireDefault(_lodash); + + var _base_object = __webpack_require__(4); + + var _base_object2 = _interopRequireDefault(_base_object); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + 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; } // Copyright 2014 Globo.com Player authors. All rights reserved. + // Use of this source code is governed by a BSD-style + // license that can be found in the LICENSE file. + + var delegateEventSplitter = /^(\S+)\s*(.*)$/; + + /** + * A base class to create ui object. + * @class UIObject + * @constructor + * @extends BaseObject + * @module base + */ + + var UIObject = function (_BaseObject) { + _inherits(UIObject, _BaseObject); + + _createClass(UIObject, [{ + key: 'tagName', + + /** + * a unique id prefixed with `'c'`, `c1, c232` + * + * @property cid + * @type String + */ + /** + * the dom element itself + * + * @property el + * @type HTMLElement + */ + /** + * the dom element wrapped by `$` + * + * @property $el + * @type HTMLElement + */ + + /** + * gets the tag name for the ui component + * @method tagName + * @default div + * @return {String} tag's name + */ + get: function get() { + return 'div'; + } + /** + * a literal object mapping element's events to methods + * @property events + * @type Object + * @example + * + *```javascript + * + * class MyButton extends UIObject { + * constructor(options) { + * super(options) + * this.myId = 0 + * } + * get events() { return { 'click': 'myClick' } } + * myClick(){ this.myId = 42 } + * } + * + * // when you click on MyButton the method `myClick` will be called + *``` + */ + + }, { + key: 'events', + get: function get() { + return {}; + } + /** + * a literal object mapping attributes and values to the element + * element's attribute name and the value the attribute value + * @property attributes + * @type Object + * @example + * + *```javascript + * + * class MyButton extends UIObject { + * constructor(options) { super(options) } + * get attributes() { return { class: 'my-button'} } + * } + * + * // MyButton.el.className will be 'my-button' + * ``` + */ + + }, { + key: 'attributes', + get: function get() { + return {}; + } + + /** + * it builds an ui component by: + * * creating an id for the component `cid` + * * making sure the element is created `$el` + * * delegating all `events` to the element + * @method constructor + * @param {Object} options the options object + */ + + }]); + + function UIObject(options) { + _classCallCheck(this, UIObject); + + var _this = _possibleConstructorReturn(this, _BaseObject.call(this, options)); + + _this.cid = (0, _utils.uniqueId)('c'); + _this._ensureElement(); + _this.delegateEvents(); + return _this; + } + + /** + * selects within the component. + * @method $ + * @param {String} selector a selector to find within the component. + * @return {HTMLElement} an element, if it exists. + * @example + * ```javascript + * fullScreenBarUIComponent.$('.button-full') //will return only `.button-full` within the component + * ``` + */ + + + UIObject.prototype.$ = function $(selector) { + return this.$el.find(selector); + }; + + /** + * render the component, usually attach it to a real existent `element` + * @method render + * @return {UIObject} itself + */ + + + UIObject.prototype.render = function render() { + return this; + }; + + /** + * removes the ui component from DOM + * @method remove + * @return {UIObject} itself + */ + + + UIObject.prototype.remove = function remove() { + this.$el.remove(); + this.stopListening(); + this.undelegateEvents(); + return this; + }; + + /** + * set element to `el` and `$el` + * @method setElement + * @param {HTMLElement} element + * @param {Boolean} delegate whether is delegate or not + * @return {UIObject} itself + */ + + + UIObject.prototype.setElement = function setElement(element, delegate) { + if (this.$el) { + this.undelegateEvents(); + } + this.$el = element instanceof _clapprZepto2.default ? element : (0, _clapprZepto2.default)(element); + this.el = this.$el[0]; + if (delegate !== false) { + this.delegateEvents(); + } + return this; + }; + + /** + * delegates all the original `events` on `element` to its callbacks + * @method delegateEvents + * @param {Object} events + * @return {UIObject} itself + */ + + + UIObject.prototype.delegateEvents = function delegateEvents(events) { + if (!(events || (events = (0, _lodash2.default)(this, 'events')))) { + return this; + } + this.undelegateEvents(); + for (var key in events) { + var method = events[key]; + if (method && method.constructor !== Function) { + method = this[events[key]]; + } + if (!method) { + continue; + } + + var match = key.match(delegateEventSplitter); + var eventName = match[1], + selector = match[2]; + //method = _.bind(method, this) + eventName += '.delegateEvents' + this.cid; + if (selector === '') { + this.$el.on(eventName, method.bind(this)); + } else { + this.$el.on(eventName, selector, method.bind(this)); + } + } + return this; + }; + + /** + * undelegats all the `events` + * @method undelegateEvents + * @return {UIObject} itself + */ + + + UIObject.prototype.undelegateEvents = function undelegateEvents() { + this.$el.off('.delegateEvents' + this.cid); + return this; + }; + + /** + * ensures the creation of this ui component + * @method _ensureElement + * @private + */ + + + UIObject.prototype._ensureElement = function _ensureElement() { + if (!this.el) { + var attrs = _clapprZepto2.default.extend({}, (0, _lodash2.default)(this, 'attributes')); + if (this.id) { + attrs.id = (0, _lodash2.default)(this, 'id'); + } + if (this.className) { + attrs['class'] = (0, _lodash2.default)(this, 'className'); + } + var $el = (0, _clapprZepto2.default)('<' + (0, _lodash2.default)(this, 'tagName') + '>').attr(attrs); + this.setElement($el, false); + } else { + this.setElement((0, _lodash2.default)(this, 'el'), false); + } + }; + + return UIObject; + }(_base_object2.default); + + exports.default = UIObject; + module.exports = exports['default']; + +/***/ }, +/* 19 */ +/***/ function(module, exports) { + + /* WEBPACK VAR INJECTION */(function(global) {/** + * lodash (Custom Build) <https://lodash.com/> + * Build: `lodash modularize exports="npm" -o ./` + * Copyright jQuery Foundation and other contributors <https://jquery.org/> + * Released under MIT license <https://lodash.com/license> + * Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE> + * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors + */ + + /** Used as the `TypeError` message for "Functions" methods. */ + var FUNC_ERROR_TEXT = 'Expected a function'; + + /** Used to stand-in for `undefined` hash values. */ + var HASH_UNDEFINED = '__lodash_hash_undefined__'; + + /** Used as references for various `Number` constants. */ + var INFINITY = 1 / 0; + + /** `Object#toString` result references. */ + var funcTag = '[object Function]', + genTag = '[object GeneratorFunction]', + symbolTag = '[object Symbol]'; + + /** Used to match property names within property paths. */ + var reIsDeepProp = /\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/, + reIsPlainProp = /^\w*$/, + reLeadingDot = /^\./, + rePropName = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g; + + /** + * Used to match `RegExp` + * [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns). + */ + var reRegExpChar = /[\\^$.*+?()[\]{}|]/g; + + /** Used to match backslashes in property paths. */ + var reEscapeChar = /\\(\\)?/g; + + /** Used to detect host constructors (Safari). */ + var reIsHostCtor = /^\[object .+?Constructor\]$/; + + /** Detect free variable `global` from Node.js. */ + var freeGlobal = typeof global == 'object' && global && global.Object === Object && global; + + /** Detect free variable `self`. */ + var freeSelf = typeof self == 'object' && self && self.Object === Object && self; + + /** Used as a reference to the global object. */ + var root = freeGlobal || freeSelf || Function('return this')(); + + /** + * Gets the value at `key` of `object`. + * + * @private + * @param {Object} [object] The object to query. + * @param {string} key The key of the property to get. + * @returns {*} Returns the property value. + */ + function getValue(object, key) { + return object == null ? undefined : object[key]; + } + + /** + * Checks if `value` is a host object in IE < 9. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a host object, else `false`. + */ + function isHostObject(value) { + // Many host objects are `Object` objects that can coerce to strings + // despite having improperly defined `toString` methods. + var result = false; + if (value != null && typeof value.toString != 'function') { + try { + result = !!(value + ''); + } catch (e) {} + } + return result; + } + + /** Used for built-in method references. */ + var arrayProto = Array.prototype, + funcProto = Function.prototype, + objectProto = Object.prototype; + + /** Used to detect overreaching core-js shims. */ + var coreJsData = root['__core-js_shared__']; + + /** Used to detect methods masquerading as native. */ + var maskSrcKey = (function() { + var uid = /[^.]+$/.exec(coreJsData && coreJsData.keys && coreJsData.keys.IE_PROTO || ''); + return uid ? ('Symbol(src)_1.' + uid) : ''; + }()); + + /** Used to resolve the decompiled source of functions. */ + var funcToString = funcProto.toString; + + /** Used to check objects for own properties. */ + var hasOwnProperty = objectProto.hasOwnProperty; + + /** + * Used to resolve the + * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring) + * of values. + */ + var objectToString = objectProto.toString; + + /** Used to detect if a method is native. */ + var reIsNative = RegExp('^' + + funcToString.call(hasOwnProperty).replace(reRegExpChar, '\\$&') + .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$' + ); + + /** Built-in value references. */ + var Symbol = root.Symbol, + splice = arrayProto.splice; + + /* Built-in method references that are verified to be native. */ + var Map = getNative(root, 'Map'), + nativeCreate = getNative(Object, 'create'); + + /** Used to convert symbols to primitives and strings. */ + var symbolProto = Symbol ? Symbol.prototype : undefined, + symbolToString = symbolProto ? symbolProto.toString : undefined; + + /** + * Creates a hash object. + * + * @private + * @constructor + * @param {Array} [entries] The key-value pairs to cache. + */ + function Hash(entries) { + var index = -1, + length = entries ? entries.length : 0; + + this.clear(); + while (++index < length) { + var entry = entries[index]; + this.set(entry[0], entry[1]); + } + } + + /** + * Removes all key-value entries from the hash. + * + * @private + * @name clear + * @memberOf Hash + */ + function hashClear() { + this.__data__ = nativeCreate ? nativeCreate(null) : {}; + } + + /** + * Removes `key` and its value from the hash. + * + * @private + * @name delete + * @memberOf Hash + * @param {Object} hash The hash to modify. + * @param {string} key The key of the value to remove. + * @returns {boolean} Returns `true` if the entry was removed, else `false`. + */ + function hashDelete(key) { + return this.has(key) && delete this.__data__[key]; + } + + /** + * Gets the hash value for `key`. + * + * @private + * @name get + * @memberOf Hash + * @param {string} key The key of the value to get. + * @returns {*} Returns the entry value. + */ + function hashGet(key) { + var data = this.__data__; + if (nativeCreate) { + var result = data[key]; + return result === HASH_UNDEFINED ? undefined : result; + } + return hasOwnProperty.call(data, key) ? data[key] : undefined; + } + + /** + * Checks if a hash value for `key` exists. + * + * @private + * @name has + * @memberOf Hash + * @param {string} key The key of the entry to check. + * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. + */ + function hashHas(key) { + var data = this.__data__; + return nativeCreate ? data[key] !== undefined : hasOwnProperty.call(data, key); + } + + /** + * Sets the hash `key` to `value`. + * + * @private + * @name set + * @memberOf Hash + * @param {string} key The key of the value to set. + * @param {*} value The value to set. + * @returns {Object} Returns the hash instance. + */ + function hashSet(key, value) { + var data = this.__data__; + data[key] = (nativeCreate && value === undefined) ? HASH_UNDEFINED : value; + return this; + } + + // Add methods to `Hash`. + Hash.prototype.clear = hashClear; + Hash.prototype['delete'] = hashDelete; + Hash.prototype.get = hashGet; + Hash.prototype.has = hashHas; + Hash.prototype.set = hashSet; + + /** + * Creates an list cache object. + * + * @private + * @constructor + * @param {Array} [entries] The key-value pairs to cache. + */ + function ListCache(entries) { + var index = -1, + length = entries ? entries.length : 0; + + this.clear(); + while (++index < length) { + var entry = entries[index]; + this.set(entry[0], entry[1]); + } + } + + /** + * Removes all key-value entries from the list cache. + * + * @private + * @name clear + * @memberOf ListCache + */ + function listCacheClear() { + this.__data__ = []; + } + + /** + * Removes `key` and its value from the list cache. + * + * @private + * @name delete + * @memberOf ListCache + * @param {string} key The key of the value to remove. + * @returns {boolean} Returns `true` if the entry was removed, else `false`. + */ + function listCacheDelete(key) { + var data = this.__data__, + index = assocIndexOf(data, key); + + if (index < 0) { + return false; + } + var lastIndex = data.length - 1; + if (index == lastIndex) { + data.pop(); + } else { + splice.call(data, index, 1); + } + return true; + } + + /** + * Gets the list cache value for `key`. + * + * @private + * @name get + * @memberOf ListCache + * @param {string} key The key of the value to get. + * @returns {*} Returns the entry value. + */ + function listCacheGet(key) { + var data = this.__data__, + index = assocIndexOf(data, key); + + return index < 0 ? undefined : data[index][1]; + } + + /** + * Checks if a list cache value for `key` exists. + * + * @private + * @name has + * @memberOf ListCache + * @param {string} key The key of the entry to check. + * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. + */ + function listCacheHas(key) { + return assocIndexOf(this.__data__, key) > -1; + } + + /** + * Sets the list cache `key` to `value`. + * + * @private + * @name set + * @memberOf ListCache + * @param {string} key The key of the value to set. + * @param {*} value The value to set. + * @returns {Object} Returns the list cache instance. + */ + function listCacheSet(key, value) { + var data = this.__data__, + index = assocIndexOf(data, key); + + if (index < 0) { + data.push([key, value]); + } else { + data[index][1] = value; + } + return this; + } + + // Add methods to `ListCache`. + ListCache.prototype.clear = listCacheClear; + ListCache.prototype['delete'] = listCacheDelete; + ListCache.prototype.get = listCacheGet; + ListCache.prototype.has = listCacheHas; + ListCache.prototype.set = listCacheSet; + + /** + * Creates a map cache object to store key-value pairs. + * + * @private + * @constructor + * @param {Array} [entries] The key-value pairs to cache. + */ + function MapCache(entries) { + var index = -1, + length = entries ? entries.length : 0; + + this.clear(); + while (++index < length) { + var entry = entries[index]; + this.set(entry[0], entry[1]); + } + } + + /** + * Removes all key-value entries from the map. + * + * @private + * @name clear + * @memberOf MapCache + */ + function mapCacheClear() { + this.__data__ = { + 'hash': new Hash, + 'map': new (Map || ListCache), + 'string': new Hash + }; + } + + /** + * Removes `key` and its value from the map. + * + * @private + * @name delete + * @memberOf MapCache + * @param {string} key The key of the value to remove. + * @returns {boolean} Returns `true` if the entry was removed, else `false`. + */ + function mapCacheDelete(key) { + return getMapData(this, key)['delete'](key); + } + + /** + * Gets the map value for `key`. + * + * @private + * @name get + * @memberOf MapCache + * @param {string} key The key of the value to get. + * @returns {*} Returns the entry value. + */ + function mapCacheGet(key) { + return getMapData(this, key).get(key); + } + + /** + * Checks if a map value for `key` exists. + * + * @private + * @name has + * @memberOf MapCache + * @param {string} key The key of the entry to check. + * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. + */ + function mapCacheHas(key) { + return getMapData(this, key).has(key); + } + + /** + * Sets the map `key` to `value`. + * + * @private + * @name set + * @memberOf MapCache + * @param {string} key The key of the value to set. + * @param {*} value The value to set. + * @returns {Object} Returns the map cache instance. + */ + function mapCacheSet(key, value) { + getMapData(this, key).set(key, value); + return this; + } + + // Add methods to `MapCache`. + MapCache.prototype.clear = mapCacheClear; + MapCache.prototype['delete'] = mapCacheDelete; + MapCache.prototype.get = mapCacheGet; + MapCache.prototype.has = mapCacheHas; + MapCache.prototype.set = mapCacheSet; + + /** + * Gets the index at which the `key` is found in `array` of key-value pairs. + * + * @private + * @param {Array} array The array to inspect. + * @param {*} key The key to search for. + * @returns {number} Returns the index of the matched value, else `-1`. + */ + function assocIndexOf(array, key) { + var length = array.length; + while (length--) { + if (eq(array[length][0], key)) { + return length; + } + } + return -1; + } + + /** + * The base implementation of `_.isNative` without bad shim checks. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a native function, + * else `false`. + */ + function baseIsNative(value) { + if (!isObject(value) || isMasked(value)) { + return false; + } + var pattern = (isFunction(value) || isHostObject(value)) ? reIsNative : reIsHostCtor; + return pattern.test(toSource(value)); + } + + /** + * The base implementation of `_.toString` which doesn't convert nullish + * values to empty strings. + * + * @private + * @param {*} value The value to process. + * @returns {string} Returns the string. + */ + function baseToString(value) { + // Exit early for strings to avoid a performance hit in some environments. + if (typeof value == 'string') { + return value; + } + if (isSymbol(value)) { + return symbolToString ? symbolToString.call(value) : ''; + } + var result = (value + ''); + return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result; + } + + /** + * Casts `value` to a path array if it's not one. + * + * @private + * @param {*} value The value to inspect. + * @returns {Array} Returns the cast property path array. + */ + function castPath(value) { + return isArray(value) ? value : stringToPath(value); + } + + /** + * Gets the data for `map`. + * + * @private + * @param {Object} map The map to query. + * @param {string} key The reference key. + * @returns {*} Returns the map data. + */ + function getMapData(map, key) { + var data = map.__data__; + return isKeyable(key) + ? data[typeof key == 'string' ? 'string' : 'hash'] + : data.map; + } + + /** + * Gets the native function at `key` of `object`. + * + * @private + * @param {Object} object The object to query. + * @param {string} key The key of the method to get. + * @returns {*} Returns the function if it's native, else `undefined`. + */ + function getNative(object, key) { + var value = getValue(object, key); + return baseIsNative(value) ? value : undefined; + } + + /** + * Checks if `value` is a property name and not a property path. + * + * @private + * @param {*} value The value to check. + * @param {Object} [object] The object to query keys on. + * @returns {boolean} Returns `true` if `value` is a property name, else `false`. + */ + function isKey(value, object) { + if (isArray(value)) { + return false; + } + var type = typeof value; + if (type == 'number' || type == 'symbol' || type == 'boolean' || + value == null || isSymbol(value)) { + return true; + } + return reIsPlainProp.test(value) || !reIsDeepProp.test(value) || + (object != null && value in Object(object)); + } + + /** + * Checks if `value` is suitable for use as unique object key. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is suitable, else `false`. + */ + function isKeyable(value) { + var type = typeof value; + return (type == 'string' || type == 'number' || type == 'symbol' || type == 'boolean') + ? (value !== '__proto__') + : (value === null); + } + + /** + * Checks if `func` has its source masked. + * + * @private + * @param {Function} func The function to check. + * @returns {boolean} Returns `true` if `func` is masked, else `false`. + */ + function isMasked(func) { + return !!maskSrcKey && (maskSrcKey in func); + } + + /** + * Converts `string` to a property path array. + * + * @private + * @param {string} string The string to convert. + * @returns {Array} Returns the property path array. + */ + var stringToPath = memoize(function(string) { + string = toString(string); + + var result = []; + if (reLeadingDot.test(string)) { + result.push(''); + } + string.replace(rePropName, function(match, number, quote, string) { + result.push(quote ? string.replace(reEscapeChar, '$1') : (number || match)); + }); + return result; + }); + + /** + * Converts `value` to a string key if it's not a string or symbol. + * + * @private + * @param {*} value The value to inspect. + * @returns {string|symbol} Returns the key. + */ + function toKey(value) { + if (typeof value == 'string' || isSymbol(value)) { + return value; + } + var result = (value + ''); + return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result; + } + + /** + * Converts `func` to its source code. + * + * @private + * @param {Function} func The function to process. + * @returns {string} Returns the source code. + */ + function toSource(func) { + if (func != null) { + try { + return funcToString.call(func); + } catch (e) {} + try { + return (func + ''); + } catch (e) {} + } + return ''; + } + + /** + * Creates a function that memoizes the result of `func`. If `resolver` is + * provided, it determines the cache key for storing the result based on the + * arguments provided to the memoized function. By default, the first argument + * provided to the memoized function is used as the map cache key. The `func` + * is invoked with the `this` binding of the memoized function. + * + * **Note:** The cache is exposed as the `cache` property on the memoized + * function. Its creation may be customized by replacing the `_.memoize.Cache` + * constructor with one whose instances implement the + * [`Map`](http://ecma-international.org/ecma-262/7.0/#sec-properties-of-the-map-prototype-object) + * method interface of `delete`, `get`, `has`, and `set`. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Function + * @param {Function} func The function to have its output memoized. + * @param {Function} [resolver] The function to resolve the cache key. + * @returns {Function} Returns the new memoized function. + * @example + * + * var object = { 'a': 1, 'b': 2 }; + * var other = { 'c': 3, 'd': 4 }; + * + * var values = _.memoize(_.values); + * values(object); + * // => [1, 2] + * + * values(other); + * // => [3, 4] + * + * object.a = 2; + * values(object); + * // => [1, 2] + * + * // Modify the result cache. + * values.cache.set(object, ['a', 'b']); + * values(object); + * // => ['a', 'b'] + * + * // Replace `_.memoize.Cache`. + * _.memoize.Cache = WeakMap; + */ + function memoize(func, resolver) { + if (typeof func != 'function' || (resolver && typeof resolver != 'function')) { + throw new TypeError(FUNC_ERROR_TEXT); + } + var memoized = function() { + var args = arguments, + key = resolver ? resolver.apply(this, args) : args[0], + cache = memoized.cache; + + if (cache.has(key)) { + return cache.get(key); + } + var result = func.apply(this, args); + memoized.cache = cache.set(key, result); + return result; + }; + memoized.cache = new (memoize.Cache || MapCache); + return memoized; + } + + // Assign cache to `_.memoize`. + memoize.Cache = MapCache; + + /** + * Performs a + * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) + * comparison between two values to determine if they are equivalent. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {boolean} Returns `true` if the values are equivalent, else `false`. + * @example + * + * var object = { 'a': 1 }; + * var other = { 'a': 1 }; + * + * _.eq(object, object); + * // => true + * + * _.eq(object, other); + * // => false + * + * _.eq('a', 'a'); + * // => true + * + * _.eq('a', Object('a')); + * // => false + * + * _.eq(NaN, NaN); + * // => true + */ + function eq(value, other) { + return value === other || (value !== value && other !== other); + } + + /** + * Checks if `value` is classified as an `Array` object. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an array, else `false`. + * @example + * + * _.isArray([1, 2, 3]); + * // => true + * + * _.isArray(document.body.children); + * // => false + * + * _.isArray('abc'); + * // => false + * + * _.isArray(_.noop); + * // => false + */ + var isArray = Array.isArray; + + /** + * Checks if `value` is classified as a `Function` object. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a function, else `false`. + * @example + * + * _.isFunction(_); + * // => true + * + * _.isFunction(/abc/); + * // => false + */ + function isFunction(value) { + // The use of `Object#toString` avoids issues with the `typeof` operator + // in Safari 8-9 which returns 'object' for typed array and other constructors. + var tag = isObject(value) ? objectToString.call(value) : ''; + return tag == funcTag || tag == genTag; + } + + /** + * Checks if `value` is the + * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types) + * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an object, else `false`. + * @example + * + * _.isObject({}); + * // => true + * + * _.isObject([1, 2, 3]); + * // => true + * + * _.isObject(_.noop); + * // => true + * + * _.isObject(null); + * // => false + */ + function isObject(value) { + var type = typeof value; + return !!value && (type == 'object' || type == 'function'); + } + + /** + * Checks if `value` is object-like. A value is object-like if it's not `null` + * and has a `typeof` result of "object". + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is object-like, else `false`. + * @example + * + * _.isObjectLike({}); + * // => true + * + * _.isObjectLike([1, 2, 3]); + * // => true + * + * _.isObjectLike(_.noop); + * // => false + * + * _.isObjectLike(null); + * // => false + */ + function isObjectLike(value) { + return !!value && typeof value == 'object'; + } + + /** + * Checks if `value` is classified as a `Symbol` primitive or object. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a symbol, else `false`. + * @example + * + * _.isSymbol(Symbol.iterator); + * // => true + * + * _.isSymbol('abc'); + * // => false + */ + function isSymbol(value) { + return typeof value == 'symbol' || + (isObjectLike(value) && objectToString.call(value) == symbolTag); + } + + /** + * Converts `value` to a string. An empty string is returned for `null` + * and `undefined` values. The sign of `-0` is preserved. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to process. + * @returns {string} Returns the string. + * @example + * + * _.toString(null); + * // => '' + * + * _.toString(-0); + * // => '-0' + * + * _.toString([1, 2, 3]); + * // => '1,2,3' + */ + function toString(value) { + return value == null ? '' : baseToString(value); + } + + /** + * This method is like `_.get` except that if the resolved value is a + * function it's invoked with the `this` binding of its parent object and + * its result is returned. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Object + * @param {Object} object The object to query. + * @param {Array|string} path The path of the property to resolve. + * @param {*} [defaultValue] The value returned for `undefined` resolved values. + * @returns {*} Returns the resolved value. + * @example + * + * var object = { 'a': [{ 'b': { 'c1': 3, 'c2': _.constant(4) } }] }; + * + * _.result(object, 'a[0].b.c1'); + * // => 3 + * + * _.result(object, 'a[0].b.c2'); + * // => 4 + * + * _.result(object, 'a[0].b.c3', 'default'); + * // => 'default' + * + * _.result(object, 'a[0].b.c3', _.constant('default')); + * // => 'default' + */ + function result(object, path, defaultValue) { + path = isKey(path, object) ? [path] : castPath(path); + + var index = -1, + length = path.length; + + // Ensure the loop is entered when path is empty. + if (!length) { + object = undefined; + length = 1; + } + while (++index < length) { + var value = object == null ? undefined : object[toKey(path[index])]; + if (value === undefined) { + index = length; + value = defaultValue; + } + object = isFunction(value) ? value.call(object) : value; + } + return object; + } + + module.exports = result; + + /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }()))) + +/***/ }, +/* 20 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + module.exports = __webpack_require__(21); + +/***/ }, +/* 21 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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 _base_object = __webpack_require__(4); + + var _base_object2 = _interopRequireDefault(_base_object); + + var _events = __webpack_require__(5); + + var _events2 = _interopRequireDefault(_events); + + var _container = __webpack_require__(22); + + var _container2 = _interopRequireDefault(_container); + + var _clapprZepto = __webpack_require__(16); + + var _clapprZepto2 = _interopRequireDefault(_clapprZepto); + + var _lodash = __webpack_require__(26); + + var _lodash2 = _interopRequireDefault(_lodash); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + 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; } // Copyright 2014 Globo.com Player authors. All rights reserved. + // Use of this source code is governed by a BSD-style + // license that can be found in the LICENSE file. + + /** + * The ContainerFactory is responsible for manage playback bootstrap and create containers. + */ + + var ContainerFactory = function (_BaseObject) { + _inherits(ContainerFactory, _BaseObject); + + _createClass(ContainerFactory, [{ + key: 'options', + get: function get() { + return this._options; + }, + set: function set(options) { + this._options = options; + } + }]); + + function ContainerFactory(options, loader, i18n) { + _classCallCheck(this, ContainerFactory); + + var _this = _possibleConstructorReturn(this, _BaseObject.call(this, options)); + + _this._i18n = i18n; + _this.loader = loader; + return _this; + } + + ContainerFactory.prototype.createContainers = function createContainers() { + var _this2 = this; + + return _clapprZepto2.default.Deferred(function (promise) { + promise.resolve(_this2.options.sources.map(function (source) { + return _this2.createContainer(source); + })); + }); + }; + + ContainerFactory.prototype.findPlaybackPlugin = function findPlaybackPlugin(source, mimeType) { + return this.loader.playbackPlugins.filter(function (p) { + return p.canPlay(source, mimeType); + })[0]; + }; + + ContainerFactory.prototype.createContainer = function createContainer(source) { + var resolvedSource = null, + mimeType = this.options.mimeType; + if ((0, _lodash2.default)(source)) { + resolvedSource = source.source.toString(); + if (source.mimeType) { + mimeType = source.mimeType; + } + } else { + resolvedSource = source.toString(); + } + + if (resolvedSource.match(/^\/\//)) resolvedSource = window.location.protocol + resolvedSource; + + var options = _clapprZepto2.default.extend({}, this.options, { + src: resolvedSource, + mimeType: mimeType + }); + var playbackPlugin = this.findPlaybackPlugin(resolvedSource, mimeType); + var playback = new playbackPlugin(options, this._i18n); + + options = _clapprZepto2.default.extend({}, options, { playback: playback }); + + var container = new _container2.default(options, this._i18n); + var defer = _clapprZepto2.default.Deferred(); + defer.promise(container); + this.addContainerPlugins(container); + this.listenToOnce(container, _events2.default.CONTAINER_READY, function () { + return defer.resolve(container); + }); + return container; + }; + + ContainerFactory.prototype.addContainerPlugins = function addContainerPlugins(container) { + this.loader.containerPlugins.forEach(function (Plugin) { + container.addPlugin(new Plugin(container)); + }); + }; + + return ContainerFactory; + }(_base_object2.default); + + exports.default = ContainerFactory; + module.exports = exports['default']; + +/***/ }, +/* 22 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + module.exports = __webpack_require__(23); + +/***/ }, +/* 23 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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 _events = __webpack_require__(5); + + var _events2 = _interopRequireDefault(_events); + + var _ui_object = __webpack_require__(18); + + var _ui_object2 = _interopRequireDefault(_ui_object); + + var _styler = __webpack_require__(15); + + var _styler2 = _interopRequireDefault(_styler); + + var _style = __webpack_require__(24); + + var _style2 = _interopRequireDefault(_style); + + var _clapprZepto = __webpack_require__(16); + + var _clapprZepto2 = _interopRequireDefault(_clapprZepto); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + 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; } // Copyright 2014 Globo.com Player authors. All rights reserved. + // Use of this source code is governed by a BSD-style + // license that can be found in the LICENSE file. + + /** + * Container is responsible for the video rendering and state + */ + + /** + * An abstraction to represent a container for a given playback + * TODO: describe its responsabilities + * @class Container + * @constructor + * @extends UIObject + * @module base + */ + var Container = function (_UIObject) { + _inherits(Container, _UIObject); + + _createClass(Container, [{ + key: 'name', + + /** + * container's name + * @method name + * @default Container + * @return {String} container's name + */ + get: function get() { + return 'Container'; + } + }, { + key: 'attributes', + get: function get() { + return { class: 'container', 'data-container': '' }; + } + }, { + key: 'events', + get: function get() { + return { + 'click': 'clicked', + 'dblclick': 'dblClicked', + 'doubleTap': 'dblClicked', + 'contextmenu': 'onContextMenu', + 'mouseenter': 'mouseEnter', + 'mouseleave': 'mouseLeave' + }; + } + + /** + * Determine if the playback has ended. + * @property ended + * @type Boolean + */ + + }, { + key: 'ended', + get: function get() { + return this.playback.ended; + } + + /** + * Determine if the playback is having to buffer in order for + * playback to be smooth. + * (i.e if a live stream is playing smoothly, this will be false) + * @property buffering + * @type Boolean + */ + + }, { + key: 'buffering', + get: function get() { + return this.playback.buffering; + } + + /** + * The internationalization plugin. + * @property i18n + * @type {Strings} + */ + + }, { + key: 'i18n', + get: function get() { + return this._i18n; + } + + /** + * it builds a container + * @method constructor + * @param {Object} options the options object + * @param {Strings} i18n the internationalization component + */ + + }]); + + function Container(options, i18n) { + _classCallCheck(this, Container); + + var _this = _possibleConstructorReturn(this, _UIObject.call(this, options)); + + _this._i18n = i18n; + _this.currentTime = 0; + _this.volume = 100; + _this.playback = options.playback; + _this.settings = _clapprZepto2.default.extend({}, _this.playback.settings); + _this.isReady = false; + _this.mediaControlDisabled = false; + _this.plugins = [_this.playback]; + _this.bindEvents(); + return _this; + } + + /** + * binds playback events to the methods of the container. + * it listens to playback's events and triggers them as container events. + * + * | Playback | + * |----------| + * | progress | + * | timeupdate | + * | ready | + * | buffering | + * | bufferfull | + * | settingsupdate | + * | loadedmetadata | + * | highdefinitionupdate | + * | bitrate | + * | playbackstate | + * | dvr | + * | mediacontrol_disable | + * | mediacontrol_enable | + * | ended | + * | play | + * | pause | + * | error | + * + * ps: the events usually translate from PLABACK_x to CONTAINER_x, you can check all the events at `Event` class. + * + * @method bindEvents + */ + + + Container.prototype.bindEvents = function bindEvents() { + this.listenTo(this.playback, _events2.default.PLAYBACK_PROGRESS, this.progress); + this.listenTo(this.playback, _events2.default.PLAYBACK_TIMEUPDATE, this.timeUpdated); + this.listenTo(this.playback, _events2.default.PLAYBACK_READY, this.ready); + this.listenTo(this.playback, _events2.default.PLAYBACK_BUFFERING, this.onBuffering); + this.listenTo(this.playback, _events2.default.PLAYBACK_BUFFERFULL, this.bufferfull); + this.listenTo(this.playback, _events2.default.PLAYBACK_SETTINGSUPDATE, this.settingsUpdate); + this.listenTo(this.playback, _events2.default.PLAYBACK_LOADEDMETADATA, this.loadedMetadata); + this.listenTo(this.playback, _events2.default.PLAYBACK_HIGHDEFINITIONUPDATE, this.highDefinitionUpdate); + this.listenTo(this.playback, _events2.default.PLAYBACK_BITRATE, this.updateBitrate); + this.listenTo(this.playback, _events2.default.PLAYBACK_PLAYBACKSTATE, this.playbackStateChanged); + this.listenTo(this.playback, _events2.default.PLAYBACK_DVR, this.playbackDvrStateChanged); + this.listenTo(this.playback, _events2.default.PLAYBACK_MEDIACONTROL_DISABLE, this.disableMediaControl); + this.listenTo(this.playback, _events2.default.PLAYBACK_MEDIACONTROL_ENABLE, this.enableMediaControl); + this.listenTo(this.playback, _events2.default.PLAYBACK_ENDED, this.onEnded); + this.listenTo(this.playback, _events2.default.PLAYBACK_PLAY, this.playing); + this.listenTo(this.playback, _events2.default.PLAYBACK_PAUSE, this.paused); + this.listenTo(this.playback, _events2.default.PLAYBACK_STOP, this.stopped); + this.listenTo(this.playback, _events2.default.PLAYBACK_ERROR, this.error); + }; + + Container.prototype.playbackStateChanged = function playbackStateChanged(state) { + this.trigger(_events2.default.CONTAINER_PLAYBACKSTATE, state); + }; + + Container.prototype.playbackDvrStateChanged = function playbackDvrStateChanged(dvrInUse) { + this.settings = this.playback.settings; + this.dvrInUse = dvrInUse; + this.trigger(_events2.default.CONTAINER_PLAYBACKDVRSTATECHANGED, dvrInUse); + }; + + Container.prototype.updateBitrate = function updateBitrate(newBitrate) { + this.trigger(_events2.default.CONTAINER_BITRATE, newBitrate); + }; + + Container.prototype.statsReport = function statsReport(metrics) { + this.trigger(_events2.default.CONTAINER_STATS_REPORT, metrics); + }; + + Container.prototype.getPlaybackType = function getPlaybackType() { + return this.playback.getPlaybackType(); + }; + + /** + * returns `true` if DVR is enable otherwise `false`. + * @method isDvrEnabled + * @return {Boolean} + */ + + + Container.prototype.isDvrEnabled = function isDvrEnabled() { + return !!this.playback.dvrEnabled; + }; + + /** + * returns `true` if DVR is in use otherwise `false`. + * @method isDvrInUse + * @return {Boolean} + */ + + + Container.prototype.isDvrInUse = function isDvrInUse() { + return !!this.dvrInUse; + }; + + /** + * destroys the container + * @method destroy + */ + + + Container.prototype.destroy = function destroy() { + this.trigger(_events2.default.CONTAINER_DESTROYED, this, this.name); + this.stopListening(); + this.plugins.forEach(function (plugin) { + return plugin.destroy(); + }); + this.$el.remove(); + }; + + Container.prototype.setStyle = function setStyle(style) { + this.$el.css(style); + }; + + Container.prototype.animate = function animate(style, duration) { + return this.$el.animate(style, duration).promise(); + }; + + Container.prototype.ready = function ready() { + this.isReady = true; + this.trigger(_events2.default.CONTAINER_READY, this.name); + }; + + Container.prototype.isPlaying = function isPlaying() { + return this.playback.isPlaying(); + }; + + Container.prototype.getStartTimeOffset = function getStartTimeOffset() { + return this.playback.getStartTimeOffset(); + }; + + Container.prototype.getCurrentTime = function getCurrentTime() { + return this.currentTime; + }; + + Container.prototype.getDuration = function getDuration() { + return this.playback.getDuration(); + }; + + Container.prototype.error = function error(errorObj) { + if (!this.isReady) { + this.ready(); + } + this.trigger(_events2.default.CONTAINER_ERROR, { error: errorObj, container: this }, this.name); + }; + + Container.prototype.loadedMetadata = function loadedMetadata(metadata) { + this.trigger(_events2.default.CONTAINER_LOADEDMETADATA, metadata); + }; + + Container.prototype.timeUpdated = function timeUpdated(timeProgress) { + this.currentTime = timeProgress.current; + this.trigger(_events2.default.CONTAINER_TIMEUPDATE, timeProgress, this.name); + }; + + Container.prototype.progress = function progress() { + for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } + + this.trigger.apply(this, [_events2.default.CONTAINER_PROGRESS].concat(args, [this.name])); + }; + + Container.prototype.playing = function playing() { + this.trigger(_events2.default.CONTAINER_PLAY, this.name); + }; + + Container.prototype.paused = function paused() { + this.trigger(_events2.default.CONTAINER_PAUSE, this.name); + }; + + /** + * plays the playback + * @method play + */ + + + Container.prototype.play = function play() { + this.playback.play(); + }; + + /** + * stops the playback + * @method stop + */ + + + Container.prototype.stop = function stop() { + this.playback.stop(); + this.currentTime = 0; + }; + + /** + * pauses the playback + * @method pause + */ + + + Container.prototype.pause = function pause() { + this.playback.pause(); + }; + + Container.prototype.onEnded = function onEnded() { + this.trigger(_events2.default.CONTAINER_ENDED, this, this.name); + this.currentTime = 0; + }; + + Container.prototype.stopped = function stopped() { + this.trigger(_events2.default.CONTAINER_STOP); + }; + + Container.prototype.clicked = function clicked() { + if (!this.options.chromeless || this.options.allowUserInteraction) { + this.trigger(_events2.default.CONTAINER_CLICK, this, this.name); + } + }; + + Container.prototype.dblClicked = function dblClicked() { + if (!this.options.chromeless || this.options.allowUserInteraction) { + this.trigger(_events2.default.CONTAINER_DBLCLICK, this, this.name); + } + }; + + Container.prototype.onContextMenu = function onContextMenu() { + if (!this.options.chromeless || this.options.allowUserInteraction) { + this.trigger(_events2.default.CONTAINER_CONTEXTMENU, this, this.name); + } + }; + + Container.prototype.seek = function seek(time) { + this.trigger(_events2.default.CONTAINER_SEEK, time, this.name); + this.playback.seek(time); + }; + + Container.prototype.seekPercentage = function seekPercentage(percentage) { + var duration = this.getDuration(); + if (percentage >= 0 && percentage <= 100) { + var time = duration * (percentage / 100); + this.seek(time); + } + }; + + Container.prototype.setVolume = function setVolume(value) { + this.volume = parseInt(value, 10); + this.trigger(_events2.default.CONTAINER_VOLUME, value, this.name); + this.playback.volume(value); + }; + + Container.prototype.fullscreen = function fullscreen() { + this.trigger(_events2.default.CONTAINER_FULLSCREEN, this.name); + }; + + Container.prototype.onBuffering = function onBuffering() { + this.trigger(_events2.default.CONTAINER_STATE_BUFFERING, this.name); + }; + + Container.prototype.bufferfull = function bufferfull() { + this.trigger(_events2.default.CONTAINER_STATE_BUFFERFULL, this.name); + }; + + /** + * adds plugin to the container + * @method addPlugin + * @param {Object} plugin + */ + + + Container.prototype.addPlugin = function addPlugin(plugin) { + this.plugins.push(plugin); + }; + + /** + * checks if a plugin, given its name, exist + * @method hasPlugin + * @param {String} name + * @return {Boolean} + */ + + + Container.prototype.hasPlugin = function hasPlugin(name) { + return !!this.getPlugin(name); + }; + + /** + * get the plugin given its name + * @method getPlugin + * @param {String} name + */ + + + Container.prototype.getPlugin = function getPlugin(name) { + return this.plugins.filter(function (plugin) { + return plugin.name === name; + })[0]; + }; + + Container.prototype.mouseEnter = function mouseEnter() { + if (!this.options.chromeless || this.options.allowUserInteraction) { + this.trigger(_events2.default.CONTAINER_MOUSE_ENTER); + } + }; + + Container.prototype.mouseLeave = function mouseLeave() { + if (!this.options.chromeless || this.options.allowUserInteraction) { + this.trigger(_events2.default.CONTAINER_MOUSE_LEAVE); + } + }; + + Container.prototype.settingsUpdate = function settingsUpdate() { + this.settings = this.playback.settings; + this.trigger(_events2.default.CONTAINER_SETTINGSUPDATE); + }; + + Container.prototype.highDefinitionUpdate = function highDefinitionUpdate(isHD) { + this.trigger(_events2.default.CONTAINER_HIGHDEFINITIONUPDATE, isHD); + }; + + Container.prototype.isHighDefinitionInUse = function isHighDefinitionInUse() { + return this.playback.isHighDefinitionInUse(); + }; + + Container.prototype.disableMediaControl = function disableMediaControl() { + if (!this.mediaControlDisabled) { + this.mediaControlDisabled = true; + this.trigger(_events2.default.CONTAINER_MEDIACONTROL_DISABLE); + } + }; + + Container.prototype.enableMediaControl = function enableMediaControl() { + if (this.mediaControlDisabled) { + this.mediaControlDisabled = false; + this.trigger(_events2.default.CONTAINER_MEDIACONTROL_ENABLE); + } + }; + + Container.prototype.updateStyle = function updateStyle() { + if (!this.options.chromeless || this.options.allowUserInteraction) { + this.$el.removeClass('chromeless'); + } else { + this.$el.addClass('chromeless'); + } + }; + + /** + * enables to configure the container after its creation + * @method configure + * @param {Object} options all the options to change in form of a javascript object + */ + + + Container.prototype.configure = function configure(options) { + this._options = _clapprZepto2.default.extend(this._options, options); + this.updateStyle(); + this.trigger(_events2.default.CONTAINER_OPTIONS_CHANGE); + }; + + Container.prototype.render = function render() { + var s = _styler2.default.getStyleFor(_style2.default); + this.$el.append(s); + this.$el.append(this.playback.render().el); + this.updateStyle(); + return this; + }; + + return Container; + }(_ui_object2.default); + + exports.default = Container; + module.exports = exports['default']; + +/***/ }, +/* 24 */ +/***/ function(module, exports, __webpack_require__) { + + exports = module.exports = __webpack_require__(25)(); + // imports + + + // module + exports.push([module.id, ".container[data-container] {\n position: absolute;\n background-color: black;\n height: 100%;\n width: 100%; }\n .container[data-container] .chromeless {\n cursor: default; }\n\n[data-player]:not(.nocursor) .container[data-container]:not(.chromeless).pointer-enabled {\n cursor: pointer; }\n", ""]); + + // exports + + +/***/ }, +/* 25 */ +/***/ function(module, exports) { + + /*
+ MIT License http://www.opensource.org/licenses/mit-license.php
+ Author Tobias Koppers @sokra
+ */
+ // css base code, injected by the css-loader
+ module.exports = function() {
+ var list = [];
+
+ // return the list of modules as css string
+ list.toString = function toString() {
+ var result = [];
+ for(var i = 0; i < this.length; i++) {
+ var item = this[i];
+ if(item[2]) {
+ result.push("@media " + item[2] + "{" + item[1] + "}");
+ } else {
+ result.push(item[1]);
+ }
+ }
+ return result.join("");
+ };
+
+ // import a list of modules into the list
+ list.i = function(modules, mediaQuery) {
+ if(typeof modules === "string")
+ modules = [[null, modules, ""]];
+ var alreadyImportedModules = {};
+ for(var i = 0; i < this.length; i++) {
+ var id = this[i][0];
+ if(typeof id === "number")
+ alreadyImportedModules[id] = true;
+ }
+ for(i = 0; i < modules.length; i++) {
+ var item = modules[i];
+ // skip already imported module
+ // this implementation is not 100% perfect for weird media query combinations
+ // when a module is imported multiple times with different media queries.
+ // I hope this will never occur (Hey this way we have smaller bundles)
+ if(typeof item[0] !== "number" || !alreadyImportedModules[item[0]]) {
+ if(mediaQuery && !item[2]) {
+ item[2] = mediaQuery;
+ } else if(mediaQuery) {
+ item[2] = "(" + item[2] + ") and (" + mediaQuery + ")";
+ }
+ list.push(item);
+ }
+ }
+ };
+ return list;
+ };
+ + +/***/ }, +/* 26 */ +/***/ function(module, exports) { + + /** + * lodash (Custom Build) <https://lodash.com/> + * Build: `lodash modularize exports="npm" -o ./` + * Copyright jQuery Foundation and other contributors <https://jquery.org/> + * Released under MIT license <https://lodash.com/license> + * Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE> + * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors + */ + + /** `Object#toString` result references. */ + var objectTag = '[object Object]'; + + /** + * Checks if `value` is a host object in IE < 9. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a host object, else `false`. + */ + function isHostObject(value) { + // Many host objects are `Object` objects that can coerce to strings + // despite having improperly defined `toString` methods. + var result = false; + if (value != null && typeof value.toString != 'function') { + try { + result = !!(value + ''); + } catch (e) {} + } + return result; + } + + /** + * Creates a unary function that invokes `func` with its argument transformed. + * + * @private + * @param {Function} func The function to wrap. + * @param {Function} transform The argument transform. + * @returns {Function} Returns the new function. + */ + function overArg(func, transform) { + return function(arg) { + return func(transform(arg)); + }; + } + + /** Used for built-in method references. */ + var funcProto = Function.prototype, + objectProto = Object.prototype; + + /** Used to resolve the decompiled source of functions. */ + var funcToString = funcProto.toString; + + /** Used to check objects for own properties. */ + var hasOwnProperty = objectProto.hasOwnProperty; + + /** Used to infer the `Object` constructor. */ + var objectCtorString = funcToString.call(Object); + + /** + * Used to resolve the + * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring) + * of values. + */ + var objectToString = objectProto.toString; + + /** Built-in value references. */ + var getPrototype = overArg(Object.getPrototypeOf, Object); + + /** + * Checks if `value` is object-like. A value is object-like if it's not `null` + * and has a `typeof` result of "object". + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is object-like, else `false`. + * @example + * + * _.isObjectLike({}); + * // => true + * + * _.isObjectLike([1, 2, 3]); + * // => true + * + * _.isObjectLike(_.noop); + * // => false + * + * _.isObjectLike(null); + * // => false + */ + function isObjectLike(value) { + return !!value && typeof value == 'object'; + } + + /** + * Checks if `value` is a plain object, that is, an object created by the + * `Object` constructor or one with a `[[Prototype]]` of `null`. + * + * @static + * @memberOf _ + * @since 0.8.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a plain object, else `false`. + * @example + * + * function Foo() { + * this.a = 1; + * } + * + * _.isPlainObject(new Foo); + * // => false + * + * _.isPlainObject([1, 2, 3]); + * // => false + * + * _.isPlainObject({ 'x': 0, 'y': 0 }); + * // => true + * + * _.isPlainObject(Object.create(null)); + * // => true + */ + function isPlainObject(value) { + if (!isObjectLike(value) || + objectToString.call(value) != objectTag || isHostObject(value)) { + return false; + } + var proto = getPrototype(value); + if (proto === null) { + return true; + } + var Ctor = hasOwnProperty.call(proto, 'constructor') && proto.constructor; + return (typeof Ctor == 'function' && + Ctor instanceof Ctor && funcToString.call(Ctor) == objectCtorString); + } + + module.exports = isPlainObject; + + +/***/ }, +/* 27 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + module.exports = __webpack_require__(28); + +/***/ }, +/* 28 */ +/***/ function(module, exports, __webpack_require__) { + + /* WEBPACK VAR INJECTION */(function(process) {'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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 _utils = __webpack_require__(2); + + var _vendor = __webpack_require__(8); + + var _events = __webpack_require__(5); + + var _events2 = _interopRequireDefault(_events); + + var _styler = __webpack_require__(15); + + var _styler2 = _interopRequireDefault(_styler); + + var _ui_object = __webpack_require__(18); + + var _ui_object2 = _interopRequireDefault(_ui_object); + + var _browser = __webpack_require__(3); + + var _browser2 = _interopRequireDefault(_browser); + + var _mediator = __webpack_require__(30); + + var _mediator2 = _interopRequireDefault(_mediator); + + var _template = __webpack_require__(17); + + var _template2 = _interopRequireDefault(_template); + + var _playback = __webpack_require__(31); + + var _playback2 = _interopRequireDefault(_playback); + + var _clapprZepto = __webpack_require__(16); + + var _clapprZepto2 = _interopRequireDefault(_clapprZepto); + + var _lodash = __webpack_require__(32); + + var _lodash2 = _interopRequireDefault(_lodash); + + var _mediaControl = __webpack_require__(34); + + var _mediaControl2 = _interopRequireDefault(_mediaControl); + + var _mediaControl3 = __webpack_require__(36); + + var _mediaControl4 = _interopRequireDefault(_mediaControl3); + + var _play = __webpack_require__(37); + + var _play2 = _interopRequireDefault(_play); + + var _pause = __webpack_require__(38); + + var _pause2 = _interopRequireDefault(_pause); + + var _stop = __webpack_require__(39); + + var _stop2 = _interopRequireDefault(_stop); + + var _volume = __webpack_require__(40); + + var _volume2 = _interopRequireDefault(_volume); + + var _mute = __webpack_require__(41); + + var _mute2 = _interopRequireDefault(_mute); + + var _expand = __webpack_require__(42); + + var _expand2 = _interopRequireDefault(_expand); + + var _shrink = __webpack_require__(43); + + var _shrink2 = _interopRequireDefault(_shrink); + + var _hd = __webpack_require__(44); + + var _hd2 = _interopRequireDefault(_hd); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + 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; } // Copyright 2014 Globo.com Player authors. All rights reserved. + // Use of this source code is governed by a BSD-style + // license that can be found in the LICENSE file. + + /** + * The MediaControl is responsible for displaying the Player controls. + */ + + var MediaControl = function (_UIObject) { + _inherits(MediaControl, _UIObject); + + _createClass(MediaControl, [{ + key: 'name', + get: function get() { + return 'MediaControl'; + } + }, { + key: 'disabled', + get: function get() { + return this.userDisabled || this.container && this.container.getPlaybackType() === _playback2.default.NO_OP; + } + }, { + key: 'attributes', + get: function get() { + return { + 'class': 'media-control', + 'data-media-control': '' + }; + } + }, { + key: 'events', + get: function get() { + return { + 'click [data-play]': 'play', + 'click [data-pause]': 'pause', + 'click [data-playpause]': 'togglePlayPause', + 'click [data-stop]': 'stop', + 'click [data-playstop]': 'togglePlayStop', + 'click [data-fullscreen]': 'toggleFullscreen', + 'click .bar-container[data-seekbar]': 'seek', + 'click .bar-container[data-volume]': 'onVolumeClick', + 'click .drawer-icon[data-volume]': 'toggleMute', + 'mouseenter .drawer-container[data-volume]': 'showVolumeBar', + 'mouseleave .drawer-container[data-volume]': 'hideVolumeBar', + 'mousedown .bar-container[data-volume]': 'startVolumeDrag', + 'mousemove .bar-container[data-volume]': 'mousemoveOnVolumeBar', + 'mousedown .bar-scrubber[data-seekbar]': 'startSeekDrag', + 'mousemove .bar-container[data-seekbar]': 'mousemoveOnSeekBar', + 'mouseleave .bar-container[data-seekbar]': 'mouseleaveOnSeekBar', + 'mouseenter .media-control-layer[data-controls]': 'setUserKeepVisible', + 'mouseleave .media-control-layer[data-controls]': 'resetUserKeepVisible' + }; + } + }, { + key: 'template', + get: function get() { + return (0, _template2.default)(_mediaControl4.default); + } + }, { + key: 'stylesheet', + get: function get() { + return _styler2.default.getStyleFor(_mediaControl2.default, { baseUrl: this.options.baseUrl }); + } + }, { + key: 'volume', + get: function get() { + return this.container && this.container.isReady ? this.container.volume : this.intendedVolume; + } + }, { + key: 'muted', + get: function get() { + return this.volume === 0; + } + }]); + + function MediaControl(options) { + _classCallCheck(this, MediaControl); + + var _this = _possibleConstructorReturn(this, _UIObject.call(this, options)); + + _this.persistConfig = _this.options.persistConfig; + _this.container = options.container; + _this.currentPositionValue = null; + _this.currentDurationValue = null; + var initialVolume = _this.persistConfig ? _utils.Config.restore('volume') : 100; + _this.setVolume(_this.options.mute ? 0 : initialVolume); + _this.keepVisible = false; + _this.fullScreenOnVideoTagSupported = null; // unknown + _this.addEventListeners(); + _this.settings = { + left: ['play', 'stop', 'pause'], + right: ['volume'], + default: ['position', 'seekbar', 'duration'] + }; + + if (_this.container) { + if (!_clapprZepto2.default.isEmptyObject(_this.container.settings)) { + _this.settings = _clapprZepto2.default.extend({}, _this.container.settings); + } + } else { + _this.settings = {}; + } + + _this.userDisabled = false; + if (_this.container && _this.container.mediaControlDisabled || _this.options.chromeless) { + _this.disable(); + } + _this.stopDragHandler = function (event) { + return _this.stopDrag(event); + }; + _this.updateDragHandler = function (event) { + return _this.updateDrag(event); + }; + (0, _clapprZepto2.default)(document).bind('mouseup', _this.stopDragHandler); + (0, _clapprZepto2.default)(document).bind('mousemove', _this.updateDragHandler); + return _this; + } + + MediaControl.prototype.addEventListeners = function addEventListeners() { + if (this.container) { + _mediator2.default.on(this.options.playerId + ':' + _events2.default.PLAYER_RESIZE, this.playerResize, this); + this.listenTo(this.container, _events2.default.CONTAINER_PLAY, this.changeTogglePlay); + this.listenTo(this.container, _events2.default.CONTAINER_PAUSE, this.changeTogglePlay); + this.listenTo(this.container, _events2.default.CONTAINER_DBLCLICK, this.toggleFullscreen); + this.listenTo(this.container, _events2.default.CONTAINER_TIMEUPDATE, this.onTimeUpdate); + this.listenTo(this.container, _events2.default.CONTAINER_PROGRESS, this.updateProgressBar); + this.listenTo(this.container, _events2.default.CONTAINER_SETTINGSUPDATE, this.settingsUpdate); + this.listenTo(this.container, _events2.default.CONTAINER_PLAYBACKDVRSTATECHANGED, this.settingsUpdate); + this.listenTo(this.container, _events2.default.CONTAINER_HIGHDEFINITIONUPDATE, this.highDefinitionUpdate); + this.listenTo(this.container, _events2.default.CONTAINER_MEDIACONTROL_DISABLE, this.disable); + this.listenTo(this.container, _events2.default.CONTAINER_MEDIACONTROL_ENABLE, this.enable); + this.listenTo(this.container, _events2.default.CONTAINER_ENDED, this.ended); + this.listenTo(this.container, _events2.default.CONTAINER_VOLUME, this.onVolumeChanged); + if (this.container.playback.el.nodeName.toLowerCase() === 'video') { + // wait until the metadata has loaded and then check if fullscreen on video tag is supported + this.listenToOnce(this.container, _events2.default.CONTAINER_LOADEDMETADATA, this.onLoadedMetadataOnVideoTag); + } + } + }; + + MediaControl.prototype.disable = function disable() { + this.userDisabled = true; + this.hide(); + this.$el.hide(); + }; + + MediaControl.prototype.enable = function enable() { + if (this.options.chromeless) return; + this.userDisabled = false; + this.show(); + }; + + MediaControl.prototype.play = function play() { + this.container.play(); + }; + + MediaControl.prototype.pause = function pause() { + this.container.pause(); + }; + + MediaControl.prototype.stop = function stop() { + this.container.stop(); + }; + + MediaControl.prototype.onVolumeChanged = function onVolumeChanged() { + this.updateVolumeUI(); + }; + + MediaControl.prototype.onLoadedMetadataOnVideoTag = function onLoadedMetadataOnVideoTag() { + var video = this.container.playback.el; + // video.webkitSupportsFullscreen is deprecated but iOS appears to only use this + // see https://github.com/clappr/clappr/issues/1127 + if (!_utils.Fullscreen.fullscreenEnabled() && video.webkitSupportsFullscreen) { + this.fullScreenOnVideoTagSupported = true; + this.settingsUpdate(); + } + }; + + MediaControl.prototype.updateVolumeUI = function updateVolumeUI() { + if (!this.rendered) { + // this will be called after a render + return; + } + // update volume bar scrubber/fill on bar mode + this.$volumeBarContainer.find('.bar-fill-2').css({}); + var containerWidth = this.$volumeBarContainer.width(); + var barWidth = this.$volumeBarBackground.width(); + var offset = (containerWidth - barWidth) / 2.0; + var pos = barWidth * this.volume / 100.0 + offset; + this.$volumeBarFill.css({ width: this.volume + '%' }); + this.$volumeBarScrubber.css({ left: pos }); + + // update volume bar segments on segmented bar mode + this.$volumeBarContainer.find('.segmented-bar-element').removeClass('fill'); + var item = Math.ceil(this.volume / 10.0); + this.$volumeBarContainer.find('.segmented-bar-element').slice(0, item).addClass('fill'); + this.$volumeIcon.html(''); + this.$volumeIcon.removeClass('muted'); + if (!this.muted) { + this.$volumeIcon.append(_volume2.default); + } else { + this.$volumeIcon.append(_mute2.default); + this.$volumeIcon.addClass('muted'); + } + this.applyButtonStyle(this.$volumeIcon); + }; + + MediaControl.prototype.changeTogglePlay = function changeTogglePlay() { + this.$playPauseToggle.html(''); + this.$playStopToggle.html(''); + if (this.container && this.container.isPlaying()) { + this.$playPauseToggle.append(_pause2.default); + this.$playStopToggle.append(_stop2.default); + this.trigger(_events2.default.MEDIACONTROL_PLAYING); + } else { + this.$playPauseToggle.append(_play2.default); + this.$playStopToggle.append(_play2.default); + this.trigger(_events2.default.MEDIACONTROL_NOTPLAYING); + if (_browser2.default.isMobile) { + this.show(); + } + } + this.applyButtonStyle(this.$playPauseToggle); + this.applyButtonStyle(this.$playStopToggle); + }; + + MediaControl.prototype.mousemoveOnSeekBar = function mousemoveOnSeekBar(event) { + if (this.settings.seekEnabled) { + var offsetX = event.pageX - this.$seekBarContainer.offset().left - this.$seekBarHover.width() / 2; + this.$seekBarHover.css({ left: offsetX }); + } + this.trigger(_events2.default.MEDIACONTROL_MOUSEMOVE_SEEKBAR, event); + }; + + MediaControl.prototype.mouseleaveOnSeekBar = function mouseleaveOnSeekBar(event) { + this.trigger(_events2.default.MEDIACONTROL_MOUSELEAVE_SEEKBAR, event); + }; + + MediaControl.prototype.onVolumeClick = function onVolumeClick(event) { + this.setVolume(this.getVolumeFromUIEvent(event)); + }; + + MediaControl.prototype.mousemoveOnVolumeBar = function mousemoveOnVolumeBar(event) { + if (this.draggingVolumeBar) { + this.setVolume(this.getVolumeFromUIEvent(event)); + } + }; + + MediaControl.prototype.playerResize = function playerResize(size) { + this.$fullscreenToggle.html(''); + if (_utils.Fullscreen.isFullscreen()) { + this.$fullscreenToggle.append(_shrink2.default); + } else { + this.$fullscreenToggle.append(_expand2.default); + } + this.applyButtonStyle(this.$fullscreenToggle); + this.$el.removeClass('w320'); + if (size.width <= 320 || this.options.hideVolumeBar) { + this.$el.addClass('w320'); + } + }; + + MediaControl.prototype.togglePlayPause = function togglePlayPause() { + if (this.container.isPlaying()) { + this.container.pause(); + } else { + this.container.play(); + } + return false; + }; + + MediaControl.prototype.togglePlayStop = function togglePlayStop() { + if (this.container.isPlaying()) { + this.container.stop(); + } else { + this.container.play(); + } + }; + + MediaControl.prototype.startSeekDrag = function startSeekDrag(event) { + if (!this.settings.seekEnabled) return; + this.draggingSeekBar = true; + this.$el.addClass('dragging'); + this.$seekBarLoaded.addClass('media-control-notransition'); + this.$seekBarPosition.addClass('media-control-notransition'); + this.$seekBarScrubber.addClass('media-control-notransition'); + if (event) { + event.preventDefault(); + } + }; + + MediaControl.prototype.startVolumeDrag = function startVolumeDrag(event) { + this.draggingVolumeBar = true; + this.$el.addClass('dragging'); + if (event) { + event.preventDefault(); + } + }; + + MediaControl.prototype.stopDrag = function stopDrag(event) { + if (this.draggingSeekBar) { + this.seek(event); + } + this.$el.removeClass('dragging'); + this.$seekBarLoaded.removeClass('media-control-notransition'); + this.$seekBarPosition.removeClass('media-control-notransition'); + this.$seekBarScrubber.removeClass('media-control-notransition dragging'); + this.draggingSeekBar = false; + this.draggingVolumeBar = false; + }; + + MediaControl.prototype.updateDrag = function updateDrag(event) { + if (this.draggingSeekBar) { + event.preventDefault(); + var offsetX = event.pageX - this.$seekBarContainer.offset().left; + var pos = offsetX / this.$seekBarContainer.width() * 100; + pos = Math.min(100, Math.max(pos, 0)); + this.setSeekPercentage(pos); + } else if (this.draggingVolumeBar) { + event.preventDefault(); + this.setVolume(this.getVolumeFromUIEvent(event)); + } + }; + + MediaControl.prototype.getVolumeFromUIEvent = function getVolumeFromUIEvent(event) { + var offsetY = event.pageX - this.$volumeBarContainer.offset().left; + var volumeFromUI = offsetY / this.$volumeBarContainer.width() * 100; + return volumeFromUI; + }; + + MediaControl.prototype.toggleMute = function toggleMute() { + this.setVolume(this.muted ? 100 : 0); + }; + + MediaControl.prototype.setVolume = function setVolume(value) { + var _this2 = this; + + value = Math.min(100, Math.max(value, 0)); + // this will hold the intended volume + // it may not actually get set to this straight away + // if the container is not ready etc + this.intendedVolume = value; + this.persistConfig && _utils.Config.persist('volume', value); + var setWhenContainerReady = function setWhenContainerReady() { + if (_this2.container.isReady) { + _this2.container.setVolume(value); + } else { + _this2.listenToOnce(_this2.container, _events2.default.CONTAINER_READY, function () { + _this2.container.setVolume(value); + }); + } + }; + + if (!this.container) { + this.listenToOnce(this, _events2.default.MEDIACONTROL_CONTAINERCHANGED, function () { + setWhenContainerReady(); + }); + } else { + setWhenContainerReady(); + } + }; + + MediaControl.prototype.toggleFullscreen = function toggleFullscreen() { + this.trigger(_events2.default.MEDIACONTROL_FULLSCREEN, this.name); + this.container.fullscreen(); + this.resetUserKeepVisible(); + }; + + MediaControl.prototype.setContainer = function setContainer(container) { + if (this.container) { + this.stopListening(this.container); + this.fullScreenOnVideoTagSupported = null; + } + _mediator2.default.off(this.options.playerId + ':' + _events2.default.PLAYER_RESIZE, this.playerResize, this); + this.container = container; + // set the new container to match the volume of the last one + this.setVolume(this.intendedVolume); + this.changeTogglePlay(); + this.addEventListeners(); + this.settingsUpdate(); + this.container.trigger(_events2.default.CONTAINER_PLAYBACKDVRSTATECHANGED, this.container.isDvrInUse()); + if (this.container.mediaControlDisabled) { + this.disable(); + } + this.trigger(_events2.default.MEDIACONTROL_CONTAINERCHANGED); + }; + + MediaControl.prototype.showVolumeBar = function showVolumeBar() { + if (this.hideVolumeId) { + clearTimeout(this.hideVolumeId); + } + this.$volumeBarContainer.removeClass('volume-bar-hide'); + }; + + MediaControl.prototype.hideVolumeBar = function hideVolumeBar() { + var _this3 = this; + + var timeout = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 400; + + if (!this.$volumeBarContainer) return; + if (this.draggingVolumeBar) { + this.hideVolumeId = setTimeout(function () { + return _this3.hideVolumeBar(); + }, timeout); + } else { + if (this.hideVolumeId) { + clearTimeout(this.hideVolumeId); + } + this.hideVolumeId = setTimeout(function () { + return _this3.$volumeBarContainer.addClass('volume-bar-hide'); + }, timeout); + } + }; + + MediaControl.prototype.ended = function ended() { + this.changeTogglePlay(); + }; + + MediaControl.prototype.updateProgressBar = function updateProgressBar(progress) { + var loadedStart = progress.start / progress.total * 100; + var loadedEnd = progress.current / progress.total * 100; + this.$seekBarLoaded.css({ left: loadedStart + '%', width: loadedEnd - loadedStart + '%' }); + }; + + MediaControl.prototype.onTimeUpdate = function onTimeUpdate(timeProgress) { + if (this.draggingSeekBar) return; + // TODO why should current time ever be negative? + var position = timeProgress.current < 0 ? timeProgress.total : timeProgress.current; + + this.currentPositionValue = position; + this.currentDurationValue = timeProgress.total; + this.renderSeekBar(); + }; + + MediaControl.prototype.renderSeekBar = function renderSeekBar() { + if (this.currentPositionValue === null || this.currentDurationValue === null) { + // this will be triggered as soon as these beocome available + return; + } + + // default to 100% + this.currentSeekBarPercentage = 100; + if (this.container.getPlaybackType() !== _playback2.default.LIVE || this.container.isDvrInUse()) { + this.currentSeekBarPercentage = this.currentPositionValue / this.currentDurationValue * 100; + } + this.setSeekPercentage(this.currentSeekBarPercentage); + + var newPosition = (0, _utils.formatTime)(this.currentPositionValue); + var newDuration = (0, _utils.formatTime)(this.currentDurationValue); + if (newPosition !== this.displayedPosition) { + this.$position.text(newPosition); + this.displayedPosition = newPosition; + } + if (newDuration !== this.displayedDuration) { + this.$duration.text(newDuration); + this.displayedDuration = newDuration; + } + }; + + MediaControl.prototype.seek = function seek(event) { + if (!this.settings.seekEnabled) return; + var offsetX = event.pageX - this.$seekBarContainer.offset().left; + var pos = offsetX / this.$seekBarContainer.width() * 100; + pos = Math.min(100, Math.max(pos, 0)); + this.container.seekPercentage(pos); + this.setSeekPercentage(pos); + return false; + }; + + MediaControl.prototype.setKeepVisible = function setKeepVisible() { + this.keepVisible = true; + }; + + MediaControl.prototype.resetKeepVisible = function resetKeepVisible() { + this.keepVisible = false; + }; + + MediaControl.prototype.setUserKeepVisible = function setUserKeepVisible() { + this.userKeepVisible = true; + }; + + MediaControl.prototype.resetUserKeepVisible = function resetUserKeepVisible() { + this.userKeepVisible = false; + }; + + MediaControl.prototype.isVisible = function isVisible() { + return !this.$el.hasClass('media-control-hide'); + }; + + MediaControl.prototype.show = function show(event) { + var _this4 = this; + + if (this.disabled) { + return; + } + var timeout = 2000; + if (!event || event.clientX !== this.lastMouseX && event.clientY !== this.lastMouseY || navigator.userAgent.match(/firefox/i)) { + clearTimeout(this.hideId); + this.$el.show(); + this.trigger(_events2.default.MEDIACONTROL_SHOW, this.name); + this.$el.removeClass('media-control-hide'); + this.hideId = setTimeout(function () { + return _this4.hide(); + }, timeout); + if (event) { + this.lastMouseX = event.clientX; + this.lastMouseY = event.clientY; + } + } + }; + + MediaControl.prototype.hide = function hide() { + var _this5 = this; + + var delay = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; + + if (!this.isVisible() || _browser2.default.isMobile && !this.container.isPlaying()) { + return; + } + var timeout = delay || 2000; + clearTimeout(this.hideId); + if (!this.disabled && this.options.hideMediaControl === false) { + return; + } + if (!this.disabled && (delay || this.userKeepVisible || this.keepVisible || this.draggingSeekBar || this.draggingVolumeBar)) { + this.hideId = setTimeout(function () { + return _this5.hide(); + }, timeout); + } else { + this.trigger(_events2.default.MEDIACONTROL_HIDE, this.name); + this.$el.addClass('media-control-hide'); + this.hideVolumeBar(0); + } + }; + + MediaControl.prototype.settingsUpdate = function settingsUpdate() { + var newSettings = this.getSettings(); + if (newSettings && !this.fullScreenOnVideoTagSupported && !_utils.Fullscreen.fullscreenEnabled()) { + // remove fullscreen from settings if it is present + newSettings.default && (0, _utils.removeArrayItem)(newSettings.default, 'fullscreen'); + newSettings.left && (0, _utils.removeArrayItem)(newSettings.left, 'fullscreen'); + newSettings.right && (0, _utils.removeArrayItem)(newSettings.right, 'fullscreen'); + } + var settingsChanged = JSON.stringify(this.settings) !== JSON.stringify(newSettings); + if (settingsChanged) { + this.settings = newSettings; + this.render(); + } + }; + + MediaControl.prototype.getSettings = function getSettings() { + return (0, _lodash2.default)({}, this.container.settings); + }; + + MediaControl.prototype.highDefinitionUpdate = function highDefinitionUpdate(isHD) { + var method = isHD ? 'addClass' : 'removeClass'; + this.$hdIndicator[method]('enabled'); + }; + + MediaControl.prototype.createCachedElements = function createCachedElements() { + var $layer = this.$el.find('.media-control-layer'); + this.$duration = $layer.find('.media-control-indicator[data-duration]'); + this.$fullscreenToggle = $layer.find('button.media-control-button[data-fullscreen]'); + this.$playPauseToggle = $layer.find('button.media-control-button[data-playpause]'); + this.$playStopToggle = $layer.find('button.media-control-button[data-playstop]'); + this.$position = $layer.find('.media-control-indicator[data-position]'); + this.$seekBarContainer = $layer.find('.bar-container[data-seekbar]'); + this.$seekBarHover = $layer.find('.bar-hover[data-seekbar]'); + this.$seekBarLoaded = $layer.find('.bar-fill-1[data-seekbar]'); + this.$seekBarPosition = $layer.find('.bar-fill-2[data-seekbar]'); + this.$seekBarScrubber = $layer.find('.bar-scrubber[data-seekbar]'); + this.$volumeBarContainer = $layer.find('.bar-container[data-volume]'); + this.$volumeContainer = $layer.find('.drawer-container[data-volume]'); + this.$volumeIcon = $layer.find('.drawer-icon[data-volume]'); + this.$volumeBarBackground = this.$el.find('.bar-background[data-volume]'); + this.$volumeBarFill = this.$el.find('.bar-fill-1[data-volume]'); + this.$volumeBarScrubber = this.$el.find('.bar-scrubber[data-volume]'); + this.$hdIndicator = this.$el.find('button.media-control-button[data-hd-indicator]'); + this.resetIndicators(); + this.initializeIcons(); + }; + + MediaControl.prototype.resetIndicators = function resetIndicators() { + this.displayedPosition = this.$position.text(); + this.displayedDuration = this.$duration.text(); + }; + + MediaControl.prototype.initializeIcons = function initializeIcons() { + var $layer = this.$el.find('.media-control-layer'); + $layer.find('button.media-control-button[data-play]').append(_play2.default); + $layer.find('button.media-control-button[data-pause]').append(_pause2.default); + $layer.find('button.media-control-button[data-stop]').append(_stop2.default); + this.$playPauseToggle.append(_play2.default); + this.$playStopToggle.append(_play2.default); + this.$volumeIcon.append(_volume2.default); + this.$fullscreenToggle.append(_expand2.default); + this.$hdIndicator.append(_hd2.default); + }; + + MediaControl.prototype.setSeekPercentage = function setSeekPercentage(value) { + value = Math.max(Math.min(value, 100.0), 0); + if (this.displayedSeekBarPercentage === value) { + // not changed since last update + return; + } + this.displayedSeekBarPercentage = value; + + this.$seekBarPosition.removeClass('media-control-notransition'); + this.$seekBarScrubber.removeClass('media-control-notransition'); + this.$seekBarPosition.css({ width: value + '%' }); + this.$seekBarScrubber.css({ left: value + '%' }); + }; + + MediaControl.prototype.seekRelative = function seekRelative(delta) { + if (!this.settings.seekEnabled) return; + var currentTime = this.container.getCurrentTime(); + var duration = this.container.getDuration(); + var position = Math.min(Math.max(currentTime + delta, 0), duration); + position = Math.min(position * 100 / duration, 100); + this.container.seekPercentage(position); + }; + + MediaControl.prototype.bindKeyEvents = function bindKeyEvents() { + var _this6 = this; + + this.unbindKeyEvents(); + this.kibo = new _vendor.Kibo(this.options.focusElement); + this.kibo.down(['space'], function () { + return _this6.togglePlayPause(); + }); + this.kibo.down(['left'], function () { + return _this6.seekRelative(-15); + }); + this.kibo.down(['right'], function () { + return _this6.seekRelative(15); + }); + var keys = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]; + keys.forEach(function (i) { + _this6.kibo.down(i.toString(), function () { + return _this6.settings.seekEnabled && _this6.container.seekPercentage(i * 10); + }); + }); + }; + + MediaControl.prototype.unbindKeyEvents = function unbindKeyEvents() { + if (this.kibo) { + this.kibo.off('space'); + this.kibo.off('left'); + this.kibo.off('right'); + this.kibo.off([1, 2, 3, 4, 5, 6, 7, 8, 9, 0]); + } + }; + + MediaControl.prototype.parseColors = function parseColors() { + if (this.options.mediacontrol) { + this.buttonsColor = this.options.mediacontrol.buttons; + var seekbarColor = this.options.mediacontrol.seekbar; + this.$el.find('.bar-fill-2[data-seekbar]').css('background-color', seekbarColor); + this.$el.find('.media-control-icon svg path').css('fill', this.buttonsColor); + this.$el.find('.segmented-bar-element[data-volume]').css('boxShadow', 'inset 2px 0 0 ' + this.buttonsColor); + } + }; + + MediaControl.prototype.applyButtonStyle = function applyButtonStyle(element) { + if (this.buttonsColor && element) { + (0, _clapprZepto2.default)(element).find('svg path').css('fill', this.buttonsColor); + } + }; + + MediaControl.prototype.destroy = function destroy() { + this.remove(); + (0, _clapprZepto2.default)(document).unbind('mouseup', this.stopDragHandler); + (0, _clapprZepto2.default)(document).unbind('mousemove', this.updateDragHandler); + this.unbindKeyEvents(); + }; + + MediaControl.prototype.render = function render() { + var _this7 = this; + + var timeout = 1000; + this.$el.html(this.template({ settings: this.settings })); + this.$el.append(this.stylesheet); + this.createCachedElements(); + this.$playPauseToggle.addClass('paused'); + this.$playStopToggle.addClass('stopped'); + + this.changeTogglePlay(); + this.hideId = setTimeout(function () { + return _this7.hide(); + }, timeout); + if (this.disabled) { + this.hide(); + } + + if (_browser2.default.isSafari && _browser2.default.isMobile) { + this.$volumeContainer.css('display', 'none'); + } + + this.$seekBarPosition.addClass('media-control-notransition'); + this.$seekBarScrubber.addClass('media-control-notransition'); + + var previousSeekPercentage = 0; + if (this.displayedSeekBarPercentage) { + previousSeekPercentage = this.displayedSeekBarPercentage; + } + this.displayedSeekBarPercentage = null; + this.setSeekPercentage(previousSeekPercentage); + + process.nextTick(function () { + if (!_this7.settings.seekEnabled) { + _this7.$seekBarContainer.addClass('seek-disabled'); + } + if (!_this7.options.disableKeyboardShortcuts) { + _this7.bindKeyEvents(); + } + _this7.playerResize({ width: _this7.options.width, height: _this7.options.height }); + _this7.hideVolumeBar(0); + }); + + this.parseColors(); + this.highDefinitionUpdate(); + + this.rendered = true; + this.updateVolumeUI(); + this.trigger(_events2.default.MEDIACONTROL_RENDERED); + return this; + }; + + return MediaControl; + }(_ui_object2.default); + + exports.default = MediaControl; + + + MediaControl.extend = function (properties) { + return (0, _utils.extend)(MediaControl, properties); + }; + module.exports = exports['default']; + /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(29))) + +/***/ }, +/* 29 */ +/***/ function(module, exports) { + + // shim for using process in browser + var process = module.exports = {}; + + // cached from whatever global is present so that test runners that stub it + // don't break things. But we need to wrap it in a try catch in case it is + // wrapped in strict mode code which doesn't define any globals. It's inside a + // function because try/catches deoptimize in certain engines. + + var cachedSetTimeout; + var cachedClearTimeout; + + function defaultSetTimout() { + throw new Error('setTimeout has not been defined'); + } + function defaultClearTimeout () { + throw new Error('clearTimeout has not been defined'); + } + (function () { + try { + if (typeof setTimeout === 'function') { + cachedSetTimeout = setTimeout; + } else { + cachedSetTimeout = defaultSetTimout; + } + } catch (e) { + cachedSetTimeout = defaultSetTimout; + } + try { + if (typeof clearTimeout === 'function') { + cachedClearTimeout = clearTimeout; + } else { + cachedClearTimeout = defaultClearTimeout; + } + } catch (e) { + cachedClearTimeout = defaultClearTimeout; + } + } ()) + function runTimeout(fun) { + if (cachedSetTimeout === setTimeout) { + //normal enviroments in sane situations + return setTimeout(fun, 0); + } + // if setTimeout wasn't available but was latter defined + if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) { + cachedSetTimeout = setTimeout; + return setTimeout(fun, 0); + } + try { + // when when somebody has screwed with setTimeout but no I.E. maddness + return cachedSetTimeout(fun, 0); + } catch(e){ + try { + // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally + return cachedSetTimeout.call(null, fun, 0); + } catch(e){ + // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error + return cachedSetTimeout.call(this, fun, 0); + } + } + + + } + function runClearTimeout(marker) { + if (cachedClearTimeout === clearTimeout) { + //normal enviroments in sane situations + return clearTimeout(marker); + } + // if clearTimeout wasn't available but was latter defined + if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) { + cachedClearTimeout = clearTimeout; + return clearTimeout(marker); + } + try { + // when when somebody has screwed with setTimeout but no I.E. maddness + return cachedClearTimeout(marker); + } catch (e){ + try { + // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally + return cachedClearTimeout.call(null, marker); + } catch (e){ + // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error. + // Some versions of I.E. have different rules for clearTimeout vs setTimeout + return cachedClearTimeout.call(this, marker); + } + } + + + + } + var queue = []; + var draining = false; + var currentQueue; + var queueIndex = -1; + + function cleanUpNextTick() { + if (!draining || !currentQueue) { + return; + } + draining = false; + if (currentQueue.length) { + queue = currentQueue.concat(queue); + } else { + queueIndex = -1; + } + if (queue.length) { + drainQueue(); + } + } + + function drainQueue() { + if (draining) { + return; + } + var timeout = runTimeout(cleanUpNextTick); + draining = true; + + var len = queue.length; + while(len) { + currentQueue = queue; + queue = []; + while (++queueIndex < len) { + if (currentQueue) { + currentQueue[queueIndex].run(); + } + } + queueIndex = -1; + len = queue.length; + } + currentQueue = null; + draining = false; + runClearTimeout(timeout); + } + + process.nextTick = function (fun) { + var args = new Array(arguments.length - 1); + if (arguments.length > 1) { + for (var i = 1; i < arguments.length; i++) { + args[i - 1] = arguments[i]; + } + } + queue.push(new Item(fun, args)); + if (queue.length === 1 && !draining) { + runTimeout(drainQueue); + } + }; + + // v8 likes predictible objects + function Item(fun, array) { + this.fun = fun; + this.array = array; + } + Item.prototype.run = function () { + this.fun.apply(null, this.array); + }; + process.title = 'browser'; + process.browser = true; + process.env = {}; + process.argv = []; + process.version = ''; // empty string to avoid regexp issues + process.versions = {}; + + function noop() {} + + process.on = noop; + process.addListener = noop; + process.once = noop; + process.off = noop; + process.removeListener = noop; + process.removeAllListeners = noop; + process.emit = noop; + + process.binding = function (name) { + throw new Error('process.binding is not supported'); + }; + + process.cwd = function () { return '/' }; + process.chdir = function (dir) { + throw new Error('process.chdir is not supported'); + }; + process.umask = function() { return 0; }; + + +/***/ }, +/* 30 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + var _events = __webpack_require__(5); + + var _events2 = _interopRequireDefault(_events); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } // Copyright 2014 Globo.com Player authors. All rights reserved. + // Use of this source code is governed by a BSD-style + // license that can be found in the LICENSE file. + + /** + * The mediator is a singleton for handling global events. + */ + + var events = new _events2.default(); + + var Mediator = function Mediator() { + _classCallCheck(this, Mediator); + }; + + exports.default = Mediator; + + + Mediator.on = function (name, callback, context) { + events.on(name, callback, context); + return; + }; + + Mediator.once = function (name, callback, context) { + events.once(name, callback, context); + return; + }; + + Mediator.off = function (name, callback, context) { + events.off(name, callback, context); + return; + }; + + Mediator.trigger = function (name) { + for (var _len = arguments.length, opts = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { + opts[_key - 1] = arguments[_key]; + } + + events.trigger.apply(events, [name].concat(opts)); + return; + }; + + Mediator.stopListening = function (obj, name, callback) { + events.stopListening(obj, name, callback); + return; + }; + module.exports = exports['default']; + +/***/ }, +/* 31 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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 _utils = __webpack_require__(2); + + var _ui_object = __webpack_require__(18); + + var _ui_object2 = _interopRequireDefault(_ui_object); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + 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; } + + /** + * An abstraction to represent a generic playback, it's like an interface to be implemented by subclasses. + * @class Playback + * @constructor + * @extends UIObject + * @module base + */ + var Playback = function (_UIObject) { + _inherits(Playback, _UIObject); + + _createClass(Playback, [{ + key: 'isAudioOnly', + + /** + * Determine if the playback does not contain video/has video but video should be ignored. + * @property isAudioOnly + * @type Boolean + */ + get: function get() { + return false; + } + + /** + * Determine if the playback has ended. + * @property ended + * @type Boolean + */ + + }, { + key: 'ended', + get: function get() { + return false; + } + + /** + * The internationalization plugin. + * @property i18n + * @type {Strings} + */ + + }, { + key: 'i18n', + get: function get() { + return this._i18n; + } + + /** + * Determine if the playback is having to buffer in order for + * playback to be smooth. + * (i.e if a live stream is playing smoothly, this will be false) + * @property buffering + * @type Boolean + */ + + }, { + key: 'buffering', + get: function get() { + return false; + } + + /** + * @method constructor + * @param {Object} options the options object + * @param {Strings} i18n the internationalization component + */ + + }]); + + function Playback(options, i18n) { + _classCallCheck(this, Playback); + + var _this = _possibleConstructorReturn(this, _UIObject.call(this, options)); + + _this.settings = {}; + _this._i18n = i18n; + return _this; + } + + /** + * plays the playback. + * @method play + */ + + + Playback.prototype.play = function play() {}; + + /** + * pauses the playback. + * @method pause + */ + + + Playback.prototype.pause = function pause() {}; + + /** + * stops the playback. + * @method stop + */ + + + Playback.prototype.stop = function stop() {}; + + /** + * seeks the playback to a given `time` in seconds + * @method seek + * @param {Number} time should be a number between 0 and the video duration + */ + + + Playback.prototype.seek = function seek(time) {}; // eslint-disable-line no-unused-vars + + /** + * seeks the playback to a given `percentage` in percentage + * @method seekPercentage + * @param {Number} time should be a number between 0 and 100 + */ + + + Playback.prototype.seekPercentage = function seekPercentage(percentage) {}; // eslint-disable-line no-unused-vars + + + /** + * The time that "0" now represents relative to when playback started. + * For a stream with a sliding window this will increase as content is + * removed from the beginning. + * @method getStartTimeOffset + * @return {Number} time (in seconds) that time "0" represents. + */ + + + Playback.prototype.getStartTimeOffset = function getStartTimeOffset() { + return 0; + }; + + /** + * gets the duration in seconds + * @method getDuration + * @return {Number} duration (in seconds) of the current source + */ + + + Playback.prototype.getDuration = function getDuration() { + return 0; + }; + + /** + * checks if the playback is playing. + * @method isPlaying + * @return {Boolean} `true` if the current playback is playing, otherwise `false` + */ + + + Playback.prototype.isPlaying = function isPlaying() { + return false; + }; + + /** + * checks if the playback is ready. + * @property isReady + * @type {Boolean} `true` if the current playback is ready, otherwise `false` + */ + + + /** + * gets the playback type (`'vod', 'live', 'aod'`) + * @method getPlaybackType + * @return {String} you should write the playback type otherwise it'll assume `'no_op'` + * @example + * ```javascript + * html5VideoPlayback.getPlaybackType() //vod + * html5AudioPlayback.getPlaybackType() //aod + * html5VideoPlayback.getPlaybackType() //live + * flashHlsPlayback.getPlaybackType() //live + * ``` + */ + Playback.prototype.getPlaybackType = function getPlaybackType() { + return Playback.NO_OP; + }; + + /** + * checks if the playback is in HD. + * @method isHighDefinitionInUse + * @return {Boolean} `true` if the playback is playing in HD, otherwise `false` + */ + + + Playback.prototype.isHighDefinitionInUse = function isHighDefinitionInUse() { + return false; + }; + + /** + * sets the volume for the playback + * @method volume + * @param {Number} value a number between 0 (`muted`) to 100 (`max`) + */ + + + Playback.prototype.volume = function volume(value) {}; // eslint-disable-line no-unused-vars + + /** + * destroys the playback, removing it from DOM + * @method destroy + */ + + + Playback.prototype.destroy = function destroy() { + this.$el.remove(); + }; + + _createClass(Playback, [{ + key: 'isReady', + get: function get() { + return false; + } + }]); + + return Playback; + }(_ui_object2.default); + + exports.default = Playback; + + + Playback.extend = function (properties) { + return (0, _utils.extend)(Playback, properties); + }; + + /** + * checks if the playback can play a given `source` + * If a mimeType is provided then this will be used instead of inferring the mimetype + * from the source extension. + * @method canPlay + * @static + * @param {String} source the given source ex: `http://example.com/play.mp4` + * @param {String} [mimeType] the given mime type, ex: `'application/vnd.apple.mpegurl'` + * @return {Boolean} `true` if the playback is playable, otherwise `false` + */ + Playback.canPlay = function (source, mimeType) { + // eslint-disable-line no-unused-vars + return false; + }; + + /** + * a playback type for video on demand + * + * @property VOD + * @static + * @type String + */ + Playback.VOD = 'vod'; + /** + * a playback type for audio on demand + * + * @property AOD + * @static + * @type String + */ + Playback.AOD = 'aod'; + /** + * a playback type for live video + * + * @property LIVE + * @static + * @type String + */ + Playback.LIVE = 'live'; + /** + * a default playback type + * + * @property NO_OP + * @static + * @type String + */ + Playback.NO_OP = 'no_op'; + /** + * the plugin type + * + * @property type + * @static + * @type String + */ + Playback.type = 'playback'; + module.exports = exports['default']; + +/***/ }, +/* 32 */ +/***/ function(module, exports, __webpack_require__) { + + /* WEBPACK VAR INJECTION */(function(global, module) {/** + * lodash (Custom Build) <https://lodash.com/> + * Build: `lodash modularize exports="npm" -o ./` + * Copyright jQuery Foundation and other contributors <https://jquery.org/> + * Released under MIT license <https://lodash.com/license> + * Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE> + * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors + */ + + /** Used as the size to enable large array optimizations. */ + var LARGE_ARRAY_SIZE = 200; + + /** Used to stand-in for `undefined` hash values. */ + var HASH_UNDEFINED = '__lodash_hash_undefined__'; + + /** Used as references for various `Number` constants. */ + var MAX_SAFE_INTEGER = 9007199254740991; + + /** `Object#toString` result references. */ + var argsTag = '[object Arguments]', + arrayTag = '[object Array]', + boolTag = '[object Boolean]', + dateTag = '[object Date]', + errorTag = '[object Error]', + funcTag = '[object Function]', + genTag = '[object GeneratorFunction]', + mapTag = '[object Map]', + numberTag = '[object Number]', + objectTag = '[object Object]', + promiseTag = '[object Promise]', + regexpTag = '[object RegExp]', + setTag = '[object Set]', + stringTag = '[object String]', + symbolTag = '[object Symbol]', + weakMapTag = '[object WeakMap]'; + + var arrayBufferTag = '[object ArrayBuffer]', + dataViewTag = '[object DataView]', + float32Tag = '[object Float32Array]', + float64Tag = '[object Float64Array]', + int8Tag = '[object Int8Array]', + int16Tag = '[object Int16Array]', + int32Tag = '[object Int32Array]', + uint8Tag = '[object Uint8Array]', + uint8ClampedTag = '[object Uint8ClampedArray]', + uint16Tag = '[object Uint16Array]', + uint32Tag = '[object Uint32Array]'; + + /** + * Used to match `RegExp` + * [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns). + */ + var reRegExpChar = /[\\^$.*+?()[\]{}|]/g; + + /** Used to match `RegExp` flags from their coerced string values. */ + var reFlags = /\w*$/; + + /** Used to detect host constructors (Safari). */ + var reIsHostCtor = /^\[object .+?Constructor\]$/; + + /** Used to detect unsigned integer values. */ + var reIsUint = /^(?:0|[1-9]\d*)$/; + + /** Used to identify `toStringTag` values of typed arrays. */ + var typedArrayTags = {}; + typedArrayTags[float32Tag] = typedArrayTags[float64Tag] = + typedArrayTags[int8Tag] = typedArrayTags[int16Tag] = + typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] = + typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] = + typedArrayTags[uint32Tag] = true; + typedArrayTags[argsTag] = typedArrayTags[arrayTag] = + typedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] = + typedArrayTags[dataViewTag] = typedArrayTags[dateTag] = + typedArrayTags[errorTag] = typedArrayTags[funcTag] = + typedArrayTags[mapTag] = typedArrayTags[numberTag] = + typedArrayTags[objectTag] = typedArrayTags[regexpTag] = + typedArrayTags[setTag] = typedArrayTags[stringTag] = + typedArrayTags[weakMapTag] = false; + + /** Used to identify `toStringTag` values supported by `_.clone`. */ + var cloneableTags = {}; + cloneableTags[argsTag] = cloneableTags[arrayTag] = + cloneableTags[arrayBufferTag] = cloneableTags[dataViewTag] = + cloneableTags[boolTag] = cloneableTags[dateTag] = + cloneableTags[float32Tag] = cloneableTags[float64Tag] = + cloneableTags[int8Tag] = cloneableTags[int16Tag] = + cloneableTags[int32Tag] = cloneableTags[mapTag] = + cloneableTags[numberTag] = cloneableTags[objectTag] = + cloneableTags[regexpTag] = cloneableTags[setTag] = + cloneableTags[stringTag] = cloneableTags[symbolTag] = + cloneableTags[uint8Tag] = cloneableTags[uint8ClampedTag] = + cloneableTags[uint16Tag] = cloneableTags[uint32Tag] = true; + cloneableTags[errorTag] = cloneableTags[funcTag] = + cloneableTags[weakMapTag] = false; + + /** Detect free variable `global` from Node.js. */ + var freeGlobal = typeof global == 'object' && global && global.Object === Object && global; + + /** Detect free variable `self`. */ + var freeSelf = typeof self == 'object' && self && self.Object === Object && self; + + /** Used as a reference to the global object. */ + var root = freeGlobal || freeSelf || Function('return this')(); + + /** Detect free variable `exports`. */ + var freeExports = typeof exports == 'object' && exports && !exports.nodeType && exports; + + /** Detect free variable `module`. */ + var freeModule = freeExports && typeof module == 'object' && module && !module.nodeType && module; + + /** Detect the popular CommonJS extension `module.exports`. */ + var moduleExports = freeModule && freeModule.exports === freeExports; + + /** Detect free variable `process` from Node.js. */ + var freeProcess = moduleExports && freeGlobal.process; + + /** Used to access faster Node.js helpers. */ + var nodeUtil = (function() { + try { + return freeProcess && freeProcess.binding('util'); + } catch (e) {} + }()); + + /* Node.js helper references. */ + var nodeIsTypedArray = nodeUtil && nodeUtil.isTypedArray; + + /** + * Adds the key-value `pair` to `map`. + * + * @private + * @param {Object} map The map to modify. + * @param {Array} pair The key-value pair to add. + * @returns {Object} Returns `map`. + */ + function addMapEntry(map, pair) { + // Don't return `map.set` because it's not chainable in IE 11. + map.set(pair[0], pair[1]); + return map; + } + + /** + * Adds `value` to `set`. + * + * @private + * @param {Object} set The set to modify. + * @param {*} value The value to add. + * @returns {Object} Returns `set`. + */ + function addSetEntry(set, value) { + // Don't return `set.add` because it's not chainable in IE 11. + set.add(value); + return set; + } + + /** + * A faster alternative to `Function#apply`, this function invokes `func` + * with the `this` binding of `thisArg` and the arguments of `args`. + * + * @private + * @param {Function} func The function to invoke. + * @param {*} thisArg The `this` binding of `func`. + * @param {Array} args The arguments to invoke `func` with. + * @returns {*} Returns the result of `func`. + */ + function apply(func, thisArg, args) { + switch (args.length) { + case 0: return func.call(thisArg); + case 1: return func.call(thisArg, args[0]); + case 2: return func.call(thisArg, args[0], args[1]); + case 3: return func.call(thisArg, args[0], args[1], args[2]); + } + return func.apply(thisArg, args); + } + + /** + * A specialized version of `_.forEach` for arrays without support for + * iteratee shorthands. + * + * @private + * @param {Array} [array] The array to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array} Returns `array`. + */ + function arrayEach(array, iteratee) { + var index = -1, + length = array ? array.length : 0; + + while (++index < length) { + if (iteratee(array[index], index, array) === false) { + break; + } + } + return array; + } + + /** + * Appends the elements of `values` to `array`. + * + * @private + * @param {Array} array The array to modify. + * @param {Array} values The values to append. + * @returns {Array} Returns `array`. + */ + function arrayPush(array, values) { + var index = -1, + length = values.length, + offset = array.length; + + while (++index < length) { + array[offset + index] = values[index]; + } + return array; + } + + /** + * A specialized version of `_.reduce` for arrays without support for + * iteratee shorthands. + * + * @private + * @param {Array} [array] The array to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @param {*} [accumulator] The initial value. + * @param {boolean} [initAccum] Specify using the first element of `array` as + * the initial value. + * @returns {*} Returns the accumulated value. + */ + function arrayReduce(array, iteratee, accumulator, initAccum) { + var index = -1, + length = array ? array.length : 0; + + if (initAccum && length) { + accumulator = array[++index]; + } + while (++index < length) { + accumulator = iteratee(accumulator, array[index], index, array); + } + return accumulator; + } + + /** + * The base implementation of `_.times` without support for iteratee shorthands + * or max array length checks. + * + * @private + * @param {number} n The number of times to invoke `iteratee`. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array} Returns the array of results. + */ + function baseTimes(n, iteratee) { + var index = -1, + result = Array(n); + + while (++index < n) { + result[index] = iteratee(index); + } + return result; + } + + /** + * The base implementation of `_.unary` without support for storing metadata. + * + * @private + * @param {Function} func The function to cap arguments for. + * @returns {Function} Returns the new capped function. + */ + function baseUnary(func) { + return function(value) { + return func(value); + }; + } + + /** + * Gets the value at `key` of `object`. + * + * @private + * @param {Object} [object] The object to query. + * @param {string} key The key of the property to get. + * @returns {*} Returns the property value. + */ + function getValue(object, key) { + return object == null ? undefined : object[key]; + } + + /** + * Checks if `value` is a host object in IE < 9. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a host object, else `false`. + */ + function isHostObject(value) { + // Many host objects are `Object` objects that can coerce to strings + // despite having improperly defined `toString` methods. + var result = false; + if (value != null && typeof value.toString != 'function') { + try { + result = !!(value + ''); + } catch (e) {} + } + return result; + } + + /** + * Converts `map` to its key-value pairs. + * + * @private + * @param {Object} map The map to convert. + * @returns {Array} Returns the key-value pairs. + */ + function mapToArray(map) { + var index = -1, + result = Array(map.size); + + map.forEach(function(value, key) { + result[++index] = [key, value]; + }); + return result; + } + + /** + * Creates a unary function that invokes `func` with its argument transformed. + * + * @private + * @param {Function} func The function to wrap. + * @param {Function} transform The argument transform. + * @returns {Function} Returns the new function. + */ + function overArg(func, transform) { + return function(arg) { + return func(transform(arg)); + }; + } + + /** + * Converts `set` to an array of its values. + * + * @private + * @param {Object} set The set to convert. + * @returns {Array} Returns the values. + */ + function setToArray(set) { + var index = -1, + result = Array(set.size); + + set.forEach(function(value) { + result[++index] = value; + }); + return result; + } + + /** Used for built-in method references. */ + var arrayProto = Array.prototype, + funcProto = Function.prototype, + objectProto = Object.prototype; + + /** Used to detect overreaching core-js shims. */ + var coreJsData = root['__core-js_shared__']; + + /** Used to detect methods masquerading as native. */ + var maskSrcKey = (function() { + var uid = /[^.]+$/.exec(coreJsData && coreJsData.keys && coreJsData.keys.IE_PROTO || ''); + return uid ? ('Symbol(src)_1.' + uid) : ''; + }()); + + /** Used to resolve the decompiled source of functions. */ + var funcToString = funcProto.toString; + + /** Used to check objects for own properties. */ + var hasOwnProperty = objectProto.hasOwnProperty; + + /** Used to infer the `Object` constructor. */ + var objectCtorString = funcToString.call(Object); + + /** + * Used to resolve the + * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring) + * of values. + */ + var objectToString = objectProto.toString; + + /** Used to detect if a method is native. */ + var reIsNative = RegExp('^' + + funcToString.call(hasOwnProperty).replace(reRegExpChar, '\\$&') + .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$' + ); + + /** Built-in value references. */ + var Buffer = moduleExports ? root.Buffer : undefined, + Symbol = root.Symbol, + Uint8Array = root.Uint8Array, + getPrototype = overArg(Object.getPrototypeOf, Object), + objectCreate = Object.create, + propertyIsEnumerable = objectProto.propertyIsEnumerable, + splice = arrayProto.splice; + + /* Built-in method references for those with the same name as other `lodash` methods. */ + var nativeGetSymbols = Object.getOwnPropertySymbols, + nativeIsBuffer = Buffer ? Buffer.isBuffer : undefined, + nativeKeys = overArg(Object.keys, Object), + nativeMax = Math.max; + + /* Built-in method references that are verified to be native. */ + var DataView = getNative(root, 'DataView'), + Map = getNative(root, 'Map'), + Promise = getNative(root, 'Promise'), + Set = getNative(root, 'Set'), + WeakMap = getNative(root, 'WeakMap'), + nativeCreate = getNative(Object, 'create'); + + /** Used to detect maps, sets, and weakmaps. */ + var dataViewCtorString = toSource(DataView), + mapCtorString = toSource(Map), + promiseCtorString = toSource(Promise), + setCtorString = toSource(Set), + weakMapCtorString = toSource(WeakMap); + + /** Used to convert symbols to primitives and strings. */ + var symbolProto = Symbol ? Symbol.prototype : undefined, + symbolValueOf = symbolProto ? symbolProto.valueOf : undefined; + + /** + * Creates a hash object. + * + * @private + * @constructor + * @param {Array} [entries] The key-value pairs to cache. + */ + function Hash(entries) { + var index = -1, + length = entries ? entries.length : 0; + + this.clear(); + while (++index < length) { + var entry = entries[index]; + this.set(entry[0], entry[1]); + } + } + + /** + * Removes all key-value entries from the hash. + * + * @private + * @name clear + * @memberOf Hash + */ + function hashClear() { + this.__data__ = nativeCreate ? nativeCreate(null) : {}; + } + + /** + * Removes `key` and its value from the hash. + * + * @private + * @name delete + * @memberOf Hash + * @param {Object} hash The hash to modify. + * @param {string} key The key of the value to remove. + * @returns {boolean} Returns `true` if the entry was removed, else `false`. + */ + function hashDelete(key) { + return this.has(key) && delete this.__data__[key]; + } + + /** + * Gets the hash value for `key`. + * + * @private + * @name get + * @memberOf Hash + * @param {string} key The key of the value to get. + * @returns {*} Returns the entry value. + */ + function hashGet(key) { + var data = this.__data__; + if (nativeCreate) { + var result = data[key]; + return result === HASH_UNDEFINED ? undefined : result; + } + return hasOwnProperty.call(data, key) ? data[key] : undefined; + } + + /** + * Checks if a hash value for `key` exists. + * + * @private + * @name has + * @memberOf Hash + * @param {string} key The key of the entry to check. + * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. + */ + function hashHas(key) { + var data = this.__data__; + return nativeCreate ? data[key] !== undefined : hasOwnProperty.call(data, key); + } + + /** + * Sets the hash `key` to `value`. + * + * @private + * @name set + * @memberOf Hash + * @param {string} key The key of the value to set. + * @param {*} value The value to set. + * @returns {Object} Returns the hash instance. + */ + function hashSet(key, value) { + var data = this.__data__; + data[key] = (nativeCreate && value === undefined) ? HASH_UNDEFINED : value; + return this; + } + + // Add methods to `Hash`. + Hash.prototype.clear = hashClear; + Hash.prototype['delete'] = hashDelete; + Hash.prototype.get = hashGet; + Hash.prototype.has = hashHas; + Hash.prototype.set = hashSet; + + /** + * Creates an list cache object. + * + * @private + * @constructor + * @param {Array} [entries] The key-value pairs to cache. + */ + function ListCache(entries) { + var index = -1, + length = entries ? entries.length : 0; + + this.clear(); + while (++index < length) { + var entry = entries[index]; + this.set(entry[0], entry[1]); + } + } + + /** + * Removes all key-value entries from the list cache. + * + * @private + * @name clear + * @memberOf ListCache + */ + function listCacheClear() { + this.__data__ = []; + } + + /** + * Removes `key` and its value from the list cache. + * + * @private + * @name delete + * @memberOf ListCache + * @param {string} key The key of the value to remove. + * @returns {boolean} Returns `true` if the entry was removed, else `false`. + */ + function listCacheDelete(key) { + var data = this.__data__, + index = assocIndexOf(data, key); + + if (index < 0) { + return false; + } + var lastIndex = data.length - 1; + if (index == lastIndex) { + data.pop(); + } else { + splice.call(data, index, 1); + } + return true; + } + + /** + * Gets the list cache value for `key`. + * + * @private + * @name get + * @memberOf ListCache + * @param {string} key The key of the value to get. + * @returns {*} Returns the entry value. + */ + function listCacheGet(key) { + var data = this.__data__, + index = assocIndexOf(data, key); + + return index < 0 ? undefined : data[index][1]; + } + + /** + * Checks if a list cache value for `key` exists. + * + * @private + * @name has + * @memberOf ListCache + * @param {string} key The key of the entry to check. + * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. + */ + function listCacheHas(key) { + return assocIndexOf(this.__data__, key) > -1; + } + + /** + * Sets the list cache `key` to `value`. + * + * @private + * @name set + * @memberOf ListCache + * @param {string} key The key of the value to set. + * @param {*} value The value to set. + * @returns {Object} Returns the list cache instance. + */ + function listCacheSet(key, value) { + var data = this.__data__, + index = assocIndexOf(data, key); + + if (index < 0) { + data.push([key, value]); + } else { + data[index][1] = value; + } + return this; + } + + // Add methods to `ListCache`. + ListCache.prototype.clear = listCacheClear; + ListCache.prototype['delete'] = listCacheDelete; + ListCache.prototype.get = listCacheGet; + ListCache.prototype.has = listCacheHas; + ListCache.prototype.set = listCacheSet; + + /** + * Creates a map cache object to store key-value pairs. + * + * @private + * @constructor + * @param {Array} [entries] The key-value pairs to cache. + */ + function MapCache(entries) { + var index = -1, + length = entries ? entries.length : 0; + + this.clear(); + while (++index < length) { + var entry = entries[index]; + this.set(entry[0], entry[1]); + } + } + + /** + * Removes all key-value entries from the map. + * + * @private + * @name clear + * @memberOf MapCache + */ + function mapCacheClear() { + this.__data__ = { + 'hash': new Hash, + 'map': new (Map || ListCache), + 'string': new Hash + }; + } + + /** + * Removes `key` and its value from the map. + * + * @private + * @name delete + * @memberOf MapCache + * @param {string} key The key of the value to remove. + * @returns {boolean} Returns `true` if the entry was removed, else `false`. + */ + function mapCacheDelete(key) { + return getMapData(this, key)['delete'](key); + } + + /** + * Gets the map value for `key`. + * + * @private + * @name get + * @memberOf MapCache + * @param {string} key The key of the value to get. + * @returns {*} Returns the entry value. + */ + function mapCacheGet(key) { + return getMapData(this, key).get(key); + } + + /** + * Checks if a map value for `key` exists. + * + * @private + * @name has + * @memberOf MapCache + * @param {string} key The key of the entry to check. + * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. + */ + function mapCacheHas(key) { + return getMapData(this, key).has(key); + } + + /** + * Sets the map `key` to `value`. + * + * @private + * @name set + * @memberOf MapCache + * @param {string} key The key of the value to set. + * @param {*} value The value to set. + * @returns {Object} Returns the map cache instance. + */ + function mapCacheSet(key, value) { + getMapData(this, key).set(key, value); + return this; + } + + // Add methods to `MapCache`. + MapCache.prototype.clear = mapCacheClear; + MapCache.prototype['delete'] = mapCacheDelete; + MapCache.prototype.get = mapCacheGet; + MapCache.prototype.has = mapCacheHas; + MapCache.prototype.set = mapCacheSet; + + /** + * Creates a stack cache object to store key-value pairs. + * + * @private + * @constructor + * @param {Array} [entries] The key-value pairs to cache. + */ + function Stack(entries) { + this.__data__ = new ListCache(entries); + } + + /** + * Removes all key-value entries from the stack. + * + * @private + * @name clear + * @memberOf Stack + */ + function stackClear() { + this.__data__ = new ListCache; + } + + /** + * Removes `key` and its value from the stack. + * + * @private + * @name delete + * @memberOf Stack + * @param {string} key The key of the value to remove. + * @returns {boolean} Returns `true` if the entry was removed, else `false`. + */ + function stackDelete(key) { + return this.__data__['delete'](key); + } + + /** + * Gets the stack value for `key`. + * + * @private + * @name get + * @memberOf Stack + * @param {string} key The key of the value to get. + * @returns {*} Returns the entry value. + */ + function stackGet(key) { + return this.__data__.get(key); + } + + /** + * Checks if a stack value for `key` exists. + * + * @private + * @name has + * @memberOf Stack + * @param {string} key The key of the entry to check. + * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. + */ + function stackHas(key) { + return this.__data__.has(key); + } + + /** + * Sets the stack `key` to `value`. + * + * @private + * @name set + * @memberOf Stack + * @param {string} key The key of the value to set. + * @param {*} value The value to set. + * @returns {Object} Returns the stack cache instance. + */ + function stackSet(key, value) { + var cache = this.__data__; + if (cache instanceof ListCache) { + var pairs = cache.__data__; + if (!Map || (pairs.length < LARGE_ARRAY_SIZE - 1)) { + pairs.push([key, value]); + return this; + } + cache = this.__data__ = new MapCache(pairs); + } + cache.set(key, value); + return this; + } + + // Add methods to `Stack`. + Stack.prototype.clear = stackClear; + Stack.prototype['delete'] = stackDelete; + Stack.prototype.get = stackGet; + Stack.prototype.has = stackHas; + Stack.prototype.set = stackSet; + + /** + * Creates an array of the enumerable property names of the array-like `value`. + * + * @private + * @param {*} value The value to query. + * @param {boolean} inherited Specify returning inherited property names. + * @returns {Array} Returns the array of property names. + */ + function arrayLikeKeys(value, inherited) { + // Safari 8.1 makes `arguments.callee` enumerable in strict mode. + // Safari 9 makes `arguments.length` enumerable in strict mode. + var result = (isArray(value) || isArguments(value)) + ? baseTimes(value.length, String) + : []; + + var length = result.length, + skipIndexes = !!length; + + for (var key in value) { + if ((inherited || hasOwnProperty.call(value, key)) && + !(skipIndexes && (key == 'length' || isIndex(key, length)))) { + result.push(key); + } + } + return result; + } + + /** + * This function is like `assignValue` except that it doesn't assign + * `undefined` values. + * + * @private + * @param {Object} object The object to modify. + * @param {string} key The key of the property to assign. + * @param {*} value The value to assign. + */ + function assignMergeValue(object, key, value) { + if ((value !== undefined && !eq(object[key], value)) || + (typeof key == 'number' && value === undefined && !(key in object))) { + object[key] = value; + } + } + + /** + * Assigns `value` to `key` of `object` if the existing value is not equivalent + * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) + * for equality comparisons. + * + * @private + * @param {Object} object The object to modify. + * @param {string} key The key of the property to assign. + * @param {*} value The value to assign. + */ + function assignValue(object, key, value) { + var objValue = object[key]; + if (!(hasOwnProperty.call(object, key) && eq(objValue, value)) || + (value === undefined && !(key in object))) { + object[key] = value; + } + } + + /** + * Gets the index at which the `key` is found in `array` of key-value pairs. + * + * @private + * @param {Array} array The array to inspect. + * @param {*} key The key to search for. + * @returns {number} Returns the index of the matched value, else `-1`. + */ + function assocIndexOf(array, key) { + var length = array.length; + while (length--) { + if (eq(array[length][0], key)) { + return length; + } + } + return -1; + } + + /** + * The base implementation of `_.assign` without support for multiple sources + * or `customizer` functions. + * + * @private + * @param {Object} object The destination object. + * @param {Object} source The source object. + * @returns {Object} Returns `object`. + */ + function baseAssign(object, source) { + return object && copyObject(source, keys(source), object); + } + + /** + * The base implementation of `_.clone` and `_.cloneDeep` which tracks + * traversed objects. + * + * @private + * @param {*} value The value to clone. + * @param {boolean} [isDeep] Specify a deep clone. + * @param {boolean} [isFull] Specify a clone including symbols. + * @param {Function} [customizer] The function to customize cloning. + * @param {string} [key] The key of `value`. + * @param {Object} [object] The parent object of `value`. + * @param {Object} [stack] Tracks traversed objects and their clone counterparts. + * @returns {*} Returns the cloned value. + */ + function baseClone(value, isDeep, isFull, customizer, key, object, stack) { + var result; + if (customizer) { + result = object ? customizer(value, key, object, stack) : customizer(value); + } + if (result !== undefined) { + return result; + } + if (!isObject(value)) { + return value; + } + var isArr = isArray(value); + if (isArr) { + result = initCloneArray(value); + if (!isDeep) { + return copyArray(value, result); + } + } else { + var tag = getTag(value), + isFunc = tag == funcTag || tag == genTag; + + if (isBuffer(value)) { + return cloneBuffer(value, isDeep); + } + if (tag == objectTag || tag == argsTag || (isFunc && !object)) { + if (isHostObject(value)) { + return object ? value : {}; + } + result = initCloneObject(isFunc ? {} : value); + if (!isDeep) { + return copySymbols(value, baseAssign(result, value)); + } + } else { + if (!cloneableTags[tag]) { + return object ? value : {}; + } + result = initCloneByTag(value, tag, baseClone, isDeep); + } + } + // Check for circular references and return its corresponding clone. + stack || (stack = new Stack); + var stacked = stack.get(value); + if (stacked) { + return stacked; + } + stack.set(value, result); + + if (!isArr) { + var props = isFull ? getAllKeys(value) : keys(value); + } + arrayEach(props || value, function(subValue, key) { + if (props) { + key = subValue; + subValue = value[key]; + } + // Recursively populate clone (susceptible to call stack limits). + assignValue(result, key, baseClone(subValue, isDeep, isFull, customizer, key, value, stack)); + }); + return result; + } + + /** + * The base implementation of `_.create` without support for assigning + * properties to the created object. + * + * @private + * @param {Object} prototype The object to inherit from. + * @returns {Object} Returns the new object. + */ + function baseCreate(proto) { + return isObject(proto) ? objectCreate(proto) : {}; + } + + /** + * The base implementation of `getAllKeys` and `getAllKeysIn` which uses + * `keysFunc` and `symbolsFunc` to get the enumerable property names and + * symbols of `object`. + * + * @private + * @param {Object} object The object to query. + * @param {Function} keysFunc The function to get the keys of `object`. + * @param {Function} symbolsFunc The function to get the symbols of `object`. + * @returns {Array} Returns the array of property names and symbols. + */ + function baseGetAllKeys(object, keysFunc, symbolsFunc) { + var result = keysFunc(object); + return isArray(object) ? result : arrayPush(result, symbolsFunc(object)); + } + + /** + * The base implementation of `getTag`. + * + * @private + * @param {*} value The value to query. + * @returns {string} Returns the `toStringTag`. + */ + function baseGetTag(value) { + return objectToString.call(value); + } + + /** + * The base implementation of `_.isNative` without bad shim checks. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a native function, + * else `false`. + */ + function baseIsNative(value) { + if (!isObject(value) || isMasked(value)) { + return false; + } + var pattern = (isFunction(value) || isHostObject(value)) ? reIsNative : reIsHostCtor; + return pattern.test(toSource(value)); + } + + /** + * The base implementation of `_.isTypedArray` without Node.js optimizations. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a typed array, else `false`. + */ + function baseIsTypedArray(value) { + return isObjectLike(value) && + isLength(value.length) && !!typedArrayTags[objectToString.call(value)]; + } + + /** + * The base implementation of `_.keys` which doesn't treat sparse arrays as dense. + * + * @private + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names. + */ + function baseKeys(object) { + if (!isPrototype(object)) { + return nativeKeys(object); + } + var result = []; + for (var key in Object(object)) { + if (hasOwnProperty.call(object, key) && key != 'constructor') { + result.push(key); + } + } + return result; + } + + /** + * The base implementation of `_.keysIn` which doesn't treat sparse arrays as dense. + * + * @private + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names. + */ + function baseKeysIn(object) { + if (!isObject(object)) { + return nativeKeysIn(object); + } + var isProto = isPrototype(object), + result = []; + + for (var key in object) { + if (!(key == 'constructor' && (isProto || !hasOwnProperty.call(object, key)))) { + result.push(key); + } + } + return result; + } + + /** + * The base implementation of `_.merge` without support for multiple sources. + * + * @private + * @param {Object} object The destination object. + * @param {Object} source The source object. + * @param {number} srcIndex The index of `source`. + * @param {Function} [customizer] The function to customize merged values. + * @param {Object} [stack] Tracks traversed source values and their merged + * counterparts. + */ + function baseMerge(object, source, srcIndex, customizer, stack) { + if (object === source) { + return; + } + if (!(isArray(source) || isTypedArray(source))) { + var props = baseKeysIn(source); + } + arrayEach(props || source, function(srcValue, key) { + if (props) { + key = srcValue; + srcValue = source[key]; + } + if (isObject(srcValue)) { + stack || (stack = new Stack); + baseMergeDeep(object, source, key, srcIndex, baseMerge, customizer, stack); + } + else { + var newValue = customizer + ? customizer(object[key], srcValue, (key + ''), object, source, stack) + : undefined; + + if (newValue === undefined) { + newValue = srcValue; + } + assignMergeValue(object, key, newValue); + } + }); + } + + /** + * A specialized version of `baseMerge` for arrays and objects which performs + * deep merges and tracks traversed objects enabling objects with circular + * references to be merged. + * + * @private + * @param {Object} object The destination object. + * @param {Object} source The source object. + * @param {string} key The key of the value to merge. + * @param {number} srcIndex The index of `source`. + * @param {Function} mergeFunc The function to merge values. + * @param {Function} [customizer] The function to customize assigned values. + * @param {Object} [stack] Tracks traversed source values and their merged + * counterparts. + */ + function baseMergeDeep(object, source, key, srcIndex, mergeFunc, customizer, stack) { + var objValue = object[key], + srcValue = source[key], + stacked = stack.get(srcValue); + + if (stacked) { + assignMergeValue(object, key, stacked); + return; + } + var newValue = customizer + ? customizer(objValue, srcValue, (key + ''), object, source, stack) + : undefined; + + var isCommon = newValue === undefined; + + if (isCommon) { + newValue = srcValue; + if (isArray(srcValue) || isTypedArray(srcValue)) { + if (isArray(objValue)) { + newValue = objValue; + } + else if (isArrayLikeObject(objValue)) { + newValue = copyArray(objValue); + } + else { + isCommon = false; + newValue = baseClone(srcValue, true); + } + } + else if (isPlainObject(srcValue) || isArguments(srcValue)) { + if (isArguments(objValue)) { + newValue = toPlainObject(objValue); + } + else if (!isObject(objValue) || (srcIndex && isFunction(objValue))) { + isCommon = false; + newValue = baseClone(srcValue, true); + } + else { + newValue = objValue; + } + } + else { + isCommon = false; + } + } + if (isCommon) { + // Recursively merge objects and arrays (susceptible to call stack limits). + stack.set(srcValue, newValue); + mergeFunc(newValue, srcValue, srcIndex, customizer, stack); + stack['delete'](srcValue); + } + assignMergeValue(object, key, newValue); + } + + /** + * The base implementation of `_.rest` which doesn't validate or coerce arguments. + * + * @private + * @param {Function} func The function to apply a rest parameter to. + * @param {number} [start=func.length-1] The start position of the rest parameter. + * @returns {Function} Returns the new function. + */ + function baseRest(func, start) { + start = nativeMax(start === undefined ? (func.length - 1) : start, 0); + return function() { + var args = arguments, + index = -1, + length = nativeMax(args.length - start, 0), + array = Array(length); + + while (++index < length) { + array[index] = args[start + index]; + } + index = -1; + var otherArgs = Array(start + 1); + while (++index < start) { + otherArgs[index] = args[index]; + } + otherArgs[start] = array; + return apply(func, this, otherArgs); + }; + } + + /** + * Creates a clone of `buffer`. + * + * @private + * @param {Buffer} buffer The buffer to clone. + * @param {boolean} [isDeep] Specify a deep clone. + * @returns {Buffer} Returns the cloned buffer. + */ + function cloneBuffer(buffer, isDeep) { + if (isDeep) { + return buffer.slice(); + } + var result = new buffer.constructor(buffer.length); + buffer.copy(result); + return result; + } + + /** + * Creates a clone of `arrayBuffer`. + * + * @private + * @param {ArrayBuffer} arrayBuffer The array buffer to clone. + * @returns {ArrayBuffer} Returns the cloned array buffer. + */ + function cloneArrayBuffer(arrayBuffer) { + var result = new arrayBuffer.constructor(arrayBuffer.byteLength); + new Uint8Array(result).set(new Uint8Array(arrayBuffer)); + return result; + } + + /** + * Creates a clone of `dataView`. + * + * @private + * @param {Object} dataView The data view to clone. + * @param {boolean} [isDeep] Specify a deep clone. + * @returns {Object} Returns the cloned data view. + */ + function cloneDataView(dataView, isDeep) { + var buffer = isDeep ? cloneArrayBuffer(dataView.buffer) : dataView.buffer; + return new dataView.constructor(buffer, dataView.byteOffset, dataView.byteLength); + } + + /** + * Creates a clone of `map`. + * + * @private + * @param {Object} map The map to clone. + * @param {Function} cloneFunc The function to clone values. + * @param {boolean} [isDeep] Specify a deep clone. + * @returns {Object} Returns the cloned map. + */ + function cloneMap(map, isDeep, cloneFunc) { + var array = isDeep ? cloneFunc(mapToArray(map), true) : mapToArray(map); + return arrayReduce(array, addMapEntry, new map.constructor); + } + + /** + * Creates a clone of `regexp`. + * + * @private + * @param {Object} regexp The regexp to clone. + * @returns {Object} Returns the cloned regexp. + */ + function cloneRegExp(regexp) { + var result = new regexp.constructor(regexp.source, reFlags.exec(regexp)); + result.lastIndex = regexp.lastIndex; + return result; + } + + /** + * Creates a clone of `set`. + * + * @private + * @param {Object} set The set to clone. + * @param {Function} cloneFunc The function to clone values. + * @param {boolean} [isDeep] Specify a deep clone. + * @returns {Object} Returns the cloned set. + */ + function cloneSet(set, isDeep, cloneFunc) { + var array = isDeep ? cloneFunc(setToArray(set), true) : setToArray(set); + return arrayReduce(array, addSetEntry, new set.constructor); + } + + /** + * Creates a clone of the `symbol` object. + * + * @private + * @param {Object} symbol The symbol object to clone. + * @returns {Object} Returns the cloned symbol object. + */ + function cloneSymbol(symbol) { + return symbolValueOf ? Object(symbolValueOf.call(symbol)) : {}; + } + + /** + * Creates a clone of `typedArray`. + * + * @private + * @param {Object} typedArray The typed array to clone. + * @param {boolean} [isDeep] Specify a deep clone. + * @returns {Object} Returns the cloned typed array. + */ + function cloneTypedArray(typedArray, isDeep) { + var buffer = isDeep ? cloneArrayBuffer(typedArray.buffer) : typedArray.buffer; + return new typedArray.constructor(buffer, typedArray.byteOffset, typedArray.length); + } + + /** + * Copies the values of `source` to `array`. + * + * @private + * @param {Array} source The array to copy values from. + * @param {Array} [array=[]] The array to copy values to. + * @returns {Array} Returns `array`. + */ + function copyArray(source, array) { + var index = -1, + length = source.length; + + array || (array = Array(length)); + while (++index < length) { + array[index] = source[index]; + } + return array; + } + + /** + * Copies properties of `source` to `object`. + * + * @private + * @param {Object} source The object to copy properties from. + * @param {Array} props The property identifiers to copy. + * @param {Object} [object={}] The object to copy properties to. + * @param {Function} [customizer] The function to customize copied values. + * @returns {Object} Returns `object`. + */ + function copyObject(source, props, object, customizer) { + object || (object = {}); + + var index = -1, + length = props.length; + + while (++index < length) { + var key = props[index]; + + var newValue = customizer + ? customizer(object[key], source[key], key, object, source) + : undefined; + + assignValue(object, key, newValue === undefined ? source[key] : newValue); + } + return object; + } + + /** + * Copies own symbol properties of `source` to `object`. + * + * @private + * @param {Object} source The object to copy symbols from. + * @param {Object} [object={}] The object to copy symbols to. + * @returns {Object} Returns `object`. + */ + function copySymbols(source, object) { + return copyObject(source, getSymbols(source), object); + } + + /** + * Creates a function like `_.assign`. + * + * @private + * @param {Function} assigner The function to assign values. + * @returns {Function} Returns the new assigner function. + */ + function createAssigner(assigner) { + return baseRest(function(object, sources) { + var index = -1, + length = sources.length, + customizer = length > 1 ? sources[length - 1] : undefined, + guard = length > 2 ? sources[2] : undefined; + + customizer = (assigner.length > 3 && typeof customizer == 'function') + ? (length--, customizer) + : undefined; + + if (guard && isIterateeCall(sources[0], sources[1], guard)) { + customizer = length < 3 ? undefined : customizer; + length = 1; + } + object = Object(object); + while (++index < length) { + var source = sources[index]; + if (source) { + assigner(object, source, index, customizer); + } + } + return object; + }); + } + + /** + * Creates an array of own enumerable property names and symbols of `object`. + * + * @private + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names and symbols. + */ + function getAllKeys(object) { + return baseGetAllKeys(object, keys, getSymbols); + } + + /** + * Gets the data for `map`. + * + * @private + * @param {Object} map The map to query. + * @param {string} key The reference key. + * @returns {*} Returns the map data. + */ + function getMapData(map, key) { + var data = map.__data__; + return isKeyable(key) + ? data[typeof key == 'string' ? 'string' : 'hash'] + : data.map; + } + + /** + * Gets the native function at `key` of `object`. + * + * @private + * @param {Object} object The object to query. + * @param {string} key The key of the method to get. + * @returns {*} Returns the function if it's native, else `undefined`. + */ + function getNative(object, key) { + var value = getValue(object, key); + return baseIsNative(value) ? value : undefined; + } + + /** + * Creates an array of the own enumerable symbol properties of `object`. + * + * @private + * @param {Object} object The object to query. + * @returns {Array} Returns the array of symbols. + */ + var getSymbols = nativeGetSymbols ? overArg(nativeGetSymbols, Object) : stubArray; + + /** + * Gets the `toStringTag` of `value`. + * + * @private + * @param {*} value The value to query. + * @returns {string} Returns the `toStringTag`. + */ + var getTag = baseGetTag; + + // Fallback for data views, maps, sets, and weak maps in IE 11, + // for data views in Edge < 14, and promises in Node.js. + if ((DataView && getTag(new DataView(new ArrayBuffer(1))) != dataViewTag) || + (Map && getTag(new Map) != mapTag) || + (Promise && getTag(Promise.resolve()) != promiseTag) || + (Set && getTag(new Set) != setTag) || + (WeakMap && getTag(new WeakMap) != weakMapTag)) { + getTag = function(value) { + var result = objectToString.call(value), + Ctor = result == objectTag ? value.constructor : undefined, + ctorString = Ctor ? toSource(Ctor) : undefined; + + if (ctorString) { + switch (ctorString) { + case dataViewCtorString: return dataViewTag; + case mapCtorString: return mapTag; + case promiseCtorString: return promiseTag; + case setCtorString: return setTag; + case weakMapCtorString: return weakMapTag; + } + } + return result; + }; + } + + /** + * Initializes an array clone. + * + * @private + * @param {Array} array The array to clone. + * @returns {Array} Returns the initialized clone. + */ + function initCloneArray(array) { + var length = array.length, + result = array.constructor(length); + + // Add properties assigned by `RegExp#exec`. + if (length && typeof array[0] == 'string' && hasOwnProperty.call(array, 'index')) { + result.index = array.index; + result.input = array.input; + } + return result; + } + + /** + * Initializes an object clone. + * + * @private + * @param {Object} object The object to clone. + * @returns {Object} Returns the initialized clone. + */ + function initCloneObject(object) { + return (typeof object.constructor == 'function' && !isPrototype(object)) + ? baseCreate(getPrototype(object)) + : {}; + } + + /** + * Initializes an object clone based on its `toStringTag`. + * + * **Note:** This function only supports cloning values with tags of + * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`. + * + * @private + * @param {Object} object The object to clone. + * @param {string} tag The `toStringTag` of the object to clone. + * @param {Function} cloneFunc The function to clone values. + * @param {boolean} [isDeep] Specify a deep clone. + * @returns {Object} Returns the initialized clone. + */ + function initCloneByTag(object, tag, cloneFunc, isDeep) { + var Ctor = object.constructor; + switch (tag) { + case arrayBufferTag: + return cloneArrayBuffer(object); + + case boolTag: + case dateTag: + return new Ctor(+object); + + case dataViewTag: + return cloneDataView(object, isDeep); + + case float32Tag: case float64Tag: + case int8Tag: case int16Tag: case int32Tag: + case uint8Tag: case uint8ClampedTag: case uint16Tag: case uint32Tag: + return cloneTypedArray(object, isDeep); + + case mapTag: + return cloneMap(object, isDeep, cloneFunc); + + case numberTag: + case stringTag: + return new Ctor(object); + + case regexpTag: + return cloneRegExp(object); + + case setTag: + return cloneSet(object, isDeep, cloneFunc); + + case symbolTag: + return cloneSymbol(object); + } + } + + /** + * Checks if `value` is a valid array-like index. + * + * @private + * @param {*} value The value to check. + * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index. + * @returns {boolean} Returns `true` if `value` is a valid index, else `false`. + */ + function isIndex(value, length) { + length = length == null ? MAX_SAFE_INTEGER : length; + return !!length && + (typeof value == 'number' || reIsUint.test(value)) && + (value > -1 && value % 1 == 0 && value < length); + } + + /** + * Checks if the given arguments are from an iteratee call. + * + * @private + * @param {*} value The potential iteratee value argument. + * @param {*} index The potential iteratee index or key argument. + * @param {*} object The potential iteratee object argument. + * @returns {boolean} Returns `true` if the arguments are from an iteratee call, + * else `false`. + */ + function isIterateeCall(value, index, object) { + if (!isObject(object)) { + return false; + } + var type = typeof index; + if (type == 'number' + ? (isArrayLike(object) && isIndex(index, object.length)) + : (type == 'string' && index in object) + ) { + return eq(object[index], value); + } + return false; + } + + /** + * Checks if `value` is suitable for use as unique object key. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is suitable, else `false`. + */ + function isKeyable(value) { + var type = typeof value; + return (type == 'string' || type == 'number' || type == 'symbol' || type == 'boolean') + ? (value !== '__proto__') + : (value === null); + } + + /** + * Checks if `func` has its source masked. + * + * @private + * @param {Function} func The function to check. + * @returns {boolean} Returns `true` if `func` is masked, else `false`. + */ + function isMasked(func) { + return !!maskSrcKey && (maskSrcKey in func); + } + + /** + * Checks if `value` is likely a prototype object. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a prototype, else `false`. + */ + function isPrototype(value) { + var Ctor = value && value.constructor, + proto = (typeof Ctor == 'function' && Ctor.prototype) || objectProto; + + return value === proto; + } + + /** + * This function is like + * [`Object.keys`](http://ecma-international.org/ecma-262/7.0/#sec-object.keys) + * except that it includes inherited enumerable properties. + * + * @private + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names. + */ + function nativeKeysIn(object) { + var result = []; + if (object != null) { + for (var key in Object(object)) { + result.push(key); + } + } + return result; + } + + /** + * Converts `func` to its source code. + * + * @private + * @param {Function} func The function to process. + * @returns {string} Returns the source code. + */ + function toSource(func) { + if (func != null) { + try { + return funcToString.call(func); + } catch (e) {} + try { + return (func + ''); + } catch (e) {} + } + return ''; + } + + /** + * Performs a + * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) + * comparison between two values to determine if they are equivalent. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {boolean} Returns `true` if the values are equivalent, else `false`. + * @example + * + * var object = { 'a': 1 }; + * var other = { 'a': 1 }; + * + * _.eq(object, object); + * // => true + * + * _.eq(object, other); + * // => false + * + * _.eq('a', 'a'); + * // => true + * + * _.eq('a', Object('a')); + * // => false + * + * _.eq(NaN, NaN); + * // => true + */ + function eq(value, other) { + return value === other || (value !== value && other !== other); + } + + /** + * Checks if `value` is likely an `arguments` object. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an `arguments` object, + * else `false`. + * @example + * + * _.isArguments(function() { return arguments; }()); + * // => true + * + * _.isArguments([1, 2, 3]); + * // => false + */ + function isArguments(value) { + // Safari 8.1 makes `arguments.callee` enumerable in strict mode. + return isArrayLikeObject(value) && hasOwnProperty.call(value, 'callee') && + (!propertyIsEnumerable.call(value, 'callee') || objectToString.call(value) == argsTag); + } + + /** + * Checks if `value` is classified as an `Array` object. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an array, else `false`. + * @example + * + * _.isArray([1, 2, 3]); + * // => true + * + * _.isArray(document.body.children); + * // => false + * + * _.isArray('abc'); + * // => false + * + * _.isArray(_.noop); + * // => false + */ + var isArray = Array.isArray; + + /** + * Checks if `value` is array-like. A value is considered array-like if it's + * not a function and has a `value.length` that's an integer greater than or + * equal to `0` and less than or equal to `Number.MAX_SAFE_INTEGER`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is array-like, else `false`. + * @example + * + * _.isArrayLike([1, 2, 3]); + * // => true + * + * _.isArrayLike(document.body.children); + * // => true + * + * _.isArrayLike('abc'); + * // => true + * + * _.isArrayLike(_.noop); + * // => false + */ + function isArrayLike(value) { + return value != null && isLength(value.length) && !isFunction(value); + } + + /** + * This method is like `_.isArrayLike` except that it also checks if `value` + * is an object. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an array-like object, + * else `false`. + * @example + * + * _.isArrayLikeObject([1, 2, 3]); + * // => true + * + * _.isArrayLikeObject(document.body.children); + * // => true + * + * _.isArrayLikeObject('abc'); + * // => false + * + * _.isArrayLikeObject(_.noop); + * // => false + */ + function isArrayLikeObject(value) { + return isObjectLike(value) && isArrayLike(value); + } + + /** + * Checks if `value` is a buffer. + * + * @static + * @memberOf _ + * @since 4.3.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a buffer, else `false`. + * @example + * + * _.isBuffer(new Buffer(2)); + * // => true + * + * _.isBuffer(new Uint8Array(2)); + * // => false + */ + var isBuffer = nativeIsBuffer || stubFalse; + + /** + * Checks if `value` is classified as a `Function` object. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a function, else `false`. + * @example + * + * _.isFunction(_); + * // => true + * + * _.isFunction(/abc/); + * // => false + */ + function isFunction(value) { + // The use of `Object#toString` avoids issues with the `typeof` operator + // in Safari 8-9 which returns 'object' for typed array and other constructors. + var tag = isObject(value) ? objectToString.call(value) : ''; + return tag == funcTag || tag == genTag; + } + + /** + * Checks if `value` is a valid array-like length. + * + * **Note:** This method is loosely based on + * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a valid length, else `false`. + * @example + * + * _.isLength(3); + * // => true + * + * _.isLength(Number.MIN_VALUE); + * // => false + * + * _.isLength(Infinity); + * // => false + * + * _.isLength('3'); + * // => false + */ + function isLength(value) { + return typeof value == 'number' && + value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER; + } + + /** + * Checks if `value` is the + * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types) + * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an object, else `false`. + * @example + * + * _.isObject({}); + * // => true + * + * _.isObject([1, 2, 3]); + * // => true + * + * _.isObject(_.noop); + * // => true + * + * _.isObject(null); + * // => false + */ + function isObject(value) { + var type = typeof value; + return !!value && (type == 'object' || type == 'function'); + } + + /** + * Checks if `value` is object-like. A value is object-like if it's not `null` + * and has a `typeof` result of "object". + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is object-like, else `false`. + * @example + * + * _.isObjectLike({}); + * // => true + * + * _.isObjectLike([1, 2, 3]); + * // => true + * + * _.isObjectLike(_.noop); + * // => false + * + * _.isObjectLike(null); + * // => false + */ + function isObjectLike(value) { + return !!value && typeof value == 'object'; + } + + /** + * Checks if `value` is a plain object, that is, an object created by the + * `Object` constructor or one with a `[[Prototype]]` of `null`. + * + * @static + * @memberOf _ + * @since 0.8.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a plain object, else `false`. + * @example + * + * function Foo() { + * this.a = 1; + * } + * + * _.isPlainObject(new Foo); + * // => false + * + * _.isPlainObject([1, 2, 3]); + * // => false + * + * _.isPlainObject({ 'x': 0, 'y': 0 }); + * // => true + * + * _.isPlainObject(Object.create(null)); + * // => true + */ + function isPlainObject(value) { + if (!isObjectLike(value) || + objectToString.call(value) != objectTag || isHostObject(value)) { + return false; + } + var proto = getPrototype(value); + if (proto === null) { + return true; + } + var Ctor = hasOwnProperty.call(proto, 'constructor') && proto.constructor; + return (typeof Ctor == 'function' && + Ctor instanceof Ctor && funcToString.call(Ctor) == objectCtorString); + } + + /** + * Checks if `value` is classified as a typed array. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a typed array, else `false`. + * @example + * + * _.isTypedArray(new Uint8Array); + * // => true + * + * _.isTypedArray([]); + * // => false + */ + var isTypedArray = nodeIsTypedArray ? baseUnary(nodeIsTypedArray) : baseIsTypedArray; + + /** + * Converts `value` to a plain object flattening inherited enumerable string + * keyed properties of `value` to own properties of the plain object. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Lang + * @param {*} value The value to convert. + * @returns {Object} Returns the converted plain object. + * @example + * + * function Foo() { + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.assign({ 'a': 1 }, new Foo); + * // => { 'a': 1, 'b': 2 } + * + * _.assign({ 'a': 1 }, _.toPlainObject(new Foo)); + * // => { 'a': 1, 'b': 2, 'c': 3 } + */ + function toPlainObject(value) { + return copyObject(value, keysIn(value)); + } + + /** + * Creates an array of the own enumerable property names of `object`. + * + * **Note:** Non-object values are coerced to objects. See the + * [ES spec](http://ecma-international.org/ecma-262/7.0/#sec-object.keys) + * for more details. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Object + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names. + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.keys(new Foo); + * // => ['a', 'b'] (iteration order is not guaranteed) + * + * _.keys('hi'); + * // => ['0', '1'] + */ + function keys(object) { + return isArrayLike(object) ? arrayLikeKeys(object) : baseKeys(object); + } + + /** + * Creates an array of the own and inherited enumerable property names of `object`. + * + * **Note:** Non-object values are coerced to objects. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Object + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names. + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.keysIn(new Foo); + * // => ['a', 'b', 'c'] (iteration order is not guaranteed) + */ + function keysIn(object) { + return isArrayLike(object) ? arrayLikeKeys(object, true) : baseKeysIn(object); + } + + /** + * This method is like `_.assign` except that it recursively merges own and + * inherited enumerable string keyed properties of source objects into the + * destination object. Source properties that resolve to `undefined` are + * skipped if a destination value exists. Array and plain object properties + * are merged recursively. Other objects and value types are overridden by + * assignment. Source objects are applied from left to right. Subsequent + * sources overwrite property assignments of previous sources. + * + * **Note:** This method mutates `object`. + * + * @static + * @memberOf _ + * @since 0.5.0 + * @category Object + * @param {Object} object The destination object. + * @param {...Object} [sources] The source objects. + * @returns {Object} Returns `object`. + * @example + * + * var object = { + * 'a': [{ 'b': 2 }, { 'd': 4 }] + * }; + * + * var other = { + * 'a': [{ 'c': 3 }, { 'e': 5 }] + * }; + * + * _.merge(object, other); + * // => { 'a': [{ 'b': 2, 'c': 3 }, { 'd': 4, 'e': 5 }] } + */ + var merge = createAssigner(function(object, source, srcIndex) { + baseMerge(object, source, srcIndex); + }); + + /** + * This method returns a new empty array. + * + * @static + * @memberOf _ + * @since 4.13.0 + * @category Util + * @returns {Array} Returns the new empty array. + * @example + * + * var arrays = _.times(2, _.stubArray); + * + * console.log(arrays); + * // => [[], []] + * + * console.log(arrays[0] === arrays[1]); + * // => false + */ + function stubArray() { + return []; + } + + /** + * This method returns `false`. + * + * @static + * @memberOf _ + * @since 4.13.0 + * @category Util + * @returns {boolean} Returns `false`. + * @example + * + * _.times(2, _.stubFalse); + * // => [false, false] + */ + function stubFalse() { + return false; + } + + module.exports = merge; + + /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }()), __webpack_require__(33)(module))) + +/***/ }, +/* 33 */ +/***/ function(module, exports) { + + module.exports = function(module) { + if(!module.webpackPolyfill) { + module.deprecate = function() {}; + module.paths = []; + // module.parent = undefined by default + module.children = []; + module.webpackPolyfill = 1; + } + return module; + } + + +/***/ }, +/* 34 */ +/***/ function(module, exports, __webpack_require__) { + + exports = module.exports = __webpack_require__(25)(); + // imports + + + // module + exports.push([module.id, ".media-control-notransition {\n -webkit-transition: none !important;\n -moz-transition: none !important;\n transition: none !important; }\n\n.media-control[data-media-control] {\n position: absolute;\n width: 100%;\n height: 100%;\n z-index: 9999;\n pointer-events: none; }\n .media-control[data-media-control].dragging {\n pointer-events: auto;\n cursor: -webkit-grabbing !important;\n cursor: grabbing !important;\n cursor: url(" + __webpack_require__(35) + "), move; }\n .media-control[data-media-control].dragging * {\n cursor: -webkit-grabbing !important;\n cursor: grabbing !important;\n cursor: url(" + __webpack_require__(35) + "), move; }\n .media-control[data-media-control] .media-control-background[data-background] {\n position: absolute;\n height: 40%;\n width: 100%;\n bottom: 0;\n background: -webkit-linear-gradient(transparent, rgba(0, 0, 0, 0.9));\n background: linear-gradient(transparent, rgba(0, 0, 0, 0.9));\n -webkit-transition: opacity 0.6s ease-out;\n -moz-transition: opacity 0.6s ease-out;\n transition: opacity 0.6s ease-out; }\n .media-control[data-media-control] .media-control-icon {\n line-height: 0;\n letter-spacing: 0;\n speak: none;\n color: #fff;\n opacity: 0.5;\n vertical-align: middle;\n text-align: left;\n -webkit-transition: all 0.1s ease;\n -moz-transition: all 0.1s ease;\n transition: all 0.1s ease; }\n .media-control[data-media-control] .media-control-icon:hover {\n color: white;\n opacity: 0.75;\n text-shadow: rgba(255, 255, 255, 0.8) 0 0 5px; }\n .media-control[data-media-control].media-control-hide .media-control-background[data-background] {\n opacity: 0; }\n .media-control[data-media-control].media-control-hide .media-control-layer[data-controls] {\n bottom: -50px; }\n .media-control[data-media-control].media-control-hide .media-control-layer[data-controls] .bar-container[data-seekbar] .bar-scrubber[data-seekbar] {\n opacity: 0; }\n .media-control[data-media-control] .media-control-layer[data-controls] {\n position: absolute;\n bottom: 7px;\n width: 100%;\n height: 32px;\n font-size: 0;\n vertical-align: middle;\n pointer-events: auto;\n -webkit-transition: bottom 0.4s ease-out;\n -moz-transition: bottom 0.4s ease-out;\n transition: bottom 0.4s ease-out; }\n .media-control[data-media-control] .media-control-layer[data-controls] .media-control-left-panel[data-media-control] {\n position: absolute;\n top: 0;\n left: 4px;\n height: 100%; }\n .media-control[data-media-control] .media-control-layer[data-controls] .media-control-center-panel[data-media-control] {\n height: 100%;\n text-align: center;\n line-height: 32px; }\n .media-control[data-media-control] .media-control-layer[data-controls] .media-control-right-panel[data-media-control] {\n position: absolute;\n top: 0;\n right: 4px;\n height: 100%; }\n .media-control[data-media-control] .media-control-layer[data-controls] button.media-control-button {\n background-color: transparent;\n border: 0;\n margin: 0 6px;\n padding: 0;\n cursor: pointer;\n display: inline-block;\n width: 32px;\n height: 100%; }\n .media-control[data-media-control] .media-control-layer[data-controls] button.media-control-button svg {\n width: 100%;\n height: 22px; }\n .media-control[data-media-control] .media-control-layer[data-controls] button.media-control-button svg path {\n fill: white; }\n .media-control[data-media-control] .media-control-layer[data-controls] button.media-control-button:focus {\n outline: none; }\n .media-control[data-media-control] .media-control-layer[data-controls] button.media-control-button[data-play] {\n float: left;\n height: 100%; }\n .media-control[data-media-control] .media-control-layer[data-controls] button.media-control-button[data-pause] {\n float: left;\n height: 100%; }\n .media-control[data-media-control] .media-control-layer[data-controls] button.media-control-button[data-stop] {\n float: left;\n height: 100%; }\n .media-control[data-media-control] .media-control-layer[data-controls] button.media-control-button[data-fullscreen] {\n float: right;\n background-color: transparent;\n border: 0;\n height: 100%; }\n .media-control[data-media-control] .media-control-layer[data-controls] button.media-control-button[data-hd-indicator] {\n cursor: default;\n float: right;\n background-color: transparent;\n border: 0;\n height: 100%;\n display: none; }\n .media-control[data-media-control] .media-control-layer[data-controls] button.media-control-button[data-hd-indicator].enabled {\n opacity: 1.0;\n display: block; }\n .media-control[data-media-control] .media-control-layer[data-controls] button.media-control-button[data-hd-indicator].enabled:hover {\n opacity: 1.0;\n text-shadow: none; }\n .media-control[data-media-control] .media-control-layer[data-controls] button.media-control-button[data-playpause] {\n float: left; }\n .media-control[data-media-control] .media-control-layer[data-controls] button.media-control-button[data-playstop] {\n float: left; }\n .media-control[data-media-control] .media-control-layer[data-controls] .media-control-indicator[data-position], .media-control[data-media-control] .media-control-layer[data-controls] .media-control-indicator[data-duration] {\n display: inline-block;\n font-size: 10px;\n color: white;\n cursor: default;\n line-height: 32px;\n position: relative; }\n .media-control[data-media-control] .media-control-layer[data-controls] .media-control-indicator[data-position] {\n margin: 0 6px 0 7px; }\n .media-control[data-media-control] .media-control-layer[data-controls] .media-control-indicator[data-duration] {\n color: rgba(255, 255, 255, 0.5);\n margin-right: 6px; }\n .media-control[data-media-control] .media-control-layer[data-controls] .media-control-indicator[data-duration]:before {\n content: \"|\";\n margin-right: 7px; }\n .media-control[data-media-control] .media-control-layer[data-controls] .bar-container[data-seekbar] {\n position: absolute;\n top: -20px;\n left: 0;\n display: inline-block;\n vertical-align: middle;\n width: 100%;\n height: 25px;\n cursor: pointer; }\n .media-control[data-media-control] .media-control-layer[data-controls] .bar-container[data-seekbar] .bar-background[data-seekbar] {\n width: 100%;\n height: 1px;\n position: relative;\n top: 12px;\n background-color: #666666; }\n .media-control[data-media-control] .media-control-layer[data-controls] .bar-container[data-seekbar] .bar-background[data-seekbar] .bar-fill-1[data-seekbar] {\n position: absolute;\n top: 0;\n left: 0;\n width: 0;\n height: 100%;\n background-color: #c2c2c2;\n -webkit-transition: all 0.1s ease-out;\n -moz-transition: all 0.1s ease-out;\n transition: all 0.1s ease-out; }\n .media-control[data-media-control] .media-control-layer[data-controls] .bar-container[data-seekbar] .bar-background[data-seekbar] .bar-fill-2[data-seekbar] {\n position: absolute;\n top: 0;\n left: 0;\n width: 0;\n height: 100%;\n background-color: #005aff;\n -webkit-transition: all 0.1s ease-out;\n -moz-transition: all 0.1s ease-out;\n transition: all 0.1s ease-out; }\n .media-control[data-media-control] .media-control-layer[data-controls] .bar-container[data-seekbar] .bar-background[data-seekbar] .bar-hover[data-seekbar] {\n opacity: 0;\n position: absolute;\n top: -3px;\n width: 5px;\n height: 7px;\n background-color: rgba(255, 255, 255, 0.5);\n -webkit-transition: opacity 0.1s ease;\n -moz-transition: opacity 0.1s ease;\n transition: opacity 0.1s ease; }\n .media-control[data-media-control] .media-control-layer[data-controls] .bar-container[data-seekbar]:hover .bar-background[data-seekbar] .bar-hover[data-seekbar] {\n opacity: 1; }\n .media-control[data-media-control] .media-control-layer[data-controls] .bar-container[data-seekbar].seek-disabled {\n cursor: default; }\n .media-control[data-media-control] .media-control-layer[data-controls] .bar-container[data-seekbar].seek-disabled:hover .bar-background[data-seekbar] .bar-hover[data-seekbar] {\n opacity: 0; }\n .media-control[data-media-control] .media-control-layer[data-controls] .bar-container[data-seekbar] .bar-scrubber[data-seekbar] {\n position: absolute;\n -webkit-transform: translateX(-50%);\n -moz-transform: translateX(-50%);\n -ms-transform: translateX(-50%);\n -o-transform: translateX(-50%);\n transform: translateX(-50%);\n top: 2px;\n left: 0;\n width: 20px;\n height: 20px;\n opacity: 1;\n -webkit-transition: all 0.1s ease-out;\n -moz-transition: all 0.1s ease-out;\n transition: all 0.1s ease-out; }\n .media-control[data-media-control] .media-control-layer[data-controls] .bar-container[data-seekbar] .bar-scrubber[data-seekbar] .bar-scrubber-icon[data-seekbar] {\n position: absolute;\n left: 6px;\n top: 6px;\n width: 8px;\n height: 8px;\n border-radius: 10px;\n box-shadow: 0 0 0 6px rgba(255, 255, 255, 0.2);\n background-color: white; }\n .media-control[data-media-control] .media-control-layer[data-controls] .drawer-container[data-volume] {\n float: right;\n display: inline-block;\n height: 32px;\n cursor: pointer;\n margin: 0 6px;\n box-sizing: border-box; }\n .media-control[data-media-control] .media-control-layer[data-controls] .drawer-container[data-volume] .drawer-icon-container[data-volume] {\n float: left;\n bottom: 0; }\n .media-control[data-media-control] .media-control-layer[data-controls] .drawer-container[data-volume] .drawer-icon-container[data-volume] .drawer-icon[data-volume] {\n background-color: transparent;\n border: 0;\n box-sizing: content-box;\n width: 32px;\n height: 32px;\n opacity: 0.5; }\n .media-control[data-media-control] .media-control-layer[data-controls] .drawer-container[data-volume] .drawer-icon-container[data-volume] .drawer-icon[data-volume]:hover {\n opacity: 0.75; }\n .media-control[data-media-control] .media-control-layer[data-controls] .drawer-container[data-volume] .drawer-icon-container[data-volume] .drawer-icon[data-volume] svg {\n height: 24px;\n position: relative;\n top: 3px; }\n .media-control[data-media-control] .media-control-layer[data-controls] .drawer-container[data-volume] .drawer-icon-container[data-volume] .drawer-icon[data-volume] svg path {\n fill: white; }\n .media-control[data-media-control] .media-control-layer[data-controls] .drawer-container[data-volume] .drawer-icon-container[data-volume] .drawer-icon[data-volume].muted svg {\n margin-left: 2px; }\n .media-control[data-media-control] .media-control-layer[data-controls] .drawer-container[data-volume] .bar-container[data-volume] {\n float: left;\n position: relative;\n overflow: hidden;\n top: 6px;\n width: 42px;\n height: 18px;\n padding: 3px 0;\n -webkit-transition: width 0.2s ease-out;\n -moz-transition: width 0.2s ease-out;\n transition: width 0.2s ease-out; }\n .media-control[data-media-control] .media-control-layer[data-controls] .drawer-container[data-volume] .bar-container[data-volume] .bar-background[data-volume] {\n height: 1px;\n position: relative;\n top: 7px;\n margin: 0 3px;\n background-color: #666666; }\n .media-control[data-media-control] .media-control-layer[data-controls] .drawer-container[data-volume] .bar-container[data-volume] .bar-background[data-volume] .bar-fill-1[data-volume] {\n position: absolute;\n top: 0;\n left: 0;\n width: 0;\n height: 100%;\n background-color: #c2c2c2;\n -webkit-transition: all 0.1s ease-out;\n -moz-transition: all 0.1s ease-out;\n transition: all 0.1s ease-out; }\n .media-control[data-media-control] .media-control-layer[data-controls] .drawer-container[data-volume] .bar-container[data-volume] .bar-background[data-volume] .bar-fill-2[data-volume] {\n position: absolute;\n top: 0;\n left: 0;\n width: 0;\n height: 100%;\n background-color: #005aff;\n -webkit-transition: all 0.1s ease-out;\n -moz-transition: all 0.1s ease-out;\n transition: all 0.1s ease-out; }\n .media-control[data-media-control] .media-control-layer[data-controls] .drawer-container[data-volume] .bar-container[data-volume] .bar-background[data-volume] .bar-hover[data-volume] {\n opacity: 0;\n position: absolute;\n top: -3px;\n width: 5px;\n height: 7px;\n background-color: rgba(255, 255, 255, 0.5);\n -webkit-transition: opacity 0.1s ease;\n -moz-transition: opacity 0.1s ease;\n transition: opacity 0.1s ease; }\n .media-control[data-media-control] .media-control-layer[data-controls] .drawer-container[data-volume] .bar-container[data-volume] .bar-scrubber[data-volume] {\n position: absolute;\n -webkit-transform: translateX(-50%);\n -moz-transform: translateX(-50%);\n -ms-transform: translateX(-50%);\n -o-transform: translateX(-50%);\n transform: translateX(-50%);\n top: 0px;\n left: 0;\n width: 20px;\n height: 20px;\n opacity: 1;\n -webkit-transition: all 0.1s ease-out;\n -moz-transition: all 0.1s ease-out;\n transition: all 0.1s ease-out; }\n .media-control[data-media-control] .media-control-layer[data-controls] .drawer-container[data-volume] .bar-container[data-volume] .bar-scrubber[data-volume] .bar-scrubber-icon[data-volume] {\n position: absolute;\n left: 6px;\n top: 6px;\n width: 8px;\n height: 8px;\n border-radius: 10px;\n box-shadow: 0 0 0 6px rgba(255, 255, 255, 0.2);\n background-color: white; }\n .media-control[data-media-control] .media-control-layer[data-controls] .drawer-container[data-volume] .bar-container[data-volume] .segmented-bar-element[data-volume] {\n float: left;\n width: 4px;\n padding-left: 2px;\n height: 12px;\n opacity: 0.5;\n box-shadow: inset 2px 0 0 white;\n -webkit-transition: -webkit-transform 0.2s ease-out;\n -moz-transition: -moz-transform 0.2s ease-out;\n transition: transform 0.2s ease-out; }\n .media-control[data-media-control] .media-control-layer[data-controls] .drawer-container[data-volume] .bar-container[data-volume] .segmented-bar-element[data-volume].fill {\n box-shadow: inset 2px 0 0 #fff;\n opacity: 1; }\n .media-control[data-media-control] .media-control-layer[data-controls] .drawer-container[data-volume] .bar-container[data-volume] .segmented-bar-element[data-volume]:nth-of-type(1) {\n padding-left: 0; }\n .media-control[data-media-control] .media-control-layer[data-controls] .drawer-container[data-volume] .bar-container[data-volume] .segmented-bar-element[data-volume]:hover {\n -webkit-transform: scaleY(1.5);\n -moz-transform: scaleY(1.5);\n -ms-transform: scaleY(1.5);\n -o-transform: scaleY(1.5);\n transform: scaleY(1.5); }\n .media-control[data-media-control].w320 .media-control-layer[data-controls] .drawer-container[data-volume] .bar-container[data-volume].volume-bar-hide {\n width: 0;\n height: 12px;\n top: 9px;\n padding: 0; }\n", ""]); + + // exports + + +/***/ }, +/* 35 */ +/***/ function(module, exports, __webpack_require__) { + + module.exports = __webpack_require__.p + "a8c874b93b3d848f39a71260c57e3863.cur"; + +/***/ }, +/* 36 */ +/***/ function(module, exports) { + + module.exports = "<div class=\"media-control-background\" data-background></div>\n<div class=\"media-control-layer\" data-controls>\n <% var renderBar = function(name) { %>\n <div class=\"bar-container\" data-<%= name %>>\n <div class=\"bar-background\" data-<%= name %>>\n <div class=\"bar-fill-1\" data-<%= name %>></div>\n <div class=\"bar-fill-2\" data-<%= name %>></div>\n <div class=\"bar-hover\" data-<%= name %>></div>\n </div>\n <div class=\"bar-scrubber\" data-<%= name %>>\n <div class=\"bar-scrubber-icon\" data-<%= name %>></div>\n </div>\n </div>\n <% }; %>\n <% var renderSegmentedBar = function(name, segments) {\n segments = segments || 10; %>\n <div class=\"bar-container\" data-<%= name %>>\n <% for (var i = 0; i < segments; i++) { %>\n <div class=\"segmented-bar-element\" data-<%= name %>></div>\n <% } %>\n </div>\n <% }; %>\n <% var renderDrawer = function(name, renderContent) { %>\n <div class=\"drawer-container\" data-<%= name %>>\n <div class=\"drawer-icon-container\" data-<%= name %>>\n <div class=\"drawer-icon media-control-icon\" data-<%= name %>></div>\n <span class=\"drawer-text\" data-<%= name %>></span>\n </div>\n <% renderContent(name); %>\n </div>\n <% }; %>\n <% var renderIndicator = function(name) { %>\n <div class=\"media-control-indicator\" data-<%= name %>></div>\n <% }; %>\n <% var renderButton = function(name) { %>\n <button type=\"button\" class=\"media-control-button media-control-icon\" data-<%= name %>></button>\n <% }; %>\n <% var templates = {\n bar: renderBar,\n segmentedBar: renderSegmentedBar,\n };\n var render = function(settingsList) {\n settingsList.forEach(function(setting) {\n if(setting === \"seekbar\") {\n renderBar(setting);\n } else if (setting === \"volume\") {\n renderDrawer(setting, settings.volumeBarTemplate ? templates[settings.volumeBarTemplate] : function(name) { return renderSegmentedBar(name); });\n } else if (setting === \"duration\" || setting === \"position\") {\n renderIndicator(setting);\n } else {\n renderButton(setting);\n }\n });\n }; %>\n <% if (settings.default && settings.default.length) { %>\n <div class=\"media-control-center-panel\" data-media-control>\n <% render(settings.default); %>\n </div>\n <% } %>\n <% if (settings.left && settings.left.length) { %>\n <div class=\"media-control-left-panel\" data-media-control>\n <% render(settings.left); %>\n </div>\n <% } %>\n <% if (settings.right && settings.right.length) { %>\n <div class=\"media-control-right-panel\" data-media-control>\n <% render(settings.right); %>\n </div>\n <% } %>\n</div>\n"; + +/***/ }, +/* 37 */ +/***/ function(module, exports) { + + module.exports = "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\"><path fill=\"#010101\" d=\"M1.425.35L14.575 8l-13.15 7.65V.35z\"></path></svg>" + +/***/ }, +/* 38 */ +/***/ function(module, exports) { + + module.exports = "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\"><path fill-rule=\"evenodd\" clip-rule=\"evenodd\" fill=\"#010101\" d=\"M1.712 14.76H6.43V1.24H1.71v13.52zm7.86-13.52v13.52h4.716V1.24H9.573z\"></path></svg>" + +/***/ }, +/* 39 */ +/***/ function(module, exports) { + + module.exports = "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\"><path fill-rule=\"evenodd\" clip-rule=\"evenodd\" fill=\"#010101\" d=\"M1.712 1.24h12.6v13.52h-12.6z\"></path></svg>" + +/***/ }, +/* 40 */ +/***/ function(module, exports) { + + module.exports = "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\"><path fill-rule=\"evenodd\" clip-rule=\"evenodd\" fill=\"#010101\" d=\"M11.5 11h-.002v1.502L7.798 10H4.5V6h3.297l3.7-2.502V4.5h.003V11zM11 4.49L7.953 6.5H5v3h2.953L11 11.51V4.49z\"></path></svg>" + +/***/ }, +/* 41 */ +/***/ function(module, exports) { + + module.exports = "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\"><path fill-rule=\"evenodd\" clip-rule=\"evenodd\" fill=\"#010101\" d=\"M9.75 11.51L6.7 9.5H3.75v-3H6.7L9.75 4.49v.664l.497.498V3.498L6.547 6H3.248v4h3.296l3.7 2.502v-2.154l-.497.5v.662zm3-5.165L12.404 6l-1.655 1.653L9.093 6l-.346.345L10.402 8 8.747 9.654l.346.347 1.655-1.653L12.403 10l.348-.346L11.097 8l1.655-1.655z\"></path></svg>" + +/***/ }, +/* 42 */ +/***/ function(module, exports) { + + module.exports = "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\"><path fill=\"#010101\" d=\"M7.156 8L4 11.156V8.5H3V13h4.5v-1H4.844L8 8.844 7.156 8zM8.5 3v1h2.657L8 7.157 8.846 8 12 4.844V7.5h1V3H8.5z\"></path></svg>" + +/***/ }, +/* 43 */ +/***/ function(module, exports) { + + module.exports = "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\"><path fill=\"#010101\" d=\"M13.5 3.344l-.844-.844L9.5 5.656V3h-1v4.5H13v-1h-2.656L13.5 3.344zM3 9.5h2.656L2.5 12.656l.844.844L6.5 10.344V13h1V8.5H3v1z\"></path></svg>" + +/***/ }, +/* 44 */ +/***/ function(module, exports) { + + module.exports = "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\"><path fill=\"#010101\" d=\"M5.375 7.062H2.637V4.26H.502v7.488h2.135V8.9h2.738v2.848h2.133V4.26H5.375v2.802zm5.97-2.81h-2.84v7.496h2.798c2.65 0 4.195-1.607 4.195-3.77v-.022c0-2.162-1.523-3.704-4.154-3.704zm2.06 3.758c0 1.21-.81 1.896-2.03 1.896h-.83V6.093h.83c1.22 0 2.03.696 2.03 1.896v.02z\"></path></svg>" + +/***/ }, +/* 45 */ +/***/ function(module, exports) { + + "use strict"; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + + // Copyright 2014 Globo.com Player authors. All rights reserved. + // Use of this source code is governed by a BSD-style + // license that can be found in the LICENSE file. + + var PlayerInfo = function PlayerInfo() { + _classCallCheck(this, PlayerInfo); + + this.options = {}; + this.playbackPlugins = []; + this.currentSize = { width: 0, height: 0 }; + }; + + PlayerInfo._players = {}; + + PlayerInfo.getInstance = function (playerId) { + return PlayerInfo._players[playerId] || (PlayerInfo._players[playerId] = new PlayerInfo()); + }; + + exports.default = PlayerInfo; + module.exports = exports["default"]; + +/***/ }, +/* 46 */ +/***/ function(module, exports, __webpack_require__) { + + exports = module.exports = __webpack_require__(25)(); + // imports + + + // module + exports.push([module.id, "@font-face {\n font-family: \"Roboto\";\n font-style: normal;\n font-weight: 400;\n src: local(\"Roboto\"), local(\"Roboto-Regular\"), url(" + __webpack_require__(47) + ") format(\"truetype\"); }\n\n[data-player] {\n -webkit-touch-callout: none;\n -webkit-user-select: none;\n -khtml-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n -o-user-select: none;\n user-select: none;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n -webkit-transform: translate3d(0, 0, 0);\n -moz-transform: translate3d(0, 0, 0);\n -ms-transform: translate3d(0, 0, 0);\n -o-transform: translate3d(0, 0, 0);\n transform: translate3d(0, 0, 0);\n position: relative;\n margin: 0;\n padding: 0;\n border: 0;\n font-style: normal;\n font-weight: normal;\n text-align: center;\n overflow: hidden;\n font-size: 100%;\n font-family: \"Roboto\", \"Open Sans\", Arial, sans-serif;\n text-shadow: 0 0 0;\n box-sizing: border-box; }\n [data-player] div, [data-player] span, [data-player] applet, [data-player] object, [data-player] iframe,\n [data-player] h1, [data-player] h2, [data-player] h3, [data-player] h4, [data-player] h5, [data-player] h6, [data-player] p, [data-player] blockquote, [data-player] pre,\n [data-player] a, [data-player] abbr, [data-player] acronym, [data-player] address, [data-player] big, [data-player] cite, [data-player] code,\n [data-player] del, [data-player] dfn, [data-player] em, [data-player] img, [data-player] ins, [data-player] kbd, [data-player] q, [data-player] s, [data-player] samp,\n [data-player] small, [data-player] strike, [data-player] strong, [data-player] sub, [data-player] sup, [data-player] tt, [data-player] var,\n [data-player] b, [data-player] u, [data-player] i, [data-player] center,\n [data-player] dl, [data-player] dt, [data-player] dd, [data-player] ol, [data-player] ul, [data-player] li,\n [data-player] fieldset, [data-player] form, [data-player] label, [data-player] legend,\n [data-player] table, [data-player] caption, [data-player] tbody, [data-player] tfoot, [data-player] thead, [data-player] tr, [data-player] th, [data-player] td,\n [data-player] article, [data-player] aside, [data-player] canvas, [data-player] details, [data-player] embed,\n [data-player] figure, [data-player] figcaption, [data-player] footer, [data-player] header, [data-player] hgroup,\n [data-player] menu, [data-player] nav, [data-player] output, [data-player] ruby, [data-player] section, [data-player] summary,\n [data-player] time, [data-player] mark, [data-player] audio, [data-player] video {\n margin: 0;\n padding: 0;\n border: 0;\n font: inherit;\n font-size: 100%;\n vertical-align: baseline; }\n [data-player] table {\n border-collapse: collapse;\n border-spacing: 0; }\n [data-player] caption, [data-player] th, [data-player] td {\n text-align: left;\n font-weight: normal;\n vertical-align: middle; }\n [data-player] q, [data-player] blockquote {\n quotes: none; }\n [data-player] q:before, [data-player] q:after, [data-player] blockquote:before, [data-player] blockquote:after {\n content: \"\";\n content: none; }\n [data-player] a img {\n border: none; }\n [data-player]:focus {\n outline: 0; }\n [data-player] * {\n max-width: none;\n box-sizing: inherit;\n float: none; }\n [data-player] div {\n display: block; }\n [data-player].fullscreen {\n width: 100% !important;\n height: 100% !important;\n top: 0;\n left: 0; }\n [data-player].nocursor {\n cursor: none; }\n\n.clappr-style {\n display: none !important; }\n", ""]); + + // exports + + +/***/ }, +/* 47 */ +/***/ function(module, exports, __webpack_require__) { + + module.exports = __webpack_require__.p + "38861cba61c66739c1452c3a71e39852.ttf"; + +/***/ }, +/* 48 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + module.exports = __webpack_require__(49); + +/***/ }, +/* 49 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + var _base_object = __webpack_require__(4); + + var _base_object2 = _interopRequireDefault(_base_object); + + var _player_info = __webpack_require__(45); + + var _player_info2 = _interopRequireDefault(_player_info); + + var _lodash = __webpack_require__(50); + + var _lodash2 = _interopRequireDefault(_lodash); + + var _html5_video = __webpack_require__(51); + + var _html5_video2 = _interopRequireDefault(_html5_video); + + var _flash = __webpack_require__(54); + + var _flash2 = _interopRequireDefault(_flash); + + var _html5_audio = __webpack_require__(61); + + var _html5_audio2 = _interopRequireDefault(_html5_audio); + + var _flashls = __webpack_require__(63); + + var _flashls2 = _interopRequireDefault(_flashls); + + var _hls = __webpack_require__(67); + + var _hls2 = _interopRequireDefault(_hls); + + var _html_img = __webpack_require__(71); + + var _html_img2 = _interopRequireDefault(_html_img); + + var _no_op = __webpack_require__(74); + + var _no_op2 = _interopRequireDefault(_no_op); + + var _spinner_three_bounce = __webpack_require__(78); + + var _spinner_three_bounce2 = _interopRequireDefault(_spinner_three_bounce); + + var _stats = __webpack_require__(83); + + var _stats2 = _interopRequireDefault(_stats); + + var _watermark = __webpack_require__(86); + + var _watermark2 = _interopRequireDefault(_watermark); + + var _poster = __webpack_require__(90); + + var _poster2 = _interopRequireDefault(_poster); + + var _google_analytics = __webpack_require__(94); + + var _google_analytics2 = _interopRequireDefault(_google_analytics); + + var _click_to_pause = __webpack_require__(96); + + var _click_to_pause2 = _interopRequireDefault(_click_to_pause); + + var _dvr_controls = __webpack_require__(98); + + var _dvr_controls2 = _interopRequireDefault(_dvr_controls); + + var _favicon = __webpack_require__(103); + + var _favicon2 = _interopRequireDefault(_favicon); + + var _seek_time = __webpack_require__(106); + + var _seek_time2 = _interopRequireDefault(_seek_time); + + var _sources = __webpack_require__(110); + + var _sources2 = _interopRequireDefault(_sources); + + var _end_video = __webpack_require__(111); + + var _end_video2 = _interopRequireDefault(_end_video); + + var _strings = __webpack_require__(112); + + var _strings2 = _interopRequireDefault(_strings); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + 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; } // Copyright 2014 Globo.com Player authors. All rights reserved. + // Use of this source code is governed by a BSD-style + // license that can be found in the LICENSE file. + + /* Playback Plugins */ + + + /* Container Plugins */ + + + /* Core Plugins */ + + + /** + * It keeps a list of the default plugins (playback, container, core) and it merges external plugins with its internals. + * @class Loader + * @constructor + * @extends BaseObject + * @module components + */ + var Loader = function (_BaseObject) { + _inherits(Loader, _BaseObject); + + /** + * builds the loader + * @method constructor + * @param {Object} externalPlugins the external plugins + * @param {Number} playerId you can embed multiple instances of clappr, therefore this is the unique id of each one. + */ + function Loader(externalPlugins, playerId) { + _classCallCheck(this, Loader); + + var _this = _possibleConstructorReturn(this, _BaseObject.call(this)); + + _this.playerId = playerId; + _this.playbackPlugins = [_hls2.default, _html5_video2.default, _html5_audio2.default, _flash2.default, _flashls2.default, _html_img2.default, _no_op2.default]; + _this.containerPlugins = [_spinner_three_bounce2.default, _watermark2.default, _poster2.default, _stats2.default, _google_analytics2.default, _click_to_pause2.default]; + _this.corePlugins = [_dvr_controls2.default, _favicon2.default, _seek_time2.default, _sources2.default, _end_video2.default, _strings2.default]; + if (externalPlugins) { + if (!Array.isArray(externalPlugins)) { + _this.validateExternalPluginsType(externalPlugins); + } + _this.addExternalPlugins(externalPlugins); + } + return _this; + } + + /** + * groups by type the external plugins that were passed through `options.plugins` it they're on a flat array + * @method addExternalPlugins + * @private + * @param {Object} an config object or an array of plugins + * @return {Object} plugins the config object with the plugins separated by type + */ + + + Loader.prototype.groupPluginsByType = function groupPluginsByType(plugins) { + if (Array.isArray(plugins)) { + plugins = plugins.reduce(function (memo, plugin) { + memo[plugin.type] || (memo[plugin.type] = []); + memo[plugin.type].push(plugin); + return memo; + }, {}); + } + return plugins; + }; + + /** + * adds all the external plugins that were passed through `options.plugins` + * @method addExternalPlugins + * @private + * @param {Object} plugins the config object with all plugins + */ + + + Loader.prototype.addExternalPlugins = function addExternalPlugins(plugins) { + plugins = this.groupPluginsByType(plugins); + var pluginName = function pluginName(plugin) { + return plugin.prototype.name; + }; + if (plugins.playback) { + this.playbackPlugins = (0, _lodash2.default)(plugins.playback.concat(this.playbackPlugins), pluginName); + } + if (plugins.container) { + this.containerPlugins = (0, _lodash2.default)(plugins.container.concat(this.containerPlugins), pluginName); + } + if (plugins.core) { + this.corePlugins = (0, _lodash2.default)(plugins.core.concat(this.corePlugins), pluginName); + } + _player_info2.default.getInstance(this.playerId).playbackPlugins = this.playbackPlugins; + }; + + /** + * validate if the external plugins that were passed through `options.plugins` are associated to the correct type + * @method validateExternalPluginsType + * @private + * @param {Object} plugins the config object with all plugins + */ + + + Loader.prototype.validateExternalPluginsType = function validateExternalPluginsType(plugins) { + var plugintypes = ['playback', 'container', 'core']; + plugintypes.forEach(function (type) { + (plugins[type] || []).forEach(function (el) { + var errorMessage = 'external ' + el.type + ' plugin on ' + type + ' array'; + if (el.type !== type) { + throw new ReferenceError(errorMessage); + } + }); + }); + }; + + return Loader; + }(_base_object2.default); + + exports.default = Loader; + module.exports = exports['default']; + +/***/ }, +/* 50 */ +/***/ function(module, exports, __webpack_require__) { + + /* WEBPACK VAR INJECTION */(function(global, module) {/** + * lodash (Custom Build) <https://lodash.com/> + * Build: `lodash modularize exports="npm" -o ./` + * Copyright jQuery Foundation and other contributors <https://jquery.org/> + * Released under MIT license <https://lodash.com/license> + * Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE> + * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors + */ + + /** Used as the size to enable large array optimizations. */ + var LARGE_ARRAY_SIZE = 200; + + /** Used as the `TypeError` message for "Functions" methods. */ + var FUNC_ERROR_TEXT = 'Expected a function'; + + /** Used to stand-in for `undefined` hash values. */ + var HASH_UNDEFINED = '__lodash_hash_undefined__'; + + /** Used to compose bitmasks for comparison styles. */ + var UNORDERED_COMPARE_FLAG = 1, + PARTIAL_COMPARE_FLAG = 2; + + /** Used as references for various `Number` constants. */ + var INFINITY = 1 / 0, + MAX_SAFE_INTEGER = 9007199254740991; + + /** `Object#toString` result references. */ + var argsTag = '[object Arguments]', + arrayTag = '[object Array]', + boolTag = '[object Boolean]', + dateTag = '[object Date]', + errorTag = '[object Error]', + funcTag = '[object Function]', + genTag = '[object GeneratorFunction]', + mapTag = '[object Map]', + numberTag = '[object Number]', + objectTag = '[object Object]', + promiseTag = '[object Promise]', + regexpTag = '[object RegExp]', + setTag = '[object Set]', + stringTag = '[object String]', + symbolTag = '[object Symbol]', + weakMapTag = '[object WeakMap]'; + + var arrayBufferTag = '[object ArrayBuffer]', + dataViewTag = '[object DataView]', + float32Tag = '[object Float32Array]', + float64Tag = '[object Float64Array]', + int8Tag = '[object Int8Array]', + int16Tag = '[object Int16Array]', + int32Tag = '[object Int32Array]', + uint8Tag = '[object Uint8Array]', + uint8ClampedTag = '[object Uint8ClampedArray]', + uint16Tag = '[object Uint16Array]', + uint32Tag = '[object Uint32Array]'; + + /** Used to match property names within property paths. */ + var reIsDeepProp = /\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/, + reIsPlainProp = /^\w*$/, + reLeadingDot = /^\./, + rePropName = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g; + + /** + * Used to match `RegExp` + * [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns). + */ + var reRegExpChar = /[\\^$.*+?()[\]{}|]/g; + + /** Used to match backslashes in property paths. */ + var reEscapeChar = /\\(\\)?/g; + + /** Used to detect host constructors (Safari). */ + var reIsHostCtor = /^\[object .+?Constructor\]$/; + + /** Used to detect unsigned integer values. */ + var reIsUint = /^(?:0|[1-9]\d*)$/; + + /** Used to identify `toStringTag` values of typed arrays. */ + var typedArrayTags = {}; + typedArrayTags[float32Tag] = typedArrayTags[float64Tag] = + typedArrayTags[int8Tag] = typedArrayTags[int16Tag] = + typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] = + typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] = + typedArrayTags[uint32Tag] = true; + typedArrayTags[argsTag] = typedArrayTags[arrayTag] = + typedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] = + typedArrayTags[dataViewTag] = typedArrayTags[dateTag] = + typedArrayTags[errorTag] = typedArrayTags[funcTag] = + typedArrayTags[mapTag] = typedArrayTags[numberTag] = + typedArrayTags[objectTag] = typedArrayTags[regexpTag] = + typedArrayTags[setTag] = typedArrayTags[stringTag] = + typedArrayTags[weakMapTag] = false; + + /** Detect free variable `global` from Node.js. */ + var freeGlobal = typeof global == 'object' && global && global.Object === Object && global; + + /** Detect free variable `self`. */ + var freeSelf = typeof self == 'object' && self && self.Object === Object && self; + + /** Used as a reference to the global object. */ + var root = freeGlobal || freeSelf || Function('return this')(); + + /** Detect free variable `exports`. */ + var freeExports = typeof exports == 'object' && exports && !exports.nodeType && exports; + + /** Detect free variable `module`. */ + var freeModule = freeExports && typeof module == 'object' && module && !module.nodeType && module; + + /** Detect the popular CommonJS extension `module.exports`. */ + var moduleExports = freeModule && freeModule.exports === freeExports; + + /** Detect free variable `process` from Node.js. */ + var freeProcess = moduleExports && freeGlobal.process; + + /** Used to access faster Node.js helpers. */ + var nodeUtil = (function() { + try { + return freeProcess && freeProcess.binding('util'); + } catch (e) {} + }()); + + /* Node.js helper references. */ + var nodeIsTypedArray = nodeUtil && nodeUtil.isTypedArray; + + /** + * A specialized version of `_.includes` for arrays without support for + * specifying an index to search from. + * + * @private + * @param {Array} [array] The array to inspect. + * @param {*} target The value to search for. + * @returns {boolean} Returns `true` if `target` is found, else `false`. + */ + function arrayIncludes(array, value) { + var length = array ? array.length : 0; + return !!length && baseIndexOf(array, value, 0) > -1; + } + + /** + * This function is like `arrayIncludes` except that it accepts a comparator. + * + * @private + * @param {Array} [array] The array to inspect. + * @param {*} target The value to search for. + * @param {Function} comparator The comparator invoked per element. + * @returns {boolean} Returns `true` if `target` is found, else `false`. + */ + function arrayIncludesWith(array, value, comparator) { + var index = -1, + length = array ? array.length : 0; + + while (++index < length) { + if (comparator(value, array[index])) { + return true; + } + } + return false; + } + + /** + * A specialized version of `_.some` for arrays without support for iteratee + * shorthands. + * + * @private + * @param {Array} [array] The array to iterate over. + * @param {Function} predicate The function invoked per iteration. + * @returns {boolean} Returns `true` if any element passes the predicate check, + * else `false`. + */ + function arraySome(array, predicate) { + var index = -1, + length = array ? array.length : 0; + + while (++index < length) { + if (predicate(array[index], index, array)) { + return true; + } + } + return false; + } + + /** + * The base implementation of `_.findIndex` and `_.findLastIndex` without + * support for iteratee shorthands. + * + * @private + * @param {Array} array The array to inspect. + * @param {Function} predicate The function invoked per iteration. + * @param {number} fromIndex The index to search from. + * @param {boolean} [fromRight] Specify iterating from right to left. + * @returns {number} Returns the index of the matched value, else `-1`. + */ + function baseFindIndex(array, predicate, fromIndex, fromRight) { + var length = array.length, + index = fromIndex + (fromRight ? 1 : -1); + + while ((fromRight ? index-- : ++index < length)) { + if (predicate(array[index], index, array)) { + return index; + } + } + return -1; + } + + /** + * The base implementation of `_.indexOf` without `fromIndex` bounds checks. + * + * @private + * @param {Array} array The array to inspect. + * @param {*} value The value to search for. + * @param {number} fromIndex The index to search from. + * @returns {number} Returns the index of the matched value, else `-1`. + */ + function baseIndexOf(array, value, fromIndex) { + if (value !== value) { + return baseFindIndex(array, baseIsNaN, fromIndex); + } + var index = fromIndex - 1, + length = array.length; + + while (++index < length) { + if (array[index] === value) { + return index; + } + } + return -1; + } + + /** + * The base implementation of `_.isNaN` without support for number objects. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is `NaN`, else `false`. + */ + function baseIsNaN(value) { + return value !== value; + } + + /** + * The base implementation of `_.property` without support for deep paths. + * + * @private + * @param {string} key The key of the property to get. + * @returns {Function} Returns the new accessor function. + */ + function baseProperty(key) { + return function(object) { + return object == null ? undefined : object[key]; + }; + } + + /** + * The base implementation of `_.times` without support for iteratee shorthands + * or max array length checks. + * + * @private + * @param {number} n The number of times to invoke `iteratee`. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array} Returns the array of results. + */ + function baseTimes(n, iteratee) { + var index = -1, + result = Array(n); + + while (++index < n) { + result[index] = iteratee(index); + } + return result; + } + + /** + * The base implementation of `_.unary` without support for storing metadata. + * + * @private + * @param {Function} func The function to cap arguments for. + * @returns {Function} Returns the new capped function. + */ + function baseUnary(func) { + return function(value) { + return func(value); + }; + } + + /** + * Checks if a cache value for `key` exists. + * + * @private + * @param {Object} cache The cache to query. + * @param {string} key The key of the entry to check. + * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. + */ + function cacheHas(cache, key) { + return cache.has(key); + } + + /** + * Gets the value at `key` of `object`. + * + * @private + * @param {Object} [object] The object to query. + * @param {string} key The key of the property to get. + * @returns {*} Returns the property value. + */ + function getValue(object, key) { + return object == null ? undefined : object[key]; + } + + /** + * Checks if `value` is a host object in IE < 9. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a host object, else `false`. + */ + function isHostObject(value) { + // Many host objects are `Object` objects that can coerce to strings + // despite having improperly defined `toString` methods. + var result = false; + if (value != null && typeof value.toString != 'function') { + try { + result = !!(value + ''); + } catch (e) {} + } + return result; + } + + /** + * Converts `map` to its key-value pairs. + * + * @private + * @param {Object} map The map to convert. + * @returns {Array} Returns the key-value pairs. + */ + function mapToArray(map) { + var index = -1, + result = Array(map.size); + + map.forEach(function(value, key) { + result[++index] = [key, value]; + }); + return result; + } + + /** + * Creates a unary function that invokes `func` with its argument transformed. + * + * @private + * @param {Function} func The function to wrap. + * @param {Function} transform The argument transform. + * @returns {Function} Returns the new function. + */ + function overArg(func, transform) { + return function(arg) { + return func(transform(arg)); + }; + } + + /** + * Converts `set` to an array of its values. + * + * @private + * @param {Object} set The set to convert. + * @returns {Array} Returns the values. + */ + function setToArray(set) { + var index = -1, + result = Array(set.size); + + set.forEach(function(value) { + result[++index] = value; + }); + return result; + } + + /** Used for built-in method references. */ + var arrayProto = Array.prototype, + funcProto = Function.prototype, + objectProto = Object.prototype; + + /** Used to detect overreaching core-js shims. */ + var coreJsData = root['__core-js_shared__']; + + /** Used to detect methods masquerading as native. */ + var maskSrcKey = (function() { + var uid = /[^.]+$/.exec(coreJsData && coreJsData.keys && coreJsData.keys.IE_PROTO || ''); + return uid ? ('Symbol(src)_1.' + uid) : ''; + }()); + + /** Used to resolve the decompiled source of functions. */ + var funcToString = funcProto.toString; + + /** Used to check objects for own properties. */ + var hasOwnProperty = objectProto.hasOwnProperty; + + /** + * Used to resolve the + * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring) + * of values. + */ + var objectToString = objectProto.toString; + + /** Used to detect if a method is native. */ + var reIsNative = RegExp('^' + + funcToString.call(hasOwnProperty).replace(reRegExpChar, '\\$&') + .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$' + ); + + /** Built-in value references. */ + var Symbol = root.Symbol, + Uint8Array = root.Uint8Array, + propertyIsEnumerable = objectProto.propertyIsEnumerable, + splice = arrayProto.splice; + + /* Built-in method references for those with the same name as other `lodash` methods. */ + var nativeKeys = overArg(Object.keys, Object); + + /* Built-in method references that are verified to be native. */ + var DataView = getNative(root, 'DataView'), + Map = getNative(root, 'Map'), + Promise = getNative(root, 'Promise'), + Set = getNative(root, 'Set'), + WeakMap = getNative(root, 'WeakMap'), + nativeCreate = getNative(Object, 'create'); + + /** Used to detect maps, sets, and weakmaps. */ + var dataViewCtorString = toSource(DataView), + mapCtorString = toSource(Map), + promiseCtorString = toSource(Promise), + setCtorString = toSource(Set), + weakMapCtorString = toSource(WeakMap); + + /** Used to convert symbols to primitives and strings. */ + var symbolProto = Symbol ? Symbol.prototype : undefined, + symbolValueOf = symbolProto ? symbolProto.valueOf : undefined, + symbolToString = symbolProto ? symbolProto.toString : undefined; + + /** + * Creates a hash object. + * + * @private + * @constructor + * @param {Array} [entries] The key-value pairs to cache. + */ + function Hash(entries) { + var index = -1, + length = entries ? entries.length : 0; + + this.clear(); + while (++index < length) { + var entry = entries[index]; + this.set(entry[0], entry[1]); + } + } + + /** + * Removes all key-value entries from the hash. + * + * @private + * @name clear + * @memberOf Hash + */ + function hashClear() { + this.__data__ = nativeCreate ? nativeCreate(null) : {}; + } + + /** + * Removes `key` and its value from the hash. + * + * @private + * @name delete + * @memberOf Hash + * @param {Object} hash The hash to modify. + * @param {string} key The key of the value to remove. + * @returns {boolean} Returns `true` if the entry was removed, else `false`. + */ + function hashDelete(key) { + return this.has(key) && delete this.__data__[key]; + } + + /** + * Gets the hash value for `key`. + * + * @private + * @name get + * @memberOf Hash + * @param {string} key The key of the value to get. + * @returns {*} Returns the entry value. + */ + function hashGet(key) { + var data = this.__data__; + if (nativeCreate) { + var result = data[key]; + return result === HASH_UNDEFINED ? undefined : result; + } + return hasOwnProperty.call(data, key) ? data[key] : undefined; + } + + /** + * Checks if a hash value for `key` exists. + * + * @private + * @name has + * @memberOf Hash + * @param {string} key The key of the entry to check. + * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. + */ + function hashHas(key) { + var data = this.__data__; + return nativeCreate ? data[key] !== undefined : hasOwnProperty.call(data, key); + } + + /** + * Sets the hash `key` to `value`. + * + * @private + * @name set + * @memberOf Hash + * @param {string} key The key of the value to set. + * @param {*} value The value to set. + * @returns {Object} Returns the hash instance. + */ + function hashSet(key, value) { + var data = this.__data__; + data[key] = (nativeCreate && value === undefined) ? HASH_UNDEFINED : value; + return this; + } + + // Add methods to `Hash`. + Hash.prototype.clear = hashClear; + Hash.prototype['delete'] = hashDelete; + Hash.prototype.get = hashGet; + Hash.prototype.has = hashHas; + Hash.prototype.set = hashSet; + + /** + * Creates an list cache object. + * + * @private + * @constructor + * @param {Array} [entries] The key-value pairs to cache. + */ + function ListCache(entries) { + var index = -1, + length = entries ? entries.length : 0; + + this.clear(); + while (++index < length) { + var entry = entries[index]; + this.set(entry[0], entry[1]); + } + } + + /** + * Removes all key-value entries from the list cache. + * + * @private + * @name clear + * @memberOf ListCache + */ + function listCacheClear() { + this.__data__ = []; + } + + /** + * Removes `key` and its value from the list cache. + * + * @private + * @name delete + * @memberOf ListCache + * @param {string} key The key of the value to remove. + * @returns {boolean} Returns `true` if the entry was removed, else `false`. + */ + function listCacheDelete(key) { + var data = this.__data__, + index = assocIndexOf(data, key); + + if (index < 0) { + return false; + } + var lastIndex = data.length - 1; + if (index == lastIndex) { + data.pop(); + } else { + splice.call(data, index, 1); + } + return true; + } + + /** + * Gets the list cache value for `key`. + * + * @private + * @name get + * @memberOf ListCache + * @param {string} key The key of the value to get. + * @returns {*} Returns the entry value. + */ + function listCacheGet(key) { + var data = this.__data__, + index = assocIndexOf(data, key); + + return index < 0 ? undefined : data[index][1]; + } + + /** + * Checks if a list cache value for `key` exists. + * + * @private + * @name has + * @memberOf ListCache + * @param {string} key The key of the entry to check. + * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. + */ + function listCacheHas(key) { + return assocIndexOf(this.__data__, key) > -1; + } + + /** + * Sets the list cache `key` to `value`. + * + * @private + * @name set + * @memberOf ListCache + * @param {string} key The key of the value to set. + * @param {*} value The value to set. + * @returns {Object} Returns the list cache instance. + */ + function listCacheSet(key, value) { + var data = this.__data__, + index = assocIndexOf(data, key); + + if (index < 0) { + data.push([key, value]); + } else { + data[index][1] = value; + } + return this; + } + + // Add methods to `ListCache`. + ListCache.prototype.clear = listCacheClear; + ListCache.prototype['delete'] = listCacheDelete; + ListCache.prototype.get = listCacheGet; + ListCache.prototype.has = listCacheHas; + ListCache.prototype.set = listCacheSet; + + /** + * Creates a map cache object to store key-value pairs. + * + * @private + * @constructor + * @param {Array} [entries] The key-value pairs to cache. + */ + function MapCache(entries) { + var index = -1, + length = entries ? entries.length : 0; + + this.clear(); + while (++index < length) { + var entry = entries[index]; + this.set(entry[0], entry[1]); + } + } + + /** + * Removes all key-value entries from the map. + * + * @private + * @name clear + * @memberOf MapCache + */ + function mapCacheClear() { + this.__data__ = { + 'hash': new Hash, + 'map': new (Map || ListCache), + 'string': new Hash + }; + } + + /** + * Removes `key` and its value from the map. + * + * @private + * @name delete + * @memberOf MapCache + * @param {string} key The key of the value to remove. + * @returns {boolean} Returns `true` if the entry was removed, else `false`. + */ + function mapCacheDelete(key) { + return getMapData(this, key)['delete'](key); + } + + /** + * Gets the map value for `key`. + * + * @private + * @name get + * @memberOf MapCache + * @param {string} key The key of the value to get. + * @returns {*} Returns the entry value. + */ + function mapCacheGet(key) { + return getMapData(this, key).get(key); + } + + /** + * Checks if a map value for `key` exists. + * + * @private + * @name has + * @memberOf MapCache + * @param {string} key The key of the entry to check. + * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. + */ + function mapCacheHas(key) { + return getMapData(this, key).has(key); + } + + /** + * Sets the map `key` to `value`. + * + * @private + * @name set + * @memberOf MapCache + * @param {string} key The key of the value to set. + * @param {*} value The value to set. + * @returns {Object} Returns the map cache instance. + */ + function mapCacheSet(key, value) { + getMapData(this, key).set(key, value); + return this; + } + + // Add methods to `MapCache`. + MapCache.prototype.clear = mapCacheClear; + MapCache.prototype['delete'] = mapCacheDelete; + MapCache.prototype.get = mapCacheGet; + MapCache.prototype.has = mapCacheHas; + MapCache.prototype.set = mapCacheSet; + + /** + * + * Creates an array cache object to store unique values. + * + * @private + * @constructor + * @param {Array} [values] The values to cache. + */ + function SetCache(values) { + var index = -1, + length = values ? values.length : 0; + + this.__data__ = new MapCache; + while (++index < length) { + this.add(values[index]); + } + } + + /** + * Adds `value` to the array cache. + * + * @private + * @name add + * @memberOf SetCache + * @alias push + * @param {*} value The value to cache. + * @returns {Object} Returns the cache instance. + */ + function setCacheAdd(value) { + this.__data__.set(value, HASH_UNDEFINED); + return this; + } + + /** + * Checks if `value` is in the array cache. + * + * @private + * @name has + * @memberOf SetCache + * @param {*} value The value to search for. + * @returns {number} Returns `true` if `value` is found, else `false`. + */ + function setCacheHas(value) { + return this.__data__.has(value); + } + + // Add methods to `SetCache`. + SetCache.prototype.add = SetCache.prototype.push = setCacheAdd; + SetCache.prototype.has = setCacheHas; + + /** + * Creates a stack cache object to store key-value pairs. + * + * @private + * @constructor + * @param {Array} [entries] The key-value pairs to cache. + */ + function Stack(entries) { + this.__data__ = new ListCache(entries); + } + + /** + * Removes all key-value entries from the stack. + * + * @private + * @name clear + * @memberOf Stack + */ + function stackClear() { + this.__data__ = new ListCache; + } + + /** + * Removes `key` and its value from the stack. + * + * @private + * @name delete + * @memberOf Stack + * @param {string} key The key of the value to remove. + * @returns {boolean} Returns `true` if the entry was removed, else `false`. + */ + function stackDelete(key) { + return this.__data__['delete'](key); + } + + /** + * Gets the stack value for `key`. + * + * @private + * @name get + * @memberOf Stack + * @param {string} key The key of the value to get. + * @returns {*} Returns the entry value. + */ + function stackGet(key) { + return this.__data__.get(key); + } + + /** + * Checks if a stack value for `key` exists. + * + * @private + * @name has + * @memberOf Stack + * @param {string} key The key of the entry to check. + * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. + */ + function stackHas(key) { + return this.__data__.has(key); + } + + /** + * Sets the stack `key` to `value`. + * + * @private + * @name set + * @memberOf Stack + * @param {string} key The key of the value to set. + * @param {*} value The value to set. + * @returns {Object} Returns the stack cache instance. + */ + function stackSet(key, value) { + var cache = this.__data__; + if (cache instanceof ListCache) { + var pairs = cache.__data__; + if (!Map || (pairs.length < LARGE_ARRAY_SIZE - 1)) { + pairs.push([key, value]); + return this; + } + cache = this.__data__ = new MapCache(pairs); + } + cache.set(key, value); + return this; + } + + // Add methods to `Stack`. + Stack.prototype.clear = stackClear; + Stack.prototype['delete'] = stackDelete; + Stack.prototype.get = stackGet; + Stack.prototype.has = stackHas; + Stack.prototype.set = stackSet; + + /** + * Creates an array of the enumerable property names of the array-like `value`. + * + * @private + * @param {*} value The value to query. + * @param {boolean} inherited Specify returning inherited property names. + * @returns {Array} Returns the array of property names. + */ + function arrayLikeKeys(value, inherited) { + // Safari 8.1 makes `arguments.callee` enumerable in strict mode. + // Safari 9 makes `arguments.length` enumerable in strict mode. + var result = (isArray(value) || isArguments(value)) + ? baseTimes(value.length, String) + : []; + + var length = result.length, + skipIndexes = !!length; + + for (var key in value) { + if ((inherited || hasOwnProperty.call(value, key)) && + !(skipIndexes && (key == 'length' || isIndex(key, length)))) { + result.push(key); + } + } + return result; + } + + /** + * Gets the index at which the `key` is found in `array` of key-value pairs. + * + * @private + * @param {Array} array The array to inspect. + * @param {*} key The key to search for. + * @returns {number} Returns the index of the matched value, else `-1`. + */ + function assocIndexOf(array, key) { + var length = array.length; + while (length--) { + if (eq(array[length][0], key)) { + return length; + } + } + return -1; + } + + /** + * The base implementation of `_.get` without support for default values. + * + * @private + * @param {Object} object The object to query. + * @param {Array|string} path The path of the property to get. + * @returns {*} Returns the resolved value. + */ + function baseGet(object, path) { + path = isKey(path, object) ? [path] : castPath(path); + + var index = 0, + length = path.length; + + while (object != null && index < length) { + object = object[toKey(path[index++])]; + } + return (index && index == length) ? object : undefined; + } + + /** + * The base implementation of `getTag`. + * + * @private + * @param {*} value The value to query. + * @returns {string} Returns the `toStringTag`. + */ + function baseGetTag(value) { + return objectToString.call(value); + } + + /** + * The base implementation of `_.hasIn` without support for deep paths. + * + * @private + * @param {Object} [object] The object to query. + * @param {Array|string} key The key to check. + * @returns {boolean} Returns `true` if `key` exists, else `false`. + */ + function baseHasIn(object, key) { + return object != null && key in Object(object); + } + + /** + * The base implementation of `_.isEqual` which supports partial comparisons + * and tracks traversed objects. + * + * @private + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @param {Function} [customizer] The function to customize comparisons. + * @param {boolean} [bitmask] The bitmask of comparison flags. + * The bitmask may be composed of the following flags: + * 1 - Unordered comparison + * 2 - Partial comparison + * @param {Object} [stack] Tracks traversed `value` and `other` objects. + * @returns {boolean} Returns `true` if the values are equivalent, else `false`. + */ + function baseIsEqual(value, other, customizer, bitmask, stack) { + if (value === other) { + return true; + } + if (value == null || other == null || (!isObject(value) && !isObjectLike(other))) { + return value !== value && other !== other; + } + return baseIsEqualDeep(value, other, baseIsEqual, customizer, bitmask, stack); + } + + /** + * A specialized version of `baseIsEqual` for arrays and objects which performs + * deep comparisons and tracks traversed objects enabling objects with circular + * references to be compared. + * + * @private + * @param {Object} object The object to compare. + * @param {Object} other The other object to compare. + * @param {Function} equalFunc The function to determine equivalents of values. + * @param {Function} [customizer] The function to customize comparisons. + * @param {number} [bitmask] The bitmask of comparison flags. See `baseIsEqual` + * for more details. + * @param {Object} [stack] Tracks traversed `object` and `other` objects. + * @returns {boolean} Returns `true` if the objects are equivalent, else `false`. + */ + function baseIsEqualDeep(object, other, equalFunc, customizer, bitmask, stack) { + var objIsArr = isArray(object), + othIsArr = isArray(other), + objTag = arrayTag, + othTag = arrayTag; + + if (!objIsArr) { + objTag = getTag(object); + objTag = objTag == argsTag ? objectTag : objTag; + } + if (!othIsArr) { + othTag = getTag(other); + othTag = othTag == argsTag ? objectTag : othTag; + } + var objIsObj = objTag == objectTag && !isHostObject(object), + othIsObj = othTag == objectTag && !isHostObject(other), + isSameTag = objTag == othTag; + + if (isSameTag && !objIsObj) { + stack || (stack = new Stack); + return (objIsArr || isTypedArray(object)) + ? equalArrays(object, other, equalFunc, customizer, bitmask, stack) + : equalByTag(object, other, objTag, equalFunc, customizer, bitmask, stack); + } + if (!(bitmask & PARTIAL_COMPARE_FLAG)) { + var objIsWrapped = objIsObj && hasOwnProperty.call(object, '__wrapped__'), + othIsWrapped = othIsObj && hasOwnProperty.call(other, '__wrapped__'); + + if (objIsWrapped || othIsWrapped) { + var objUnwrapped = objIsWrapped ? object.value() : object, + othUnwrapped = othIsWrapped ? other.value() : other; + + stack || (stack = new Stack); + return equalFunc(objUnwrapped, othUnwrapped, customizer, bitmask, stack); + } + } + if (!isSameTag) { + return false; + } + stack || (stack = new Stack); + return equalObjects(object, other, equalFunc, customizer, bitmask, stack); + } + + /** + * The base implementation of `_.isMatch` without support for iteratee shorthands. + * + * @private + * @param {Object} object The object to inspect. + * @param {Object} source The object of property values to match. + * @param {Array} matchData The property names, values, and compare flags to match. + * @param {Function} [customizer] The function to customize comparisons. + * @returns {boolean} Returns `true` if `object` is a match, else `false`. + */ + function baseIsMatch(object, source, matchData, customizer) { + var index = matchData.length, + length = index, + noCustomizer = !customizer; + + if (object == null) { + return !length; + } + object = Object(object); + while (index--) { + var data = matchData[index]; + if ((noCustomizer && data[2]) + ? data[1] !== object[data[0]] + : !(data[0] in object) + ) { + return false; + } + } + while (++index < length) { + data = matchData[index]; + var key = data[0], + objValue = object[key], + srcValue = data[1]; + + if (noCustomizer && data[2]) { + if (objValue === undefined && !(key in object)) { + return false; + } + } else { + var stack = new Stack; + if (customizer) { + var result = customizer(objValue, srcValue, key, object, source, stack); + } + if (!(result === undefined + ? baseIsEqual(srcValue, objValue, customizer, UNORDERED_COMPARE_FLAG | PARTIAL_COMPARE_FLAG, stack) + : result + )) { + return false; + } + } + } + return true; + } + + /** + * The base implementation of `_.isNative` without bad shim checks. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a native function, + * else `false`. + */ + function baseIsNative(value) { + if (!isObject(value) || isMasked(value)) { + return false; + } + var pattern = (isFunction(value) || isHostObject(value)) ? reIsNative : reIsHostCtor; + return pattern.test(toSource(value)); + } + + /** + * The base implementation of `_.isTypedArray` without Node.js optimizations. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a typed array, else `false`. + */ + function baseIsTypedArray(value) { + return isObjectLike(value) && + isLength(value.length) && !!typedArrayTags[objectToString.call(value)]; + } + + /** + * The base implementation of `_.iteratee`. + * + * @private + * @param {*} [value=_.identity] The value to convert to an iteratee. + * @returns {Function} Returns the iteratee. + */ + function baseIteratee(value) { + // Don't store the `typeof` result in a variable to avoid a JIT bug in Safari 9. + // See https://bugs.webkit.org/show_bug.cgi?id=156034 for more details. + if (typeof value == 'function') { + return value; + } + if (value == null) { + return identity; + } + if (typeof value == 'object') { + return isArray(value) + ? baseMatchesProperty(value[0], value[1]) + : baseMatches(value); + } + return property(value); + } + + /** + * The base implementation of `_.keys` which doesn't treat sparse arrays as dense. + * + * @private + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names. + */ + function baseKeys(object) { + if (!isPrototype(object)) { + return nativeKeys(object); + } + var result = []; + for (var key in Object(object)) { + if (hasOwnProperty.call(object, key) && key != 'constructor') { + result.push(key); + } + } + return result; + } + + /** + * The base implementation of `_.matches` which doesn't clone `source`. + * + * @private + * @param {Object} source The object of property values to match. + * @returns {Function} Returns the new spec function. + */ + function baseMatches(source) { + var matchData = getMatchData(source); + if (matchData.length == 1 && matchData[0][2]) { + return matchesStrictComparable(matchData[0][0], matchData[0][1]); + } + return function(object) { + return object === source || baseIsMatch(object, source, matchData); + }; + } + + /** + * The base implementation of `_.matchesProperty` which doesn't clone `srcValue`. + * + * @private + * @param {string} path The path of the property to get. + * @param {*} srcValue The value to match. + * @returns {Function} Returns the new spec function. + */ + function baseMatchesProperty(path, srcValue) { + if (isKey(path) && isStrictComparable(srcValue)) { + return matchesStrictComparable(toKey(path), srcValue); + } + return function(object) { + var objValue = get(object, path); + return (objValue === undefined && objValue === srcValue) + ? hasIn(object, path) + : baseIsEqual(srcValue, objValue, undefined, UNORDERED_COMPARE_FLAG | PARTIAL_COMPARE_FLAG); + }; + } + + /** + * A specialized version of `baseProperty` which supports deep paths. + * + * @private + * @param {Array|string} path The path of the property to get. + * @returns {Function} Returns the new accessor function. + */ + function basePropertyDeep(path) { + return function(object) { + return baseGet(object, path); + }; + } + + /** + * The base implementation of `_.toString` which doesn't convert nullish + * values to empty strings. + * + * @private + * @param {*} value The value to process. + * @returns {string} Returns the string. + */ + function baseToString(value) { + // Exit early for strings to avoid a performance hit in some environments. + if (typeof value == 'string') { + return value; + } + if (isSymbol(value)) { + return symbolToString ? symbolToString.call(value) : ''; + } + var result = (value + ''); + return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result; + } + + /** + * The base implementation of `_.uniqBy` without support for iteratee shorthands. + * + * @private + * @param {Array} array The array to inspect. + * @param {Function} [iteratee] The iteratee invoked per element. + * @param {Function} [comparator] The comparator invoked per element. + * @returns {Array} Returns the new duplicate free array. + */ + function baseUniq(array, iteratee, comparator) { + var index = -1, + includes = arrayIncludes, + length = array.length, + isCommon = true, + result = [], + seen = result; + + if (comparator) { + isCommon = false; + includes = arrayIncludesWith; + } + else if (length >= LARGE_ARRAY_SIZE) { + var set = iteratee ? null : createSet(array); + if (set) { + return setToArray(set); + } + isCommon = false; + includes = cacheHas; + seen = new SetCache; + } + else { + seen = iteratee ? [] : result; + } + outer: + while (++index < length) { + var value = array[index], + computed = iteratee ? iteratee(value) : value; + + value = (comparator || value !== 0) ? value : 0; + if (isCommon && computed === computed) { + var seenIndex = seen.length; + while (seenIndex--) { + if (seen[seenIndex] === computed) { + continue outer; + } + } + if (iteratee) { + seen.push(computed); + } + result.push(value); + } + else if (!includes(seen, computed, comparator)) { + if (seen !== result) { + seen.push(computed); + } + result.push(value); + } + } + return result; + } + + /** + * Casts `value` to a path array if it's not one. + * + * @private + * @param {*} value The value to inspect. + * @returns {Array} Returns the cast property path array. + */ + function castPath(value) { + return isArray(value) ? value : stringToPath(value); + } + + /** + * Creates a set object of `values`. + * + * @private + * @param {Array} values The values to add to the set. + * @returns {Object} Returns the new set. + */ + var createSet = !(Set && (1 / setToArray(new Set([,-0]))[1]) == INFINITY) ? noop : function(values) { + return new Set(values); + }; + + /** + * A specialized version of `baseIsEqualDeep` for arrays with support for + * partial deep comparisons. + * + * @private + * @param {Array} array The array to compare. + * @param {Array} other The other array to compare. + * @param {Function} equalFunc The function to determine equivalents of values. + * @param {Function} customizer The function to customize comparisons. + * @param {number} bitmask The bitmask of comparison flags. See `baseIsEqual` + * for more details. + * @param {Object} stack Tracks traversed `array` and `other` objects. + * @returns {boolean} Returns `true` if the arrays are equivalent, else `false`. + */ + function equalArrays(array, other, equalFunc, customizer, bitmask, stack) { + var isPartial = bitmask & PARTIAL_COMPARE_FLAG, + arrLength = array.length, + othLength = other.length; + + if (arrLength != othLength && !(isPartial && othLength > arrLength)) { + return false; + } + // Assume cyclic values are equal. + var stacked = stack.get(array); + if (stacked && stack.get(other)) { + return stacked == other; + } + var index = -1, + result = true, + seen = (bitmask & UNORDERED_COMPARE_FLAG) ? new SetCache : undefined; + + stack.set(array, other); + stack.set(other, array); + + // Ignore non-index properties. + while (++index < arrLength) { + var arrValue = array[index], + othValue = other[index]; + + if (customizer) { + var compared = isPartial + ? customizer(othValue, arrValue, index, other, array, stack) + : customizer(arrValue, othValue, index, array, other, stack); + } + if (compared !== undefined) { + if (compared) { + continue; + } + result = false; + break; + } + // Recursively compare arrays (susceptible to call stack limits). + if (seen) { + if (!arraySome(other, function(othValue, othIndex) { + if (!seen.has(othIndex) && + (arrValue === othValue || equalFunc(arrValue, othValue, customizer, bitmask, stack))) { + return seen.add(othIndex); + } + })) { + result = false; + break; + } + } else if (!( + arrValue === othValue || + equalFunc(arrValue, othValue, customizer, bitmask, stack) + )) { + result = false; + break; + } + } + stack['delete'](array); + stack['delete'](other); + return result; + } + + /** + * A specialized version of `baseIsEqualDeep` for comparing objects of + * the same `toStringTag`. + * + * **Note:** This function only supports comparing values with tags of + * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`. + * + * @private + * @param {Object} object The object to compare. + * @param {Object} other The other object to compare. + * @param {string} tag The `toStringTag` of the objects to compare. + * @param {Function} equalFunc The function to determine equivalents of values. + * @param {Function} customizer The function to customize comparisons. + * @param {number} bitmask The bitmask of comparison flags. See `baseIsEqual` + * for more details. + * @param {Object} stack Tracks traversed `object` and `other` objects. + * @returns {boolean} Returns `true` if the objects are equivalent, else `false`. + */ + function equalByTag(object, other, tag, equalFunc, customizer, bitmask, stack) { + switch (tag) { + case dataViewTag: + if ((object.byteLength != other.byteLength) || + (object.byteOffset != other.byteOffset)) { + return false; + } + object = object.buffer; + other = other.buffer; + + case arrayBufferTag: + if ((object.byteLength != other.byteLength) || + !equalFunc(new Uint8Array(object), new Uint8Array(other))) { + return false; + } + return true; + + case boolTag: + case dateTag: + case numberTag: + // Coerce booleans to `1` or `0` and dates to milliseconds. + // Invalid dates are coerced to `NaN`. + return eq(+object, +other); + + case errorTag: + return object.name == other.name && object.message == other.message; + + case regexpTag: + case stringTag: + // Coerce regexes to strings and treat strings, primitives and objects, + // as equal. See http://www.ecma-international.org/ecma-262/7.0/#sec-regexp.prototype.tostring + // for more details. + return object == (other + ''); + + case mapTag: + var convert = mapToArray; + + case setTag: + var isPartial = bitmask & PARTIAL_COMPARE_FLAG; + convert || (convert = setToArray); + + if (object.size != other.size && !isPartial) { + return false; + } + // Assume cyclic values are equal. + var stacked = stack.get(object); + if (stacked) { + return stacked == other; + } + bitmask |= UNORDERED_COMPARE_FLAG; + + // Recursively compare objects (susceptible to call stack limits). + stack.set(object, other); + var result = equalArrays(convert(object), convert(other), equalFunc, customizer, bitmask, stack); + stack['delete'](object); + return result; + + case symbolTag: + if (symbolValueOf) { + return symbolValueOf.call(object) == symbolValueOf.call(other); + } + } + return false; + } + + /** + * A specialized version of `baseIsEqualDeep` for objects with support for + * partial deep comparisons. + * + * @private + * @param {Object} object The object to compare. + * @param {Object} other The other object to compare. + * @param {Function} equalFunc The function to determine equivalents of values. + * @param {Function} customizer The function to customize comparisons. + * @param {number} bitmask The bitmask of comparison flags. See `baseIsEqual` + * for more details. + * @param {Object} stack Tracks traversed `object` and `other` objects. + * @returns {boolean} Returns `true` if the objects are equivalent, else `false`. + */ + function equalObjects(object, other, equalFunc, customizer, bitmask, stack) { + var isPartial = bitmask & PARTIAL_COMPARE_FLAG, + objProps = keys(object), + objLength = objProps.length, + othProps = keys(other), + othLength = othProps.length; + + if (objLength != othLength && !isPartial) { + return false; + } + var index = objLength; + while (index--) { + var key = objProps[index]; + if (!(isPartial ? key in other : hasOwnProperty.call(other, key))) { + return false; + } + } + // Assume cyclic values are equal. + var stacked = stack.get(object); + if (stacked && stack.get(other)) { + return stacked == other; + } + var result = true; + stack.set(object, other); + stack.set(other, object); + + var skipCtor = isPartial; + while (++index < objLength) { + key = objProps[index]; + var objValue = object[key], + othValue = other[key]; + + if (customizer) { + var compared = isPartial + ? customizer(othValue, objValue, key, other, object, stack) + : customizer(objValue, othValue, key, object, other, stack); + } + // Recursively compare objects (susceptible to call stack limits). + if (!(compared === undefined + ? (objValue === othValue || equalFunc(objValue, othValue, customizer, bitmask, stack)) + : compared + )) { + result = false; + break; + } + skipCtor || (skipCtor = key == 'constructor'); + } + if (result && !skipCtor) { + var objCtor = object.constructor, + othCtor = other.constructor; + + // Non `Object` object instances with different constructors are not equal. + if (objCtor != othCtor && + ('constructor' in object && 'constructor' in other) && + !(typeof objCtor == 'function' && objCtor instanceof objCtor && + typeof othCtor == 'function' && othCtor instanceof othCtor)) { + result = false; + } + } + stack['delete'](object); + stack['delete'](other); + return result; + } + + /** + * Gets the data for `map`. + * + * @private + * @param {Object} map The map to query. + * @param {string} key The reference key. + * @returns {*} Returns the map data. + */ + function getMapData(map, key) { + var data = map.__data__; + return isKeyable(key) + ? data[typeof key == 'string' ? 'string' : 'hash'] + : data.map; + } + + /** + * Gets the property names, values, and compare flags of `object`. + * + * @private + * @param {Object} object The object to query. + * @returns {Array} Returns the match data of `object`. + */ + function getMatchData(object) { + var result = keys(object), + length = result.length; + + while (length--) { + var key = result[length], + value = object[key]; + + result[length] = [key, value, isStrictComparable(value)]; + } + return result; + } + + /** + * Gets the native function at `key` of `object`. + * + * @private + * @param {Object} object The object to query. + * @param {string} key The key of the method to get. + * @returns {*} Returns the function if it's native, else `undefined`. + */ + function getNative(object, key) { + var value = getValue(object, key); + return baseIsNative(value) ? value : undefined; + } + + /** + * Gets the `toStringTag` of `value`. + * + * @private + * @param {*} value The value to query. + * @returns {string} Returns the `toStringTag`. + */ + var getTag = baseGetTag; + + // Fallback for data views, maps, sets, and weak maps in IE 11, + // for data views in Edge < 14, and promises in Node.js. + if ((DataView && getTag(new DataView(new ArrayBuffer(1))) != dataViewTag) || + (Map && getTag(new Map) != mapTag) || + (Promise && getTag(Promise.resolve()) != promiseTag) || + (Set && getTag(new Set) != setTag) || + (WeakMap && getTag(new WeakMap) != weakMapTag)) { + getTag = function(value) { + var result = objectToString.call(value), + Ctor = result == objectTag ? value.constructor : undefined, + ctorString = Ctor ? toSource(Ctor) : undefined; + + if (ctorString) { + switch (ctorString) { + case dataViewCtorString: return dataViewTag; + case mapCtorString: return mapTag; + case promiseCtorString: return promiseTag; + case setCtorString: return setTag; + case weakMapCtorString: return weakMapTag; + } + } + return result; + }; + } + + /** + * Checks if `path` exists on `object`. + * + * @private + * @param {Object} object The object to query. + * @param {Array|string} path The path to check. + * @param {Function} hasFunc The function to check properties. + * @returns {boolean} Returns `true` if `path` exists, else `false`. + */ + function hasPath(object, path, hasFunc) { + path = isKey(path, object) ? [path] : castPath(path); + + var result, + index = -1, + length = path.length; + + while (++index < length) { + var key = toKey(path[index]); + if (!(result = object != null && hasFunc(object, key))) { + break; + } + object = object[key]; + } + if (result) { + return result; + } + var length = object ? object.length : 0; + return !!length && isLength(length) && isIndex(key, length) && + (isArray(object) || isArguments(object)); + } + + /** + * Checks if `value` is a valid array-like index. + * + * @private + * @param {*} value The value to check. + * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index. + * @returns {boolean} Returns `true` if `value` is a valid index, else `false`. + */ + function isIndex(value, length) { + length = length == null ? MAX_SAFE_INTEGER : length; + return !!length && + (typeof value == 'number' || reIsUint.test(value)) && + (value > -1 && value % 1 == 0 && value < length); + } + + /** + * Checks if `value` is a property name and not a property path. + * + * @private + * @param {*} value The value to check. + * @param {Object} [object] The object to query keys on. + * @returns {boolean} Returns `true` if `value` is a property name, else `false`. + */ + function isKey(value, object) { + if (isArray(value)) { + return false; + } + var type = typeof value; + if (type == 'number' || type == 'symbol' || type == 'boolean' || + value == null || isSymbol(value)) { + return true; + } + return reIsPlainProp.test(value) || !reIsDeepProp.test(value) || + (object != null && value in Object(object)); + } + + /** + * Checks if `value` is suitable for use as unique object key. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is suitable, else `false`. + */ + function isKeyable(value) { + var type = typeof value; + return (type == 'string' || type == 'number' || type == 'symbol' || type == 'boolean') + ? (value !== '__proto__') + : (value === null); + } + + /** + * Checks if `func` has its source masked. + * + * @private + * @param {Function} func The function to check. + * @returns {boolean} Returns `true` if `func` is masked, else `false`. + */ + function isMasked(func) { + return !!maskSrcKey && (maskSrcKey in func); + } + + /** + * Checks if `value` is likely a prototype object. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a prototype, else `false`. + */ + function isPrototype(value) { + var Ctor = value && value.constructor, + proto = (typeof Ctor == 'function' && Ctor.prototype) || objectProto; + + return value === proto; + } + + /** + * Checks if `value` is suitable for strict equality comparisons, i.e. `===`. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` if suitable for strict + * equality comparisons, else `false`. + */ + function isStrictComparable(value) { + return value === value && !isObject(value); + } + + /** + * A specialized version of `matchesProperty` for source values suitable + * for strict equality comparisons, i.e. `===`. + * + * @private + * @param {string} key The key of the property to get. + * @param {*} srcValue The value to match. + * @returns {Function} Returns the new spec function. + */ + function matchesStrictComparable(key, srcValue) { + return function(object) { + if (object == null) { + return false; + } + return object[key] === srcValue && + (srcValue !== undefined || (key in Object(object))); + }; + } + + /** + * Converts `string` to a property path array. + * + * @private + * @param {string} string The string to convert. + * @returns {Array} Returns the property path array. + */ + var stringToPath = memoize(function(string) { + string = toString(string); + + var result = []; + if (reLeadingDot.test(string)) { + result.push(''); + } + string.replace(rePropName, function(match, number, quote, string) { + result.push(quote ? string.replace(reEscapeChar, '$1') : (number || match)); + }); + return result; + }); + + /** + * Converts `value` to a string key if it's not a string or symbol. + * + * @private + * @param {*} value The value to inspect. + * @returns {string|symbol} Returns the key. + */ + function toKey(value) { + if (typeof value == 'string' || isSymbol(value)) { + return value; + } + var result = (value + ''); + return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result; + } + + /** + * Converts `func` to its source code. + * + * @private + * @param {Function} func The function to process. + * @returns {string} Returns the source code. + */ + function toSource(func) { + if (func != null) { + try { + return funcToString.call(func); + } catch (e) {} + try { + return (func + ''); + } catch (e) {} + } + return ''; + } + + /** + * This method is like `_.uniq` except that it accepts `iteratee` which is + * invoked for each element in `array` to generate the criterion by which + * uniqueness is computed. The iteratee is invoked with one argument: (value). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {Array} array The array to inspect. + * @param {Function} [iteratee=_.identity] + * The iteratee invoked per element. + * @returns {Array} Returns the new duplicate free array. + * @example + * + * _.uniqBy([2.1, 1.2, 2.3], Math.floor); + * // => [2.1, 1.2] + * + * // The `_.property` iteratee shorthand. + * _.uniqBy([{ 'x': 1 }, { 'x': 2 }, { 'x': 1 }], 'x'); + * // => [{ 'x': 1 }, { 'x': 2 }] + */ + function uniqBy(array, iteratee) { + return (array && array.length) + ? baseUniq(array, baseIteratee(iteratee, 2)) + : []; + } + + /** + * Creates a function that memoizes the result of `func`. If `resolver` is + * provided, it determines the cache key for storing the result based on the + * arguments provided to the memoized function. By default, the first argument + * provided to the memoized function is used as the map cache key. The `func` + * is invoked with the `this` binding of the memoized function. + * + * **Note:** The cache is exposed as the `cache` property on the memoized + * function. Its creation may be customized by replacing the `_.memoize.Cache` + * constructor with one whose instances implement the + * [`Map`](http://ecma-international.org/ecma-262/7.0/#sec-properties-of-the-map-prototype-object) + * method interface of `delete`, `get`, `has`, and `set`. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Function + * @param {Function} func The function to have its output memoized. + * @param {Function} [resolver] The function to resolve the cache key. + * @returns {Function} Returns the new memoized function. + * @example + * + * var object = { 'a': 1, 'b': 2 }; + * var other = { 'c': 3, 'd': 4 }; + * + * var values = _.memoize(_.values); + * values(object); + * // => [1, 2] + * + * values(other); + * // => [3, 4] + * + * object.a = 2; + * values(object); + * // => [1, 2] + * + * // Modify the result cache. + * values.cache.set(object, ['a', 'b']); + * values(object); + * // => ['a', 'b'] + * + * // Replace `_.memoize.Cache`. + * _.memoize.Cache = WeakMap; + */ + function memoize(func, resolver) { + if (typeof func != 'function' || (resolver && typeof resolver != 'function')) { + throw new TypeError(FUNC_ERROR_TEXT); + } + var memoized = function() { + var args = arguments, + key = resolver ? resolver.apply(this, args) : args[0], + cache = memoized.cache; + + if (cache.has(key)) { + return cache.get(key); + } + var result = func.apply(this, args); + memoized.cache = cache.set(key, result); + return result; + }; + memoized.cache = new (memoize.Cache || MapCache); + return memoized; + } + + // Assign cache to `_.memoize`. + memoize.Cache = MapCache; + + /** + * Performs a + * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) + * comparison between two values to determine if they are equivalent. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {boolean} Returns `true` if the values are equivalent, else `false`. + * @example + * + * var object = { 'a': 1 }; + * var other = { 'a': 1 }; + * + * _.eq(object, object); + * // => true + * + * _.eq(object, other); + * // => false + * + * _.eq('a', 'a'); + * // => true + * + * _.eq('a', Object('a')); + * // => false + * + * _.eq(NaN, NaN); + * // => true + */ + function eq(value, other) { + return value === other || (value !== value && other !== other); + } + + /** + * Checks if `value` is likely an `arguments` object. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an `arguments` object, + * else `false`. + * @example + * + * _.isArguments(function() { return arguments; }()); + * // => true + * + * _.isArguments([1, 2, 3]); + * // => false + */ + function isArguments(value) { + // Safari 8.1 makes `arguments.callee` enumerable in strict mode. + return isArrayLikeObject(value) && hasOwnProperty.call(value, 'callee') && + (!propertyIsEnumerable.call(value, 'callee') || objectToString.call(value) == argsTag); + } + + /** + * Checks if `value` is classified as an `Array` object. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an array, else `false`. + * @example + * + * _.isArray([1, 2, 3]); + * // => true + * + * _.isArray(document.body.children); + * // => false + * + * _.isArray('abc'); + * // => false + * + * _.isArray(_.noop); + * // => false + */ + var isArray = Array.isArray; + + /** + * Checks if `value` is array-like. A value is considered array-like if it's + * not a function and has a `value.length` that's an integer greater than or + * equal to `0` and less than or equal to `Number.MAX_SAFE_INTEGER`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is array-like, else `false`. + * @example + * + * _.isArrayLike([1, 2, 3]); + * // => true + * + * _.isArrayLike(document.body.children); + * // => true + * + * _.isArrayLike('abc'); + * // => true + * + * _.isArrayLike(_.noop); + * // => false + */ + function isArrayLike(value) { + return value != null && isLength(value.length) && !isFunction(value); + } + + /** + * This method is like `_.isArrayLike` except that it also checks if `value` + * is an object. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an array-like object, + * else `false`. + * @example + * + * _.isArrayLikeObject([1, 2, 3]); + * // => true + * + * _.isArrayLikeObject(document.body.children); + * // => true + * + * _.isArrayLikeObject('abc'); + * // => false + * + * _.isArrayLikeObject(_.noop); + * // => false + */ + function isArrayLikeObject(value) { + return isObjectLike(value) && isArrayLike(value); + } + + /** + * Checks if `value` is classified as a `Function` object. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a function, else `false`. + * @example + * + * _.isFunction(_); + * // => true + * + * _.isFunction(/abc/); + * // => false + */ + function isFunction(value) { + // The use of `Object#toString` avoids issues with the `typeof` operator + // in Safari 8-9 which returns 'object' for typed array and other constructors. + var tag = isObject(value) ? objectToString.call(value) : ''; + return tag == funcTag || tag == genTag; + } + + /** + * Checks if `value` is a valid array-like length. + * + * **Note:** This method is loosely based on + * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a valid length, else `false`. + * @example + * + * _.isLength(3); + * // => true + * + * _.isLength(Number.MIN_VALUE); + * // => false + * + * _.isLength(Infinity); + * // => false + * + * _.isLength('3'); + * // => false + */ + function isLength(value) { + return typeof value == 'number' && + value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER; + } + + /** + * Checks if `value` is the + * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types) + * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an object, else `false`. + * @example + * + * _.isObject({}); + * // => true + * + * _.isObject([1, 2, 3]); + * // => true + * + * _.isObject(_.noop); + * // => true + * + * _.isObject(null); + * // => false + */ + function isObject(value) { + var type = typeof value; + return !!value && (type == 'object' || type == 'function'); + } + + /** + * Checks if `value` is object-like. A value is object-like if it's not `null` + * and has a `typeof` result of "object". + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is object-like, else `false`. + * @example + * + * _.isObjectLike({}); + * // => true + * + * _.isObjectLike([1, 2, 3]); + * // => true + * + * _.isObjectLike(_.noop); + * // => false + * + * _.isObjectLike(null); + * // => false + */ + function isObjectLike(value) { + return !!value && typeof value == 'object'; + } + + /** + * Checks if `value` is classified as a `Symbol` primitive or object. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a symbol, else `false`. + * @example + * + * _.isSymbol(Symbol.iterator); + * // => true + * + * _.isSymbol('abc'); + * // => false + */ + function isSymbol(value) { + return typeof value == 'symbol' || + (isObjectLike(value) && objectToString.call(value) == symbolTag); + } + + /** + * Checks if `value` is classified as a typed array. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a typed array, else `false`. + * @example + * + * _.isTypedArray(new Uint8Array); + * // => true + * + * _.isTypedArray([]); + * // => false + */ + var isTypedArray = nodeIsTypedArray ? baseUnary(nodeIsTypedArray) : baseIsTypedArray; + + /** + * Converts `value` to a string. An empty string is returned for `null` + * and `undefined` values. The sign of `-0` is preserved. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to process. + * @returns {string} Returns the string. + * @example + * + * _.toString(null); + * // => '' + * + * _.toString(-0); + * // => '-0' + * + * _.toString([1, 2, 3]); + * // => '1,2,3' + */ + function toString(value) { + return value == null ? '' : baseToString(value); + } + + /** + * Gets the value at `path` of `object`. If the resolved value is + * `undefined`, the `defaultValue` is returned in its place. + * + * @static + * @memberOf _ + * @since 3.7.0 + * @category Object + * @param {Object} object The object to query. + * @param {Array|string} path The path of the property to get. + * @param {*} [defaultValue] The value returned for `undefined` resolved values. + * @returns {*} Returns the resolved value. + * @example + * + * var object = { 'a': [{ 'b': { 'c': 3 } }] }; + * + * _.get(object, 'a[0].b.c'); + * // => 3 + * + * _.get(object, ['a', '0', 'b', 'c']); + * // => 3 + * + * _.get(object, 'a.b.c', 'default'); + * // => 'default' + */ + function get(object, path, defaultValue) { + var result = object == null ? undefined : baseGet(object, path); + return result === undefined ? defaultValue : result; + } + + /** + * Checks if `path` is a direct or inherited property of `object`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Object + * @param {Object} object The object to query. + * @param {Array|string} path The path to check. + * @returns {boolean} Returns `true` if `path` exists, else `false`. + * @example + * + * var object = _.create({ 'a': _.create({ 'b': 2 }) }); + * + * _.hasIn(object, 'a'); + * // => true + * + * _.hasIn(object, 'a.b'); + * // => true + * + * _.hasIn(object, ['a', 'b']); + * // => true + * + * _.hasIn(object, 'b'); + * // => false + */ + function hasIn(object, path) { + return object != null && hasPath(object, path, baseHasIn); + } + + /** + * Creates an array of the own enumerable property names of `object`. + * + * **Note:** Non-object values are coerced to objects. See the + * [ES spec](http://ecma-international.org/ecma-262/7.0/#sec-object.keys) + * for more details. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Object + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names. + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.keys(new Foo); + * // => ['a', 'b'] (iteration order is not guaranteed) + * + * _.keys('hi'); + * // => ['0', '1'] + */ + function keys(object) { + return isArrayLike(object) ? arrayLikeKeys(object) : baseKeys(object); + } + + /** + * This method returns the first argument it receives. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Util + * @param {*} value Any value. + * @returns {*} Returns `value`. + * @example + * + * var object = { 'a': 1 }; + * + * console.log(_.identity(object) === object); + * // => true + */ + function identity(value) { + return value; + } + + /** + * This method returns `undefined`. + * + * @static + * @memberOf _ + * @since 2.3.0 + * @category Util + * @example + * + * _.times(2, _.noop); + * // => [undefined, undefined] + */ + function noop() { + // No operation performed. + } + + /** + * Creates a function that returns the value at `path` of a given object. + * + * @static + * @memberOf _ + * @since 2.4.0 + * @category Util + * @param {Array|string} path The path of the property to get. + * @returns {Function} Returns the new accessor function. + * @example + * + * var objects = [ + * { 'a': { 'b': 2 } }, + * { 'a': { 'b': 1 } } + * ]; + * + * _.map(objects, _.property('a.b')); + * // => [2, 1] + * + * _.map(_.sortBy(objects, _.property(['a', 'b'])), 'a.b'); + * // => [1, 2] + */ + function property(path) { + return isKey(path) ? baseProperty(toKey(path)) : basePropertyDeep(path); + } + + module.exports = uniqBy; + + /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }()), __webpack_require__(33)(module))) + +/***/ }, +/* 51 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + module.exports = __webpack_require__(52); + +/***/ }, +/* 52 */ +/***/ function(module, exports, __webpack_require__) { + + /* WEBPACK VAR INJECTION */(function(process) {'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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 _utils = __webpack_require__(2); + + var _playback = __webpack_require__(31); + + var _playback2 = _interopRequireDefault(_playback); + + var _styler = __webpack_require__(15); + + var _styler2 = _interopRequireDefault(_styler); + + var _browser = __webpack_require__(3); + + var _browser2 = _interopRequireDefault(_browser); + + var _events = __webpack_require__(5); + + var _events2 = _interopRequireDefault(_events); + + var _style = __webpack_require__(53); + + var _style2 = _interopRequireDefault(_style); + + var _clapprZepto = __webpack_require__(16); + + var _clapprZepto2 = _interopRequireDefault(_clapprZepto); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + 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; } + + 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); } } // Copyright 2014 Globo.com Player authors. All rights reserved. + // Use of this source code is governed by a BSD-style + // license that can be found in the LICENSE file. + + var MIMETYPES = { + 'mp4': ['avc1.42E01E', 'avc1.58A01E', 'avc1.4D401E', 'avc1.64001E', 'mp4v.20.8', 'mp4v.20.240', 'mp4a.40.2'].map(function (codec) { + return 'video/mp4; codecs="' + codec + ', mp4a.40.2"'; + }), + 'ogg': ['video/ogg; codecs="theora, vorbis"', 'video/ogg; codecs="dirac"', 'video/ogg; codecs="theora, speex"'], + '3gpp': ['video/3gpp; codecs="mp4v.20.8, samr"'], + 'webm': ['video/webm; codecs="vp8, vorbis"'], + 'mkv': ['video/x-matroska; codecs="theora, vorbis"'], + 'm3u8': ['application/x-mpegurl'] + }; + MIMETYPES['ogv'] = MIMETYPES['ogg']; + MIMETYPES['3gp'] = MIMETYPES['3gpp']; + + var AUDIO_MIMETYPES = { + 'wav': ['audio/wav'], + 'mp3': ['audio/mp3', 'audio/mpeg;codecs="mp3"'], + 'aac': ['audio/mp4;codecs="mp4a.40.5"'], + 'oga': ['audio/ogg'] + }; + + var KNOWN_AUDIO_MIMETYPES = Object.keys(AUDIO_MIMETYPES).reduce(function (acc, k) { + return [].concat(_toConsumableArray(acc), _toConsumableArray(AUDIO_MIMETYPES[k])); + }, []); + + // TODO: rename this Playback to HTML5Playback (breaking change, only after 0.3.0) + + var HTML5Video = function (_Playback) { + _inherits(HTML5Video, _Playback); + + _createClass(HTML5Video, [{ + key: 'name', + get: function get() { + return 'html5_video'; + } + }, { + key: 'tagName', + get: function get() { + return this.isAudioOnly ? 'audio' : 'video'; + } + }, { + key: 'isAudioOnly', + get: function get() { + var resourceUrl = this.options.src; + var mimeTypes = HTML5Video._mimeTypesForUrl(resourceUrl, AUDIO_MIMETYPES, this.options.mimeType); + return this.options.playback && this.options.playback.audioOnly || this.options.audioOnly || KNOWN_AUDIO_MIMETYPES.indexOf(mimeTypes[0]) >= 0; + } + }, { + key: 'attributes', + get: function get() { + return { + 'data-html5-video': '' + }; + } + }, { + key: 'events', + get: function get() { + return { + 'canplay': '_onCanPlay', + 'canplaythrough': '_handleBufferingEvents', + 'durationchange': '_onDurationChange', + 'ended': '_onEnded', + 'error': '_onError', + 'loadeddata': '_onLoadedData', + 'loadedmetadata': '_onLoadedMetadata', + 'pause': '_onPause', + 'playing': '_onPlaying', + 'progress': '_onProgress', + 'seeked': '_handleBufferingEvents', + 'seeking': '_handleBufferingEvents', + 'stalled': '_handleBufferingEvents', + 'timeupdate': '_onTimeUpdate', + 'waiting': '_onWaiting' + }; + } + + /** + * Determine if the playback has ended. + * @property ended + * @type Boolean + */ + + }, { + key: 'ended', + get: function get() { + return this.el.ended; + } + + /** + * Determine if the playback is having to buffer in order for + * playback to be smooth. + * This is related to the PLAYBACK_BUFFERING and PLAYBACK_BUFFERFULL events + * @property buffering + * @type Boolean + */ + + }, { + key: 'buffering', + get: function get() { + return !!this._bufferingState; + } + }]); + + function HTML5Video() { + _classCallCheck(this, HTML5Video); + + for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } + + var _this = _possibleConstructorReturn(this, _Playback.call.apply(_Playback, [this].concat(args))); + + _this._loadStarted = false; + _this._playheadMoving = false; + _this._playheadMovingTimer = null; + _this._stopped = false; + _this._setupSrc(_this.options.src); + // backwards compatibility (TODO: remove on 0.3.0) + _this.options.playback || (_this.options.playback = _this.options || {}); + _this.options.playback.disableContextMenu = _this.options.playback.disableContextMenu || _this.options.disableVideoTagContextMenu; + + var playbackConfig = _this.options.playback; + var preload = playbackConfig.preload || (_browser2.default.isSafari ? 'auto' : _this.options.preload); + + _clapprZepto2.default.extend(_this.el, { + loop: _this.options.loop, + poster: _this.options.poster, + preload: preload || 'metadata', + controls: (playbackConfig.controls || _this.options.useVideoTagDefaultControls) && 'controls', + crossOrigin: playbackConfig.crossOrigin, + 'x-webkit-playsinline': playbackConfig.playInline + }); + + // TODO should settings be private? + _this.settings = { default: ['seekbar'] }; + _this.settings.left = ['playpause', 'position', 'duration']; + _this.settings.right = ['fullscreen', 'volume', 'hd-indicator']; + + // https://github.com/clappr/clappr/issues/1076 + _this.options.autoPlay && process.nextTick(function () { + return _this.play(); + }); + return _this; + } + + /** + * Sets the source url on the <video> element, and also the 'src' property. + * @method setupSrc + * @private + * @param {String} srcUrl The source URL. + */ + + + HTML5Video.prototype._setupSrc = function _setupSrc(srcUrl) { + if (this.el.src === srcUrl) { + return; + } + this._src = srcUrl; + this.el.src = srcUrl; + }; + + HTML5Video.prototype._onLoadedMetadata = function _onLoadedMetadata(e) { + this._handleBufferingEvents(); + this.trigger(_events2.default.PLAYBACK_LOADEDMETADATA, { duration: e.target.duration, data: e }); + this._updateSettings(); + var autoSeekFromUrl = typeof this._options.autoSeekFromUrl === 'undefined' || this._options.autoSeekFromUrl; + if (this.getPlaybackType() !== _playback2.default.LIVE && autoSeekFromUrl) { + this._checkInitialSeek(); + } + }; + + HTML5Video.prototype._onDurationChange = function _onDurationChange() { + this._updateSettings(); + this._onTimeUpdate(); + // onProgress uses the duration + this._onProgress(); + }; + + HTML5Video.prototype._updateSettings = function _updateSettings() { + // we can't figure out if hls resource is VoD or not until it is being loaded or duration has changed. + // that's why we check it again and update media control accordingly. + if (this.getPlaybackType() === _playback2.default.VOD || this.getPlaybackType() === _playback2.default.AOD) { + this.settings.left = ['playpause', 'position', 'duration']; + } else { + this.settings.left = ['playstop']; + } + this.settings.seekEnabled = this.isSeekEnabled(); + this.trigger(_events2.default.PLAYBACK_SETTINGSUPDATE); + }; + + HTML5Video.prototype.isSeekEnabled = function isSeekEnabled() { + return isFinite(this.getDuration()); + }; + + HTML5Video.prototype.getPlaybackType = function getPlaybackType() { + var onDemandType = this.tagName === 'audio' ? _playback2.default.AOD : _playback2.default.VOD; + return [0, undefined, Infinity].indexOf(this.el.duration) >= 0 ? _playback2.default.LIVE : onDemandType; + }; + + HTML5Video.prototype.isHighDefinitionInUse = function isHighDefinitionInUse() { + return false; + }; + + HTML5Video.prototype.play = function play() { + this.trigger(_events2.default.PLAYBACK_PLAY_INTENT); + this._stopped = false; + this._setupSrc(this._src); + this._handleBufferingEvents(); + this.el.play(); + }; + + HTML5Video.prototype.pause = function pause() { + this.el.pause(); + }; + + HTML5Video.prototype.stop = function stop() { + this.pause(); + this._stopped = true; + // src will be added again in play() + this.el.removeAttribute('src'); + this._stopPlayheadMovingChecks(); + this._handleBufferingEvents(); + this.trigger(_events2.default.PLAYBACK_STOP); + }; + + HTML5Video.prototype.volume = function volume(value) { + this.el.volume = value / 100; + }; + + HTML5Video.prototype.mute = function mute() { + this.el.volume = 0; + }; + + HTML5Video.prototype.unmute = function unmute() { + this.el.volume = 1; + }; + + HTML5Video.prototype.isMuted = function isMuted() { + return !!this.el.volume; + }; + + HTML5Video.prototype.isPlaying = function isPlaying() { + return !this.el.paused && !this.el.ended; + }; + + HTML5Video.prototype._startPlayheadMovingChecks = function _startPlayheadMovingChecks() { + if (this._playheadMovingTimer !== null) { + return; + } + this._playheadMovingTimeOnCheck = null; + this._determineIfPlayheadMoving(); + this._playheadMovingTimer = setInterval(this._determineIfPlayheadMoving.bind(this), 500); + }; + + HTML5Video.prototype._stopPlayheadMovingChecks = function _stopPlayheadMovingChecks() { + if (this._playheadMovingTimer === null) { + return; + } + clearInterval(this._playheadMovingTimer); + this._playheadMovingTimer = null; + this._playheadMoving = false; + }; + + HTML5Video.prototype._determineIfPlayheadMoving = function _determineIfPlayheadMoving() { + var before = this._playheadMovingTimeOnCheck; + var now = this.el.currentTime; + this._playheadMoving = before !== now; + this._playheadMovingTimeOnCheck = now; + this._handleBufferingEvents(); + }; + + // this seems to happen when the user is having to wait + // for something to happen AFTER A USER INTERACTION + // e.g the player might be buffering, but when `play()` is called + // only at this point will this be called. + // Or the user may seek somewhere but the new area requires buffering, + // so it will fire then as well. + // On devices where playing is blocked until requested with a user action, + // buffering may start, but never finish until the user initiates a play, + // but this only happens when play is actually requested + + + HTML5Video.prototype._onWaiting = function _onWaiting() { + this._loadStarted = true; + this._handleBufferingEvents(); + }; + + // called after the first frame has loaded + // note this doesn't fire on ios before the user has requested play + // ideally the "loadstart" event would be used instead, but this fires + // before a user has requested play on iOS, and also this is always fired + // even if the preload setting is "none". In both these cases this causes + // infinite buffering until the user does something which isn't great. + + + HTML5Video.prototype._onLoadedData = function _onLoadedData() { + this._loadStarted = true; + this._handleBufferingEvents(); + }; + + // note this doesn't fire on ios before user has requested play + + + HTML5Video.prototype._onCanPlay = function _onCanPlay() { + this._handleBufferingEvents(); + }; + + HTML5Video.prototype._onPlaying = function _onPlaying() { + this._startPlayheadMovingChecks(); + this._handleBufferingEvents(); + this.trigger(_events2.default.PLAYBACK_PLAY); + }; + + HTML5Video.prototype._onPause = function _onPause() { + this._stopPlayheadMovingChecks(); + this._handleBufferingEvents(); + this.trigger(_events2.default.PLAYBACK_PAUSE); + }; + + HTML5Video.prototype._onEnded = function _onEnded() { + this._handleBufferingEvents(); + this.trigger(_events2.default.PLAYBACK_ENDED, this.name); + }; + + // The playback should be classed as buffering if the following are true: + // - the ready state is less then HAVE_FUTURE_DATA or the playhead isn't moving and it should be + // - the media hasn't "ended", + // - the media hasn't been stopped + // - loading has started + + + HTML5Video.prototype._handleBufferingEvents = function _handleBufferingEvents() { + var playheadShouldBeMoving = !this.el.ended && !this.el.paused; + var buffering = this._loadStarted && !this.el.ended && !this._stopped && (playheadShouldBeMoving && !this._playheadMoving || this.el.readyState < this.el.HAVE_FUTURE_DATA); + if (this._bufferingState !== buffering) { + this._bufferingState = buffering; + if (buffering) { + this.trigger(_events2.default.PLAYBACK_BUFFERING, this.name); + } else { + this.trigger(_events2.default.PLAYBACK_BUFFERFULL, this.name); + } + } + }; + + HTML5Video.prototype._onError = function _onError() { + this.trigger(_events2.default.PLAYBACK_ERROR, this.el.error, this.name); + }; + + HTML5Video.prototype.destroy = function destroy() { + this.$el.remove(); + this.el.src = ''; + this._src = null; + }; + + HTML5Video.prototype.seek = function seek(time) { + this.el.currentTime = time; + }; + + HTML5Video.prototype.seekPercentage = function seekPercentage(percentage) { + var time = this.el.duration * (percentage / 100); + this.seek(time); + }; + + HTML5Video.prototype._checkInitialSeek = function _checkInitialSeek() { + var seekTime = (0, _utils.seekStringToSeconds)(window.location.href); + if (seekTime !== 0) { + this.seek(seekTime); + } + }; + + HTML5Video.prototype.getCurrentTime = function getCurrentTime() { + return this.el.currentTime; + }; + + HTML5Video.prototype.getDuration = function getDuration() { + return this.el.duration; + }; + + HTML5Video.prototype._onTimeUpdate = function _onTimeUpdate() { + this._handleBufferingEvents(); + if (this.getPlaybackType() === _playback2.default.LIVE) { + this.trigger(_events2.default.PLAYBACK_TIMEUPDATE, { current: 1, total: 1 }, this.name); + } else { + this.trigger(_events2.default.PLAYBACK_TIMEUPDATE, { current: this.el.currentTime, total: this.el.duration }, this.name); + } + }; + + HTML5Video.prototype._onProgress = function _onProgress() { + if (!this.el.buffered.length) { + return; + } + var buffered = []; + var bufferedPos = 0; + for (var i = 0; i < this.el.buffered.length; i++) { + buffered = [].concat(_toConsumableArray(buffered), [{ start: this.el.buffered.start(i), end: this.el.buffered.end(i) }]); + if (this.el.currentTime >= buffered[i].start && this.el.currentTime <= buffered[i].end) { + bufferedPos = i; + } + } + var progress = { + start: buffered[bufferedPos].start, + current: buffered[bufferedPos].end, + total: this.el.duration + }; + this.trigger(_events2.default.PLAYBACK_PROGRESS, progress, buffered); + }; + + HTML5Video.prototype._typeFor = function _typeFor(src) { + var mimeTypes = HTML5Video._mimeTypesForUrl(src, MIMETYPES, this.options.mimeType); + if (mimeTypes.length == 0) { + mimeTypes = HTML5Video._mimeTypesForUrl(src, AUDIO_MIMETYPES, this.options.mimeType); + } + var mimeType = mimeTypes[0] || ''; + return mimeType.split(';')[0]; + }; + + HTML5Video.prototype._ready = function _ready() { + if (this._isReadyState) { + return; + } + this._isReadyState = true; + this.trigger(_events2.default.PLAYBACK_READY, this.name); + }; + + HTML5Video.prototype.render = function render() { + var style = _styler2.default.getStyleFor(_style2.default); + + if (this.options.playback.disableContextMenu) { + this.$el.on('contextmenu', function () { + return false; + }); + } + + this.$el.append(style); + this._ready(); + return this; + }; + + _createClass(HTML5Video, [{ + key: 'isReady', + get: function get() { + return this._isReadyState; + } + }]); + + return HTML5Video; + }(_playback2.default); + + exports.default = HTML5Video; + + + HTML5Video._mimeTypesForUrl = function (resourceUrl, mimeTypesByExtension, mimeType) { + var extension = (resourceUrl.split('?')[0].match(/.*\.(.*)$/) || [])[1]; + var mimeTypes = mimeType || extension && mimeTypesByExtension[extension.toLowerCase()] || []; + return mimeTypes.constructor === Array ? mimeTypes : [mimeTypes]; + }; + + HTML5Video._canPlay = function (type, mimeTypesByExtension, resourceUrl, mimeType) { + var mimeTypes = HTML5Video._mimeTypesForUrl(resourceUrl, mimeTypesByExtension, mimeType); + var media = document.createElement(type); + return !!mimeTypes.filter(function (mediaType) { + return !!media.canPlayType(mediaType).replace(/no/, ''); + })[0]; + }; + + HTML5Video.canPlay = function (resourceUrl, mimeType) { + return HTML5Video._canPlay('audio', AUDIO_MIMETYPES, resourceUrl, mimeType) || HTML5Video._canPlay('video', MIMETYPES, resourceUrl, mimeType); + }; + + module.exports = HTML5Video; + module.exports = exports['default']; + /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(29))) + +/***/ }, +/* 53 */ +/***/ function(module, exports, __webpack_require__) { + + exports = module.exports = __webpack_require__(25)(); + // imports + + + // module + exports.push([module.id, "[data-html5-video] {\n position: absolute;\n height: 100%;\n width: 100%;\n display: block; }\n", ""]); + + // exports + + +/***/ }, +/* 54 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + module.exports = __webpack_require__(55); + +/***/ }, +/* 55 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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 _utils = __webpack_require__(2); + + var _base_flash_playback = __webpack_require__(56); + + var _base_flash_playback2 = _interopRequireDefault(_base_flash_playback); + + var _browser = __webpack_require__(3); + + var _browser2 = _interopRequireDefault(_browser); + + var _mediator = __webpack_require__(30); + + var _mediator2 = _interopRequireDefault(_mediator); + + var _template = __webpack_require__(17); + + var _template2 = _interopRequireDefault(_template); + + var _clapprZepto = __webpack_require__(16); + + var _clapprZepto2 = _interopRequireDefault(_clapprZepto); + + var _events = __webpack_require__(5); + + var _events2 = _interopRequireDefault(_events); + + var _playback = __webpack_require__(31); + + var _playback2 = _interopRequireDefault(_playback); + + var _Player = __webpack_require__(60); + + var _Player2 = _interopRequireDefault(_Player); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + 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; } // Copyright 2014 Globo.com Player authors. All rights reserved. + // Use of this source code is governed by a BSD-style + // license that can be found in the LICENSE file. + + var MAX_ATTEMPTS = 60; + + var Flash = function (_BaseFlashPlayback) { + _inherits(Flash, _BaseFlashPlayback); + + _createClass(Flash, [{ + key: 'name', + get: function get() { + return 'flash'; + } + }, { + key: 'swfPath', + get: function get() { + return (0, _template2.default)(_Player2.default)({ baseUrl: this._baseUrl }); + } + + /** + * Determine if the playback has ended. + * @property ended + * @type Boolean + */ + + }, { + key: 'ended', + get: function get() { + return this._currentState === 'ENDED'; + } + + /** + * Determine if the playback is buffering. + * This is related to the PLAYBACK_BUFFERING and PLAYBACK_BUFFERFULL events + * @property buffering + * @type Boolean + */ + + }, { + key: 'buffering', + get: function get() { + return !!this._bufferingState && this._currentState !== 'ENDED'; + } + }]); + + function Flash() { + _classCallCheck(this, Flash); + + for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } + + var _this = _possibleConstructorReturn(this, _BaseFlashPlayback.call.apply(_BaseFlashPlayback, [this].concat(args))); + + _this._src = _this.options.src; + _this._baseUrl = _this.options.baseUrl; + _this._autoPlay = _this.options.autoPlay; + _this.settings = { default: ['seekbar'] }; + _this.settings.left = ['playpause', 'position', 'duration']; + _this.settings.right = ['fullscreen', 'volume']; + _this.settings.seekEnabled = true; + _this._isReadyState = false; + _this._addListeners(); + return _this; + } + + Flash.prototype._bootstrap = function _bootstrap() { + var _this2 = this; + + if (this.el.playerPlay) { + this.el.width = '100%'; + this.el.height = '100%'; + if (this._currentState === 'PLAYING') { + this._firstPlay(); + } else { + this._currentState = 'IDLE'; + this._autoPlay && this.play(); + } + (0, _clapprZepto2.default)('<div style="position: absolute; top: 0; left: 0; width: 100%; height: 100%" />').insertAfter(this.$el); + if (this.getDuration() > 0) { + this._metadataLoaded(); + } else { + _mediator2.default.once(this.uniqueId + ':timeupdate', this._metadataLoaded, this); + } + } else { + this._attempts = this._attempts || 0; + if (++this._attempts <= MAX_ATTEMPTS) { + setTimeout(function () { + return _this2._bootstrap(); + }, 50); + } else { + this.trigger(_events2.default.PLAYBACK_ERROR, { message: 'Max number of attempts reached' }, this.name); + } + } + }; + + Flash.prototype._metadataLoaded = function _metadataLoaded() { + this._isReadyState = true; + this.trigger(_events2.default.PLAYBACK_READY, this.name); + this.trigger(_events2.default.PLAYBACK_SETTINGSUPDATE, this.name); + }; + + Flash.prototype.getPlaybackType = function getPlaybackType() { + return _playback2.default.VOD; + }; + + Flash.prototype.isHighDefinitionInUse = function isHighDefinitionInUse() { + return false; + }; + + Flash.prototype._updateTime = function _updateTime() { + this.trigger(_events2.default.PLAYBACK_TIMEUPDATE, { current: this.el.getPosition(), total: this.el.getDuration() }, this.name); + }; + + Flash.prototype._addListeners = function _addListeners() { + _mediator2.default.on(this.uniqueId + ':progress', this._progress, this); + _mediator2.default.on(this.uniqueId + ':timeupdate', this._updateTime, this); + _mediator2.default.on(this.uniqueId + ':statechanged', this._checkState, this); + _mediator2.default.on(this.uniqueId + ':flashready', this._bootstrap, this); + }; + + Flash.prototype.stopListening = function stopListening() { + _BaseFlashPlayback.prototype.stopListening.call(this); + _mediator2.default.off(this.uniqueId + ':progress'); + _mediator2.default.off(this.uniqueId + ':timeupdate'); + _mediator2.default.off(this.uniqueId + ':statechanged'); + _mediator2.default.off(this.uniqueId + ':flashready'); + }; + + Flash.prototype._checkState = function _checkState() { + if (this._isIdle || this._currentState === 'PAUSED') { + return; + } else if (this._currentState !== 'PLAYING_BUFFERING' && this.el.getState() === 'PLAYING_BUFFERING') { + this._bufferingState = true; + this.trigger(_events2.default.PLAYBACK_BUFFERING, this.name); + this._currentState = 'PLAYING_BUFFERING'; + } else if (this.el.getState() === 'PLAYING') { + this._bufferingState = false; + this.trigger(_events2.default.PLAYBACK_BUFFERFULL, this.name); + this._currentState = 'PLAYING'; + } else if (this.el.getState() === 'IDLE') { + this._currentState = 'IDLE'; + } else if (this.el.getState() === 'ENDED') { + this.trigger(_events2.default.PLAYBACK_ENDED, this.name); + this.trigger(_events2.default.PLAYBACK_TIMEUPDATE, { current: 0, total: this.el.getDuration() }, this.name); + this._currentState = 'ENDED'; + this._isIdle = true; + } + }; + + Flash.prototype._progress = function _progress() { + if (this._currentState !== 'IDLE' && this._currentState !== 'ENDED') { + this.trigger(_events2.default.PLAYBACK_PROGRESS, { + start: 0, + current: this.el.getBytesLoaded(), + total: this.el.getBytesTotal() + }); + } + }; + + Flash.prototype._firstPlay = function _firstPlay() { + var _this3 = this; + + if (this.el.playerPlay) { + this._isIdle = false; + this.el.playerPlay(this._src); + this.listenToOnce(this, _events2.default.PLAYBACK_BUFFERFULL, function () { + return _this3._checkInitialSeek(); + }); + this._currentState = 'PLAYING'; + } else { + this.listenToOnce(this, _events2.default.PLAYBACK_READY, this._firstPlay); + } + }; + + Flash.prototype._checkInitialSeek = function _checkInitialSeek() { + var seekTime = (0, _utils.seekStringToSeconds)(window.location.href); + if (seekTime !== 0) { + this.seekSeconds(seekTime); + } + }; + + Flash.prototype.play = function play() { + this.trigger(_events2.default.PLAYBACK_PLAY_INTENT); + if (this._currentState === 'PAUSED' || this._currentState === 'PLAYING_BUFFERING') { + this._currentState = 'PLAYING'; + this.el.playerResume(); + this.trigger(_events2.default.PLAYBACK_PLAY, this.name); + } else if (this._currentState !== 'PLAYING') { + this._firstPlay(); + this.trigger(_events2.default.PLAYBACK_PLAY, this.name); + } + }; + + Flash.prototype.volume = function volume(value) { + var _this4 = this; + + if (this.isReady) { + this.el.playerVolume(value); + } else { + this.listenToOnce(this, _events2.default.PLAYBACK_BUFFERFULL, function () { + return _this4.volume(value); + }); + } + }; + + Flash.prototype.pause = function pause() { + this._currentState = 'PAUSED'; + this.el.playerPause(); + this.trigger(_events2.default.PLAYBACK_PAUSE, this.name); + }; + + Flash.prototype.stop = function stop() { + this.el.playerStop(); + this.trigger(_events2.default.PLAYBACK_STOP); + this.trigger(_events2.default.PLAYBACK_TIMEUPDATE, { current: 0, total: 0 }, this.name); + }; + + Flash.prototype.isPlaying = function isPlaying() { + return !!(this.isReady && this._currentState.indexOf('PLAYING') > -1); + }; + + Flash.prototype.getDuration = function getDuration() { + return this.el.getDuration(); + }; + + Flash.prototype.seekPercentage = function seekPercentage(percentage) { + var _this5 = this; + + if (this.el.getDuration() > 0) { + var seekSeconds = this.el.getDuration() * (percentage / 100); + this.seek(seekSeconds); + } else { + this.listenToOnce(this, _events2.default.PLAYBACK_BUFFERFULL, function () { + return _this5.seekPercentage(percentage); + }); + } + }; + + Flash.prototype.seek = function seek(time) { + var _this6 = this; + + if (this.isReady && this.el.playerSeek) { + this.el.playerSeek(time); + this.trigger(_events2.default.PLAYBACK_TIMEUPDATE, { current: time, total: this.el.getDuration() }, this.name); + if (this._currentState === 'PAUSED') { + this.el.playerPause(); + } + } else { + this.listenToOnce(this, _events2.default.PLAYBACK_BUFFERFULL, function () { + return _this6.seek(time); + }); + } + }; + + Flash.prototype.destroy = function destroy() { + clearInterval(this.bootstrapId); + _BaseFlashPlayback.prototype.stopListening.call(this); + this.$el.remove(); + }; + + _createClass(Flash, [{ + key: 'isReady', + get: function get() { + return this._isReadyState; + } + }]); + + return Flash; + }(_base_flash_playback2.default); + + exports.default = Flash; + + + Flash.canPlay = function (resource) { + if (!_browser2.default.hasFlash || !resource || resource.constructor !== String) { + return false; + } else { + var resourceParts = resource.split('?')[0].match(/.*\.(.*)$/) || []; + return resourceParts.length > 1 && !_browser2.default.isMobile && resourceParts[1].toLowerCase().match(/^(mp4|mov|f4v|3gpp|3gp)$/); + } + }; + module.exports = exports['default']; + +/***/ }, +/* 56 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + exports.default = __webpack_require__(57); + module.exports = exports['default']; + +/***/ }, +/* 57 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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 _playback = __webpack_require__(31); + + var _playback2 = _interopRequireDefault(_playback); + + var _styler = __webpack_require__(15); + + var _styler2 = _interopRequireDefault(_styler); + + var _template = __webpack_require__(17); + + var _template2 = _interopRequireDefault(_template); + + var _browser = __webpack_require__(3); + + var _browser2 = _interopRequireDefault(_browser); + + var _flash = __webpack_require__(58); + + var _flash2 = _interopRequireDefault(_flash); + + var _flash3 = __webpack_require__(59); + + var _flash4 = _interopRequireDefault(_flash3); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + 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; } // Copyright 2015 Globo.com Player authors. All rights reserved. + // Use of this source code is governed by a BSD-style + // license that can be found in the LICENSE file. + + var IE_CLASSID = 'clsid:d27cdb6e-ae6d-11cf-96b8-444553540000'; + + var BaseFlashPlayback = function (_Playback) { + _inherits(BaseFlashPlayback, _Playback); + + function BaseFlashPlayback() { + _classCallCheck(this, BaseFlashPlayback); + + return _possibleConstructorReturn(this, _Playback.apply(this, arguments)); + } + + BaseFlashPlayback.prototype.setElement = function setElement(element) { + this.$el = element; + this.el = element[0]; + }; + + BaseFlashPlayback.prototype._setupFirefox = function _setupFirefox() { + var $el = this.$('embed'); + $el.attr('data-flash-playback', this.name); + $el.addClass(this.attributes.class); + this.setElement($el); + }; + + BaseFlashPlayback.prototype.render = function render() { + this.$el.html(this.template({ + cid: this.cid, + swfPath: this.swfPath, + baseUrl: this.baseUrl, + playbackId: this.uniqueId, + wmode: this.wmode, + callbackName: 'window.Clappr.flashlsCallbacks.' + this.cid })); + + if (_browser2.default.isIE) { + this.$('embed').remove(); + + if (_browser2.default.isLegacyIE) { + this.$el.attr('classid', IE_CLASSID); + } + } + + if (_browser2.default.isFirefox) { + this._setupFirefox(); + } + + this.el.id = this.cid; + this.$el.append(_styler2.default.getStyleFor(_flash4.default)); + + return this; + }; + + _createClass(BaseFlashPlayback, [{ + key: 'tagName', + get: function get() { + return 'object'; + } + }, { + key: 'swfPath', + get: function get() { + return ''; + } + }, { + key: 'wmode', + get: function get() { + return 'transparent'; + } + }, { + key: 'template', + get: function get() { + return (0, _template2.default)(_flash2.default); + } + }, { + key: 'attributes', + get: function get() { + var type = 'application/x-shockwave-flash'; + + if (_browser2.default.isLegacyIE) { + type = ''; + } + + return { + class: 'clappr-flash-playback', + type: type, + width: '100%', + height: '100%', + 'data-flash-playback': this.name + }; + } + }]); + + return BaseFlashPlayback; + }(_playback2.default); + + exports.default = BaseFlashPlayback; + module.exports = exports['default']; + +/***/ }, +/* 58 */ +/***/ function(module, exports) { + + module.exports = "<param name=\"movie\" value=\"<%= swfPath %>?inline=1\">\n<param name=\"quality\" value=\"autohigh\">\n<param name=\"swliveconnect\" value=\"true\">\n<param name=\"allowScriptAccess\" value=\"always\">\n<param name=\"bgcolor\" value=\"#000000\">\n<param name=\"allowFullScreen\" value=\"false\">\n<param name=\"wmode\" value=\"<%= wmode %>\">\n<param name=\"tabindex\" value=\"1\">\n<param name=\"FlashVars\" value=\"playbackId=<%= playbackId %>&callback=<%= callbackName %>\">\n<embed\n name=\"<%= cid %>\"\n type=\"application/x-shockwave-flash\"\n disabled=\"disabled\"\n tabindex=\"-1\"\n enablecontextmenu=\"false\"\n allowScriptAccess=\"always\"\n quality=\"autohigh\"\n pluginspage=\"http://www.macromedia.com/go/getflashplayer\"\n wmode=\"<%= wmode %>\"\n swliveconnect=\"true\"\n allowfullscreen=\"false\"\n bgcolor=\"#000000\"\n FlashVars=\"playbackId=<%= playbackId %>&callback=<%= callbackName %>\"\n src=\"<%= swfPath %>\"\n width=\"100%\"\n height=\"100%\">\n</embed>\n"; + +/***/ }, +/* 59 */ +/***/ function(module, exports, __webpack_require__) { + + exports = module.exports = __webpack_require__(25)(); + // imports + + + // module + exports.push([module.id, ".clappr-flash-playback[data-flash-playback] {\n display: block;\n position: absolute;\n top: 0;\n left: 0;\n height: 100%;\n width: 100%;\n pointer-events: none; }\n", ""]); + + // exports + + +/***/ }, +/* 60 */ +/***/ function(module, exports, __webpack_require__) { + + module.exports = __webpack_require__.p + "4b76590b32dab62bc95c1b7951efae78.swf"; + +/***/ }, +/* 61 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + module.exports = __webpack_require__(62); + +/***/ }, +/* 62 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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 _events = __webpack_require__(5); + + var _events2 = _interopRequireDefault(_events); + + var _playback = __webpack_require__(31); + + var _playback2 = _interopRequireDefault(_playback); + + var _html5_video = __webpack_require__(51); + + var _html5_video2 = _interopRequireDefault(_html5_video); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + 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; } // Copyright 2014 Globo.com Player authors. All rights reserved. + // Use of this source code is governed by a BSD-style + // license that can be found in the LICENSE file. + + // TODO: remove this playback and change HTML5Video to HTML5Playback (breaking change, only after 0.3.0) + var HTML5Audio = function (_HTML5Video) { + _inherits(HTML5Audio, _HTML5Video); + + function HTML5Audio() { + _classCallCheck(this, HTML5Audio); + + return _possibleConstructorReturn(this, _HTML5Video.apply(this, arguments)); + } + + HTML5Audio.prototype.updateSettings = function updateSettings() { + this.settings.left = ['playpause', 'position', 'duration']; + this.settings.seekEnabled = this.isSeekEnabled(); + this.trigger(_events2.default.PLAYBACK_SETTINGSUPDATE); + }; + + HTML5Audio.prototype.getPlaybackType = function getPlaybackType() { + return _playback2.default.AOD; + }; + + _createClass(HTML5Audio, [{ + key: 'name', + get: function get() { + return 'html5_audio'; + } + }, { + key: 'tagName', + get: function get() { + return 'audio'; + } + }, { + key: 'isAudioOnly', + get: function get() { + return true; + } + }]); + + return HTML5Audio; + }(_html5_video2.default); + + exports.default = HTML5Audio; + + + HTML5Audio.canPlay = function (resourceUrl, mimeType) { + var mimetypes = { + 'wav': ['audio/wav'], + 'mp3': ['audio/mp3', 'audio/mpeg;codecs="mp3"'], + 'aac': ['audio/mp4;codecs="mp4a.40.5"'], + 'oga': ['audio/ogg'] + }; + return _html5_video2.default._canPlay('audio', mimetypes, resourceUrl, mimeType); + }; + module.exports = exports['default']; + +/***/ }, +/* 63 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + module.exports = __webpack_require__(64); + +/***/ }, +/* 64 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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 _base_flash_playback = __webpack_require__(56); + + var _base_flash_playback2 = _interopRequireDefault(_base_flash_playback); + + var _events = __webpack_require__(5); + + var _events2 = _interopRequireDefault(_events); + + var _template = __webpack_require__(17); + + var _template2 = _interopRequireDefault(_template); + + var _playback = __webpack_require__(31); + + var _playback2 = _interopRequireDefault(_playback); + + var _mediator = __webpack_require__(30); + + var _mediator2 = _interopRequireDefault(_mediator); + + var _browser = __webpack_require__(3); + + var _browser2 = _interopRequireDefault(_browser); + + var _flashls_events = __webpack_require__(65); + + var _flashls_events2 = _interopRequireDefault(_flashls_events); + + var _HLSPlayer = __webpack_require__(66); + + var _HLSPlayer2 = _interopRequireDefault(_HLSPlayer); + + var _clapprZepto = __webpack_require__(16); + + var _clapprZepto2 = _interopRequireDefault(_clapprZepto); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + 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; } // Copyright 2014 Globo.com Player authors. All rights reserved. + // Use of this source code is governed by a BSD-style + // license that can be found in the LICENSE file. + + var MAX_ATTEMPTS = 60; + var AUTO = -1; + + var FlasHLS = function (_BaseFlashPlayback) { + _inherits(FlasHLS, _BaseFlashPlayback); + + _createClass(FlasHLS, [{ + key: 'name', + get: function get() { + return 'flashls'; + } + }, { + key: 'swfPath', + get: function get() { + return (0, _template2.default)(_HLSPlayer2.default)({ baseUrl: this._baseUrl }); + } + }, { + key: 'levels', + get: function get() { + return this._levels || []; + } + }, { + key: 'currentLevel', + get: function get() { + if (this._currentLevel === null || this._currentLevel === undefined) { + return AUTO; + } else { + return this._currentLevel; //0 is a valid level ID + } + }, + set: function set(id) { + this._currentLevel = id; + this.trigger(_events2.default.PLAYBACK_LEVEL_SWITCH_START); + this.el.playerSetCurrentLevel(id); + } + + /** + * Determine if the playback has ended. + * @property ended + * @type Boolean + */ + + }, { + key: 'ended', + get: function get() { + return this._hasEnded; + } + + /** + * Determine if the playback is buffering. + * This is related to the PLAYBACK_BUFFERING and PLAYBACK_BUFFERFULL events + * @property buffering + * @type Boolean + */ + + }, { + key: 'buffering', + get: function get() { + return !!this._bufferingState && !this._hasEnded; + } + }]); + + function FlasHLS() { + _classCallCheck(this, FlasHLS); + + for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } + + var _this = _possibleConstructorReturn(this, _BaseFlashPlayback.call.apply(_BaseFlashPlayback, [this].concat(args))); + + _this._src = _this.options.src; + _this._baseUrl = _this.options.baseUrl; + _this._initHlsParameters(_this.options); + // TODO can this be private? + _this.highDefinition = false; + _this._autoPlay = _this.options.autoPlay; + _this._loop = _this.options.loop; + _this._defaultSettings = { + left: ['playstop'], + default: ['seekbar'], + right: ['fullscreen', 'volume', 'hd-indicator'], + seekEnabled: false + }; + _this.settings = _clapprZepto2.default.extend({}, _this._defaultSettings); + _this._playbackType = _playback2.default.LIVE; + _this._hasEnded = false; + _this._addListeners(); + return _this; + } + + FlasHLS.prototype._initHlsParameters = function _initHlsParameters(options) { + this._autoStartLoad = options.autoStartLoad === undefined ? true : options.autoStartLoad; + this._capLevelToStage = options.capLevelToStage === undefined ? false : options.capLevelToStage; + this._maxLevelCappingMode = options.maxLevelCappingMode === undefined ? 'downscale' : options.maxLevelCappingMode; + this._minBufferLength = options.minBufferLength === undefined ? -1 : options.minBufferLength; + this._minBufferLengthCapping = options.minBufferLengthCapping === undefined ? -1 : options.minBufferLengthCapping; + this._maxBufferLength = options.maxBufferLength === undefined ? 120 : options.maxBufferLength; + this._maxBackBufferLength = options.maxBackBufferLength === undefined ? 30 : options.maxBackBufferLength; + this._lowBufferLength = options.lowBufferLength === undefined ? 3 : options.lowBufferLength; + this._mediaTimePeriod = options.mediaTimePeriod === undefined ? 100 : options.mediaTimePeriod; + this._fpsDroppedMonitoringPeriod = options.fpsDroppedMonitoringPeriod === undefined ? 5000 : options.fpsDroppedMonitoringPeriod; + this._fpsDroppedMonitoringThreshold = options.fpsDroppedMonitoringThreshold === undefined ? 0.2 : options.fpsDroppedMonitoringThreshold; + this._capLevelonFPSDrop = options.capLevelonFPSDrop === undefined ? false : options.capLevelonFPSDrop; + this._smoothAutoSwitchonFPSDrop = options.smoothAutoSwitchonFPSDrop === undefined ? this.capLevelonFPSDrop : options.smoothAutoSwitchonFPSDrop; + this._switchDownOnLevelError = options.switchDownOnLevelError === undefined ? true : options.switchDownOnLevelError; + this._seekMode = options.seekMode === undefined ? 'ACCURATE' : options.seekMode; + this._keyLoadMaxRetry = options.keyLoadMaxRetry === undefined ? 3 : options.keyLoadMaxRetry; + this._keyLoadMaxRetryTimeout = options.keyLoadMaxRetryTimeout === undefined ? 64000 : options.keyLoadMaxRetryTimeout; + this._fragmentLoadMaxRetry = options.fragmentLoadMaxRetry === undefined ? 3 : options.fragmentLoadMaxRetry; + this._fragmentLoadMaxRetryTimeout = options.fragmentLoadMaxRetryTimeout === undefined ? 4000 : options.fragmentLoadMaxRetryTimeout; + this._fragmentLoadSkipAfterMaxRetry = options.fragmentLoadSkipAfterMaxRetry === undefined ? true : options.fragmentLoadSkipAfterMaxRetry; + this._maxSkippedFragments = options.maxSkippedFragments === undefined ? 5 : options.maxSkippedFragments; + this._flushLiveURLCache = options.flushLiveURLCache === undefined ? false : options.flushLiveURLCache; + this._initialLiveManifestSize = options.initialLiveManifestSize === undefined ? 1 : options.initialLiveManifestSize; + this._manifestLoadMaxRetry = options.manifestLoadMaxRetry === undefined ? 3 : options.manifestLoadMaxRetry; + this._manifestLoadMaxRetryTimeout = options.manifestLoadMaxRetryTimeout === undefined ? 64000 : options.manifestLoadMaxRetryTimeout; + this._manifestRedundantLoadmaxRetry = options.manifestRedundantLoadmaxRetry === undefined ? 3 : options.manifestRedundantLoadmaxRetry; + this._startFromBitrate = options.startFromBitrate === undefined ? -1 : options.startFromBitrate; + this._startFromLevel = options.startFromLevel === undefined ? -1 : options.startFromLevel; + this._autoStartMaxDuration = options.autoStartMaxDuration === undefined ? -1 : options.autoStartMaxDuration; + this._seekFromLevel = options.seekFromLevel === undefined ? -1 : options.seekFromLevel; + this._useHardwareVideoDecoder = options.useHardwareVideoDecoder === undefined ? false : options.useHardwareVideoDecoder; + this._hlsLogEnabled = options.hlsLogEnabled === undefined ? true : options.hlsLogEnabled; + this._logDebug = options.logDebug === undefined ? false : options.logDebug; + this._logDebug2 = options.logDebug2 === undefined ? false : options.logDebug2; + this._logWarn = options.logWarn === undefined ? true : options.logWarn; + this._logError = options.logError === undefined ? true : options.logError; + this._hlsMinimumDvrSize = options.hlsMinimumDvrSize === undefined ? 60 : options.hlsMinimumDvrSize; + }; + + FlasHLS.prototype._addListeners = function _addListeners() { + var _this2 = this; + + _mediator2.default.on(this.cid + ':flashready', function () { + return _this2._bootstrap(); + }); + _mediator2.default.on(this.cid + ':timeupdate', function (timeMetrics) { + return _this2._updateTime(timeMetrics); + }); + _mediator2.default.on(this.cid + ':playbackstate', function (state) { + return _this2._setPlaybackState(state); + }); + _mediator2.default.on(this.cid + ':levelchanged', function (level) { + return _this2._levelChanged(level); + }); + _mediator2.default.on(this.cid + ':error', function (code, url, message) { + return _this2._flashPlaybackError(code, url, message); + }); + _mediator2.default.on(this.cid + ':fragmentloaded', function (loadmetrics) { + return _this2._onFragmentLoaded(loadmetrics); + }); + _mediator2.default.on(this.cid + ':levelendlist', function (level) { + return _this2._onLevelEndlist(level); + }); + }; + + FlasHLS.prototype.stopListening = function stopListening() { + _BaseFlashPlayback.prototype.stopListening.call(this); + _mediator2.default.off(this.cid + ':flashready'); + _mediator2.default.off(this.cid + ':timeupdate'); + _mediator2.default.off(this.cid + ':playbackstate'); + _mediator2.default.off(this.cid + ':levelchanged'); + _mediator2.default.off(this.cid + ':playbackerror'); + _mediator2.default.off(this.cid + ':fragmentloaded'); + _mediator2.default.off(this.cid + ':manifestloaded'); + _mediator2.default.off(this.cid + ':levelendlist'); + }; + + FlasHLS.prototype._bootstrap = function _bootstrap() { + var _this3 = this; + + if (this.el.playerLoad) { + this.el.width = '100%'; + this.el.height = '100%'; + this._isReadyState = true; + this._srcLoaded = false; + this._currentState = 'IDLE'; + this._setFlashSettings(); + this._updatePlaybackType(); + if (this._autoPlay || this._shouldPlayOnManifestLoaded) { + this.play(); + } + this.trigger(_events2.default.PLAYBACK_READY, this.name); + } else { + this._bootstrapAttempts = this._bootstrapAttempts || 0; + if (++this._bootstrapAttempts <= MAX_ATTEMPTS) { + setTimeout(function () { + return _this3._bootstrap(); + }, 50); + } else { + this.trigger(_events2.default.PLAYBACK_ERROR, { message: 'Max number of attempts reached' }, this.name); + } + } + }; + + FlasHLS.prototype._setFlashSettings = function _setFlashSettings() { + this.el.playerSetAutoStartLoad(this._autoStartLoad); + this.el.playerSetCapLevelToStage(this._capLevelToStage); + this.el.playerSetMaxLevelCappingMode(this._maxLevelCappingMode); + this.el.playerSetMinBufferLength(this._minBufferLength); + this.el.playerSetMinBufferLengthCapping(this._minBufferLengthCapping); + this.el.playerSetMaxBufferLength(this._maxBufferLength); + this.el.playerSetMaxBackBufferLength(this._maxBackBufferLength); + this.el.playerSetLowBufferLength(this._lowBufferLength); + this.el.playerSetMediaTimePeriod(this._mediaTimePeriod); + this.el.playerSetFpsDroppedMonitoringPeriod(this._fpsDroppedMonitoringPeriod); + this.el.playerSetFpsDroppedMonitoringThreshold(this._fpsDroppedMonitoringThreshold); + this.el.playerSetCapLevelonFPSDrop(this._capLevelonFPSDrop); + this.el.playerSetSmoothAutoSwitchonFPSDrop(this._smoothAutoSwitchonFPSDrop); + this.el.playerSetSwitchDownOnLevelError(this._switchDownOnLevelError); + this.el.playerSetSeekMode(this._seekMode); + this.el.playerSetKeyLoadMaxRetry(this._keyLoadMaxRetry); + this.el.playerSetKeyLoadMaxRetryTimeout(this._keyLoadMaxRetryTimeout); + this.el.playerSetFragmentLoadMaxRetry(this._fragmentLoadMaxRetry); + this.el.playerSetFragmentLoadMaxRetryTimeout(this._fragmentLoadMaxRetryTimeout); + this.el.playerSetFragmentLoadSkipAfterMaxRetry(this._fragmentLoadSkipAfterMaxRetry); + this.el.playerSetMaxSkippedFragments(this._maxSkippedFragments); + this.el.playerSetFlushLiveURLCache(this._flushLiveURLCache); + this.el.playerSetInitialLiveManifestSize(this._initialLiveManifestSize); + this.el.playerSetManifestLoadMaxRetry(this._manifestLoadMaxRetry); + this.el.playerSetManifestLoadMaxRetryTimeout(this._manifestLoadMaxRetryTimeout); + this.el.playerSetManifestRedundantLoadmaxRetry(this._manifestRedundantLoadmaxRetry); + this.el.playerSetStartFromBitrate(this._startFromBitrate); + this.el.playerSetStartFromLevel(this._startFromLevel); + this.el.playerSetAutoStartMaxDuration(this._autoStartMaxDuration); + this.el.playerSetSeekFromLevel(this._seekFromLevel); + this.el.playerSetUseHardwareVideoDecoder(this._useHardwareVideoDecoder); + this.el.playerSetLogInfo(this._hlsLogEnabled); + this.el.playerSetLogDebug(this._logDebug); + this.el.playerSetLogDebug2(this._logDebug2); + this.el.playerSetLogWarn(this._logWarn); + this.el.playerSetLogError(this._logError); + }; + + FlasHLS.prototype.setAutoStartLoad = function setAutoStartLoad(autoStartLoad) { + this._autoStartLoad = autoStartLoad; + this.el.playerSetAutoStartLoad(this._autoStartLoad); + }; + + FlasHLS.prototype.setCapLevelToStage = function setCapLevelToStage(capLevelToStage) { + this._capLevelToStage = capLevelToStage; + this.el.playerSetCapLevelToStage(this._capLevelToStage); + }; + + FlasHLS.prototype.setMaxLevelCappingMode = function setMaxLevelCappingMode(maxLevelCappingMode) { + this._maxLevelCappingMode = maxLevelCappingMode; + this.el.playerSetMaxLevelCappingMode(this._maxLevelCappingMode); + }; + + FlasHLS.prototype.setSetMinBufferLength = function setSetMinBufferLength(minBufferLength) { + this._minBufferLength = minBufferLength; + this.el.playerSetMinBufferLength(this._minBufferLength); + }; + + FlasHLS.prototype.setMinBufferLengthCapping = function setMinBufferLengthCapping(minBufferLengthCapping) { + this._minBufferLengthCapping = minBufferLengthCapping; + this.el.playerSetMinBufferLengthCapping(this._minBufferLengthCapping); + }; + + FlasHLS.prototype.setMaxBufferLength = function setMaxBufferLength(maxBufferLength) { + this._maxBufferLength = maxBufferLength; + this.el.playerSetMaxBufferLength(this._maxBufferLength); + }; + + FlasHLS.prototype.setMaxBackBufferLength = function setMaxBackBufferLength(maxBackBufferLength) { + this._maxBackBufferLength = maxBackBufferLength; + this.el.playerSetMaxBackBufferLength(this._maxBackBufferLength); + }; + + FlasHLS.prototype.setLowBufferLength = function setLowBufferLength(lowBufferLength) { + this._lowBufferLength = lowBufferLength; + this.el.playerSetLowBufferLength(this._lowBufferLength); + }; + + FlasHLS.prototype.setMediaTimePeriod = function setMediaTimePeriod(mediaTimePeriod) { + this._mediaTimePeriod = mediaTimePeriod; + this.el.playerSetMediaTimePeriod(this._mediaTimePeriod); + }; + + FlasHLS.prototype.setFpsDroppedMonitoringPeriod = function setFpsDroppedMonitoringPeriod(fpsDroppedMonitoringPeriod) { + this._fpsDroppedMonitoringPeriod = fpsDroppedMonitoringPeriod; + this.el.playerSetFpsDroppedMonitoringPeriod(this._fpsDroppedMonitoringPeriod); + }; + + FlasHLS.prototype.setFpsDroppedMonitoringThreshold = function setFpsDroppedMonitoringThreshold(fpsDroppedMonitoringThreshold) { + this._fpsDroppedMonitoringThreshold = fpsDroppedMonitoringThreshold; + this.el.playerSetFpsDroppedMonitoringThreshold(this._fpsDroppedMonitoringThreshold); + }; + + FlasHLS.prototype.setCapLevelonFPSDrop = function setCapLevelonFPSDrop(capLevelonFPSDrop) { + this._capLevelonFPSDrop = capLevelonFPSDrop; + this.el.playerSetCapLevelonFPSDrop(this._capLevelonFPSDrop); + }; + + FlasHLS.prototype.setSmoothAutoSwitchonFPSDrop = function setSmoothAutoSwitchonFPSDrop(smoothAutoSwitchonFPSDrop) { + this._smoothAutoSwitchonFPSDrop = smoothAutoSwitchonFPSDrop; + this.el.playerSetSmoothAutoSwitchonFPSDrop(this._smoothAutoSwitchonFPSDrop); + }; + + FlasHLS.prototype.setSwitchDownOnLevelError = function setSwitchDownOnLevelError(switchDownOnLevelError) { + this._switchDownOnLevelError = switchDownOnLevelError; + this.el.playerSetSwitchDownOnLevelError(this._switchDownOnLevelError); + }; + + FlasHLS.prototype.setSeekMode = function setSeekMode(seekMode) { + this._seekMode = seekMode; + this.el.playerSetSeekMode(this._seekMode); + }; + + FlasHLS.prototype.setKeyLoadMaxRetry = function setKeyLoadMaxRetry(keyLoadMaxRetry) { + this._keyLoadMaxRetry = keyLoadMaxRetry; + this.el.playerSetKeyLoadMaxRetry(this._keyLoadMaxRetry); + }; + + FlasHLS.prototype.setKeyLoadMaxRetryTimeout = function setKeyLoadMaxRetryTimeout(keyLoadMaxRetryTimeout) { + this._keyLoadMaxRetryTimeout = keyLoadMaxRetryTimeout; + this.el.playerSetKeyLoadMaxRetryTimeout(this._keyLoadMaxRetryTimeout); + }; + + FlasHLS.prototype.setFragmentLoadMaxRetry = function setFragmentLoadMaxRetry(fragmentLoadMaxRetry) { + this._fragmentLoadMaxRetry = fragmentLoadMaxRetry; + this.el.playerSetFragmentLoadMaxRetry(this._fragmentLoadMaxRetry); + }; + + FlasHLS.prototype.setFragmentLoadMaxRetryTimeout = function setFragmentLoadMaxRetryTimeout(fragmentLoadMaxRetryTimeout) { + this._fragmentLoadMaxRetryTimeout = fragmentLoadMaxRetryTimeout; + this.el.playerSetFragmentLoadMaxRetryTimeout(this._fragmentLoadMaxRetryTimeout); + }; + + FlasHLS.prototype.setFragmentLoadSkipAfterMaxRetry = function setFragmentLoadSkipAfterMaxRetry(fragmentLoadSkipAfterMaxRetry) { + this._fragmentLoadSkipAfterMaxRetry = fragmentLoadSkipAfterMaxRetry; + this.el.playerSetFragmentLoadSkipAfterMaxRetry(this._fragmentLoadSkipAfterMaxRetry); + }; + + FlasHLS.prototype.setMaxSkippedFragments = function setMaxSkippedFragments(maxSkippedFragments) { + this._maxSkippedFragments = maxSkippedFragments; + this.el.playerSetMaxSkippedFragments(this._maxSkippedFragments); + }; + + FlasHLS.prototype.setFlushLiveURLCache = function setFlushLiveURLCache(flushLiveURLCache) { + this._flushLiveURLCache = flushLiveURLCache; + this.el.playerSetFlushLiveURLCache(this._flushLiveURLCache); + }; + + FlasHLS.prototype.setInitialLiveManifestSize = function setInitialLiveManifestSize(initialLiveManifestSize) { + this._initialLiveManifestSize = initialLiveManifestSize; + this.el.playerSetInitialLiveManifestSize(this._initialLiveManifestSize); + }; + + FlasHLS.prototype.setManifestLoadMaxRetry = function setManifestLoadMaxRetry(manifestLoadMaxRetry) { + this._manifestLoadMaxRetry = manifestLoadMaxRetry; + this.el.playerSetManifestLoadMaxRetry(this._manifestLoadMaxRetry); + }; + + FlasHLS.prototype.setManifestLoadMaxRetryTimeout = function setManifestLoadMaxRetryTimeout(manifestLoadMaxRetryTimeout) { + this._manifestLoadMaxRetryTimeout = manifestLoadMaxRetryTimeout; + this.el.playerSetManifestLoadMaxRetryTimeout(this._manifestLoadMaxRetryTimeout); + }; + + FlasHLS.prototype.setManifestRedundantLoadmaxRetry = function setManifestRedundantLoadmaxRetry(manifestRedundantLoadmaxRetry) { + this._manifestRedundantLoadmaxRetry = manifestRedundantLoadmaxRetry; + this.el.playerSetManifestRedundantLoadmaxRetry(this._manifestRedundantLoadmaxRetry); + }; + + FlasHLS.prototype.setStartFromBitrate = function setStartFromBitrate(startFromBitrate) { + this._startFromBitrate = startFromBitrate; + this.el.playerSetStartFromBitrate(this._startFromBitrate); + }; + + FlasHLS.prototype.setStartFromLevel = function setStartFromLevel(startFromLevel) { + this._startFromLevel = startFromLevel; + this.el.playerSetStartFromLevel(this._startFromLevel); + }; + + FlasHLS.prototype.setAutoStartMaxDuration = function setAutoStartMaxDuration(autoStartMaxDuration) { + this._autoStartMaxDuration = autoStartMaxDuration; + this.el.playerSetAutoStartMaxDuration(this._autoStartMaxDuration); + }; + + FlasHLS.prototype.setSeekFromLevel = function setSeekFromLevel(seekFromLevel) { + this._seekFromLevel = seekFromLevel; + this.el.playerSetSeekFromLevel(this._seekFromLevel); + }; + + FlasHLS.prototype.setUseHardwareVideoDecoder = function setUseHardwareVideoDecoder(useHardwareVideoDecoder) { + this._useHardwareVideoDecoder = useHardwareVideoDecoder; + this.el.playerSetUseHardwareVideoDecoder(this._useHardwareVideoDecoder); + }; + + FlasHLS.prototype.setSetLogInfo = function setSetLogInfo(hlsLogEnabled) { + this._hlsLogEnabled = hlsLogEnabled; + this.el.playerSetLogInfo(this._hlsLogEnabled); + }; + + FlasHLS.prototype.setLogDebug = function setLogDebug(logDebug) { + this._logDebug = logDebug; + this.el.playerSetLogDebug(this._logDebug); + }; + + FlasHLS.prototype.setLogDebug2 = function setLogDebug2(logDebug2) { + this._logDebug2 = logDebug2; + this.el.playerSetLogDebug2(this._logDebug2); + }; + + FlasHLS.prototype.setLogWarn = function setLogWarn(logWarn) { + this._logWarn = logWarn; + this.el.playerSetLogWarn(this._logWarn); + }; + + FlasHLS.prototype.setLogError = function setLogError(logError) { + this._logError = logError; + this.el.playerSetLogError(this._logError); + }; + + FlasHLS.prototype._levelChanged = function _levelChanged(level) { + var currentLevel = this.el.getLevels()[level]; + if (currentLevel) { + this.highDefinition = currentLevel.height >= 720 || currentLevel.bitrate / 1000 >= 2000; + this.trigger(_events2.default.PLAYBACK_HIGHDEFINITIONUPDATE, this.highDefinition); + + if (!this._levels || this._levels.length === 0) this._fillLevels(); + + this.trigger(_events2.default.PLAYBACK_BITRATE, { + height: currentLevel.height, + width: currentLevel.width, + bandwidth: currentLevel.bitrate, + bitrate: currentLevel.bitrate, + level: level + }); + this.trigger(_events2.default.PLAYBACK_LEVEL_SWITCH_END); + } + }; + + FlasHLS.prototype._updateTime = function _updateTime(timeMetrics) { + if (this._currentState === 'IDLE') { + return; + } + + var duration = this._normalizeDuration(timeMetrics.duration); + var position = Math.min(Math.max(timeMetrics.position, 0), duration); + var previousDVRStatus = this._dvrEnabled; + var livePlayback = this._playbackType === _playback2.default.LIVE; + this._dvrEnabled = livePlayback && duration > this._hlsMinimumDvrSize; + + if (duration === 100 || livePlayback === undefined) { + return; + } + + if (this._dvrEnabled !== previousDVRStatus) { + this._updateSettings(); + this.trigger(_events2.default.PLAYBACK_SETTINGSUPDATE, this.name); + } + + if (livePlayback && (!this._dvrEnabled || !this._dvrInUse)) { + position = duration; + } + + this.trigger(_events2.default.PLAYBACK_TIMEUPDATE, { current: position, total: duration }, this.name); + }; + + FlasHLS.prototype.play = function play() { + this.trigger(_events2.default.PLAYBACK_PLAY_INTENT); + if (this._currentState === 'PAUSED') { + this.el.playerResume(); + } else if (!this._srcLoaded && this._currentState !== 'PLAYING') { + this._firstPlay(); + } else { + this.el.playerPlay(); + } + }; + + FlasHLS.prototype.getPlaybackType = function getPlaybackType() { + return this._playbackType ? this._playbackType : null; + }; + + FlasHLS.prototype.getCurrentLevelIndex = function getCurrentLevelIndex() { + return this._currentLevel; + }; + + FlasHLS.prototype.getCurrentLevel = function getCurrentLevel() { + return this.levels[this.currentLevel]; + }; + + FlasHLS.prototype.getCurrentBitrate = function getCurrentBitrate() { + return this.levels[this.currentLevel].bitrate; + }; + + FlasHLS.prototype.setCurrentLevel = function setCurrentLevel(level) { + this.currentLevel = level; + }; + + FlasHLS.prototype.isHighDefinitionInUse = function isHighDefinitionInUse() { + return this.highDefinition; + }; + + FlasHLS.prototype.getLevels = function getLevels() { + return this.levels; + }; + + FlasHLS.prototype._setPlaybackState = function _setPlaybackState(state) { + if (['PLAYING_BUFFERING', 'PAUSED_BUFFERING'].indexOf(state) >= 0) { + this._bufferingState = true; + this.trigger(_events2.default.PLAYBACK_BUFFERING, this.name); + this._updateCurrentState(state); + } else if (['PLAYING', 'PAUSED'].indexOf(state) >= 0) { + if (['PLAYING_BUFFERING', 'PAUSED_BUFFERING', 'IDLE'].indexOf(this._currentState) >= 0) { + this._bufferingState = false; + this.trigger(_events2.default.PLAYBACK_BUFFERFULL, this.name); + } + this._updateCurrentState(state); + } else if (state === 'IDLE') { + this._srcLoaded = false; + if (this._loop && ['PLAYING_BUFFERING', 'PLAYING'].indexOf(this._currentState) >= 0) { + this.play(); + this.seek(0); + } else { + this._updateCurrentState(state); + this._hasEnded = true; + this.trigger(_events2.default.PLAYBACK_TIMEUPDATE, { current: 0, total: this.getDuration() }, this.name); + this.trigger(_events2.default.PLAYBACK_ENDED, this.name); + } + } + }; + + FlasHLS.prototype._updateCurrentState = function _updateCurrentState(state) { + this._currentState = state; + if (state !== 'IDLE') { + this._hasEnded = false; + } + this._updatePlaybackType(); + if (state === 'PLAYING') { + this.trigger(_events2.default.PLAYBACK_PLAY, this.name); + } else if (state === 'PAUSED') { + this.trigger(_events2.default.PLAYBACK_PAUSE, this.name); + } + }; + + FlasHLS.prototype._updatePlaybackType = function _updatePlaybackType() { + this._playbackType = this.el.getType(); + if (this._playbackType) { + this._playbackType = this._playbackType.toLowerCase(); + if (this._playbackType === _playback2.default.VOD) { + this._startReportingProgress(); + } else { + this._stopReportingProgress(); + } + } + this.trigger(_events2.default.PLAYBACK_PLAYBACKSTATE, { type: this._playbackType }); + }; + + FlasHLS.prototype._startReportingProgress = function _startReportingProgress() { + if (!this._reportingProgress) { + this._reportingProgress = true; + } + }; + + FlasHLS.prototype._stopReportingProgress = function _stopReportingProgress() { + this._reportingProgress = false; + }; + + FlasHLS.prototype._onFragmentLoaded = function _onFragmentLoaded(loadmetrics) { + this.trigger(_events2.default.PLAYBACK_FRAGMENT_LOADED, loadmetrics); + if (this._reportingProgress && this.el.getPosition) { + var buffered = this.el.getPosition() + this.el.getbufferLength(); + this.trigger(_events2.default.PLAYBACK_PROGRESS, { + start: this.el.getPosition(), + current: buffered, + total: this.el.getDuration() + }); + } + }; + + FlasHLS.prototype._onLevelEndlist = function _onLevelEndlist() { + this._updatePlaybackType(); + }; + + FlasHLS.prototype._firstPlay = function _firstPlay() { + var _this4 = this; + + this._shouldPlayOnManifestLoaded = true; + if (this.el.playerLoad) { + _mediator2.default.once(this.cid + ':manifestloaded', function (duration, loadmetrics) { + return _this4._manifestLoaded(duration, loadmetrics); + }); + this._setFlashSettings(); //ensure flushLiveURLCache will work (#327) + this.el.playerLoad(this._src); + this._srcLoaded = true; + } + }; + + FlasHLS.prototype.volume = function volume(value) { + var _this5 = this; + + if (this.isReady) { + this.el.playerVolume(value); + } else { + this.listenToOnce(this, _events2.default.PLAYBACK_BUFFERFULL, function () { + return _this5.volume(value); + }); + } + }; + + FlasHLS.prototype.pause = function pause() { + if (this._playbackType !== _playback2.default.LIVE || this._dvrEnabled) { + this.el.playerPause(); + if (this._playbackType === _playback2.default.LIVE && this._dvrEnabled) { + this._updateDvr(true); + } + } + }; + + FlasHLS.prototype.stop = function stop() { + this._srcLoaded = false; + this.el.playerStop(); + this.trigger(_events2.default.PLAYBACK_STOP); + this.trigger(_events2.default.PLAYBACK_TIMEUPDATE, { current: 0, total: 0 }, this.name); + }; + + FlasHLS.prototype.isPlaying = function isPlaying() { + if (this._currentState) { + return !!this._currentState.match(/playing/i); + } + return false; + }; + + FlasHLS.prototype.getDuration = function getDuration() { + return this._normalizeDuration(this.el.getDuration()); + }; + + FlasHLS.prototype._normalizeDuration = function _normalizeDuration(duration) { + if (this._playbackType === _playback2.default.LIVE) { + // estimate 10 seconds of buffer time for live streams for seek positions + duration = Math.max(0, duration - 10); + } + return duration; + }; + + FlasHLS.prototype.seekPercentage = function seekPercentage(percentage) { + var duration = this.el.getDuration(); + var time = 0; + if (percentage > 0) { + time = duration * percentage / 100; + } + this.seek(time); + }; + + FlasHLS.prototype.seek = function seek(time) { + var duration = this.getDuration(); + if (this._playbackType === _playback2.default.LIVE) { + // seek operations to a time within 3 seconds from live stream will position playhead back to live + var dvrInUse = duration - time > 3; + this._updateDvr(dvrInUse); + } + this.el.playerSeek(time); + this.trigger(_events2.default.PLAYBACK_TIMEUPDATE, { current: time, total: duration }, this.name); + }; + + FlasHLS.prototype._updateDvr = function _updateDvr(dvrInUse) { + var previousDvrInUse = !!this._dvrInUse; + this._dvrInUse = dvrInUse; + if (this._dvrInUse !== previousDvrInUse) { + this._updateSettings(); + this.trigger(_events2.default.PLAYBACK_DVR, this._dvrInUse); + this.trigger(_events2.default.PLAYBACK_STATS_ADD, { 'dvr': this._dvrInUse }); + } + }; + + FlasHLS.prototype._flashPlaybackError = function _flashPlaybackError(code, url, message) { + this.trigger(_events2.default.PLAYBACK_ERROR, { code: code, url: url, message: message }); + this.trigger(_events2.default.PLAYBACK_STOP); + }; + + FlasHLS.prototype._manifestLoaded = function _manifestLoaded(duration, loadmetrics) { + if (this._shouldPlayOnManifestLoaded) { + this._shouldPlayOnManifestLoaded = false; + // this method initialises the player (and starts playback) + // this needs to happen before PLAYBACK_LOADEDMETADATA is fired + // as the user may call seek() in a LOADEDMETADATA listener. + /// when playerPlay() is called the player seeks to 0 + this.el.playerPlay(); + } + + this._fillLevels(); + this.trigger(_events2.default.PLAYBACK_LOADEDMETADATA, { duration: duration, data: loadmetrics }); + }; + + FlasHLS.prototype._fillLevels = function _fillLevels() { + var levels = this.el.getLevels(); + var levelsLength = levels.length; + this._levels = []; + + for (var index = 0; index < levelsLength; index++) { + this._levels.push({ id: index, label: levels[index].height + 'p', level: levels[index] }); + } + this.trigger(_events2.default.PLAYBACK_LEVELS_AVAILABLE, this._levels); + }; + + FlasHLS.prototype.destroy = function destroy() { + this.stopListening(); + this.$el.remove(); + }; + + FlasHLS.prototype._updateSettings = function _updateSettings() { + this.settings = _clapprZepto2.default.extend({}, this._defaultSettings); + if (this._playbackType === _playback2.default.VOD || this._dvrInUse) { + this.settings.left = ['playpause', 'position', 'duration']; + this.settings.seekEnabled = true; + } else if (this._dvrEnabled) { + this.settings.left = ['playpause']; + this.settings.seekEnabled = true; + } else { + this.settings.seekEnabled = false; + } + }; + + FlasHLS.prototype._createCallbacks = function _createCallbacks() { + var _this6 = this; + + if (!window.Clappr) { + window.Clappr = {}; + } + if (!window.Clappr.flashlsCallbacks) { + window.Clappr.flashlsCallbacks = {}; + } + this.flashlsEvents = new _flashls_events2.default(this.cid); + window.Clappr.flashlsCallbacks[this.cid] = function (eventName, args) { + _this6.flashlsEvents[eventName].apply(_this6.flashlsEvents, args); + }; + }; + + FlasHLS.prototype.render = function render() { + _BaseFlashPlayback.prototype.render.call(this); + this._createCallbacks(); + return this; + }; + + _createClass(FlasHLS, [{ + key: 'isReady', + get: function get() { + return this._isReadyState; + } + }, { + key: 'dvrEnabled', + get: function get() { + return !!this._dvrEnabled; + } + }]); + + return FlasHLS; + }(_base_flash_playback2.default); + + exports.default = FlasHLS; + + + FlasHLS.canPlay = function (resource, mimeType) { + var resourceParts = resource.split('?')[0].match(/.*\.(.*)$/) || []; + return _browser2.default.hasFlash && (resourceParts.length > 1 && resourceParts[1].toLowerCase() === 'm3u8' || mimeType === 'application/x-mpegURL' || mimeType === 'application/vnd.apple.mpegurl'); + }; + module.exports = exports['default']; + +/***/ }, +/* 65 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + var _mediator = __webpack_require__(30); + + var _mediator2 = _interopRequireDefault(_mediator); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + + var HLSEvents = function () { + function HLSEvents(instanceId) { + _classCallCheck(this, HLSEvents); + + this.instanceId = instanceId; + } + + HLSEvents.prototype.ready = function ready() { + _mediator2.default.trigger(this.instanceId + ':flashready'); + }; + + HLSEvents.prototype.videoSize = function videoSize(width, height) { + _mediator2.default.trigger(this.instanceId + ':videosizechanged', width, height); + }; + + HLSEvents.prototype.complete = function complete() { + _mediator2.default.trigger(this.instanceId + ':complete'); + }; + + HLSEvents.prototype.error = function error(code, url, message) { + _mediator2.default.trigger(this.instanceId + ':error', code, url, message); + }; + + HLSEvents.prototype.manifest = function manifest(duration, loadmetrics) { + _mediator2.default.trigger(this.instanceId + ':manifestloaded', duration, loadmetrics); + }; + + HLSEvents.prototype.audioLevelLoaded = function audioLevelLoaded(loadmetrics) { + _mediator2.default.trigger(this.instanceId + ':audiolevelloaded', loadmetrics); + }; + + HLSEvents.prototype.levelLoaded = function levelLoaded(loadmetrics) { + _mediator2.default.trigger(this.instanceId + ':levelloaded', loadmetrics); + }; + + HLSEvents.prototype.levelEndlist = function levelEndlist(level) { + _mediator2.default.trigger(this.instanceId + ':levelendlist', level); + }; + + HLSEvents.prototype.fragmentLoaded = function fragmentLoaded(loadmetrics) { + _mediator2.default.trigger(this.instanceId + ':fragmentloaded', loadmetrics); + }; + + HLSEvents.prototype.fragmentPlaying = function fragmentPlaying(playmetrics) { + _mediator2.default.trigger(this.instanceId + ':fragmentplaying', playmetrics); + }; + + HLSEvents.prototype.position = function position(timemetrics) { + _mediator2.default.trigger(this.instanceId + ':timeupdate', timemetrics); + }; + + HLSEvents.prototype.state = function state(newState) { + _mediator2.default.trigger(this.instanceId + ':playbackstate', newState); + }; + + HLSEvents.prototype.seekState = function seekState(newState) { + _mediator2.default.trigger(this.instanceId + ':seekstate', newState); + }; + + HLSEvents.prototype.switch = function _switch(newLevel) { + _mediator2.default.trigger(this.instanceId + ':levelchanged', newLevel); + }; + + HLSEvents.prototype.audioTracksListChange = function audioTracksListChange(trackList) { + _mediator2.default.trigger(this.instanceId + ':audiotracklistchanged', trackList); + }; + + HLSEvents.prototype.audioTrackChange = function audioTrackChange(trackId) { + _mediator2.default.trigger(this.instanceId + ':audiotrackchanged', trackId); + }; + + return HLSEvents; + }(); + + exports.default = HLSEvents; + module.exports = exports['default']; + +/***/ }, +/* 66 */ +/***/ function(module, exports, __webpack_require__) { + + module.exports = __webpack_require__.p + "809981e5b09d5336c45d72d0869ada2a.swf"; + +/***/ }, +/* 67 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + module.exports = __webpack_require__(68); + +/***/ }, +/* 68 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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 _html5_video = __webpack_require__(51); + + var _html5_video2 = _interopRequireDefault(_html5_video); + + var _hls = __webpack_require__(69); + + var _hls2 = _interopRequireDefault(_hls); + + var _lodash = __webpack_require__(70); + + var _lodash2 = _interopRequireDefault(_lodash); + + var _events = __webpack_require__(5); + + var _events2 = _interopRequireDefault(_events); + + var _playback = __webpack_require__(31); + + var _playback2 = _interopRequireDefault(_playback); + + var _browser = __webpack_require__(3); + + var _browser2 = _interopRequireDefault(_browser); + + var _utils = __webpack_require__(2); + + var _log = __webpack_require__(6); + + var _log2 = _interopRequireDefault(_log); + + 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; } // Copyright 2014 Globo.com Player authors. All rights reserved. + // Use of this source code is governed by a BSD-style + // license that can be found in the LICENSE file. + + var AUTO = -1; + + var HLS = function (_HTML5VideoPlayback) { + _inherits(HLS, _HTML5VideoPlayback); + + _createClass(HLS, [{ + key: 'name', + get: function get() { + return 'hls'; + } + }, { + key: 'levels', + get: function get() { + return this._levels || []; + } + }, { + key: 'currentLevel', + get: function get() { + if (this._currentLevel === null || this._currentLevel === undefined) { + return AUTO; + } else { + return this._currentLevel; //0 is a valid level ID + } + }, + set: function set(id) { + this._currentLevel = id; + this.trigger(_events2.default.PLAYBACK_LEVEL_SWITCH_START); + this._hls.currentLevel = this._currentLevel; + } + }, { + key: '_startTime', + get: function get() { + if (this._playbackType === _playback2.default.LIVE && this._playlistType !== 'EVENT') { + return this._extrapolatedStartTime; + } + return this._playableRegionStartTime; + } + }, { + key: '_now', + get: function get() { + return (0, _utils.now)(); + } + + // the time in the video element which should represent the start of the sliding window + // extrapolated to increase in real time (instead of jumping as the early segments are removed) + + }, { + key: '_extrapolatedStartTime', + get: function get() { + if (!this._localStartTimeCorrelation) { + return this._playableRegionStartTime; + } + var corr = this._localStartTimeCorrelation; + var timePassed = this._now - corr.local; + var extrapolatedWindowStartTime = (corr.remote + timePassed) / 1000; + // cap at the end of the extrapolated window duration + return Math.min(extrapolatedWindowStartTime, this._playableRegionStartTime + this._extrapolatedWindowDuration); + } + + // the time in the video element which should represent the end of the content + // extrapolated to increase in real time (instead of jumping as segments are added) + + }, { + key: '_extrapolatedEndTime', + get: function get() { + var actualEndTime = this._playableRegionStartTime + this._playableRegionDuration; + if (!this._localEndTimeCorrelation) { + return actualEndTime; + } + var corr = this._localEndTimeCorrelation; + var timePassed = this._now - corr.local; + var extrapolatedEndTime = (corr.remote + timePassed) / 1000; + return Math.max(actualEndTime - this._extrapolatedWindowDuration, Math.min(extrapolatedEndTime, actualEndTime)); + } + }, { + key: '_duration', + get: function get() { + return this._extrapolatedEndTime - this._startTime; + } + + // Returns the duration (seconds) of the window that the extrapolated start time is allowed + // to move in before being capped. + // The extrapolated start time should never reach the cap at the end of the window as the + // window should slide as chunks are removed from the start. + // This also applies to the extrapolated end time in the same way. + // + // If chunks aren't being removed for some reason that the start time will reach and remain fixed at + // playableRegionStartTime + extrapolatedWindowDuration + // + // <-- window duration --> + // I.e playableRegionStartTime |-----------------------| + // | --> . . . + // . --> | --> . . + // . . --> | --> . + // . . . --> | + // . . . . + // extrapolatedStartTime + + }, { + key: '_extrapolatedWindowDuration', + get: function get() { + if (this._segmentTargetDuration === null) { + return 0; + } + return this._extrapolatedWindowNumSegments * this._segmentTargetDuration; + } + }]); + + function HLS() { + _classCallCheck(this, HLS); + + for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } + + // backwards compatibility (TODO: remove on 0.3.0) + var _this = _possibleConstructorReturn(this, _HTML5VideoPlayback.call.apply(_HTML5VideoPlayback, [this].concat(args))); + + _this.options.playback || (_this.options.playback = _this.options); + _this._minDvrSize = typeof _this.options.hlsMinimumDvrSize === 'undefined' ? 60 : _this.options.hlsMinimumDvrSize; + // The size of the start time extrapolation window measured as a multiple of segments. + // Should be 2 or higher, or 0 to disable. Should only need to be increased above 2 if more than one segment is + // removed from the start of the playlist at a time. E.g if the playlist is cached for 10 seconds and new chunks are + // added/removed every 5. + _this._extrapolatedWindowNumSegments = !_this.options.playback || typeof _this.options.playback.extrapolatedWindowNumSegments === 'undefined' ? 2 : _this.options.playback.extrapolatedWindowNumSegments; + + _this._playbackType = _playback2.default.VOD; + _this._lastTimeUpdate = null; + _this._lastDuration = null; + // for hls streams which have dvr with a sliding window, + // the content at the start of the playlist is removed as new + // content is appended at the end. + // this means the actual playable start time will increase as the + // start content is deleted + // For streams with dvr where the entire recording is kept from the + // beginning this should stay as 0 + _this._playableRegionStartTime = 0; + // {local, remote} remote is the time in the video element that should represent 0 + // local is the system time when the 'remote' measurment took place + _this._localStartTimeCorrelation = null; + // {local, remote} remote is the time in the video element that should represents the end + // local is the system time when the 'remote' measurment took place + _this._localEndTimeCorrelation = null; + // if content is removed from the beginning then this empty area should + // be ignored. "playableRegionDuration" excludes the empty area + _this._playableRegionDuration = 0; + // true when the actual duration is longer than hlsjs's live sync point + // when this is false playableRegionDuration will be the actual duration + // when this is true playableRegionDuration will exclude the time after the sync point + _this._durationExcludesAfterLiveSyncPoint = false; + // #EXT-X-TARGETDURATION + _this._segmentTargetDuration = null; + // #EXT-X-PLAYLIST-TYPE + _this._playlistType = null; + _this._recoverAttemptsRemaining = _this.options.hlsRecoverAttempts || 16; + _this._startTimeUpdateTimer(); + return _this; + } + + HLS.prototype._setupHls = function _setupHls() { + var _this2 = this; + + this._hls = new _hls2.default(this.options.playback.hlsjsConfig || {}); + this._hls.on(_hls2.default.Events.MEDIA_ATTACHED, function () { + return _this2._hls.loadSource(_this2.options.src); + }); + this._hls.on(_hls2.default.Events.LEVEL_LOADED, function (evt, data) { + return _this2._updatePlaybackType(evt, data); + }); + this._hls.on(_hls2.default.Events.LEVEL_UPDATED, function (evt, data) { + return _this2._onLevelUpdated(evt, data); + }); + this._hls.on(_hls2.default.Events.LEVEL_SWITCH, function (evt, data) { + return _this2._onLevelSwitch(evt, data); + }); + this._hls.on(_hls2.default.Events.FRAG_LOADED, function (evt, data) { + return _this2._onFragmentLoaded(evt, data); + }); + this._hls.on(_hls2.default.Events.ERROR, function (evt, data) { + return _this2._onHLSJSError(evt, data); + }); + this._hls.attachMedia(this.el); + }; + + HLS.prototype._recover = function _recover(evt, data) { + if (!this._recoveredDecodingError) { + this._recoveredDecodingError = true; + this._hls.recoverMediaError(); + } else if (!this._recoveredAudioCodecError) { + this._recoveredAudioCodecError = true; + this._hls.swapAudioCodec(); + this._hls.recoverMediaError(); + } else { + _log2.default.error('hlsjs: failed to recover'); + this.trigger(_events2.default.PLAYBACK_ERROR, 'hlsjs: could not recover from error, evt ' + evt + ', data ' + data + ' ', this.name); + } + }; + + // override + + + HLS.prototype._setupSrc = function _setupSrc(srcUrl) {// eslint-disable-line no-unused-vars + // this playback manages the src on the video element itself + }; + + HLS.prototype._startTimeUpdateTimer = function _startTimeUpdateTimer() { + var _this3 = this; + + this._timeUpdateTimer = setInterval(function () { + _this3._onDurationChange(); + _this3._onTimeUpdate(); + }, 100); + }; + + HLS.prototype._stopTimeUpdateTimer = function _stopTimeUpdateTimer() { + clearInterval(this._timeUpdateTimer); + }; + + // the duration on the video element itself should not be used + // as this does not necesarily represent the duration of the stream + // https://github.com/clappr/clappr/issues/668#issuecomment-157036678 + + + HLS.prototype.getDuration = function getDuration() { + return this._duration; + }; + + HLS.prototype.getCurrentTime = function getCurrentTime() { + // e.g. can be < 0 if user pauses near the start + // eventually they will then be kicked to the end by hlsjs if they run out of buffer + // before the official start time + return Math.max(0, this.el.currentTime - this._startTime); + }; + + // the time that "0" now represents relative to when playback started + // for a stream with a sliding window this will increase as content is + // removed from the beginning + + + HLS.prototype.getStartTimeOffset = function getStartTimeOffset() { + return this._startTime; + }; + + HLS.prototype.seekPercentage = function seekPercentage(percentage) { + var seekTo = this._duration; + if (percentage > 0) { + seekTo = this._duration * (percentage / 100); + } + this.seek(seekTo); + }; + + HLS.prototype.seek = function seek(time) { + if (time < 0) { + _log2.default.warn('Attempt to seek to a negative time. Resetting to live point. Use seekToLivePoint() to seek to the live point.'); + time = this.getDuration(); + } + // assume live if time within 3 seconds of end of stream + this.dvrEnabled && this._updateDvr(time < this.getDuration() - 3); + time += this._startTime; + _HTML5VideoPlayback.prototype.seek.call(this, time); + }; + + HLS.prototype.seekToLivePoint = function seekToLivePoint() { + this.seek(this.getDuration()); + }; + + HLS.prototype._updateDvr = function _updateDvr(status) { + this.trigger(_events2.default.PLAYBACK_DVR, status); + this.trigger(_events2.default.PLAYBACK_STATS_ADD, { 'dvr': status }); + }; + + HLS.prototype._updateSettings = function _updateSettings() { + if (this._playbackType === _playback2.default.VOD) { + this.settings.left = ['playpause', 'position', 'duration']; + } else if (this.dvrEnabled) { + this.settings.left = ['playpause']; + } else { + this.settings.left = ['playstop']; + } + this.settings.seekEnabled = this.isSeekEnabled(); + this.trigger(_events2.default.PLAYBACK_SETTINGSUPDATE); + }; + + HLS.prototype._onHLSJSError = function _onHLSJSError(evt, data) { + // only report/handle errors if they are fatal + // hlsjs should automatically handle non fatal errors + if (data.fatal) { + if (this._recoverAttemptsRemaining > 0) { + this._recoverAttemptsRemaining -= 1; + switch (data.type) { + case _hls2.default.ErrorTypes.NETWORK_ERROR: + _log2.default.warn('hlsjs: trying to recover from network error, evt ' + evt + ', data ' + data + ' '); + this._hls.startLoad(); + break; + case _hls2.default.ErrorTypes.MEDIA_ERROR: + _log2.default.warn('hlsjs: trying to recover from media error, evt ' + evt + ', data ' + data + ' '); + this._recover(evt, data); + break; + default: + _log2.default.error('hlsjs: trying to recover from error, evt ' + evt + ', data ' + data + ' '); + this.trigger(_events2.default.PLAYBACK_ERROR, 'hlsjs: could not recover from error, evt ' + evt + ', data ' + data + ' ', this.name); + break; + } + } else { + _log2.default.error('hlsjs: could not recover from error after maximum number of attempts, evt ' + evt + ', data ' + data + ' '); + this.trigger(_events2.default.PLAYBACK_ERROR, { evt: evt, data: data }, this.name); + } + } else { + _log2.default.warn('hlsjs: non-fatal error occurred, evt ' + evt + ', data ' + data + ' '); + } + }; + + HLS.prototype._onTimeUpdate = function _onTimeUpdate() { + var update = { current: this.getCurrentTime(), total: this.getDuration() }; + if ((0, _lodash2.default)(update, this._lastTimeUpdate)) { + return; + } + this._lastTimeUpdate = update; + this.trigger(_events2.default.PLAYBACK_TIMEUPDATE, update, this.name); + }; + + HLS.prototype._onDurationChange = function _onDurationChange() { + var duration = this.getDuration(); + if (this._lastDuration === duration) { + return; + } + this._lastDuration = duration; + _HTML5VideoPlayback.prototype._onDurationChange.call(this); + }; + + HLS.prototype._onProgress = function _onProgress() { + if (!this.el.buffered.length) { + return; + } + var buffered = []; + var bufferedPos = 0; + for (var i = 0; i < this.el.buffered.length; i++) { + buffered = [].concat(_toConsumableArray(buffered), [{ + // for a stream with sliding window dvr something that is buffered my slide off the start of the timeline + start: Math.max(0, this.el.buffered.start(i) - this._playableRegionStartTime), + end: Math.max(0, this.el.buffered.end(i) - this._playableRegionStartTime) + }]); + if (this.el.currentTime >= buffered[i].start && this.el.currentTime <= buffered[i].end) { + bufferedPos = i; + } + } + var progress = { + start: buffered[bufferedPos].start, + current: buffered[bufferedPos].end, + total: this.getDuration() + }; + this.trigger(_events2.default.PLAYBACK_PROGRESS, progress, buffered); + }; + + HLS.prototype.play = function play() { + if (!this._hls) { + this._setupHls(); + } + _HTML5VideoPlayback.prototype.play.call(this); + }; + + HLS.prototype.pause = function pause() { + if (!this._hls) { + return; + } + _HTML5VideoPlayback.prototype.pause.call(this); + if (this.dvrEnabled) { + this._updateDvr(true); + } + }; + + HLS.prototype.stop = function stop() { + if (this._hls) { + _HTML5VideoPlayback.prototype.stop.call(this); + this._hls.destroy(); + delete this._hls; + } + }; + + HLS.prototype.destroy = function destroy() { + this._stopTimeUpdateTimer(); + if (this._hls) { + this._hls.destroy(); + delete this._hls; + } + _HTML5VideoPlayback.prototype.destroy.call(this); + }; + + HLS.prototype._updatePlaybackType = function _updatePlaybackType(evt, data) { + this._playbackType = data.details.live ? _playback2.default.LIVE : _playback2.default.VOD; + this._fillLevels(); + this._onLevelUpdated(evt, data); + }; + + HLS.prototype._fillLevels = function _fillLevels() { + this._levels = this._hls.levels.map(function (level, index) { + return { id: index, level: level, label: level.bitrate / 1000 + 'Kbps' }; + }); + this.trigger(_events2.default.PLAYBACK_LEVELS_AVAILABLE, this._levels); + }; + + HLS.prototype._onLevelUpdated = function _onLevelUpdated(evt, data) { + this._segmentTargetDuration = data.details.targetduration; + this._playlistType = data.details.type || null; + + var startTimeChanged = false; + var durationChanged = false; + var fragments = data.details.fragments; + var previousPlayableRegionStartTime = this._playableRegionStartTime; + var previousPlayableRegionDuration = this._playableRegionDuration; + + if (fragments.length === 0) { + return; + } + + if (this._playableRegionStartTime !== fragments[0].start) { + startTimeChanged = true; + this._playableRegionStartTime = fragments[0].start; + } + + if (startTimeChanged) { + if (!this._localStartTimeCorrelation) { + // set the correlation to map to middle of the extrapolation window + this._localStartTimeCorrelation = { + local: this._now, + remote: (fragments[0].start + this._extrapolatedWindowDuration / 2) * 1000 + }; + } else { + // check if the correlation still works + var corr = this._localStartTimeCorrelation; + var timePassed = this._now - corr.local; + // this should point to a time within the extrapolation window + var startTime = (corr.remote + timePassed) / 1000; + if (startTime < fragments[0].start) { + // our start time is now earlier than the first chunk + // (maybe the chunk was removed early) + // reset correlation so that it sits at the beginning of the first available chunk + this._localStartTimeCorrelation = { + local: this._now, + remote: fragments[0].start * 1000 + }; + } else if (startTime > previousPlayableRegionStartTime + this._extrapolatedWindowDuration) { + // start time was past the end of the old extrapolation window (so would have been capped) + // see if now that time would be inside the window, and if it would be set the correlation + // so that it resumes from the time it was at at the end of the old window + // update the correlation so that the time starts counting again from the value it's on now + this._localStartTimeCorrelation = { + local: this._now, + remote: Math.max(fragments[0].start, previousPlayableRegionStartTime + this._extrapolatedWindowDuration) * 1000 + }; + } + } + } + + var newDuration = data.details.totalduration; + // if it's a live stream then shorten the duration to remove access + // to the area after hlsjs's live sync point + // seeks to areas after this point sometimes have issues + if (this._playbackType === _playback2.default.LIVE) { + var fragmentTargetDuration = data.details.targetduration; + var hlsjsConfig = this.options.playback || {}; + var liveSyncDurationCount = hlsjsConfig.liveSyncDurationCount || _hls2.default.DefaultConfig.liveSyncDurationCount; + var hiddenAreaDuration = fragmentTargetDuration * liveSyncDurationCount; + if (hiddenAreaDuration <= newDuration) { + newDuration -= hiddenAreaDuration; + this._durationExcludesAfterLiveSyncPoint = true; + } else { + this._durationExcludesAfterLiveSyncPoint = false; + } + } + + if (newDuration !== this._playableRegionDuration) { + durationChanged = true; + this._playableRegionDuration = newDuration; + } + + // Note the end time is not the playableRegionDuration + // The end time will always increase even if content is removed from the beginning + var endTime = fragments[0].start + newDuration; + var previousEndTime = previousPlayableRegionStartTime + previousPlayableRegionDuration; + var endTimeChanged = endTime !== previousEndTime; + if (endTimeChanged) { + if (!this._localEndTimeCorrelation) { + // set the correlation to map to the end + this._localEndTimeCorrelation = { + local: this._now, + remote: endTime * 1000 + }; + } else { + // check if the correlation still works + var _corr = this._localEndTimeCorrelation; + var _timePassed = this._now - _corr.local; + // this should point to a time within the extrapolation window from the end + var extrapolatedEndTime = (_corr.remote + _timePassed) / 1000; + if (extrapolatedEndTime > endTime) { + this._localEndTimeCorrelation = { + local: this._now, + remote: endTime * 1000 + }; + } else if (extrapolatedEndTime < endTime - this._extrapolatedWindowDuration) { + // our extrapolated end time is now earlier than the extrapolation window from the actual end time + // (maybe a chunk became available early) + // reset correlation so that it sits at the beginning of the extrapolation window from the end time + this._localEndTimeCorrelation = { + local: this._now, + remote: (endTime - this._extrapolatedWindowDuration) * 1000 + }; + } else if (extrapolatedEndTime > previousEndTime) { + // end time was past the old end time (so would have been capped) + // set the correlation so that it resumes from the time it was at at the end of the old window + this._localEndTimeCorrelation = { + local: this._now, + remote: previousEndTime * 1000 + }; + } + } + } + + // now that the values have been updated call any methods that use on them so they get the updated values + // immediately + durationChanged && this._onDurationChange(); + startTimeChanged && this._onProgress(); + }; + + HLS.prototype._onFragmentLoaded = function _onFragmentLoaded(evt, data) { + this.trigger(_events2.default.PLAYBACK_FRAGMENT_LOADED, data); + }; + + HLS.prototype._onLevelSwitch = function _onLevelSwitch(evt, data) { + if (!this.levels.length) { + this._fillLevels(); + } + this.trigger(_events2.default.PLAYBACK_LEVEL_SWITCH_END); + this.trigger(_events2.default.PLAYBACK_LEVEL_SWITCH, data); + var currentLevel = this._hls.levels[data.level]; + if (currentLevel) { + // TODO should highDefinition be private and maybe have a read only accessor if it's used somewhere + this.highDefinition = currentLevel.height >= 720 || currentLevel.bitrate / 1000 >= 2000; + this.trigger(_events2.default.PLAYBACK_HIGHDEFINITIONUPDATE, this.highDefinition); + this.trigger(_events2.default.PLAYBACK_BITRATE, { + height: currentLevel.height, + width: currentLevel.width, + bandwidth: currentLevel.bitrate, + bitrate: currentLevel.bitrate, + level: data.level + }); + } + }; + + HLS.prototype.getPlaybackType = function getPlaybackType() { + return this._playbackType; + }; + + HLS.prototype.isSeekEnabled = function isSeekEnabled() { + return this._playbackType === _playback2.default.VOD || this.dvrEnabled; + }; + + _createClass(HLS, [{ + key: 'dvrEnabled', + get: function get() { + // enabled when: + // - the duration does not include content after hlsjs's live sync point + // - the playable region duration is longer than the configured duration to enable dvr after + // - the playback type is LIVE. + return this._durationExcludesAfterLiveSyncPoint && this._duration >= this._minDvrSize && this.getPlaybackType() === _playback2.default.LIVE; + } + }]); + + return HLS; + }(_html5_video2.default); + + exports.default = HLS; + + + HLS.canPlay = function (resource, mimeType) { + var resourceParts = resource.split('?')[0].match(/.*\.(.*)$/) || []; + var isHls = resourceParts.length > 1 && resourceParts[1].toLowerCase() === 'm3u8' || mimeType === 'application/x-mpegURL' || mimeType === 'application/vnd.apple.mpegurl'; + + return !!(_hls2.default.isSupported() && isHls && !_browser2.default.isSafari); + }; + module.exports = exports['default']; + +/***/ }, +/* 69 */ +/***/ function(module, exports, __webpack_require__) { + + var require;var require;(function(f){if(true){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Hls = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return require(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){ + // Copyright Joyent, Inc. and other Node contributors. + // + // Permission is hereby granted, free of charge, to any person obtaining a + // copy of this software and associated documentation files (the + // "Software"), to deal in the Software without restriction, including + // without limitation the rights to use, copy, modify, merge, publish, + // distribute, sublicense, and/or sell copies of the Software, and to permit + // persons to whom the Software is furnished to do so, subject to the + // following conditions: + // + // The above copyright notice and this permission notice shall be included + // in all copies or substantial portions of the Software. + // + // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + // USE OR OTHER DEALINGS IN THE SOFTWARE. + + function EventEmitter() { + this._events = this._events || {}; + this._maxListeners = this._maxListeners || undefined; + } + module.exports = EventEmitter; + + // Backwards-compat with node 0.10.x + EventEmitter.EventEmitter = EventEmitter; + + EventEmitter.prototype._events = undefined; + EventEmitter.prototype._maxListeners = undefined; + + // By default EventEmitters will print a warning if more than 10 listeners are + // added to it. This is a useful default which helps finding memory leaks. + EventEmitter.defaultMaxListeners = 10; + + // Obviously not all Emitters should be limited to 10. This function allows + // that to be increased. Set to zero for unlimited. + EventEmitter.prototype.setMaxListeners = function(n) { + if (!isNumber(n) || n < 0 || isNaN(n)) + throw TypeError('n must be a positive number'); + this._maxListeners = n; + return this; + }; + + EventEmitter.prototype.emit = function(type) { + var er, handler, len, args, i, listeners; + + if (!this._events) + this._events = {}; + + // If there is no 'error' event listener then throw. + if (type === 'error') { + if (!this._events.error || + (isObject(this._events.error) && !this._events.error.length)) { + er = arguments[1]; + if (er instanceof Error) { + throw er; // Unhandled 'error' event + } + throw TypeError('Uncaught, unspecified "error" event.'); + } + } + + handler = this._events[type]; + + if (isUndefined(handler)) + return false; + + if (isFunction(handler)) { + switch (arguments.length) { + // fast cases + case 1: + handler.call(this); + break; + case 2: + handler.call(this, arguments[1]); + break; + case 3: + handler.call(this, arguments[1], arguments[2]); + break; + // slower + default: + args = Array.prototype.slice.call(arguments, 1); + handler.apply(this, args); + } + } else if (isObject(handler)) { + args = Array.prototype.slice.call(arguments, 1); + listeners = handler.slice(); + len = listeners.length; + for (i = 0; i < len; i++) + listeners[i].apply(this, args); + } + + return true; + }; + + EventEmitter.prototype.addListener = function(type, listener) { + var m; + + if (!isFunction(listener)) + throw TypeError('listener must be a function'); + + if (!this._events) + this._events = {}; + + // To avoid recursion in the case that type === "newListener"! Before + // adding it to the listeners, first emit "newListener". + if (this._events.newListener) + this.emit('newListener', type, + isFunction(listener.listener) ? + listener.listener : listener); + + if (!this._events[type]) + // Optimize the case of one listener. Don't need the extra array object. + this._events[type] = listener; + else if (isObject(this._events[type])) + // If we've already got an array, just append. + this._events[type].push(listener); + else + // Adding the second element, need to change to array. + this._events[type] = [this._events[type], listener]; + + // Check for listener leak + if (isObject(this._events[type]) && !this._events[type].warned) { + if (!isUndefined(this._maxListeners)) { + m = this._maxListeners; + } else { + m = EventEmitter.defaultMaxListeners; + } + + if (m && m > 0 && this._events[type].length > m) { + this._events[type].warned = true; + console.error('(node) warning: possible EventEmitter memory ' + + 'leak detected. %d listeners added. ' + + 'Use emitter.setMaxListeners() to increase limit.', + this._events[type].length); + if (typeof console.trace === 'function') { + // not supported in IE 10 + console.trace(); + } + } + } + + return this; + }; + + EventEmitter.prototype.on = EventEmitter.prototype.addListener; + + EventEmitter.prototype.once = function(type, listener) { + if (!isFunction(listener)) + throw TypeError('listener must be a function'); + + var fired = false; + + function g() { + this.removeListener(type, g); + + if (!fired) { + fired = true; + listener.apply(this, arguments); + } + } + + g.listener = listener; + this.on(type, g); + + return this; + }; + + // emits a 'removeListener' event iff the listener was removed + EventEmitter.prototype.removeListener = function(type, listener) { + var list, position, length, i; + + if (!isFunction(listener)) + throw TypeError('listener must be a function'); + + if (!this._events || !this._events[type]) + return this; + + list = this._events[type]; + length = list.length; + position = -1; + + if (list === listener || + (isFunction(list.listener) && list.listener === listener)) { + delete this._events[type]; + if (this._events.removeListener) + this.emit('removeListener', type, listener); + + } else if (isObject(list)) { + for (i = length; i-- > 0;) { + if (list[i] === listener || + (list[i].listener && list[i].listener === listener)) { + position = i; + break; + } + } + + if (position < 0) + return this; + + if (list.length === 1) { + list.length = 0; + delete this._events[type]; + } else { + list.splice(position, 1); + } + + if (this._events.removeListener) + this.emit('removeListener', type, listener); + } + + return this; + }; + + EventEmitter.prototype.removeAllListeners = function(type) { + var key, listeners; + + if (!this._events) + return this; + + // not listening for removeListener, no need to emit + if (!this._events.removeListener) { + if (arguments.length === 0) + this._events = {}; + else if (this._events[type]) + delete this._events[type]; + return this; + } + + // emit removeListener for all listeners on all events + if (arguments.length === 0) { + for (key in this._events) { + if (key === 'removeListener') continue; + this.removeAllListeners(key); + } + this.removeAllListeners('removeListener'); + this._events = {}; + return this; + } + + listeners = this._events[type]; + + if (isFunction(listeners)) { + this.removeListener(type, listeners); + } else if (listeners) { + // LIFO order + while (listeners.length) + this.removeListener(type, listeners[listeners.length - 1]); + } + delete this._events[type]; + + return this; + }; + + EventEmitter.prototype.listeners = function(type) { + var ret; + if (!this._events || !this._events[type]) + ret = []; + else if (isFunction(this._events[type])) + ret = [this._events[type]]; + else + ret = this._events[type].slice(); + return ret; + }; + + EventEmitter.prototype.listenerCount = function(type) { + if (this._events) { + var evlistener = this._events[type]; + + if (isFunction(evlistener)) + return 1; + else if (evlistener) + return evlistener.length; + } + return 0; + }; + + EventEmitter.listenerCount = function(emitter, type) { + return emitter.listenerCount(type); + }; + + function isFunction(arg) { + return typeof arg === 'function'; + } + + function isNumber(arg) { + return typeof arg === 'number'; + } + + function isObject(arg) { + return typeof arg === 'object' && arg !== null; + } + + function isUndefined(arg) { + return arg === void 0; + } + + },{}],2:[function(_dereq_,module,exports){ + var bundleFn = arguments[3]; + var sources = arguments[4]; + var cache = arguments[5]; + + var stringify = JSON.stringify; + + module.exports = function (fn, options) { + var wkey; + var cacheKeys = Object.keys(cache); + + for (var i = 0, l = cacheKeys.length; i < l; i++) { + var key = cacheKeys[i]; + var exp = cache[key].exports; + // Using babel as a transpiler to use esmodule, the export will always + // be an object with the default export as a property of it. To ensure + // the existing api and babel esmodule exports are both supported we + // check for both + if (exp === fn || exp && exp.default === fn) { + wkey = key; + break; + } + } + + if (!wkey) { + wkey = Math.floor(Math.pow(16, 8) * Math.random()).toString(16); + var wcache = {}; + for (var i = 0, l = cacheKeys.length; i < l; i++) { + var key = cacheKeys[i]; + wcache[key] = key; + } + sources[wkey] = [ + Function(['require','module','exports'], '(' + fn + ')(self)'), + wcache + ]; + } + var skey = Math.floor(Math.pow(16, 8) * Math.random()).toString(16); + + var scache = {}; scache[wkey] = wkey; + sources[skey] = [ + Function(['require'], ( + // try to call default if defined to also support babel esmodule + // exports + 'var f = require(' + stringify(wkey) + ');' + + '(f.default ? f.default : f)(self);' + )), + scache + ]; + + var workerSources = {}; + resolveSources(skey); + + function resolveSources(key) { + workerSources[key] = true; + + for (var depPath in sources[key][1]) { + var depKey = sources[key][1][depPath]; + if (!workerSources[depKey]) { + resolveSources(depKey); + } + } + } + + var src = '(' + bundleFn + ')({' + + Object.keys(workerSources).map(function (key) { + return stringify(key) + ':[' + + sources[key][0] + + ',' + stringify(sources[key][1]) + ']' + ; + }).join(',') + + '},{},[' + stringify(skey) + '])' + ; + + var URL = window.URL || window.webkitURL || window.mozURL || window.msURL; + + var blob = new Blob([src], { type: 'text/javascript' }); + if (options && options.bare) { return blob; } + var workerUrl = URL.createObjectURL(blob); + var worker = new Worker(workerUrl); + worker.objectURL = workerUrl; + return worker; + }; + + },{}],3:[function(_dereq_,module,exports){ + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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 _events = _dereq_(26); + + var _events2 = _interopRequireDefault(_events); + + var _eventHandler = _dereq_(25); + + var _eventHandler2 = _interopRequireDefault(_eventHandler); + + var _bufferHelper = _dereq_(28); + + var _bufferHelper2 = _interopRequireDefault(_bufferHelper); + + var _errors = _dereq_(24); + + var _logger = _dereq_(43); + + var _ewmaBandwidthEstimator = _dereq_(8); + + var _ewmaBandwidthEstimator2 = _interopRequireDefault(_ewmaBandwidthEstimator); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + 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; } /* + * simple ABR Controller + * - compute next level based on last fragment bw heuristics + * - implement an abandon rules triggered if we have less than 2 frag buffered and if computed bw shows that we risk buffer stalling + */ + + var AbrController = function (_EventHandler) { + _inherits(AbrController, _EventHandler); + + function AbrController(hls) { + _classCallCheck(this, AbrController); + + var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(AbrController).call(this, hls, _events2.default.FRAG_LOADING, _events2.default.FRAG_LOADED, _events2.default.FRAG_BUFFERED, _events2.default.ERROR)); + + _this.lastLoadedFragLevel = 0; + _this._autoLevelCapping = -1; + _this._nextAutoLevel = -1; + _this.hls = hls; + _this.onCheck = _this.abandonRulesCheck.bind(_this); + return _this; + } + + _createClass(AbrController, [{ + key: 'destroy', + value: function destroy() { + this.clearTimer(); + _eventHandler2.default.prototype.destroy.call(this); + } + }, { + key: 'onFragLoading', + value: function onFragLoading(data) { + var frag = data.frag; + if (frag.type === 'main') { + if (!this.timer) { + this.timer = setInterval(this.onCheck, 100); + } + // lazy init of bw Estimator, rationale is that we use different params for Live/VoD + // so we need to wait for stream manifest / playlist type to instantiate it. + if (!this.bwEstimator) { + var hls = this.hls, + level = data.frag.level, + isLive = hls.levels[level].details.live, + config = hls.config, + ewmaFast = void 0, + ewmaSlow = void 0; + + if (isLive) { + ewmaFast = config.abrEwmaFastLive; + ewmaSlow = config.abrEwmaSlowLive; + } else { + ewmaFast = config.abrEwmaFastVoD; + ewmaSlow = config.abrEwmaSlowVoD; + } + this.bwEstimator = new _ewmaBandwidthEstimator2.default(hls, ewmaSlow, ewmaFast, config.abrEwmaDefaultEstimate); + } + this.fragCurrent = frag; + } + } + }, { + key: 'abandonRulesCheck', + value: function abandonRulesCheck() { + /* + monitor fragment retrieval time... + we compute expected time of arrival of the complete fragment. + we compare it to expected time of buffer starvation + */ + var hls = this.hls, + v = hls.media, + frag = this.fragCurrent, + loader = frag.loader; + + // if loader has been destroyed or loading has been aborted, stop timer and return + if (!loader || loader.stats && loader.stats.aborted) { + _logger.logger.warn('frag loader destroy or aborted, disarm abandonRules'); + this.clearTimer(); + return; + } + var stats = loader.stats; + /* only monitor frag retrieval time if + (video not paused OR first fragment being loaded(ready state === HAVE_NOTHING = 0)) AND autoswitching enabled AND not lowest level (=> means that we have several levels) */ + if (v && (!v.paused && v.playbackRate !== 0 || !v.readyState) && frag.autoLevel && frag.level) { + var requestDelay = performance.now() - stats.trequest, + playbackRate = Math.abs(v.playbackRate); + // monitor fragment load progress after half of expected fragment duration,to stabilize bitrate + if (requestDelay > 500 * frag.duration / playbackRate) { + var levels = hls.levels, + loadRate = Math.max(1, stats.bw ? stats.bw / 8 : stats.loaded * 1000 / requestDelay), + // byte/s; at least 1 byte/s to avoid division by zero + // compute expected fragment length using frag duration and level bitrate. also ensure that expected len is gte than already loaded size + expectedLen = stats.total ? stats.total : Math.max(stats.loaded, Math.round(frag.duration * levels[frag.level].bitrate / 8)), + pos = v.currentTime, + fragLoadedDelay = (expectedLen - stats.loaded) / loadRate, + bufferStarvationDelay = (_bufferHelper2.default.bufferInfo(v, pos, hls.config.maxBufferHole).end - pos) / playbackRate; + // consider emergency switch down only if we have less than 2 frag buffered AND + // time to finish loading current fragment is bigger than buffer starvation delay + // ie if we risk buffer starvation if bw does not increase quickly + if (bufferStarvationDelay < 2 * frag.duration / playbackRate && fragLoadedDelay > bufferStarvationDelay) { + var fragLevelNextLoadedDelay = void 0, + nextLoadLevel = void 0; + // lets iterate through lower level and try to find the biggest one that could avoid rebuffering + // we start from current level - 1 and we step down , until we find a matching level + for (nextLoadLevel = frag.level - 1; nextLoadLevel >= 0; nextLoadLevel--) { + // compute time to load next fragment at lower level + // 0.8 : consider only 80% of current bw to be conservative + // 8 = bits per byte (bps/Bps) + fragLevelNextLoadedDelay = frag.duration * levels[nextLoadLevel].bitrate / (8 * 0.8 * loadRate); + if (fragLevelNextLoadedDelay < bufferStarvationDelay) { + // we found a lower level that be rebuffering free with current estimated bw ! + break; + } + } + // only emergency switch down if it takes less time to load new fragment at lowest level instead + // of finishing loading current one ... + if (fragLevelNextLoadedDelay < fragLoadedDelay) { + // ensure nextLoadLevel is not negative + nextLoadLevel = Math.max(0, nextLoadLevel); + _logger.logger.warn('loading too slow, abort fragment loading and switch to level ' + nextLoadLevel + ':fragLoadedDelay[' + nextLoadLevel + ']<fragLoadedDelay[' + (frag.level - 1) + '];bufferStarvationDelay:' + fragLevelNextLoadedDelay.toFixed(1) + '<' + fragLoadedDelay.toFixed(1) + ':' + bufferStarvationDelay.toFixed(1)); + // force next load level in auto mode + hls.nextLoadLevel = nextLoadLevel; + // update bw estimate for this fragment before cancelling load (this will help reducing the bw) + this.bwEstimator.sample(requestDelay, stats.loaded); + //abort fragment loading + loader.abort(); + // stop abandon rules timer + this.clearTimer(); + hls.trigger(_events2.default.FRAG_LOAD_EMERGENCY_ABORTED, { frag: frag, stats: stats }); + } + } + } + } + } + }, { + key: 'onFragLoaded', + value: function onFragLoaded(data) { + var frag = data.frag; + if (frag.type === 'main') { + // stop monitoring bw once frag loaded + this.clearTimer(); + // store level id after successful fragment load + this.lastLoadedFragLevel = frag.level; + // reset forced auto level value so that next level will be selected + this._nextAutoLevel = -1; + // if fragment has been loaded to perform a bitrate test, + if (data.frag.bitrateTest) { + var stats = data.stats; + stats.tparsed = stats.tbuffered = stats.tload; + this.onFragBuffered(data); + } + } + } + }, { + key: 'onFragBuffered', + value: function onFragBuffered(data) { + var stats = data.stats, + frag = data.frag; + // only update stats on first frag buffering + // if same frag is loaded multiple times, it might be in browser cache, and loaded quickly + // and leading to wrong bw estimation + // on bitrate test, also only update stats once (if tload = tbuffered == on FRAG_LOADED) + if (stats.aborted !== true && frag.loadCounter === 1 && frag.type === 'main' && (!frag.bitrateTest || stats.tload === stats.tbuffered)) { + var fragLoadingProcessingMs = stats.tbuffered - stats.trequest; + _logger.logger.log('latency/loading/parsing/append/kbps:' + Math.round(stats.tfirst - stats.trequest) + '/' + Math.round(stats.tload - stats.tfirst) + '/' + Math.round(stats.tparsed - stats.tload) + '/' + Math.round(stats.tbuffered - stats.tparsed) + '/' + Math.round(8 * stats.loaded / (stats.tbuffered - stats.trequest))); + this.bwEstimator.sample(fragLoadingProcessingMs, stats.loaded); + // if fragment has been loaded to perform a bitrate test, (hls.startLevel = -1), store bitrate test delay duration + if (frag.bitrateTest) { + this.bitrateTestDelay = fragLoadingProcessingMs / 1000; + } else { + this.bitrateTestDelay = 0; + } + } + } + }, { + key: 'onError', + value: function onError(data) { + // stop timer in case of frag loading error + switch (data.details) { + case _errors.ErrorDetails.FRAG_LOAD_ERROR: + case _errors.ErrorDetails.FRAG_LOAD_TIMEOUT: + this.clearTimer(); + break; + default: + break; + } + } + }, { + key: 'clearTimer', + value: function clearTimer() { + if (this.timer) { + clearInterval(this.timer); + this.timer = null; + } + } + + /** Return the capping/max level value that could be used by automatic level selection algorithm **/ + + }, { + key: 'findBestLevel', + value: function findBestLevel(currentLevel, currentFragDuration, currentBw, minAutoLevel, maxAutoLevel, maxFetchDuration, bwFactor, bwUpFactor, levels) { + for (var i = maxAutoLevel; i >= minAutoLevel; i--) { + var levelInfo = levels[i], + levelDetails = levelInfo.details, + avgDuration = levelDetails ? levelDetails.totalduration / levelDetails.fragments.length : currentFragDuration, + adjustedbw = void 0; + // follow algorithm captured from stagefright : + // https://android.googlesource.com/platform/frameworks/av/+/master/media/libstagefright/httplive/LiveSession.cpp + // Pick the highest bandwidth stream below or equal to estimated bandwidth. + // consider only 80% of the available bandwidth, but if we are switching up, + // be even more conservative (70%) to avoid overestimating and immediately + // switching back. + if (i <= currentLevel) { + adjustedbw = bwFactor * currentBw; + } else { + adjustedbw = bwUpFactor * currentBw; + } + var bitrate = levels[i].bitrate, + fetchDuration = bitrate * avgDuration / adjustedbw; + + _logger.logger.trace('level/adjustedbw/bitrate/avgDuration/maxFetchDuration/fetchDuration: ' + i + '/' + Math.round(adjustedbw) + '/' + bitrate + '/' + avgDuration + '/' + maxFetchDuration + '/' + fetchDuration); + // if adjusted bw is greater than level bitrate AND + if (adjustedbw > bitrate && ( + // fragment fetchDuration unknown or fragment fetchDuration less than max allowed fetch duration, then this level matches + !fetchDuration || fetchDuration < maxFetchDuration)) { + // as we are looping from highest to lowest, this will return the best achievable quality level + + return i; + } + } + // not enough time budget even with quality level 0 ... rebuffering might happen + return -1; + } + }, { + key: 'autoLevelCapping', + get: function get() { + return this._autoLevelCapping; + } + + /** set the capping/max level value that could be used by automatic level selection algorithm **/ + , + set: function set(newLevel) { + this._autoLevelCapping = newLevel; + } + }, { + key: 'nextAutoLevel', + get: function get() { + var nextAutoLevel = this._nextAutoLevel, + bwEstimator = this.bwEstimator, + hls = this.hls, + levels = hls.levels, + minAutoBitrate = hls.config.minAutoBitrate; + // in case next auto level has been forced, and bw not available or not reliable + if (nextAutoLevel !== -1 && (!bwEstimator || !bwEstimator.canEstimate())) { + // cap next auto level by max auto level + return Math.min(nextAutoLevel, this.maxAutoLevel); + } + // compute next level using ABR logic + var nextABRAutoLevel = this.nextABRAutoLevel; + if (nextAutoLevel !== -1) { + // nextAutoLevel is defined, use it to cap ABR computed quality level + nextABRAutoLevel = Math.min(nextAutoLevel, nextABRAutoLevel); + } + if (minAutoBitrate !== undefined) { + while (levels[nextABRAutoLevel].bitrate < minAutoBitrate) { + nextABRAutoLevel++; + } + } + return nextABRAutoLevel; + }, + set: function set(nextLevel) { + this._nextAutoLevel = nextLevel; + } + }, { + key: 'minAutoLevel', + get: function get() { + var hls = this.hls, + levels = hls.levels, + minAutoBitrate = hls.config.minAutoBitrate; + for (var i = 0; i < levels.length; i++) { + if (levels[i].bitrate > minAutoBitrate) { + return i; + } + } + return 0; + } + }, { + key: 'maxAutoLevel', + get: function get() { + var levels = this.hls.levels, + autoLevelCapping = this._autoLevelCapping, + maxAutoLevel; + if (autoLevelCapping === -1 && levels && levels.length) { + maxAutoLevel = levels.length - 1; + } else { + maxAutoLevel = autoLevelCapping; + } + return maxAutoLevel; + } + }, { + key: 'nextABRAutoLevel', + get: function get() { + var hls = this.hls, + maxAutoLevel = this.maxAutoLevel, + levels = hls.levels, + config = hls.config, + minAutoLevel = this.minAutoLevel; + var v = hls.media, + currentLevel = this.lastLoadedFragLevel, + currentFragDuration = this.fragCurrent ? this.fragCurrent.duration : 0, + pos = v ? v.currentTime : 0, + + // playbackRate is the absolute value of the playback rate; if v.playbackRate is 0, we use 1 to load as + // if we're playing back at the normal rate. + playbackRate = v && v.playbackRate !== 0 ? Math.abs(v.playbackRate) : 1.0, + avgbw = this.bwEstimator ? this.bwEstimator.getEstimate() : config.abrEwmaDefaultEstimate, + + // bufferStarvationDelay is the wall-clock time left until the playback buffer is exhausted. + bufferStarvationDelay = (_bufferHelper2.default.bufferInfo(v, pos, config.maxBufferHole).end - pos) / playbackRate; + + // First, look to see if we can find a level matching with our avg bandwidth AND that could also guarantee no rebuffering at all + var bestLevel = this.findBestLevel(currentLevel, currentFragDuration, avgbw, minAutoLevel, maxAutoLevel, bufferStarvationDelay, config.abrBandWidthFactor, config.abrBandWidthUpFactor, levels); + if (bestLevel >= 0) { + return bestLevel; + } else { + _logger.logger.trace('rebuffering expected to happen, lets try to find a quality level minimizing the rebuffering'); + // not possible to get rid of rebuffering ... let's try to find level that will guarantee less than maxStarvationDelay of rebuffering + // if no matching level found, logic will return 0 + var maxStarvationDelay = config.maxStarvationDelay, + bwFactor = config.abrBandWidthFactor, + bwUpFactor = config.abrBandWidthUpFactor; + if (bufferStarvationDelay === 0) { + // in case buffer is empty, let's check if previous fragment was loaded to perform a bitrate test + var bitrateTestDelay = this.bitrateTestDelay; + if (bitrateTestDelay) { + // if it is the case, then we need to adjust our max starvation delay using maxLoadingDelay config value + // max video loading delay used in automatic start level selection : + // in that mode ABR controller will ensure that video loading time (ie the time to fetch the first fragment at lowest quality level + + // the time to fetch the fragment at the appropriate quality level is less than ```maxLoadingDelay``` ) + maxStarvationDelay = config.maxLoadingDelay - bitrateTestDelay; + _logger.logger.trace('bitrate test took ' + Math.round(1000 * bitrateTestDelay) + 'ms, set first fragment max fetchDuration to ' + Math.round(1000 * maxStarvationDelay) + ' ms'); + // don't use conservative factor on bitrate test + bwFactor = bwUpFactor = 1; + } + } + bestLevel = this.findBestLevel(currentLevel, currentFragDuration, avgbw, minAutoLevel, maxAutoLevel, bufferStarvationDelay + maxStarvationDelay, bwFactor, bwUpFactor, levels); + return Math.max(bestLevel, 0); + } + } + }]); + + return AbrController; + }(_eventHandler2.default); + + exports.default = AbrController; + + },{"24":24,"25":25,"26":26,"28":28,"43":43,"8":8}],4:[function(_dereq_,module,exports){ + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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 _binarySearch = _dereq_(39); + + var _binarySearch2 = _interopRequireDefault(_binarySearch); + + var _bufferHelper = _dereq_(28); + + var _bufferHelper2 = _interopRequireDefault(_bufferHelper); + + var _demuxer = _dereq_(20); + + var _demuxer2 = _interopRequireDefault(_demuxer); + + var _events = _dereq_(26); + + var _events2 = _interopRequireDefault(_events); + + var _eventHandler = _dereq_(25); + + var _eventHandler2 = _interopRequireDefault(_eventHandler); + + var _levelHelper = _dereq_(29); + + var _levelHelper2 = _interopRequireDefault(_levelHelper); + + var _timeRanges = _dereq_(45); + + var _timeRanges2 = _interopRequireDefault(_timeRanges); + + var _errors = _dereq_(24); + + var _logger = _dereq_(43); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + 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; } /* + * Audio Stream Controller + */ + + var State = { + STOPPED: 'STOPPED', + STARTING: 'STARTING', + IDLE: 'IDLE', + PAUSED: 'PAUSED', + KEY_LOADING: 'KEY_LOADING', + FRAG_LOADING: 'FRAG_LOADING', + FRAG_LOADING_WAITING_RETRY: 'FRAG_LOADING_WAITING_RETRY', + WAITING_TRACK: 'WAITING_TRACK', + PARSING: 'PARSING', + PARSED: 'PARSED', + ENDED: 'ENDED', + ERROR: 'ERROR' + }; + + var AudioStreamController = function (_EventHandler) { + _inherits(AudioStreamController, _EventHandler); + + function AudioStreamController(hls) { + _classCallCheck(this, AudioStreamController); + + var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(AudioStreamController).call(this, hls, _events2.default.MEDIA_ATTACHED, _events2.default.MEDIA_DETACHING, _events2.default.AUDIO_TRACKS_UPDATED, _events2.default.AUDIO_TRACK_SWITCH, _events2.default.AUDIO_TRACK_LOADED, _events2.default.KEY_LOADED, _events2.default.FRAG_LOADED, _events2.default.FRAG_PARSING_INIT_SEGMENT, _events2.default.FRAG_PARSING_DATA, _events2.default.FRAG_PARSED, _events2.default.ERROR, _events2.default.BUFFER_CREATED, _events2.default.BUFFER_APPENDED, _events2.default.BUFFER_FLUSHED)); + + _this.config = hls.config; + _this.audioCodecSwap = false; + _this.ticks = 0; + _this.ontick = _this.tick.bind(_this); + return _this; + } + + _createClass(AudioStreamController, [{ + key: 'destroy', + value: function destroy() { + this.stopLoad(); + if (this.timer) { + clearInterval(this.timer); + this.timer = null; + } + _eventHandler2.default.prototype.destroy.call(this); + this.state = State.STOPPED; + } + }, { + key: 'startLoad', + value: function startLoad(startPosition) { + if (this.tracks) { + var media = this.media, + lastCurrentTime = this.lastCurrentTime; + this.stopLoad(); + if (!this.timer) { + this.timer = setInterval(this.ontick, 100); + } + this.fragLoadError = 0; + if (media && lastCurrentTime) { + _logger.logger.log('configure startPosition @' + lastCurrentTime); + this.state = State.IDLE; + } else { + this.lastCurrentTime = this.startPosition ? this.startPosition : startPosition; + this.state = State.STARTING; + } + this.nextLoadPosition = this.startPosition = this.lastCurrentTime; + this.tick(); + } else { + this.startPosition = startPosition; + this.state = State.STOPPED; + } + } + }, { + key: 'stopLoad', + value: function stopLoad() { + var frag = this.fragCurrent; + if (frag) { + if (frag.loader) { + frag.loader.abort(); + } + this.fragCurrent = null; + } + this.fragPrevious = null; + if (this.demuxer) { + this.demuxer.destroy(); + this.demuxer = null; + } + this.state = State.STOPPED; + } + }, { + key: 'tick', + value: function tick() { + this.ticks++; + if (this.ticks === 1) { + this.doTick(); + if (this.ticks > 1) { + setTimeout(this.tick, 1); + } + this.ticks = 0; + } + } + }, { + key: 'doTick', + value: function doTick() { + var pos, + track, + trackDetails, + hls = this.hls, + config = hls.config; + //logger.log('audioStream:' + this.state); + switch (this.state) { + case State.ERROR: + //don't do anything in error state to avoid breaking further ... + case State.PAUSED: + //don't do anything in paused state either ... + break; + case State.STARTING: + this.state = State.WAITING_TRACK; + this.loadedmetadata = false; + break; + case State.IDLE: + // if video not attached AND + // start fragment already requested OR start frag prefetch disable + // exit loop + // => if media not attached but start frag prefetch is enabled and start frag not requested yet, we will not exit loop + if (!this.media && (this.startFragRequested || !config.startFragPrefetch)) { + break; + } + // determine next candidate fragment to be loaded, based on current position and + // end of buffer position + // if we have not yet loaded any fragment, start loading from start position + if (this.loadedmetadata) { + pos = this.media.currentTime; + } else { + pos = this.nextLoadPosition; + } + var media = this.mediaBuffer ? this.mediaBuffer : this.media; + var bufferInfo = _bufferHelper2.default.bufferInfo(media, pos, config.maxBufferHole), + bufferLen = bufferInfo.len, + bufferEnd = bufferInfo.end, + fragPrevious = this.fragPrevious, + maxBufLen = config.maxMaxBufferLength; + + // if buffer length is less than maxBufLen try to load a new fragment + if (bufferLen < maxBufLen && this.trackId < this.tracks.length) { + trackDetails = this.tracks[this.trackId].details; + // if track info not retrieved yet, switch state and wait for track retrieval + if (typeof trackDetails === 'undefined') { + this.state = State.WAITING_TRACK; + break; + } + + // we just got done loading the final fragment, check if we need to finalize media stream + if (!trackDetails.live && fragPrevious && fragPrevious.sn === trackDetails.endSN) { + // if we are not seeking or if we are seeking but everything (almost) til the end is buffered, let's signal eos + // we don't compare exactly media.duration === bufferInfo.end as there could be some subtle media duration difference when switching + // between different renditions. using half frag duration should help cope with these cases. + if (!this.media.seeking || this.media.duration - bufferEnd < fragPrevious.duration / 2) { + // Finalize the media stream + this.hls.trigger(_events2.default.BUFFER_EOS, { type: 'audio' }); + this.state = State.ENDED; + break; + } + } + + // find fragment index, contiguous with end of buffer position + var fragments = trackDetails.fragments, + fragLen = fragments.length, + start = fragments[0].start, + end = fragments[fragLen - 1].start + fragments[fragLen - 1].duration, + frag = void 0; + + // if bufferEnd before start of playlist, load first fragment + if (bufferEnd < start) { + frag = fragments[0]; + } else { + (function () { + var foundFrag = void 0; + var maxFragLookUpTolerance = config.maxFragLookUpTolerance; + if (bufferEnd < end) { + if (bufferEnd > end - maxFragLookUpTolerance) { + maxFragLookUpTolerance = 0; + } + foundFrag = _binarySearch2.default.search(fragments, function (candidate) { + // offset should be within fragment boundary - config.maxFragLookUpTolerance + // this is to cope with situations like + // bufferEnd = 9.991 + // frag[Ø] : [0,10] + // frag[1] : [10,20] + // bufferEnd is within frag[0] range ... although what we are expecting is to return frag[1] here + // frag start frag start+duration + // |-----------------------------| + // <---> <---> + // ...--------><-----------------------------><---------.... + // previous frag matching fragment next frag + // return -1 return 0 return 1 + //logger.log(`level/sn/start/end/bufEnd:${level}/${candidate.sn}/${candidate.start}/${(candidate.start+candidate.duration)}/${bufferEnd}`); + if (candidate.start + candidate.duration - maxFragLookUpTolerance <= bufferEnd) { + return 1; + } else if (candidate.start - maxFragLookUpTolerance > bufferEnd) { + return -1; + } + return 0; + }); + } else { + // reach end of playlist + foundFrag = fragments[fragLen - 1]; + } + if (foundFrag) { + frag = foundFrag; + start = foundFrag.start; + //logger.log('find SN matching with pos:' + bufferEnd + ':' + frag.sn); + if (fragPrevious && frag.level === fragPrevious.level && frag.sn === fragPrevious.sn) { + if (frag.sn < trackDetails.endSN) { + frag = fragments[frag.sn + 1 - trackDetails.startSN]; + _logger.logger.log('SN just loaded, load next one: ' + frag.sn); + } else { + frag = null; + } + } + } + })(); + } + if (frag) { + //logger.log(' loading frag ' + i +',pos/bufEnd:' + pos.toFixed(3) + '/' + bufferEnd.toFixed(3)); + if (frag.decryptdata.uri != null && frag.decryptdata.key == null) { + _logger.logger.log('Loading key for ' + frag.sn + ' of [' + trackDetails.startSN + ' ,' + trackDetails.endSN + '],track ' + this.trackId); + this.state = State.KEY_LOADING; + hls.trigger(_events2.default.KEY_LOADING, { frag: frag }); + } else { + _logger.logger.log('Loading ' + frag.sn + ' of [' + trackDetails.startSN + ' ,' + trackDetails.endSN + '],track ' + this.trackId + ', currentTime:' + pos + ',bufferEnd:' + bufferEnd.toFixed(3)); + // ensure that we are not reloading the same fragments in loop ... + if (this.fragLoadIdx !== undefined) { + this.fragLoadIdx++; + } else { + this.fragLoadIdx = 0; + } + if (frag.loadCounter) { + frag.loadCounter++; + var maxThreshold = config.fragLoadingLoopThreshold; + // if this frag has already been loaded 3 times, and if it has been reloaded recently + if (frag.loadCounter > maxThreshold && Math.abs(this.fragLoadIdx - frag.loadIdx) < maxThreshold) { + hls.trigger(_events2.default.ERROR, { type: _errors.ErrorTypes.MEDIA_ERROR, details: _errors.ErrorDetails.FRAG_LOOP_LOADING_ERROR, fatal: false, frag: frag }); + return; + } + } else { + frag.loadCounter = 1; + } + frag.loadIdx = this.fragLoadIdx; + this.fragCurrent = frag; + this.startFragRequested = true; + hls.trigger(_events2.default.FRAG_LOADING, { frag: frag }); + this.state = State.FRAG_LOADING; + } + } + } + break; + case State.WAITING_TRACK: + track = this.tracks[this.trackId]; + // check if playlist is already loaded + if (track && track.details) { + this.state = State.IDLE; + } + break; + case State.FRAG_LOADING_WAITING_RETRY: + var now = performance.now(); + var retryDate = this.retryDate; + media = this.media; + var isSeeking = media && media.seeking; + // if current time is gt than retryDate, or if media seeking let's switch to IDLE state to retry loading + if (!retryDate || now >= retryDate || isSeeking) { + _logger.logger.log('audioStreamController: retryDate reached, switch back to IDLE state'); + this.state = State.IDLE; + } + break; + case State.STOPPED: + case State.FRAG_LOADING: + case State.PARSING: + case State.PARSED: + case State.ENDED: + break; + default: + break; + } + } + }, { + key: 'onMediaAttached', + value: function onMediaAttached(data) { + var media = this.media = this.mediaBuffer = data.media; + this.onvseeking = this.onMediaSeeking.bind(this); + this.onvended = this.onMediaEnded.bind(this); + media.addEventListener('seeking', this.onvseeking); + media.addEventListener('ended', this.onvended); + var config = this.config; + if (this.tracks && config.autoStartLoad) { + this.startLoad(config.startPosition); + } + } + }, { + key: 'onMediaDetaching', + value: function onMediaDetaching() { + var media = this.media; + if (media && media.ended) { + _logger.logger.log('MSE detaching and video ended, reset startPosition'); + this.startPosition = this.lastCurrentTime = 0; + } + + // reset fragment loading counter on MSE detaching to avoid reporting FRAG_LOOP_LOADING_ERROR after error recovery + var tracks = this.tracks; + if (tracks) { + // reset fragment load counter + tracks.forEach(function (track) { + if (track.details) { + track.details.fragments.forEach(function (fragment) { + fragment.loadCounter = undefined; + }); + } + }); + } + // remove video listeners + if (media) { + media.removeEventListener('seeking', this.onvseeking); + media.removeEventListener('ended', this.onvended); + this.onvseeking = this.onvseeked = this.onvended = null; + } + this.media = null; + this.loadedmetadata = false; + this.stopLoad(); + } + }, { + key: 'onMediaSeeking', + value: function onMediaSeeking() { + if (this.state === State.ENDED) { + // switch to IDLE state to check for potential new fragment + this.state = State.IDLE; + } + if (this.media) { + this.lastCurrentTime = this.media.currentTime; + } + // avoid reporting fragment loop loading error in case user is seeking several times on same position + if (this.fragLoadIdx !== undefined) { + this.fragLoadIdx += 2 * this.config.fragLoadingLoopThreshold; + } + // tick to speed up processing + this.tick(); + } + }, { + key: 'onMediaEnded', + value: function onMediaEnded() { + // reset startPosition and lastCurrentTime to restart playback @ stream beginning + this.startPosition = this.lastCurrentTime = 0; + } + }, { + key: 'onAudioTracksUpdated', + value: function onAudioTracksUpdated(data) { + _logger.logger.log('audio tracks updated'); + this.tracks = data.audioTracks; + } + }, { + key: 'onAudioTrackSwitch', + value: function onAudioTrackSwitch(data) { + // if any URL found on new audio track, it is an alternate audio track + var altAudio = !!data.url; + this.trackId = data.id; + this.state = State.IDLE; + + this.fragCurrent = null; + this.state = State.PAUSED; + // destroy useless demuxer when switching audio to main + if (!altAudio) { + if (this.demuxer) { + this.demuxer.destroy(); + this.demuxer = null; + } + } else { + // switching to audio track, start timer if not already started + if (!this.timer) { + this.timer = setInterval(this.ontick, 100); + } + } + // flush audio source buffer + this.hls.trigger(_events2.default.BUFFER_FLUSHING, { startOffset: 0, endOffset: Number.POSITIVE_INFINITY, type: 'audio' }); + this.tick(); + } + }, { + key: 'onAudioTrackLoaded', + value: function onAudioTrackLoaded(data) { + var details = data.details, + trackId = data.id, + track = this.tracks[trackId], + duration = details.totalduration; + + _logger.logger.log('track ' + trackId + ' loaded [' + details.startSN + ',' + details.endSN + '],duration:' + duration); + details.PTSKnown = false; + track.details = details; + + // compute start position + if (!this.startFragRequested) { + // compute start position if set to -1. use it straight away if value is defined + if (this.startPosition === -1) { + // first, check if start time offset has been set in playlist, if yes, use this value + var startTimeOffset = details.startTimeOffset; + if (!isNaN(startTimeOffset)) { + _logger.logger.log('start time offset found in playlist, adjust startPosition to ' + startTimeOffset); + this.startPosition = startTimeOffset; + } else { + this.startPosition = 0; + } + } + this.nextLoadPosition = this.startPosition; + } + // only switch batck to IDLE state if we were waiting for track to start downloading a new fragment + if (this.state === State.WAITING_TRACK) { + this.state = State.IDLE; + } + //trigger handler right now + this.tick(); + } + }, { + key: 'onKeyLoaded', + value: function onKeyLoaded() { + if (this.state === State.KEY_LOADING) { + this.state = State.IDLE; + this.tick(); + } + } + }, { + key: 'onFragLoaded', + value: function onFragLoaded(data) { + var fragCurrent = this.fragCurrent; + if (this.state === State.FRAG_LOADING && fragCurrent && data.frag.type === 'audio' && data.frag.level === fragCurrent.level && data.frag.sn === fragCurrent.sn) { + this.state = State.PARSING; + // transmux the MPEG-TS data to ISO-BMFF segments + this.stats = data.stats; + var track = this.tracks[this.trackId], + details = track.details, + duration = details.totalduration, + start = fragCurrent.start, + trackId = fragCurrent.level, + sn = fragCurrent.sn, + audioCodec = this.config.defaultAudioCodec || track.audioCodec; + this.pendingAppending = 0; + if (!this.demuxer) { + this.demuxer = new _demuxer2.default(this.hls, 'audio'); + } + _logger.logger.log('Demuxing ' + sn + ' of [' + details.startSN + ' ,' + details.endSN + '],track ' + trackId); + // time Offset is accurate if level PTS is known, or if playlist is not sliding (not live) + var accurateTimeOffset = details.PTSKnown || !details.live; + this.demuxer.push(data.payload, audioCodec, null, start, fragCurrent.cc, trackId, sn, duration, fragCurrent.decryptdata, accurateTimeOffset); + } + this.fragLoadError = 0; + } + }, { + key: 'onFragParsingInitSegment', + value: function onFragParsingInitSegment(data) { + var fragCurrent = this.fragCurrent; + if (fragCurrent && data.id === 'audio' && data.sn === fragCurrent.sn && data.level === fragCurrent.level && this.state === State.PARSING) { + var tracks = data.tracks, + track = void 0; + + // include levelCodec in audio and video tracks + track = tracks.audio; + if (track) { + track.levelCodec = 'mp4a.40.2'; + track.id = data.id; + this.hls.trigger(_events2.default.BUFFER_CODECS, tracks); + _logger.logger.log('audio track:audio,container:' + track.container + ',codecs[level/parsed]=[' + track.levelCodec + '/' + track.codec + ']'); + var initSegment = track.initSegment; + if (initSegment) { + this.pendingAppending++; + this.hls.trigger(_events2.default.BUFFER_APPENDING, { type: 'audio', data: initSegment, parent: 'audio', content: 'initSegment' }); + } + //trigger handler right now + this.tick(); + } + } + } + }, { + key: 'onFragParsingData', + value: function onFragParsingData(data) { + var _this2 = this; + + var fragCurrent = this.fragCurrent; + if (fragCurrent && data.id === 'audio' && data.sn === fragCurrent.sn && data.level === fragCurrent.level && this.state === State.PARSING) { + var track = this.tracks[this.trackId], + frag = this.fragCurrent; + + _logger.logger.log('parsed ' + data.type + ',PTS:[' + data.startPTS.toFixed(3) + ',' + data.endPTS.toFixed(3) + '],DTS:[' + data.startDTS.toFixed(3) + '/' + data.endDTS.toFixed(3) + '],nb:' + data.nb); + _levelHelper2.default.updateFragPTSDTS(track.details, frag.sn, data.startPTS, data.endPTS); + + [data.data1, data.data2].forEach(function (buffer) { + if (buffer) { + _this2.pendingAppending++; + _this2.hls.trigger(_events2.default.BUFFER_APPENDING, { type: data.type, data: buffer, parent: 'audio', content: 'data' }); + } + }); + this.nextLoadPosition = data.endPTS; + //trigger handler right now + this.tick(); + } + } + }, { + key: 'onFragParsed', + value: function onFragParsed(data) { + var fragCurrent = this.fragCurrent; + if (fragCurrent && data.id === 'audio' && data.sn === fragCurrent.sn && data.level === fragCurrent.level && this.state === State.PARSING) { + this.stats.tparsed = performance.now(); + this.state = State.PARSED; + this._checkAppendedParsed(); + } + } + }, { + key: 'onBufferCreated', + value: function onBufferCreated(data) { + var audioTrack = data.tracks.audio; + if (audioTrack) { + this.mediaBuffer = audioTrack.buffer; + this.loadedmetadata = true; + } + } + }, { + key: 'onBufferAppended', + value: function onBufferAppended(data) { + if (data.parent === 'audio') { + switch (this.state) { + case State.PARSING: + case State.PARSED: + this.pendingAppending--; + this._checkAppendedParsed(); + break; + default: + break; + } + } + } + }, { + key: '_checkAppendedParsed', + value: function _checkAppendedParsed() { + //trigger handler right now + if (this.state === State.PARSED && this.pendingAppending === 0) { + var frag = this.fragCurrent, + stats = this.stats; + if (frag) { + this.fragPrevious = frag; + stats.tbuffered = performance.now(); + this.hls.trigger(_events2.default.FRAG_BUFFERED, { stats: stats, frag: frag, id: 'audio' }); + var media = this.mediaBuffer ? this.mediaBuffer : this.media; + _logger.logger.log('audio buffered : ' + _timeRanges2.default.toString(media.buffered)); + this.state = State.IDLE; + } + this.tick(); + } + } + }, { + key: 'onError', + value: function onError(data) { + var frag = data.frag; + // don't handle frag error not related to audio fragment + if (frag && frag.type !== 'audio') { + return; + } + switch (data.details) { + case _errors.ErrorDetails.FRAG_LOAD_ERROR: + case _errors.ErrorDetails.FRAG_LOAD_TIMEOUT: + if (!data.fatal) { + var loadError = this.fragLoadError; + if (loadError) { + loadError++; + } else { + loadError = 1; + } + var config = this.config; + if (loadError <= config.fragLoadingMaxRetry) { + this.fragLoadError = loadError; + // reset load counter to avoid frag loop loading error + frag.loadCounter = 0; + // exponential backoff capped to config.fragLoadingMaxRetryTimeout + var delay = Math.min(Math.pow(2, loadError - 1) * config.fragLoadingRetryDelay, config.fragLoadingMaxRetryTimeout); + _logger.logger.warn('audioStreamController: frag loading failed, retry in ' + delay + ' ms'); + this.retryDate = performance.now() + delay; + // retry loading state + this.state = State.FRAG_LOADING_WAITING_RETRY; + } else { + _logger.logger.error('audioStreamController: ' + data.details + ' reaches max retry, redispatch as fatal ...'); + // redispatch same error but with fatal set to true + data.fatal = true; + this.hls.trigger(_events2.default.ERROR, data); + this.state = State.ERROR; + } + } + break; + case _errors.ErrorDetails.FRAG_LOOP_LOADING_ERROR: + case _errors.ErrorDetails.AUDIO_TRACK_LOAD_ERROR: + case _errors.ErrorDetails.AUDIO_TRACK_LOAD_TIMEOUT: + case _errors.ErrorDetails.KEY_LOAD_ERROR: + case _errors.ErrorDetails.KEY_LOAD_TIMEOUT: + // when in ERROR state, don't switch back to IDLE state in case a non-fatal error is received + if (this.state !== State.ERROR) { + // if fatal error, stop processing, otherwise move to IDLE to retry loading + this.state = data.fatal ? State.ERROR : State.IDLE; + _logger.logger.warn('audioStreamController: ' + data.details + ' while loading frag,switch to ' + this.state + ' state ...'); + } + break; + default: + break; + } + } + }, { + key: 'onBufferFlushed', + value: function onBufferFlushed() { + // increase fragment load Index to avoid frag loop loading error after buffer flush + this.fragLoadIdx += 2 * this.config.fragLoadingLoopThreshold; + // move to IDLE once flush complete. this should trigger new fragment loading + this.state = State.IDLE; + // reset reference to frag + this.fragPrevious = null; + this.tick(); + } + }]); + + return AudioStreamController; + }(_eventHandler2.default); + + exports.default = AudioStreamController; + + },{"20":20,"24":24,"25":25,"26":26,"28":28,"29":29,"39":39,"43":43,"45":45}],5:[function(_dereq_,module,exports){ + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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 _events = _dereq_(26); + + var _events2 = _interopRequireDefault(_events); + + var _eventHandler = _dereq_(25); + + var _eventHandler2 = _interopRequireDefault(_eventHandler); + + var _logger = _dereq_(43); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + 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; } /* + * audio track controller + */ + + var AudioTrackController = function (_EventHandler) { + _inherits(AudioTrackController, _EventHandler); + + function AudioTrackController(hls) { + _classCallCheck(this, AudioTrackController); + + return _possibleConstructorReturn(this, Object.getPrototypeOf(AudioTrackController).call(this, hls, _events2.default.MANIFEST_LOADING, _events2.default.MANIFEST_LOADED, _events2.default.AUDIO_TRACK_LOADED)); + } + + _createClass(AudioTrackController, [{ + key: 'destroy', + value: function destroy() { + _eventHandler2.default.prototype.destroy.call(this); + } + }, { + key: 'onManifestLoading', + value: function onManifestLoading() { + // reset audio tracks on manifest loading + this.tracks = []; + this.trackId = -1; + } + }, { + key: 'onManifestLoaded', + value: function onManifestLoaded(data) { + var _this2 = this; + + var tracks = data.audioTracks || []; + var defaultFound = false; + this.tracks = tracks; + this.hls.trigger(_events2.default.AUDIO_TRACKS_UPDATED, { audioTracks: tracks }); + // loop through available audio tracks and autoselect default if needed + var id = 0; + tracks.forEach(function (track) { + if (track.default) { + _this2.audioTrack = id; + defaultFound = true; + return; + } + id++; + }); + if (defaultFound === false && tracks.length) { + _logger.logger.log('no default audio track defined, use first audio track as default'); + this.audioTrack = 0; + } + } + }, { + key: 'onAudioTrackLoaded', + value: function onAudioTrackLoaded(data) { + if (data.id < this.tracks.length) { + _logger.logger.log('audioTrack ' + data.id + ' loaded'); + this.tracks[data.id].details = data.details; + // check if current playlist is a live playlist + if (data.details.live && !this.timer) { + // if live playlist we will have to reload it periodically + // set reload period to playlist target duration + this.timer = setInterval(this.ontick, 1000 * data.details.targetduration); + } + if (!data.details.live && this.timer) { + // playlist is not live and timer is armed : stopping it + clearInterval(this.timer); + this.timer = null; + } + } + } + + /** get alternate audio tracks list from playlist **/ + + }, { + key: 'setAudioTrackInternal', + value: function setAudioTrackInternal(newId) { + // check if level idx is valid + if (newId >= 0 && newId < this.tracks.length) { + // stopping live reloading timer if any + if (this.timer) { + clearInterval(this.timer); + this.timer = null; + } + this.trackId = newId; + _logger.logger.log('switching to audioTrack ' + newId); + var audioTrack = this.tracks[newId], + type = audioTrack.type, + url = audioTrack.url; + this.hls.trigger(_events2.default.AUDIO_TRACK_SWITCH, { id: newId, type: type, url: url }); + // check if we need to load playlist for this audio Track + var details = audioTrack.details; + if (url && (details === undefined || details.live === true)) { + // track not retrieved yet, or live playlist we need to (re)load it + _logger.logger.log('(re)loading playlist for audioTrack ' + newId); + this.hls.trigger(_events2.default.AUDIO_TRACK_LOADING, { url: url, id: newId }); + } + } + } + }, { + key: 'audioTracks', + get: function get() { + return this.tracks; + } + + /** get index of the selected audio track (index in audio track lists) **/ + + }, { + key: 'audioTrack', + get: function get() { + return this.trackId; + } + + /** select an audio track, based on its index in audio track lists**/ + , + set: function set(audioTrackId) { + if (this.trackId !== audioTrackId || this.tracks[audioTrackId].details === undefined) { + this.setAudioTrackInternal(audioTrackId); + } + } + }]); + + return AudioTrackController; + }(_eventHandler2.default); + + exports.default = AudioTrackController; + + },{"25":25,"26":26,"43":43}],6:[function(_dereq_,module,exports){ + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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 _events = _dereq_(26); + + var _events2 = _interopRequireDefault(_events); + + var _eventHandler = _dereq_(25); + + var _eventHandler2 = _interopRequireDefault(_eventHandler); + + var _logger = _dereq_(43); + + var _errors = _dereq_(24); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + 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; } /* + * Buffer Controller + */ + + var BufferController = function (_EventHandler) { + _inherits(BufferController, _EventHandler); + + function BufferController(hls) { + _classCallCheck(this, BufferController); + + // the value that we have set mediasource.duration to + // (the actual duration may be tweaked slighly by the browser) + + var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(BufferController).call(this, hls, _events2.default.MEDIA_ATTACHING, _events2.default.MEDIA_DETACHING, _events2.default.MANIFEST_PARSED, _events2.default.BUFFER_RESET, _events2.default.BUFFER_APPENDING, _events2.default.BUFFER_CODECS, _events2.default.BUFFER_EOS, _events2.default.BUFFER_FLUSHING, _events2.default.LEVEL_UPDATED)); + + _this._msDuration = null; + // the value that we want to set mediaSource.duration to + _this._levelDuration = null; + + // Source Buffer listeners + _this.onsbue = _this.onSBUpdateEnd.bind(_this); + _this.onsbe = _this.onSBUpdateError.bind(_this); + _this.pendingTracks = {}; + return _this; + } + + _createClass(BufferController, [{ + key: 'destroy', + value: function destroy() { + _eventHandler2.default.prototype.destroy.call(this); + } + }, { + key: 'onManifestParsed', + value: function onManifestParsed(data) { + var audioExpected = data.audio, + videoExpected = data.video, + sourceBufferNb = 0; + // in case of alt audio 2 BUFFER_CODECS events will be triggered, one per stream controller + // sourcebuffers will be created all at once when the expected nb of tracks will be reached + // in case alt audio is not used, only one BUFFER_CODEC event will be fired from main stream controller + // it will contain the expected nb of source buffers, no need to compute it + if (data.altAudio && (audioExpected || videoExpected)) { + sourceBufferNb = (audioExpected ? 1 : 0) + (videoExpected ? 1 : 0); + _logger.logger.log(sourceBufferNb + ' sourceBuffer(s) expected'); + } + this.sourceBufferNb = sourceBufferNb; + } + }, { + key: 'onMediaAttaching', + value: function onMediaAttaching(data) { + var media = this.media = data.media; + if (media) { + // setup the media source + var ms = this.mediaSource = new MediaSource(); + //Media Source listeners + this.onmso = this.onMediaSourceOpen.bind(this); + this.onmse = this.onMediaSourceEnded.bind(this); + this.onmsc = this.onMediaSourceClose.bind(this); + ms.addEventListener('sourceopen', this.onmso); + ms.addEventListener('sourceended', this.onmse); + ms.addEventListener('sourceclose', this.onmsc); + // link video and media Source + media.src = URL.createObjectURL(ms); + } + } + }, { + key: 'onMediaDetaching', + value: function onMediaDetaching() { + _logger.logger.log('media source detaching'); + var ms = this.mediaSource; + if (ms) { + if (ms.readyState === 'open') { + try { + // endOfStream could trigger exception if any sourcebuffer is in updating state + // we don't really care about checking sourcebuffer state here, + // as we are anyway detaching the MediaSource + // let's just avoid this exception to propagate + ms.endOfStream(); + } catch (err) { + _logger.logger.warn('onMediaDetaching:' + err.message + ' while calling endOfStream'); + } + } + ms.removeEventListener('sourceopen', this.onmso); + ms.removeEventListener('sourceended', this.onmse); + ms.removeEventListener('sourceclose', this.onmsc); + + // Detach properly the MediaSource from the HTMLMediaElement as + // suggested in https://github.com/w3c/media-source/issues/53. + if (this.media) { + this.media.removeAttribute('src'); + this.media.load(); + } + + this.mediaSource = null; + this.media = null; + this.pendingTracks = {}; + this.sourceBuffer = {}; + this.flushRange = []; + this.segments = []; + this.appended = 0; + } + this.onmso = this.onmse = this.onmsc = null; + this.hls.trigger(_events2.default.MEDIA_DETACHED); + } + }, { + key: 'onMediaSourceOpen', + value: function onMediaSourceOpen() { + _logger.logger.log('media source opened'); + this.hls.trigger(_events2.default.MEDIA_ATTACHED, { media: this.media }); + var mediaSource = this.mediaSource; + if (mediaSource) { + // once received, don't listen anymore to sourceopen event + mediaSource.removeEventListener('sourceopen', this.onmso); + } + this.checkPendingTracks(); + } + }, { + key: 'checkPendingTracks', + value: function checkPendingTracks() { + // if any buffer codecs pending, check if we have enough to create sourceBuffers + var pendingTracks = this.pendingTracks, + pendingTracksNb = Object.keys(pendingTracks).length; + // if any pending tracks and (if nb of pending tracks gt or equal than expected nb or if unknown expected nb) + if (pendingTracksNb && (this.sourceBufferNb <= pendingTracksNb || this.sourceBufferNb === 0)) { + // ok, let's create them now ! + this.createSourceBuffers(pendingTracks); + this.pendingTracks = {}; + // append any pending segments now ! + this.doAppending(); + } + } + }, { + key: 'onMediaSourceClose', + value: function onMediaSourceClose() { + _logger.logger.log('media source closed'); + } + }, { + key: 'onMediaSourceEnded', + value: function onMediaSourceEnded() { + _logger.logger.log('media source ended'); + } + }, { + key: 'onSBUpdateEnd', + value: function onSBUpdateEnd() { + + if (this._needsFlush) { + this.doFlush(); + } + + if (this._needsEos) { + this.checkEos(); + } + this.appending = false; + this.hls.trigger(_events2.default.BUFFER_APPENDED, { parent: this.parent }); + + // don't append in flushing mode + if (!this._needsFlush) { + this.doAppending(); + } + + this.updateMediaElementDuration(); + } + }, { + key: 'onSBUpdateError', + value: function onSBUpdateError(event) { + _logger.logger.error('sourceBuffer error:' + event); + // according to http://www.w3.org/TR/media-source/#sourcebuffer-append-error + // this error might not always be fatal (it is fatal if decode error is set, in that case + // it will be followed by a mediaElement error ...) + this.hls.trigger(_events2.default.ERROR, { type: _errors.ErrorTypes.MEDIA_ERROR, details: _errors.ErrorDetails.BUFFER_APPENDING_ERROR, fatal: false }); + // we don't need to do more than that, as accordin to the spec, updateend will be fired just after + } + }, { + key: 'onBufferReset', + value: function onBufferReset() { + var sourceBuffer = this.sourceBuffer; + for (var type in sourceBuffer) { + var sb = sourceBuffer[type]; + try { + this.mediaSource.removeSourceBuffer(sb); + sb.removeEventListener('updateend', this.onsbue); + sb.removeEventListener('error', this.onsbe); + } catch (err) {} + } + this.sourceBuffer = {}; + this.flushRange = []; + this.segments = []; + this.appended = 0; + } + }, { + key: 'onBufferCodecs', + value: function onBufferCodecs(tracks) { + // if source buffer(s) not created yet, appended buffer tracks in this.pendingTracks + // if sourcebuffers already created, do nothing ... + if (Object.keys(this.sourceBuffer).length === 0) { + for (var trackName in tracks) { + this.pendingTracks[trackName] = tracks[trackName]; + } + var mediaSource = this.mediaSource; + if (mediaSource && mediaSource.readyState === 'open') { + // try to create sourcebuffers if mediasource opened + this.checkPendingTracks(); + } + } + } + }, { + key: 'createSourceBuffers', + value: function createSourceBuffers(tracks) { + var sourceBuffer = this.sourceBuffer, + mediaSource = this.mediaSource; + + for (var trackName in tracks) { + if (!sourceBuffer[trackName]) { + var track = tracks[trackName]; + // use levelCodec as first priority + var codec = track.levelCodec || track.codec; + var mimeType = track.container + ';codecs=' + codec; + _logger.logger.log('creating sourceBuffer(' + mimeType + ')'); + try { + var sb = sourceBuffer[trackName] = mediaSource.addSourceBuffer(mimeType); + sb.addEventListener('updateend', this.onsbue); + sb.addEventListener('error', this.onsbe); + track.buffer = sb; + } catch (err) { + _logger.logger.error('error while trying to add sourceBuffer:' + err.message); + this.hls.trigger(_events2.default.ERROR, { type: _errors.ErrorTypes.MEDIA_ERROR, details: _errors.ErrorDetails.BUFFER_ADD_CODEC_ERROR, fatal: false, err: err, mimeType: mimeType }); + } + } + } + this.hls.trigger(_events2.default.BUFFER_CREATED, { tracks: tracks }); + } + }, { + key: 'onBufferAppending', + value: function onBufferAppending(data) { + if (!this._needsFlush) { + if (!this.segments) { + this.segments = [data]; + } else { + this.segments.push(data); + } + this.doAppending(); + } + } + }, { + key: 'onBufferAppendFail', + value: function onBufferAppendFail(data) { + _logger.logger.error('sourceBuffer error:' + data.event); + // according to http://www.w3.org/TR/media-source/#sourcebuffer-append-error + // this error might not always be fatal (it is fatal if decode error is set, in that case + // it will be followed by a mediaElement error ...) + this.hls.trigger(_events2.default.ERROR, { type: _errors.ErrorTypes.MEDIA_ERROR, details: _errors.ErrorDetails.BUFFER_APPENDING_ERROR, fatal: false, frag: this.fragCurrent }); + } + + // on BUFFER_EOS mark matching sourcebuffer(s) as ended and trigger checkEos() + + }, { + key: 'onBufferEos', + value: function onBufferEos(data) { + var sb = this.sourceBuffer; + var dataType = data.type; + for (var type in sb) { + if (!dataType || type === dataType) { + if (!sb[type].ended) { + sb[type].ended = true; + _logger.logger.log(type + ' sourceBuffer now EOS'); + } + } + } + this.checkEos(); + } + + // if all source buffers are marked as ended, signal endOfStream() to MediaSource. + + }, { + key: 'checkEos', + value: function checkEos() { + var sb = this.sourceBuffer, + mediaSource = this.mediaSource; + if (!mediaSource || mediaSource.readyState !== 'open') { + this._needsEos = false; + return; + } + for (var type in sb) { + if (!sb[type].ended) { + return; + } + if (sb[type].updating) { + this._needsEos = true; + return; + } + } + _logger.logger.log('all media data available, signal endOfStream() to MediaSource and stop loading fragment'); + //Notify the media element that it now has all of the media data + mediaSource.endOfStream(); + this._needsEos = false; + } + }, { + key: 'onBufferFlushing', + value: function onBufferFlushing(data) { + this.flushRange.push({ start: data.startOffset, end: data.endOffset, type: data.type }); + // attempt flush immediatly + this.flushBufferCounter = 0; + this.doFlush(); + } + }, { + key: 'onLevelUpdated', + value: function onLevelUpdated(event) { + var details = event.details; + if (details.fragments.length === 0) { + return; + } + this._levelDuration = details.totalduration + details.fragments[0].start; + this.updateMediaElementDuration(); + } + + // https://github.com/dailymotion/hls.js/issues/355 + + }, { + key: 'updateMediaElementDuration', + value: function updateMediaElementDuration() { + if (this._levelDuration === null) { + return; + } + var media = this.media, + mediaSource = this.mediaSource, + sourceBuffer = this.sourceBuffer; + if (!media || !mediaSource || !sourceBuffer || media.readyState === 0 || mediaSource.readyState !== 'open') { + return; + } + for (var type in sourceBuffer) { + if (sourceBuffer[type].updating) { + // can't set duration whilst a buffer is updating + return; + } + } + if (this._msDuration === null) { + // initialise to the value that the media source is reporting + this._msDuration = mediaSource.duration; + } + // this._levelDuration was the last value we set. + // not using mediaSource.duration as the browser may tweak this value + // only update mediasource duration if its value increase, this is to avoid + // flushing already buffered portion when switching between quality level, as they + if (this._levelDuration > this._msDuration) { + _logger.logger.log('Updating mediasource duration to ' + this._levelDuration); + mediaSource.duration = this._levelDuration; + this._msDuration = this._levelDuration; + } + } + }, { + key: 'doFlush', + value: function doFlush() { + // loop through all buffer ranges to flush + while (this.flushRange.length) { + var range = this.flushRange[0]; + // flushBuffer will abort any buffer append in progress and flush Audio/Video Buffer + if (this.flushBuffer(range.start, range.end, range.type)) { + // range flushed, remove from flush array + this.flushRange.shift(); + this.flushBufferCounter = 0; + } else { + this._needsFlush = true; + // avoid looping, wait for SB update end to retrigger a flush + return; + } + } + if (this.flushRange.length === 0) { + // everything flushed + this._needsFlush = false; + + // let's recompute this.appended, which is used to avoid flush looping + var appended = 0; + var sourceBuffer = this.sourceBuffer; + for (var type in sourceBuffer) { + appended += sourceBuffer[type].buffered.length; + } + this.appended = appended; + this.hls.trigger(_events2.default.BUFFER_FLUSHED); + } + } + }, { + key: 'doAppending', + value: function doAppending() { + var hls = this.hls, + sourceBuffer = this.sourceBuffer, + segments = this.segments; + if (Object.keys(sourceBuffer).length) { + if (this.media.error) { + this.segments = []; + _logger.logger.error('trying to append although a media error occured, flush segment and abort'); + return; + } + if (this.appending) { + //logger.log(`sb appending in progress`); + return; + } + if (segments && segments.length) { + var segment = segments.shift(); + try { + var type = segment.type; + if (sourceBuffer[type]) { + // reset sourceBuffer ended flag before appending segment + sourceBuffer[type].ended = false; + //logger.log(`appending ${segment.content} ${segment.type} SB, size:${segment.data.length}, ${segment.parent}`); + this.parent = segment.parent; + sourceBuffer[type].appendBuffer(segment.data); + this.appendError = 0; + this.appended++; + this.appending = true; + } else { + // in case we don't have any source buffer matching with this segment type, + // it means that Mediasource fails to create sourcebuffer + // discard this segment, and trigger update end + this.onSBUpdateEnd(); + } + } catch (err) { + // in case any error occured while appending, put back segment in segments table + _logger.logger.error('error while trying to append buffer:' + err.message); + segments.unshift(segment); + var event = { type: _errors.ErrorTypes.MEDIA_ERROR }; + if (err.code !== 22) { + if (this.appendError) { + this.appendError++; + } else { + this.appendError = 1; + } + event.details = _errors.ErrorDetails.BUFFER_APPEND_ERROR; + event.frag = this.fragCurrent; + /* with UHD content, we could get loop of quota exceeded error until + browser is able to evict some data from sourcebuffer. retrying help recovering this + */ + if (this.appendError > hls.config.appendErrorMaxRetry) { + _logger.logger.log('fail ' + hls.config.appendErrorMaxRetry + ' times to append segment in sourceBuffer'); + segments = []; + event.fatal = true; + hls.trigger(_events2.default.ERROR, event); + return; + } else { + event.fatal = false; + hls.trigger(_events2.default.ERROR, event); + } + } else { + // QuotaExceededError: http://www.w3.org/TR/html5/infrastructure.html#quotaexceedederror + // let's stop appending any segments, and report BUFFER_FULL_ERROR error + this.segments = []; + event.details = _errors.ErrorDetails.BUFFER_FULL_ERROR; + hls.trigger(_events2.default.ERROR, event); + return; + } + } + } + } + } + + /* + flush specified buffered range, + return true once range has been flushed. + as sourceBuffer.remove() is asynchronous, flushBuffer will be retriggered on sourceBuffer update end + */ + + }, { + key: 'flushBuffer', + value: function flushBuffer(startOffset, endOffset, typeIn) { + var sb, + i, + bufStart, + bufEnd, + flushStart, + flushEnd, + sourceBuffer = this.sourceBuffer; + if (Object.keys(sourceBuffer).length) { + _logger.logger.log('flushBuffer,pos/start/end: ' + this.media.currentTime + '/' + startOffset + '/' + endOffset); + // safeguard to avoid infinite looping : don't try to flush more than the nb of appended segments + if (this.flushBufferCounter < this.appended) { + for (var type in sourceBuffer) { + // check if sourcebuffer type is defined (typeIn): if yes, let's only flush this one + // if no, let's flush all sourcebuffers + if (typeIn && type !== typeIn) { + continue; + } + sb = sourceBuffer[type]; + // we are going to flush buffer, mark source buffer as 'not ended' + sb.ended = false; + if (!sb.updating) { + for (i = 0; i < sb.buffered.length; i++) { + bufStart = sb.buffered.start(i); + bufEnd = sb.buffered.end(i); + // workaround firefox not able to properly flush multiple buffered range. + if (navigator.userAgent.toLowerCase().indexOf('firefox') !== -1 && endOffset === Number.POSITIVE_INFINITY) { + flushStart = startOffset; + flushEnd = endOffset; + } else { + flushStart = Math.max(bufStart, startOffset); + flushEnd = Math.min(bufEnd, endOffset); + } + /* sometimes sourcebuffer.remove() does not flush + the exact expected time range. + to avoid rounding issues/infinite loop, + only flush buffer range of length greater than 500ms. + */ + if (Math.min(flushEnd, bufEnd) - flushStart > 0.5) { + this.flushBufferCounter++; + _logger.logger.log('flush ' + type + ' [' + flushStart + ',' + flushEnd + '], of [' + bufStart + ',' + bufEnd + '], pos:' + this.media.currentTime); + sb.remove(flushStart, flushEnd); + return false; + } + } + } else { + //logger.log('abort ' + type + ' append in progress'); + // this will abort any appending in progress + //sb.abort(); + _logger.logger.warn('cannot flush, sb updating in progress'); + return false; + } + } + } else { + _logger.logger.warn('abort flushing too many retries'); + } + _logger.logger.log('buffer flushed'); + } + // everything flushed ! + return true; + } + }]); + + return BufferController; + }(_eventHandler2.default); + + exports.default = BufferController; + + },{"24":24,"25":25,"26":26,"43":43}],7:[function(_dereq_,module,exports){ + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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 _events = _dereq_(26); + + var _events2 = _interopRequireDefault(_events); + + var _eventHandler = _dereq_(25); + + var _eventHandler2 = _interopRequireDefault(_eventHandler); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + 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; } /* + * cap stream level to media size dimension controller + */ + + var CapLevelController = function (_EventHandler) { + _inherits(CapLevelController, _EventHandler); + + function CapLevelController(hls) { + _classCallCheck(this, CapLevelController); + + return _possibleConstructorReturn(this, Object.getPrototypeOf(CapLevelController).call(this, hls, _events2.default.FPS_DROP_LEVEL_CAPPING, _events2.default.MEDIA_ATTACHING, _events2.default.MANIFEST_PARSED)); + } + + _createClass(CapLevelController, [{ + key: 'destroy', + value: function destroy() { + if (this.hls.config.capLevelToPlayerSize) { + this.media = this.restrictedLevels = null; + this.autoLevelCapping = Number.POSITIVE_INFINITY; + if (this.timer) { + this.timer = clearInterval(this.timer); + } + } + } + }, { + key: 'onFpsDropLevelCapping', + value: function onFpsDropLevelCapping(data) { + if (!this.restrictedLevels) { + this.restrictedLevels = []; + } + if (!this.isLevelRestricted(data.droppedLevel)) { + this.restrictedLevels.push(data.droppedLevel); + } + } + }, { + key: 'onMediaAttaching', + value: function onMediaAttaching(data) { + this.media = data.media instanceof HTMLVideoElement ? data.media : null; + } + }, { + key: 'onManifestParsed', + value: function onManifestParsed(data) { + if (this.hls.config.capLevelToPlayerSize) { + this.autoLevelCapping = Number.POSITIVE_INFINITY; + this.levels = data.levels; + this.hls.firstLevel = this.getMaxLevel(data.firstLevel); + clearInterval(this.timer); + this.timer = setInterval(this.detectPlayerSize.bind(this), 1000); + this.detectPlayerSize(); + } + } + }, { + key: 'detectPlayerSize', + value: function detectPlayerSize() { + if (this.media) { + var levelsLength = this.levels ? this.levels.length : 0; + if (levelsLength) { + this.hls.autoLevelCapping = this.getMaxLevel(levelsLength - 1); + if (this.hls.autoLevelCapping > this.autoLevelCapping) { + // if auto level capping has a higher value for the previous one, flush the buffer using nextLevelSwitch + // usually happen when the user go to the fullscreen mode. + this.hls.streamController.nextLevelSwitch(); + } + this.autoLevelCapping = this.hls.autoLevelCapping; + } + } + } + + /* + * returns level should be the one with the dimensions equal or greater than the media (player) dimensions (so the video will be downscaled) + */ + + }, { + key: 'getMaxLevel', + value: function getMaxLevel(capLevelIndex) { + var result = 0, + i = void 0, + level = void 0, + mWidth = this.mediaWidth, + mHeight = this.mediaHeight, + lWidth = 0, + lHeight = 0; + + for (i = 0; i <= capLevelIndex; i++) { + level = this.levels[i]; + if (this.isLevelRestricted(i)) { + break; + } + result = i; + lWidth = level.width; + lHeight = level.height; + if (mWidth <= lWidth || mHeight <= lHeight) { + break; + } + } + return result; + } + }, { + key: 'isLevelRestricted', + value: function isLevelRestricted(level) { + return this.restrictedLevels && this.restrictedLevels.indexOf(level) !== -1 ? true : false; + } + }, { + key: 'contentScaleFactor', + get: function get() { + var pixelRatio = 1; + try { + pixelRatio = window.devicePixelRatio; + } catch (e) {} + return pixelRatio; + } + }, { + key: 'mediaWidth', + get: function get() { + var width = void 0; + if (this.media) { + width = this.media.width || this.media.clientWidth || this.media.offsetWidth; + width *= this.contentScaleFactor; + } + return width; + } + }, { + key: 'mediaHeight', + get: function get() { + var height = void 0; + if (this.media) { + height = this.media.height || this.media.clientHeight || this.media.offsetHeight; + height *= this.contentScaleFactor; + } + return height; + } + }]); + + return CapLevelController; + }(_eventHandler2.default); + + exports.default = CapLevelController; + + },{"25":25,"26":26}],8:[function(_dereq_,module,exports){ + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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; }; }(); /* + * EWMA Bandwidth Estimator + * - heavily inspired from shaka-player + * Tracks bandwidth samples and estimates available bandwidth. + * Based on the minimum of two exponentially-weighted moving averages with + * different half-lives. + */ + + var _ewma = _dereq_(42); + + var _ewma2 = _interopRequireDefault(_ewma); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + + var EwmaBandWidthEstimator = function () { + function EwmaBandWidthEstimator(hls, slow, fast, defaultEstimate) { + _classCallCheck(this, EwmaBandWidthEstimator); + + this.hls = hls; + this.defaultEstimate_ = defaultEstimate; + this.minWeight_ = 0.001; + this.minDelayMs_ = 50; + this.slow_ = new _ewma2.default(slow); + this.fast_ = new _ewma2.default(fast); + } + + _createClass(EwmaBandWidthEstimator, [{ + key: 'sample', + value: function sample(durationMs, numBytes) { + durationMs = Math.max(durationMs, this.minDelayMs_); + var bandwidth = 8000 * numBytes / durationMs, + + //console.log('instant bw:'+ Math.round(bandwidth)); + // we weight sample using loading duration.... + weight = durationMs / 1000; + this.fast_.sample(weight, bandwidth); + this.slow_.sample(weight, bandwidth); + } + }, { + key: 'canEstimate', + value: function canEstimate() { + var fast = this.fast_; + return fast && fast.getTotalWeight() >= this.minWeight_; + } + }, { + key: 'getEstimate', + value: function getEstimate() { + if (this.canEstimate()) { + //console.log('slow estimate:'+ Math.round(this.slow_.getEstimate())); + //console.log('fast estimate:'+ Math.round(this.fast_.getEstimate())); + // Take the minimum of these two estimates. This should have the effect of + // adapting down quickly, but up more slowly. + return Math.min(this.fast_.getEstimate(), this.slow_.getEstimate()); + } else { + return this.defaultEstimate_; + } + } + }, { + key: 'destroy', + value: function destroy() {} + }]); + + return EwmaBandWidthEstimator; + }(); + + exports.default = EwmaBandWidthEstimator; + + },{"42":42}],9:[function(_dereq_,module,exports){ + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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 _events = _dereq_(26); + + var _events2 = _interopRequireDefault(_events); + + var _eventHandler = _dereq_(25); + + var _eventHandler2 = _interopRequireDefault(_eventHandler); + + var _logger = _dereq_(43); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + 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; } /* + * FPS Controller + */ + + var FPSController = function (_EventHandler) { + _inherits(FPSController, _EventHandler); + + function FPSController(hls) { + _classCallCheck(this, FPSController); + + return _possibleConstructorReturn(this, Object.getPrototypeOf(FPSController).call(this, hls, _events2.default.MEDIA_ATTACHING)); + } + + _createClass(FPSController, [{ + key: 'destroy', + value: function destroy() { + if (this.timer) { + clearInterval(this.timer); + } + this.isVideoPlaybackQualityAvailable = false; + } + }, { + key: 'onMediaAttaching', + value: function onMediaAttaching(data) { + if (this.hls.config.capLevelOnFPSDrop) { + this.video = data.media instanceof HTMLVideoElement ? data.media : null; + if (typeof this.video.getVideoPlaybackQuality === 'function') { + this.isVideoPlaybackQualityAvailable = true; + } + clearInterval(this.timer); + this.timer = setInterval(this.checkFPSInterval.bind(this), this.hls.config.fpsDroppedMonitoringPeriod); + } + } + }, { + key: 'checkFPS', + value: function checkFPS(video, decodedFrames, droppedFrames) { + var currentTime = performance.now(); + if (decodedFrames) { + if (this.lastTime) { + var currentPeriod = currentTime - this.lastTime, + currentDropped = droppedFrames - this.lastDroppedFrames, + currentDecoded = decodedFrames - this.lastDecodedFrames, + droppedFPS = 1000 * currentDropped / currentPeriod; + this.hls.trigger(_events2.default.FPS_DROP, { currentDropped: currentDropped, currentDecoded: currentDecoded, totalDroppedFrames: droppedFrames }); + if (droppedFPS > 0) { + //logger.log('checkFPS : droppedFPS/decodedFPS:' + droppedFPS/(1000 * currentDecoded / currentPeriod)); + if (currentDropped > this.hls.config.fpsDroppedMonitoringThreshold * currentDecoded) { + var currentLevel = this.hls.currentLevel; + _logger.logger.warn('drop FPS ratio greater than max allowed value for currentLevel: ' + currentLevel); + if (currentLevel > 0 && (this.hls.autoLevelCapping === -1 || this.hls.autoLevelCapping >= currentLevel)) { + currentLevel = currentLevel - 1; + this.hls.trigger(_events2.default.FPS_DROP_LEVEL_CAPPING, { level: currentLevel, droppedLevel: this.hls.currentLevel }); + this.hls.autoLevelCapping = currentLevel; + this.hls.streamController.nextLevelSwitch(); + } + } + } + } + this.lastTime = currentTime; + this.lastDroppedFrames = droppedFrames; + this.lastDecodedFrames = decodedFrames; + } + } + }, { + key: 'checkFPSInterval', + value: function checkFPSInterval() { + if (this.video) { + if (this.isVideoPlaybackQualityAvailable) { + var videoPlaybackQuality = this.video.getVideoPlaybackQuality(); + this.checkFPS(this.video, videoPlaybackQuality.totalVideoFrames, videoPlaybackQuality.droppedVideoFrames); + } else { + this.checkFPS(this.video, this.video.webkitDecodedFrameCount, this.video.webkitDroppedFrameCount); + } + } + } + }]); + + return FPSController; + }(_eventHandler2.default); + + exports.default = FPSController; + + },{"25":25,"26":26,"43":43}],10:[function(_dereq_,module,exports){ + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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 _events = _dereq_(26); + + var _events2 = _interopRequireDefault(_events); + + var _eventHandler = _dereq_(25); + + var _eventHandler2 = _interopRequireDefault(_eventHandler); + + var _logger = _dereq_(43); + + var _errors = _dereq_(24); + + var _bufferHelper = _dereq_(28); + + var _bufferHelper2 = _interopRequireDefault(_bufferHelper); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + 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; } /* + * Level Controller + */ + + var LevelController = function (_EventHandler) { + _inherits(LevelController, _EventHandler); + + function LevelController(hls) { + _classCallCheck(this, LevelController); + + var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(LevelController).call(this, hls, _events2.default.MANIFEST_LOADED, _events2.default.LEVEL_LOADED, _events2.default.ERROR)); + + _this.ontick = _this.tick.bind(_this); + _this._manualLevel = _this._autoLevelCapping = -1; + return _this; + } + + _createClass(LevelController, [{ + key: 'destroy', + value: function destroy() { + if (this.timer) { + clearTimeout(this.timer); + this.timer = null; + } + this._manualLevel = -1; + } + }, { + key: 'startLoad', + value: function startLoad() { + this.canload = true; + // speed up live playlist refresh if timer exists + if (this.timer) { + this.tick(); + } + } + }, { + key: 'stopLoad', + value: function stopLoad() { + this.canload = false; + } + }, { + key: 'onManifestLoaded', + value: function onManifestLoaded(data) { + var levels0 = [], + levels = [], + bitrateStart, + i, + bitrateSet = {}, + videoCodecFound = false, + audioCodecFound = false, + hls = this.hls; + + // regroup redundant level together + data.levels.forEach(function (level) { + if (level.videoCodec) { + videoCodecFound = true; + } + if (level.audioCodec || level.attrs && level.attrs.AUDIO) { + audioCodecFound = true; + } + var redundantLevelId = bitrateSet[level.bitrate]; + if (redundantLevelId === undefined) { + bitrateSet[level.bitrate] = levels0.length; + level.url = [level.url]; + level.urlId = 0; + levels0.push(level); + } else { + levels0[redundantLevelId].url.push(level.url); + } + }); + + // remove audio-only level if we also have levels with audio+video codecs signalled + if (videoCodecFound && audioCodecFound) { + levels0.forEach(function (level) { + if (level.videoCodec) { + levels.push(level); + } + }); + } else { + levels = levels0; + } + + // only keep level with supported audio/video codecs + levels = levels.filter(function (level) { + var checkSupportedAudio = function checkSupportedAudio(codec) { + return MediaSource.isTypeSupported('audio/mp4;codecs=' + codec); + }; + var checkSupportedVideo = function checkSupportedVideo(codec) { + return MediaSource.isTypeSupported('video/mp4;codecs=' + codec); + }; + var audioCodec = level.audioCodec, + videoCodec = level.videoCodec; + + return (!audioCodec || checkSupportedAudio(audioCodec)) && (!videoCodec || checkSupportedVideo(videoCodec)); + }); + + if (levels.length) { + // start bitrate is the first bitrate of the manifest + bitrateStart = levels[0].bitrate; + // sort level on bitrate + levels.sort(function (a, b) { + return a.bitrate - b.bitrate; + }); + this._levels = levels; + // find index of first level in sorted levels + for (i = 0; i < levels.length; i++) { + if (levels[i].bitrate === bitrateStart) { + this._firstLevel = i; + _logger.logger.log('manifest loaded,' + levels.length + ' level(s) found, first bitrate:' + bitrateStart); + break; + } + } + hls.trigger(_events2.default.MANIFEST_PARSED, { levels: this._levels, firstLevel: this._firstLevel, stats: data.stats, audio: audioCodecFound, video: videoCodecFound, altAudio: data.audioTracks.length > 0 }); + } else { + hls.trigger(_events2.default.ERROR, { type: _errors.ErrorTypes.MEDIA_ERROR, details: _errors.ErrorDetails.MANIFEST_INCOMPATIBLE_CODECS_ERROR, fatal: true, url: hls.url, reason: 'no level with compatible codecs found in manifest' }); + } + return; + } + }, { + key: 'setLevelInternal', + value: function setLevelInternal(newLevel) { + var levels = this._levels; + // check if level idx is valid + if (newLevel >= 0 && newLevel < levels.length) { + // stopping live reloading timer if any + if (this.timer) { + clearTimeout(this.timer); + this.timer = null; + } + if (this._level !== newLevel) { + _logger.logger.log('switching to level ' + newLevel); + this._level = newLevel; + } + this.hls.trigger(_events2.default.LEVEL_SWITCH, { level: newLevel }); + var level = levels[newLevel], + levelDetails = level.details; + // check if we need to load playlist for this level + if (!levelDetails || levelDetails.live === true) { + // level not retrieved yet, or live playlist we need to (re)load it + var urlId = level.urlId; + this.hls.trigger(_events2.default.LEVEL_LOADING, { url: level.url[urlId], level: newLevel, id: urlId }); + } + } else { + // invalid level id given, trigger error + this.hls.trigger(_events2.default.ERROR, { type: _errors.ErrorTypes.OTHER_ERROR, details: _errors.ErrorDetails.LEVEL_SWITCH_ERROR, level: newLevel, fatal: false, reason: 'invalid level idx' }); + } + } + }, { + key: 'onError', + value: function onError(data) { + if (data.fatal) { + return; + } + + var details = data.details, + hls = this.hls, + levelId = void 0, + level = void 0, + levelError = false; + // try to recover not fatal errors + switch (details) { + case _errors.ErrorDetails.FRAG_LOAD_ERROR: + case _errors.ErrorDetails.FRAG_LOAD_TIMEOUT: + case _errors.ErrorDetails.FRAG_LOOP_LOADING_ERROR: + case _errors.ErrorDetails.KEY_LOAD_ERROR: + case _errors.ErrorDetails.KEY_LOAD_TIMEOUT: + levelId = data.frag.level; + break; + case _errors.ErrorDetails.LEVEL_LOAD_ERROR: + case _errors.ErrorDetails.LEVEL_LOAD_TIMEOUT: + levelId = data.context.level; + levelError = true; + break; + default: + break; + } + /* try to switch to a redundant stream if any available. + * if no redundant stream available, emergency switch down (if in auto mode and current level not 0) + * otherwise, we cannot recover this network error ... + */ + if (levelId !== undefined) { + level = this._levels[levelId]; + if (level.urlId < level.url.length - 1) { + level.urlId++; + level.details = undefined; + _logger.logger.warn('level controller,' + details + ' for level ' + levelId + ': switching to redundant stream id ' + level.urlId); + } else { + // we could try to recover if in auto mode and current level not lowest level (0) + var recoverable = this._manualLevel === -1 && levelId; + if (recoverable) { + _logger.logger.warn('level controller,' + details + ': emergency switch-down for next fragment'); + hls.abrController.nextAutoLevel = 0; + } else if (level && level.details && level.details.live) { + _logger.logger.warn('level controller,' + details + ' on live stream, discard'); + if (levelError) { + // reset this._level so that another call to set level() will retrigger a frag load + this._level = undefined; + } + // other errors are handled by stream controller + } else if (details === _errors.ErrorDetails.LEVEL_LOAD_ERROR || details === _errors.ErrorDetails.LEVEL_LOAD_TIMEOUT) { + var _hls = this.hls, + media = _hls.media, + + // 0.5 : tolerance needed as some browsers stalls playback before reaching buffered end + mediaBuffered = media && _bufferHelper2.default.isBuffered(media, media.currentTime) && _bufferHelper2.default.isBuffered(media, media.currentTime + 0.5); + if (mediaBuffered) { + var retryDelay = _hls.config.levelLoadingRetryDelay; + _logger.logger.warn('level controller,' + details + ', but media buffered, retry in ' + retryDelay + 'ms'); + this.timer = setTimeout(this.ontick, retryDelay); + } else { + _logger.logger.error('cannot recover ' + details + ' error'); + this._level = undefined; + // stopping live reloading timer if any + if (this.timer) { + clearTimeout(this.timer); + this.timer = null; + } + // redispatch same error but with fatal set to true + data.fatal = true; + _hls.trigger(_events2.default.ERROR, data); + } + } + } + } + } + }, { + key: 'onLevelLoaded', + value: function onLevelLoaded(data) { + // only process level loaded events matching with expected level + if (data.level === this._level) { + var newDetails = data.details; + // if current playlist is a live playlist, arm a timer to reload it + if (newDetails.live) { + var reloadInterval = 1000 * (newDetails.averagetargetduration ? newDetails.averagetargetduration : newDetails.targetduration), + curLevel = this._levels[data.level], + curDetails = curLevel.details; + if (curDetails && newDetails.endSN === curDetails.endSN) { + // follow HLS Spec, If the client reloads a Playlist file and finds that it has not + // changed then it MUST wait for a period of one-half the target + // duration before retrying. + reloadInterval /= 2; + _logger.logger.log('same live playlist, reload twice faster'); + } + // decrement reloadInterval with level loading delay + reloadInterval -= performance.now() - data.stats.trequest; + // in any case, don't reload more than every second + reloadInterval = Math.max(1000, Math.round(reloadInterval)); + _logger.logger.log('live playlist, reload in ' + reloadInterval + ' ms'); + this.timer = setTimeout(this.ontick, reloadInterval); + } else { + this.timer = null; + } + } + } + }, { + key: 'tick', + value: function tick() { + var levelId = this._level; + if (levelId !== undefined && this.canload) { + var level = this._levels[levelId], + urlId = level.urlId; + this.hls.trigger(_events2.default.LEVEL_LOADING, { url: level.url[urlId], level: levelId, id: urlId }); + } + } + }, { + key: 'levels', + get: function get() { + return this._levels; + } + }, { + key: 'level', + get: function get() { + return this._level; + }, + set: function set(newLevel) { + var levels = this._levels; + if (levels && levels.length > newLevel) { + if (this._level !== newLevel || levels[newLevel].details === undefined) { + this.setLevelInternal(newLevel); + } + } + } + }, { + key: 'manualLevel', + get: function get() { + return this._manualLevel; + }, + set: function set(newLevel) { + this._manualLevel = newLevel; + if (this._startLevel === undefined) { + this._startLevel = newLevel; + } + if (newLevel !== -1) { + this.level = newLevel; + } + } + }, { + key: 'firstLevel', + get: function get() { + return this._firstLevel; + }, + set: function set(newLevel) { + this._firstLevel = newLevel; + } + }, { + key: 'startLevel', + get: function get() { + // hls.startLevel takes precedence over config.startLevel + // if none of these values are defined, fallback on this._firstLevel (first quality level appearing in variant manifest) + if (this._startLevel === undefined) { + var configStartLevel = this.hls.config.startLevel; + if (configStartLevel !== undefined) { + return configStartLevel; + } else { + return this._firstLevel; + } + } else { + return this._startLevel; + } + }, + set: function set(newLevel) { + this._startLevel = newLevel; + } + }, { + key: 'nextLoadLevel', + get: function get() { + if (this._manualLevel !== -1) { + return this._manualLevel; + } else { + return this.hls.abrController.nextAutoLevel; + } + }, + set: function set(nextLevel) { + this.level = nextLevel; + if (this._manualLevel === -1) { + this.hls.abrController.nextAutoLevel = nextLevel; + } + } + }]); + + return LevelController; + }(_eventHandler2.default); + + exports.default = LevelController; + + },{"24":24,"25":25,"26":26,"28":28,"43":43}],11:[function(_dereq_,module,exports){ + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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 _binarySearch = _dereq_(39); + + var _binarySearch2 = _interopRequireDefault(_binarySearch); + + var _bufferHelper = _dereq_(28); + + var _bufferHelper2 = _interopRequireDefault(_bufferHelper); + + var _demuxer = _dereq_(20); + + var _demuxer2 = _interopRequireDefault(_demuxer); + + var _events = _dereq_(26); + + var _events2 = _interopRequireDefault(_events); + + var _eventHandler = _dereq_(25); + + var _eventHandler2 = _interopRequireDefault(_eventHandler); + + var _levelHelper = _dereq_(29); + + var _levelHelper2 = _interopRequireDefault(_levelHelper); + + var _timeRanges = _dereq_(45); + + var _timeRanges2 = _interopRequireDefault(_timeRanges); + + var _errors = _dereq_(24); + + var _logger = _dereq_(43); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + 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; } /* + * Stream Controller + */ + + var State = { + STOPPED: 'STOPPED', + IDLE: 'IDLE', + PAUSED: 'PAUSED', + KEY_LOADING: 'KEY_LOADING', + FRAG_LOADING: 'FRAG_LOADING', + FRAG_LOADING_WAITING_RETRY: 'FRAG_LOADING_WAITING_RETRY', + WAITING_LEVEL: 'WAITING_LEVEL', + PARSING: 'PARSING', + PARSED: 'PARSED', + ENDED: 'ENDED', + ERROR: 'ERROR' + }; + + var StreamController = function (_EventHandler) { + _inherits(StreamController, _EventHandler); + + function StreamController(hls) { + _classCallCheck(this, StreamController); + + var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(StreamController).call(this, hls, _events2.default.MEDIA_ATTACHED, _events2.default.MEDIA_DETACHING, _events2.default.MANIFEST_LOADING, _events2.default.MANIFEST_PARSED, _events2.default.LEVEL_LOADED, _events2.default.KEY_LOADED, _events2.default.FRAG_LOADED, _events2.default.FRAG_LOAD_EMERGENCY_ABORTED, _events2.default.FRAG_PARSING_INIT_SEGMENT, _events2.default.FRAG_PARSING_DATA, _events2.default.FRAG_PARSED, _events2.default.ERROR, _events2.default.AUDIO_TRACK_SWITCH, _events2.default.BUFFER_CREATED, _events2.default.BUFFER_APPENDED, _events2.default.BUFFER_FLUSHED)); + + _this.config = hls.config; + _this.audioCodecSwap = false; + _this.ticks = 0; + _this.ontick = _this.tick.bind(_this); + return _this; + } + + _createClass(StreamController, [{ + key: 'destroy', + value: function destroy() { + this.stopLoad(); + if (this.timer) { + clearInterval(this.timer); + this.timer = null; + } + _eventHandler2.default.prototype.destroy.call(this); + this.state = State.STOPPED; + } + }, { + key: 'startLoad', + value: function startLoad(startPosition) { + if (this.levels) { + var media = this.media, + lastCurrentTime = this.lastCurrentTime, + hls = this.hls; + this.stopLoad(); + if (!this.timer) { + this.timer = setInterval(this.ontick, 100); + } + this.level = -1; + this.fragLoadError = 0; + if (media && lastCurrentTime > 0) { + _logger.logger.log('configure startPosition @' + lastCurrentTime.toFixed(3)); + if (!this.lastPaused) { + _logger.logger.log('resuming video'); + media.play(); + } + } else { + this.lastCurrentTime = this.startPosition ? this.startPosition : startPosition; + } + if (!this.startFragRequested) { + // determine load level + var startLevel = hls.startLevel; + if (startLevel === -1) { + // -1 : guess start Level by doing a bitrate test by loading first fragment of lowest quality level + startLevel = 0; + this.bitrateTest = true; + } + // set new level to playlist loader : this will trigger start level load + // hls.nextLoadLevel remains until it is set to a new value or until a new frag is successfully loaded + this.level = hls.nextLoadLevel = startLevel; + this.loadedmetadata = false; + } + this.state = State.IDLE; + this.nextLoadPosition = this.startPosition = this.lastCurrentTime; + this.tick(); + } else { + _logger.logger.warn('cannot start loading as manifest not parsed yet'); + this.state = State.STOPPED; + } + } + }, { + key: 'stopLoad', + value: function stopLoad() { + var frag = this.fragCurrent; + if (frag) { + if (frag.loader) { + frag.loader.abort(); + } + this.fragCurrent = null; + } + this.fragPrevious = null; + if (this.demuxer) { + this.demuxer.destroy(); + this.demuxer = null; + } + this.state = State.STOPPED; + } + }, { + key: 'tick', + value: function tick() { + this.ticks++; + if (this.ticks === 1) { + this.doTick(); + if (this.ticks > 1) { + setTimeout(this.tick, 1); + } + this.ticks = 0; + } + } + }, { + key: 'doTick', + value: function doTick() { + switch (this.state) { + case State.ERROR: + //don't do anything in error state to avoid breaking further ... + case State.PAUSED: + //don't do anything in paused state either ... + break; + case State.IDLE: + // when this returns false there was an error and we shall return immediatly + // from current tick + if (!this._doTickIdle()) { + return; + } + break; + case State.WAITING_LEVEL: + var level = this.levels[this.level]; + // check if playlist is already loaded + if (level && level.details) { + this.state = State.IDLE; + } + break; + case State.FRAG_LOADING_WAITING_RETRY: + var now = performance.now(); + var retryDate = this.retryDate; + // if current time is gt than retryDate, or if media seeking let's switch to IDLE state to retry loading + if (!retryDate || now >= retryDate || this.media && this.media.seeking) { + _logger.logger.log('mediaController: retryDate reached, switch back to IDLE state'); + this.state = State.IDLE; + } + break; + case State.ERROR: + case State.PAUSED: + case State.STOPPED: + case State.FRAG_LOADING: + case State.PARSING: + case State.PARSED: + case State.ENDED: + break; + default: + break; + } + // check buffer + this._checkBuffer(); + // check/update current fragment + this._checkFragmentChanged(); + } + + // Ironically the "idle" state is the on we do the most logic in it seems .... + // NOTE: Maybe we could rather schedule a check for buffer length after half of the currently + // played segment, or on pause/play/seek instead of naively checking every 100ms? + + }, { + key: '_doTickIdle', + value: function _doTickIdle() { + var hls = this.hls, + config = hls.config, + media = this.media; + + // if video not attached AND + // start fragment already requested OR start frag prefetch disable + // exit loop + // => if media not attached but start frag prefetch is enabled and start frag not requested yet, we will not exit loop + if (!media && (this.startFragRequested || !config.startFragPrefetch)) { + return true; + } + + // if we have not yet loaded any fragment, start loading from start position + var pos = void 0; + if (this.loadedmetadata) { + pos = media.currentTime; + } else { + pos = this.nextLoadPosition; + } + // determine next load level + var level = hls.nextLoadLevel; + + // compute max Buffer Length that we could get from this load level, based on level bitrate. don't buffer more than 60 MB and more than 30s + var maxBufLen = void 0; + if (this.levels[level].hasOwnProperty('bitrate')) { + maxBufLen = Math.max(8 * config.maxBufferSize / this.levels[level].bitrate, config.maxBufferLength); + } else { + maxBufLen = config.maxBufferLength; + } + maxBufLen = Math.min(maxBufLen, config.maxMaxBufferLength); + + // determine next candidate fragment to be loaded, based on current position and end of buffer position + // ensure up to `config.maxMaxBufferLength` of buffer upfront + + var bufferInfo = _bufferHelper2.default.bufferInfo(this.mediaBuffer ? this.mediaBuffer : media, pos, config.maxBufferHole), + bufferLen = bufferInfo.len; + // Stay idle if we are still with buffer margins + if (bufferLen >= maxBufLen) { + return true; + } + + // if buffer length is less than maxBufLen try to load a new fragment ... + _logger.logger.trace('buffer length of ' + bufferLen.toFixed(3) + ' is below max of ' + maxBufLen.toFixed(3) + '. checking for more payload ...'); + + // set next load level : this will trigger a playlist load if needed + hls.nextLoadLevel = level; + this.level = level; + + var levelDetails = this.levels[level].details; + // if level info not retrieved yet, switch state and wait for level retrieval + // if live playlist, ensure that new playlist has been refreshed to avoid loading/try to load + // a useless and outdated fragment (that might even introduce load error if it is already out of the live playlist) + if (typeof levelDetails === 'undefined' || levelDetails.live && this.levelLastLoaded !== level) { + this.state = State.WAITING_LEVEL; + return true; + } + + // we just got done loading the final fragment, check if we need to finalize media stream + var fragPrevious = this.fragPrevious; + if (!levelDetails.live && fragPrevious && fragPrevious.sn === levelDetails.endSN) { + // if (we are not seeking AND current position is buffered) OR (if we are seeking but everything (almost) til the end is buffered), let's signal eos + // we don't compare exactly media.duration === bufferInfo.end as there could be some subtle media duration difference when switching + // between different renditions. using half frag duration should help cope with these cases. + if (!media.seeking && bufferInfo.len || media.duration - bufferInfo.end <= fragPrevious.duration / 2) { + // Finalize the media stream + var data = {}; + if (this.altAudio) { + data.type = 'video'; + } + this.hls.trigger(_events2.default.BUFFER_EOS, data); + this.state = State.ENDED; + return true; + } + } + + // if we have the levelDetails for the selected variant, lets continue enrichen our stream (load keys/fragments or trigger EOS, etc..) + return this._fetchPayloadOrEos({ pos: pos, bufferInfo: bufferInfo, levelDetails: levelDetails }); + } + }, { + key: '_fetchPayloadOrEos', + value: function _fetchPayloadOrEos(_ref) { + var pos = _ref.pos; + var bufferInfo = _ref.bufferInfo; + var levelDetails = _ref.levelDetails; + + var fragPrevious = this.fragPrevious, + level = this.level, + fragments = levelDetails.fragments, + fragLen = fragments.length; + + // empty playlist + if (fragLen === 0) { + return false; + } + + // find fragment index, contiguous with end of buffer position + var start = fragments[0].start, + end = fragments[fragLen - 1].start + fragments[fragLen - 1].duration, + bufferEnd = bufferInfo.end, + frag = void 0; + + // in case of live playlist we need to ensure that requested position is not located before playlist start + if (levelDetails.live) { + frag = this._ensureFragmentAtLivePoint({ levelDetails: levelDetails, bufferEnd: bufferEnd, start: start, end: end, fragPrevious: fragPrevious, fragments: fragments, fragLen: fragLen }); + // if it explicitely returns null don't load any fragment and exit function now + if (frag === null) { + return false; + } + } else { + // VoD playlist: if bufferEnd before start of playlist, load first fragment + if (bufferEnd < start) { + frag = fragments[0]; + } + } + if (!frag) { + frag = this._findFragment({ start: start, fragPrevious: fragPrevious, fragLen: fragLen, fragments: fragments, bufferEnd: bufferEnd, end: end, levelDetails: levelDetails }); + } + if (frag) { + return this._loadFragmentOrKey({ frag: frag, level: level, levelDetails: levelDetails, pos: pos, bufferEnd: bufferEnd }); + } + return true; + } + }, { + key: '_ensureFragmentAtLivePoint', + value: function _ensureFragmentAtLivePoint(_ref2) { + var levelDetails = _ref2.levelDetails; + var bufferEnd = _ref2.bufferEnd; + var start = _ref2.start; + var end = _ref2.end; + var fragPrevious = _ref2.fragPrevious; + var fragments = _ref2.fragments; + var fragLen = _ref2.fragLen; + + var config = this.hls.config, + media = this.media; + + var frag = void 0; + + // check if requested position is within seekable boundaries : + //logger.log(`start/pos/bufEnd/seeking:${start.toFixed(3)}/${pos.toFixed(3)}/${bufferEnd.toFixed(3)}/${this.media.seeking}`); + var maxLatency = config.liveMaxLatencyDuration !== undefined ? config.liveMaxLatencyDuration : config.liveMaxLatencyDurationCount * levelDetails.targetduration; + + if (bufferEnd < Math.max(start, end - maxLatency)) { + var liveSyncPosition = this.liveSyncPosition = this.computeLivePosition(start, levelDetails); + _logger.logger.log('buffer end: ' + bufferEnd.toFixed(3) + ' is located too far from the end of live sliding playlist, reset currentTime to : ' + liveSyncPosition.toFixed(3)); + bufferEnd = liveSyncPosition; + if (media && media.readyState && media.duration > liveSyncPosition) { + media.currentTime = liveSyncPosition; + } + } + + // if end of buffer greater than live edge, don't load any fragment + // this could happen if live playlist intermittently slides in the past. + // level 1 loaded [182580161,182580167] + // level 1 loaded [182580162,182580169] + // Loading 182580168 of [182580162 ,182580169],level 1 .. + // Loading 182580169 of [182580162 ,182580169],level 1 .. + // level 1 loaded [182580162,182580168] <============= here we should have bufferEnd > end. in that case break to avoid reloading 182580168 + // level 1 loaded [182580164,182580171] + // + // don't return null in case media not loaded yet (readystate === 0) + if (levelDetails.PTSKnown && bufferEnd > end && media && media.readyState) { + return null; + } + + if (this.startFragRequested && !levelDetails.PTSKnown) { + /* we are switching level on live playlist, but we don't have any PTS info for that quality level ... + try to load frag matching with next SN. + even if SN are not synchronized between playlists, loading this frag will help us + compute playlist sliding and find the right one after in case it was not the right consecutive one */ + if (fragPrevious) { + var targetSN = fragPrevious.sn + 1; + if (targetSN >= levelDetails.startSN && targetSN <= levelDetails.endSN) { + frag = fragments[targetSN - levelDetails.startSN]; + _logger.logger.log('live playlist, switching playlist, load frag with next SN: ' + frag.sn); + } + } + if (!frag) { + /* we have no idea about which fragment should be loaded. + so let's load mid fragment. it will help computing playlist sliding and find the right one + */ + frag = fragments[Math.min(fragLen - 1, Math.round(fragLen / 2))]; + _logger.logger.log('live playlist, switching playlist, unknown, load middle frag : ' + frag.sn); + } + } + return frag; + } + }, { + key: '_findFragment', + value: function _findFragment(_ref3) { + var start = _ref3.start; + var fragPrevious = _ref3.fragPrevious; + var fragLen = _ref3.fragLen; + var fragments = _ref3.fragments; + var bufferEnd = _ref3.bufferEnd; + var end = _ref3.end; + var levelDetails = _ref3.levelDetails; + + var config = this.hls.config; + + var frag = void 0, + foundFrag = void 0, + maxFragLookUpTolerance = config.maxFragLookUpTolerance; + + if (bufferEnd < end) { + if (bufferEnd > end - maxFragLookUpTolerance) { + maxFragLookUpTolerance = 0; + } + foundFrag = _binarySearch2.default.search(fragments, function (candidate) { + // offset should be within fragment boundary - config.maxFragLookUpTolerance + // this is to cope with situations like + // bufferEnd = 9.991 + // frag[Ø] : [0,10] + // frag[1] : [10,20] + // bufferEnd is within frag[0] range ... although what we are expecting is to return frag[1] here + // frag start frag start+duration + // |-----------------------------| + // <---> <---> + // ...--------><-----------------------------><---------.... + // previous frag matching fragment next frag + // return -1 return 0 return 1 + //logger.log(`level/sn/start/end/bufEnd:${level}/${candidate.sn}/${candidate.start}/${(candidate.start+candidate.duration)}/${bufferEnd}`); + if (candidate.start + candidate.duration - maxFragLookUpTolerance <= bufferEnd) { + return 1; + } // if maxFragLookUpTolerance will have negative value then don't return -1 for first element + else if (candidate.start - maxFragLookUpTolerance > bufferEnd && candidate.start) { + return -1; + } + return 0; + }); + } else { + // reach end of playlist + foundFrag = fragments[fragLen - 1]; + } + if (foundFrag) { + frag = foundFrag; + start = foundFrag.start; + //logger.log('find SN matching with pos:' + bufferEnd + ':' + frag.sn); + if (fragPrevious && frag.level === fragPrevious.level && frag.sn === fragPrevious.sn) { + if (frag.sn < levelDetails.endSN) { + var deltaPTS = fragPrevious.deltaPTS, + curSNIdx = frag.sn - levelDetails.startSN; + // if there is a significant delta between audio and video, larger than max allowed hole, + // and if previous remuxed fragment did not start with a keyframe. (fragPrevious.dropped) + // let's try to load previous fragment again to get last keyframe + // then we will reload again current fragment (that way we should be able to fill the buffer hole ...) + if (deltaPTS && deltaPTS > config.maxBufferHole && fragPrevious.dropped && curSNIdx) { + frag = fragments[curSNIdx - 1]; + _logger.logger.warn('SN just loaded, with large PTS gap between audio and video, maybe frag is not starting with a keyframe ? load previous one to try to overcome this'); + // decrement previous frag load counter to avoid frag loop loading error when next fragment will get reloaded + fragPrevious.loadCounter--; + } else { + frag = fragments[curSNIdx + 1]; + _logger.logger.log('SN just loaded, load next one: ' + frag.sn); + } + } else { + frag = null; + } + } + } + return frag; + } + }, { + key: '_loadFragmentOrKey', + value: function _loadFragmentOrKey(_ref4) { + var frag = _ref4.frag; + var level = _ref4.level; + var levelDetails = _ref4.levelDetails; + var pos = _ref4.pos; + var bufferEnd = _ref4.bufferEnd; + + var hls = this.hls, + config = hls.config; + + //logger.log('loading frag ' + i +',pos/bufEnd:' + pos.toFixed(3) + '/' + bufferEnd.toFixed(3)); + if (frag.decryptdata.uri != null && frag.decryptdata.key == null) { + _logger.logger.log('Loading key for ' + frag.sn + ' of [' + levelDetails.startSN + ' ,' + levelDetails.endSN + '],level ' + level); + this.state = State.KEY_LOADING; + hls.trigger(_events2.default.KEY_LOADING, { frag: frag }); + } else { + _logger.logger.log('Loading ' + frag.sn + ' of [' + levelDetails.startSN + ' ,' + levelDetails.endSN + '],level ' + level + ', currentTime:' + pos.toFixed(3) + ',bufferEnd:' + bufferEnd.toFixed(3)); + // ensure that we are not reloading the same fragments in loop ... + if (this.fragLoadIdx !== undefined) { + this.fragLoadIdx++; + } else { + this.fragLoadIdx = 0; + } + if (frag.loadCounter) { + frag.loadCounter++; + var maxThreshold = config.fragLoadingLoopThreshold; + // if this frag has already been loaded 3 times, and if it has been reloaded recently + if (frag.loadCounter > maxThreshold && Math.abs(this.fragLoadIdx - frag.loadIdx) < maxThreshold) { + hls.trigger(_events2.default.ERROR, { type: _errors.ErrorTypes.MEDIA_ERROR, details: _errors.ErrorDetails.FRAG_LOOP_LOADING_ERROR, fatal: false, frag: frag }); + return false; + } + } else { + frag.loadCounter = 1; + } + frag.loadIdx = this.fragLoadIdx; + this.fragCurrent = frag; + this.startFragRequested = true; + frag.autoLevel = hls.autoLevelEnabled; + frag.bitrateTest = this.bitrateTest; + hls.trigger(_events2.default.FRAG_LOADING, { frag: frag }); + this.state = State.FRAG_LOADING; + return true; + } + } + }, { + key: 'getBufferRange', + value: function getBufferRange(position) { + var i, + range, + bufferRange = this.bufferRange; + if (bufferRange) { + for (i = bufferRange.length - 1; i >= 0; i--) { + range = bufferRange[i]; + if (position >= range.start && position <= range.end) { + return range; + } + } + } + return null; + } + }, { + key: 'followingBufferRange', + value: function followingBufferRange(range) { + if (range) { + // try to get range of next fragment (500ms after this range) + return this.getBufferRange(range.end + 0.5); + } + return null; + } + }, { + key: '_checkFragmentChanged', + value: function _checkFragmentChanged() { + var rangeCurrent, + currentTime, + video = this.media; + if (video && video.readyState && video.seeking === false) { + currentTime = video.currentTime; + /* if video element is in seeked state, currentTime can only increase. + (assuming that playback rate is positive ...) + As sometimes currentTime jumps back to zero after a + media decode error, check this, to avoid seeking back to + wrong position after a media decode error + */ + if (currentTime > video.playbackRate * this.lastCurrentTime) { + this.lastCurrentTime = currentTime; + } + if (_bufferHelper2.default.isBuffered(video, currentTime)) { + rangeCurrent = this.getBufferRange(currentTime); + } else if (_bufferHelper2.default.isBuffered(video, currentTime + 0.1)) { + /* ensure that FRAG_CHANGED event is triggered at startup, + when first video frame is displayed and playback is paused. + add a tolerance of 100ms, in case current position is not buffered, + check if current pos+100ms is buffered and use that buffer range + for FRAG_CHANGED event reporting */ + rangeCurrent = this.getBufferRange(currentTime + 0.1); + } + if (rangeCurrent) { + var fragPlaying = rangeCurrent.frag; + if (fragPlaying !== this.fragPlaying) { + this.fragPlaying = fragPlaying; + this.hls.trigger(_events2.default.FRAG_CHANGED, { frag: fragPlaying }); + } + } + } + } + + /* + on immediate level switch : + - pause playback if playing + - cancel any pending load request + - and trigger a buffer flush + */ + + }, { + key: 'immediateLevelSwitch', + value: function immediateLevelSwitch() { + _logger.logger.log('immediateLevelSwitch'); + if (!this.immediateSwitch) { + this.immediateSwitch = true; + var media = this.media, + previouslyPaused = void 0; + if (media) { + previouslyPaused = media.paused; + media.pause(); + } else { + // don't restart playback after instant level switch in case media not attached + previouslyPaused = true; + } + this.previouslyPaused = previouslyPaused; + } + var fragCurrent = this.fragCurrent; + if (fragCurrent && fragCurrent.loader) { + fragCurrent.loader.abort(); + } + this.fragCurrent = null; + this.state = State.PAUSED; + // flush everything + this.hls.trigger(_events2.default.BUFFER_FLUSHING, { startOffset: 0, endOffset: Number.POSITIVE_INFINITY }); + } + + /* + on immediate level switch end, after new fragment has been buffered : + - nudge video decoder by slightly adjusting video currentTime (if currentTime buffered) + - resume the playback if needed + */ + + }, { + key: 'immediateLevelSwitchEnd', + value: function immediateLevelSwitchEnd() { + var media = this.media; + if (media && media.buffered.length) { + this.immediateSwitch = false; + if (_bufferHelper2.default.isBuffered(media, media.currentTime)) { + // only nudge if currentTime is buffered + media.currentTime -= 0.0001; + } + if (!this.previouslyPaused) { + media.play(); + } + } + } + }, { + key: 'nextLevelSwitch', + value: function nextLevelSwitch() { + /* try to switch ASAP without breaking video playback : + in order to ensure smooth but quick level switching, + we need to find the next flushable buffer range + we should take into account new segment fetch time + */ + var media = this.media; + // ensure that media is defined and that metadata are available (to retrieve currentTime) + if (media && media.readyState) { + var fetchdelay = void 0, + currentRange = void 0, + nextRange = void 0; + // increase fragment load Index to avoid frag loop loading error after buffer flush + this.fragLoadIdx += 2 * this.config.fragLoadingLoopThreshold; + currentRange = this.getBufferRange(media.currentTime); + if (currentRange && currentRange.start > 1) { + // flush buffer preceding current fragment (flush until current fragment start offset) + // minus 1s to avoid video freezing, that could happen if we flush keyframe of current video ... + this.state = State.PAUSED; + this.hls.trigger(_events2.default.BUFFER_FLUSHING, { startOffset: 0, endOffset: currentRange.start - 1 }); + } + if (!media.paused) { + // add a safety delay of 1s + var nextLevelId = this.hls.nextLoadLevel, + nextLevel = this.levels[nextLevelId], + fragLastKbps = this.fragLastKbps; + if (fragLastKbps && this.fragCurrent) { + fetchdelay = this.fragCurrent.duration * nextLevel.bitrate / (1000 * fragLastKbps) + 1; + } else { + fetchdelay = 0; + } + } else { + fetchdelay = 0; + } + //logger.log('fetchdelay:'+fetchdelay); + // find buffer range that will be reached once new fragment will be fetched + nextRange = this.getBufferRange(media.currentTime + fetchdelay); + if (nextRange) { + // we can flush buffer range following this one without stalling playback + nextRange = this.followingBufferRange(nextRange); + if (nextRange) { + // if we are here, we can also cancel any loading/demuxing in progress, as they are useless + var fragCurrent = this.fragCurrent; + if (fragCurrent && fragCurrent.loader) { + fragCurrent.loader.abort(); + } + this.fragCurrent = null; + // flush position is the start position of this new buffer + this.state = State.PAUSED; + this.hls.trigger(_events2.default.BUFFER_FLUSHING, { startOffset: nextRange.start, endOffset: Number.POSITIVE_INFINITY }); + } + } + } + } + }, { + key: 'onMediaAttached', + value: function onMediaAttached(data) { + var media = this.media = this.mediaBuffer = data.media; + this.onvseeking = this.onMediaSeeking.bind(this); + this.onvseeked = this.onMediaSeeked.bind(this); + this.onvended = this.onMediaEnded.bind(this); + media.addEventListener('seeking', this.onvseeking); + media.addEventListener('seeked', this.onvseeked); + media.addEventListener('ended', this.onvended); + var config = this.config; + if (this.levels && config.autoStartLoad) { + this.hls.startLoad(config.startPosition); + } + } + }, { + key: 'onMediaDetaching', + value: function onMediaDetaching() { + var media = this.media; + if (media && media.ended) { + _logger.logger.log('MSE detaching and video ended, reset startPosition'); + this.startPosition = this.lastCurrentTime = 0; + } + + // reset fragment loading counter on MSE detaching to avoid reporting FRAG_LOOP_LOADING_ERROR after error recovery + var levels = this.levels; + if (levels) { + // reset fragment load counter + levels.forEach(function (level) { + if (level.details) { + level.details.fragments.forEach(function (fragment) { + fragment.loadCounter = undefined; + }); + } + }); + } + // remove video listeners + if (media) { + media.removeEventListener('seeking', this.onvseeking); + media.removeEventListener('seeked', this.onvseeked); + media.removeEventListener('ended', this.onvended); + this.onvseeking = this.onvseeked = this.onvended = null; + } + this.media = null; + this.loadedmetadata = false; + this.stopLoad(); + } + }, { + key: 'onMediaSeeking', + value: function onMediaSeeking() { + var media = this.media, + currentTime = media ? media.currentTime : undefined, + config = this.config; + _logger.logger.log('media seeking to ' + currentTime.toFixed(3)); + if (this.state === State.FRAG_LOADING) { + var bufferInfo = _bufferHelper2.default.bufferInfo(media, currentTime, this.config.maxBufferHole), + fragCurrent = this.fragCurrent; + // check if we are seeking to a unbuffered area AND if frag loading is in progress + if (bufferInfo.len === 0 && fragCurrent) { + var tolerance = config.maxFragLookUpTolerance, + fragStartOffset = fragCurrent.start - tolerance, + fragEndOffset = fragCurrent.start + fragCurrent.duration + tolerance; + // check if we seek position will be out of currently loaded frag range : if out cancel frag load, if in, don't do anything + if (currentTime < fragStartOffset || currentTime > fragEndOffset) { + if (fragCurrent.loader) { + _logger.logger.log('seeking outside of buffer while fragment load in progress, cancel fragment load'); + fragCurrent.loader.abort(); + } + this.fragCurrent = null; + this.fragPrevious = null; + // switch to IDLE state to load new fragment + this.state = State.IDLE; + } else { + _logger.logger.log('seeking outside of buffer but within currently loaded fragment range'); + } + } + } else if (this.state === State.ENDED) { + // switch to IDLE state to check for potential new fragment + this.state = State.IDLE; + } + if (media) { + this.lastCurrentTime = currentTime; + } + // avoid reporting fragment loop loading error in case user is seeking several times on same position + if (this.state !== State.FRAG_LOADING && this.fragLoadIdx !== undefined) { + this.fragLoadIdx += 2 * config.fragLoadingLoopThreshold; + } + // tick to speed up processing + this.tick(); + } + }, { + key: 'onMediaSeeked', + value: function onMediaSeeked() { + _logger.logger.log('media seeked to ' + this.media.currentTime.toFixed(3)); + // tick to speed up FRAGMENT_PLAYING triggering + this.tick(); + } + }, { + key: 'onMediaEnded', + value: function onMediaEnded() { + _logger.logger.log('media ended'); + // reset startPosition and lastCurrentTime to restart playback @ stream beginning + this.startPosition = this.lastCurrentTime = 0; + } + }, { + key: 'onManifestLoading', + value: function onManifestLoading() { + // reset buffer on manifest loading + _logger.logger.log('trigger BUFFER_RESET'); + this.hls.trigger(_events2.default.BUFFER_RESET); + this.bufferRange = []; + this.stalled = false; + this.startPosition = this.lastCurrentTime = 0; + } + }, { + key: 'onManifestParsed', + value: function onManifestParsed(data) { + var aac = false, + heaac = false, + codec; + data.levels.forEach(function (level) { + // detect if we have different kind of audio codecs used amongst playlists + codec = level.audioCodec; + if (codec) { + if (codec.indexOf('mp4a.40.2') !== -1) { + aac = true; + } + if (codec.indexOf('mp4a.40.5') !== -1) { + heaac = true; + } + } + }); + this.audioCodecSwitch = aac && heaac; + if (this.audioCodecSwitch) { + _logger.logger.log('both AAC/HE-AAC audio found in levels; declaring level codec as HE-AAC'); + } + this.levels = data.levels; + this.startLevelLoaded = false; + this.startFragRequested = false; + var config = this.config; + if (config.autoStartLoad) { + this.hls.startLoad(config.startPosition); + } + } + }, { + key: 'onLevelLoaded', + value: function onLevelLoaded(data) { + var newDetails = data.details, + newLevelId = data.level, + curLevel = this.levels[newLevelId], + duration = newDetails.totalduration, + sliding = 0; + + _logger.logger.log('level ' + newLevelId + ' loaded [' + newDetails.startSN + ',' + newDetails.endSN + '],duration:' + duration); + this.levelLastLoaded = newLevelId; + + if (newDetails.live) { + var curDetails = curLevel.details; + if (curDetails && newDetails.fragments.length > 0) { + // we already have details for that level, merge them + _levelHelper2.default.mergeDetails(curDetails, newDetails); + sliding = newDetails.fragments[0].start; + this.liveSyncPosition = this.computeLivePosition(sliding, curDetails); + if (newDetails.PTSKnown) { + _logger.logger.log('live playlist sliding:' + sliding.toFixed(3)); + } else { + _logger.logger.log('live playlist - outdated PTS, unknown sliding'); + } + } else { + newDetails.PTSKnown = false; + _logger.logger.log('live playlist - first load, unknown sliding'); + } + } else { + newDetails.PTSKnown = false; + } + // override level info + curLevel.details = newDetails; + this.hls.trigger(_events2.default.LEVEL_UPDATED, { details: newDetails, level: newLevelId }); + + if (this.startFragRequested === false) { + // compute start position if set to -1. use it straight away if value is defined + if (this.startPosition === -1 || this.lastCurrentTime === -1) { + // first, check if start time offset has been set in playlist, if yes, use this value + var startTimeOffset = newDetails.startTimeOffset; + if (!isNaN(startTimeOffset)) { + if (startTimeOffset < 0) { + _logger.logger.log('negative start time offset ' + startTimeOffset + ', count from end of last fragment'); + startTimeOffset = sliding + duration + startTimeOffset; + } + _logger.logger.log('start time offset found in playlist, adjust startPosition to ' + startTimeOffset); + this.startPosition = startTimeOffset; + } else { + // if live playlist, set start position to be fragment N-this.config.liveSyncDurationCount (usually 3) + if (newDetails.live) { + this.startPosition = this.computeLivePosition(sliding, newDetails); + _logger.logger.log('configure startPosition to ' + this.startPosition); + } else { + this.startPosition = 0; + } + } + } + this.nextLoadPosition = this.startPosition; + } + // only switch batck to IDLE state if we were waiting for level to start downloading a new fragment + if (this.state === State.WAITING_LEVEL) { + this.state = State.IDLE; + } + //trigger handler right now + this.tick(); + } + }, { + key: 'onKeyLoaded', + value: function onKeyLoaded() { + if (this.state === State.KEY_LOADING) { + this.state = State.IDLE; + this.tick(); + } + } + }, { + key: 'onFragLoaded', + value: function onFragLoaded(data) { + var fragCurrent = this.fragCurrent, + fragLoaded = data.frag; + if (this.state === State.FRAG_LOADING && fragCurrent && fragLoaded.type === 'main' && fragLoaded.level === fragCurrent.level && fragLoaded.sn === fragCurrent.sn) { + var stats = data.stats, + currentLevel = this.levels[fragCurrent.level], + details = currentLevel.details; + _logger.logger.log('Loaded ' + fragCurrent.sn + ' of [' + details.startSN + ' ,' + details.endSN + '],level ' + fragCurrent.level); + // reset frag bitrate test in any case after frag loaded event + this.bitrateTest = false; + // if this frag was loaded to perform a bitrate test AND if hls.nextLoadLevel is greater than 0 + // then this means that we should be able to load a fragment at a higher quality level + if (fragLoaded.bitrateTest === true && this.hls.nextLoadLevel) { + // switch back to IDLE state ... we just loaded a fragment to determine adequate start bitrate and initialize autoswitch algo + this.state = State.IDLE; + this.startFragRequested = false; + stats.tparsed = stats.tbuffered = performance.now(); + this.hls.trigger(_events2.default.FRAG_BUFFERED, { stats: stats, frag: fragCurrent, id: 'main' }); + this.tick(); + } else { + this.state = State.PARSING; + // transmux the MPEG-TS data to ISO-BMFF segments + this.stats = stats; + var duration = details.totalduration, + start = !isNaN(fragCurrent.startDTS) ? fragCurrent.startDTS : fragCurrent.start, + level = fragCurrent.level, + sn = fragCurrent.sn, + audioCodec = this.config.defaultAudioCodec || currentLevel.audioCodec; + if (this.audioCodecSwap) { + _logger.logger.log('swapping playlist audio codec'); + if (audioCodec === undefined) { + audioCodec = this.lastAudioCodec; + } + if (audioCodec) { + if (audioCodec.indexOf('mp4a.40.5') !== -1) { + audioCodec = 'mp4a.40.2'; + } else { + audioCodec = 'mp4a.40.5'; + } + } + } + this.pendingAppending = 0; + _logger.logger.log('Parsing ' + sn + ' of [' + details.startSN + ' ,' + details.endSN + '],level ' + level + ', cc ' + fragCurrent.cc); + var demuxer = this.demuxer; + if (!demuxer) { + demuxer = this.demuxer = new _demuxer2.default(this.hls, 'main'); + } + // time Offset is accurate if level PTS is known, or if playlist is not sliding (not live) + var accurateTimeOffset = details.PTSKnown || !details.live; + demuxer.push(data.payload, audioCodec, currentLevel.videoCodec, start, fragCurrent.cc, level, sn, duration, fragCurrent.decryptdata, accurateTimeOffset); + } + } + this.fragLoadError = 0; + } + }, { + key: 'onFragParsingInitSegment', + value: function onFragParsingInitSegment(data) { + var fragCurrent = this.fragCurrent; + if (fragCurrent && data.id === 'main' && data.sn === fragCurrent.sn && data.level === fragCurrent.level && this.state === State.PARSING) { + var tracks = data.tracks, + trackName, + track; + + // if audio track is expected to come from audio stream controller, discard any coming from main + if (tracks.audio && this.altAudio) { + delete tracks.audio; + } + // include levelCodec in audio and video tracks + track = tracks.audio; + if (track) { + var audioCodec = this.levels[this.level].audioCodec, + ua = navigator.userAgent.toLowerCase(); + if (audioCodec && this.audioCodecSwap) { + _logger.logger.log('swapping playlist audio codec'); + if (audioCodec.indexOf('mp4a.40.5') !== -1) { + audioCodec = 'mp4a.40.2'; + } else { + audioCodec = 'mp4a.40.5'; + } + } + // in case AAC and HE-AAC audio codecs are signalled in manifest + // force HE-AAC , as it seems that most browsers prefers that way, + // except for mono streams OR on FF + // these conditions might need to be reviewed ... + if (this.audioCodecSwitch) { + // don't force HE-AAC if mono stream + if (track.metadata.channelCount !== 1 && + // don't force HE-AAC if firefox + ua.indexOf('firefox') === -1) { + audioCodec = 'mp4a.40.5'; + } + } + // HE-AAC is broken on Android, always signal audio codec as AAC even if variant manifest states otherwise + if (ua.indexOf('android') !== -1) { + audioCodec = 'mp4a.40.2'; + _logger.logger.log('Android: force audio codec to' + audioCodec); + } + track.levelCodec = audioCodec; + track.id = data.id; + } + track = tracks.video; + if (track) { + track.levelCodec = this.levels[this.level].videoCodec; + track.id = data.id; + } + + // if remuxer specify that a unique track needs to generated, + // let's merge all tracks together + if (data.unique) { + var mergedTrack = { + codec: '', + levelCodec: '' + }; + for (trackName in data.tracks) { + track = tracks[trackName]; + mergedTrack.container = track.container; + if (mergedTrack.codec) { + mergedTrack.codec += ','; + mergedTrack.levelCodec += ','; + } + if (track.codec) { + mergedTrack.codec += track.codec; + } + if (track.levelCodec) { + mergedTrack.levelCodec += track.levelCodec; + } + } + tracks = { audiovideo: mergedTrack }; + } + this.hls.trigger(_events2.default.BUFFER_CODECS, tracks); + // loop through tracks that are going to be provided to bufferController + for (trackName in tracks) { + track = tracks[trackName]; + _logger.logger.log('main track:' + trackName + ',container:' + track.container + ',codecs[level/parsed]=[' + track.levelCodec + '/' + track.codec + ']'); + var initSegment = track.initSegment; + if (initSegment) { + this.pendingAppending++; + this.hls.trigger(_events2.default.BUFFER_APPENDING, { type: trackName, data: initSegment, parent: 'main', content: 'initSegment' }); + } + } + //trigger handler right now + this.tick(); + } + } + }, { + key: 'onFragParsingData', + value: function onFragParsingData(data) { + var _this2 = this; + + var fragCurrent = this.fragCurrent; + if (fragCurrent && data.id === 'main' && data.sn === fragCurrent.sn && data.level === fragCurrent.level && !(data.type === 'audio' && this.altAudio) && // filter out main audio if audio track is loaded through audio stream controller + this.state === State.PARSING) { + var level = this.levels[this.level], + frag = this.fragCurrent; + + _logger.logger.log('Parsed ' + data.type + ',PTS:[' + data.startPTS.toFixed(3) + ',' + data.endPTS.toFixed(3) + '],DTS:[' + data.startDTS.toFixed(3) + '/' + data.endDTS.toFixed(3) + '],nb:' + data.nb + ',dropped:' + (data.dropped || 0)); + + var drift = _levelHelper2.default.updateFragPTSDTS(level.details, frag.sn, data.startPTS, data.endPTS, data.startDTS, data.endDTS), + hls = this.hls; + hls.trigger(_events2.default.LEVEL_PTS_UPDATED, { details: level.details, level: this.level, drift: drift }); + + // has remuxer dropped video frames located before first keyframe ? + if (data.type === 'video') { + frag.dropped = data.dropped; + } + + [data.data1, data.data2].forEach(function (buffer) { + if (buffer) { + _this2.pendingAppending++; + hls.trigger(_events2.default.BUFFER_APPENDING, { type: data.type, data: buffer, parent: 'main', content: 'data' }); + } + }); + + this.nextLoadPosition = data.endPTS; + this.bufferRange.push({ type: data.type, start: data.startPTS, end: data.endPTS, frag: frag }); + + //trigger handler right now + this.tick(); + } + } + }, { + key: 'onFragParsed', + value: function onFragParsed(data) { + var fragCurrent = this.fragCurrent; + if (fragCurrent && data.id === 'main' && data.sn === fragCurrent.sn && data.level === fragCurrent.level && this.state === State.PARSING) { + this.stats.tparsed = performance.now(); + this.state = State.PARSED; + this._checkAppendedParsed(); + } + } + }, { + key: 'onAudioTrackSwitch', + value: function onAudioTrackSwitch(data) { + // if any URL found on new audio track, it is an alternate audio track + var altAudio = !!data.url; + // if we switch on main audio, ensure that main fragment scheduling is synced with media.buffered + if (!altAudio) { + if (this.mediaBuffer !== this.media) { + _logger.logger.log('switching on main audio, use media.buffered to schedule main fragment loading'); + this.mediaBuffer = this.media; + var fragCurrent = this.fragCurrent; + // we need to refill audio buffer from main: cancel any frag loading to speed up audio switch + if (fragCurrent.loader) { + _logger.logger.log('switching to main audio track, cancel main fragment load'); + fragCurrent.loader.abort(); + } + this.fragCurrent = null; + this.fragPrevious = null; + // destroy demuxer to force init segment generation (following audio switch) + if (this.demuxer) { + this.demuxer.destroy(); + this.demuxer = null; + } + // switch to IDLE state to load new fragment + this.state = State.IDLE; + } + } else { + // if we switch on alternate audio, ensure that main fragment scheduling is synced with video sourcebuffer buffered + if (this.videoBuffer && this.mediaBuffer !== this.videoBuffer) { + _logger.logger.log('switching on alternate audio, use video.buffered to schedule main fragment loading'); + this.mediaBuffer = this.videoBuffer; + } + } + this.altAudio = altAudio; + } + }, { + key: 'onBufferCreated', + value: function onBufferCreated(data) { + var tracks = data.tracks, + mediaTrack = void 0, + name = void 0, + alternate = false; + for (var type in tracks) { + var track = tracks[type]; + if (track.id === 'main') { + name = type; + mediaTrack = track; + // keep video source buffer reference + if (type === 'video') { + this.videoBuffer = tracks[type].buffer; + } + } else { + alternate = true; + } + } + if (alternate && mediaTrack) { + _logger.logger.log('alternate track found, use ' + name + '.buffered to schedule main fragment loading'); + this.mediaBuffer = mediaTrack.buffer; + } else { + this.mediaBuffer = this.media; + } + } + }, { + key: 'onBufferAppended', + value: function onBufferAppended(data) { + if (data.parent === 'main') { + switch (this.state) { + case State.PARSING: + case State.PARSED: + this.pendingAppending--; + this._checkAppendedParsed(); + break; + default: + break; + } + } + } + }, { + key: '_checkAppendedParsed', + value: function _checkAppendedParsed() { + //trigger handler right now + if (this.state === State.PARSED && this.pendingAppending === 0) { + var frag = this.fragCurrent, + stats = this.stats; + if (frag) { + this.fragPrevious = frag; + stats.tbuffered = performance.now(); + // we should get rid of this.fragLastKbps + this.fragLastKbps = Math.round(8 * stats.total / (stats.tbuffered - stats.tfirst)); + this.hls.trigger(_events2.default.FRAG_BUFFERED, { stats: stats, frag: frag, id: 'main' }); + var media = this.mediaBuffer ? this.mediaBuffer : this.media; + _logger.logger.log('main buffered : ' + _timeRanges2.default.toString(media.buffered)); + this.state = State.IDLE; + } + this.tick(); + } + } + }, { + key: 'onError', + value: function onError(data) { + var frag = data.frag || this.fragCurrent; + // don't handle frag error not related to main fragment + if (frag && frag.type !== 'main') { + return; + } + var media = this.media, + + // 0.5 : tolerance needed as some browsers stalls playback before reaching buffered end + mediaBuffered = media && _bufferHelper2.default.isBuffered(media, media.currentTime) && _bufferHelper2.default.isBuffered(media, media.currentTime + 0.5); + switch (data.details) { + case _errors.ErrorDetails.FRAG_LOAD_ERROR: + case _errors.ErrorDetails.FRAG_LOAD_TIMEOUT: + if (!data.fatal) { + var loadError = this.fragLoadError; + if (loadError) { + loadError++; + } else { + loadError = 1; + } + var config = this.config; + // keep retrying / don't raise fatal network error if current position is buffered + if (loadError <= config.fragLoadingMaxRetry || mediaBuffered) { + this.fragLoadError = loadError; + // reset load counter to avoid frag loop loading error + frag.loadCounter = 0; + // exponential backoff capped to config.fragLoadingMaxRetryTimeout + var delay = Math.min(Math.pow(2, loadError - 1) * config.fragLoadingRetryDelay, config.fragLoadingMaxRetryTimeout); + _logger.logger.warn('mediaController: frag loading failed, retry in ' + delay + ' ms'); + this.retryDate = performance.now() + delay; + // retry loading state + this.state = State.FRAG_LOADING_WAITING_RETRY; + } else { + _logger.logger.error('mediaController: ' + data.details + ' reaches max retry, redispatch as fatal ...'); + // redispatch same error but with fatal set to true + data.fatal = true; + this.hls.trigger(_events2.default.ERROR, data); + this.state = State.ERROR; + } + } + break; + case _errors.ErrorDetails.FRAG_LOOP_LOADING_ERROR: + if (!data.fatal) { + // if buffer is not empty + if (mediaBuffered) { + // try to reduce max buffer length : rationale is that we could get + // frag loop loading error because of buffer eviction + this._reduceMaxBufferLength(frag.duration); + this.state = State.IDLE; + } else { + // buffer empty. report as fatal if in manual mode or if lowest level. + // level controller takes care of emergency switch down logic + if (!frag.autoLevel || frag.level === 0) { + // redispatch same error but with fatal set to true + data.fatal = true; + this.hls.trigger(_events2.default.ERROR, data); + this.state = State.ERROR; + } + } + } + break; + case _errors.ErrorDetails.LEVEL_LOAD_ERROR: + case _errors.ErrorDetails.LEVEL_LOAD_TIMEOUT: + case _errors.ErrorDetails.KEY_LOAD_ERROR: + case _errors.ErrorDetails.KEY_LOAD_TIMEOUT: + // when in ERROR state, don't switch back to IDLE state in case a non-fatal error is received + if (this.state !== State.ERROR) { + // if fatal error, stop processing, otherwise move to IDLE to retry loading + this.state = data.fatal ? State.ERROR : State.IDLE; + _logger.logger.warn('mediaController: ' + data.details + ' while loading frag,switch to ' + this.state + ' state ...'); + } + break; + case _errors.ErrorDetails.BUFFER_FULL_ERROR: + // if in appending state + if (this.state === State.PARSING || this.state === State.PARSED) { + // reduce max buf len if current position is buffered + if (mediaBuffered) { + this._reduceMaxBufferLength(frag.duration); + this.state = State.IDLE; + } else { + // current position is not buffered, but browser is still complaining about buffer full error + // this happens on IE/Edge, refer to https://github.com/dailymotion/hls.js/pull/708 + // in that case flush the whole buffer to recover + _logger.logger.warn('buffer full error also media.currentTime is not buffered, flush everything'); + this.fragCurrent = null; + this.state = State.PAUSED; + // flush everything + this.hls.trigger(_events2.default.BUFFER_FLUSHING, { startOffset: 0, endOffset: Number.POSITIVE_INFINITY }); + } + } + break; + default: + break; + } + } + }, { + key: '_reduceMaxBufferLength', + value: function _reduceMaxBufferLength(minLength) { + var config = this.config; + if (config.maxMaxBufferLength >= minLength) { + // reduce max buffer length as it might be too high. we do this to avoid loop flushing ... + config.maxMaxBufferLength /= 2; + _logger.logger.warn('reduce max buffer length to ' + config.maxMaxBufferLength + 's and switch to IDLE state'); + // increase fragment load Index to avoid frag loop loading error after buffer flush + this.fragLoadIdx += 2 * config.fragLoadingLoopThreshold; + } + } + }, { + key: '_checkBuffer', + value: function _checkBuffer() { + var media = this.media; + // if ready state different from HAVE_NOTHING (numeric value 0), we are allowed to seek + if (media && media.readyState) { + var currentTime = media.currentTime, + buffered = media.buffered; + // adjust currentTime to start position on loaded metadata + if (!this.loadedmetadata && buffered.length) { + this.loadedmetadata = true; + // only adjust currentTime if different from startPosition or if startPosition not buffered + // at that stage, there should be only one buffered range, as we reach that code after first fragment has been buffered + var startPosition = this.startPosition, + startPositionBuffered = _bufferHelper2.default.isBuffered(media, startPosition); + // if currentTime not matching with expected startPosition or startPosition not buffered + if (currentTime !== startPosition || !startPositionBuffered) { + _logger.logger.log('target start position:' + startPosition); + // if startPosition not buffered, let's seek to buffered.start(0) + if (!startPositionBuffered) { + startPosition = buffered.start(0); + _logger.logger.log('target start position not buffered, seek to buffered.start(0) ' + startPosition); + } + _logger.logger.log('adjust currentTime from ' + currentTime + ' to ' + startPosition); + media.currentTime = startPosition; + } + } else if (this.immediateSwitch) { + this.immediateLevelSwitchEnd(); + } else { + var bufferInfo = _bufferHelper2.default.bufferInfo(media, currentTime, 0), + expectedPlaying = !(media.paused || // not playing when media is paused + media.ended || // not playing when media is ended + media.buffered.length === 0), + // not playing if nothing buffered + jumpThreshold = 0.5, + // tolerance needed as some browsers stalls playback before reaching buffered range end + playheadMoving = currentTime > media.playbackRate * this.lastCurrentTime, + config = this.config; + + if (this.stalled && playheadMoving) { + this.stalled = false; + _logger.logger.log('playback not stuck anymore @' + currentTime); + } + // check buffer upfront + // if less than jumpThreshold second is buffered, let's check in more details + if (expectedPlaying && bufferInfo.len <= jumpThreshold) { + if (playheadMoving) { + // playhead moving + jumpThreshold = 0; + this.seekHoleNudgeDuration = 0; + } else { + // playhead not moving AND media expected to play + if (!this.stalled) { + this.seekHoleNudgeDuration = 0; + _logger.logger.log('playback seems stuck @' + currentTime); + this.hls.trigger(_events2.default.ERROR, { type: _errors.ErrorTypes.MEDIA_ERROR, details: _errors.ErrorDetails.BUFFER_STALLED_ERROR, fatal: false }); + this.stalled = true; + } else { + this.seekHoleNudgeDuration += config.seekHoleNudgeDuration; + } + } + // if we are below threshold, try to jump to start of next buffer range if close + if (bufferInfo.len <= jumpThreshold) { + // no buffer available @ currentTime, check if next buffer is close (within a config.maxSeekHole second range) + var nextBufferStart = bufferInfo.nextStart, + delta = nextBufferStart - currentTime; + if (nextBufferStart && delta < config.maxSeekHole && delta > 0) { + // next buffer is close ! adjust currentTime to nextBufferStart + // this will ensure effective video decoding + _logger.logger.log('adjust currentTime from ' + media.currentTime + ' to next buffered @ ' + nextBufferStart + ' + nudge ' + this.seekHoleNudgeDuration); + var hole = nextBufferStart + this.seekHoleNudgeDuration - media.currentTime; + media.currentTime = nextBufferStart + this.seekHoleNudgeDuration; + this.hls.trigger(_events2.default.ERROR, { type: _errors.ErrorTypes.MEDIA_ERROR, details: _errors.ErrorDetails.BUFFER_SEEK_OVER_HOLE, fatal: false, hole: hole }); + } + } + } + } + } + } + }, { + key: 'onFragLoadEmergencyAborted', + value: function onFragLoadEmergencyAborted() { + this.state = State.IDLE; + // if loadedmetadata is not set, it means that we are emergency switch down on first frag + // in that case, reset startFragRequested flag + if (!this.loadedmetadata) { + this.startFragRequested = false; + } + this.tick(); + } + }, { + key: 'onBufferFlushed', + value: function onBufferFlushed() { + /* after successful buffer flushing, rebuild buffer Range array + loop through existing buffer range and check if + corresponding range is still buffered. only push to new array already buffered range + use mediaBuffered instead of media (so that we will check against video.buffered ranges in case of alt audio track) + */ + var media = this.mediaBuffer ? this.mediaBuffer : this.media, + bufferRange = this.bufferRange, + newRange = [], + range = void 0, + i = void 0; + for (i = 0; i < bufferRange.length; i++) { + range = bufferRange[i]; + if (_bufferHelper2.default.isBuffered(media, (range.start + range.end) / 2)) { + newRange.push(range); + } + } + this.bufferRange = newRange; + + // increase fragment load Index to avoid frag loop loading error after buffer flush + this.fragLoadIdx += 2 * this.config.fragLoadingLoopThreshold; + // move to IDLE once flush complete. this should trigger new fragment loading + this.state = State.IDLE; + // reset reference to frag + this.fragPrevious = null; + } + }, { + key: 'swapAudioCodec', + value: function swapAudioCodec() { + this.audioCodecSwap = !this.audioCodecSwap; + } + }, { + key: 'computeLivePosition', + value: function computeLivePosition(sliding, levelDetails) { + var targetLatency = this.config.liveSyncDuration !== undefined ? this.config.liveSyncDuration : this.config.liveSyncDurationCount * levelDetails.targetduration; + return sliding + Math.max(0, levelDetails.totalduration - targetLatency); + } + }, { + key: 'state', + set: function set(nextState) { + if (this.state !== nextState) { + var previousState = this.state; + this._state = nextState; + _logger.logger.log('engine state transition from ' + previousState + ' to ' + nextState); + this.hls.trigger(_events2.default.STREAM_STATE_TRANSITION, { previousState: previousState, nextState: nextState }); + } + }, + get: function get() { + return this._state; + } + }, { + key: 'currentLevel', + get: function get() { + var media = this.media; + if (media) { + var range = this.getBufferRange(media.currentTime); + if (range) { + return range.frag.level; + } + } + return -1; + } + }, { + key: 'nextBufferRange', + get: function get() { + var media = this.media; + if (media) { + // first get end range of current fragment + return this.followingBufferRange(this.getBufferRange(media.currentTime)); + } else { + return null; + } + } + }, { + key: 'nextLevel', + get: function get() { + var range = this.nextBufferRange; + if (range) { + return range.frag.level; + } else { + return -1; + } + } + }, { + key: 'liveSyncPosition', + get: function get() { + return this._liveSyncPosition; + }, + set: function set(value) { + this._liveSyncPosition = value; + } + }]); + + return StreamController; + }(_eventHandler2.default); + + exports.default = StreamController; + + },{"20":20,"24":24,"25":25,"26":26,"28":28,"29":29,"39":39,"43":43,"45":45}],12:[function(_dereq_,module,exports){ + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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 _events = _dereq_(26); + + var _events2 = _interopRequireDefault(_events); + + var _eventHandler = _dereq_(25); + + var _eventHandler2 = _interopRequireDefault(_eventHandler); + + var _cea608Parser = _dereq_(40); + + var _cea608Parser2 = _interopRequireDefault(_cea608Parser); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + 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; } /* + * Timeline Controller + */ + + var TimelineController = function (_EventHandler) { + _inherits(TimelineController, _EventHandler); + + function TimelineController(hls) { + _classCallCheck(this, TimelineController); + + var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(TimelineController).call(this, hls, _events2.default.MEDIA_ATTACHING, _events2.default.MEDIA_DETACHING, _events2.default.FRAG_PARSING_USERDATA, _events2.default.MANIFEST_LOADING, _events2.default.FRAG_LOADED, _events2.default.LEVEL_SWITCH)); + + _this.hls = hls; + _this.config = hls.config; + _this.enabled = true; + _this.Cues = hls.config.cueHandler; + + if (_this.config.enableCEA708Captions) { + var self = _this; + + var channel1 = { + 'newCue': function newCue(startTime, endTime, screen) { + if (!self.textTrack1) { + //Enable reuse of existing text track. + var existingTrack1 = self.getExistingTrack('1'); + if (!existingTrack1) { + self.textTrack1 = self.createTextTrack('captions', 'English', 'en'); + self.textTrack1.textTrack1 = true; + } else { + self.textTrack1 = existingTrack1; + self.clearCurrentCues(self.textTrack1); + + var e = new window.Event('addtrack'); + e.track = self.textTrack1; + self.media.dispatchEvent(e); + } + } + + self.Cues.newCue(self.textTrack1, startTime, endTime, screen); + } + }; + + var channel2 = { + 'newCue': function newCue(startTime, endTime, screen) { + if (!self.textTrack2) { + //Enable reuse of existing text track. + var existingTrack2 = self.getExistingTrack('2'); + if (!existingTrack2) { + self.textTrack2 = self.createTextTrack('captions', 'Spanish', 'es'); + self.textTrack2.textTrack2 = true; + } else { + self.textTrack2 = existingTrack2; + self.clearCurrentCues(self.textTrack2); + + var e = new window.Event('addtrack'); + e.track = self.textTrack2; + self.media.dispatchEvent(e); + } + } + + self.Cues.newCue(self.textTrack2, startTime, endTime, screen); + } + }; + + _this.cea608Parser = new _cea608Parser2.default(0, channel1, channel2); + } + return _this; + } + + _createClass(TimelineController, [{ + key: 'clearCurrentCues', + value: function clearCurrentCues(track) { + if (track && track.cues) { + while (track.cues.length > 0) { + track.removeCue(track.cues[0]); + } + } + } + }, { + key: 'getExistingTrack', + value: function getExistingTrack(channelNumber) { + var media = this.media; + if (media) { + for (var i = 0; i < media.textTracks.length; i++) { + var textTrack = media.textTracks[i]; + var propName = 'textTrack' + channelNumber; + if (textTrack[propName] === true) { + return textTrack; + } + } + } + return null; + } + }, { + key: 'createTextTrack', + value: function createTextTrack(kind, label, lang) { + if (this.media) { + return this.media.addTextTrack(kind, label, lang); + } + } + }, { + key: 'destroy', + value: function destroy() { + _eventHandler2.default.prototype.destroy.call(this); + } + }, { + key: 'onMediaAttaching', + value: function onMediaAttaching(data) { + this.media = data.media; + } + }, { + key: 'onMediaDetaching', + value: function onMediaDetaching() {} + }, { + key: 'onManifestLoading', + value: function onManifestLoading() { + this.lastPts = Number.NEGATIVE_INFINITY; + } + }, { + key: 'onLevelSwitch', + value: function onLevelSwitch() { + if (this.hls.currentLevel.closedCaptions === 'NONE') { + this.enabled = false; + } else { + this.enabled = true; + } + } + }, { + key: 'onFragLoaded', + value: function onFragLoaded(data) { + if (data.frag.type === 'main') { + var pts = data.frag.start; //Number.POSITIVE_INFINITY; + // if this is a frag for a previously loaded timerange, remove all captions + // TODO: consider just removing captions for the timerange + if (pts <= this.lastPts) { + this.clearCurrentCues(this.textTrack1); + this.clearCurrentCues(this.textTrack2); + } + this.lastPts = pts; + } + } + }, { + key: 'onFragParsingUserdata', + value: function onFragParsingUserdata(data) { + // push all of the CEA-708 messages into the interpreter + // immediately. It will create the proper timestamps based on our PTS value + if (this.enabled) { + for (var i = 0; i < data.samples.length; i++) { + var ccdatas = this.extractCea608Data(data.samples[i].bytes); + this.cea608Parser.addData(data.samples[i].pts, ccdatas); + } + } + } + }, { + key: 'extractCea608Data', + value: function extractCea608Data(byteArray) { + var count = byteArray[0] & 31; + var position = 2; + var tmpByte, ccbyte1, ccbyte2, ccValid, ccType; + var actualCCBytes = []; + + for (var j = 0; j < count; j++) { + tmpByte = byteArray[position++]; + ccbyte1 = 0x7F & byteArray[position++]; + ccbyte2 = 0x7F & byteArray[position++]; + ccValid = (4 & tmpByte) === 0 ? false : true; + ccType = 3 & tmpByte; + + if (ccbyte1 === 0 && ccbyte2 === 0) { + continue; + } + + if (ccValid) { + if (ccType === 0) // || ccType === 1 + { + actualCCBytes.push(ccbyte1); + actualCCBytes.push(ccbyte2); + } + } + } + return actualCCBytes; + } + }]); + + return TimelineController; + }(_eventHandler2.default); + + exports.default = TimelineController; + + },{"25":25,"26":26,"40":40}],13:[function(_dereq_,module,exports){ + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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; }; }(); + + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + + /* + * + * This file contains an adaptation of the AES decryption algorithm + * from the Standford Javascript Cryptography Library. That work is + * covered by the following copyright and permissions notice: + * + * Copyright 2009-2010 Emily Stark, Mike Hamburg, Dan Boneh. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation + * are those of the authors and should not be interpreted as representing + * official policies, either expressed or implied, of the authors. + */ + + var AES = function () { + + /** + * Schedule out an AES key for both encryption and decryption. This + * is a low-level class. Use a cipher mode to do bulk encryption. + * + * @constructor + * @param key {Array} The key as an array of 4, 6 or 8 words. + */ + + function AES(key) { + _classCallCheck(this, AES); + + /** + * The expanded S-box and inverse S-box tables. These will be computed + * on the client so that we don't have to send them down the wire. + * + * There are two tables, _tables[0] is for encryption and + * _tables[1] is for decryption. + * + * The first 4 sub-tables are the expanded S-box with MixColumns. The + * last (_tables[01][4]) is the S-box itself. + * + * @private + */ + this._tables = [[[], [], [], [], []], [[], [], [], [], []]]; + + this._precompute(); + + var i, + j, + tmp, + encKey, + decKey, + sbox = this._tables[0][4], + decTable = this._tables[1], + keyLen = key.length, + rcon = 1; + + if (keyLen !== 4 && keyLen !== 6 && keyLen !== 8) { + throw new Error('Invalid aes key size=' + keyLen); + } + + encKey = key.slice(0); + decKey = []; + this._key = [encKey, decKey]; + + // schedule encryption keys + for (i = keyLen; i < 4 * keyLen + 28; i++) { + tmp = encKey[i - 1]; + + // apply sbox + if (i % keyLen === 0 || keyLen === 8 && i % keyLen === 4) { + tmp = sbox[tmp >>> 24] << 24 ^ sbox[tmp >> 16 & 255] << 16 ^ sbox[tmp >> 8 & 255] << 8 ^ sbox[tmp & 255]; + + // shift rows and add rcon + if (i % keyLen === 0) { + tmp = tmp << 8 ^ tmp >>> 24 ^ rcon << 24; + rcon = rcon << 1 ^ (rcon >> 7) * 283; + } + } + + encKey[i] = encKey[i - keyLen] ^ tmp; + } + + // schedule decryption keys + for (j = 0; i; j++, i--) { + tmp = encKey[j & 3 ? i : i - 4]; + if (i <= 4 || j < 4) { + decKey[j] = tmp; + } else { + decKey[j] = decTable[0][sbox[tmp >>> 24]] ^ decTable[1][sbox[tmp >> 16 & 255]] ^ decTable[2][sbox[tmp >> 8 & 255]] ^ decTable[3][sbox[tmp & 255]]; + } + } + } + + /** + * Expand the S-box tables. + * + * @private + */ + + + _createClass(AES, [{ + key: '_precompute', + value: function _precompute() { + var encTable = this._tables[0], + decTable = this._tables[1], + sbox = encTable[4], + sboxInv = decTable[4], + i, + x, + xInv, + d = [], + th = [], + x2, + x4, + x8, + s, + tEnc, + tDec; + + // Compute double and third tables + for (i = 0; i < 256; i++) { + th[(d[i] = i << 1 ^ (i >> 7) * 283) ^ i] = i; + } + + for (x = xInv = 0; !sbox[x]; x ^= x2 || 1, xInv = th[xInv] || 1) { + // Compute sbox + s = xInv ^ xInv << 1 ^ xInv << 2 ^ xInv << 3 ^ xInv << 4; + s = s >> 8 ^ s & 255 ^ 99; + sbox[x] = s; + sboxInv[s] = x; + + // Compute MixColumns + x8 = d[x4 = d[x2 = d[x]]]; + tDec = x8 * 0x1010101 ^ x4 * 0x10001 ^ x2 * 0x101 ^ x * 0x1010100; + tEnc = d[s] * 0x101 ^ s * 0x1010100; + + for (i = 0; i < 4; i++) { + encTable[i][x] = tEnc = tEnc << 24 ^ tEnc >>> 8; + decTable[i][s] = tDec = tDec << 24 ^ tDec >>> 8; + } + } + + // Compactify. Considerable speedup on Firefox. + for (i = 0; i < 5; i++) { + encTable[i] = encTable[i].slice(0); + decTable[i] = decTable[i].slice(0); + } + } + + /** + * Decrypt 16 bytes, specified as four 32-bit words. + * @param encrypted0 {number} the first word to decrypt + * @param encrypted1 {number} the second word to decrypt + * @param encrypted2 {number} the third word to decrypt + * @param encrypted3 {number} the fourth word to decrypt + * @param out {Int32Array} the array to write the decrypted words + * into + * @param offset {number} the offset into the output array to start + * writing results + * @return {Array} The plaintext. + */ + + }, { + key: 'decrypt', + value: function decrypt(encrypted0, encrypted1, encrypted2, encrypted3, out, offset) { + var key = this._key[1], + + // state variables a,b,c,d are loaded with pre-whitened data + a = encrypted0 ^ key[0], + b = encrypted3 ^ key[1], + c = encrypted2 ^ key[2], + d = encrypted1 ^ key[3], + a2, + b2, + c2, + nInnerRounds = key.length / 4 - 2, + // key.length === 2 ? + i, + kIndex = 4, + table = this._tables[1], + + + // load up the tables + table0 = table[0], + table1 = table[1], + table2 = table[2], + table3 = table[3], + sbox = table[4]; + + // Inner rounds. Cribbed from OpenSSL. + for (i = 0; i < nInnerRounds; i++) { + a2 = table0[a >>> 24] ^ table1[b >> 16 & 255] ^ table2[c >> 8 & 255] ^ table3[d & 255] ^ key[kIndex]; + b2 = table0[b >>> 24] ^ table1[c >> 16 & 255] ^ table2[d >> 8 & 255] ^ table3[a & 255] ^ key[kIndex + 1]; + c2 = table0[c >>> 24] ^ table1[d >> 16 & 255] ^ table2[a >> 8 & 255] ^ table3[b & 255] ^ key[kIndex + 2]; + d = table0[d >>> 24] ^ table1[a >> 16 & 255] ^ table2[b >> 8 & 255] ^ table3[c & 255] ^ key[kIndex + 3]; + kIndex += 4; + a = a2;b = b2;c = c2; + } + + // Last round. + for (i = 0; i < 4; i++) { + out[(3 & -i) + offset] = sbox[a >>> 24] << 24 ^ sbox[b >> 16 & 255] << 16 ^ sbox[c >> 8 & 255] << 8 ^ sbox[d & 255] ^ key[kIndex++]; + a2 = a;a = b;b = c;c = d;d = a2; + } + } + }]); + + return AES; + }(); + + exports.default = AES; + + },{}],14:[function(_dereq_,module,exports){ + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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; }; }(); /* + * + * This file contains an adaptation of the AES decryption algorithm + * from the Standford Javascript Cryptography Library. That work is + * covered by the following copyright and permissions notice: + * + * Copyright 2009-2010 Emily Stark, Mike Hamburg, Dan Boneh. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation + * are those of the authors and should not be interpreted as representing + * official policies, either expressed or implied, of the authors. + */ + + var _aes = _dereq_(13); + + var _aes2 = _interopRequireDefault(_aes); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + + var AES128Decrypter = function () { + function AES128Decrypter(key, initVector) { + _classCallCheck(this, AES128Decrypter); + + this.key = key; + this.iv = initVector; + } + + /** + * Convert network-order (big-endian) bytes into their little-endian + * representation. + */ + + + _createClass(AES128Decrypter, [{ + key: 'ntoh', + value: function ntoh(word) { + return word << 24 | (word & 0xff00) << 8 | (word & 0xff0000) >> 8 | word >>> 24; + } + + /** + * Decrypt bytes using AES-128 with CBC and PKCS#7 padding. + * @param encrypted {Uint8Array} the encrypted bytes + * @param key {Uint32Array} the bytes of the decryption key + * @param initVector {Uint32Array} the initialization vector (IV) to + * use for the first round of CBC. + * @return {Uint8Array} the decrypted bytes + * + * @see http://en.wikipedia.org/wiki/Advanced_Encryption_Standard + * @see http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_Block_Chaining_.28CBC.29 + * @see https://tools.ietf.org/html/rfc2315 + */ + + }, { + key: 'doDecrypt', + value: function doDecrypt(encrypted, key, initVector) { + var + // word-level access to the encrypted bytes + encrypted32 = new Int32Array(encrypted.buffer, encrypted.byteOffset, encrypted.byteLength >> 2), + decipher = new _aes2.default(Array.prototype.slice.call(key)), + + + // byte and word-level access for the decrypted output + decrypted = new Uint8Array(encrypted.byteLength), + decrypted32 = new Int32Array(decrypted.buffer), + + + // temporary variables for working with the IV, encrypted, and + // decrypted data + init0, + init1, + init2, + init3, + encrypted0, + encrypted1, + encrypted2, + encrypted3, + + + // iteration variable + wordIx; + + // pull out the words of the IV to ensure we don't modify the + // passed-in reference and easier access + init0 = ~ ~initVector[0]; + init1 = ~ ~initVector[1]; + init2 = ~ ~initVector[2]; + init3 = ~ ~initVector[3]; + + // decrypt four word sequences, applying cipher-block chaining (CBC) + // to each decrypted block + for (wordIx = 0; wordIx < encrypted32.length; wordIx += 4) { + // convert big-endian (network order) words into little-endian + // (javascript order) + encrypted0 = ~ ~this.ntoh(encrypted32[wordIx]); + encrypted1 = ~ ~this.ntoh(encrypted32[wordIx + 1]); + encrypted2 = ~ ~this.ntoh(encrypted32[wordIx + 2]); + encrypted3 = ~ ~this.ntoh(encrypted32[wordIx + 3]); + + // decrypt the block + decipher.decrypt(encrypted0, encrypted1, encrypted2, encrypted3, decrypted32, wordIx); + + // XOR with the IV, and restore network byte-order to obtain the + // plaintext + decrypted32[wordIx] = this.ntoh(decrypted32[wordIx] ^ init0); + decrypted32[wordIx + 1] = this.ntoh(decrypted32[wordIx + 1] ^ init1); + decrypted32[wordIx + 2] = this.ntoh(decrypted32[wordIx + 2] ^ init2); + decrypted32[wordIx + 3] = this.ntoh(decrypted32[wordIx + 3] ^ init3); + + // setup the IV for the next round + init0 = encrypted0; + init1 = encrypted1; + init2 = encrypted2; + init3 = encrypted3; + } + + return decrypted; + } + }, { + key: 'localDecrypt', + value: function localDecrypt(encrypted, key, initVector, decrypted) { + var bytes = this.doDecrypt(encrypted, key, initVector); + decrypted.set(bytes, encrypted.byteOffset); + } + }, { + key: 'decrypt', + value: function decrypt(encrypted) { + var step = 4 * 8000, + + //encrypted32 = new Int32Array(encrypted.buffer), + encrypted32 = new Int32Array(encrypted), + decrypted = new Uint8Array(encrypted.byteLength), + i = 0; + + // split up the encryption job and do the individual chunks asynchronously + var key = this.key; + var initVector = this.iv; + this.localDecrypt(encrypted32.subarray(i, i + step), key, initVector, decrypted); + + for (i = step; i < encrypted32.length; i += step) { + initVector = new Uint32Array([this.ntoh(encrypted32[i - 4]), this.ntoh(encrypted32[i - 3]), this.ntoh(encrypted32[i - 2]), this.ntoh(encrypted32[i - 1])]); + this.localDecrypt(encrypted32.subarray(i, i + step), key, initVector, decrypted); + } + + return decrypted; + } + }]); + + return AES128Decrypter; + }(); + + exports.default = AES128Decrypter; + + },{"13":13}],15:[function(_dereq_,module,exports){ + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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; }; }(); /* + * AES128 decryption. + */ + + var _aes128Decrypter = _dereq_(14); + + var _aes128Decrypter2 = _interopRequireDefault(_aes128Decrypter); + + var _errors = _dereq_(24); + + var _logger = _dereq_(43); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + + var Decrypter = function () { + function Decrypter(hls) { + _classCallCheck(this, Decrypter); + + this.hls = hls; + try { + var browserCrypto = window ? window.crypto : crypto; + this.subtle = browserCrypto.subtle || browserCrypto.webkitSubtle; + this.disableWebCrypto = !this.subtle; + } catch (e) { + this.disableWebCrypto = true; + } + } + + _createClass(Decrypter, [{ + key: 'destroy', + value: function destroy() {} + }, { + key: 'decrypt', + value: function decrypt(data, key, iv, callback) { + if (this.disableWebCrypto && this.hls.config.enableSoftwareAES) { + this.decryptBySoftware(data, key, iv, callback); + } else { + this.decryptByWebCrypto(data, key, iv, callback); + } + } + }, { + key: 'decryptByWebCrypto', + value: function decryptByWebCrypto(data, key, iv, callback) { + var _this = this; + + _logger.logger.log('decrypting by WebCrypto API'); + + this.subtle.importKey('raw', key, { name: 'AES-CBC', length: 128 }, false, ['decrypt']).then(function (importedKey) { + _this.subtle.decrypt({ name: 'AES-CBC', iv: iv.buffer }, importedKey, data).then(callback).catch(function (err) { + _this.onWebCryptoError(err, data, key, iv, callback); + }); + }).catch(function (err) { + _this.onWebCryptoError(err, data, key, iv, callback); + }); + } + }, { + key: 'decryptBySoftware', + value: function decryptBySoftware(data, key8, iv8, callback) { + _logger.logger.log('decrypting by JavaScript Implementation'); + + var view = new DataView(key8.buffer); + var key = new Uint32Array([view.getUint32(0), view.getUint32(4), view.getUint32(8), view.getUint32(12)]); + + view = new DataView(iv8.buffer); + var iv = new Uint32Array([view.getUint32(0), view.getUint32(4), view.getUint32(8), view.getUint32(12)]); + + var decrypter = new _aes128Decrypter2.default(key, iv); + callback(decrypter.decrypt(data).buffer); + } + }, { + key: 'onWebCryptoError', + value: function onWebCryptoError(err, data, key, iv, callback) { + if (this.hls.config.enableSoftwareAES) { + _logger.logger.log('disabling to use WebCrypto API'); + this.disableWebCrypto = true; + this.decryptBySoftware(data, key, iv, callback); + } else { + _logger.logger.error('decrypting error : ' + err.message); + this.hls.trigger(Event.ERROR, { type: _errors.ErrorTypes.MEDIA_ERROR, details: _errors.ErrorDetails.FRAG_DECRYPT_ERROR, fatal: true, reason: err.message }); + } + } + }]); + + return Decrypter; + }(); + + exports.default = Decrypter; + + },{"14":14,"24":24,"43":43}],16:[function(_dereq_,module,exports){ + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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; }; }(); /** + * AAC demuxer + */ + + + var _adts = _dereq_(17); + + var _adts2 = _interopRequireDefault(_adts); + + var _logger = _dereq_(43); + + var _id = _dereq_(22); + + var _id2 = _interopRequireDefault(_id); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + + var AACDemuxer = function () { + function AACDemuxer(observer, id, remuxerClass, config) { + _classCallCheck(this, AACDemuxer); + + this.observer = observer; + this.id = id; + this.remuxerClass = remuxerClass; + this.config = config; + this.remuxer = new this.remuxerClass(observer, id, config); + this.insertDiscontinuity(); + } + + _createClass(AACDemuxer, [{ + key: 'insertDiscontinuity', + value: function insertDiscontinuity() { + this._aacTrack = { container: 'audio/adts', type: 'audio', id: -1, sequenceNumber: 0, samples: [], len: 0 }; + } + }, { + key: 'push', + + + // feed incoming data to the front of the parsing pipeline + value: function push(data, audioCodec, videoCodec, timeOffset, cc, level, sn, duration, accurateTimeOffset) { + var track, + id3 = new _id2.default(data), + pts = 90 * id3.timeStamp, + config, + frameLength, + frameDuration, + frameIndex, + offset, + headerLength, + stamp, + len, + aacSample; + + var contiguous = false; + if (cc !== this.lastCC) { + _logger.logger.log(this.id + ' discontinuity detected'); + this.lastCC = cc; + this.insertDiscontinuity(); + this.remuxer.switchLevel(); + this.remuxer.insertDiscontinuity(); + } else if (level !== this.lastLevel) { + _logger.logger.log('audio track switch detected'); + this.lastLevel = level; + this.remuxer.switchLevel(); + this.insertDiscontinuity(); + } else if (sn === this.lastSN + 1) { + contiguous = true; + } + track = this._aacTrack; + this.lastSN = sn; + this.lastLevel = level; + + // look for ADTS header (0xFFFx) + for (offset = id3.length, len = data.length; offset < len - 1; offset++) { + if (data[offset] === 0xff && (data[offset + 1] & 0xf0) === 0xf0) { + break; + } + } + + if (!track.audiosamplerate) { + config = _adts2.default.getAudioConfig(this.observer, data, offset, audioCodec); + track.config = config.config; + track.audiosamplerate = config.samplerate; + track.channelCount = config.channelCount; + track.codec = config.codec; + track.duration = duration; + _logger.logger.log('parsed codec:' + track.codec + ',rate:' + config.samplerate + ',nb channel:' + config.channelCount); + } + frameIndex = 0; + frameDuration = 1024 * 90000 / track.audiosamplerate; + while (offset + 5 < len) { + // The protection skip bit tells us if we have 2 bytes of CRC data at the end of the ADTS header + headerLength = !!(data[offset + 1] & 0x01) ? 7 : 9; + // retrieve frame size + frameLength = (data[offset + 3] & 0x03) << 11 | data[offset + 4] << 3 | (data[offset + 5] & 0xE0) >>> 5; + frameLength -= headerLength; + //stamp = pes.pts; + + if (frameLength > 0 && offset + headerLength + frameLength <= len) { + stamp = pts + frameIndex * frameDuration; + //logger.log(`AAC frame, offset/length/total/pts:${offset+headerLength}/${frameLength}/${data.byteLength}/${(stamp/90).toFixed(0)}`); + aacSample = { unit: data.subarray(offset + headerLength, offset + headerLength + frameLength), pts: stamp, dts: stamp }; + track.samples.push(aacSample); + track.len += frameLength; + offset += frameLength + headerLength; + frameIndex++; + // look for ADTS header (0xFFFx) + for (; offset < len - 1; offset++) { + if (data[offset] === 0xff && (data[offset + 1] & 0xf0) === 0xf0) { + break; + } + } + } else { + break; + } + } + this.remuxer.remux(level, sn, this._aacTrack, { samples: [] }, { samples: [{ pts: pts, dts: pts, unit: id3.payload }] }, { samples: [] }, timeOffset, contiguous, accurateTimeOffset); + } + }, { + key: 'destroy', + value: function destroy() {} + }], [{ + key: 'probe', + value: function probe(data) { + // check if data contains ID3 timestamp and ADTS sync worc + var id3 = new _id2.default(data), + offset, + len; + if (id3.hasTimeStamp) { + // look for ADTS header (0xFFFx) + for (offset = id3.length, len = data.length; offset < len - 1; offset++) { + if (data[offset] === 0xff && (data[offset + 1] & 0xf0) === 0xf0) { + //logger.log('ADTS sync word found !'); + return true; + } + } + } + return false; + } + }]); + + return AACDemuxer; + }(); + + exports.default = AACDemuxer; + + },{"17":17,"22":22,"43":43}],17:[function(_dereq_,module,exports){ + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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; }; }(); /** + * ADTS parser helper + */ + + + var _logger = _dereq_(43); + + var _errors = _dereq_(24); + + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + + var ADTS = function () { + function ADTS() { + _classCallCheck(this, ADTS); + } + + _createClass(ADTS, null, [{ + key: 'getAudioConfig', + value: function getAudioConfig(observer, data, offset, audioCodec) { + var adtsObjectType, + // :int + adtsSampleingIndex, + // :int + adtsExtensionSampleingIndex, + // :int + adtsChanelConfig, + // :int + config, + userAgent = navigator.userAgent.toLowerCase(), + adtsSampleingRates = [96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350]; + // byte 2 + adtsObjectType = ((data[offset + 2] & 0xC0) >>> 6) + 1; + adtsSampleingIndex = (data[offset + 2] & 0x3C) >>> 2; + if (adtsSampleingIndex > adtsSampleingRates.length - 1) { + observer.trigger(Event.ERROR, { type: _errors.ErrorTypes.MEDIA_ERROR, details: _errors.ErrorDetails.FRAG_PARSING_ERROR, fatal: true, reason: 'invalid ADTS sampling index:' + adtsSampleingIndex }); + return; + } + adtsChanelConfig = (data[offset + 2] & 0x01) << 2; + // byte 3 + adtsChanelConfig |= (data[offset + 3] & 0xC0) >>> 6; + _logger.logger.log('manifest codec:' + audioCodec + ',ADTS data:type:' + adtsObjectType + ',sampleingIndex:' + adtsSampleingIndex + '[' + adtsSampleingRates[adtsSampleingIndex] + 'Hz],channelConfig:' + adtsChanelConfig); + // firefox/Opera: freq less than 24kHz = AAC SBR (HE-AAC) + if (/firefox|OPR/i.test(userAgent)) { + if (adtsSampleingIndex >= 6) { + adtsObjectType = 5; + config = new Array(4); + // HE-AAC uses SBR (Spectral Band Replication) , high frequencies are constructed from low frequencies + // there is a factor 2 between frame sample rate and output sample rate + // multiply frequency by 2 (see table below, equivalent to substract 3) + adtsExtensionSampleingIndex = adtsSampleingIndex - 3; + } else { + adtsObjectType = 2; + config = new Array(2); + adtsExtensionSampleingIndex = adtsSampleingIndex; + } + // Android : always use AAC + } else if (userAgent.indexOf('android') !== -1) { + adtsObjectType = 2; + config = new Array(2); + adtsExtensionSampleingIndex = adtsSampleingIndex; + } else { + /* for other browsers (Chrome/Vivaldi ...) + always force audio type to be HE-AAC SBR, as some browsers do not support audio codec switch properly (like Chrome ...) + */ + adtsObjectType = 5; + config = new Array(4); + // if (manifest codec is HE-AAC or HE-AACv2) OR (manifest codec not specified AND frequency less than 24kHz) + if (audioCodec && (audioCodec.indexOf('mp4a.40.29') !== -1 || audioCodec.indexOf('mp4a.40.5') !== -1) || !audioCodec && adtsSampleingIndex >= 6) { + // HE-AAC uses SBR (Spectral Band Replication) , high frequencies are constructed from low frequencies + // there is a factor 2 between frame sample rate and output sample rate + // multiply frequency by 2 (see table below, equivalent to substract 3) + adtsExtensionSampleingIndex = adtsSampleingIndex - 3; + } else { + // if (manifest codec is AAC) AND (frequency less than 24kHz AND nb channel is 1) OR (manifest codec not specified and mono audio) + // Chrome fails to play back with low frequency AAC LC mono when initialized with HE-AAC. This is not a problem with stereo. + if (audioCodec && audioCodec.indexOf('mp4a.40.2') !== -1 && adtsSampleingIndex >= 6 && adtsChanelConfig === 1 || !audioCodec && adtsChanelConfig === 1) { + adtsObjectType = 2; + config = new Array(2); + } + adtsExtensionSampleingIndex = adtsSampleingIndex; + } + } + /* refer to http://wiki.multimedia.cx/index.php?title=MPEG-4_Audio#Audio_Specific_Config + ISO 14496-3 (AAC).pdf - Table 1.13 — Syntax of AudioSpecificConfig() + Audio Profile / Audio Object Type + 0: Null + 1: AAC Main + 2: AAC LC (Low Complexity) + 3: AAC SSR (Scalable Sample Rate) + 4: AAC LTP (Long Term Prediction) + 5: SBR (Spectral Band Replication) + 6: AAC Scalable + sampling freq + 0: 96000 Hz + 1: 88200 Hz + 2: 64000 Hz + 3: 48000 Hz + 4: 44100 Hz + 5: 32000 Hz + 6: 24000 Hz + 7: 22050 Hz + 8: 16000 Hz + 9: 12000 Hz + 10: 11025 Hz + 11: 8000 Hz + 12: 7350 Hz + 13: Reserved + 14: Reserved + 15: frequency is written explictly + Channel Configurations + These are the channel configurations: + 0: Defined in AOT Specifc Config + 1: 1 channel: front-center + 2: 2 channels: front-left, front-right + */ + // audioObjectType = profile => profile, the MPEG-4 Audio Object Type minus 1 + config[0] = adtsObjectType << 3; + // samplingFrequencyIndex + config[0] |= (adtsSampleingIndex & 0x0E) >> 1; + config[1] |= (adtsSampleingIndex & 0x01) << 7; + // channelConfiguration + config[1] |= adtsChanelConfig << 3; + if (adtsObjectType === 5) { + // adtsExtensionSampleingIndex + config[1] |= (adtsExtensionSampleingIndex & 0x0E) >> 1; + config[2] = (adtsExtensionSampleingIndex & 0x01) << 7; + // adtsObjectType (force to 2, chrome is checking that object type is less than 5 ??? + // https://chromium.googlesource.com/chromium/src.git/+/master/media/formats/mp4/aac.cc + config[2] |= 2 << 2; + config[3] = 0; + } + return { config: config, samplerate: adtsSampleingRates[adtsSampleingIndex], channelCount: adtsChanelConfig, codec: 'mp4a.40.' + adtsObjectType }; + } + }]); + + return ADTS; + }(); + + exports.default = ADTS; + + },{"24":24,"43":43}],18:[function(_dereq_,module,exports){ + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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; }; }(); /* inline demuxer. + * probe fragments and instantiate appropriate demuxer depending on content type (TSDemuxer, AACDemuxer, ...) + */ + + var _events = _dereq_(26); + + var _events2 = _interopRequireDefault(_events); + + var _errors = _dereq_(24); + + var _aacdemuxer = _dereq_(16); + + var _aacdemuxer2 = _interopRequireDefault(_aacdemuxer); + + var _tsdemuxer = _dereq_(23); + + var _tsdemuxer2 = _interopRequireDefault(_tsdemuxer); + + var _mp4Remuxer = _dereq_(36); + + var _mp4Remuxer2 = _interopRequireDefault(_mp4Remuxer); + + var _passthroughRemuxer = _dereq_(37); + + var _passthroughRemuxer2 = _interopRequireDefault(_passthroughRemuxer); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + + var DemuxerInline = function () { + function DemuxerInline(hls, id, typeSupported) { + var config = arguments.length <= 3 || arguments[3] === undefined ? null : arguments[3]; + + _classCallCheck(this, DemuxerInline); + + this.hls = hls; + this.id = id; + this.config = this.hls.config || config; + this.typeSupported = typeSupported; + } + + _createClass(DemuxerInline, [{ + key: 'destroy', + value: function destroy() { + var demuxer = this.demuxer; + if (demuxer) { + demuxer.destroy(); + } + } + }, { + key: 'push', + value: function push(data, audioCodec, videoCodec, timeOffset, cc, level, sn, duration, accurateTimeOffset) { + var demuxer = this.demuxer; + if (!demuxer) { + var hls = this.hls, + id = this.id; + // probe for content type + if (_tsdemuxer2.default.probe(data)) { + if (this.typeSupported.mp2t === true) { + demuxer = new _tsdemuxer2.default(hls, id, _passthroughRemuxer2.default, this.config); + } else { + demuxer = new _tsdemuxer2.default(hls, id, _mp4Remuxer2.default, this.config); + } + } else if (_aacdemuxer2.default.probe(data)) { + demuxer = new _aacdemuxer2.default(hls, id, _mp4Remuxer2.default, this.config); + } else { + hls.trigger(_events2.default.ERROR, { type: _errors.ErrorTypes.MEDIA_ERROR, id: id, details: _errors.ErrorDetails.FRAG_PARSING_ERROR, fatal: true, reason: 'no demux matching with content found' }); + return; + } + this.demuxer = demuxer; + } + demuxer.push(data, audioCodec, videoCodec, timeOffset, cc, level, sn, duration, accurateTimeOffset); + } + }]); + + return DemuxerInline; + }(); + + exports.default = DemuxerInline; + + },{"16":16,"23":23,"24":24,"26":26,"36":36,"37":37}],19:[function(_dereq_,module,exports){ + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + var _demuxerInline = _dereq_(18); + + var _demuxerInline2 = _interopRequireDefault(_demuxerInline); + + var _events = _dereq_(26); + + var _events2 = _interopRequireDefault(_events); + + var _logger = _dereq_(43); + + var _events3 = _dereq_(1); + + var _events4 = _interopRequireDefault(_events3); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + /* demuxer web worker. + * - listen to worker message, and trigger DemuxerInline upon reception of Fragments. + * - provides MP4 Boxes back to main thread using [transferable objects](https://developers.google.com/web/updates/2011/12/Transferable-Objects-Lightning-Fast) in order to minimize message passing overhead. + */ + + var DemuxerWorker = function DemuxerWorker(self) { + // observer setup + var observer = new _events4.default(); + observer.trigger = function trigger(event) { + for (var _len = arguments.length, data = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { + data[_key - 1] = arguments[_key]; + } + + observer.emit.apply(observer, [event, event].concat(data)); + }; + + observer.off = function off(event) { + for (var _len2 = arguments.length, data = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) { + data[_key2 - 1] = arguments[_key2]; + } + + observer.removeListener.apply(observer, [event].concat(data)); + }; + + var forwardMessage = function forwardMessage(ev, data) { + self.postMessage({ event: ev, data: data }); + }; + + self.addEventListener('message', function (ev) { + var data = ev.data; + //console.log('demuxer cmd:' + data.cmd); + switch (data.cmd) { + case 'init': + var config = JSON.parse(data.config); + self.demuxer = new _demuxerInline2.default(observer, data.id, data.typeSupported, config); + try { + (0, _logger.enableLogs)(config.debug); + } catch (err) { + console.warn('demuxerWorker: unable to enable logs'); + } + // signal end of worker init + forwardMessage('init', null); + break; + case 'demux': + self.demuxer.push(new Uint8Array(data.data), data.audioCodec, data.videoCodec, data.timeOffset, data.cc, data.level, data.sn, data.duration, data.accurateTimeOffset); + break; + default: + break; + } + }); + + // forward events to main thread + observer.on(_events2.default.FRAG_PARSING_INIT_SEGMENT, forwardMessage); + observer.on(_events2.default.FRAG_PARSED, forwardMessage); + observer.on(_events2.default.ERROR, forwardMessage); + observer.on(_events2.default.FRAG_PARSING_METADATA, forwardMessage); + observer.on(_events2.default.FRAG_PARSING_USERDATA, forwardMessage); + + // special case for FRAG_PARSING_DATA: pass data1/data2 as transferable object (no copy) + observer.on(_events2.default.FRAG_PARSING_DATA, function (ev, data) { + var data1 = data.data1.buffer, + data2 = data.data2.buffer; + // remove data1 and data2 reference from data to avoid copying them ... + delete data.data1; + delete data.data2; + self.postMessage({ event: ev, data: data, data1: data1, data2: data2 }, [data1, data2]); + }); + }; + + exports.default = DemuxerWorker; + + },{"1":1,"18":18,"26":26,"43":43}],20:[function(_dereq_,module,exports){ + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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 _events = _dereq_(26); + + var _events2 = _interopRequireDefault(_events); + + var _demuxerInline = _dereq_(18); + + var _demuxerInline2 = _interopRequireDefault(_demuxerInline); + + var _demuxerWorker = _dereq_(19); + + var _demuxerWorker2 = _interopRequireDefault(_demuxerWorker); + + var _logger = _dereq_(43); + + var _decrypter = _dereq_(15); + + var _decrypter2 = _interopRequireDefault(_decrypter); + + var _errors = _dereq_(24); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + + var Demuxer = function () { + function Demuxer(hls, id) { + _classCallCheck(this, Demuxer); + + this.hls = hls; + this.id = id; + var typeSupported = { + mp4: MediaSource.isTypeSupported('video/mp4'), + mp2t: hls.config.enableMP2TPassThrough && MediaSource.isTypeSupported('video/mp2t') + }; + if (hls.config.enableWorker && typeof Worker !== 'undefined') { + _logger.logger.log('demuxing in webworker'); + var w = void 0; + try { + var work = _dereq_(2); + w = this.w = work(_demuxerWorker2.default); + this.onwmsg = this.onWorkerMessage.bind(this); + w.addEventListener('message', this.onwmsg); + w.onerror = function (event) { + hls.trigger(_events2.default.ERROR, { type: _errors.ErrorTypes.OTHER_ERROR, details: _errors.ErrorDetails.INTERNAL_EXCEPTION, fatal: true, event: 'demuxerWorker', err: { message: event.message + ' (' + event.filename + ':' + event.lineno + ')' } }); + }; + w.postMessage({ cmd: 'init', typeSupported: typeSupported, id: id, config: JSON.stringify(hls.config) }); + } catch (err) { + _logger.logger.error('error while initializing DemuxerWorker, fallback on DemuxerInline'); + if (w) { + // revoke the Object URL that was used to create demuxer worker, so as not to leak it + URL.revokeObjectURL(w.objectURL); + } + this.demuxer = new _demuxerInline2.default(hls, id, typeSupported); + } + } else { + this.demuxer = new _demuxerInline2.default(hls, id, typeSupported); + } + this.demuxInitialized = true; + } + + _createClass(Demuxer, [{ + key: 'destroy', + value: function destroy() { + var w = this.w; + if (w) { + w.removeEventListener('message', this.onwmsg); + w.terminate(); + this.w = null; + } else { + var demuxer = this.demuxer; + if (demuxer) { + demuxer.destroy(); + this.demuxer = null; + } + } + var decrypter = this.decrypter; + if (decrypter) { + decrypter.destroy(); + this.decrypter = null; + } + } + }, { + key: 'pushDecrypted', + value: function pushDecrypted(data, audioCodec, videoCodec, timeOffset, cc, level, sn, duration, accurateTimeOffset) { + var w = this.w; + if (w) { + // post fragment payload as transferable objects (no copy) + w.postMessage({ cmd: 'demux', data: data, audioCodec: audioCodec, videoCodec: videoCodec, timeOffset: timeOffset, cc: cc, level: level, sn: sn, duration: duration, accurateTimeOffset: accurateTimeOffset }, [data]); + } else { + var demuxer = this.demuxer; + if (demuxer) { + demuxer.push(new Uint8Array(data), audioCodec, videoCodec, timeOffset, cc, level, sn, duration, accurateTimeOffset); + } + } + } + }, { + key: 'push', + value: function push(data, audioCodec, videoCodec, timeOffset, cc, level, sn, duration, decryptdata, accurateTimeOffset) { + if (data.byteLength > 0 && decryptdata != null && decryptdata.key != null && decryptdata.method === 'AES-128') { + if (this.decrypter == null) { + this.decrypter = new _decrypter2.default(this.hls); + } + + var localthis = this; + this.decrypter.decrypt(data, decryptdata.key, decryptdata.iv, function (decryptedData) { + localthis.pushDecrypted(decryptedData, audioCodec, videoCodec, timeOffset, cc, level, sn, duration, accurateTimeOffset); + }); + } else { + this.pushDecrypted(data, audioCodec, videoCodec, timeOffset, cc, level, sn, duration, accurateTimeOffset); + } + } + }, { + key: 'onWorkerMessage', + value: function onWorkerMessage(ev) { + var data = ev.data, + hls = this.hls; + //console.log('onWorkerMessage:' + data.event); + switch (data.event) { + case 'init': + // revoke the Object URL that was used to create demuxer worker, so as not to leak it + URL.revokeObjectURL(this.w.objectURL); + break; + // special case for FRAG_PARSING_DATA: data1 and data2 are transferable objects + case _events2.default.FRAG_PARSING_DATA: + data.data.data1 = new Uint8Array(data.data1); + data.data.data2 = new Uint8Array(data.data2); + /* falls through */ + default: + hls.trigger(data.event, data.data); + break; + } + } + }]); + + return Demuxer; + }(); + + exports.default = Demuxer; + + },{"15":15,"18":18,"19":19,"2":2,"24":24,"26":26,"43":43}],21:[function(_dereq_,module,exports){ + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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; }; }(); /** + * Parser for exponential Golomb codes, a variable-bitwidth number encoding scheme used by h264. + */ + + var _logger = _dereq_(43); + + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + + var ExpGolomb = function () { + function ExpGolomb(data) { + _classCallCheck(this, ExpGolomb); + + this.data = data; + // the number of bytes left to examine in this.data + this.bytesAvailable = this.data.byteLength; + // the current word being examined + this.word = 0; // :uint + // the number of bits left to examine in the current word + this.bitsAvailable = 0; // :uint + } + + // ():void + + + _createClass(ExpGolomb, [{ + key: 'loadWord', + value: function loadWord() { + var position = this.data.byteLength - this.bytesAvailable, + workingBytes = new Uint8Array(4), + availableBytes = Math.min(4, this.bytesAvailable); + if (availableBytes === 0) { + throw new Error('no bytes available'); + } + workingBytes.set(this.data.subarray(position, position + availableBytes)); + this.word = new DataView(workingBytes.buffer).getUint32(0); + // track the amount of this.data that has been processed + this.bitsAvailable = availableBytes * 8; + this.bytesAvailable -= availableBytes; + } + + // (count:int):void + + }, { + key: 'skipBits', + value: function skipBits(count) { + var skipBytes; // :int + if (this.bitsAvailable > count) { + this.word <<= count; + this.bitsAvailable -= count; + } else { + count -= this.bitsAvailable; + skipBytes = count >> 3; + count -= skipBytes >> 3; + this.bytesAvailable -= skipBytes; + this.loadWord(); + this.word <<= count; + this.bitsAvailable -= count; + } + } + + // (size:int):uint + + }, { + key: 'readBits', + value: function readBits(size) { + var bits = Math.min(this.bitsAvailable, size), + // :uint + valu = this.word >>> 32 - bits; // :uint + if (size > 32) { + _logger.logger.error('Cannot read more than 32 bits at a time'); + } + this.bitsAvailable -= bits; + if (this.bitsAvailable > 0) { + this.word <<= bits; + } else if (this.bytesAvailable > 0) { + this.loadWord(); + } + bits = size - bits; + if (bits > 0 && this.bitsAvailable) { + return valu << bits | this.readBits(bits); + } else { + return valu; + } + } + + // ():uint + + }, { + key: 'skipLZ', + value: function skipLZ() { + var leadingZeroCount; // :uint + for (leadingZeroCount = 0; leadingZeroCount < this.bitsAvailable; ++leadingZeroCount) { + if (0 !== (this.word & 0x80000000 >>> leadingZeroCount)) { + // the first bit of working word is 1 + this.word <<= leadingZeroCount; + this.bitsAvailable -= leadingZeroCount; + return leadingZeroCount; + } + } + // we exhausted word and still have not found a 1 + this.loadWord(); + return leadingZeroCount + this.skipLZ(); + } + + // ():void + + }, { + key: 'skipUEG', + value: function skipUEG() { + this.skipBits(1 + this.skipLZ()); + } + + // ():void + + }, { + key: 'skipEG', + value: function skipEG() { + this.skipBits(1 + this.skipLZ()); + } + + // ():uint + + }, { + key: 'readUEG', + value: function readUEG() { + var clz = this.skipLZ(); // :uint + return this.readBits(clz + 1) - 1; + } + + // ():int + + }, { + key: 'readEG', + value: function readEG() { + var valu = this.readUEG(); // :int + if (0x01 & valu) { + // the number is odd if the low order bit is set + return 1 + valu >>> 1; // add 1 to make it even, and divide by 2 + } else { + return -1 * (valu >>> 1); // divide by two then make it negative + } + } + + // Some convenience functions + // :Boolean + + }, { + key: 'readBoolean', + value: function readBoolean() { + return 1 === this.readBits(1); + } + + // ():int + + }, { + key: 'readUByte', + value: function readUByte() { + return this.readBits(8); + } + + // ():int + + }, { + key: 'readUShort', + value: function readUShort() { + return this.readBits(16); + } + // ():int + + }, { + key: 'readUInt', + value: function readUInt() { + return this.readBits(32); + } + + /** + * Advance the ExpGolomb decoder past a scaling list. The scaling + * list is optionally transmitted as part of a sequence parameter + * set and is not relevant to transmuxing. + * @param count {number} the number of entries in this scaling list + * @see Recommendation ITU-T H.264, Section 7.3.2.1.1.1 + */ + + }, { + key: 'skipScalingList', + value: function skipScalingList(count) { + var lastScale = 8, + nextScale = 8, + j, + deltaScale; + for (j = 0; j < count; j++) { + if (nextScale !== 0) { + deltaScale = this.readEG(); + nextScale = (lastScale + deltaScale + 256) % 256; + } + lastScale = nextScale === 0 ? lastScale : nextScale; + } + } + + /** + * Read a sequence parameter set and return some interesting video + * properties. A sequence parameter set is the H264 metadata that + * describes the properties of upcoming video frames. + * @param data {Uint8Array} the bytes of a sequence parameter set + * @return {object} an object with configuration parsed from the + * sequence parameter set, including the dimensions of the + * associated video frames. + */ + + }, { + key: 'readSPS', + value: function readSPS() { + var frameCropLeftOffset = 0, + frameCropRightOffset = 0, + frameCropTopOffset = 0, + frameCropBottomOffset = 0, + sarScale = 1, + profileIdc, + profileCompat, + levelIdc, + numRefFramesInPicOrderCntCycle, + picWidthInMbsMinus1, + picHeightInMapUnitsMinus1, + frameMbsOnlyFlag, + scalingListCount, + i; + this.readUByte(); + profileIdc = this.readUByte(); // profile_idc + profileCompat = this.readBits(5); // constraint_set[0-4]_flag, u(5) + this.skipBits(3); // reserved_zero_3bits u(3), + levelIdc = this.readUByte(); //level_idc u(8) + this.skipUEG(); // seq_parameter_set_id + // some profiles have more optional data we don't need + if (profileIdc === 100 || profileIdc === 110 || profileIdc === 122 || profileIdc === 244 || profileIdc === 44 || profileIdc === 83 || profileIdc === 86 || profileIdc === 118 || profileIdc === 128) { + var chromaFormatIdc = this.readUEG(); + if (chromaFormatIdc === 3) { + this.skipBits(1); // separate_colour_plane_flag + } + this.skipUEG(); // bit_depth_luma_minus8 + this.skipUEG(); // bit_depth_chroma_minus8 + this.skipBits(1); // qpprime_y_zero_transform_bypass_flag + if (this.readBoolean()) { + // seq_scaling_matrix_present_flag + scalingListCount = chromaFormatIdc !== 3 ? 8 : 12; + for (i = 0; i < scalingListCount; i++) { + if (this.readBoolean()) { + // seq_scaling_list_present_flag[ i ] + if (i < 6) { + this.skipScalingList(16); + } else { + this.skipScalingList(64); + } + } + } + } + } + this.skipUEG(); // log2_max_frame_num_minus4 + var picOrderCntType = this.readUEG(); + if (picOrderCntType === 0) { + this.readUEG(); //log2_max_pic_order_cnt_lsb_minus4 + } else if (picOrderCntType === 1) { + this.skipBits(1); // delta_pic_order_always_zero_flag + this.skipEG(); // offset_for_non_ref_pic + this.skipEG(); // offset_for_top_to_bottom_field + numRefFramesInPicOrderCntCycle = this.readUEG(); + for (i = 0; i < numRefFramesInPicOrderCntCycle; i++) { + this.skipEG(); // offset_for_ref_frame[ i ] + } + } + this.skipUEG(); // max_num_ref_frames + this.skipBits(1); // gaps_in_frame_num_value_allowed_flag + picWidthInMbsMinus1 = this.readUEG(); + picHeightInMapUnitsMinus1 = this.readUEG(); + frameMbsOnlyFlag = this.readBits(1); + if (frameMbsOnlyFlag === 0) { + this.skipBits(1); // mb_adaptive_frame_field_flag + } + this.skipBits(1); // direct_8x8_inference_flag + if (this.readBoolean()) { + // frame_cropping_flag + frameCropLeftOffset = this.readUEG(); + frameCropRightOffset = this.readUEG(); + frameCropTopOffset = this.readUEG(); + frameCropBottomOffset = this.readUEG(); + } + if (this.readBoolean()) { + // vui_parameters_present_flag + if (this.readBoolean()) { + // aspect_ratio_info_present_flag + var sarRatio = void 0; + var aspectRatioIdc = this.readUByte(); + switch (aspectRatioIdc) { + case 1: + sarRatio = [1, 1];break; + case 2: + sarRatio = [12, 11];break; + case 3: + sarRatio = [10, 11];break; + case 4: + sarRatio = [16, 11];break; + case 5: + sarRatio = [40, 33];break; + case 6: + sarRatio = [24, 11];break; + case 7: + sarRatio = [20, 11];break; + case 8: + sarRatio = [32, 11];break; + case 9: + sarRatio = [80, 33];break; + case 10: + sarRatio = [18, 11];break; + case 11: + sarRatio = [15, 11];break; + case 12: + sarRatio = [64, 33];break; + case 13: + sarRatio = [160, 99];break; + case 14: + sarRatio = [4, 3];break; + case 15: + sarRatio = [3, 2];break; + case 16: + sarRatio = [2, 1];break; + case 255: + { + sarRatio = [this.readUByte() << 8 | this.readUByte(), this.readUByte() << 8 | this.readUByte()]; + break; + } + } + if (sarRatio) { + sarScale = sarRatio[0] / sarRatio[1]; + } + } + } + return { + width: Math.ceil(((picWidthInMbsMinus1 + 1) * 16 - frameCropLeftOffset * 2 - frameCropRightOffset * 2) * sarScale), + height: (2 - frameMbsOnlyFlag) * (picHeightInMapUnitsMinus1 + 1) * 16 - (frameMbsOnlyFlag ? 2 : 4) * (frameCropTopOffset + frameCropBottomOffset) + }; + } + }, { + key: 'readSliceType', + value: function readSliceType() { + // skip NALu type + this.readUByte(); + // discard first_mb_in_slice + this.readUEG(); + // return slice_type + return this.readUEG(); + } + }]); + + return ExpGolomb; + }(); + + exports.default = ExpGolomb; + + },{"43":43}],22:[function(_dereq_,module,exports){ + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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; }; }(); /** + * ID3 parser + */ + + + var _logger = _dereq_(43); + + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + + //import Hex from '../utils/hex'; + + var ID3 = function () { + function ID3(data) { + _classCallCheck(this, ID3); + + this._hasTimeStamp = false; + var offset = 0, + byte1, + byte2, + byte3, + byte4, + tagSize, + endPos, + header, + len; + do { + header = this.readUTF(data, offset, 3); + offset += 3; + // first check for ID3 header + if (header === 'ID3') { + // skip 24 bits + offset += 3; + // retrieve tag(s) length + byte1 = data[offset++] & 0x7f; + byte2 = data[offset++] & 0x7f; + byte3 = data[offset++] & 0x7f; + byte4 = data[offset++] & 0x7f; + tagSize = (byte1 << 21) + (byte2 << 14) + (byte3 << 7) + byte4; + endPos = offset + tagSize; + //logger.log(`ID3 tag found, size/end: ${tagSize}/${endPos}`); + + // read ID3 tags + this._parseID3Frames(data, offset, endPos); + offset = endPos; + } else if (header === '3DI') { + // http://id3.org/id3v2.4.0-structure chapter 3.4. ID3v2 footer + offset += 7; + _logger.logger.log('3DI footer found, end: ' + offset); + } else { + offset -= 3; + len = offset; + if (len) { + //logger.log(`ID3 len: ${len}`); + if (!this.hasTimeStamp) { + _logger.logger.warn('ID3 tag found, but no timestamp'); + } + this._length = len; + this._payload = data.subarray(0, len); + } + return; + } + } while (true); + } + + _createClass(ID3, [{ + key: 'readUTF', + value: function readUTF(data, start, len) { + + var result = '', + offset = start, + end = start + len; + do { + result += String.fromCharCode(data[offset++]); + } while (offset < end); + return result; + } + }, { + key: '_parseID3Frames', + value: function _parseID3Frames(data, offset, endPos) { + var tagId, tagLen, tagStart, tagFlags, timestamp; + while (offset + 8 <= endPos) { + tagId = this.readUTF(data, offset, 4); + offset += 4; + + tagLen = data[offset++] << 24 + data[offset++] << 16 + data[offset++] << 8 + data[offset++]; + + tagFlags = data[offset++] << 8 + data[offset++]; + + tagStart = offset; + //logger.log("ID3 tag id:" + tagId); + switch (tagId) { + case 'PRIV': + //logger.log('parse frame:' + Hex.hexDump(data.subarray(offset,endPos))); + // owner should be "com.apple.streaming.transportStreamTimestamp" + if (this.readUTF(data, offset, 44) === 'com.apple.streaming.transportStreamTimestamp') { + offset += 44; + // smelling even better ! we found the right descriptor + // skip null character (string end) + 3 first bytes + offset += 4; + + // timestamp is 33 bit expressed as a big-endian eight-octet number, with the upper 31 bits set to zero. + var pts33Bit = data[offset++] & 0x1; + this._hasTimeStamp = true; + + timestamp = ((data[offset++] << 23) + (data[offset++] << 15) + (data[offset++] << 7) + data[offset++]) / 45; + + if (pts33Bit) { + timestamp += 47721858.84; // 2^32 / 90 + } + timestamp = Math.round(timestamp); + _logger.logger.trace('ID3 timestamp found: ' + timestamp); + this._timeStamp = timestamp; + } + break; + default: + break; + } + } + } + }, { + key: 'hasTimeStamp', + get: function get() { + return this._hasTimeStamp; + } + }, { + key: 'timeStamp', + get: function get() { + return this._timeStamp; + } + }, { + key: 'length', + get: function get() { + return this._length; + } + }, { + key: 'payload', + get: function get() { + return this._payload; + } + }]); + + return ID3; + }(); + + exports.default = ID3; + + },{"43":43}],23:[function(_dereq_,module,exports){ + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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; }; }(); /** + * highly optimized TS demuxer: + * parse PAT, PMT + * extract PES packet from audio and video PIDs + * extract AVC/H264 NAL units and AAC/ADTS samples from PES packet + * trigger the remuxer upon parsing completion + * it also tries to workaround as best as it can audio codec switch (HE-AAC to AAC and vice versa), without having to restart the MediaSource. + * it also controls the remuxing process : + * upon discontinuity or level switch detection, it will also notifies the remuxer so that it can reset its state. + */ + + // import Hex from '../utils/hex'; + + + var _adts = _dereq_(17); + + var _adts2 = _interopRequireDefault(_adts); + + var _events = _dereq_(26); + + var _events2 = _interopRequireDefault(_events); + + var _expGolomb = _dereq_(21); + + var _expGolomb2 = _interopRequireDefault(_expGolomb); + + var _logger = _dereq_(43); + + var _errors = _dereq_(24); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + + var TSDemuxer = function () { + function TSDemuxer(observer, id, remuxerClass, config) { + _classCallCheck(this, TSDemuxer); + + this.observer = observer; + this.id = id; + this.remuxerClass = remuxerClass; + this.config = config; + this.lastCC = 0; + this.remuxer = new this.remuxerClass(observer, id, config); + } + + _createClass(TSDemuxer, [{ + key: 'switchLevel', + value: function switchLevel() { + this.pmtParsed = false; + this._pmtId = -1; + this._avcTrack = { container: 'video/mp2t', type: 'video', id: -1, sequenceNumber: 0, samples: [], len: 0, dropped: 0 }; + this._aacTrack = { container: 'video/mp2t', type: 'audio', id: -1, sequenceNumber: 0, samples: [], len: 0 }; + this._id3Track = { type: 'id3', id: -1, sequenceNumber: 0, samples: [], len: 0 }; + this._txtTrack = { type: 'text', id: -1, sequenceNumber: 0, samples: [], len: 0 }; + // flush any partial content + this.aacOverFlow = null; + this.aacLastPTS = null; + this.avcSample = null; + this.remuxer.switchLevel(); + } + }, { + key: 'insertDiscontinuity', + value: function insertDiscontinuity() { + this.switchLevel(); + this.remuxer.insertDiscontinuity(); + } + + // feed incoming data to the front of the parsing pipeline + + }, { + key: 'push', + value: function push(data, audioCodec, videoCodec, timeOffset, cc, level, sn, duration, accurateTimeOffset) { + var start, + len = data.length, + stt, + pid, + atf, + offset, + pes, + codecsOnly = this.remuxer.passthrough, + unknownPIDs = false; + + this.audioCodec = audioCodec; + this.videoCodec = videoCodec; + this._duration = duration; + this.contiguous = false; + this.accurateTimeOffset = accurateTimeOffset; + if (cc !== this.lastCC) { + _logger.logger.log('discontinuity detected'); + this.insertDiscontinuity(); + this.lastCC = cc; + } + if (level !== this.lastLevel) { + _logger.logger.log('level switch detected'); + this.switchLevel(); + this.lastLevel = level; + } else if (sn === this.lastSN + 1) { + this.contiguous = true; + } + this.lastSN = sn; + + var pmtParsed = this.pmtParsed, + avcTrack = this._avcTrack, + aacTrack = this._aacTrack, + id3Track = this._id3Track, + avcId = avcTrack.id, + aacId = aacTrack.id, + id3Id = id3Track.id, + pmtId = this._pmtId, + avcData = avcTrack.pesData, + aacData = aacTrack.pesData, + id3Data = id3Track.pesData, + parsePAT = this._parsePAT, + parsePMT = this._parsePMT, + parsePES = this._parsePES, + parseAVCPES = this._parseAVCPES.bind(this), + parseAACPES = this._parseAACPES.bind(this), + parseID3PES = this._parseID3PES.bind(this); + + // don't parse last TS packet if incomplete + len -= len % 188; + // loop through TS packets + for (start = 0; start < len; start += 188) { + if (data[start] === 0x47) { + stt = !!(data[start + 1] & 0x40); + // pid is a 13-bit field starting at the last bit of TS[1] + pid = ((data[start + 1] & 0x1f) << 8) + data[start + 2]; + atf = (data[start + 3] & 0x30) >> 4; + // if an adaption field is present, its length is specified by the fifth byte of the TS packet header. + if (atf > 1) { + offset = start + 5 + data[start + 4]; + // continue if there is only adaptation field + if (offset === start + 188) { + continue; + } + } else { + offset = start + 4; + } + switch (pid) { + case avcId: + if (stt) { + if (avcData && (pes = parsePES(avcData))) { + parseAVCPES(pes, false); + if (codecsOnly) { + // if we have video codec info AND + // if audio PID is undefined OR if we have audio codec info, + // we have all codec info ! + if (avcTrack.codec && (aacId === -1 || aacTrack.codec)) { + this.remux(level, sn, data, timeOffset); + return; + } + } + } + avcData = { data: [], size: 0 }; + } + if (avcData) { + avcData.data.push(data.subarray(offset, start + 188)); + avcData.size += start + 188 - offset; + } + break; + case aacId: + if (stt) { + if (aacData && (pes = parsePES(aacData))) { + parseAACPES(pes); + if (codecsOnly) { + // here we now that we have audio codec info + // if video PID is undefined OR if we have video codec info, + // we have all codec infos ! + if (aacTrack.codec && (avcId === -1 || avcTrack.codec)) { + this.remux(level, sn, data, timeOffset); + return; + } + } + } + aacData = { data: [], size: 0 }; + } + if (aacData) { + aacData.data.push(data.subarray(offset, start + 188)); + aacData.size += start + 188 - offset; + } + break; + case id3Id: + if (stt) { + if (id3Data && (pes = parsePES(id3Data))) { + parseID3PES(pes); + } + id3Data = { data: [], size: 0 }; + } + if (id3Data) { + id3Data.data.push(data.subarray(offset, start + 188)); + id3Data.size += start + 188 - offset; + } + break; + case 0: + if (stt) { + offset += data[offset] + 1; + } + pmtId = this._pmtId = parsePAT(data, offset); + break; + case pmtId: + if (stt) { + offset += data[offset] + 1; + } + var parsedPIDs = parsePMT(data, offset); + avcId = avcTrack.id = parsedPIDs.avc; + aacId = aacTrack.id = parsedPIDs.aac; + id3Id = id3Track.id = parsedPIDs.id3; + if (unknownPIDs && !pmtParsed) { + _logger.logger.log('reparse from beginning'); + unknownPIDs = false; + // we set it to -188, the += 188 in the for loop will reset start to 0 + start = -188; + } + pmtParsed = this.pmtParsed = true; + break; + case 17: + case 0x1fff: + break; + default: + unknownPIDs = true; + break; + } + } else { + this.observer.trigger(_events2.default.ERROR, { type: _errors.ErrorTypes.MEDIA_ERROR, id: this.id, details: _errors.ErrorDetails.FRAG_PARSING_ERROR, fatal: false, reason: 'TS packet did not start with 0x47' }); + } + } + // try to parse last PES packets + if (avcData && (pes = parsePES(avcData))) { + parseAVCPES(pes, true); + avcTrack.pesData = null; + } else { + // either avcData null or PES truncated, keep it for next frag parsing + avcTrack.pesData = avcData; + } + + if (aacData && (pes = parsePES(aacData))) { + parseAACPES(pes); + aacTrack.pesData = null; + } else { + if (aacData && aacData.size) { + _logger.logger.log('last AAC PES packet truncated,might overlap between fragments'); + } + // either aacData null or PES truncated, keep it for next frag parsing + aacTrack.pesData = aacData; + } + + if (id3Data && (pes = parsePES(id3Data))) { + parseID3PES(pes); + id3Track.pesData = null; + } else { + // either id3Data null or PES truncated, keep it for next frag parsing + id3Track.pesData = id3Data; + } + this.remux(level, sn, null, timeOffset); + } + }, { + key: 'remux', + value: function remux(level, sn, data, timeOffset) { + var avcTrack = this._avcTrack, + samples = avcTrack.samples; + + // compute total/avc sample length and nb of NAL units + var trackData = samples.reduce(function (prevSampleData, curSample) { + var sampleData = curSample.units.units.reduce(function (prevUnitData, curUnit) { + return { + len: prevUnitData.len + curUnit.data.length, + nbNalu: prevUnitData.nbNalu + 1 + }; + }, { len: 0, nbNalu: 0 }); + curSample.length = sampleData.len; + return { + len: prevSampleData.len + sampleData.len, + nbNalu: prevSampleData.nbNalu + sampleData.nbNalu + }; + }, { len: 0, nbNalu: 0 }); + avcTrack.len = trackData.len; + avcTrack.nbNalu = trackData.nbNalu; + this.remuxer.remux(level, sn, this._aacTrack, this._avcTrack, this._id3Track, this._txtTrack, timeOffset, this.contiguous, this.accurateTimeOffset, data); + } + }, { + key: 'destroy', + value: function destroy() { + this.switchLevel(); + this._initPTS = this._initDTS = undefined; + this._duration = 0; + } + }, { + key: '_parsePAT', + value: function _parsePAT(data, offset) { + // skip the PSI header and parse the first PMT entry + return (data[offset + 10] & 0x1F) << 8 | data[offset + 11]; + //logger.log('PMT PID:' + this._pmtId); + } + }, { + key: '_parsePMT', + value: function _parsePMT(data, offset) { + var sectionLength, + tableEnd, + programInfoLength, + pid, + result = { aac: -1, avc: -1, id3: -1 }; + sectionLength = (data[offset + 1] & 0x0f) << 8 | data[offset + 2]; + tableEnd = offset + 3 + sectionLength - 4; + // to determine where the table is, we have to figure out how + // long the program info descriptors are + programInfoLength = (data[offset + 10] & 0x0f) << 8 | data[offset + 11]; + // advance the offset to the first entry in the mapping table + offset += 12 + programInfoLength; + while (offset < tableEnd) { + pid = (data[offset + 1] & 0x1F) << 8 | data[offset + 2]; + switch (data[offset]) { + // ISO/IEC 13818-7 ADTS AAC (MPEG-2 lower bit-rate audio) + case 0x0f: + //logger.log('AAC PID:' + pid); + if (result.aac === -1) { + result.aac = pid; + } + break; + // Packetized metadata (ID3) + case 0x15: + //logger.log('ID3 PID:' + pid); + if (result.id3 === -1) { + result.id3 = pid; + } + break; + // ITU-T Rec. H.264 and ISO/IEC 14496-10 (lower bit-rate video) + case 0x1b: + //logger.log('AVC PID:' + pid); + if (result.avc === -1) { + result.avc = pid; + } + break; + case 0x24: + _logger.logger.warn('HEVC stream type found, not supported for now'); + break; + default: + _logger.logger.log('unkown stream type:' + data[offset]); + break; + } + // move to the next table entry + // skip past the elementary stream descriptors, if present + offset += ((data[offset + 3] & 0x0F) << 8 | data[offset + 4]) + 5; + } + return result; + } + }, { + key: '_parsePES', + value: function _parsePES(stream) { + var i = 0, + frag, + pesFlags, + pesPrefix, + pesLen, + pesHdrLen, + pesData, + pesPts, + pesDts, + payloadStartOffset, + data = stream.data; + // safety check + if (!stream || stream.size === 0) { + return null; + } + + // we might need up to 19 bytes to read PES header + // if first chunk of data is less than 19 bytes, let's merge it with following ones until we get 19 bytes + // usually only one merge is needed (and this is rare ...) + while (data[0].length < 19 && data.length > 1) { + var newData = new Uint8Array(data[0].length + data[1].length); + newData.set(data[0]); + newData.set(data[1], data[0].length); + data[0] = newData; + data.splice(1, 1); + } + //retrieve PTS/DTS from first fragment + frag = data[0]; + pesPrefix = (frag[0] << 16) + (frag[1] << 8) + frag[2]; + if (pesPrefix === 1) { + pesLen = (frag[4] << 8) + frag[5]; + // if PES len is not zero and not matching with total len, stop parsing. PES might be truncated + // minus 6 : PES header size + if (pesLen && pesLen !== stream.size - 6) { + return null; + } + pesFlags = frag[7]; + if (pesFlags & 0xC0) { + /* PES header described here : http://dvd.sourceforge.net/dvdinfo/pes-hdr.html + as PTS / DTS is 33 bit we cannot use bitwise operator in JS, + as Bitwise operators treat their operands as a sequence of 32 bits */ + pesPts = (frag[9] & 0x0E) * 536870912 + // 1 << 29 + (frag[10] & 0xFF) * 4194304 + // 1 << 22 + (frag[11] & 0xFE) * 16384 + // 1 << 14 + (frag[12] & 0xFF) * 128 + // 1 << 7 + (frag[13] & 0xFE) / 2; + // check if greater than 2^32 -1 + if (pesPts > 4294967295) { + // decrement 2^33 + pesPts -= 8589934592; + } + if (pesFlags & 0x40) { + pesDts = (frag[14] & 0x0E) * 536870912 + // 1 << 29 + (frag[15] & 0xFF) * 4194304 + // 1 << 22 + (frag[16] & 0xFE) * 16384 + // 1 << 14 + (frag[17] & 0xFF) * 128 + // 1 << 7 + (frag[18] & 0xFE) / 2; + // check if greater than 2^32 -1 + if (pesDts > 4294967295) { + // decrement 2^33 + pesDts -= 8589934592; + } + } else { + pesDts = pesPts; + } + } + pesHdrLen = frag[8]; + // 9 bytes : 6 bytes for PES header + 3 bytes for PES extension + payloadStartOffset = pesHdrLen + 9; + + stream.size -= payloadStartOffset; + //reassemble PES packet + pesData = new Uint8Array(stream.size); + while (data.length) { + frag = data.shift(); + var len = frag.byteLength; + if (payloadStartOffset) { + if (payloadStartOffset > len) { + // trim full frag if PES header bigger than frag + payloadStartOffset -= len; + continue; + } else { + // trim partial frag if PES header smaller than frag + frag = frag.subarray(payloadStartOffset); + len -= payloadStartOffset; + payloadStartOffset = 0; + } + } + pesData.set(frag, i); + i += len; + } + if (pesLen) { + // payload size : remove PES header + PES extension + pesLen -= pesHdrLen + 3; + } + return { data: pesData, pts: pesPts, dts: pesDts, len: pesLen }; + } else { + return null; + } + } + }, { + key: 'pushAccesUnit', + value: function pushAccesUnit(avcSample, avcTrack) { + if (avcSample.units.units.length) { + // only push AVC sample if starting with a keyframe is not mandatory OR + // if keyframe already found in this fragment OR + // keyframe found in last fragment (track.sps) AND + // samples already appended (we already found a keyframe in this fragment) OR fragment is contiguous + if (!this.config.forceKeyFrameOnDiscontinuity || avcSample.key === true || avcTrack.sps && (avcTrack.samples.length || this.contiguous)) { + avcTrack.samples.push(avcSample); + } else { + // dropped samples, track it + avcTrack.dropped++; + } + } + if (avcSample.debug.length) { + _logger.logger.log(avcSample.pts + '/' + avcSample.dts + ':' + avcSample.debug + ',' + avcSample.units.length); + } + } + }, { + key: '_parseAVCPES', + value: function _parseAVCPES(pes, last) { + var _this = this; + + //logger.log('parse new PES'); + var track = this._avcTrack, + units = this._parseAVCNALu(pes.data), + debug = false, + expGolombDecoder, + avcSample = this.avcSample, + push, + i; + //free pes.data to save up some memory + pes.data = null; + + units.forEach(function (unit) { + switch (unit.type) { + //NDR + case 1: + push = true; + if (debug && avcSample) { + avcSample.debug += 'NDR '; + } + break; + //IDR + case 5: + push = true; + // handle PES not starting with AUD + if (!avcSample) { + avcSample = _this.avcSample = _this._createAVCSample(true, pes.pts, pes.dts, ''); + } + if (debug) { + avcSample.debug += 'IDR '; + } + avcSample.key = true; + break; + //SEI + case 6: + push = true; + if (debug && avcSample) { + avcSample.debug += 'SEI '; + } + expGolombDecoder = new _expGolomb2.default(_this.discardEPB(unit.data)); + + // skip frameType + expGolombDecoder.readUByte(); + + var payloadType = 0; + var payloadSize = 0; + var endOfCaptions = false; + var b = 0; + + while (!endOfCaptions && expGolombDecoder.bytesAvailable > 1) { + payloadType = 0; + do { + b = expGolombDecoder.readUByte(); + payloadType += b; + } while (b === 0xFF); + + // Parse payload size. + payloadSize = 0; + do { + b = expGolombDecoder.readUByte(); + payloadSize += b; + } while (b === 0xFF); + + // TODO: there can be more than one payload in an SEI packet... + // TODO: need to read type and size in a while loop to get them all + if (payloadType === 4 && expGolombDecoder.bytesAvailable !== 0) { + + endOfCaptions = true; + + var countryCode = expGolombDecoder.readUByte(); + + if (countryCode === 181) { + var providerCode = expGolombDecoder.readUShort(); + + if (providerCode === 49) { + var userStructure = expGolombDecoder.readUInt(); + + if (userStructure === 0x47413934) { + var userDataType = expGolombDecoder.readUByte(); + + // Raw CEA-608 bytes wrapped in CEA-708 packet + if (userDataType === 3) { + var firstByte = expGolombDecoder.readUByte(); + var secondByte = expGolombDecoder.readUByte(); + + var totalCCs = 31 & firstByte; + var byteArray = [firstByte, secondByte]; + + for (i = 0; i < totalCCs; i++) { + // 3 bytes per CC + byteArray.push(expGolombDecoder.readUByte()); + byteArray.push(expGolombDecoder.readUByte()); + byteArray.push(expGolombDecoder.readUByte()); + } + + _this._insertSampleInOrder(_this._txtTrack.samples, { type: 3, pts: pes.pts, bytes: byteArray }); + } + } + } + } + } else if (payloadSize < expGolombDecoder.bytesAvailable) { + for (i = 0; i < payloadSize; i++) { + expGolombDecoder.readUByte(); + } + } + } + break; + //SPS + case 7: + push = true; + if (debug && avcSample) { + avcSample.debug += 'SPS '; + } + if (!track.sps) { + expGolombDecoder = new _expGolomb2.default(unit.data); + var config = expGolombDecoder.readSPS(); + track.width = config.width; + track.height = config.height; + track.sps = [unit.data]; + track.duration = _this._duration; + var codecarray = unit.data.subarray(1, 4); + var codecstring = 'avc1.'; + for (i = 0; i < 3; i++) { + var h = codecarray[i].toString(16); + if (h.length < 2) { + h = '0' + h; + } + codecstring += h; + } + track.codec = codecstring; + } + break; + //PPS + case 8: + push = true; + if (debug && avcSample) { + avcSample.debug += 'PPS '; + } + if (!track.pps) { + track.pps = [unit.data]; + } + break; + // AUD + case 9: + push = false; + if (avcSample) { + _this.pushAccesUnit(avcSample, track); + } + avcSample = _this.avcSample = _this._createAVCSample(false, pes.pts, pes.dts, debug ? 'AUD ' : ''); + break; + // Filler Data + case 12: + push = false; + break; + default: + push = false; + if (avcSample) { + avcSample.debug += 'unknown NAL ' + unit.type + ' '; + } + break; + } + if (avcSample && push) { + var _units = avcSample.units; + _units.units.push(unit); + } + }); + // if last PES packet, push samples + if (last && avcSample) { + this.pushAccesUnit(avcSample, track); + this.avcSample = null; + } + } + }, { + key: '_createAVCSample', + value: function _createAVCSample(key, pts, dts, debug) { + return { key: key, pts: pts, dts: dts, units: { units: [], length: 0 }, debug: debug }; + } + }, { + key: '_insertSampleInOrder', + value: function _insertSampleInOrder(arr, data) { + var len = arr.length; + if (len > 0) { + if (data.pts >= arr[len - 1].pts) { + arr.push(data); + } else { + for (var pos = len - 1; pos >= 0; pos--) { + if (data.pts < arr[pos].pts) { + arr.splice(pos, 0, data); + break; + } + } + } + } else { + arr.push(data); + } + } + }, { + key: '_getLastNalUnit', + value: function _getLastNalUnit() { + var avcSample = this.avcSample, + lastUnit = void 0; + // try to fallback to previous sample if current one is empty + if (!avcSample || avcSample.units.units.length === 0) { + var track = this._avcTrack, + samples = track.samples; + avcSample = samples[samples.length - 1]; + } + if (avcSample) { + var units = avcSample.units.units; + lastUnit = units[units.length - 1]; + } + return lastUnit; + } + }, { + key: '_parseAVCNALu', + value: function _parseAVCNALu(array) { + var i = 0, + len = array.byteLength, + value, + overflow, + track = this._avcTrack, + state = track.naluState || 0, + lastState = state; + var units = [], + unit, + unitType, + lastUnitStart = -1, + lastUnitType; + //logger.log('PES:' + Hex.hexDump(array)); + while (i < len) { + value = array[i++]; + // finding 3 or 4-byte start codes (00 00 01 OR 00 00 00 01) + switch (state) { + case 0: + if (value === 0) { + state = 1; + } + break; + case 1: + if (value === 0) { + state = 2; + } else { + state = 0; + } + break; + case 2: + case 3: + if (value === 0) { + state = 3; + } else if (value === 1) { + if (lastUnitStart >= 0) { + unit = { data: array.subarray(lastUnitStart, i - state - 1), type: lastUnitType }; + //logger.log('pushing NALU, type/size:' + unit.type + '/' + unit.data.byteLength); + units.push(unit); + } else { + // lastUnitStart is undefined => this is the first start code found in this PES packet + // first check if start code delimiter is overlapping between 2 PES packets, + // ie it started in last packet (lastState not zero) + // and ended at the beginning of this PES packet (i <= 4 - lastState) + var lastUnit = this._getLastNalUnit(); + if (lastUnit) { + if (lastState && i <= 4 - lastState) { + // start delimiter overlapping between PES packets + // strip start delimiter bytes from the end of last NAL unit + // check if lastUnit had a state different from zero + if (lastUnit.state) { + // strip last bytes + lastUnit.data = lastUnit.data.subarray(0, lastUnit.data.byteLength - lastState); + } + } + // If NAL units are not starting right at the beginning of the PES packet, push preceding data into previous NAL unit. + overflow = i - state - 1; + if (overflow > 0) { + //logger.log('first NALU found with overflow:' + overflow); + var tmp = new Uint8Array(lastUnit.data.byteLength + overflow); + tmp.set(lastUnit.data, 0); + tmp.set(array.subarray(0, overflow), lastUnit.data.byteLength); + lastUnit.data = tmp; + } + } + } + // check if we can read unit type + if (i < len) { + unitType = array[i] & 0x1f; + //logger.log('find NALU @ offset:' + i + ',type:' + unitType); + lastUnitStart = i; + lastUnitType = unitType; + state = 0; + } else { + // not enough byte to read unit type. let's read it on next PES parsing + state = -1; + } + } else { + state = 0; + } + break; + case -1: + // special use case where we found 3 or 4-byte start codes exactly at the end of previous PES packet + lastUnitStart = 0; + // NALu type is value read from offset 0 + lastUnitType = value & 0x1f; + state = 0; + break; + default: + break; + } + } + if (lastUnitStart >= 0 && state >= 0) { + unit = { data: array.subarray(lastUnitStart, len), type: lastUnitType, state: state }; + units.push(unit); + //logger.log('pushing NALU, type/size/state:' + unit.type + '/' + unit.data.byteLength + '/' + state); + } + // no NALu found + if (units.length === 0) { + // append pes.data to previous NAL unit + var _lastUnit = this._getLastNalUnit(); + if (_lastUnit) { + var _tmp = new Uint8Array(_lastUnit.data.byteLength + array.byteLength); + _tmp.set(_lastUnit.data, 0); + _tmp.set(array, _lastUnit.data.byteLength); + _lastUnit.data = _tmp; + } + } + track.naluState = state; + return units; + } + + /** + * remove Emulation Prevention bytes from a RBSP + */ + + }, { + key: 'discardEPB', + value: function discardEPB(data) { + var length = data.byteLength, + EPBPositions = [], + i = 1, + newLength, + newData; + + // Find all `Emulation Prevention Bytes` + while (i < length - 2) { + if (data[i] === 0 && data[i + 1] === 0 && data[i + 2] === 0x03) { + EPBPositions.push(i + 2); + i += 2; + } else { + i++; + } + } + + // If no Emulation Prevention Bytes were found just return the original + // array + if (EPBPositions.length === 0) { + return data; + } + + // Create a new array to hold the NAL unit data + newLength = length - EPBPositions.length; + newData = new Uint8Array(newLength); + var sourceIndex = 0; + + for (i = 0; i < newLength; sourceIndex++, i++) { + if (sourceIndex === EPBPositions[0]) { + // Skip this byte + sourceIndex++; + // Remove this position index + EPBPositions.shift(); + } + newData[i] = data[sourceIndex]; + } + return newData; + } + }, { + key: '_parseAACPES', + value: function _parseAACPES(pes) { + var track = this._aacTrack, + data = pes.data, + pts = pes.pts, + startOffset = 0, + aacOverFlow = this.aacOverFlow, + aacLastPTS = this.aacLastPTS, + config, + frameLength, + frameDuration, + frameIndex, + offset, + headerLength, + stamp, + len, + aacSample; + if (aacOverFlow) { + var tmp = new Uint8Array(aacOverFlow.byteLength + data.byteLength); + tmp.set(aacOverFlow, 0); + tmp.set(data, aacOverFlow.byteLength); + //logger.log(`AAC: append overflowing ${aacOverFlow.byteLength} bytes to beginning of new PES`); + data = tmp; + } + // look for ADTS header (0xFFFx) + for (offset = startOffset, len = data.length; offset < len - 1; offset++) { + if (data[offset] === 0xff && (data[offset + 1] & 0xf0) === 0xf0) { + break; + } + } + // if ADTS header does not start straight from the beginning of the PES payload, raise an error + if (offset) { + var reason, fatal; + if (offset < len - 1) { + reason = 'AAC PES did not start with ADTS header,offset:' + offset; + fatal = false; + } else { + reason = 'no ADTS header found in AAC PES'; + fatal = true; + } + _logger.logger.warn('parsing error:' + reason); + this.observer.trigger(_events2.default.ERROR, { type: _errors.ErrorTypes.MEDIA_ERROR, id: this.id, details: _errors.ErrorDetails.FRAG_PARSING_ERROR, fatal: fatal, reason: reason }); + if (fatal) { + return; + } + } + if (!track.audiosamplerate) { + config = _adts2.default.getAudioConfig(this.observer, data, offset, this.audioCodec); + track.config = config.config; + track.audiosamplerate = config.samplerate; + track.channelCount = config.channelCount; + track.codec = config.codec; + track.duration = this._duration; + _logger.logger.log('parsed codec:' + track.codec + ',rate:' + config.samplerate + ',nb channel:' + config.channelCount); + } + frameIndex = 0; + frameDuration = 1024 * 90000 / track.audiosamplerate; + + // if last AAC frame is overflowing, we should ensure timestamps are contiguous: + // first sample PTS should be equal to last sample PTS + frameDuration + if (aacOverFlow && aacLastPTS) { + var newPTS = aacLastPTS + frameDuration; + if (Math.abs(newPTS - pts) > 1) { + _logger.logger.log('AAC: align PTS for overlapping frames by ' + Math.round((newPTS - pts) / 90)); + pts = newPTS; + } + } + + while (offset + 5 < len) { + // The protection skip bit tells us if we have 2 bytes of CRC data at the end of the ADTS header + headerLength = !!(data[offset + 1] & 0x01) ? 7 : 9; + // retrieve frame size + frameLength = (data[offset + 3] & 0x03) << 11 | data[offset + 4] << 3 | (data[offset + 5] & 0xE0) >>> 5; + frameLength -= headerLength; + //stamp = pes.pts; + + if (frameLength > 0 && offset + headerLength + frameLength <= len) { + stamp = pts + frameIndex * frameDuration; + //logger.log(`AAC frame, offset/length/total/pts:${offset+headerLength}/${frameLength}/${data.byteLength}/${(stamp/90).toFixed(0)}`); + aacSample = { unit: data.subarray(offset + headerLength, offset + headerLength + frameLength), pts: stamp, dts: stamp }; + track.samples.push(aacSample); + track.len += frameLength; + offset += frameLength + headerLength; + frameIndex++; + // look for ADTS header (0xFFFx) + for (; offset < len - 1; offset++) { + if (data[offset] === 0xff && (data[offset + 1] & 0xf0) === 0xf0) { + break; + } + } + } else { + break; + } + } + if (offset < len) { + aacOverFlow = data.subarray(offset, len); + //logger.log(`AAC: overflow detected:${len-offset}`); + } else { + aacOverFlow = null; + } + this.aacOverFlow = aacOverFlow; + this.aacLastPTS = stamp; + } + }, { + key: '_parseID3PES', + value: function _parseID3PES(pes) { + this._id3Track.samples.push(pes); + } + }], [{ + key: 'probe', + value: function probe(data) { + // a TS fragment should contain at least 3 TS packets, a PAT, a PMT, and one PID, each starting with 0x47 + if (data.length >= 3 * 188 && data[0] === 0x47 && data[188] === 0x47 && data[2 * 188] === 0x47) { + return true; + } else { + return false; + } + } + }]); + + return TSDemuxer; + }(); + + exports.default = TSDemuxer; + + },{"17":17,"21":21,"24":24,"26":26,"43":43}],24:[function(_dereq_,module,exports){ + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + var ErrorTypes = exports.ErrorTypes = { + // Identifier for a network error (loading error / timeout ...) + NETWORK_ERROR: 'networkError', + // Identifier for a media Error (video/parsing/mediasource error) + MEDIA_ERROR: 'mediaError', + // Identifier for all other errors + OTHER_ERROR: 'otherError' + }; + + var ErrorDetails = exports.ErrorDetails = { + // Identifier for a manifest load error - data: { url : faulty URL, response : { code: error code, text: error text }} + MANIFEST_LOAD_ERROR: 'manifestLoadError', + // Identifier for a manifest load timeout - data: { url : faulty URL, response : { code: error code, text: error text }} + MANIFEST_LOAD_TIMEOUT: 'manifestLoadTimeOut', + // Identifier for a manifest parsing error - data: { url : faulty URL, reason : error reason} + MANIFEST_PARSING_ERROR: 'manifestParsingError', + // Identifier for a manifest with only incompatible codecs error - data: { url : faulty URL, reason : error reason} + MANIFEST_INCOMPATIBLE_CODECS_ERROR: 'manifestIncompatibleCodecsError', + // Identifier for a level load error - data: { url : faulty URL, response : { code: error code, text: error text }} + LEVEL_LOAD_ERROR: 'levelLoadError', + // Identifier for a level load timeout - data: { url : faulty URL, response : { code: error code, text: error text }} + LEVEL_LOAD_TIMEOUT: 'levelLoadTimeOut', + // Identifier for a level switch error - data: { level : faulty level Id, event : error description} + LEVEL_SWITCH_ERROR: 'levelSwitchError', + // Identifier for an audio track load error - data: { url : faulty URL, response : { code: error code, text: error text }} + AUDIO_TRACK_LOAD_ERROR: 'audioTrackLoadError', + // Identifier for an audio track load timeout - data: { url : faulty URL, response : { code: error code, text: error text }} + AUDIO_TRACK_LOAD_TIMEOUT: 'audioTrackLoadTimeOut', + // Identifier for fragment load error - data: { frag : fragment object, response : { code: error code, text: error text }} + FRAG_LOAD_ERROR: 'fragLoadError', + // Identifier for fragment loop loading error - data: { frag : fragment object} + FRAG_LOOP_LOADING_ERROR: 'fragLoopLoadingError', + // Identifier for fragment load timeout error - data: { frag : fragment object} + FRAG_LOAD_TIMEOUT: 'fragLoadTimeOut', + // Identifier for a fragment decryption error event - data: parsing error description + FRAG_DECRYPT_ERROR: 'fragDecryptError', + // Identifier for a fragment parsing error event - data: parsing error description + FRAG_PARSING_ERROR: 'fragParsingError', + // Identifier for decrypt key load error - data: { frag : fragment object, response : { code: error code, text: error text }} + KEY_LOAD_ERROR: 'keyLoadError', + // Identifier for decrypt key load timeout error - data: { frag : fragment object} + KEY_LOAD_TIMEOUT: 'keyLoadTimeOut', + // Triggered when an exception occurs while adding a sourceBuffer to MediaSource - data : { err : exception , mimeType : mimeType } + BUFFER_ADD_CODEC_ERROR: 'bufferAddCodecError', + // Identifier for a buffer append error - data: append error description + BUFFER_APPEND_ERROR: 'bufferAppendError', + // Identifier for a buffer appending error event - data: appending error description + BUFFER_APPENDING_ERROR: 'bufferAppendingError', + // Identifier for a buffer stalled error event + BUFFER_STALLED_ERROR: 'bufferStalledError', + // Identifier for a buffer full event + BUFFER_FULL_ERROR: 'bufferFullError', + // Identifier for a buffer seek over hole event + BUFFER_SEEK_OVER_HOLE: 'bufferSeekOverHole', + // Identifier for an internal exception happening inside hls.js while handling an event + INTERNAL_EXCEPTION: 'internalException' + }; + + },{}],25:[function(_dereq_,module,exports){ + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "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; }; }(); /* + * + * All objects in the event handling chain should inherit from this class + * + */ + + var _logger = _dereq_(43); + + var _errors = _dereq_(24); + + var _events = _dereq_(26); + + var _events2 = _interopRequireDefault(_events); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + + var EventHandler = function () { + function EventHandler(hls) { + _classCallCheck(this, EventHandler); + + this.hls = hls; + this.onEvent = this.onEvent.bind(this); + + for (var _len = arguments.length, events = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { + events[_key - 1] = arguments[_key]; + } + + this.handledEvents = events; + this.useGenericHandler = true; + + this.registerListeners(); + } + + _createClass(EventHandler, [{ + key: 'destroy', + value: function destroy() { + this.unregisterListeners(); + } + }, { + key: 'isEventHandler', + value: function isEventHandler() { + return _typeof(this.handledEvents) === 'object' && this.handledEvents.length && typeof this.onEvent === 'function'; + } + }, { + key: 'registerListeners', + value: function registerListeners() { + if (this.isEventHandler()) { + this.handledEvents.forEach(function (event) { + if (event === 'hlsEventGeneric') { + throw new Error('Forbidden event name: ' + event); + } + this.hls.on(event, this.onEvent); + }.bind(this)); + } + } + }, { + key: 'unregisterListeners', + value: function unregisterListeners() { + if (this.isEventHandler()) { + this.handledEvents.forEach(function (event) { + this.hls.off(event, this.onEvent); + }.bind(this)); + } + } + + /** + * arguments: event (string), data (any) + */ + + }, { + key: 'onEvent', + value: function onEvent(event, data) { + this.onEventGeneric(event, data); + } + }, { + key: 'onEventGeneric', + value: function onEventGeneric(event, data) { + var eventToFunction = function eventToFunction(event, data) { + var funcName = 'on' + event.replace('hls', ''); + if (typeof this[funcName] !== 'function') { + throw new Error('Event ' + event + ' has no generic handler in this ' + this.constructor.name + ' class (tried ' + funcName + ')'); + } + return this[funcName].bind(this, data); + }; + try { + eventToFunction.call(this, event, data).call(); + } catch (err) { + _logger.logger.error('internal error happened while processing ' + event + ':' + err.message); + this.hls.trigger(_events2.default.ERROR, { type: _errors.ErrorTypes.OTHER_ERROR, details: _errors.ErrorDetails.INTERNAL_EXCEPTION, fatal: false, event: event, err: err }); + } + } + }]); + + return EventHandler; + }(); + + exports.default = EventHandler; + + },{"24":24,"26":26,"43":43}],26:[function(_dereq_,module,exports){ + 'use strict'; + + module.exports = { + // fired before MediaSource is attaching to media element - data: { media } + MEDIA_ATTACHING: 'hlsMediaAttaching', + // fired when MediaSource has been succesfully attached to media element - data: { } + MEDIA_ATTACHED: 'hlsMediaAttached', + // fired before detaching MediaSource from media element - data: { } + MEDIA_DETACHING: 'hlsMediaDetaching', + // fired when MediaSource has been detached from media element - data: { } + MEDIA_DETACHED: 'hlsMediaDetached', + // fired when we buffer is going to be resetted + BUFFER_RESET: 'hlsBufferReset', + // fired when we know about the codecs that we need buffers for to push into - data: {tracks : { container, codec, levelCodec, initSegment, metadata }} + BUFFER_CODECS: 'hlsBufferCodecs', + // fired when sourcebuffers have been created data: { tracks : tracks} + BUFFER_CREATED: 'hlsBufferCreated', + // fired when we append a segment to the buffer - data: { segment: segment object } + BUFFER_APPENDING: 'hlsBufferAppending', + // fired when we are done with appending a media segment to the buffer data : { parent : segment parent that triggered BUFFER_APPENDING } + BUFFER_APPENDED: 'hlsBufferAppended', + // fired when the stream is finished and we want to notify the media buffer that there will be no more data + BUFFER_EOS: 'hlsBufferEos', + // fired when the media buffer should be flushed - data {startOffset, endOffset} + BUFFER_FLUSHING: 'hlsBufferFlushing', + // fired when the media has been flushed + BUFFER_FLUSHED: 'hlsBufferFlushed', + // fired to signal that a manifest loading starts - data: { url : manifestURL} + MANIFEST_LOADING: 'hlsManifestLoading', + // fired after manifest has been loaded - data: { levels : [available quality levels] , audioTracks : [ available audio tracks], url : manifestURL, stats : { trequest, tfirst, tload, mtime}} + MANIFEST_LOADED: 'hlsManifestLoaded', + // fired after manifest has been parsed - data: { levels : [available quality levels] , firstLevel : index of first quality level appearing in Manifest} + MANIFEST_PARSED: 'hlsManifestParsed', + // fired when a level playlist loading starts - data: { url : level URL level : id of level being loaded} + LEVEL_LOADING: 'hlsLevelLoading', + // fired when a level playlist loading finishes - data: { details : levelDetails object, level : id of loaded level, stats : { trequest, tfirst, tload, mtime} } + LEVEL_LOADED: 'hlsLevelLoaded', + // fired when a level's details have been updated based on previous details, after it has been loaded. - data: { details : levelDetails object, level : id of updated level } + LEVEL_UPDATED: 'hlsLevelUpdated', + // fired when a level's PTS information has been updated after parsing a fragment - data: { details : levelDetails object, level : id of updated level, drift: PTS drift observed when parsing last fragment } + LEVEL_PTS_UPDATED: 'hlsLevelPtsUpdated', + // fired when a level switch is requested - data: { level : id of new level } + LEVEL_SWITCH: 'hlsLevelSwitch', + // fired to notify that audio track lists has been updated data: { audioTracks : audioTracks} + AUDIO_TRACKS_UPDATED: 'hlsAudioTracksUpdated', + // fired when an audio track switch occurs - data: { id : audio track id} + AUDIO_TRACK_SWITCH: 'hlsAudioTrackSwitch', + // fired when an audio track loading starts - data: { url : audio track URL id : audio track id} + AUDIO_TRACK_LOADING: 'hlsAudioTrackLoading', + // fired when an audio track loading finishes - data: { details : levelDetails object, id : audio track id, stats : { trequest, tfirst, tload, mtime} } + AUDIO_TRACK_LOADED: 'hlsAudioTrackLoaded', + // fired when a fragment loading starts - data: { frag : fragment object} + FRAG_LOADING: 'hlsFragLoading', + // fired when a fragment loading is progressing - data: { frag : fragment object, { trequest, tfirst, loaded}} + FRAG_LOAD_PROGRESS: 'hlsFragLoadProgress', + // Identifier for fragment load aborting for emergency switch down - data: {frag : fragment object} + FRAG_LOAD_EMERGENCY_ABORTED: 'hlsFragLoadEmergencyAborted', + // fired when a fragment loading is completed - data: { frag : fragment object, payload : fragment payload, stats : { trequest, tfirst, tload, length}} + FRAG_LOADED: 'hlsFragLoaded', + // fired when Init Segment has been extracted from fragment - data: { id : demuxer id, level : levelId, sn : sequence number, moov : moov MP4 box, codecs : codecs found while parsing fragment} + FRAG_PARSING_INIT_SEGMENT: 'hlsFragParsingInitSegment', + // fired when parsing sei text is completed - data: { id : demuxer id, , level : levelId, sn : sequence number, samples : [ sei samples pes ] } + FRAG_PARSING_USERDATA: 'hlsFragParsingUserdata', + // fired when parsing id3 is completed - data: { id : demuxer id, , level : levelId, sn : sequence number, samples : [ id3 samples pes ] } + FRAG_PARSING_METADATA: 'hlsFragParsingMetadata', + // fired when data have been extracted from fragment - data: { id : demuxer id, level : levelId, sn : sequence number, data1 : moof MP4 box or TS fragments, data2 : mdat MP4 box or null} + FRAG_PARSING_DATA: 'hlsFragParsingData', + // fired when fragment parsing is completed - data: { id : demuxer id; level : levelId, sn : sequence number, } + FRAG_PARSED: 'hlsFragParsed', + // fired when fragment remuxed MP4 boxes have all been appended into SourceBuffer - data: { id : demuxer id,frag : fragment object, stats : { trequest, tfirst, tload, tparsed, tbuffered, length} } + FRAG_BUFFERED: 'hlsFragBuffered', + // fired when fragment matching with current media position is changing - data : { id : demuxer id, frag : fragment object } + FRAG_CHANGED: 'hlsFragChanged', + // Identifier for a FPS drop event - data: {curentDropped, currentDecoded, totalDroppedFrames} + FPS_DROP: 'hlsFpsDrop', + //triggered when FPS drop triggers auto level capping - data: {level, droppedlevel} + FPS_DROP_LEVEL_CAPPING: 'hlsFpsDropLevelCapping', + // Identifier for an error event - data: { type : error type, details : error details, fatal : if true, hls.js cannot/will not try to recover, if false, hls.js will try to recover,other error specific data} + ERROR: 'hlsError', + // fired when hls.js instance starts destroying. Different from MEDIA_DETACHED as one could want to detach and reattach a media to the instance of hls.js to handle mid-rolls for example + DESTROYING: 'hlsDestroying', + // fired when a decrypt key loading starts - data: { frag : fragment object} + KEY_LOADING: 'hlsKeyLoading', + // fired when a decrypt key loading is completed - data: { frag : fragment object, payload : key payload, stats : { trequest, tfirst, tload, length}} + KEY_LOADED: 'hlsKeyLoaded', + // fired upon stream controller state transitions - data: {previousState, nextState} + STREAM_STATE_TRANSITION: 'hlsStreamStateTransition' + }; + + },{}],27:[function(_dereq_,module,exports){ + "use strict"; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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; }; }(); + + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + + /** + * AAC helper + */ + + var AAC = function () { + function AAC() { + _classCallCheck(this, AAC); + } + + _createClass(AAC, null, [{ + key: "getSilentFrame", + value: function getSilentFrame(channelCount) { + if (channelCount === 1) { + return new Uint8Array([0x00, 0xc8, 0x00, 0x80, 0x23, 0x80]); + } else if (channelCount === 2) { + return new Uint8Array([0x21, 0x00, 0x49, 0x90, 0x02, 0x19, 0x00, 0x23, 0x80]); + } else if (channelCount === 3) { + return new Uint8Array([0x00, 0xc8, 0x00, 0x80, 0x20, 0x84, 0x01, 0x26, 0x40, 0x08, 0x64, 0x00, 0x8e]); + } else if (channelCount === 4) { + return new Uint8Array([0x00, 0xc8, 0x00, 0x80, 0x20, 0x84, 0x01, 0x26, 0x40, 0x08, 0x64, 0x00, 0x80, 0x2c, 0x80, 0x08, 0x02, 0x38]); + } else if (channelCount === 5) { + return new Uint8Array([0x00, 0xc8, 0x00, 0x80, 0x20, 0x84, 0x01, 0x26, 0x40, 0x08, 0x64, 0x00, 0x82, 0x30, 0x04, 0x99, 0x00, 0x21, 0x90, 0x02, 0x38]); + } else if (channelCount === 6) { + return new Uint8Array([0x00, 0xc8, 0x00, 0x80, 0x20, 0x84, 0x01, 0x26, 0x40, 0x08, 0x64, 0x00, 0x82, 0x30, 0x04, 0x99, 0x00, 0x21, 0x90, 0x02, 0x00, 0xb2, 0x00, 0x20, 0x08, 0xe0]); + } + return null; + } + }]); + + return AAC; + }(); + + exports.default = AAC; + + },{}],28:[function(_dereq_,module,exports){ + "use strict"; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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; }; }(); + + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + + /** + * Buffer Helper class, providing methods dealing buffer length retrieval + */ + + var BufferHelper = function () { + function BufferHelper() { + _classCallCheck(this, BufferHelper); + } + + _createClass(BufferHelper, null, [{ + key: "isBuffered", + value: function isBuffered(media, position) { + if (media) { + var buffered = media.buffered; + for (var i = 0; i < buffered.length; i++) { + if (position >= buffered.start(i) && position <= buffered.end(i)) { + return true; + } + } + } + return false; + } + }, { + key: "bufferInfo", + value: function bufferInfo(media, pos, maxHoleDuration) { + if (media) { + var vbuffered = media.buffered, + buffered = [], + i; + for (i = 0; i < vbuffered.length; i++) { + buffered.push({ start: vbuffered.start(i), end: vbuffered.end(i) }); + } + return this.bufferedInfo(buffered, pos, maxHoleDuration); + } else { + return { len: 0, start: 0, end: 0, nextStart: undefined }; + } + } + }, { + key: "bufferedInfo", + value: function bufferedInfo(buffered, pos, maxHoleDuration) { + var buffered2 = [], + + // bufferStart and bufferEnd are buffer boundaries around current video position + bufferLen, + bufferStart, + bufferEnd, + bufferStartNext, + i; + // sort on buffer.start/smaller end (IE does not always return sorted buffered range) + buffered.sort(function (a, b) { + var diff = a.start - b.start; + if (diff) { + return diff; + } else { + return b.end - a.end; + } + }); + // there might be some small holes between buffer time range + // consider that holes smaller than maxHoleDuration are irrelevant and build another + // buffer time range representations that discards those holes + for (i = 0; i < buffered.length; i++) { + var buf2len = buffered2.length; + if (buf2len) { + var buf2end = buffered2[buf2len - 1].end; + // if small hole (value between 0 or maxHoleDuration ) or overlapping (negative) + if (buffered[i].start - buf2end < maxHoleDuration) { + // merge overlapping time ranges + // update lastRange.end only if smaller than item.end + // e.g. [ 1, 15] with [ 2,8] => [ 1,15] (no need to modify lastRange.end) + // whereas [ 1, 8] with [ 2,15] => [ 1,15] ( lastRange should switch from [1,8] to [1,15]) + if (buffered[i].end > buf2end) { + buffered2[buf2len - 1].end = buffered[i].end; + } + } else { + // big hole + buffered2.push(buffered[i]); + } + } else { + // first value + buffered2.push(buffered[i]); + } + } + for (i = 0, bufferLen = 0, bufferStart = bufferEnd = pos; i < buffered2.length; i++) { + var start = buffered2[i].start, + end = buffered2[i].end; + //logger.log('buf start/end:' + buffered.start(i) + '/' + buffered.end(i)); + if (pos + maxHoleDuration >= start && pos < end) { + // play position is inside this buffer TimeRange, retrieve end of buffer position and buffer length + bufferStart = start; + bufferEnd = end; + bufferLen = bufferEnd - pos; + } else if (pos + maxHoleDuration < start) { + bufferStartNext = start; + break; + } + } + return { len: bufferLen, start: bufferStart, end: bufferEnd, nextStart: bufferStartNext }; + } + }]); + + return BufferHelper; + }(); + + exports.default = BufferHelper; + + },{}],29:[function(_dereq_,module,exports){ + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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; }; }(); /** + * Level Helper class, providing methods dealing with playlist sliding and drift + */ + + var _logger = _dereq_(43); + + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + + var LevelHelper = function () { + function LevelHelper() { + _classCallCheck(this, LevelHelper); + } + + _createClass(LevelHelper, null, [{ + key: 'mergeDetails', + value: function mergeDetails(oldDetails, newDetails) { + var start = Math.max(oldDetails.startSN, newDetails.startSN) - newDetails.startSN, + end = Math.min(oldDetails.endSN, newDetails.endSN) - newDetails.startSN, + delta = newDetails.startSN - oldDetails.startSN, + oldfragments = oldDetails.fragments, + newfragments = newDetails.fragments, + ccOffset = 0, + PTSFrag; + + // check if old/new playlists have fragments in common + if (end < start) { + newDetails.PTSKnown = false; + return; + } + // loop through overlapping SN and update startPTS , cc, and duration if any found + for (var i = start; i <= end; i++) { + var oldFrag = oldfragments[delta + i], + newFrag = newfragments[i]; + if (newFrag && oldFrag) { + ccOffset = oldFrag.cc - newFrag.cc; + if (!isNaN(oldFrag.startPTS)) { + newFrag.start = newFrag.startPTS = oldFrag.startPTS; + newFrag.endPTS = oldFrag.endPTS; + newFrag.duration = oldFrag.duration; + PTSFrag = newFrag; + } + } + } + + if (ccOffset) { + _logger.logger.log('discontinuity sliding from playlist, take drift into account'); + for (i = 0; i < newfragments.length; i++) { + newfragments[i].cc += ccOffset; + } + } + + // if at least one fragment contains PTS info, recompute PTS information for all fragments + if (PTSFrag) { + LevelHelper.updateFragPTSDTS(newDetails, PTSFrag.sn, PTSFrag.startPTS, PTSFrag.endPTS, PTSFrag.startDTS, PTSFrag.endDTS); + } else { + // ensure that delta is within oldfragments range + // also adjust sliding in case delta is 0 (we could have old=[50-60] and new=old=[50-61]) + // in that case we also need to adjust start offset of all fragments + if (delta >= 0 && delta < oldfragments.length) { + // adjust start by sliding offset + var sliding = oldfragments[delta].start; + for (i = 0; i < newfragments.length; i++) { + newfragments[i].start += sliding; + } + } + } + // if we are here, it means we have fragments overlapping between + // old and new level. reliable PTS info is thus relying on old level + newDetails.PTSKnown = oldDetails.PTSKnown; + return; + } + }, { + key: 'updateFragPTSDTS', + value: function updateFragPTSDTS(details, sn, startPTS, endPTS, startDTS, endDTS) { + var fragIdx, fragments, frag, i; + // exit if sn out of range + if (sn < details.startSN || sn > details.endSN) { + return 0; + } + fragIdx = sn - details.startSN; + fragments = details.fragments; + frag = fragments[fragIdx]; + if (!isNaN(frag.startPTS)) { + // delta PTS between audio and video + var deltaPTS = Math.abs(frag.startPTS - startPTS); + if (isNaN(frag.deltaPTS)) { + frag.deltaPTS = deltaPTS; + } else { + frag.deltaPTS = Math.max(deltaPTS, frag.deltaPTS); + } + startPTS = Math.min(startPTS, frag.startPTS); + endPTS = Math.max(endPTS, frag.endPTS); + startDTS = Math.min(startDTS, frag.startDTS); + endDTS = Math.max(endDTS, frag.endDTS); + } + + var drift = startPTS - frag.start; + + frag.start = frag.startPTS = startPTS; + frag.endPTS = endPTS; + frag.startDTS = startDTS; + frag.endDTS = endDTS; + frag.duration = endPTS - startPTS; + // adjust fragment PTS/duration from seqnum-1 to frag 0 + for (i = fragIdx; i > 0; i--) { + LevelHelper.updatePTS(fragments, i, i - 1); + } + + // adjust fragment PTS/duration from seqnum to last frag + for (i = fragIdx; i < fragments.length - 1; i++) { + LevelHelper.updatePTS(fragments, i, i + 1); + } + details.PTSKnown = true; + //logger.log(` frag start/end:${startPTS.toFixed(3)}/${endPTS.toFixed(3)}`); + + return drift; + } + }, { + key: 'updatePTS', + value: function updatePTS(fragments, fromIdx, toIdx) { + var fragFrom = fragments[fromIdx], + fragTo = fragments[toIdx], + fragToPTS = fragTo.startPTS; + // if we know startPTS[toIdx] + if (!isNaN(fragToPTS)) { + // update fragment duration. + // it helps to fix drifts between playlist reported duration and fragment real duration + if (toIdx > fromIdx) { + fragFrom.duration = fragToPTS - fragFrom.start; + if (fragFrom.duration < 0) { + _logger.logger.warn('negative duration computed for frag ' + fragFrom.sn + ',level ' + fragFrom.level + ', there should be some duration drift between playlist and fragment!'); + } + } else { + fragTo.duration = fragFrom.start - fragToPTS; + if (fragTo.duration < 0) { + _logger.logger.warn('negative duration computed for frag ' + fragTo.sn + ',level ' + fragTo.level + ', there should be some duration drift between playlist and fragment!'); + } + } + } else { + // we dont know startPTS[toIdx] + if (toIdx > fromIdx) { + fragTo.start = fragFrom.start + fragFrom.duration; + } else { + fragTo.start = fragFrom.start - fragTo.duration; + } + } + } + }]); + + return LevelHelper; + }(); + + exports.default = LevelHelper; + + },{"43":43}],30:[function(_dereq_,module,exports){ + /** + * HLS interface + */ + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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; }; }(); + //import FetchLoader from './utils/fetch-loader'; + + + var _events = _dereq_(26); + + var _events2 = _interopRequireDefault(_events); + + var _errors = _dereq_(24); + + var _playlistLoader = _dereq_(34); + + var _playlistLoader2 = _interopRequireDefault(_playlistLoader); + + var _fragmentLoader = _dereq_(32); + + var _fragmentLoader2 = _interopRequireDefault(_fragmentLoader); + + var _abrController = _dereq_(3); + + var _abrController2 = _interopRequireDefault(_abrController); + + var _bufferController = _dereq_(6); + + var _bufferController2 = _interopRequireDefault(_bufferController); + + var _capLevelController = _dereq_(7); + + var _capLevelController2 = _interopRequireDefault(_capLevelController); + + var _audioStreamController = _dereq_(4); + + var _audioStreamController2 = _interopRequireDefault(_audioStreamController); + + var _streamController = _dereq_(11); + + var _streamController2 = _interopRequireDefault(_streamController); + + var _levelController = _dereq_(10); + + var _levelController2 = _interopRequireDefault(_levelController); + + var _timelineController = _dereq_(12); + + var _timelineController2 = _interopRequireDefault(_timelineController); + + var _fpsController = _dereq_(9); + + var _fpsController2 = _interopRequireDefault(_fpsController); + + var _audioTrackController = _dereq_(5); + + var _audioTrackController2 = _interopRequireDefault(_audioTrackController); + + var _logger = _dereq_(43); + + var _xhrLoader = _dereq_(47); + + var _xhrLoader2 = _interopRequireDefault(_xhrLoader); + + var _events3 = _dereq_(1); + + var _events4 = _interopRequireDefault(_events3); + + var _keyLoader = _dereq_(33); + + var _keyLoader2 = _interopRequireDefault(_keyLoader); + + var _cues = _dereq_(41); + + var _cues2 = _interopRequireDefault(_cues); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + + var Hls = function () { + _createClass(Hls, null, [{ + key: 'isSupported', + value: function isSupported() { + return window.MediaSource && typeof window.MediaSource.isTypeSupported === 'function' && window.MediaSource.isTypeSupported('video/mp4; codecs="avc1.42E01E,mp4a.40.2"'); + } + }, { + key: 'version', + get: function get() { + // replaced with browserify-versionify transform + return '0.6.7'; + } + }, { + key: 'Events', + get: function get() { + return _events2.default; + } + }, { + key: 'ErrorTypes', + get: function get() { + return _errors.ErrorTypes; + } + }, { + key: 'ErrorDetails', + get: function get() { + return _errors.ErrorDetails; + } + }, { + key: 'DefaultConfig', + get: function get() { + if (!Hls.defaultConfig) { + Hls.defaultConfig = { + autoStartLoad: true, + startPosition: -1, + defaultAudioCodec: undefined, + debug: false, + capLevelOnFPSDrop: false, + capLevelToPlayerSize: false, + maxBufferLength: 30, + maxBufferSize: 60 * 1000 * 1000, + maxBufferHole: 0.5, + maxSeekHole: 2, + seekHoleNudgeDuration: 0.01, + stalledInBufferedNudgeThreshold: 10, + maxFragLookUpTolerance: 0.2, + liveSyncDurationCount: 3, + liveMaxLatencyDurationCount: Infinity, + liveSyncDuration: undefined, + liveMaxLatencyDuration: undefined, + maxMaxBufferLength: 600, + enableWorker: true, + enableSoftwareAES: true, + manifestLoadingTimeOut: 10000, + manifestLoadingMaxRetry: 1, + manifestLoadingRetryDelay: 1000, + manifestLoadingMaxRetryTimeout: 64000, + startLevel: undefined, + levelLoadingTimeOut: 10000, + levelLoadingMaxRetry: 4, + levelLoadingRetryDelay: 1000, + levelLoadingMaxRetryTimeout: 64000, + fragLoadingTimeOut: 20000, + fragLoadingMaxRetry: 6, + fragLoadingRetryDelay: 1000, + fragLoadingMaxRetryTimeout: 64000, + fragLoadingLoopThreshold: 3, + startFragPrefetch: false, + fpsDroppedMonitoringPeriod: 5000, + fpsDroppedMonitoringThreshold: 0.2, + appendErrorMaxRetry: 3, + loader: _xhrLoader2.default, + //loader: FetchLoader, + fLoader: undefined, + pLoader: undefined, + xhrSetup: undefined, + fetchSetup: undefined, + abrController: _abrController2.default, + bufferController: _bufferController2.default, + capLevelController: _capLevelController2.default, + fpsController: _fpsController2.default, + streamController: _streamController2.default, + audioStreamController: _audioStreamController2.default, + timelineController: _timelineController2.default, + cueHandler: _cues2.default, + enableCEA708Captions: true, + enableMP2TPassThrough: false, + stretchShortVideoTrack: false, + forceKeyFrameOnDiscontinuity: true, + abrEwmaFastLive: 5, + abrEwmaSlowLive: 9, + abrEwmaFastVoD: 3, + abrEwmaSlowVoD: 9, + abrEwmaDefaultEstimate: 5e5, // 500 kbps + abrBandWidthFactor: 0.8, + abrBandWidthUpFactor: 0.7, + maxStarvationDelay: 4, + maxLoadingDelay: 4, + minAutoBitrate: 0 + }; + } + return Hls.defaultConfig; + }, + set: function set(defaultConfig) { + Hls.defaultConfig = defaultConfig; + } + }]); + + function Hls() { + var config = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; + + _classCallCheck(this, Hls); + + var defaultConfig = Hls.DefaultConfig; + + if ((config.liveSyncDurationCount || config.liveMaxLatencyDurationCount) && (config.liveSyncDuration || config.liveMaxLatencyDuration)) { + throw new Error('Illegal hls.js config: don\'t mix up liveSyncDurationCount/liveMaxLatencyDurationCount and liveSyncDuration/liveMaxLatencyDuration'); + } + + for (var prop in defaultConfig) { + if (prop in config) { + continue; + } + config[prop] = defaultConfig[prop]; + } + + if (config.liveMaxLatencyDurationCount !== undefined && config.liveMaxLatencyDurationCount <= config.liveSyncDurationCount) { + throw new Error('Illegal hls.js config: "liveMaxLatencyDurationCount" must be gt "liveSyncDurationCount"'); + } + + if (config.liveMaxLatencyDuration !== undefined && (config.liveMaxLatencyDuration <= config.liveSyncDuration || config.liveSyncDuration === undefined)) { + throw new Error('Illegal hls.js config: "liveMaxLatencyDuration" must be gt "liveSyncDuration"'); + } + + (0, _logger.enableLogs)(config.debug); + this.config = config; + // observer setup + var observer = this.observer = new _events4.default(); + observer.trigger = function trigger(event) { + for (var _len = arguments.length, data = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { + data[_key - 1] = arguments[_key]; + } + + observer.emit.apply(observer, [event, event].concat(data)); + }; + + observer.off = function off(event) { + for (var _len2 = arguments.length, data = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) { + data[_key2 - 1] = arguments[_key2]; + } + + observer.removeListener.apply(observer, [event].concat(data)); + }; + this.on = observer.on.bind(observer); + this.off = observer.off.bind(observer); + this.trigger = observer.trigger.bind(observer); + this.playlistLoader = new _playlistLoader2.default(this); + this.fragmentLoader = new _fragmentLoader2.default(this); + this.levelController = new _levelController2.default(this); + this.abrController = new config.abrController(this); + this.bufferController = new config.bufferController(this); + this.capLevelController = new config.capLevelController(this); + this.fpsController = new config.fpsController(this); + this.streamController = new config.streamController(this); + this.audioStreamController = new config.audioStreamController(this); + this.timelineController = new config.timelineController(this); + this.audioTrackController = new _audioTrackController2.default(this); + this.keyLoader = new _keyLoader2.default(this); + } + + _createClass(Hls, [{ + key: 'destroy', + value: function destroy() { + _logger.logger.log('destroy'); + this.trigger(_events2.default.DESTROYING); + this.detachMedia(); + this.playlistLoader.destroy(); + this.fragmentLoader.destroy(); + this.levelController.destroy(); + this.abrController.destroy(); + this.bufferController.destroy(); + this.capLevelController.destroy(); + this.fpsController.destroy(); + this.streamController.destroy(); + this.audioStreamController.destroy(); + this.timelineController.destroy(); + this.audioTrackController.destroy(); + this.keyLoader.destroy(); + this.url = null; + this.observer.removeAllListeners(); + } + }, { + key: 'attachMedia', + value: function attachMedia(media) { + _logger.logger.log('attachMedia'); + this.media = media; + this.trigger(_events2.default.MEDIA_ATTACHING, { media: media }); + } + }, { + key: 'detachMedia', + value: function detachMedia() { + _logger.logger.log('detachMedia'); + this.trigger(_events2.default.MEDIA_DETACHING); + this.media = null; + } + }, { + key: 'loadSource', + value: function loadSource(url) { + _logger.logger.log('loadSource:' + url); + this.url = url; + // when attaching to a source URL, trigger a playlist load + this.trigger(_events2.default.MANIFEST_LOADING, { url: url }); + } + }, { + key: 'startLoad', + value: function startLoad() { + var startPosition = arguments.length <= 0 || arguments[0] === undefined ? -1 : arguments[0]; + + _logger.logger.log('startLoad'); + this.levelController.startLoad(); + this.streamController.startLoad(startPosition); + this.audioStreamController.startLoad(startPosition); + } + }, { + key: 'stopLoad', + value: function stopLoad() { + _logger.logger.log('stopLoad'); + this.levelController.stopLoad(); + this.streamController.stopLoad(); + this.audioStreamController.stopLoad(); + } + }, { + key: 'swapAudioCodec', + value: function swapAudioCodec() { + _logger.logger.log('swapAudioCodec'); + this.streamController.swapAudioCodec(); + } + }, { + key: 'recoverMediaError', + value: function recoverMediaError() { + _logger.logger.log('recoverMediaError'); + var media = this.media; + this.detachMedia(); + this.attachMedia(media); + } + + /** Return all quality levels **/ + + }, { + key: 'levels', + get: function get() { + return this.levelController.levels; + } + + /** Return current playback quality level **/ + + }, { + key: 'currentLevel', + get: function get() { + return this.streamController.currentLevel; + } + + /* set quality level immediately (-1 for automatic level selection) */ + , + set: function set(newLevel) { + _logger.logger.log('set currentLevel:' + newLevel); + this.loadLevel = newLevel; + this.streamController.immediateLevelSwitch(); + } + + /** Return next playback quality level (quality level of next fragment) **/ + + }, { + key: 'nextLevel', + get: function get() { + return this.streamController.nextLevel; + } + + /* set quality level for next fragment (-1 for automatic level selection) */ + , + set: function set(newLevel) { + _logger.logger.log('set nextLevel:' + newLevel); + this.levelController.manualLevel = newLevel; + this.streamController.nextLevelSwitch(); + } + + /** Return the quality level of current/last loaded fragment **/ + + }, { + key: 'loadLevel', + get: function get() { + return this.levelController.level; + } + + /* set quality level for current/next loaded fragment (-1 for automatic level selection) */ + , + set: function set(newLevel) { + _logger.logger.log('set loadLevel:' + newLevel); + this.levelController.manualLevel = newLevel; + } + + /** Return the quality level of next loaded fragment **/ + + }, { + key: 'nextLoadLevel', + get: function get() { + return this.levelController.nextLoadLevel; + } + + /** set quality level of next loaded fragment **/ + , + set: function set(level) { + this.levelController.nextLoadLevel = level; + } + + /** Return first level (index of first level referenced in manifest) + **/ + + }, { + key: 'firstLevel', + get: function get() { + return this.levelController.firstLevel; + } + + /** set first level (index of first level referenced in manifest) + **/ + , + set: function set(newLevel) { + _logger.logger.log('set firstLevel:' + newLevel); + this.levelController.firstLevel = newLevel; + } + + /** Return start level (level of first fragment that will be played back) + if not overrided by user, first level appearing in manifest will be used as start level + if -1 : automatic start level selection, playback will start from level matching download bandwidth (determined from download of first segment) + **/ + + }, { + key: 'startLevel', + get: function get() { + return this.levelController.startLevel; + } + + /** set start level (level of first fragment that will be played back) + if not overrided by user, first level appearing in manifest will be used as start level + if -1 : automatic start level selection, playback will start from level matching download bandwidth (determined from download of first segment) + **/ + , + set: function set(newLevel) { + _logger.logger.log('set startLevel:' + newLevel); + this.levelController.startLevel = newLevel; + } + + /** Return the capping/max level value that could be used by automatic level selection algorithm **/ + + }, { + key: 'autoLevelCapping', + get: function get() { + return this.abrController.autoLevelCapping; + } + + /** set the capping/max level value that could be used by automatic level selection algorithm **/ + , + set: function set(newLevel) { + _logger.logger.log('set autoLevelCapping:' + newLevel); + this.abrController.autoLevelCapping = newLevel; + } + + /* check if we are in automatic level selection mode */ + + }, { + key: 'autoLevelEnabled', + get: function get() { + return this.levelController.manualLevel === -1; + } + + /* return manual level */ + + }, { + key: 'manualLevel', + get: function get() { + return this.levelController.manualLevel; + } + + /** get alternate audio tracks list from playlist **/ + + }, { + key: 'audioTracks', + get: function get() { + return this.audioTrackController.audioTracks; + } + + /** get index of the selected audio track (index in audio track lists) **/ + + }, { + key: 'audioTrack', + get: function get() { + return this.audioTrackController.audioTrack; + } + + /** select an audio track, based on its index in audio track lists**/ + , + set: function set(audioTrackId) { + this.audioTrackController.audioTrack = audioTrackId; + } + }, { + key: 'liveSyncPosition', + get: function get() { + return this.streamController.liveSyncPosition; + } + }]); + + return Hls; + }(); + + exports.default = Hls; + + },{"1":1,"10":10,"11":11,"12":12,"24":24,"26":26,"3":3,"32":32,"33":33,"34":34,"4":4,"41":41,"43":43,"47":47,"5":5,"6":6,"7":7,"9":9}],31:[function(_dereq_,module,exports){ + 'use strict'; + + // This is mostly for support of the es6 module export + // syntax with the babel compiler, it looks like it doesnt support + // function exports like we are used to in node/commonjs + module.exports = _dereq_(30).default; + + },{"30":30}],32:[function(_dereq_,module,exports){ + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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 _events = _dereq_(26); + + var _events2 = _interopRequireDefault(_events); + + var _eventHandler = _dereq_(25); + + var _eventHandler2 = _interopRequireDefault(_eventHandler); + + var _errors = _dereq_(24); + + var _logger = _dereq_(43); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + 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; } /* + * Fragment Loader + */ + + var FragmentLoader = function (_EventHandler) { + _inherits(FragmentLoader, _EventHandler); + + function FragmentLoader(hls) { + _classCallCheck(this, FragmentLoader); + + var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(FragmentLoader).call(this, hls, _events2.default.FRAG_LOADING)); + + _this.loaders = {}; + return _this; + } + + _createClass(FragmentLoader, [{ + key: 'destroy', + value: function destroy() { + var loaders = this.loaders; + for (var loaderName in loaders) { + var loader = loaders[loaderName]; + if (loader) { + loader.destroy(); + } + } + this.loaders = {}; + _eventHandler2.default.prototype.destroy.call(this); + } + }, { + key: 'onFragLoading', + value: function onFragLoading(data) { + var frag = data.frag, + type = frag.type, + loader = this.loaders[type], + config = this.hls.config; + + frag.loaded = 0; + if (loader) { + _logger.logger.warn('abort previous fragment loader for type:' + type); + loader.abort(); + } + loader = this.loaders[type] = frag.loader = typeof config.fLoader !== 'undefined' ? new config.fLoader(config) : new config.loader(config); + + var loaderContext = void 0, + loaderConfig = void 0, + loaderCallbacks = void 0; + loaderContext = { url: frag.url, frag: frag, responseType: 'arraybuffer', progressData: false }; + var start = frag.byteRangeStartOffset, + end = frag.byteRangeEndOffset; + if (!isNaN(start) && !isNaN(end)) { + loaderContext.rangeStart = start; + loaderContext.rangeEnd = end; + } + loaderConfig = { timeout: config.fragLoadingTimeOut, maxRetry: 0, retryDelay: 0, maxRetryDelay: config.fragLoadingMaxRetryTimeout }; + loaderCallbacks = { onSuccess: this.loadsuccess.bind(this), onError: this.loaderror.bind(this), onTimeout: this.loadtimeout.bind(this), onProgress: this.loadprogress.bind(this) }; + loader.load(loaderContext, loaderConfig, loaderCallbacks); + } + }, { + key: 'loadsuccess', + value: function loadsuccess(response, stats, context) { + var payload = response.data, + frag = context.frag; + // detach fragment loader on load success + frag.loader = undefined; + this.loaders[frag.type] = undefined; + this.hls.trigger(_events2.default.FRAG_LOADED, { payload: payload, frag: frag, stats: stats }); + } + }, { + key: 'loaderror', + value: function loaderror(response, context) { + var loader = context.loader; + if (loader) { + loader.abort(); + } + this.loaders[context.type] = undefined; + this.hls.trigger(_events2.default.ERROR, { type: _errors.ErrorTypes.NETWORK_ERROR, details: _errors.ErrorDetails.FRAG_LOAD_ERROR, fatal: false, frag: context.frag, response: response }); + } + }, { + key: 'loadtimeout', + value: function loadtimeout(stats, context) { + var loader = context.loader; + if (loader) { + loader.abort(); + } + this.loaders[context.type] = undefined; + this.hls.trigger(_events2.default.ERROR, { type: _errors.ErrorTypes.NETWORK_ERROR, details: _errors.ErrorDetails.FRAG_LOAD_TIMEOUT, fatal: false, frag: context.frag }); + } + + // data will be used for progressive parsing + + }, { + key: 'loadprogress', + value: function loadprogress(stats, context, data) { + // jshint ignore:line + var frag = context.frag; + frag.loaded = stats.loaded; + this.hls.trigger(_events2.default.FRAG_LOAD_PROGRESS, { frag: frag, stats: stats }); + } + }]); + + return FragmentLoader; + }(_eventHandler2.default); + + exports.default = FragmentLoader; + + },{"24":24,"25":25,"26":26,"43":43}],33:[function(_dereq_,module,exports){ + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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 _events = _dereq_(26); + + var _events2 = _interopRequireDefault(_events); + + var _eventHandler = _dereq_(25); + + var _eventHandler2 = _interopRequireDefault(_eventHandler); + + var _errors = _dereq_(24); + + var _logger = _dereq_(43); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + 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; } /* + * Decrypt key Loader + */ + + var KeyLoader = function (_EventHandler) { + _inherits(KeyLoader, _EventHandler); + + function KeyLoader(hls) { + _classCallCheck(this, KeyLoader); + + var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(KeyLoader).call(this, hls, _events2.default.KEY_LOADING)); + + _this.loaders = {}; + _this.decryptkey = null; + _this.decrypturl = null; + return _this; + } + + _createClass(KeyLoader, [{ + key: 'destroy', + value: function destroy() { + for (var loaderName in this.loaders) { + var loader = this.loaders[loaderName]; + if (loader) { + loader.destroy(); + } + } + this.loaders = {}; + _eventHandler2.default.prototype.destroy.call(this); + } + }, { + key: 'onKeyLoading', + value: function onKeyLoading(data) { + var frag = data.frag, + type = frag.type, + loader = this.loaders[type], + decryptdata = frag.decryptdata, + uri = decryptdata.uri; + // if uri is different from previous one or if decrypt key not retrieved yet + if (uri !== this.decrypturl || this.decryptkey === null) { + var config = this.hls.config; + + if (loader) { + _logger.logger.warn('abort previous fragment loader for type:' + type); + loader.abort(); + } + frag.loader = this.loaders[type] = new config.loader(config); + this.decrypturl = uri; + this.decryptkey = null; + + var loaderContext = void 0, + loaderConfig = void 0, + loaderCallbacks = void 0; + loaderContext = { url: uri, frag: frag, responseType: 'arraybuffer' }; + loaderConfig = { timeout: config.fragLoadingTimeOut, maxRetry: config.fragLoadingMaxRetry, retryDelay: config.fragLoadingRetryDelay, maxRetryDelay: config.fragLoadingMaxRetryTimeout }; + loaderCallbacks = { onSuccess: this.loadsuccess.bind(this), onError: this.loaderror.bind(this), onTimeout: this.loadtimeout.bind(this) }; + frag.loader.load(loaderContext, loaderConfig, loaderCallbacks); + } else if (this.decryptkey) { + // we already loaded this key, return it + decryptdata.key = this.decryptkey; + this.hls.trigger(_events2.default.KEY_LOADED, { frag: frag }); + } + } + }, { + key: 'loadsuccess', + value: function loadsuccess(response, stats, context) { + var frag = context.frag; + this.decryptkey = frag.decryptdata.key = new Uint8Array(response.data); + // detach fragment loader on load success + frag.loader = undefined; + this.loaders[context.type] = undefined; + this.hls.trigger(_events2.default.KEY_LOADED, { frag: frag }); + } + }, { + key: 'loaderror', + value: function loaderror(response, context) { + var frag = context.frag, + loader = frag.loader; + if (loader) { + loader.abort(); + } + this.loaders[context.type] = undefined; + this.hls.trigger(_events2.default.ERROR, { type: _errors.ErrorTypes.NETWORK_ERROR, details: _errors.ErrorDetails.KEY_LOAD_ERROR, fatal: false, frag: frag, response: response }); + } + }, { + key: 'loadtimeout', + value: function loadtimeout(stats, context) { + var frag = context.frag, + loader = frag.loader; + if (loader) { + loader.abort(); + } + this.loaders[context.type] = undefined; + this.hls.trigger(_events2.default.ERROR, { type: _errors.ErrorTypes.NETWORK_ERROR, details: _errors.ErrorDetails.KEY_LOAD_TIMEOUT, fatal: false, frag: frag }); + } + }]); + + return KeyLoader; + }(_eventHandler2.default); + + exports.default = KeyLoader; + + },{"24":24,"25":25,"26":26,"43":43}],34:[function(_dereq_,module,exports){ + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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 _events = _dereq_(26); + + var _events2 = _interopRequireDefault(_events); + + var _eventHandler = _dereq_(25); + + var _eventHandler2 = _interopRequireDefault(_eventHandler); + + var _errors = _dereq_(24); + + var _url = _dereq_(46); + + var _url2 = _interopRequireDefault(_url); + + var _attrList = _dereq_(38); + + var _attrList2 = _interopRequireDefault(_attrList); + + var _logger = _dereq_(43); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + 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; } /** + * Playlist Loader + */ + + var PlaylistLoader = function (_EventHandler) { + _inherits(PlaylistLoader, _EventHandler); + + function PlaylistLoader(hls) { + _classCallCheck(this, PlaylistLoader); + + var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(PlaylistLoader).call(this, hls, _events2.default.MANIFEST_LOADING, _events2.default.LEVEL_LOADING, _events2.default.AUDIO_TRACK_LOADING)); + + _this.loaders = {}; + return _this; + } + + _createClass(PlaylistLoader, [{ + key: 'destroy', + value: function destroy() { + for (var loaderName in this.loaders) { + var loader = this.loaders[loaderName]; + if (loader) { + loader.destroy(); + } + } + this.loaders = {}; + _eventHandler2.default.prototype.destroy.call(this); + } + }, { + key: 'onManifestLoading', + value: function onManifestLoading(data) { + this.load(data.url, { type: 'manifest' }); + } + }, { + key: 'onLevelLoading', + value: function onLevelLoading(data) { + this.load(data.url, { type: 'level', level: data.level, id: data.id }); + } + }, { + key: 'onAudioTrackLoading', + value: function onAudioTrackLoading(data) { + this.load(data.url, { type: 'audioTrack', id: data.id }); + } + }, { + key: 'load', + value: function load(url, context) { + var loader = this.loaders[context.type]; + if (loader) { + var loaderContext = loader.context; + if (loaderContext && loaderContext.url === url) { + _logger.logger.trace('playlist request ongoing'); + return; + } else { + _logger.logger.warn('abort previous loader for type:' + context.type); + loader.abort(); + } + } + var config = this.hls.config, + retry = void 0, + timeout = void 0, + retryDelay = void 0, + maxRetryDelay = void 0; + if (context.type === 'manifest') { + retry = config.manifestLoadingMaxRetry; + timeout = config.manifestLoadingTimeOut; + retryDelay = config.manifestLoadingRetryDelay; + maxRetryDelay = config.manifestLoadingMaxRetryTimeOut; + } else { + retry = config.levelLoadingMaxRetry; + timeout = config.levelLoadingTimeOut; + retryDelay = config.levelLoadingRetryDelay; + maxRetryDelay = config.levelLoadingMaxRetryTimeOut; + _logger.logger.log('loading playlist for level ' + context.level); + } + loader = this.loaders[context.type] = context.loader = typeof config.pLoader !== 'undefined' ? new config.pLoader(config) : new config.loader(config); + context.url = url; + context.responseType = ''; + + var loaderConfig = void 0, + loaderCallbacks = void 0; + loaderConfig = { timeout: timeout, maxRetry: retry, retryDelay: retryDelay, maxRetryDelay: maxRetryDelay }; + loaderCallbacks = { onSuccess: this.loadsuccess.bind(this), onError: this.loaderror.bind(this), onTimeout: this.loadtimeout.bind(this) }; + loader.load(context, loaderConfig, loaderCallbacks); + } + }, { + key: 'resolve', + value: function resolve(url, baseUrl) { + return _url2.default.buildAbsoluteURL(baseUrl, url); + } + }, { + key: 'parseMasterPlaylist', + value: function parseMasterPlaylist(string, baseurl) { + var levels = [], + result = void 0; + + // https://regex101.com is your friend + var re = /#EXT-X-STREAM-INF:([^\n\r]*)[\r\n]+([^\r\n]+)/g; + while ((result = re.exec(string)) != null) { + var level = {}; + + var attrs = level.attrs = new _attrList2.default(result[1]); + level.url = this.resolve(result[2], baseurl); + + var resolution = attrs.decimalResolution('RESOLUTION'); + if (resolution) { + level.width = resolution.width; + level.height = resolution.height; + } + level.bitrate = attrs.decimalInteger('AVERAGE-BANDWIDTH') || attrs.decimalInteger('BANDWIDTH'); + level.name = attrs.NAME; + + var codecs = attrs.CODECS; + if (codecs) { + codecs = codecs.split(','); + for (var i = 0; i < codecs.length; i++) { + var codec = codecs[i]; + if (codec.indexOf('avc1') !== -1) { + level.videoCodec = this.avc1toavcoti(codec); + } else { + level.audioCodec = codec; + } + } + } + + levels.push(level); + } + return levels; + } + }, { + key: 'parseMasterPlaylistMedia', + value: function parseMasterPlaylistMedia(string, baseurl, type) { + var result = void 0, + medias = []; + + // https://regex101.com is your friend + var re = /#EXT-X-MEDIA:(.*)/g; + while ((result = re.exec(string)) != null) { + var media = {}; + var attrs = new _attrList2.default(result[1]); + if (attrs.TYPE === type) { + media.groupId = attrs['GROUP-ID']; + media.name = attrs.NAME; + media.type = type; + media.default = attrs.DEFAULT === 'YES'; + media.autoselect = attrs.AUTOSELECT === 'YES'; + media.forced = attrs.FORCED === 'YES'; + if (attrs.URI) { + media.url = this.resolve(attrs.URI, baseurl); + } + media.lang = attrs.LANGUAGE; + if (!media.name) { + media.name = media.lang; + } + medias.push(media); + } + } + return medias; + } + /** + * Utility method for parseLevelPlaylist to create an initialization vector for a given segment + * @returns {Uint8Array} + */ + + }, { + key: 'createInitializationVector', + value: function createInitializationVector(segmentNumber) { + var uint8View = new Uint8Array(16); + + for (var i = 12; i < 16; i++) { + uint8View[i] = segmentNumber >> 8 * (15 - i) & 0xff; + } + + return uint8View; + } + + /** + * Utility method for parseLevelPlaylist to get a fragment's decryption data from the currently parsed encryption key data + * @param levelkey - a playlist's encryption info + * @param segmentNumber - the fragment's segment number + * @returns {*} - an object to be applied as a fragment's decryptdata + */ + + }, { + key: 'fragmentDecryptdataFromLevelkey', + value: function fragmentDecryptdataFromLevelkey(levelkey, segmentNumber) { + var decryptdata = levelkey; + + if (levelkey && levelkey.method && levelkey.uri && !levelkey.iv) { + decryptdata = this.cloneObj(levelkey); + decryptdata.iv = this.createInitializationVector(segmentNumber); + } + + return decryptdata; + } + }, { + key: 'avc1toavcoti', + value: function avc1toavcoti(codec) { + var result, + avcdata = codec.split('.'); + if (avcdata.length > 2) { + result = avcdata.shift() + '.'; + result += parseInt(avcdata.shift()).toString(16); + result += ('000' + parseInt(avcdata.shift()).toString(16)).substr(-4); + } else { + result = codec; + } + return result; + } + }, { + key: 'cloneObj', + value: function cloneObj(obj) { + return JSON.parse(JSON.stringify(obj)); + } + }, { + key: 'parseLevelPlaylist', + value: function parseLevelPlaylist(string, baseurl, id, type) { + var currentSN = 0, + fragdecryptdata, + totalduration = 0, + level = { type: null, version: null, url: baseurl, fragments: [], live: true, startSN: 0 }, + levelkey = { method: null, key: null, iv: null, uri: null }, + cc = 0, + programDateTime = null, + frag = null, + result, + regexp, + duration = null, + title = null, + byteRangeEndOffset = null, + byteRangeStartOffset = null, + tagList = []; + + regexp = /(?:(?:#(EXTM3U))|(?:#EXT-X-(PLAYLIST-TYPE):(.+))|(?:#EXT-X-(MEDIA-SEQUENCE):(\d+))|(?:#EXT-X-(TARGETDURATION):(\d+))|(?:#EXT-X-(KEY):(.+))|(?:#EXT-X-(START):(.+))|(?:#EXT(INF):(\d+(?:\.\d+)?)(?:,(.*))?)|(?:(?!#)()(\S.+))|(?:#EXT-X-(BYTERANGE):(\d+(?:@\d+(?:\.\d+)?)?)|(?:#EXT-X-(ENDLIST))|(?:#EXT-X-(DIS)CONTINUITY))|(?:#EXT-X-(PROGRAM-DATE-TIME):(.+))|(?:#EXT-X-(VERSION):(\d+))|(?:(#)(.*):(.*))|(?:(#)(.*)))(?:.*)\r?\n?/g; + while ((result = regexp.exec(string)) !== null) { + result.shift(); + result = result.filter(function (n) { + return n !== undefined; + }); + switch (result[0]) { + case 'PLAYLIST-TYPE': + level.type = result[1].toUpperCase(); + break; + case 'MEDIA-SEQUENCE': + currentSN = level.startSN = parseInt(result[1]); + break; + case 'TARGETDURATION': + level.targetduration = parseFloat(result[1]); + break; + case 'VERSION': + level.version = parseInt(result[1]); + break; + case 'EXTM3U': + break; + case 'ENDLIST': + level.live = false; + break; + case 'DIS': + cc++; + tagList.push(result); + break; + case 'BYTERANGE': + var params = result[1].split('@'); + if (params.length === 1) { + byteRangeStartOffset = byteRangeEndOffset; + } else { + byteRangeStartOffset = parseInt(params[1]); + } + byteRangeEndOffset = parseInt(params[0]) + byteRangeStartOffset; + break; + case 'INF': + duration = parseFloat(result[1]); + title = result[2] ? result[2] : null; + tagList.push(result); + break; + case '': + // url + if (!isNaN(duration)) { + var sn = currentSN++; + fragdecryptdata = this.fragmentDecryptdataFromLevelkey(levelkey, sn); + var url = result[1] ? this.resolve(result[1], baseurl) : null; + frag = { url: url, + type: type, + duration: duration, + title: title, + start: totalduration, + sn: sn, + level: id, + cc: cc, + decryptdata: fragdecryptdata, + programDateTime: programDateTime, + tagList: tagList }; + // only include byte range options if used/needed + if (byteRangeStartOffset !== null) { + frag.byteRangeStartOffset = byteRangeStartOffset; + frag.byteRangeEndOffset = byteRangeEndOffset; + } + level.fragments.push(frag); + totalduration += duration; + duration = null; + title = null; + byteRangeStartOffset = null; + programDateTime = null; + tagList = []; + } + break; + case 'KEY': + // https://tools.ietf.org/html/draft-pantos-http-live-streaming-08#section-3.4.4 + var decryptparams = result[1]; + var keyAttrs = new _attrList2.default(decryptparams); + var decryptmethod = keyAttrs.enumeratedString('METHOD'), + decrypturi = keyAttrs.URI, + decryptiv = keyAttrs.hexadecimalInteger('IV'); + if (decryptmethod) { + levelkey = { method: null, key: null, iv: null, uri: null }; + if (decrypturi && decryptmethod === 'AES-128') { + levelkey.method = decryptmethod; + // URI to get the key + levelkey.uri = this.resolve(decrypturi, baseurl); + levelkey.key = null; + // Initialization Vector (IV) + levelkey.iv = decryptiv; + } + } + break; + case 'START': + var startParams = result[1]; + var startAttrs = new _attrList2.default(startParams); + var startTimeOffset = startAttrs.decimalFloatingPoint('TIME-OFFSET'); + //TIME-OFFSET can be 0 + if (!isNaN(startTimeOffset)) { + level.startTimeOffset = startTimeOffset; + } + break; + case 'PROGRAM-DATE-TIME': + programDateTime = new Date(Date.parse(result[1])); + tagList.push(result); + break; + case '#': + result.shift(); + tagList.push(result); + break; + default: + _logger.logger.warn('line parsed but not handled: ' + result); + break; + } + } + //logger.log('found ' + level.fragments.length + ' fragments'); + if (frag && !frag.url) { + level.fragments.pop(); + totalduration -= frag.duration; + } + level.totalduration = totalduration; + level.averagetargetduration = totalduration / level.fragments.length; + level.endSN = currentSN - 1; + return level; + } + }, { + key: 'loadsuccess', + value: function loadsuccess(response, stats, context) { + var string = response.data, + url = response.url, + type = context.type, + id = context.id, + level = context.level, + hls = this.hls; + + this.loaders[type] = undefined; + // responseURL not supported on some browsers (it is used to detect URL redirection) + // data-uri mode also not supported (but no need to detect redirection) + if (url === undefined || url.indexOf('data:') === 0) { + // fallback to initial URL + url = context.url; + } + stats.tload = performance.now(); + //stats.mtime = new Date(target.getResponseHeader('Last-Modified')); + if (string.indexOf('#EXTM3U') === 0) { + if (string.indexOf('#EXTINF:') > 0) { + var isLevel = type !== 'audioTrack', + levelDetails = this.parseLevelPlaylist(string, url, (isLevel ? level : id) || 0, isLevel ? 'main' : 'audio'); + if (type === 'manifest') { + // first request, stream manifest (no master playlist), fire manifest loaded event with level details + hls.trigger(_events2.default.MANIFEST_LOADED, { levels: [{ url: url, details: levelDetails }], audioTracks: [], url: url, stats: stats }); + } + stats.tparsed = performance.now(); + if (isLevel) { + hls.trigger(_events2.default.LEVEL_LOADED, { details: levelDetails, level: level || 0, id: id || 0, stats: stats }); + } else { + hls.trigger(_events2.default.AUDIO_TRACK_LOADED, { details: levelDetails, id: id, stats: stats }); + } + } else { + var levels = this.parseMasterPlaylist(string, url); + // multi level playlist, parse level info + if (levels.length) { + var audiotracks = this.parseMasterPlaylistMedia(string, url, 'AUDIO'); + if (audiotracks.length) { + // check if we have found an audio track embedded in main playlist (audio track without URI attribute) + var embeddedAudioFound = false; + audiotracks.forEach(function (audioTrack) { + if (!audioTrack.url) { + embeddedAudioFound = true; + } + }); + // if no embedded audio track defined, but audio codec signaled in quality level, we need to signal this main audio track + // this could happen with playlists with alt audio rendition in which quality levels (main) contains both audio+video. but with mixed audio track not signaled + if (embeddedAudioFound === false && levels[0].audioCodec && !levels[0].attrs.AUDIO) { + _logger.logger.log('audio codec signaled in quality level, but no embedded audio track signaled, create one'); + audiotracks.unshift({ type: 'main', name: 'main' }); + } + } + hls.trigger(_events2.default.MANIFEST_LOADED, { levels: levels, audioTracks: audiotracks, url: url, stats: stats }); + } else { + hls.trigger(_events2.default.ERROR, { type: _errors.ErrorTypes.NETWORK_ERROR, details: _errors.ErrorDetails.MANIFEST_PARSING_ERROR, fatal: true, url: url, reason: 'no level found in manifest' }); + } + } + } else { + hls.trigger(_events2.default.ERROR, { type: _errors.ErrorTypes.NETWORK_ERROR, details: _errors.ErrorDetails.MANIFEST_PARSING_ERROR, fatal: true, url: url, reason: 'no EXTM3U delimiter' }); + } + } + }, { + key: 'loaderror', + value: function loaderror(response, context) { + var details, + fatal, + loader = context.loader; + switch (context.type) { + case 'manifest': + details = _errors.ErrorDetails.MANIFEST_LOAD_ERROR; + fatal = true; + break; + case 'level': + details = _errors.ErrorDetails.LEVEL_LOAD_ERROR; + fatal = false; + break; + case 'audioTrack': + details = _errors.ErrorDetails.AUDIO_TRACK_LOAD_ERROR; + fatal = false; + break; + } + if (loader) { + loader.abort(); + this.loaders[context.type] = undefined; + } + this.hls.trigger(_events2.default.ERROR, { type: _errors.ErrorTypes.NETWORK_ERROR, details: details, fatal: fatal, url: loader.url, loader: loader, response: response, context: context }); + } + }, { + key: 'loadtimeout', + value: function loadtimeout(stats, context) { + var details, + fatal, + loader = context.loader; + switch (context.type) { + case 'manifest': + details = _errors.ErrorDetails.MANIFEST_LOAD_TIMEOUT; + fatal = true; + break; + case 'level': + details = _errors.ErrorDetails.LEVEL_LOAD_TIMEOUT; + fatal = false; + break; + case 'audioTrack': + details = _errors.ErrorDetails.AUDIO_TRACK_LOAD_TIMEOUT; + fatal = false; + break; + } + if (loader) { + loader.abort(); + this.loaders[context.type] = undefined; + } + this.hls.trigger(_events2.default.ERROR, { type: _errors.ErrorTypes.NETWORK_ERROR, details: details, fatal: fatal, url: loader.url, loader: loader, context: context }); + } + }]); + + return PlaylistLoader; + }(_eventHandler2.default); + + exports.default = PlaylistLoader; + + },{"24":24,"25":25,"26":26,"38":38,"43":43,"46":46}],35:[function(_dereq_,module,exports){ + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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; }; }(); + + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + + /** + * Generate MP4 Box + */ + + //import Hex from '../utils/hex'; + + var MP4 = function () { + function MP4() { + _classCallCheck(this, MP4); + } + + _createClass(MP4, null, [{ + key: 'init', + value: function init() { + MP4.types = { + avc1: [], // codingname + avcC: [], + btrt: [], + dinf: [], + dref: [], + esds: [], + ftyp: [], + hdlr: [], + mdat: [], + mdhd: [], + mdia: [], + mfhd: [], + minf: [], + moof: [], + moov: [], + mp4a: [], + mvex: [], + mvhd: [], + sdtp: [], + stbl: [], + stco: [], + stsc: [], + stsd: [], + stsz: [], + stts: [], + tfdt: [], + tfhd: [], + traf: [], + trak: [], + trun: [], + trex: [], + tkhd: [], + vmhd: [], + smhd: [] + }; + + var i; + for (i in MP4.types) { + if (MP4.types.hasOwnProperty(i)) { + MP4.types[i] = [i.charCodeAt(0), i.charCodeAt(1), i.charCodeAt(2), i.charCodeAt(3)]; + } + } + + var videoHdlr = new Uint8Array([0x00, // version 0 + 0x00, 0x00, 0x00, // flags + 0x00, 0x00, 0x00, 0x00, // pre_defined + 0x76, 0x69, 0x64, 0x65, // handler_type: 'vide' + 0x00, 0x00, 0x00, 0x00, // reserved + 0x00, 0x00, 0x00, 0x00, // reserved + 0x00, 0x00, 0x00, 0x00, // reserved + 0x56, 0x69, 0x64, 0x65, 0x6f, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x00 // name: 'VideoHandler' + ]); + + var audioHdlr = new Uint8Array([0x00, // version 0 + 0x00, 0x00, 0x00, // flags + 0x00, 0x00, 0x00, 0x00, // pre_defined + 0x73, 0x6f, 0x75, 0x6e, // handler_type: 'soun' + 0x00, 0x00, 0x00, 0x00, // reserved + 0x00, 0x00, 0x00, 0x00, // reserved + 0x00, 0x00, 0x00, 0x00, // reserved + 0x53, 0x6f, 0x75, 0x6e, 0x64, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x00 // name: 'SoundHandler' + ]); + + MP4.HDLR_TYPES = { + 'video': videoHdlr, + 'audio': audioHdlr + }; + + var dref = new Uint8Array([0x00, // version 0 + 0x00, 0x00, 0x00, // flags + 0x00, 0x00, 0x00, 0x01, // entry_count + 0x00, 0x00, 0x00, 0x0c, // entry_size + 0x75, 0x72, 0x6c, 0x20, // 'url' type + 0x00, // version 0 + 0x00, 0x00, 0x01 // entry_flags + ]); + + var stco = new Uint8Array([0x00, // version + 0x00, 0x00, 0x00, // flags + 0x00, 0x00, 0x00, 0x00 // entry_count + ]); + + MP4.STTS = MP4.STSC = MP4.STCO = stco; + + MP4.STSZ = new Uint8Array([0x00, // version + 0x00, 0x00, 0x00, // flags + 0x00, 0x00, 0x00, 0x00, // sample_size + 0x00, 0x00, 0x00, 0x00]); + // sample_count + MP4.VMHD = new Uint8Array([0x00, // version + 0x00, 0x00, 0x01, // flags + 0x00, 0x00, // graphicsmode + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // opcolor + ]); + MP4.SMHD = new Uint8Array([0x00, // version + 0x00, 0x00, 0x00, // flags + 0x00, 0x00, // balance + 0x00, 0x00 // reserved + ]); + + MP4.STSD = new Uint8Array([0x00, // version 0 + 0x00, 0x00, 0x00, // flags + 0x00, 0x00, 0x00, 0x01]); // entry_count + + var majorBrand = new Uint8Array([105, 115, 111, 109]); // isom + var avc1Brand = new Uint8Array([97, 118, 99, 49]); // avc1 + var minorVersion = new Uint8Array([0, 0, 0, 1]); + + MP4.FTYP = MP4.box(MP4.types.ftyp, majorBrand, minorVersion, majorBrand, avc1Brand); + MP4.DINF = MP4.box(MP4.types.dinf, MP4.box(MP4.types.dref, dref)); + } + }, { + key: 'box', + value: function box(type) { + var payload = Array.prototype.slice.call(arguments, 1), + size = 8, + i = payload.length, + len = i, + result; + // calculate the total size we need to allocate + while (i--) { + size += payload[i].byteLength; + } + result = new Uint8Array(size); + result[0] = size >> 24 & 0xff; + result[1] = size >> 16 & 0xff; + result[2] = size >> 8 & 0xff; + result[3] = size & 0xff; + result.set(type, 4); + // copy the payload into the result + for (i = 0, size = 8; i < len; i++) { + // copy payload[i] array @ offset size + result.set(payload[i], size); + size += payload[i].byteLength; + } + return result; + } + }, { + key: 'hdlr', + value: function hdlr(type) { + return MP4.box(MP4.types.hdlr, MP4.HDLR_TYPES[type]); + } + }, { + key: 'mdat', + value: function mdat(data) { + return MP4.box(MP4.types.mdat, data); + } + }, { + key: 'mdhd', + value: function mdhd(timescale, duration) { + duration *= timescale; + return MP4.box(MP4.types.mdhd, new Uint8Array([0x00, // version 0 + 0x00, 0x00, 0x00, // flags + 0x00, 0x00, 0x00, 0x02, // creation_time + 0x00, 0x00, 0x00, 0x03, // modification_time + timescale >> 24 & 0xFF, timescale >> 16 & 0xFF, timescale >> 8 & 0xFF, timescale & 0xFF, // timescale + duration >> 24, duration >> 16 & 0xFF, duration >> 8 & 0xFF, duration & 0xFF, // duration + 0x55, 0xc4, // 'und' language (undetermined) + 0x00, 0x00])); + } + }, { + key: 'mdia', + value: function mdia(track) { + return MP4.box(MP4.types.mdia, MP4.mdhd(track.timescale, track.duration), MP4.hdlr(track.type), MP4.minf(track)); + } + }, { + key: 'mfhd', + value: function mfhd(sequenceNumber) { + return MP4.box(MP4.types.mfhd, new Uint8Array([0x00, 0x00, 0x00, 0x00, // flags + sequenceNumber >> 24, sequenceNumber >> 16 & 0xFF, sequenceNumber >> 8 & 0xFF, sequenceNumber & 0xFF])); + } + }, { + key: 'minf', + // sequence_number + value: function minf(track) { + if (track.type === 'audio') { + return MP4.box(MP4.types.minf, MP4.box(MP4.types.smhd, MP4.SMHD), MP4.DINF, MP4.stbl(track)); + } else { + return MP4.box(MP4.types.minf, MP4.box(MP4.types.vmhd, MP4.VMHD), MP4.DINF, MP4.stbl(track)); + } + } + }, { + key: 'moof', + value: function moof(sn, baseMediaDecodeTime, track) { + return MP4.box(MP4.types.moof, MP4.mfhd(sn), MP4.traf(track, baseMediaDecodeTime)); + } + /** + * @param tracks... (optional) {array} the tracks associated with this movie + */ + + }, { + key: 'moov', + value: function moov(tracks) { + var i = tracks.length, + boxes = []; + + while (i--) { + boxes[i] = MP4.trak(tracks[i]); + } + + return MP4.box.apply(null, [MP4.types.moov, MP4.mvhd(tracks[0].timescale, tracks[0].duration)].concat(boxes).concat(MP4.mvex(tracks))); + } + }, { + key: 'mvex', + value: function mvex(tracks) { + var i = tracks.length, + boxes = []; + + while (i--) { + boxes[i] = MP4.trex(tracks[i]); + } + return MP4.box.apply(null, [MP4.types.mvex].concat(boxes)); + } + }, { + key: 'mvhd', + value: function mvhd(timescale, duration) { + duration *= timescale; + var bytes = new Uint8Array([0x00, // version 0 + 0x00, 0x00, 0x00, // flags + 0x00, 0x00, 0x00, 0x01, // creation_time + 0x00, 0x00, 0x00, 0x02, // modification_time + timescale >> 24 & 0xFF, timescale >> 16 & 0xFF, timescale >> 8 & 0xFF, timescale & 0xFF, // timescale + duration >> 24 & 0xFF, duration >> 16 & 0xFF, duration >> 8 & 0xFF, duration & 0xFF, // duration + 0x00, 0x01, 0x00, 0x00, // 1.0 rate + 0x01, 0x00, // 1.0 volume + 0x00, 0x00, // reserved + 0x00, 0x00, 0x00, 0x00, // reserved + 0x00, 0x00, 0x00, 0x00, // reserved + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, // transformation: unity matrix + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // pre_defined + 0xff, 0xff, 0xff, 0xff // next_track_ID + ]); + return MP4.box(MP4.types.mvhd, bytes); + } + }, { + key: 'sdtp', + value: function sdtp(track) { + var samples = track.samples || [], + bytes = new Uint8Array(4 + samples.length), + flags, + i; + // leave the full box header (4 bytes) all zero + // write the sample table + for (i = 0; i < samples.length; i++) { + flags = samples[i].flags; + bytes[i + 4] = flags.dependsOn << 4 | flags.isDependedOn << 2 | flags.hasRedundancy; + } + + return MP4.box(MP4.types.sdtp, bytes); + } + }, { + key: 'stbl', + value: function stbl(track) { + return MP4.box(MP4.types.stbl, MP4.stsd(track), MP4.box(MP4.types.stts, MP4.STTS), MP4.box(MP4.types.stsc, MP4.STSC), MP4.box(MP4.types.stsz, MP4.STSZ), MP4.box(MP4.types.stco, MP4.STCO)); + } + }, { + key: 'avc1', + value: function avc1(track) { + var sps = [], + pps = [], + i, + data, + len; + // assemble the SPSs + + for (i = 0; i < track.sps.length; i++) { + data = track.sps[i]; + len = data.byteLength; + sps.push(len >>> 8 & 0xFF); + sps.push(len & 0xFF); + sps = sps.concat(Array.prototype.slice.call(data)); // SPS + } + + // assemble the PPSs + for (i = 0; i < track.pps.length; i++) { + data = track.pps[i]; + len = data.byteLength; + pps.push(len >>> 8 & 0xFF); + pps.push(len & 0xFF); + pps = pps.concat(Array.prototype.slice.call(data)); + } + + var avcc = MP4.box(MP4.types.avcC, new Uint8Array([0x01, // version + sps[3], // profile + sps[4], // profile compat + sps[5], // level + 0xfc | 3, // lengthSizeMinusOne, hard-coded to 4 bytes + 0xE0 | track.sps.length // 3bit reserved (111) + numOfSequenceParameterSets + ].concat(sps).concat([track.pps.length // numOfPictureParameterSets + ]).concat(pps))), + // "PPS" + width = track.width, + height = track.height; + //console.log('avcc:' + Hex.hexDump(avcc)); + return MP4.box(MP4.types.avc1, new Uint8Array([0x00, 0x00, 0x00, // reserved + 0x00, 0x00, 0x00, // reserved + 0x00, 0x01, // data_reference_index + 0x00, 0x00, // pre_defined + 0x00, 0x00, // reserved + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // pre_defined + width >> 8 & 0xFF, width & 0xff, // width + height >> 8 & 0xFF, height & 0xff, // height + 0x00, 0x48, 0x00, 0x00, // horizresolution + 0x00, 0x48, 0x00, 0x00, // vertresolution + 0x00, 0x00, 0x00, 0x00, // reserved + 0x00, 0x01, // frame_count + 0x12, 0x64, 0x61, 0x69, 0x6C, //dailymotion/hls.js + 0x79, 0x6D, 0x6F, 0x74, 0x69, 0x6F, 0x6E, 0x2F, 0x68, 0x6C, 0x73, 0x2E, 0x6A, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // compressorname + 0x00, 0x18, // depth = 24 + 0x11, 0x11]), // pre_defined = -1 + avcc, MP4.box(MP4.types.btrt, new Uint8Array([0x00, 0x1c, 0x9c, 0x80, // bufferSizeDB + 0x00, 0x2d, 0xc6, 0xc0, // maxBitrate + 0x00, 0x2d, 0xc6, 0xc0])) // avgBitrate + ); + } + }, { + key: 'esds', + value: function esds(track) { + var configlen = track.config.length; + return new Uint8Array([0x00, // version 0 + 0x00, 0x00, 0x00, // flags + + 0x03, // descriptor_type + 0x17 + configlen, // length + 0x00, 0x01, //es_id + 0x00, // stream_priority + + 0x04, // descriptor_type + 0x0f + configlen, // length + 0x40, //codec : mpeg4_audio + 0x15, // stream_type + 0x00, 0x00, 0x00, // buffer_size + 0x00, 0x00, 0x00, 0x00, // maxBitrate + 0x00, 0x00, 0x00, 0x00, // avgBitrate + + 0x05 // descriptor_type + ].concat([configlen]).concat(track.config).concat([0x06, 0x01, 0x02])); // GASpecificConfig)); // length + audio config descriptor + } + }, { + key: 'mp4a', + value: function mp4a(track) { + var audiosamplerate = track.audiosamplerate; + return MP4.box(MP4.types.mp4a, new Uint8Array([0x00, 0x00, 0x00, // reserved + 0x00, 0x00, 0x00, // reserved + 0x00, 0x01, // data_reference_index + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // reserved + 0x00, track.channelCount, // channelcount + 0x00, 0x10, // sampleSize:16bits + 0x00, 0x00, 0x00, 0x00, // reserved2 + audiosamplerate >> 8 & 0xFF, audiosamplerate & 0xff, // + 0x00, 0x00]), MP4.box(MP4.types.esds, MP4.esds(track))); + } + }, { + key: 'stsd', + value: function stsd(track) { + if (track.type === 'audio') { + return MP4.box(MP4.types.stsd, MP4.STSD, MP4.mp4a(track)); + } else { + return MP4.box(MP4.types.stsd, MP4.STSD, MP4.avc1(track)); + } + } + }, { + key: 'tkhd', + value: function tkhd(track) { + var id = track.id, + duration = track.duration * track.timescale, + width = track.width, + height = track.height; + return MP4.box(MP4.types.tkhd, new Uint8Array([0x00, // version 0 + 0x00, 0x00, 0x07, // flags + 0x00, 0x00, 0x00, 0x00, // creation_time + 0x00, 0x00, 0x00, 0x00, // modification_time + id >> 24 & 0xFF, id >> 16 & 0xFF, id >> 8 & 0xFF, id & 0xFF, // track_ID + 0x00, 0x00, 0x00, 0x00, // reserved + duration >> 24, duration >> 16 & 0xFF, duration >> 8 & 0xFF, duration & 0xFF, // duration + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // reserved + 0x00, 0x00, // layer + 0x00, 0x00, // alternate_group + 0x00, 0x00, // non-audio track volume + 0x00, 0x00, // reserved + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, // transformation: unity matrix + width >> 8 & 0xFF, width & 0xFF, 0x00, 0x00, // width + height >> 8 & 0xFF, height & 0xFF, 0x00, 0x00 // height + ])); + } + }, { + key: 'traf', + value: function traf(track, baseMediaDecodeTime) { + var sampleDependencyTable = MP4.sdtp(track), + id = track.id; + return MP4.box(MP4.types.traf, MP4.box(MP4.types.tfhd, new Uint8Array([0x00, // version 0 + 0x00, 0x00, 0x00, // flags + id >> 24, id >> 16 & 0XFF, id >> 8 & 0XFF, id & 0xFF])), // track_ID + MP4.box(MP4.types.tfdt, new Uint8Array([0x00, // version 0 + 0x00, 0x00, 0x00, // flags + baseMediaDecodeTime >> 24, baseMediaDecodeTime >> 16 & 0XFF, baseMediaDecodeTime >> 8 & 0XFF, baseMediaDecodeTime & 0xFF])), // baseMediaDecodeTime + MP4.trun(track, sampleDependencyTable.length + 16 + // tfhd + 16 + // tfdt + 8 + // traf header + 16 + // mfhd + 8 + // moof header + 8), // mdat header + sampleDependencyTable); + } + + /** + * Generate a track box. + * @param track {object} a track definition + * @return {Uint8Array} the track box + */ + + }, { + key: 'trak', + value: function trak(track) { + track.duration = track.duration || 0xffffffff; + return MP4.box(MP4.types.trak, MP4.tkhd(track), MP4.mdia(track)); + } + }, { + key: 'trex', + value: function trex(track) { + var id = track.id; + return MP4.box(MP4.types.trex, new Uint8Array([0x00, // version 0 + 0x00, 0x00, 0x00, // flags + id >> 24, id >> 16 & 0XFF, id >> 8 & 0XFF, id & 0xFF, // track_ID + 0x00, 0x00, 0x00, 0x01, // default_sample_description_index + 0x00, 0x00, 0x00, 0x00, // default_sample_duration + 0x00, 0x00, 0x00, 0x00, // default_sample_size + 0x00, 0x01, 0x00, 0x01 // default_sample_flags + ])); + } + }, { + key: 'trun', + value: function trun(track, offset) { + var samples = track.samples || [], + len = samples.length, + arraylen = 12 + 16 * len, + array = new Uint8Array(arraylen), + i, + sample, + duration, + size, + flags, + cts; + offset += 8 + arraylen; + array.set([0x00, // version 0 + 0x00, 0x0f, 0x01, // flags + len >>> 24 & 0xFF, len >>> 16 & 0xFF, len >>> 8 & 0xFF, len & 0xFF, // sample_count + offset >>> 24 & 0xFF, offset >>> 16 & 0xFF, offset >>> 8 & 0xFF, offset & 0xFF // data_offset + ], 0); + for (i = 0; i < len; i++) { + sample = samples[i]; + duration = sample.duration; + size = sample.size; + flags = sample.flags; + cts = sample.cts; + array.set([duration >>> 24 & 0xFF, duration >>> 16 & 0xFF, duration >>> 8 & 0xFF, duration & 0xFF, // sample_duration + size >>> 24 & 0xFF, size >>> 16 & 0xFF, size >>> 8 & 0xFF, size & 0xFF, // sample_size + flags.isLeading << 2 | flags.dependsOn, flags.isDependedOn << 6 | flags.hasRedundancy << 4 | flags.paddingValue << 1 | flags.isNonSync, flags.degradPrio & 0xF0 << 8, flags.degradPrio & 0x0F, // sample_flags + cts >>> 24 & 0xFF, cts >>> 16 & 0xFF, cts >>> 8 & 0xFF, cts & 0xFF // sample_composition_time_offset + ], 12 + 16 * i); + } + return MP4.box(MP4.types.trun, array); + } + }, { + key: 'initSegment', + value: function initSegment(tracks) { + if (!MP4.types) { + MP4.init(); + } + var movie = MP4.moov(tracks), + result; + result = new Uint8Array(MP4.FTYP.byteLength + movie.byteLength); + result.set(MP4.FTYP); + result.set(movie, MP4.FTYP.byteLength); + return result; + } + }]); + + return MP4; + }(); + + exports.default = MP4; + + },{}],36:[function(_dereq_,module,exports){ + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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; }; }(); /** + * fMP4 remuxer + */ + + var _aac = _dereq_(27); + + var _aac2 = _interopRequireDefault(_aac); + + var _events = _dereq_(26); + + var _events2 = _interopRequireDefault(_events); + + var _logger = _dereq_(43); + + var _mp4Generator = _dereq_(35); + + var _mp4Generator2 = _interopRequireDefault(_mp4Generator); + + var _errors = _dereq_(24); + + _dereq_(44); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + + var MP4Remuxer = function () { + function MP4Remuxer(observer, id, config) { + _classCallCheck(this, MP4Remuxer); + + this.observer = observer; + this.id = id; + this.config = config; + this.ISGenerated = false; + this.PES2MP4SCALEFACTOR = 4; + this.PES_TIMESCALE = 90000; + this.MP4_TIMESCALE = this.PES_TIMESCALE / this.PES2MP4SCALEFACTOR; + } + + _createClass(MP4Remuxer, [{ + key: 'destroy', + value: function destroy() {} + }, { + key: 'insertDiscontinuity', + value: function insertDiscontinuity() { + this._initPTS = this._initDTS = undefined; + } + }, { + key: 'switchLevel', + value: function switchLevel() { + this.ISGenerated = false; + } + }, { + key: 'remux', + value: function remux(level, sn, audioTrack, videoTrack, id3Track, textTrack, timeOffset, contiguous, accurateTimeOffset) { + this.level = level; + this.sn = sn; + // generate Init Segment if needed + if (!this.ISGenerated) { + this.generateIS(audioTrack, videoTrack, timeOffset); + } + + if (this.ISGenerated) { + // Purposefully remuxing audio before video, so that remuxVideo can use nextAacPts, which is + // calculated in remuxAudio. + //logger.log('nb AAC samples:' + audioTrack.samples.length); + if (audioTrack.samples.length) { + var audioData = this.remuxAudio(audioTrack, timeOffset, contiguous, accurateTimeOffset); + //logger.log('nb AVC samples:' + videoTrack.samples.length); + if (videoTrack.samples.length) { + var audioTrackLength = void 0; + if (audioData) { + audioTrackLength = audioData.endPTS - audioData.startPTS; + } + this.remuxVideo(videoTrack, timeOffset, contiguous, audioTrackLength); + } + } else { + var videoData = void 0; + //logger.log('nb AVC samples:' + videoTrack.samples.length); + if (videoTrack.samples.length) { + videoData = this.remuxVideo(videoTrack, timeOffset, contiguous); + } + if (videoData && audioTrack.codec) { + this.remuxEmptyAudio(audioTrack, timeOffset, contiguous, videoData); + } + } + } + //logger.log('nb ID3 samples:' + audioTrack.samples.length); + if (id3Track.samples.length) { + this.remuxID3(id3Track, timeOffset); + } + //logger.log('nb ID3 samples:' + audioTrack.samples.length); + if (textTrack.samples.length) { + this.remuxText(textTrack, timeOffset); + } + //notify end of parsing + this.observer.trigger(_events2.default.FRAG_PARSED, { id: this.id, level: this.level, sn: this.sn }); + } + }, { + key: 'generateIS', + value: function generateIS(audioTrack, videoTrack, timeOffset) { + var observer = this.observer, + audioSamples = audioTrack.samples, + videoSamples = videoTrack.samples, + pesTimeScale = this.PES_TIMESCALE, + tracks = {}, + data = { id: this.id, level: this.level, sn: this.sn, tracks: tracks, unique: false }, + computePTSDTS = this._initPTS === undefined, + initPTS, + initDTS; + + if (computePTSDTS) { + initPTS = initDTS = Infinity; + } + if (audioTrack.config && audioSamples.length) { + audioTrack.timescale = audioTrack.audiosamplerate; + // MP4 duration (track duration in seconds multiplied by timescale) is coded on 32 bits + // we know that each AAC sample contains 1024 frames.... + // in order to avoid overflowing the 32 bit counter for large duration, we use smaller timescale (timescale/gcd) + // we just need to ensure that AAC sample duration will still be an integer (will be 1024/gcd) + if (audioTrack.timescale * audioTrack.duration > Math.pow(2, 32)) { + (function () { + var greatestCommonDivisor = function greatestCommonDivisor(a, b) { + if (!b) { + return a; + } + return greatestCommonDivisor(b, a % b); + }; + audioTrack.timescale = audioTrack.audiosamplerate / greatestCommonDivisor(audioTrack.audiosamplerate, 1024); + })(); + } + _logger.logger.log('audio mp4 timescale :' + audioTrack.timescale); + tracks.audio = { + container: 'audio/mp4', + codec: audioTrack.codec, + initSegment: _mp4Generator2.default.initSegment([audioTrack]), + metadata: { + channelCount: audioTrack.channelCount + } + }; + if (computePTSDTS) { + // remember first PTS of this demuxing context. for audio, PTS = DTS + initPTS = initDTS = audioSamples[0].pts - pesTimeScale * timeOffset; + } + } + + if (videoTrack.sps && videoTrack.pps && videoSamples.length) { + videoTrack.timescale = this.MP4_TIMESCALE; + tracks.video = { + container: 'video/mp4', + codec: videoTrack.codec, + initSegment: _mp4Generator2.default.initSegment([videoTrack]), + metadata: { + width: videoTrack.width, + height: videoTrack.height + } + }; + if (computePTSDTS) { + initPTS = Math.min(initPTS, videoSamples[0].pts - pesTimeScale * timeOffset); + initDTS = Math.min(initDTS, videoSamples[0].dts - pesTimeScale * timeOffset); + } + } + + if (Object.keys(tracks).length) { + observer.trigger(_events2.default.FRAG_PARSING_INIT_SEGMENT, data); + this.ISGenerated = true; + if (computePTSDTS) { + this._initPTS = initPTS; + this._initDTS = initDTS; + } + } else { + observer.trigger(_events2.default.ERROR, { type: _errors.ErrorTypes.MEDIA_ERROR, id: this.id, details: _errors.ErrorDetails.FRAG_PARSING_ERROR, fatal: false, reason: 'no audio/video samples found' }); + } + } + }, { + key: 'remuxVideo', + value: function remuxVideo(track, timeOffset, contiguous, audioTrackLength) { + var offset = 8, + pesTimeScale = this.PES_TIMESCALE, + pes2mp4ScaleFactor = this.PES2MP4SCALEFACTOR, + mp4SampleDuration, + mdat, + moof, + firstPTS, + firstDTS, + nextDTS, + lastPTS, + lastDTS, + inputSamples = track.samples, + outputSamples = []; + + // for (let i = 0; i < track.samples.length; i++) { + // let avcSample = track.samples[i]; + // let units = avcSample.units.units; + // let unitsString = ''; + // for (let j = 0; j < units.length ; j++) { + // unitsString += units[j].type + ','; + // if (units[j].data.length < 500) { + // unitsString += Hex.hexDump(units[j].data); + // } + // } + // logger.log(avcSample.pts + '/' + avcSample.dts + ',' + unitsString + avcSample.units.length); + // } + + // handle broken streams with PTS < DTS, tolerance up 200ms (18000 in 90kHz timescale) + var PTSDTSshift = inputSamples.reduce(function (prev, curr) { + return Math.max(Math.min(prev, curr.pts - curr.dts), -18000); + }, 0); + if (PTSDTSshift < 0) { + _logger.logger.warn('PTS < DTS detected in video samples, shifting DTS by ' + Math.round(PTSDTSshift / 90) + ' ms to overcome this issue'); + for (var i = 0; i < inputSamples.length; i++) { + inputSamples[i].dts += PTSDTSshift; + } + } + + // PTS is coded on 33bits, and can loop from -2^32 to 2^32 + // PTSNormalize will make PTS/DTS value monotonic, we use last known DTS value as reference value + var nextAvcDts = void 0; + // contiguous fragments are consecutive fragments from same quality level (same level, new SN = old SN + 1) + if (contiguous) { + // if parsed fragment is contiguous with last one, let's use last DTS value as reference + nextAvcDts = this.nextAvcDts; + } else { + // if not contiguous, let's use target timeOffset + nextAvcDts = timeOffset * pesTimeScale; + } + + // compute first DTS and last DTS, normalize them against reference value + var sample = inputSamples[0]; + firstDTS = Math.max(this._PTSNormalize(sample.dts - this._initDTS, nextAvcDts), 0); + firstPTS = Math.max(this._PTSNormalize(sample.pts - this._initDTS, nextAvcDts), 0); + + // check timestamp continuity accross consecutive fragments (this is to remove inter-fragment gap/hole) + var delta = Math.round((firstDTS - nextAvcDts) / 90); + // if fragment are contiguous, detect hole/overlapping between fragments + if (contiguous) { + if (delta) { + if (delta > 1) { + _logger.logger.log('AVC:' + delta + ' ms hole between fragments detected,filling it'); + } else if (delta < -1) { + _logger.logger.log('AVC:' + -delta + ' ms overlapping between fragments detected'); + } + // remove hole/gap : set DTS to next expected DTS + firstDTS = nextAvcDts; + inputSamples[0].dts = firstDTS + this._initDTS; + // offset PTS as well, ensure that PTS is smaller or equal than new DTS + firstPTS = Math.max(firstPTS - delta, nextAvcDts); + inputSamples[0].pts = firstPTS + this._initDTS; + _logger.logger.log('Video/PTS/DTS adjusted: ' + Math.round(firstPTS / 90) + '/' + Math.round(firstDTS / 90) + ',delta:' + delta + ' ms'); + } + } + nextDTS = firstDTS; + + // compute lastPTS/lastDTS + sample = inputSamples[inputSamples.length - 1]; + lastDTS = Math.max(this._PTSNormalize(sample.dts - this._initDTS, nextAvcDts), 0); + lastPTS = Math.max(this._PTSNormalize(sample.pts - this._initDTS, nextAvcDts), 0); + lastPTS = Math.max(lastPTS, lastDTS); + + var vendor = navigator.vendor, + userAgent = navigator.userAgent, + isSafari = vendor && vendor.indexOf('Apple') > -1 && userAgent && !userAgent.match('CriOS'); + + // on Safari let's signal the same sample duration for all samples + // sample duration (as expected by trun MP4 boxes), should be the delta between sample DTS + // set this constant duration as being the avg delta between consecutive DTS. + if (isSafari) { + mp4SampleDuration = Math.round((lastDTS - firstDTS) / (pes2mp4ScaleFactor * (inputSamples.length - 1))); + } + + // normalize all PTS/DTS now ... + for (var _i = 0; _i < inputSamples.length; _i++) { + var _sample = inputSamples[_i]; + if (isSafari) { + // sample DTS is computed using a constant decoding offset (mp4SampleDuration) between samples + _sample.dts = firstDTS + _i * pes2mp4ScaleFactor * mp4SampleDuration; + } else { + // ensure sample monotonic DTS + _sample.dts = Math.max(this._PTSNormalize(_sample.dts - this._initDTS, nextAvcDts), firstDTS); + // ensure dts is a multiple of scale factor to avoid rounding issues + _sample.dts = Math.round(_sample.dts / pes2mp4ScaleFactor) * pes2mp4ScaleFactor; + } + // we normalize PTS against nextAvcDts, we also substract initDTS (some streams don't start @ PTS O) + // and we ensure that computed value is greater or equal than sample DTS + _sample.pts = Math.max(this._PTSNormalize(_sample.pts - this._initDTS, nextAvcDts), _sample.dts); + // ensure pts is a multiple of scale factor to avoid rounding issues + _sample.pts = Math.round(_sample.pts / pes2mp4ScaleFactor) * pes2mp4ScaleFactor; + } + + /* concatenate the video data and construct the mdat in place + (need 8 more bytes to fill length and mpdat type) */ + mdat = new Uint8Array(track.len + 4 * track.nbNalu + 8); + var view = new DataView(mdat.buffer); + view.setUint32(0, mdat.byteLength); + mdat.set(_mp4Generator2.default.types.mdat, 4); + + for (var _i2 = 0; _i2 < inputSamples.length; _i2++) { + var avcSample = inputSamples[_i2], + mp4SampleLength = 0, + compositionTimeOffset = void 0; + // convert NALU bitstream to MP4 format (prepend NALU with size field) + while (avcSample.units.units.length) { + var unit = avcSample.units.units.shift(); + view.setUint32(offset, unit.data.byteLength); + offset += 4; + mdat.set(unit.data, offset); + offset += unit.data.byteLength; + mp4SampleLength += 4 + unit.data.byteLength; + } + + if (!isSafari) { + // expected sample duration is the Decoding Timestamp diff of consecutive samples + if (_i2 < inputSamples.length - 1) { + mp4SampleDuration = inputSamples[_i2 + 1].dts - avcSample.dts; + } else { + var config = this.config, + lastFrameDuration = avcSample.dts - inputSamples[_i2 > 0 ? _i2 - 1 : _i2].dts; + if (config.stretchShortVideoTrack) { + // In some cases, a segment's audio track duration may exceed the video track duration. + // Since we've already remuxed audio, and we know how long the audio track is, we look to + // see if the delta to the next segment is longer than the minimum of maxBufferHole and + // maxSeekHole. If so, playback would potentially get stuck, so we artificially inflate + // the duration of the last frame to minimize any potential gap between segments. + var maxBufferHole = config.maxBufferHole, + maxSeekHole = config.maxSeekHole, + gapTolerance = Math.floor(Math.min(maxBufferHole, maxSeekHole) * pesTimeScale), + deltaToFrameEnd = (audioTrackLength ? firstPTS + audioTrackLength * pesTimeScale : this.nextAacPts) - avcSample.pts; + if (deltaToFrameEnd > gapTolerance) { + // We subtract lastFrameDuration from deltaToFrameEnd to try to prevent any video + // frame overlap. maxBufferHole/maxSeekHole should be >> lastFrameDuration anyway. + mp4SampleDuration = deltaToFrameEnd - lastFrameDuration; + if (mp4SampleDuration < 0) { + mp4SampleDuration = lastFrameDuration; + } + _logger.logger.log('It is approximately ' + deltaToFrameEnd / 90 + ' ms to the next segment; using duration ' + mp4SampleDuration / 90 + ' ms for the last video frame.'); + } else { + mp4SampleDuration = lastFrameDuration; + } + } else { + mp4SampleDuration = lastFrameDuration; + } + } + mp4SampleDuration /= pes2mp4ScaleFactor; + compositionTimeOffset = Math.round((avcSample.pts - avcSample.dts) / pes2mp4ScaleFactor); + } else { + compositionTimeOffset = Math.max(0, mp4SampleDuration * Math.round((avcSample.pts - avcSample.dts) / (pes2mp4ScaleFactor * mp4SampleDuration))); + } + + //console.log('PTS/DTS/initDTS/normPTS/normDTS/relative PTS : ${avcSample.pts}/${avcSample.dts}/${this._initDTS}/${ptsnorm}/${dtsnorm}/${(avcSample.pts/4294967296).toFixed(3)}'); + outputSamples.push({ + size: mp4SampleLength, + // constant duration + duration: mp4SampleDuration, + cts: compositionTimeOffset, + flags: { + isLeading: 0, + isDependedOn: 0, + hasRedundancy: 0, + degradPrio: 0, + dependsOn: avcSample.key ? 2 : 1, + isNonSync: avcSample.key ? 0 : 1 + } + }); + } + // next AVC sample DTS should be equal to last sample DTS + last sample duration (in PES timescale) + this.nextAvcDts = lastDTS + mp4SampleDuration * pes2mp4ScaleFactor; + var dropped = track.dropped; + track.len = 0; + track.nbNalu = 0; + track.dropped = 0; + if (outputSamples.length && navigator.userAgent.toLowerCase().indexOf('chrome') > -1) { + var flags = outputSamples[0].flags; + // chrome workaround, mark first sample as being a Random Access Point to avoid sourcebuffer append issue + // https://code.google.com/p/chromium/issues/detail?id=229412 + flags.dependsOn = 2; + flags.isNonSync = 0; + } + track.samples = outputSamples; + moof = _mp4Generator2.default.moof(track.sequenceNumber++, firstDTS / pes2mp4ScaleFactor, track); + track.samples = []; + + var data = { + id: this.id, + level: this.level, + sn: this.sn, + data1: moof, + data2: mdat, + startPTS: firstPTS / pesTimeScale, + endPTS: (lastPTS + pes2mp4ScaleFactor * mp4SampleDuration) / pesTimeScale, + startDTS: firstDTS / pesTimeScale, + endDTS: this.nextAvcDts / pesTimeScale, + type: 'video', + nb: outputSamples.length, + dropped: dropped + }; + this.observer.trigger(_events2.default.FRAG_PARSING_DATA, data); + return data; + } + }, { + key: 'remuxAudio', + value: function remuxAudio(track, timeOffset, contiguous, accurateTimeOffset) { + var pesTimeScale = this.PES_TIMESCALE, + mp4timeScale = track.timescale, + pes2mp4ScaleFactor = pesTimeScale / mp4timeScale, + expectedSampleDuration = track.timescale * 1024 / track.audiosamplerate; + var view, + offset = 8, + aacSample, + mp4Sample, + unit, + mdat, + moof, + firstPTS, + firstDTS, + lastDTS, + pts, + dts, + ptsnorm, + dtsnorm, + samples = [], + samples0 = [], + fillFrame, + newStamp; + + track.samples.sort(function (a, b) { + return a.pts - b.pts; + }); + samples0 = track.samples; + + // for audio samples, also consider consecutive fragments as being contiguous (even if a level switch occurs), + // for sake of clarity: + // consecutive fragments are frags with less than 100ms gaps between new time offset and next expected PTS + // contiguous fragments are consecutive fragments from same quality level (same level, new SN = old SN + 1) + // this helps ensuring audio continuity + // and this also avoids audio glitches/cut when switching quality, or reporting wrong duration on first audio frame + + contiguous |= samples0.length && this.nextAacPts && Math.abs(timeOffset - this.nextAacPts / pesTimeScale) < 0.1; + + var nextAacPts = contiguous ? this.nextAacPts : timeOffset * pesTimeScale; + + // If the audio track is missing samples, the frames seem to get "left-shifted" within the + // resulting mp4 segment, causing sync issues and leaving gaps at the end of the audio segment. + // In an effort to prevent this from happening, we inject frames here where there are gaps. + // When possible, we inject a silent frame; when that's not possible, we duplicate the last + // frame. + var pesFrameDuration = expectedSampleDuration * pes2mp4ScaleFactor; + var nextPtsNorm = nextAacPts; + + // only inject/drop audio frames in case time offset is accurate + if (accurateTimeOffset) { + for (var i = 0; i < samples0.length;) { + // First, let's see how far off this frame is from where we expect it to be + var sample = samples0[i], + ptsNorm = this._PTSNormalize(sample.pts - this._initDTS, nextAacPts), + delta = ptsNorm - nextPtsNorm; + + // If we're overlapping by more than a duration, drop this sample + if (delta <= -pesFrameDuration) { + _logger.logger.warn('Dropping 1 audio frame @ ' + Math.round(nextPtsNorm / 90) / 1000 + 's due to ' + Math.round(Math.abs(delta / 90)) + ' ms overlap.'); + samples0.splice(i, 1); + track.len -= sample.unit.length; + // Don't touch nextPtsNorm or i + } + // Otherwise, if we're more than a frame away from where we should be, insert missing frames + else if (delta >= pesFrameDuration) { + var missing = Math.round(delta / pesFrameDuration); + _logger.logger.warn('Injecting ' + missing + ' audio frame @ ' + Math.round(nextPtsNorm / 90) / 1000 + 's due to ' + Math.round(delta / 90) + ' ms gap.'); + for (var j = 0; j < missing; j++) { + newStamp = nextPtsNorm + this._initDTS; + newStamp = Math.max(newStamp, this._initDTS); + fillFrame = _aac2.default.getSilentFrame(track.channelCount); + if (!fillFrame) { + _logger.logger.log('Unable to get silent frame for given audio codec; duplicating last frame instead.'); + fillFrame = sample.unit.slice(0); + } + samples0.splice(i, 0, { unit: fillFrame, pts: newStamp, dts: newStamp }); + track.len += fillFrame.length; + nextPtsNorm += pesFrameDuration; + i += 1; + } + + // Adjust sample to next expected pts + sample.pts = sample.dts = nextPtsNorm + this._initDTS; + nextPtsNorm += pesFrameDuration; + i += 1; + } + // Otherwise, we're within half a frame duration, so just adjust pts + else { + if (Math.abs(delta) > 0.1 * pesFrameDuration) { + //logger.log(`Invalid frame delta ${Math.round(ptsNorm - nextPtsNorm + pesFrameDuration)} at PTS ${Math.round(ptsNorm / 90)} (should be ${Math.round(pesFrameDuration)}).`); + } + nextPtsNorm += pesFrameDuration; + if (i === 0) { + sample.pts = sample.dts = this._initDTS + nextAacPts; + } else { + sample.pts = sample.dts = samples0[i - 1].pts + pesFrameDuration; + } + i += 1; + } + } + } + + while (samples0.length) { + aacSample = samples0.shift(); + unit = aacSample.unit; + pts = aacSample.pts - this._initDTS; + dts = aacSample.dts - this._initDTS; + //logger.log(`Audio/PTS:${Math.round(pts/90)}`); + // if not first sample + if (lastDTS !== undefined) { + ptsnorm = this._PTSNormalize(pts, lastDTS); + dtsnorm = this._PTSNormalize(dts, lastDTS); + mp4Sample.duration = Math.round((dtsnorm - lastDTS) / pes2mp4ScaleFactor); + } else { + ptsnorm = this._PTSNormalize(pts, nextAacPts); + dtsnorm = this._PTSNormalize(dts, nextAacPts); + var _delta = Math.round(1000 * (ptsnorm - nextAacPts) / pesTimeScale), + numMissingFrames = 0; + // if fragment are contiguous, detect hole/overlapping between fragments + // contiguous fragments are consecutive fragments from same quality level (same level, new SN = old SN + 1) + if (contiguous) { + // log delta + if (_delta) { + if (_delta > 0) { + numMissingFrames = Math.round((ptsnorm - nextAacPts) / pesFrameDuration); + _logger.logger.log(_delta + ' ms hole between AAC samples detected,filling it'); + if (numMissingFrames > 0) { + fillFrame = _aac2.default.getSilentFrame(track.channelCount); + if (!fillFrame) { + fillFrame = unit.slice(0); + } + track.len += numMissingFrames * fillFrame.length; + } + // if we have frame overlap, overlapping for more than half a frame duraion + } else if (_delta < -12) { + // drop overlapping audio frames... browser will deal with it + _logger.logger.log(-_delta + ' ms overlapping between AAC samples detected, drop frame'); + track.len -= unit.byteLength; + continue; + } + // set PTS/DTS to expected PTS/DTS + ptsnorm = dtsnorm = nextAacPts; + } + } + // remember first PTS of our aacSamples, ensure value is positive + firstPTS = Math.max(0, ptsnorm); + firstDTS = Math.max(0, dtsnorm); + if (track.len > 0) { + /* concatenate the audio data and construct the mdat in place + (need 8 more bytes to fill length and mdat type) */ + mdat = new Uint8Array(track.len + 8); + view = new DataView(mdat.buffer); + view.setUint32(0, mdat.byteLength); + mdat.set(_mp4Generator2.default.types.mdat, 4); + } else { + // no audio samples + return; + } + for (var _i3 = 0; _i3 < numMissingFrames; _i3++) { + newStamp = ptsnorm - (numMissingFrames - _i3) * pesFrameDuration; + fillFrame = _aac2.default.getSilentFrame(track.channelCount); + if (!fillFrame) { + _logger.logger.log('Unable to get silent frame for given audio codec; duplicating this frame instead.'); + fillFrame = unit.slice(0); + } + mdat.set(fillFrame, offset); + offset += fillFrame.byteLength; + mp4Sample = { + size: fillFrame.byteLength, + cts: 0, + duration: 1024, + flags: { + isLeading: 0, + isDependedOn: 0, + hasRedundancy: 0, + degradPrio: 0, + dependsOn: 1 + } + }; + samples.push(mp4Sample); + } + } + mdat.set(unit, offset); + offset += unit.byteLength; + //console.log('PTS/DTS/initDTS/normPTS/normDTS/relative PTS : ${aacSample.pts}/${aacSample.dts}/${this._initDTS}/${ptsnorm}/${dtsnorm}/${(aacSample.pts/4294967296).toFixed(3)}'); + mp4Sample = { + size: unit.byteLength, + cts: 0, + duration: 0, + flags: { + isLeading: 0, + isDependedOn: 0, + hasRedundancy: 0, + degradPrio: 0, + dependsOn: 1 + } + }; + samples.push(mp4Sample); + lastDTS = dtsnorm; + } + var lastSampleDuration = 0; + var nbSamples = samples.length; + //set last sample duration as being identical to previous sample + if (nbSamples >= 2) { + lastSampleDuration = samples[nbSamples - 2].duration; + mp4Sample.duration = lastSampleDuration; + } + if (nbSamples) { + // next aac sample PTS should be equal to last sample PTS + duration + this.nextAacPts = ptsnorm + pes2mp4ScaleFactor * lastSampleDuration; + //logger.log('Audio/PTS/PTSend:' + aacSample.pts.toFixed(0) + '/' + this.nextAacDts.toFixed(0)); + track.len = 0; + track.samples = samples; + moof = _mp4Generator2.default.moof(track.sequenceNumber++, firstDTS / pes2mp4ScaleFactor, track); + track.samples = []; + var audioData = { + id: this.id, + level: this.level, + sn: this.sn, + data1: moof, + data2: mdat, + startPTS: firstPTS / pesTimeScale, + endPTS: this.nextAacPts / pesTimeScale, + startDTS: firstDTS / pesTimeScale, + endDTS: (dtsnorm + pes2mp4ScaleFactor * lastSampleDuration) / pesTimeScale, + type: 'audio', + nb: nbSamples + }; + this.observer.trigger(_events2.default.FRAG_PARSING_DATA, audioData); + return audioData; + } + return null; + } + }, { + key: 'remuxEmptyAudio', + value: function remuxEmptyAudio(track, timeOffset, contiguous, videoData) { + var pesTimeScale = this.PES_TIMESCALE, + mp4timeScale = track.timescale ? track.timescale : track.audiosamplerate, + pes2mp4ScaleFactor = pesTimeScale / mp4timeScale, + + + // sync with video's timestamp + startDTS = videoData.startDTS * pesTimeScale + this._initDTS, + endDTS = videoData.endDTS * pesTimeScale + this._initDTS, + + + // one sample's duration value + sampleDuration = 1024, + frameDuration = pes2mp4ScaleFactor * sampleDuration, + + + // samples count of this segment's duration + nbSamples = Math.ceil((endDTS - startDTS) / frameDuration), + + + // silent frame + silentFrame = _aac2.default.getSilentFrame(track.channelCount); + + // Can't remux if we can't generate a silent frame... + if (!silentFrame) { + _logger.logger.trace('Unable to remuxEmptyAudio since we were unable to get a silent frame for given audio codec!'); + return; + } + + var samples = []; + for (var i = 0; i < nbSamples; i++) { + var stamp = startDTS + i * frameDuration; + samples.push({ unit: silentFrame.slice(0), pts: stamp, dts: stamp }); + track.len += silentFrame.length; + } + track.samples = samples; + + this.remuxAudio(track, timeOffset, contiguous); + } + }, { + key: 'remuxID3', + value: function remuxID3(track, timeOffset) { + var length = track.samples.length, + sample; + // consume samples + if (length) { + for (var index = 0; index < length; index++) { + sample = track.samples[index]; + // setting id3 pts, dts to relative time + // using this._initPTS and this._initDTS to calculate relative time + sample.pts = (sample.pts - this._initPTS) / this.PES_TIMESCALE; + sample.dts = (sample.dts - this._initDTS) / this.PES_TIMESCALE; + } + this.observer.trigger(_events2.default.FRAG_PARSING_METADATA, { + id: this.id, + level: this.level, + sn: this.sn, + samples: track.samples + }); + } + + track.samples = []; + timeOffset = timeOffset; + } + }, { + key: 'remuxText', + value: function remuxText(track, timeOffset) { + track.samples.sort(function (a, b) { + return a.pts - b.pts; + }); + + var length = track.samples.length, + sample; + // consume samples + if (length) { + for (var index = 0; index < length; index++) { + sample = track.samples[index]; + // setting text pts, dts to relative time + // using this._initPTS and this._initDTS to calculate relative time + sample.pts = (sample.pts - this._initPTS) / this.PES_TIMESCALE; + } + this.observer.trigger(_events2.default.FRAG_PARSING_USERDATA, { + id: this.id, + level: this.level, + sn: this.sn, + samples: track.samples + }); + } + + track.samples = []; + timeOffset = timeOffset; + } + }, { + key: '_PTSNormalize', + value: function _PTSNormalize(value, reference) { + var offset; + if (reference === undefined) { + return value; + } + if (reference < value) { + // - 2^33 + offset = -8589934592; + } else { + // + 2^33 + offset = 8589934592; + } + /* PTS is 33bit (from 0 to 2^33 -1) + if diff between value and reference is bigger than half of the amplitude (2^32) then it means that + PTS looping occured. fill the gap */ + while (Math.abs(value - reference) > 4294967296) { + value += offset; + } + return value; + } + }, { + key: 'passthrough', + get: function get() { + return false; + } + }]); + + return MP4Remuxer; + }(); + + exports.default = MP4Remuxer; + + },{"24":24,"26":26,"27":27,"35":35,"43":43,"44":44}],37:[function(_dereq_,module,exports){ + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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; }; }(); /** + * passthrough remuxer + */ + + + var _events = _dereq_(26); + + var _events2 = _interopRequireDefault(_events); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + + var PassThroughRemuxer = function () { + function PassThroughRemuxer(observer, id) { + _classCallCheck(this, PassThroughRemuxer); + + this.observer = observer; + this.id = id; + this.ISGenerated = false; + } + + _createClass(PassThroughRemuxer, [{ + key: 'destroy', + value: function destroy() {} + }, { + key: 'insertDiscontinuity', + value: function insertDiscontinuity() {} + }, { + key: 'switchLevel', + value: function switchLevel() { + this.ISGenerated = false; + } + }, { + key: 'remux', + value: function remux(audioTrack, videoTrack, id3Track, textTrack, timeOffset, rawData) { + var observer = this.observer; + // generate Init Segment if needed + if (!this.ISGenerated) { + var tracks = {}, + data = { id: this.id, tracks: tracks, unique: true }, + track = videoTrack, + codec = track.codec; + + if (codec) { + data.tracks.video = { + container: track.container, + codec: codec, + metadata: { + width: track.width, + height: track.height + } + }; + } + + track = audioTrack; + codec = track.codec; + if (codec) { + data.tracks.audio = { + container: track.container, + codec: codec, + metadata: { + channelCount: track.channelCount + } + }; + } + this.ISGenerated = true; + observer.trigger(_events2.default.FRAG_PARSING_INIT_SEGMENT, data); + } + observer.trigger(_events2.default.FRAG_PARSING_DATA, { + id: this.id, + data1: rawData, + startPTS: timeOffset, + startDTS: timeOffset, + type: 'audiovideo', + nb: 1, + dropped: 0 + }); + } + }, { + key: 'passthrough', + get: function get() { + return true; + } + }]); + + return PassThroughRemuxer; + }(); + + exports.default = PassThroughRemuxer; + + },{"26":26}],38:[function(_dereq_,module,exports){ + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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; }; }(); + + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + + // adapted from https://github.com/kanongil/node-m3u8parse/blob/master/attrlist.js + + var AttrList = function () { + function AttrList(attrs) { + _classCallCheck(this, AttrList); + + if (typeof attrs === 'string') { + attrs = AttrList.parseAttrList(attrs); + } + for (var attr in attrs) { + if (attrs.hasOwnProperty(attr)) { + this[attr] = attrs[attr]; + } + } + } + + _createClass(AttrList, [{ + key: 'decimalInteger', + value: function decimalInteger(attrName) { + var intValue = parseInt(this[attrName], 10); + if (intValue > Number.MAX_SAFE_INTEGER) { + return Infinity; + } + return intValue; + } + }, { + key: 'hexadecimalInteger', + value: function hexadecimalInteger(attrName) { + if (this[attrName]) { + var stringValue = (this[attrName] || '0x').slice(2); + stringValue = (stringValue.length & 1 ? '0' : '') + stringValue; + + var value = new Uint8Array(stringValue.length / 2); + for (var i = 0; i < stringValue.length / 2; i++) { + value[i] = parseInt(stringValue.slice(i * 2, i * 2 + 2), 16); + } + return value; + } else { + return null; + } + } + }, { + key: 'hexadecimalIntegerAsNumber', + value: function hexadecimalIntegerAsNumber(attrName) { + var intValue = parseInt(this[attrName], 16); + if (intValue > Number.MAX_SAFE_INTEGER) { + return Infinity; + } + return intValue; + } + }, { + key: 'decimalFloatingPoint', + value: function decimalFloatingPoint(attrName) { + return parseFloat(this[attrName]); + } + }, { + key: 'enumeratedString', + value: function enumeratedString(attrName) { + return this[attrName]; + } + }, { + key: 'decimalResolution', + value: function decimalResolution(attrName) { + var res = /^(\d+)x(\d+)$/.exec(this[attrName]); + if (res === null) { + return undefined; + } + return { + width: parseInt(res[1], 10), + height: parseInt(res[2], 10) + }; + } + }], [{ + key: 'parseAttrList', + value: function parseAttrList(input) { + var re = /\s*(.+?)\s*=((?:\".*?\")|.*?)(?:,|$)/g; + var match, + attrs = {}; + while ((match = re.exec(input)) !== null) { + var value = match[2], + quote = '"'; + + if (value.indexOf(quote) === 0 && value.lastIndexOf(quote) === value.length - 1) { + value = value.slice(1, -1); + } + attrs[match[1]] = value; + } + return attrs; + } + }]); + + return AttrList; + }(); + + exports.default = AttrList; + + },{}],39:[function(_dereq_,module,exports){ + "use strict"; + + var BinarySearch = { + /** + * Searches for an item in an array which matches a certain condition. + * This requires the condition to only match one item in the array, + * and for the array to be ordered. + * + * @param {Array} list The array to search. + * @param {Function} comparisonFunction + * Called and provided a candidate item as the first argument. + * Should return: + * > -1 if the item should be located at a lower index than the provided item. + * > 1 if the item should be located at a higher index than the provided item. + * > 0 if the item is the item you're looking for. + * + * @return {*} The object if it is found or null otherwise. + */ + search: function search(list, comparisonFunction) { + var minIndex = 0; + var maxIndex = list.length - 1; + var currentIndex = null; + var currentElement = null; + + while (minIndex <= maxIndex) { + currentIndex = (minIndex + maxIndex) / 2 | 0; + currentElement = list[currentIndex]; + + var comparisonResult = comparisonFunction(currentElement); + if (comparisonResult > 0) { + minIndex = currentIndex + 1; + } else if (comparisonResult < 0) { + maxIndex = currentIndex - 1; + } else { + return currentElement; + } + } + + return null; + } + }; + + module.exports = BinarySearch; + + },{}],40:[function(_dereq_,module,exports){ + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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; }; }(); + + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + + /** + * + * This code was ported from the dash.js project at: + * https://github.com/Dash-Industry-Forum/dash.js/blob/development/externals/cea608-parser.js + * https://github.com/Dash-Industry-Forum/dash.js/commit/8269b26a761e0853bb21d78780ed945144ecdd4d#diff-71bc295a2d6b6b7093a1d3290d53a4b2 + * + * The original copyright appears below: + * + * The copyright in this software is being made available under the BSD License, + * included below. This software may be subject to other third party and contributor + * rights, including patent rights, and no such rights are granted under this license. + * + * Copyright (c) 2015-2016, DASH Industry Forum. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * 2. Neither the name of Dash Industry Forum nor the names of its + * contributors may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + /** + * Exceptions from regular ASCII. CodePoints are mapped to UTF-16 codes + */ + + var specialCea608CharsCodes = { + 0x2a: 0xe1, // lowercase a, acute accent + 0x5c: 0xe9, // lowercase e, acute accent + 0x5e: 0xed, // lowercase i, acute accent + 0x5f: 0xf3, // lowercase o, acute accent + 0x60: 0xfa, // lowercase u, acute accent + 0x7b: 0xe7, // lowercase c with cedilla + 0x7c: 0xf7, // division symbol + 0x7d: 0xd1, // uppercase N tilde + 0x7e: 0xf1, // lowercase n tilde + 0x7f: 0x2588, // Full block + // THIS BLOCK INCLUDES THE 16 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS + // THAT COME FROM HI BYTE=0x11 AND LOW BETWEEN 0x30 AND 0x3F + // THIS MEANS THAT \x50 MUST BE ADDED TO THE VALUES + 0x80: 0xae, // Registered symbol (R) + 0x81: 0xb0, // degree sign + 0x82: 0xbd, // 1/2 symbol + 0x83: 0xbf, // Inverted (open) question mark + 0x84: 0x2122, // Trademark symbol (TM) + 0x85: 0xa2, // Cents symbol + 0x86: 0xa3, // Pounds sterling + 0x87: 0x266a, // Music 8'th note + 0x88: 0xe0, // lowercase a, grave accent + 0x89: 0x20, // transparent space (regular) + 0x8a: 0xe8, // lowercase e, grave accent + 0x8b: 0xe2, // lowercase a, circumflex accent + 0x8c: 0xea, // lowercase e, circumflex accent + 0x8d: 0xee, // lowercase i, circumflex accent + 0x8e: 0xf4, // lowercase o, circumflex accent + 0x8f: 0xfb, // lowercase u, circumflex accent + // THIS BLOCK INCLUDES THE 32 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS + // THAT COME FROM HI BYTE=0x12 AND LOW BETWEEN 0x20 AND 0x3F + 0x90: 0xc1, // capital letter A with acute + 0x91: 0xc9, // capital letter E with acute + 0x92: 0xd3, // capital letter O with acute + 0x93: 0xda, // capital letter U with acute + 0x94: 0xdc, // capital letter U with diaresis + 0x95: 0xfc, // lowercase letter U with diaeresis + 0x96: 0x2018, // opening single quote + 0x97: 0xa1, // inverted exclamation mark + 0x98: 0x2a, // asterisk + 0x99: 0x2019, // closing single quote + 0x9a: 0x2501, // box drawings heavy horizontal + 0x9b: 0xa9, // copyright sign + 0x9c: 0x2120, // Service mark + 0x9d: 0x2022, // (round) bullet + 0x9e: 0x201c, // Left double quotation mark + 0x9f: 0x201d, // Right double quotation mark + 0xa0: 0xc0, // uppercase A, grave accent + 0xa1: 0xc2, // uppercase A, circumflex + 0xa2: 0xc7, // uppercase C with cedilla + 0xa3: 0xc8, // uppercase E, grave accent + 0xa4: 0xca, // uppercase E, circumflex + 0xa5: 0xcb, // capital letter E with diaresis + 0xa6: 0xeb, // lowercase letter e with diaresis + 0xa7: 0xce, // uppercase I, circumflex + 0xa8: 0xcf, // uppercase I, with diaresis + 0xa9: 0xef, // lowercase i, with diaresis + 0xaa: 0xd4, // uppercase O, circumflex + 0xab: 0xd9, // uppercase U, grave accent + 0xac: 0xf9, // lowercase u, grave accent + 0xad: 0xdb, // uppercase U, circumflex + 0xae: 0xab, // left-pointing double angle quotation mark + 0xaf: 0xbb, // right-pointing double angle quotation mark + // THIS BLOCK INCLUDES THE 32 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS + // THAT COME FROM HI BYTE=0x13 AND LOW BETWEEN 0x20 AND 0x3F + 0xb0: 0xc3, // Uppercase A, tilde + 0xb1: 0xe3, // Lowercase a, tilde + 0xb2: 0xcd, // Uppercase I, acute accent + 0xb3: 0xcc, // Uppercase I, grave accent + 0xb4: 0xec, // Lowercase i, grave accent + 0xb5: 0xd2, // Uppercase O, grave accent + 0xb6: 0xf2, // Lowercase o, grave accent + 0xb7: 0xd5, // Uppercase O, tilde + 0xb8: 0xf5, // Lowercase o, tilde + 0xb9: 0x7b, // Open curly brace + 0xba: 0x7d, // Closing curly brace + 0xbb: 0x5c, // Backslash + 0xbc: 0x5e, // Caret + 0xbd: 0x5f, // Underscore + 0xbe: 0x7c, // Pipe (vertical line) + 0xbf: 0x223c, // Tilde operator + 0xc0: 0xc4, // Uppercase A, umlaut + 0xc1: 0xe4, // Lowercase A, umlaut + 0xc2: 0xd6, // Uppercase O, umlaut + 0xc3: 0xf6, // Lowercase o, umlaut + 0xc4: 0xdf, // Esszett (sharp S) + 0xc5: 0xa5, // Yen symbol + 0xc6: 0xa4, // Generic currency sign + 0xc7: 0x2503, // Box drawings heavy vertical + 0xc8: 0xc5, // Uppercase A, ring + 0xc9: 0xe5, // Lowercase A, ring + 0xca: 0xd8, // Uppercase O, stroke + 0xcb: 0xf8, // Lowercase o, strok + 0xcc: 0x250f, // Box drawings heavy down and right + 0xcd: 0x2513, // Box drawings heavy down and left + 0xce: 0x2517, // Box drawings heavy up and right + 0xcf: 0x251b // Box drawings heavy up and left + }; + + /** + * Utils + */ + var getCharForByte = function getCharForByte(byte) { + var charCode = byte; + if (specialCea608CharsCodes.hasOwnProperty(byte)) { + charCode = specialCea608CharsCodes[byte]; + } + return String.fromCharCode(charCode); + }; + + var NR_ROWS = 15, + NR_COLS = 32; + // Tables to look up row from PAC data + var rowsLowCh1 = { 0x11: 1, 0x12: 3, 0x15: 5, 0x16: 7, 0x17: 9, 0x10: 11, 0x13: 12, 0x14: 14 }; + var rowsHighCh1 = { 0x11: 2, 0x12: 4, 0x15: 6, 0x16: 8, 0x17: 10, 0x13: 13, 0x14: 15 }; + var rowsLowCh2 = { 0x19: 1, 0x1A: 3, 0x1D: 5, 0x1E: 7, 0x1F: 9, 0x18: 11, 0x1B: 12, 0x1C: 14 }; + var rowsHighCh2 = { 0x19: 2, 0x1A: 4, 0x1D: 6, 0x1E: 8, 0x1F: 10, 0x1B: 13, 0x1C: 15 }; + + var backgroundColors = ['white', 'green', 'blue', 'cyan', 'red', 'yellow', 'magenta', 'black', 'transparent']; + + /** + * Simple logger class to be able to write with time-stamps and filter on level. + */ + var logger = { + verboseFilter: { 'DATA': 3, 'DEBUG': 3, 'INFO': 2, 'WARNING': 2, 'TEXT': 1, 'ERROR': 0 }, + time: null, + verboseLevel: 0, // Only write errors + setTime: function setTime(newTime) { + this.time = newTime; + }, + log: function log(severity, msg) { + var minLevel = this.verboseFilter[severity]; + if (this.verboseLevel >= minLevel) { + console.log(this.time + ' [' + severity + '] ' + msg); + } + } + }; + + var numArrayToHexArray = function numArrayToHexArray(numArray) { + var hexArray = []; + for (var j = 0; j < numArray.length; j++) { + hexArray.push(numArray[j].toString(16)); + } + return hexArray; + }; + + var PenState = function () { + function PenState(foreground, underline, italics, background, flash) { + _classCallCheck(this, PenState); + + this.foreground = foreground || 'white'; + this.underline = underline || false; + this.italics = italics || false; + this.background = background || 'black'; + this.flash = flash || false; + } + + _createClass(PenState, [{ + key: 'reset', + value: function reset() { + this.foreground = 'white'; + this.underline = false; + this.italics = false; + this.background = 'black'; + this.flash = false; + } + }, { + key: 'setStyles', + value: function setStyles(styles) { + var attribs = ['foreground', 'underline', 'italics', 'background', 'flash']; + for (var i = 0; i < attribs.length; i++) { + var style = attribs[i]; + if (styles.hasOwnProperty(style)) { + this[style] = styles[style]; + } + } + } + }, { + key: 'isDefault', + value: function isDefault() { + return this.foreground === 'white' && !this.underline && !this.italics && this.background === 'black' && !this.flash; + } + }, { + key: 'equals', + value: function equals(other) { + return this.foreground === other.foreground && this.underline === other.underline && this.italics === other.italics && this.background === other.background && this.flash === other.flash; + } + }, { + key: 'copy', + value: function copy(newPenState) { + this.foreground = newPenState.foreground; + this.underline = newPenState.underline; + this.italics = newPenState.italics; + this.background = newPenState.background; + this.flash = newPenState.flash; + } + }, { + key: 'toString', + value: function toString() { + return 'color=' + this.foreground + ', underline=' + this.underline + ', italics=' + this.italics + ', background=' + this.background + ', flash=' + this.flash; + } + }]); + + return PenState; + }(); + + /** + * Unicode character with styling and background. + * @constructor + */ + + + var StyledUnicodeChar = function () { + function StyledUnicodeChar(uchar, foreground, underline, italics, background, flash) { + _classCallCheck(this, StyledUnicodeChar); + + this.uchar = uchar || ' '; // unicode character + this.penState = new PenState(foreground, underline, italics, background, flash); + } + + _createClass(StyledUnicodeChar, [{ + key: 'reset', + value: function reset() { + this.uchar = ' '; + this.penState.reset(); + } + }, { + key: 'setChar', + value: function setChar(uchar, newPenState) { + this.uchar = uchar; + this.penState.copy(newPenState); + } + }, { + key: 'setPenState', + value: function setPenState(newPenState) { + this.penState.copy(newPenState); + } + }, { + key: 'equals', + value: function equals(other) { + return this.uchar === other.uchar && this.penState.equals(other.penState); + } + }, { + key: 'copy', + value: function copy(newChar) { + this.uchar = newChar.uchar; + this.penState.copy(newChar.penState); + } + }, { + key: 'isEmpty', + value: function isEmpty() { + return this.uchar === ' ' && this.penState.isDefault(); + } + }]); + + return StyledUnicodeChar; + }(); + + /** + * CEA-608 row consisting of NR_COLS instances of StyledUnicodeChar. + * @constructor + */ + + + var Row = function () { + function Row() { + _classCallCheck(this, Row); + + this.chars = []; + for (var i = 0; i < NR_COLS; i++) { + this.chars.push(new StyledUnicodeChar()); + } + this.pos = 0; + this.currPenState = new PenState(); + } + + _createClass(Row, [{ + key: 'equals', + value: function equals(other) { + var equal = true; + for (var i = 0; i < NR_COLS; i++) { + if (!this.chars[i].equals(other.chars[i])) { + equal = false; + break; + } + } + return equal; + } + }, { + key: 'copy', + value: function copy(other) { + for (var i = 0; i < NR_COLS; i++) { + this.chars[i].copy(other.chars[i]); + } + } + }, { + key: 'isEmpty', + value: function isEmpty() { + var empty = true; + for (var i = 0; i < NR_COLS; i++) { + if (!this.chars[i].isEmpty()) { + empty = false; + break; + } + } + return empty; + } + + /** + * Set the cursor to a valid column. + */ + + }, { + key: 'setCursor', + value: function setCursor(absPos) { + if (this.pos !== absPos) { + this.pos = absPos; + } + if (this.pos < 0) { + logger.log('ERROR', 'Negative cursor position ' + this.pos); + this.pos = 0; + } else if (this.pos > NR_COLS) { + logger.log('ERROR', 'Too large cursor position ' + this.pos); + this.pos = NR_COLS; + } + } + + /** + * Move the cursor relative to current position. + */ + + }, { + key: 'moveCursor', + value: function moveCursor(relPos) { + var newPos = this.pos + relPos; + if (relPos > 1) { + for (var i = this.pos + 1; i < newPos + 1; i++) { + this.chars[i].setPenState(this.currPenState); + } + } + this.setCursor(newPos); + } + + /** + * Backspace, move one step back and clear character. + */ + + }, { + key: 'backSpace', + value: function backSpace() { + this.moveCursor(-1); + this.chars[this.pos].setChar(' ', this.currPenState); + } + }, { + key: 'insertChar', + value: function insertChar(byte) { + if (byte >= 0x90) { + //Extended char + this.backSpace(); + } + var char = getCharForByte(byte); + if (this.pos >= NR_COLS) { + logger.log('ERROR', 'Cannot insert ' + byte.toString(16) + ' (' + char + ') at position ' + this.pos + '. Skipping it!'); + return; + } + this.chars[this.pos].setChar(char, this.currPenState); + this.moveCursor(1); + } + }, { + key: 'clearFromPos', + value: function clearFromPos(startPos) { + var i; + for (i = startPos; i < NR_COLS; i++) { + this.chars[i].reset(); + } + } + }, { + key: 'clear', + value: function clear() { + this.clearFromPos(0); + this.pos = 0; + this.currPenState.reset(); + } + }, { + key: 'clearToEndOfRow', + value: function clearToEndOfRow() { + this.clearFromPos(this.pos); + } + }, { + key: 'getTextString', + value: function getTextString() { + var chars = []; + var empty = true; + for (var i = 0; i < NR_COLS; i++) { + var char = this.chars[i].uchar; + if (char !== ' ') { + empty = false; + } + chars.push(char); + } + if (empty) { + return ''; + } else { + return chars.join(''); + } + } + }, { + key: 'setPenStyles', + value: function setPenStyles(styles) { + this.currPenState.setStyles(styles); + var currChar = this.chars[this.pos]; + currChar.setPenState(this.currPenState); + } + }]); + + return Row; + }(); + + /** + * Keep a CEA-608 screen of 32x15 styled characters + * @constructor + */ + + + var CaptionScreen = function () { + function CaptionScreen() { + _classCallCheck(this, CaptionScreen); + + this.rows = []; + for (var i = 0; i < NR_ROWS; i++) { + this.rows.push(new Row()); // Note that we use zero-based numbering (0-14) + } + this.currRow = NR_ROWS - 1; + this.nrRollUpRows = null; + this.reset(); + } + + _createClass(CaptionScreen, [{ + key: 'reset', + value: function reset() { + for (var i = 0; i < NR_ROWS; i++) { + this.rows[i].clear(); + } + this.currRow = NR_ROWS - 1; + } + }, { + key: 'equals', + value: function equals(other) { + var equal = true; + for (var i = 0; i < NR_ROWS; i++) { + if (!this.rows[i].equals(other.rows[i])) { + equal = false; + break; + } + } + return equal; + } + }, { + key: 'copy', + value: function copy(other) { + for (var i = 0; i < NR_ROWS; i++) { + this.rows[i].copy(other.rows[i]); + } + } + }, { + key: 'isEmpty', + value: function isEmpty() { + var empty = true; + for (var i = 0; i < NR_ROWS; i++) { + if (!this.rows[i].isEmpty()) { + empty = false; + break; + } + } + return empty; + } + }, { + key: 'backSpace', + value: function backSpace() { + var row = this.rows[this.currRow]; + row.backSpace(); + } + }, { + key: 'clearToEndOfRow', + value: function clearToEndOfRow() { + var row = this.rows[this.currRow]; + row.clearToEndOfRow(); + } + + /** + * Insert a character (without styling) in the current row. + */ + + }, { + key: 'insertChar', + value: function insertChar(char) { + var row = this.rows[this.currRow]; + row.insertChar(char); + } + }, { + key: 'setPen', + value: function setPen(styles) { + var row = this.rows[this.currRow]; + row.setPenStyles(styles); + } + }, { + key: 'moveCursor', + value: function moveCursor(relPos) { + var row = this.rows[this.currRow]; + row.moveCursor(relPos); + } + }, { + key: 'setCursor', + value: function setCursor(absPos) { + logger.log('INFO', 'setCursor: ' + absPos); + var row = this.rows[this.currRow]; + row.setCursor(absPos); + } + }, { + key: 'setPAC', + value: function setPAC(pacData) { + logger.log('INFO', 'pacData = ' + JSON.stringify(pacData)); + var newRow = pacData.row - 1; + if (this.nrRollUpRows && newRow < this.nrRollUpRows - 1) { + newRow = this.nrRollUpRows - 1; + } + this.currRow = newRow; + var row = this.rows[this.currRow]; + if (pacData.indent !== null) { + var indent = pacData.indent; + var prevPos = Math.max(indent - 1, 0); + row.setCursor(pacData.indent); + pacData.color = row.chars[prevPos].penState.foreground; + } + var styles = { foreground: pacData.color, underline: pacData.underline, italics: pacData.italics, background: 'black', flash: false }; + this.setPen(styles); + } + + /** + * Set background/extra foreground, but first do back_space, and then insert space (backwards compatibility). + */ + + }, { + key: 'setBkgData', + value: function setBkgData(bkgData) { + + logger.log('INFO', 'bkgData = ' + JSON.stringify(bkgData)); + this.backSpace(); + this.setPen(bkgData); + this.insertChar(0x20); //Space + } + }, { + key: 'setRollUpRows', + value: function setRollUpRows(nrRows) { + this.nrRollUpRows = nrRows; + } + }, { + key: 'rollUp', + value: function rollUp() { + if (this.nrRollUpRows === null) { + logger.log('DEBUG', 'roll_up but nrRollUpRows not set yet'); + return; //Not properly setup + } + logger.log('TEXT', this.getDisplayText()); + var topRowIndex = this.currRow + 1 - this.nrRollUpRows; + var topRow = this.rows.splice(topRowIndex, 1)[0]; + topRow.clear(); + this.rows.splice(this.currRow, 0, topRow); + logger.log('INFO', 'Rolling up'); + //logger.log('TEXT', this.get_display_text()) + } + + /** + * Get all non-empty rows with as unicode text. + */ + + }, { + key: 'getDisplayText', + value: function getDisplayText(asOneRow) { + asOneRow = asOneRow || false; + var displayText = []; + var text = ''; + var rowNr = -1; + for (var i = 0; i < NR_ROWS; i++) { + var rowText = this.rows[i].getTextString(); + if (rowText) { + rowNr = i + 1; + if (asOneRow) { + displayText.push('Row ' + rowNr + ': \'' + rowText + '\''); + } else { + displayText.push(rowText.trim()); + } + } + } + if (displayText.length > 0) { + if (asOneRow) { + text = '[' + displayText.join(' | ') + ']'; + } else { + text = displayText.join('\n'); + } + } + return text; + } + }, { + key: 'getTextAndFormat', + value: function getTextAndFormat() { + return this.rows; + } + }]); + + return CaptionScreen; + }(); + + //var modes = ['MODE_ROLL-UP', 'MODE_POP-ON', 'MODE_PAINT-ON', 'MODE_TEXT']; + + var Cea608Channel = function () { + function Cea608Channel(channelNumber, outputFilter) { + _classCallCheck(this, Cea608Channel); + + this.chNr = channelNumber; + this.outputFilter = outputFilter; + this.mode = null; + this.verbose = 0; + this.displayedMemory = new CaptionScreen(); + this.nonDisplayedMemory = new CaptionScreen(); + this.lastOutputScreen = new CaptionScreen(); + this.currRollUpRow = this.displayedMemory.rows[NR_ROWS - 1]; + this.writeScreen = this.displayedMemory; + this.mode = null; + this.cueStartTime = null; // Keeps track of where a cue started. + } + + _createClass(Cea608Channel, [{ + key: 'reset', + value: function reset() { + this.mode = null; + this.displayedMemory.reset(); + this.nonDisplayedMemory.reset(); + this.lastOutputScreen.reset(); + this.currRollUpRow = this.displayedMemory.rows[NR_ROWS - 1]; + this.writeScreen = this.displayedMemory; + this.mode = null; + this.cueStartTime = null; + this.lastCueEndTime = null; + } + }, { + key: 'getHandler', + value: function getHandler() { + return this.outputFilter; + } + }, { + key: 'setHandler', + value: function setHandler(newHandler) { + this.outputFilter = newHandler; + } + }, { + key: 'setPAC', + value: function setPAC(pacData) { + this.writeScreen.setPAC(pacData); + } + }, { + key: 'setBkgData', + value: function setBkgData(bkgData) { + this.writeScreen.setBkgData(bkgData); + } + }, { + key: 'setMode', + value: function setMode(newMode) { + if (newMode === this.mode) { + return; + } + this.mode = newMode; + logger.log('INFO', 'MODE=' + newMode); + if (this.mode === 'MODE_POP-ON') { + this.writeScreen = this.nonDisplayedMemory; + } else { + this.writeScreen = this.displayedMemory; + this.writeScreen.reset(); + } + if (this.mode !== 'MODE_ROLL-UP') { + this.displayedMemory.nrRollUpRows = null; + this.nonDisplayedMemory.nrRollUpRows = null; + } + this.mode = newMode; + } + }, { + key: 'insertChars', + value: function insertChars(chars) { + for (var i = 0; i < chars.length; i++) { + this.writeScreen.insertChar(chars[i]); + } + var screen = this.writeScreen === this.displayedMemory ? 'DISP' : 'NON_DISP'; + logger.log('INFO', screen + ': ' + this.writeScreen.getDisplayText(true)); + if (this.mode === 'MODE_PAINT-ON' || this.mode === 'MODE_ROLL-UP') { + logger.log('TEXT', 'DISPLAYED: ' + this.displayedMemory.getDisplayText(true)); + this.outputDataUpdate(); + } + } + }, { + key: 'ccRCL', + value: function ccRCL() { + // Resume Caption Loading (switch mode to Pop On) + logger.log('INFO', 'RCL - Resume Caption Loading'); + this.setMode('MODE_POP-ON'); + } + }, { + key: 'ccBS', + value: function ccBS() { + // BackSpace + logger.log('INFO', 'BS - BackSpace'); + if (this.mode === 'MODE_TEXT') { + return; + } + this.writeScreen.backSpace(); + if (this.writeScreen === this.displayedMemory) { + this.outputDataUpdate(); + } + } + }, { + key: 'ccAOF', + value: function ccAOF() { + // Reserved (formerly Alarm Off) + return; + } + }, { + key: 'ccAON', + value: function ccAON() { + // Reserved (formerly Alarm On) + return; + } + }, { + key: 'ccDER', + value: function ccDER() { + // Delete to End of Row + logger.log('INFO', 'DER- Delete to End of Row'); + this.writeScreen.clearToEndOfRow(); + this.outputDataUpdate(); + } + }, { + key: 'ccRU', + value: function ccRU(nrRows) { + //Roll-Up Captions-2,3,or 4 Rows + logger.log('INFO', 'RU(' + nrRows + ') - Roll Up'); + this.writeScreen = this.displayedMemory; + this.setMode('MODE_ROLL-UP'); + this.writeScreen.setRollUpRows(nrRows); + } + }, { + key: 'ccFON', + value: function ccFON() { + //Flash On + logger.log('INFO', 'FON - Flash On'); + this.writeScreen.setPen({ flash: true }); + } + }, { + key: 'ccRDC', + value: function ccRDC() { + // Resume Direct Captioning (switch mode to PaintOn) + logger.log('INFO', 'RDC - Resume Direct Captioning'); + this.setMode('MODE_PAINT-ON'); + } + }, { + key: 'ccTR', + value: function ccTR() { + // Text Restart in text mode (not supported, however) + logger.log('INFO', 'TR'); + this.setMode('MODE_TEXT'); + } + }, { + key: 'ccRTD', + value: function ccRTD() { + // Resume Text Display in Text mode (not supported, however) + logger.log('INFO', 'RTD'); + this.setMode('MODE_TEXT'); + } + }, { + key: 'ccEDM', + value: function ccEDM() { + // Erase Displayed Memory + logger.log('INFO', 'EDM - Erase Displayed Memory'); + this.displayedMemory.reset(); + this.outputDataUpdate(); + } + }, { + key: 'ccCR', + value: function ccCR() { + // Carriage Return + logger.log('CR - Carriage Return'); + this.writeScreen.rollUp(); + this.outputDataUpdate(); + } + }, { + key: 'ccENM', + value: function ccENM() { + //Erase Non-Displayed Memory + logger.log('INFO', 'ENM - Erase Non-displayed Memory'); + this.nonDisplayedMemory.reset(); + } + }, { + key: 'ccEOC', + value: function ccEOC() { + //End of Caption (Flip Memories) + logger.log('INFO', 'EOC - End Of Caption'); + if (this.mode === 'MODE_POP-ON') { + var tmp = this.displayedMemory; + this.displayedMemory = this.nonDisplayedMemory; + this.nonDisplayedMemory = tmp; + this.writeScreen = this.nonDisplayedMemory; + logger.log('TEXT', 'DISP: ' + this.displayedMemory.getDisplayText()); + } + this.outputDataUpdate(); + } + }, { + key: 'ccTO', + value: function ccTO(nrCols) { + // Tab Offset 1,2, or 3 columns + logger.log('INFO', 'TO(' + nrCols + ') - Tab Offset'); + this.writeScreen.moveCursor(nrCols); + } + }, { + key: 'ccMIDROW', + value: function ccMIDROW(secondByte) { + // Parse MIDROW command + var styles = { flash: false }; + styles.underline = secondByte % 2 === 1; + styles.italics = secondByte >= 0x2e; + if (!styles.italics) { + var colorIndex = Math.floor(secondByte / 2) - 0x10; + var colors = ['white', 'green', 'blue', 'cyan', 'red', 'yellow', 'magenta']; + styles.foreground = colors[colorIndex]; + } else { + styles.foreground = 'white'; + } + logger.log('INFO', 'MIDROW: ' + JSON.stringify(styles)); + this.writeScreen.setPen(styles); + } + }, { + key: 'outputDataUpdate', + value: function outputDataUpdate() { + var t = logger.time; + if (t === null) { + return; + } + if (this.outputFilter) { + if (this.outputFilter.updateData) { + this.outputFilter.updateData(t, this.displayedMemory); + } + if (this.cueStartTime === null && !this.displayedMemory.isEmpty()) { + // Start of a new cue + this.cueStartTime = t; + } else { + if (!this.displayedMemory.equals(this.lastOutputScreen)) { + if (this.outputFilter.newCue) { + this.outputFilter.newCue(this.cueStartTime, t, this.lastOutputScreen); + } + this.cueStartTime = this.displayedMemory.isEmpty() ? null : t; + } + } + this.lastOutputScreen.copy(this.displayedMemory); + } + } + }, { + key: 'cueSplitAtTime', + value: function cueSplitAtTime(t) { + if (this.outputFilter) { + if (!this.displayedMemory.isEmpty()) { + if (this.outputFilter.newCue) { + this.outputFilter.newCue(this.cueStartTime, t, this.displayedMemory); + } + this.cueStartTime = t; + } + } + } + }]); + + return Cea608Channel; + }(); + + var Cea608Parser = function () { + function Cea608Parser(field, out1, out2) { + _classCallCheck(this, Cea608Parser); + + this.field = field || 1; + this.outputs = [out1, out2]; + this.channels = [new Cea608Channel(1, out1), new Cea608Channel(2, out2)]; + this.currChNr = -1; // Will be 1 or 2 + this.lastCmdA = null; // First byte of last command + this.lastCmdB = null; // Second byte of last command + this.bufferedData = []; + this.startTime = null; + this.lastTime = null; + this.dataCounters = { 'padding': 0, 'char': 0, 'cmd': 0, 'other': 0 }; + } + + _createClass(Cea608Parser, [{ + key: 'getHandler', + value: function getHandler(index) { + return this.channels[index].getHandler(); + } + }, { + key: 'setHandler', + value: function setHandler(index, newHandler) { + this.channels[index].setHandler(newHandler); + } + + /** + * Add data for time t in forms of list of bytes (unsigned ints). The bytes are treated as pairs. + */ + + }, { + key: 'addData', + value: function addData(t, byteList) { + var cmdFound, + a, + b, + charsFound = false; + + this.lastTime = t; + logger.setTime(t); + + for (var i = 0; i < byteList.length; i += 2) { + a = byteList[i] & 0x7f; + b = byteList[i + 1] & 0x7f; + if (a === 0 && b === 0) { + this.dataCounters.padding += 2; + continue; + } else { + logger.log('DATA', '[' + numArrayToHexArray([byteList[i], byteList[i + 1]]) + '] -> (' + numArrayToHexArray([a, b]) + ')'); + } + cmdFound = this.parseCmd(a, b); + if (!cmdFound) { + cmdFound = this.parseMidrow(a, b); + } + if (!cmdFound) { + cmdFound = this.parsePAC(a, b); + } + if (!cmdFound) { + cmdFound = this.parseBackgroundAttributes(a, b); + } + if (!cmdFound) { + charsFound = this.parseChars(a, b); + if (charsFound) { + if (this.currChNr && this.currChNr >= 0) { + var channel = this.channels[this.currChNr - 1]; + channel.insertChars(charsFound); + } else { + logger.log('WARNING', 'No channel found yet. TEXT-MODE?'); + } + } + } + if (cmdFound) { + this.dataCounters.cmd += 2; + } else if (charsFound) { + this.dataCounters.char += 2; + } else { + this.dataCounters.other += 2; + logger.log('WARNING', 'Couldn\'t parse cleaned data ' + numArrayToHexArray([a, b]) + ' orig: ' + numArrayToHexArray([byteList[i], byteList[i + 1]])); + } + } + } + + /** + * Parse Command. + * @returns {Boolean} Tells if a command was found + */ + + }, { + key: 'parseCmd', + value: function parseCmd(a, b) { + var chNr = null; + + var cond1 = (a === 0x14 || a === 0x1C) && 0x20 <= b && b <= 0x2F; + var cond2 = (a === 0x17 || a === 0x1F) && 0x21 <= b && b <= 0x23; + if (!(cond1 || cond2)) { + return false; + } + + if (a === this.lastCmdA && b === this.lastCmdB) { + this.lastCmdA = null; + this.lastCmdB = null; // Repeated commands are dropped (once) + logger.log('DEBUG', 'Repeated command (' + numArrayToHexArray([a, b]) + ') is dropped'); + return true; + } + + if (a === 0x14 || a === 0x17) { + chNr = 1; + } else { + chNr = 2; // (a === 0x1C || a=== 0x1f) + } + + var channel = this.channels[chNr - 1]; + + if (a === 0x14 || a === 0x1C) { + if (b === 0x20) { + channel.ccRCL(); + } else if (b === 0x21) { + channel.ccBS(); + } else if (b === 0x22) { + channel.ccAOF(); + } else if (b === 0x23) { + channel.ccAON(); + } else if (b === 0x24) { + channel.ccDER(); + } else if (b === 0x25) { + channel.ccRU(2); + } else if (b === 0x26) { + channel.ccRU(3); + } else if (b === 0x27) { + channel.ccRU(4); + } else if (b === 0x28) { + channel.ccFON(); + } else if (b === 0x29) { + channel.ccRDC(); + } else if (b === 0x2A) { + channel.ccTR(); + } else if (b === 0x2B) { + channel.ccRTD(); + } else if (b === 0x2C) { + channel.ccEDM(); + } else if (b === 0x2D) { + channel.ccCR(); + } else if (b === 0x2E) { + channel.ccENM(); + } else if (b === 0x2F) { + channel.ccEOC(); + } + } else { + //a == 0x17 || a == 0x1F + channel.ccTO(b - 0x20); + } + this.lastCmdA = a; + this.lastCmdB = b; + this.currChNr = chNr; + return true; + } + + /** + * Parse midrow styling command + * @returns {Boolean} + */ + + }, { + key: 'parseMidrow', + value: function parseMidrow(a, b) { + var chNr = null; + + if ((a === 0x11 || a === 0x19) && 0x20 <= b && b <= 0x2f) { + if (a === 0x11) { + chNr = 1; + } else { + chNr = 2; + } + if (chNr !== this.currChNr) { + logger.log('ERROR', 'Mismatch channel in midrow parsing'); + return false; + } + var channel = this.channels[chNr - 1]; + channel.ccMIDROW(b); + logger.log('DEBUG', 'MIDROW (' + numArrayToHexArray([a, b]) + ')'); + return true; + } + return false; + } + /** + * Parse Preable Access Codes (Table 53). + * @returns {Boolean} Tells if PAC found + */ + + }, { + key: 'parsePAC', + value: function parsePAC(a, b) { + + var chNr = null; + var row = null; + + var case1 = (0x11 <= a && a <= 0x17 || 0x19 <= a && a <= 0x1F) && 0x40 <= b && b <= 0x7F; + var case2 = (a === 0x10 || a === 0x18) && 0x40 <= b && b <= 0x5F; + if (!(case1 || case2)) { + return false; + } + + if (a === this.lastCmdA && b === this.lastCmdB) { + this.lastCmdA = null; + this.lastCmdB = null; + return true; // Repeated commands are dropped (once) + } + + chNr = a <= 0x17 ? 1 : 2; + + if (0x40 <= b && b <= 0x5F) { + row = chNr === 1 ? rowsLowCh1[a] : rowsLowCh2[a]; + } else { + // 0x60 <= b <= 0x7F + row = chNr === 1 ? rowsHighCh1[a] : rowsHighCh2[a]; + } + var pacData = this.interpretPAC(row, b); + var channel = this.channels[chNr - 1]; + channel.setPAC(pacData); + this.lastCmdA = a; + this.lastCmdB = b; + this.currChNr = chNr; + return true; + } + + /** + * Interpret the second byte of the pac, and return the information. + * @returns {Object} pacData with style parameters. + */ + + }, { + key: 'interpretPAC', + value: function interpretPAC(row, byte) { + var pacIndex = byte; + var pacData = { color: null, italics: false, indent: null, underline: false, row: row }; + + if (byte > 0x5F) { + pacIndex = byte - 0x60; + } else { + pacIndex = byte - 0x40; + } + pacData.underline = (pacIndex & 1) === 1; + if (pacIndex <= 0xd) { + pacData.color = ['white', 'green', 'blue', 'cyan', 'red', 'yellow', 'magenta', 'white'][Math.floor(pacIndex / 2)]; + } else if (pacIndex <= 0xf) { + pacData.italics = true; + pacData.color = 'white'; + } else { + pacData.indent = Math.floor((pacIndex - 0x10) / 2) * 4; + } + return pacData; // Note that row has zero offset. The spec uses 1. + } + + /** + * Parse characters. + * @returns An array with 1 to 2 codes corresponding to chars, if found. null otherwise. + */ + + }, { + key: 'parseChars', + value: function parseChars(a, b) { + + var channelNr = null, + charCodes = null, + charCode1 = null; + + if (a >= 0x19) { + channelNr = 2; + charCode1 = a - 8; + } else { + channelNr = 1; + charCode1 = a; + } + if (0x11 <= charCode1 && charCode1 <= 0x13) { + // Special character + var oneCode = b; + if (charCode1 === 0x11) { + oneCode = b + 0x50; + } else if (charCode1 === 0x12) { + oneCode = b + 0x70; + } else { + oneCode = b + 0x90; + } + logger.log('INFO', 'Special char \'' + getCharForByte(oneCode) + '\' in channel ' + channelNr); + charCodes = [oneCode]; + } else if (0x20 <= a && a <= 0x7f) { + charCodes = b === 0 ? [a] : [a, b]; + } + if (charCodes) { + var hexCodes = numArrayToHexArray(charCodes); + logger.log('DEBUG', 'Char codes = ' + hexCodes.join(',')); + this.lastCmdA = null; + this.lastCmdB = null; + } + return charCodes; + } + + /** + * Parse extended background attributes as well as new foreground color black. + * @returns{Boolean} Tells if background attributes are found + */ + + }, { + key: 'parseBackgroundAttributes', + value: function parseBackgroundAttributes(a, b) { + var bkgData, index, chNr, channel; + + var case1 = (a === 0x10 || a === 0x18) && 0x20 <= b && b <= 0x2f; + var case2 = (a === 0x17 || a === 0x1f) && 0x2d <= b && b <= 0x2f; + if (!(case1 || case2)) { + return false; + } + bkgData = {}; + if (a === 0x10 || a === 0x18) { + index = Math.floor((b - 0x20) / 2); + bkgData.background = backgroundColors[index]; + if (b % 2 === 1) { + bkgData.background = bkgData.background + '_semi'; + } + } else if (b === 0x2d) { + bkgData.background = 'transparent'; + } else { + bkgData.foreground = 'black'; + if (b === 0x2f) { + bkgData.underline = true; + } + } + chNr = a < 0x18 ? 1 : 2; + channel = this.channels[chNr - 1]; + channel.setBkgData(bkgData); + this.lastCmdA = null; + this.lastCmdB = null; + return true; + } + + /** + * Reset state of parser and its channels. + */ + + }, { + key: 'reset', + value: function reset() { + for (var i = 0; i < this.channels.length; i++) { + if (this.channels[i]) { + this.channels[i].reset(); + } + } + this.lastCmdA = null; + this.lastCmdB = null; + } + + /** + * Trigger the generation of a cue, and the start of a new one if displayScreens are not empty. + */ + + }, { + key: 'cueSplitAtTime', + value: function cueSplitAtTime(t) { + for (var i = 0; i < this.channels.length; i++) { + if (this.channels[i]) { + this.channels[i].cueSplitAtTime(t); + } + } + } + }]); + + return Cea608Parser; + }(); + + exports.default = Cea608Parser; + + },{}],41:[function(_dereq_,module,exports){ + 'use strict'; + + var Cues = { + + newCue: function newCue(track, startTime, endTime, captionScreen) { + var row; + var cue; + var indenting; + var indent; + var text; + var VTTCue = window.VTTCue || window.TextTrackCue; + + for (var r = 0; r < captionScreen.rows.length; r++) { + row = captionScreen.rows[r]; + indenting = true; + indent = 0; + text = ''; + + if (!row.isEmpty()) { + for (var c = 0; c < row.chars.length; c++) { + if (row.chars[c].uchar.match(/\s/) && indenting) { + indent++; + } else { + text += row.chars[c].uchar; + indenting = false; + } + } + cue = new VTTCue(startTime, endTime, text.trim()); + + if (indent >= 16) { + indent--; + } else { + indent++; + } + + // VTTCue.line get's flakey when using controls, so let's now include line 13&14 + // also, drop line 1 since it's to close to the top + if (navigator.userAgent.match(/Firefox\//)) { + cue.line = r + 1; + } else { + cue.line = r > 7 ? r - 2 : r + 1; + } + cue.align = 'left'; + cue.position = 100 * (indent / 32) + (navigator.userAgent.match(/Firefox\//) ? 50 : 0); + track.addCue(cue); + } + } + } + + }; + + module.exports = Cues; + + },{}],42:[function(_dereq_,module,exports){ + "use strict"; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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; }; }(); + + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + + /* + * compute an Exponential Weighted moving average + * - https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average + * - heavily inspired from shaka-player + */ + + var EWMA = function () { + + // About half of the estimated value will be from the last |halfLife| samples by weight. + + function EWMA(halfLife) { + _classCallCheck(this, EWMA); + + // Larger values of alpha expire historical data more slowly. + this.alpha_ = halfLife ? Math.exp(Math.log(0.5) / halfLife) : 0; + this.estimate_ = 0; + this.totalWeight_ = 0; + } + + _createClass(EWMA, [{ + key: "sample", + value: function sample(weight, value) { + var adjAlpha = Math.pow(this.alpha_, weight); + this.estimate_ = value * (1 - adjAlpha) + adjAlpha * this.estimate_; + this.totalWeight_ += weight; + } + }, { + key: "getTotalWeight", + value: function getTotalWeight() { + return this.totalWeight_; + } + }, { + key: "getEstimate", + value: function getEstimate() { + if (this.alpha_) { + var zeroFactor = 1 - Math.pow(this.alpha_, this.totalWeight_); + return this.estimate_ / zeroFactor; + } else { + return this.estimate_; + } + } + }]); + + return EWMA; + }(); + + exports.default = EWMA; + + },{}],43:[function(_dereq_,module,exports){ + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; }; + + function noop() {} + + var fakeLogger = { + trace: noop, + debug: noop, + log: noop, + warn: noop, + info: noop, + error: noop + }; + + var exportedLogger = fakeLogger; + + /*globals self: false */ + + //let lastCallTime; + // function formatMsgWithTimeInfo(type, msg) { + // const now = Date.now(); + // const diff = lastCallTime ? '+' + (now - lastCallTime) : '0'; + // lastCallTime = now; + // msg = (new Date(now)).toISOString() + ' | [' + type + '] > ' + msg + ' ( ' + diff + ' ms )'; + // return msg; + // } + + function formatMsg(type, msg) { + msg = '[' + type + '] > ' + msg; + return msg; + } + + function consolePrintFn(type) { + var func = self.console[type]; + if (func) { + return function () { + for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } + + if (args[0]) { + args[0] = formatMsg(type, args[0]); + } + func.apply(self.console, args); + }; + } + return noop; + } + + function exportLoggerFunctions(debugConfig) { + for (var _len2 = arguments.length, functions = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) { + functions[_key2 - 1] = arguments[_key2]; + } + + functions.forEach(function (type) { + exportedLogger[type] = debugConfig[type] ? debugConfig[type].bind(debugConfig) : consolePrintFn(type); + }); + } + + var enableLogs = exports.enableLogs = function enableLogs(debugConfig) { + if (debugConfig === true || (typeof debugConfig === 'undefined' ? 'undefined' : _typeof(debugConfig)) === 'object') { + exportLoggerFunctions(debugConfig, + // Remove out from list here to hard-disable a log-level + //'trace', + 'debug', 'log', 'info', 'warn', 'error'); + // Some browsers don't allow to use bind on console object anyway + // fallback to default if needed + try { + exportedLogger.log(); + } catch (e) { + exportedLogger = fakeLogger; + } + } else { + exportedLogger = fakeLogger; + } + }; + + var logger = exports.logger = exportedLogger; + + },{}],44:[function(_dereq_,module,exports){ + 'use strict'; + + if (typeof ArrayBuffer !== 'undefined' && !ArrayBuffer.prototype.slice) { + ArrayBuffer.prototype.slice = function (start, end) { + var that = new Uint8Array(this); + if (end === undefined) { + end = that.length; + } + var result = new ArrayBuffer(end - start); + var resultArray = new Uint8Array(result); + for (var i = 0; i < resultArray.length; i++) { + resultArray[i] = that[i + start]; + } + return result; + }; + } + + },{}],45:[function(_dereq_,module,exports){ + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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; }; }(); + + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + + /** + * TimeRanges to string helper + */ + + var TimeRanges = function () { + function TimeRanges() { + _classCallCheck(this, TimeRanges); + } + + _createClass(TimeRanges, null, [{ + key: 'toString', + value: function toString(r) { + var log = '', + len = r.length; + for (var i = 0; i < len; i++) { + log += '[' + r.start(i).toFixed(3) + ',' + r.end(i).toFixed(3) + ']'; + } + return log; + } + }]); + + return TimeRanges; + }(); + + exports.default = TimeRanges; + + },{}],46:[function(_dereq_,module,exports){ + 'use strict'; + + var URLHelper = { + // build an absolute URL from a relative one using the provided baseURL + // if relativeURL is an absolute URL it will be returned as is. + buildAbsoluteURL: function buildAbsoluteURL(baseURL, relativeURL) { + // remove any remaining space and CRLF + relativeURL = relativeURL.trim(); + if (/^[a-z]+:/i.test(relativeURL)) { + // complete url, not relative + return relativeURL; + } + + var relativeURLQuery = null; + var relativeURLHash = null; + + var relativeURLHashSplit = /^([^#]*)(.*)$/.exec(relativeURL); + if (relativeURLHashSplit) { + relativeURLHash = relativeURLHashSplit[2]; + relativeURL = relativeURLHashSplit[1]; + } + var relativeURLQuerySplit = /^([^\?]*)(.*)$/.exec(relativeURL); + if (relativeURLQuerySplit) { + relativeURLQuery = relativeURLQuerySplit[2]; + relativeURL = relativeURLQuerySplit[1]; + } + + var baseURLHashSplit = /^([^#]*)(.*)$/.exec(baseURL); + if (baseURLHashSplit) { + baseURL = baseURLHashSplit[1]; + } + var baseURLQuerySplit = /^([^\?]*)(.*)$/.exec(baseURL); + if (baseURLQuerySplit) { + baseURL = baseURLQuerySplit[1]; + } + + var baseURLDomainSplit = /^(([a-z]+:)?\/\/[a-z0-9\.\-_~]+(:[0-9]+)?)?(\/.*)$/i.exec(baseURL); + if (!baseURLDomainSplit) { + throw new Error('Error trying to parse base URL.'); + } + + // e.g. 'http:', 'https:', '' + var baseURLProtocol = baseURLDomainSplit[2] || ''; + // e.g. 'http://example.com', '//example.com', '' + var baseURLProtocolDomain = baseURLDomainSplit[1] || ''; + // e.g. '/a/b/c/playlist.m3u8' + var baseURLPath = baseURLDomainSplit[4]; + + var builtURL = null; + if (/^\/\//.test(relativeURL)) { + // relative url starts wth '//' so copy protocol (which may be '' if baseUrl didn't provide one) + builtURL = baseURLProtocol + '//' + URLHelper.buildAbsolutePath('', relativeURL.substring(2)); + } else if (/^\//.test(relativeURL)) { + // relative url starts with '/' so start from root of domain + builtURL = baseURLProtocolDomain + '/' + URLHelper.buildAbsolutePath('', relativeURL.substring(1)); + } else { + builtURL = URLHelper.buildAbsolutePath(baseURLProtocolDomain + baseURLPath, relativeURL); + } + + // put the query and hash parts back + if (relativeURLQuery) { + builtURL += relativeURLQuery; + } + if (relativeURLHash) { + builtURL += relativeURLHash; + } + return builtURL; + }, + + // build an absolute path using the provided basePath + // adapted from https://developer.mozilla.org/en-US/docs/Web/API/document/cookie#Using_relative_URLs_in_the_path_parameter + // this does not handle the case where relativePath is "/" or "//". These cases should be handled outside this. + buildAbsolutePath: function buildAbsolutePath(basePath, relativePath) { + var sRelPath = relativePath; + var nUpLn, + sDir = '', + sPath = basePath.replace(/[^\/]*$/, sRelPath.replace(/(\/|^)(?:\.?\/+)+/g, '$1')); + for (var nEnd, nStart = 0; nEnd = sPath.indexOf('/../', nStart), nEnd > -1; nStart = nEnd + nUpLn) { + nUpLn = /^\/(?:\.\.\/)*/.exec(sPath.slice(nEnd))[0].length; + sDir = (sDir + sPath.substring(nStart, nEnd)).replace(new RegExp('(?:\\\/+[^\\\/]*){0,' + (nUpLn - 1) / 3 + '}$'), '/'); + } + return sDir + sPath.substr(nStart); + } + }; + + module.exports = URLHelper; + + },{}],47:[function(_dereq_,module,exports){ + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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; }; }(); /** + * XHR based logger + */ + + var _logger = _dereq_(43); + + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + + var XhrLoader = function () { + function XhrLoader(config) { + _classCallCheck(this, XhrLoader); + + if (config && config.xhrSetup) { + this.xhrSetup = config.xhrSetup; + } + } + + _createClass(XhrLoader, [{ + key: 'destroy', + value: function destroy() { + this.abort(); + this.loader = null; + } + }, { + key: 'abort', + value: function abort() { + var loader = this.loader; + if (loader && loader.readyState !== 4) { + this.stats.aborted = true; + loader.abort(); + } + + window.clearTimeout(this.requestTimeout); + this.requestTimeout = null; + window.clearTimeout(this.retryTimeout); + this.retryTimeout = null; + } + }, { + key: 'load', + value: function load(context, config, callbacks) { + this.context = context; + this.config = config; + this.callbacks = callbacks; + this.stats = { trequest: performance.now(), retry: 0 }; + this.retryDelay = config.retryDelay; + this.loadInternal(); + } + }, { + key: 'loadInternal', + value: function loadInternal() { + var xhr, + context = this.context; + + if (typeof XDomainRequest !== 'undefined') { + xhr = this.loader = new XDomainRequest(); + } else { + xhr = this.loader = new XMLHttpRequest(); + } + + xhr.onreadystatechange = this.readystatechange.bind(this); + xhr.onprogress = this.loadprogress.bind(this); + + xhr.open('GET', context.url, true); + + if (context.rangeEnd) { + xhr.setRequestHeader('Range', 'bytes=' + context.rangeStart + '-' + (context.rangeEnd - 1)); + } + xhr.responseType = context.responseType; + var stats = this.stats; + stats.tfirst = 0; + stats.loaded = 0; + if (this.xhrSetup) { + this.xhrSetup(xhr, context.url); + } + // setup timeout before we perform request + this.requestTimeout = window.setTimeout(this.loadtimeout.bind(this), this.config.timeout); + xhr.send(); + } + }, { + key: 'readystatechange', + value: function readystatechange(event) { + var xhr = event.currentTarget, + readyState = xhr.readyState, + stats = this.stats, + context = this.context, + config = this.config; + + // don't proceed if xhr has been aborted + if (stats.aborted) { + return; + } + + // in any case clear the current xhrs timeout + window.clearTimeout(this.requestTimeout); + + // HEADERS_RECEIVED + if (readyState >= 2) { + if (stats.tfirst === 0) { + stats.tfirst = Math.max(performance.now(), stats.trequest); + // reset timeout to total timeout duration minus the time it took to receive headers + this.requestTimeout = window.setTimeout(this.loadtimeout.bind(this), config.timeout - (stats.tfirst - stats.trequest)); + } + if (readyState === 4) { + var status = xhr.status; + // http status between 200 to 299 are all successful + if (status >= 200 && status < 300) { + stats.tload = Math.max(stats.tfirst, performance.now()); + var data = void 0, + len = void 0; + if (context.responseType === 'arraybuffer') { + data = xhr.response; + len = data.byteLength; + } else { + data = xhr.responseText; + len = data.length; + } + stats.loaded = stats.total = len; + var response = { url: xhr.responseURL, data: data }; + this.callbacks.onSuccess(response, stats, context); + } else { + // if max nb of retries reached or if http status between 400 and 499 (such error cannot be recovered, retrying is useless), return error + if (stats.retry >= config.maxRetry || status >= 400 && status < 499) { + _logger.logger.error(status + ' while loading ' + context.url); + this.callbacks.onError({ code: status, text: xhr.statusText }, context); + } else { + // retry + _logger.logger.warn(status + ' while loading ' + context.url + ', retrying in ' + this.retryDelay + '...'); + // aborts and resets internal state + this.destroy(); + // schedule retry + this.retryTimeout = window.setTimeout(this.loadInternal.bind(this), this.retryDelay); + // set exponential backoff + this.retryDelay = Math.min(2 * this.retryDelay, config.maxRetryDelay); + stats.retry++; + } + } + } + } + } + }, { + key: 'loadtimeout', + value: function loadtimeout() { + _logger.logger.warn('timeout while loading ' + this.context.url); + this.callbacks.onTimeout(this.stats, this.context); + } + }, { + key: 'loadprogress', + value: function loadprogress(event) { + var stats = this.stats; + stats.loaded = event.loaded; + if (event.lengthComputable) { + stats.total = event.total; + } + var onProgress = this.callbacks.onProgress; + if (onProgress) { + // last args is to provide on progress data + onProgress(stats, this.context, null); + } + } + }]); + + return XhrLoader; + }(); + + exports.default = XhrLoader; + + },{"43":43}]},{},[31])(31) + }); + //# sourceMappingURL=hls.js.map + + +/***/ }, +/* 70 */ +/***/ function(module, exports, __webpack_require__) { + + /* WEBPACK VAR INJECTION */(function(global, module) {/** + * lodash (Custom Build) <https://lodash.com/> + * Build: `lodash modularize exports="npm" -o ./` + * Copyright jQuery Foundation and other contributors <https://jquery.org/> + * Released under MIT license <https://lodash.com/license> + * Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE> + * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors + */ + + /** Used as the size to enable large array optimizations. */ + var LARGE_ARRAY_SIZE = 200; + + /** Used to stand-in for `undefined` hash values. */ + var HASH_UNDEFINED = '__lodash_hash_undefined__'; + + /** Used to compose bitmasks for comparison styles. */ + var UNORDERED_COMPARE_FLAG = 1, + PARTIAL_COMPARE_FLAG = 2; + + /** Used as references for various `Number` constants. */ + var MAX_SAFE_INTEGER = 9007199254740991; + + /** `Object#toString` result references. */ + var argsTag = '[object Arguments]', + arrayTag = '[object Array]', + boolTag = '[object Boolean]', + dateTag = '[object Date]', + errorTag = '[object Error]', + funcTag = '[object Function]', + genTag = '[object GeneratorFunction]', + mapTag = '[object Map]', + numberTag = '[object Number]', + objectTag = '[object Object]', + promiseTag = '[object Promise]', + regexpTag = '[object RegExp]', + setTag = '[object Set]', + stringTag = '[object String]', + symbolTag = '[object Symbol]', + weakMapTag = '[object WeakMap]'; + + var arrayBufferTag = '[object ArrayBuffer]', + dataViewTag = '[object DataView]', + float32Tag = '[object Float32Array]', + float64Tag = '[object Float64Array]', + int8Tag = '[object Int8Array]', + int16Tag = '[object Int16Array]', + int32Tag = '[object Int32Array]', + uint8Tag = '[object Uint8Array]', + uint8ClampedTag = '[object Uint8ClampedArray]', + uint16Tag = '[object Uint16Array]', + uint32Tag = '[object Uint32Array]'; + + /** + * Used to match `RegExp` + * [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns). + */ + var reRegExpChar = /[\\^$.*+?()[\]{}|]/g; + + /** Used to detect host constructors (Safari). */ + var reIsHostCtor = /^\[object .+?Constructor\]$/; + + /** Used to detect unsigned integer values. */ + var reIsUint = /^(?:0|[1-9]\d*)$/; + + /** Used to identify `toStringTag` values of typed arrays. */ + var typedArrayTags = {}; + typedArrayTags[float32Tag] = typedArrayTags[float64Tag] = + typedArrayTags[int8Tag] = typedArrayTags[int16Tag] = + typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] = + typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] = + typedArrayTags[uint32Tag] = true; + typedArrayTags[argsTag] = typedArrayTags[arrayTag] = + typedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] = + typedArrayTags[dataViewTag] = typedArrayTags[dateTag] = + typedArrayTags[errorTag] = typedArrayTags[funcTag] = + typedArrayTags[mapTag] = typedArrayTags[numberTag] = + typedArrayTags[objectTag] = typedArrayTags[regexpTag] = + typedArrayTags[setTag] = typedArrayTags[stringTag] = + typedArrayTags[weakMapTag] = false; + + /** Detect free variable `global` from Node.js. */ + var freeGlobal = typeof global == 'object' && global && global.Object === Object && global; + + /** Detect free variable `self`. */ + var freeSelf = typeof self == 'object' && self && self.Object === Object && self; + + /** Used as a reference to the global object. */ + var root = freeGlobal || freeSelf || Function('return this')(); + + /** Detect free variable `exports`. */ + var freeExports = typeof exports == 'object' && exports && !exports.nodeType && exports; + + /** Detect free variable `module`. */ + var freeModule = freeExports && typeof module == 'object' && module && !module.nodeType && module; + + /** Detect the popular CommonJS extension `module.exports`. */ + var moduleExports = freeModule && freeModule.exports === freeExports; + + /** Detect free variable `process` from Node.js. */ + var freeProcess = moduleExports && freeGlobal.process; + + /** Used to access faster Node.js helpers. */ + var nodeUtil = (function() { + try { + return freeProcess && freeProcess.binding('util'); + } catch (e) {} + }()); + + /* Node.js helper references. */ + var nodeIsTypedArray = nodeUtil && nodeUtil.isTypedArray; + + /** + * A specialized version of `_.some` for arrays without support for iteratee + * shorthands. + * + * @private + * @param {Array} [array] The array to iterate over. + * @param {Function} predicate The function invoked per iteration. + * @returns {boolean} Returns `true` if any element passes the predicate check, + * else `false`. + */ + function arraySome(array, predicate) { + var index = -1, + length = array ? array.length : 0; + + while (++index < length) { + if (predicate(array[index], index, array)) { + return true; + } + } + return false; + } + + /** + * The base implementation of `_.times` without support for iteratee shorthands + * or max array length checks. + * + * @private + * @param {number} n The number of times to invoke `iteratee`. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array} Returns the array of results. + */ + function baseTimes(n, iteratee) { + var index = -1, + result = Array(n); + + while (++index < n) { + result[index] = iteratee(index); + } + return result; + } + + /** + * The base implementation of `_.unary` without support for storing metadata. + * + * @private + * @param {Function} func The function to cap arguments for. + * @returns {Function} Returns the new capped function. + */ + function baseUnary(func) { + return function(value) { + return func(value); + }; + } + + /** + * Gets the value at `key` of `object`. + * + * @private + * @param {Object} [object] The object to query. + * @param {string} key The key of the property to get. + * @returns {*} Returns the property value. + */ + function getValue(object, key) { + return object == null ? undefined : object[key]; + } + + /** + * Checks if `value` is a host object in IE < 9. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a host object, else `false`. + */ + function isHostObject(value) { + // Many host objects are `Object` objects that can coerce to strings + // despite having improperly defined `toString` methods. + var result = false; + if (value != null && typeof value.toString != 'function') { + try { + result = !!(value + ''); + } catch (e) {} + } + return result; + } + + /** + * Converts `map` to its key-value pairs. + * + * @private + * @param {Object} map The map to convert. + * @returns {Array} Returns the key-value pairs. + */ + function mapToArray(map) { + var index = -1, + result = Array(map.size); + + map.forEach(function(value, key) { + result[++index] = [key, value]; + }); + return result; + } + + /** + * Creates a unary function that invokes `func` with its argument transformed. + * + * @private + * @param {Function} func The function to wrap. + * @param {Function} transform The argument transform. + * @returns {Function} Returns the new function. + */ + function overArg(func, transform) { + return function(arg) { + return func(transform(arg)); + }; + } + + /** + * Converts `set` to an array of its values. + * + * @private + * @param {Object} set The set to convert. + * @returns {Array} Returns the values. + */ + function setToArray(set) { + var index = -1, + result = Array(set.size); + + set.forEach(function(value) { + result[++index] = value; + }); + return result; + } + + /** Used for built-in method references. */ + var arrayProto = Array.prototype, + funcProto = Function.prototype, + objectProto = Object.prototype; + + /** Used to detect overreaching core-js shims. */ + var coreJsData = root['__core-js_shared__']; + + /** Used to detect methods masquerading as native. */ + var maskSrcKey = (function() { + var uid = /[^.]+$/.exec(coreJsData && coreJsData.keys && coreJsData.keys.IE_PROTO || ''); + return uid ? ('Symbol(src)_1.' + uid) : ''; + }()); + + /** Used to resolve the decompiled source of functions. */ + var funcToString = funcProto.toString; + + /** Used to check objects for own properties. */ + var hasOwnProperty = objectProto.hasOwnProperty; + + /** + * Used to resolve the + * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring) + * of values. + */ + var objectToString = objectProto.toString; + + /** Used to detect if a method is native. */ + var reIsNative = RegExp('^' + + funcToString.call(hasOwnProperty).replace(reRegExpChar, '\\$&') + .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$' + ); + + /** Built-in value references. */ + var Symbol = root.Symbol, + Uint8Array = root.Uint8Array, + propertyIsEnumerable = objectProto.propertyIsEnumerable, + splice = arrayProto.splice; + + /* Built-in method references for those with the same name as other `lodash` methods. */ + var nativeKeys = overArg(Object.keys, Object); + + /* Built-in method references that are verified to be native. */ + var DataView = getNative(root, 'DataView'), + Map = getNative(root, 'Map'), + Promise = getNative(root, 'Promise'), + Set = getNative(root, 'Set'), + WeakMap = getNative(root, 'WeakMap'), + nativeCreate = getNative(Object, 'create'); + + /** Used to detect maps, sets, and weakmaps. */ + var dataViewCtorString = toSource(DataView), + mapCtorString = toSource(Map), + promiseCtorString = toSource(Promise), + setCtorString = toSource(Set), + weakMapCtorString = toSource(WeakMap); + + /** Used to convert symbols to primitives and strings. */ + var symbolProto = Symbol ? Symbol.prototype : undefined, + symbolValueOf = symbolProto ? symbolProto.valueOf : undefined; + + /** + * Creates a hash object. + * + * @private + * @constructor + * @param {Array} [entries] The key-value pairs to cache. + */ + function Hash(entries) { + var index = -1, + length = entries ? entries.length : 0; + + this.clear(); + while (++index < length) { + var entry = entries[index]; + this.set(entry[0], entry[1]); + } + } + + /** + * Removes all key-value entries from the hash. + * + * @private + * @name clear + * @memberOf Hash + */ + function hashClear() { + this.__data__ = nativeCreate ? nativeCreate(null) : {}; + } + + /** + * Removes `key` and its value from the hash. + * + * @private + * @name delete + * @memberOf Hash + * @param {Object} hash The hash to modify. + * @param {string} key The key of the value to remove. + * @returns {boolean} Returns `true` if the entry was removed, else `false`. + */ + function hashDelete(key) { + return this.has(key) && delete this.__data__[key]; + } + + /** + * Gets the hash value for `key`. + * + * @private + * @name get + * @memberOf Hash + * @param {string} key The key of the value to get. + * @returns {*} Returns the entry value. + */ + function hashGet(key) { + var data = this.__data__; + if (nativeCreate) { + var result = data[key]; + return result === HASH_UNDEFINED ? undefined : result; + } + return hasOwnProperty.call(data, key) ? data[key] : undefined; + } + + /** + * Checks if a hash value for `key` exists. + * + * @private + * @name has + * @memberOf Hash + * @param {string} key The key of the entry to check. + * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. + */ + function hashHas(key) { + var data = this.__data__; + return nativeCreate ? data[key] !== undefined : hasOwnProperty.call(data, key); + } + + /** + * Sets the hash `key` to `value`. + * + * @private + * @name set + * @memberOf Hash + * @param {string} key The key of the value to set. + * @param {*} value The value to set. + * @returns {Object} Returns the hash instance. + */ + function hashSet(key, value) { + var data = this.__data__; + data[key] = (nativeCreate && value === undefined) ? HASH_UNDEFINED : value; + return this; + } + + // Add methods to `Hash`. + Hash.prototype.clear = hashClear; + Hash.prototype['delete'] = hashDelete; + Hash.prototype.get = hashGet; + Hash.prototype.has = hashHas; + Hash.prototype.set = hashSet; + + /** + * Creates an list cache object. + * + * @private + * @constructor + * @param {Array} [entries] The key-value pairs to cache. + */ + function ListCache(entries) { + var index = -1, + length = entries ? entries.length : 0; + + this.clear(); + while (++index < length) { + var entry = entries[index]; + this.set(entry[0], entry[1]); + } + } + + /** + * Removes all key-value entries from the list cache. + * + * @private + * @name clear + * @memberOf ListCache + */ + function listCacheClear() { + this.__data__ = []; + } + + /** + * Removes `key` and its value from the list cache. + * + * @private + * @name delete + * @memberOf ListCache + * @param {string} key The key of the value to remove. + * @returns {boolean} Returns `true` if the entry was removed, else `false`. + */ + function listCacheDelete(key) { + var data = this.__data__, + index = assocIndexOf(data, key); + + if (index < 0) { + return false; + } + var lastIndex = data.length - 1; + if (index == lastIndex) { + data.pop(); + } else { + splice.call(data, index, 1); + } + return true; + } + + /** + * Gets the list cache value for `key`. + * + * @private + * @name get + * @memberOf ListCache + * @param {string} key The key of the value to get. + * @returns {*} Returns the entry value. + */ + function listCacheGet(key) { + var data = this.__data__, + index = assocIndexOf(data, key); + + return index < 0 ? undefined : data[index][1]; + } + + /** + * Checks if a list cache value for `key` exists. + * + * @private + * @name has + * @memberOf ListCache + * @param {string} key The key of the entry to check. + * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. + */ + function listCacheHas(key) { + return assocIndexOf(this.__data__, key) > -1; + } + + /** + * Sets the list cache `key` to `value`. + * + * @private + * @name set + * @memberOf ListCache + * @param {string} key The key of the value to set. + * @param {*} value The value to set. + * @returns {Object} Returns the list cache instance. + */ + function listCacheSet(key, value) { + var data = this.__data__, + index = assocIndexOf(data, key); + + if (index < 0) { + data.push([key, value]); + } else { + data[index][1] = value; + } + return this; + } + + // Add methods to `ListCache`. + ListCache.prototype.clear = listCacheClear; + ListCache.prototype['delete'] = listCacheDelete; + ListCache.prototype.get = listCacheGet; + ListCache.prototype.has = listCacheHas; + ListCache.prototype.set = listCacheSet; + + /** + * Creates a map cache object to store key-value pairs. + * + * @private + * @constructor + * @param {Array} [entries] The key-value pairs to cache. + */ + function MapCache(entries) { + var index = -1, + length = entries ? entries.length : 0; + + this.clear(); + while (++index < length) { + var entry = entries[index]; + this.set(entry[0], entry[1]); + } + } + + /** + * Removes all key-value entries from the map. + * + * @private + * @name clear + * @memberOf MapCache + */ + function mapCacheClear() { + this.__data__ = { + 'hash': new Hash, + 'map': new (Map || ListCache), + 'string': new Hash + }; + } + + /** + * Removes `key` and its value from the map. + * + * @private + * @name delete + * @memberOf MapCache + * @param {string} key The key of the value to remove. + * @returns {boolean} Returns `true` if the entry was removed, else `false`. + */ + function mapCacheDelete(key) { + return getMapData(this, key)['delete'](key); + } + + /** + * Gets the map value for `key`. + * + * @private + * @name get + * @memberOf MapCache + * @param {string} key The key of the value to get. + * @returns {*} Returns the entry value. + */ + function mapCacheGet(key) { + return getMapData(this, key).get(key); + } + + /** + * Checks if a map value for `key` exists. + * + * @private + * @name has + * @memberOf MapCache + * @param {string} key The key of the entry to check. + * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. + */ + function mapCacheHas(key) { + return getMapData(this, key).has(key); + } + + /** + * Sets the map `key` to `value`. + * + * @private + * @name set + * @memberOf MapCache + * @param {string} key The key of the value to set. + * @param {*} value The value to set. + * @returns {Object} Returns the map cache instance. + */ + function mapCacheSet(key, value) { + getMapData(this, key).set(key, value); + return this; + } + + // Add methods to `MapCache`. + MapCache.prototype.clear = mapCacheClear; + MapCache.prototype['delete'] = mapCacheDelete; + MapCache.prototype.get = mapCacheGet; + MapCache.prototype.has = mapCacheHas; + MapCache.prototype.set = mapCacheSet; + + /** + * + * Creates an array cache object to store unique values. + * + * @private + * @constructor + * @param {Array} [values] The values to cache. + */ + function SetCache(values) { + var index = -1, + length = values ? values.length : 0; + + this.__data__ = new MapCache; + while (++index < length) { + this.add(values[index]); + } + } + + /** + * Adds `value` to the array cache. + * + * @private + * @name add + * @memberOf SetCache + * @alias push + * @param {*} value The value to cache. + * @returns {Object} Returns the cache instance. + */ + function setCacheAdd(value) { + this.__data__.set(value, HASH_UNDEFINED); + return this; + } + + /** + * Checks if `value` is in the array cache. + * + * @private + * @name has + * @memberOf SetCache + * @param {*} value The value to search for. + * @returns {number} Returns `true` if `value` is found, else `false`. + */ + function setCacheHas(value) { + return this.__data__.has(value); + } + + // Add methods to `SetCache`. + SetCache.prototype.add = SetCache.prototype.push = setCacheAdd; + SetCache.prototype.has = setCacheHas; + + /** + * Creates a stack cache object to store key-value pairs. + * + * @private + * @constructor + * @param {Array} [entries] The key-value pairs to cache. + */ + function Stack(entries) { + this.__data__ = new ListCache(entries); + } + + /** + * Removes all key-value entries from the stack. + * + * @private + * @name clear + * @memberOf Stack + */ + function stackClear() { + this.__data__ = new ListCache; + } + + /** + * Removes `key` and its value from the stack. + * + * @private + * @name delete + * @memberOf Stack + * @param {string} key The key of the value to remove. + * @returns {boolean} Returns `true` if the entry was removed, else `false`. + */ + function stackDelete(key) { + return this.__data__['delete'](key); + } + + /** + * Gets the stack value for `key`. + * + * @private + * @name get + * @memberOf Stack + * @param {string} key The key of the value to get. + * @returns {*} Returns the entry value. + */ + function stackGet(key) { + return this.__data__.get(key); + } + + /** + * Checks if a stack value for `key` exists. + * + * @private + * @name has + * @memberOf Stack + * @param {string} key The key of the entry to check. + * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. + */ + function stackHas(key) { + return this.__data__.has(key); + } + + /** + * Sets the stack `key` to `value`. + * + * @private + * @name set + * @memberOf Stack + * @param {string} key The key of the value to set. + * @param {*} value The value to set. + * @returns {Object} Returns the stack cache instance. + */ + function stackSet(key, value) { + var cache = this.__data__; + if (cache instanceof ListCache) { + var pairs = cache.__data__; + if (!Map || (pairs.length < LARGE_ARRAY_SIZE - 1)) { + pairs.push([key, value]); + return this; + } + cache = this.__data__ = new MapCache(pairs); + } + cache.set(key, value); + return this; + } + + // Add methods to `Stack`. + Stack.prototype.clear = stackClear; + Stack.prototype['delete'] = stackDelete; + Stack.prototype.get = stackGet; + Stack.prototype.has = stackHas; + Stack.prototype.set = stackSet; + + /** + * Creates an array of the enumerable property names of the array-like `value`. + * + * @private + * @param {*} value The value to query. + * @param {boolean} inherited Specify returning inherited property names. + * @returns {Array} Returns the array of property names. + */ + function arrayLikeKeys(value, inherited) { + // Safari 8.1 makes `arguments.callee` enumerable in strict mode. + // Safari 9 makes `arguments.length` enumerable in strict mode. + var result = (isArray(value) || isArguments(value)) + ? baseTimes(value.length, String) + : []; + + var length = result.length, + skipIndexes = !!length; + + for (var key in value) { + if ((inherited || hasOwnProperty.call(value, key)) && + !(skipIndexes && (key == 'length' || isIndex(key, length)))) { + result.push(key); + } + } + return result; + } + + /** + * Gets the index at which the `key` is found in `array` of key-value pairs. + * + * @private + * @param {Array} array The array to inspect. + * @param {*} key The key to search for. + * @returns {number} Returns the index of the matched value, else `-1`. + */ + function assocIndexOf(array, key) { + var length = array.length; + while (length--) { + if (eq(array[length][0], key)) { + return length; + } + } + return -1; + } + + /** + * The base implementation of `getTag`. + * + * @private + * @param {*} value The value to query. + * @returns {string} Returns the `toStringTag`. + */ + function baseGetTag(value) { + return objectToString.call(value); + } + + /** + * The base implementation of `_.isEqual` which supports partial comparisons + * and tracks traversed objects. + * + * @private + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @param {Function} [customizer] The function to customize comparisons. + * @param {boolean} [bitmask] The bitmask of comparison flags. + * The bitmask may be composed of the following flags: + * 1 - Unordered comparison + * 2 - Partial comparison + * @param {Object} [stack] Tracks traversed `value` and `other` objects. + * @returns {boolean} Returns `true` if the values are equivalent, else `false`. + */ + function baseIsEqual(value, other, customizer, bitmask, stack) { + if (value === other) { + return true; + } + if (value == null || other == null || (!isObject(value) && !isObjectLike(other))) { + return value !== value && other !== other; + } + return baseIsEqualDeep(value, other, baseIsEqual, customizer, bitmask, stack); + } + + /** + * A specialized version of `baseIsEqual` for arrays and objects which performs + * deep comparisons and tracks traversed objects enabling objects with circular + * references to be compared. + * + * @private + * @param {Object} object The object to compare. + * @param {Object} other The other object to compare. + * @param {Function} equalFunc The function to determine equivalents of values. + * @param {Function} [customizer] The function to customize comparisons. + * @param {number} [bitmask] The bitmask of comparison flags. See `baseIsEqual` + * for more details. + * @param {Object} [stack] Tracks traversed `object` and `other` objects. + * @returns {boolean} Returns `true` if the objects are equivalent, else `false`. + */ + function baseIsEqualDeep(object, other, equalFunc, customizer, bitmask, stack) { + var objIsArr = isArray(object), + othIsArr = isArray(other), + objTag = arrayTag, + othTag = arrayTag; + + if (!objIsArr) { + objTag = getTag(object); + objTag = objTag == argsTag ? objectTag : objTag; + } + if (!othIsArr) { + othTag = getTag(other); + othTag = othTag == argsTag ? objectTag : othTag; + } + var objIsObj = objTag == objectTag && !isHostObject(object), + othIsObj = othTag == objectTag && !isHostObject(other), + isSameTag = objTag == othTag; + + if (isSameTag && !objIsObj) { + stack || (stack = new Stack); + return (objIsArr || isTypedArray(object)) + ? equalArrays(object, other, equalFunc, customizer, bitmask, stack) + : equalByTag(object, other, objTag, equalFunc, customizer, bitmask, stack); + } + if (!(bitmask & PARTIAL_COMPARE_FLAG)) { + var objIsWrapped = objIsObj && hasOwnProperty.call(object, '__wrapped__'), + othIsWrapped = othIsObj && hasOwnProperty.call(other, '__wrapped__'); + + if (objIsWrapped || othIsWrapped) { + var objUnwrapped = objIsWrapped ? object.value() : object, + othUnwrapped = othIsWrapped ? other.value() : other; + + stack || (stack = new Stack); + return equalFunc(objUnwrapped, othUnwrapped, customizer, bitmask, stack); + } + } + if (!isSameTag) { + return false; + } + stack || (stack = new Stack); + return equalObjects(object, other, equalFunc, customizer, bitmask, stack); + } + + /** + * The base implementation of `_.isNative` without bad shim checks. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a native function, + * else `false`. + */ + function baseIsNative(value) { + if (!isObject(value) || isMasked(value)) { + return false; + } + var pattern = (isFunction(value) || isHostObject(value)) ? reIsNative : reIsHostCtor; + return pattern.test(toSource(value)); + } + + /** + * The base implementation of `_.isTypedArray` without Node.js optimizations. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a typed array, else `false`. + */ + function baseIsTypedArray(value) { + return isObjectLike(value) && + isLength(value.length) && !!typedArrayTags[objectToString.call(value)]; + } + + /** + * The base implementation of `_.keys` which doesn't treat sparse arrays as dense. + * + * @private + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names. + */ + function baseKeys(object) { + if (!isPrototype(object)) { + return nativeKeys(object); + } + var result = []; + for (var key in Object(object)) { + if (hasOwnProperty.call(object, key) && key != 'constructor') { + result.push(key); + } + } + return result; + } + + /** + * A specialized version of `baseIsEqualDeep` for arrays with support for + * partial deep comparisons. + * + * @private + * @param {Array} array The array to compare. + * @param {Array} other The other array to compare. + * @param {Function} equalFunc The function to determine equivalents of values. + * @param {Function} customizer The function to customize comparisons. + * @param {number} bitmask The bitmask of comparison flags. See `baseIsEqual` + * for more details. + * @param {Object} stack Tracks traversed `array` and `other` objects. + * @returns {boolean} Returns `true` if the arrays are equivalent, else `false`. + */ + function equalArrays(array, other, equalFunc, customizer, bitmask, stack) { + var isPartial = bitmask & PARTIAL_COMPARE_FLAG, + arrLength = array.length, + othLength = other.length; + + if (arrLength != othLength && !(isPartial && othLength > arrLength)) { + return false; + } + // Assume cyclic values are equal. + var stacked = stack.get(array); + if (stacked && stack.get(other)) { + return stacked == other; + } + var index = -1, + result = true, + seen = (bitmask & UNORDERED_COMPARE_FLAG) ? new SetCache : undefined; + + stack.set(array, other); + stack.set(other, array); + + // Ignore non-index properties. + while (++index < arrLength) { + var arrValue = array[index], + othValue = other[index]; + + if (customizer) { + var compared = isPartial + ? customizer(othValue, arrValue, index, other, array, stack) + : customizer(arrValue, othValue, index, array, other, stack); + } + if (compared !== undefined) { + if (compared) { + continue; + } + result = false; + break; + } + // Recursively compare arrays (susceptible to call stack limits). + if (seen) { + if (!arraySome(other, function(othValue, othIndex) { + if (!seen.has(othIndex) && + (arrValue === othValue || equalFunc(arrValue, othValue, customizer, bitmask, stack))) { + return seen.add(othIndex); + } + })) { + result = false; + break; + } + } else if (!( + arrValue === othValue || + equalFunc(arrValue, othValue, customizer, bitmask, stack) + )) { + result = false; + break; + } + } + stack['delete'](array); + stack['delete'](other); + return result; + } + + /** + * A specialized version of `baseIsEqualDeep` for comparing objects of + * the same `toStringTag`. + * + * **Note:** This function only supports comparing values with tags of + * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`. + * + * @private + * @param {Object} object The object to compare. + * @param {Object} other The other object to compare. + * @param {string} tag The `toStringTag` of the objects to compare. + * @param {Function} equalFunc The function to determine equivalents of values. + * @param {Function} customizer The function to customize comparisons. + * @param {number} bitmask The bitmask of comparison flags. See `baseIsEqual` + * for more details. + * @param {Object} stack Tracks traversed `object` and `other` objects. + * @returns {boolean} Returns `true` if the objects are equivalent, else `false`. + */ + function equalByTag(object, other, tag, equalFunc, customizer, bitmask, stack) { + switch (tag) { + case dataViewTag: + if ((object.byteLength != other.byteLength) || + (object.byteOffset != other.byteOffset)) { + return false; + } + object = object.buffer; + other = other.buffer; + + case arrayBufferTag: + if ((object.byteLength != other.byteLength) || + !equalFunc(new Uint8Array(object), new Uint8Array(other))) { + return false; + } + return true; + + case boolTag: + case dateTag: + case numberTag: + // Coerce booleans to `1` or `0` and dates to milliseconds. + // Invalid dates are coerced to `NaN`. + return eq(+object, +other); + + case errorTag: + return object.name == other.name && object.message == other.message; + + case regexpTag: + case stringTag: + // Coerce regexes to strings and treat strings, primitives and objects, + // as equal. See http://www.ecma-international.org/ecma-262/7.0/#sec-regexp.prototype.tostring + // for more details. + return object == (other + ''); + + case mapTag: + var convert = mapToArray; + + case setTag: + var isPartial = bitmask & PARTIAL_COMPARE_FLAG; + convert || (convert = setToArray); + + if (object.size != other.size && !isPartial) { + return false; + } + // Assume cyclic values are equal. + var stacked = stack.get(object); + if (stacked) { + return stacked == other; + } + bitmask |= UNORDERED_COMPARE_FLAG; + + // Recursively compare objects (susceptible to call stack limits). + stack.set(object, other); + var result = equalArrays(convert(object), convert(other), equalFunc, customizer, bitmask, stack); + stack['delete'](object); + return result; + + case symbolTag: + if (symbolValueOf) { + return symbolValueOf.call(object) == symbolValueOf.call(other); + } + } + return false; + } + + /** + * A specialized version of `baseIsEqualDeep` for objects with support for + * partial deep comparisons. + * + * @private + * @param {Object} object The object to compare. + * @param {Object} other The other object to compare. + * @param {Function} equalFunc The function to determine equivalents of values. + * @param {Function} customizer The function to customize comparisons. + * @param {number} bitmask The bitmask of comparison flags. See `baseIsEqual` + * for more details. + * @param {Object} stack Tracks traversed `object` and `other` objects. + * @returns {boolean} Returns `true` if the objects are equivalent, else `false`. + */ + function equalObjects(object, other, equalFunc, customizer, bitmask, stack) { + var isPartial = bitmask & PARTIAL_COMPARE_FLAG, + objProps = keys(object), + objLength = objProps.length, + othProps = keys(other), + othLength = othProps.length; + + if (objLength != othLength && !isPartial) { + return false; + } + var index = objLength; + while (index--) { + var key = objProps[index]; + if (!(isPartial ? key in other : hasOwnProperty.call(other, key))) { + return false; + } + } + // Assume cyclic values are equal. + var stacked = stack.get(object); + if (stacked && stack.get(other)) { + return stacked == other; + } + var result = true; + stack.set(object, other); + stack.set(other, object); + + var skipCtor = isPartial; + while (++index < objLength) { + key = objProps[index]; + var objValue = object[key], + othValue = other[key]; + + if (customizer) { + var compared = isPartial + ? customizer(othValue, objValue, key, other, object, stack) + : customizer(objValue, othValue, key, object, other, stack); + } + // Recursively compare objects (susceptible to call stack limits). + if (!(compared === undefined + ? (objValue === othValue || equalFunc(objValue, othValue, customizer, bitmask, stack)) + : compared + )) { + result = false; + break; + } + skipCtor || (skipCtor = key == 'constructor'); + } + if (result && !skipCtor) { + var objCtor = object.constructor, + othCtor = other.constructor; + + // Non `Object` object instances with different constructors are not equal. + if (objCtor != othCtor && + ('constructor' in object && 'constructor' in other) && + !(typeof objCtor == 'function' && objCtor instanceof objCtor && + typeof othCtor == 'function' && othCtor instanceof othCtor)) { + result = false; + } + } + stack['delete'](object); + stack['delete'](other); + return result; + } + + /** + * Gets the data for `map`. + * + * @private + * @param {Object} map The map to query. + * @param {string} key The reference key. + * @returns {*} Returns the map data. + */ + function getMapData(map, key) { + var data = map.__data__; + return isKeyable(key) + ? data[typeof key == 'string' ? 'string' : 'hash'] + : data.map; + } + + /** + * Gets the native function at `key` of `object`. + * + * @private + * @param {Object} object The object to query. + * @param {string} key The key of the method to get. + * @returns {*} Returns the function if it's native, else `undefined`. + */ + function getNative(object, key) { + var value = getValue(object, key); + return baseIsNative(value) ? value : undefined; + } + + /** + * Gets the `toStringTag` of `value`. + * + * @private + * @param {*} value The value to query. + * @returns {string} Returns the `toStringTag`. + */ + var getTag = baseGetTag; + + // Fallback for data views, maps, sets, and weak maps in IE 11, + // for data views in Edge < 14, and promises in Node.js. + if ((DataView && getTag(new DataView(new ArrayBuffer(1))) != dataViewTag) || + (Map && getTag(new Map) != mapTag) || + (Promise && getTag(Promise.resolve()) != promiseTag) || + (Set && getTag(new Set) != setTag) || + (WeakMap && getTag(new WeakMap) != weakMapTag)) { + getTag = function(value) { + var result = objectToString.call(value), + Ctor = result == objectTag ? value.constructor : undefined, + ctorString = Ctor ? toSource(Ctor) : undefined; + + if (ctorString) { + switch (ctorString) { + case dataViewCtorString: return dataViewTag; + case mapCtorString: return mapTag; + case promiseCtorString: return promiseTag; + case setCtorString: return setTag; + case weakMapCtorString: return weakMapTag; + } + } + return result; + }; + } + + /** + * Checks if `value` is a valid array-like index. + * + * @private + * @param {*} value The value to check. + * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index. + * @returns {boolean} Returns `true` if `value` is a valid index, else `false`. + */ + function isIndex(value, length) { + length = length == null ? MAX_SAFE_INTEGER : length; + return !!length && + (typeof value == 'number' || reIsUint.test(value)) && + (value > -1 && value % 1 == 0 && value < length); + } + + /** + * Checks if `value` is suitable for use as unique object key. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is suitable, else `false`. + */ + function isKeyable(value) { + var type = typeof value; + return (type == 'string' || type == 'number' || type == 'symbol' || type == 'boolean') + ? (value !== '__proto__') + : (value === null); + } + + /** + * Checks if `func` has its source masked. + * + * @private + * @param {Function} func The function to check. + * @returns {boolean} Returns `true` if `func` is masked, else `false`. + */ + function isMasked(func) { + return !!maskSrcKey && (maskSrcKey in func); + } + + /** + * Checks if `value` is likely a prototype object. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a prototype, else `false`. + */ + function isPrototype(value) { + var Ctor = value && value.constructor, + proto = (typeof Ctor == 'function' && Ctor.prototype) || objectProto; + + return value === proto; + } + + /** + * Converts `func` to its source code. + * + * @private + * @param {Function} func The function to process. + * @returns {string} Returns the source code. + */ + function toSource(func) { + if (func != null) { + try { + return funcToString.call(func); + } catch (e) {} + try { + return (func + ''); + } catch (e) {} + } + return ''; + } + + /** + * Performs a + * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) + * comparison between two values to determine if they are equivalent. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {boolean} Returns `true` if the values are equivalent, else `false`. + * @example + * + * var object = { 'a': 1 }; + * var other = { 'a': 1 }; + * + * _.eq(object, object); + * // => true + * + * _.eq(object, other); + * // => false + * + * _.eq('a', 'a'); + * // => true + * + * _.eq('a', Object('a')); + * // => false + * + * _.eq(NaN, NaN); + * // => true + */ + function eq(value, other) { + return value === other || (value !== value && other !== other); + } + + /** + * Checks if `value` is likely an `arguments` object. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an `arguments` object, + * else `false`. + * @example + * + * _.isArguments(function() { return arguments; }()); + * // => true + * + * _.isArguments([1, 2, 3]); + * // => false + */ + function isArguments(value) { + // Safari 8.1 makes `arguments.callee` enumerable in strict mode. + return isArrayLikeObject(value) && hasOwnProperty.call(value, 'callee') && + (!propertyIsEnumerable.call(value, 'callee') || objectToString.call(value) == argsTag); + } + + /** + * Checks if `value` is classified as an `Array` object. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an array, else `false`. + * @example + * + * _.isArray([1, 2, 3]); + * // => true + * + * _.isArray(document.body.children); + * // => false + * + * _.isArray('abc'); + * // => false + * + * _.isArray(_.noop); + * // => false + */ + var isArray = Array.isArray; + + /** + * Checks if `value` is array-like. A value is considered array-like if it's + * not a function and has a `value.length` that's an integer greater than or + * equal to `0` and less than or equal to `Number.MAX_SAFE_INTEGER`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is array-like, else `false`. + * @example + * + * _.isArrayLike([1, 2, 3]); + * // => true + * + * _.isArrayLike(document.body.children); + * // => true + * + * _.isArrayLike('abc'); + * // => true + * + * _.isArrayLike(_.noop); + * // => false + */ + function isArrayLike(value) { + return value != null && isLength(value.length) && !isFunction(value); + } + + /** + * This method is like `_.isArrayLike` except that it also checks if `value` + * is an object. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an array-like object, + * else `false`. + * @example + * + * _.isArrayLikeObject([1, 2, 3]); + * // => true + * + * _.isArrayLikeObject(document.body.children); + * // => true + * + * _.isArrayLikeObject('abc'); + * // => false + * + * _.isArrayLikeObject(_.noop); + * // => false + */ + function isArrayLikeObject(value) { + return isObjectLike(value) && isArrayLike(value); + } + + /** + * Performs a deep comparison between two values to determine if they are + * equivalent. + * + * **Note:** This method supports comparing arrays, array buffers, booleans, + * date objects, error objects, maps, numbers, `Object` objects, regexes, + * sets, strings, symbols, and typed arrays. `Object` objects are compared + * by their own, not inherited, enumerable properties. Functions and DOM + * nodes are **not** supported. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {boolean} Returns `true` if the values are equivalent, else `false`. + * @example + * + * var object = { 'a': 1 }; + * var other = { 'a': 1 }; + * + * _.isEqual(object, other); + * // => true + * + * object === other; + * // => false + */ + function isEqual(value, other) { + return baseIsEqual(value, other); + } + + /** + * Checks if `value` is classified as a `Function` object. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a function, else `false`. + * @example + * + * _.isFunction(_); + * // => true + * + * _.isFunction(/abc/); + * // => false + */ + function isFunction(value) { + // The use of `Object#toString` avoids issues with the `typeof` operator + // in Safari 8-9 which returns 'object' for typed array and other constructors. + var tag = isObject(value) ? objectToString.call(value) : ''; + return tag == funcTag || tag == genTag; + } + + /** + * Checks if `value` is a valid array-like length. + * + * **Note:** This method is loosely based on + * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a valid length, else `false`. + * @example + * + * _.isLength(3); + * // => true + * + * _.isLength(Number.MIN_VALUE); + * // => false + * + * _.isLength(Infinity); + * // => false + * + * _.isLength('3'); + * // => false + */ + function isLength(value) { + return typeof value == 'number' && + value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER; + } + + /** + * Checks if `value` is the + * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types) + * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an object, else `false`. + * @example + * + * _.isObject({}); + * // => true + * + * _.isObject([1, 2, 3]); + * // => true + * + * _.isObject(_.noop); + * // => true + * + * _.isObject(null); + * // => false + */ + function isObject(value) { + var type = typeof value; + return !!value && (type == 'object' || type == 'function'); + } + + /** + * Checks if `value` is object-like. A value is object-like if it's not `null` + * and has a `typeof` result of "object". + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is object-like, else `false`. + * @example + * + * _.isObjectLike({}); + * // => true + * + * _.isObjectLike([1, 2, 3]); + * // => true + * + * _.isObjectLike(_.noop); + * // => false + * + * _.isObjectLike(null); + * // => false + */ + function isObjectLike(value) { + return !!value && typeof value == 'object'; + } + + /** + * Checks if `value` is classified as a typed array. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a typed array, else `false`. + * @example + * + * _.isTypedArray(new Uint8Array); + * // => true + * + * _.isTypedArray([]); + * // => false + */ + var isTypedArray = nodeIsTypedArray ? baseUnary(nodeIsTypedArray) : baseIsTypedArray; + + /** + * Creates an array of the own enumerable property names of `object`. + * + * **Note:** Non-object values are coerced to objects. See the + * [ES spec](http://ecma-international.org/ecma-262/7.0/#sec-object.keys) + * for more details. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Object + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names. + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.keys(new Foo); + * // => ['a', 'b'] (iteration order is not guaranteed) + * + * _.keys('hi'); + * // => ['0', '1'] + */ + function keys(object) { + return isArrayLike(object) ? arrayLikeKeys(object) : baseKeys(object); + } + + module.exports = isEqual; + + /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }()), __webpack_require__(33)(module))) + +/***/ }, +/* 71 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + module.exports = __webpack_require__(72); + +/***/ }, +/* 72 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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 _playback = __webpack_require__(31); + + var _playback2 = _interopRequireDefault(_playback); + + var _styler = __webpack_require__(15); + + var _styler2 = _interopRequireDefault(_styler); + + var _style = __webpack_require__(73); + + var _style2 = _interopRequireDefault(_style); + + var _events = __webpack_require__(5); + + var _events2 = _interopRequireDefault(_events); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + 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; } // Copyright 2014 Globo.com Player authors. All rights reserved. + // Use of this source code is governed by a BSD-style + // license that can be found in the LICENSE file. + + var HTMLImg = function (_Playback) { + _inherits(HTMLImg, _Playback); + + HTMLImg.prototype.getPlaybackType = function getPlaybackType() { + return _playback2.default.NO_OP; + }; + + _createClass(HTMLImg, [{ + key: 'name', + get: function get() { + return 'html_img'; + } + }, { + key: 'tagName', + get: function get() { + return 'img'; + } + }, { + key: 'attributes', + get: function get() { + return { + 'data-html-img': '' + }; + } + }, { + key: 'events', + get: function get() { + return { + 'load': '_onLoad', + 'abort': '_onError', + 'error': '_onError' + }; + } + }]); + + function HTMLImg(params) { + _classCallCheck(this, HTMLImg); + + var _this = _possibleConstructorReturn(this, _Playback.call(this, params)); + + _this.el.src = params.src; + return _this; + } + + HTMLImg.prototype.render = function render() { + var style = _styler2.default.getStyleFor(_style2.default); + this.$el.append(style); + this.trigger(_events2.default.PLAYBACK_READY, this.name); + return this; + }; + + HTMLImg.prototype._onLoad = function _onLoad() { + this.trigger(_events2.default.PLAYBACK_ENDED, this.name); + }; + + HTMLImg.prototype._onError = function _onError(evt) { + var m = evt.type === 'error' ? 'load error' : 'loading aborted'; + this.trigger(_events2.default.PLAYBACK_ERROR, { message: m }, this.name); + }; + + return HTMLImg; + }(_playback2.default); + + exports.default = HTMLImg; + + + HTMLImg.canPlay = function (resource) { + return (/\.(png|jpg|jpeg|gif|bmp|tiff|pgm|pnm|webp)(|\?.*)$/i.test(resource) + ); + }; + module.exports = exports['default']; + +/***/ }, +/* 73 */ +/***/ function(module, exports, __webpack_require__) { + + exports = module.exports = __webpack_require__(25)(); + // imports + + + // module + exports.push([module.id, "[data-html-img] {\n max-width: 100%;\n max-height: 100%; }\n", ""]); + + // exports + + +/***/ }, +/* 74 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + module.exports = __webpack_require__(75); + +/***/ }, +/* 75 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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 _utils = __webpack_require__(2); + + var _playback = __webpack_require__(31); + + var _playback2 = _interopRequireDefault(_playback); + + var _template = __webpack_require__(17); + + var _template2 = _interopRequireDefault(_template); + + var _styler = __webpack_require__(15); + + var _styler2 = _interopRequireDefault(_styler); + + var _events = __webpack_require__(5); + + var _events2 = _interopRequireDefault(_events); + + var _style = __webpack_require__(76); + + var _style2 = _interopRequireDefault(_style); + + var _error = __webpack_require__(77); + + var _error2 = _interopRequireDefault(_error); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + 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; } + + var NoOp = function (_Playback) { + _inherits(NoOp, _Playback); + + _createClass(NoOp, [{ + key: 'name', + get: function get() { + return 'no_op'; + } + }, { + key: 'template', + get: function get() { + return (0, _template2.default)(_error2.default); + } + }, { + key: 'attributes', + get: function get() { + return { 'data-no-op': '' }; + } + }]); + + function NoOp() { + _classCallCheck(this, NoOp); + + for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } + + var _this = _possibleConstructorReturn(this, _Playback.call.apply(_Playback, [this].concat(args))); + + _this._noiseFrameNum = -1; + _this._started = false; + return _this; + } + + NoOp.prototype.render = function render() { + var style = _styler2.default.getStyleFor(_style2.default); + var playbackNotSupported = this.options.playbackNotSupportedMessage || this.i18n.t('playback_not_supported'); + this.$el.html(this.template({ message: playbackNotSupported })); + this.$el.append(style); + this.trigger(_events2.default.PLAYBACK_READY, this.name); + var showForNoOp = !!(this.options.poster && this.options.poster.showForNoOp); + if (this.options.autoPlay || !showForNoOp) { + this.play(); + } + return this; + }; + + NoOp.prototype.play = function play() { + if (!this._started) { + this._started = true; + this.trigger(_events2.default.PLAYBACK_PLAY); + this._animate(); + } + }; + + NoOp.prototype._noise = function _noise() { + this._noiseFrameNum = (this._noiseFrameNum + 1) % 5; + if (this._noiseFrameNum) { + // only update noise every 5 frames to save cpu + return; + } + + var idata = this.context.createImageData(this.context.canvas.width, this.context.canvas.height); + var buffer32 = void 0; + try { + buffer32 = new Uint32Array(idata.data.buffer); + } catch (err) { + buffer32 = new Uint32Array(this.context.canvas.width * this.context.canvas.height * 4); + var data = idata.data; + for (var i = 0; i < data.length; i++) { + buffer32[i] = data[i]; + } + } + + var len = buffer32.length, + m = Math.random() * 6 + 4; + var run = 0, + color = 0; + for (var _i = 0; _i < len;) { + if (run < 0) { + run = m * Math.random(); + var p = Math.pow(Math.random(), 0.4); + color = 255 * p << 24; + } + run -= 1; + buffer32[_i++] = color; + } + this.context.putImageData(idata, 0, 0); + }; + + NoOp.prototype._loop = function _loop() { + var _this2 = this; + + if (this._stop) { + return; + } + this._noise(); + this._animationHandle = (0, _utils.requestAnimationFrame)(function () { + return _this2._loop(); + }); + }; + + NoOp.prototype.destroy = function destroy() { + if (this._animationHandle) { + (0, _utils.cancelAnimationFrame)(this._animationHandle); + this._stop = true; + } + }; + + NoOp.prototype._animate = function _animate() { + this.canvas = this.$el.find('canvas[data-no-op-canvas]')[0]; + this.context = this.canvas.getContext('2d'); + this._loop(); + }; + + return NoOp; + }(_playback2.default); + + exports.default = NoOp; + + + NoOp.canPlay = function (source) { + // eslint-disable-line no-unused-vars + return true; + }; + module.exports = exports['default']; + +/***/ }, +/* 76 */ +/***/ function(module, exports, __webpack_require__) { + + exports = module.exports = __webpack_require__(25)(); + // imports + + + // module + exports.push([module.id, "[data-no-op] {\n position: absolute;\n height: 100%;\n width: 100%;\n text-align: center; }\n\n[data-no-op] p[data-no-op-msg] {\n position: absolute;\n text-align: center;\n font-size: 25px;\n left: 0;\n right: 0;\n color: white;\n padding: 10px;\n /* center vertically */\n top: 50%;\n -webkit-transform: translateY(-50%);\n -moz-transform: translateY(-50%);\n -ms-transform: translateY(-50%);\n -o-transform: translateY(-50%);\n transform: translateY(-50%);\n max-height: 100%;\n overflow: auto; }\n\n[data-no-op] canvas[data-no-op-canvas] {\n background-color: #777;\n height: 100%;\n width: 100%; }\n", ""]); + + // exports + + +/***/ }, +/* 77 */ +/***/ function(module, exports) { + + module.exports = "<canvas data-no-op-canvas></canvas>\n<p data-no-op-msg><%=message%><p>\n"; + +/***/ }, +/* 78 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + module.exports = __webpack_require__(79); + +/***/ }, +/* 79 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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 _ui_container_plugin = __webpack_require__(80); + + var _ui_container_plugin2 = _interopRequireDefault(_ui_container_plugin); + + var _events = __webpack_require__(5); + + var _events2 = _interopRequireDefault(_events); + + var _styler = __webpack_require__(15); + + var _styler2 = _interopRequireDefault(_styler); + + var _template = __webpack_require__(17); + + var _template2 = _interopRequireDefault(_template); + + var _spinner = __webpack_require__(81); + + var _spinner2 = _interopRequireDefault(_spinner); + + var _spinner3 = __webpack_require__(82); + + var _spinner4 = _interopRequireDefault(_spinner3); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + 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; } // Copyright 2014 Globo.com Player authors. All rights reserved. + // Use of this source code is governed by a BSD-style + // license that can be found in the LICENSE file. + + var SpinnerThreeBouncePlugin = function (_UIContainerPlugin) { + _inherits(SpinnerThreeBouncePlugin, _UIContainerPlugin); + + _createClass(SpinnerThreeBouncePlugin, [{ + key: 'name', + get: function get() { + return 'spinner'; + } + }, { + key: 'attributes', + get: function get() { + return { + 'data-spinner': '', + 'class': 'spinner-three-bounce' + }; + } + }]); + + function SpinnerThreeBouncePlugin(container) { + _classCallCheck(this, SpinnerThreeBouncePlugin); + + var _this = _possibleConstructorReturn(this, _UIContainerPlugin.call(this, container)); + + _this.template = (0, _template2.default)(_spinner2.default); + _this.showTimeout = null; + _this.listenTo(_this.container, _events2.default.CONTAINER_STATE_BUFFERING, _this.onBuffering); + _this.listenTo(_this.container, _events2.default.CONTAINER_STATE_BUFFERFULL, _this.onBufferFull); + _this.listenTo(_this.container, _events2.default.CONTAINER_STOP, _this.onStop); + _this.listenTo(_this.container, _events2.default.CONTAINER_ENDED, _this.onStop); + _this.listenTo(_this.container, _events2.default.CONTAINER_ERROR, _this.onStop); + _this.render(); + return _this; + } + + SpinnerThreeBouncePlugin.prototype.onBuffering = function onBuffering() { + this.show(); + }; + + SpinnerThreeBouncePlugin.prototype.onBufferFull = function onBufferFull() { + this.hide(); + }; + + SpinnerThreeBouncePlugin.prototype.onStop = function onStop() { + this.hide(); + }; + + SpinnerThreeBouncePlugin.prototype.show = function show() { + var _this2 = this; + + if (this.showTimeout === null) { + this.showTimeout = setTimeout(function () { + return _this2.$el.show(); + }, 300); + } + }; + + SpinnerThreeBouncePlugin.prototype.hide = function hide() { + if (this.showTimeout !== null) { + clearTimeout(this.showTimeout); + this.showTimeout = null; + } + this.$el.hide(); + }; + + SpinnerThreeBouncePlugin.prototype.render = function render() { + this.$el.html(this.template()); + var style = _styler2.default.getStyleFor(_spinner4.default); + this.container.$el.append(style); + this.container.$el.append(this.$el); + this.$el.hide(); + if (this.container.buffering) { + this.onBuffering(); + } + return this; + }; + + return SpinnerThreeBouncePlugin; + }(_ui_container_plugin2.default); + + exports.default = SpinnerThreeBouncePlugin; + module.exports = exports['default']; + +/***/ }, +/* 80 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + var _utils = __webpack_require__(2); + + var _ui_object = __webpack_require__(18); + + var _ui_object2 = _interopRequireDefault(_ui_object); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + 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; } // Copyright 2014 Globo.com Player authors. All rights reserved. + // Use of this source code is governed by a BSD-style + // license that can be found in the LICENSE file. + + /** + * The base class for an ui container plugin + * @class UIContainerPlugin + * @constructor + * @extends UIObject + * @module base + */ + var UIContainerPlugin = function (_UIObject) { + _inherits(UIContainerPlugin, _UIObject); + + function UIContainerPlugin(container) { + _classCallCheck(this, UIContainerPlugin); + + var _this = _possibleConstructorReturn(this, _UIObject.call(this, container.options)); + + _this.container = container; + _this.enabled = true; + _this.bindEvents(); + return _this; + } + + UIContainerPlugin.prototype.enable = function enable() { + if (!this.enabled) { + this.bindEvents(); + this.$el.show(); + this.enabled = true; + } + }; + + UIContainerPlugin.prototype.disable = function disable() { + this.stopListening(); + this.$el.hide(); + this.enabled = false; + }; + + UIContainerPlugin.prototype.bindEvents = function bindEvents() {}; + + UIContainerPlugin.prototype.destroy = function destroy() { + this.remove(); + }; + + return UIContainerPlugin; + }(_ui_object2.default); + + exports.default = UIContainerPlugin; + + + UIContainerPlugin.extend = function (properties) { + return (0, _utils.extend)(UIContainerPlugin, properties); + }; + + UIContainerPlugin.type = 'container'; + module.exports = exports['default']; + +/***/ }, +/* 81 */ +/***/ function(module, exports) { + + module.exports = "<div data-bounce1></div><div data-bounce2></div><div data-bounce3></div>\n"; + +/***/ }, +/* 82 */ +/***/ function(module, exports, __webpack_require__) { + + exports = module.exports = __webpack_require__(25)(); + // imports + + + // module + exports.push([module.id, ".spinner-three-bounce[data-spinner] {\n position: absolute;\n margin: 0 auto;\n width: 70px;\n text-align: center;\n z-index: 999;\n left: 0;\n right: 0;\n margin-left: auto;\n margin-right: auto;\n /* center vertically */\n top: 50%;\n -webkit-transform: translateY(-50%);\n -moz-transform: translateY(-50%);\n -ms-transform: translateY(-50%);\n -o-transform: translateY(-50%);\n transform: translateY(-50%); }\n .spinner-three-bounce[data-spinner] > div {\n width: 18px;\n height: 18px;\n background-color: #FFFFFF;\n border-radius: 100%;\n display: inline-block;\n -webkit-animation: bouncedelay 1.4s infinite ease-in-out;\n -moz-animation: bouncedelay 1.4s infinite ease-in-out;\n animation: bouncedelay 1.4s infinite ease-in-out;\n /* Prevent first frame from flickering when animation starts */\n -webkit-animation-fill-mode: both;\n -moz-animation-fill-mode: both;\n animation-fill-mode: both; }\n .spinner-three-bounce[data-spinner] [data-bounce1] {\n -webkit-animation-delay: -0.32s;\n -moz-animation-delay: -0.32s;\n animation-delay: -0.32s; }\n .spinner-three-bounce[data-spinner] [data-bounce2] {\n -webkit-animation-delay: -0.16s;\n -moz-animation-delay: -0.16s;\n animation-delay: -0.16s; }\n\n@-webkit-keyframes bouncedelay {\n 0%, 80%, 100% {\n -webkit-transform: scale(0); }\n 40% {\n -webkit-transform: scale(1); } }\n\n@-moz-keyframes bouncedelay {\n 0%, 80%, 100% {\n -moz-transform: scale(0); }\n 40% {\n -moz-transform: scale(1); } }\n\n@keyframes bouncedelay {\n 0%, 80%, 100% {\n -webkit-transform: scale(0);\n -moz-transform: scale(0);\n -ms-transform: scale(0);\n -o-transform: scale(0);\n transform: scale(0); }\n 40% {\n -webkit-transform: scale(1);\n -moz-transform: scale(1);\n -ms-transform: scale(1);\n -o-transform: scale(1);\n transform: scale(1); } }\n", ""]); + + // exports + + +/***/ }, +/* 83 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + module.exports = __webpack_require__(84); + +/***/ }, +/* 84 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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 _container_plugin = __webpack_require__(85); + + var _container_plugin2 = _interopRequireDefault(_container_plugin); + + var _events = __webpack_require__(5); + + var _events2 = _interopRequireDefault(_events); + + var _clapprZepto = __webpack_require__(16); + + var _clapprZepto2 = _interopRequireDefault(_clapprZepto); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + 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; } // Copyright 2014 Globo.com Player authors. All rights reserved. + // Use of this source code is governed by a BSD-style + // license that can be found in the LICENSE file. + + var StatsPlugin = function (_ContainerPlugin) { + _inherits(StatsPlugin, _ContainerPlugin); + + _createClass(StatsPlugin, [{ + key: 'name', + get: function get() { + return 'stats'; + } + }]); + + function StatsPlugin(container) { + _classCallCheck(this, StatsPlugin); + + var _this = _possibleConstructorReturn(this, _ContainerPlugin.call(this, container)); + + _this.setInitialAttrs(); + _this.reportInterval = _this.options.reportInterval || 5000; + _this.state = 'IDLE'; + return _this; + } + + StatsPlugin.prototype.bindEvents = function bindEvents() { + this.listenTo(this.container.playback, _events2.default.PLAYBACK_PLAY, this.onPlay); + this.listenTo(this.container, _events2.default.CONTAINER_STOP, this.onStop); + this.listenTo(this.container, _events2.default.CONTAINER_ENDED, this.onStop); + this.listenTo(this.container, _events2.default.CONTAINER_DESTROYED, this.onStop); + this.listenTo(this.container, _events2.default.CONTAINER_STATE_BUFFERING, this.onBuffering); + this.listenTo(this.container, _events2.default.CONTAINER_STATE_BUFFERFULL, this.onBufferFull); + this.listenTo(this.container, _events2.default.CONTAINER_STATS_ADD, this.onStatsAdd); + this.listenTo(this.container, _events2.default.CONTAINER_BITRATE, this.onStatsAdd); + this.listenTo(this.container.playback, _events2.default.PLAYBACK_STATS_ADD, this.onStatsAdd); + }; + + StatsPlugin.prototype.setInitialAttrs = function setInitialAttrs() { + this.firstPlay = true; + this.startupTime = 0; + this.rebufferingTime = 0; + this.watchingTime = 0; + this.rebuffers = 0; + this.externalMetrics = {}; + }; + + StatsPlugin.prototype.onPlay = function onPlay() { + this.state = 'PLAYING'; + this.watchingTimeInit = Date.now(); + if (!this.intervalId) { + this.intervalId = setInterval(this.report.bind(this), this.reportInterval); + } + }; + + StatsPlugin.prototype.onStop = function onStop() { + clearInterval(this.intervalId); + this.report(); + this.intervalId = undefined; + this.state = 'STOPPED'; + }; + + StatsPlugin.prototype.onBuffering = function onBuffering() { + if (this.firstPlay) { + this.startupTimeInit = Date.now(); + } else { + this.rebufferingTimeInit = Date.now(); + } + this.state = 'BUFFERING'; + this.rebuffers++; + }; + + StatsPlugin.prototype.onBufferFull = function onBufferFull() { + if (this.firstPlay && this.startupTimeInit) { + this.firstPlay = false; + this.startupTime = Date.now() - this.startupTimeInit; + this.watchingTimeInit = Date.now(); + } else if (this.rebufferingTimeInit) { + this.rebufferingTime += this.getRebufferingTime(); + } + this.rebufferingTimeInit = undefined; + this.state = 'PLAYING'; + }; + + StatsPlugin.prototype.getRebufferingTime = function getRebufferingTime() { + return Date.now() - this.rebufferingTimeInit; + }; + + StatsPlugin.prototype.getWatchingTime = function getWatchingTime() { + var totalTime = Date.now() - this.watchingTimeInit; + return totalTime - this.rebufferingTime; + }; + + StatsPlugin.prototype.isRebuffering = function isRebuffering() { + return !!this.rebufferingTimeInit; + }; + + StatsPlugin.prototype.onStatsAdd = function onStatsAdd(metric) { + _clapprZepto2.default.extend(this.externalMetrics, metric); + }; + + StatsPlugin.prototype.getStats = function getStats() { + var metrics = { + startupTime: this.startupTime, + rebuffers: this.rebuffers, + rebufferingTime: this.isRebuffering() ? this.rebufferingTime + this.getRebufferingTime() : this.rebufferingTime, + watchingTime: this.isRebuffering() ? this.getWatchingTime() - this.getRebufferingTime() : this.getWatchingTime() + }; + _clapprZepto2.default.extend(metrics, this.externalMetrics); + return metrics; + }; + + StatsPlugin.prototype.report = function report() { + this.container.statsReport(this.getStats()); + }; + + return StatsPlugin; + }(_container_plugin2.default); + + exports.default = StatsPlugin; + module.exports = exports['default']; + +/***/ }, +/* 85 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + var _base_object = __webpack_require__(4); + + var _base_object2 = _interopRequireDefault(_base_object); + + var _utils = __webpack_require__(2); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + 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; } + + /** + * The base class for a container plugin + * @class ContainerPlugin + * @constructor + * @extends UIObject + * @module base + */ + var ContainerPlugin = function (_BaseObject) { + _inherits(ContainerPlugin, _BaseObject); + + function ContainerPlugin(container) { + _classCallCheck(this, ContainerPlugin); + + var _this = _possibleConstructorReturn(this, _BaseObject.call(this, container.options)); + + _this.container = container; + _this.enabled = true; + _this.bindEvents(); + return _this; + } + + ContainerPlugin.prototype.enable = function enable() { + if (!this.enabled) { + this.bindEvents(); + this.enabled = true; + } + }; + + ContainerPlugin.prototype.disable = function disable() { + if (this.enabled) { + this.stopListening(); + this.enabled = false; + } + }; + + ContainerPlugin.prototype.bindEvents = function bindEvents() {}; + + ContainerPlugin.prototype.destroy = function destroy() { + this.stopListening(); + }; + + return ContainerPlugin; + }(_base_object2.default); + + exports.default = ContainerPlugin; + + + ContainerPlugin.extend = function (properties) { + return (0, _utils.extend)(ContainerPlugin, properties); + }; + + ContainerPlugin.type = 'container'; + module.exports = exports['default']; + +/***/ }, +/* 86 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + module.exports = __webpack_require__(87); + +/***/ }, +/* 87 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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 _ui_container_plugin = __webpack_require__(80); + + var _ui_container_plugin2 = _interopRequireDefault(_ui_container_plugin); + + var _events = __webpack_require__(5); + + var _events2 = _interopRequireDefault(_events); + + var _styler = __webpack_require__(15); + + var _styler2 = _interopRequireDefault(_styler); + + var _template = __webpack_require__(17); + + var _template2 = _interopRequireDefault(_template); + + var _watermark = __webpack_require__(88); + + var _watermark2 = _interopRequireDefault(_watermark); + + var _watermark3 = __webpack_require__(89); + + var _watermark4 = _interopRequireDefault(_watermark3); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + 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; } // Copyright 2014 Globo.com Player authors. All rights reserved. + // Use of this source code is governed by a BSD-style + // license that can be found in the LICENSE file. + + var WaterMarkPlugin = function (_UIContainerPlugin) { + _inherits(WaterMarkPlugin, _UIContainerPlugin); + + _createClass(WaterMarkPlugin, [{ + key: 'name', + get: function get() { + return 'watermark'; + } + }, { + key: 'template', + get: function get() { + return (0, _template2.default)(_watermark4.default); + } + }]); + + function WaterMarkPlugin(container) { + _classCallCheck(this, WaterMarkPlugin); + + var _this = _possibleConstructorReturn(this, _UIContainerPlugin.call(this, container)); + + _this.configure(); + return _this; + } + + WaterMarkPlugin.prototype.bindEvents = function bindEvents() { + this.listenTo(this.container, _events2.default.CONTAINER_PLAY, this.onPlay); + this.listenTo(this.container, _events2.default.CONTAINER_STOP, this.onStop); + this.listenTo(this.container, _events2.default.CONTAINER_OPTIONS_CHANGE, this.configure); + }; + + WaterMarkPlugin.prototype.configure = function configure() { + this.position = this.options.position || 'bottom-right'; + if (this.options.watermark) { + this.imageUrl = this.options.watermark; + this.imageLink = this.options.watermarkLink; + this.render(); + } else { + this.$el.remove(); + } + }; + + WaterMarkPlugin.prototype.onPlay = function onPlay() { + if (!this.hidden) this.$el.show(); + }; + + WaterMarkPlugin.prototype.onStop = function onStop() { + this.$el.hide(); + }; + + WaterMarkPlugin.prototype.render = function render() { + this.$el.hide(); + var templateOptions = { position: this.position, imageUrl: this.imageUrl, imageLink: this.imageLink }; + this.$el.html(this.template(templateOptions)); + var style = _styler2.default.getStyleFor(_watermark2.default); + this.container.$el.append(style); + this.container.$el.append(this.$el); + return this; + }; + + return WaterMarkPlugin; + }(_ui_container_plugin2.default); + + exports.default = WaterMarkPlugin; + module.exports = exports['default']; + +/***/ }, +/* 88 */ +/***/ function(module, exports, __webpack_require__) { + + exports = module.exports = __webpack_require__(25)(); + // imports + + + // module + exports.push([module.id, "[data-watermark] {\n position: absolute;\n min-width: 70px;\n max-width: 200px;\n width: 12%;\n text-align: center;\n z-index: 10; }\n\n[data-watermark] a {\n outline: none;\n cursor: pointer; }\n\n[data-watermark] img {\n max-width: 100%; }\n\n[data-watermark-bottom-left] {\n bottom: 10px;\n left: 10px; }\n\n[data-watermark-bottom-right] {\n bottom: 10px;\n right: 42px; }\n\n[data-watermark-top-left] {\n top: 10px;\n left: 10px; }\n\n[data-watermark-top-right] {\n top: 10px;\n right: 37px; }\n", ""]); + + // exports + + +/***/ }, +/* 89 */ +/***/ function(module, exports) { + + module.exports = "<div data-watermark data-watermark-<%=position %>>\n<% if(typeof imageLink !== 'undefined') { %>\n<a target=_blank href=\"<%= imageLink %>\">\n<% } %>\n<img src=\"<%= imageUrl %>\">\n<% if(typeof imageLink !== 'undefined') { %>\n</a>\n<% } %>\n</div>\n"; + +/***/ }, +/* 90 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + module.exports = __webpack_require__(91); + +/***/ }, +/* 91 */ +/***/ function(module, exports, __webpack_require__) { + + /* WEBPACK VAR INJECTION */(function(process) {'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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 _ui_container_plugin = __webpack_require__(80); + + var _ui_container_plugin2 = _interopRequireDefault(_ui_container_plugin); + + var _events = __webpack_require__(5); + + var _events2 = _interopRequireDefault(_events); + + var _styler = __webpack_require__(15); + + var _styler2 = _interopRequireDefault(_styler); + + var _template = __webpack_require__(17); + + var _template2 = _interopRequireDefault(_template); + + var _playback = __webpack_require__(31); + + var _playback2 = _interopRequireDefault(_playback); + + var _poster = __webpack_require__(92); + + var _poster2 = _interopRequireDefault(_poster); + + var _poster3 = __webpack_require__(93); + + var _poster4 = _interopRequireDefault(_poster3); + + var _play = __webpack_require__(37); + + var _play2 = _interopRequireDefault(_play); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + 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; } //Copyright 2014 Globo.com Player authors. All rights reserved. + // Use of this source code is governed by a BSD-style + // license that can be found in the LICENSE file. + + var PosterPlugin = function (_UIContainerPlugin) { + _inherits(PosterPlugin, _UIContainerPlugin); + + _createClass(PosterPlugin, [{ + key: 'name', + get: function get() { + return 'poster'; + } + }, { + key: 'template', + get: function get() { + return (0, _template2.default)(_poster4.default); + } + }, { + key: 'shouldRender', + get: function get() { + var showForNoOp = !!(this.options.poster && this.options.poster.showForNoOp); + return this.container.playback.name !== 'html_img' && (this.container.playback.getPlaybackType() !== _playback2.default.NO_OP || showForNoOp); + } + }, { + key: 'attributes', + get: function get() { + return { + 'class': 'player-poster', + 'data-poster': '' + }; + } + }, { + key: 'events', + get: function get() { + return { + 'click': 'clicked' + }; + } + }, { + key: 'showOnVideoEnd', + get: function get() { + return !this.options.poster || this.options.poster.showOnVideoEnd || this.options.poster.showOnVideoEnd === undefined; + } + }]); + + function PosterPlugin(container) { + _classCallCheck(this, PosterPlugin); + + var _this = _possibleConstructorReturn(this, _UIContainerPlugin.call(this, container)); + + _this.hasStartedPlaying = false; + _this.playRequested = false; + _this.render(); + process.nextTick(function () { + return _this.update(); + }); + return _this; + } + + PosterPlugin.prototype.bindEvents = function bindEvents() { + this.listenTo(this.container, _events2.default.CONTAINER_STOP, this.onStop); + this.listenTo(this.container, _events2.default.CONTAINER_PLAY, this.onPlay); + this.listenTo(this.container, _events2.default.CONTAINER_STATE_BUFFERING, this.update); + this.listenTo(this.container, _events2.default.CONTAINER_STATE_BUFFERFULL, this.update); + this.listenTo(this.container, _events2.default.CONTAINER_OPTIONS_CHANGE, this.render); + this.showOnVideoEnd && this.listenTo(this.container, _events2.default.CONTAINER_ENDED, this.onStop); + }; + + PosterPlugin.prototype.stopListening = function stopListening() { + _UIContainerPlugin.prototype.stopListening.call(this); + }; + + PosterPlugin.prototype.onPlay = function onPlay() { + this.hasStartedPlaying = true; + this.update(); + }; + + PosterPlugin.prototype.onStop = function onStop() { + this.hasStartedPlaying = false; + this.playRequested = false; + this.update(); + }; + + PosterPlugin.prototype.showPlayButton = function showPlayButton(show) { + if (show && (!this.options.chromeless || this.options.allowUserInteraction)) { + this.$playButton.show(); + this.$el.addClass('clickable'); + } else { + this.$playButton.hide(); + this.$el.removeClass('clickable'); + } + }; + + PosterPlugin.prototype.clicked = function clicked() { + if (!this.options.chromeless || this.options.allowUserInteraction) { + this.playRequested = true; + this.update(); + this.container.play(); + } + return false; + }; + + PosterPlugin.prototype.shouldHideOnPlay = function shouldHideOnPlay() { + // Audio broadcasts should keep the poster up; video should hide poster while playing. + return !this.container.playback.isAudioOnly; + }; + + PosterPlugin.prototype.update = function update() { + if (!this.shouldRender) { + return; + } + var showPlayButton = !this.playRequested && !this.hasStartedPlaying && !this.container.buffering; + this.showPlayButton(showPlayButton); + if (!this.hasStartedPlaying) { + this.container.disableMediaControl(); + this.$el.show(); + } else { + this.container.enableMediaControl(); + if (this.shouldHideOnPlay()) { + this.$el.hide(); + } + } + }; + + PosterPlugin.prototype.render = function render() { + if (!this.shouldRender) { + return; + } + var style = _styler2.default.getStyleFor(_poster2.default, { baseUrl: this.options.baseUrl }); + this.$el.html(this.template()); + this.$el.append(style); + if (this.options.poster) { + var posterUrl = this.options.poster.url || this.options.poster; + this.$el.css({ 'background-image': 'url(' + posterUrl + ')' }); + } + this.container.$el.append(this.el); + this.$playWrapper = this.$el.find('.play-wrapper'); + this.$playWrapper.append(_play2.default); + this.$playButton = this.$playWrapper.find('svg'); + this.$playButton.addClass('poster-icon'); + this.$playButton.attr('data-poster', ''); + + var buttonsColor = this.options.mediacontrol && this.options.mediacontrol.buttons; + if (buttonsColor) { + this.$el.find('svg path').css('fill', buttonsColor); + } + + if (this.options.mediacontrol && this.options.mediacontrol.buttons) { + buttonsColor = this.options.mediacontrol.buttons; + this.$playButton.css('color', buttonsColor); + } + this.update(); + return this; + }; + + return PosterPlugin; + }(_ui_container_plugin2.default); + + exports.default = PosterPlugin; + module.exports = exports['default']; + /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(29))) + +/***/ }, +/* 92 */ +/***/ function(module, exports, __webpack_require__) { + + exports = module.exports = __webpack_require__(25)(); + // imports + + + // module + exports.push([module.id, ".player-poster[data-poster] {\n display: -webkit-box;\n display: -moz-box;\n display: box;\n display: -webkit-flex;\n display: -moz-flex;\n display: -ms-flexbox;\n display: flex;\n -webkit-box-pack: center;\n -moz-box-pack: center;\n box-pack: center;\n -webkit-justify-content: center;\n -moz-justify-content: center;\n -ms-justify-content: center;\n -o-justify-content: center;\n justify-content: center;\n -ms-flex-pack: center;\n -webkit-box-align: center;\n -moz-box-align: center;\n box-align: center;\n -webkit-align-items: center;\n -moz-align-items: center;\n -ms-align-items: center;\n -o-align-items: center;\n align-items: center;\n -ms-flex-align: center;\n position: absolute;\n height: 100%;\n width: 100%;\n z-index: 998;\n top: 0;\n left: 0;\n background-color: #000;\n background-size: cover;\n background-repeat: no-repeat;\n background-position: 50% 50%; }\n .player-poster[data-poster].clickable {\n cursor: pointer; }\n .player-poster[data-poster]:hover .play-wrapper[data-poster] {\n opacity: 1; }\n .player-poster[data-poster] .play-wrapper[data-poster] {\n width: 100%;\n height: 25%;\n margin: 0 auto;\n opacity: 0.75;\n -webkit-transition: opacity 0.1s ease;\n -moz-transition: opacity 0.1s ease;\n transition: opacity 0.1s ease; }\n .player-poster[data-poster] .play-wrapper[data-poster] svg {\n height: 100%; }\n .player-poster[data-poster] .play-wrapper[data-poster] svg path {\n fill: #fff; }\n", ""]); + + // exports + + +/***/ }, +/* 93 */ +/***/ function(module, exports) { + + module.exports = "<div class=\"play-wrapper\" data-poster></div>\n"; + +/***/ }, +/* 94 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + module.exports = __webpack_require__(95); + +/***/ }, +/* 95 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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 _container_plugin = __webpack_require__(85); + + var _container_plugin2 = _interopRequireDefault(_container_plugin); + + var _events = __webpack_require__(5); + + var _events2 = _interopRequireDefault(_events); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + 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; } // Copyright 2014 Globo.com Player authors. All rights reserved. + // Use of this source code is governed by a BSD-style + // license that can be found in the LICENSE file. + + var GoogleAnalytics = function (_ContainerPlugin) { + _inherits(GoogleAnalytics, _ContainerPlugin); + + _createClass(GoogleAnalytics, [{ + key: 'name', + get: function get() { + return 'google_analytics'; + } + }]); + + function GoogleAnalytics(container) { + _classCallCheck(this, GoogleAnalytics); + + var _this = _possibleConstructorReturn(this, _ContainerPlugin.call(this, container)); + + if (_this.container.options.gaAccount) { + _this.account = _this.container.options.gaAccount; + _this.trackerName = _this.container.options.gaTrackerName ? _this.container.options.gaTrackerName + '.' : 'Clappr.'; + _this.domainName = _this.container.options.gaDomainName; + _this.currentHDState = undefined; + _this.embedScript(); + } + return _this; + } + + GoogleAnalytics.prototype.embedScript = function embedScript() { + var _this2 = this; + + if (!window._gat) { + var script = document.createElement('script'); + script.setAttribute('type', 'text/javascript'); + script.setAttribute('async', 'async'); + script.setAttribute('src', '//www.google-analytics.com/ga.js'); + script.onload = function () { + return _this2.addEventListeners(); + }; + document.body.appendChild(script); + } else { + this.addEventListeners(); + } + }; + + GoogleAnalytics.prototype.addEventListeners = function addEventListeners() { + var _this3 = this; + + if (this.container) { + this.listenTo(this.container, _events2.default.CONTAINER_READY, this.onReady); + this.listenTo(this.container, _events2.default.CONTAINER_PLAY, this.onPlay); + this.listenTo(this.container, _events2.default.CONTAINER_STOP, this.onStop); + this.listenTo(this.container, _events2.default.CONTAINER_PAUSE, this.onPause); + this.listenTo(this.container, _events2.default.CONTAINER_ENDED, this.onEnded); + this.listenTo(this.container, _events2.default.CONTAINER_STATE_BUFFERING, this.onBuffering); + this.listenTo(this.container, _events2.default.CONTAINER_STATE_BUFFERFULL, this.onBufferFull); + this.listenTo(this.container, _events2.default.CONTAINER_ERROR, this.onError); + this.listenTo(this.container, _events2.default.CONTAINER_PLAYBACKSTATE, this.onPlaybackChanged); + this.listenTo(this.container, _events2.default.CONTAINER_VOLUME, function (event) { + return _this3.onVolumeChanged(event); + }); + this.listenTo(this.container, _events2.default.CONTAINER_SEEK, function (event) { + return _this3.onSeek(event); + }); + this.listenTo(this.container, _events2.default.CONTAINER_FULL_SCREEN, this.onFullscreen); + this.listenTo(this.container, _events2.default.CONTAINER_HIGHDEFINITIONUPDATE, this.onHD); + this.listenTo(this.container, _events2.default.CONTAINER_PLAYBACKDVRSTATECHANGED, this.onDVR); + } + _gaq.push([this.trackerName + '_setAccount', this.account]); + if (this.domainName) _gaq.push([this.trackerName + '_setDomainName', this.domainName]); + }; + + GoogleAnalytics.prototype.onReady = function onReady() { + this.push(['Video', 'Playback', this.container.playback.name]); + }; + + GoogleAnalytics.prototype.onPlay = function onPlay() { + this.push(['Video', 'Play', this.container.playback.src]); + }; + + GoogleAnalytics.prototype.onStop = function onStop() { + this.push(['Video', 'Stop', this.container.playback.src]); + }; + + GoogleAnalytics.prototype.onEnded = function onEnded() { + this.push(['Video', 'Ended', this.container.playback.src]); + }; + + GoogleAnalytics.prototype.onBuffering = function onBuffering() { + this.push(['Video', 'Buffering', this.container.playback.src]); + }; + + GoogleAnalytics.prototype.onBufferFull = function onBufferFull() { + this.push(['Video', 'Bufferfull', this.container.playback.src]); + }; + + GoogleAnalytics.prototype.onError = function onError() { + this.push(['Video', 'Error', this.container.playback.src]); + }; + + GoogleAnalytics.prototype.onHD = function onHD(isHD) { + var status = isHD ? 'ON' : 'OFF'; + if (status !== this.currentHDState) { + this.currentHDState = status; + this.push(['Video', 'HD - ' + status, this.container.playback.src]); + } + }; + + GoogleAnalytics.prototype.onPlaybackChanged = function onPlaybackChanged(playbackState) { + if (playbackState.type !== null) { + this.push(['Video', 'Playback Type - ' + playbackState.type, this.container.playback.src]); + } + }; + + GoogleAnalytics.prototype.onDVR = function onDVR(dvrInUse) { + var status = dvrInUse ? 'ON' : 'OFF'; + this.push(['Interaction', 'DVR - ' + status, this.container.playback.src]); + }; + + GoogleAnalytics.prototype.onPause = function onPause() { + this.push(['Video', 'Pause', this.container.playback.src]); + }; + + GoogleAnalytics.prototype.onSeek = function onSeek() { + this.push(['Video', 'Seek', this.container.playback.src]); + }; + + GoogleAnalytics.prototype.onVolumeChanged = function onVolumeChanged() { + this.push(['Interaction', 'Volume', this.container.playback.src]); + }; + + GoogleAnalytics.prototype.onFullscreen = function onFullscreen() { + this.push(['Interaction', 'Fullscreen', this.container.playback.src]); + }; + + GoogleAnalytics.prototype.push = function push(array) { + var res = [this.trackerName + '_trackEvent'].concat(array); + _gaq.push(res); + }; + + return GoogleAnalytics; + }(_container_plugin2.default); + + exports.default = GoogleAnalytics; + module.exports = exports['default']; + +/***/ }, +/* 96 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + module.exports = __webpack_require__(97); + +/***/ }, +/* 97 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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 _container_plugin = __webpack_require__(85); + + var _container_plugin2 = _interopRequireDefault(_container_plugin); + + var _events = __webpack_require__(5); + + var _events2 = _interopRequireDefault(_events); + + var _playback = __webpack_require__(31); + + var _playback2 = _interopRequireDefault(_playback); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + 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; } //Copyright 2014 Globo.com Player authors. All rights reserved. + // Use of this source code is governed by a BSD-style + // license that can be found in the LICENSE file. + + var ClickToPausePlugin = function (_ContainerPlugin) { + _inherits(ClickToPausePlugin, _ContainerPlugin); + + _createClass(ClickToPausePlugin, [{ + key: 'name', + get: function get() { + return 'click_to_pause'; + } + }]); + + function ClickToPausePlugin(container) { + _classCallCheck(this, ClickToPausePlugin); + + return _possibleConstructorReturn(this, _ContainerPlugin.call(this, container)); + } + + ClickToPausePlugin.prototype.bindEvents = function bindEvents() { + this.listenTo(this.container, _events2.default.CONTAINER_CLICK, this.click); + this.listenTo(this.container, _events2.default.CONTAINER_SETTINGSUPDATE, this.settingsUpdate); + }; + + ClickToPausePlugin.prototype.click = function click() { + if (this.container.getPlaybackType() !== _playback2.default.LIVE || this.container.isDvrEnabled()) { + if (this.container.isPlaying()) { + this.container.pause(); + } else { + this.container.play(); + } + } + }; + + ClickToPausePlugin.prototype.settingsUpdate = function settingsUpdate() { + this.container.$el.removeClass('pointer-enabled'); + if (this.container.getPlaybackType() !== _playback2.default.LIVE || this.container.isDvrEnabled()) { + this.container.$el.addClass('pointer-enabled'); + } + }; + + return ClickToPausePlugin; + }(_container_plugin2.default); + + exports.default = ClickToPausePlugin; + module.exports = exports['default']; + +/***/ }, +/* 98 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + module.exports = __webpack_require__(99); + +/***/ }, +/* 99 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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 _ui_core_plugin = __webpack_require__(100); + + var _ui_core_plugin2 = _interopRequireDefault(_ui_core_plugin); + + var _template = __webpack_require__(17); + + var _template2 = _interopRequireDefault(_template); + + var _playback = __webpack_require__(31); + + var _playback2 = _interopRequireDefault(_playback); + + var _styler = __webpack_require__(15); + + var _styler2 = _interopRequireDefault(_styler); + + var _events = __webpack_require__(5); + + var _events2 = _interopRequireDefault(_events); + + var _dvr_controls = __webpack_require__(101); + + var _dvr_controls2 = _interopRequireDefault(_dvr_controls); + + var _index = __webpack_require__(102); + + var _index2 = _interopRequireDefault(_index); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + 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; } + + var DVRControls = function (_UICorePlugin) { + _inherits(DVRControls, _UICorePlugin); + + _createClass(DVRControls, [{ + key: 'template', + get: function get() { + return (0, _template2.default)(_index2.default); + } + }, { + key: 'name', + get: function get() { + return 'dvr_controls'; + } + }, { + key: 'events', + get: function get() { + return { + 'click .live-button': 'click' + }; + } + }, { + key: 'attributes', + get: function get() { + return { + 'class': 'dvr-controls', + 'data-dvr-controls': '' + }; + } + }]); + + function DVRControls(core) { + _classCallCheck(this, DVRControls); + + var _this = _possibleConstructorReturn(this, _UICorePlugin.call(this, core)); + + _this.settingsUpdate(); + return _this; + } + + DVRControls.prototype.bindEvents = function bindEvents() { + this.listenTo(this.core.mediaControl, _events2.default.MEDIACONTROL_CONTAINERCHANGED, this.containerChanged); + this.listenTo(this.core.mediaControl, _events2.default.MEDIACONTROL_RENDERED, this.settingsUpdate); + this.listenTo(this.core, _events2.default.CORE_OPTIONS_CHANGE, this.render); + if (this.core.getCurrentContainer()) { + this.listenToOnce(this.core.getCurrentContainer(), _events2.default.CONTAINER_TIMEUPDATE, this.render); + this.listenTo(this.core.getCurrentContainer(), _events2.default.CONTAINER_PLAYBACKDVRSTATECHANGED, this.dvrChanged); + } + }; + + DVRControls.prototype.containerChanged = function containerChanged() { + this.stopListening(); + this.bindEvents(); + }; + + DVRControls.prototype.dvrChanged = function dvrChanged(dvrEnabled) { + this.settingsUpdate(); + this.core.mediaControl.$el.addClass('live'); + if (dvrEnabled) { + this.core.mediaControl.$el.addClass('dvr'); + this.core.mediaControl.$el.find('.media-control-indicator[data-position], .media-control-indicator[data-duration]').hide(); + } else { + this.core.mediaControl.$el.removeClass('dvr'); + } + }; + + DVRControls.prototype.click = function click() { + var mediaControl = this.core.mediaControl; + var container = mediaControl.container; + if (!container.isPlaying()) { + container.play(); + } + if (mediaControl.$el.hasClass('dvr')) { + container.seek(container.getDuration()); + } + }; + + DVRControls.prototype.settingsUpdate = function settingsUpdate() { + var _this2 = this; + + this.stopListening(); + if (this.shouldRender()) { + this.render(); + this.$el.click(function () { + return _this2.click(); + }); + } + this.bindEvents(); + }; + + DVRControls.prototype.shouldRender = function shouldRender() { + var useDvrControls = this.core.options.useDvrControls === undefined || !!this.core.options.useDvrControls; + return useDvrControls && this.core.getPlaybackType() === _playback2.default.LIVE; + }; + + DVRControls.prototype.render = function render() { + this.style = this.style || _styler2.default.getStyleFor(_dvr_controls2.default, { baseUrl: this.core.options.baseUrl }); + this.$el.html(this.template({ + live: this.core.i18n.t('live'), + backToLive: this.core.i18n.t('back_to_live') + })); + this.$el.append(this.style); + if (this.shouldRender()) { + this.core.mediaControl.$el.addClass('live'); + this.core.mediaControl.$('.media-control-left-panel[data-media-control]').append(this.$el); + } + return this; + }; + + return DVRControls; + }(_ui_core_plugin2.default); + + exports.default = DVRControls; + module.exports = exports['default']; + +/***/ }, +/* 100 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + var _utils = __webpack_require__(2); + + var _ui_object = __webpack_require__(18); + + var _ui_object2 = _interopRequireDefault(_ui_object); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + 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; } + + var UICorePlugin = function (_UIObject) { + _inherits(UICorePlugin, _UIObject); + + function UICorePlugin(core) { + _classCallCheck(this, UICorePlugin); + + var _this = _possibleConstructorReturn(this, _UIObject.call(this, core.options)); + + _this.core = core; + _this.enabled = true; + _this.bindEvents(); + _this.render(); + return _this; + } + + UICorePlugin.prototype.bindEvents = function bindEvents() {}; + + UICorePlugin.prototype.getExternalInterface = function getExternalInterface() { + return {}; + }; + + UICorePlugin.prototype.enable = function enable() { + if (!this.enabled) { + this.bindEvents(); + this.$el.show(); + this.enabled = true; + } + }; + + UICorePlugin.prototype.disable = function disable() { + this.stopListening(); + this.$el.hide(); + this.enabled = false; + }; + + UICorePlugin.prototype.destroy = function destroy() { + this.remove(); + }; + + UICorePlugin.prototype.render = function render() { + return this; + }; + + return UICorePlugin; + }(_ui_object2.default); + + exports.default = UICorePlugin; + + + UICorePlugin.extend = function (properties) { + return (0, _utils.extend)(UICorePlugin, properties); + }; + + UICorePlugin.type = 'core'; + module.exports = exports['default']; + +/***/ }, +/* 101 */ +/***/ function(module, exports, __webpack_require__) { + + exports = module.exports = __webpack_require__(25)(); + // imports + + + // module + exports.push([module.id, ".dvr-controls[data-dvr-controls] {\n display: inline-block;\n float: left;\n color: #fff;\n line-height: 32px;\n font-size: 10px;\n font-weight: bold;\n margin-left: 6px; }\n .dvr-controls[data-dvr-controls] .live-info {\n cursor: default;\n font-family: \"Roboto\", \"Open Sans\", Arial, sans-serif;\n text-transform: uppercase; }\n .dvr-controls[data-dvr-controls] .live-info:before {\n content: \"\";\n display: inline-block;\n position: relative;\n width: 7px;\n height: 7px;\n border-radius: 3.5px;\n margin-right: 3.5px;\n background-color: #ff0101; }\n .dvr-controls[data-dvr-controls] .live-info.disabled {\n opacity: 0.3; }\n .dvr-controls[data-dvr-controls] .live-info.disabled:before {\n background-color: #fff; }\n .dvr-controls[data-dvr-controls] .live-button {\n cursor: pointer;\n outline: none;\n display: none;\n border: 0;\n color: #fff;\n background-color: transparent;\n height: 32px;\n padding: 0;\n opacity: 0.7;\n font-family: \"Roboto\", \"Open Sans\", Arial, sans-serif;\n text-transform: uppercase;\n -webkit-transition: all 0.1s ease;\n -moz-transition: all 0.1s ease;\n transition: all 0.1s ease; }\n .dvr-controls[data-dvr-controls] .live-button:before {\n content: \"\";\n display: inline-block;\n position: relative;\n width: 7px;\n height: 7px;\n border-radius: 3.5px;\n margin-right: 3.5px;\n background-color: #fff; }\n .dvr-controls[data-dvr-controls] .live-button:hover {\n opacity: 1;\n text-shadow: rgba(255, 255, 255, 0.75) 0 0 5px; }\n\n.dvr .dvr-controls[data-dvr-controls] .live-info {\n display: none; }\n\n.dvr .dvr-controls[data-dvr-controls] .live-button {\n display: block; }\n\n.dvr.media-control.live[data-media-control] .media-control-layer[data-controls] .bar-container[data-seekbar] .bar-background[data-seekbar] .bar-fill-2[data-seekbar] {\n background-color: #005aff; }\n\n.media-control.live[data-media-control] .media-control-layer[data-controls] .bar-container[data-seekbar] .bar-background[data-seekbar] .bar-fill-2[data-seekbar] {\n background-color: #ff0101; }\n", ""]); + + // exports + + +/***/ }, +/* 102 */ +/***/ function(module, exports) { + + module.exports = "<div class=\"live-info\"><%= live %></div>\n<button type=\"button\" class=\"live-button\"><%= backToLive %></button>\n"; + +/***/ }, +/* 103 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + module.exports = __webpack_require__(104); + +/***/ }, +/* 104 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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 _core_plugin = __webpack_require__(105); + + var _core_plugin2 = _interopRequireDefault(_core_plugin); + + var _events = __webpack_require__(5); + + var _events2 = _interopRequireDefault(_events); + + var _clapprZepto = __webpack_require__(16); + + var _clapprZepto2 = _interopRequireDefault(_clapprZepto); + + var _play = __webpack_require__(37); + + var _play2 = _interopRequireDefault(_play); + + var _pause = __webpack_require__(38); + + var _pause2 = _interopRequireDefault(_pause); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + 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; } + + var oldIcon = (0, _clapprZepto2.default)('link[rel="shortcut icon"]'); + + var Favicon = function (_CorePlugin) { + _inherits(Favicon, _CorePlugin); + + _createClass(Favicon, [{ + key: 'name', + get: function get() { + return 'favicon'; + } + }, { + key: 'oldIcon', + get: function get() { + return oldIcon; + } + }]); + + function Favicon(core) { + _classCallCheck(this, Favicon); + + var _this = _possibleConstructorReturn(this, _CorePlugin.call(this, core)); + + _this._container = null; + _this.configure(); + return _this; + } + + Favicon.prototype.configure = function configure() { + if (this.core.options.changeFavicon) { + if (!this.enabled) { + this.stopListening(this.core, _events2.default.CORE_OPTIONS_CHANGE); + this.enable(); + } + } else if (this.enabled) { + this.disable(); + this.listenTo(this.core, _events2.default.CORE_OPTIONS_CHANGE, this.configure); + } + }; + + Favicon.prototype.bindEvents = function bindEvents() { + this.listenTo(this.core, _events2.default.CORE_OPTIONS_CHANGE, this.configure); + this.listenTo(this.core.mediaControl, _events2.default.MEDIACONTROL_CONTAINERCHANGED, this.containerChanged); + if (this.core.mediaControl.container) { + this.containerChanged(); + } + }; + + Favicon.prototype.containerChanged = function containerChanged() { + this._container && this.stopListening(this._container); + this._container = this.core.mediaControl.container; + this.listenTo(this._container, _events2.default.CONTAINER_PLAY, this.setPlayIcon); + this.listenTo(this._container, _events2.default.CONTAINER_PAUSE, this.setPauseIcon); + this.listenTo(this._container, _events2.default.CONTAINER_STOP, this.resetIcon); + this.listenTo(this._container, _events2.default.CONTAINER_ENDED, this.resetIcon); + this.listenTo(this._container, _events2.default.CONTAINER_ERROR, this.resetIcon); + this.resetIcon(); + }; + + Favicon.prototype.disable = function disable() { + _CorePlugin.prototype.disable.call(this); + this.resetIcon(); + }; + + Favicon.prototype.destroy = function destroy() { + _CorePlugin.prototype.destroy.call(this); + this.resetIcon(); + }; + + Favicon.prototype.createIcon = function createIcon(svg) { + var canvas = (0, _clapprZepto2.default)('<canvas/>'); + canvas[0].width = 16; + canvas[0].height = 16; + var ctx = canvas[0].getContext('2d'); + ctx.fillStyle = '#000'; + var d = (0, _clapprZepto2.default)(svg).find('path').attr('d'); + var path = new Path2D(d); + ctx.fill(path); + var icon = (0, _clapprZepto2.default)('<link rel="shortcut icon" type="image/png"/>'); + icon.attr('href', canvas[0].toDataURL('image/png')); + return icon; + }; + + Favicon.prototype.setPlayIcon = function setPlayIcon() { + if (!this.playIcon) { + this.playIcon = this.createIcon(_play2.default); + } + this.changeIcon(this.playIcon); + }; + + Favicon.prototype.setPauseIcon = function setPauseIcon() { + if (!this.pauseIcon) { + this.pauseIcon = this.createIcon(_pause2.default); + } + this.changeIcon(this.pauseIcon); + }; + + Favicon.prototype.resetIcon = function resetIcon() { + (0, _clapprZepto2.default)('link[rel="shortcut icon"]').remove(); + (0, _clapprZepto2.default)('head').append(this.oldIcon); + }; + + Favicon.prototype.changeIcon = function changeIcon(icon) { + if (icon) { + (0, _clapprZepto2.default)('link[rel="shortcut icon"]').remove(); + (0, _clapprZepto2.default)('head').append(icon); + } + }; + + return Favicon; + }(_core_plugin2.default); + + exports.default = Favicon; + module.exports = exports['default']; + +/***/ }, +/* 105 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + var _utils = __webpack_require__(2); + + var _base_object = __webpack_require__(4); + + var _base_object2 = _interopRequireDefault(_base_object); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + 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; } + + var CorePlugin = function (_BaseObject) { + _inherits(CorePlugin, _BaseObject); + + function CorePlugin(core) { + _classCallCheck(this, CorePlugin); + + var _this = _possibleConstructorReturn(this, _BaseObject.call(this, core.options)); + + _this.core = core; + _this.enabled = true; + _this.bindEvents(); + return _this; + } + + CorePlugin.prototype.bindEvents = function bindEvents() {}; + + CorePlugin.prototype.enable = function enable() { + if (!this.enabled) { + this.bindEvents(); + this.enabled = true; + } + }; + + CorePlugin.prototype.disable = function disable() { + if (this.enabled) { + this.stopListening(); + this.enabled = false; + } + }; + + CorePlugin.prototype.getExternalInterface = function getExternalInterface() { + return {}; + }; + + CorePlugin.prototype.destroy = function destroy() { + this.stopListening(); + }; + + return CorePlugin; + }(_base_object2.default); + + exports.default = CorePlugin; + + + CorePlugin.extend = function (properties) { + return (0, _utils.extend)(CorePlugin, properties); + }; + + CorePlugin.type = 'core'; + module.exports = exports['default']; + +/***/ }, +/* 106 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + module.exports = __webpack_require__(107); + +/***/ }, +/* 107 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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 _utils = __webpack_require__(2); + + var _ui_core_plugin = __webpack_require__(100); + + var _ui_core_plugin2 = _interopRequireDefault(_ui_core_plugin); + + var _styler = __webpack_require__(15); + + var _styler2 = _interopRequireDefault(_styler); + + var _template = __webpack_require__(17); + + var _template2 = _interopRequireDefault(_template); + + var _events = __webpack_require__(5); + + var _events2 = _interopRequireDefault(_events); + + var _playback = __webpack_require__(31); + + var _playback2 = _interopRequireDefault(_playback); + + var _seek_time = __webpack_require__(108); + + var _seek_time2 = _interopRequireDefault(_seek_time); + + var _seek_time3 = __webpack_require__(109); + + var _seek_time4 = _interopRequireDefault(_seek_time3); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + 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; } // Copyright 2014 Globo.com Player authors. All rights reserved. + // Use of this source code is governed by a BSD-style + // license that can be found in the LICENSE file. + + var SeekTime = function (_UICorePlugin) { + _inherits(SeekTime, _UICorePlugin); + + _createClass(SeekTime, [{ + key: 'name', + get: function get() { + return 'seek_time'; + } + }, { + key: 'template', + get: function get() { + return (0, _template2.default)(_seek_time4.default); + } + }, { + key: 'attributes', + get: function get() { + return { + 'class': 'seek-time', + 'data-seek-time': '' + }; + } + }, { + key: 'mediaControl', + get: function get() { + return this.core.mediaControl; + } + }, { + key: 'mediaControlContainer', + get: function get() { + return this.mediaControl.container; + } + }, { + key: 'isLiveStreamWithDvr', + get: function get() { + return this.mediaControlContainer && this.mediaControlContainer.getPlaybackType() === _playback2.default.LIVE && this.mediaControlContainer.isDvrEnabled(); + } + }, { + key: 'durationShown', + get: function get() { + return this.isLiveStreamWithDvr && !this.useActualLiveTime; + } + }, { + key: 'useActualLiveTime', + get: function get() { + return this.actualLiveTime && this.isLiveStreamWithDvr; + } + }]); + + function SeekTime(core) { + _classCallCheck(this, SeekTime); + + var _this = _possibleConstructorReturn(this, _UICorePlugin.call(this, core)); + + _this.hoveringOverSeekBar = false; + _this.hoverPosition = null; + _this.duration = null; + _this.actualLiveTime = !!_this.mediaControl.options.actualLiveTime; + if (_this.actualLiveTime) { + if (_this.mediaControl.options.actualLiveServerTime) { + _this.actualLiveServerTimeDiff = new Date().getTime() - new Date(_this.mediaControl.options.actualLiveServerTime).getTime(); + } else { + _this.actualLiveServerTimeDiff = 0; + } + } + return _this; + } + + SeekTime.prototype.bindEvents = function bindEvents() { + this.listenTo(this.mediaControl, _events2.default.MEDIACONTROL_RENDERED, this.render); + this.listenTo(this.mediaControl, _events2.default.MEDIACONTROL_MOUSEMOVE_SEEKBAR, this.showTime); + this.listenTo(this.mediaControl, _events2.default.MEDIACONTROL_MOUSELEAVE_SEEKBAR, this.hideTime); + this.listenTo(this.mediaControl, _events2.default.MEDIACONTROL_CONTAINERCHANGED, this.onContainerChanged); + if (this.mediaControlContainer) { + this.listenTo(this.mediaControlContainer, _events2.default.CONTAINER_PLAYBACKDVRSTATECHANGED, this.update); + this.listenTo(this.mediaControlContainer, _events2.default.CONTAINER_TIMEUPDATE, this.updateDuration); + } + }; + + SeekTime.prototype.onContainerChanged = function onContainerChanged() { + this.stopListening(); + this.bindEvents(); + }; + + SeekTime.prototype.updateDuration = function updateDuration(timeProgress) { + this.duration = timeProgress.total; + this.update(); + }; + + SeekTime.prototype.showTime = function showTime(event) { + this.hoveringOverSeekBar = true; + this.calculateHoverPosition(event); + this.update(); + }; + + SeekTime.prototype.hideTime = function hideTime() { + this.hoveringOverSeekBar = false; + this.update(); + }; + + SeekTime.prototype.calculateHoverPosition = function calculateHoverPosition(event) { + var offset = event.pageX - this.mediaControl.$seekBarContainer.offset().left; + // proportion into the seek bar that the mouse is hovered over 0-1 + this.hoverPosition = Math.min(1, Math.max(offset / this.mediaControl.$seekBarContainer.width(), 0)); + }; + + SeekTime.prototype.getSeekTime = function getSeekTime() { + var seekTime = void 0, + secondsSinceMidnight = void 0; + if (this.useActualLiveTime) { + var d = new Date(new Date().getTime() - this.actualLiveServerTimeDiff), + e = new Date(d); + secondsSinceMidnight = (e - d.setHours(0, 0, 0, 0)) / 1000; + seekTime = secondsSinceMidnight - this.duration + this.hoverPosition * this.duration; + if (seekTime < 0) { + seekTime += 86400; + } + } else { + seekTime = this.hoverPosition * this.duration; + } + return { seekTime: seekTime, secondsSinceMidnight: secondsSinceMidnight }; + }; + + SeekTime.prototype.update = function update() { + if (!this.rendered) { + // update() is always called after a render + return; + } + if (!this.shouldBeVisible()) { + this.$el.hide(); + this.$el.css('left', '-100%'); + } else { + var seekTime = this.getSeekTime(); + var currentSeekTime = (0, _utils.formatTime)(seekTime.seekTime, this.useActualLiveTime); + // only update dom if necessary, ie time actually changed + if (currentSeekTime !== this.displayedSeekTime) { + this.$seekTimeEl.text(currentSeekTime); + this.displayedSeekTime = currentSeekTime; + } + + if (this.durationShown) { + this.$durationEl.show(); + var currentDuration = (0, _utils.formatTime)(this.actualLiveTime ? seekTime.secondsSinceMidnight : this.duration, this.actualLiveTime); + if (currentDuration !== this.displayedDuration) { + this.$durationEl.text(currentDuration); + this.displayedDuration = currentDuration; + } + } else { + this.$durationEl.hide(); + } + + // the element must be unhidden before its width is requested, otherwise it's width will be reported as 0 + this.$el.show(); + var containerWidth = this.mediaControl.$seekBarContainer.width(); + var elWidth = this.$el.width(); + var elLeftPos = this.hoverPosition * containerWidth; + elLeftPos -= elWidth / 2; + elLeftPos = Math.max(0, Math.min(elLeftPos, containerWidth - elWidth)); + this.$el.css('left', elLeftPos); + } + }; + + SeekTime.prototype.shouldBeVisible = function shouldBeVisible() { + return this.mediaControlContainer && this.mediaControlContainer.settings.seekEnabled && this.hoveringOverSeekBar && this.hoverPosition !== null && this.duration !== null; + }; + + SeekTime.prototype.render = function render() { + this.rendered = true; + this.displayedDuration = null; + this.displayedSeekTime = null; + var style = _styler2.default.getStyleFor(_seek_time2.default); + this.$el.html(this.template()); + this.$el.append(style); + this.$el.hide(); + this.mediaControl.$el.append(this.el); + this.$seekTimeEl = this.$el.find('[data-seek-time]'); + this.$durationEl = this.$el.find('[data-duration]'); + this.$durationEl.hide(); + this.update(); + }; + + return SeekTime; + }(_ui_core_plugin2.default); + + exports.default = SeekTime; + module.exports = exports['default']; + +/***/ }, +/* 108 */ +/***/ function(module, exports, __webpack_require__) { + + exports = module.exports = __webpack_require__(25)(); + // imports + + + // module + exports.push([module.id, ".seek-time[data-seek-time] {\n position: absolute;\n white-space: nowrap;\n height: 20px;\n line-height: 20px;\n font-size: 0;\n left: -100%;\n bottom: 55px;\n background-color: rgba(2, 2, 2, 0.5);\n z-index: 9999;\n -webkit-transition: opacity 0.1s ease;\n -moz-transition: opacity 0.1s ease;\n transition: opacity 0.1s ease; }\n .seek-time[data-seek-time].hidden[data-seek-time] {\n opacity: 0; }\n .seek-time[data-seek-time] [data-seek-time] {\n display: inline-block;\n color: white;\n font-size: 10px;\n padding-left: 7px;\n padding-right: 7px;\n vertical-align: top; }\n .seek-time[data-seek-time] [data-duration] {\n display: inline-block;\n color: rgba(255, 255, 255, 0.5);\n font-size: 10px;\n padding-right: 7px;\n vertical-align: top; }\n .seek-time[data-seek-time] [data-duration]:before {\n content: \"|\";\n margin-right: 7px; }\n", ""]); + + // exports + + +/***/ }, +/* 109 */ +/***/ function(module, exports) { + + module.exports = "<span data-seek-time></span>\n<span data-duration></span>\n"; + +/***/ }, +/* 110 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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 _core_plugin = __webpack_require__(105); + + var _core_plugin2 = _interopRequireDefault(_core_plugin); + + var _events = __webpack_require__(5); + + var _events2 = _interopRequireDefault(_events); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + 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; } + + var SourcesPlugin = function (_CorePlugin) { + _inherits(SourcesPlugin, _CorePlugin); + + function SourcesPlugin() { + _classCallCheck(this, SourcesPlugin); + + return _possibleConstructorReturn(this, _CorePlugin.apply(this, arguments)); + } + + SourcesPlugin.prototype.bindEvents = function bindEvents() { + this.listenTo(this.core, _events2.default.CORE_CONTAINERS_CREATED, this.onContainersCreated); + }; + + SourcesPlugin.prototype.onContainersCreated = function onContainersCreated() { + var firstValidSource = this.core.containers.filter(function (container) { + return container.playback.name !== 'no_op'; + })[0] || this.core.containers[0]; + if (firstValidSource) { + this.core.containers.forEach(function (container) { + if (container !== firstValidSource) { + container.destroy(); + } + }); + } + }; + + _createClass(SourcesPlugin, [{ + key: 'name', + get: function get() { + return 'sources'; + } + }]); + + return SourcesPlugin; + }(_core_plugin2.default); + + exports.default = SourcesPlugin; + module.exports = exports['default']; + +/***/ }, +/* 111 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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 _events = __webpack_require__(5); + + var _events2 = _interopRequireDefault(_events); + + var _core_plugin = __webpack_require__(105); + + var _core_plugin2 = _interopRequireDefault(_core_plugin); + + var _utils = __webpack_require__(2); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + 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; } + + var EndVideo = function (_CorePlugin) { + _inherits(EndVideo, _CorePlugin); + + function EndVideo() { + _classCallCheck(this, EndVideo); + + return _possibleConstructorReturn(this, _CorePlugin.apply(this, arguments)); + } + + EndVideo.prototype.bindEvents = function bindEvents() { + this.listenTo(this.core.mediaControl, _events2.default.MEDIACONTROL_CONTAINERCHANGED, this.containerChanged); + var container = this.core.getCurrentContainer(); + if (container) { + this.listenTo(container, _events2.default.CONTAINER_ENDED, this.ended); + this.listenTo(container, _events2.default.CONTAINER_STOP, this.ended); + } + }; + + EndVideo.prototype.containerChanged = function containerChanged() { + this.stopListening(); + this.bindEvents(); + }; + + EndVideo.prototype.ended = function ended() { + var exitOnEnd = typeof this.core.options.exitFullscreenOnEnd === 'undefined' || this.core.options.exitFullscreenOnEnd; + if (exitOnEnd && _utils.Fullscreen.isFullscreen()) { + this.core.toggleFullscreen(); + } + }; + + _createClass(EndVideo, [{ + key: 'name', + get: function get() { + return 'end_video'; + } + }]); + + return EndVideo; + }(_core_plugin2.default); + + exports.default = EndVideo; + module.exports = exports['default']; + +/***/ }, +/* 112 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + 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 _utils = __webpack_require__(2); + + var _core_plugin = __webpack_require__(105); + + var _core_plugin2 = _interopRequireDefault(_core_plugin); + + var _lodash = __webpack_require__(32); + + var _lodash2 = _interopRequireDefault(_lodash); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + 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; } + + /** + * The internationalization (i18n) plugin + * @class Strings + * @constructor + * @extends CorePlugin + * @module plugins + */ + var Strings = function (_CorePlugin) { + _inherits(Strings, _CorePlugin); + + _createClass(Strings, [{ + key: 'name', + get: function get() { + return 'strings'; + } + }]); + + function Strings(core) { + _classCallCheck(this, Strings); + + var _this = _possibleConstructorReturn(this, _CorePlugin.call(this, core)); + + _this._initializeMessages(); + return _this; + } + /** + * Gets a translated string for the given key. + * @method t + * @param {String} key the key to all messages + * @return {String} translated label + */ + + + Strings.prototype.t = function t(key) { + var lang = this._language(); + var i18n = lang && this._messages[lang] || this._messages['en']; + return i18n[key] || key; + }; + + Strings.prototype._language = function _language() { + return this.core.options.language || (0, _utils.getBrowserLanguage)(); + }; + + Strings.prototype._initializeMessages = function _initializeMessages() { + this._messages = (0, _lodash2.default)({ + 'en': { + 'live': 'live', + 'back_to_live': 'back to live', + 'playback_not_supported': 'Your browser does not support the playback of this video. Please try using a different browser.' + }, + 'pt': { + 'live': 'ao vivo', + 'back_to_live': 'voltar para o ao vivo', + 'playback_not_supported': 'Seu navegador não supporta a reprodução deste video. Por favor, tente usar um navegador diferente.' + }, + 'es': { + 'live': 'vivo', + 'back_to_live': 'volver en vivo', + 'playback_not_supported': 'Su navegador no soporta la reproducción de un video. Por favor, trate de usar un navegador diferente.' + }, + 'ru': { + 'live': 'прямой эфир', + 'back_to_live': 'к прямому эфиру', + 'playback_not_supported': 'Ваш браузер не поддерживает воспроизведение этого видео. Пожалуйста, попробуйте другой браузер.' + } + }, this.core.options.strings || {}); + + this._messages['pt-BR'] = this._messages['pt']; + this._messages['en-US'] = this._messages['en']; + this._messages['es-419'] = this._messages['es']; + }; + + return Strings; + }(_core_plugin2.default); + + exports.default = Strings; + module.exports = exports['default']; + +/***/ } +/******/ ]) +}); +;
\ No newline at end of file |